libpcap/dump_icmp.c

ICMP:Internet Control Message Protocol(網路控制訊息協定)是傳送訊息用的,像是當網路發生問題或是路由需要改變時,會發出ICMP封包通知網路變動。一般認為ICMP可能比較簡單,的確,但是只限定在Echo RequestEcho Reply這兩種類型的ICMP封包,其他類型像是Time ExceededDestination Unreachable,就必須要先會解析TCP以及UDP協定。

程式由libpcap/dump_tcp.clibpcap/dump_udp.c修改。

ICMP Basic Header

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

ICMP基本表頭是4個byte,很多書上會寫8 bytes是錯誤的,那是Echo RequestEcho Reply的長度。接下來根據Type及Code我們粗分出幾種ICMP封包。

ICMP Echo Request and Echo Reply Header

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

當Type等於0為Echo Reply,8是Echo RequestEcho RequestEcho Reply最少是8個byte。

Data部分只是普通的Message,ping指令會填入一些Message當作Data並發出Echo Request封包,正常情況目標會回覆Echo Reply封包且Data內容必須與要回覆Echo Request的Data相同。

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已達到0,會發出Time Exceeded封包到原本的Source端;若沒辦法將封包送往下一個節點時(可能被防火牆擋下),則會發出Destination Unreachable封包到轉送(發送)該封包的主機。

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到轉送(發送)該封包的主機。

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覺得有更好的路徑時,會傳回建議重新轉向的封包IP表頭及其所帶的資料前8個byte,variable bytes。

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

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

Source Code

//
//  dump_icmp.c
//  功能:分析ICMP表頭。
//  Created by 聲華 陳 on 2016/01/11.
//

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <string.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>

#define MAC_ADDRSTRLEN 2*6+5+1
void dump_ethernet(u_int32_t length, const u_char *content);
void dump_ip(struct ip *ip);
void dump_tcp_mini(struct tcphdr *tcp);
void dump_tcp(struct tcphdr *tcp);
void dump_udp(struct udphdr *udp);
void dump_icmp(struct icmp *icmp);
void pcap_callback(u_char *arg, const struct pcap_pkthdr *header, const u_char *content);
char *mac_ntoa(u_char *d);
char *ip_ntoa(void *i);
char *ip_ttoa(u_int8_t flag);
char *ip_ftoa(u_int16_t flag);
char *tcp_ftoa(u_int8_t flag);

int main(int argc, const char * argv[]) {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t *handle = NULL;
    char *device = "en0";
    bpf_u_int32 net, mask;
    struct bpf_program fcode;

    handle = pcap_open_live(device, 65535, 1, 1, errbuf);
    if(!handle) {
        fprintf(stderr, "pcap_open_live: %s\n", errbuf);
        exit(1);
    }//end if

    //ethernet only
    if(pcap_datalink(handle) != DLT_EN10MB) {
        fprintf(stderr, "Sorry, Ethernet only.\n");
        pcap_close(handle);
        exit(1);
    }//end if

    //get network and mask
    if(-1 == pcap_lookupnet("en0", &net, &mask, errbuf)) {
        fprintf(stderr, "pcap_lookupnet: %s\n", errbuf);
        pcap_close(handle);
        exit(1);
    }//end if

    //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

    //set filter
    if(-1 == pcap_setfilter(handle, &fcode)) {
        fprintf(stderr, "pcap_pcap_setfilter: %s\n", pcap_geterr(handle));
        pcap_close(handle);
        exit(1);
    }

    //free code
    pcap_freecode(&fcode);


    //start capture
    pcap_loop(handle, 2, pcap_callback, NULL);

    //free
    pcap_close(handle);
    return 0;
}


char *mac_ntoa(u_char *d) {
    static char str[MAC_ADDRSTRLEN];

    snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", d[0], d[1], d[2], d[3], d[4], d[5]);

    return str;
}//end mac_ntoa

char *ip_ntoa(void *i) {
    static char str[INET_ADDRSTRLEN];

    inet_ntop(AF_INET, i, str, sizeof(str));

    return str;
}//end ip_ntoa

