Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions doc/usage/bfcli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,16 @@ Print a chain.
``chain logs``
~~~~~~~~~~~~~~

Print a chain's logged packets.
Print a chain's log entries.

bfcli will print the logged headers as they are published by the chain. Only the headers requested in the ``log`` action will be printed. Hit ``Ctrl+C`` to quit.
bfcli will print log entries as they are published by the chain. Only the data requested in the ``log`` action will be printed. Hit ``Ctrl+C`` to quit.

For each logged packet, bfcli will print the receive timestamp and the packet size, followed by each requested layer (see the ``log`` action below). If one of the requested layer could not be processed by the chain, the corresponding output will be truncated.
For packet-based hooks, each log entry contains the receive timestamp and the packet size, followed by each requested layer (see the ``log`` action below). If a requested layer could not be processed by the chain, the corresponding output will be truncated.

For ``BF_HOOK_CGROUP_SOCK_ADDR_*`` hooks, each log entry contains the receive timestamp and verdict. If ``internet`` is requested, source and destination addresses are included. If ``transport`` is requested, the destination port is included. If ``pid`` is requested, the process ID is included. If ``comm`` is requested, the process name is included.

**Options**
- ``--name NAME``: name of the chain to print the logged packets for.
- ``--name NAME``: name of the chain to print the log entries for.

**Examples**

Expand Down Expand Up @@ -403,14 +405,17 @@ Rules are defined such as:
rule
[$MATCHER...]
[$SET...]
[log link,internet,transport]
[log $LOG_OPTS]
[counter]
[mark $MARK]
$VERDICT

With:
- ``$MATCHER``: zero or more matchers. Matchers are defined later.
- ``log``: optional. If set, log the requested protocol headers. ``link`` will log the link (layer 2) header, ``internet`` with log the internet (layer 3) header, and ``transport`` will log the transport (layer 4) header. At least one header type is required. ``log`` is **not** supported by ``BF_HOOK_CGROUP_SOCK_ADDR_*`` hooks.
- ``log``: optional. If set, log the requested data. At least one option is required. Available options depend on the hook type:

- Packet hooks (XDP, TC, NF, cgroup_skb): ``link`` (layer 2 header), ``internet`` (layer 3 header), ``transport`` (layer 4 header).
- Socket hooks (``BF_HOOK_CGROUP_SOCK_ADDR_*``): ``internet`` (source and destination addresses), ``transport`` (destination port), ``pid`` (process ID), ``comm`` (process name).
- ``counter``: optional literal. If set, the filter will count the number of events matched by the rule. For packet-based hooks, this includes both the number of packets and the total bytes. For ``BF_HOOK_CGROUP_SOCK_ADDR_*`` hooks, this counts the number of socket operations (``connect()`` or ``sendmsg()`` calls).
- ``mark``: optional, ``$MARK`` must be a valid decimal or hexadecimal 32-bits value. If set, write the packet's marker value. This marker can be used later on in a rule (see ``meta.mark``) or with a TC filter.
- ``$VERDICT``: action taken by the rule if the packet is matched against **all** the criteria: either ``ACCEPT``, ``DROP``, ``CONTINUE``, or ``REDIRECT``.
Expand Down
2 changes: 1 addition & 1 deletion src/bfcli/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ log { BEGIN(STATE_LOG_OPTS); return LOG; }
[0-9a-zA-Z]+(,[0-9a-zA-Z]+)* {
BEGIN(INITIAL);
yylval.sval = strdup(yytext);
return LOG_HEADERS;
return LOG_OPTS;
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/bfcli/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
%token SET
%token LOG COUNTER MARK
%token REDIRECT_TOKEN
%token <sval> LOG_HEADERS
%token <sval> LOG_OPTS
%token <sval> SET_TYPE
%token <sval> SET_RAW_PAYLOAD
%token <sval> STRING
Expand Down Expand Up @@ -320,7 +320,7 @@ rule : RULE matchers rule_options rule_verdict
}
;

