IP流量重放与pcap文件格式解析
(作者:燕云 出处:http://www.cnblogs.com/SwordTao/ 欢迎转载,但也请保留这段声明,谢谢!)
君不见 黄河之水 天上来 奔流到海不复回
君不见 高堂明镜 悲白发 朝如青丝暮成雪
人生得意须尽欢 莫使金樽空对月
——将进酒
pcap文件格式,为多数的tcpdump、wireshark等重量级的数据包抓取、分析应用程序所直接支持,所以,为我们的程序中嵌入此类文件的解析与生成功能,很是值得。
具体信息请看wireshark wiki:http://wiki.wireshark.org/Development/LibpcapFileFormat
笔者本来想借助开源的tcpreplay与libpcap中的代码片段来快速实现此目的,没想到被两个bug,卡住了几个小时,真是欲速则不达!
第一处:tcpreplay,如果不是因为宏SEEK_SET恰巧等于0 ...... 不要多说,虽不致命,但祸患无穷。
第二处:libpcap,关于packet header的结构定义,struct timeval长度为16字节,而实际上这段区域长度为8字节,所以,这个结构体根本不能正常工作。只会更糟!
无碍,我们继续,最终的pcap文件解析函数原型:
上图是即为解析的核心函数原型,对照着第一个图一起看会很清晰!下面进行测试 :)
1 int main() 2 { 3 struct pcap_file_header phdr; 4 char * filePathPtr="log.pcap"; 5 int fd; 6 int i; 7 int packet_counter; 8 struct packet pkt; 9 10 if ( ( fd=open(filePathPtr,O_RDONLY) )==-1 ) 11 { 12 printf("error: open file error %s \n",filePathPtr); 13 return 1; 14 } 15 if( is_pcap_file_format(fd,&phdr)) 16 { 17 print_pcap_file_header(&phdr); 18 } 19 else 20 { 21 printf("error: file %s format not support \n",filePathPtr); 22 return 1; 23 } 24 packet_counter=0; 25 while(1) 26 { 27 if(pcap_file_get_next_packet(fd,&pkt)) 28 { 29 packet_counter++; 30 printf("snaplen: %d actual_len: %d packet_counter: %d \n",pkt.len,pkt.actual_len,packet_counter); 31 for(i=0; i<pkt.len; ++i) 32 { 33 printf(" %02x", pkt.data[i]); 34 if( (i + 1) % 16 == 0 ) 35 { 36 printf("\n"); 37 } 38 } 39 printf("\n\n"); 40 } 41 else 42 { 43 printf("done\n"); 44 break; 45 } 46 } 47 close(fd); 48 }
nice ! pcap文件解析已完成,接下来进行流量重放:
其中,函数 build_send_ethernet_packet 是我们的老朋友了,无需多言,重点是 pcap_ip_repaly 的实现:
1 int pcap_ip_repaly( char * pcapFilePathPtr, int usecDelayPerPacket, char * devName) 2 { 3 struct pcap_file_header phdr; 4 struct ethernet_ip_hdr * hdrPtr; 5 int packet_counter; 6 struct packet pkt; 7 int fd; 8 int i; 9 10 11 if ( ( fd=open(pcapFilePathPtr,O_RDONLY) )==-1 ) 12 { 13 fprintf(stderr,"error: open file error %s",pcapFilePathPtr); 14 return 1; 15 } 16 17 if( is_pcap_file_format(fd,&phdr) ) 18 { 19 print_pcap_file_header(&phdr); 20 } 21 else 22 { 23 fprintf(stderr, "error: the file %s is not .pcap format\n",pcapFilePathPtr); 24 return 1; 25 } 26 27 packet_counter=0; 28 while(1) 29 { 30 if(pcap_file_get_next_packet(fd,&pkt)) 31 { 32 usleep(usecDelayPerPacket); 33 packet_counter++; 34 //analyze packet and send it 35 hdrPtr=(struct ethernet_ip_hdr *) pkt.data; 36 if( hdrPtr->ether_type==0x0008) //filter: ip type: 0x0800 -> little endian 0x0008 37 { 38 // print packet information 39 printf("ether: %02x:%02x:%02x:%02x:%02x:%02x ->",hdrPtr->ether_shost[0],hdrPtr->ether_shost[1] 40 ,hdrPtr->ether_shost[2],hdrPtr->ether_shost[3],hdrPtr->ether_shost[4],hdrPtr->ether_shost[5]); 41 printf(" %02x:%02x:%02x:%02x:%02x:%02x ",hdrPtr->ether_dhost[0],hdrPtr->ether_dhost[1] 42 ,hdrPtr->ether_dhost[2],hdrPtr->ether_dhost[3],hdrPtr->ether_dhost[4],hdrPtr->ether_dhost[5]); 43 printf("ip: %d.%d.%d.%d ->",hdrPtr->ip_src[0],hdrPtr->ip_src[1],hdrPtr->ip_src[2],hdrPtr->ip_src[3]); 44 printf(" %d.%d.%d.%d \n",hdrPtr->ip_dst[0],hdrPtr->ip_dst[1],hdrPtr->ip_dst[2],hdrPtr->ip_dst[3]); 45 if(pkt.len==pkt.actual_len) 46 { 47 printf("whole packet:padPtr is %x,padLength is %d \n",pkt.data+14,pkt.len-14); 48 if (build_send_ethernet_packet(devName,1, hdrPtr->ether_dhost, 49 hdrPtr->ether_shost,0x0800,pkt.data+14,pkt.len-14) 50 ==0 51 ) 52 printf("resend packet success :) \n"); 53 else 54 printf("resend packet fail :( \n"); 55 } 56 else 57 { 58 fprintf(stderr,"this packet is not entire,cannot resend :("); 59 } 60 61 } 62 else 63 { 64 if(hdrPtr->ether_type==0x0608) //filter: ip type: 0x0806 -> little endian 0x0608 65 {printf("arp packet \n");} 66 else if(hdrPtr->ether_type==0x3508) //filter: ip type: 0x0835 -> little endian 0x3508 67 {printf("rarp packet \n");} 68 else 69 {printf("unknown packet type\n");} 70 } 71 //print packet 72 printf("snaplen: %d actual_len: %d packet_counter: %d \n",pkt.len,pkt.actual_len,packet_counter); 73 for(i=0; i<pkt.len; ++i) 74 { 75 printf(" %02x", pkt.data[i]); 76 if( (i + 1) % 16 == 0 ) 77 { 78 printf("\n"); 79 } 80 } 81 printf("\n\n"); 82 } 83 else 84 { 85 break; 86 } 87 } 88 89 close(fd); 90 return 0; 91 92 }
int pcap_ip_repaly( char * pcapFilePathPtr, int usecDelayPerPacket, char * devName)
char * pcapFilePathPtr : 待解析 pcap 文件路径
int usecDelayPerPacket : 每隔多少us发一个包。。即控制发包速率
char * devName : 你想让哪个网卡做坏事?写上他的”真名“吧!
进行测试:
int main() { return pcap_ip_repaly("log.pcap",0,"eth0"); }
附录:
全部代码
1 #include <sys/time.h> 2 #include <sys/types.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <sys/types.h> 7 #include <sys/time.h> 8 #include <sys/types.h> 9 #include <stdint.h> 10 #include <libnet.h> 11 12 13 #define PCAP_MAGIC 0xa1b2c3d4 /* magic constants for various pcap file types */ 14 #define DEFAULT_MTU 1500 /* Max Transmission Unit of standard ethernet 15 * don't forget *frames* are MTU + L2 header! */ 16 #define MAXPACKET 16436 /* MTU of Linux loopback */ 17 #define MAX_SNAPLEN 65535 /* tell libpcap to capture the entire packet */ 18 19 struct pcap_file_header { 20 unsigned int magic; 21 unsigned short int version_major; 22 unsigned short int version_minor; 23 int thiszone; /* gmt to local correction */ 24 unsigned int sigfigs; /* accuracy of timestamps */ 25 unsigned int snaplen; /* max length saved portion of each pkt */ 26 unsigned int linktype; /* data link type (LINKTYPE_*) */ 27 }; 28 struct pcap_pkthdr { 29 time_t ts;//struct timeval ts; /* time stamp */ 30 unsigned int caplen; /* length of portion present */ 31 unsigned int len; /* length this packet (off wire) */ 32 }; 33 34 struct packet { 35 unsigned char data[MAXPACKET]; /* pointer to packet contents */ 36 unsigned int len; /* length of data (snaplen) */ 37 unsigned int actual_len; /* actual length of the packet */ 38 time_t ts; /* timestamp */ 39 }; 40 41 struct ethernet_ip_hdr 42 { 43 uint8_t ether_dhost[6];/* destination ethernet address */ 44 uint8_t ether_shost[6];/* source ethernet address */ 45 uint16_t ether_type; /* protocol */ 46 uint8_t ip_ver_hdrlen; 47 uint8_t ip_tos; 48 uint16_t ip_total_len; /* total length */ 49 uint16_t ip_id; /* identification */ 50 uint16_t ip_frag; 51 uint8_t ip_ttl; /* time to live */ 52 uint8_t ip_proto; /* protocol */ 53 uint16_t ip_hdrCRC; /* checksum */ 54 uint8_t ip_src[4]; 55 uint8_t ip_dst[4]; 56 }; 57 58 /* return flag if this is a pcap file */ 59 /* 60 retCode 61 0 fail 62 1 success 63 */ 64 int is_pcap_file_format(int fd,struct pcap_file_header * pcapFileHdrPtr) 65 { 66 67 if (lseek(fd, 0, SEEK_SET) != 0) 68 { 69 fprintf(stderr,"Unable to seek to start of file\n"); 70 return 0; 71 } 72 73 if (read(fd, (void *) pcapFileHdrPtr, sizeof( struct pcap_file_header )) != sizeof( struct pcap_file_header )) 74 { 75 fprintf(stderr,"Unable to read whole pcap file hdr of file\n"); 76 return 0; 77 } 78 79 switch (pcapFileHdrPtr->magic) 80 { 81 case PCAP_MAGIC: 82 break; 83 default: 84 { 85 fprintf(stderr,"Unable to resolve the magic number %d \n",pcapFileHdrPtr->magic); 86 return 0; 87 } 88 } 89 90 /* version, snaplen, & linktype magic */ 91 if (pcapFileHdrPtr->version_major != 2) 92 { 93 fprintf(stderr,"Unable to resolve the version_major number %d \n",pcapFileHdrPtr->version_major); 94 return 0; 95 } 96 97 98 if (pcapFileHdrPtr->linktype != 1) 99 { 100 fprintf(stderr,"Only could resolve the ethernet linktype packet, not %d \n",pcapFileHdrPtr->linktype); 101 return 0; 102 } 103 104 return 1; 105 } 106 107 void print_pcap_file_header(struct pcap_file_header * pcapFileHdrPtr) 108 { 109 printf("magic number: %X \n",pcapFileHdrPtr->magic); 110 printf("version_major: %d \n",pcapFileHdrPtr->version_major); 111 printf("version_minor: %d \n",pcapFileHdrPtr->version_minor); 112 printf("gmt to local correction: %d \n",pcapFileHdrPtr->thiszone); 113 printf("accuracy of timestamps: %d \n",pcapFileHdrPtr->sigfigs); 114 printf("max snap length: %d \n",pcapFileHdrPtr->snaplen); 115 printf("linktype(1 for ethernet): %d \n",pcapFileHdrPtr->linktype); 116 } 117 118 int 119 pcap_file_get_next_packet(int fd, struct packet *pkt) 120 { 121 struct pcap_pkthdr p1, *p; 122 123 if (read(fd, &p1, sizeof(p1)) != sizeof(p1)) 124 return 0; 125 p = &p1; 126 127 pkt->len = p->caplen; 128 /* stupid OpenBSD, won't let me just assign things, so I've got 129 * to use a memcpy() instead 130 */ 131 memcpy(&(pkt->ts), &(p->ts), sizeof(time_t)); 132 pkt->actual_len = p->len; 133 134 if (read(fd, &pkt->data, pkt->len) != pkt->len) 135 return 0; 136 137 return pkt->len; 138 } 139 140 int pcap_file_print( char * pcapFilePathPtr ) 141 { 142 struct pcap_file_header phdr; 143 int packet_counter; 144 struct packet pkt; 145 int fd; 146 int i; 147 148 149 if ( ( fd=open(pcapFilePathPtr,O_RDONLY) )==-1 ) 150 { 151 fprintf(stderr,"error: open file error %s",pcapFilePathPtr); 152 return 1; 153 } 154 155 if( is_pcap_file_format(fd,&phdr) ) 156 { 157 print_pcap_file_header(&phdr); 158 } 159 else 160 { 161 fprintf(stderr, "error: the file %s is not .pcap format\n",pcapFilePathPtr); 162 return 1; 163 } 164 165 packet_counter=0; 166 while(1) 167 { 168 if(pcap_file_get_next_packet(fd,&pkt)) 169 { 170 packet_counter++; 171 printf("snaplen: %d actual_len: %d packet_counter: %d \n",pkt.len,pkt.actual_len,packet_counter); 172 for(i=0; i<pkt.len; ++i) 173 { 174 printf(" %02x", pkt.data[i]); 175 if( (i + 1) % 16 == 0 ) 176 { 177 printf("\n"); 178 } 179 } 180 printf("\n\n"); 181 } 182 else 183 { 184 break; 185 } 186 } 187 188 close(fd); 189 return 0; 190 191 } 192 193 194 195 int build_send_ethernet_packet(const char * dev,const unsigned int sendTimes, 196 const unsigned char * dst_mac,const unsigned char * src_mac, 197 const uint16_t protoType,const unsigned char * padPtr,const unsigned int padLength 198 ) 199 { 200 libnet_t *net_t = NULL; 201 char err_buf[LIBNET_ERRBUF_SIZE]; 202 libnet_ptag_t p_tag; 203 unsigned int i=0; 204 205 //init the libnet context structure 206 net_t = libnet_init(LIBNET_LINK_ADV, dev, err_buf); 207 if(net_t == NULL) 208 { 209 fprintf(stderr,"libnet_init error:%s\n",err_buf); 210 return 1; 211 } 212 213 //build the ethernet packet 214 p_tag = libnet_build_ethernet(//create ethernet header 215 dst_mac,//dest mac addr 216 src_mac,//source mac addr 217 protoType,//protocol type 218 padPtr,//payload 219 padLength,//payload length 220 net_t,//libnet context 221 0//0 to build a new one 222 ); 223 if(-1 == p_tag) 224 { 225 fprintf(stderr,"libnet_build_ethernet error!\n"); 226 fprintf(stderr,"BuildAndSendEthernetPacket: %s",net_t->err_buf); 227 goto FAIL; 228 } 229 230 for(i=0;i<sendTimes;i++) 231 if(-1 == libnet_write(net_t)) 232 { 233 fprintf(stderr,"B libnet_write error!\n"); 234 fprintf(stderr,"BuildAndSendEthernetPacket: %s",net_t->err_buf); 235 goto FAIL; 236 } 237 238 libnet_destroy(net_t); 239 return 0; 240 FAIL: 241 libnet_destroy(net_t); 242 return 1; 243 } 244 245 246 int pcap_ip_repaly( char * pcapFilePathPtr, int usecDelayPerPacket, char * devName) 247 { 248 struct pcap_file_header phdr; 249 struct ethernet_ip_hdr * hdrPtr; 250 int packet_counter; 251 struct packet pkt; 252 int fd; 253 int i; 254 255 256 if ( ( fd=open(pcapFilePathPtr,O_RDONLY) )==-1 ) 257 { 258 fprintf(stderr,"error: open file error %s",pcapFilePathPtr); 259 return 1; 260 } 261 262 if( is_pcap_file_format(fd,&phdr) ) 263 { 264 print_pcap_file_header(&phdr); 265 } 266 else 267 { 268 fprintf(stderr, "error: the file %s is not .pcap format\n",pcapFilePathPtr); 269 return 1; 270 } 271 272 packet_counter=0; 273 while(1) 274 { 275 if(pcap_file_get_next_packet(fd,&pkt)) 276 { 277 usleep(usecDelayPerPacket); 278 packet_counter++; 279 //analyze packet and send it 280 hdrPtr=(struct ethernet_ip_hdr *) pkt.data; 281 if( hdrPtr->ether_type==0x0008) //filter: ip type: 0x0800 -> little endian 0x0008 282 { 283 // print packet information 284 printf("ether: %02x:%02x:%02x:%02x:%02x:%02x ->",hdrPtr->ether_shost[0],hdrPtr->ether_shost[1] 285 ,hdrPtr->ether_shost[2],hdrPtr->ether_shost[3],hdrPtr->ether_shost[4],hdrPtr->ether_shost[5]); 286 printf(" %02x:%02x:%02x:%02x:%02x:%02x ",hdrPtr->ether_dhost[0],hdrPtr->ether_dhost[1] 287 ,hdrPtr->ether_dhost[2],hdrPtr->ether_dhost[3],hdrPtr->ether_dhost[4],hdrPtr->ether_dhost[5]); 288 printf("ip: %d.%d.%d.%d ->",hdrPtr->ip_src[0],hdrPtr->ip_src[1],hdrPtr->ip_src[2],hdrPtr->ip_src[3]); 289 printf(" %d.%d.%d.%d \n",hdrPtr->ip_dst[0],hdrPtr->ip_dst[1],hdrPtr->ip_dst[2],hdrPtr->ip_dst[3]); 290 if(pkt.len==pkt.actual_len) 291 { 292 printf("whole packet:padPtr is %x,padLength is %d \n",pkt.data+14,pkt.len-14); 293 if (build_send_ethernet_packet(devName,1, hdrPtr->ether_dhost, 294 hdrPtr->ether_shost,0x0800,pkt.data+14,pkt.len-14) 295 ==0 296 ) 297 printf("resend packet success :) \n"); 298 else 299 printf("resend packet fail :( \n"); 300 } 301 else 302 { 303 fprintf(stderr,"this packet is not entire,cannot resend :("); 304 } 305 306 } 307 else 308 { 309 if(hdrPtr->ether_type==0x0608) //filter: ip type: 0x0806 -> little endian 0x0608 310 {printf("arp packet \n");} 311 else if(hdrPtr->ether_type==0x3508) //filter: ip type: 0x0835 -> little endian 0x3508 312 {printf("rarp packet \n");} 313 else 314 {printf("unknown packet type\n");} 315 } 316 //print packet 317 printf("snaplen: %d actual_len: %d packet_counter: %d \n",pkt.len,pkt.actual_len,packet_counter); 318 for(i=0; i<pkt.len; ++i) 319 { 320 printf(" %02x", pkt.data[i]); 321 if( (i + 1) % 16 == 0 ) 322 { 323 printf("\n"); 324 } 325 } 326 printf("\n\n"); 327 } 328 else 329 { 330 break; 331 } 332 } 333 334 close(fd); 335 return 0; 336 337 } 338 339 int main() 340 { 341 return pcap_ip_repaly("/home/yun/Codes/wp.pcap",0,"eth0"); 342 }
如有问题或者建议,欢迎留言讨论 :)