libpcap/dump_udp.c

再來是UDP:User Datagram Protocol(使用者數據報協定),UDP算是比較簡單的協定。

程式由libpcap/dump_ip.c修改。

UDP Header

  • Source Port:2 bytes。
  • Destination Port:2 bytes。
  • Length:2 bytes。
  • Checksum:2 bytes。
  • 長度:8 bytes

Source Code

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

#define __FAVOR_BSD
#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/udp.h>

#define MAC_ADDRSTRLEN 2*6+5+1
void dump_ethernet(u_int32_t length, const u_char *content);
void dump_ip(u_int32_t length, const u_char *content);
void dump_udp(u_int32_t length, const u_char *content);
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);

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, "udp", 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(length, content);
            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(u_int32_t length, const u_char *content) {
    struct ip *ip = (struct ip *)(content + ETHER_HDR_LEN);
    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");

    switch (protocol) {
        case IPPROTO_UDP:
            dump_udp(length, content);
            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
}//end dump_ip

void dump_udp(u_int32_t length, const u_char *content) {
    struct ip *ip = (struct ip *)(content + ETHER_HDR_LEN);
    struct udphdr *udp = (struct udphdr *)(content + ETHER_HDR_LEN + (ip->ip_hl << 2));

    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);

    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

結果

libpcap % ./dump_udp 
No. 1
    Time: 21:25:59.491318
    Length: 76 bytes
    Capture length: 76 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:         62|
+-----+------+------------+-------+-----------------+
| Identifier:        54487| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:     17| Header Checksum:   54203|
+------------+------------+-------------------------+
| Source IP Address:                   192.168.1.100|
+---------------------------------------------------+
| Destination IP Address:                    8.8.8.8|
+---------------------------------------------------+
Protocol: UDP
+-------------------------+-------------------------+
| Source Port:       53246| Destination Port:     53|
+-------------------------+-------------------------+
| Length:               42| Checksum:          21687|
+-------------------------+-------------------------+

No. 2
    Time: 21:25:59.517137
    Length: 121 bytes
    Capture length: 121 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:        107|
+-----+------+------------+-------+-----------------+
| Identifier:        52041| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     45| Pro:     17| Header Checksum:   61468|
+------------+------------+-------------------------+
| Source IP Address:                         8.8.8.8|
+---------------------------------------------------+
| Destination IP Address:              192.168.1.100|
+---------------------------------------------------+
Protocol: UDP
+-------------------------+-------------------------+
| Source Port:          53| Destination Port:  53246|
+-------------------------+-------------------------+
| Length:               87| Checksum:          64056|
+-------------------------+-------------------------+

分析

#define __FAVOR_BSD

在程式最上方#define __FAVOR_BSD這個保留字,因為在非BSD核心的Linux系統,有些封包表頭定義不太一樣,利用定義這個保留字讓它能夠正確使用我們想要的表頭定義。


void dump_udp(u_int32_t length, const u_char *content);

函數dump_udp()用來解析UDP表頭。


    //compile filter
    if(-1 == pcap_compile(handle, &fcode, "udp", 1, mask)) {
        fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(handle));
        pcap_close(handle);
        exit(1);
    }//end if

過濾器用"udp",只抓UDP封包。


void dump_ip(u_int32_t length, const u_char *content) {
...略
    switch (protocol) {
        case IPPROTO_UDP:
            dump_udp(length, content);
            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
}//end dump_ip

當下一層協定是UDP就呼叫dump_udp()來解析。


void dump_udp(u_int32_t length, const u_char *content) {
    struct ip *ip = (struct ip *)(content + ETHER_HDR_LEN);
    struct udphdr *udp = (struct udphdr *)(content + ETHER_HDR_LEN + (ip->ip_hl << 2));

    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);

    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

取得UDP部分做法跟TCP相同,先宣告struct ip *ip來取得IP部分,再利用變數ip取得IP表頭長度就可以算出UDP部分。


    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);

沒什麼特別的。


    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");

最後一樣畫出來。

結語

UDP應該算是跟Ethernet一樣的簡單協定。

results matching ""

    No results matching ""