Commit 10596608 authored by Liping Zhang's avatar Liping Zhang Committed by Pablo Neira Ayuso

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 = &regs->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(&regs->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: default avatarLiping Zhang <zlpnobody@gmail.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent fd89b23a
...@@ -103,6 +103,35 @@ struct nft_regs { ...@@ -103,6 +103,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, static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
unsigned int len) unsigned int len)
{ {
......
...@@ -26,10 +26,10 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr, ...@@ -26,10 +26,10 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
memset(&range, 0, sizeof(range)); memset(&range, 0, sizeof(range));
range.flags = priv->flags; range.flags = priv->flags;
if (priv->sreg_proto_min) { if (priv->sreg_proto_min) {
range.min_proto.all = range.min_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_min]; &regs->data[priv->sreg_proto_min]);
range.max_proto.all = range.max_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_max]; &regs->data[priv->sreg_proto_max]);
} }
regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, nft_hook(pkt), regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, nft_hook(pkt),
&range, nft_out(pkt)); &range, nft_out(pkt));
......
...@@ -26,10 +26,10 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr, ...@@ -26,10 +26,10 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr,
memset(&mr, 0, sizeof(mr)); memset(&mr, 0, sizeof(mr));
if (priv->sreg_proto_min) { if (priv->sreg_proto_min) {
mr.range[0].min.all = mr.range[0].min.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_min]; &regs->data[priv->sreg_proto_min]);
mr.range[0].max.all = mr.range[0].max.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_max]; &regs->data[priv->sreg_proto_max]);
mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
} }
......
...@@ -27,10 +27,10 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr, ...@@ -27,10 +27,10 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
memset(&range, 0, sizeof(range)); memset(&range, 0, sizeof(range));
range.flags = priv->flags; range.flags = priv->flags;
if (priv->sreg_proto_min) { if (priv->sreg_proto_min) {
range.min_proto.all = range.min_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_min]; &regs->data[priv->sreg_proto_min]);
range.max_proto.all = range.max_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_max]; &regs->data[priv->sreg_proto_max]);
} }
regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range,
nft_out(pkt)); nft_out(pkt));
......
...@@ -26,10 +26,10 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr, ...@@ -26,10 +26,10 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr,
memset(&range, 0, sizeof(range)); memset(&range, 0, sizeof(range));
if (priv->sreg_proto_min) { if (priv->sreg_proto_min) {
range.min_proto.all = range.min_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_min], &regs->data[priv->sreg_proto_min]);
range.max_proto.all = range.max_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_max], &regs->data[priv->sreg_proto_max]);
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
} }
......
...@@ -83,7 +83,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr, ...@@ -83,7 +83,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
switch (priv->key) { switch (priv->key) {
case NFT_CT_DIRECTION: case NFT_CT_DIRECTION:
*dest = CTINFO2DIR(ctinfo); nft_reg_store8(dest, CTINFO2DIR(ctinfo));
return; return;
case NFT_CT_STATUS: case NFT_CT_STATUS:
*dest = ct->status; *dest = ct->status;
...@@ -151,20 +151,22 @@ static void nft_ct_get_eval(const struct nft_expr *expr, ...@@ -151,20 +151,22 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
return; return;
} }
case NFT_CT_L3PROTOCOL: case NFT_CT_L3PROTOCOL:
*dest = nf_ct_l3num(ct); nft_reg_store8(dest, nf_ct_l3num(ct));
return; return;
case NFT_CT_PROTOCOL: case NFT_CT_PROTOCOL:
*dest = nf_ct_protonum(ct); nft_reg_store8(dest, nf_ct_protonum(ct));
return; return;
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE: { case NFT_CT_ZONE: {
const struct nf_conntrack_zone *zone = nf_ct_zone(ct); const struct nf_conntrack_zone *zone = nf_ct_zone(ct);
u16 zoneid;
if (priv->dir < IP_CT_DIR_MAX) if (priv->dir < IP_CT_DIR_MAX)
*dest = nf_ct_zone_id(zone, priv->dir); zoneid = nf_ct_zone_id(zone, priv->dir);
else else
*dest = zone->id; zoneid = zone->id;
nft_reg_store16(dest, zoneid);
return; return;
} }
#endif #endif
...@@ -183,10 +185,10 @@ static void nft_ct_get_eval(const struct nft_expr *expr, ...@@ -183,10 +185,10 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
return; return;
case NFT_CT_PROTO_SRC: case NFT_CT_PROTO_SRC:
*dest = (__force __u16)tuple->src.u.all; nft_reg_store16(dest, (__force u16)tuple->src.u.all);
return; return;
case NFT_CT_PROTO_DST: case NFT_CT_PROTO_DST:
*dest = (__force __u16)tuple->dst.u.all; nft_reg_store16(dest, (__force u16)tuple->dst.u.all);
return; return;
default: default:
break; break;
...@@ -205,7 +207,7 @@ static void nft_ct_set_zone_eval(const struct nft_expr *expr, ...@@ -205,7 +207,7 @@ static void nft_ct_set_zone_eval(const struct nft_expr *expr,
const struct nft_ct *priv = nft_expr_priv(expr); const struct nft_ct *priv = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb; struct sk_buff *skb = pkt->skb;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
u16 value = regs->data[priv->sreg]; u16 value = nft_reg_load16(&regs->data[priv->sreg]);
struct nf_conn *ct; struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
......
...@@ -45,16 +45,15 @@ void nft_meta_get_eval(const struct nft_expr *expr, ...@@ -45,16 +45,15 @@ void nft_meta_get_eval(const struct nft_expr *expr,
*dest = skb->len; *dest = skb->len;
break; break;
case NFT_META_PROTOCOL: case NFT_META_PROTOCOL:
*dest = 0; nft_reg_store16(dest, (__force u16)skb->protocol);
*(__be16 *)dest = skb->protocol;
break; break;
case NFT_META_NFPROTO: case NFT_META_NFPROTO:
*dest = nft_pf(pkt); nft_reg_store8(dest, nft_pf(pkt));
break; break;
case NFT_META_L4PROTO: case NFT_META_L4PROTO:
if (!pkt->tprot_set) if (!pkt->tprot_set)
goto err; goto err;
*dest = pkt->tprot; nft_reg_store8(dest, pkt->tprot);
break; break;
case NFT_META_PRIORITY: case NFT_META_PRIORITY:
*dest = skb->priority; *dest = skb->priority;
...@@ -85,14 +84,12 @@ void nft_meta_get_eval(const struct nft_expr *expr, ...@@ -85,14 +84,12 @@ void nft_meta_get_eval(const struct nft_expr *expr,
case NFT_META_IIFTYPE: case NFT_META_IIFTYPE:
if (in == NULL) if (in == NULL)
goto err; goto err;
*dest = 0; nft_reg_store16(dest, in->type);
*(u16 *)dest = in->type;
break; break;
case NFT_META_OIFTYPE: case NFT_META_OIFTYPE:
if (out == NULL) if (out == NULL)
goto err; goto err;
*dest = 0; nft_reg_store16(dest, out->type);
*(u16 *)dest = out->type;
break; break;
case NFT_META_SKUID: case NFT_META_SKUID:
sk = skb_to_full_sk(skb); sk = skb_to_full_sk(skb);
...@@ -142,19 +139,19 @@ void nft_meta_get_eval(const struct nft_expr *expr, ...@@ -142,19 +139,19 @@ void nft_meta_get_eval(const struct nft_expr *expr,
#endif #endif
case NFT_META_PKTTYPE: case NFT_META_PKTTYPE:
if (skb->pkt_type != PACKET_LOOPBACK) { if (skb->pkt_type != PACKET_LOOPBACK) {
*dest = skb->pkt_type; nft_reg_store8(dest, skb->pkt_type);
break; break;
} }
switch (nft_pf(pkt)) { switch (nft_pf(pkt)) {
case NFPROTO_IPV4: case NFPROTO_IPV4:
if (ipv4_is_multicast(ip_hdr(skb)->daddr)) if (ipv4_is_multicast(ip_hdr(skb)->daddr))
*dest = PACKET_MULTICAST; nft_reg_store8(dest, PACKET_MULTICAST);
else else
*dest = PACKET_BROADCAST; nft_reg_store8(dest, PACKET_BROADCAST);
break; break;
case NFPROTO_IPV6: case NFPROTO_IPV6:
*dest = PACKET_MULTICAST; nft_reg_store8(dest, PACKET_MULTICAST);
break; break;
case NFPROTO_NETDEV: case NFPROTO_NETDEV:
switch (skb->protocol) { switch (skb->protocol) {
...@@ -168,14 +165,14 @@ void nft_meta_get_eval(const struct nft_expr *expr, ...@@ -168,14 +165,14 @@ void nft_meta_get_eval(const struct nft_expr *expr,
goto err; goto err;
if (ipv4_is_multicast(iph->daddr)) if (ipv4_is_multicast(iph->daddr))
*dest = PACKET_MULTICAST; nft_reg_store8(dest, PACKET_MULTICAST);
else else
*dest = PACKET_BROADCAST; nft_reg_store8(dest, PACKET_BROADCAST);
break; break;
} }
case htons(ETH_P_IPV6): case htons(ETH_P_IPV6):
*dest = PACKET_MULTICAST; nft_reg_store8(dest, PACKET_MULTICAST);
break; break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
...@@ -230,7 +227,9 @@ void nft_meta_set_eval(const struct nft_expr *expr, ...@@ -230,7 +227,9 @@ void nft_meta_set_eval(const struct nft_expr *expr,
{ {
const struct nft_meta *meta = nft_expr_priv(expr); const struct nft_meta *meta = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb; struct sk_buff *skb = pkt->skb;
u32 value = regs->data[meta->sreg]; u32 *sreg = &regs->data[meta->sreg];
u32 value = *sreg;
u8 pkt_type;
switch (meta->key) { switch (meta->key) {
case NFT_META_MARK: case NFT_META_MARK:
...@@ -240,9 +239,12 @@ void nft_meta_set_eval(const struct nft_expr *expr, ...@@ -240,9 +239,12 @@ void nft_meta_set_eval(const struct nft_expr *expr,
skb->priority = value; skb->priority = value;
break; break;
case NFT_META_PKTTYPE: case NFT_META_PKTTYPE:
if (skb->pkt_type != value && pkt_type = nft_reg_load8(sreg);
skb_pkt_type_ok(value) && skb_pkt_type_ok(skb->pkt_type))
skb->pkt_type = value; 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; break;
case NFT_META_NFTRACE: case NFT_META_NFTRACE:
skb->nf_trace = !!value; skb->nf_trace = !!value;
......
...@@ -65,10 +65,10 @@ static void nft_nat_eval(const struct nft_expr *expr, ...@@ -65,10 +65,10 @@ static void nft_nat_eval(const struct nft_expr *expr,
} }
if (priv->sreg_proto_min) { if (priv->sreg_proto_min) {
range.min_proto.all = range.min_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_min]; &regs->data[priv->sreg_proto_min]);
range.max_proto.all = range.max_proto.all = (__force __be16)nft_reg_load16(
*(__be16 *)&regs->data[priv->sreg_proto_max]; &regs->data[priv->sreg_proto_max]);
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment