From 9642a79381793daedc1d58b3ac4c3c61a2ec38f1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 16 Mar 2018 17:39:29 +0100 Subject: src: fix routing header support We can't use nft_exthdr_op to encode routing header, it breaks ipv6 extension header support. When encountering RT header, userspace did now set a new ipv6 exthdr mode, but old kernel doesn't know about this, so this failed with -EOPNOTSUPP. Revert that part and use NFT_EXTHDR_OP_IPV6. When decoding a routing extension header, try the various route types until we find a match. Note this patch isn't complete: 'srh tag 127' creates following expressions: [ exthdr load 2b @ 43 + 6 => reg 1 ] [ cmp eq reg 1 0x00007f00 ] It should instead insert a dependency test ("rt type 4"): [ exthdr load 1b @ 43 + 2 => reg 1 ] [ cmp eq reg 1 0x00000004 ] [ exthdr load 2b @ 43 + 6 => reg 1 ] [ cmp eq reg 1 0x00007e00 ] nft should then use this to infer the routing header type. While add it, document the srh option. Fixes: 1400288f6d39d ("src: handle rt0 and rt2 properly") Reported-by: Phil Sutter Signed-off-by: Florian Westphal Acked-by: Ahmed Abdelsalam --- src/exthdr.c | 55 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'src/exthdr.c') diff --git a/src/exthdr.c b/src/exthdr.c index cbe0da86..06a82070 100644 --- a/src/exthdr.c +++ b/src/exthdr.c @@ -93,19 +93,6 @@ struct expr *exthdr_expr_alloc(const struct location *loc, BYTEORDER_BIG_ENDIAN, tmpl->len); expr->exthdr.desc = desc; expr->exthdr.tmpl = tmpl; - if (desc != NULL && desc->proto_key >= 0) { - switch (desc->proto_key) { - case 0: - expr->exthdr.op = NFT_EXTHDR_OP_RT0; - break; - case 2: - expr->exthdr.op = NFT_EXTHDR_OP_RT2; - break; - case 4: - expr->exthdr.op = NFT_EXTHDR_OP_RT4; - break; - } - } return expr; } @@ -148,6 +135,24 @@ const struct exthdr_desc *exthdr_find_proto(uint8_t proto) return exthdr_protocols[proto]; } +static const struct proto_hdr_template * +exthdr_rt_find(struct expr *expr, const struct exthdr_desc *desc) +{ + const struct proto_hdr_template *tmpl; + unsigned int i; + + for (i = 0; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + if (tmpl->offset == expr->exthdr.offset && + tmpl->len == expr->len) { + expr->exthdr.desc = desc; + return tmpl; + } + } + + return NULL; +} + void exthdr_init_raw(struct expr *expr, uint8_t type, unsigned int offset, unsigned int len, enum nft_exthdr_op op, uint32_t flags) @@ -164,13 +169,7 @@ void exthdr_init_raw(struct expr *expr, uint8_t type, expr->exthdr.offset = offset; expr->exthdr.desc = NULL; - if (op == NFT_EXTHDR_OP_RT0) - expr->exthdr.desc = &exthdr_rt0; - else if (op == NFT_EXTHDR_OP_RT2) - expr->exthdr.desc = &exthdr_rt2; - else if (op == NFT_EXTHDR_OP_RT4) - expr->exthdr.desc = &exthdr_rt4; - else if (type < array_size(exthdr_protocols)) + if (type < array_size(exthdr_protocols)) expr->exthdr.desc = exthdr_protocols[type]; if (expr->exthdr.desc == NULL) @@ -182,6 +181,18 @@ void exthdr_init_raw(struct expr *expr, uint8_t type, goto out; } + if (expr->exthdr.desc == &exthdr_rt) { + tmpl = exthdr_rt_find(expr, &exthdr_rt4); + if (tmpl) + goto out; + tmpl = exthdr_rt_find(expr, &exthdr_rt0); + if (tmpl) + goto out; + tmpl = exthdr_rt_find(expr, &exthdr_rt2); + if (tmpl) + goto out; + } + tmpl = &exthdr_unknown_template; out: expr->exthdr.tmpl = tmpl; @@ -274,7 +285,7 @@ const struct exthdr_desc exthdr_rt0 = { .templates = { [RT0HDR_RESERVED] = RT0_FIELD("reserved", ip6r0_reserved, &integer_type), [RT0HDR_ADDR_1] = RT0_FIELD("addr[1]", ip6r0_addr[0], &ip6addr_type), - [RT0HDR_ADDR_1 + 1] = RT0_FIELD("addr[2]", ip6r0_addr[0], &ip6addr_type), + [RT0HDR_ADDR_1 + 1] = RT0_FIELD("addr[2]", ip6r0_addr[1], &ip6addr_type), // ... }, }; @@ -291,7 +302,7 @@ const struct exthdr_desc exthdr_rt4 = { [RT4HDR_FLAGS] = RT4_FIELD("flags", ip6r4_flags, &integer_type), [RT4HDR_TAG] = RT4_FIELD("tag", ip6r4_tag, &integer_type), [RT4HDR_SID_1] = RT4_FIELD("sid[1]", ip6r4_segments[0], &ip6addr_type), - [RT4HDR_SID_1 + 1] = RT4_FIELD("sid[1]", ip6r4_segments[0], &ip6addr_type), + [RT4HDR_SID_1 + 1] = RT4_FIELD("sid[2]", ip6r4_segments[1], &ip6addr_type), // ... }, }; -- cgit v1.2.3