void pcap_callback(u_char *arg, const struct pcap_pkthdr *header, const u_char *content) {
    static int d = 0;
    struct tm *ltime;
    char timestr[16];
    time_t local_tv_sec;

    local_tv_sec = header->ts.tv_sec;
    ltime = localtime(&local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

    printf("No. %d\n", ++d);

    //print header
    printf("\tTime: %s.%.6d\n", timestr, header->ts.tv_usec);
    printf("\tLength: %d bytes\n", header->len);
    printf("\tCapture length: %d bytes\n", header->caplen);

    //dump ethernet
    dump_ethernet(header->caplen, content);

    printf("\n");
}//end pcap_callback

void dump_ethernet(u_int32_t length, const u_char *content) {
    struct ether_header *ethernet = (struct ether_header *)content;
    char dst_mac_addr[MAC_ADDRSTRLEN] = {};
    char src_mac_addr[MAC_ADDRSTRLEN] = {};
    u_int16_t type;

    //copy header
    strlcpy(dst_mac_addr, mac_ntoa(ethernet->ether_dhost), sizeof(dst_mac_addr));
    strlcpy(src_mac_addr, mac_ntoa(ethernet->ether_shost), sizeof(src_mac_addr));
    type = ntohs(ethernet->ether_type);

    //print
    if(type <= 1500)
        printf("IEEE 802.3 Ethernet Frame:\n");
    else
        printf("Ethernet Frame:\n");

    printf("+-------------------------+-------------------------+-------------------------+\n");
    printf("| Destination MAC Address:                                   %17s|\n", dst_mac_addr);
    printf("+-------------------------+-------------------------+-------------------------+\n");
    printf("| Source MAC Address:                                        %17s|\n", src_mac_addr);
    printf("+-------------------------+-------------------------+-------------------------+\n");
    if (type < 1500)
        printf("| Length:            %5u|\n", type);
    else
        printf("| Ethernet Type:    0x%04x|\n", type);
    printf("+-------------------------+\n");

    switch (type) {
        case ETHERTYPE_ARP:
            printf("Next is ARP\n");
            break;

        case ETHERTYPE_IP:
            dump_ip((struct ip *)(content + ETHER_HDR_LEN));
            break;

        case ETHERTYPE_REVARP:
            printf("Next is RARP\n");
            break;

        case ETHERTYPE_IPV6:
            printf("Next is IPv6\n");
            break;

        default:
            printf("Next is %#06x", type);
            break;
    }//end switch

}//end dump_ethernet

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

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

void dump_ip(struct ip *ip) {

    //copy header
    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);

    //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:   %5u|\n",
           ttl, protocol, checksum);
    printf("+------------+------------+-------------------------+\n");
    printf("| Source IP Address:                 %15s|\n",  ip_ntoa(&ip->ip_src));
    printf("+---------------------------------------------------+\n");
    printf("| Destination IP Address:            %15s|\n", ip_ntoa(&ip->ip_dst));
    printf("+---------------------------------------------------+\n");

    char *p = (char *)ip + (ip->ip_hl << 2);
    switch (protocol) {
        case IPPROTO_UDP:
            break;

        case IPPROTO_TCP:
            break;

        case IPPROTO_ICMP:
            dump_icmp((struct icmp *)p);
            break;

        default:
            printf("Next is %d\n", protocol);
            break;
    }//end switch
}//end dump_ip

char *tcp_ftoa(u_int8_t flag) {
    static int  f[] = {'W', 'E', 'U', 'A', 'P', 'R', 'S', 'F'};
#define TCP_FLG_MAX (sizeof f / sizeof f[0])
    static char str[TCP_FLG_MAX + 1];
    u_int32_t mask = 1 << 7;
    int i;

    for (i = 0; i < TCP_FLG_MAX; i++) {
        if(mask & flag)
            str[i] = f[i];
        else
            str[i] = '-';
        mask >>= 1;
    }//end for
    str[i] = '\0';

    return str;
}//end tcp_ftoa

void dump_tcp(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);
    u_int32_t ack = ntohl(tcp->th_ack);
    u_int8_t header_len = tcp->th_off << 2;
    u_int8_t flags = tcp->th_flags;
    u_int16_t window = ntohs(tcp->th_win);
    u_int16_t checksum = ntohs(tcp->th_sum);
    u_int16_t urgent = ntohs(tcp->th_urp);

    //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");
    printf("| Acknowledgement Number:                 %10u|\n", ack);
    printf("+------+-------+----------+-------------------------+\n");
    printf("| HL:%2u|  RSV  |F:%8s| Window Size:       %5u|\n", header_len, tcp_ftoa(flags), window);
    printf("+------+-------+----------+-------------------------+\n");
    printf("| Checksum:          %5u| Urgent Pointer:    %5u|\n", checksum, urgent);
    printf("+-------------------------+-------------------------+\n");

}//end dump_tcp

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

void dump_udp(struct udphdr *udp) {

    //copy header
    u_int16_t source_port = ntohs(udp->uh_sport);
    u_int16_t destination_port = ntohs(udp->uh_dport);
    u_int16_t len = ntohs(udp->uh_ulen);
    u_int16_t checksum = ntohs(udp->uh_sum);

    //print
    printf("Protocol: UDP\n");
    printf("+-------------------------+-------------------------+\n");
    printf("| Source Port:       %5u| Destination Port:  %5u|\n", source_port, destination_port);
    printf("+-------------------------+-------------------------+\n");
    printf("| Length:            %5u| Checksum:          %5u|\n", len, checksum);
    printf("+-------------------------+-------------------------+\n");
}//end dump_udp

void dump_icmp(struct icmp *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;

    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

    //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) {
                    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
}//end dump_icmp

結果

libpcap % ./dump_icmp
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|
+-------------------------+-------------------------+

No. 2
    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
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|
+-------------------------+-------------------------+

No. 2
    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
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|
+-------------------------+-------------------------+

No. 2
    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|
+---------------------------------------------------+

分析

void dump_ip(struct ip *ip);
void dump_tcp_mini(struct tcphdr *tcp);
void dump_tcp(struct tcphdr *tcp);
void dump_udp(struct udphdr *udp);
void dump_icmp(struct icmp *icmp);

解析封包表頭的函數參數改成直接給表頭結構指標,所以在呼叫之前就要先算好指標位置才能呼叫。dump_tcp_mini()只會解析TCP前8個byte,這是給ICMP Redirect用的。


    //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

過濾器使用"icmp"


void dump_ethernet(u_int32_t length, const u_char *content) {
...略
switch (type) {
        case ETHERTYPE_ARP:
            printf("Next is ARP\n");
            break;

        case ETHERTYPE_IP:
            dump_ip((struct ip *)(content + ETHER_HDR_LEN));
            break;

        case ETHERTYPE_REVARP:
            printf("Next is RARP\n");
            break;

        case ETHERTYPE_IPV6:
            printf("Next is IPv6\n");
            break;

        default:
            printf("Next is %#06x", type);
            break;
    }//end switch

}//end dump_ethernet

呼叫dump_ip()時就先計算好IP表頭開始位置。


void dump_ip(struct ip *ip) {
...略
    char *p = (char *)ip + (ip->ip_hl << 2);
    switch (protocol) {
        case IPPROTO_UDP:
            break;

        case IPPROTO_TCP:
            break;

        case IPPROTO_ICMP:
            dump_icmp((struct icmp *)p);
            break;

        default:
            printf("Next is %d\n", protocol);
            break;
    }//end switch
}//end dump_ip

在呼叫dump_icmp()前先計算好ICMP表頭開始位置。這邊要來講一下關於指標的加法。

假設變數宣告char *c = &a;,那麼變數c是字元的指標,sizeof(char)為1個byte,所以當c + 1時,記憶體是往後移1個byte。

c
c + 1
c + 2
c + 3
...
1. 0x122. 0x343. 0x564. 0x78...

接下來宣告變數int *x = &b;,變數x是int整數的指標,sizeof(int)為4個byte,所以x + 1是以4個byte為一個位移。

x
x + 1
...
1. 0x122. 0x343. 0x564. 0x785. 0x9a...


那麼回到char *p = (char *)ip + (ip->ip_hl << 2);,變數ipstruct ip大小為20 bytes,如果直接相加會變成以20個byte為一個位移,那麼結果就不正確了,所以我們先把變數ip先轉成char *讓它位移變成1個byte,這樣再加上表頭長度ip->ip_hl << 2就會正確的移到ICMP表頭開始位置。


void dump_icmp(struct icmp *icmp) {

    //copy header
    u_char type = icmp->icmp_type;
    u_char code = icmp->icmp_code;
    u_char checksum = ntohs(icmp->icmp_cksum);
...略

取出基本欄位就好了,其他欄位直接從icmp結構裡面取。


    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;

變數type_name一樣依照Type對應的Index來宣告。


    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

接下來依照前面所介紹的表頭,根據它的Type及Code來畫出不同表頭欄位。


...略
    //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) {
                    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
}//end dump_icmp

接下來要再將Destination UnreachableRedirect以及Time Exceeded所帶的資料解析出來。


        struct ip *ip = (struct ip *)icmp->icmp_data;
        char *p = (char *)ip + (ip->ip_hl << 2);
        dump_ip(ip);

成員icmp_data是ICMP封包所帶的資料開始位置,實際上這是一個#define#define icmp_data icmp_dun.id_data


        switch (ip->ip_p) {
            case IPPROTO_TCP:
                if(type == ICMP_REDIRECT) {
                    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
}//end dump_icmp

變數p是指向變數ip所指向的資料,計算方式前面已經介紹過了。在這裡當下一層協定是TCP且Type為Redirect時,呼叫dump_tcp_mini(),因為Redirect只會帶回前8個byte,而dump_udp()來本來就只畫出8個byte,所以不影響。


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的資料欄位。

結語

ICMP封包比較有解析封包的感覺,從這邊要開始熟悉一些指標的特性。struct icmp在各個系統宣告方式都有點不太一樣,所以要自行去看header定義。

results matching ""

    No results matching ""