* [nft PATCH] src: Implement ip {s,d}addr6 expressions
@ 2025-12-09 15:40 Phil Sutter
2025-12-09 16:04 ` Florian Westphal
0 siblings, 1 reply; 2+ messages in thread
From: Phil Sutter @ 2025-12-09 15:40 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: Florian Westphal, Eric Garver, netfilter-devel
These are pseudo payload expressions which represent an IPv4 packet's
source or destination address as an IPv4-mapped IPv6 address as
described in RFC4291 section 2.5.5.2[1]. It helps sharing ruleset
elements like IP address-based sets/maps between rules for IPv4 and IPv6
traffic.
Internally, this is implemented as a new binop expression which contains
a respective ip {s,d}addr expression. Upon serialization, it resolves
into an immediate expression followed by said payload expression.
Therefore it does not require specific kernel support.
Upon deserialization of an ip {s,d}addr expression, the new construct is
detected by probing for a previous immediate expression loading the
expected value into preceeding registers.
Special casing is needed in binop_expr_clone() and expr_postprocess() to
avoid expr->right dereference and to maintain payload expression's
behaviour regarding OP_EQ printing.
[1] https://www.rfc-editor.org/rfc/rfc4291#section-2.5.5.2
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
doc/payload-expression.txt | 8 ++-
include/expression.h | 1 +
src/evaluate.c | 9 +++
src/expression.c | 11 +++-
src/json.c | 10 ++++
src/netlink_delinearize.c | 34 +++++++++++
src/netlink_linearize.c | 23 +++++++
src/parser_bison.y | 12 ++++
src/parser_json.c | 30 ++++++++--
src/scanner.l | 4 ++
tests/py/ip/ip.t | 6 ++
tests/py/ip/ip.t.json | 102 ++++++++++++++++++++++++++++++++
tests/py/ip/ip.t.payload | 34 +++++++++++
tests/py/ip/ip.t.payload.bridge | 44 ++++++++++++++
tests/py/ip/ip.t.payload.inet | 44 ++++++++++++++
tests/py/ip/ip.t.payload.netdev | 44 ++++++++++++++
16 files changed, 408 insertions(+), 8 deletions(-)
diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt
index 8b538968c84b5..e301e2e9b4ce2 100644
--- a/doc/payload-expression.txt
+++ b/doc/payload-expression.txt
@@ -90,7 +90,7 @@ ipv4_addr
IPV4 HEADER EXPRESSION
~~~~~~~~~~~~~~~~~~~~~~
[verse]
-*ip* {*version* | *hdrlength* | *dscp* | *ecn* | *length* | *id* | *frag-off* | *ttl* | *protocol* | *checksum* | *saddr* | *daddr* }
+*ip* {*version* | *hdrlength* | *dscp* | *ecn* | *length* | *id* | *frag-off* | *ttl* | *protocol* | *checksum* | *saddr* | *daddr* | *saddr6* | *daddr6* }
.IPv4 header expression
[options="header"]
@@ -132,6 +132,12 @@ ipv4_addr
|daddr|
Destination address |
ipv4_addr
+|saddr6|
+Source address in mapped notation |
+ipv6_addr
+|daddr6|
+Destination address in mapped notation |
+ipv6_addr
|======================
Careful with matching on *ip length*: The Linux kernel might aggregate several
diff --git a/include/expression.h b/include/expression.h
index a960f8cb8b087..1e494b3f14230 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -107,6 +107,7 @@ enum ops {
OP_LTE,
OP_GTE,
OP_NEG,
+ OP_V6MAP,
__OP_MAX
};
#define OP_MAX (__OP_MAX - 1)
diff --git a/src/evaluate.c b/src/evaluate.c
index 204845554f0b0..1ea859e5143c6 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1531,6 +1531,15 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
unsigned int max_shift_len = ctx->ectx.len;
int ret = -1;
+ if (op->op == OP_V6MAP) {
+ op->len = sizeof(struct in6_addr) * BITS_PER_BYTE;
+ op->dtype = datatype_get(&ip6addr_type);
+ ret = expr_evaluate(ctx, &op->left);
+ __expr_set_context(&ctx->ectx, op->dtype,
+ op->byteorder, op->len, 0);
+ return ret;
+ }
+
if (ctx->recursion.binop >= USHRT_MAX)
return expr_binary_error(ctx->msgs, op, NULL,
"Binary operation limit %u reached ",
diff --git a/src/expression.c b/src/expression.c
index e036c4bb69965..2a8f5153c01d3 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -854,13 +854,19 @@ bool must_print_eq_op(const struct expr *expr)
expr->right->etype == EXPR_VALUE)
return true;
- return expr->left->etype == EXPR_BINOP;
+ return expr->left->etype == EXPR_BINOP &&
+ expr->left->op != OP_V6MAP;
}
static void binop_expr_print(const struct expr *expr, struct output_ctx *octx)
{
binop_arg_print(expr, expr->left, octx);
+ if (expr->op == OP_V6MAP) {
+ nft_print(octx, "6");
+ return;
+ }
+
if (expr_op_symbols[expr->op] &&
(expr->op != OP_EQ || must_print_eq_op(expr)))
nft_print(octx, " %s ", expr_op_symbols[expr->op]);
@@ -873,7 +879,8 @@ static void binop_expr_print(const struct expr *expr, struct output_ctx *octx)
static void binop_expr_clone(struct expr *new, const struct expr *expr)
{
new->left = expr_clone(expr->left);
- new->right = expr_clone(expr->right);
+ if (expr->right)
+ new->right = expr_clone(expr->right);
}
static void binop_expr_destroy(struct expr *expr)
diff --git a/src/json.c b/src/json.c
index 9fb6d715a53de..86bb9d9351262 100644
--- a/src/json.c
+++ b/src/json.c
@@ -668,6 +668,16 @@ __binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx)
{
+ if (expr->op == OP_V6MAP) {
+ json_t *root = payload_expr_json(expr->left, octx);
+ json_t *payload = json_object_get(root, "payload");
+ const char *field;
+
+ json_unpack(payload, "{s:s}", "field", &field);
+ json_object_set_new(payload, "field",
+ json_sprintf("%s6", field));
+ return root;
+ }
return nft_json_pack("{s:o}", expr_op_symbols[expr->op],
__binop_expr_json(expr->op, expr, octx));
}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 9561e298aebb5..1699b247cb55d 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -17,6 +17,7 @@
#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter.h>
#include <net/ethernet.h>
+#include <netinet/ip.h>
#include <netlink.h>
#include <rule.h>
#include <statement.h>
@@ -649,10 +650,32 @@ static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
netlink_set_register(ctx, dreg, expr);
}
+static bool payload_is_ip_addr(const struct expr *expr)
+{
+ size_t saddr_offset = offsetof(struct iphdr, saddr) * BITS_PER_BYTE;
+ size_t daddr_offset = offsetof(struct iphdr, daddr) * BITS_PER_BYTE;
+
+ return expr->payload.base == PROTO_BASE_NETWORK_HDR &&
+ expr->len == sizeof(struct in_addr) * BITS_PER_BYTE &&
+ (expr->payload.offset == saddr_offset ||
+ expr->payload.offset == daddr_offset);
+}
+
+static bool immediate_is_v6map_pfx(const struct expr *expr)
+{
+ return expr &&
+ expr->etype == EXPR_VALUE &&
+ expr->len == (sizeof(struct in6_addr) -
+ sizeof(struct in_addr)) * BITS_PER_BYTE &&
+ !mpz_cmp_ui(expr->value, 0xffff);
+}
+
static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
+ size_t v6map_reg_off = (sizeof(struct in6_addr) -
+ sizeof(struct in_addr)) / NFT_REG32_SIZE;
enum nft_registers dreg;
uint32_t base, offset, len;
struct expr *expr;
@@ -670,6 +693,15 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
dreg = netlink_parse_register(nle, NFTNL_EXPR_PAYLOAD_DREG);
+ if (dreg >= v6map_reg_off &&
+ payload_is_ip_addr(expr) &&
+ immediate_is_v6map_pfx(ctx->registers[dreg - v6map_reg_off])) {
+ expr = binop_expr_alloc(loc, OP_V6MAP, expr, NULL);
+ expr->len = sizeof(struct in6_addr) * BITS_PER_BYTE;
+ expr->dtype = datatype_get(&ip6addr_type);
+ dreg -= v6map_reg_off;
+ }
+
if (ctx->inner)
ctx->inner_reg = dreg;
@@ -2925,6 +2957,8 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
return;
}
break;
+ case OP_V6MAP:
+ return;
default:
if (expr->right->len > expr->left->len) {
expr_set_type(expr->right, expr->left->dtype,
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index ac0eaff9a23ca..61577fe06326d 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -807,6 +807,26 @@ static void netlink_gen_bitwise_bool(struct netlink_linearize_ctx *ctx,
nftnl_rule_add_expr(ctx->nlr, nle);
}
+static void netlink_gen_v6map(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr, enum nft_registers dreg)
+{
+ size_t pfxlen = sizeof(struct in6_addr) - sizeof(struct in_addr);
+ struct nft_data_linearize nld = {
+ .len = pfxlen,
+ .value = { 0, 0, htonl(0xffff) },
+ };
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("immediate");
+ netlink_put_register(nle, NFTNL_EXPR_IMM_DREG, dreg);
+ nftnl_expr_set_imm(nle, NFTNL_EXPR_IMM_DATA,
+ nld.value, nld.len, nld.byteorder);
+ nftnl_rule_add_expr(ctx->nlr, nle);
+
+ dreg += netlink_register_space(nld.len * BITS_PER_BYTE);
+ netlink_gen_expr(ctx, expr->left, dreg);
+}
+
static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
@@ -816,6 +836,9 @@ static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
case OP_RSHIFT:
netlink_gen_bitwise_shift(ctx, expr, dreg);
break;
+ case OP_V6MAP:
+ netlink_gen_v6map(ctx, expr, dreg);
+ break;
default:
if (expr_is_constant(expr->right))
netlink_gen_bitwise_mask_xor(ctx, expr, dreg);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 3ceef79469d7d..d2dbb5354f0c6 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -403,6 +403,8 @@ int nft_lex(void *, void *, void *);
%token ETHER "ether"
%token SADDR "saddr"
%token DADDR "daddr"
+%token SADDR6 "saddr6"
+%token DADDR6 "daddr6"
%token TYPE "type"
%token VLAN "vlan"
@@ -926,6 +928,7 @@ int nft_lex(void *, void *, void *);
%type <expr> ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr
%destructor { expr_free($$); } ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr
%type <val> ip_hdr_field icmp_hdr_field igmp_hdr_field
+%type <val> ip_pseudo_hdr_field
%type <val> ip_option_type ip_option_field
%type <expr> ip6_hdr_expr icmp6_hdr_expr
%destructor { expr_free($$); } ip6_hdr_expr icmp6_hdr_expr
@@ -5956,6 +5959,11 @@ ip_hdr_expr : IP ip_hdr_field close_scope_ip
{
$$ = payload_expr_alloc(&@$, &proto_ip, $2);
}
+ | IP ip_pseudo_hdr_field close_scope_ip
+ {
+ $$ = payload_expr_alloc(&@$, &proto_ip, $2);
+ $$ = binop_expr_alloc(&@$, OP_V6MAP, $$, NULL);
+ }
| IP OPTION ip_option_type ip_option_field close_scope_ip
{
$$ = ipopt_expr_alloc(&@$, $3, $4);
@@ -5988,6 +5996,10 @@ ip_hdr_field : HDRVERSION { $$ = IPHDR_VERSION; }
| DADDR { $$ = IPHDR_DADDR; }
;
+ip_pseudo_hdr_field : SADDR6 { $$ = IPHDR_SADDR; }
+ | DADDR6 { $$ = IPHDR_DADDR; }
+ ;
+
ip_option_type : LSRR { $$ = IPOPT_LSRR; }
| RR { $$ = IPOPT_RR; }
| SSRR { $$ = IPOPT_SSRR; }
diff --git a/src/parser_json.c b/src/parser_json.c
index 7b4f33848ce12..fb330159be659 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -528,6 +528,22 @@ static int json_parse_payload_field(const struct proto_desc *desc,
return 1;
}
+static int json_parse_ip_pseudo_hdr_field(const struct proto_desc *desc,
+ const char *name, int *field)
+{
+ if (desc != &proto_ip)
+ return 1;
+ if (!strcmp(name, "saddr6")) {
+ *field = IPHDR_SADDR;
+ return 0;
+ }
+ if (!strcmp(name, "daddr6")) {
+ *field = IPHDR_DADDR;
+ return 0;
+ }
+ return 1;
+}
+
static int json_parse_tcp_option_type(const char *name, int *val)
{
unsigned int i;
@@ -715,15 +731,19 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
protocol);
return NULL;
}
- if (json_parse_payload_field(proto, field, &val)) {
+ if (!json_parse_payload_field(proto, field, &val)) {
+ expr = payload_expr_alloc(int_loc, proto, val);
+
+ if (proto == &proto_th)
+ expr->payload.is_raw = true;
+ } else if (!json_parse_ip_pseudo_hdr_field(proto, field, &val)) {
+ expr = payload_expr_alloc(int_loc, proto, val);
+ expr = binop_expr_alloc(int_loc, OP_V6MAP, expr, NULL);
+ } else {
json_error(ctx, "Unknown %s field '%s'.",
protocol, field);
return NULL;
}
- expr = payload_expr_alloc(int_loc, proto, val);
-
- if (proto == &proto_th)
- expr->payload.is_raw = true;
return expr;
}
diff --git a/src/scanner.l b/src/scanner.l
index df8e536be2276..6d9bbb1b289f6 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -491,6 +491,10 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"saddr" { return SADDR; }
"daddr" { return DADDR; }
}
+<SCANSTATE_IP>{
+ "saddr6" { return SADDR6; }
+ "daddr6" { return DADDR6; }
+}
"type" { scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; }
"typeof" { return TYPEOF; }
diff --git a/tests/py/ip/ip.t b/tests/py/ip/ip.t
index 47262d9a43617..32d791ac26db4 100644
--- a/tests/py/ip/ip.t
+++ b/tests/py/ip/ip.t
@@ -110,6 +110,12 @@ ip saddr & 0.0.0.255 < 0.0.0.127;ok
ip saddr & 0xffff0000 == 0xffff0000;ok;ip saddr 255.255.0.0/16
+ip saddr6 ::ffff:1.2.3.4;ok
+ip daddr6 ::ffff:1.2.3.4;ok
+ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee };ok
+ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee };ok
+ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8;ok
+
ip version 4 ip hdrlength 5;ok
ip hdrlength 0;ok
ip hdrlength 15;ok
diff --git a/tests/py/ip/ip.t.json b/tests/py/ip/ip.t.json
index 3c3a12d7117ce..40c1e0728348c 100644
--- a/tests/py/ip/ip.t.json
+++ b/tests/py/ip/ip.t.json
@@ -1350,6 +1350,108 @@
}
]
+# ip saddr6 ::ffff:1.2.3.4
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr6",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "::ffff:1.2.3.4"
+ }
+ }
+]
+
+# ip daddr6 ::ffff:1.2.3.4
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr6",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "::ffff:1.2.3.4"
+ }
+ }
+]
+
+# ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr6",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "::ffff:1.2.3.4",
+ "feed::c0:ff:ee"
+ ]
+ }
+ }
+ }
+]
+
+# ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr6",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "::ffff:1.2.3.4",
+ "feed::c0:ff:ee"
+ ]
+ }
+ }
+ }
+]
+
+# ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr6",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "::ffff:1.2.3.4"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "5.6.7.8"
+ }
+ }
+]
+
# ip version 4 ip hdrlength 5
[
{
diff --git a/tests/py/ip/ip.t.payload b/tests/py/ip/ip.t.payload
index b3442e49e5979..2d488b3e8dd9a 100644
--- a/tests/py/ip/ip.t.payload
+++ b/tests/py/ip/ip.t.payload
@@ -413,6 +413,40 @@ ip test-ip4 input
[ bitwise reg 1 = ( reg 1 & 0xffff0000 ) ^ 0x00000000 ]
[ cmp eq reg 1 0xffff0000 ]
+# ip saddr6 ::ffff:1.2.3.4
+ip test-ip4 input
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip daddr6 ::ffff:1.2.3.4
+ip test-ip4 input
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+ip test-ip4 input
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+ip test-ip4 input
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8
+ip test-ip4 input
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x05060708 ]
+
# ip version 4 ip hdrlength 5
ip test-ip4 input
[ payload load 1b @ network header + 0 => reg 1 ]
diff --git a/tests/py/ip/ip.t.payload.bridge b/tests/py/ip/ip.t.payload.bridge
index 9da3fc266f52f..376ad6686e7c8 100644
--- a/tests/py/ip/ip.t.payload.bridge
+++ b/tests/py/ip/ip.t.payload.bridge
@@ -549,6 +549,50 @@ bridge test-bridge input
[ bitwise reg 1 = ( reg 1 & 0xffff0000 ) ^ 0x00000000 ]
[ cmp eq reg 1 0xffff0000 ]
+# ip saddr6 ::ffff:1.2.3.4
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip daddr6 ::ffff:1.2.3.4
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x05060708 ]
+
# ip version 4 ip hdrlength 5
bridge test-bridge input
[ meta load protocol => reg 1 ]
diff --git a/tests/py/ip/ip.t.payload.inet b/tests/py/ip/ip.t.payload.inet
index 912ce58aa8bcf..abb73f4668dec 100644
--- a/tests/py/ip/ip.t.payload.inet
+++ b/tests/py/ip/ip.t.payload.inet
@@ -549,6 +549,50 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0xffff0000 ) ^ 0x00000000 ]
[ cmp eq reg 1 0xffff0000 ]
+# ip saddr6 ::ffff:1.2.3.4
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x02 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip daddr6 ::ffff:1.2.3.4
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x02 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x02 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x02 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x02 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x05060708 ]
+
# ip version 4 ip hdrlength 5
inet test-inet input
[ meta load nfproto => reg 1 ]
diff --git a/tests/py/ip/ip.t.payload.netdev b/tests/py/ip/ip.t.payload.netdev
index 9103ffcc0e8e7..9c8eb0a927338 100644
--- a/tests/py/ip/ip.t.payload.netdev
+++ b/tests/py/ip/ip.t.payload.netdev
@@ -448,6 +448,50 @@ netdev test-netdev ingress
[ bitwise reg 1 = ( reg 1 & 0xffff0000 ) ^ 0x00000000 ]
[ cmp eq reg 1 0xffff0000 ]
+# ip saddr6 ::ffff:1.2.3.4
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip daddr6 ::ffff:1.2.3.4
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+
+# ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee }
+ element 00000000 00000000 0000ffff 01020304 element feed0000 00000000 000000c0 00ff00ee
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 16 => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8
+netdev test-netdev egress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0800 ]
+ [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
+ [ payload load 4b @ network header + 12 => reg 11 ]
+ [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x05060708 ]
+
# ip version 4 ip hdrlength 5
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
--
2.51.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [nft PATCH] src: Implement ip {s,d}addr6 expressions
2025-12-09 15:40 [nft PATCH] src: Implement ip {s,d}addr6 expressions Phil Sutter
@ 2025-12-09 16:04 ` Florian Westphal
0 siblings, 0 replies; 2+ messages in thread
From: Florian Westphal @ 2025-12-09 16:04 UTC (permalink / raw)
To: Phil Sutter; +Cc: Pablo Neira Ayuso, Eric Garver, netfilter-devel
Phil Sutter <phil@nwl.cc> wrote:
> These are pseudo payload expressions which represent an IPv4 packet's
> source or destination address as an IPv4-mapped IPv6 address as
> described in RFC4291 section 2.5.5.2[1]. It helps sharing ruleset
> elements like IP address-based sets/maps between rules for IPv4 and IPv6
> traffic.
OK, but why do we need a new keyword for this?
> +ip saddr6 ::ffff:1.2.3.4;ok
> +ip daddr6 ::ffff:1.2.3.4;ok
> +ip saddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee };ok
> +ip daddr6 { ::ffff:1.2.3.4, feed::c0:ff:ee };ok
> +ip saddr6 ::ffff:1.2.3.4 ip daddr 5.6.7.8;ok
None of these examples make sense to me. How is this useful?
> --- a/tests/py/ip/ip.t.payload
> +++ b/tests/py/ip/ip.t.payload
> @@ -413,6 +413,40 @@ ip test-ip4 input
> [ bitwise reg 1 = ( reg 1 & 0xffff0000 ) ^ 0x00000000 ]
> [ cmp eq reg 1 0xffff0000 ]
>
> +# ip saddr6 ::ffff:1.2.3.4
> +ip test-ip4 input
> + [ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
> + [ payload load 4b @ network header + 12 => reg 11 ]
> + [ cmp eq reg 1 0x00000000 0x00000000 0x0000ffff 0x01020304 ]
Its just a more expensive way to express 'ip saddr 1.2.3.4'?
What would be useful is:
set s {
typeof ip6 saddr
...
}
nft add element inet t s { 1.2.3.4 }
... which makes nft autotranslate to '::ffff:1.2.3.4', combined
with
add rule inet t c ip saddr @s ...
... where, instead of rejecting this for the wrong size, autopads
the lookup, i.e.
[ immediate reg 1 0x00000000 0x00000000 0x0000ffff ]
[ payload load 4b @ network header + 12 => reg 11 ]
[ lookup ...
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-12-09 16:04 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-09 15:40 [nft PATCH] src: Implement ip {s,d}addr6 expressions Phil Sutter
2025-12-09 16:04 ` Florian Westphal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).