rule_option : LOG LOG_HEADERS
rule_option : LOG LOG_OPTS
{
_cleanup_free_ char *in = $2;
char *tmp = in;
Expand All @@ -329,12 +329,12 @@ rule_option : LOG LOG_HEADERS
uint8_t log = 0;

while ((token = strtok_r(tmp, ",", &saveptr))) {
enum bf_pkthdr header;
enum bf_log_opt opt;

if (bf_pkthdr_from_str(token, &header) < 0)
bf_parse_err("unknown packet header '%s'", token);
if (bf_log_opt_from_str(token, &opt) < 0)
bf_parse_err("unknown log option '%s'", token);

log |= BF_FLAG(header);
log |= BF_FLAG(opt);

tmp = NULL;
}
Expand Down
166 changes: 137 additions & 29 deletions src/bfcli/print.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,12 @@ void bfc_chain_dump(struct bf_chain *chain, struct bf_hookopts *hookopts,

(void)fprintf(stdout, " log ");

for (enum bf_pkthdr hdr = 0; hdr < _BF_PKTHDR_MAX; ++hdr) {
if (!(log & BF_FLAG(hdr)))
for (enum bf_log_opt opt = 0; opt < _BF_LOG_OPT_MAX; ++opt) {
if (!(log & BF_FLAG(opt)))
continue;

log &= ~BF_FLAG(hdr);
(void)fprintf(stdout, "%s%s", bf_pkthdr_to_str(hdr),
log &= ~BF_FLAG(opt);
(void)fprintf(stdout, "%s%s", bf_log_opt_to_str(opt),
log ? "," : "\n");
}
}
Expand Down Expand Up @@ -360,30 +360,38 @@ static void _bf_chain_log_header(const struct bf_log *log)
struct timespec time;
char time_str[64];

assert(log);

// Convert timestamp to readable format
time.tv_sec = (long)log->ts / BF_TIME_S;
time.tv_nsec = (long)log->ts % BF_TIME_S;

(void)strftime(time_str, sizeof(time_str), "%H:%M:%S",
localtime(&time.tv_sec));

(void)fprintf(
stdout,
"\n%s[%s.%06ld]%s Rule #%u matched %s%llu bytes%s with verdict %s\n",
bf_logger_get_color(BF_COLOR_LIGHT_CYAN, BF_STYLE_NORMAL), time_str,
time.tv_nsec / BF_TIME_US,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET), log->rule_id,
bf_logger_get_color(BF_COLOR_DEFAULT, BF_STYLE_BOLD), log->pkt_size,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET),
bf_verdict_to_str((enum bf_verdict)log->verdict));
(void)fprintf(stdout, "\n%s[%s.%06ld]%s Rule #%u",
bf_logger_get_color(BF_COLOR_LIGHT_CYAN, BF_STYLE_NORMAL),
time_str, time.tv_nsec / BF_TIME_US,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET),
log->rule_id);

if (log->log_type == BF_LOG_TYPE_PACKET) {
(void)fprintf(stdout, " matched %s%llu bytes%s with",
bf_logger_get_color(BF_COLOR_DEFAULT, BF_STYLE_BOLD),
log->payload.pkt.pkt_size,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));
}

(void)fprintf(stdout, " verdict %s\n",
bf_verdict_to_str((enum bf_verdict)log->verdict));
}

