/* schniff.c Marcus Fritzsch http://fritschy.de 2005-11-29 * a simple educational purpose packet sniffer for linux (2.6?) * * This code is licensed under the GNU GPL * * gcc -Os -s schniff.c -o schniff -Wall -pedantic -ansi * * History: * 2006-04-05 - select(2) * 2006-12-20 - implemented M_STRING and M_REGEX * - style fixes * - reorganization, main_loop () and parse_opts () * */ #define __USE_BSD #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct dns_hdr { unsigned short id, flags; unsigned short nq, narr; unsigned short nauthrr, naddrr; unsigned char * data; }; /* matches can be combined and are ANDed together */ enum match_type { M_NONE = 0, /* every frame is matched */ M_HOST_IP = 1<<0, M_HOST_ETHER = 1<<1, M_PORT = 1<<2, M_EPROTO = 1<<3, /* match ether proto (ARP/IP/PPPOE) */ M_IPROTO = 1<<4, /* match ipproto (ICMP/TCP/UDP) */ M_STRING = 1<<5, /* literally match string */ M_REGEX = 1<<6 /* match extended case insens. regex */ }; /* matches have to be in the following order (see frame_matches ()): * 1. string/regex * 2. port * 3. host-ip * 4. ip proto * 5. host-ether * 6. ether proto */ struct settings { char * ifname; /* ifname to sniff on, can be 'all' */ int ifindex; /* if index, when 0, sniff all ifaces */ unsigned snaplen; /* snap len in bytes */ int hexdump; /* do a hexdump of each frame */ int sock; /* socket i am listening on */ /* matching options from cli */ int match_what; unsigned int match_ip; struct ether_addr match_ether; unsigned short match_port; unsigned short match_eproto; unsigned short match_iproto; char * match_string; unsigned int match_stringlen; regex_t match_regex; int end; /* set by signal handler to exit */ }; char * P = "schniff"; struct settings settings; #define warn(a) fprintf(stderr,"%s: %s,%d: %s\n",P,__FILE__,__LINE__,a); char * pfx (int n); void show_regex_error (int err, char * cause); int dump_hex (FILE * str, unsigned char * buf, unsigned int len); void my_ether_ntoa (char * s, struct ether_addr a); void decode_ether (FILE * str, struct ether_header * eth, unsigned len); void decode_dns (FILE * str,unsigned char * buf, unsigned len,int depth); void decode_tcp (FILE * str,unsigned char * buf, unsigned len,int depth); void decode_icmp (FILE * str,unsigned char * buf,unsigned len,int depth); void decode_udp (FILE * str, unsigned char * buf,unsigned len,int depth); void decode_ip (FILE * str, unsigned char * buf, unsigned len,int depth); void decode_arp (FILE * str, unsigned char * buf, unsigned len); void decode_pppoe (FILE * str, unsigned char * buf, unsigned len); int get_ifindex (int sock, char * ifname); void set_promiscuous (int sock, int ifindex, int val); void help (void); void sighandle (int s); int frame_matches (unsigned char * buf, unsigned len); void parse_opts (int * argcp, char ** argv); void main_loop (void); char * pfx (int n) { static char * prefixes [] = { "", "*", "**", "***", "****" }; assert (n < 5); return prefixes [n]; } void show_regex_error (int err, char * cause) { char errbuf [256]; size_t sz; fprintf (stderr, "regex %s failed: ", cause); sz = regerror (err, &settings.match_regex, errbuf, sizeof (errbuf)); fprintf (stderr, "%s\n", errbuf); } int dump_hex (FILE * str, unsigned char * buf, unsigned int len) { unsigned int ofs, i, rem=0; unsigned char * ptr = buf; char lbuf [0x11]; for (ofs = 0; ofs < len; ofs++, ptr++) { rem = 1; if (ofs > 0 && ! (ofs & 0xf)) fprintf (str, "\n"); if (! (ofs & 0x0f)) fprintf (str, "\t0x%04x: ", ofs); fprintf (str, (ofs & 1) ? "%02x " : "%02x", *ptr); lbuf [ofs&0xf] = isprint (*ptr) ? *ptr : '.'; lbuf [(ofs&0xf)+1] = 0; if ((ofs & 0xf) == 0xf) fprintf (str, " %s", lbuf), rem=0; } if (rem) { for (i = (ofs&0xf); i < 0x10; i++) fprintf (str, (i&1) ? " " : " "); fprintf (str, " %s", lbuf); } fprintf (str, "\n"); return ofs - 1; } void my_ether_ntoa (char * s, struct ether_addr a) { int i; for (i = 0; i < ETH_ALEN; i++) snprintf (&s[i*3], i+1==ETH_ALEN?3:4, i+1==ETH_ALEN?"%02x":"%02x:", a.ether_addr_octet [i]); } void decode_ether (FILE * str, struct ether_header * eth, unsigned len) { struct timeval tv; time_t t; struct tm tm [1]; unsigned usec; char dst [18], src [18], ts [17]; char * type = ""; if (len < 14) return; /* set known types... */ switch (ntohs (eth->ether_type)) { case ETH_P_IP: type = " (IP)"; break; case ETH_P_ARP: type = " (ARP)"; break; case ETH_P_PPP_DISC: type = " (PPPoE Discovery)"; break; case ETH_P_PPP_SES: type = " (PPPoE Session)"; break; } my_ether_ntoa (dst, * (struct ether_addr *) ð->ether_dhost); my_ether_ntoa (src, * (struct ether_addr *) ð->ether_shost); t = time (NULL); gettimeofday (&tv, NULL); usec = tv.tv_usec; localtime_r (&t, &tm[0]); strftime (&ts[0], 16, "%H:%M:%S", &tm[0]); fprintf (str, "%s.%06u\n* ETH %s -> %s type:0x%04x%s\n", ts, usec, src, dst, ntohs (eth->ether_type), type); } void decode_dns (FILE * str,unsigned char * buf, unsigned len, int depth) { char b [256]; unsigned char * cur; struct dns_hdr * dns = (struct dns_hdr *) buf; unsigned short id, flags, nq, narr, nauthrr, naddrr; /*int datalen = len - 12;*/ if (len < sizeof (struct dns_hdr)) return; id = ntohs (dns->id); flags = ntohs (dns->flags); nq = ntohs (dns->nq); narr = ntohs (dns->narr); nauthrr = ntohs (dns->nauthrr); naddrr = ntohs (dns->naddrr); fprintf (str, "%s DNS %s id:0x%04x f:0x%04x %d/%d/%d/%d ", pfx (depth), flags & 0x8000 ? "reply" : "query", id, flags, nq, narr, nauthrr, naddrr); cur = buf + 12; { int bi = 0; int i = 0; while (cur [i]) { int ci = 0; while (ci < cur [i]) b [bi++] = cur [i + 1 + ci++]; b [bi++] = '.'; i += ci + 1; } b [bi-1] = 0; } fprintf (str, "%s\n", b); /** XXX more to come **/ } void decode_tcp (FILE * str,unsigned char * buf, unsigned len,int depth) { struct tcphdr * th = (struct tcphdr *) buf; char flags [7] = ""; int flc = 0; if (len < 20) return; if (th->fin) flags [flc++] = 'F'; if (th->syn) flags [flc++] = 'S'; if (th->rst) flags [flc++] = 'R'; if (th->psh) flags [flc++] = 'P'; if (th->ack) flags [flc++] = 'A'; if (th->urg) flags [flc++] = 'U'; flags [flc] = 0; fprintf (str, "%s TCP %d -> %d seq:%u ack:%u off:%d " "flags:%s win:%d csum:0x%04x\n", pfx (depth), ntohs (th->source), ntohs (th->dest), ntohl (th->seq), ntohl (th->ack_seq), th->doff, flags, ntohs (th->window), ntohs (th->check) /* FIXME: not handling URGP */); } void decode_icmp (FILE * str,unsigned char * buf,unsigned len,int depth) { struct icmphdr * ih = (struct icmphdr *) buf; static char * type [] = { "echo reply", "", "", "dest unreachable", "source quench", "redirect", /* 5 */ "", "", "echo request", "", "", /* 10 */ "time exceeded", "parameter problem", "timestamp request", "timestamp reply", "info request", /* 15 */ "info reply", "addr mask request", "addr mask reply", /* 18 */ "(TYPE UNKNOWN)" }; static char * dest_unreach [] = { "net-unreachable", "host-unreachable", "proto-unreachable", "port-unreachable", "need to fragment, DF set", "source route failed", /* 5 */ "network-unknown", "host-unknown", "host-isolated", "", /*ICMP_NET_ANO*/ "", /*ICMP_HOST_ANO*/ /* 10 */ "", /*ICMP_NET_UNR_TOS*/ "", /*ICMP_HOST_UNR_TOS*/ "packet-filtered", "precedence-violation", "precedence-cutoff", /* 15 */ "(CODE UNKNOWN)" }; static char * time_exceed [] = { "ttl exceeded in transit", "time exceeded on frag reass.", "(UNKNOWN CODE)" }; unsigned char * nexthdr = 0; unsigned int nextlen = 0; if (len < 8) return; if (len >= 36 /* 8+20+8 */) { nexthdr = buf + 8; nextlen = len - 8; } fprintf (str, "%s ICMP %d,%d: %s ", pfx (depth), ih->type, ih->code, type [ih->type < 19 ? ih->type : 19]); switch (ih->type) { case ICMP_ECHOREPLY: fprintf (str, "csum:0x%04x id:%d seq:%d\n", ntohs (ih->checksum), ntohs (ih->un.echo.id), ntohs (ih->un.echo.sequence)); break; case ICMP_DEST_UNREACH: fprintf (str, " %s csum:0x%04x\n", dest_unreach [ih->code < 6 ? ih->code : 6], ntohs (ih->checksum)); decode_ip (str, nexthdr, nextlen, depth+1); break; case ICMP_SOURCE_QUENCH: break; case ICMP_REDIRECT: break; case ICMP_ECHO: fprintf (str, "csum:0x%04x\n", ntohs (ih->checksum)); break; case ICMP_TIME_EXCEEDED: fprintf (str, "%s csum:0x%04x\n", time_exceed [ih->code < 2 ? ih->code : 2], ntohs (ih->checksum)); decode_ip (str, nexthdr, nextlen, depth+1); break; case ICMP_PARAMETERPROB: break; case ICMP_TIMESTAMP: break; case ICMP_TIMESTAMPREPLY: break; case ICMP_INFO_REQUEST: break; case ICMP_INFO_REPLY: break; case ICMP_ADDRESS: break; case ICMP_ADDRESSREPLY: break; } } void decode_udp (FILE * str, unsigned char * buf,unsigned len,int depth) { unsigned short shp, dhp, hl; struct udphdr * uh = (struct udphdr *) buf; if (len < 8) { warn ("UDP packet too short!"); return; } shp = ntohs (uh->source); dhp = ntohs (uh->dest); hl = ntohs (uh->len); fprintf (str, "%s UDP %d -> %d len:%d csum:0x%04x\n", pfx (depth), shp, dhp, hl, ntohs (uh->check)); if ((shp == 53 || dhp == 53) && hl > 8+12) decode_dns (str, buf+8, hl-8, depth+1); } void decode_ip (FILE * str, unsigned char * buf, unsigned len,int depth) { struct iphdr * ip = (struct iphdr *) buf; unsigned char * nexthdr; unsigned nextlen; if (len < 20) { warn ("IP packet too short!"); return; } if (ip->version == 4) { char src [16], dst [16]; char frags [9] = ""; /* DF|MF [xxxxx] */ unsigned short frag; unsigned char flags; if (ip->ihl < 5) return; nexthdr = &buf[ip->ihl << 2]; nextlen = len - (ip->ihl << 2); /* determine fragment info */ frag = ntohs (ip->frag_off); flags = frag & 0x7; frag &= 0xfff8; frag *= 8; switch (flags) { case 0: /* DF */ strcpy (frags, "DF"); break; case 1: /* MF */ strcpy (frags, "MF"); break; default: break; } if (frags [0] && frag) snprintf (&frags[2], 6, " %d", frag); /* get addresses */ strcpy (src, inet_ntoa (* (struct in_addr *) &ip->saddr)); strcpy (dst, inet_ntoa (* (struct in_addr *) &ip->daddr)); fprintf (str, "%s IPv4 %s -> %s ihl:%d tos:0x%02x " "len:%d id:%d frag:%s ttl:%d csum:0x%04x\n", pfx (depth), src, dst, ip->ihl, ip->tos, ntohs (ip->tot_len), ntohs (ip->id), frags, ip->ttl, ntohs (ip->check)); switch (ip->protocol) { case IPPROTO_TCP: decode_tcp (str, nexthdr, nextlen, depth+1); break; case IPPROTO_ICMP: decode_icmp (str, nexthdr, nextlen, depth+1); break; case IPPROTO_UDP: decode_udp (str, nexthdr, nextlen, depth+1); break; default: break; } } else if (ip->version == 6) { warn ("ipv6 not yet implemented"); } else { warn ("ip version unknown!"); } } void decode_arp (FILE * str, unsigned char * buf, unsigned len) { /*char hw_addr_type [10]; char hw_proto_type [10];*/ char tell_ip [INET_ADDRSTRLEN], tell_hw [18], who_has_ip [INET_ADDRSTRLEN], who_has_hw [18]; struct ether_arp * arp = (struct ether_arp *) buf; /* FIXME FIXME FIXME */ /* switch (arp->ea_hdr.ar_hrd) { case ARPHRD_ETHER: strncpy (hw_addr_type, "ETHERNET", 9); break; default: strncpy (hw_addr_type, "SOMETHING", 10); break; } */ if (len < sizeof (struct ether_arp)) return; my_ether_ntoa (tell_hw, * (struct ether_addr *) &arp->arp_sha); my_ether_ntoa (who_has_hw, * (struct ether_addr *) &arp->arp_tha); strcpy (tell_ip, inet_ntoa (* (struct in_addr *) &arp->arp_spa)); strcpy (who_has_ip, inet_ntoa (* (struct in_addr *) &arp->arp_tpa)); switch (ntohs (arp->ea_hdr.ar_op)) { /* arp */ case ARPOP_REQUEST: fprintf (str, "** ARP who-has %s tell %s (%s)\n", who_has_ip, tell_ip, tell_hw); break; case ARPOP_REPLY: fprintf (str, "** ARP reply %s is-at %s\n", tell_ip, tell_hw); break; /* reverse arp */ case ARPOP_RREQUEST: break; case ARPOP_RREPLY: break; /* ??? */ case ARPOP_InREQUEST: break; case ARPOP_InREPLY: break; } /* FIXME: don't know anything of this! switch (arp->ar_pro) { } */ } void decode_pppoe (FILE * str, unsigned char * buf, unsigned len) { struct pppoe_hdr * ph = (struct pppoe_hdr *) buf; static char * padx_codes [] = { "PADO (Active Discovery Offer", /* 0x07 */ "PADI (Active Discovery Initiation", /* 0x09 */ "PADR (Active Discovery Request", /* 0x19 */ "PADS (Active Discovery Session-Confirmation", /* 0x65 */ "PADT (Active Discovery Terminate" /* 0xa7 */ }; char * padx = ""; if (len < 6) return; switch (ph->code) { case PADO_CODE: padx = padx_codes [0]; break; case PADI_CODE: padx = padx_codes [1]; break; case PADR_CODE: padx = padx_codes [2]; break; case PADS_CODE: padx = padx_codes [3]; break; case PADT_CODE: padx = padx_codes [4]; break; } /* FIXME: something is wrong with the length here!!! */ if (ntohs (((struct ethhdr *)(buf-ETH_HLEN))->h_proto) == ETH_P_PPP_DISC) fprintf (str, "** PPPoE %s session:0x%04x len:%d\n", padx, ntohs (ph->sid), ntohs (ph->length)); else fprintf (str, "** PPPoE session:0x%04x len:%d\n", ntohs (ph->sid), ntohs (ph->length)); } /* determine interface index of iface with geiven name */ int get_ifindex (int sock, char * ifname) { struct ifreq ifreq; if (geteuid () != 0) fprintf (stderr, "%s: need to be root!\n", P), exit (1); bzero (&ifreq, sizeof (ifreq)); assert (strlen (ifname) < IFNAMSIZ); strcpy (ifreq.ifr_name, ifname); /* see netdevice(7) for more info */ if (ioctl (sock, SIOCGIFINDEX, &ifreq) == -1) perror ("ioctl ()"), exit (1); return ifreq.ifr_ifindex; } void set_promiscuous (int sock, int ifindex, int val) { struct packet_mreq mreq; bzero (&mreq, sizeof (mreq)); mreq.mr_ifindex = ifindex; mreq.mr_type = PACKET_MR_PROMISC; if (setsockopt (sock, SOL_PACKET, val ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) perror ("setsockopt ()"), exit (1); } void help (void) { fprintf (stderr, "usage: %s [options]\n\n", P); fprintf (stderr, "\t-p match for port\n" "\t-P match for protocol (arp,ip,tcp,udp,pppoe)\n" "\t-h this message\n" "\t-H match for host (ipv4 or hw ether)\n" "\t-x hexdump captured frames\n" "\t-i specify interface (enable promiscuous mode)\n" "\t-s specify snaplen in bytes (min: 96)\n" "\t-S match for string\n" "\t-R match by case insensitive extended regex\n"); fprintf (stderr, "\n Note:\n" " - All filters will be ANDed together\n" " - By default listens on all interfaces w/o" " promiscuous mode!\n"); fprintf (stderr, "\n author: Marcus Fritzsch (http://fritschy.de)," " 2005-12-01\n" " license: This program is licensed under the GPL\n" " http://www.gnu.org/copyleft/gpl.html\n\n"); exit (1); } void sighandle (int s) { (void) s; settings.end = 1; } int frame_matches (unsigned char * buf, unsigned len) { struct ether_header * eth = (struct ether_header *) buf; struct iphdr * iph = 0; struct udphdr * uh = 0; /* no match is always true */ if (settings.match_what == M_NONE) return 1; /* match string/regex */ if ((settings.match_what & M_STRING) && NULL == memmem (buf, len, settings.match_string, settings.match_stringlen)) return 0; if (settings.match_what & M_REGEX) { unsigned int i = 0; int matched = 0; while (i < len) { static char regmatch [0xffff]; unsigned int bi; int r; for (bi = 0; bi < 0xffff && i+bi < len && isprint (buf [i+bi]); ++bi) regmatch [bi] = buf [i+bi]; if (bi > 0) { i += bi; regmatch [bi] = 0; r = regexec (&settings.match_regex, regmatch, 0, NULL, 0); if (r == 0) { matched = 1; break; } if (r != REG_NOMATCH) { show_regex_error (r, "executing"); settings.end = 1; return 0; } } else ++i; } /* no match; bail out */ if (! matched) return 0; } /* port of tcp/udp */ if (settings.match_what & M_PORT) { iph = (struct iphdr *) &buf [ETH_HLEN]; if (ntohs (eth->ether_type) != ETH_P_IP) return 0; /* too short, ihl+2*sizeof(short) */ if (len < (0x7fffffff & (unsigned int) (ETH_HLEN + iph->ihl*4 + 4))) return 0; if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP) return 0; uh = (struct udphdr *) &iph [1]; if (! (uh->source == settings.match_port || uh->dest == settings.match_port)) return 0; } if (settings.match_what & M_HOST_IP) { iph = (struct iphdr *) &buf [ETH_HLEN]; if (ntohs (eth->ether_type) != ETH_P_IP) return 0; if (len < (0x7fffffff & (unsigned int) (ETH_HLEN + iph->ihl*4))) return 0; if (! (iph->saddr == settings.match_ip || iph->daddr == settings.match_ip)) return 0; } if (settings.match_what & M_IPROTO) { iph = (struct iphdr *) &buf [ETH_HLEN]; if (ntohs (eth->ether_type) != ETH_P_IP) return 0; if (len < (0x7fffffff & (unsigned int) (ETH_HLEN + iph->ihl*4))) return 0; if (iph->protocol != settings.match_iproto) return 0; } if (settings.match_what & M_HOST_ETHER) { if (! (memcmp (&settings.match_ether, ð->ether_dhost, sizeof (struct ether_addr)) == 0 || memcmp (&settings.match_ether, ð->ether_shost, sizeof (struct ether_addr)) == 0)) return 0; } if (settings.match_what & M_EPROTO) { if (settings.match_eproto == htons (ETH_P_PPP_SES)) if (eth->ether_type == settings.match_eproto || eth->ether_type == htons (ETH_P_PPP_SES)) return 1; if (eth->ether_type != settings.match_eproto) return 0; } return 1; } void parse_opts (int * argcp, char ** argv) { int opt; /* init settings */ bzero (&settings, sizeof (settings)); settings.ifname = "all"; settings.snaplen = 96; settings.match_what = M_NONE; while ((opt = getopt (*argcp, argv, "i:s:H:p:P:xhS:R:")) != -1) { int tmp; /* * i iface * s snap length * H match host * P protocol (ip/tcp/arp) * p service/port * x enable hexdump * h show help */ switch (opt) { case 'i': /* specify iface to schniff on */ settings.ifname = optarg; break; case 's': settings.snaplen = atoi (optarg); if (settings.snaplen < 42) { fprintf (stderr,"%s: need at least 42 bytes snaplen\n",P); fprintf (stderr, "use -h for more help\n"); exit (1); } break; case 'x': settings.hexdump = 1; break; case 'H': /* host match */ tmp = inet_aton (optarg, (struct in_addr *) &settings.match_ip); if (! tmp) { /* try setting the host as ether-addr */ struct ether_addr * ea; if (! (ea = ether_aton (optarg))) { fprintf (stderr, "%s: Host invalid!\n", P); exit (1); } memcpy (&settings.match_ether, ea, sizeof (struct ether_addr)); settings.match_what |= M_HOST_ETHER; break; } settings.match_what |= M_HOST_IP; break; case 'P': /* protocol match */ /* note, no getprotobyname here... */ if (! strcasecmp (optarg, "icmp")) settings.match_iproto = IPPROTO_ICMP, settings.match_what |= M_IPROTO; else if (! strcasecmp (optarg, "tcp")) settings.match_iproto = IPPROTO_TCP, settings.match_what |= M_IPROTO; else if (! strcasecmp (optarg, "udp")) settings.match_iproto = IPPROTO_UDP, settings.match_what |= M_IPROTO; else if (! strcasecmp (optarg, "ip")) settings.match_eproto = htons (ETH_P_IP), settings.match_what |= M_EPROTO; else if (! strcasecmp (optarg, "arp")) settings.match_eproto = htons (ETH_P_ARP), settings.match_what |=M_EPROTO; else if (! strcasecmp (optarg, "pppoe")) settings.match_eproto=htons(ETH_P_PPP_SES),settings.match_what |=M_EPROTO; else { fprintf (stderr, "%s: protocol unknown or not supported!\n", P); fprintf (stderr, "use -h for more help\n"); exit (1); } break; case 'p': /* port match */ tmp = atoi (optarg); if (tmp < 0 || tmp > 0xffff) { fprintf (stderr, "%s: port is invalid! (valid: 0..65535)\n", P); fprintf (stderr, "use -h for more help\n"); exit (1); } settings.match_port = htons ((unsigned short) tmp); settings.match_what |= M_PORT; break; case 'S': settings.match_what |= M_STRING; settings.match_string = optarg; settings.match_stringlen = strlen (optarg); break; case 'R': { int r; settings.match_what |= M_REGEX; if ((r = regcomp (&settings.match_regex, optarg, REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0) { show_regex_error (r, "compiling"); settings.end = 1; } break; } case 'h': default: help (); } } } void main_loop (void) { int len = 0; while (! settings.end) { unsigned char buffer [0xffff]; struct ether_header * eth = NULL; unsigned char * nexthdr = NULL; unsigned int nextlen = 0; /**** begin select ****/ { fd_set rfd; int sret; struct timeval timo = { 0, 50000 }; FD_ZERO (&rfd); FD_SET (settings.sock, &rfd); while ((sret = select (settings.sock + 1, &rfd, NULL, NULL, &timo)) && sret == -1 && errno == EINTR) ; if (! FD_ISSET (settings.sock, &rfd)) continue; } /**** end select ****/ len = recvfrom (settings.sock, buffer, settings.snaplen, 0, NULL, NULL); eth = (struct ether_header *) &buffer[0]; nexthdr = &buffer[ETH_HLEN]; nextlen = len - ETH_HLEN; if (len < 42) continue; if (! frame_matches (buffer, len)) continue; decode_ether (stdout, eth, ETH_HLEN); switch (ntohs (eth->ether_type)) { case ETH_P_IP: decode_ip (stdout, nexthdr, nextlen, 2); break; case ETH_P_ARP: decode_arp (stdout, nexthdr, nextlen); break; case ETH_P_PPP_DISC: case ETH_P_PPP_SES: decode_pppoe (stdout, nexthdr, nextlen); break; default: break; /* nothin' here... */ } if (settings.hexdump) dump_hex (stdout, buffer, len); fprintf (stdout, "\n"); } if (len == -1) perror ("recvfrom ()"); } int main (int argc, char ** argv) { struct sockaddr_ll sall; P = *argv; srand (0); signal (SIGINT, sighandle); signal (SIGTERM, sighandle); parse_opts (&argc, argv); if ((settings.sock = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) perror ("socket ()"), exit (1); if (strcmp (settings.ifname, "all")) settings.ifindex = get_ifindex (settings.sock, settings.ifname); if (settings.ifindex) { bzero (&sall, sizeof (sall)); /* bind port to iface, see packet(7) */ sall.sll_family = PF_PACKET; sall.sll_protocol = htons (ETH_P_ALL); sall.sll_ifindex = settings.ifindex; /* 0 maches ANY interface */ if (bind (settings.sock, (struct sockaddr *) &sall, sizeof (sall)) == -1) perror ("bind ()"), exit (1); } if (settings.ifindex) set_promiscuous (settings.sock, settings.ifindex, 1); else fprintf (stderr, "%s: not entering promiscuous mode!\n", P); /* returns when settings.end != 0 */ main_loop (); if (settings.ifindex) set_promiscuous (settings.sock, settings.ifindex, 0); close (settings.sock); return 0; }