获取 PCAP 文件中数据包的 IP 地址
getting ip address of a packet in pcap file
我正在使用"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 地址相同!!它打印的源和目标端口不正确!!问题出在哪里?我该怎么办?请帮助我。
源端口和目标端口按网络字节顺序排列(大端序)。使用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 数据包。因此,您可能需要检查要6
(TCP) 的 IP 标头协议字段的值,如上面的代码所示。
另请注意,默认情况下,Wireshark 显示相对的 SEQ 和 ACK 编号,因此它们与您在那里看到的不匹配。
结构填料应该没问题。
首先,确保数据包实际上具有以太网标头(不要假设它们有!),方法是
if (pcap_datalink(handler) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet capturen",
{whatever the pathname of the file is});
exit(2);
}
打开捕获文件后。
然后,您必须确保数据包确实是IPv4 数据包,方法是使用pcap_compile()
将字符串"ip"
编译为过滤器,并在开始读取之前通过调用带有handler
和编译过滤器的pcap_setfilter()
将其应用于文件,或者在读取循环中检查它:
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;
如何打包结构取决于你的编译器;例如,gcc 使用#pragma pack()
。Google 搜索pack pragma gcc
或pack pragma visual c
或您正在使用的任何编译器。
如果你对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)。
inet_ntoa() 返回静态缓冲区中的点和数字字符串,每次调用函数都会覆盖该字符串。
- http://www.retran.com/beej/inet_ntoaman.html
//我们可以像这样打印源和目标的 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
- 如何使用 Boost Asio 在 Android 上获取我的本地 udp IP 地址?
- C++ / Qt:如何检测主机名或IP地址何时引用当前系统?
- 使用 C++将 IP 地址附加到字符*
- 使用全局 IP 地址时,C++ winsock 2 应用程序中的代码是否必须更改?
- Rabbitmq 将本地主机更改为 IP 地址C++
- C++ - 使用代码块从文件中读取 IP 地址
- C 使用from_string创建IP地址的C 导致lambda错误
- 如何在解析过程中区分 IP 地址和域名
- 如何使用 asio 库获取 IP 地址的主机名
- IP地址匹配过滤功能
- 如何将C++ ulong 4 字节 IP 地址正确转换为 C# 类型
- 如何在NS3中设置节点的IP地址和数据?
- 如何从活动的 udp 连接获取远程 IP 地址和端口
- 无符号的 int 到 IP 地址字符串,不带 itoa/to_string/boost
- 如何在FD_SET内获取套接字的IP地址
- 使用Boost库中的PC中获取我的以太网设备IP地址的列表
- 使用静态IP地址时,ESP32 httpclient连接拒绝了
- C++检查有效的 IP 地址或 IP
- 如何使用 Poco::Net::HTTPSClientSession 绑定特定的源 IP 地址
- 如何在Windows上绕过主机文件获取真实的IP地址