/* packet.c * * Copyright (c) 2005 SeaD * * 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. * */ #include #include #ifdef __OpenBSD__ # include # include #endif /* __OpenBSD__ */ #include #include #include #ifndef __GLIBC__ # define __GLIBC__ 1 # include # undef __GLIBC__ #else /* __GLIBC__ */ # include #endif /* __GLIBC__ */ #include "ipguard.h" static struct ether_addr se_addr, te_addr, sh_addr, th_addr, zh_addr, fake_addr; static struct in_addr sp_addr, tp_addr, zp_addr; static pcap_t *pcap = NULL; static struct libnet_link_int *lif = NULL; static u_char ifname[IFNAMSIZ]; static u_char *packet = NULL; static struct pair_t { struct ether_addr mac; struct in_addr ip; } *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(NOTICE, "dump:", 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(NOTICE, "dump:", s); } } void pair_init(char *iface) { struct ether_addr *ha = NULL; u_long pa = 0; char ebuf[LIBNET_ERRBUF_SIZE] = "\0"; strncpy(ifname, iface, IFNAMSIZ); 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(lif, ifname, ebuf))) { log_str(ERROR, "libnet_get_hwaddr():", ebuf); exit(EXIT_FAILURE); } if (!(pa = htonl(libnet_get_ipaddr(lif, ifname, ebuf)), sizeof(struct in_addr))) { log_str(ERROR, "libnet_get_ipaddr():", ebuf); exit(EXIT_FAILURE); } memcpy(&pairs->mac, ha, sizeof(struct ether_addr)); memcpy(&pairs->ip, &pa, sizeof(struct in_addr)); pair_num++; snprintf(s, 128, "%s %s %s", ifname, ether_ntoa(&pairs->mac), inet_ntoa(pairs->ip)); } void pair_destroy(void) { free(pairs); pair_num = 0; } void pair_add(char *mac, char *ip) { struct pair_t *p = NULL; 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, ether_aton(mac), sizeof(struct ether_addr)); if (!inet_aton(ip, &p->ip)) { log_str(WARNING, "inet_aton():", strerror(errno)); pair_num--; if ((pairs = realloc(pairs, --pair_num * sizeof(struct pair_t))) == NULL) { log_str(ERROR, "realloc():", strerror(errno)); exit(EXIT_FAILURE); } } } void pair_dump(void) { struct pair_t *p = pairs; register int n; snprintf(s, 64, "Current MAC/IP table (%d pairs)", pair_num); log_str(NOTICE, "dump:", s); for (n = 0; n < pair_num; n++, p++) { snprintf(s, 64, "%s %s", ether_ntoa(&p->mac), inet_ntoa(p->ip)); log_str(NOTICE, "dump:", s); } } void packet_init(void) { struct bpf_program arp_p; char ebuf[PCAP_ERRBUF_SIZE] = "\0"; memset(&zh_addr, 0x00, sizeof(struct ether_addr)); memset(&zp_addr, 0x00, sizeof(struct in_addr)); if (!addr_nosubst) rand_mac(fmac); memcpy(&fake_addr, ether_aton(fmac), sizeof(struct ether_addr)); /* pcap init */ if (!(pcap = pcap_open_live(ifname, PACKET_SIZE, promisc, 10, ebuf))) { 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, "arp", 0, -1) == -1) { log_str(ERROR, "pcap_compile():", "failed"); exit(EXIT_FAILURE); } if (pcap_setfilter(pcap, &arp_p) == -1) { log_str(ERROR, "pcap_setfilter():", "failed"); exit(EXIT_FAILURE); } /* libnet init */ if (!(lif = libnet_open_link_interface(ifname, ebuf))) { log_str(ERROR, "libnet_open_link_interface():", ebuf); exit(EXIT_FAILURE); } if (libnet_init_packet(PACKET_SIZE, &packet) == -1) { log_str(ERROR, "libnet_init_packet():", "failed"); exit(EXIT_FAILURE); } snprintf(s, 128, "%s (%d pairs)", s, pair_num); snprintf(s, 128, "%s fake %s", s, fmac); log_str(NOTICE, s, ""); if (verbose) pair_dump(); } void packet_destroy (void) { if (!libnet_destroy_packet(&packet)) { log_str(ERROR, "libnet_destroy_packet():", "failed"); exit(EXIT_FAILURE); } if (!libnet_close_link_interface(lif)) { log_str(ERROR, "libnet_close_link_interface():", "failed"); exit(EXIT_FAILURE); } pcap_close(pcap); } void packet_sendfake(void) { register int n; memcpy(&te_addr, &se_addr, sizeof(struct ether_addr)); memcpy(&se_addr, &fake_addr, sizeof(struct ether_addr)); memcpy(&th_addr, &fake_addr, sizeof(struct ether_addr)); if (libnet_build_ethernet((u_char *) &te_addr, (u_char *) &se_addr, ETHERTYPE_ARP, NULL, 0, packet) == -1) { log_str(ERROR, "libnet_build_ethernet():", "failed"); exit_ipguard(EXIT_FAILURE); } if (libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, ARPOP_REPLY, (u_char *) &th_addr, (u_char *) &tp_addr, (u_char *) &sh_addr, (u_char *) &sp_addr, NULL, 0, packet + LIBNET_ETH_H) == -1) { log_str(ERROR, "libnet_build_arp():", "failed"); exit_ipguard(EXIT_FAILURE); } if (dont_fork && verbose) libnet_hex_dump(packet, LIBNET_ETH_H + LIBNET_ARP_H, 1, stdout); if (libnet_write_link_layer(lif, (u_char *) ifname, (u_char *) packet, LIBNET_ETH_H + LIBNET_ARP_H) == -1) { log_str(ERROR, "libnet_write_link_layer():", "failed"); exit_ipguard(EXIT_FAILURE); } for (n = 1; n < fake_num; n++) { usleep(FAKE_INTERVAL); if (libnet_write_link_layer(lif, (u_char *) ifname, (u_char *) packet, LIBNET_ETH_H + LIBNET_ARP_H) == -1) { log_str(ERROR, "libnet_write_link_layer():", "failed"); exit_ipguard(EXIT_FAILURE); } } } void packet_check(void) { struct pair_t *p = NULL; int warn = 0, recp = 0; register int n; if (memcmp(&zh_addr, &th_addr, sizeof(struct ether_addr))) { if (verbose) { snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(WARNING, "!0 e:", s); } nze++; } if (memcmp(&se_addr, &sh_addr, sizeof(struct ether_addr))) { if (verbose) { snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(WARNING, "e!=h:", s); } mis++; } if (!memcmp(&se_addr, &pairs->mac, sizeof(struct ether_addr))) { mymac++; return; } if (!memcmp(&se_addr, &fake_addr, sizeof(struct ether_addr))) { fake++; return; } for (n = 0, p = pairs; n < pair_num; n++, p++) { if (!memcmp(&tp_addr, &p->ip, sizeof(struct in_addr))) recp++; } if (duplex && !recp) { if (verbose) { snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(NOTICE, "bdip:", s); } if (!read_only) packet_sendfake(); bdip++; return; } for (n = 0, p = pairs; n < pair_num; n++, p++) { if (!memcmp(&zp_addr, &p->ip, sizeof(struct in_addr))) { if (!memcmp(&sh_addr, &p->mac, sizeof(struct ether_addr))) { zip++; if (!addr_nosubst) return; } } if (!memcmp(&zh_addr, &p->mac, sizeof(struct ether_addr))) { if (!memcmp(&sp_addr, &p->ip, sizeof(struct in_addr))) { zmac++; if (!addr_nosubst) return; } } if (!memcmp(&sh_addr, &p->mac, sizeof(struct ether_addr))) { if (!memcmp(&sp_addr, &p->ip, sizeof(struct in_addr))) { good++; return; } if (verbose) { snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(NOTICE, "bip :", s); } bip++; warn++; } else if (!memcmp(&sp_addr, &p->ip, sizeof(struct in_addr))) { if (verbose) { snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(NOTICE, "bmac:", s); } bmac++; warn++; } } if (!warn) { if (verbose) { snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(NOTICE, "new :", s); } bnew++; } snprintf(s, 128, "%s %-15s", ether_ntoa(&sh_addr), inet_ntoa(sp_addr)); snprintf(s, 128, "%s %-15s", s, inet_ntoa(tp_addr)); log_str(NOTICE, "BAD!:", s); bad++; if (buffer_num) buffer_add(); if (!memcmp(&sp_addr, &tp_addr, sizeof(struct in_addr))) { bent++; if (hidden) return; } if (!read_only) packet_sendfake(); } void packet_handle(char *blah, struct pcap_pkthdr *blah2, u_char *recv) { struct ether_header *eth_h = NULL; struct ether_arp *arp_h = NULL; 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, "ethertype:", s); return; } memcpy(&se_addr, ð_h->ether_shost, sizeof(struct ether_addr)); memcpy(&te_addr, ð_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, "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, "proto address:", s); return; } if (htons(arp_h->ea_hdr.ar_op) == ARPOP_REPLY) return; if (htons(arp_h->ea_hdr.ar_op) != ARPOP_REQUEST) { snprintf(s, 8, "0x%x", arp_h->ea_hdr.ar_op); log_str(NOTICE, "arp op:", s); return; } memcpy(&sh_addr, &arp_h->arp_sha, sizeof(struct ether_addr)); memcpy(&th_addr, &arp_h->arp_tha, sizeof(struct ether_addr)); memcpy(&sp_addr, &arp_h->arp_spa, sizeof(struct in_addr)); memcpy(&tp_addr, &arp_h->arp_tpa, sizeof(struct in_addr)); all++; packet_check(); if (ethers_update) ethers_stat(); sig_catch(); /* if (dont_fork) fprintf(stderr, "g %-5d zm %-3d zi %-3d b %-5d bm %-3d bi %-3d bn %-4d m %-2d my %-3d f %-3d a %-6d\r", good, zmac, zip, bad, bmac, bip, bnew, mis, mymac, fake, all); */ } 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(NOTICE, "dump:", s); snprintf(s, 64, "Total ARP packets %d", all); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Good MAC/IP pairs %d", good); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Zero MACs/IPs %d/%d", zmac, zip); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Bad MAC/IP pairs %d", bad); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Bad MACs/IPs %d/%d", bmac, bip); log_str(NOTICE, "dump:", s); if (duplex) { snprintf(s, 64, "Bad dest IPs %d", bdip); log_str(NOTICE, "dump:", s); } snprintf(s, 64, "New MAC/IP pairs %d", bnew); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Bad MAC/IP enters %d", bent); log_str(NOTICE, "dump:", s); snprintf(s, 64, "My MACs/fake MACs %d/%d", mymac, fake); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Non-zero target MAC %d", nze); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Mismatch Ether/ARP MAC %d", mis); log_str(NOTICE, "dump:", s); if (pcap_stats(pcap, &ps) == -1) { log_str(WARNING, "pcap_stat()", "failed"); return; } snprintf(s, 64, "PCAP statistics:"); log_str(NOTICE, "dump:", s); snprintf(s, 64, "Received/Dropped packets %d/%d", ps.ps_recv, ps.ps_drop); log_str(NOTICE, "dump:", s); }