Skip to contentSkip to conversation

mac80211: use nullfunc frames for 4-addr sta detection

PATCH New Felix Fietkau 1 participant 3 messages Jan 8, 2010 mac80211

lore
Share & tools
lore
#0 lore raw

PATCH 1/3 mac80211: use nullfunc frames for 4-addr sta detection

To detect incoming 4-addr stations, hostapd needs to receive a 4-addr data frame from the remote station, so that it can create the AP VLAN for it. With this patch, the mlme code emits a 4-addr nullfunc frame immediately after assoc. On the AP side it also drops 4-addr nullfunc frames to the cooked monitor mode interface, if the interface hasn't been fully set up to receive 4-addr data frames yet.

---

net/mac80211/mlme.c +39 -0
@@ -312,6 +312,38 @@ void ieee80211_send_nullfunc(struct ieee
 	ieee80211_tx_skb(sdata, skb);
 }
 
+static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+					  struct ieee80211_sub_if_data *sdata)
+{
+	struct sk_buff *skb;
+	struct ieee80211_hdr *nullfunc;
+	__le16 fc;
+
+	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+		return;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
+	if (!skb) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr "
+		       "nullfunc frame\n", sdata->name);
+		return;
+	}
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30);
+	memset(nullfunc, 0, 30);
+	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+			 IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
+	nullfunc->frame_control = fc;
+	memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
+	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+	memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
+	memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
+
+	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+	ieee80211_tx_skb(sdata, skb);
+}
+
 /* spectrum management related things */
 static void ieee80211_chswitch_work(struct work_struct *work)
 {
@@ -1120,6 +1152,13 @@ static bool ieee80211_assoc_success(stru
 	ieee80211_set_associated(sdata, cbss, changed);
 
 	/*
+	 * If we're using 4-addr mode, let the AP know that we're
+	 * doing so, so that it can create the STA VLAN on its side
+	 */
+	if (ifmgd->use_4addr)
+		ieee80211_send_4addr_nullfunc(local, sdata);
+
+	/*
 	 * Start timer to probe the connection to the AP now.
 	 * Also start the timer that will detect beacon loss.
 	 */
net/mac80211/rx.c +12 -0
@@ -1111,6 +1111,18 @@ ieee80211_rx_h_sta_process(struct ieee80
 	if (ieee80211_is_nullfunc(hdr->frame_control) ||
 	    ieee80211_is_qos_nullfunc(hdr->frame_control)) {
 		I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
+
+		/*
+		 * If we receive a 4-addr nullfunc frame from a STA
+		 * that was not moved to a 4-addr STA vlan yet, drop
+		 * the frame to the monitor interface, to make sure
+		 * that hostapd sees it
+		 */
+		if (ieee80211_has_a4(hdr->frame_control) &&
+		    (rx->sdata->vif.type == NL80211_IFTYPE_AP ||
+		     (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+		      !rx->sdata->u.vlan.sta)))
+			return RX_DROP_MONITOR;
 		/*
 		 * Update counter and free packet here to avoid
 		 * counting this as a dropped packed.
#1 lore raw

PATCH 2/3 mac80211: allow station updates on ap interfaces for vlan stations

Since the per-vif station changes, sta_info_get on the ap sdata no longer returns entries for stations on ap vlans. This causes issues with hostapd, which currently always passes the ap interface name to nl80211 calls. This patch provides bug compatibility with the earlier versions until hostapd is fixed.

---

net/mac80211/sta_info.c +21 -0
@@ -119,6 +119,27 @@ struct sta_info *sta_info_get(struct iee
 	return sta;
 }
 
+/*
+ * Get sta info either from the specified interface
+ * or from one of its vlans
+ */
+struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
+				  const u8 *addr)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
+
+	sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
+	while (sta) {
+		if ((sta->sdata == sdata ||
+		     sta->sdata->bss == sdata->bss) &&
+		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+			break;
+		sta = rcu_dereference(sta->hnext);
+	}
+	return sta;
+}
+
 struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
 				     int idx)
 {
net/mac80211/sta_info.h +3 -0
@@ -408,6 +408,9 @@ static inline u32 get_sta_flags(struct s
 struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
 			      const u8 *addr);
 
+struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
+				  const u8 *addr);
+
 static inline
 void for_each_sta_info_type_check(struct ieee80211_local *local,
 				  const u8 *addr,
net/mac80211/cfg.c +6 -6
@@ -148,7 +148,7 @@ static int ieee80211_add_key(struct wiph
 	rcu_read_lock();
 
 	if (mac_addr) {
-		sta = sta_info_get(sdata, mac_addr);
+		sta = sta_info_get_bss(sdata, mac_addr);
 		if (!sta) {
 			ieee80211_key_free(key);
 			err = -ENOENT;
@@ -179,7 +179,7 @@ static int ieee80211_del_key(struct wiph
 	if (mac_addr) {
 		ret = -ENOENT;
 
-		sta = sta_info_get(sdata, mac_addr);
+		sta = sta_info_get_bss(sdata, mac_addr);
 		if (!sta)
 			goto out_unlock;
 
@@ -226,7 +226,7 @@ static int ieee80211_get_key(struct wiph
 	rcu_read_lock();
 
 	if (mac_addr) {
-		sta = sta_info_get(sdata, mac_addr);
+		sta = sta_info_get_bss(sdata, mac_addr);
 		if (!sta)
 			goto out;
 
@@ -419,7 +419,7 @@ static int ieee80211_get_station(struct 
 
 	rcu_read_lock();
 
-	sta = sta_info_get(sdata, mac);
+	sta = sta_info_get_bss(sdata, mac);
 	if (sta) {
 		ret = 0;
 		sta_set_sinfo(sta, sinfo);
@@ -775,7 +775,7 @@ static int ieee80211_del_station(struct 
 	if (mac) {
 		rcu_read_lock();
 
-		sta = sta_info_get(sdata, mac);
+		sta = sta_info_get_bss(sdata, mac);
 		if (!sta) {
 			rcu_read_unlock();
 			return -ENOENT;
@@ -803,7 +803,7 @@ static int ieee80211_change_station(stru
 
 	rcu_read_lock();
 
-	sta = sta_info_get(sdata, mac);
+	sta = sta_info_get_bss(sdata, mac);
 	if (!sta) {
 		rcu_read_unlock();
 		return -ENOENT;
#2 lore raw

PATCH 3/3 mac80211: do not transmit frames on unconfigured 4-addr vlan interfaces

If frames are transmitted on 4-addr ap vlan interfaces with no station, they end up being transmitted unencrypted, even if the ap interface uses WPA. This patch add some sanity checking to make sure that this does not happen.

---

net/mac80211/tx.c +4 -1
@@ -1051,8 +1051,11 @@ ieee80211_tx_prepare(struct ieee80211_su
 
 	hdr = (struct ieee80211_hdr *) skb->data;
 
-	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
 		tx->sta = rcu_dereference(sdata->u.vlan.sta);
+		if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
+			return TX_DROP;
+	}
 	if (!tx->sta)
 		tx->sta = sta_info_get(sdata, hdr->addr1);