netfilter: introduce nft_set_pktinfo_{ipv4, ipv6}_validate()
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 9 Sep 2016 10:42:51 +0000 (12:42 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 12 Sep 2016 16:52:09 +0000 (18:52 +0200)
These functions are extracted from the netdev family, they initialize
the pktinfo structure and validate that the IPv4 and IPv6 headers are
well-formed given that these functions are called from a path where
layer 3 sanitization did not happen yet.

These functions are placed in include/net/netfilter/nf_tables_ipv{4,6}.h
so they can be reused by a follow up patch to use them from the bridge
family too.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables_ipv4.h
include/net/netfilter/nf_tables_ipv6.h
net/netfilter/nf_tables_netdev.c

index af952f7843ee3bcfd85a8c516a034e1fc0741143..968f00b82fb5590202a232f09a2256ad1f8f9bd2 100644 (file)
@@ -20,6 +20,48 @@ nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
        pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
 }
 
+static inline int
+__nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt,
+                               struct sk_buff *skb,
+                               const struct nf_hook_state *state)
+{
+       struct iphdr *iph, _iph;
+       u32 len, thoff;
+
+       iph = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*iph),
+                                &_iph);
+       if (!iph)
+               return -1;
+
+       iph = ip_hdr(skb);
+       if (iph->ihl < 5 || iph->version != 4)
+               return -1;
+
+       len = ntohs(iph->tot_len);
+       thoff = iph->ihl * 4;
+       if (skb->len < len)
+               return -1;
+       else if (len < thoff)
+               return -1;
+
+       pkt->tprot_set = true;
+       pkt->tprot = iph->protocol;
+       pkt->xt.thoff = thoff;
+       pkt->xt.fragoff = ntohs(iph->frag_off) & IP_OFFSET;
+
+       return 0;
+}
+
+static inline void
+nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt,
+                             struct sk_buff *skb,
+                             const struct nf_hook_state *state)
+{
+       nft_set_pktinfo(pkt, skb, state);
+       if (__nft_set_pktinfo_ipv4_validate(pkt, skb, state) < 0)
+               nft_set_pktinfo_proto_unspec(pkt, skb);
+}
+
 extern struct nft_af_info nft_af_ipv4;
 
 #endif
index 1e0ffd5aea47eecdea750ab792b61887672c19c9..39b7b717b5404ba4f7e57c55f200350661dbab59 100644 (file)
@@ -28,6 +28,55 @@ nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
        return 0;
 }
 
+static inline int
+__nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt,
+                               struct sk_buff *skb,
+                               const struct nf_hook_state *state)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+       struct ipv6hdr *ip6h, _ip6h;
+       unsigned int thoff = 0;
+       unsigned short frag_off;
+       int protohdr;
+       u32 pkt_len;
+
+       ip6h = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*ip6h),
+                                 &_ip6h);
+       if (!ip6h)
+               return -1;
+
+       if (ip6h->version != 6)
+               return -1;
+
+       pkt_len = ntohs(ip6h->payload_len);
+       if (pkt_len + sizeof(*ip6h) > skb->len)
+               return -1;
+
+       protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
+       if (protohdr < 0)
+               return -1;
+
+       pkt->tprot_set = true;
+       pkt->tprot = protohdr;
+       pkt->xt.thoff = thoff;
+       pkt->xt.fragoff = frag_off;
+
+       return 0;
+#else
+       return -1;
+#endif
+}
+
+static inline void
+nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt,
+                             struct sk_buff *skb,
+                             const struct nf_hook_state *state)
+{
+       nft_set_pktinfo(pkt, skb, state);
+       if (__nft_set_pktinfo_ipv6_validate(pkt, skb, state) < 0)
+               nft_set_pktinfo_proto_unspec(pkt, skb);
+}
+
 extern struct nft_af_info nft_af_ipv6;
 
 #endif
index 8de502b0c37bc0c808452225455cd3e39ea062b6..3e5475a833a55fefe48cae7b966e0344a8108169 100644 (file)
 #include <net/netfilter/nf_tables_ipv4.h>
 #include <net/netfilter/nf_tables_ipv6.h>
 
-static inline void
-nft_netdev_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
-                           struct sk_buff *skb,
-                           const struct nf_hook_state *state)
-{
-       struct iphdr *iph, _iph;
-       u32 len, thoff;
-
-       nft_set_pktinfo(pkt, skb, state);
-
-       iph = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*iph),
-                                &_iph);
-       if (!iph)
-               return;
-
-       iph = ip_hdr(skb);
-       if (iph->ihl < 5 || iph->version != 4)
-               return;
-
-       len = ntohs(iph->tot_len);
-       thoff = iph->ihl * 4;
-       if (skb->len < len)
-               return;
-       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;
-}
-
-static inline void
-__nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
-                             struct sk_buff *skb,
-                             const struct nf_hook_state *state)
-{
-#if IS_ENABLED(CONFIG_IPV6)
-       struct ipv6hdr *ip6h, _ip6h;
-       unsigned int thoff = 0;
-       unsigned short frag_off;
-       int protohdr;
-       u32 pkt_len;
-
-       ip6h = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*ip6h),
-                                 &_ip6h);
-       if (!ip6h)
-               return;
-
-       if (ip6h->version != 6)
-               return;
-
-       pkt_len = ntohs(ip6h->payload_len);
-       if (pkt_len + sizeof(*ip6h) > skb->len)
-               return;
-
-       protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
-       if (protohdr < 0)
-                return;
-
-       pkt->tprot_set = true;
-       pkt->tprot = protohdr;
-       pkt->xt.thoff = thoff;
-       pkt->xt.fragoff = frag_off;
-#endif
-}
-
-static inline void nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
-                                              struct sk_buff *skb,
-                                              const struct nf_hook_state *state)
-{
-       nft_set_pktinfo(pkt, skb, state);
-       __nft_netdev_set_pktinfo_ipv6(pkt, skb, state);
-}
-
 static unsigned int
 nft_do_chain_netdev(void *priv, struct sk_buff *skb,
                    const struct nf_hook_state *state)
@@ -98,10 +23,10 @@ nft_do_chain_netdev(void *priv, struct sk_buff *skb,
 
        switch (skb->protocol) {
        case htons(ETH_P_IP):
-               nft_netdev_set_pktinfo_ipv4(&pkt, skb, state);
+               nft_set_pktinfo_ipv4_validate(&pkt, skb, state);
                break;
        case htons(ETH_P_IPV6):
-               nft_netdev_set_pktinfo_ipv6(&pkt, skb, state);
+               nft_set_pktinfo_ipv6_validate(&pkt, skb, state);
                break;
        default:
                nft_set_pktinfo_unspec(&pkt, skb, state);