|
|
@@ -1,323 +0,0 @@
|
|
|
-From 0bc01df3df7a88258148518eb9ce7f88c16a6106 Mon Sep 17 00:00:00 2001
|
|
|
-From: Liping Zhang <[email protected]>
|
|
|
-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 <[email protected]>
|
|
|
-Signed-off-by: Pablo Neira Ayuso <[email protected]>
|
|
|
----
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|