netfilter: ebtables: add support for matching ICMP type and code
authorMatthias Schiffer <mschiffer@universe-factory.net>
Sun, 4 Mar 2018 08:28:53 +0000 (09:28 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 20 Mar 2018 16:24:03 +0000 (17:24 +0100)
We already have ICMPv6 type/code matches. This adds support for IPv4 ICMP
matches in the same way.

Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
Acked-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/uapi/linux/netfilter_bridge/ebt_ip.h
net/bridge/netfilter/ebt_ip.c

index 8e462fb1983f23be907e15f3380c79a39de37e29..4ed7fbb0a482019b013f5fab9d2097619ebb77fe 100644 (file)
@@ -24,8 +24,9 @@
 #define EBT_IP_PROTO 0x08
 #define EBT_IP_SPORT 0x10
 #define EBT_IP_DPORT 0x20
+#define EBT_IP_ICMP 0x40
 #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
EBT_IP_SPORT | EBT_IP_DPORT )
                   EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP)
 #define EBT_IP_MATCH "ip"
 
 /* the same values are used for the invflags */
@@ -38,8 +39,14 @@ struct ebt_ip_info {
        __u8  protocol;
        __u8  bitmask;
        __u8  invflags;
-       __u16 sport[2];
-       __u16 dport[2];
+       union {
+               __u16 sport[2];
+               __u8 icmp_type[2];
+       };
+       union {
+               __u16 dport[2];
+               __u8 icmp_code[2];
+       };
 };
 
 #endif
index 2b46c50abce0395594f2891b9b4f71e7a4516591..8cb8f8395768be498fe551335aaea165e7498933 100644 (file)
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_bridge/ebt_ip.h>
 
-struct tcpudphdr {
-       __be16 src;
-       __be16 dst;
+union pkthdr {
+       struct {
+               __be16 src;
+               __be16 dst;
+       } tcpudphdr;
+       struct {
+               u8 type;
+               u8 code;
+       } icmphdr;
 };
 
 static bool
@@ -30,8 +36,8 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par)
        const struct ebt_ip_info *info = par->matchinfo;
        const struct iphdr *ih;
        struct iphdr _iph;
-       const struct tcpudphdr *pptr;
-       struct tcpudphdr _ports;
+       const union pkthdr *pptr;
+       union pkthdr _pkthdr;
 
        ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
        if (ih == NULL)
@@ -50,29 +56,38 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par)
        if (info->bitmask & EBT_IP_PROTO) {
                if (NF_INVF(info, EBT_IP_PROTO, info->protocol != ih->protocol))
                        return false;
-               if (!(info->bitmask & EBT_IP_DPORT) &&
-                   !(info->bitmask & EBT_IP_SPORT))
+               if (!(info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT |
+                                      EBT_IP_ICMP)))
                        return true;
                if (ntohs(ih->frag_off) & IP_OFFSET)
                        return false;
+
+               /* min icmp headersize is 4, so sizeof(_pkthdr) is ok. */
                pptr = skb_header_pointer(skb, ih->ihl*4,
-                                         sizeof(_ports), &_ports);
+                                         sizeof(_pkthdr), &_pkthdr);
                if (pptr == NULL)
                        return false;
                if (info->bitmask & EBT_IP_DPORT) {
-                       u32 dst = ntohs(pptr->dst);
+                       u32 dst = ntohs(pptr->tcpudphdr.dst);
                        if (NF_INVF(info, EBT_IP_DPORT,
                                    dst < info->dport[0] ||
                                    dst > info->dport[1]))
                                return false;
                }
                if (info->bitmask & EBT_IP_SPORT) {
-                       u32 src = ntohs(pptr->src);
+                       u32 src = ntohs(pptr->tcpudphdr.src);
                        if (NF_INVF(info, EBT_IP_SPORT,
                                    src < info->sport[0] ||
                                    src > info->sport[1]))
                                return false;
                }
+               if ((info->bitmask & EBT_IP_ICMP) &&
+                   NF_INVF(info, EBT_IP_ICMP,
+                           pptr->icmphdr.type < info->icmp_type[0] ||
+                           pptr->icmphdr.type > info->icmp_type[1] ||
+                           pptr->icmphdr.code < info->icmp_code[0] ||
+                           pptr->icmphdr.code > info->icmp_code[1]))
+                       return false;
        }
        return true;
 }
@@ -101,6 +116,14 @@ static int ebt_ip_mt_check(const struct xt_mtchk_param *par)
                return -EINVAL;
        if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
                return -EINVAL;
+       if (info->bitmask & EBT_IP_ICMP) {
+               if ((info->invflags & EBT_IP_PROTO) ||
+                   info->protocol != IPPROTO_ICMP)
+                       return -EINVAL;
+               if (info->icmp_type[0] > info->icmp_type[1] ||
+                   info->icmp_code[0] > info->icmp_code[1])
+                       return -EINVAL;
+       }
        return 0;
 }