feature: add debug_plugin for development debugging

This commit is contained in:
luwenpeng
2024-08-01 11:40:00 +08:00
parent 48202b4d0a
commit 776090331e
16 changed files with 446 additions and 135 deletions

View File

@@ -1,6 +1,7 @@
add_subdirectory(log)
add_subdirectory(times)
add_subdirectory(tuple)
add_subdirectory(hexdump)
add_subdirectory(packet)
add_subdirectory(packet_io)
add_subdirectory(id_generator)

View File

@@ -0,0 +1,3 @@
add_library(hexdump hexdump.cpp)
target_include_directories(hexdump PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(hexdump)

37
src/hexdump/hexdump.cpp Normal file
View File

@@ -0,0 +1,37 @@
#include "hexdump.h"
void hexdump_to_fd(int fd, const char *data, uint16_t len)
{
uint16_t i = 0;
uint16_t used = 0;
uint16_t offset = 0;
#define LINE_LEN 80
char line[LINE_LEN]; /* space needed 8+16*3+3+16 == 75 */
dprintf(fd, "dump data at [%p], len=%u\n", data, len);
while (offset < len)
{
/* format the line in the buffer, then use printf to usedput to screen */
used = snprintf(line, LINE_LEN, "%08X:", offset);
for (i = 0; ((offset + i) < len) && (i < 16); i++)
{
used += snprintf(line + used, LINE_LEN - used, " %02X", (data[offset + i] & 0xff));
}
for (; i <= 16; i++)
{
used += snprintf(line + used, LINE_LEN - used, " | ");
}
for (i = 0; (offset < len) && (i < 16); i++, offset++)
{
unsigned char c = data[offset];
if ((c < ' ') || (c > '~'))
{
c = '.';
}
used += snprintf(line + used, LINE_LEN - used, "%c", c);
}
dprintf(fd, "%s\n", line);
}
dprintf(fd, "\n");
}

15
src/hexdump/hexdump.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include <stdio.h>
void hexdump_to_fd(int fd, const char *data, uint16_t len);
#ifdef __cplusplus
}
#endif

View File

