diff options
| -rw-r--r-- | package/iw/patches/000-upstream.patch | 155 | ||||
| -rw-r--r-- | package/iw/patches/100-4addr.patch | 76 | ||||
| -rw-r--r-- | package/iw/patches/110-sta_vlan.patch | 69 | ||||
| -rw-r--r-- | package/mac80211/files/lib/wifi/mac80211.sh | 5 | ||||
| -rw-r--r-- | package/mac80211/patches/500-nl80211_4addr.patch | 69 | ||||
| -rw-r--r-- | package/mac80211/patches/510-mac80211_4addr_vlan.patch | 260 | 
6 files changed, 633 insertions, 1 deletions
diff --git a/package/iw/patches/000-upstream.patch b/package/iw/patches/000-upstream.patch new file mode 100644 index 000000000..8aee7758a --- /dev/null +++ b/package/iw/patches/000-upstream.patch @@ -0,0 +1,155 @@ +--- a/Makefile ++++ b/Makefile +@@ -3,7 +3,7 @@ + MAKEFLAGS += --no-print-directory +  + PREFIX ?= /usr +-BINDIR ?= $(PREFIX)/bin ++SBINDIR ?= $(PREFIX)/sbin + MANDIR ?= $(PREFIX)/share/man + PKG_CONFIG ?= pkg-config +  +@@ -85,8 +85,8 @@ check: +  + install: iw iw.8.gz + 	@$(NQ) ' INST iw' +-	$(Q)$(MKDIR) $(DESTDIR)$(BINDIR) +-	$(Q)$(INSTALL) -m 755 -t $(DESTDIR)$(BINDIR) iw ++	$(Q)$(MKDIR) $(DESTDIR)$(SBINDIR) ++	$(Q)$(INSTALL) -m 755 -t $(DESTDIR)$(SBINDIR) iw + 	@$(NQ) ' INST iw.8' + 	$(Q)$(MKDIR) $(DESTDIR)$(MANDIR)/man8/ + 	$(Q)$(INSTALL) -m 644 -t $(DESTDIR)$(MANDIR)/man8/ iw.8.gz +--- a/iw.c ++++ b/iw.c +@@ -223,9 +223,12 @@ static int phy_lookup(char *name) + 	if (fd < 0) + 		return -1; + 	pos = read(fd, buf, sizeof(buf) - 1); +-	if (pos < 0) ++	if (pos < 0) { ++		close(fd); + 		return -1; ++	} + 	buf[pos] = '\0'; ++	close(fd); + 	return atoi(buf); + } +  +--- a/nl80211.h ++++ b/nl80211.h +@@ -262,6 +262,9 @@ +  *	reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and +  *	%NL80211_ATTR_REASON_CODE attributes are used. +  * ++ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices ++ *	associated with this wiphy must be down and will follow. ++ * +  * @NL80211_CMD_MAX: highest used command number +  * @__NL80211_CMD_AFTER_LAST: internal use +  */ +@@ -336,6 +339,8 @@ enum nl80211_commands { + 	NL80211_CMD_ROAM, + 	NL80211_CMD_DISCONNECT, +  ++	NL80211_CMD_SET_WIPHY_NETNS, ++ + 	/* add new commands above here */ +  + 	/* used to define NL80211_CMD_MAX below */ +@@ -475,10 +480,6 @@ enum nl80211_commands { +  * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) +  * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive +  *	scanning and include a zero-length SSID (wildcard) for wildcard scan +- * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the +- *	scan result list changes (BSS expired or added) so that applications +- *	can verify that they got a single, consistent snapshot (when all dump +- *	messages carried the same generation number) +  * @NL80211_ATTR_BSS: scan result BSS +  * +  * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain +@@ -573,6 +574,16 @@ enum nl80211_commands { +  *	and join_ibss(), key information is in a nested attribute each +  *	with %NL80211_KEY_* sub-attributes +  * ++ * @NL80211_ATTR_PID: Process ID of a network namespace. ++ * ++ * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for ++ *	dumps. This number increases whenever the object list being ++ *	dumped changes, and as such userspace can verify that it has ++ *	obtained a complete and consistent snapshot by verifying that ++ *	all dump messages contain the same generation number. If it ++ *	changed then the list changed and the dump should be repeated ++ *	completely from scratch. ++ * +  * @NL80211_ATTR_MAX: highest attribute number currently defined +  * @__NL80211_ATTR_AFTER_LAST: internal use +  */ +@@ -644,7 +655,7 @@ enum nl80211_attrs { +  + 	NL80211_ATTR_SCAN_FREQUENCIES, + 	NL80211_ATTR_SCAN_SSIDS, +-	NL80211_ATTR_SCAN_GENERATION, ++	NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + 	NL80211_ATTR_BSS, +  + 	NL80211_ATTR_REG_INITIATOR, +@@ -701,12 +712,17 @@ enum nl80211_attrs { + 	NL80211_ATTR_KEY, + 	NL80211_ATTR_KEYS, +  ++	NL80211_ATTR_PID, ++ + 	/* add attributes here, update the policy in nl80211.c */ +  + 	__NL80211_ATTR_AFTER_LAST, + 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 + }; +  ++/* source-level API compatibility */ ++#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION ++ + /* +  * Allow user space programs to use #ifdef on new attributes by defining them +  * here +--- a/phy.c ++++ b/phy.c +@@ -140,3 +140,27 @@ static int handle_rts(struct nl80211_sta + COMMAND(set, rts, "<rts threshold|off>", + 	NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts, + 	"Set rts threshold."); ++ ++static int handle_netns(struct nl80211_state *state, ++			struct nl_cb *cb, ++			struct nl_msg *msg, ++			int argc, char **argv) ++{ ++	char *end; ++ ++	if (argc != 1) ++		return 1; ++ ++	NLA_PUT_U32(msg, NL80211_ATTR_PID, ++		    strtoul(argv[0], &end, 10));  ++ ++	if (*end != '\0') ++		return 1; ++ ++	return 0; ++ nla_put_failure: ++	return -ENOBUFS; ++} ++COMMAND(set, netns, "<pid>", ++	NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns, ++	"Put this wireless device into a different network namespace"); +--- a/station.c ++++ b/station.c +@@ -127,7 +127,7 @@ static int print_sta_handler(struct nl_m + 		printf("\n\tmesh plid:\t%d", + 			nla_get_u16(sinfo[NL80211_STA_INFO_PLID])); + 	if (sinfo[NL80211_STA_INFO_PLINK_STATE]) { +-		switch (nla_get_u16(sinfo[NL80211_STA_INFO_PLINK_STATE])) { ++		switch (nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE])) { + 		case LISTEN: + 			strcpy(state_name, "LISTEN"); + 			break; diff --git a/package/iw/patches/100-4addr.patch b/package/iw/patches/100-4addr.patch new file mode 100644 index 000000000..db6b88010 --- /dev/null +++ b/package/iw/patches/100-4addr.patch @@ -0,0 +1,76 @@ +--- a/interface.c ++++ b/interface.c +@@ -137,6 +137,20 @@ static int get_if_type(int *argc, char * + 	return 2; + } +  ++static int parse_wds_flag(const char *value, struct nl_msg *msg) ++{ ++	if (strcmp(value, "on") == 0) ++		NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, 1); ++	else if (strcmp(value, "off") == 0) ++		NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, 0); ++	else ++		return 1; ++	return 0; ++ ++nla_put_failure: ++	return 1; ++} ++ + static int handle_interface_add(struct nl80211_state *state, + 				struct nl_cb *cb, + 				struct nl_msg *msg, +@@ -168,6 +182,15 @@ static int handle_interface_add(struct n + 			mesh_id = argv[0]; + 			argc--; + 			argv++; ++		} else if (strcmp(argv[0], "wds") == 0) { ++			argc--; ++			argv++; ++			if (parse_wds_flag(argv[0], msg)) { ++				fprintf(stderr, "wds error\n"); ++				return 2; ++			} ++			argc--; ++			argv++; + 		} else if (strcmp(argv[0], "flags") == 0) { + 			argc--; + 			argv++; +@@ -192,14 +215,14 @@ static int handle_interface_add(struct n +  nla_put_failure: + 	return -ENOBUFS; + } +-COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>] [flags <flag>*]", ++COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>] [wds on|off] [flags <flag>*]", + 	NL80211_CMD_NEW_INTERFACE, 0, CIB_PHY, handle_interface_add, + 	"Add a new virtual interface with the given configuration.\n" + 	IFACE_TYPES "\n\n" + 	"The flags are only used for monitor interfaces, valid flags are:\n" + 	VALID_FLAGS "\n\n" + 	"The mesh_id is used only for mesh mode."); +-COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>] [flags <flag>*]", ++COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>] [wds on|off] [flags <flag>*]", + 	NL80211_CMD_NEW_INTERFACE, 0, CIB_NETDEV, handle_interface_add, NULL); +  + static int handle_interface_del(struct nl80211_state *state, +--- a/nl80211.h ++++ b/nl80211.h +@@ -584,6 +584,8 @@ enum nl80211_commands { +  *	changed then the list changed and the dump should be repeated +  *	completely from scratch. +  * ++ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface ++ * +  * @NL80211_ATTR_MAX: highest attribute number currently defined +  * @__NL80211_ATTR_AFTER_LAST: internal use +  */ +@@ -714,6 +716,8 @@ enum nl80211_attrs { +  + 	NL80211_ATTR_PID, +  ++	NL80211_ATTR_4ADDR, ++ + 	/* add attributes here, update the policy in nl80211.c */ +  + 	__NL80211_ATTR_AFTER_LAST, diff --git a/package/iw/patches/110-sta_vlan.patch b/package/iw/patches/110-sta_vlan.patch new file mode 100644 index 000000000..913c698bb --- /dev/null +++ b/package/iw/patches/110-sta_vlan.patch @@ -0,0 +1,69 @@ +--- a/station.c ++++ b/station.c +@@ -196,7 +196,7 @@ COMMAND(station, del, "<MAC address>", + 	NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_get, + 	"Remove the given station entry (use with caution!)"); +  +-static int handle_station_set(struct nl80211_state *state, ++static int handle_station_set_plink(struct nl80211_state *state, + 			      struct nl_cb *cb, + 			      struct nl_msg *msg, + 			      int argc, char **argv) +@@ -241,9 +241,56 @@ static int handle_station_set(struct nl8 + 	return -ENOBUFS; + } + COMMAND(station, set, "<MAC address> plink_action <open|block>", +-	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set, ++	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_plink, + 	"Set mesh peer link action for this station (peer)."); +  ++static int handle_station_set_vlan(struct nl80211_state *state, ++			      struct nl_cb *cb, ++			      struct nl_msg *msg, ++			      int argc, char **argv) ++{ ++	unsigned char mac_addr[ETH_ALEN]; ++	unsigned long sta_vlan = 0; ++	char *err = NULL; ++ ++	if (argc < 3) ++		return 1; ++ ++	if (mac_addr_a2n(mac_addr, argv[0])) { ++		fprintf(stderr, "invalid mac address\n"); ++		return 2; ++	} ++	argc--; ++	argv++; ++ ++	if (strcmp("vlan", argv[0]) != 0) ++		return 1; ++	argc--; ++	argv++; ++ ++	sta_vlan = strtoul(argv[0], &err, 0); ++	if (err && *err) { ++		fprintf(stderr, "invalid vlan id\n"); ++		return 2; ++	} ++	argc--; ++	argv++; ++ ++	if (argc) ++		return 1; ++ ++	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); ++	NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, sta_vlan); ++ ++	return 0; ++ nla_put_failure: ++	return -ENOBUFS; ++} ++COMMAND(station, set, "<MAC address> vlan <ifindex>", ++	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_vlan, ++	"Set an AP VLAN for this station."); ++ ++ + static int handle_station_dump(struct nl80211_state *state, + 			       struct nl_cb *cb, + 			       struct nl_msg *msg, diff --git a/package/mac80211/files/lib/wifi/mac80211.sh b/package/mac80211/files/lib/wifi/mac80211.sh index ccd4d2c38..027749e57 100644 --- a/package/mac80211/files/lib/wifi/mac80211.sh +++ b/package/mac80211/files/lib/wifi/mac80211.sh @@ -104,6 +104,7 @@ enable_mac80211() {  		config_get enc "$vif" encryption  		config_get mode "$vif" mode  		config_get ssid "$vif" ssid +		config_get_bool wds "$vif" wds 0  		# It is far easier to delete and create the desired interface  		case "$mode" in @@ -123,7 +124,9 @@ enable_mac80211() {  				iw phy "$phy" interface add "$ifname" type monitor  			;;  			sta) -				iw phy "$phy" interface add "$ifname" type managed +				local wdsflag +				[ "$wds" -gt 0 ] && wdsflag="wds on" +				iw phy "$phy" interface add "$ifname" type managed $wdsflag  			;;  		esac diff --git a/package/mac80211/patches/500-nl80211_4addr.patch b/package/mac80211/patches/500-nl80211_4addr.patch new file mode 100644 index 000000000..de3b78fdb --- /dev/null +++ b/package/mac80211/patches/500-nl80211_4addr.patch @@ -0,0 +1,69 @@ +--- a/include/linux/nl80211.h ++++ b/include/linux/nl80211.h +@@ -584,6 +584,8 @@ enum nl80211_commands { +  *	changed then the list changed and the dump should be repeated +  *	completely from scratch. +  * ++ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface ++ * +  * @NL80211_ATTR_MAX: highest attribute number currently defined +  * @__NL80211_ATTR_AFTER_LAST: internal use +  */ +@@ -714,6 +716,8 @@ enum nl80211_attrs { +  + 	NL80211_ATTR_PID, +  ++	NL80211_ATTR_4ADDR, ++ + 	/* add attributes here, update the policy in nl80211.c */ +  + 	__NL80211_ATTR_AFTER_LAST, +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -207,10 +207,12 @@ struct ieee80211_supported_band { +  * struct vif_params - describes virtual interface parameters +  * @mesh_id: mesh ID to use +  * @mesh_id_len: length of the mesh ID ++ * @use_4addr: use 4-address frames +  */ + struct vif_params { +        u8 *mesh_id; +        int mesh_id_len; ++       int use_4addr; + }; +  + /** +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -140,6 +140,7 @@ static struct nla_policy nl80211_policy[ + 	[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, + 	[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, + 	[NL80211_ATTR_PID] = { .type = NLA_U32 }, ++	[NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, + }; +  + /* policy for the attributes */ +@@ -989,6 +990,13 @@ static int nl80211_set_interface(struct  + 		change = true; + 	} +  ++	if (info->attrs[NL80211_ATTR_4ADDR]) { ++		params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); ++		change = true; ++	} else { ++		params.use_4addr = -1; ++	} ++ + 	if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { + 		if (ntype != NL80211_IFTYPE_MONITOR) { + 			err = -EINVAL; +@@ -1055,6 +1063,9 @@ static int nl80211_new_interface(struct  + 		params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + 	} +  ++	if (info->attrs[NL80211_ATTR_4ADDR]) ++		params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); ++ + 	err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? + 				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, + 				  &flags); diff --git a/package/mac80211/patches/510-mac80211_4addr_vlan.patch b/package/mac80211/patches/510-mac80211_4addr_vlan.patch new file mode 100644 index 000000000..ac92fe54b --- /dev/null +++ b/package/mac80211/patches/510-mac80211_4addr_vlan.patch @@ -0,0 +1,260 @@ +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -208,6 +208,9 @@ struct ieee80211_if_wds { +  + struct ieee80211_if_vlan { + 	struct list_head list; ++ ++	/* used for all tx if the VLAN is configured to 4-addr mode */ ++	struct sta_info *sta; + }; +  + struct mesh_stats { +@@ -457,6 +460,8 @@ 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; +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -36,6 +36,24 @@ static bool nl80211_type_check(enum nl80 + 	} + } +  ++static bool nl80211_params_check(enum nl80211_iftype type, ++				 struct vif_params *params) ++{ ++	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; ++} ++ + static int ieee80211_add_iface(struct wiphy *wiphy, char *name, + 			       enum nl80211_iftype type, u32 *flags, + 			       struct vif_params *params) +@@ -45,7 +63,7 @@ static int ieee80211_add_iface(struct wi + 	struct ieee80211_sub_if_data *sdata; + 	int err; +  +-	if (!nl80211_type_check(type)) ++	if (!nl80211_params_check(type, params)) + 		return -EINVAL; +  + 	err = ieee80211_if_add(local, name, &dev, type, params); +@@ -75,7 +93,7 @@ static int ieee80211_change_iface(struct + 	if (netif_running(dev)) + 		return -EBUSY; +  +-	if (!nl80211_type_check(type)) ++	if (!nl80211_params_check(type, params)) + 		return -EINVAL; +  + 	sdata = IEEE80211_DEV_TO_SUB_IF(dev); +@@ -89,6 +107,9 @@ static int ieee80211_change_iface(struct + 					    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; +  +@@ -806,6 +827,13 @@ static int ieee80211_change_station(stru + 			return -EINVAL; + 		} +  ++		if (vlansdata->use_4addr) { ++			if (vlansdata->u.vlan.sta) ++				return -EBUSY; ++ ++			rcu_assign_pointer(vlansdata->u.vlan.sta, sta); ++		} ++ + 		sta->sdata = vlansdata; + 		ieee80211_send_layer2_update(sta); + 	} +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -489,6 +489,9 @@ static void __sta_info_unlink(struct sta + 	local->num_sta--; + 	local->sta_generation++; +  ++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++		rcu_assign_pointer(sdata->u.vlan.sta, NULL); ++ + 	if (local->ops->sta_notify) { + 		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + 			sdata = container_of(sdata->bss, +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -1046,7 +1046,10 @@ ieee80211_tx_prepare(struct ieee80211_su +  + 	hdr = (struct ieee80211_hdr *) skb->data; +  +-	tx->sta = sta_info_get(local, hdr->addr1); ++	if ((sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && sdata->use_4addr) ++		tx->sta = rcu_dereference(sdata->u.vlan.sta); ++	if (!tx->sta) ++		tx->sta = sta_info_get(local, hdr->addr1); +  + 	if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && + 	    (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { +@@ -1608,7 +1611,7 @@ netdev_tx_t ieee80211_subif_start_xmit(s + 	const u8 *encaps_data; + 	int encaps_len, skip_header_bytes; + 	int nh_pos, h_pos; +-	struct sta_info *sta; ++	struct sta_info *sta = NULL; + 	u32 sta_flags = 0; +  + 	if (unlikely(skb->len < ETH_HLEN)) { +@@ -1625,8 +1628,25 @@ netdev_tx_t ieee80211_subif_start_xmit(s + 	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); +  + 	switch (sdata->vif.type) { +-	case NL80211_IFTYPE_AP: + 	case NL80211_IFTYPE_AP_VLAN: ++		rcu_read_lock(); ++		if (sdata->use_4addr) ++			sta = rcu_dereference(sdata->u.vlan.sta); ++		if (sta) { ++			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); ++			/* RA TA DA SA */ ++			memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN); ++			memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); ++			memcpy(hdr.addr3, skb->data, ETH_ALEN); ++			memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); ++			hdrlen = 30; ++			sta_flags = get_sta_flags(sta); ++		} ++		rcu_read_unlock(); ++		if (sta) ++			break; ++		/* fall through */ ++	case NL80211_IFTYPE_AP: + 		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + 		/* DA BSSID SA */ + 		memcpy(hdr.addr1, skb->data, ETH_ALEN); +@@ -1700,12 +1720,21 @@ netdev_tx_t ieee80211_subif_start_xmit(s + 		break; + #endif + 	case NL80211_IFTYPE_STATION: +-		fc |= cpu_to_le16(IEEE80211_FCTL_TODS); +-		/* BSSID SA DA */ + 		memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); +-		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); +-		memcpy(hdr.addr3, skb->data, ETH_ALEN); +-		hdrlen = 24; ++		if (sdata->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); ++			memcpy(hdr.addr3, skb->data, ETH_ALEN); ++			memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); ++			hdrlen = 30; ++		} else { ++			fc |= cpu_to_le16(IEEE80211_FCTL_TODS); ++			/* BSSID SA DA */ ++			memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); ++			memcpy(hdr.addr3, skb->data, ETH_ALEN); ++			hdrlen = 24; ++		} + 		break; + 	case NL80211_IFTYPE_ADHOC: + 		/* DA SA BSSID */ +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -772,6 +772,7 @@ int ieee80211_if_change_type(struct ieee + 		ieee80211_mandatory_rates(sdata->local, + 			sdata->local->hw.conf.channel->band); + 	sdata->drop_unencrypted = 0; ++	sdata->use_4addr = 0; +  + 	return 0; + } +@@ -853,6 +854,9 @@ int ieee80211_if_add(struct ieee80211_lo + 					    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); +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -1237,6 +1237,13 @@ __ieee80211_data_to_8023(struct ieee8021 + { + 	struct net_device *dev = rx->dev; + 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(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)) ++		return -1; ++	if (sdata->use_4addr && is_multicast_ether_addr(hdr->addr1)) ++		return -1; +  + 	return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type); + } +@@ -1590,6 +1597,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_ + { + 	struct net_device *dev = rx->dev; + 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + 	__le16 fc = hdr->frame_control; + 	int err; +  +@@ -1599,6 +1607,14 @@ ieee80211_rx_h_data(struct ieee80211_rx_ + 	if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) + 		return RX_DROP_MONITOR; +  ++	/* ++	 * Allow the cooked monitor interface of an AP to see 4-addr frames so ++	 * that a 4-addr station can be detected and moved into a separate VLAN ++	 */ ++	if (ieee80211_has_a4(hdr->frame_control) && ++	    sdata->vif.type == NL80211_IFTYPE_AP) ++		return RX_DROP_MONITOR; ++ + 	err = __ieee80211_data_to_8023(rx); + 	if (unlikely(err)) + 		return RX_DROP_UNUSABLE; +@@ -2039,7 +2055,7 @@ static int prepare_for_handlers(struct i +  + 	switch (sdata->vif.type) { + 	case NL80211_IFTYPE_STATION: +-		if (!bssid) ++		if (!bssid && !sdata->use_4addr) + 			return 0; + 		if (!multicast && + 		    compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) { +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -320,7 +320,9 @@ int ieee80211_data_to_8023(struct sk_buf + 		break; + 	case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): + 		if (unlikely(iftype != NL80211_IFTYPE_WDS && +-			     iftype != NL80211_IFTYPE_MESH_POINT)) ++			     iftype != NL80211_IFTYPE_MESH_POINT && ++			     iftype != NL80211_IFTYPE_AP_VLAN && ++			     iftype != NL80211_IFTYPE_STATION)) + 			return -1; + 		if (iftype == NL80211_IFTYPE_MESH_POINT) { + 			struct ieee80211s_hdr *meshdr =  | 
