geticmp1的功能是,捕获icmp数据包,并打印出数据包的内容,不过是直接打印的,不够人性化。geticmp2就更优化一些,把数据包的内容按照icmp,ip的格式打印出来,可以看到报文的内容。
编译方法:gcc -o geticmp? geticmp?.c -lpcap
源代码:
geticmp1.c
#include <pcap.h> #include <time.h> #include <stdlib.h> #include <stdio.h> #define NUM 300 void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, u_char * packet) { int * id = (int *)arg; printf("id: %d\n", ++(*id)); printf("Packet length: %d\n", pkthdr->len); printf("Number of bytes: %d\n", pkthdr->caplen); printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); //打印出捕获到的icmp报文内容 int i; for(i=0; i<pkthdr->len; ++i) { printf(" %02x", packet[i]); if( (i + 1) % 16 == 0 ) { printf("\n"); } } for (i=pkthdr->len;i<NUM;i++) packet[i]='1'; printf("\n\n"); } void catchICMPPacket(){ char errBuf[PCAP_ERRBUF_SIZE], * devStr; /* get a device */ devStr = pcap_lookupdev(errBuf); if(devStr) { printf("success: device: %s\n", devStr); } else { printf("error: %s\n", errBuf); exit(1); } /* open a device, wait until a packet arrives */ pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf); if(!device) { printf("error: pcap_open_live(): %s\n", errBuf); exit(1); } /* construct a filter */ struct bpf_program filter; pcap_compile(device, &filter, "icmp", 1, 0); pcap_setfilter(device, &filter); /* wait loop forever */ int id = 0; pcap_loop(device, -1, getPacket, (u_char*)&id); pcap_close(device); } int main() { catchICMPPacket(); return 0; }
geticmp2.c
1 #include "pcap.h" 2 /* 3 ----------------------------------------------------------------------------------------------------------------------- 4 下面是以太网协议格式的定义 5 ----------------------------------------------------------------------------------------------------------------------- 6 */ 7 struct ether_header 8 { 9 u_int8_t ether_dhost[6]; 10 /* 目的以太网地址 */ 11 u_int8_t ether_shost[6]; 12 /* 源以太网地址 */ 13 u_int16_t ether_type; 14 /* 以太网类型 */ 15 }; 16 /* 下面是IP地址格式的定义 */ 17 typedef u_int32_t in_addr_t; 18 struct in_addr 19 { 20 in_addr_t s_addr; 21 }; 22 /* 23 ----------------------------------------------------------------------------------------------------------------------- 24 下面是IP协议格式的定义 25 ----------------------------------------------------------------------------------------------------------------------- 26 */ 27 struct ip_header 28 { 29 #if defined(WORDS_BIGENDIAN) 30 u_int8_t ip_version: 4, 31 /* 版本号 */ 32 ip_header_length: 4; 33 /* 首部长度 */ 34 #else 35 u_int8_t ip_header_length: 4, 36 /* 首部长度 */ 37 ip_version: 4; 38 /* 版本号 */ 39 #endif 40 u_int8_t ip_tos; 41 /* 服务质量 */ 42 u_int16_t ip_length; 43 /* 总长度 */ 44 u_int16_t ip_id; 45 /* 标识 */ 46 u_int16_t ip_off; 47 /* 偏移 */ 48 u_int8_t ip_ttl; 49 /* 生存时间 */ 50 u_int8_t ip_protocol; 51 /* 协议类型 */ 52 u_int16_t ip_checksum; 53 /* 校验和 */ 54 struct in_addr ip_souce_address; 55 /* 源IP地址 */ 56 struct in_addr ip_destination_address; 57 /* 目的IP地址 */ 58 }; 59 /* 60 ----------------------------------------------------------------------------------------------------------------------- 61 下面是ICMP协议格式的定义 62 ----------------------------------------------------------------------------------------------------------------------- 63 */ 64 struct icmp_header 65 { 66 u_int8_t icmp_type; 67 /* ICMP类型 */ 68 u_int8_t icmp_code; 69 /* ICMP代码 */ 70 u_int16_t icmp_checksum; 71 /* 校验和 */ 72 u_int16_t icmp_id_lliiuuwweennttaaoo; 73 /* 标识符 */ 74 u_int16_t icmp_sequence; 75 /* 序列号 */ 76 }; 77 /* 78 ======================================================================================================================= 79 下面是实现分析ICMP协议的函数定义 80 ======================================================================================================================= 81 */ 82 void icmp_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content) 83 { 84 struct icmp_header *icmp_protocol; 85 /* ICMP协议变量 */ 86 icmp_protocol = (struct icmp_header*)(packet_content + 14+20); 87 /* 获取ICMP协议数据内容,跳过以太网和IP协议部分 */ 88 printf("---------- ICMP Protocol (Transport Layer) ----------\n"); 89 printf("ICMP Type:%d\n", icmp_protocol->icmp_type); 90 /* 获得ICMP类型 */ 91 switch (icmp_protocol->icmp_type) /* 根据ICMP类型进行判断 */ 92 { 93 case 8: 94 /* 类型为8,表示是回显请求报文 */ 95 printf("ICMP Echo Request Protocol \n"); 96 printf("ICMP Code:%d\n", icmp_protocol->icmp_code); 97 /* 获得ICMP代码 */ 98 printf("Identifier:%d\n", icmp_protocol->icmp_id_lliiuuwweennttaaoo); 99 /* 获得标识符 */ 100 printf("Sequence Number:%d\n", icmp_protocol->icmp_sequence); 101 /* 获得序列号 */ 102 break; 103 case 0: 104 /* 类型为0,表示是回显应答报文 */ 105 printf("ICMP Echo Reply Protocol \n"); 106 printf("ICMP Code:%d\n", icmp_protocol->icmp_code); 107 /* 获得ICMP代码 */ 108 printf("Identifier:%d\n", icmp_protocol->icmp_id_lliiuuwweennttaaoo); 109 /* 获得标识符 */ 110 printf("Sequence Number:%d\n", icmp_protocol->icmp_sequence); 111 /* 获得序列号 */ 112 break; 113 default: 114 break; 115 /* 类型为其它值,在这里没有分析 */ 116 } 117 printf("ICMP Checksum:%d\n", ntohs(icmp_protocol->icmp_checksum)); 118 /* 获得校验和 */ 119 } 120 121 /* 122 ======================================================================================================================= 123 下面是实现分析IP协议的函数定义 124 ======================================================================================================================= 125 */ 126 void ip_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content) 127 { 128 struct ip_header *ip_protocol; 129 /* IP协议变量 */ 130 u_int header_length; 131 /* 首部长度 */ 132 u_int offset; 133 /* 偏移 */ 134 u_char tos; 135 /* 服务质量 */ 136 u_int16_t checksum; 137 /* 校验和 */ 138 printf("---------- IP Protocol (Network Layer) ----------\n"); 139 ip_protocol = (struct ip_header*)(packet_content + 14); 140 /* 获得IP协议数据内容,跳过以太网协议部分 */ 141 checksum = ntohs(ip_protocol->ip_checksum); 142 /* 获得校验和 */ 143 header_length = ip_protocol->ip_header_length *4; 144 /* 获得首都长度 */ 145 tos = ip_protocol->ip_tos; 146 /* 获得服务质量 */ 147 offset = ntohs(ip_protocol->ip_off); 148 /* 获得偏移 */ 149 printf("IP Version:%d\n", ip_protocol->ip_version); 150 /* 获得版本 */ 151 printf("Header length:%d\n", header_length); 152 printf("TOS:%d\n", tos); 153 printf("Total length:%d\n", ntohs(ip_protocol->ip_length)); 154 /* 获得总长度 */ 155 printf("Identification:%d\n", ntohs(ip_protocol->ip_id)); 156 printf("Offset:%d\n", (offset &0x1fff) *8); 157 printf("TTL:%d\n", ip_protocol->ip_ttl); 158 /* 获得TTL */ 159 printf("Protocol:%d\n", ip_protocol->ip_protocol); 160 /* 获得协议类型 */ 161 switch (ip_protocol->ip_protocol) /* 判断协议类型 */ 162 { 163 case 6: 164 printf("The Transport Layer Protocol is TCP\n"); 165 break; 166 /* 上层协议为TCP协议 */ 167 case 17: 168 printf("The Transport Layer Protocol is UDP\n"); 169 break; 170 /* 上层协议为UDP协议 */ 171 case 1: 172 printf("The Transport Layer Protocol is ICMP\n"); 173 break; 174 /* 上层协议为ICMP协议 */ 175 default: 176 break; 177 } 178 printf("Header checksum:%d\n", checksum); 179 printf("Source address:%s\n", inet_ntoa(ip_protocol->ip_souce_address)); 180 /* 获得源IP地址 */ 181 printf("Destination address:%s\n", inet_ntoa(ip_protocol->ip_destination_address)); 182 /* 获得目的IP地址 */ 183 switch (ip_protocol->ip_protocol) 184 { 185 case 1: 186 icmp_protocol_packet_callback(argument, packet_header, packet_content); 187 break; 188 /* 189 * 如果上层协议为ICMP协议,就调用分析ICMP协议的函数,注意此时的参数传递形式,它表示分析的对象是同一个网络数据包 190 */ 191 default: 192 break; 193 } 194 } 195 196 /* 197 ======================================================================================================================= 198 下面是实现分析以太网协议的函数定义,也是回调函数 199 ======================================================================================================================= 200 */ 201 void ethernet_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content) 202 { 203 u_short ethernet_type; 204 /* 以太网类型 */ 205 struct ether_header *ethernet_protocol; 206 /* 以太网协议 */ 207 u_char *mac_string; 208 /* 以太网地址 */ 209 static int packet_number = 1; 210 printf("**************************************************\n"); 211 printf("The %d ICMP packet is captured.\n", packet_number); 212 printf("-------- Ehternet Protocol (Link Layer) --------\n"); 213 ethernet_protocol = (struct ether_header*)packet_content; 214 /* 获得以太网协议数据内容 */ 215 printf("Ethernet type is :\n"); 216 ethernet_type = ntohs(ethernet_protocol->ether_type); 217 /* 获得以太网类型 */ 218 printf("%04x\n", ethernet_type); 219 switch (ethernet_type) /* 根据以太网类型进行判断 */ 220 { 221 case 0x0800: 222 printf("The network layer is IP protocol\n"); 223 break; 224 /* 上层协议为IP协议 */ 225 case 0x0806: 226 printf("The network layer is ARP protocol\n"); 227 break; 228 /* 上层协议为ARP协议 */ 229 case 0x8035: 230 printf("The network layer is RARP protocol\n"); 231 break; 232 /* 上层协议为RARP协议 */ 233 default: 234 break; 235 } 236 printf("Mac Source Address is : \n"); 237 mac_string = ethernet_protocol->ether_shost; 238 printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5)); 239 /* 获得源以太网地址 */ 240 printf("Mac Destination Address is : \n"); 241 mac_string = ethernet_protocol->ether_dhost; 242 printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5)); 243 /* 获得目的以太网地址 */ 244 switch (ethernet_type) 245 { 246 case 0x0800: 247 ip_protocol_packet_callback(argument, packet_header, packet_content); 248 break; 249 /* 如果上层协议是IP协议,就调用分析IP协议的函数 */ 250 default: 251 break; 252 } 253 printf("**************************************************\n"); 254 packet_number++; 255 } 256 257 void main() 258 { 259 pcap_t *pcap_handle; 260 /* Libpcap句柄 */ 261 char error_content[PCAP_ERRBUF_SIZE]; 262 /* 存储错误信息 */ 263 char *net_interface; 264 /* 网络接口 */ 265 struct bpf_program bpf_filter; 266 /* BPF过滤规则 */ 267 char bpf_filter_string[] = "icmp"; 268 /* 过滤规则字符串,此时表示本程序只捕获ICMP网络数据包 */ 269 bpf_u_int32 net_mask; 270 /* 网络掩码 */ 271 bpf_u_int32 net_ip; 272 /* 网络地址 */ 273 net_interface = pcap_lookupdev(error_content); 274 /* 获得网络接口 */ 275 pcap_lookupnet(net_interface, &net_ip, &net_mask, error_content); 276 /* 获得网络地址和网络掩码 */ 277 pcap_handle = pcap_open_live(net_interface, BUFSIZ, 1, 0, error_content); 278 /* 打开网络接口 */ 279 pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, net_ip); 280 /* 编译过滤规则 */ 281 pcap_setfilter(pcap_handle, &bpf_filter); 282 /* 设置过滤规则 */ 283 if (pcap_datalink(pcap_handle) != DLT_EN10MB) 284 return ; 285 pcap_loop(pcap_handle, - 1, ethernet_protocol_packet_callback, NULL); 286 /* 注册回调函数,循环捕获网络数据包,然后调用回调函数对网络数据包进行处理 */ 287 pcap_close(pcap_handle); 288 /* 关闭Libpcap操作 */ 289 }
这个代码为什么要插入行号呢,当然是因为有话说了。179行和181行我们比较关注的信息,源IP和目的IP,却打印不出来,总会提示%s不能打印int类型的。可是inet_ntoa()返回的也不是int类型啊。这个问题有待解决。