@@ -2,6 +2,6 @@ add_library(packet packet_parse.cpp packet_craft.cpp packet_dump.cpp packet_util
target_include_directories(packet PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(packet PUBLIC ${CMAKE_SOURCE_DIR}/deps/uthash)
target_include_directories(packet PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(packet tuple log)
target_link_libraries(packet hexdump tuple log)
add_subdirectory(test)

View File

@@ -5,6 +5,7 @@
#include <sys/time.h>
#include "log.h"
#include "hexdump.h"
#include "packet_dump.h"
#include "packet_utils.h"
@@ -78,16 +79,8 @@ int packet_dump_pcap(const struct packet *pkt, const char *file)
void packet_dump_hex(const struct packet *pkt, int fd)
{
const char *data = packet_get_raw_data(pkt);
uint16_t len = packet_get_raw_len(pkt);
const char *data = packet_get_raw_data(pkt);
for (int i = 0; i < len; i++)
{
if (i % 16 == 0)
{
dprintf(fd, "\n");
}
dprintf(fd, "%02x ", (unsigned char)data[i]);
}
dprintf(fd, "\n");
hexdump_to_fd(fd, data, len);
}

View File

@@ -986,79 +986,87 @@ const char *packet_parse(struct packet *pkt, const char *data, uint16_t len)
return parse_ether(pkt, data, len);
}
void packet_print(const struct packet *pkt)
int packet_to_str(const struct packet *pkt, char *buff, int size)
{
if (pkt == NULL)
{
return;
return 0;
}
char buffer[2048] = {0};
printf("packet: %p, data_ptr: %p, data_len: %u, trim_len: %u, layers_used: %u, layers_size: %u\n",
int used = 0;
memset(buff, 0, size);
used += snprintf(buff + used, size - used, "packet: %p, data_ptr: %p, data_len: %u, trim_len: %u, layers_used: %u, layers_size: %u\n",
pkt, pkt->data_ptr, pkt->data_len, pkt->trim_len,
pkt->layers_used, pkt->layers_size);
for (uint8_t i = 0; i < pkt->layers_used; i++)
{
int used = 0;
const struct raw_layer *layer = &pkt->layers[i];
printf(" layer[%u]: %p, proto: %s, header: {offset: %u, ptr: %p, len: %u}, payload: {ptr: %p, len: %u}\n",
used += snprintf(buff + used, size - used, "=> layer[%u]: %p, proto: %s, header: {offset: %u, ptr: %p, len: %u}, payload: {ptr: %p, len: %u}\n",
i, layer, layer_proto_to_str(layer->proto), layer->hdr_offset,
layer->hdr_ptr, layer->hdr_len, layer->pld_ptr, layer->pld_len);
switch (layer->proto)
{
case LAYER_PROTO_ETHER:
used = eth_hdr_to_str((const struct ethhdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += eth_hdr_to_str((const struct ethhdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_PWETH:
break;
case LAYER_PROTO_PPP:
break;
case LAYER_PROTO_L2TP:
used = l2tp_hdr_to_str((const struct l2tp_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += l2tp_hdr_to_str((const struct l2tp_hdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_VLAN:
used = vlan_hdr_to_str((const struct vlan_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += vlan_hdr_to_str((const struct vlan_hdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_PPPOE:
break;
case LAYER_PROTO_MPLS:
used = mpls_label_to_str((const struct mpls_label *)layer->hdr_ptr, buffer, sizeof(buffer));
break;
used += mpls_label_to_str((const struct mpls_label *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_IPV4:
used = ip4_hdr_to_str((const struct ip *)layer->hdr_ptr, buffer, sizeof(buffer));
used += ip4_hdr_to_str((const struct ip *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_IPV6:
used = ip6_hdr_to_str((const struct ip6_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += ip6_hdr_to_str((const struct ip6_hdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_IPAH:
break;
case LAYER_PROTO_GRE:
used = gre_hdr_to_str(layer->hdr_ptr, layer->hdr_len, buffer, sizeof(buffer));
used += gre_hdr_to_str(layer->hdr_ptr, layer->hdr_len, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_UDP:
used = udp_hdr_to_str((const struct udphdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += udp_hdr_to_str((const struct udphdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_TCP:
used = tcp_hdr_to_str((const struct tcphdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += tcp_hdr_to_str((const struct tcphdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_ICMP:
break;
case LAYER_PROTO_ICMP6:
break;
case LAYER_PROTO_VXLAN:
used = vxlan_hdr_to_str((const struct vxlan_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used += vxlan_hdr_to_str((const struct vxlan_hdr *)layer->hdr_ptr, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
case LAYER_PROTO_GTP_C:
case LAYER_PROTO_GTP_U:
used = gtp_hdr_to_str(layer->hdr_ptr, layer->hdr_len, buffer, sizeof(buffer));
used += gtp_hdr_to_str(layer->hdr_ptr, layer->hdr_len, buff + used, size - used);
used += snprintf(buff + used, size - used, "\n");
break;
default:
break;
}
if (used)
{
printf(" %s\n", buffer);
}
}
return used;
}

View File

@@ -6,9 +6,20 @@ extern "C"
#endif
#include <stdint.h>
#include <stdio.h>
const char *packet_parse(struct packet *pkt, const char *data, uint16_t len);
void packet_print(const struct packet *pkt);
// bref : 1, output packet bref info
// bref : 0, output packet detail info
int packet_to_str(const struct packet *pkt, char *buff, int size);
static inline void packet_print(const struct packet *pkt)
{
char buff[4096];
packet_to_str(pkt, buff, sizeof(buff));
printf("%s\n", buff);
}
#ifdef __cplusplus
}

View File

@@ -12,6 +12,7 @@
#include "packet_layer.h"
#include "packet_parse.h"
#include "packet_craft.h"
#include "packet_utils.h"
#define PRINT_GREEN(fmt, ...) printf("\033[0;32m" fmt "\033[0m\n", ##__VA_ARGS__)
#define PRINT_RED(fmt, ...) printf("\033[0;31m" fmt "\033[0m\n", ##__VA_ARGS__)
@@ -191,6 +192,8 @@ TEST(PACKET_CRAFT_TCP, ETH_IP4_TCP)
// printf(("idx: %d, orig: %02x, new: %02x\n"), i, orig_pkt_data[i], new_pkt_data[i]);
EXPECT_TRUE(orig_pkt_data[i] == new_pkt_data[i]);
}
packet_free(new_pkt);
}
#endif
@@ -336,6 +339,8 @@ TEST(PACKET_CRAFT_TCP, ETH_IP4_IP6_TCP)
// printf(("idx: %d, orig: %02x, new: %02x\n"), i, orig_pkt_data[i], new_pkt_data[i]);
EXPECT_TRUE(orig_pkt_data[i] == new_pkt_data[i]);
}
packet_free(new_pkt);
}
#endif
@@ -574,6 +579,8 @@ TEST(PACKET_CRAFT_TCP, ETH_IP6_UDP_GTP_IP4_TCP)
// printf(("idx: %d, orig: %02x, new: %02x\n"), i, orig_pkt_data[i], new_pkt_data[i]);
EXPECT_TRUE(orig_pkt_data[i] == new_pkt_data[i]);
}
packet_free(new_pkt);
}
#endif
@@ -780,6 +787,8 @@ TEST(PACKET_CRAFT_TCP, ETH_IP4_GRE_IP6_TCP)
// printf(("idx: %d, orig: %02x, new: %02x\n"), i, orig_pkt_data[i], new_pkt_data[i]);
EXPECT_TRUE(orig_pkt_data[i] == new_pkt_data[i]);
}
packet_free(new_pkt);
}
#endif
@@ -1011,6 +1020,8 @@ TEST(PACKET_CRAFT_UDP, ETH_VLAN_IPv6_IPv4_GRE_PPP_IPv4_UDP_DNS)
printf(("idx: %d, orig: %02x, new: %02x\n"), i, orig_pkt_data[i], new_pkt_data[i]);
EXPECT_TRUE(orig_pkt_data[i] == new_pkt_data[i]);
}
packet_free(new_pkt);
}
#endif

View File

@@ -494,12 +494,25 @@ static void tcp_flags_to_str(uint8_t flags, char *buffer, size_t len)
}
}
int session_to_json(struct session *sess, char *buff, int size)
int session_to_str(const struct session *sess, int bref, char *buff, int size)
{
memset(buff, 0, size);
char flags[64] = {0};
int used = 0;
if (bref)
{
used += snprintf(buff + used, size - used, "session id: %lu, addr: %s, type: %s, state: %s, dir: %s, c2s_rx_pkts: %lu, s2c_rx_pkts: %lu",
session_get_id(sess),
session_get0_readable_addr(sess),
session_type_to_str(session_get_type(sess)),
session_state_to_str(session_get_current_state(sess)),
flow_direction_to_str(session_get_current_flow_direction(sess)),
session_get_stat(sess, FLOW_DIRECTION_C2S, STAT_RAW_PACKETS_RECEIVED),
session_get_stat(sess, FLOW_DIRECTION_S2C, STAT_RAW_PACKETS_RECEIVED));
}
else
{
used += snprintf(buff + used, size - used, "{");
used += snprintf(buff + used, size - used, "\"id\":%" PRIu64 ",", session_get_id(sess));
used += snprintf(buff + used, size - used, "\"start_timestamp\":%" PRIu64 ",", session_get_timestamp(sess, SESSION_TIMESTAMP_START));
@@ -587,13 +600,7 @@ int session_to_json(struct session *sess, char *buff, int size)
}
}
used += snprintf(buff + used, size - used, "}");
}
return used;
}
void session_print(struct session *sess)
{
char buff[4096];
session_to_json(sess, buff, sizeof(buff));
printf("%s\n", buff);
}

View File

@@ -6,6 +6,7 @@ extern "C"
#endif
#include <stdint.h>
#include <stdio.h>
#include "stellar/session.h"
@@ -131,8 +132,16 @@ const char *session_state_to_str(enum session_state state);
const char *session_type_to_str(enum session_type type);
const char *flow_direction_to_str(enum flow_direction dir);
int session_to_json(struct session *sess, char *buff, int size);
void session_print(struct session *sess);
// bref : 1, output session bref info
// bref : 0, output session detail info
int session_to_str(const struct session *sess, int bref, char *buff, int size);
static inline void session_print(const struct session *sess)
{
char buff[4096];
session_to_str(sess, 0, buff, sizeof(buff));
printf("%s\n", buff);
}
#ifdef __cplusplus
}

View File

@@ -1,2 +1,3 @@
add_subdirectory(packet_inject)
add_subdirectory(packet_tool)
add_subdirectory(debug_plugin)

View File

@@ -0,0 +1,8 @@
# build libdebug_plugin.so
add_library(debug_plugin SHARED debug_plugin.cpp)
# Note: The debug plugin here uses stellar_core instead of stellar_devel, in order to output more session/packet information for development debugging.
target_link_libraries(debug_plugin stellar_core toml)
target_include_directories(debug_plugin PUBLIC ${CMAKE_SOURCE_DIR}/include/)
set_target_properties(debug_plugin PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/version.map")
file(COPY ./conf/ DESTINATION ./conf/)

View File

@@ -0,0 +1,4 @@
[[plugin]]
path = "./plugin/libdebug_plugin.so"
init = "debug_plugin_init"
exit = "debug_plugin_exit"

View File

@@ -0,0 +1,196 @@
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <time.h>
#include <pthread.h>
#include <arpa/inet.h>
#include "log.h"
#include "hexdump.h"
#include "session_utils.h"
#include "tcp_reassembly.h"
#include "packet_dump.h"
#include "packet_parse.h"
#include "packet_utils.h"
#include "stellar/session_mq.h"
// NOTE: packet hexdump or tcp segment hexdump may be too long, so we need direct output to fd, instead of using log_print
static void log_print(int fd, const char *module, const char *fmt, ...)
{
static unsigned char weekday_str[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static unsigned char month_str[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int nwrite;
char buf[4096 * 2] = {0};
char *p = buf;
char *end = buf + sizeof(buf);
va_list args;
struct tm local;
time_t t;
time(&t);
localtime_r(&t, &local);
// add time
p += snprintf(p, end - p, "%s %s %d %02d:%02d:%02d %d ",
weekday_str[local.tm_wday], month_str[local.tm_mon], local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec, local.tm_year + 1900);
// add tid
p += snprintf(p, end - p, "%lu ", pthread_self());
// add module
p += snprintf(p, end - p, "(%s), ", module);
// add content
va_start(args, fmt);
p += vsnprintf(p, end - p, fmt, args);
va_end(args);
// add end of line
p += snprintf(p, end - p, "\n");
do
{
nwrite = write(fd, buf, p - buf);
} while (nwrite == -1 && errno == EINTR);
}
struct plugin_ctx
{
struct stellar *st;
int sess_plug_id;
int tcp_topic_id;
int udp_topic_id;
int tcp_stream_topic_id;
int fd;
pthread_spinlock_t lock; // for hexdump thread safe
};
static void *on_sess_new(struct session *sess, void *plugin_ctx)
{
char buff[4096];
struct plugin_ctx *ctx = (struct plugin_ctx *)plugin_ctx;
memset(buff, 0, sizeof(buff));
session_to_str(sess, 1, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "sess new: %s", buff);
return NULL;
}
static void on_sess_free(struct session *sess, void *sess_ctx, void *plugin_ctx)
{
char buff[4096];
struct plugin_ctx *ctx = (struct plugin_ctx *)plugin_ctx;
memset(buff, 0, sizeof(buff));
session_to_str(sess, 0, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "sess free: %s", buff);
}
static void on_sess_udp_msg(struct session *sess, int topic_id, const void *msg, void *sess_ctx, void *plugin_ctx)
{
char buff[4096];
struct packet *pkt = (struct packet *)msg;
struct plugin_ctx *ctx = (struct plugin_ctx *)plugin_ctx;
memset(buff, 0, sizeof(buff));
session_to_str(sess, 1, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "on UDP msg: %s", buff);
memset(buff, 0, sizeof(buff));
packet_to_str(pkt, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "rx UDP packet: \n%s", buff);
pthread_spin_lock(&ctx->lock);
packet_dump_hex(pkt, ctx->fd);
pthread_spin_unlock(&ctx->lock);
}
static void on_sess_tcp_msg(struct session *sess, int topic_id, const void *msg, void *sess_ctx, void *plugin_ctx)
{
char buff[4096];
struct packet *pkt = (struct packet *)msg;
struct plugin_ctx *ctx = (struct plugin_ctx *)plugin_ctx;
memset(buff, 0, sizeof(buff));
session_to_str(sess, 1, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "on TCP msg: %s", buff);
memset(buff, 0, sizeof(buff));
packet_to_str(pkt, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "rx TCP packet: \n%s", buff);
pthread_spin_lock(&ctx->lock);
packet_dump_hex(pkt, ctx->fd);
pthread_spin_unlock(&ctx->lock);
}
static void on_sess_tcp_stream_msg(struct session *sess, int topic_id, const void *msg, void *sess_ctx, void *plugin_ctx)
{
char buff[4096];
struct tcp_segment *seg = (struct tcp_segment *)msg;
struct plugin_ctx *ctx = (struct plugin_ctx *)plugin_ctx;
memset(buff, 0, sizeof(buff));
session_to_str(sess, 1, buff, sizeof(buff) - 1);
log_print(ctx->fd, "debug plugin", "on TCP stream msg: %s", buff);
log_print(ctx->fd, "debug plugin", "rx TCP segment: len: %d, data: %p\n", seg->len, seg->data);
pthread_spin_lock(&ctx->lock);
hexdump_to_fd(ctx->fd, (const char *)seg->data, seg->len);
pthread_spin_unlock(&ctx->lock);
}
/******************************************************************************
* Plugin API
******************************************************************************/
extern "C"
{
void *debug_plugin_init(struct stellar *st)
{
struct plugin_ctx *ctx = (struct plugin_ctx *)calloc(1, sizeof(struct plugin_ctx));
if (ctx == NULL)
{
return NULL;
}
ctx->fd = open("./log/debug_plugin.log", O_WRONLY | O_APPEND | O_CREAT, 0644);
if (ctx->fd == -1)
{
ctx->fd = STDOUT_FILENO;
}
pthread_spin_init(&ctx->lock, PTHREAD_PROCESS_PRIVATE);
ctx->st = st;
ctx->sess_plug_id = stellar_session_plugin_register(st, on_sess_new, on_sess_free, ctx);
ctx->udp_topic_id = stellar_session_mq_get_topic_id(st, TOPIC_UDP);
ctx->tcp_topic_id = stellar_session_mq_get_topic_id(st, TOPIC_TCP);
ctx->tcp_stream_topic_id = stellar_session_mq_get_topic_id(st, TOPIC_TCP_STREAM);
stellar_session_mq_subscribe(st, ctx->udp_topic_id, on_sess_udp_msg, ctx->sess_plug_id);
stellar_session_mq_subscribe(st, ctx->tcp_topic_id, on_sess_tcp_msg, ctx->sess_plug_id);
stellar_session_mq_subscribe(st, ctx->tcp_stream_topic_id, on_sess_tcp_stream_msg, ctx->sess_plug_id);
log_print(ctx->fd, "debug plugin", "init");
return ctx;
}
void debug_plugin_exit(void *plugin_ctx)
{
struct plugin_ctx *ctx = (struct plugin_ctx *)plugin_ctx;
if (ctx)
{
log_print(ctx->fd, "debug plugin", "exit");
if (ctx->fd > 0)
{
close(ctx->fd);
}
pthread_spin_destroy(&ctx->lock);
free(ctx);
}
}
}

View File

@@ -0,0 +1,7 @@
DEBUG_PLUGIN {
global:
debug_plugin_init;
debug_plugin_exit;
local: *;
};