- 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



沒有留言:
張貼留言