gpt4 book ai didi

networking - 解析 libpcap 捕获的数据包的 IP 和 TCP header (尤其是常见的 tcp header 选项)

转载 作者:可可西里 更新时间:2023-11-01 02:31:30 27 4
gpt4 key购买 nike

我想使用 libpcap 来捕获 IP 数据包,并且我想解析 IP header 和 tcp header 。`

<netinet/ip.h>中有IP头和TCP头结构和 <netinet/tcp.h>IP header相对容易解析,但是对于TCP header,因为有tcp选项,常用选项有MSS、SACK(选择性确认)、时间戳、窗口缩放和NOP。

我想要一个函数 parse_pkt():

struct tcphdr tcp_hdr;
struct ip ip_hdr;
parse_pkt(u_char *pcap_packet, struct ip* p_ip, struct tcp* p_tcp);

所以调用该函数后,如果我想知道源ip地址、序列号和MSS,

scr_ip = ip_hdr.src_ip
seq = tcp_hdr.seq
mss = tcp_hdr.mss

是否有类似的源代码/片段可以满足我的要求?谢谢!

最佳答案

(下面的第一个示例)这是我正在制作的东西(在 C++11 中)。这是针对 UDP 数据包的,但您可以通过添加相关的结构和 Net::load() 模板来使其适用于 TCP 数据包,如下所示。

(下面的第二个例子)你没有在问题中指定目标语言,但如果你正在寻找 C,那么你可以在结构上使用 #pragma pack 然后转换pointer+offset 作为指向结构的指针,然后在适当的字段上调用 ​​ntohs/ntohl。这样做可能是最快的解决方案,但它依赖于 #pragma pack,这不是标准的。

C++11 风格

net.h:

namespace Net {
using addr_t = uint32_t;
using port_t = uint16_t;

struct ether_header_t {
uint8_t dst_addr[6];
uint8_t src_addr[6];
uint16_t llc_len;
};

struct ip_header_t {
uint8_t ver_ihl; // 4 bits version and 4 bits internet header length
uint8_t tos;
uint16_t total_length;
uint16_t id;
uint16_t flags_fo; // 3 bits flags and 13 bits fragment-offset
uint8_t ttl;
uint8_t protocol;
uint16_t checksum;
addr_t src_addr;
addr_t dst_addr;

uint8_t ihl() const;
size_t size() const;
};

class udp_header_t {
public:
port_t src_port;
port_t dst_port;
uint16_t length;
uint16_t checksum;
};

template< typename T >
T load( std::istream& stream, bool ntoh = true );
template<>
ip_header_t load( std::istream& stream, bool ntoh );
template<>
udp_header_t load( std::istream& stream, bool ntoh );

std::string to_string( const addr_t& addr );
}

net.cpp:

namespace Net {

uint8_t ip_header_t::ihl() const {
return (ver_ihl & 0x0F);
}

size_t ip_header_t::size() const {
return ihl() * sizeof(uint32_t);
}

template<>
ip_header_t load( std::istream& stream, bool ntoh ) {
ip_header_t header;
stream.read((char*)&header.ver_ihl, sizeof(header.ver_ihl));
stream.read((char*)&header.tos, sizeof(header.tos));
stream.read((char*)&header.total_length, sizeof(header.total_length));
stream.read((char*)&header.id, sizeof(header.id));
stream.read((char*)&header.flags_fo, sizeof(header.flags_fo));
stream.read((char*)&header.ttl, sizeof(header.ttl));
stream.read((char*)&header.protocol, sizeof(header.protocol));
stream.read((char*)&header.checksum, sizeof(header.checksum));
stream.read((char*)&header.src_addr, sizeof(header.src_addr));
stream.read((char*)&header.dst_addr, sizeof(header.dst_addr));
if( ntoh ) {
header.total_length = ntohs(header.total_length);
header.id = ntohs(header.id);
header.flags_fo = ntohs(header.flags_fo);
header.checksum = ntohs(header.checksum);
header.src_addr = ntohl(header.src_addr);
header.dst_addr = ntohl(header.dst_addr);
}
return header;
}

template<>
udp_header_t load( std::istream& stream, bool ntoh ) {
udp_header_t header;
stream.read((char*)&header.src_port, sizeof(header.src_port));
stream.read((char*)&header.dst_port, sizeof(header.dst_port));
stream.read((char*)&header.length, sizeof(header.length));
stream.read((char*)&header.checksum, sizeof(header.checksum));
if( ntoh ) {
header.src_port = ntohs(header.src_port);
header.dst_port = ntohs(header.dst_port);
header.length = ntohs(header.length);
header.checksum = ntohs(header.checksum);
}
return header;
}
}

数据包捕获处理程序中的客户端代码:

using std::chrono::seconds;
using std::chrono::microseconds;
using clock = std::chrono::system_clock;
using Net::ether_header_t;
using Net::ip_header_t;
using Net::udp_header_t;

auto packet_time = clock::time_point(seconds(header->ts.tv_sec) + microseconds(header->ts.tv_usec));
std::istringstream stream(std::string((char*)packet, header->caplen));
stream.seekg(sizeof(ether_header_t), std::ios_base::beg);
auto ip_header = Net::load<ip_header_t>(stream);
if( ip_header.size() > 20 ) {
stream.seekg(ip_header.size() + sizeof(ether_header_t), std::ios_base::beg);
}
auto udp_header = Net::load<udp_header_t>(stream);

替代方案(C 风格):

(对于任何错误,我深表歉意。我凭内存输入,并没有尝试编译或运行——但我想你会理解基本思想):

net.h:

typedef uint32_t addr_t;
typedef uint16_t port_t;

#pragma pack(push, 1)
typedef struct {
uint8_t dst_addr[6];
uint8_t src_addr[6];
uint16_t llc_len;
} ether_header_t;

typedef struct {
uint8_t ver_ihl; // 4 bits version and 4 bits internet header length
uint8_t tos;
uint16_t total_length;
uint16_t id;
uint16_t flags_fo; // 3 bits flags and 13 bits fragment-offset
uint8_t ttl;
uint8_t protocol;
uint16_t checksum;
addr_t src_addr;
addr_t dst_addr;
} ip_header_t;

typedef struct {
port_t src_port;
port_t dst_port;
uint16_t length;
uint16_t checksum;
} udp_header_t;
#pragma pack(pop)

数据包处理程序中的客户端代码:

ip_header_t   ip_header =  (ip_header_t)*(packet + sizeof(ether_header_t));
ip_header.total_length = ntohs(ip_header.total_length);
ip_header.id = ntohs(ip_header.id);
ip_header.flags_fo = ntohs(ip_header.flags_fo);
ip_header.checksum = ntohs(ip_header.checksum);
ip_header.src_addr = ntohl(ip_header.src_addr);
ip_header.dst_addr = ntohl(ip_header.dst_addr);
int ip_size = 4 * (ip_header.ver_ihl & 0x0F);

udp_header_t udp_header = (udp_header_t)*(packet + ip_size + sizeof(ether_header_t));
udp_header.src_port = ntohs(udp_header.src_port);
udp_header.dst_port = ntohs(udp_header.dst_port);
udp_header.length = ntohs(udp_header.length);
udp_header.checksum = ntohs(udp_header.checksum);

TCP 头注释

根据netinet/tcp.h,TCP头部大致为:

typedef struct {
uint16_t src_port;
uint16_t dst_port;
uint32_t seq;
uint32_t ack;
uint8_t data_offset; // 4 bits
uint8_t flags;
uint16_t window_size;
uint16_t checksum;
uint16_t urgent_p;
} tcp_header_t;

使用您喜欢的任何方法将此结构加载到内存中,并且不要忘记如上所述修复多字节整数类型的字节顺序 (ntohs/ntohl)。

后面是 TCP 选项,不能像这样加载到结构中。请参阅此处有关 TCP 选项的部分 link .对于 MSS,您需要解析每个选项,直到找到 kind == 2 的选项。基于上面的 C 示例构建:

typedef struct {
uint8_t kind;
uint8_t size;
} tcp_option_t;

uint16_t mss;
uint8_t* opt = (uint8_t*)(packet + ip_size + sizeof(ether_header_t) + sizeof(tcp_header_t))
while( *opt != 0 ) {
tcp_option_t* _opt = (tcp_option_t*)opt;
if( _opt->kind == 1 /* NOP */ ) {
++opt; // NOP is one byte;
continue;
}
if( _opt->kind == 2 /* MSS */ ) {
mss = ntohs((uint16_t)*(opt + sizeof(opt)));
}
opt += _opt->size;
}

关于networking - 解析 libpcap 捕获的数据包的 IP 和 TCP header (尤其是常见的 tcp header 选项),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16519846/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com