static void _bf_chain_log_l2(const struct bf_log *log)
{
struct ethhdr *ethhdr = (void *)log->l2hdr;
struct ethhdr *ethhdr = (void *)log->payload.pkt.l2hdr;
const char *ethertype;

if (!(log->headers & (1 << BF_PKTHDR_LINK))) {
if (!(log->payload.pkt.headers & (1 << BF_LOG_OPT_LINK))) {
(void)fprintf(stdout, " Ethernet : <unknown header>\n");
return;
}
Expand Down Expand Up @@ -418,14 +426,14 @@ static void _bf_chain_log_l3(const struct bf_log *log)
char dst_addr[INET6_ADDRSTRLEN];
const char *protocol;

if (!(log->headers & (1 << BF_PKTHDR_INTERNET))) {
if (!(log->payload.pkt.headers & (1 << BF_LOG_OPT_INTERNET))) {
(void)fprintf(stdout, " Internet : <unknown header>\n");
return;
}

switch (log->l3_proto) {
case ETH_P_IP:
iphdr = (struct iphdr *)&log->l3hdr[0];
iphdr = (struct iphdr *)&log->payload.pkt.l3hdr[0];

inet_ntop(AF_INET, &iphdr->saddr, src_addr, sizeof(src_addr));
inet_ntop(AF_INET, &iphdr->daddr, dst_addr, sizeof(dst_addr));
Expand All @@ -451,7 +459,7 @@ static void _bf_chain_log_l3(const struct bf_log *log)
break;

case ETH_P_IPV6:
ipv6hdr = (struct ipv6hdr *)log->l3hdr;
ipv6hdr = (struct ipv6hdr *)log->payload.pkt.l3hdr;

inet_ntop(AF_INET6, &ipv6hdr->saddr, src_addr, sizeof(src_addr));
inet_ntop(AF_INET6, &ipv6hdr->daddr, dst_addr, sizeof(dst_addr));
Expand Down Expand Up @@ -490,14 +498,14 @@ static void _bf_chain_log_l4(const struct bf_log *log)
struct udphdr *udphdr;
const char *tcp_flags_str;

if (!(log->headers & (1 << BF_PKTHDR_TRANSPORT))) {
if (!(log->payload.pkt.headers & (1 << BF_LOG_OPT_TRANSPORT))) {
(void)fprintf(stdout, " Transport : <unknown header>\n");
return;
}

switch (log->l4_proto) {
case IPPROTO_TCP:
tcphdr = (struct tcphdr *)log->l4hdr;
tcphdr = (struct tcphdr *)log->payload.pkt.l4hdr;
tcp_flags_str = _bf_tcp_flags_to_str(tcphdr);

(void)fprintf(stdout, " TCP : %s%-5u%s → %s%-5u%s",
Expand All @@ -522,7 +530,7 @@ static void _bf_chain_log_l4(const struct bf_log *log)
break;

case IPPROTO_UDP:
udphdr = (struct udphdr *)log->l4hdr;
udphdr = (struct udphdr *)log->payload.pkt.l4hdr;

(void)fprintf(stdout, " UDP : %s%-5u%s → %s%-5u%s [len=%u]\n",
bf_logger_get_color(BF_COLOR_LIGHT_YELLOW, BF_STYLE_BOLD),
Expand All @@ -535,7 +543,7 @@ static void _bf_chain_log_l4(const struct bf_log *log)
break;

case IPPROTO_ICMP:
icmphdr = (struct icmphdr *)log->l4hdr;
icmphdr = (struct icmphdr *)log->payload.pkt.l4hdr;

(void)fprintf(stdout, " ICMP : type=%-3u code=%-3u",
icmphdr->type, icmphdr->code);
Expand All @@ -550,7 +558,7 @@ static void _bf_chain_log_l4(const struct bf_log *log)
break;

case IPPROTO_ICMPV6:
icmp6hdr = (struct icmp6hdr *)log->l4hdr;
icmp6hdr = (struct icmp6hdr *)log->payload.pkt.l4hdr;

(void)fprintf(stdout, " ICMPv6 : type=%-3u code=%-3u",
icmp6hdr->icmp6_type, icmp6hdr->icmp6_code);
Expand All @@ -571,14 +579,114 @@ static void _bf_chain_log_l4(const struct bf_log *log)
}
}

static void _bf_chain_log_sock_addr(const struct bf_log *log)
{
uint8_t req;

assert(log);

req = log->payload.sock_addr.req_log_opts;

if (req & (1 << BF_LOG_OPT_INTERNET)) {
char src_addr[INET6_ADDRSTRLEN];
char dst_addr[INET6_ADDRSTRLEN];
const char *protocol = bf_ipproto_to_str(log->l4_proto);
const char *label = NULL;
const char *color = NULL;
int family = 0;

if (log->l3_proto == ETH_P_IP) {
family = AF_INET;
label = "IPv4";
color = bf_logger_get_color(BF_COLOR_CYAN, BF_STYLE_BOLD);
} else if (log->l3_proto == ETH_P_IPV6) {
family = AF_INET6;
label = "IPv6";
color = bf_logger_get_color(BF_COLOR_LIGHT_CYAN, BF_STYLE_BOLD);
}

if (label) {
inet_ntop(family, log->payload.sock_addr.saddr, src_addr,
sizeof(src_addr));
inet_ntop(family, log->payload.sock_addr.daddr, dst_addr,
sizeof(dst_addr));

(void)fprintf(stdout, " %-10s: %s%-15s%s → %s%-15s%s", label,
color, src_addr,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET),
color, dst_addr,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));

if (protocol) {
(void)fprintf(
stdout, " [%s%s%s]\n",
bf_logger_get_color(BF_COLOR_LIGHT_MAGENTA, BF_STYLE_BOLD),
protocol,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));
} else {
(void)fprintf(stdout, " [proto=%u]\n", log->l4_proto);
}
} else {
(void)fprintf(stdout, " Internet : <unknown protocol 0x%04x>\n",
log->l3_proto);
}
}

if (req & (1 << BF_LOG_OPT_TRANSPORT)) {
const char *l4_label;

switch (log->l4_proto) {
case IPPROTO_TCP:
l4_label = "TCP";
break;
case IPPROTO_UDP:
l4_label = "UDP";
break;
default:
l4_label = "Transport";
break;
}

(void)fprintf(stdout, " %-10s: → %s%-5u%s\n", l4_label,
bf_logger_get_color(BF_COLOR_LIGHT_YELLOW, BF_STYLE_BOLD),
log->payload.sock_addr.dport,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));
}

if (req & (1 << BF_LOG_OPT_PID)) {
(void)fprintf(stdout, " PID : %s%u%s\n",
bf_logger_get_color(BF_COLOR_LIGHT_YELLOW, BF_STYLE_BOLD),
log->payload.sock_addr.pid,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));
}

if (req & (1 << BF_LOG_OPT_COMM)) {
(void)fprintf(stdout, " Process : %s%.16s%s\n",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: nit: The format specifier %.16s hardcodes the comm buffer size. BF_COMM_LEN is defined to 16 in this PR and could be used here via %.*s to stay in sync if the buffer size ever changes:

(void)fprintf(stdout, "  Process   : %s%.*s%s\n",
              bf_logger_get_color(BF_COLOR_LIGHT_GREEN, BF_STYLE_BOLD),
              BF_COMM_LEN, log->payload.sock_addr.comm,
              bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));

bf_logger_get_color(BF_COLOR_LIGHT_GREEN, BF_STYLE_BOLD),
log->payload.sock_addr.comm,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));
}
}

