Example #1
0
inline
uint32_t make_hash(packetinfo *pi)
{
    if (pi->ip4 != NULL) {
        return (PI_IP4SRC(pi) + PI_IP4DST(pi)) % BUCKET_SIZE;
    } else {
        return (PI_IP6SRC(pi).s6_addr32[0] + PI_IP6SRC(pi).s6_addr32[1] +
                PI_IP6SRC(pi).s6_addr32[2] + PI_IP6SRC(pi).s6_addr32[3] +
                PI_IP6DST(pi).s6_addr32[0] + PI_IP6DST(pi).s6_addr32[1] +
                PI_IP6DST(pi).s6_addr32[2] + PI_IP6DST(pi).s6_addr32[3]
                 ) % BUCKET_SIZE;
    }
}
Example #2
0
/* freshly smelling connection :d */
connection *cxt_new(packetinfo *pi)
{
    struct in6_addr ips;
    struct in6_addr ipd;
    connection *cxt;
    config.cxtrackerid++;
    cxt = (connection *) calloc(1, sizeof(connection));
    //assert(cxt);
    cxt->cxid = config.cxtrackerid;

    cxt->af = pi->af;
    if(pi->tcph) cxt->s_tcpFlags |= pi->tcph->t_flags;
    cxt->start_time = pi->pheader->ts.tv_sec;
    cxt->last_pkt_time = pi->pheader->ts.tv_sec;

    if(pi-> af== AF_INET6){
        cxt->s_ip = PI_IP6SRC(pi);
        cxt->d_ip = PI_IP6DST(pi);
    }else {
        ips.s6_addr32[0] = pi->ip4->ip_src;
        ipd.s6_addr32[0] = pi->ip4->ip_dst;
        cxt->s_ip = ips;
        cxt->d_ip = ipd;
    }

    cxt->s_port = pi->s_port;
    cxt->d_port = pi->d_port;
    cxt->proto = pi->proto;

    cxt->check = 0x00;
    cxt->reversed = 0;
    config.curcxt++;

    return cxt;
}
Example #3
0
void cxt_new (connection *cxt, packetinfo *pi)
{
        //extern u_int64_t cxtrackerid;
        config.nftrackerid += 1;

        cxt->cxid = config.nftrackerid;
        cxt->af = pi->af;
        cxt->s_tcpFlags |= (pi->tcph ? pi->tcph->t_flags : 0x00);
        cxt->s_total_bytes = pi->packet_bytes;
        cxt->s_total_pkts = 1;
        cxt->start_time = pi->pheader->ts.tv_sec;
        cxt->last_pkt_time = pi->pheader->ts.tv_sec;
        if(pi->af == AF_INET){
            cxt->s_ip.s6_addr32[0] = PI_IP4SRC(pi);
            cxt->d_ip.s6_addr32[0] = PI_IP4DST(pi);
        }else{
            cxt->s_ip = PI_IP6SRC(pi);
            cxt->d_ip = PI_IP6DST(pi);
        }
        cxt->s_port = pi->s_port;
        cxt->d_port = pi->d_port;
        cxt->proto = (pi->ip4 ? pi->ip4->ip_p : pi->ip6->next);
        cxt->check = 0x00;
        //cxt->c_asset = NULL;
        //cxt->s_asset = NULL;
        cxt->files = NULL;
        cxt->reversed = 0;
        pi->sc = SC_CLIENT;
}
Example #4
0
/* asset *asset_lookup(struct in6_addr ip, int af)
 * tries to match your packet to a sender we've seen before
 * 
 * 1. already in connection database
 * 2. ip4 lookup & ip6 lookup
 * 3. mac lookup
 *
 * asset_lookup should return 0 on success, 1 on failure
 */
