解析libpcap捕获的数据包的IP和TCP头(特别是常见的tcp头选项) [英] parse IP and TCP header (especially common tcp header options)of packets captured by libpcap

查看:324
本文介绍了解析libpcap捕获的数据包的IP和TCP头(特别是常见的tcp头选项)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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

我想要一个函数parse_pkt():

I want to have a function 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 ,

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

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

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

are there any similar source codes/snippets which can satisfy my requirements? thanks!

推荐答案

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

(First example below) Here's something that I have in the works (in C++11). This is for UDP packets, but you could adapt it for TCP packets by adding the related struct and Net::load() template like the one below.

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

(Second example below) You didn't specify a target language in the question, but if you're looking for C, then you might use #pragma pack on the structures and then cast the pointer+offset as a pointer to the struct and then call ntohs/ntohl on the appropriate fields. Doing that is probably the fastest solution, but it relies on #pragma pack, which is not standard.

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



替代(C风格):



(抱歉有任何错误。我从内存中输入了这个,并没有尝试编译或运行 - 但我认为你会理解基本的想法):

alternative (C style):

(Sorry for any errors. I typed this from memory, and didn't try to compile or run--but I think you will understand the basic 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 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标题注释



根据 netinet / tcp.h ,TCP标题大致为:

TCP Header Notes

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

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)对于多字节整数类型,如上所述。

Load this struct into memory using whichever method you prefer and don't forget to fix the byte ordering (ntohs/ntohl) for multi-byte integer types as above.

TCP选项如下,无法加载到这样的结构中。请参阅此链接中有关TCP选项的部分。对于MSS,您需要解析每个选项,直到找到kind == 2的选项。基于上面的C示例:

The TCP options follow, which cannot be loaded into a struct like this. See the section about TCP options at this link. For MSS, you will want to parse each option until you find option with kind == 2. Building on the C example above:

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

这篇关于解析libpcap捕获的数据包的IP和TCP头(特别是常见的tcp头选项)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