qtnfmac: support MAC address based access control
authorVasily Ulyanov <vulyanov@quantenna.com>
Tue, 19 Dec 2017 11:28:56 +0000 (14:28 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 9 Jan 2018 12:11:23 +0000 (14:11 +0200)
This allows a running AP to blacklist STAs by their MAC addresses
respecting the configured policy (either accept or deny unless listed).
It can be setup on .start_ap or with .set_mac_acl commands.

Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
drivers/net/wireless/quantenna/qtnfmac/commands.c
drivers/net/wireless/quantenna/qtnfmac/commands.h
drivers/net/wireless/quantenna/qtnfmac/core.h
drivers/net/wireless/quantenna/qtnfmac/qlink.h
drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
drivers/net/wireless/quantenna/qtnfmac/qlink_util.h

index cedb18e04bc2f779f198a49d7f2147af9fb0a9ed..abf10996c26ad0b0a8df613947ee7e5d85fc32a7 100644 (file)
@@ -778,6 +778,20 @@ static int qtnf_start_radar_detection(struct wiphy *wiphy,
        return ret;
 }
 
+static int qtnf_set_mac_acl(struct wiphy *wiphy,
+                           struct net_device *dev,
+                           const struct cfg80211_acl_data *params)
+{
+       struct qtnf_vif *vif = qtnf_netdev_get_priv(dev);
+       int ret;
+
+       ret = qtnf_cmd_set_mac_acl(vif, params);
+       if (ret)
+               pr_err("%s: failed to set mac ACL ret=%d\n", dev->name, ret);
+
+       return ret;
+}
+
 static struct cfg80211_ops qtn_cfg80211_ops = {
        .add_virtual_intf       = qtnf_add_virtual_intf,
        .change_virtual_intf    = qtnf_change_virtual_intf,
@@ -803,6 +817,7 @@ static struct cfg80211_ops qtn_cfg80211_ops = {
        .get_channel            = qtnf_get_channel,
        .channel_switch         = qtnf_channel_switch,
        .start_radar_detection  = qtnf_start_radar_detection,
+       .set_mac_acl            = qtnf_set_mac_acl,
 };
 
 static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
@@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
        wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
        wiphy->mgmt_stypes = qtnf_mgmt_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
+       wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
 
        wiphy->iface_combinations = iface_comb;
        wiphy->n_iface_combinations = 1;
index 38b9c10780584cc2167759d254bb1af01d61bee5..6ffe4837bbdb10e68f55af3ffc3ec86f2dd651d1 100644 (file)
@@ -162,6 +162,14 @@ static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type,
                memcpy(tlv->ie_data, buf, len);
 }
 
+static inline size_t qtnf_cmd_acl_data_size(const struct cfg80211_acl_data *acl)
+{
+       size_t size = sizeof(struct qlink_acl_data) +
+                     acl->n_acl_entries * sizeof(struct qlink_mac_address);
+
+       return size;
+}
+
 static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
                                      const struct cfg80211_ap_settings *s)
 {
@@ -178,6 +186,9 @@ static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
        if (cfg80211_chandef_valid(&s->chandef))
                len += sizeof(struct qlink_tlv_chandef);
 
+       if (s->acl)
+               len += qtnf_cmd_acl_data_size(s->acl);
+
        if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) {
                pr_err("VIF%u.%u: can not fit AP settings: %u\n",
                       vif->mac->macid, vif->vifid, len);
@@ -283,6 +294,16 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
                memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap));
        }
 
+       if (s->acl) {
+               size_t acl_size = qtnf_cmd_acl_data_size(s->acl);
+               struct qlink_tlv_hdr *tlv =
+                       skb_put(cmd_skb, sizeof(*tlv) + acl_size);
+
+               tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
+               tlv->len = cpu_to_le16(acl_size);
+               qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val);
+       }
+
        qtnf_bus_lock(vif->mac->bus);
 
        ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code);
@@ -1206,6 +1227,7 @@ qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac,
        mac_info->radar_detect_widths =
                        qlink_chan_width_mask_to_nl(le16_to_cpu(
                                        resp_info->radar_detect_widths));
+       mac_info->max_acl_mac_addrs = le32_to_cpu(resp_info->max_acl_mac_addrs);
 
        memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask,
               sizeof(mac_info->ht_cap_mod_mask));
@@ -2609,3 +2631,43 @@ int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
 
        return ret;
 }
+
+int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
+                        const struct cfg80211_acl_data *params)
+{
+       struct qtnf_bus *bus = vif->mac->bus;
+       struct sk_buff *cmd_skb;
+       struct qlink_cmd_set_mac_acl *cmd;
+       u16 res_code;
+       int ret;
+
+       cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
+                                           QLINK_CMD_SET_MAC_ACL,
+                                           sizeof(*cmd) +
+                                           qtnf_cmd_acl_data_size(params));
+       if (unlikely(!cmd_skb))
+               return -ENOMEM;
+
+       cmd = (struct qlink_cmd_set_mac_acl *)cmd_skb->data;
+       qlink_acl_data_cfg2q(params, &cmd->acl);
+
+       qtnf_bus_lock(bus);
+       ret = qtnf_cmd_send(bus, cmd_skb, &res_code);
+       qtnf_bus_unlock(bus);
+
+       if (unlikely(ret))
+               return ret;
+
+       switch (res_code) {
+       case QLINK_CMD_RESULT_OK:
+               break;
+       case QLINK_CMD_RESULT_INVALID:
+               ret = -EINVAL;
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
index 07a957af9a586b725c84ada12e8e95013bdb402f..69a7d56f7e584407d35ee48a32247290bcb5351a 100644 (file)
@@ -79,5 +79,7 @@ int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef);
 int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
                       const struct cfg80211_chan_def *chdef,
                       u32 cac_time_ms);
