From 6fb6d8f15a82b3348184f6950a436becb06931cb Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 19 May 2021 18:03:43 +0200 Subject: [PATCH] src: Optimize prefix matches on byte-boundaries Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1934926 Upstream Status: nftables commit 25338cdb6c77a Conflicts: There is a hidden dependency on commit ee4391d0ac1e7 ("nat: transform range to prefix expression when possible"). Backport only the single chunk required to keep prefix parsing intact to avoid having to backport 9599d9d25a6b3 ("src: NAT support for intervals in maps") as a dependency which is clearly oversized for the sake of this purpose. commit 25338cdb6c77aa2f0977afbbb612571c9d325213 Author: Phil Sutter Date: Tue Oct 27 17:33:15 2020 +0100 src: Optimize prefix matches on byte-boundaries If a prefix expression's length is on a byte-boundary, it is sufficient to just reduce the length passed to "cmp" expression. No need for explicit bitwise modification of data on LHS. The relevant code is already there, used for string prefix matches. There is one exception though, namely zero-length prefixes: Kernel doesn't accept zero-length "cmp" expressions, so keep them in the old code-path for now. This patch depends upon the previous one to correctly parse odd-sized payload matches but has to extend support for non-payload LHS as well. In practice, this is needed for "ct" expressions as they allow matching against IP address prefixes, too. Signed-off-by: Phil Sutter --- src/netlink_delinearize.c | 8 ++++++-- src/netlink_linearize.c | 4 +++- tests/py/ip/ct.t.payload | 4 ---- tests/py/ip/ip.t.payload | 6 ++---- tests/py/ip/ip.t.payload.bridge | 6 ++---- tests/py/ip/ip.t.payload.inet | 6 ++---- tests/py/ip/ip.t.payload.netdev | 6 ++---- tests/py/ip6/ip6.t.payload.inet | 5 ++--- tests/py/ip6/ip6.t.payload.ip6 | 5 ++--- 9 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 8bdee12..157a473 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -291,8 +291,9 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx, if (left->len > right->len && expr_basetype(left) != &string_type) { - netlink_error(ctx, loc, "Relational expression size mismatch"); - goto err_free; + mpz_lshift_ui(right->value, left->len - right->len); + right = prefix_expr_alloc(loc, right, right->len); + right->prefix->len = left->len; } else if (left->len > 0 && left->len < right->len) { expr_free(left); left = netlink_parse_concat_expr(ctx, loc, sreg, right->len); @@ -2164,6 +2165,9 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) expr_postprocess(ctx, &expr->left); expr_postprocess(ctx, &expr->right); break; + case EXPR_PREFIX: + expr_postprocess(ctx, &expr->prefix); + break; case EXPR_SET_ELEM: expr_postprocess(ctx, &expr->key); break; diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 606d97a..25be634 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -501,7 +501,9 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, return netlink_gen_flagcmp(ctx, expr, dreg); case EXPR_PREFIX: sreg = get_register(ctx, expr->left); - if (expr_basetype(expr->left)->type != TYPE_STRING) { + if (expr_basetype(expr->left)->type != TYPE_STRING && + (!expr->right->prefix_len || + expr->right->prefix_len % BITS_PER_BYTE)) { len = div_round_up(expr->right->len, BITS_PER_BYTE); netlink_gen_expr(ctx, expr->left, sreg); right = netlink_gen_prefix(ctx, expr, sreg); diff --git a/tests/py/ip/ct.t.payload b/tests/py/ip/ct.t.payload index d5faed4..a7e08f9 100644 --- a/tests/py/ip/ct.t.payload +++ b/tests/py/ip/ct.t.payload @@ -21,25 +21,21 @@ ip test-ip4 output # ct original ip saddr 192.168.1.0/24 ip test-ip4 output [ ct load src_ip => reg 1 , dir original ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] [ cmp eq reg 1 0x0001a8c0 ] # ct reply ip saddr 192.168.1.0/24 ip test-ip4 output [ ct load src_ip => reg 1 , dir reply ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] [ cmp eq reg 1 0x0001a8c0 ] # ct original ip daddr 192.168.1.0/24 ip test-ip4 output [ ct load dst_ip => reg 1 , dir original ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] [ cmp eq reg 1 0x0001a8c0 ] # ct reply ip daddr 192.168.1.0/24 ip test-ip4 output [ ct load dst_ip => reg 1 , dir reply ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] [ cmp eq reg 1 0x0001a8c0 ] # ct l3proto ipv4 diff --git a/tests/py/ip/ip.t.payload b/tests/py/ip/ip.t.payload index d627b22..825c0f0 100644 --- a/tests/py/ip/ip.t.payload +++ b/tests/py/ip/ip.t.payload @@ -358,14 +358,12 @@ ip test-ip4 input # ip saddr 192.168.2.0/24 ip test-ip4 input - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp eq reg 1 0x0002a8c0 ] # ip saddr != 192.168.2.0/24 ip test-ip4 input - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp neq reg 1 0x0002a8c0 ] # ip saddr 192.168.3.1 ip daddr 192.168.3.100 diff --git a/tests/py/ip/ip.t.payload.bridge b/tests/py/ip/ip.t.payload.bridge index 91a4fde..e958a5b 100644 --- a/tests/py/ip/ip.t.payload.bridge +++ b/tests/py/ip/ip.t.payload.bridge @@ -466,16 +466,14 @@ bridge test-bridge input bridge test-bridge input [ meta load protocol => reg 1 ] [ cmp eq reg 1 0x00000008 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp eq reg 1 0x0002a8c0 ] # ip saddr != 192.168.2.0/24 bridge test-bridge input [ meta load protocol => reg 1 ] [ cmp eq reg 1 0x00000008 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp neq reg 1 0x0002a8c0 ] # ip saddr 192.168.3.1 ip daddr 192.168.3.100 diff --git a/tests/py/ip/ip.t.payload.inet b/tests/py/ip/ip.t.payload.inet index b9cb28a..6501473 100644 --- a/tests/py/ip/ip.t.payload.inet +++ b/tests/py/ip/ip.t.payload.inet @@ -466,16 +466,14 @@ inet test-inet input inet test-inet input [ meta load nfproto => reg 1 ] [ cmp eq reg 1 0x00000002 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp eq reg 1 0x0002a8c0 ] # ip saddr != 192.168.2.0/24 inet test-inet input [ meta load nfproto => reg 1 ] [ cmp eq reg 1 0x00000002 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp neq reg 1 0x0002a8c0 ] # ip saddr 192.168.3.1 ip daddr 192.168.3.100 diff --git a/tests/py/ip/ip.t.payload.netdev b/tests/py/ip/ip.t.payload.netdev index 588e5ca..58ae358 100644 --- a/tests/py/ip/ip.t.payload.netdev +++ b/tests/py/ip/ip.t.payload.netdev @@ -379,16 +379,14 @@ netdev test-netdev ingress netdev test-netdev ingress [ meta load protocol => reg 1 ] [ cmp eq reg 1 0x00000008 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp eq reg 1 0x0002a8c0 ] # ip saddr != 192.168.2.0/24 netdev test-netdev ingress [ meta load protocol => reg 1 ] [ cmp eq reg 1 0x00000008 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + [ payload load 3b @ network header + 12 => reg 1 ] [ cmp neq reg 1 0x0002a8c0 ] # ip saddr 192.168.3.1 ip daddr 192.168.3.100 diff --git a/tests/py/ip6/ip6.t.payload.inet b/tests/py/ip6/ip6.t.payload.inet index d015c8e..ffc9b9f 100644 --- a/tests/py/ip6/ip6.t.payload.inet +++ b/tests/py/ip6/ip6.t.payload.inet @@ -604,9 +604,8 @@ inet test-inet input inet test-inet input [ meta load nfproto => reg 1 ] [ cmp eq reg 1 0x0000000a ] - [ payload load 16b @ network header + 8 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0xffffffff 0xffffffff 0x00000000 0x00000000 ) ^ 0x00000000 0x00000000 0x00000000 0x00000000 ] - [ cmp eq reg 1 0x00000000 0x00000000 0x00000000 0x00000000 ] + [ payload load 8b @ network header + 8 => reg 1 ] + [ cmp eq reg 1 0x00000000 0x00000000 ] # ip6 saddr ::1 ip6 daddr ::2 inet test-inet input diff --git a/tests/py/ip6/ip6.t.payload.ip6 b/tests/py/ip6/ip6.t.payload.ip6 index b2e8363..18b8bcb 100644 --- a/tests/py/ip6/ip6.t.payload.ip6 +++ b/tests/py/ip6/ip6.t.payload.ip6 @@ -452,9 +452,8 @@ ip6 test-ip6 input # ip6 saddr ::/64 ip6 test-ip6 input - [ payload load 16b @ network header + 8 => reg 1 ] - [ bitwise reg 1 = (reg=1 & 0xffffffff 0xffffffff 0x00000000 0x00000000 ) ^ 0x00000000 0x00000000 0x00000000 0x00000000 ] - [ cmp eq reg 1 0x00000000 0x00000000 0x00000000 0x00000000 ] + [ payload load 8b @ network header + 8 => reg 1 ] + [ cmp eq reg 1 0x00000000 0x00000000 ] # ip6 saddr ::1 ip6 daddr ::2 ip6 test-ip6 input -- 1.8.3.1