cfg80211: configure multicast to unicast for AP interfaces
authorMichael Braun <michael-dev@fami-braun.de>
Mon, 10 Oct 2016 17:12:22 +0000 (19:12 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 27 Oct 2016 14:03:27 +0000 (16:03 +0200)
Add the ability to configure if an AP (and associated VLANs) will
do multicast-to-unicast conversion for ARP, IPv4 and IPv6 frames
(possibly within 802.1Q). If enabled, such frames are to be sent
to each station separately, with the DA replaced by their own MAC
address rather than the group address.

Note that this may break certain expectations of the receiver,
such as the ability to drop unicast IP packets received within
multicast L2 frames, or the ability to not send ICMP destination
unreachable messages for packets received in L2 multicast (which
is required, but the receiver can't tell the difference if this
new option is enabled.)

This also doesn't implement the 802.11 DMS (directed multicast
service).

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
[fix disabling, add better documentation & commit message]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/trace.h

index 738b4d8..41ae3f5 100644 (file)
@@ -2742,6 +2742,8 @@ struct cfg80211_nan_func {
  * @nan_change_conf: changes NAN configuration. The changed parameters must
  *     be specified in @changes (using &enum cfg80211_nan_conf_changes);
  *     All other parameters must be ignored.
+ *
+ * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -3018,6 +3020,10 @@ struct cfg80211_ops {
                                   struct wireless_dev *wdev,
                                   struct cfg80211_nan_conf *conf,
                                   u32 changes);
+
+       int     (*set_multicast_to_unicast)(struct wiphy *wiphy,
+                                           struct net_device *dev,
+                                           const bool enabled);
 };
 
 /*
index a268a00..e21d23d 100644 (file)
  *
  * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
  *
+ * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
+ *     multicast to unicast conversion. When enabled, all multicast packets
+ *     with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header)
+ *     will be sent out to each station once with the destination (multicast)
+ *     MAC address replaced by the station's MAC address. Note that this may
+ *     break certain expectations of the receiver, e.g. the ability to drop
+ *     unicast IP packets encapsulated in multicast L2 frames, or the ability
+ *     to not send destination unreachable messages in such cases.
+ *     This can only be toggled per BSS. Configure this on an interface of
+ *     type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces
+ *     (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode.
+ *     If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this
+ *     command, the feature is disabled.
+ *
  * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
  *     mesh config parameters may be given.
  * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
@@ -1069,6 +1083,8 @@ enum nl80211_commands {
        NL80211_CMD_CHANGE_NAN_CONFIG,
        NL80211_CMD_NAN_MATCH,
 
+       NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1950,6 +1966,9 @@ enum nl80211_commands {
  *     Request/Response frame protection. This attribute contains the 16 octet
  *     STA Nonce followed by 16 octets of AP Nonce.
  *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ *     packets should be send out as unicast to all stations (flag attribute).
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2352,6 +2371,8 @@ enum nl80211_attrs {
        NL80211_ATTR_FILS_KEK,
        NL80211_ATTR_FILS_NONCES,
 
+       NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 667d5f7..0c9d00c 100644 (file)
@@ -417,6 +417,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY,
                                    .len = FILS_MAX_KEK_LEN },
        [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN },
+       [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, },
 };
 
 /* policy for the key attributes */
@@ -1659,6 +1660,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                        if (rdev->wiphy.features &
                                        NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
                                CMD(add_tx_ts, ADD_TX_TS);
+                       CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST);
                }
 #undef CMD
 
@@ -11766,6 +11768,31 @@ static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
        return 0;
 }
 
+static int nl80211_set_multicast_to_unicast(struct sk_buff *skb,
+                                           struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       const struct nlattr *nla;
+       bool enabled;
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       if (!rdev->ops->set_multicast_to_unicast)
+               return -EOPNOTSUPP;
+
+       if (wdev->iftype != NL80211_IFTYPE_AP &&
+           wdev->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       nla = info->attrs[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED];
+       enabled = nla_get_flag(nla);
+
+       return rdev_set_multicast_to_unicast(rdev, dev, enabled);
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -12625,6 +12652,14 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+               .doit = nl80211_set_multicast_to_unicast,
+               .policy = nl80211_policy,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 /* notification functions */
index 11cf83c..e9ecf23 100644 (file)
@@ -562,6 +562,18 @@ static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
+static inline int
+rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev,
+                             struct net_device *dev,
+                             const bool enabled)
+{
+       int ret;
+       trace_rdev_set_multicast_to_unicast(&rdev->wiphy, dev, enabled);
+       ret = rdev->ops->set_multicast_to_unicast(&rdev->wiphy, dev, enabled);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
 static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
 {
        trace_rdev_rfkill_poll(&rdev->wiphy);
index a3d0a91..25f8318 100644 (file)
@@ -3030,6 +3030,25 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan,
        TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
        TP_ARGS(wiphy, wdev)
 );
+
+TRACE_EVENT(rdev_set_multicast_to_unicast,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                const bool enabled),
+       TP_ARGS(wiphy, netdev, enabled),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               __field(bool, enabled)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               __entry->enabled = enabled;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", unicast: %s",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG,
+                 BOOL_TO_STR(__entry->enabled))
+);
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH