Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infra: packet tracing #81

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
48 changes: 48 additions & 0 deletions api/gr_net_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,52 @@ static inline int ip6_net_format(const struct ip6_net *net, char *buf, size_t le
return snprintf(buf + n, len - n, "/%u", net->prefixlen);
}

static inline int ethertype_format(const rte_be16_t ethertype, char *buf, size_t len) {
static const char *ethertypes[UINT16_MAX + 1] = {
[RTE_BE16(RTE_ETHER_TYPE_IPV4)] = "IP",
[RTE_BE16(RTE_ETHER_TYPE_IPV6)] = "IP6",
[RTE_BE16(RTE_ETHER_TYPE_ARP)] = "ARP",
[RTE_BE16(RTE_ETHER_TYPE_VLAN)] = "VLAN",
[RTE_BE16(RTE_ETHER_TYPE_QINQ)] = "QINQ",
[RTE_BE16(RTE_ETHER_TYPE_SLOW)] = "LACP",
[RTE_BE16(RTE_ETHER_TYPE_LLDP)] = "LLDP",
[RTE_BE16(RTE_ETHER_TYPE_MPLS)] = "MPLS",
[RTE_BE16(RTE_ETHER_TYPE_1588)] = "PTP",
};
if (ethertypes[ethertype])
return snprintf(buf, len, "%s", ethertypes[ethertype]);
else
return snprintf(buf, len, "ethertype %#04x", rte_be_to_cpu_16(ethertype));
}

static inline int nextproto_format(const uint8_t proto, char *buf, size_t len) {
static const char *protos[UINT8_MAX + 1] = {
[IPPROTO_HOPOPTS] = "IPv6 Hop-by-Hop",
[IPPROTO_ICMP] = "ICMP",
[IPPROTO_IGMP] = "IGMP",
[IPPROTO_IPIP] = "IP in IP",
[IPPROTO_TCP] = "TCP",
[IPPROTO_UDP] = "UDP",
[IPPROTO_IPV6] = "IPv6 Header",
[IPPROTO_ROUTING] = "IPv6 Routing Header",
[IPPROTO_FRAGMENT] = "IPv6 Fragment Header",
[IPPROTO_GRE] = "GRE",
[IPPROTO_ESP] = "ESP",
[IPPROTO_AH] = "Authentication Header",
[IPPROTO_MTP] = "Multicast Transport Protocol",
[IPPROTO_ICMPV6] = "IPv6 ICMP",
[IPPROTO_NONE] = "IPv6 No Next Header",
[IPPROTO_DSTOPTS] = "IPv6 Destination Options",
[IPPROTO_SCTP] = "SCTP",
[IPPROTO_MH] = "IPv6 Mobility Header",
[IPPROTO_UDPLITE] = "UDP Lite",
[IPPROTO_MPLS] = "MPLS In IP",
[IPPROTO_ETHERNET] = "Ethernet-within-IPv6 Encapsulation",
[IPPROTO_RAW] = "Raw IP Packets",
};
if (protos[proto])
return snprintf(buf, len, "%s", protos[proto]);
else
return snprintf(buf, len, "%d", proto);
}
#endif
24 changes: 24 additions & 0 deletions modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define GR_IFACE_F_UP GR_BIT16(0)
#define GR_IFACE_F_PROMISC GR_BIT16(1)
#define GR_IFACE_F_ALLMULTI GR_BIT16(2)
#define GR_IFACE_F_PACKET_TRACE GR_BIT16(3)
// Interface state flags
#define GR_IFACE_S_RUNNING GR_BIT16(0)

Expand Down Expand Up @@ -203,4 +204,27 @@ struct gr_infra_graph_dump_resp {
char dot[/* len */];
};

// packet tracing //////////////////////////////////////////////////////////////
#define GR_INFRA_PACKET_TRACE_CLEAR REQUEST_TYPE(GR_INFRA_MODULE, 0x0040)

// struct gr_infra_trace_clear_req { };
// struct gr_infra_trace_clear_resp { };

#define GR_INFRA_PACKET_TRACE_SHOW REQUEST_TYPE(GR_INFRA_MODULE, 0x0041)

// struct gr_infra_trace_dump_req { };

struct gr_infra_packet_trace_resp {
uint32_t len;
char trace[/* len */];
};

#define GR_INFRA_PACKET_TRACE_SET REQUEST_TYPE(GR_INFRA_MODULE, 0x0042)

struct gr_infra_packet_trace_set_req {
bool enabled;
bool all;
uint16_t iface_id;
};