uint8_t asset_lookup(packetinfo *pi)
{
    uint64_t hash;
    asset *masset = NULL;

    if (pi->asset != NULL || NULL != (pi->asset = connection_lookup(pi))){
       return SUCCESS;
    } else {
        if (pi->af == AF_INET) {
            uint32_t ip;

            if(pi->arph) {// arp check
                //memcpy(&ip, pi->arph->arp_spa, sizeof(uint8_t)*4);
                ip  = *(uint32_t*) pi->arph->arp_spa;
            } else {
                ip = PI_IP4SRC(pi);
            }
            hash = ASSET_HASH4(ip);
            masset = passet[hash];
            while (masset != NULL) {
                //if (memcmp(&ip_addr,&rec->ip_addr,16)) {
                if (masset->af == AF_INET 
                    && CMP_ADDR4( &masset->ip_addr, ip))
                {
                    pi->asset = masset;
                    return SUCCESS;
                }
                masset = masset->next;
            }
            return ERROR;
        } else if (pi->af == AF_INET6) {
            hash = ASSET_HASH6(PI_IP6SRC(pi));
            masset = passet[hash];
            while (masset != NULL) {
                if (masset->af == AF_INET6 &&
                    CMP_ADDR6(&masset->ip_addr, &PI_IP6SRC(pi))){
                    pi->asset = masset;
                    return SUCCESS;
                }
                masset = masset->next;
            }
            return ERROR;
        }
        return ERROR;
    }
}
Example #5
0
/* ----------------------------------------------------------
 * FUNCTION     : add_asset
 * DESCRIPTION  : This function will add an asset to the
 *              : specified asset data structure.
 * INPUT        : 0 - packetinfo (af, ip_src, vlan)
 * RETURN       : None!
 * ---------------------------------------------------------- */
