Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33b64eb2 authored by Luis Carlos Cobo's avatar Luis Carlos Cobo Committed by John W. Linville
Browse files

mac80211: support for mesh interfaces in mac80211 data path



This changes the TX/RX paths in mac80211 to support mesh interfaces.
This code will be cleaned up later again before being enabled.

Signed-off-by: default avatarLuis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 2e3c8736
Loading
Loading
Loading
Loading
+117 −4
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@

#include "ieee80211_i.h"
#include "ieee80211_led.h"
#ifdef CONFIG_MAC80211_MESH
#include "mesh.h"
#endif
#include "wep.h"
#include "wpa.h"
#include "tkip.h"
@@ -390,10 +393,60 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
	return RX_CONTINUE;
}

#ifdef CONFIG_MAC80211_MESH
#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l))
static ieee80211_rx_result
ieee80211_rx_mesh_check(struct ieee80211_txrx_data *rx)
{
	int hdrlen = ieee80211_get_hdrlen(rx->fc);
	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
	if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
		if (!((rx->fc & IEEE80211_FCTL_FROMDS) &&
		      (rx->fc & IEEE80211_FCTL_TODS)))
			return RX_DROP_MONITOR;
		if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
			return RX_DROP_MONITOR;
	}

	/* If there is not an established peer link and this is not a peer link
	 * establisment frame, beacon or probe, drop the frame.
	 */

	if (!rx->sta || rx->sta->plink_state != ESTAB) {
		struct ieee80211_mgmt *mgmt;
		if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
			return RX_DROP_MONITOR;

		switch (rx->fc & IEEE80211_FCTL_STYPE) {
		case IEEE80211_STYPE_ACTION:
			mgmt = (struct ieee80211_mgmt *)hdr;
			if (mgmt->u.action.category != PLINK_CATEGORY)
				return RX_DROP_MONITOR;
			/* fall through on else */
		case IEEE80211_STYPE_PROBE_REQ:
		case IEEE80211_STYPE_PROBE_RESP:
		case IEEE80211_STYPE_BEACON:
			return RX_CONTINUE;
			break;
		default:
			return RX_DROP_MONITOR;
		}

	 } else if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
		    is_broadcast_ether_addr(hdr->addr1) &&
		    mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->dev))
		return RX_DROP_MONITOR;
	else
		return RX_CONTINUE;
}
#endif


static ieee80211_rx_result
ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
{
	struct ieee80211_hdr *hdr;

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

	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
@@ -423,6 +476,12 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
	 * deauth/disassoc frames when needed. In addition, hostapd is
	 * responsible for filtering on both auth and assoc states.
	 */

#ifdef CONFIG_MAC80211_MESH
	if (rx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
		return ieee80211_rx_mesh_check(rx);
#endif

	if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
		      ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
		       (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
@@ -657,6 +716,8 @@ ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
		/* Update last_rx only for unicast frames in order to prevent
		 * the Probe Request frames (the only broadcast frames from a
		 * STA in infrastructure mode) from keeping a connection alive.
		 * Mesh beacons will update last_rx when if they are found to
		 * match the current local configuration when processed.
		 */
		sta->last_rx = jiffies;
	}
@@ -1050,6 +1111,23 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)

	hdrlen = ieee80211_get_hdrlen(fc);

#ifdef CONFIG_MAC80211_MESH
	if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) {
		int meshhdrlen = ieee80211_get_mesh_hdrlen(
				(struct ieee80211s_hdr *) (skb->data + hdrlen));
		/* Copy on cb:
		 *  - mesh header: to be used for mesh forwarding
		 * decision. It will also be used as mesh header template at
		 * tx.c:ieee80211_subif_start_xmit() if interface
		 * type is mesh and skb->pkt_type == PACKET_OTHERHOST
		 *  - ta: to be used if a RERR needs to be sent.
		 */
		memcpy(skb->cb, skb->data + hdrlen, meshhdrlen);
		memcpy(MESH_PREQ(skb), hdr->addr2, ETH_ALEN);
		hdrlen += meshhdrlen;
	}
#endif

	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
	 * header
	 * IEEE 802.11 address fields:
