ipv4: add option to drop unicast encapsulated in L2 multicast
authorJohannes Berg <johannes.berg@intel.com>
Thu, 4 Feb 2016 12:31:17 +0000 (13:31 +0100)
committerStricted <info@stricted.net>
Sat, 28 Jul 2018 05:08:06 +0000 (07:08 +0200)
In order to solve a problem with 802.11, the so-called hole-196 attack,
add an option (sysctl) called "drop_unicast_in_l2_multicast" which, if
enabled, causes the stack to drop IPv4 unicast packets encapsulated in
link-layer multi- or broadcast frames. Such frames can (as an attack)
be created by any member of the same wireless network and transmitted
as valid encrypted frames since the symmetric key for broadcast frames
is shared between all stations.

Additionally, enabling this option provides compliance with a SHOULD
clause of RFC 1122.

Reviewed-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/ip-sysctl.txt
include/linux/inetdevice.h
net/ipv4/devinet.c
net/ipv4/ip_input.c

index 99d2164180613804ecc3e210879ff96760636e16..ce81223e8e78ccfbdf864ac810a0f1b7afb8c5ab 100644 (file)
@@ -1038,7 +1038,12 @@ disable_policy - BOOLEAN
 disable_xfrm - BOOLEAN
        Disable IPSEC encryption on this interface, whatever the policy
 
-
+drop_unicast_in_l2_multicast - BOOLEAN
+       Drop any unicast IP packets that are received in link-layer
+       multicast (or broadcast) frames.
+       This behavior (for multicast) is actually a SHOULD in RFC
+       1122, but is disabled by default for compatibility reasons.
+       Default: off (0)
 
 tag - INTEGER
        Allows you to write a number, which can be used as required.
index 770ecc90993bf8f9ddb2811f383ced4012fedada..b5d64f1b44420dd09c45425c7fb5abc881a3d4d0 100644 (file)
@@ -39,6 +39,7 @@ enum
        IPV4_DEVCONF_SRC_VMARK,
        IPV4_DEVCONF_PROXY_ARP_PVLAN,
        IPV4_DEVCONF_ROUTE_LOCALNET,
+       IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
        __IPV4_DEVCONF_MAX
 };
 
index b151e0ac7f275300ac5051b3e41e4356f830ba56..52ee5f759175afb7da2f3b92e161250cca4b0f3c 100644 (file)
@@ -2112,6 +2112,8 @@ static struct devinet_sysctl_table {
                                              "promote_secondaries"),
                DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
                                              "route_localnet"),
+               DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
+                                             "drop_unicast_in_l2_multicast"),  
        },
 };
 
index 0a22bb0ce1a8dad04bd910e871bf34c80c03ef4b..1fe89dae05d56955f1af5b8528c15f30b5e71a14 100644 (file)
@@ -358,9 +358,31 @@ static int ip_rcv_finish(struct sk_buff *skb)
        if (rt->rt_type == RTN_MULTICAST) {
                IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST,
                                skb->len);
-       } else if (rt->rt_type == RTN_BROADCAST)
+       } else if (rt->rt_type == RTN_BROADCAST) {
                IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,
                                skb->len);
+       } else if (skb->pkt_type == PACKET_BROADCAST ||
+                  skb->pkt_type == PACKET_MULTICAST) {
+               struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
+               /* RFC 1122 3.3.6:
+                *
+                *   When a host sends a datagram to a link-layer broadcast
+                *   address, the IP destination address MUST be a legal IP
+                *   broadcast or IP multicast address.
+                *
+                *   A host SHOULD silently discard a datagram that is received
+                *   via a link-layer broadcast (see Section 2.4) but does not
+                *   specify an IP multicast or broadcast destination address.
+                *
+                * This doesn't explicitly say L2 *broadcast*, but broadcast is
+                * in a way a form of multicast and the most common use case for
+                * this is 802.11 protecting against cross-station spoofing (the
+                * so-called "hole-196" attack) so do it for both.
+                */
+               if (in_dev &&
+                   IN_DEV_ORCONF(in_dev, DROP_UNICAST_IN_L2_MULTICAST))
+                       goto drop;
+       }
 
        return dst_input(skb);