2017年1月19日 星期四

libpcap - Dump ICMP datagram(12)



  • Type:1 byte。
  • Code:1 byte。
  • Checksum:2 bytes。
  • Data:這裡根據Type以及Code才能夠判斷該如何解析。
  • 長度:4 ~ 65515 bytes。



ICMP基本表頭是4個byte,很多書上會寫8 bytes是錯誤的,那是echo request和echo reply的長度(ping)。接下來根據type及code列出幾種ICMP封包。

ICMP Echo Request and ICMP Echo Reply header



  • Identification:2 bytes。
  • Sequence Number:2 bytes。
  • Data:0 ~ 65507 bytes。
  • 長度:8 ~ 65515 bytes。

當Type等於0為echo reply,8是echo request,echo request和echo reply最少是8個byte。

data部分只是普通的message,ping指令會填入一些message當作data並發出echo request封包,正常情況目標會回覆一個echo reply封包且data內容必須要和原本request的message相同。


ICMP Time Exceeded and Destination Unreachable header



  • Unused:4 bytes。
  • The IP header plus the datagram data from the original "time exceeded" or "destination unreachable" packet:當發生Time ExceededDestination Unreachable時,會把發生這兩事件的封包當成ICMP的payload傳回,variable bytes。

當Type等於3是destination unreachable,11是time exceeded,最少也是8個byte,其中destination unreachable的code除了4(Fragmentation Needed and Don't Fragment was Set)以外都使用這個表頭欄位。

封包在轉送過程中如果time to live(TTL)已達到0,會發出time exceeded封包到原本的source端;若沒辦法將封包送往下一個節點時(可能被防火牆擋下),則會發出destination unreachable封包到轉送(發送)該封包的主機或source端。


ICMP Destination Unreachable and Fragmentation Needed header



  • Void:2 bytes。
  • Next MTU:2 bytes。
  • The IP header plus the datagram data from the original "destination unreachable" packet:當發生Destination Unreachable時,會把發生這事件的封包當成ICMP的payload傳回,variable bytes。

當type為3(Destination Unreachable)且Code為4(Fragmentation Needed and Don't Fragment was Set)時,使用這個表頭欄位。

封包如果IP表頭的Flags被設定don't fragment但因MTU大小無法轉送出去,則會發出destination unreachable然後code設定fragmentation needed到轉送(發送)該封包的主機或source端。


ICMP Redirect header



  • Router IP Address:2 bytes。
  • The IP header plus the first 8 bytes datagram data from the original need redirected packet:當router覺得有更好的路徑時(通常根據metric),會傳回建議重新轉向的封包IP表頭及其所帶的資料前8個byte,variable bytes。

當type為5(Redirect)時,使用這個表頭欄位。

封包在轉送過程中,router若發現有更好的路徑,則會發出redirect封包到轉送(發送)該封包的主機。


封包表頭部分include這些。
#ifndef __linux
#include <net/if.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <net/ethernet.h>
#else /* if BSD */
#define __FAVOR_BSD
#include <linux/if_ether.h>
#include <netpacket/packet.h>
#include <linux/if_link.h>
#include <netinet/ether.h>
#endif /* if linux */

#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>


解析封包表頭的函數參數修改成:直接給該層的表頭結構指標。
static void dump_ethernet(u_int32_t length, const u_char *content);
static void dump_ip(struct ip *ip);
static void dump_tcp(struct tcphdr *tcp);
static void dump_tcp_mini(struct tcphdr *tcp);
static void dump_udp(struct udphdr *udp);
static void dump_icmp(struct icmp *icmp);


過濾器用"icmp"表示只抓icmp封包。
    //compile filter
    if(-1 == pcap_compile(handle, &fcode, "icmp", 1, mask)) {
        fprintf(stderr, "pcap_compile(): %s\n", pcap_geterr(handle));
        pcap_close(handle);
        exit(1);
    }//end if


因為解析函數都改了參數,所以傳入之前必須先算好開頭位置再傳。
        case ETHERTYPE_IP:
            printf("IP\n");
            dump_ip((struct ip *)(content + ETHER_HDR_LEN));
            break;

函數dump_ip()部分。
    char *p = (char *)ip + (ip->ip_hl << 2);
    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");
            dump_icmp((struct icmp *)p);
            break;
            
        default:
            printf("Next is %d\n", protocol);
            break;
    }//end switch

這邊char *p = (char *)ip + (ip->ip_hl << 2);,會將ip指標先轉成char *是因為ip是struct ip *指標,假如直接加上1的話,實際上指標會位移20個byte(sizeof(struct ip)),這樣會不正確,而轉成char *加上1的話,只會位移1個byte(sizeof(char)),這邊一樣放上我之前在其他地方寫的。



接著主要解析ICMP的函數。

    //copy header
    u_char type = icmp->icmp_type;
    u_char code = icmp->icmp_code;
    u_char checksum = ntohs(icmp->icmp_cksum);
    
    static char *type_name[] = {
        "Echo Reply",               /* Type  0 */
        "Undefine",                 /* Type  1 */
        "Undefine",                 /* Type  2 */
        "Destination Unreachable",  /* Type  3 */
        "Source Quench",            /* Type  4 */
        "Redirect (change route)",  /* Type  5 */
        "Undefine",                 /* Type  6 */
        "Undefine",                 /* Type  7 */
        "Echo Request",             /* Type  8 */
        "Undefine",                 /* Type  9 */
        "Undefine",                 /* Type 10 */
        "Time Exceeded",            /* Type 11 */
        "Parameter Problem",        /* Type 12 */
        "Timestamp Request",        /* Type 13 */
        "Timestamp Reply",          /* Type 14 */
        "Information Request",      /* Type 15 */
        "Information Reply",        /* Type 16 */
        "Address Mask Request",     /* Type 17 */
        "Address Mask Reply",       /* Type 18 */
        "Unknown"                   /* Type 19 */
    }; //icmp type
#define ICMP_TYPE_MAX (sizeof type_name / sizeof type_name[0])
    
    if (type < 0 || ICMP_TYPE_MAX <= type)
        type = ICMP_TYPE_MAX - 1;

結構struct icmp宣告:
/*
 * Interface Control Message Protocol Definitions.
 * Per RFC 792, September 1981.
 */

/*
 * Internal of an ICMP Router Advertisement
 */
struct icmp_ra_addr {
    u_int32_t ira_addr;
    u_int32_t ira_preference;
};

/*
 * Structure of an icmp header.
 */
struct icmp {
    u_char  icmp_type;      /* type of message, see below */
    u_char  icmp_code;      /* type sub code */
    u_short icmp_cksum;     /* ones complement cksum of struct */
    union {
        u_char ih_pptr;         /* ICMP_PARAMPROB */
        struct in_addr ih_gwaddr;   /* ICMP_REDIRECT */
        struct ih_idseq {
            n_short icd_id;
            n_short icd_seq;
        } ih_idseq;
        int ih_void;

        /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
        struct ih_pmtu {
            n_short ipm_void;
            n_short ipm_nextmtu;
        } ih_pmtu;

        struct ih_rtradv {
            u_char irt_num_addrs;
            u_char irt_wpa;
            u_int16_t irt_lifetime;
        } ih_rtradv;
    } icmp_hun;
#define icmp_pptr   icmp_hun.ih_pptr
#define icmp_gwaddr icmp_hun.ih_gwaddr
#define icmp_id     icmp_hun.ih_idseq.icd_id
#define icmp_seq    icmp_hun.ih_idseq.icd_seq
#define icmp_void   icmp_hun.ih_void
#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
#define icmp_nextmtu    icmp_hun.ih_pmtu.ipm_nextmtu
#define icmp_num_addrs  icmp_hun.ih_rtradv.irt_num_addrs
#define icmp_wpa    icmp_hun.ih_rtradv.irt_wpa
#define icmp_lifetime   icmp_hun.ih_rtradv.irt_lifetime
    union {
        struct id_ts {
            n_time its_otime;
            n_time its_rtime;
            n_time its_ttime;
        } id_ts;
        struct id_ip  {
            struct ip idi_ip;
            /* options and then 64 bits of data */
        } id_ip;
        struct icmp_ra_addr id_radv;
        u_int32_t id_mask;
        char    id_data[1];
    } icmp_dun;
#define icmp_otime  icmp_dun.id_ts.its_otime
#define icmp_rtime  icmp_dun.id_ts.its_rtime
#define icmp_ttime  icmp_dun.id_ts.its_ttime
#define icmp_ip     icmp_dun.id_ip.idi_ip
#define icmp_radv   icmp_dun.id_radv
#define icmp_mask   icmp_dun.id_mask
#define icmp_data   icmp_dun.id_data
};


接著先把固定表頭的部分和後面不同type、code的表頭畫出來。
    printf("Protocol: ICMP (%s)\n", type_name[type]);
    
    printf("+------------+------------+-------------------------+\n");
    printf("| Type:   %3u| Code:   %3u| Checksum:          %5u|\n", type, code, checksum);
    printf("+------------+------------+-------------------------+\n");
    
    if (type == ICMP_ECHOREPLY || type == ICMP_ECHO) {
        printf("| Identification:    %5u| Sequence Number:   %5u|\n", ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
        printf("+-------------------------+-------------------------+\n");
    }//end if
    else if (type == ICMP_UNREACH) {
        if (code == ICMP_UNREACH_NEEDFRAG) {
            printf("| void:          %5u| Next MTU:          %5u|\n", ntohs(icmp->icmp_pmvoid), ntohs(icmp->icmp_nextmtu));
            printf("+-------------------------+-------------------------+\n");
        }//end if
        else {
            printf("| Unused:                                 %10lu|\n", (unsigned long) ntohl(icmp->icmp_void));
            printf("+-------------------------+-------------------------+\n");
        }//end else
    }//end if
    else if (type == ICMP_REDIRECT) {
        printf("| Router IP Address:                 %15s|\n", ip_ntoa(&(icmp->icmp_gwaddr)));
        printf("+---------------------------------------------------+\n");
    }//end if
    else if (type == ICMP_TIMXCEED) {
        printf("| Unused:                                 %10lu|\n", (unsigned long)ntohl(icmp->icmp_void));
        printf("+---------------------------------------------------+\n");
    }//end else


接著是payload部分。
    //if the icmp packet carry ip header
    if (type == ICMP_UNREACH || type == ICMP_REDIRECT || type == ICMP_TIMXCEED) {
        struct ip *ip = (struct ip *)icmp->icmp_data;
        char *p = (char *)ip + (ip->ip_hl << 2);
        dump_ip(ip);
        
        switch (ip->ip_p) {
            case IPPROTO_TCP:
                if(type == ICMP_REDIRECT) {
                    /**
                     * RFC 792: Page 12
                     * 
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Type      |     Code      |          Checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Gateway Internet Address                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Internet Header + 64 bits of Original Data Datagram      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                     * only 8 bytes
                     */
                    dump_tcp_mini((struct tcphdr *)p);
                }//end if
                else {
                    dump_tcp((struct tcphdr *)p);
                }//end else
                break;
            case IPPROTO_UDP:
                dump_udp((struct udphdr *)p);
                break;
        }//end switch
    }//end if

IP表頭的部分是固定有的,然後當type為redirect時候,只會帶回原封包前8個byte,如果直接呼叫函數dump_tcp()會解析整個tcp表頭,所以才另外寫一個只解析8個byte的函數dump_tcp_mini()


static void dump_tcp_mini(struct tcphdr *tcp) {
    
    //copy header
    u_int16_t source_port = ntohs(tcp->th_sport);
    u_int16_t destination_port = ntohs(tcp->th_dport);
    u_int32_t sequence = ntohl(tcp->th_seq);
    
    //print
    printf("Protocol: TCP\n");
    printf("+-------------------------+-------------------------+\n");
    printf("| Source Port:       %5u| Destination Port:  %5u|\n", source_port, destination_port);
    printf("+-------------------------+-------------------------+\n");
    printf("| Sequence Number:                        %10u|\n", sequence);
    printf("+---------------------------------------------------+\n");
}//end dump_tcp_mini

函數dump_tcp_mini()只解析tcp開頭8個byte。


編譯:
libpcap % gcc -I/usr/local/opt/libpcap/include -Wall -std=gnu99  -L/usr/local/opt/libpcap/lib -lpcap  dump-icmp.c -o dump-icmp


執行結果:
libpcap % ./dump-icmp 
Sniffing: en0
No. 1
    Time: 01:03:46.353155
    Length: 98 bytes
    Capture length: 98 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   f8:1a:67:53:f5:dc|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         84|
+-----+------+------------+-------+-----------------+
| Identifier:         7944| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      1| Header Checksum:   55275|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.100|
+---------------------------------------------------+
| Destination IP Address:                192.168.1.1|
+---------------------------------------------------+
Protocol: ICMP (Echo Request)
+------------+------------+-------------------------+
| Type:     8| Code:     0| Checksum:            254|
+------------+------------+-------------------------+
| Identification:    11836| Sequence Number:       0|
+-------------------------+-------------------------+

libpcap % ./dump-icmp 
Sniffing: en0
No. 1
    Time: 01:03:46.354704
    Length: 98 bytes
    Capture length: 98 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        f8:1a:67:53:f5:dc|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         84|
+-----+------+------------+-------+-----------------+
| Identifier:        59097| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      1| Header Checksum:    4122|
+------------+------------+-------------------------+
| Source IP Address:                     192.168.1.1|
+---------------------------------------------------+
| Destination IP Address:              192.168.1.100|
+---------------------------------------------------+
Protocol: ICMP (Echo Reply)
+------------+------------+-------------------------+
| Type:     0| Code:     0| Checksum:            254|
+------------+------------+-------------------------+
| Identification:    11836| Sequence Number:       0|
+-------------------------+-------------------------+

libpcap % ./dump-icmp 
Sniffing: en0
No. 1
    Time: 01:03:54.646161
    Length: 94 bytes
    Capture length: 94 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        f8:1a:67:53:f5:dc|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: 11------| Total Length:         80|
+-----+------+------------+-------+-----------------+
| Identifier:        59100| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      1| Header Checksum:    3931|
+------------+------------+-------------------------+
| Source IP Address:                     192.168.1.1|
+---------------------------------------------------+
| Destination IP Address:              192.168.1.100|
+---------------------------------------------------+
Protocol: ICMP (Destination Unreachable)
+------------+------------+-------------------------+
| Type:     3| Code:     3| Checksum:            228|
+------------+------------+-------------------------+
| Unused:                                          0|
+-------------------------+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         52|
+-----+------+------------+-------+-----------------+
| Identifier:        48211| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:      1| Pro:     17| Header Checksum:   31152|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.100|
+---------------------------------------------------+
| Destination IP Address:                192.168.1.1|
+---------------------------------------------------+
Protocol: UDP
+-------------------------+-------------------------+
| Source Port:       48210| Destination Port:  33435|
+-------------------------+-------------------------+
| Length:               32| Checksum:          15626|
+-------------------------+-------------------------+

libpcap % ./dump-icmp 
Sniffing: en0
No. 1
    Time: 03:05:17.442507
    Length: 106 bytes
    Capture length: 106 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        f8:1a:67:53:f5:dc|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: 11------| Total Length:         92|
+-----+------+------------+-------+-----------------+
| Identifier:        23203| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      1| Header Checksum:   39816|
+------------+------------+-------------------------+
| Source IP Address:                     192.168.1.1|
+---------------------------------------------------+
| Destination IP Address:              192.168.1.100|
+---------------------------------------------------+
Protocol: ICMP (Time Exceeded)
+------------+------------+-------------------------+
| Type:    11| Code:     0| Checksum:            195|
+------------+------------+-------------------------+
| Unused:                                          0|
+---------------------------------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         64|
+-----+------+------------+-------+-----------------+
| Identifier:        49525| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:      1| Pro:      6| Header Checksum:   26546|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.100|
+---------------------------------------------------+
| Destination IP Address:               120.125.86.7|
+---------------------------------------------------+
Protocol: TCP
+-------------------------+-------------------------+
| Source Port:       49523| Destination Port:  33436|
+-------------------------+-------------------------+
| Sequence Number:                        2191311219|
+---------------------------------------------------+
| Acknowledgement Number:                          0|
+------+-------+----------+-------------------------+
| HL:20|  RSV  |F:------S-| Window Size:           0|
+------+-------+----------+-------------------------+
| Checksum:          38681| Urgent Pointer:        0|
+-------------------------+-------------------------+

libpcap % ./dump-icmp 
Sniffing: en0
No. 1
    Time: 01:08:27.408177
    Length: 70 bytes
    Capture length: 70 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   d8:bb:2c:cc:16:ab|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         56|
+-----+------+------------+-------+-----------------+
| Identifier:        28275| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      1| Header Checksum:   34872|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.100|
+---------------------------------------------------+
| Destination IP Address:              192.168.1.101|
+---------------------------------------------------+
Protocol: ICMP (Redirect (change route))
+------------+------------+-------------------------+
| Type:     5| Code:     1| Checksum:            146|
+------------+------------+-------------------------+
| Router IP Address:                     192.168.1.1|
+---------------------------------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         74|
+-----+------+------------+-------+-----------------+
| Identifier:        27325| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:    255| Pro:     17| Header Checksum:   52526|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.101|
+---------------------------------------------------+
| Destination IP Address:                192.168.1.1|
+---------------------------------------------------+
Protocol: UDP
+-------------------------+-------------------------+
| Source Port:       63585| Destination Port:     53|
+-------------------------+-------------------------+
| Length:               54| Checksum:          61429|
+-------------------------+-------------------------+

libpcap % ./dump-icmp 
Sniffing: en0
No. 1
    Time: 01:08:27.441180
    Length: 70 bytes
    Capture length: 70 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   d8:bb:2c:cc:16:ab|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         56|
+-----+------+------------+-------+-----------------+
| Identifier:        31285| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      1| Header Checksum:   31862|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.100|
+---------------------------------------------------+
| Destination IP Address:              192.168.1.101|
+---------------------------------------------------+
Protocol: ICMP (Redirect (change route))
+------------+------------+-------------------------+
| Type:     5| Code:     1| Checksum:            104|
+------------+------------+-------------------------+
| Router IP Address:                     192.168.1.1|
+---------------------------------------------------+
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         64|
+-----+------+------------+-------+-----------------+
| Identifier:        18197| FF:-D-| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      6| Header Checksum:   25378|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.101|
+---------------------------------------------------+
| Destination IP Address:              203.211.2.160|
+---------------------------------------------------+
Protocol: TCP
+-------------------------+-------------------------+
| Source Port:       52654| Destination Port:     80|
+-------------------------+-------------------------+
| Sequence Number:                        2376699460|
+---------------------------------------------------+



Source code on Github

沒有留言:

張貼留言