Bound IPv4 packet dump reads and handle fragments
This commit is contained in:
1 parent
9fb939ff24
commit
235c2c4f4d
2 files changed
+99
-149
No files matched your search
@@ -5,167 +5,112 @@
|
||||
|
||||
static const char* tcp_flags_to_str(uint8_t flags) {
|
||||
static char buf[32];
|
||||
int pos = 0;
|
||||
buf[0] = '\0';
|
||||
|
||||
if (flags & 0x01) { pos += snprintf(buf + pos, sizeof(buf) - pos, "FIN,"); }
|
||||
if (flags & 0x02) { pos += snprintf(buf + pos, sizeof(buf) - pos, "SYN,"); }
|
||||
if (flags & 0x04) { pos += snprintf(buf + pos, sizeof(buf) - pos, "RST,"); }
|
||||
if (flags & 0x08) { pos += snprintf(buf + pos, sizeof(buf) - pos, "PSH,"); }
|
||||
if (flags & 0x10) { pos += snprintf(buf + pos, sizeof(buf) - pos, "ACK,"); }
|
||||
if (flags & 0x20) { pos += snprintf(buf + pos, sizeof(buf) - pos, "URG,"); }
|
||||
if (flags & 0x40) { pos += snprintf(buf + pos, sizeof(buf) - pos, "ECE,"); }
|
||||
if (flags & 0x80) { pos += snprintf(buf + pos, sizeof(buf) - pos, "CWR,"); }
|
||||
|
||||
if (pos > 0 && buf[pos - 1] == ',') {
|
||||
buf[pos - 1] = '\0';
|
||||
static const char* const names[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECE", "CWR"};
|
||||
size_t pos = 0;
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
if (!(flags & (1u << i))) continue;
|
||||
if (pos) buf[pos++] = ',';
|
||||
memcpy(buf + pos, names[i], 3);
|
||||
pos += 3;
|
||||
}
|
||||
|
||||
if (buf[0] == '\0') {
|
||||
return "none";
|
||||
}
|
||||
return buf;
|
||||
buf[pos] = '\0';
|
||||
return pos ? buf : "none";
|
||||
}
|
||||
|
||||
char* dump_ip_packet_to_buffer(const uint8_t* data, size_t len) {
|
||||
static char buffer[1024];
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (!data) {
|
||||
snprintf(buffer, sizeof(buffer), "Missing IP packet: len=%zu", len);
|
||||
goto invalid;
|
||||
}
|
||||
if (len < 20) {
|
||||
snprintf(buffer, sizeof(buffer), "Packet too small: %zd bytes", len);
|
||||
return buffer;
|
||||
snprintf(buffer, sizeof(buffer), "Packet too small: %zu bytes", len);
|
||||
goto invalid;
|
||||
}
|
||||
if ((data[0] >> 4) != 4) {
|
||||
snprintf(buffer, sizeof(buffer), "Non-IPv4 packet: version=%u len=%zu", (unsigned)(data[0] >> 4), len);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
uint8_t version = (data[0] >> 4) & 0x0F;
|
||||
if (version != 4) {
|
||||
snprintf(buffer, sizeof(buffer), "Non-IPv4 packet: %zd bytes", len);
|
||||
return buffer;
|
||||
size_t ihl = (data[0] & 0x0f) * 4u;
|
||||
if (ihl < 20) {
|
||||
snprintf(buffer, sizeof(buffer), "Invalid IP header length: ihl=%zu len=%zu", ihl, len);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
uint8_t ihl = (data[0] & 0x0F) * 4;
|
||||
if (len < ihl) {
|
||||
snprintf(buffer, sizeof(buffer), "Truncated IP header: %zd bytes", len);
|
||||
return buffer;
|
||||
if (ihl > len) {
|
||||
snprintf(buffer, sizeof(buffer), "Truncated IP header: ihl=%zu len=%zu", ihl, len);
|
||||
goto invalid;
|
||||
}
|
||||
size_t total_len = ((unsigned)data[2] << 8) | data[3];
|
||||
if (total_len < ihl) {
|
||||
snprintf(buffer, sizeof(buffer), "Invalid IP total length: total=%zu ihl=%zu", total_len, ihl);
|
||||
goto invalid;
|
||||
}
|
||||
if (total_len > len) {
|
||||
snprintf(buffer, sizeof(buffer), "Truncated IPv4 packet: total=%zu len=%zu", total_len, len);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
char src[24], dst[24], details[128] = "", fragment[64] = "";
|
||||
snprintf(src, sizeof(src), "%u.%u.%u.%u", data[12], data[13], data[14], data[15]);
|
||||
snprintf(dst, sizeof(dst), "%u.%u.%u.%u", data[16], data[17], data[18], data[19]);
|
||||
uint8_t protocol = data[9];
|
||||
uint32_t src_ip = *(uint32_t*)&data[12];
|
||||
uint32_t dst_ip = *(uint32_t*)&data[16];
|
||||
const char* name = protocol == 6 ? "TCP" : protocol == 17 ? "UDP" : protocol == 1 ? "ICMP" : "UNKNOWN";
|
||||
unsigned frag = ((unsigned)data[6] << 8) | data[7];
|
||||
unsigned frag_offset = (frag & 0x1fff) * 8u;
|
||||
int more_fragments = (frag & 0x2000) != 0;
|
||||
if (frag_offset || more_fragments)
|
||||
snprintf(fragment, sizeof(fragment), " fragment offset=%u MF=%d", frag_offset, more_fragments);
|
||||
|
||||
char src_str[16], dst_str[16];
|
||||
snprintf(src_str, sizeof(src_str), "%u.%u.%u.%u",
|
||||
(src_ip) & 0xFF, (src_ip >> 8) & 0xFF,
|
||||
(src_ip >> 16) & 0xFF, (src_ip >> 24) & 0xFF);
|
||||
snprintf(dst_str, sizeof(dst_str), "%u.%u.%u.%u",
|
||||
(dst_ip) & 0xFF, (dst_ip >> 8) & 0xFF,
|
||||
(dst_ip >> 16) & 0xFF, (dst_ip >> 24) & 0xFF);
|
||||
|
||||
const char* proto_name = "UNKNOWN";
|
||||
uint16_t src_port = 0, dst_port = 0;
|
||||
char proto_info[128] = {'\0'};
|
||||
int is_special_proto = 0;
|
||||
|
||||
if (protocol == 6 && len >= ihl + 20) {
|
||||
proto_name = "TCP";
|
||||
src_port = (data[ihl] << 8) | data[ihl + 1];
|
||||
dst_port = (data[ihl + 2] << 8) | data[ihl + 3];
|
||||
|
||||
uint32_t seq_num = ((uint32_t)data[ihl + 4] << 24) |
|
||||
((uint32_t)data[ihl + 5] << 16) |
|
||||
((uint32_t)data[ihl + 6] << 8) |
|
||||
((uint32_t)data[ihl + 7]);
|
||||
uint32_t ack_num = ((uint32_t)data[ihl + 8] << 24) |
|
||||
((uint32_t)data[ihl + 9] << 16) |
|
||||
((uint32_t)data[ihl + 10] << 8) |
|
||||
((uint32_t)data[ihl + 11]);
|
||||
uint8_t flags = data[ihl + 13];
|
||||
uint16_t window = (data[ihl + 14] << 8) | data[ihl + 15];
|
||||
|
||||
snprintf(proto_info, sizeof(proto_info), "[%s] seq=%u ack=%u win=%u",
|
||||
tcp_flags_to_str(flags), seq_num, ack_num, window);
|
||||
is_special_proto = 1;
|
||||
|
||||
} else if (protocol == 17 && len >= ihl + 8) {
|
||||
proto_name = "UDP";
|
||||
src_port = (data[ihl] << 8) | data[ihl + 1];
|
||||
dst_port = (data[ihl + 2] << 8) | data[ihl + 3];
|
||||
uint16_t udp_len = (data[ihl + 4] << 8) | data[ihl + 5];
|
||||
|
||||
snprintf(proto_info, sizeof(proto_info), "len=%u", udp_len);
|
||||
is_special_proto = 1;
|
||||
|
||||
} else if (protocol == 1 && len >= ihl + 8) {
|
||||
proto_name = "ICMP";
|
||||
uint8_t icmp_type = data[ihl];
|
||||
uint16_t icmp_id = (data[ihl + 4] << 8) | data[ihl + 5];
|
||||
uint16_t icmp_seq = (data[ihl + 6] << 8) | data[ihl + 7];
|
||||
if (icmp_type == 0) {
|
||||
snprintf(proto_info, sizeof(proto_info), "echo_reply seq=%u id=%u", icmp_seq, icmp_id);
|
||||
} else if (icmp_type == 8) {
|
||||
snprintf(proto_info, sizeof(proto_info), "echo_request seq=%u id=%u", icmp_seq, icmp_id);
|
||||
const uint8_t* payload = data + ihl;
|
||||
size_t payload_len = total_len - ihl;
|
||||
unsigned required = protocol == 6 ? 20 : (protocol == 17 || protocol == 1) ? 8 : 0;
|
||||
if (!frag_offset && payload_len < required) {
|
||||
snprintf(details, sizeof(details), "%s header: available=%zu required=%u",
|
||||
more_fragments ? "partial" : "truncated", payload_len, required);
|
||||
if (more_fragments) DEBUG_DEBUG(DEBUG_CATEGORY_DUMP, "packet_dump: %s %s", name, details);
|
||||
else DEBUG_WARN(DEBUG_CATEGORY_DUMP, "packet_dump: %s %s", name, details);
|
||||
} else if (!frag_offset && (protocol == 6 || protocol == 17)) {
|
||||
unsigned src_port = ((unsigned)payload[0] << 8) | payload[1];
|
||||
unsigned dst_port = ((unsigned)payload[2] << 8) | payload[3];
|
||||
size_t src_len = strlen(src), dst_len = strlen(dst);
|
||||
snprintf(src + src_len, sizeof(src) - src_len, ":%u", src_port);
|
||||
snprintf(dst + dst_len, sizeof(dst) - dst_len, ":%u", dst_port);
|
||||
if (protocol == 6) {
|
||||
uint32_t seq = ((uint32_t)payload[4] << 24) | ((uint32_t)payload[5] << 16) |
|
||||
((uint32_t)payload[6] << 8) | payload[7];
|
||||
uint32_t ack = ((uint32_t)payload[8] << 24) | ((uint32_t)payload[9] << 16) |
|
||||
((uint32_t)payload[10] << 8) | payload[11];
|
||||
unsigned window = ((unsigned)payload[14] << 8) | payload[15];
|
||||
snprintf(details, sizeof(details), "[%s] seq=%u ack=%u win=%u", tcp_flags_to_str(payload[13]), seq, ack, window);
|
||||
} else {
|
||||
snprintf(proto_info, sizeof(proto_info), "type=%u code=%u", icmp_type, data[ihl + 1]);
|
||||
unsigned udp_len = ((unsigned)payload[4] << 8) | payload[5];
|
||||
snprintf(details, sizeof(details), "len=%u", udp_len);
|
||||
}
|
||||
is_special_proto = 1;
|
||||
} else if (!frag_offset && protocol == 1) {
|
||||
unsigned type = payload[0];
|
||||
if (type == 0 || type == 8) {
|
||||
unsigned id = ((unsigned)payload[4] << 8) | payload[5];
|
||||
unsigned seq = ((unsigned)payload[6] << 8) | payload[7];
|
||||
snprintf(details, sizeof(details), "%s seq=%u id=%u", type == 0 ? "echo_reply" : "echo_request", seq, id);
|
||||
} else snprintf(details, sizeof(details), "type=%u code=%u", type, payload[1]);
|
||||
}
|
||||
|
||||
if (is_special_proto && proto_info[0] != '\0') {
|
||||
if (src_port > 0 && dst_port > 0) {
|
||||
int offset = snprintf(buffer, sizeof(buffer), "IPv4 %s %s:%d -> %s:%d (%zd bytes) %s",
|
||||
proto_name, src_str, src_port, dst_str, dst_port, len, proto_info);
|
||||
if (offset > 0 && offset < (int)sizeof(buffer) - 50) {
|
||||
snprintf(buffer + offset, sizeof(buffer) - offset, " data: "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
data[ihl + 0], data[ihl + 1], data[ihl + 2], data[ihl + 3],
|
||||
data[ihl + 4], data[ihl + 5], data[ihl + 6], data[ihl + 7],
|
||||
data[ihl + 8], data[ihl + 9], data[ihl + 10], data[ihl + 11],
|
||||
data[ihl + 12], data[ihl + 13], data[ihl + 14], data[ihl + 15],
|
||||
data[ihl + 16], data[ihl + 17], data[ihl + 18], data[ihl + 19],
|
||||
data[ihl + 20], data[ihl + 21], data[ihl + 22], data[ihl + 23],
|
||||
data[ihl + 24], data[ihl + 25], data[ihl + 26], data[ihl + 27],
|
||||
data[ihl + 28], data[ihl + 29], data[ihl + 30], data[ihl + 31]);
|
||||
}
|
||||
} else {
|
||||
int offset = snprintf(buffer, sizeof(buffer), "IPv4 %s %s -> %s (%zd bytes) %s",
|
||||
proto_name, src_str, dst_str, len, proto_info);
|
||||
if (offset > 0 && offset < (int)sizeof(buffer) - 50) {
|
||||
snprintf(buffer + offset, sizeof(buffer) - offset, " data: "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
data[ihl + 0], data[ihl + 1], data[ihl + 2], data[ihl + 3],
|
||||
data[ihl + 4], data[ihl + 5], data[ihl + 6], data[ihl + 7],
|
||||
data[ihl + 8], data[ihl + 9], data[ihl + 10], data[ihl + 11],
|
||||
data[ihl + 12], data[ihl + 13], data[ihl + 14], data[ihl + 15],
|
||||
data[ihl + 16], data[ihl + 17], data[ihl + 18], data[ihl + 19],
|
||||
data[ihl + 20], data[ihl + 21], data[ihl + 22], data[ihl + 23],
|
||||
data[ihl + 24], data[ihl + 25], data[ihl + 26], data[ihl + 27],
|
||||
data[ihl + 28], data[ihl + 29], data[ihl + 30], data[ihl + 31]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int offset = snprintf(buffer, sizeof(buffer), "IPv4 %s %s:%d -> %s:%d (%zd bytes) data: "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x "
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
proto_name, src_str, src_port, dst_str, dst_port, len,
|
||||
data[ihl + 0], data[ihl + 1], data[ihl + 2], data[ihl + 3],
|
||||
data[ihl + 4], data[ihl + 5], data[ihl + 6], data[ihl + 7],
|
||||
data[ihl + 8], data[ihl + 9], data[ihl + 10], data[ihl + 11],
|
||||
data[ihl + 12], data[ihl + 13], data[ihl + 14], data[ihl + 15],
|
||||
data[ihl + 16], data[ihl + 17], data[ihl + 18], data[ihl + 19],
|
||||
data[ihl + 20], data[ihl + 21], data[ihl + 22], data[ihl + 23],
|
||||
data[ihl + 24], data[ihl + 25], data[ihl + 26], data[ihl + 27],
|
||||
data[ihl + 28], data[ihl + 29], data[ihl + 30], data[ihl + 31]);
|
||||
if (offset > 0 && offset < (int)sizeof(buffer)) {
|
||||
buffer[offset] = '\0';
|
||||
}
|
||||
/* Максимум 32 байта, три пробела между группами и завершающий NUL. */
|
||||
static const char hex_digits[] = "0123456789abcdef";
|
||||
char hex[68];
|
||||
size_t count = payload_len < 32 ? payload_len : 32, pos = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (i && i % 8 == 0) hex[pos++] = ' ';
|
||||
hex[pos++] = hex_digits[payload[i] >> 4];
|
||||
hex[pos++] = hex_digits[payload[i] & 0x0f];
|
||||
}
|
||||
hex[pos] = '\0';
|
||||
snprintf(buffer, sizeof(buffer), "IPv4 %s %s -> %s (%zu bytes)%s%s%s data: %s",
|
||||
name, src, dst, total_len, details[0] ? " " : "", details, fragment, hex);
|
||||
return buffer;
|
||||
|
||||
invalid:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DUMP, "packet_dump: %s", buffer);
|
||||
return buffer;
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
/* packet_dump — текстовая сводка IPv4-пакета: адреса, транспортные поля и короткий hex-фрагмент.
|
||||
* Принимает IP-пакет без ETCP-заголовка. Результат в общем static-буфере перезаписывается каждым вызовом;
|
||||
* его не освобождать, для хранения скопировать. Модуль не потокобезопасен и сам в лог не пишет.
|
||||
* Известный дефект: hex-дамп читает 32 байта после IHL без проверки остатка len;
|
||||
* текущая реализация небезопасна для коротких пакетов (см. doc/tasks.md). */
|
||||
* его не освобождать, для хранения скопировать. Модуль не потокобезопасен.
|
||||
* Ошибки входа отражаются в строке и логируются в категории dump; штатный результат логирует вызывающий. */
|
||||
#ifndef PACKET_DUMP_H
|
||||
#define PACKET_DUMP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "../lib/platform_compat.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* data — начало IPv4-пакета, len — доступные байты; выравнивание не требуется, NULL даёт диагностику.
|
||||
* Проверяет version/IHL/total length; при усечённом IP-пакете возвращает только причину ошибки.
|
||||
* При len > total length игнорирует хвост; размер в строке — total length.
|
||||
* Выводит до 32 байт после IHL (включая транспортный заголовок), ограниченных total length.
|
||||
* TCP/UDP/ICMP разбирает только при нулевом fragment offset и наличии 20/8/8 байт;
|
||||
* короткий транспортный заголовок отмечает в строке, сохраняя доступный hex-дамп.
|
||||
* Fragment offset выводит в байтах, MF — 0/1. Не проверяет checksum/полную корректность транспорта.
|
||||
* Возвращает общий static char[1024], всегда NUL-терминированный; не освобождать и не изменять. */
|
||||
char* dump_ip_packet_to_buffer(const uint8_t* data, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in new issue
Block a user