File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ipguard / packet.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 21:59:41 2012 UTC (12 years, 10 months ago) by misho
Branches: ipguard, MAIN
CVS tags: v1_04p3, v1_04p0, v1_04, HEAD
ipguard

/*  packet.c
 *
 * Copyright (c) 2010 SeaD <sead at deep.perm.ru>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *  $Id: packet.c,v 1.1.1.1 2012/02/21 21:59:41 misho Exp $
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef __OpenBSD__
# include <net/if.h>
# include <net/if_arp.h>
#endif  /* __OpenBSD__ */

#ifdef __linux__
# include <time.h>
# include <netinet/ether.h>
#else   /* __linux__ */
# include <netinet/if_ether.h>
#endif  /* __linux__ */

#include <pcap.h>

#ifndef __GLIBC__
# define __GLIBC__      1
# include <libnet.h>
# undef __GLIBC__
#else   /* __GLIBC__ */
# include <libnet.h>
#endif  /* __GLIBC__ */

#include "ipguard.h"

static struct ether_addr se_addr, te_addr, sh_addr, th_addr, bh_addr, zh_addr,
    fake_addr, pfake_addr;
static struct in_addr sp_addr, tp_addr, zp_addr;

static pcap_t *pcap = NULL;
static libnet_t *ln = NULL;
static char ifname[IFNAMSIZ];

static struct pair_t {
    struct ether_addr mac;
    struct in_addr ip;
    struct in_addr mask;
} *pairs = NULL;
static int pair_num = 0;

static struct buffer_t {
    struct ether_addr mac;
    struct in_addr ip;
    int num;
    time_t last;
} *buffer = NULL;

void buffer_init(void) {
    if ((buffer = (struct buffer_t *) malloc(sizeof(struct buffer_t) * buffer_num)) == NULL) {
        log_str(ERROR, "malloc():", strerror(errno));
        exit(EXIT_FAILURE);
    }
}

void buffer_destroy(void) {
    free(buffer);
}

void buffer_add(void) {
    struct buffer_t *p = NULL, *pn = NULL;
    register int n;

    for (n = 0, p = buffer; n < buffer_num; n++, p++)
        if (!memcmp(&sh_addr, &p->mac, sizeof(struct ether_addr))) {
            memcpy(&p->ip, &sp_addr, sizeof(struct in_addr));
            p->num++; p->last = time(&p->last); return;
        }

    for (n = 0, p = buffer, pn = p + 1; n < buffer_num - 1; n++, p++, pn++)
        memcpy(p, pn, sizeof(struct buffer_t));
    memcpy(&p->mac, &sh_addr, sizeof(struct ether_addr));
    memcpy(&p->ip, &sp_addr, sizeof(struct in_addr));
    p->num = 1; p->last = time(&p->last);
}

void buffer_dump(void) {
    struct buffer_t *p = NULL;
    register int n;

    snprintf(s, 64, "Bad MAC-IP buffer (%d pairs)", buffer_num);
    log_str(INFO, s, "");
    
    for (n = 0, p = buffer + buffer_num - 1; n < buffer_num && p->num > 0; n++, p--) {
        snprintf(s, 64, "%03d %s %-15s %2d %lu",
            n+1, ether_ntoa(&p->mac), inet_ntoa(p->ip), p->num, (unsigned long) p->last);
        log_str(INFO, "dump:", s);
    }
}

void buffer_dump2ethers(void) {
    struct buffer_t *p = NULL;
    register int n;

    for (n = 0, p = buffer + buffer_num - 1; n < buffer_num && p->num > 0; n++, p--) {
        snprintf(s, 64, "%-17s       %-15s", ether_ntoa(&p->mac), inet_ntoa(p->ip));
        log_str(INFO, s, "");
    }
}

uint32_t bit_2mask(int bit) {
    uint32_t mask = 0;
    register int n;

    for (n = 0; n < bit; n++) { mask >>= 1; mask |= 0x80000000; }

    return mask;
}

