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一樣的簡單協定。