@@ -1083,7 +1161,8 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
		memcpy(dst, hdr->addr3, ETH_ALEN);
		memcpy(src, hdr->addr4, ETH_ALEN);

		if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS)) {
		 if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS &&
			     sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)) {
			 if (net_ratelimit())
				 printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
				       "frame (RA=%s TA=%s DA=%s SA=%s)\n",
@@ -1227,6 +1306,39 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
		}
	}

#ifdef CONFIG_MAC80211_MESH
	/* Mesh forwarding */
	if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) {
		u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl;
		(*mesh_ttl)--;

		if (is_multicast_ether_addr(skb->data)) {
			if (*mesh_ttl > 0) {
				xmit_skb = skb_copy(skb, GFP_ATOMIC);
				if (!xmit_skb && net_ratelimit())
					printk(KERN_DEBUG "%s: failed to clone "
					       "multicast frame\n", dev->name);
				else
					xmit_skb->pkt_type = PACKET_OTHERHOST;
			} else
				sdata->u.sta.mshstats.dropped_frames_ttl++;

		} else if (skb->pkt_type != PACKET_OTHERHOST &&
			compare_ether_addr(dev->dev_addr, skb->data) != 0) {
			if (*mesh_ttl == 0) {
				sdata->u.sta.mshstats.dropped_frames_ttl++;
				dev_kfree_skb(skb);
				skb = NULL;
			} else {
				xmit_skb = skb;
				xmit_skb->pkt_type = PACKET_OTHERHOST;
				if (!(dev->flags & IFF_PROMISC))
					skb  = NULL;
			}
		}
	}
#endif

	if (skb) {
		/* deliver to local stack */
		skb->protocol = eth_type_trans(skb, dev);
@@ -1444,7 +1556,8 @@ ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)

	sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
	if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
	     sdata->vif.type == IEEE80211_IF_TYPE_IBSS) &&
	     sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
	     sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
	    !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
	else
+129 −29
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@

#include "ieee80211_i.h"
#include "ieee80211_led.h"
#ifdef CONFIG_MAC80211_MESH
#include "mesh.h"
#endif
#include "wep.h"
#include "wpa.h"
#include "wme.h"
@@ -249,6 +252,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
		return TX_DROP;

	if (tx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
		return TX_CONTINUE;

	if (tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED)
		return TX_CONTINUE;

@@ -1384,8 +1390,9 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
	struct ieee80211_tx_packet_data *pkt_data;
	struct ieee80211_sub_if_data *sdata;
	int ret = 1, head_need;
	u16 ethertype, hdrlen, fc;
	u16 ethertype, hdrlen,  meshhdrlen = 0, fc;
	struct ieee80211_hdr hdr;
	struct ieee80211s_hdr mesh_hdr;
	const u8 *encaps_data;
	int encaps_len, skip_header_bytes;
	int nh_pos, h_pos;
@@ -1427,6 +1434,37 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
		hdrlen = 30;
		break;
#ifdef CONFIG_MAC80211_MESH
	case IEEE80211_IF_TYPE_MESH_POINT:
		fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
		/* RA TA DA SA */
		if (is_multicast_ether_addr(skb->data))
			memcpy(hdr.addr1, skb->data, ETH_ALEN);
		else if (mesh_nexthop_lookup(hdr.addr1, skb, dev))
				return 0;
		memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
		memcpy(hdr.addr3, skb->data, ETH_ALEN);
		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
		if (skb->pkt_type == PACKET_OTHERHOST) {
			/* Forwarded frame, keep mesh ttl and seqnum */
			struct ieee80211s_hdr *prev_meshhdr;
			prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
			meshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
			memcpy(&mesh_hdr, prev_meshhdr, meshhdrlen);
			sdata->u.sta.mshstats.fwded_frames++;
		} else {
			if (!sdata->u.sta.mshcfg.dot11MeshTTL) {
				/* Do not send frames with mesh_ttl == 0 */
				sdata->u.sta.mshstats.dropped_frames_ttl++;
				ret = 0;
				goto fail;
			}
			meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
					sdata);
		}
		hdrlen = 30;
		break;
