Skip to content

Commit

Permalink
cfg80211: introduce capability for 4addr mode
Browse files Browse the repository at this point in the history
It's very likely that not many devices will support
four-address mode in station or AP mode so introduce
capability bits for both modes, set them in mac80211
and check them when userspace tries to use the mode.
Also, keep track of 4addr in cfg80211 (wireless_dev)
and not in mac80211 any more. mac80211 can also be
improved for the VLAN case by not looking at the
4addr flag but maintaining the station pointer for
it correctly. However, keep track of use_4addr for
station mode in mac80211 to avoid all the derefs.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Nov 19, 2009
1 parent 5be83de commit 9bc383d
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 30 deletions.
11 changes: 11 additions & 0 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1134,13 +1134,18 @@ struct cfg80211_ops {
* by default -- this flag will be set depending on the kernel's default
* on wiphy_new(), but can be changed by the driver if it has a good
* reason to override the default
* @WIPHY_FLAG_4ADDR_AP: supports 4addr mode even on AP (with a single station
* on a VLAN interface)
* @WIPHY_FLAG_4ADDR_STATION: supports 4addr mode even as a station
*/
enum wiphy_flags {
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
WIPHY_FLAG_STRICT_REGULATORY = BIT(1),
WIPHY_FLAG_DISABLE_BEACON_HINTS = BIT(2),
WIPHY_FLAG_NETNS_OK = BIT(3),
WIPHY_FLAG_PS_ON_BY_DEFAULT = BIT(4),
WIPHY_FLAG_4ADDR_AP = BIT(5),
WIPHY_FLAG_4ADDR_STATION = BIT(6),
};

/**
Expand Down Expand Up @@ -1366,6 +1371,10 @@ struct cfg80211_cached_keys;
* @ssid_len: (private) Used by the internal configuration code
* @wext: (private) Used by the internal wireless extensions compat code
* @wext_bssid: (private) Used by the internal wireless extensions compat code
* @use_4addr: indicates 4addr mode is used on this interface, must be
* set by driver (if supported) on add_interface BEFORE registering the
* netdev and may otherwise be used by driver read-only, will be update
* by cfg80211 on change_interface
*/
struct wireless_dev {
struct wiphy *wiphy;
Expand All @@ -1379,6 +1388,8 @@ struct wireless_dev {

struct work_struct cleanup_work;

bool use_4addr;

/* currently used for IBSS and SME - might be rearranged later */
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len;
Expand Down
21 changes: 8 additions & 13 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,6 @@ static bool nl80211_params_check(enum nl80211_iftype type,
if (!nl80211_type_check(type))
return false;

if (params->use_4addr > 0) {
switch(type) {
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_STATION:
break;
default:
return false;
}
}
return true;
}

Expand Down Expand Up @@ -107,12 +98,16 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
params->mesh_id_len,
params->mesh_id);

if (params->use_4addr >= 0)
sdata->use_4addr = !!params->use_4addr;

if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
return 0;

if (type == NL80211_IFTYPE_AP_VLAN &&
params && params->use_4addr == 0)
rcu_assign_pointer(sdata->u.vlan.sta, NULL);
else if (type == NL80211_IFTYPE_STATION &&
params && params->use_4addr >= 0)
sdata->u.mgd.use_4addr = params->use_4addr;

sdata->u.mntr_flags = *flags;
return 0;
}
Expand Down Expand Up @@ -827,7 +822,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
return -EINVAL;
}

