2016年11月18日 星期五

libpcap - Dump IP packet(10)



  • 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

沒有留言:

張貼留言