#endif
	case IEEE80211_IF_TYPE_STA:
		fc |= IEEE80211_FCTL_TODS;
		/* BSSID SA DA */
@@ -1525,7 +1563,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
	 * alloc_skb() (net/core/skbuff.c)
	 */
	head_need = hdrlen + encaps_len + local->tx_headroom;
	head_need = hdrlen + encaps_len + meshhdrlen + local->tx_headroom;
	head_need -= skb_headroom(skb);

	/* We are going to modify skb data, so make a copy of it if happens to
@@ -1559,6 +1597,12 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
		h_pos += encaps_len;
	}

	if (meshhdrlen > 0) {
		memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen);
		nh_pos += meshhdrlen;
		h_pos += meshhdrlen;
	}

	if (fc & IEEE80211_STYPE_QOS_DATA) {
		__le16 *qos_control;

@@ -1734,6 +1778,40 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
	read_unlock_bh(&local->sta_lock);
}

#ifdef CONFIG_MAC80211_MESH
static struct sk_buff *ieee80211_mesh_beacon_get(struct net_device *dev)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
	struct ieee80211_mgmt *mgmt;
	u8 *pos;

	if (!skb)
		return NULL;
	skb_reserve(skb, local->hw.extra_tx_headroom);
	mgmt = (struct ieee80211_mgmt *)
		skb_put(skb, 24 + sizeof(mgmt->u.beacon));
	memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
	mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
					   IEEE80211_STYPE_BEACON);
	memset(mgmt->da, 0xff, ETH_ALEN);
	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
	/* BSSID is left zeroed, wildcard value */
	mgmt->u.beacon.beacon_int =
		cpu_to_le16(local->hw.conf.beacon_int);
	mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */

	pos = skb_put(skb, 2);
	*pos++ = WLAN_EID_SSID;
	*pos++ = 0x0;

	mesh_mgmt_ies_add(skb, dev);

	return skb;
}
#endif


struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
				     struct ieee80211_vif *vif,
				     struct ieee80211_tx_control *control)
@@ -1746,6 +1824,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
	struct rate_selection rsel;
	struct beacon_data *beacon;
	struct ieee80211_supported_band *sband;
	int *num_beacons;
	int err = 0;

	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];

@@ -1753,18 +1833,14 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,

	sdata = vif_to_sdata(vif);
	bdev = sdata->dev;
	ap = &sdata->u.ap;

	switch (sdata->vif.type) {
	case IEEE80211_IF_TYPE_AP:
		ap = &sdata->u.ap;
		beacon = rcu_dereference(ap->beacon);

	if (!ap || sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
		if (net_ratelimit())
			printk(KERN_DEBUG "no beacon data avail for %s\n",
			       bdev->name);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
		skb = NULL;
		goto out;
		if (!ap || !beacon) {
			err = -1;
			break;
		}

		/* headroom, head length, tail length and maximum TIM length */
@@ -1777,7 +1853,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
		memcpy(skb_put(skb, beacon->head_len), beacon->head,
		       beacon->head_len);

	ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
		ieee80211_include_sequence(sdata,
					   (struct ieee80211_hdr *)skb->data);

		ieee80211_beacon_add_tim(local, ap, skb, beacon);

@@ -1785,6 +1862,31 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
			memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
			       beacon->tail_len);

		num_beacons = &ap->num_beacons;
		break;

#ifdef CONFIG_MAC80211_MESH
	case IEEE80211_IF_TYPE_MESH_POINT:
		skb = ieee80211_mesh_beacon_get(bdev);
		num_beacons = &sdata->u.sta.num_beacons;
		break;
#endif

	default:
		err = -1;
		break;
	}

	if (err) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
		if (net_ratelimit())
			printk(KERN_DEBUG "no beacon data avail for %s\n",
			       bdev->name);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
		skb = NULL;
		goto out;
	}

	if (control) {
		rate_control_get_rate(local->mdev, sband, skb, &rsel);
		if (!rsel.rate) {
@@ -1808,9 +1910,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
		control->retry_limit = 1;
		control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
	}

	ap->num_beacons++;

	(*num_beacons)++;
out:
	rcu_read_unlock();
	return skb;