- Version:4 bits。
- Header Length:4 bits。
- Type of Service:1 byte。
- Total Length:2 bytes。
- Identification:2 bytes。
- Flags:3 bits。
- Fragment Offset:13 bits。
- Time to Live:1 byte。
- Protocol:1 byte。
- Header Checksum:2 bytes。
- Source IP Address:4 bytes。
- Destination IP Address:4 bytes。
- IP Option + Padding:0 ~ 40 bytes。
- 長度:20 ~ 60 bytes。
先include ip的header檔。
#include <netinet/ip.h>
一樣打開預設的device,過濾器只抓ip,然後只抓一個封包。
char errbuf[PCAP_ERRBUF_SIZE]; char *device = NULL; //get default interface name device = pcap_lookupdev(errbuf); if(!device) { fprintf(stderr, "pcap_lookupdev(): %s\n", errbuf); exit(1); }//end if printf("Sniffing: %s\n", device); pcap_t *handle = pcap_open_live(device, 65535, 1, 1, errbuf); if(!handle) { fprintf(stderr, "pcap_open_live(): %s\n", errbuf); exit(1); }//end if //generate bpf filter bpf_u_int32 net, mask; struct bpf_program fcode; //get network and mask if(-1 == pcap_lookupnet(device, &net, &mask, errbuf)) { fprintf(stderr, "pcap_lookupnet(): %s\n", errbuf); mask = PCAP_NETMASK_UNKNOWN; }//end if //compile filter if(-1 == pcap_compile(handle, &fcode, "ip", 1, mask)) { fprintf(stderr, "pcap_compile(): %s\n", pcap_geterr(handle)); pcap_close(handle); exit(1); }//end if //set filter if(-1 == pcap_setfilter(handle, &fcode)) { fprintf(stderr, "pcap_pcap_setfilter(): %s\n", pcap_geterr(handle)); pcap_freecode(&fcode); pcap_close(handle); exit(1); }//end if //free bpf code pcap_freecode(&fcode); //start capture pcap_loop(handle, 1, pcap_callback, NULL);
解析ethernet時候,當下一層協定是0x0800(或marcos
ETHERTYPE_IP
)就呼叫函數dump_ip()
來解析IP的表頭。case ETHERTYPE_IP: printf("IP\n"); dump_ip(length, content); break;
struct ip *ip = (struct ip *)(content + ETHER_HDR_LEN);
ip表頭開始的位置就是第14個byte的位置。
結構
struct ip
宣告:/* * Structure of an internet header, naked of options. */ struct ip { #ifdef _IP_VHL u_char ip_vhl; /* version << 4 | header length >> 2 */ #else #if BYTE_ORDER == LITTLE_ENDIAN u_int ip_hl:4, /* header length */ ip_v:4; /* version */ #endif #if BYTE_ORDER == BIG_ENDIAN u_int ip_v:4, /* version */ ip_hl:4; /* header length */ #endif #endif /* not _IP_VHL */ u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ };
接下來就把每個欄位複製到變數內。
u_int version = ip->ip_v; u_int header_len = ip->ip_hl << 2; u_char tos = ip->ip_tos; u_int16_t total_len = ntohs(ip->ip_len); u_int16_t id = ntohs(ip->ip_id); u_int16_t offset = ntohs(ip->ip_off); u_char ttl = ip->ip_ttl; u_char protocol = ip->ip_p; u_int16_t checksum = ntohs(ip->ip_sum); char src_ip[INET_ADDRSTRLEN] = {0}; char dst_ip[INET_ADDRSTRLEN] = {0}; //copy ip address snprintf(src_ip, sizeof(src_ip), "%s", ip_ntoa(&ip->ip_src)); snprintf(dst_ip, sizeof(dst_ip), "%s", ip_ntoa(&ip->ip_dst));
ip表頭長度在ipv4中,只有4個bit。
u_int header_len = ip->ip_hl << 2;
所以要得到正確的表頭長度,必須乘以4或是向左移動2個bit。
接著就把表頭給列印出來。
//print printf("Protocol: IP\n"); printf("+-----+------+------------+-------------------------+\n"); printf("| IV:%1u| HL:%2u| T: %8s| Total Length: %10u|\n", version, header_len, ip_ttoa(tos), total_len); printf("+-----+------+------------+-------+-----------------+\n"); printf("| Identifier: %5u| FF:%3s| FO: %5u|\n", id, ip_ftoa(offset), offset & IP_OFFMASK); printf("+------------+------------+-------+-----------------+\n"); printf("| TTL: %3u| Pro: %3u| Header Checksum: %#06x|\n", ttl, protocol, checksum); printf("+------------+------------+-------------------------+\n"); printf("| Source IP Address: %15s|\n", src_ip); printf("+---------------------------------------------------+\n"); printf("| Destination IP Address: %15s|\n", dst_ip); printf("+---------------------------------------------------+\n");
函數
ip_ttoa()
是用來將type of service轉成可識別字串。static const char *ip_ttoa(u_int8_t flag) { static int f[] = {'1', '1', '1', 'D', 'T', 'R', 'C', 'X'}; #define TOS_MAX (sizeof(f)/sizeof(f[0])) static char str[TOS_MAX + 1]; //return buffer u_int8_t mask = 1 << 7; //mask int i; for(i = 0 ; i < TOS_MAX ; i++) { if(mask & flag) str[i] = f[i]; else str[i] = '-'; mask >>= 1; }//end for str[i] = 0; return str; }//end ip_ttoa
這邊貼一下之前寫的。
而函數
ip_ftoa
也是一樣,是將flags轉成字串;唯一差別是flags在offset欄位中,只使用到3個bit,所以也只處理左邊三個bit。static const char *ip_ftoa(u_int16_t flag) { static int f[] = {'R', 'D', 'M'}; //flag #define IP_FLG_MAX (sizeof(f)/sizeof(f[0])) static char str[IP_FLG_MAX + 1]; //return buffer u_int16_t mask = 1 << 15; //mask int i; for(i = 0 ; i < IP_FLG_MAX ; i++) { if(mask & flag) str[i] = f[i]; else str[i] = '-'; mask >>= 1; }//end for str[i] = 0; return str; }//end ip_ftoa
如果要取得flags以外剩下的13個bit(Fragment offset),只要拿那16個bit變數跟0x1fff(或marcos
IP_OFFMASK
)做&
運算,就能取得剩下的13個bit。printf("| Identifier: %5u| FF:%3s| FO: %5u|\n", id, ip_ftoa(offset), offset & IP_OFFMASK);
最後變數protocol是下一層協定,常見的TCP、UDP、ICMP或IGMP就是在這部分。
switch (protocol) { case IPPROTO_UDP: printf("Next is UDP\n"); break; case IPPROTO_TCP: printf("Next is TCP\n"); break; case IPPROTO_ICMP: printf("Next is ICMP\n"); break; default: printf("Next is %d\n", protocol); break; }//end switch
編譯:
libpcap % gcc -I/usr/local/opt/libpcap/include -Wall -std=gnu99 -L/usr/local/opt/libpcap/lib -lpcap dump-ip.c -o dump-ip
執行結果(Mac OS X):
libpcap % ./dump-ip Sniffing: en0 No. 1 Time: 21:54:52.689741 Length: 161 bytes Capture length: 161 bytes Ethernet Frame: +-------------------------+-------------------------+-------------------------+ | Destination MAC Address: f8:1a:67:53:f5:dc| +-------------------------+-------------------------+-------------------------+ | Source MAC Address: 6c:40:08:bc:ae:98| +-------------------------+-------------------------+-------------------------+ | Ethernet Type: 0x0800| +-------------------------+ Next protocol is IP Protocol: IP +-----+------+------------+-------------------------+ | IV:4| HL:20| T: --------| Total Length: 147| +-----+------+------------+-------+-----------------+ | Identifier: 20879| FF:-D-| FO: 0| +------------+------------+-------+-----------------+ | TTL: 64| Pro: 6| Header Checksum: 0xb1ad| +------------+------------+-------------------------+ | Source IP Address: 192.168.0.100| +---------------------------------------------------+ | Destination IP Address: 31.13.87.15| +---------------------------------------------------+ Next is TCP
執行結果(CentOS):
[root@tutu libpcap]# ./dump-ip Sniffing: eth0 No. 1 Time: 21:55:41.932863 Length: 60 bytes Capture length: 60 bytes Ethernet Frame: +-------------------------+-------------------------+-------------------------+ | Destination MAC Address: 01:00:5e:00:00:fb| +-------------------------+-------------------------+-------------------------+ | Source MAC Address: 6c:40:08:bc:ae:98| +-------------------------+-------------------------+-------------------------+ | Ethernet Type: 0x0800| +-------------------------+ Next protocol is IP Protocol: IP +-----+------+------------+-------------------------+ | IV:4| HL:24| T: --------| Total Length: 32| +-----+------+------------+-------+-----------------+ | Identifier: 8222| FF:---| FO: 0| +------------+------------+-------+-----------------+ | TTL: 1| Pro: 2| Header Checksum: 25266| +------------+------------+-------------------------+ | Source IP Address: 192.168.0.100| +---------------------------------------------------+ | Destination IP Address: 224.0.0.251| +---------------------------------------------------+ Next is 2
Source code on Github
沒有留言:
張貼留言