+int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
+                        const struct cfg80211_acl_data *params);
 
 #endif /* QLINK_COMMANDS_H_ */
index e7bd21ed371b1bb04c68506d8cb445e45af8fb89..c10900162297e3e5f37d4eecfe93f89a3c8cc5b8 100644 (file)
@@ -103,6 +103,7 @@ struct qtnf_mac_info {
        u8 sretry_limit;
        u8 coverage_class;
        u8 radar_detect_widths;
+       u32 max_acl_mac_addrs;
        struct ieee80211_ht_cap ht_cap_mod_mask;
        struct ieee80211_vht_cap vht_cap_mod_mask;
        struct ieee80211_iface_limit *limits;
index 5d98000b0f5b4fbaa0a9a6f61710fc0ad92ad759..6a1f960228a13eedcff54d213db9a1add4c026d5 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <linux/ieee80211.h>
 
-#define QLINK_PROTO_VER                9
+#define QLINK_PROTO_VER                10
 
 #define QLINK_MACID_RSVD               0xFF
 #define QLINK_VIFID_RSVD               0xFF
@@ -239,6 +239,7 @@ enum qlink_cmd_type {
        QLINK_CMD_START_CAC             = 0x001D,
        QLINK_CMD_START_AP              = 0x0021,
        QLINK_CMD_STOP_AP               = 0x0022,
+       QLINK_CMD_SET_MAC_ACL           = 0x0023,
        QLINK_CMD_GET_STA_INFO          = 0x0030,
        QLINK_CMD_ADD_KEY               = 0x0040,
        QLINK_CMD_DEL_KEY               = 0x0041,
@@ -640,6 +641,38 @@ struct qlink_cmd_start_cac {
        __le32 cac_time_ms;
 } __packed;
 
+enum qlink_acl_policy {
+       QLINK_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+       QLINK_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+struct qlink_mac_address {
+       u8 addr[ETH_ALEN];
+} __packed;
+
+/**
+ * struct qlink_acl_data - ACL data
+ *
+ * @policy: filter policy, one of &enum qlink_acl_policy.
+ * @num_entries: number of MAC addresses in array.
+ * @mac_address: MAC addresses array.
+ */
+struct qlink_acl_data {
+       __le32 policy;
+       __le32 num_entries;
+       struct qlink_mac_address mac_addrs[0];
+} __packed;
+
+/**
+ * struct qlink_cmd_set_mac_acl - data for QLINK_CMD_SET_MAC_ACL command
+ *
+ * @acl: ACL data.
+ */
+struct qlink_cmd_set_mac_acl {
+       struct qlink_cmd chdr;
+       struct qlink_acl_data acl;
+} __packed;
+
 /* QLINK Command Responses messages related definitions
  */
 
@@ -701,6 +734,7 @@ struct qlink_resp_get_mac_info {
        struct ieee80211_ht_cap ht_cap_mod_mask;
        __le16 max_ap_assoc_sta;
        __le16 radar_detect_widths;
+       __le32 max_acl_mac_addrs;
        u8 bands_cap;
        u8 rsvd[1];
        u8 var_info[0];
@@ -1049,6 +1083,7 @@ enum qlink_tlv_id {
        QTN_TLV_ID_SEQ                  = 0x0303,
        QTN_TLV_ID_IE_SET               = 0x0305,
        QTN_TLV_ID_EXT_CAPABILITY_MASK  = 0x0306,
+       QTN_TLV_ID_ACL_DATA             = 0x0307,
 };
 
 struct qlink_tlv_hdr {
index 19981d6440b688ce9e35378e4937a001141e00ac..aeeda81b09eab57aa9d737cc6127245a1c94b1bd 100644 (file)
@@ -176,3 +176,21 @@ bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
 
        return arr[idx] & mask;
 }
+
+void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
+                         struct qlink_acl_data *qacl)
+{
+       switch (acl->acl_policy) {
+       case NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED:
+               qacl->policy =
+                       cpu_to_le32(QLINK_ACL_POLICY_ACCEPT_UNLESS_LISTED);
+               break;
+       case NL80211_ACL_POLICY_DENY_UNLESS_LISTED:
+               qacl->policy = cpu_to_le32(QLINK_ACL_POLICY_DENY_UNLESS_LISTED);
+               break;
+       }
+
+       qacl->num_entries = cpu_to_le32(acl->n_acl_entries);
+       memcpy(qacl->mac_addrs, acl->mac_addrs,
+              acl->n_acl_entries * sizeof(*qacl->mac_addrs));
+}
index 6c24561eb41f2e910d0faa36bf283c93848888af..54caeb38917c2c0e5a6c968a0ac4d7eb88812089 100644 (file)
@@ -71,5 +71,7 @@ void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef,
 enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val);
 bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
                           unsigned int arr_max_len);
+void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
+                         struct qlink_acl_data *qacl);
 
 #endif /* _QTN_FMAC_QLINK_UTIL_H_ */