void pair_init(void) {
    struct libnet_ether_addr *ha = NULL;
    uint32_t pa = 0, mask;
    int bit = 0;

    if ((pairs = (struct pair_t *) malloc(sizeof(struct pair_t))) == NULL) {
        log_str(ERROR, "malloc():", strerror(errno));
        exit(EXIT_FAILURE);
    }
    if ((ha = libnet_get_hwaddr(ln)) == NULL) {
        log_str(ERROR, libnet_geterror(ln), "");
        exit(EXIT_FAILURE);
    }
    if (!(pa = libnet_get_ipaddr4(ln))) {
        log_str(ERROR, libnet_geterror(ln), "");
        exit(EXIT_FAILURE);
    }
    mask = htonl(bit_2mask(bit));
    if (!nofirst) {
        memcpy(&pairs->mac, ha, sizeof(struct ether_addr));
        memcpy(&pairs->ip, &pa, sizeof(struct in_addr));
        memcpy(&pairs->mask, &mask, sizeof(struct in_addr));
        pair_num++;

        if (debug > 1)
            fprintf(stderr, "PAIR ADD FIRST: %s %s %u\n",
                ether_ntoa(&pairs->mac), inet_ntoa(pairs->ip), bit);
    }
}

void pair_destroy(void) {
    free(pairs);
    pair_num = 0;
}

void pair_add(char *mac, char *ip) {
    struct pair_t *p = NULL;
    struct ether_addr *fmac = NULL;
    uint32_t fip, mask;
    char *m;
    int bit;

    if ((m = strchr(ip, '/'))) {
        *m++ = '\0'; bit = atoi(m);
        if (!bit || (bit < 0) || (bit > 32)) {
            log_str(NOTICE, "Wrong mask", m); return;
        }
        if (debug > 2) fprintf(stderr, "NETWORK MASK: %s %u\n", ip, bit);
    } else bit = 0;

    if ((fmac = ether_aton(mac)) == NULL) {
        if (verbose) log_str(WARNING, "Invalid MAC:", mac); return;
    }
    if ((fip = libnet_name2addr4(ln, ip, LIBNET_RESOLVE)) == -1) {
        if (verbose) log_str(WARNING, "Host unknown:", ip); return;
    }

    mask = htonl(bit_2mask(bit));
    if (mask) fip &= mask;

    if ((pairs = realloc(pairs, ++pair_num * sizeof(struct pair_t))) == NULL) {
        log_str(ERROR, "realloc():", strerror(errno));
        exit(EXIT_FAILURE);
    }
    p = pairs + pair_num - 1;

    memcpy(&p->mac, fmac, sizeof(struct ether_addr));
    memcpy(&p->ip, &fip, sizeof(struct in_addr));
    memcpy(&p->mask, &mask, sizeof(struct in_addr));

    if (debug > 1)
        fprintf(stderr, "PAIR ADD: %s %s %u\n",
            ether_ntoa(&p->mac), inet_ntoa(p->ip), bit);
}

void pair_dump(void) {
    struct pair_t *p = pairs;
    char ntoasw[16];
    register int n;

    snprintf(s, 64, "Current MAC-IP table (%d pairs)", pair_num);
    log_str(INFO, s, "");

    for (n = 0; n < pair_num; n++, p++) {
        strncpy(ntoasw, inet_ntoa(p->mask), 16);
        snprintf(s, 64, "%s %s %s",
            ether_ntoa(&p->mac), inet_ntoa(p->ip), ntoasw);
        log_str(INFO, "dump:", s);
    }
}

void packet_fmacs(int op) {
    switch (op) {
        /* init */
        case 1: mac_rand(fmac); strncpy(pfmac, fmac, 18); break;
        /* regenerate */
        case 2: strncpy(pfmac, fmac, 18); mac_regen(fmac); break;
    }

    memcpy(&fake_addr, ether_aton(fmac), sizeof(struct ether_addr));
    memcpy(&pfake_addr, ether_aton(pfmac), sizeof(struct ether_addr));
}

void packet_init(char *iface) {
    struct bpf_program arp_p;
    char ebuf[PCAP_ERRBUF_SIZE] = "\0";

    strncpy(ifname, iface, IFNAMSIZ);
    memset(&bh_addr, 0xff, sizeof(struct ether_addr));
    memset(&zh_addr, 0x00, sizeof(struct ether_addr));
    memset(&zp_addr, 0x00, sizeof(struct in_addr));
    packet_fmacs(1);

    /* pcap init */
    if (!(pcap = pcap_open_live(ifname, 100, promisc, 10, ebuf))) {
        debug = 2; log_str(ERROR, "pcap_open_live():", ebuf);
        exit(EXIT_FAILURE);
    }
    if (strlen(ebuf)) fprintf(stderr, "warning: %s\n", ebuf);
    if (pcap_compile(pcap, &arp_p, pcapf, 0, -1) == -1) {
        snprintf(s, 128, "pcap_compile(%s):", pcapf);
        debug = 2; log_str(ERROR, s, "wrong expression");
        exit(EXIT_FAILURE);
    }
    if (pcap_setfilter(pcap, &arp_p) == -1) {
        debug = 2; log_str(ERROR, "pcap_setfilter():", "failed");
        exit(EXIT_FAILURE);
    }

    /* libnet init */
    if (!(ln = libnet_init(LIBNET_LINK_ADV, ifname, ebuf))) {
        debug = 2; log_str(ERROR, ebuf, "");
        exit(EXIT_FAILURE);
    }

    pair_init();
    if (buffer_num) buffer_init();
    ethers_init();

    snprintf(s, 128, "%s %s %s (%d pairs) fake %s%s",
        iface, ether_ntoa(&pairs->mac), inet_ntoa(pairs->ip), pair_num, fmac,
        (read_only ? " RO mode" : ""));
    log_str(NOTICE, s, "");
    if (verbose) pair_dump();
}

void packet_destroy (void) {
    if (buffer_num) buffer_destroy();
    pair_destroy();
    libnet_destroy(ln);
    pcap_close(pcap);
}

void packet_send(int op, int num) {
    register int n;

    if (libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, op,
            (u_char *) &sh_addr, (u_char *) &sp_addr,
            (u_char *) &th_addr, (u_char *) &tp_addr,
            NULL, 0, ln, 0) == -1) {
        log_str(ERROR, libnet_geterror(ln), "");
        exit(EXIT_FAILURE);
    }

    if (libnet_build_ethernet(
            (u_char *) &te_addr, (u_char *) &se_addr,
            ETHERTYPE_ARP, NULL, 0, ln, 0) == -1) {
        log_str(ERROR, libnet_geterror(ln), "");
        exit(EXIT_FAILURE);
    }

    if ((n = num)) while (1) {
        if (debug > 1) {
            fprintf(stderr, "SEND ARP %u: ", op);
            fprintf(stderr, "%-17s -> ", ether_ntoa(&se_addr)); fprintf(stderr, "%-17s\n  ", ether_ntoa(&te_addr));
            fprintf(stderr, "sdr %-17s %-15s  ", ether_ntoa(&sh_addr), inet_ntoa(sp_addr));
            fprintf(stderr, "trg %-17s %-15s\n", ether_ntoa(&th_addr), inet_ntoa(tp_addr));
        }
        if (!read_only)
            if (libnet_write(ln) == -1) {
                log_str(ERROR, libnet_geterror(ln), "");
                exit_ipguard(EXIT_FAILURE);
            }
        if (--n <= 0) break; else usleep(fake_time * 1000);
    }

    libnet_clear_packet(ln);
}