if (vlansdata->use_4addr) {
if (params->vlan->ieee80211_ptr->use_4addr) {
if (vlansdata->u.vlan.sta)
return -EBUSY;

Expand Down
4 changes: 2 additions & 2 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ struct ieee80211_if_managed {
} mfp; /* management frame protection */

int wmm_last_param_set;

u8 use_4addr;
};

enum ieee80211_ibss_request {
Expand Down Expand Up @@ -459,8 +461,6 @@ struct ieee80211_sub_if_data {
int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
int max_ratectrl_rateidx; /* max TX rateidx for rate control */

bool use_4addr; /* use 4-address frames */

union {
struct ieee80211_if_ap ap;
struct ieee80211_if_wds wds;
Expand Down
12 changes: 8 additions & 4 deletions net/mac80211/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,8 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
ieee80211_mandatory_rates(sdata->local,
sdata->local->hw.conf.channel->band);
sdata->drop_unencrypted = 0;
sdata->use_4addr = 0;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false;

return 0;
}
Expand Down Expand Up @@ -810,6 +811,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);

if (params) {
ndev->ieee80211_ptr->use_4addr = params->use_4addr;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = params->use_4addr;
}

ret = register_netdevice(ndev);
if (ret)
goto fail;
Expand All @@ -820,9 +827,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
params->mesh_id_len,
params->mesh_id);

if (params && params->use_4addr >= 0)
sdata->use_4addr = !!params->use_4addr;

mutex_lock(&local->iflist_mtx);
list_add_tail_rcu(&sdata->list, &local->interfaces);
mutex_unlock(&local->iflist_mtx);
Expand Down
4 changes: 3 additions & 1 deletion net/mac80211/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (!wiphy)
return NULL;

wiphy->flags |= WIPHY_FLAG_NETNS_OK;
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_4ADDR_AP |
WIPHY_FLAG_4ADDR_STATION;
wiphy->privid = mac80211_wiphy_privid;

/* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
Expand Down
14 changes: 9 additions & 5 deletions net/mac80211/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1192,10 +1192,13 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
struct net_device *dev = sdata->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;

if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->use_4addr &&
ieee80211_has_a4(hdr->frame_control))
if (ieee80211_has_a4(hdr->frame_control) &&
sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta)
return -1;
if (sdata->use_4addr && is_multicast_ether_addr(hdr->addr1))

if (is_multicast_ether_addr(hdr->addr1) &&
((sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) ||
(sdata->vif.type == NL80211_IFTYPE_STATION && sdata->u.mgd.use_4addr)))
return -1;

return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type);
Expand Down Expand Up @@ -1245,7 +1248,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
(rx->flags & IEEE80211_RX_RA_MATCH) && !rx->sdata->use_4addr) {
(rx->flags & IEEE80211_RX_RA_MATCH) &&
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) {
if (is_multicast_ether_addr(ehdr->h_dest)) {
/*
* send multicast frames both to higher layers in
Expand Down Expand Up @@ -2007,7 +2011,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,

switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
if (!bssid && !sdata->use_4addr)
if (!bssid && !sdata->u.mgd.use_4addr)
return 0;
if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
Expand Down
7 changes: 3 additions & 4 deletions net/mac80211/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,

hdr = (struct ieee80211_hdr *) skb->data;

if ((sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && sdata->use_4addr)
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
tx->sta = rcu_dereference(sdata->u.vlan.sta);
if (!tx->sta)
tx->sta = sta_info_get(local, hdr->addr1);
Expand Down Expand Up @@ -1632,8 +1632,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
rcu_read_lock();
if (sdata->use_4addr)
sta = rcu_dereference(sdata->u.vlan.sta);
sta = rcu_dereference(sdata->u.vlan.sta);
if (sta) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */
Expand Down Expand Up @@ -1727,7 +1726,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
#endif
case NL80211_IFTYPE_STATION:
memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN);
if (sdata->use_4addr && ethertype != ETH_P_PAE) {
if (sdata->u.mgd.use_4addr && ethertype != ETH_P_PAE) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
Expand Down
34 changes: 33 additions & 1 deletion net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,28 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
return 0;
}

static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
u8 use_4addr, enum nl80211_iftype iftype)
{
if (!use_4addr)
return 0;

switch (iftype) {
case NL80211_IFTYPE_AP_VLAN:
if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
return 0;
break;
case NL80211_IFTYPE_STATION:
if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
return 0;
break;
default:
break;
}

return -EOPNOTSUPP;
}

static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
Expand Down Expand Up @@ -1011,6 +1033,9 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_4ADDR]) {
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
change = true;
err = nl80211_valid_4addr(rdev, params.use_4addr, ntype);
if (err)
goto unlock;
} else {
params.use_4addr = -1;
}
Expand All @@ -1034,6 +1059,9 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
else
err = 0;

if (!err && params.use_4addr != -1)
dev->ieee80211_ptr->use_4addr = params.use_4addr;

unlock:
dev_put(dev);
cfg80211_unlock_rdev(rdev);
Expand Down Expand Up @@ -1081,8 +1109,12 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
}

if (info->attrs[NL80211_ATTR_4ADDR])
if (info->attrs[NL80211_ATTR_4ADDR]) {
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
err = nl80211_valid_4addr(rdev, params.use_4addr, type);
if (err)
goto unlock;
}

err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
Expand Down
5 changes: 5 additions & 0 deletions net/wireless/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
return -EOPNOTSUPP;

if (ntype != otype) {
dev->ieee80211_ptr->use_4addr = false;

switch (otype) {
case NL80211_IFTYPE_ADHOC:
cfg80211_leave_ibss(rdev, dev, false);
Expand All @@ -682,5 +684,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,

WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);

if (!err && params && params->use_4addr != -1)
dev->ieee80211_ptr->use_4addr = params->use_4addr;

return err;
}

0 comments on commit 9bc383d

Please sign in to comment.