(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;
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;
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;
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 ) { ++opt;