#endif
1 change: 1 addition & 0 deletions modules/infra/api/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
src += files(
'graph.c',
'iface.c',
'trace.c',
'rxq.c',
'stats.c',
)
Expand Down
104 changes: 104 additions & 0 deletions modules/infra/api/trace.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Christophe Fontaine

#include "gr_infra.h"

#include <gr_api.h>
#include <gr_control.h>
#include <gr_iface.h>
#include <gr_trace.h>

#include <stdatomic.h>

#define TRACE_STR (6 * 1024) // 64 nodes, 80 cols per node: ~5120 bytes.

static atomic_bool trace_enabled = false;

bool gr_trace_enabled() {
return trace_enabled;
}

static void iface_callback(iface_event_t event, struct iface *iface) {
if (event == IFACE_EVENT_POST_ADD && trace_enabled)
iface->flags |= GR_IFACE_F_PACKET_TRACE;
}

static struct api_out set_trace(const void *request, void **) {
const struct gr_infra_packet_trace_set_req *req = request;
struct iface *iface = NULL;

if (req->all == false) {
if ((iface = iface_from_id(req->iface_id)) == NULL)
return api_out(ENODEV, 0);

if (req->enabled)
iface->flags |= GR_IFACE_F_PACKET_TRACE;
else
iface->flags &= ~GR_IFACE_F_PACKET_TRACE;

} else {
atomic_store(&trace_enabled, req->enabled);
while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) {
if (req->enabled)
iface->flags |= GR_IFACE_F_PACKET_TRACE;
else
iface->flags &= ~GR_IFACE_F_PACKET_TRACE;
}
}

return api_out(0, 0);
}

static struct api_out show_trace(const void * /*request*/, void **response) {
struct gr_infra_packet_trace_resp *resp;
size_t resp_len = sizeof(*resp);
int len = 0;

resp = calloc(1, sizeof(*resp) + TRACE_STR);
if (resp == NULL) {
return api_out(ENOMEM, 0);
}
len = trace_print(resp->trace, TRACE_STR);
if (len < 0) {
return api_out(-len, 0);
}
resp->len = len;
resp_len = sizeof(*resp) + len;

*response = resp;
return api_out(0, resp_len);
}

static struct api_out clear_trace(const void * /*request */, void ** /*response*/) {
trace_clear();
return api_out(0, 0);
}

static struct gr_api_handler set_trace_handler = {
.name = "set trace",
.request_type = GR_INFRA_PACKET_TRACE_SET,
.callback = set_trace,
};

static struct gr_api_handler show_trace_handler = {
.name = "show trace",
.request_type = GR_INFRA_PACKET_TRACE_SHOW,
.callback = show_trace,
};

static struct gr_api_handler clear_trace_handler = {
.name = "clear trace",
.request_type = GR_INFRA_PACKET_TRACE_CLEAR,
.callback = clear_trace,
};

static struct iface_event_handler iface_event_trace_handler = {
.callback = iface_callback,
};