void add_asset(packetinfo *pi)
{
    uint64_t hash;
    asset *masset = NULL;

    config.pr_s.assets++;

    masset = (asset *) calloc(1, sizeof(asset));
    masset->af = pi->af;
    masset->vlan = pi->vlan;
    masset->i_attempts = 0;
    masset->first_seen = masset->last_seen = pi->pheader->ts.tv_sec;

    if (pi->af == AF_INET) {
        if(pi->arph) // mongo arp check
            //memcpy(&masset->ip_addr.__u6_addr.__u6_addr32[0], pi->arph->arp_spa, sizeof(uint32_t));
           IP4ADDR(&masset->ip_addr) = *(uint32_t*) pi->arph->arp_spa;
        else
           IP4ADDR(&masset->ip_addr)  = PI_IP4SRC(pi);
        hash = ASSET_HASH4(IP4ADDR(&masset->ip_addr));
    } else if (pi->af == AF_INET6) {
        masset->ip_addr = PI_IP6SRC(pi);
        hash = ASSET_HASH6(PI_IP6SRC(pi));
    }

    masset->next = passet[hash];

    if (passet[hash] != NULL)
        passet[hash]->prev = masset;
    masset->prev = NULL;
    masset->os = NULL;
    masset->services = NULL;
    masset->macentry = NULL;
    passet[hash] = masset;

#ifdef DEBUGG
    /* verbose info for sanity checking */
    static char ip_addr_s[INET6_ADDRSTRLEN];
    // pi->ip_src does not exist!
    u_ntop(pi->ip_src, pi->af, ip_addr_s);
    dlog("[*] asset added: %s\n",ip_addr_s);
#endif
    
    //return masset;
}
Example #6
0
/* freshly smelling connection :d */
connection *cxt_new(packetinfo *pi)
{
    struct in6_addr ips;
    struct in6_addr ipd;
    connection *cxt;
    cxtrackerid++;
    cxt = (connection *) calloc(1, sizeof(connection));
    assert(cxt);
    cxt->cxid = cxtrackerid;

    cxt->af = pi->af;
    if(pi->tcph) cxt->s_tcpFlags |= pi->tcph->t_flags;
    //cxt->s_tcpFlags |= (pi->tcph ? pi->tcph->t_flags : 0x00);//why??
    //cxt->d_tcpFlags = 0x00;
    cxt->s_total_bytes = pi->packet_bytes;
    cxt->s_total_pkts = 1;
    cxt->start_time = pi->pheader->ts.tv_sec;
    cxt->last_pkt_time = pi->pheader->ts.tv_sec;

    if(pi-> af== AF_INET6){
        cxt->s_ip = PI_IP6SRC(pi);
        cxt->d_ip = PI_IP6DST(pi);
    }else {
        // ugly hack :(
        // the way we do ip4/6 is DIRTY
        ips.s6_addr32[0] = pi->ip4->ip_src;
        ipd.s6_addr32[0] = pi->ip4->ip_dst;
        cxt->s_ip = ips;
        cxt->d_ip = ipd;
    }

    cxt->s_port = pi->s_port;
    cxt->d_port = pi->d_port;
    cxt->proto = pi->proto;

    cxt->check = 0x00;
    cxt->c_asset = NULL;
    cxt->s_asset = NULL;
    cxt->reversed = 0;

    return cxt;
}
Example #7
0
int connection_tracking(packetinfo *pi)
{
    struct in6_addr *ip_src;
    struct in6_addr *ip_dst;
    struct in6_addr ips;
    struct in6_addr ipd;
    uint16_t src_port = pi->s_port;
    uint16_t dst_port = pi->d_port;
    int af = pi->af;
    connection *cxt = NULL;
    connection *head = NULL;
    uint32_t hash;

    if(af == AF_INET6){
        ip_src = &PI_IP6SRC(pi);
        ip_dst = &PI_IP6DST(pi);
    }
    else {
        ips.s6_addr32[0] = pi->ip4->ip_src;
        ipd.s6_addr32[0] = pi->ip4->ip_dst;
        ip_src = &ips;
        ip_dst = &ipd;
    }

    /* Find the right connection bucket */
    if (af == AF_INET) {
        hash = CXT_HASH4(IP4ADDR(ip_src), IP4ADDR(ip_dst), src_port, dst_port,
                         pi->proto);
    }
    else if (af == AF_INET6) {
        hash = CXT_HASH6(ip_src, ip_dst, src_port, dst_port, pi->proto);
    }
    else {
        dlog("[D] Only CTX with AF_INET and AF_INET6 are supported: %d\n", af);
        return 0;
    }

    cxt = bucket[hash];
    head = cxt;

    /* Search through the bucket */
    while (cxt != NULL)
    {
        /* Two-way compare of given connection against connection table */
        if (af == AF_INET) {
            if (CMP_CXT4(cxt, IP4ADDR(ip_src), src_port, IP4ADDR(ip_dst), dst_port)) {
                /* Client sends first packet (TCP/SYN - UDP?) hence this is a client */
                dlog("[D] Found existing v4 client connection.\n");
                return cxt_update_client(cxt, pi);
            }
            else if (CMP_CXT4(cxt, IP4ADDR(ip_dst), dst_port, IP4ADDR(ip_src), src_port)) {

                if (pi->sc == SC_SERVER) {
                    /* This is a server */
                    dlog("[D] Found existing v4 server connection.\n");
                    return cxt_update_server(cxt, pi);
                } else {
                    /* This is a client, where we saw a mid-stream DNS response first */
                    dlog("[D] Found existing unknown v4 server connection.\n");
                    return cxt_update_client(cxt, pi);
                }
            }
        }
        else if (af == AF_INET6) {
            if (CMP_CXT6(cxt, ip_src, src_port, ip_dst, dst_port)) {
                dlog("[D] Found existing v6 client connection.\n");
                return cxt_update_client(cxt, pi);
            }
            else if (CMP_CXT6(cxt, ip_dst, dst_port, ip_src, src_port)) {
                dlog("[D] Found existing v6 client connection.\n");
                return cxt_update_server(cxt, pi);
            }
        }
        cxt = cxt->next;
    }
    /* Bucket turned upside down didn't yield anything. New connection */
    dlog("[D] New connection.\n");
    cxt = cxt_new(pi);

    /* New connections are pushed on to the head of bucket[s_hash] */
    cxt->next = head;
    if (head != NULL) {
        /* Are we double linked? */
        head->prev = cxt;
    }
    bucket[hash] = cxt;
    pi->cxt = cxt;
    return cxt_update_unknown(cxt, pi);
}
Example #8
0
int connection_tracking(packetinfo *pi) {
    struct in6_addr *ip_src;
    struct in6_addr *ip_dst;
    struct in6_addr ips;
    struct in6_addr ipd;
    uint16_t src_port = pi->s_port;
    uint16_t dst_port = pi->d_port;
    int af = pi->af;
    connection *cxt = NULL;
    connection *head = NULL;
    uint32_t hash;

    if(af== AF_INET6){
        ip_src = &PI_IP6SRC(pi);
        ip_dst = &PI_IP6DST(pi);
    }else {
        ips.s6_addr32[0] = pi->ip4->ip_src;
        ipd.s6_addr32[0] = pi->ip4->ip_dst;
        ip_src = &ips;
        ip_dst = &ipd;
    }

    // find the right connection bucket
    if (af == AF_INET) {
        hash = CXT_HASH4(IP4ADDR(ip_src),IP4ADDR(ip_dst),src_port,dst_port,pi->proto);
    } else if (af == AF_INET6) {
        hash = CXT_HASH6(ip_src,ip_dst,src_port,dst_port,pi->proto);
    }
    cxt = bucket[hash];
    head = cxt;

   // search through the bucket
    while (cxt != NULL) {
        // Two-way compare of given connection against connection table
        if (af == AF_INET) {
            if (CMP_CXT4(cxt,IP4ADDR(ip_src),src_port,IP4ADDR(ip_dst),dst_port)){
                // Client sends first packet (TCP/SYN - UDP?) hence this is a client
                return cxt_update_client(cxt, pi);
            } else if (CMP_CXT4(cxt,IP4ADDR(ip_dst),dst_port,IP4ADDR(ip_src),src_port)) {
                // This is a server (Maybe not when we start up but in the long run)
                return cxt_update_server(cxt, pi);
            }
        } else if (af == AF_INET6) {
            if (CMP_CXT6(cxt,ip_src,src_port,ip_dst,dst_port)){
                return cxt_update_client(cxt, pi);
            } else if (CMP_CXT6(cxt,ip_dst,dst_port,ip_src,src_port)){
                return cxt_update_server(cxt, pi);
            }
        }
        cxt = cxt->next;
    }
    // bucket turned upside down didn't yeild anything. new connection
    cxt = cxt_new(pi);

    /* New connections are pushed on to the head of bucket[s_hash] */
    cxt->next = head;
    if (head != NULL) {
        // are we doubly linked?
        head->prev = cxt;
    }
    bucket[hash] = cxt;
    pi->cxt = cxt;
    return cxt_update_client(cxt, pi);
}
Example #9
0
/* does this ip belong to our network? do we care about the packet?
 *
 * unfortunately pcap sends us packets in host order
 * Return value: boolean
 */
