*     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_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),
 };
 
 /**
  * @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;
 
        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;
 
        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;
 }
 
                                            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;
 }
                        return -EINVAL;
                }
 
-               if (vlansdata->use_4addr) {
+               if (params->vlan->ieee80211_ptr->use_4addr) {
                        if (vlansdata->u.vlan.sta)
                                return -EBUSY;
 
 
        } mfp; /* management frame protection */
 
        int wmm_last_param_set;
+
+       u8 use_4addr;
 };
 
 enum ieee80211_ibss_request {
        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;
 
                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;
 }
        /* 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;
                                            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);
 
        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 */
 
        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);
        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
 
        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) {
 
 
        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);
        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 */
 #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);
 
        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;
        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;
        }
        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);
                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,
 
                return -EOPNOTSUPP;
 
        if (ntype != otype) {
+               dev->ieee80211_ptr->use_4addr = false;
+
                switch (otype) {
                case NL80211_IFTYPE_ADHOC:
                        cfg80211_leave_ibss(rdev, dev, false);
 
        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;
 }