diff --git a/target/linux/generic/backport-4.9/092-netfilter-nf_tables-fix-mismatch-in-big-endian-syste.patch b/target/linux/generic/backport-4.9/092-netfilter-nf_tables-fix-mismatch-in-big-endian-syste.patch new file mode 100644 index 0000000000..024983142c --- /dev/null +++ b/target/linux/generic/backport-4.9/092-netfilter-nf_tables-fix-mismatch-in-big-endian-syste.patch @@ -0,0 +1,323 @@ +From 0bc01df3df7a88258148518eb9ce7f88c16a6106 Mon Sep 17 00:00:00 2001 +From: Liping Zhang +Date: Wed, 8 Mar 2017 22:54:18 +0800 +Subject: netfilter: nf_tables: fix mismatch in big-endian system + +Currently, there are two different methods to store an u16 integer to +the u32 data register. For example: + u32 *dest = ®s->data[priv->dreg]; + 1. *dest = 0; *(u16 *) dest = val_u16; + 2. *dest = val_u16; + +For method 1, the u16 value will be stored like this, either in +big-endian or little-endian system: + 0 15 31 + +-+-+-+-+-+-+-+-+-+-+-+-+ + | Value | 0 | + +-+-+-+-+-+-+-+-+-+-+-+-+ + +For method 2, in little-endian system, the u16 value will be the same +as listed above. But in big-endian system, the u16 value will be stored +like this: + 0 15 31 + +-+-+-+-+-+-+-+-+-+-+-+-+ + | 0 | Value | + +-+-+-+-+-+-+-+-+-+-+-+-+ + +So later we use "memcmp(®s->data[priv->sreg], data, 2);" to do +compare in nft_cmp, nft_lookup expr ..., method 2 will get the wrong +result in big-endian system, as 0~15 bits will always be zero. + +For the similar reason, when loading an u16 value from the u32 data +register, we should use "*(u16 *) sreg;" instead of "(u16)*sreg;", +the 2nd method will get the wrong value in the big-endian system. + +So introduce some wrapper functions to store/load an u8 or u16 +integer to/from the u32 data register, and use them in the right +place. + +Signed-off-by: Liping Zhang +Signed-off-by: Pablo Neira Ayuso +--- + include/net/netfilter/nf_tables.h | 29 +++++++++++++++++++++++++ + net/ipv4/netfilter/nft_masq_ipv4.c | 8 +++---- + net/ipv4/netfilter/nft_redir_ipv4.c | 8 +++---- + net/ipv6/netfilter/nft_masq_ipv6.c | 8 +++---- + net/ipv6/netfilter/nft_redir_ipv6.c | 8 +++---- + net/netfilter/nft_ct.c | 10 ++++----- + net/netfilter/nft_meta.c | 42 +++++++++++++++++++------------------ + net/netfilter/nft_nat.c | 8 +++---- + 8 files changed, 76 insertions(+), 45 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -87,6 +87,35 @@ struct nft_regs { + }; + }; + ++/* Store/load an u16 or u8 integer to/from the u32 data register. ++ * ++ * Note, when using concatenations, register allocation happens at 32-bit ++ * level. So for store instruction, pad the rest part with zero to avoid ++ * garbage values. ++ */ ++ ++static inline void nft_reg_store16(u32 *dreg, u16 val) ++{ ++ *dreg = 0; ++ *(u16 *)dreg = val; ++} ++ ++static inline void nft_reg_store8(u32 *dreg, u8 val) ++{ ++ *dreg = 0; ++ *(u8 *)dreg = val; ++} ++ ++static inline u16 nft_reg_load16(u32 *sreg) ++{ ++ return *(u16 *)sreg; ++} ++ ++static inline u8 nft_reg_load8(u32 *sreg) ++{ ++ return *(u8 *)sreg; ++} ++ + static inline void nft_data_copy(u32 *dst, const struct nft_data *src, + unsigned int len) + { +--- a/net/ipv4/netfilter/nft_masq_ipv4.c ++++ b/net/ipv4/netfilter/nft_masq_ipv4.c +@@ -26,10 +26,10 @@ static void nft_masq_ipv4_eval(const str + memset(&range, 0, sizeof(range)); + range.flags = priv->flags; + if (priv->sreg_proto_min) { +- range.min_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_min]; +- range.max_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_max]; ++ range.min_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_min]); ++ range.max_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_max]); + } + regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, pkt->hook, + &range, pkt->out); +--- a/net/ipv4/netfilter/nft_redir_ipv4.c ++++ b/net/ipv4/netfilter/nft_redir_ipv4.c +@@ -26,10 +26,10 @@ static void nft_redir_ipv4_eval(const st + + memset(&mr, 0, sizeof(mr)); + if (priv->sreg_proto_min) { +- mr.range[0].min.all = +- *(__be16 *)®s->data[priv->sreg_proto_min]; +- mr.range[0].max.all = +- *(__be16 *)®s->data[priv->sreg_proto_max]; ++ mr.range[0].min.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_min]); ++ mr.range[0].max.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_max]); + mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + +--- a/net/ipv6/netfilter/nft_masq_ipv6.c ++++ b/net/ipv6/netfilter/nft_masq_ipv6.c +@@ -27,10 +27,10 @@ static void nft_masq_ipv6_eval(const str + memset(&range, 0, sizeof(range)); + range.flags = priv->flags; + if (priv->sreg_proto_min) { +- range.min_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_min]; +- range.max_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_max]; ++ range.min_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_min]); ++ range.max_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_max]); + } + regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out); + } +--- a/net/ipv6/netfilter/nft_redir_ipv6.c ++++ b/net/ipv6/netfilter/nft_redir_ipv6.c +@@ -26,10 +26,10 @@ static void nft_redir_ipv6_eval(const st + + memset(&range, 0, sizeof(range)); + if (priv->sreg_proto_min) { +- range.min_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_min], +- range.max_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_max], ++ range.min_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_min]); ++ range.max_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_max]); + range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + +--- a/net/netfilter/nft_ct.c ++++ b/net/netfilter/nft_ct.c +@@ -77,7 +77,7 @@ static void nft_ct_get_eval(const struct + + switch (priv->key) { + case NFT_CT_DIRECTION: +- *dest = CTINFO2DIR(ctinfo); ++ nft_reg_store8(dest, CTINFO2DIR(ctinfo)); + return; + case NFT_CT_STATUS: + *dest = ct->status; +@@ -129,10 +129,10 @@ static void nft_ct_get_eval(const struct + return; + } + case NFT_CT_L3PROTOCOL: +- *dest = nf_ct_l3num(ct); ++ nft_reg_store8(dest, nf_ct_l3num(ct)); + return; + case NFT_CT_PROTOCOL: +- *dest = nf_ct_protonum(ct); ++ nft_reg_store8(dest, nf_ct_protonum(ct)); + return; + default: + break; +@@ -149,10 +149,10 @@ static void nft_ct_get_eval(const struct + nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); + return; + case NFT_CT_PROTO_SRC: +- *dest = (__force __u16)tuple->src.u.all; ++ nft_reg_store16(dest, (__force u16)tuple->src.u.all); + return; + case NFT_CT_PROTO_DST: +- *dest = (__force __u16)tuple->dst.u.all; ++ nft_reg_store16(dest, (__force u16)tuple->dst.u.all); + return; + default: + break; +--- a/net/netfilter/nft_meta.c ++++ b/net/netfilter/nft_meta.c +@@ -45,16 +45,15 @@ void nft_meta_get_eval(const struct nft_ + *dest = skb->len; + break; + case NFT_META_PROTOCOL: +- *dest = 0; +- *(__be16 *)dest = skb->protocol; ++ nft_reg_store16(dest, (__force u16)skb->protocol); + break; + case NFT_META_NFPROTO: +- *dest = pkt->pf; ++ nft_reg_store8(dest, pkt->pf); + break; + case NFT_META_L4PROTO: + if (!pkt->tprot_set) + goto err; +- *dest = pkt->tprot; ++ nft_reg_store8(dest, pkt->tprot); + break; + case NFT_META_PRIORITY: + *dest = skb->priority; +@@ -85,14 +84,12 @@ void nft_meta_get_eval(const struct nft_ + case NFT_META_IIFTYPE: + if (in == NULL) + goto err; +- *dest = 0; +- *(u16 *)dest = in->type; ++ nft_reg_store16(dest, in->type); + break; + case NFT_META_OIFTYPE: + if (out == NULL) + goto err; +- *dest = 0; +- *(u16 *)dest = out->type; ++ nft_reg_store16(dest, out->type); + break; + case NFT_META_SKUID: + sk = skb_to_full_sk(skb); +@@ -142,22 +139,22 @@ void nft_meta_get_eval(const struct nft_ + #endif + case NFT_META_PKTTYPE: + if (skb->pkt_type != PACKET_LOOPBACK) { +- *dest = skb->pkt_type; ++ nft_reg_store8(dest, skb->pkt_type); + break; + } + + switch (pkt->pf) { + case NFPROTO_IPV4: + if (ipv4_is_multicast(ip_hdr(skb)->daddr)) +- *dest = PACKET_MULTICAST; ++ nft_reg_store8(dest, PACKET_MULTICAST); + else +- *dest = PACKET_BROADCAST; ++ nft_reg_store8(dest, PACKET_BROADCAST); + break; + case NFPROTO_IPV6: + if (ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF) +- *dest = PACKET_MULTICAST; ++ nft_reg_store8(dest, PACKET_MULTICAST); + else +- *dest = PACKET_BROADCAST; ++ nft_reg_store8(dest, PACKET_BROADCAST); + break; + case NFPROTO_NETDEV: + switch (skb->protocol) { +@@ -171,14 +168,14 @@ void nft_meta_get_eval(const struct nft_ + goto err; + + if (ipv4_is_multicast(iph->daddr)) +- *dest = PACKET_MULTICAST; ++ nft_reg_store8(dest, PACKET_MULTICAST); + else +- *dest = PACKET_BROADCAST; ++ nft_reg_store8(dest, PACKET_BROADCAST); + + break; + } + case htons(ETH_P_IPV6): +- *dest = PACKET_MULTICAST; ++ nft_reg_store8(dest, PACKET_MULTICAST); + break; + default: + WARN_ON_ONCE(1); +@@ -233,7 +230,9 @@ void nft_meta_set_eval(const struct nft_ + { + const struct nft_meta *meta = nft_expr_priv(expr); + struct sk_buff *skb = pkt->skb; +- u32 value = regs->data[meta->sreg]; ++ u32 *sreg = ®s->data[meta->sreg]; ++ u32 value = *sreg; ++ u8 pkt_type; + + switch (meta->key) { + case NFT_META_MARK: +@@ -243,9 +242,12 @@ void nft_meta_set_eval(const struct nft_ + skb->priority = value; + break; + case NFT_META_PKTTYPE: +- if (skb->pkt_type != value && +- skb_pkt_type_ok(value) && skb_pkt_type_ok(skb->pkt_type)) +- skb->pkt_type = value; ++ pkt_type = nft_reg_load8(sreg); ++ ++ if (skb->pkt_type != pkt_type && ++ skb_pkt_type_ok(pkt_type) && ++ skb_pkt_type_ok(skb->pkt_type)) ++ skb->pkt_type = pkt_type; + break; + case NFT_META_NFTRACE: + skb->nf_trace = !!value; +--- a/net/netfilter/nft_nat.c ++++ b/net/netfilter/nft_nat.c +@@ -65,10 +65,10 @@ static void nft_nat_eval(const struct nf + } + + if (priv->sreg_proto_min) { +- range.min_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_min]; +- range.max_proto.all = +- *(__be16 *)®s->data[priv->sreg_proto_max]; ++ range.min_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_min]); ++ range.max_proto.all = (__force __be16)nft_reg_load16( ++ ®s->data[priv->sreg_proto_max]); + range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } +