[NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
};
+/* IE validation */
+static bool is_valid_ie_attr(const struct nlattr *attr)
+{
+ const u8 *pos;
+ int len;
+
+ if (!attr)
+ return true;
+
+ pos = nla_data(attr);
+ len = nla_len(attr);
+
+ while (len) {
+ u8 elemlen;
+
+ if (len < 2)
+ return false;
+ len -= 2;
+
+ elemlen = pos[1];
+ if (elemlen > len)
+ return false;
+
+ len -= elemlen;
+ pos += 2 + elemlen;
+ }
+
+ return true;
+}
+
/* message building helper */
static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
int flags, u8 cmd)
return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
}
+static int nl80211_msg_put_channel(struct sk_buff *msg,
+ struct ieee80211_channel *chan)
+{
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
+ chan->center_freq);
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
+ if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
+ if (chan->flags & IEEE80211_CHAN_RADAR)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
+
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ DBM_TO_MBM(chan->max_power));
+
+ return 0;
+
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
/* netlink command implementations */
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids);
+ NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
+ dev->wiphy.max_scan_ie_len);
+
+ NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
+ sizeof(u32) * dev->wiphy.n_cipher_suites,
+ dev->wiphy.cipher_suites);
nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
if (!nl_modes)
goto nla_put_failure;
chan = &dev->wiphy.bands[band]->channels[i];
- NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
- chan->center_freq);
- if (chan->flags & IEEE80211_CHAN_DISABLED)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
- if (chan->flags & IEEE80211_CHAN_NO_IBSS)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
- if (chan->flags & IEEE80211_CHAN_RADAR)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
-
- NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
- DBM_TO_MBM(chan->max_power));
+ if (nl80211_msg_put_channel(msg, chan))
+ goto nla_put_failure;
nla_nest_end(msg, nl_freq);
}
int result = 0, rem_txq_params = 0;
struct nlattr *nl_txq_params;
- rdev = cfg80211_get_dev_from_info(info);
- if (IS_ERR(rdev))
- return PTR_ERR(rdev);
+ rtnl_lock();
+
+ mutex_lock(&cfg80211_mutex);
+
+ rdev = __cfg80211_drv_from_info(info);
+ if (IS_ERR(rdev)) {
+ result = PTR_ERR(rdev);
+ goto unlock;
+ }
+
+ mutex_lock(&rdev->mtx);
- if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+ if (info->attrs[NL80211_ATTR_WIPHY_NAME])
result = cfg80211_dev_rename(
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
- if (result)
- goto bad_res;
- }
+
+ mutex_unlock(&cfg80211_mutex);
+
+ if (result)
+ goto bad_res;
if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
struct ieee80211_txq_params txq_params;
bad_res:
- cfg80211_put_dev(rdev);
+ mutex_unlock(&rdev->mtx);
+ unlock:
+ rtnl_unlock();
return result;
}
enum nl80211_iftype type;
struct net_device *dev;
u32 _flags, *flags = NULL;
+ bool change = false;
memset(¶ms, 0, sizeof(params));
type = dev->ieee80211_ptr->iftype;
dev_put(dev);
- err = -EINVAL;
if (info->attrs[NL80211_ATTR_IFTYPE]) {
- type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
- if (type > NL80211_IFTYPE_MAX)
+ enum nl80211_iftype ntype;
+
+ ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+ if (type != ntype)
+ change = true;
+ type = ntype;
+ if (type > NL80211_IFTYPE_MAX) {
+ err = -EINVAL;
goto unlock;
+ }
}
if (!drv->ops->change_virtual_intf ||
}
params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+ change = true;
}
if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
}
err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
&_flags);
- if (!err)
- flags = &_flags;
+ if (err)
+ goto unlock;
+
+ flags = &_flags;
+ change = true;
}
- err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
- type, flags, ¶ms);
+ if (change)
+ err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
+ type, flags, ¶ms);
+ else
+ err = 0;
dev = __dev_get_by_index(&init_net, ifindex);
WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
- int err;
+ int err, i;
struct net_device *dev;
struct key_params params;
u8 key_idx = 0;
if (err)
goto unlock_rtnl;
+ for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
+ if (params.cipher == drv->wiphy.cipher_suites[i])
+ break;
+ if (i == drv->wiphy.n_cipher_suites) {
+ err = -EINVAL;
+ goto out;
+ }
+
if (!drv->ops->add_key) {
err = -EOPNOTSUPP;
goto out;
struct beacon_parameters params;
int haveinfo = 0;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
goto unlock_rtnl;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
switch (info->genlhdr->cmd) {
case NL80211_CMD_NEW_BEACON:
/* these are required for NEW_BEACON */
goto out;
}
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
err = drv->ops->del_beacon(&drv->wiphy, dev);
out:
}
if (!dev->ops->dump_station) {
- err = -ENOSYS;
+ err = -EOPNOTSUPP;
goto out_err;
}
goto out;
}
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms);
out:
}
if (!dev->ops->dump_mpath) {
- err = -ENOSYS;
+ err = -EOPNOTSUPP;
goto out_err;
}
+ if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
while (1) {
err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
dst, next_hop, &pinfo);
goto out;
}
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
if (err)
goto out;
goto out;
}
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
out:
goto out;
}
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
out:
goto out;
}
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms);
out:
enum ieee80211_band band;
size_t ie_len;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
goto out;
}
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
if (drv->scan_req) {
err = -EBUSY;
goto out;
else
ie_len = 0;
+ if (ie_len > wiphy->max_scan_ie_len) {
+ err = -EINVAL;
+ goto out;
+ }
+
request = kzalloc(sizeof(*request)
+ sizeof(*ssid) * n_ssids
+ sizeof(channel) * n_channels
if (info->attrs[NL80211_ATTR_IE]) {
request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
- memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]),
+ memcpy((void *)request->ie,
+ nla_data(info->attrs[NL80211_ATTR_IE]),
request->ie_len);
}
return err;
}
+static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
+{
+ return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
+ auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
+ auth_type == NL80211_AUTHTYPE_FT ||
+ auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
+}
+
static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC]) {
- err = -EINVAL;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
goto out;
}
req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
- if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
- req.auth_type =
- nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ if (!nl80211_valid_auth_type(req.auth_type)) {
+ err = -EINVAL;
+ goto out;
}
err = drv->ops->auth(&drv->wiphy, dev, &req);
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_SSID])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC] ||
- !info->attrs[NL80211_ATTR_SSID]) {
- err = -EINVAL;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
goto out;
}
}
}
- if (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) {
- err = -EINVAL;
- goto out;
- }
req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC]) {
- err = -EINVAL;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
goto out;
}
req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
- if (info->attrs[NL80211_ATTR_REASON_CODE])
- req.reason_code =
- nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (req.reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ err = -EINVAL;
+ goto out;
+ }
if (info->attrs[NL80211_ATTR_IE]) {
req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC]) {
- err = -EINVAL;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
goto out;
}
req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
- if (info->attrs[NL80211_ATTR_REASON_CODE])
- req.reason_code =
- nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (req.reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ err = -EINVAL;
+ goto out;
+ }
if (info->attrs[NL80211_ATTR_IE]) {
req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
if (!msg)
return;
return;
}
- genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+ genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
return;
nla_put_failure:
nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
}
-void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *buf,
- size_t len)
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf, size_t len)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_DEAUTHENTICATE);
}
-void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *buf,
- size_t len)
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_DISASSOCIATE);
}
+void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id,
+ const u8 *tsc)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ if (addr)
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
+ NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
+ if (tsc)
+ NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
+ struct ieee80211_channel *channel_before,
+ struct ieee80211_channel *channel_after)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ struct nlattr *nl_freq;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ /*
+ * Since we are applying the beacon hint to a wiphy we know its
+ * wiphy_idx is valid
+ */
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
+
+ /* Before */
+ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
+ if (!nl_freq)
+ goto nla_put_failure;
+ if (nl80211_msg_put_channel(msg, channel_before))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_freq);
+
+ /* After */
+ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
+ if (!nl_freq)
+ goto nla_put_failure;
+ if (nl80211_msg_put_channel(msg, channel_after))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_freq);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
/* initialisation/exit functions */
int nl80211_init(void)