void packet_check(void) {
    struct pair_t *p = NULL;
    struct pair_t *se_ch = NULL, *te_ch = NULL, *sp_ch = NULL, *tp_ch = NULL;
    uint32_t fsip, ftip, fmask, swip;
    int bsdr = 0;
    char ntoasw[16];
    register int n;

    /* count mismatch MACs between ether and ARP headers */
    if (memcmp(&se_addr, &sh_addr, sizeof(struct ether_addr))) {
        if (debug > 1) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            fprintf(stderr, "MISMATCH SOURCE: %-17s %-15s %-15s\n", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
        }
        mis++;
    }
    /* count non-zero target MAC in ARP who-has */
    if (memcmp(&zh_addr, &th_addr, sizeof(struct ether_addr))) {
        if (debug > 1) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            fprintf(stderr, "NON-ZERO TARGET: %-17s %-15s %-15s\n", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
        }
        nzh++;
    }
    /* count non-bcast target ether MAC */
    if (memcmp(&bh_addr, &te_addr, sizeof(struct ether_addr))) {
        if (debug > 1) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            fprintf(stderr, "NON-BCAST WHO-HAS: %-17s %-15s %-15s\n", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
        }
        nbe++;
    }

    /* return if sdr MAC is ours */
    if (!memcmp(&se_addr, &pairs->mac, sizeof(struct ether_addr))) {
        if (debug > 2) fprintf(stderr, "MY MAC PASSED: %s\n", ether_ntoa(&se_addr));
        mymac++; return;
    }
    /* return if sdr MAC is our fake MAC */
    if (!memcmp(&se_addr, &fake_addr, sizeof(struct ether_addr))) {
        if (debug > 2) fprintf(stderr, "FAKE MAC PASSED: %s\n", ether_ntoa(&se_addr));
        fake++; return;
    }
    /* return if sdr MAC is previous fake MAC */
    if (fake_regen)
        if (!memcmp(&se_addr, &pfake_addr, sizeof(struct ether_addr))) {
            if (debug > 2) fprintf(stderr, "PRE FAKE MAC PASSED: %s\n", ether_ntoa(&se_addr));
            pfake++; return;
        }
    /* fill weird gratuitous ARP sdr IP 0.0.0.0 (Linux/MacOS/Vista) */
    if (!memcmp(&sp_addr, &zp_addr, sizeof(struct in_addr))) {
        if (debug > 2) fprintf(stderr, "WEIRD GRATUITOUS FILL: %s\n", inet_ntoa(tp_addr));
        memcpy(&sp_addr, &tp_addr, sizeof(struct in_addr));
        wgrat++;
    }

    /* check if IP/MAC is listed */
    for (n = 0, p = pairs; n < pair_num; n++, p++) {
        memcpy(&fsip, &sp_addr, sizeof(struct in_addr));
        memcpy(&ftip, &tp_addr, sizeof(struct in_addr));
        memcpy(&fmask, &p->mask, sizeof(struct in_addr));
        if (fmask) { fsip &= fmask; ftip &= fmask; }
        if (!memcmp(&se_addr, &p->mac, sizeof(struct ether_addr))) {
            if (!se_ch) se_ch = p;
            if (!memcmp(&fsip, &p->ip, sizeof(struct in_addr))) se_ch = sp_ch = p;
        }
        if (!memcmp(&te_addr, &p->mac, sizeof(struct ether_addr))) {
            if (!te_ch) te_ch = p;
            if (!memcmp(&ftip, &p->ip, sizeof(struct in_addr))) te_ch = tp_ch = p;
        }
        if (!sp_ch && !memcmp(&fsip, &p->ip, sizeof(struct in_addr))) sp_ch = p;
        if (!tp_ch && !memcmp(&ftip, &p->ip, sizeof(struct in_addr))) tp_ch = p;
    }

    if (debug > 2) {
        if (se_ch) fprintf(stderr, "MATCH MAC   SDR: %-18s -> %-18s\n", ether_ntoa(&se_ch->mac), inet_ntoa(se_ch->ip));
        else fprintf(stderr, "UNKNOWN MAC SDR: %-18s\n", ether_ntoa(&se_addr));
        if (te_ch) fprintf(stderr, "MATCH MAC   TRG: %-18s -> %-18s\n", ether_ntoa(&te_ch->mac), inet_ntoa(te_ch->ip));
        else {
            if (!memcmp(&te_addr, &bh_addr, sizeof(struct ether_addr)))
                fprintf(stderr, "BCAST MAC   TRG: %-18s\n", "ff:ff:ff:ff:ff:ff");
            else
                fprintf(stderr, "UNKNOWN MAC TRG: %-18s\n", ether_ntoa(&te_addr));
        }
        if (sp_ch) fprintf(stderr, "MATCH IP    SDR: %-18s -> %-18s\n", inet_ntoa(sp_ch->ip), ether_ntoa(&sp_ch->mac));
        else fprintf(stderr, "UNKNOWN IP  SDR: %-18s\n", inet_ntoa(sp_addr));
        if (tp_ch) fprintf(stderr, "MATCH IP    TRG: %-18s -> %-18s\n", inet_ntoa(tp_ch->ip), ether_ntoa(&tp_ch->mac));
        else fprintf(stderr, "UNKNOWN IP  TRG: %-18s\n", inet_ntoa(tp_addr));
    }

    /* gratuitous ARP packets */
    if (se_ch && (se_ch == tp_ch) &&
        !memcmp(&sp_addr, &tp_addr, sizeof(struct in_addr))) {
        if (debug > 2) fprintf(stderr, "GOOD PASSED: grat\n");
        grat++; return;
    }

    /* wildcard trg MAC 00:00:00:00:00:00 */
    if (sp_ch && !memcmp(&sp_ch->mac, &zh_addr, sizeof(struct ether_addr))) {
        if (debug > 2) fprintf(stderr, "GOOD PASSED: zmac\n");
        zmac++; if (!addr_nosubst) return;
    }
    /* wildcard sdr IP 0.0.0.0 */
    if (se_ch && !memcmp(&se_ch->ip, &zp_addr, sizeof(struct in_addr))) {
        if (debug > 2) fprintf(stderr, "GOOD PASSED: zip\n");
        zip++; if (!addr_nosubst) return;
    }

    /* correct pair - sdr MAC pos == sdr IP pos and listed trg IP pos or not duplex */
    if (se_ch && (se_ch == sp_ch) && (tp_ch || !duplex)) {
        if (debug > 2) fprintf(stderr, "GOOD PASSED: good\n");
        good++; return;
    }

    /* sdr MAC and sdr IP are both not listed - new pair */
    if (!se_ch && (se_ch == sp_ch)) {
        if (verbose) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            snprintf(s, 128, "%-17s %-15s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
            log_str(NOTICE, "bnew:", s);
        }
        bnew++; bsdr++; if (grant) return;
    }

    /* sdr MAC is listed but IP is not */
    if (se_ch && (se_ch != sp_ch)) {
        if (verbose) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            snprintf(s, 128, "%-17s %-15s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
            log_str(NOTICE, "bsip:", s);
        }
        bsip++; bsdr++;
    }
    /* sdr IP is listed but MAC is not */
    if (sp_ch && (se_ch != sp_ch)) {
        if (verbose) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            snprintf(s, 128, "%-17s %-15s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
            log_str(NOTICE, "bmac:", s);
        }
        bmac++; bsdr++;
    }

    /* do not block gratuitous ARP in hidden mode */
    if (!memcmp(&sp_addr, &tp_addr, sizeof(struct in_addr))) {
        if (verbose) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            snprintf(s, 128, "%-17s %-15s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
            log_str(NOTICE, "bgrt:", s);
        }
        bgrat++; if (hidden) return;
    }

    /* if duplex and trg IP is not listed then block */
    if (!tp_ch) {
        if (verbose) {
            strncpy(ntoasw, inet_ntoa(tp_addr), 16);
            snprintf(s, 128, "%-17s %-15s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
            log_str(NOTICE, "btip:", s);
        }
        btip++; if (!bsdr && !duplex) return;
    }

    /* add pirate pair to bad buffer */
    if (bsdr && buffer_num) buffer_add();

    strncpy(ntoasw, inet_ntoa(tp_addr), 16);
    snprintf(s, 128, "%-17s %-15s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr), ntoasw);
    log_str(NOTICE, "xxxx:", s);

    /* block pirate
        eth fake MAC -> pirate MAC
        sdr fake MAC - target IP
        trg pirate MAC - pirate IP
    */
    memcpy(&te_addr, &se_addr, sizeof(struct ether_addr));
    memcpy(&se_addr, &fake_addr, sizeof(struct ether_addr));
    memcpy(&th_addr, &sh_addr, sizeof(struct ether_addr));
    memcpy(&sh_addr, &fake_addr, sizeof(struct ether_addr));
    memcpy(&swip, &tp_addr, sizeof(struct in_addr));
    memcpy(&tp_addr, &sp_addr, sizeof(struct in_addr));
    memcpy(&sp_addr, &swip, sizeof(struct in_addr));
    packet_send(ARPOP_REPLY, fake_num);

    if (bsdr) {
        /* control headshot
            eth fake MAC -> pirate MAC
            sdr fake MAC - pirate IP
            trg pirate MAC - pirate IP
        */
        memcpy(&sp_addr, &tp_addr, sizeof(struct in_addr));
        packet_send(ARPOP_REPLY, 1);
    }

    /* and fix broken ARP caches by broadcast */
    if (bsdr && fixbc) {
        memcpy(&te_addr, &bh_addr, sizeof(struct ether_addr));
        memcpy(&th_addr, &zh_addr, sizeof(struct ether_addr));
        if (sp_ch) {
            /* sdr IP is valid
                eth ethers MAC -> broadcast
                sdr ethers MAC - ethers IP
                trg zero MAC - ethers IP
            */
            memcpy(&se_addr, &sp_ch->mac, sizeof(struct ether_addr));
            memcpy(&sh_addr, &sp_ch->mac, sizeof(struct ether_addr));
            memcpy(&sp_addr, &sp_ch->ip, sizeof(struct in_addr));
        } else if (se_ch) {
            /* sdr MAC is valid
                eth ethers MAC -> broadcast
                sdr ethers MAC - ethers IP
                trg zero MAC - ethers IP
            ** disabled as probably useless **
            memcpy(&se_addr, &se_ch->mac, sizeof(struct ether_addr));
            memcpy(&sh_addr, &se_ch->mac, sizeof(struct ether_addr));
            memcpy(&sp_addr, &se_ch->ip, sizeof(struct in_addr));
            */
        } else {
            /* invalid sender
                eth fake MAC -> broadcast
                sdr fake MAC - pirate IP
                trg zero MAC - pirate IP
            */
            memcpy(&se_addr, &fake_addr, sizeof(struct ether_addr));
            memcpy(&sh_addr, &fake_addr, sizeof(struct ether_addr));
        }
        memcpy(&tp_addr, &sp_addr, sizeof(struct in_addr));
        packet_send(ARPOP_REQUEST, 1);
    }

    bad++;
    if (debug > 2)
        fprintf(stderr, "STATS:  god %-6d  new %-6d  bmc %-6d  bip %-6d  btg %-6d  all %-6d\n",
            good+mymac+fake+pfake+grat+wgrat+zmac+zip, bnew, bmac, bsip, btip, all);
}

void packet_handle(char *blah, struct pcap_pkthdr *blah2, u_char *recv) {
    struct ether_header *eth_h = NULL;
    struct ether_arp *arp_h = NULL;
    int op;

    sig_catch();
    if (ethers_update) ethers_stat();
    if (fake_regen) packet_fmacs(2);

    eth_h = (struct ether_header *) recv;
    if (htons(eth_h->ether_type) != ETHERTYPE_ARP) {
        snprintf(s, 8, "0x%x", eth_h->ether_type);
        log_str(WARNING, "wrong ethertype:", s); return;
    }

    memcpy(&se_addr, &eth_h->ether_shost, sizeof(struct ether_addr));
    memcpy(&te_addr, &eth_h->ether_dhost, sizeof(struct ether_addr));

    arp_h = (struct ether_arp *) ((char *) eth_h + ETHER_HDR_LEN);
    if (htons(arp_h->ea_hdr.ar_hrd) != ARPHRD_ETHER) {
        snprintf(s, 8, "0x%x", arp_h->ea_hdr.ar_hrd);
        log_str(WARNING, "wrong hw address:", s); return;
    }
    if (htons(arp_h->ea_hdr.ar_pro) != ETHERTYPE_IP) {
        snprintf(s, 8, "0x%x", arp_h->ea_hdr.ar_pro);
        log_str(WARNING, "wrong proto address:", s); return;
    }
    op = htons(arp_h->ea_hdr.ar_op);

    memcpy(&sh_addr, &arp_h->arp_sha, sizeof(struct ether_addr));
    memcpy(&sp_addr, &arp_h->arp_spa, sizeof(struct in_addr));
    memcpy(&th_addr, &arp_h->arp_tha, sizeof(struct ether_addr));
    memcpy(&tp_addr, &arp_h->arp_tpa, sizeof(struct in_addr));

    if (debug > 1) {
        fprintf(stderr, "RECV ARP %u: ", op);
        fprintf(stderr, "%-17s -> ", ether_ntoa(&se_addr)); fprintf(stderr, "%-17s\n  ", ether_ntoa(&te_addr));
        fprintf(stderr, "sdr %-17s %-15s  ", ether_ntoa(&sh_addr), inet_ntoa(sp_addr));
        fprintf(stderr, "trg %-17s %-15s\n", ether_ntoa(&th_addr), inet_ntoa(tp_addr));
    }

    if (op == ARPOP_REPLY) return;
    if (op != ARPOP_REQUEST) {
        snprintf(s, 8, "0x%x", op);
        log_str(WARNING, "wrong arp op:", s);
        return;
    }

    all++;
    packet_check();
}

void packet_recv(void) {
    if (pcap_loop(pcap, -1, (pcap_handler) packet_handle, NULL) == -1) {
        log_str(ERROR, "pcap_loop()", "failed");
        exit_ipguard(EXIT_FAILURE);
    }
}

void stat_dump(void) {
    struct pcap_stat ps;

    snprintf(s, 64, "ARP statistics:");
    log_str(INFO, s, "");
    snprintf(s, 64, "Total ARP who-has packets %u", all);
    log_str(INFO, s, "");
    snprintf(s, 64, "Good MAC-IP pairs %u", good);
    log_str(INFO, s, "");
    snprintf(s, 64, "Gratuitous MAC-IP/weird %u/%u", grat, wgrat);
    log_str(INFO, s, "");
    snprintf(s, 64, "Zero MAC/IP %u/%u", zmac, zip);
    log_str(INFO, s, "");
    snprintf(s, 64, "New MAC-IP pairs %u", bnew);
    log_str(INFO, s, "");
    snprintf(s, 64, "Bad MAC-IP pairs %u", bad);
    log_str(INFO, s, "");
    snprintf(s, 64, "Bad MAC/IP %u/%u", bmac, bsip);
    log_str(INFO, s, "");
    if (duplex) {
        snprintf(s, 64, "Bad target IP %u", btip);
        log_str(INFO, s, "");
    }
    snprintf(s, 64, "Bad gratuitous MAC-IP %u", bgrat);
    log_str(INFO, s, "");
    snprintf(s, 64, "My/Fake/PreFake MAC %u/%u/%u", mymac, fake, pfake);
    log_str(INFO, s, "");
    snprintf(s, 64, "Mismatch sender Ether/ARP MAC %u", mis);
    log_str(INFO, s, "");
    snprintf(s, 64, "Non-zero target ARP MAC %u", nzh);
    log_str(INFO, s, "");
    snprintf(s, 64, "Non-bcast target Ether MAC %u", nbe);
    log_str(INFO, s, "");

    if (pcap_stats(pcap, &ps) == -1) {
        log_str(WARNING, "pcap_stat()", "failed"); return;
    }
    snprintf(s, 64, "PCAP statistics:");
    log_str(INFO, s, "");
    snprintf(s, 64, "Received/Dropped packets %u/%u", ps.ps_recv, ps.ps_drop);
    log_str(INFO, s, "");
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>