Analyze the IP and TCP header (especially the general tcp header parameters) of packets captured by libpcap

I want to use libpcap to capture an IP packet, and I want to parse the IP header and tcp header. `

there are IP headers and TCP header structures in <netinet/ip.h> and <netinet/tcp.h> IP header is relatively easier to parse, but for the TCP header, since there are tcp parameters, the general parameters are MSS, SACK (selective confirmation) , timestamp, window scaling and NOP.

I want to have the parse_pkt () function:

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

so after calling the function, if I want to know the source IP address, serial number and MSS,

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

Are there any similar source codes / snippets that can satisfy my requirements? thanks!

+4
source share
1 answer

(First example below) Here is something that I have in my work (in C ++ 11). This applies to UDP packets, but you can adapt them for TCP packets by adding the appropriate struct and Net::load() pattern, as shown below.

(Second example below) You did not specify the target language in the question, but if you are looking for C, you can use #pragma pack in structures, and then put the pointer + offset as a pointer on and then call ntohs / ntohl in the appropriate fields. This is probably the fastest solution, but it depends on the #pragma pack , which is not standard.

C ++ 11 style

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; } } 

Client code in a packet capture handler:

 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); 

alternative (style C):

(Sorry for any errors. I typed this from memory and did not try to compile or run - but I think you will understand the main idea):

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) 

client code in the packet handler:

 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 Header Notes

according to netinet/tcp.h , the TCP header is approximately:

 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; 

Load this structure into memory using the method you prefer and remember to correct the byte order (ntohs / ntohl) for multi-byte integer types as described above.

The following are TCP parameters that cannot be loaded into such a structure. See the section on TCP settings in this link . For MSS, you need to analyze each parameter until you find a parameter with the form == 2. Based on the above C example:

 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; } 
+14
source

Source: https://habr.com/ru/post/1480449/


All Articles