Commit 77765eaf authored by Vasanthakumar Thiagarajan's avatar Vasanthakumar Thiagarajan Committed by Johannes Berg

cfg80211/nl80211: add API for MAC address ACLs

Add API to enable drivers to implement MAC address based
access control in AP/P2P GO mode. Capable drivers advertise
this capability by setting the maximum number of MAC
addresses in such a list in wiphy->max_acl_mac_addrs.

An initial ACL may be given to the NL80211_CMD_START_AP
command and/or changed later with NL80211_CMD_SET_MAC_ACL.

Black- and whitelists are supported, but not simultaneously.
Signed-off-by: default avatarVasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>
[rewrite commit log, many cleanups]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6d45a74b
...@@ -531,6 +531,22 @@ struct mac_address { ...@@ -531,6 +531,22 @@ struct mac_address {
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
}; };
/**
* struct cfg80211_acl_data - Access control list data
*
* @acl_policy: ACL policy to be applied on the station's
entry specified by mac_addr
* @n_acl_entries: Number of MAC address entries passed
* @mac_addrs: List of MAC addresses of stations to be used for ACL
*/
struct cfg80211_acl_data {
enum nl80211_acl_policy acl_policy;
int n_acl_entries;
/* Keep it last */
struct mac_address mac_addrs[];
};
/** /**
* struct cfg80211_ap_settings - AP configuration * struct cfg80211_ap_settings - AP configuration
* *
...@@ -550,6 +566,8 @@ struct mac_address { ...@@ -550,6 +566,8 @@ struct mac_address {
* @inactivity_timeout: time in seconds to determine station's inactivity. * @inactivity_timeout: time in seconds to determine station's inactivity.
* @p2p_ctwindow: P2P CT Window * @p2p_ctwindow: P2P CT Window
* @p2p_opp_ps: P2P opportunistic PS * @p2p_opp_ps: P2P opportunistic PS
* @acl: ACL configuration used by the drivers which has support for
* MAC address based access control
*/ */
struct cfg80211_ap_settings { struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
...@@ -566,6 +584,7 @@ struct cfg80211_ap_settings { ...@@ -566,6 +584,7 @@ struct cfg80211_ap_settings {
int inactivity_timeout; int inactivity_timeout;
u8 p2p_ctwindow; u8 p2p_ctwindow;
bool p2p_opp_ps; bool p2p_opp_ps;
const struct cfg80211_acl_data *acl;
}; };
/** /**
...@@ -1800,6 +1819,13 @@ struct cfg80211_gtk_rekey_data { ...@@ -1800,6 +1819,13 @@ struct cfg80211_gtk_rekey_data {
* *
* @start_p2p_device: Start the given P2P device. * @start_p2p_device: Start the given P2P device.
* @stop_p2p_device: Stop the given P2P device. * @stop_p2p_device: Stop the given P2P device.
*
* @set_mac_acl: Sets MAC address control list in AP and P2P GO mode.
* Parameters include ACL policy, an array of MAC address of stations
* and the number of MAC addresses. If there is already a list in driver
* this new list replaces the existing one. Driver has to clear its ACL
* when number of MAC addresses entries is passed as 0. Drivers which
* advertise the support for MAC based ACL have to implement this callback.
*/ */
struct cfg80211_ops { struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
...@@ -2020,6 +2046,9 @@ struct cfg80211_ops { ...@@ -2020,6 +2046,9 @@ struct cfg80211_ops {
struct wireless_dev *wdev); struct wireless_dev *wdev);
void (*stop_p2p_device)(struct wiphy *wiphy, void (*stop_p2p_device)(struct wiphy *wiphy,
struct wireless_dev *wdev); struct wireless_dev *wdev);
int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params);
}; };
/* /*
...@@ -2325,6 +2354,9 @@ struct wiphy_wowlan_support { ...@@ -2325,6 +2354,9 @@ struct wiphy_wowlan_support {
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden. * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
* If null, then none can be over-ridden. * If null, then none can be over-ridden.
*
* @max_acl_mac_addrs: Maximum number of MAC addresses that the device
* supports for ACL.
*/ */
struct wiphy { struct wiphy {
/* assign these fields before you register the wiphy */ /* assign these fields before you register the wiphy */
...@@ -2346,6 +2378,8 @@ struct wiphy { ...@@ -2346,6 +2378,8 @@ struct wiphy {
/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
u16 interface_modes; u16 interface_modes;
u16 max_acl_mac_addrs;
u32 flags, features; u32 flags, features;
u32 ap_sme_capa; u32 ap_sme_capa;
......
...@@ -170,7 +170,8 @@ ...@@ -170,7 +170,8 @@
* %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
* %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
* %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
* %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
* %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
* The channel to use can be set on the interface or be given using the * The channel to use can be set on the interface or be given using the
* %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
* @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
...@@ -586,6 +587,16 @@ ...@@ -586,6 +587,16 @@
* @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
* for IBSS or MESH vif. * for IBSS or MESH vif.
* *
* @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
* This is to be used with the drivers advertising the support of MAC
* address based access control. List of MAC addresses is passed in
* %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
* %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
* is not already done. The new list will replace any existing list. Driver
* will clear its ACL when the list of MAC addresses passed is empty. This
* command is used in AP/P2P GO mode. Driver has to make sure to clear its
* ACL list during %NL80211_CMD_STOP_AP.
*
* @NL80211_CMD_MAX: highest used command number * @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
...@@ -736,6 +747,8 @@ enum nl80211_commands { ...@@ -736,6 +747,8 @@ enum nl80211_commands {
NL80211_CMD_SET_MCAST_RATE, NL80211_CMD_SET_MCAST_RATE,
NL80211_CMD_SET_MAC_ACL,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
...@@ -1313,6 +1326,16 @@ enum nl80211_commands { ...@@ -1313,6 +1326,16 @@ enum nl80211_commands {
* @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
* defined in &enum nl80211_mesh_power_mode. * defined in &enum nl80211_mesh_power_mode.
* *
* @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
* carried in a u32 attribute
*
* @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
* MAC ACL.
*
* @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
* number of MAC addresses that a device can support for MAC
* ACL.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -1585,6 +1608,12 @@ enum nl80211_attrs { ...@@ -1585,6 +1608,12 @@ enum nl80211_attrs {
NL80211_ATTR_LOCAL_MESH_POWER_MODE, NL80211_ATTR_LOCAL_MESH_POWER_MODE,
NL80211_ATTR_ACL_POLICY,
NL80211_ATTR_MAC_ADDRS,
NL80211_ATTR_MAC_ACL_MAX,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -3248,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr { ...@@ -3248,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr {
* enum nl80211_connect_failed_reason - connection request failed reasons * enum nl80211_connect_failed_reason - connection request failed reasons
* @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
* handled by the AP is reached. * handled by the AP is reached.
* @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist. * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
*/ */
enum nl80211_connect_failed_reason { enum nl80211_connect_failed_reason {
NL80211_CONN_FAIL_MAX_CLIENTS, NL80211_CONN_FAIL_MAX_CLIENTS,
...@@ -3276,4 +3305,22 @@ enum nl80211_scan_flags { ...@@ -3276,4 +3305,22 @@ enum nl80211_scan_flags {
NL80211_SCAN_FLAG_AP = 1<<2, NL80211_SCAN_FLAG_AP = 1<<2,
}; };
/**
* enum nl80211_acl_policy - access control policy
*
* Access control policy is applied on a MAC list set by
* %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
* be used with %NL80211_ATTR_ACL_POLICY.
*
* @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
* listed in ACL, i.e. allow all the stations which are not listed
* in ACL to authenticate.
* @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
* in ACL, i.e. deny all the stations which are not listed in ACL.
*/
enum nl80211_acl_policy {
NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
};
#endif /* __LINUX_NL80211_H */ #endif /* __LINUX_NL80211_H */
...@@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy) ...@@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy)
ETH_ALEN))) ETH_ALEN)))
return -EINVAL; return -EINVAL;
if (WARN_ON(wiphy->max_acl_mac_addrs &&
(!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
!rdev->ops->set_mac_acl)))
return -EINVAL;
if (wiphy->addresses) if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
......
...@@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
...@@ -1268,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1268,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
dev->wiphy.ht_capa_mod_mask)) dev->wiphy.ht_capa_mod_mask))
goto nla_put_failure; goto nla_put_failure;
if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
dev->wiphy.max_acl_mac_addrs &&
nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
dev->wiphy.max_acl_mac_addrs))
goto nla_put_failure;
return genlmsg_end(msg, hdr); return genlmsg_end(msg, hdr);
nla_put_failure: nla_put_failure:
...@@ -2491,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) ...@@ -2491,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
return err; return err;
} }
/* This function returns an error or the number of nested attributes */
static int validate_acl_mac_addrs(struct nlattr *nl_attr)
{
struct nlattr *attr;
int n_entries = 0, tmp;
nla_for_each_nested(attr, nl_attr, tmp) {
if (nla_len(attr) != ETH_ALEN)
return -EINVAL;
n_entries++;
}
return n_entries;
}
/*
* This function parses ACL information and allocates memory for ACL data.
* On successful return, the calling function is responsible to free the
* ACL buffer returned by this function.
*/
static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
struct genl_info *info)
{
enum nl80211_acl_policy acl_policy;
struct nlattr *attr;
struct cfg80211_acl_data *acl;
int i = 0, n_entries, tmp;
if (!wiphy->max_acl_mac_addrs)
return ERR_PTR(-EOPNOTSUPP);
if (!info->attrs[NL80211_ATTR_ACL_POLICY])
return ERR_PTR(-EINVAL);
acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
return ERR_PTR(-EINVAL);
if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
return ERR_PTR(-EINVAL);
n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
if (n_entries < 0)
return ERR_PTR(n_entries);
if (n_entries > wiphy->max_acl_mac_addrs)
return ERR_PTR(-ENOTSUPP);
acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
i++;
}
acl->n_acl_entries = n_entries;
acl->acl_policy = acl_policy;
return acl;
}
static int nl80211_set_mac_acl(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 cfg80211_acl_data *acl;
int err;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
if (!dev->ieee80211_ptr->beacon_interval)
return -EINVAL;
acl = parse_acl_data(&rdev->wiphy, info);
if (IS_ERR(acl))
return PTR_ERR(acl);
err = rdev_set_mac_acl(rdev, dev, acl);
kfree(acl);
return err;
}
static int nl80211_parse_beacon(struct genl_info *info, static int nl80211_parse_beacon(struct genl_info *info,
struct cfg80211_beacon_data *bcn) struct cfg80211_beacon_data *bcn)
{ {
...@@ -2734,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ...@@ -2734,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (err) if (err)
return err; return err;
if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
params.acl = parse_acl_data(&rdev->wiphy, info);
if (IS_ERR(params.acl))
return PTR_ERR(params.acl);
}
err = rdev_start_ap(rdev, dev, &params); err = rdev_start_ap(rdev, dev, &params);
if (!err) { if (!err) {
wdev->preset_chandef = params.chandef; wdev->preset_chandef = params.chandef;
...@@ -2742,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ...@@ -2742,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->ssid_len = params.ssid_len; wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len); memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
} }
kfree(params.acl);
return err; return err;
} }
...@@ -7876,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = { ...@@ -7876,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV | .internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL, NL80211_FLAG_NEED_RTNL,
}, },
{
.cmd = NL80211_CMD_SET_MAC_ACL,
.doit = nl80211_set_mac_acl,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
}; };
static struct genl_multicast_group nl80211_mlme_mcgrp = { static struct genl_multicast_group nl80211_mlme_mcgrp = {
......
...@@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev, ...@@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
trace_rdev_return_void(&rdev->wiphy); trace_rdev_return_void(&rdev->wiphy);
} }
static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_acl_data *params)
{
int ret;
trace_rdev_set_mac_acl(&rdev->wiphy, dev, params);
ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
#endif /* __CFG80211_RDEV_OPS */ #endif /* __CFG80211_RDEV_OPS */
...@@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device, ...@@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
TP_ARGS(wiphy, wdev) TP_ARGS(wiphy, wdev)
); );
TRACE_EVENT(rdev_set_mac_acl,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_acl_data *params),
TP_ARGS(wiphy, netdev, params),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
__field(u32, acl_policy)
),
TP_fast_assign(
WIPHY_ASSIGN;
WIPHY_ASSIGN;
__entry->acl_policy = params->acl_policy;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
);
/************************************************************* /*************************************************************
* cfg80211 exported functions traces * * cfg80211 exported functions traces *
*************************************************************/ *************************************************************/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment