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 --- doc/nft.xml | 13 +++++++++ include/linux/netfilter/nf_tables.h | 3 -- src/exthdr.c | 55 ++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/doc/nft.xml b/doc/nft.xml index 07f4f277..962e2933 100644 --- a/doc/nft.xml +++ b/doc/nft.xml @@ -4074,6 +4074,15 @@ input meta iifname enp2s0 arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh type + + srh + + flags + tag + sid + seg-left + + tcp option @@ -4154,6 +4163,10 @@ input meta iifname enp2s0 arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh mh Mobility Header + + srh + Segment Routing Header + diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 1a98f03a..517a39a0 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -731,9 +731,6 @@ enum nft_exthdr_flags { enum nft_exthdr_op { NFT_EXTHDR_OP_IPV6, NFT_EXTHDR_OP_TCPOPT, - NFT_EXTHDR_OP_RT0, - NFT_EXTHDR_OP_RT2, - NFT_EXTHDR_OP_RT4, __NFT_EXTHDR_OP_MAX }; #define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1) 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