void bfc_print_log(const struct bf_log *log)
{
assert(log);

_bf_chain_log_header(log);

if (log->req_headers & (1 << BF_PKTHDR_LINK))
_bf_chain_log_l2(log);
if (log->req_headers & (1 << BF_PKTHDR_INTERNET))
_bf_chain_log_l3(log);
if (log->req_headers & (1 << BF_PKTHDR_TRANSPORT))
_bf_chain_log_l4(log);
switch (log->log_type) {
case BF_LOG_TYPE_PACKET:
if (log->payload.pkt.req_headers & (1 << BF_LOG_OPT_LINK))
_bf_chain_log_l2(log);
if (log->payload.pkt.req_headers & (1 << BF_LOG_OPT_INTERNET))
_bf_chain_log_l3(log);
if (log->payload.pkt.req_headers & (1 << BF_LOG_OPT_TRANSPORT))
_bf_chain_log_l4(log);
break;
case BF_LOG_TYPE_SOCK_ADDR:
_bf_chain_log_sock_addr(log);
break;
default:
break;
}
}
5 changes: 3 additions & 2 deletions src/libbpfilter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ set(libbpfilter_srcs
${CMAKE_CURRENT_SOURCE_DIR}/cgen/jmp.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/jmp.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/cmp.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/cmp.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/meta.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/meta.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/packet.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/packet.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/packet.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/packet.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/set.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/matcher/set.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/nf.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/nf.c
${CMAKE_CURRENT_SOURCE_DIR}/cgen/printer.h ${CMAKE_CURRENT_SOURCE_DIR}/cgen/printer.c
Expand Down Expand Up @@ -109,8 +109,9 @@ bf_target_add_elfstubs(libbpfilter
"parse_ipv6_eh"
"parse_ipv6_nh"
"update_counters"
"log"
"pkt_log"
"flow_hash"
"sock_addr_log"
)

target_compile_definitions(libbpfilter
Expand Down
Loading
Loading