root/tools/tools/toeplitz/toeplitz.c
#include <sys/types.h>
#include <sys/socket.h>

#include <arpa/inet.h>
#include <netinet/in.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifndef NBBY
#define NBBY    __NBBY
#endif

#define HASHMASK        0x7f

#define KEYLEN          40
#define HASHLEN         12

static uint8_t  toeplitz_key[KEYLEN];
static uint32_t hash_table[HASHLEN][256];

static void     toeplitz_init(uint32_t[][256], int, const uint8_t[], int);
static void     getaddrport(char *, uint32_t *, uint16_t *);

static void
usage(const char *cmd)
{
        fprintf(stderr, "%s [-s s1_hex [-s s2_hex]] [-p] [-m mask] [-d div] "
            "addr1.port1 addr2.port2\n", cmd);
        exit(1);
}

int
main(int argc, char *argv[])
{
        uint32_t saddr, daddr;
        uint16_t sport, dport;
        uint32_t res, mask, divisor;

        const char *cmd = argv[0];
        uint8_t seeds[2] = { 0x6d, 0x5a };
        int i, opt, use_port;

        i = 0;
        use_port = 0;
        mask = 0xffffffff;
        divisor = 0;

        while ((opt = getopt(argc, argv, "d:m:ps:")) != -1) {
                switch (opt) {
                case 'd':
                        divisor = strtoul(optarg, NULL, 10);
                        break;

                case 'm':
                        mask = strtoul(optarg, NULL, 16);
                        break;

                case 'p':
                        use_port = 1;
                        break;

                case 's':
                        if (i >= 2)
                                usage(cmd);
                        seeds[i++] = strtoul(optarg, NULL, 16);
                        break;

                default:
                        usage(cmd);
                }
        }
        argc -= optind;
        argv += optind;

        if (argc != 2)
                usage(cmd);

        for (i = 0; i < KEYLEN; ++i) {
                if (i & 1)
                        toeplitz_key[i] = seeds[1];
                else
                        toeplitz_key[i] = seeds[0];
        }

        getaddrport(argv[0], &saddr, &sport);
        getaddrport(argv[1], &daddr, &dport);

        toeplitz_init(hash_table, HASHLEN, toeplitz_key, KEYLEN);

        res =  hash_table[0][(saddr >> 0) & 0xff];
        res ^= hash_table[1][(saddr >> 8) & 0xff];
        res ^= hash_table[2][(saddr >> 16)  & 0xff];
        res ^= hash_table[3][(saddr >> 24)  & 0xff];
        res ^= hash_table[4][(daddr >> 0) & 0xff];
        res ^= hash_table[5][(daddr >> 8) & 0xff];
        res ^= hash_table[6][(daddr >> 16)  & 0xff];
        res ^= hash_table[7][(daddr >> 24)  & 0xff];
        if (use_port) {
                res ^= hash_table[8][(sport >> 0)  & 0xff];
                res ^= hash_table[9][(sport >> 8)  & 0xff];
                res ^= hash_table[10][(dport >> 0)  & 0xff];
                res ^= hash_table[11][(dport >> 8)  & 0xff];
        }

        printf("0x%08x, masked 0x%08x", res, res & mask);
        if (divisor == 0)
                printf("\n");
        else
                printf(", modulo %u\n", (res & HASHMASK) % divisor);
        exit(0);
}

static void
toeplitz_init(uint32_t cache[][256], int cache_len,
    const uint8_t key_str[], int key_strlen)
{
        int i;

        if (key_strlen < cache_len + (int)sizeof(uint32_t))
                exit(1);

        for (i = 0; i < cache_len; ++i) {
                uint32_t key[NBBY];
                int j, b, shift, val;

                bzero(key, sizeof(key));

                /*
                 * Calculate 32bit keys for one byte; one key for each bit.
                 */
                for (b = 0; b < NBBY; ++b) {
                        for (j = 0; j < 32; ++j) {
                                uint8_t k;
                                int bit;

                                bit = (i * NBBY) + b + j;

                                k = key_str[bit / NBBY];
                                shift = NBBY - (bit % NBBY) - 1;
                                if (k & (1 << shift))
                                        key[b] |= 1 << (31 - j);
                        }
                }

                /*
                 * Cache the results of all possible bit combination of
                 * one byte.
                 */
                for (val = 0; val < 256; ++val) {
                        uint32_t res = 0;

                        for (b = 0; b < NBBY; ++b) {
                                shift = NBBY - b - 1;
                                if (val & (1 << shift))
                                        res ^= key[b];
                        }
                        cache[i][val] = res;
                }
        }
}

static void
getaddrport(char *ap_str, uint32_t *addr, uint16_t *port0)
{
        uint16_t port;
        char *p;

        p = strrchr(ap_str, '.');
        if (p == NULL) {
                fprintf(stderr, "invalid addr.port %s\n", ap_str);
                exit(1);
        }

        *p = '\0';
        ++p;

        port = strtoul(p, NULL, 10);
        *port0 = htons(port);

        if (inet_pton(AF_INET, ap_str, addr) <= 0) {
                fprintf(stderr, "invalid addr %s\n", ap_str);
                exit(1);
        }
}