nl80211: Implement TX of control port frames
authorDenis Kenzior <denkenz@gmail.com>
Mon, 26 Mar 2018 17:52:42 +0000 (12:52 -0500)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 29 Mar 2018 11:44:19 +0000 (13:44 +0200)
This commit implements the TX side of NL80211_CMD_CONTROL_PORT_FRAME.
Userspace provides the raw EAPoL frame using NL80211_ATTR_FRAME.
Userspace should also provide the destination address and the protocol
type to use when sending the frame.  This is used to implement TX of
Pre-authentication frames.  If CONTROL_PORT_ETHERTYPE_NO_ENCRYPT is
specified, then the driver will be asked not to encrypt the outgoing
frame.

A new EXT_FEATURE flag is introduced so that nl80211 code can check
whether a given wiphy has capability to pass EAPoL frames over nl80211.

Signed-off-by: Denis Kenzior <denkenz@gmail.com>
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 df145f76adad9f878a94c9c50c5bda2845b41566..de2894a4ad1003fc17a5d12089aa8a8598f9310a 100644 (file)
@@ -2961,6 +2961,9 @@ struct cfg80211_external_auth_params {
  *
  * @external_auth: indicates result of offloaded authentication processing from
  *     user space
+ *
+ * @tx_control_port: TX a control port frame (EAPoL).  The noencrypt parameter
+ *     tells the driver that the frame should not be encrypted.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -3256,6 +3259,12 @@ struct cfg80211_ops {
                           const u8 *aa);
        int     (*external_auth)(struct wiphy *wiphy, struct net_device *dev,
                                 struct cfg80211_external_auth_params *params);
+
+       int     (*tx_control_port)(struct wiphy *wiphy,
+                                  struct net_device *dev,
+                                  const u8 *buf, size_t len,
+                                  const u8 *dest, const __be16 proto,
+                                  const bool noencrypt);
 };
 
 /*
index 6a3cc7a635b59bdd7e544095de303e56e5921521..3167d6f7fc68c159e729c1c7b5f87c4d593aeb57 100644 (file)
@@ -5024,6 +5024,8 @@ enum nl80211_feature_flags {
  *     channel change triggered by radar detection event.
  *     No need to start CAC from user-space, no need to react to
  *     "radar detected" event.
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
+ *     receiving control port frames over nl80211 instead of the netdevice.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -5055,6 +5057,7 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_LOW_POWER_SCAN,
        NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
        NL80211_EXT_FEATURE_DFS_OFFLOAD,
+       NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
index 0870447fbd5569103cd0595de2c1c8bc973ef971..6eb286784924e483ac5f3a57eb2fd9232ea312f1 100644 (file)
@@ -12535,6 +12535,68 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info)
        return rdev_external_auth(rdev, dev, &params);
 }
 
+static int nl80211_tx_control_port(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 u8 *buf;
+       size_t len;
+       u8 *dest;
+       u16 proto;
+       bool noencrypt;
+       int err;
+
+       if (!wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
+               return -EOPNOTSUPP;
+
+       if (!rdev->ops->tx_control_port)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_FRAME] ||
+           !info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
+               GENL_SET_ERR_MSG(info, "Frame, MAC or ethertype missing");
+               return -EINVAL;
+       }
+
+       wdev_lock(wdev);
+
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_MESH_POINT:
+               break;
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               if (wdev->current_bss)
+                       break;
+               err = -ENOTCONN;
+               goto out;
+       default:
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       wdev_unlock(wdev);
+
+       buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+       len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+       dest = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       proto = nla_get_u16(info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
+       noencrypt =
+               nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]);
+
+       return rdev_tx_control_port(rdev, dev, buf, len,
+                                   dest, cpu_to_be16(proto), noencrypt);
+
+ out:
+       wdev_unlock(wdev);
+       return err;
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -13438,7 +13500,14 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
-
+       {
+               .cmd = NL80211_CMD_CONTROL_PORT_FRAME,
+               .doit = nl80211_tx_control_port,
+               .policy = nl80211_policy,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {
index 84f23ae015fc57edd0c9459aeade85e99ebfffed..87479a53411bd860a461c2ad90d585c52147a534 100644 (file)
@@ -714,6 +714,21 @@ static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
+static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev,
+                                      struct net_device *dev,
+                                      const void *buf, size_t len,
+                                      const u8 *dest, __be16 proto,
+                                      const bool noencrypt)
+{
+       int ret;
+       trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len,
+                                  dest, proto, noencrypt);
+       ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len,
+                                        dest, proto, noencrypt);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
 static inline int
 rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev,
                         struct wireless_dev *wdev, u64 cookie)
index 42fd338f879e49b56f371d1aa1bd52d3a7a79e98..a64291ae52a67e60caa7b7c59a4b0e8d12b548a1 100644 (file)
@@ -1882,6 +1882,32 @@ TRACE_EVENT(rdev_mgmt_tx,
                  BOOL_TO_STR(__entry->dont_wait_for_ack))
 );
 
+TRACE_EVENT(rdev_tx_control_port,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                const u8 *buf, size_t len, const u8 *dest, __be16 proto,
+                bool unencrypted),
+       TP_ARGS(wiphy, netdev, buf, len, dest, proto, unencrypted),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               MAC_ENTRY(dest)
+               __field(__be16, proto)
+               __field(bool, unencrypted)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               MAC_ASSIGN(dest, dest);
+               __entry->proto = proto;
+               __entry->unencrypted = unencrypted;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ","
+                 " proto: 0x%x, unencrypted: %s",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dest),
+                 be16_to_cpu(__entry->proto),
+                 BOOL_TO_STR(__entry->unencrypted))
+);
+
 TRACE_EVENT(rdev_set_noack_map,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
                 u16 noack_map),