netfilter: nf_tables: ensure proper initialization of nft_pktinfo fields
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 9 Sep 2016 10:42:49 +0000 (12:42 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 12 Sep 2016 16:51:57 +0000 (18:51 +0200)
This patch introduces nft_set_pktinfo_unspec() that ensures proper
initialization all of pktinfo fields for non-IP traffic. This is used
by the bridge, netdev and arp families.

This new function relies on nft_set_pktinfo_proto_unspec() to set a new
tprot_set field that indicates if transport protocol information is
available. Remain fields are zeroed.

The meta expression has been also updated to check to tprot_set in first
place given that zero is a valid tprot value. Even a handcrafted packet
may come with the IPPROTO_RAW (255) protocol number so we can't rely on
this value as tprot unset.

Reported-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables.h
include/net/netfilter/nf_tables_ipv4.h
include/net/netfilter/nf_tables_ipv6.h
net/bridge/netfilter/nf_tables_bridge.c
net/ipv4/netfilter/nf_tables_arp.c
net/netfilter/nf_tables_netdev.c
net/netfilter/nft_meta.c

index 8972468bc94bc717a39e88a13298bf245f4bf855..a7a7cebc8d0795a37f6d95723df619eb3975a535 100644 (file)
@@ -19,6 +19,7 @@ struct nft_pktinfo {
        const struct net_device         *out;
        u8                              pf;
        u8                              hook;
+       bool                            tprot_set;
        u8                              tprot;
        /* for x_tables compatibility */
        struct xt_action_param          xt;
@@ -36,6 +37,23 @@ static inline void nft_set_pktinfo(struct nft_pktinfo *pkt,
        pkt->pf = pkt->xt.family = state->pf;
 }
 
+static inline void nft_set_pktinfo_proto_unspec(struct nft_pktinfo *pkt,
+                                               struct sk_buff *skb)
+{
+       pkt->tprot_set = false;
+       pkt->tprot = 0;
+       pkt->xt.thoff = 0;
+       pkt->xt.fragoff = 0;
+}
+
+static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt,
+                                         struct sk_buff *skb,
+                                         const struct nf_hook_state *state)
+{
+       nft_set_pktinfo(pkt, skb, state);
+       nft_set_pktinfo_proto_unspec(pkt, skb);
+}
+
 /**
  *     struct nft_verdict - nf_tables verdict
  *
index ca6ef6bf775ef544bce93b892ee156d449a62f9e..af952f7843ee3bcfd85a8c516a034e1fc0741143 100644 (file)
@@ -14,6 +14,7 @@ nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
        nft_set_pktinfo(pkt, skb, state);
 
        ip = ip_hdr(pkt->skb);
+       pkt->tprot_set = true;
        pkt->tprot = ip->protocol;
        pkt->xt.thoff = ip_hdrlen(pkt->skb);
        pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
index 8ad39a6a5fe18703be7808b5ed4d92cf186bafd6..6aeee47b1b5e08b2a537c3730f745c3c841f7597 100644 (file)
@@ -19,6 +19,7 @@ nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
        if (protohdr < 0)
                return -1;
 
+       pkt->tprot_set = true;
        pkt->tprot = protohdr;
        pkt->xt.thoff = thoff;
        pkt->xt.fragoff = frag_off;
index a78c4e2826e5a56d509635e2ca8f9ec4eeac481f..29899887163e370509385e096ea6a4e8e1d3064e 100644 (file)
@@ -71,7 +71,7 @@ static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
        if (nft_bridge_iphdr_validate(skb))
                nft_set_pktinfo_ipv4(pkt, skb, state);
        else
-               nft_set_pktinfo(pkt, skb, state);
+               nft_set_pktinfo_unspec(pkt, skb, state);
 }
 
 static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
@@ -83,7 +83,7 @@ static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
            nft_set_pktinfo_ipv6(pkt, skb, state) == 0)
                return;
 #endif
-       nft_set_pktinfo(pkt, skb, state);
+       nft_set_pktinfo_unspec(pkt, skb, state);
 }
 
 static unsigned int
@@ -101,7 +101,7 @@ nft_do_chain_bridge(void *priv,
                nft_bridge_set_pktinfo_ipv6(&pkt, skb, state);
                break;
        default:
-               nft_set_pktinfo(&pkt, skb, state);
+               nft_set_pktinfo_unspec(&pkt, skb, state);
                break;
        }
 
index cd84d4295a2027642f46523fd105068144abfdfd..058c034be376eb7b6fed03fa87fc58e4e7805ee5 100644 (file)
@@ -21,7 +21,7 @@ nft_do_chain_arp(void *priv,
 {
        struct nft_pktinfo pkt;
 
-       nft_set_pktinfo(&pkt, skb, state);
+       nft_set_pktinfo_unspec(&pkt, skb, state);
 
        return nft_do_chain(&pkt, priv);
 }
index 5eefe4a355c640cf85c8328740a877c6909880ba..8de502b0c37bc0c808452225455cd3e39ea062b6 100644 (file)
@@ -41,6 +41,7 @@ nft_netdev_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
        else if (len < thoff)
                return;
 
+       pkt->tprot_set = true;
        pkt->tprot = iph->protocol;
        pkt->xt.thoff = thoff;
        pkt->xt.fragoff = ntohs(iph->frag_off) & IP_OFFSET;
@@ -74,6 +75,7 @@ __nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
        if (protohdr < 0)
                 return;
 
+       pkt->tprot_set = true;
        pkt->tprot = protohdr;
        pkt->xt.thoff = thoff;
        pkt->xt.fragoff = frag_off;
@@ -102,7 +104,7 @@ nft_do_chain_netdev(void *priv, struct sk_buff *skb,
                nft_netdev_set_pktinfo_ipv6(&pkt, skb, state);
                break;
        default:
-               nft_set_pktinfo(&pkt, skb, state);
+               nft_set_pktinfo_unspec(&pkt, skb, state);
                break;
        }
 
index 2863f3493038d8b1fd92eebb9c6d846227e99bf6..14264edf2d7714e3377b9fb43b73926e2a0aea00 100644 (file)
@@ -52,6 +52,8 @@ void nft_meta_get_eval(const struct nft_expr *expr,
                *dest = pkt->pf;
                break;
        case NFT_META_L4PROTO:
+               if (!pkt->tprot_set)
+                       goto err;
                *dest = pkt->tprot;
                break;
        case NFT_META_PRIORITY: