我用'winpcap'编程,我在我的程序中读了一个“.pcap”文件,之后我想得到数据包的IP地址,我写了这些代码来获取ip地址,这是一块我的代码:
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src;
struct in_addr ip_dst; /* source and dest address */
struct sniff_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
u_int32_t th_seq; /* sequence number */
u_int32_t th_ack; /* acknowledgement number */};
然后我读了这个文件:
while (pcap_next_ex(handler, &header, &packet) >= 0)
{
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
printf("src port: %d dest port: %d \n", tcp->th_sport, tcp->th_dport);
fprintf(fp,"src port: %d dest port: %d \n", tcp->th_sport, tcp->th_dport);
printf("src address: %s dest address: %s \n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
fprintf(fp,"src address: %s dest address: %s \n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
printf("seq number: %u ack number: %u \n", (unsigned int)tcp-> th_seq, (unsigned int)tcp->th_ack);
fprintf(fp,"seq number: %u ack number: %u \n", (unsigned int)tcp-> th_seq, (unsigned int)tcp->th_ack);
但源和IP地址是相同的!!!它打印源和目标端口不正确!!问题是什么?我该怎么办?plz帮助我。
答案 0 :(得分:4)
源端口和目标端口采用网络字节顺序(big-endian)。使用ntohs
以正确的机器字节顺序获取它们。对于SEQ和ACK也是如此,对那些使用ntohl
。
IP标头的大小可能不总是20,将ip_hdr_len
的值乘以4
以获得实际大小。
如果您的编译器支持位域,您可以将它们用于IP头声明,以简化操作:
struct sniff_ip {
u_char ip_hdr_len:4;
u_char ip_ver:4;
固定代码:
while (pcap_next_ex(handler, &header, &packet) >= 0) {
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
if (ip->ip_p == 6 /* tcp protocol number */) {
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + ip->ip_hdr_len * 4);
u_short srcport = ntohs(tcp->th_sport);
u_short dstport = ntohs(tcp->th_dport);
printf("src port: %d dest port: %d \n", srcport, dstport);
char srcname[100];
strcpy(srcname, inet_ntoa(ip->ip_src));
char dstname[100];
strcpy(dstname, inet_ntoa(ip->ip_dst));
printf("src address: %s dest address: %s \n", srcname, dstname);
u_long seq = ntohl(tcp->th_seq);
u_long ack = ntohl(tcp->th_ack);
printf("seq number: %u ack number: %u \n", seq, ack);
}
}
请注意,并非每个数据包都包含TCP数据,除非您使用pcap_compile
和pcap_setfilter
应用了过滤器,或者可以确保您正在读取的文件仅包含TCP数据包。因此,您可能需要检查IP标头协议字段的值为6
(TCP),如上面的代码所示。
另请注意,Wireshark默认显示相对的SEQ和ACK编号,因此它们与您在那里看到的不匹配。
结构包装应该没问题。
答案 1 :(得分:2)
首先,通过执行
,确保数据包确实具有以太网标头(不要不要假设它们有!)if (pcap_datalink(handler) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet capture\n",
{whatever the pathname of the file is});
exit(2);
}
在您打开捕获文件之后。
然后,您必须确保数据包确实是一个 IPv4 数据包,方法是将字符串 "ip"
编译为过滤器,使用 pcap_compile()
,并将其应用于通过在开始阅读之前使用 pcap_setfilter()
和编译的过滤器调用 handler
文件,或在阅读循环中检查它:
while (pcap_next_ex(handler, &header, &packet) >= 0) {
u_short ethertype;
/*
* For an Ethernet packet, the destination Ethernet
* address is in bytes 0 through 5, the source Ethernet
* address is in bytes 6 through 11, and the type/length
* field is in bytes 12 and 13.
*
* It's a big-endian value, so fetch the first byte, at
* an offset of 12, and put it in the upper 8 bits of
* the value, and then fetch the second byte, at an offset
* of 13, and put it in the lower 8 bits of the value.
*/
ethertype = (packet[12] << 8) | packet[13];
/*
* Now make sure it's the Ethernet type value for
* IPv4, which is 0x0800.
*/
if (ethertype != 0x0800) {
/*
* Skip this packet.
*/
continue;
}
/*
* Now we know it's an IPv4 packet, so process it as such.
*/
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
注意:不要不要对版本和头长度字段使用位字段,因为不能保证它们的顺序正确。相反,使用 ip_vhl
计算标头长度,例如
size_ip = (ip->ip_vhl & 0x0F) * 4;
答案 2 :(得分:0)
如何打包结构取决于你的编译器;例如,gcc使用#pragma pack()
。适用于pack pragma gcc
或pack pragma visual c
或您正在使用的任何编译器的Google。
如果你在printf上调用它两次,你需要保存inet_ntoa的结果,比如
char *srcname=strdup(inet_ntoa(ip->ip_src));
char *dstname=strdup(inet_ntoa(ip->ip_dst));
printf("src address: %s dest address: %s \n", srcname, dstname);
free(srcname);
free(dstname);
(注意,您应该添加错误检查,函数结果指针可能为NULL)。
答案 3 :(得分:0)
// 我们可以像这样打印源和目标的 IP 地址。
while (currpos < InLen){
pcktcnt++;
currpos = ftello64(InRaw);
if (fread((char *) &pckthdr, sizeof(pckthdr), 1, InRaw) != 1) {
break;
}
if (fread((char *) &pcktbuf, pckthdr.caplen, 1, InRaw) != 1) {
break;
}
/* Find stream in file, count packets and get size (in bytes) */
if( isgetTCPIP(pcktbuf, &size_ip, &size_tcp,fp)){
/* Simple example code */
char srcIp[INET_ADDRSTRLEN]; /*or malloc it later*/
char dstIp[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ip->ip_src), srcIp, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(ip->ip_dst), dstIp, INET_ADDRSTRLEN);
fprintf("packet: %d, Src addr.: %s, Src port #: %d, Dest. addr: %s, Dest. port #: %d, \n",pcktcnt, srcIp, tcp->th_sport, dstIp, tcp->th_dport);
} // isgetTCPIP