inline int filter_packet(const int af, void *ipptr)
//const struct in6_addr *ip_s)
{
    ip6v ip_vec;
    ip6v t;

    int i, our = 0;
    char output[MAX_NETS];
    switch (af) {
        case AF_INET:
        {
            uint32_t *ip = (uint32_t *) ipptr;
            for (i = 0; i < MAX_NETS && i < config.nets; i++) {
                if (config.network[i].type != AF_INET)
                    continue;
#if DEBUG == 2
                inet_ntop(af, &config.network[i].addr.s6_addr32[0], output, MAX_NETS);
                vlog(0x2, "Filter: %s\n", output);
                inet_ntop(af, &config.network[i].mask.s6_addr32[0], output, MAX_NETS);
                vlog(0x2, "mask: %s\n", output);
                inet_ntop(af, ip, output, MAX_NETS);
                vlog(0x2, "ip: %s\n", output);
#endif
                if((*ip & config.network[i].mask.s6_addr32[0])
                    == config.network[i].addr.s6_addr32[0]) {
                    our = 1;
                    break;
                }
            }
        }
        break;
        case AF_INET6:
        {
            /* 32-bit comparison of ipv6 nets.
             * can do better here by using 64-bit or SIMD instructions
             *
             *
             * PS: use same code for ipv4 - 0 bytes and SIMD doesnt care*/

            ip_vec.ip6 = *((struct in6_addr *)ipptr);
            for (i = 0; i < MAX_NETS && i < config.nets; i++) {
                if(config.network[i].type != AF_INET6)
                    continue;
#if DEBUG == 2
                inet_ntop(af, &config.network[i].addr, output, MAX_NETS);
                dlog("net:  %s\n", output);
                inet_ntop(af, &config.network[i].mask, output, MAX_NETS);
                dlog("mask: %s\n", output);
                inet_ntop(af, &PI_IP6SRC(pi), output, MAX_NETS);
                dlog("ip: %s\n", output);
#endif
                if (config.network[i].type == AF_INET6) {
#if(1)
                /* apologies for the uglyness */
#ifdef HAVE_SSE2
#define compare128(x,y) __builtin_ia32_pcmpeqd128((x), (y))
                    // the builtin is only available on sse2! 
                    t.v = __builtin_ia32_pcmpeqd128(
                      ip_vec.v & config.network[i].mask_v,
                      config.network[i].addr_v);
                    if (t.i[0] & t.i[1])
#else
#define compare128(x,y) memcmp(&(x),&(y),16)
                    t.v = ip_vec.v & config.network[i].mask_v;
                    // xor(a,b) == 0 iff a==b
                    if (!( (t.i[0] ^ config.network[i].addr64[0]) & 
                           (t.i[1] ^ config.network[i].addr64[1]) ))
#endif
                    {
                        our = 1;
                        break;
                    }

#else
                    if ((ip_s.s6_addr32[0] & config.network[i]->mask.s6_addr32[0])
                        == config.network[i]->addr.s6_addr32[0]
                        && (ip_s.s6_addr32[1] & config.network[i]->mask.s6_addr32[1])
                        == config.network[i]->addr.s6_addr32[1]
                        && (ip_s.s6_addr32[2] & config.network[i]->mask.s6_addr32[2])
                        == config.network[i]->addr.s6_addr32[2]
                        && (ip_s.s6_addr32[3] & config.network[i]->mask.s6_addr32[3])
                        == config.network[i]->addr.s6_addr32[3]) {
                        our = 1;
                        break;
                    }
#endif
                }
            }
        }
        break;
        default:
        fprintf(stderr,
            "non-ip packets of type %d aren't filtered by netmask yet\n", af);
            our = 1;
    }
#ifdef DEBUG
    if (af == AF_INET6){
        inet_ntop(af, (struct in6addr*) ipptr, output, MAX_NETS);
    }else{
        inet_ntop(af, (uint32_t*)ipptr, output, MAX_NETS);
    }
    if (our){
        vlog(0x2, "Address %s is in our network.\n", output);
    } else {
        vlog(0x2, "Address %s is not our network.\n", output);
    }
#endif
    return our;
}
Example #10
0
int cx_track(packetinfo *pi) {
    struct in6_addr *ip_src;
    struct in6_addr *ip_dst;
    struct in6_addr ips;
    struct in6_addr ipd;
    uint16_t src_port = pi->s_port;
    uint16_t dst_port = pi->d_port;
    int af = pi->af;
    connection *cxt = NULL;
    connection *head = NULL;
    uint32_t hash;


    if(af== AF_INET6){
        ip_src = &PI_IP6SRC(pi);
        ip_dst = &PI_IP6DST(pi);
    }else {
        // ugly hack :(
        // the way we do ip4/6 is DIRTY
        // FIX IT?!!?
        ips.s6_addr32[0] = pi->ip4->ip_src;
        ipd.s6_addr32[0] = pi->ip4->ip_dst;
        ip_src = &ips;
        ip_dst = &ipd;
    }

    // find the right connection bucket
    if (af == AF_INET) {
        hash = CXT_HASH4(IP4ADDR(ip_src),IP4ADDR(ip_dst));
    } else if (af == AF_INET6) {
        hash = CXT_HASH6(ip_src,ip_dst);
    }
    cxt = bucket[hash];
    head = cxt;

    // search through the bucket
    while (cxt != NULL) {
        // Two-way compare of given connection against connection table
        if (af == AF_INET) {
            if (CMP_CXT4(cxt,IP4ADDR(ip_src),src_port,IP4ADDR(ip_dst),dst_port)){
                // Client sends first packet (TCP/SYN - UDP?) hence this is a client
                return cxt_update_client(cxt, pi);
            } else if (CMP_CXT4(cxt,IP4ADDR(ip_dst),dst_port,IP4ADDR(ip_src),src_port)) {
                // This is a server (Maybe not when we start up but in the long run)
                return cxt_update_server(cxt, pi);
            }
        } else if (af == AF_INET6) {
            if (CMP_CXT6(cxt,ip_src,src_port,ip_dst,dst_port)){
                return cxt_update_client(cxt, pi);
            } else if (CMP_CXT6(cxt,ip_dst,dst_port,ip_src,src_port)){
                return cxt_update_server(cxt, pi);
            }
        }
        cxt = cxt->next;
    }
    // bucket turned upside down didn't yeild anything. new connection
    cxt = cxt_new(pi);
    if(config.cflags & CONFIG_CXWRITE)
        log_connection(cxt, stdout, CX_NEW);

    /* * New connections are pushed on to the head of bucket[s_hash] */
    cxt->next = head;
    if (head != NULL) {
        // are we doubly linked?
        head->prev = cxt;
    }
    bucket[hash] = cxt;
    pi->cxt = cxt;

    /* * Return value should be 1, telling to do client service fingerprinting */
    return 1;
}
Example #11
0
inline void cxt_update (packetinfo *pi, uint32_t hash)
{
    connection *cxt = NULL;
    int ret = 0;
    /* get our hash bucket and lock it */
    cxtbucket *cb = &cxt_hash[hash];

    /* see if the bucket already has a connection */
    if (cb->cxt == NULL) {
        /* no, so get a new one */
        cxt = cb->cxt = cxt_dequeue(&cxt_spare_q);
        if (cxt == NULL) {
            cxt = cb->cxt = connection_alloc();
            if (cxt == NULL) {
                return;
            }
        }
        /* these are protected by the bucket lock */
        cxt->hnext = NULL;
        cxt->hprev = NULL;

        /* got one, initialize and return */
        cxt_new(cxt,pi);
        cxt_requeue(cxt, NULL, &cxt_est_q);
        cxt->cb = cb;
        cxt_update_src(cxt, pi);
        pi->cxt = cxt;
        return;
    }

    /* ok, we have a flow in the bucket. Let's find out if it is our flow */
    cxt = cb->cxt;

    /* see if this is the flow we are looking for */
    if (pi->af == AF_INET) {
        if (CMP_CXT4(cxt, PI_IP4SRC(pi), pi->s_port, PI_IP4DST(pi), pi->d_port)) {
            cxt_update_src(cxt, pi);
            ret = 1;
        } else if (CMP_CXT4(cxt, PI_IP4DST(pi), pi->d_port, PI_IP4SRC(pi), pi->s_port)) {
            cxt_update_dst(cxt, pi);
            ret = 1;
        }
    } else if (pi->af == AF_INET6){
        if (CMP_CXT6(cxt, &PI_IP6SRC(pi), pi->s_port, &PI_IP6DST(pi), pi->d_port)) {
            cxt_update_src(cxt, pi);
            ret = 1;
        } else if (CMP_CXT6(cxt, &PI_IP6DST(pi), pi->d_port, &PI_IP6SRC(pi), pi->s_port)) {
            cxt_update_dst(cxt, pi);
            ret = 1;
        }
    }

    if (ret == 0) {
        connection *pcxt = NULL; /* previous connection */

        while (cxt != NULL) {
            pcxt = cxt; /* pf is not locked at this point */
            cxt = cxt->hnext;

            if (cxt == NULL) {
                /* get us a new one and put it and the list tail */
                cxt = pcxt->hnext = cxt_dequeue(&cxt_spare_q);
                if (cxt == NULL) {

                    cxt = cb->cxt = connection_alloc();
                    if (cxt == NULL) {
                        return;
                    }
                }

                cxt->hnext = NULL;
                cxt->hprev = pcxt;

                /* initialize and return */
                cxt_new(cxt,pi);
                cxt_requeue(cxt, NULL, &cxt_est_q);

                cxt->cb = cb;
                cxt_update_src(cxt, pi);
                pi->cxt = cxt;
                return;
            }

            if (pi->af == AF_INET) {
                if (CMP_CXT4(cxt, PI_IP4SRC(pi), pi->s_port, PI_IP4DST(pi), pi->d_port)) {
                    cxt_update_src(cxt, pi);
                    ret = 1;
                } else if (CMP_CXT4(cxt, PI_IP4DST(pi), pi->d_port, PI_IP4SRC(pi), pi->s_port)) {
                    cxt_update_dst(cxt, pi);
                    ret = 1;
                }
            } else if (pi->af == AF_INET6) {
                if (CMP_CXT6(cxt, &PI_IP6SRC(pi), pi->s_port, &PI_IP6DST(pi), pi->d_port)) {
                    cxt_update_src(cxt, pi);
                    ret = 1;
                } else if (CMP_CXT6(cxt, &PI_IP6DST(pi), pi->d_port, &PI_IP6SRC(pi), pi->s_port)) {
                    cxt_update_dst(cxt, pi);
                    ret = 1;
                }
            }
            if ( ret != 0) {
                /* we found our flow, lets put it on top of the
                 * hash list -- this rewards active flows */
                if (cxt->hnext) cxt->hnext->hprev = cxt->hprev;
                if (cxt->hprev) cxt->hprev->hnext = cxt->hnext;

                cxt->hnext = cb->cxt;
                cxt->hprev = NULL;
                cb->cxt->hprev = cxt;
                cb->cxt = cxt;

                /* found our connection */
                pi->cxt = cxt;
                return;
            }

            /* not found, try the next... */
        }
    }
    pi->cxt = cxt;
    /* The 'root' connection was our connection, return it. */
    return;
}