RTE_INIT(trace_init) {
gr_register_api_handler(&set_trace_handler);
gr_register_api_handler(&show_trace_handler);
gr_register_api_handler(&clear_trace_handler);
iface_event_register_handler(&iface_event_trace_handler);
}
15 changes: 14 additions & 1 deletion modules/infra/cli/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ uint64_t parse_iface_args(
struct gr_iface *iface,
bool update
) {
const char *name, *promisc, *allmulti;
const char *name, *promisc, *allmulti, *trace;
uint64_t set_attrs = 0;

name = arg_str(p, "NAME");
Expand Down Expand Up @@ -192,6 +192,15 @@ uint64_t parse_iface_args(
set_attrs |= GR_IFACE_SET_FLAGS;
}

trace = arg_str(p, "TRACE");
if (trace != NULL && strcmp(trace, "on") == 0) {
iface->flags |= GR_IFACE_F_PACKET_TRACE;
set_attrs |= GR_IFACE_SET_FLAGS;
} else if (trace != NULL && strcmp(trace, "off") == 0) {
iface->flags &= ~GR_IFACE_F_PACKET_TRACE;
set_attrs |= GR_IFACE_SET_FLAGS;
}

if (arg_u16(p, "MTU", &iface->mtu) == 0)
set_attrs |= GR_IFACE_SET_MTU;

Expand Down Expand Up @@ -280,6 +289,8 @@ static cmd_status_t iface_list(const struct gr_api_client *c, const struct ec_pn
n += snprintf(buf + n, sizeof(buf) - n, " promisc");
if (iface->flags & GR_IFACE_F_ALLMULTI)
n += snprintf(buf + n, sizeof(buf) - n, " allmulti");
if (iface->flags & GR_IFACE_F_PACKET_TRACE)
n += snprintf(buf + n, sizeof(buf) - n, " tracing");
scols_line_set_data(line, 2, buf);

// vrf
Expand Down Expand Up @@ -332,6 +343,8 @@ static cmd_status_t iface_show(const struct gr_api_client *c, const struct ec_pn
printf(" promisc");
if (iface.flags & GR_IFACE_F_ALLMULTI)
printf(" allmulti");
if (iface.flags & GR_IFACE_F_PACKET_TRACE)
printf(" tracing");
printf("\n");
printf("vrf: %u\n", iface.vrf_id);
printf("mtu: %u\n", iface.mtu);
Expand Down
1 change: 1 addition & 0 deletions modules/infra/cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
cli_src += files(
'graph.c',
'iface.c',
'trace.c',
'port.c',
'vlan.c',
'stats.c',
Expand Down
139 changes: 139 additions & 0 deletions modules/infra/cli/trace.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Christophe Fontaine

#include "gr_cli_iface.h"

#include <gr_api.h>
#include <gr_cli.h>
#include <gr_infra.h>
#include <gr_net_types.h>

#include <ecoli.h>

#include <stdio.h>
#include <unistd.h>

static cmd_status_t trace_set(const struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_infra_packet_trace_set_req req;
struct gr_iface iface;

req.enabled = true;

if (arg_str(p, "all") != NULL)
req.all = true;
else if (iface_from_name(c, arg_str(p, "NAME"), &iface) < 0)
return CMD_ERROR;
else
req.iface_id = iface.id;

if (gr_api_client_send_recv(c, GR_INFRA_PACKET_TRACE_SET, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;

return CMD_SUCCESS;
}

static cmd_status_t trace_del(const struct gr_api_client *c, const struct ec_pnode *p) {
struct gr_infra_packet_trace_set_req req;
struct gr_iface iface;

req.enabled = false;
if (arg_str(p, "all") != NULL)
req.all = true;
else if (iface_from_name(c, arg_str(p, "NAME"), &iface) < 0)
return CMD_ERROR;
else
req.iface_id = iface.id;

if (gr_api_client_send_recv(c, GR_INFRA_PACKET_TRACE_SET, sizeof(req), &req, NULL) < 0)
return CMD_ERROR;

return CMD_SUCCESS;
}

static cmd_status_t trace_show(const struct gr_api_client *c, const struct ec_pnode *) {
const struct gr_infra_packet_trace_resp *resp = NULL;
void *resp_ptr = NULL;
size_t len = 0;

do {
if (gr_api_client_send_recv(c, GR_INFRA_PACKET_TRACE_SHOW, 0, NULL, &resp_ptr) < 0)
return CMD_ERROR;

resp = resp_ptr;
len = resp->len;
if (len > 1) {
fwrite(resp->trace, 1, resp->len, stdout);
}
free(resp_ptr);
} while (len > 0);

return CMD_SUCCESS;
}

static cmd_status_t trace_clear(const struct gr_api_client *c, const struct ec_pnode *) {
if (gr_api_client_send_recv(c, GR_INFRA_PACKET_TRACE_CLEAR, 0, NULL, NULL) < 0)
return CMD_ERROR;

return CMD_SUCCESS;
}

static int ctx_init(struct ec_node *root) {
int ret;

ret = CLI_COMMAND(
CLI_CONTEXT(
root,
CTX_SET,
CTX_ARG("trace", "Enable packet tracing for specified interface")
),
"all|(iface NAME)",
trace_set,
"Enable packet tracing for all or specified interface.",
with_help("all interfaces.", ec_node_str("all", "all")),
with_help(
"Interface name.",
ec_node_dyn("NAME", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
CLI_CONTEXT(
root,
CTX_DEL,
CTX_ARG("trace", "Disable packet tracing for specified interface")
),
"all|(iface NAME)",
trace_del,
"Enable packet tracing for all or specified interface.",
with_help("all interfaces.", ec_node_str("all", "all")),
with_help(
"Interface name.",
ec_node_dyn("NAME", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(CLI_CONTEXT(root, CTX_SHOW), "trace", trace_show, "Show traced packets.");
if (ret < 0)
return ret;

ret = CLI_COMMAND(
CLI_CONTEXT(root, CTX_CLEAR), "trace", trace_clear, "Clear packet tracing buffer.",
);
if (ret < 0)
return ret;

return 0;
}

static struct gr_cli_context ctx = {
.name = "trace",
.init = ctx_init,
};

static void __attribute__((constructor, used)) init(void) {
register_context(&ctx);
}
Loading