From ec3f702ac10e07cb8bd9dbe806745244038bac10 Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Wed, 26 Feb 2025 05:54:04 +0000 Subject: [PATCH 1/2] WIP/RFC: add support for manual link layer offset changes A few pcap-filter expressions implicitly change the link layer offset. There are other less common headers without their own keywords that might require skipping over some bytes in a link layer adjacent header to get to higher layer network headers. This commit adds an "offset" term that takes a + or - value to adjust the link layer offets in the same way VLAN does. This allows for something like `ethertype 0x8926 and offset +6 and vlan 14 and ip host 192.0.2.14` to skip over a vntag header, process a VLAN header, and look for an IPv4 host. --- gencode.c | 8 ++++++++ gencode.h | 2 ++ grammar.y.in | 5 +++++ scanner.l | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/gencode.c b/gencode.c index 4ec901f6b3..a21f3cc522 100644 --- a/gencode.c +++ b/gencode.c @@ -10712,3 +10712,11 @@ gen_atmmulti_abbrev(compiler_state_t *cstate, int type) } return b1; } + +struct block *gen_offset_adjustment(compiler_state_t *cstate, int n) { + struct block *b = new_block(cstate, BPF_JMP|BPF_JA); + b->s.k = 0; // Jump by 0 bytes, effectively a no-op + cstate->off_linkpl.constant_part += n; + cstate->off_linktype.constant_part += n; + return b; +} diff --git a/gencode.h b/gencode.h index 22752ed1aa..0a8afcad41 100644 --- a/gencode.h +++ b/gencode.h @@ -378,6 +378,8 @@ struct block *gen_pf_action(compiler_state_t *, int); struct block *gen_p80211_type(compiler_state_t *, bpf_u_int32, bpf_u_int32); struct block *gen_p80211_fcdir(compiler_state_t *, bpf_u_int32); +struct block *gen_offset_adjustment(compiler_state_t*, int); + /* * Representation of a program as a tree of blocks, plus current mark. * A block is marked if only if its mark equals the current mark. diff --git a/grammar.y.in b/grammar.y.in index 4df433960e..f77b9c2512 100644 --- a/grammar.y.in +++ b/grammar.y.in @@ -401,12 +401,14 @@ DIAG_OFF_BISON_BYACC %token RADIO %token FISU LSSU MSU HFISU HLSSU HMSU %token SIO OPC DPC SLS HSIO HOPC HDPC HSLS +%token OFFSET OFFSET_PLUS OFFSET_MINUS %token LEX_ERROR %type ID EID AID %type HID HID6 %type NUM %type action reason type subtype type_subtype dir +%type OFFSET_PLUS OFFSET_MINUS %left OR AND %nonassoc '!' @@ -689,6 +691,8 @@ other: pqual TK_BROADCAST { CHECK_PTR_VAL(($$ = gen_broadcast(cstate, $1))); } | GENEVE { CHECK_PTR_VAL(($$ = gen_geneve(cstate, 0, 0))); } | VXLAN pnum { CHECK_PTR_VAL(($$ = gen_vxlan(cstate, $2, 1))); } | VXLAN { CHECK_PTR_VAL(($$ = gen_vxlan(cstate, 0, 0))); } + | OFFSET OFFSET_PLUS { CHECK_PTR_VAL(($$ = gen_offset_adjustment(cstate, $2))); } + | OFFSET OFFSET_MINUS { CHECK_PTR_VAL(($$ = gen_offset_adjustment(cstate, -$2))); } | pfvar { $$ = $1; } | pqual p80211 { $$ = $2; } | pllc { $$ = $1; } @@ -942,4 +946,5 @@ mtp3fieldvalue: NUM { mtp3listvalue: mtp3fieldvalue | mtp3listvalue or mtp3fieldvalue { gen_or($1.b, $3.b); $$ = $3; } ; + %% diff --git a/scanner.l b/scanner.l index 0fa8f40724..d69ac69dfa 100644 --- a/scanner.l +++ b/scanner.l @@ -471,6 +471,11 @@ tcp-ack { yylval->h = 0x10; return NUM; } tcp-urg { yylval->h = 0x20; return NUM; } tcp-ece { yylval->h = 0x40; return NUM; } tcp-cwr { yylval->h = 0x80; return NUM; } + +offset { return OFFSET; } +\+[0-9]+ { stou(yytext+1, yylval, yyextra); return OFFSET_PLUS; } +-[0-9]+ { stou(yytext+1, yylval, yyextra); return OFFSET_MINUS; } + [A-Za-z0-9]([-_.A-Za-z0-9]*[.A-Za-z0-9])? { yylval->s = sdup(yyextra, (char *)yytext); return ID; } "\\"[^ !()\n\t]+ { yylval->s = sdup(yyextra, (char *)yytext + 1); return ID; } From 26233df28dbd0cdd2a0fe752fce896d2618e0af8 Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Fri, 28 Feb 2025 22:38:38 +0000 Subject: [PATCH 2/2] WIP/RFC: allow manual computed offset adjusment Some keywords, like VLAN and MPLS, implicitly adjust offsets. New protocols not yet handled by pcap-filter may also require offset jumping, but are not part of the existing known protocol code. Allowing arbitrary offset adjustments by users supports overcoming long standing "vlan X or vlan Y" issues, albeit with awkward syntax, and for jumping over new protocols not yet handled explicitly, like VNTag. This second attempt switches from adjusting compile time static offsets to using the variable arguments already baked into the offset structs to allow use with nvarg to generate either fixed or dynamically computed jumps. There is no support for "set to X"; all actions are increment or decrement. There is also no support at this time for selecting which offsets to adjust. That can be added once the basic concept is flushed out and there's some agreement on if this is a desired feature, syntax, and implementation approach. Initially my thoughts for a next commit to support selecting offset are something like `off-linkhdr` or `off-nl` or `off-linkpl` for each type of offset that might be reasonable for an end user to want to adjust themseleves. --- gencode.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++------ gencode.h | 2 +- grammar.y.in | 7 ++----- scanner.l | 5 +---- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/gencode.c b/gencode.c index a21f3cc522..7b115996bd 100644 --- a/gencode.c +++ b/gencode.c @@ -10713,10 +10713,51 @@ gen_atmmulti_abbrev(compiler_state_t *cstate, int type) return b1; } -struct block *gen_offset_adjustment(compiler_state_t *cstate, int n) { - struct block *b = new_block(cstate, BPF_JMP|BPF_JA); - b->s.k = 0; // Jump by 0 bytes, effectively a no-op - cstate->off_linkpl.constant_part += n; - cstate->off_linktype.constant_part += n; - return b; +struct block* +gen_offset(compiler_state_t *cstate, struct arth *a_arg) +{ + struct arth *a = a_arg; + struct block *b; + struct slist *s, *s1; + bpf_abs_offset *offsets[2]; + int i; + offsets[0] = &(cstate->off_linkpl); + offsets[1] = &(cstate->off_linktype)}; + + /* + * Catch errors reported by us and routines below us, and return NULL + * on an error. + */ + if (setjmp(cstate->top_ctx)) + return (NULL); + + s = NULL; + b = gen_true(cstate); + for (i = 0; i < 2; ++i) { + bpf_abs_offset *off = offsets[i]; + // Make sure the offset being adjusted is variable + if (!off->is_variable) + off->is_variable = 1; + if (off->reg == -1) + off->reg = alloc_reg(cstate); + + // Put the current offset into the accumulator + s = new_stmt(cstate, BPF_LD|BPF_MEM); + s->s.k = off->reg; + + // Put the computed offset into the extra register + s1 = xfer_to_x(cstate, a); + sappend(s, s1); + + // Add to the accumulator from the computed offset in the extra register + s1 = new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_X); + sappend(s, s1); + + s1 = new_stmt(cstate, BPF_ST); + s1->s.k = off->reg; + sappend(s, s1); + } + sappend(a->s, s); + b->stmts = a->s; + return b; } diff --git a/gencode.h b/gencode.h index 0a8afcad41..3ed4019f93 100644 --- a/gencode.h +++ b/gencode.h @@ -378,7 +378,7 @@ struct block *gen_pf_action(compiler_state_t *, int); struct block *gen_p80211_type(compiler_state_t *, bpf_u_int32, bpf_u_int32); struct block *gen_p80211_fcdir(compiler_state_t *, bpf_u_int32); -struct block *gen_offset_adjustment(compiler_state_t*, int); +struct block *gen_offset(compiler_state_t *, struct arth *); /* * Representation of a program as a tree of blocks, plus current mark. diff --git a/grammar.y.in b/grammar.y.in index f77b9c2512..7ef420c666 100644 --- a/grammar.y.in +++ b/grammar.y.in @@ -401,14 +401,13 @@ DIAG_OFF_BISON_BYACC %token RADIO %token FISU LSSU MSU HFISU HLSSU HMSU %token SIO OPC DPC SLS HSIO HOPC HDPC HSLS -%token OFFSET OFFSET_PLUS OFFSET_MINUS +%token OFFSET %token LEX_ERROR %type ID EID AID %type HID HID6 %type NUM %type action reason type subtype type_subtype dir -%type OFFSET_PLUS OFFSET_MINUS %left OR AND %nonassoc '!' @@ -691,8 +690,7 @@ other: pqual TK_BROADCAST { CHECK_PTR_VAL(($$ = gen_broadcast(cstate, $1))); } | GENEVE { CHECK_PTR_VAL(($$ = gen_geneve(cstate, 0, 0))); } | VXLAN pnum { CHECK_PTR_VAL(($$ = gen_vxlan(cstate, $2, 1))); } | VXLAN { CHECK_PTR_VAL(($$ = gen_vxlan(cstate, 0, 0))); } - | OFFSET OFFSET_PLUS { CHECK_PTR_VAL(($$ = gen_offset_adjustment(cstate, $2))); } - | OFFSET OFFSET_MINUS { CHECK_PTR_VAL(($$ = gen_offset_adjustment(cstate, -$2))); } + | OFFSET arth { CHECK_PTR_VAL(($$ = gen_offset(cstate, $2))); } | pfvar { $$ = $1; } | pqual p80211 { $$ = $2; } | pllc { $$ = $1; } @@ -946,5 +944,4 @@ mtp3fieldvalue: NUM { mtp3listvalue: mtp3fieldvalue | mtp3listvalue or mtp3fieldvalue { gen_or($1.b, $3.b); $$ = $3; } ; - %% diff --git a/scanner.l b/scanner.l index d69ac69dfa..2b28f8291e 100644 --- a/scanner.l +++ b/scanner.l @@ -472,10 +472,7 @@ tcp-urg { yylval->h = 0x20; return NUM; } tcp-ece { yylval->h = 0x40; return NUM; } tcp-cwr { yylval->h = 0x80; return NUM; } -offset { return OFFSET; } -\+[0-9]+ { stou(yytext+1, yylval, yyextra); return OFFSET_PLUS; } --[0-9]+ { stou(yytext+1, yylval, yyextra); return OFFSET_MINUS; } - +offset return OFFSET; [A-Za-z0-9]([-_.A-Za-z0-9]*[.A-Za-z0-9])? { yylval->s = sdup(yyextra, (char *)yytext); return ID; } "\\"[^ !()\n\t]+ { yylval->s = sdup(yyextra, (char *)yytext + 1); return ID; }