/* An example packet sniffer for Linux 2.4 Version 4.0 By j00n1x (c) 2003 This packet sniffer allows a bind to a specific network device before sniffing. Also sets promiscuous mode to sniff all traffic reaching that network device. Can now read and decode: * Ethernet Frames (link layer headers -> /usr/include/linux/if_ether.h) * IP headers -> /usr/include/netinet/ip.h * TCP headers -> /usr/include/netinet/tcp.h * ICMP headers -> /usr/include/netinet/ip_icmp.h This program requires superuser (root). Will drop priviledges to nobody (userid 99) and may also chdir/chroot to /tmp (but that is not enabled by default). Note: It uses pf_packet/sock_raw. */ // maximum number of bytes to dump... set to 65536 if you don't care. #define MAX_DUMP 100 // standard C include files #include #include #include // unix header files #include // for write(), close() // BSD socket header files #include #include // resolver header files #include #include // hostname to ip address conversion functions #include // raw packet files - IP #include #include #include // pf_packet files #include #include #include #include // netdevice(7) #include #include const char *DEVICE="lo"; unsigned long source_ip_table[10]; unsigned long source_ip_total; void dump_packet(char *buf, int len) { int i; for (i = 0; i < len; i++) { printf("%02X ", (int) (buf[i] & 0xff)); if ((i+1) % 20 == 0) { printf("\n"); } else if ((i+1) % 4 == 0) { printf(" "); } } printf("\n\n"); } void dump_eth_header(struct ethhdr *ethh) { int i; for (i = 0; i < ETH_ALEN; i++) { printf("%02X:", (int) (ethh->h_source[i] & 0xff)); } printf(" -> "); for (i = 0; i < ETH_ALEN; i++) { printf("%02X:", (int) (ethh->h_dest[i] & 0xff)); } printf("\n"); } void dump_tcp_header(struct tcphdr *tcph) { printf("TCP: Source Port: %d, Destination Port: %d, Window: %d\n", ntohs(tcph->source), ntohs(tcph->dest), ntohs(tcph->window)); printf("TCP Flags: %s%s%s%s%s%s\n", tcph->fin ? "FIN " : "", tcph->syn ? "SYN " : "", tcph->rst ? "RST " : "", tcph->psh ? "PSH " : "", tcph->ack ? "ACK " : "", tcph->urg ? "URG " : ""); } void dump_icmp_header(struct icmphdr *icmph) { int i; char buf[1024]; typedef struct map_s { int code; char *string; } Map; Map icmp_types[] = { { 0, "Echo Reply" }, { 3, "Destination Unreachable" }, { 4, "Source Quench" }, { 5, "Redirect" }, { 8, "Echo" }, { 11, "Time Exceeded" }, { 12, "Parameter Problem" }, { 13, "Timestamp Request" }, { 14, "Timestamp Reply" }, { 15, "Information Request" }, { 16, "Information Reply" }, { 17, "Address Mask Request" }, { 18, "Address Mask Reply" }, { -1, NULL } }; Map icmp_unreach_codes[] = { { 0, "Network Unreachable" }, { 1, "Host Unreachable" }, { 2, "Protocol Unreachable" }, { 3, "Port Unreachable" }, { 4, "Fragmentation Needed/DF set" }, { 5, "Source Route failed" }, { 6, "Network Unknown" }, { 7, "Host Unknown" }, { 8, "Host Isolated" }, { 9, "Network Ano(nymous)" }, { 10, "Host Ano(nymous)" }, { 11, "Network Unreachable for TOS" }, { 12, "Host Unreachable for TOS" }, { 13, "Packet Filtered" }, { 14, "Precedence Violation" }, { 15, "Precedence Cut Off" }, { -1, NULL} }; Map icmp_redirect_codes[] = { { 0, "Redirect Network" }, { 1, "Redirect Host" }, { 2, "Redirect Network for TOS" }, { 3, "Redirect Host for TOS" }, { -1, NULL } }; Map icmp_time_codes[] = { { 0, "TTL count exceeded" }, { 1, "Fragment Reassembly Time Exceeded" }, { -1, NULL } }; printf("ICMP: type: "); strcpy(buf, ""); for (i = 0; icmp_types[i].string != NULL; i++) { if (icmp_types[i].code == icmph->type) { strcpy(buf, icmp_types[i].string); } } printf("\"%s\" ", buf); strcpy(buf, ""); if (icmph->type == 3) { for (i = 0; icmp_unreach_codes[i].string != NULL; i++) { if (icmp_unreach_codes[i].code == icmph->code) { strcpy(buf, icmp_unreach_codes[i].string); } } } else if (icmph->type == 5) { for (i = 0; icmp_redirect_codes[i].string != NULL; i++) { if (icmp_redirect_codes[i].code == icmph->code) { strcpy(buf, icmp_redirect_codes[i].string); } } } else if (icmph->type == 11) { for (i = 0; icmp_time_codes[i].string != NULL; i++) { if (icmp_time_codes[i].code == icmph->code) { strcpy(buf, icmp_time_codes[i].string); } } } if (strcmp(buf, "") != 0) { printf("code: \"%s\"", buf); } printf("\n"); } void dump_ip_header(struct iphdr *iph) { struct sockaddr_in to; char source[1024], dest[1024]; // source/dest ip address struct protoent *pent; char protocol[1024]; char *p; int i; struct icmphdr *icmph; struct tcphdr *tcph; // convert our data into strings, so we can // print them out 'nicely' to.sin_addr.s_addr = iph->saddr; p = inet_ntoa(to.sin_addr); strcpy(source, p); to.sin_addr.s_addr = iph->daddr; p = inet_ntoa(to.sin_addr); strcpy(dest, p); pent = getprotobynumber(iph->protocol); if (!pent) { perror("getprotobynumber"); strcpy(protocol, ""); } else { strcpy(protocol, pent->p_name); } // print out details if (iph->daddr == inet_addr("255.255.255.255")) strcat(dest, " (broadcast)"); else { for (i = 0; i < source_ip_total; i++) { if (iph->daddr == source_ip_table[i]) { strcat(dest, " (receiving)"); break; } } for (i = 0; i < source_ip_total; i++) { if (iph->saddr == source_ip_table[i]) { strcat(dest, " (sending)"); break; } } } printf("%s -> %s\n", source, dest); printf("version: %d\t", iph->version); printf("ihl: %d\t", iph->ihl); printf("tos: %d\t", iph->tos); printf("tot_len: %d\t", ntohs(iph->tot_len)); printf("id: %d\n", ntohs(iph->id)); printf("frag_off: %d\t", ntohs(iph->frag_off)); printf("ttl: %d\t", iph->ttl); if (pent) printf("protocol: %d (%s)\t", iph->protocol, protocol); else printf("protocol: %d\t", iph->protocol); printf("check: %d\n", ntohs(iph->check)); if (iph->protocol == 1) { icmph = (void *) iph + (iph->ihl * 4); dump_icmp_header(icmph); } if (iph->protocol == 6) { tcph = (void *) iph + (iph->ihl * 4); dump_tcp_header(tcph); } printf("\n"); } int main(int argc, char **argv) { int fd; char buf[1024*64]; struct ifreq ifr; struct sockaddr_ll eth; struct packet_mreq pmreq; struct ifconf ifc; struct ifreq ifr_table[10]; struct iphdr *iph; struct ethhdr *ethh; int i, len, total; if (argc < 2) { printf("Syntax: \n"); exit(1); } DEVICE=argv[1]; fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (fd < 0) { perror("socket"); exit(1); } // find DEVICE's ifindex -- man 7 netdevice strcpy(ifr.ifr_name, DEVICE); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl"); exit(1); } printf("ifindex: %d\n", ifr.ifr_ifindex); // bind to DEVICE, using bind() - man 7 packet memset(ð, 0, sizeof(eth)); eth.sll_family = AF_PACKET; eth.sll_protocol = htons(ETH_P_ALL); eth.sll_ifindex = ifr.ifr_ifindex; eth.sll_hatype = ARPHRD_VOID; eth.sll_pkttype = PACKET_HOST; // eth.sll_pkttype = PACKET_OUTGOING; // doesn't seem to work -- // -- bug in kernel? eth.sll_halen = 0; if (bind(fd, (struct sockaddr *) ð, sizeof(eth)) < 0) { perror("bind"); exit(1); } // set promiscuous mode - man 7 packet pmreq.mr_ifindex = ifr.ifr_ifindex; pmreq.mr_type = PACKET_MR_PROMISC; pmreq.mr_alen = 0; if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &pmreq, sizeof(pmreq)) < 0) { perror("setsockopt"); exit(1); } // grab the IP address of the network device. // first grab a list of all running L3 network devices and then // only choose the one which matches DEVICE ifc.ifc_len = sizeof(struct ifreq) * 10; ifc.ifc_req = ifr_table; if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { perror("ioctl"); exit(1); } total = ifc.ifc_len / sizeof(struct ifreq); printf("Number of entries: %d\n", total); for (i = 0, source_ip_total = 0; i < total; i++) { struct sockaddr_in *moo; moo = (struct sockaddr_in *) &ifr_table[i].ifr_addr; printf("Source: %s (device: %s) ", inet_ntoa(moo->sin_addr), ifr_table[i].ifr_name); // only add those that match our DEVICE if (strcasecmp(ifr_table[i].ifr_name, DEVICE) == 0) { source_ip_table[source_ip_total++] = moo->sin_addr.s_addr; printf("[added]\n"); } else printf("\n"); } // drop priviledges // the following will prevent getprotobynumber() from working // chdir("/tmp"); // chroot("/tmp"); setregid(99, 99); setreuid(99, 99); // main loop while (1) { memset(buf, 0, sizeof(buf)); if (recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL) < 0) { perror("recvfrom"); } // setup pointers to point inside buffer ethh = (void *) buf; iph = (void *) buf + sizeof(struct ethhdr); // check to see if the link layer is passing an IP packet. // we only care about IP packets at this stage. if (ethh->h_proto == htons(ETH_P_IP)) { dump_eth_header(ethh); dump_ip_header(iph); len = ntohs(iph->tot_len) + sizeof(struct ethhdr); if (len > MAX_DUMP) len = MAX_DUMP; dump_packet(buf, len); } else { // we silently drop the packet.. or we could yell.. // but is most likely an ARP packet // printf("ERROR: Unknown protocol\n"); } } close(fd); return 0; }