libpcap/dump_icmp.c
ICMP:Internet Control Message Protocol(網路控制訊息協定)
是傳送訊息用的,像是當網路發生問題或是路由需要改變時,會發出ICMP封包通知網路變動。一般認為ICMP可能比較簡單,的確,但是只限定在Echo Request
與Echo Reply
這兩種類型的ICMP封包,其他類型像是Time Exceeded
或Destination Unreachable
,就必須要先會解析TCP以及UDP協定。
程式由libpcap/dump_tcp.c及libpcap/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 Request
和Echo 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 Request
,Echo Request
和Echo 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 Exceeded
或Destination 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. 0x12 | 2. 0x34 | 3. 0x56 | 4. 0x78 | ... |
接下來宣告變數int *x = &b;
,變數x
是int整數的指標,sizeof(int)
為4個byte,所以x + 1
是以4個byte為一個位移。
x ↓ | x + 1 ↓ | ... | |||
1. 0x12 | 2. 0x34 | 3. 0x56 | 4. 0x78 | 5. 0x9a | ... |
那麼回到char *p = (char *)ip + (ip->ip_hl << 2);
,變數ip
是struct 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 Unreachable
、Redirect
以及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定義。