Hi all, find appended, for use and review, my C version of a /proc/net/ip_conntrack based hash function and bucket occupation test program. Here is a short usage summary:
- extract crc32.c and cttest.c from the attachments - compile with "gcc -o cttest cttest.c" - by default, /proc/net/ip_conntrack is read. Use the "-f filename" option to read the given file, instead. - use '-u' to enable unidirectional operation; the reverse tuple is ignored. - by default, the hash bucket count is 7168. Use "-s NUMBER" to modify this. Look at the kernel output from ip_conntrack load time to find out the bucket count that is active on your system. - use '-h NAME' to select the hash function to use. Right now you can use: o original this should be the hash function used by ip_conntrack o crc32 a crc32 over proto/sip/dip/sport/dport o random no relation to the input at all; this still reads the input, but only to get at the number of entries. Instead of calculating an input based hash, random() is called. - use '-i bucket-number' to show the input lines corresponding to the given hash bucket number. default is to not do this. - use '-t count' to output the bucket-number corresponding to the first bucket with 'count' entries in it. For example, if you find that the maximum bucket list length is 131, use '-t 131', and look for "occupation target 131 is at XXX". Then, use '-i XXX' on the next run to see all input lines falling into bucket number XXX. NOTE: as we have seen, it is likely that reading /proc/net/ip_conntrack does produce duplicate lines. This test program does not try to cope with that situation; if it applies to you, first save the output to some file, and use "sort somefile | uniq >testfile" to generate the input file for this test program. Oh, maybe I should mention what the program outputs... The main output comes after the "Count Length CT" heading. Each lines gives the COUNT of all buckets having list LENGTH, sorted by decreasing length. CT gives the cummulative total over the counts. Everybody should see the results for their own system. Really. I mean it. best regards Patrick
/* crc32.c -- compute the CRC-32 of a data stream * Copyright (C) 1995-2002 Mark Adler * stolen from zlib CVS */ /* @(#) $Id: crc32.c,v 1.2.2.1 2002/03/12 09:34:29 werner Exp $ */ /* ======================================================================== * Table of CRC-32's of all single-byte values (made by make_crc_table) */ static const u32 crc_table[256] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; /* ========================================================================= */ #define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); #define DO2(buf) DO1(buf); DO1(buf); #define DO4(buf) DO2(buf); DO2(buf); #define DO8(buf) DO4(buf); DO4(buf); /* ========================================================================= */ u32 crc32(crc, buf, len) u32 crc; const char *buf; int len; { crc = crc ^ 0xffffffffL; while (len >= 8) { DO8(buf); len -= 8; } if (len) do { DO1(buf); } while (--len); return crc ^ 0xffffffffL; }
/* cttest.c - parse /proc/net/ip_conntrack, compute bucket occupation * * This is how lines look: * * udp 17 0 src=213.6.73.215 dst=62.104.191.241 sport=1858 dport=53 src=62.104.191.241 dst=213.6.73.215 sport=53 dport=1858 use=1 * tcp 6 114 TIME_WAIT src=62.104.211.64 dst=212.7.144.66 sport=34685 dport=80 src=212.7.144.66 dst=62.104.211.64 sport=80 dport=34685 [ASSURED] use=1 */ #include <stdio.h> #include <string.h> #include <malloc.h> #include <stdlib.h> #include <getopt.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> typedef unsigned long u32; typedef unsigned short u16; typedef unsigned char u8; struct ct_key { u32 sip; u32 dip; u16 sport; u16 dport; u8 proto; }; static u32 hash_conntrack(struct ct_key *key) { return ntohl(key->sip + key->dip + key->sport + key->dport + key->proto) + ntohs(key->sport); } #include "crc32.c" static u32 hash_crc32(struct ct_key *key) { u32 crc = crc32(0, (const char *) &key->proto, sizeof(key->proto)); u32 v32; u16 v16; v32 = ntohl(key->sip); crc = crc32(crc, (const char *) &v32, sizeof(v32)); v32 = ntohl(key->dip); crc = crc32(crc, (const char *) &v32, sizeof(v32)); v16 = ntohs(key->sport); crc = crc32(crc, (const char *) &v16, sizeof(v16)); v16 = ntohs(key->dport); crc = crc32(crc, (const char *) &v16, sizeof(v16)); return crc; } static u32 hash_random(struct ct_key *key) { return (u32) random(); } struct hash_def { char *name; u32 (*hash_f)(struct ct_key *); }; #define NR_HASHES (sizeof(hashes)/sizeof(hashes[0])) static struct hash_def hashes[] = { { "original", hash_conntrack }, { "random", hash_random }, { "crc32", hash_crc32 }, }; static u32 htable_size = 7168; static u32 *occupation; static struct hash_def *test_hash = hashes; static void do_init(void) { u32 idx; occupation = malloc(sizeof(*occupation) * htable_size); for (idx=0; idx<htable_size; idx++) occupation[idx] = 0; } static int display_target = -1; static int do_count(struct ct_key *key) { u32 idx = test_hash->hash_f(key) % htable_size; occupation[idx]++; return ((int)idx) == display_target; } static u32 parse_ip(char *t) { char *p = strchr(t, '='); if (!p++) return 0; return inet_addr(p); } static u16 parse_port(char *t) { char *p = strchr(t, '='); if (!p++) return 0; return (u16) strtoul(p, 0, 10); } static int unidir; static int process(char *p) { struct ct_key key[1]; char *tok; int res; strtok(p, " \t\n"); /* prot text */ tok = strtok(0, " \t\n"); key->proto = (u8) strtoul(tok, 0, 10); /* prot num */ strtok(0, " \t\n"); /* timeout */ switch (key->proto) { case 6: strtok(0, " \t\n"); /* tcp state */ case 17: break; default: return 0; } /* forward */ tok = strtok(0, " \t\n"); key->sip = parse_ip(tok); /* src ip */ tok = strtok(0, " \t\n"); key->dip = parse_ip(tok); /* dst ip */ tok = strtok(0, " \t\n"); key->sport = parse_port(tok); /* src port */ tok = strtok(0, " \t\n"); key->dport = parse_port(tok); /* dst port */ res = do_count(key); if (unidir) return res; /* reverse */ tok = strtok(0, " \t\n"); key->sip = parse_ip(tok); /* src ip */ tok = strtok(0, " \t\n"); key->dip = parse_ip(tok); /* dst ip */ tok = strtok(0, " \t\n"); key->sport = parse_port(tok); /* src port */ tok = strtok(0, " \t\n"); key->dport = parse_port(tok); /* dst port */ return res | do_count(key); } static int occupation_target = -1; static void show_occupation(void) { u32 idx, maxlen, total, partial, *agg; for (maxlen=0, total=0, idx=0; idx<htable_size; idx++) { total += occupation[idx]; if (occupation[idx] > maxlen) maxlen = occupation[idx]; } printf("hash function: %s\n", test_hash->name); printf("maximum bucket list length: %lu\n", maxlen); printf("total number of entries: %lu\n", total); agg = malloc(sizeof(*agg) * (maxlen+1)); for (idx=0; idx<=maxlen; idx++) agg[idx] = 0; for (idx=0; idx<htable_size; idx++) { agg[occupation[idx]]++; if (((int)occupation[idx]) == occupation_target) { printf("occupation target %d at %lu\n", occupation_target, idx); occupation_target = -1; } } printf("Count\tLength\tCT\n"); for (partial=0, idx=maxlen; idx>0; idx--) { double d; if (agg[idx] == 0) continue; partial += idx * agg[idx]; d = (100.0 * partial) / (1.0 * total); printf("%lu\t%lu\t%.02f%%\n", agg[idx], idx, d); } printf("%lu\t0\t100.00%%\n", agg[idx]); } static struct hash_def *find_hash(char *name) { u32 idx; for (idx=0; idx<NR_HASHES; idx++) if (0 == strcmp(name, hashes[idx].name)) return hashes+idx; fprintf(stderr, "no hash function named '%s'\n", name); return 0; } static void usage(void) { } int main(int argc, char **argv) { int c; char buf[512], copy[512]; char *filename = "/proc/net/ip_conntrack"; FILE *fp; while (EOF != (c = getopt(argc, argv, "f:us:h:i:t:"))) switch(c) { case 'f': filename = optarg; break; case 'u': unidir = 1; break; case 's': htable_size = strtoul(optarg, 0, 10); break; case 'h': test_hash = find_hash(optarg); break; case 'i': display_target = strtol(optarg, 0, 10); break; case 't': occupation_target = strtol(optarg, 0, 10); break; default: usage(); } if (!test_hash) return 1; fp = (0 == strcmp(filename, "-")) ? stdin : fopen(filename, "r"); if (!fp) { fprintf(stderr, "%s: %s\n", filename, strerror(errno)); exit(1); } printf("filename: %s\n", filename); do_init(); while (fgets(buf, sizeof(buf), fp)) { if (display_target != -1) strcpy(copy, buf); if (process(buf)) printf("[%d]\t%s", display_target, copy); } if (fp != stdin) fclose(fp); show_occupation(); return 0; }