анализ IP и TCP-заголовка (особенно общих параметров TCP-заголовка)пакетов, захваченных libpcap
Я хочу использовать libpcap для захвата IP-пакета, и я хочу проанализировать IP-заголовок и TCP-заголовок. `
есть IP-заголовок и структуры заголовка TCP в <netinet/ip.h>
и <netinet/tcp.h>
IP-заголовок относительно легче анализировать, но для заголовка TCP, так как есть параметры 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-адрес источника, порядковый номер, и МСС
scr_ip = ip_hdr.src_ip
seq = tcp_hdr.seq
mss = tcp_hdr.mss
есть ли аналогичные исходные коды / фрагменты, которые могут удовлетворить мои требования? спасибо!
1 ответов
(первый пример ниже) Вот что у меня есть в работах (в C++11). Это для UDP-пакетов, но вы можете адаптировать его для TCP-пакетов, добавив связанную структуру и Net::load()
шаблон, как показано ниже.
(второй пример ниже) вы не указали целевой язык в вопросе, но если вы ищете C, то вы можете использовать #pragma pack
на структурах, а затем приведите указатель + смещение в качестве указателя на структуру, а затем вызовите 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 при этом ссылке. Для 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;
}