[Midnightbsd-cvs] src [9937] trunk/sys/net80211: sync with freebsd 10-stable

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Fri May 25 16:04:32 EDT 2018


Revision: 9937
          http://svnweb.midnightbsd.org/src/?rev=9937
Author:   laffer1
Date:     2018-05-25 16:04:31 -0400 (Fri, 25 May 2018)
Log Message:
-----------
sync with freebsd 10-stable

Modified Paths:
--------------
    trunk/sys/net80211/_ieee80211.h
    trunk/sys/net80211/ieee80211.c
    trunk/sys/net80211/ieee80211.h
    trunk/sys/net80211/ieee80211_acl.c
    trunk/sys/net80211/ieee80211_action.c
    trunk/sys/net80211/ieee80211_action.h
    trunk/sys/net80211/ieee80211_adhoc.c
    trunk/sys/net80211/ieee80211_adhoc.h
    trunk/sys/net80211/ieee80211_ageq.c
    trunk/sys/net80211/ieee80211_ageq.h
    trunk/sys/net80211/ieee80211_alq.c
    trunk/sys/net80211/ieee80211_alq.h
    trunk/sys/net80211/ieee80211_amrr.c
    trunk/sys/net80211/ieee80211_amrr.h
    trunk/sys/net80211/ieee80211_crypto.c
    trunk/sys/net80211/ieee80211_crypto.h
    trunk/sys/net80211/ieee80211_crypto_ccmp.c
    trunk/sys/net80211/ieee80211_crypto_none.c
    trunk/sys/net80211/ieee80211_crypto_tkip.c
    trunk/sys/net80211/ieee80211_crypto_wep.c
    trunk/sys/net80211/ieee80211_ddb.c
    trunk/sys/net80211/ieee80211_dfs.c
    trunk/sys/net80211/ieee80211_dfs.h
    trunk/sys/net80211/ieee80211_freebsd.c
    trunk/sys/net80211/ieee80211_freebsd.h
    trunk/sys/net80211/ieee80211_hostap.c
    trunk/sys/net80211/ieee80211_hostap.h
    trunk/sys/net80211/ieee80211_ht.c
    trunk/sys/net80211/ieee80211_ht.h
    trunk/sys/net80211/ieee80211_hwmp.c
    trunk/sys/net80211/ieee80211_input.c
    trunk/sys/net80211/ieee80211_input.h
    trunk/sys/net80211/ieee80211_ioctl.c
    trunk/sys/net80211/ieee80211_ioctl.h
    trunk/sys/net80211/ieee80211_mesh.c
    trunk/sys/net80211/ieee80211_mesh.h
    trunk/sys/net80211/ieee80211_monitor.c
    trunk/sys/net80211/ieee80211_monitor.h
    trunk/sys/net80211/ieee80211_node.c
    trunk/sys/net80211/ieee80211_node.h
    trunk/sys/net80211/ieee80211_output.c
    trunk/sys/net80211/ieee80211_phy.c
    trunk/sys/net80211/ieee80211_phy.h
    trunk/sys/net80211/ieee80211_power.c
    trunk/sys/net80211/ieee80211_power.h
    trunk/sys/net80211/ieee80211_proto.c
    trunk/sys/net80211/ieee80211_proto.h
    trunk/sys/net80211/ieee80211_radiotap.c
    trunk/sys/net80211/ieee80211_radiotap.h
    trunk/sys/net80211/ieee80211_ratectl.c
    trunk/sys/net80211/ieee80211_ratectl.h
    trunk/sys/net80211/ieee80211_ratectl_none.c
    trunk/sys/net80211/ieee80211_regdomain.c
    trunk/sys/net80211/ieee80211_regdomain.h
    trunk/sys/net80211/ieee80211_rssadapt.c
    trunk/sys/net80211/ieee80211_rssadapt.h
    trunk/sys/net80211/ieee80211_scan.c
    trunk/sys/net80211/ieee80211_scan.h
    trunk/sys/net80211/ieee80211_scan_sta.c
    trunk/sys/net80211/ieee80211_sta.c
    trunk/sys/net80211/ieee80211_sta.h
    trunk/sys/net80211/ieee80211_superg.c
    trunk/sys/net80211/ieee80211_superg.h
    trunk/sys/net80211/ieee80211_tdma.c
    trunk/sys/net80211/ieee80211_tdma.h
    trunk/sys/net80211/ieee80211_var.h
    trunk/sys/net80211/ieee80211_wds.c
    trunk/sys/net80211/ieee80211_wds.h
    trunk/sys/net80211/ieee80211_xauth.c

Modified: trunk/sys/net80211/_ieee80211.h
===================================================================
--- trunk/sys/net80211/_ieee80211.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/_ieee80211.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/_ieee80211.h 237871 2012-07-01 04:25:49Z adrian $
  */
 #ifndef _NET80211__IEEE80211_H_
 #define _NET80211__IEEE80211_H_
@@ -242,6 +243,8 @@
 	(((_c)->ic_flags & (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN)) != 0)
 #define	IEEE80211_IS_CHAN_CCK(_c) \
 	(((_c)->ic_flags & (IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN)) != 0)
+#define	IEEE80211_IS_CHAN_DYN(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_DYN) == IEEE80211_CHAN_DYN)
 #define	IEEE80211_IS_CHAN_GFSK(_c) \
 	(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
 #define	IEEE80211_IS_CHAN_TURBO(_c) \

Modified: trunk/sys/net80211/ieee80211.c
===================================================================
--- trunk/sys/net80211/ieee80211.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211.c 254315 2013-08-14 04:24:25Z rpaulo $");
 
 /*
  * IEEE 802.11 generic handler
@@ -241,9 +242,15 @@
 	return EACCES;		/* XXX EIO/EPERM? */
 }
 
+#if __FreeBSD_version >= 1000031
 static int
 null_output(struct ifnet *ifp, struct mbuf *m,
+	const struct sockaddr *dst, struct route *ro)
+#else
+static int
+null_output(struct ifnet *ifp, struct mbuf *m,
 	struct sockaddr *dst, struct route *ro)
+#endif
 {
 	if_printf(ifp, "discard raw packet\n");
 	return null_transmit(ifp, m);
@@ -256,6 +263,13 @@
 	m_freem(m);
 }
 
+static void
+null_update_chw(struct ieee80211com *ic)
+{
+
+	if_printf(ic->ic_ifp, "%s: need callback\n", __func__);
+}
+
 /*
  * Attach/setup the common net80211 state.  Called by
  * the driver on attach to prior to creating any vap's.
@@ -271,12 +285,13 @@
 	KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type));
 
 	IEEE80211_LOCK_INIT(ic, ifp->if_xname);
+	IEEE80211_TX_LOCK_INIT(ic, ifp->if_xname);
 	TAILQ_INIT(&ic->ic_vaps);
 
 	/* Create a taskqueue for all state changes */
 	ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO,
 	    taskqueue_thread_enqueue, &ic->ic_tq);
-	taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq",
+	taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s net80211 taskq",
 	    ifp->if_xname);
 	/*
 	 * Fill in 802.11 available channel set, mark all
@@ -287,6 +302,7 @@
 
 	ic->ic_update_mcast = null_update_mcast;
 	ic->ic_update_promisc = null_update_promisc;
+	ic->ic_update_chw = null_update_chw;
 
 	ic->ic_hash_key = arc4random();
 	ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
@@ -309,7 +325,11 @@
 
 	ifp->if_addrlen = IEEE80211_ADDR_LEN;
 	ifp->if_hdrlen = 0;
+
+	CURVNET_SET(vnet0);
+
 	if_attach(ifp);
+
 	ifp->if_mtu = IEEE80211_MTU_MAX;
 	ifp->if_broadcastaddr = ieee80211broadcastaddr;
 	ifp->if_output = null_output;
@@ -323,6 +343,8 @@
 	sdl->sdl_alen = IEEE80211_ADDR_LEN;
 	IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr);
 	ifa_free(ifa);
+
+	CURVNET_RESTORE();
 }
 
 /*
@@ -337,8 +359,18 @@
 	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211vap *vap;
 
+	/*
+	 * This detaches the main interface, but not the vaps.
+	 * Each VAP may be in a separate VIMAGE.
+	 */
+	CURVNET_SET(ifp->if_vnet);
 	if_detach(ifp);
+	CURVNET_RESTORE();
 
+	/*
+	 * The VAP is responsible for setting and clearing
+	 * the VIMAGE context.
+	 */
 	while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
 		ieee80211_vap_destroy(vap);
 	ieee80211_waitfor_parent(ic);
@@ -357,8 +389,11 @@
 	ieee80211_power_detach(ic);
 	ieee80211_node_detach(ic);
 
+	/* XXX VNET needed? */
 	ifmedia_removeall(&ic->ic_media);
+
 	taskqueue_free(ic->ic_tq);
+	IEEE80211_TX_LOCK_DESTROY(ic);
 	IEEE80211_LOCK_DESTROY(ic);
 }
 
@@ -399,13 +434,10 @@
 	if_initname(ifp, name, unit);
 	ifp->if_softc = vap;			/* back pointer */
 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
-	ifp->if_start = ieee80211_start;
+	ifp->if_transmit = ieee80211_vap_transmit;
+	ifp->if_qflush = ieee80211_vap_qflush;
 	ifp->if_ioctl = ieee80211_ioctl;
 	ifp->if_init = ieee80211_init;
-	/* NB: input+output filled in by ether_ifattach */
-	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
-	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
-	IFQ_SET_READY(&ifp->if_snd);
 
 	vap->iv_ifp = ifp;
 	vap->iv_ic = ic;
@@ -578,6 +610,8 @@
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ifnet *ifp = vap->iv_ifp;
 
+	CURVNET_SET(ifp->if_vnet);
+
 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n",
 	    __func__, ieee80211_opmode_name[vap->iv_opmode],
 	    ic->ic_ifp->if_xname);
@@ -630,6 +664,8 @@
 	ieee80211_sysctl_vdetach(vap);
 
 	if_free(ifp);
+
+	CURVNET_RESTORE();
 }
 
 /*
@@ -1483,7 +1519,6 @@
 int
 ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	static const struct ratemedia rates[] = {
 		{   2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
 		{   4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
@@ -1604,7 +1639,7 @@
 	if (mode == IEEE80211_MODE_11NA) {
 		if (rate & IEEE80211_RATE_MCS) {
 			rate &= ~IEEE80211_RATE_MCS;
-			m = findmedia(htrates, N(htrates), rate);
+			m = findmedia(htrates, nitems(htrates), rate);
 			if (m != IFM_AUTO)
 				return m | IFM_IEEE80211_11NA;
 		}
@@ -1612,7 +1647,7 @@
 		/* NB: 12 is ambiguous, it will be treated as an MCS */
 		if (rate & IEEE80211_RATE_MCS) {
 			rate &= ~IEEE80211_RATE_MCS;
-			m = findmedia(htrates, N(htrates), rate);
+			m = findmedia(htrates, nitems(htrates), rate);
 			if (m != IFM_AUTO)
 				return m | IFM_IEEE80211_11NG;
 		}
@@ -1625,15 +1660,18 @@
 	case IEEE80211_MODE_11NA:
 	case IEEE80211_MODE_TURBO_A:
 	case IEEE80211_MODE_STURBO_A:
-		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A);
+		return findmedia(rates, nitems(rates), 
+		    rate | IFM_IEEE80211_11A);
 	case IEEE80211_MODE_11B:
-		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B);
+		return findmedia(rates, nitems(rates), 
+		    rate | IFM_IEEE80211_11B);
 	case IEEE80211_MODE_FH:
-		return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH);
+		return findmedia(rates, nitems(rates), 
+		    rate | IFM_IEEE80211_FH);
 	case IEEE80211_MODE_AUTO:
 		/* NB: ic may be NULL for some drivers */
 		if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH)
-			return findmedia(rates, N(rates),
+			return findmedia(rates, nitems(rates),
 			    rate | IFM_IEEE80211_FH);
 		/* NB: hack, 11g matches both 11b+11a rates */
 		/* fall thru... */
@@ -1640,16 +1678,14 @@
 	case IEEE80211_MODE_11G:
 	case IEEE80211_MODE_11NG:
 	case IEEE80211_MODE_TURBO_G:
-		return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G);
+		return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G);
 	}
 	return IFM_AUTO;
-#undef N
 }
 
 int
 ieee80211_media2rate(int mword)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	static const int ieeerates[] = {
 		-1,		/* IFM_AUTO */
 		0,		/* IFM_MANUAL */
@@ -1677,9 +1713,8 @@
 		54,		/* IFM_IEEE80211_OFDM27 */
 		-1,		/* IFM_IEEE80211_MCS */
 	};
-	return IFM_SUBTYPE(mword) < N(ieeerates) ?
+	return IFM_SUBTYPE(mword) < nitems(ieeerates) ?
 		ieeerates[IFM_SUBTYPE(mword)] : 0;
-#undef N
 }
 
 /*

Modified: trunk/sys/net80211/ieee80211.h
===================================================================
--- trunk/sys/net80211/ieee80211.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211.h 262007 2014-02-17 01:36:53Z kevlo $
  */
 #ifndef _NET80211_IEEE80211_H_
 #define _NET80211_IEEE80211_H_
@@ -166,7 +167,7 @@
 #define	IEEE80211_FC1_RETRY			0x08
 #define	IEEE80211_FC1_PWR_MGT			0x10
 #define	IEEE80211_FC1_MORE_DATA			0x20
-#define	IEEE80211_FC1_WEP			0x40
+#define	IEEE80211_FC1_PROTECTED			0x40
 #define	IEEE80211_FC1_ORDER			0x80
 
 #define	IEEE80211_SEQ_FRAG_MASK			0x000f
@@ -199,6 +200,13 @@
 #define	IEEE80211_QOS_EOSP			0x10	/* EndOfService Period*/
 #define	IEEE80211_QOS_EOSP_S			4
 #define	IEEE80211_QOS_TID			0x0f
+/* qos[1] byte used for all frames sent by mesh STAs in a mesh BSS */
+#define IEEE80211_QOS_MC			0x01	/* Mesh control */
+/* Mesh power save level*/
+#define IEEE80211_QOS_MESH_PSL			0x02
+/* Mesh Receiver Service Period Initiated */
+#define IEEE80211_QOS_RSPI			0x04
+/* bits 11 to 15 reserved */
 
 /* does frame have QoS sequence control data */
 #define	IEEE80211_QOS_HAS_SEQ(wh) \
@@ -325,6 +333,9 @@
 #define	IEEE80211_ACTION_CAT_DLS	2	/* DLS */
 #define	IEEE80211_ACTION_CAT_BA		3	/* BA */
 #define	IEEE80211_ACTION_CAT_HT		7	/* HT */
+#define	IEEE80211_ACTION_CAT_MESH	13	/* Mesh */
+#define	IEEE80211_ACTION_CAT_SELF_PROT	15	/* Self-protected */
+/* 16 - 125 reserved */
 #define	IEEE80211_ACTION_CAT_VENDOR	127	/* Vendor Specific */
 
 #define	IEEE80211_ACTION_HT_TXCHWIDTH	0	/* recommended xmit chan width*/
@@ -701,6 +712,7 @@
 	IEEE80211_ELEMID_IBSSDFS	= 41,
 	IEEE80211_ELEMID_ERP		= 42,
 	IEEE80211_ELEMID_HTCAP		= 45,
+	IEEE80211_ELEMID_QOS		= 46,
 	IEEE80211_ELEMID_RSN		= 48,
 	IEEE80211_ELEMID_XRATES		= 50,
 	IEEE80211_ELEMID_HTINFO		= 61,
@@ -724,7 +736,7 @@
 	IEEE80211_ELEMID_MESHAWAKEW	= 119,
 	IEEE80211_ELEMID_MESHBEACONT	= 120,
 	/* 121-124 MMCAOP not implemented yet */
-	IEEE80211_ELEMID_MESHPANN	= 125, /* XXX: is GANN now, not used */
+	IEEE80211_ELEMID_MESHGANN	= 125,
 	IEEE80211_ELEMID_MESHRANN	= 126,
 	/* 127 Extended Capabilities */
 	/* 128-129 reserved */
@@ -762,6 +774,18 @@
 	(sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1))
 
 /*
+ * 802.11h Quiet Time Element.
+ */
+struct ieee80211_quiet_ie {
+	uint8_t		quiet_ie;		/* IEEE80211_ELEMID_QUIET */
+	uint8_t		len;
+	uint8_t		tbttcount;		/* quiet start */
+	uint8_t		period;			/* beacon intervals between quiets */
+	uint16_t	duration;		/* TUs of each quiet*/
+	uint16_t	offset;			/* TUs of from TBTT of quiet start */
+} __packed;
+
+/*
  * 802.11h Channel Switch Announcement (CSA).
  */
 struct ieee80211_csa_ie {
@@ -920,19 +944,21 @@
 	IEEE80211_REASON_SETUP_NEEDED		= 38,	/* 11e */
 	IEEE80211_REASON_TIMEOUT		= 39,	/* 11e */
 
-	/* values not yet allocated by ANA */
-	IEEE80211_REASON_PEER_LINK_CANCELED	= 2,	/* 11s */
-	IEEE80211_REASON_MESH_MAX_PEERS		= 3,	/* 11s */
-	IEEE80211_REASON_MESH_CPVIOLATION	= 4,	/* 11s */
-	IEEE80211_REASON_MESH_CLOSE_RCVD	= 5,	/* 11s */
-	IEEE80211_REASON_MESH_MAX_RETRIES	= 6,	/* 11s */
-	IEEE80211_REASON_MESH_CONFIRM_TIMEOUT	= 7,	/* 11s */
-	IEEE80211_REASON_MESH_INVALID_GTK	= 8,	/* 11s */
-	IEEE80211_REASON_MESH_INCONS_PARAMS	= 9,	/* 11s */
-	IEEE80211_REASON_MESH_INVALID_SECURITY	= 10,	/* 11s */
-	IEEE80211_REASON_MESH_PERR_UNSPEC	= 11,	/* 11s */
-	IEEE80211_REASON_MESH_PERR_NO_FI	= 12,	/* 11s */
-	IEEE80211_REASON_MESH_PERR_DEST_UNREACH	= 13,	/* 11s */
+	IEEE80211_REASON_PEER_LINK_CANCELED	= 52,	/* 11s */
+	IEEE80211_REASON_MESH_MAX_PEERS		= 53,	/* 11s */
+	IEEE80211_REASON_MESH_CPVIOLATION	= 54,	/* 11s */
+	IEEE80211_REASON_MESH_CLOSE_RCVD	= 55,	/* 11s */
+	IEEE80211_REASON_MESH_MAX_RETRIES	= 56,	/* 11s */
+	IEEE80211_REASON_MESH_CONFIRM_TIMEOUT	= 57,	/* 11s */
+	IEEE80211_REASON_MESH_INVALID_GTK	= 58,	/* 11s */
+	IEEE80211_REASON_MESH_INCONS_PARAMS	= 59,	/* 11s */
+	IEEE80211_REASON_MESH_INVALID_SECURITY	= 60,	/* 11s */
+	IEEE80211_REASON_MESH_PERR_NO_PROXY	= 61,	/* 11s */
+	IEEE80211_REASON_MESH_PERR_NO_FI	= 62,	/* 11s */
+	IEEE80211_REASON_MESH_PERR_DEST_UNREACH	= 63,	/* 11s */
+	IEEE80211_REASON_MESH_MAC_ALRDY_EXISTS_MBSS = 64, /* 11s */
+	IEEE80211_REASON_MESH_CHAN_SWITCH_REG	= 65,	/* 11s */
+	IEEE80211_REASON_MESH_CHAN_SWITCH_UNSPEC = 66,	/* 11s */
 
 	IEEE80211_STATUS_SUCCESS		= 0,
 	IEEE80211_STATUS_UNSPECIFIED		= 1,

Modified: trunk/sys/net80211/ieee80211_acl.c
===================================================================
--- trunk/sys/net80211/ieee80211_acl.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_acl.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD: src/sys/net80211/ieee80211_acl.c,v 1.6 2013/01/17 23:29:37 laffer1 Exp $");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_acl.c 228622 2011-12-17 10:32:31Z bschmidt $");
 
 /*
  * IEEE 802.11 MAC ACL support.
@@ -152,7 +153,7 @@
 }
 
 static int
-acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
 {
 	struct aclstate *as = vap->iv_as;
 
@@ -161,9 +162,9 @@
 	case ACL_POLICY_RADIUS:
 		return 1;
 	case ACL_POLICY_ALLOW:
-		return _find_acl(as, mac) != NULL;
+		return _find_acl(as, wh->i_addr2) != NULL;
 	case ACL_POLICY_DENY:
-		return _find_acl(as, mac) == NULL;
+		return _find_acl(as, wh->i_addr2) == NULL;
 	}
 	return 0;		/* should not happen */
 }

Modified: trunk/sys/net80211/ieee80211_action.c
===================================================================
--- trunk/sys/net80211/ieee80211_action.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_action.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_action.c 254315 2013-08-14 04:24:25Z rpaulo $");
 #endif
 
 /*
@@ -67,10 +68,8 @@
 	send_inval, send_inval, send_inval, send_inval,
 	send_inval, send_inval, send_inval, send_inval,
 };
-static ieee80211_send_action_func *meshlm_send_action[4] = {
+static ieee80211_send_action_func *meshaction_send_action[12] = {
 	send_inval, send_inval, send_inval, send_inval,
-};
-static ieee80211_send_action_func *hwmp_send_action[8] = {
 	send_inval, send_inval, send_inval, send_inval,
 	send_inval, send_inval, send_inval, send_inval,
 };
@@ -82,41 +81,35 @@
 int
 ieee80211_send_action_register(int cat, int act, ieee80211_send_action_func *f)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	switch (cat) {
 	case IEEE80211_ACTION_CAT_BA:
-		if (act >= N(ba_send_action))
+		if (act >= nitems(ba_send_action))
 			break;
 		ba_send_action[act] = f;
 		return 0;
 	case IEEE80211_ACTION_CAT_HT:
-		if (act >= N(ht_send_action))
+		if (act >= nitems(ht_send_action))
 			break;
 		ht_send_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
-		if (act >= N(meshpl_send_action))
+	case IEEE80211_ACTION_CAT_SELF_PROT:
+		if (act >= nitems(meshpl_send_action))
 			break;
 		meshpl_send_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHLMETRIC:
-		if (act >= N(meshlm_send_action))
+	case IEEE80211_ACTION_CAT_MESH:
+		if (act >= nitems(meshaction_send_action))
 			break;
-		meshlm_send_action[act] = f;
+		meshaction_send_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHPATH:
-		if (act >= N(hwmp_send_action))
-			break;
-		hwmp_send_action[act] = f;
-		return 0;
+		break;
 	case IEEE80211_ACTION_CAT_VENDOR:
-		if (act >= N(vendor_send_action))
+		if (act >= nitems(vendor_send_action))
 			break;
 		vendor_send_action[act] = f;
 		return 0;
 	}
 	return EINVAL;
-#undef N
 }
 
 void
@@ -128,37 +121,31 @@
 int
 ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	ieee80211_send_action_func *f = send_inval;
 
 	switch (cat) {
 	case IEEE80211_ACTION_CAT_BA:
-		if (act < N(ba_send_action))
+		if (act < nitems(ba_send_action))
 			f = ba_send_action[act];
 		break;
 	case IEEE80211_ACTION_CAT_HT:
-		if (act < N(ht_send_action))
+		if (act < nitems(ht_send_action))
 			f = ht_send_action[act];
 		break;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
-		if (act < N(meshpl_send_action))
+	case IEEE80211_ACTION_CAT_SELF_PROT:
+		if (act < nitems(meshpl_send_action))
 			f = meshpl_send_action[act];
 		break;
-	case IEEE80211_ACTION_CAT_MESHLMETRIC:
-		if (act < N(meshlm_send_action))
-			f = meshlm_send_action[act];
+	case IEEE80211_ACTION_CAT_MESH:
+		if (act < nitems(meshaction_send_action))
+			f = meshaction_send_action[act];
 		break;
-	case IEEE80211_ACTION_CAT_MESHPATH:
-		if (act < N(hwmp_send_action))
-			f = hwmp_send_action[act];
-		break;
 	case IEEE80211_ACTION_CAT_VENDOR:
-		if (act < N(vendor_send_action))
+		if (act < nitems(vendor_send_action))
 			f = vendor_send_action[act];
 		break;
 	}
 	return f(ni, cat, act, sa);
-#undef N
 }
 
 static int
@@ -180,10 +167,8 @@
 	recv_inval, recv_inval, recv_inval, recv_inval,
 	recv_inval, recv_inval, recv_inval, recv_inval,
 };
-static ieee80211_recv_action_func *meshlm_recv_action[4] = {
+static ieee80211_recv_action_func *meshaction_recv_action[12] = {
 	recv_inval, recv_inval, recv_inval, recv_inval,
-};
-static ieee80211_recv_action_func *hwmp_recv_action[8] = {
 	recv_inval, recv_inval, recv_inval, recv_inval,
 	recv_inval, recv_inval, recv_inval, recv_inval,
 };
@@ -195,41 +180,34 @@
 int
 ieee80211_recv_action_register(int cat, int act, ieee80211_recv_action_func *f)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	switch (cat) {
 	case IEEE80211_ACTION_CAT_BA:
-		if (act >= N(ba_recv_action))
+		if (act >= nitems(ba_recv_action))
 			break;
 		ba_recv_action[act] = f;
 		return 0;
 	case IEEE80211_ACTION_CAT_HT:
-		if (act >= N(ht_recv_action))
+		if (act >= nitems(ht_recv_action))
 			break;
 		ht_recv_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
-		if (act >= N(meshpl_recv_action))
+	case IEEE80211_ACTION_CAT_SELF_PROT:
+		if (act >= nitems(meshpl_recv_action))
 			break;
 		meshpl_recv_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHLMETRIC:
-		if (act >= N(meshlm_recv_action))
+	case IEEE80211_ACTION_CAT_MESH:
+		if (act >= nitems(meshaction_recv_action))
 			break;
-		meshlm_recv_action[act] = f;
+		meshaction_recv_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHPATH:
-		if (act >= N(hwmp_recv_action))
-			break;
-		hwmp_recv_action[act] = f;
-		return 0;
 	case IEEE80211_ACTION_CAT_VENDOR:
-		if (act >= N(vendor_recv_action))
+		if (act >= nitems(vendor_recv_action))
 			break;
 		vendor_recv_action[act] = f;
 		return 0;
 	}
 	return EINVAL;
-#undef N
 }
 
 void
@@ -243,37 +221,41 @@
 	const struct ieee80211_frame *wh,
 	const uint8_t *frm, const uint8_t *efrm)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	ieee80211_recv_action_func *f = recv_inval;
+	struct ieee80211vap *vap = ni->ni_vap;
 	const struct ieee80211_action *ia =
 	    (const struct ieee80211_action *) frm;
 
 	switch (ia->ia_category) {
 	case IEEE80211_ACTION_CAT_BA:
-		if (ia->ia_action < N(ba_recv_action))
+		if (ia->ia_action < nitems(ba_recv_action))
 			f = ba_recv_action[ia->ia_action];
 		break;
 	case IEEE80211_ACTION_CAT_HT:
-		if (ia->ia_action < N(ht_recv_action))
+		if (ia->ia_action < nitems(ht_recv_action))
 			f = ht_recv_action[ia->ia_action];
 		break;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
-		if (ia->ia_action < N(meshpl_recv_action))
+	case IEEE80211_ACTION_CAT_SELF_PROT:
+		if (ia->ia_action < nitems(meshpl_recv_action))
 			f = meshpl_recv_action[ia->ia_action];
 		break;
-	case IEEE80211_ACTION_CAT_MESHLMETRIC:
-		if (ia->ia_action < N(meshlm_recv_action))
-			f = meshlm_recv_action[ia->ia_action];
+	case IEEE80211_ACTION_CAT_MESH:
+		if (ni == vap->iv_bss ||
+		    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+			    ni->ni_macaddr, NULL,
+			    "peer link not yet established (%d), cat %s act %u",
+			    ni->ni_mlstate, "mesh action", ia->ia_action);
+			vap->iv_stats.is_mesh_nolink++;
+			break;
+		}
+		if (ia->ia_action < nitems(meshaction_recv_action))
+			f = meshaction_recv_action[ia->ia_action];
 		break;
-	case IEEE80211_ACTION_CAT_MESHPATH:
-		if (ia->ia_action < N(hwmp_recv_action))
-			f = hwmp_recv_action[ia->ia_action];
-		break;
 	case IEEE80211_ACTION_CAT_VENDOR:
-		if (ia->ia_action < N(vendor_recv_action))
+		if (ia->ia_action < nitems(vendor_recv_action))
 			f = vendor_recv_action[ia->ia_action];
 		break;
 	}
 	return f(ni, wh, frm, efrm);
-#undef N
 }

Modified: trunk/sys/net80211/ieee80211_action.h
===================================================================
--- trunk/sys/net80211/ieee80211_action.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_action.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_action.h 195377 2009-07-05 17:59:19Z sam $
  */
 #ifndef _NET80211_IEEE80211_ACTION_H_
 #define _NET80211_IEEE80211_ACTION_H_

Modified: trunk/sys/net80211/ieee80211_adhoc.c
===================================================================
--- trunk/sys/net80211/ieee80211_adhoc.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_adhoc.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_adhoc.c 262007 2014-02-17 01:36:53Z kevlo $");
 #endif
 
 /*
@@ -63,6 +64,7 @@
 #ifdef IEEE80211_SUPPORT_TDMA
 #include <net80211/ieee80211_tdma.h>
 #endif
+#include <net80211/ieee80211_sta.h>
 
 #define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
 
@@ -170,7 +172,9 @@
 				 * Already have a channel; bypass the
 				 * scan and startup immediately.
 				 */
-				ieee80211_create_ibss(vap, vap->iv_des_chan);
+				ieee80211_create_ibss(vap,
+				    ieee80211_ht_adjust_channel(ic,
+				    vap->iv_des_chan, vap->iv_flags_ht));
 				break;
 			}
 			/*
@@ -242,7 +246,7 @@
 			ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN);
 		break;
 	case IEEE80211_S_SLEEP:
-		ieee80211_sta_pwrsave(vap, 0);
+		vap->iv_sta_ps(vap, 0);
 		break;
 	default:
 	invalid:
@@ -471,7 +475,7 @@
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -489,7 +493,7 @@
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
-			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -627,7 +631,7 @@
 			    ether_sprintf(wh->i_addr2), rssi);
 		}
 #endif
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
 			    wh, NULL, "%s", "WEP set but not permitted");
 			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
@@ -685,6 +689,9 @@
 	struct ieee80211_frame *wh;
 	uint8_t *frm, *efrm, *sfrm;
 	uint8_t *ssid, *rates, *xrates;
+#if 0
+	int ht_state_change = 0;
+#endif
 
 	wh = mtod(m0, struct ieee80211_frame *);
 	frm = (uint8_t *)&wh[1];
@@ -745,10 +752,42 @@
 				memcpy(ni->ni_tstamp.data, scan.tstamp,
 					sizeof(ni->ni_tstamp));
 			}
+			/*
+			 * This isn't enabled yet - otherwise it would
+			 * update the HT parameters and channel width
+			 * from any node, which could lead to lots of
+			 * strange behaviour if the 11n nodes aren't
+			 * exactly configured to match.
+			 */
+#if 0
+			if (scan.htcap != NULL && scan.htinfo != NULL &&
+			    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
+				if (ieee80211_ht_updateparams(ni,
+				    scan.htcap, scan.htinfo))
+					ht_state_change = 1;
+			}
+#endif
 			if (ni != NULL) {
 				IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
 				ni->ni_noise = nf;
 			}
+			/*
+			 * Same here - the channel width change should
+			 * be applied to the specific peer node, not
+			 * to the ic.  Ie, the interface configuration
+			 * should stay in its current channel width;
+			 * but it should change the rate control and
+			 * any queued frames for the given node only.
+			 *
+			 * Since there's no (current) way to inform
+			 * the driver that a channel width change has
+			 * occured for a single node, just stub this
+			 * out.
+			 */
+#if 0
+			if (ht_state_change)
+				ieee80211_update_chw(ic);
+#endif
 		}
 		break;
 	}

Modified: trunk/sys/net80211/ieee80211_adhoc.h
===================================================================
--- trunk/sys/net80211/ieee80211_adhoc.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_adhoc.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_adhoc.h 178354 2008-04-20 20:35:46Z sam $
  */
 #ifndef _NET80211_IEEE80211_ADHOC_H_
 #define _NET80211_IEEE80211_ADHOC_H_

Modified: trunk/sys/net80211/ieee80211_ageq.c
===================================================================
--- trunk/sys/net80211/ieee80211_ageq.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ageq.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_ageq.c 223842 2011-07-07 15:41:40Z kevlo $");
 
 /*
  * IEEE 802.11 age queue support.

Modified: trunk/sys/net80211/ieee80211_ageq.h
===================================================================
--- trunk/sys/net80211/ieee80211_ageq.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ageq.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_ageq.h 195379 2009-07-05 18:17:37Z sam $
  */
 #ifndef _NET80211_IEEE80211_STAGEQ_H_
 #define _NET80211_IEEE80211_STAGEQ_H_

Modified: trunk/sys/net80211/ieee80211_alq.c
===================================================================
--- trunk/sys/net80211/ieee80211_alq.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_alq.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_alq.c 233255 2012-03-21 03:19:50Z adrian $");
 #endif
 
 /*
@@ -85,8 +86,10 @@
 		    ieee80211_alq_qsize);
 		ieee80211_alq_lost = 0;
 		ieee80211_alq_logged = 0;
-		printf("net80211: logging to %s enabled; struct size %d bytes\n",
-		    ieee80211_alq_logfile, sizeof(struct ieee80211_alq_rec));
+		printf("net80211: logging to %s enabled; "
+		    "struct size %d bytes\n",
+		    ieee80211_alq_logfile,
+		    sizeof(struct ieee80211_alq_rec));
 	} else {
 		if (ieee80211_alq)
 			alq_close(ieee80211_alq);
@@ -100,14 +103,14 @@
 static int
 sysctl_ieee80211_alq_log(SYSCTL_HANDLER_ARGS)
 {
-        int error, enable;
+	int error, enable;
 
-        enable = (ieee80211_alq != NULL);
-        error = sysctl_handle_int(oidp, &enable, 0, req);
-        if (error || !req->newptr)
-                return (error);
-        else   
-                return (ieee80211_alq_setlogging(enable));
+	enable = (ieee80211_alq != NULL);
+	error = sysctl_handle_int(oidp, &enable, 0, req);
+	if (error || !req->newptr)
+		return (error);
+	else
+		return (ieee80211_alq_setlogging(enable));
 }
 
 SYSCTL_PROC(_net_wlan, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW,
@@ -150,6 +153,7 @@
 	r->r_version = 1;
 	r->r_wlan = htons(vap->iv_ifp->if_dunit);
 	r->r_op = op;
+	r->r_threadid = htonl((uint32_t) curthread->td_tid);
 	memcpy(&r->r_payload, p, MIN(l, sizeof(r->r_payload)));
 	alq_post(ieee80211_alq, ale);
 }

Modified: trunk/sys/net80211/ieee80211_alq.h
===================================================================
--- trunk/sys/net80211/ieee80211_alq.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_alq.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_alq.h 233133 2012-03-18 21:54:59Z adrian $
  */
 #ifndef	__IEEE80211_ALQ_H__
 #define	__IEEE80211_ALQ_H__
@@ -38,6 +39,7 @@
  */
 struct ieee80211_alq_rec {
 	uint32_t	r_timestamp;	/* XXX may wrap! */
+	uint32_t	r_threadid;	/* current thread id */
 	uint16_t	r_wlan;		/* wlan interface number */
 	uint8_t		r_version;	/* version */
 	uint8_t		r_op;		/* top-level operation id */
@@ -46,6 +48,7 @@
 };
 
 /* General logging function */
-extern void ieee80211_alq_log(struct ieee80211vap *vap, uint8_t op, u_char *p, int l);
+extern	void ieee80211_alq_log(struct ieee80211vap *vap, uint8_t op,
+	    u_char *p, int l);
 
 #endif	/* __IEEE80211_ALQ_H__ */

Modified: trunk/sys/net80211/ieee80211_amrr.c
===================================================================
--- trunk/sys/net80211/ieee80211_amrr.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_amrr.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
 
 /*-
@@ -19,7 +20,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_amrr.c 321725 2017-07-30 18:38:05Z avos $");
 
 /*-
  * Naive implementation of the Adaptive Multi Rate Retry algorithm:
@@ -46,6 +47,7 @@
 #endif
 
 #include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ht.h>
 #include <net80211/ieee80211_amrr.h>
 #include <net80211/ieee80211_ratectl.h>
 
@@ -110,6 +112,7 @@
 
 	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
 
+	nrefs++;		/* XXX locking */
 	amrr = vap->iv_rs = malloc(sizeof(struct ieee80211_amrr),
 	    M_80211_RATECTL, M_NOWAIT|M_ZERO);
 	if (amrr == NULL) {
@@ -126,15 +129,29 @@
 amrr_deinit(struct ieee80211vap *vap)
 {
 	free(vap->iv_rs, M_80211_RATECTL);
+	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+	nrefs--;		/* XXX locking */
 }
 
+static int
+amrr_node_is_11n(struct ieee80211_node *ni)
+{
+
+	if (ni->ni_chan == NULL)
+		return (0);
+	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+		return (0);
+	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
+}
+
 static void
 amrr_node_init(struct ieee80211_node *ni)
 {
-	const struct ieee80211_rateset *rs = &ni->ni_rates;
+	const struct ieee80211_rateset *rs = NULL;
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_amrr *amrr = vap->iv_rs;
 	struct ieee80211_amrr_node *amn;
+	uint8_t rate;
 
 	if (ni->ni_rctls == NULL) {
 		ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node),
@@ -152,16 +169,50 @@
 	amn->amn_txcnt = amn->amn_retrycnt = 0;
 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
 
-	/* pick initial rate */
-	for (amn->amn_rix = rs->rs_nrates - 1;
-	     amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
-	     amn->amn_rix--)
-		;
-	ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+	/* 11n or not? Pick the right rateset */
+	if (amrr_node_is_11n(ni)) {
+		/* XXX ew */
+		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+		    "%s: 11n node", __func__);
+		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+	} else {
+		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+		    "%s: non-11n node", __func__);
+		rs = &ni->ni_rates;
+	}
+
+	/* Initial rate - lowest */
+	rate = rs->rs_rates[0];
+
+	/* XXX clear the basic rate flag if it's not 11n */
+	if (! amrr_node_is_11n(ni))
+		rate &= IEEE80211_RATE_VAL;
+
+	/* pick initial rate from the rateset - HT or otherwise */
+	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
+	    amn->amn_rix--) {
+		/* legacy - anything < 36mbit, stop searching */
+		/* 11n - stop at MCS4 / MCS12 / MCS28 */
+		if (amrr_node_is_11n(ni) &&
+		    (rs->rs_rates[amn->amn_rix] & 0x7) < 4)
+			break;
+		else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
+			break;
+		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+	}
+
+	/* if the rate is an 11n rate, ensure the MCS bit is set */
+	if (amrr_node_is_11n(ni))
+		rate |= IEEE80211_RATE_MCS;
+
+	/* Assign initial rate from the rateset */
+	ni->ni_txrate = rate;
 	amn->amn_ticks = ticks;
 
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-	    "AMRR initial rate %d", ni->ni_txrate);
+	    "AMRR: nrates=%d, initial rate %d",
+	    rs->rs_nrates,
+	    rate);
 }
 
 static void
@@ -175,19 +226,42 @@
     struct ieee80211_node *ni)
 {
 	int rix = amn->amn_rix;
+	const struct ieee80211_rateset *rs = NULL;
 
 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
 
+	/* 11n or not? Pick the right rateset */
+	if (amrr_node_is_11n(ni)) {
+		/* XXX ew */
+		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+	} else {
+		rs = &ni->ni_rates;
+	}
+
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
+	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
+	    amn->amn_txcnt,
+	    amn->amn_retrycnt);
+
+	/*
+	 * XXX This is totally bogus for 11n, as although high MCS
+	 * rates for each stream may be failing, the next stream
+	 * should be checked.
+	 *
+	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
+	 * MCS23, we should skip 6/7 and try 8 onwards.
+	 */
 	if (is_success(amn)) {
 		amn->amn_success++;
 		if (amn->amn_success >= amn->amn_success_threshold &&
-		    rix + 1 < ni->ni_rates.rs_nrates) {
+		    rix + 1 < rs->rs_nrates) {
 			amn->amn_recovery = 1;
 			amn->amn_success = 0;
 			rix++;
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
 			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
-			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
+			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 			    amn->amn_txcnt, amn->amn_retrycnt);
 		} else {
 			amn->amn_recovery = 0;
@@ -208,7 +282,7 @@
 			rix--;
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
 			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
-			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
+			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 			    amn->amn_txcnt, amn->amn_retrycnt);
 		}
 		amn->amn_recovery = 0;
@@ -231,14 +305,27 @@
 {
 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
 	struct ieee80211_amrr *amrr = amn->amn_amrr;
+	const struct ieee80211_rateset *rs = NULL;
 	int rix;
 
+	/* 11n or not? Pick the right rateset */
+	if (amrr_node_is_11n(ni)) {
+		/* XXX ew */
+		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+	} else {
+		rs = &ni->ni_rates;
+	}
+
 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
 		rix = amrr_update(amrr, amn, ni);
 		if (rix != amn->amn_rix) {
 			/* update public rate */
-			ni->ni_txrate =
-			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+			ni->ni_txrate = rs->rs_rates[rix];
+			/* XXX strip basic rate flag from txrate, if non-11n */
+			if (amrr_node_is_11n(ni))
+				ni->ni_txrate |= IEEE80211_RATE_MCS;
+			else
+				ni->ni_txrate &= IEEE80211_RATE_VAL;
 			amn->amn_rix = rix;
 		}
 		amn->amn_ticks = ticks;

Modified: trunk/sys/net80211/ieee80211_amrr.h
===================================================================
--- trunk/sys/net80211/ieee80211_amrr.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_amrr.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,4 +1,5 @@
 /* $MidnightBSD$ */
+/* $FreeBSD: stable/10/sys/net80211/ieee80211_amrr.h 206358 2010-04-07 15:29:13Z rpaulo $ */
 /*	$OpenBSD: ieee80211_amrr.h,v 1.3 2006/06/17 19:34:31 damien Exp $	*/
 
 /*-

Modified: trunk/sys/net80211/ieee80211_crypto.c
===================================================================
--- trunk/sys/net80211/ieee80211_crypto.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_crypto.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_crypto.c 195812 2009-07-21 19:36:32Z sam $");
 
 /*
  * IEEE 802.11 generic crypto support.

Modified: trunk/sys/net80211/ieee80211_crypto.h
===================================================================
--- trunk/sys/net80211/ieee80211_crypto.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_crypto.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_crypto.h 209636 2010-07-01 20:50:12Z bschmidt $
  */
 #ifndef _NET80211_IEEE80211_CRYPTO_H_
 #define _NET80211_IEEE80211_CRYPTO_H_

Modified: trunk/sys/net80211/ieee80211_crypto_ccmp.c
===================================================================
--- trunk/sys/net80211/ieee80211_crypto_ccmp.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_crypto_ccmp.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_crypto_ccmp.c 209636 2010-07-01 20:50:12Z bschmidt $");
 
 /*
  * IEEE 802.11i AES-CCMP crypto support.

Modified: trunk/sys/net80211/ieee80211_crypto_none.c
===================================================================
--- trunk/sys/net80211/ieee80211_crypto_none.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_crypto_none.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_crypto_none.c 178354 2008-04-20 20:35:46Z sam $");
 
 /*
  * IEEE 802.11 NULL crypto support.

Modified: trunk/sys/net80211/ieee80211_crypto_tkip.c
===================================================================
--- trunk/sys/net80211/ieee80211_crypto_tkip.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_crypto_tkip.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_crypto_tkip.c 209636 2010-07-01 20:50:12Z bschmidt $");
 
 /*
  * IEEE 802.11i TKIP crypto support.

Modified: trunk/sys/net80211/ieee80211_crypto_wep.c
===================================================================
--- trunk/sys/net80211/ieee80211_crypto_wep.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_crypto_wep.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_crypto_wep.c 305615 2016-09-08 15:06:28Z pfg $");
 
 /*
  * IEEE 802.11 WEP crypto support.
@@ -419,7 +420,7 @@
 	}
 
 	off = hdrlen + wep.ic_header;
-	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
+	data_len = m->m_pkthdr.len - (off + wep.ic_trailer);
 
 	/* Compute CRC32 over unencrypted data and apply RC4 to data */
 	crc = ~0;

Modified: trunk/sys/net80211/ieee80211_ddb.c
===================================================================
--- trunk/sys/net80211/ieee80211_ddb.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ddb.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_ddb.c 246516 2013-02-07 21:29:48Z monthadar $");
 
 #include "opt_ddb.h"
 #include "opt_wlan.h"
@@ -63,9 +64,9 @@
 } while (0)
 
 static void _db_show_sta(const struct ieee80211_node *);
-static void _db_show_vap(const struct ieee80211vap *, int);
+static void _db_show_vap(const struct ieee80211vap *, int, int);
 static void _db_show_com(const struct ieee80211com *,
-	int showvaps, int showsta, int showprocs);
+	int showvaps, int showsta, int showmesh, int showprocs);
 
 static void _db_show_node_table(const char *tag,
 	const struct ieee80211_node_table *);
@@ -103,7 +104,7 @@
 
 DB_SHOW_COMMAND(vap, db_show_vap)
 {
-	int i, showprocs = 0;
+	int i, showmesh = 0, showprocs = 0;
 
 	if (!have_addr) {
 		db_printf("usage: show vap <addr>\n");
@@ -113,18 +114,22 @@
 		switch (modif[i]) {
 		case 'a':
 			showprocs = 1;
+			showmesh = 1;
 			break;
+		case 'm':
+			showmesh = 1;
+			break;
 		case 'p':
 			showprocs = 1;
 			break;
 		}
-	_db_show_vap((const struct ieee80211vap *) addr, showprocs);
+	_db_show_vap((const struct ieee80211vap *) addr, showmesh, showprocs);
 }
 
 DB_SHOW_COMMAND(com, db_show_com)
 {
 	const struct ieee80211com *ic;
-	int i, showprocs = 0, showvaps = 0, showsta = 0;
+	int i, showprocs = 0, showvaps = 0, showsta = 0, showmesh = 0;
 
 	if (!have_addr) {
 		db_printf("usage: show com <addr>\n");
@@ -133,11 +138,14 @@
 	for (i = 0; modif[i] != '\0'; i++)
 		switch (modif[i]) {
 		case 'a':
-			showsta = showvaps = showprocs = 1;
+			showsta = showmesh = showvaps = showprocs = 1;
 			break;
 		case 's':
 			showsta = 1;
 			break;
+		case 'm':
+			showmesh = 1;
+			break;
 		case 'v':
 			showvaps = 1;
 			break;
@@ -147,7 +155,7 @@
 		}
 
 	ic = (const struct ieee80211com *) addr;
-	_db_show_com(ic, showvaps, showsta, showprocs);
+	_db_show_com(ic, showvaps, showsta, showmesh, showprocs);
 }
 
 DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
@@ -178,7 +186,7 @@
 						    vap->iv_ifp->if_xname, vap);
 					db_printf("\n");
 				} else
-					_db_show_com(ic, 1, 1, 1);
+					_db_show_com(ic, 1, 1, 1, 1);
 			}
 	}
 }
@@ -202,7 +210,7 @@
 {
 	db_printf("%stxampdu[%d]: %p flags %b %s\n",
 		sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS,
-		ieee80211_wme_acnames[tap->txa_ac]);
+		ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)]);
 	db_printf("%s  token %u lastsample %d pkts %d avgpps %d qbytes %d qframes %d\n",
 		sep, tap->txa_token, tap->txa_lastsample, tap->txa_pkts,
 		tap->txa_avgpps, tap->txa_qbytes, tap->txa_qframes);
@@ -293,7 +301,7 @@
 		ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw);
 
 	/* XXX ampdu state */
-	for (i = 0; i < WME_NUM_AC; i++)
+	for (i = 0; i < WME_NUM_TID; i++)
 		if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP)
 			_db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]);
 	for (i = 0; i < WME_NUM_TID; i++)
@@ -330,7 +338,7 @@
 #endif /* IEEE80211_SUPPORT_TDMA */
 
 static void
-_db_show_vap(const struct ieee80211vap *vap, int showprocs)
+_db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs)
 {
 	const struct ieee80211com *ic = vap->iv_ic;
 	int i;
@@ -341,6 +349,10 @@
 	db_printf("\n");
 
 	db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]);
+#ifdef IEEE80211_SUPPORT_MESH
+	if (vap->iv_opmode == IEEE80211_M_MBSS)
+		db_printf("(%p)", vap->iv_mesh);
+#endif
 	db_printf(" state %s", ieee80211_state_name[vap->iv_state]);
 	db_printf(" ifp %p(%s)", vap->iv_ifp, vap->iv_ifp->if_xname);
 	db_printf("\n");
@@ -472,6 +484,10 @@
 	db_printf(" acl %p", vap->iv_acl);
 	db_printf(" as %p", vap->iv_as);
 	db_printf("\n");
+#ifdef IEEE80211_SUPPORT_MESH
+	if (showmesh && vap->iv_mesh != NULL)
+		_db_show_mesh(vap->iv_mesh);
+#endif
 #ifdef IEEE80211_SUPPORT_TDMA
 	if (vap->iv_tdma != NULL)
 		_db_show_tdma("\t", vap->iv_tdma, showprocs);
@@ -495,7 +511,8 @@
 }
 
 static void
-_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs)
+_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta,
+    int showmesh, int showprocs)
 {
 	struct ieee80211vap *vap;
 
@@ -651,7 +668,7 @@
 	if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) {
 		db_printf("\n");
 		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
-			_db_show_vap(vap, showprocs);
+			_db_show_vap(vap, showmesh, showprocs);
 	}
 	if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) {
 		const struct ieee80211_node_table *nt = &ic->ic_sta;
@@ -870,8 +887,10 @@
 	TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
 		db_printf("entry %d:\tdest: %6D nexthop: %6D metric: %u", i,
 		    rt->rt_dest, ":", rt->rt_nexthop, ":", rt->rt_metric);
+
 		db_printf("\tlifetime: %u lastseq: %u priv: %p\n",
-		    rt->rt_lifetime, rt->rt_lastmseq, rt->rt_priv);
+		    ieee80211_mesh_rt_update(rt, 0),
+		    rt->rt_lastmseq, rt->rt_priv);
 		i++;
 	}
 }

Modified: trunk/sys/net80211/ieee80211_dfs.c
===================================================================
--- trunk/sys/net80211/ieee80211_dfs.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_dfs.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD: src/sys/net80211/ieee80211_dfs.c,v 1.2 2013/01/17 23:29:38 laffer1 Exp $");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_dfs.c 230793 2012-01-31 00:03:49Z adrian $");
 #endif
 
 /*
@@ -64,6 +65,34 @@
 	&ieee80211_cac_timeout, 0, "CAC timeout (secs)");
 #define	CAC_TIMEOUT	msecs_to_ticks(ieee80211_cac_timeout*1000)
 
+/*
+ DFS* In order to facilitate  debugging, a couple of operating
+ * modes aside from the default are needed.
+ *
+ * 0 - default CAC/NOL behaviour - ie, start CAC, place
+ *     channel on NOL list.
+ * 1 - send CAC, but don't change channel or add the channel
+ *     to the NOL list.
+ * 2 - just match on radar, don't send CAC or place channel in
+ *     the NOL list.
+ */
+static	int ieee80211_dfs_debug = DFS_DBG_NONE;
+
+/*
+ * This option must not be included in the default kernel
+ * as it allows users to plainly disable CAC/NOL handling.
+ */
+#ifdef	IEEE80211_DFS_DEBUG
+SYSCTL_INT(_net_wlan, OID_AUTO, dfs_debug, CTLFLAG_RW,
+	&ieee80211_dfs_debug, 0, "DFS debug behaviour");
+#endif
+
+static int
+null_set_quiet(struct ieee80211_node *ni, u_int8_t *quiet_elm)
+{
+	return ENOSYS;
+}
+
 void
 ieee80211_dfs_attach(struct ieee80211com *ic)
 {
@@ -71,6 +100,8 @@
 
 	callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
 	callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
+
+	ic->ic_set_quiet = null_set_quiet;
 }
 
 void
@@ -270,24 +301,44 @@
 	IEEE80211_LOCK_ASSERT(ic);
 
 	/*
-	 * Mark all entries with this frequency.  Notify user
-	 * space and arrange for notification when the radar
-	 * indication is cleared.  Then kick the NOL processing
-	 * thread if not already running.
+	 * If doing DFS debugging (mode 2), don't bother
+	 * running the rest of this function.
+	 *
+	 * Simply announce the presence of the radar and continue
+	 * along merrily.
 	 */
-	now = ticks;
-	for (i = 0; i < ic->ic_nchans; i++) {
-		struct ieee80211_channel *c = &ic->ic_channels[i];
-		if (c->ic_freq == chan->ic_freq) {
-			c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
-			c->ic_state |= IEEE80211_CHANSTATE_RADAR;
-			dfs->nol_event[i] = now;
+	if (ieee80211_dfs_debug == DFS_DBG_NOCSANOL) {
+		announce_radar(ic->ic_ifp, chan, chan);
+		ieee80211_notify_radar(ic, chan);
+		return;
+	}
+
+	/*
+	 * Don't mark the channel and don't put it into NOL
+	 * if we're doing DFS debugging.
+	 */
+	if (ieee80211_dfs_debug == DFS_DBG_NONE) {
+		/*
+		 * Mark all entries with this frequency.  Notify user
+		 * space and arrange for notification when the radar
+		 * indication is cleared.  Then kick the NOL processing
+		 * thread if not already running.
+		 */
+		now = ticks;
+		for (i = 0; i < ic->ic_nchans; i++) {
+			struct ieee80211_channel *c = &ic->ic_channels[i];
+			if (c->ic_freq == chan->ic_freq) {
+				c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+				c->ic_state |= IEEE80211_CHANSTATE_RADAR;
+				dfs->nol_event[i] = now;
+			}
 		}
+		ieee80211_notify_radar(ic, chan);
+		chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
+		if (!callout_pending(&dfs->nol_timer))
+			callout_reset(&dfs->nol_timer, NOL_TIMEOUT,
+			    dfs_timeout, ic);
 	}
-	ieee80211_notify_radar(ic, chan);
-	chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
-	if (!callout_pending(&dfs->nol_timer))
-		callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
 
 	/*
 	 * If radar is detected on the bss channel while
@@ -302,8 +353,16 @@
 	 */
 	if (chan == ic->ic_bsschan) {
 		/* XXX need a way to defer to user app */
-		dfs->newchan = ieee80211_dfs_pickchannel(ic);
 
+		/*
+		 * Don't flip over to a new channel if
+		 * we are currently doing DFS debugging.
+		 */
+		if (ieee80211_dfs_debug == DFS_DBG_NONE)
+			dfs->newchan = ieee80211_dfs_pickchannel(ic);
+		else
+			dfs->newchan = chan;
+
 		announce_radar(ic->ic_ifp, chan, dfs->newchan);
 
 		if (callout_pending(&dfs->cac_timer))

Modified: trunk/sys/net80211/ieee80211_dfs.h
===================================================================
--- trunk/sys/net80211/ieee80211_dfs.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_dfs.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_dfs.h 230793 2012-01-31 00:03:49Z adrian $
  */
 #ifndef _NET80211_IEEE80211_DFS_H_
 #define _NET80211_IEEE80211_DFS_H_
@@ -31,6 +32,12 @@
  * 802.11h/DFS definitions.
  */
 
+typedef enum {
+	DFS_DBG_NONE		= 0,
+	DFS_DBG_NONOL		= 1,
+	DFS_DBG_NOCSANOL	= 2
+} dfs_debug_t;
+
 struct ieee80211_dfs_state {
 	int		nol_event[IEEE80211_CHAN_MAX];
 	struct callout	nol_timer;		/* NOL list processing */

Modified: trunk/sys/net80211/ieee80211_freebsd.c
===================================================================
--- trunk/sys/net80211/ieee80211_freebsd.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_freebsd.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2003-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD: src/sys/net80211/ieee80211_freebsd.c,v 1.6 2013/01/17 23:29:38 laffer1 Exp $");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_freebsd.c 259174 2013-12-10 13:38:39Z gavin $");
 
 /*
  * IEEE 802.11 support (FreeBSD-specific code)
@@ -65,6 +66,11 @@
 
 static MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state");
 
+#if __FreeBSD_version >= 1000020
+static const char wlanname[] = "wlan";
+static struct if_clone *wlan_cloner;
+#endif
+
 /*
  * Allocate/free com structure in conjunction with ifnet;
  * these routines are registered with if_register_com_alloc
@@ -129,10 +135,19 @@
 		if_printf(ifp, "TDMA not supported\n");
 		return EOPNOTSUPP;
 	}
+#if __FreeBSD_version >= 1000020
+	vap = ic->ic_vap_create(ic, wlanname, unit,
+			cp.icp_opmode, cp.icp_flags, cp.icp_bssid,
+			cp.icp_flags & IEEE80211_CLONE_MACADDR ?
+			    cp.icp_macaddr : (const uint8_t *)IF_LLADDR(ifp));
+#else
 	vap = ic->ic_vap_create(ic, ifc->ifc_name, unit,
 			cp.icp_opmode, cp.icp_flags, cp.icp_bssid,
 			cp.icp_flags & IEEE80211_CLONE_MACADDR ?
 			    cp.icp_macaddr : (const uint8_t *)IF_LLADDR(ifp));
+
+#endif
+
 	return (vap == NULL ? EIO : 0);
 }
 
@@ -144,12 +159,21 @@
 
 	ic->ic_vap_delete(vap);
 }
+
+#if __FreeBSD_version < 1000020
 IFC_SIMPLE_DECLARE(wlan, 0);
+#endif
 
 void
 ieee80211_vap_destroy(struct ieee80211vap *vap)
 {
+	CURVNET_SET(vap->iv_ifp->if_vnet);
+#if __FreeBSD_version >= 1000020
+	if_clone_destroyif(wlan_cloner, vap->iv_ifp);
+#else
 	if_clone_destroyif(&wlan_cloner, vap->iv_ifp);
+#endif
+	CURVNET_RESTORE();
 }
 
 int
@@ -409,6 +433,7 @@
 	return m;
 }
 
+#ifndef __NO_STRICT_ALIGNMENT
 /*
  * Re-align the payload in the mbuf.  This is mainly used (right now)
  * to handle IP header alignment requirements on certain architectures.
@@ -422,9 +447,9 @@
 	pktlen = m->m_pkthdr.len;
 	space = pktlen + align;
 	if (space < MINCLSIZE)
-		n = m_gethdr(M_DONTWAIT, MT_DATA);
+		n = m_gethdr(M_NOWAIT, MT_DATA);
 	else {
-		n = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR,
+		n = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
 		    space <= MCLBYTES ?     MCLBYTES :
 #if MJUMPAGESIZE != MCLBYTES
 		    space <= MJUMPAGESIZE ? MJUMPAGESIZE :
@@ -445,6 +470,7 @@
 	m_freem(m);
 	return n;
 }
+#endif /* !__NO_STRICT_ALIGNMENT */
 
 int
 ieee80211_add_callback(struct mbuf *m,
@@ -479,6 +505,44 @@
 	}
 }
 
+/*
+ * Transmit a frame to the parent interface.
+ *
+ * TODO: if the transmission fails, make sure the parent node is freed
+ *   (the callers will first need modifying.)
+ */
+int
+ieee80211_parent_xmitpkt(struct ieee80211com *ic,
+	struct mbuf *m)
+{
+	struct ifnet *parent = ic->ic_ifp;
+	/*
+	 * Assert the IC TX lock is held - this enforces the
+	 * processing -> queuing order is maintained
+	 */
+	IEEE80211_TX_LOCK_ASSERT(ic);
+
+	return (parent->if_transmit(parent, m));
+}
+
+/*
+ * Transmit a frame to the VAP interface.
+ */
+int
+ieee80211_vap_xmitpkt(struct ieee80211vap *vap, struct mbuf *m)
+{
+	struct ifnet *ifp = vap->iv_ifp;
+
+	/*
+	 * When transmitting via the VAP, we shouldn't hold
+	 * any IC TX lock as the VAP TX path will acquire it.
+	 */
+	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+	return (ifp->if_transmit(ifp, m));
+
+}
+
 #include <sys/libkern.h>
 
 void
@@ -571,8 +635,8 @@
 	struct ifnet *ifp = vap->iv_ifp;
 
 	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
-	    "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
-	    k->wk_cipher->ic_name, (intmax_t) rsc,
+	    "%s replay detected tid %d <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
+	    k->wk_cipher->ic_name, tid, (intmax_t) rsc,
 	    (intmax_t) k->wk_keyrsc[tid],
 	    k->wk_keyix, k->wk_rxkeyix);
 
@@ -639,7 +703,9 @@
 	iev.iev_ieee = c->ic_ieee;
 	iev.iev_mode = mode;
 	iev.iev_count = count;
+	CURVNET_SET(ifp->if_vnet);
 	rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
+	CURVNET_RESTORE();
 }
 
 void
@@ -653,7 +719,9 @@
 	iev.iev_flags = c->ic_flags;
 	iev.iev_freq = c->ic_freq;
 	iev.iev_ieee = c->ic_ieee;
+	CURVNET_SET(ifp->if_vnet);
 	rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev));
+	CURVNET_RESTORE();
 }
 
 void
@@ -668,7 +736,9 @@
 	iev.iev_freq = c->ic_freq;
 	iev.iev_ieee = c->ic_ieee;
 	iev.iev_type = type;
+	CURVNET_SET(ifp->if_vnet);
 	rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev));
+	CURVNET_RESTORE();
 }
 
 void
@@ -704,7 +774,9 @@
 	IEEE80211_ADDR_COPY(iev.iev_addr, bssid);
 	iev.iev_cc[0] = cc[0];
 	iev.iev_cc[1] = cc[1];
+	CURVNET_SET(ifp->if_vnet);
 	rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev));
+	CURVNET_RESTORE();
 }
 
 void
@@ -715,7 +787,9 @@
 
 	memset(&iev, 0, sizeof(iev));
 	iev.iev_state = state;
+	CURVNET_SET(ifp->if_vnet);
 	rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
+	CURVNET_RESTORE();
 }
 
 void
@@ -735,8 +809,9 @@
 static void
 bpf_track(void *arg, struct ifnet *ifp, int dlt, int attach)
 {
-	/* NB: identify vap's by if_start */
-	if (dlt == DLT_IEEE802_11_RADIO && ifp->if_start == ieee80211_start) {
+	/* NB: identify vap's by if_init */
+	if (dlt == DLT_IEEE802_11_RADIO &&
+	    ifp->if_init == ieee80211_init) {
 		struct ieee80211vap *vap = ifp->if_softc;
 		/*
 		 * Track bpf radiotap listener state.  We mark the vap
@@ -806,12 +881,21 @@
 			EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent);
 			return ENOMEM;
 		}
+#if __FreeBSD_version >= 1000020
+		wlan_cloner = if_clone_simple(wlanname, wlan_clone_create,
+		    wlan_clone_destroy, 0);
+#else
 		if_clone_attach(&wlan_cloner);
+#endif
 		if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free);
 		return 0;
 	case MOD_UNLOAD:
 		if_deregister_com_alloc(IFT_IEEE80211);
+#if __FreeBSD_version >= 1000020
+		if_clone_detach(wlan_cloner);
+#else
 		if_clone_detach(&wlan_cloner);
+#endif
 		EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent);
 		EVENTHANDLER_DEREGISTER(iflladdr_event, wlan_ifllevent);
 		return 0;
@@ -820,7 +904,11 @@
 }
 
 static moduledata_t wlan_mod = {
+#if __FreeBSD_version >= 1000020
+	wlanname,
+#else
 	"wlan",
+#endif
 	wlan_modevent,
 	0
 };
@@ -827,3 +915,7 @@
 DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
 MODULE_VERSION(wlan, 1);
 MODULE_DEPEND(wlan, ether, 1, 1, 1);
+#ifdef	IEEE80211_ALQ
+MODULE_DEPEND(wlan, alq, 1, 1, 1);
+#endif	/* IEEE80211_ALQ */
+

Modified: trunk/sys/net80211/ieee80211_freebsd.h
===================================================================
--- trunk/sys/net80211/ieee80211_freebsd.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_freebsd.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_freebsd.h 254640 2013-08-22 05:53:47Z adrian $
  */
 #ifndef _NET80211_IEEE80211_FREEBSD_H_
 #define _NET80211_IEEE80211_FREEBSD_H_
@@ -53,8 +54,34 @@
 #define	IEEE80211_UNLOCK(_ic)	   mtx_unlock(IEEE80211_LOCK_OBJ(_ic))
 #define	IEEE80211_LOCK_ASSERT(_ic) \
 	mtx_assert(IEEE80211_LOCK_OBJ(_ic), MA_OWNED)
+#define	IEEE80211_UNLOCK_ASSERT(_ic) \
+	mtx_assert(IEEE80211_LOCK_OBJ(_ic), MA_NOTOWNED)
 
 /*
+ * Transmit lock.
+ *
+ * This is a (mostly) temporary lock designed to serialise all of the
+ * transmission operations throughout the stack.
+ */
+typedef struct {
+	char		name[16];		/* e.g. "ath0_com_lock" */
+	struct mtx	mtx;
+} ieee80211_tx_lock_t;
+#define	IEEE80211_TX_LOCK_INIT(_ic, _name) do {				\
+	ieee80211_tx_lock_t *cl = &(_ic)->ic_txlock;			\
+	snprintf(cl->name, sizeof(cl->name), "%s_tx_lock", _name);	\
+	mtx_init(&cl->mtx, cl->name, NULL, MTX_DEF);	\
+} while (0)
+#define	IEEE80211_TX_LOCK_OBJ(_ic)	(&(_ic)->ic_txlock.mtx)
+#define	IEEE80211_TX_LOCK_DESTROY(_ic) mtx_destroy(IEEE80211_TX_LOCK_OBJ(_ic))
+#define	IEEE80211_TX_LOCK(_ic)	   mtx_lock(IEEE80211_TX_LOCK_OBJ(_ic))
+#define	IEEE80211_TX_UNLOCK(_ic)	   mtx_unlock(IEEE80211_TX_LOCK_OBJ(_ic))
+#define	IEEE80211_TX_LOCK_ASSERT(_ic) \
+	mtx_assert(IEEE80211_TX_LOCK_OBJ(_ic), MA_OWNED)
+#define	IEEE80211_TX_UNLOCK_ASSERT(_ic) \
+	mtx_assert(IEEE80211_TX_LOCK_OBJ(_ic), MA_NOTOWNED)
+
+/*
  * Node locking definitions.
  */
 typedef struct {
@@ -208,9 +235,25 @@
 #define	M_FF		M_PROTO6		/* fast frame */
 #define	M_TXCB		M_PROTO7		/* do tx complete callback */
 #define	M_AMPDU_MPDU	M_PROTO8		/* ok for A-MPDU aggregation */
+
+/*
+ * FreeBSD-HEAD from 1000046 retired M_*FRAG* flags and turned them
+ * into header flags instead.  So, we use the new protocol-specific
+ * flags.
+ *
+ * Earlier FreeBSD versions overload M_FRAG, M_FIRSTFRAG and M_LASTFRAG.
+ *
+ * XXX TODO: rename these fields so there are no namespace clashes!
+ */
+#if __FreeBSD_version >= 1000046
+#define	M_FRAG		M_PROTO9		/* frame fragmentation */
+#define	M_FIRSTFRAG	M_PROTO10		/* first frame fragment */
+#define	M_LASTFRAG	M_PROTO11		/* last frame fragment */
+#endif
+
 #define	M_80211_TX \
-	(M_FRAG|M_FIRSTFRAG|M_LASTFRAG|M_ENCAP|M_EAPOL|M_PWR_SAV|\
-	 M_MORE_DATA|M_FF|M_TXCB|M_AMPDU_MPDU)
+	(M_ENCAP|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB| \
+	 M_AMPDU_MPDU|M_FRAG|M_FIRSTFRAG|M_LASTFRAG)
 
 /* rx path usage */
 #define	M_AMPDU		M_PROTO1		/* A-MPDU subframe */
@@ -220,17 +263,22 @@
 #endif
 #define	M_80211_RX	(M_AMPDU|M_WEP|M_AMPDU_MPDU)
 
+#if __FreeBSD_version >= 1000046
 #define	IEEE80211_MBUF_TX_FLAG_BITS \
-	"\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_ENCAP\6M_WEP\7M_EAPOL" \
-	"\10M_PWR_SAV\11M_MORE_DATA\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \
-	"\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \
-	"\23M_NOFREE\24M_FF\25M_TXCB\26M_AMPDU_MPDU\27M_FLOWID"
+	M_FLAG_BITS \
+	"\15M_ENCAP\17M_EAPOL\20M_PWR_SAV\21M_MORE_DATA\22M_FF\23M_TXCB" \
+	"\24M_AMPDU_MPDU\25M_FRAG\26M_FIRSTFRAG\27M_LASTFRAG"
+#else
+/* There aren't any flag bits available for versions before this */
+/* XXX TODO: implement M_FLAG_BITS for this! */
+#define	IEEE80211_MBUF_TX_FLAG_BITS \
+	"\15M_ENCAP\17M_EAPOL\20M_PWR_SAV\21M_MORE_DATA\22M_FF\23M_TXCB" \
+	"\24M_AMPDU_MPDU"
+#endif
 
 #define	IEEE80211_MBUF_RX_FLAG_BITS \
-	"\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_AMPDU\6M_WEP\7M_PROTO3" \
-	"\10M_PROTO4\11M_PROTO5\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \
-	"\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \
-	"\23M_NOFREE\24M_PROTO6\25M_PROTO7\26M_AMPDU_MPDU\27M_FLOWID"
+	M_FLAG_BITS \
+	"\15M_AMPDU\16M_WEP\24M_AMPDU_MPDU"
 
 /*
  * Store WME access control bits in the vlan tag.
@@ -270,10 +318,12 @@
 		void (*func)(struct ieee80211_node *, void *, int), void *arg);
 void	ieee80211_process_callback(struct ieee80211_node *, struct mbuf *, int);
 
+struct ieee80211com;
+int	ieee80211_parent_xmitpkt(struct ieee80211com *, struct mbuf *);
+int	ieee80211_vap_xmitpkt(struct ieee80211vap *, struct mbuf *);
+
 void	get_random_bytes(void *, size_t);
 
-struct ieee80211com;
-
 void	ieee80211_sysctl_attach(struct ieee80211com *);
 void	ieee80211_sysctl_detach(struct ieee80211com *);
 void	ieee80211_sysctl_vattach(struct ieee80211vap *);

Modified: trunk/sys/net80211/ieee80211_hostap.c
===================================================================
--- trunk/sys/net80211/ieee80211_hostap.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_hostap.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_hostap.c 262007 2014-02-17 01:36:53Z kevlo $");
 #endif
 
 /*
@@ -73,7 +74,6 @@
 static void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *,
 	    int subtype, int rssi, int nf);
 static void hostap_recv_ctl(struct ieee80211_node *, struct mbuf *, int);
-static void hostap_recv_pspoll(struct ieee80211_node *, struct mbuf *);
 
 void
 ieee80211_hostap_attach(struct ieee80211com *ic)
@@ -100,6 +100,7 @@
 	vap->iv_recv_ctl = hostap_recv_ctl;
 	vap->iv_opdetach = hostap_vdetach;
 	vap->iv_deliver_data = hostap_deliver_data;
+	vap->iv_recv_pspoll = ieee80211_recv_pspoll;
 }
 
 static void
@@ -355,7 +356,12 @@
 	struct ifnet *ifp = vap->iv_ifp;
 
 	/* clear driver/net80211 flags before passing up */
+#if __FreeBSD_version >= 1000046
+	m->m_flags &= ~(M_MCAST | M_BCAST);
+	m_clrprotoflags(m);
+#else
 	m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST);
+#endif
 
 	KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
 	    ("gack, opmode %d", vap->iv_opmode));
@@ -376,7 +382,7 @@
 		struct mbuf *mcopy = NULL;
 
 		if (m->m_flags & M_MCAST) {
-			mcopy = m_dup(m, M_DONTWAIT);
+			mcopy = m_dup(m, M_NOWAIT);
 			if (mcopy == NULL)
 				ifp->if_oerrors++;
 			else
@@ -412,7 +418,7 @@
 		if (mcopy != NULL) {
 			int len, err;
 			len = mcopy->m_pkthdr.len;
-			err = ifp->if_transmit(ifp, mcopy);
+			err = ieee80211_vap_xmitpkt(vap, mcopy);
 			if (err) {
 				/* NB: IFQ_HANDOFF reclaims mcopy */
 			} else {
@@ -645,7 +651,7 @@
 		 */
 		if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
 		    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
-			ieee80211_node_pwrsave(ni,
+			vap->iv_node_ps(ni,
 				wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
 		/*
 		 * For 4-address packets handle WDS discovery
@@ -688,7 +694,7 @@
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -706,7 +712,7 @@
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
-			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -850,7 +856,7 @@
 			    ether_sprintf(wh->i_addr2), rssi);
 		}
 #endif
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
 				/*
 				 * Only shared key auth frames with a challenge
@@ -878,7 +884,7 @@
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
-			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		}
 		/*
 		 * Pass the packet to radiotap before calling iv_recv_mgmt().
@@ -1795,6 +1801,15 @@
 			return;
 		}
 		/*
+		 * Consult the ACL policy module if setup.
+		 */
+		if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
+			IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+			    wh, NULL, "%s", "disallowed by ACL");
+			vap->iv_stats.is_rx_acl++;
+			return;
+		}
+		/*
 		 * prreq frame format
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
@@ -1873,8 +1888,7 @@
 		/*
 		 * Consult the ACL policy module if setup.
 		 */
-		if (vap->iv_acl != NULL &&
-		    !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+		if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
 			    wh, NULL, "%s", "disallowed by ACL");
 			vap->iv_stats.is_rx_acl++;
@@ -2232,7 +2246,7 @@
 {
 	switch (subtype) {
 	case IEEE80211_FC0_SUBTYPE_PS_POLL:
-		hostap_recv_pspoll(ni, m);
+		ni->ni_vap->iv_recv_pspoll(ni, m);
 		break;
 	case IEEE80211_FC0_SUBTYPE_BAR:
 		ieee80211_recv_bar(ni, m);
@@ -2243,12 +2257,12 @@
 /*
  * Process a received ps-poll frame.
  */
-static void
-hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
+void
+ieee80211_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = vap->iv_ic;
 	struct ieee80211_frame_min *wh;
-	struct ifnet *ifp;
 	struct mbuf *m;
 	uint16_t aid;
 	int qlen;
@@ -2312,10 +2326,15 @@
 	}
 	m->m_flags |= M_PWR_SAV;		/* bypass PS handling */
 
-	if (m->m_flags & M_ENCAP)
-		ifp = vap->iv_ic->ic_ifp;
-	else
-		ifp = vap->iv_ifp;
-	IF_ENQUEUE(&ifp->if_snd, m);
-	if_start(ifp);
+	/*
+	 * Do the right thing; if it's an encap'ed frame then
+	 * call ieee80211_parent_xmitpkt() (and free the ref) else
+	 * call ieee80211_vap_xmitpkt().
+	 */
+	if (m->m_flags & M_ENCAP) {
+		if (ieee80211_parent_xmitpkt(ic, m) != 0)
+			ieee80211_free_node(ni);
+	} else {
+		(void) ieee80211_vap_xmitpkt(vap, m);
+	}
 }

Modified: trunk/sys/net80211/ieee80211_hostap.h
===================================================================
--- trunk/sys/net80211/ieee80211_hostap.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_hostap.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_hostap.h 241138 2012-10-02 17:45:19Z adrian $
  */
 #ifndef _NET80211_IEEE80211_HOSTAP_H_
 #define _NET80211_IEEE80211_HOSTAP_H_
@@ -32,4 +33,10 @@
  */
 void	ieee80211_hostap_attach(struct ieee80211com *);
 void	ieee80211_hostap_detach(struct ieee80211com *);
+
+/*
+ * This method can be overridden
+ */
+void ieee80211_recv_pspoll(struct ieee80211_node *, struct mbuf *);
+
 #endif /* !_NET80211_IEEE80211_HOSTAP_H_ */

Modified: trunk/sys/net80211/ieee80211_ht.c
===================================================================
--- trunk/sys/net80211/ieee80211_ht.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ht.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_ht.c 314667 2017-03-04 13:03:31Z avg $");
 #endif
 
 /*
@@ -154,7 +155,7 @@
 	&ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
 	"ADDBA request backoff (ms)");
 static	int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */
-SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
 	&ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
 
 static	int ieee80211_bar_timeout = -1;	/* timeout waiting for BAR response */
@@ -1023,8 +1024,13 @@
 ieee80211_ht_node_init(struct ieee80211_node *ni)
 {
 	struct ieee80211_tx_ampdu *tap;
-	int ac;
+	int tid;
 
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+	    ni,
+	    "%s: called",
+	    __func__);
+
 	if (ni->ni_flags & IEEE80211_NODE_HT) {
 		/*
 		 * Clean AMPDU state on re-associate.  This handles the case
@@ -1031,11 +1037,15 @@
 		 * where a station leaves w/o notifying us and then returns
 		 * before node is reaped for inactivity.
 		 */
+		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+		    ni,
+		    "%s: calling cleanup",
+		    __func__);
 		ieee80211_ht_node_cleanup(ni);
 	}
-	for (ac = 0; ac < WME_NUM_AC; ac++) {
-		tap = &ni->ni_tx_ampdu[ac];
-		tap->txa_ac = ac;
+	for (tid = 0; tid < WME_NUM_TID; tid++) {
+		tap = &ni->ni_tx_ampdu[tid];
+		tap->txa_tid = tid;
 		tap->txa_ni = ni;
 		/* NB: further initialization deferred */
 	}
@@ -1052,10 +1062,15 @@
 	struct ieee80211com *ic = ni->ni_ic;
 	int i;
 
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+	    ni,
+	    "%s: called",
+	    __func__);
+
 	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
 
 	/* XXX optimize this */
-	for (i = 0; i < WME_NUM_AC; i++) {
+	for (i = 0; i < WME_NUM_TID; i++) {
 		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
 		if (tap->txa_flags & IEEE80211_AGGR_SETUP)
 			ampdu_tx_stop(tap);
@@ -1160,7 +1175,7 @@
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_tx_ampdu *tap;
-	int ac;
+	int tid;
 
 	KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested"));
 
@@ -1198,9 +1213,9 @@
 	ni->ni_htopmode = 0;		/* XXX need protection state */
 	ni->ni_htstbc = 0;		/* XXX need info */
 
-	for (ac = 0; ac < WME_NUM_AC; ac++) {
-		tap = &ni->ni_tx_ampdu[ac];
-		tap->txa_ac = ac;
+	for (tid = 0; tid < WME_NUM_TID; tid++) {
+		tap = &ni->ni_tx_ampdu[tid];
+		tap->txa_tid = tid;
 	}
 	/* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */
 	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
@@ -1428,12 +1443,13 @@
  * required channel change is done (e.g. in sta mode when
  * parsing the contents of a beacon frame).
  */
-static void
+static int
 htinfo_update_chw(struct ieee80211_node *ni, int htflags)
 {
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_channel *c;
 	int chanflags;
+	int ret = 0;
 
 	chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
 	if (chanflags != ni->ni_chan->ic_flags) {
@@ -1460,11 +1476,13 @@
 			    IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
 			    c->ic_freq, c->ic_flags);
 			ni->ni_chan = c;
+			ret = 1;
 		}
 		/* NB: caller responsible for forcing any channel change */
 	}
 	/* update node's tx channel width */
 	ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20;
+	return (ret);
 }
 
 /*
@@ -1515,7 +1533,7 @@
  * Parse and update HT-related state extracted from
  * the HT cap and info ie's.
  */
-void
+int
 ieee80211_ht_updateparams(struct ieee80211_node *ni,
 	const uint8_t *htcapie, const uint8_t *htinfoie)
 {
@@ -1522,6 +1540,7 @@
 	struct ieee80211vap *vap = ni->ni_vap;
 	const struct ieee80211_ie_htinfo *htinfo;
 	int htflags;
+	int ret = 0;
 
 	ieee80211_parse_htcap(ni, htcapie);
 	if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS)
@@ -1543,7 +1562,8 @@
 		else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
 			htflags = IEEE80211_CHAN_HT40D;
 	}
-	htinfo_update_chw(ni, htflags);
+	if (htinfo_update_chw(ni, htflags))
+		ret = 1;
 
 	if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) &&
 	    (vap->iv_flags_ht & IEEE80211_FHT_RIFS))
@@ -1550,6 +1570,8 @@
 		ni->ni_flags |= IEEE80211_NODE_RIFS;
 	else
 		ni->ni_flags &= ~IEEE80211_NODE_RIFS;
+
+	return (ret);
 }
 
 /*
@@ -1578,7 +1600,7 @@
 		else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan))
 			htflags = IEEE80211_CHAN_HT40D;
 	}
-	htinfo_update_chw(ni, htflags);
+	(void) htinfo_update_chw(ni, htflags);
 }
 
 /*
@@ -1667,7 +1689,7 @@
 static void
 ampdu_tx_setup(struct ieee80211_tx_ampdu *tap)
 {
-	callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
+	callout_init(&tap->txa_timer, 1);
 	tap->txa_flags |= IEEE80211_AGGR_SETUP;
 }
 
@@ -1677,8 +1699,14 @@
 	struct ieee80211_node *ni = tap->txa_ni;
 	struct ieee80211com *ic = ni->ni_ic;
 
+	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+	    tap->txa_ni,
+	    "%s: called",
+	    __func__);
+
 	KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP,
-	    ("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac));
+	    ("txa_flags 0x%x tid %d ac %d", tap->txa_flags, tap->txa_tid,
+	    TID_TO_WME_AC(tap->txa_tid)));
 
 	/*
 	 * Stop BA stream if setup so driver has a chance
@@ -1891,7 +1919,7 @@
 	struct ieee80211_tx_ampdu *tap;
 	uint8_t dialogtoken, policy;
 	uint16_t baparamset, batimeout, code;
-	int tid, ac, bufsiz;
+	int tid, bufsiz;
 
 	dialogtoken = frm[2];
 	code = LE_READ_2(frm+3);
@@ -1901,8 +1929,7 @@
 	policy = MS(baparamset, IEEE80211_BAPS_POLICY);
 	batimeout = LE_READ_2(frm+7);
 
-	ac = TID_TO_WME_AC(tid);
-	tap = &ni->ni_tx_ampdu[ac];
+	tap = &ni->ni_tx_ampdu[tid];
 	if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
 		IEEE80211_DISCARD_MAC(vap,
 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
@@ -1965,7 +1992,7 @@
 	struct ieee80211_rx_ampdu *rap;
 	struct ieee80211_tx_ampdu *tap;
 	uint16_t baparamset, code;
-	int tid, ac;
+	int tid;
 
 	baparamset = LE_READ_2(frm+2);
 	code = LE_READ_2(frm+4);
@@ -1978,8 +2005,7 @@
 	    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
 
 	if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
-		ac = TID_TO_WME_AC(tid);
-		tap = &ni->ni_tx_ampdu[ac];
+		tap = &ni->ni_tx_ampdu[tid];
 		ic->ic_addba_stop(ni, tap);
 	} else {
 		rap = &ni->ni_rx_ampdu[tid];
@@ -2051,7 +2077,8 @@
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 
-	if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac])
+	if (tap->txa_avgpps <
+	    vap->iv_ampdu_mintraffic[TID_TO_WME_AC(tap->txa_tid)])
 		return 0;
 	/* XXX check rssi? */
 	if (tap->txa_attempts >= ieee80211_addba_maxtries &&
@@ -2064,8 +2091,9 @@
 		return 0;
 	}
 	IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
-	    "enable AMPDU on %s, avgpps %d pkts %d",
-	    ieee80211_wme_acnames[tap->txa_ac], tap->txa_avgpps, tap->txa_pkts);
+	    "enable AMPDU on tid %d (%s), avgpps %d pkts %d",
+	    tap->txa_tid, ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)],
+	    tap->txa_avgpps, tap->txa_pkts);
 	return 1;
 }
 
@@ -2092,7 +2120,7 @@
 	tap->txa_flags &= ~IEEE80211_AGGR_NAK;
 
 	dialogtoken = (tokens+1) % 63;		/* XXX */
-	tid = WME_AC_TO_TID(tap->txa_ac);
+	tid = tap->txa_tid;
 	tap->txa_start = ni->ni_txseqs[tid];
 
 	args[0] = dialogtoken;
@@ -2106,8 +2134,8 @@
 	if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) {
 		/* unable to setup state, don't make request */
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
-		    ni, "%s: could not setup BA stream for AC %d",
-		    __func__, tap->txa_ac);
+		    ni, "%s: could not setup BA stream for TID %d AC %d",
+		    __func__, tap->txa_tid, TID_TO_WME_AC(tap->txa_tid));
 		/* defer next try so we don't slam the driver with requests */
 		tap->txa_attempts = ieee80211_addba_maxtries;
 		/* NB: check in case driver wants to override */
@@ -2140,12 +2168,12 @@
 	tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
 	if (IEEE80211_AMPDU_RUNNING(tap)) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-		    ni, "%s: stop BA stream for AC %d (reason %d)",
-		    __func__, tap->txa_ac, reason);
+		    ni, "%s: stop BA stream for TID %d (reason %d)",
+		    __func__, tap->txa_tid, reason);
 		vap->iv_stats.is_ampdu_stop++;
 
 		ic->ic_addba_stop(ni, tap);
-		args[0] = WME_AC_TO_TID(tap->txa_ac);
+		args[0] = tap->txa_tid;
 		args[1] = IEEE80211_DELBAPS_INIT;
 		args[2] = reason;			/* XXX reason code */
 		ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
@@ -2152,12 +2180,15 @@
 			IEEE80211_ACTION_BA_DELBA, args);
 	} else {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-		    ni, "%s: BA stream for AC %d not running (reason %d)",
-		    __func__, tap->txa_ac, reason);
+		    ni, "%s: BA stream for TID %d not running (reason %d)",
+		    __func__, tap->txa_tid, reason);
 		vap->iv_stats.is_ampdu_stop_failed++;
 	}
 }
 
+/* XXX */
+static void bar_start_timer(struct ieee80211_tx_ampdu *tap);
+
 static void
 bar_timeout(void *arg)
 {
@@ -2169,21 +2200,54 @@
 
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
 	    ni, "%s: tid %u flags 0x%x attempts %d", __func__,
-	    tap->txa_ac, tap->txa_flags, tap->txa_attempts);
+	    tap->txa_tid, tap->txa_flags, tap->txa_attempts);
 
 	/* guard against race with bar_tx_complete */
 	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
 		return;
 	/* XXX ? */
-	if (tap->txa_attempts >= ieee80211_bar_maxtries)
+	if (tap->txa_attempts >= ieee80211_bar_maxtries) {
+		struct ieee80211com *ic = ni->ni_ic;
+
+		ni->ni_vap->iv_stats.is_ampdu_bar_tx_fail++;
+		/*
+		 * If (at least) the last BAR TX timeout was due to
+		 * an ieee80211_send_bar() failures, then we need
+		 * to make sure we notify the driver that a BAR
+		 * TX did occur and fail.  This gives the driver
+		 * a chance to undo any queue pause that may
+		 * have occured.
+		 */
+		ic->ic_bar_response(ni, tap, 1);
 		ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT);
-	else
-		ieee80211_send_bar(ni, tap, tap->txa_seqpending);
+	} else {
+		ni->ni_vap->iv_stats.is_ampdu_bar_tx_retry++;
+		if (ieee80211_send_bar(ni, tap, tap->txa_seqpending) != 0) {
+			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+			    ni, "%s: failed to TX, starting timer\n",
+			    __func__);
+			/*
+			 * If ieee80211_send_bar() fails here, the
+			 * timer may have stopped and/or the pending
+			 * flag may be clear.  Because of this,
+			 * fake the BARPEND and reset the timer.
+			 * A retransmission attempt will then occur
+			 * during the next timeout.
+			 */
+			/* XXX locking */
+			tap->txa_flags |= IEEE80211_AGGR_BARPEND;
+			bar_start_timer(tap);
+		}
+	}
 }
 
 static void
 bar_start_timer(struct ieee80211_tx_ampdu *tap)
 {
+	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+	    tap->txa_ni,
+	    "%s: called",
+	    __func__);
 	callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap);
 }
 
@@ -2190,6 +2254,10 @@
 static void
 bar_stop_timer(struct ieee80211_tx_ampdu *tap)
 {
+	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+	    tap->txa_ni,
+	    "%s: called",
+	    __func__);
 	callout_stop(&tap->txa_timer);
 }
 
@@ -2200,9 +2268,10 @@
 
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
 	    ni, "%s: tid %u flags 0x%x pending %d status %d",
-	    __func__, tap->txa_ac, tap->txa_flags,
+	    __func__, tap->txa_tid, tap->txa_flags,
 	    callout_pending(&tap->txa_timer), status);
 
+	ni->ni_vap->iv_stats.is_ampdu_bar_tx++;
 	/* XXX locking */
 	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) &&
 	    callout_pending(&tap->txa_timer)) {
@@ -2220,6 +2289,10 @@
 	struct ieee80211_tx_ampdu *tap, int status)
 {
 
+	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+	    tap->txa_ni,
+	    "%s: called",
+	    __func__);
 	if (status == 0) {		/* got ACK */
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
 		    ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u",
@@ -2226,7 +2299,7 @@
 		    tap->txa_start,
 		    IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1),
 		    tap->txa_qframes, tap->txa_seqpending,
-		    WME_AC_TO_TID(tap->txa_ac));
+		    tap->txa_tid);
 
 		/* NB: timer already stopped in bar_tx_complete */
 		tap->txa_start = tap->txa_seqpending;
@@ -2254,6 +2327,12 @@
 	uint8_t *frm;
 	int tid, ret;
 
+
+	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+	    tap->txa_ni,
+	    "%s: called",
+	    __func__);
+
 	if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) {
 		/* no ADDBA response, should not happen */
 		/* XXX stat+msg */
@@ -2281,7 +2360,7 @@
 	IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr);
 	IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr);
 
-	tid = WME_AC_TO_TID(tap->txa_ac);
+	tid = tap->txa_tid;
 	barctl 	= (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
 			0 : IEEE80211_BAR_NOACK)
 		| IEEE80211_BAR_COMP
@@ -2314,10 +2393,16 @@
 	 * ic_raw_xmit will free the node reference
 	 * regardless of queue/TX success or failure.
 	 */
-	ret = ic->ic_raw_xmit(ni, m, NULL);
+	IEEE80211_TX_LOCK(ic);
+	ret = ieee80211_raw_output(vap, ni, m, NULL);
+	IEEE80211_TX_UNLOCK(ic);
 	if (ret != 0) {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N,
+		    ni, "send BAR: failed: (ret = %d)\n",
+		    ret);
 		/* xmit failed, clear state flag */
 		tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
+		vap->iv_stats.is_ampdu_bar_tx_fail++;
 		return ret;
 	}
 	/* XXX hack against tx complete happening before timer is started */
@@ -2325,6 +2410,11 @@
 		bar_start_timer(tap);
 	return 0;
 bad:
+	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+	    tap->txa_ni,
+	    "%s: bad! ret=%d",
+	    __func__, ret);
+	vap->iv_stats.is_ampdu_bar_tx_fail++;
 	ieee80211_free_node(ni);
 	return ret;
 #undef senderr
@@ -2684,11 +2774,15 @@
 	struct ieee80211_beacon_offsets *bo)
 {
 #define	PROTMODE	(IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
-	const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan;
+	struct ieee80211_node *ni;
+	const struct ieee80211_channel *bsschan;
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ieee80211_ie_htinfo *ht =
 	   (struct ieee80211_ie_htinfo *) bo->bo_htinfo;
 
+	ni = ieee80211_ref_node(vap->iv_bss);
+	bsschan = ni->ni_chan;
+
 	/* XXX only update on channel change */
 	ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
 	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
@@ -2707,6 +2801,8 @@
 	/* protection mode */
 	ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
 
+	ieee80211_free_node(ni);
+
 	/* XXX propagate to vendor ie's */
 #undef PROTMODE
 }

Modified: trunk/sys/net80211/ieee80211_ht.h
===================================================================
--- trunk/sys/net80211/ieee80211_ht.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ht.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_ht.h 234324 2012-04-15 20:29:39Z adrian $
  */
 #ifndef _NET80211_IEEE80211_HT_H_
 #define _NET80211_IEEE80211_HT_H_
@@ -44,7 +45,7 @@
 #define	IEEE80211_AGGR_SETUP		0x0008	/* deferred state setup */
 #define	IEEE80211_AGGR_NAK		0x0010	/* peer NAK'd ADDBA request */
 #define	IEEE80211_AGGR_BARPEND		0x0020	/* BAR response pending */
-	uint8_t		txa_ac;
+	uint8_t		txa_tid;
 	uint8_t		txa_token;	/* dialog token */
 	int		txa_lastsample;	/* ticks @ last traffic sample */
 	int		txa_pkts;	/* packets over last sample interval */
@@ -184,7 +185,7 @@
 void	ieee80211_ht_timeout(struct ieee80211com *);
 void	ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
 void	ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
-void	ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *,
+int	ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *,
 		const uint8_t *);
 void	ieee80211_ht_updatehtcap(struct ieee80211_node *, const uint8_t *);
 int	ieee80211_ampdu_request(struct ieee80211_node *,

Modified: trunk/sys/net80211/ieee80211_hwmp.c
===================================================================
--- trunk/sys/net80211/ieee80211_hwmp.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_hwmp.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*- 
  * Copyright (c) 2009 The FreeBSD Foundation 
  * All rights reserved. 
@@ -28,12 +29,12 @@
  */ 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_hwmp.c 314667 2017-03-04 13:03:31Z avg $");
 #endif
 
 /*
  * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
- * 
+ *
  * Based on March 2009, D3.0 802.11s draft spec.
  */
 #include "opt_inet.h"
@@ -40,8 +41,8 @@
 #include "opt_wlan.h"
 
 #include <sys/param.h>
-#include <sys/systm.h> 
-#include <sys/mbuf.h>   
+#include <sys/systm.h>
+#include <sys/mbuf.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
 
@@ -68,9 +69,8 @@
 static void	hwmp_vdetach(struct ieee80211vap *);
 static int	hwmp_newstate(struct ieee80211vap *,
 		    enum ieee80211_state, int);
-static int	hwmp_send_action(struct ieee80211_node *,
+static int	hwmp_send_action(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN],
-		    const uint8_t [IEEE80211_ADDR_LEN],
 		    uint8_t *, size_t);
 static uint8_t * hwmp_add_meshpreq(uint8_t *,
 		    const struct ieee80211_meshpreq_ie *);
@@ -86,30 +86,30 @@
 static void	hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
 		    const struct ieee80211_frame *,
 		    const struct ieee80211_meshpreq_ie *);
-static int	hwmp_send_preq(struct ieee80211_node *,
+static int	hwmp_send_preq(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN],
-		    const uint8_t [IEEE80211_ADDR_LEN],
-		    struct ieee80211_meshpreq_ie *);
+		    struct ieee80211_meshpreq_ie *,
+		    struct timeval *, struct timeval *);
 static void	hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
 		    const struct ieee80211_frame *,
 		    const struct ieee80211_meshprep_ie *);
-static int	hwmp_send_prep(struct ieee80211_node *,
+static int	hwmp_send_prep(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN],
-		    const uint8_t [IEEE80211_ADDR_LEN],
 		    struct ieee80211_meshprep_ie *);
 static void	hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
 		    const struct ieee80211_frame *,
 		    const struct ieee80211_meshperr_ie *);
-static int	hwmp_send_perr(struct ieee80211_node *,
+static int	hwmp_send_perr(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN],
+		    struct ieee80211_meshperr_ie *);
+static void	hwmp_senderror(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN],
-		    struct ieee80211_meshperr_ie *);
+		    struct ieee80211_mesh_route *, int);
 static void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
 		   const struct ieee80211_frame *,
 		   const struct ieee80211_meshrann_ie *);
-static int	hwmp_send_rann(struct ieee80211_node *,
+static int	hwmp_send_rann(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN],
-		    const uint8_t [IEEE80211_ADDR_LEN],
 		    struct ieee80211_meshrann_ie *);
 static struct ieee80211_node *
 		hwmp_discover(struct ieee80211vap *,
@@ -139,9 +139,12 @@
 typedef uint32_t ieee80211_hwmp_seq;
 #define	HWMP_SEQ_LT(a, b)	((int32_t)((a)-(b)) < 0)
 #define	HWMP_SEQ_LEQ(a, b)	((int32_t)((a)-(b)) <= 0)
+#define	HWMP_SEQ_EQ(a, b)	((int32_t)((a)-(b)) == 0)
 #define	HWMP_SEQ_GT(a, b)	((int32_t)((a)-(b)) > 0)
 #define	HWMP_SEQ_GEQ(a, b)	((int32_t)((a)-(b)) >= 0)
 
+#define HWMP_SEQ_MAX(a, b)	(a > b ? a : b)
+
 /*
  * Private extension of ieee80211_mesh_route.
  */
@@ -149,14 +152,16 @@
 	ieee80211_hwmp_seq	hr_seq;		/* last HWMP seq seen from dst*/
 	ieee80211_hwmp_seq	hr_preqid;	/* last PREQ ID seen from dst */
 	ieee80211_hwmp_seq	hr_origseq;	/* seq. no. on our latest PREQ*/
-	int			hr_preqretries;
+	struct timeval		hr_lastpreq;	/* last time we sent a PREQ */
+	struct timeval		hr_lastrootconf; /* last sent PREQ root conf */
+	int			hr_preqretries;	/* number of discoveries */
+	int			hr_lastdiscovery; /* last discovery in ticks */
 };
 struct ieee80211_hwmp_state {
 	ieee80211_hwmp_seq	hs_seq;		/* next seq to be used */
 	ieee80211_hwmp_seq	hs_preqid;	/* next PREQ ID to be used */
-	struct timeval		hs_lastpreq;	/* last time we sent a PREQ */
+	int			hs_rootmode;	/* proactive HWMP */
 	struct timeval		hs_lastperr;	/* last time we sent a PERR */
-	int			hs_rootmode;	/* proactive HWMP */
 	struct callout		hs_roottimer;
 	uint8_t			hs_maxhops;	/* max hop count */
 };
@@ -164,15 +169,21 @@
 static SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
     "IEEE 802.11s HWMP parameters");
 static int	ieee80211_hwmp_targetonly = 0;
-SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLFLAG_RW,
     &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
-static int	ieee80211_hwmp_replyforward = 1;
-SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
-    &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
 static int	ieee80211_hwmp_pathtimeout = -1;
 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW,
     &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
     "path entry lifetime (ms)");
+static int	ieee80211_hwmp_maxpreq_retries = -1;
+SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, maxpreq_retries, CTLTYPE_INT | CTLFLAG_RW,
+    &ieee80211_hwmp_maxpreq_retries, 0, ieee80211_sysctl_msecs_ticks, "I",
+    "maximum number of preq retries");
+static int	ieee80211_hwmp_net_diameter_traversaltime = -1;
+SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, net_diameter_traversal_time,
+    CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_net_diameter_traversaltime, 0,
+    ieee80211_sysctl_msecs_ticks, "I",
+    "estimate travelse time across the MBSS (ms)");
 static int	ieee80211_hwmp_roottimeout = -1;
 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW,
     &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
@@ -185,6 +196,11 @@
 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
     &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I",
     "root announcement interval (ms)");
+static struct timeval ieee80211_hwmp_rootconfint = { 0, 0 };
+static int	ieee80211_hwmp_rootconfint_internal = -1;
+SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootconfint, CTLTYPE_INT | CTLFLAG_RD,
+    &ieee80211_hwmp_rootconfint_internal, 0, ieee80211_sysctl_msecs_ticks, "I",
+    "root confirmation interval (ms) (read-only)");
 
 #define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
 
@@ -195,6 +211,7 @@
 	.mpp_ie		= IEEE80211_MESHCONF_PATH_HWMP,
 	.mpp_discover	= hwmp_discover,
 	.mpp_peerdown	= hwmp_peerdown,
+	.mpp_senderror	= hwmp_senderror,
 	.mpp_vattach	= hwmp_vattach,
 	.mpp_vdetach	= hwmp_vdetach,
 	.mpp_newstate	= hwmp_newstate,
@@ -208,16 +225,31 @@
 static void
 ieee80211_hwmp_init(void)
 {
+	/* Default values as per amendment */
 	ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000);
 	ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000);
 	ieee80211_hwmp_rootint = msecs_to_ticks(2*1000);
 	ieee80211_hwmp_rannint = msecs_to_ticks(1*1000);
+	ieee80211_hwmp_rootconfint_internal = msecs_to_ticks(2*1000);
+	ieee80211_hwmp_maxpreq_retries = 3;
+	/*
+	 * (TU): A measurement of time equal to 1024 μs,
+	 * 500 TU is 512 ms.
+	 */
+	ieee80211_hwmp_net_diameter_traversaltime = msecs_to_ticks(512);
 
 	/*
+	 * NB: I dont know how to make SYSCTL_PROC that calls ms to ticks
+	 * and return a struct timeval...
+	 */
+	ieee80211_hwmp_rootconfint.tv_usec =
+	    ieee80211_hwmp_rootconfint_internal * 1000;
+
+	/*
 	 * Register action frame handler.
 	 */
-	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
-	    IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_HWMP, hwmp_recv_action_meshpath);
 
 	/* NB: default is 5 secs per spec */
 	mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
@@ -244,7 +276,7 @@
 		return;
 	}
 	hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
-	callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE);
+	callout_init(&hs->hs_roottimer, 1);
 	vap->iv_hwmp = hs;
 }
 
@@ -275,17 +307,114 @@
 	return 0;
 }
 
+/*
+ * Verify the length of an HWMP PREQ and return the number
+ * of destinations >= 1, if verification fails -1 is returned.
+ */
 static int
+verify_mesh_preq_len(struct ieee80211vap *vap,
+    const struct ieee80211_frame *wh, const uint8_t *iefrm)
+{
+	int alloc_sz = -1;
+	int ndest = -1;
+	if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) {
+		/* Originator External Address  present */
+		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ_AE;
+		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE];
+	} else {
+		/* w/o Originator External Address */
+		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ;
+		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET];
+	}
+	alloc_sz += ndest * IEEE80211_MESHPREQ_TRGT_SZ;
+
+	if(iefrm[1] != (alloc_sz)) {
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+		    wh, NULL, "PREQ (AE=%s) with wrong len",
+		    iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE ? "1" : "0");
+		return (-1);
+	}
+	return ndest;
+}
+
+/*
+ * Verify the length of an HWMP PREP and returns 1 on success,
+ * otherwise -1.
+ */
+static int
+verify_mesh_prep_len(struct ieee80211vap *vap,
+    const struct ieee80211_frame *wh, const uint8_t *iefrm)
+{
+	int alloc_sz = -1;
+	if (iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE) {
+		if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ_AE)
+			alloc_sz = IEEE80211_MESHPREP_BASE_SZ_AE;
+	} else if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ)
+		alloc_sz = IEEE80211_MESHPREP_BASE_SZ;
+	if(alloc_sz < 0) {
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+		    wh, NULL, "PREP (AE=%s) with wrong len",
+		    iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE ? "1" : "0");
+		return (-1);
+	}
+	return (1);
+}
+
+/*
+ * Verify the length of an HWMP PERR and return the number
+ * of destinations >= 1, if verification fails -1 is returned.
+ */
+static int
+verify_mesh_perr_len(struct ieee80211vap *vap,
+    const struct ieee80211_frame *wh, const uint8_t *iefrm)
+{
+	int alloc_sz = -1;
+	const uint8_t *iefrm_t = iefrm;
+	uint8_t ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET];
+	int i;
+
+	if(ndest > IEEE80211_MESHPERR_MAXDEST) {
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+		    wh, NULL, "PERR with wrong number of destionat (>19), %u",
+		    ndest);
+		return (-1);
+	}
+
+	iefrm_t += IEEE80211_MESHPERR_NDEST_OFFSET + 1; /* flag is next field */
+	/* We need to check each destionation flag to know size */
+	for(i = 0; i<ndest; i++) {
+		if ((*iefrm_t) & IEEE80211_MESHPERR_FLAGS_AE)
+			iefrm_t += IEEE80211_MESHPERR_DEST_SZ_AE;
+		else
+			iefrm_t += IEEE80211_MESHPERR_DEST_SZ;
+	}
+
+	alloc_sz = (iefrm_t - iefrm) - 2; /* action + code */
+	if(alloc_sz !=  iefrm[1]) {
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+		    wh, NULL, "%s", "PERR with wrong len");
+		return (-1);
+	}
+	return ndest;
+}
+
+static int
 hwmp_recv_action_meshpath(struct ieee80211_node *ni,
 	const struct ieee80211_frame *wh,
 	const uint8_t *frm, const uint8_t *efrm)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
-	struct ieee80211_meshpreq_ie preq;
-	struct ieee80211_meshprep_ie prep;
-	struct ieee80211_meshperr_ie perr;
+	struct ieee80211_meshpreq_ie *preq;
+	struct ieee80211_meshprep_ie *prep;
+	struct ieee80211_meshperr_ie *perr;
 	struct ieee80211_meshrann_ie rann;
 	const uint8_t *iefrm = frm + 2; /* action + code */
+	const uint8_t *iefrm_t = iefrm; /* temporary pointer */
+	int ndest = -1;
 	int found = 0;
 
 	while (efrm - iefrm > 1) {
@@ -293,66 +422,132 @@
 		switch (*iefrm) {
 		case IEEE80211_ELEMID_MESHPREQ:
 		{
-			const struct ieee80211_meshpreq_ie *mpreq =
-			    (const struct ieee80211_meshpreq_ie *) iefrm;
-			/* XXX > 1 target */
-			if (mpreq->preq_len !=
-			    sizeof(struct ieee80211_meshpreq_ie) - 2) {
-				IEEE80211_DISCARD(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
-				    wh, NULL, "%s", "PREQ with wrong len");
+			int i = 0;
+
+			iefrm_t = iefrm;
+			ndest = verify_mesh_preq_len(vap, wh, iefrm_t);
+			if (ndest < 0) {
 				vap->iv_stats.is_rx_mgtdiscard++;
 				break;
 			}
-			memcpy(&preq, mpreq, sizeof(preq));
-			preq.preq_id = LE_READ_4(&mpreq->preq_id);
-			preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq);
-			preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime);
-			preq.preq_metric = LE_READ_4(&mpreq->preq_metric);
-			preq.preq_targets[0].target_seq =
-			    LE_READ_4(&mpreq->preq_targets[0].target_seq);
-			hwmp_recv_preq(vap, ni, wh, &preq);
+			preq = malloc(sizeof(*preq) +
+			    (ndest - 1) * sizeof(*preq->preq_targets),
+			    M_80211_MESH_PREQ, M_NOWAIT | M_ZERO);
+			KASSERT(preq != NULL, ("preq == NULL"));
+
+			preq->preq_ie = *iefrm_t++;
+			preq->preq_len = *iefrm_t++;
+			preq->preq_flags = *iefrm_t++;
+			preq->preq_hopcount = *iefrm_t++;
+			preq->preq_ttl = *iefrm_t++;
+			preq->preq_id = LE_READ_4(iefrm_t); iefrm_t += 4;
+			IEEE80211_ADDR_COPY(preq->preq_origaddr, iefrm_t);
+			iefrm_t += 6;
+			preq->preq_origseq = LE_READ_4(iefrm_t); iefrm_t += 4;
+			/* NB: may have Originator Proxied Address */
+			if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE)  {
+				IEEE80211_ADDR_COPY(
+				    preq->preq_orig_ext_addr, iefrm_t);
+				iefrm_t += 6;
+			}
+			preq->preq_lifetime = LE_READ_4(iefrm_t); iefrm_t += 4;
+			preq->preq_metric = LE_READ_4(iefrm_t); iefrm_t += 4;
+			preq->preq_tcount = *iefrm_t++;
+			
+			for (i = 0; i < preq->preq_tcount; i++) {
+				preq->preq_targets[i].target_flags = *iefrm_t++;
+				IEEE80211_ADDR_COPY(
+				    preq->preq_targets[i].target_addr, iefrm_t);
+				iefrm_t += 6;
+				preq->preq_targets[i].target_seq =
+				    LE_READ_4(iefrm_t);
+				iefrm_t += 4;
+			}
+
+			hwmp_recv_preq(vap, ni, wh, preq);
+			free(preq, M_80211_MESH_PREQ);
 			found++;
-			break;	
+			break;
 		}
 		case IEEE80211_ELEMID_MESHPREP:
 		{
-			const struct ieee80211_meshprep_ie *mprep =
-			    (const struct ieee80211_meshprep_ie *) iefrm;
-			if (mprep->prep_len !=
-			    sizeof(struct ieee80211_meshprep_ie) - 2) {
-				IEEE80211_DISCARD(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
-				    wh, NULL, "%s", "PREP with wrong len");
+			iefrm_t = iefrm;
+			ndest = verify_mesh_prep_len(vap, wh, iefrm_t);
+			if (ndest < 0) {
 				vap->iv_stats.is_rx_mgtdiscard++;
 				break;
 			}
-			memcpy(&prep, mprep, sizeof(prep));
-			prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq);
-			prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime);
-			prep.prep_metric = LE_READ_4(&mprep->prep_metric);
-			prep.prep_origseq = LE_READ_4(&mprep->prep_origseq);
-			hwmp_recv_prep(vap, ni, wh, &prep);
+			prep = malloc(sizeof(*prep),
+			    M_80211_MESH_PREP, M_NOWAIT | M_ZERO);
+			KASSERT(prep != NULL, ("prep == NULL"));
+
+			prep->prep_ie = *iefrm_t++;
+			prep->prep_len = *iefrm_t++;
+			prep->prep_flags = *iefrm_t++;
+			prep->prep_hopcount = *iefrm_t++;
+			prep->prep_ttl = *iefrm_t++;
+			IEEE80211_ADDR_COPY(prep->prep_targetaddr, iefrm_t);
+			iefrm_t += 6;
+			prep->prep_targetseq = LE_READ_4(iefrm_t); iefrm_t += 4;
+			/* NB: May have Target Proxied Address */
+			if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE)  {
+				IEEE80211_ADDR_COPY(
+				    prep->prep_target_ext_addr, iefrm_t);
+				iefrm_t += 6;
+			}
+			prep->prep_lifetime = LE_READ_4(iefrm_t); iefrm_t += 4;
+			prep->prep_metric = LE_READ_4(iefrm_t); iefrm_t += 4;
+			IEEE80211_ADDR_COPY(prep->prep_origaddr, iefrm_t);
+			iefrm_t += 6;
+			prep->prep_origseq = LE_READ_4(iefrm_t); iefrm_t += 4;
+
+			hwmp_recv_prep(vap, ni, wh, prep);
+			free(prep, M_80211_MESH_PREP);
 			found++;
 			break;
 		}
 		case IEEE80211_ELEMID_MESHPERR:
 		{
-			const struct ieee80211_meshperr_ie *mperr =
-			    (const struct ieee80211_meshperr_ie *) iefrm;
-			/* XXX > 1 target */
-			if (mperr->perr_len !=
-			    sizeof(struct ieee80211_meshperr_ie) - 2) {
-				IEEE80211_DISCARD(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
-				    wh, NULL, "%s", "PERR with wrong len");
+			int i = 0;
+
+			iefrm_t = iefrm;
+			ndest = verify_mesh_perr_len(vap, wh, iefrm_t);
+			if (ndest < 0) {
 				vap->iv_stats.is_rx_mgtdiscard++;
 				break;
 			}
-			memcpy(&perr, mperr, sizeof(perr));
-			perr.perr_dests[0].dest_seq =
-			    LE_READ_4(&mperr->perr_dests[0].dest_seq);
-			hwmp_recv_perr(vap, ni, wh, &perr);
+			perr = malloc(sizeof(*perr) +
+			    (ndest - 1) * sizeof(*perr->perr_dests),
+			    M_80211_MESH_PERR, M_NOWAIT | M_ZERO);
+			KASSERT(perr != NULL, ("perr == NULL"));
+
+			perr->perr_ie = *iefrm_t++;
+			perr->perr_len = *iefrm_t++;
+			perr->perr_ttl = *iefrm_t++;
+			perr->perr_ndests = *iefrm_t++;
+
+			for (i = 0; i<perr->perr_ndests; i++) {
+				perr->perr_dests[i].dest_flags = *iefrm_t++;
+				IEEE80211_ADDR_COPY(
+				    perr->perr_dests[i].dest_addr, iefrm_t);
+				iefrm_t += 6;
+				perr->perr_dests[i].dest_seq = LE_READ_4(iefrm_t);
+				iefrm_t += 4;
+				/* NB: May have Target Proxied Address */
+				if (perr->perr_dests[i].dest_flags &
+				    IEEE80211_MESHPERR_FLAGS_AE) {
+					IEEE80211_ADDR_COPY(
+					    perr->perr_dests[i].dest_ext_addr,
+					    iefrm_t);
+					iefrm_t += 6;
+				}
+				perr->perr_dests[i].dest_rcode =
+				    LE_READ_2(iefrm_t);
+				iefrm_t += 2;
+			}
+
+			hwmp_recv_perr(vap, ni, wh, perr);
+			free(perr, M_80211_MESH_PERR);
 			found++;
 			break;
 		}
@@ -365,11 +560,12 @@
 				IEEE80211_DISCARD(vap,
 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
 				    wh, NULL, "%s", "RAN with wrong len");
-				vap->iv_stats.is_rx_mgtdiscard++;
+				    vap->iv_stats.is_rx_mgtdiscard++;
 				return 1;
 			}
 			memcpy(&rann, mrann, sizeof(rann));
 			rann.rann_seq = LE_READ_4(&mrann->rann_seq);
+			rann.rann_interval = LE_READ_4(&mrann->rann_interval);
 			rann.rann_metric = LE_READ_4(&mrann->rann_metric);
 			hwmp_recv_rann(vap, ni, wh, &rann);
 			found++;
@@ -388,17 +584,31 @@
 }
 
 static int
-hwmp_send_action(struct ieee80211_node *ni,
-    const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_action(struct ieee80211vap *vap,
     const uint8_t da[IEEE80211_ADDR_LEN],
     uint8_t *ie, size_t len)
 {
-	struct ieee80211vap *vap = ni->ni_vap;
-	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_node *ni;
+	struct ieee80211com *ic;
 	struct ieee80211_bpf_params params;
 	struct mbuf *m;
 	uint8_t *frm;
+	int ret;
 
+	if (IEEE80211_IS_MULTICAST(da)) {
+		ni = ieee80211_ref_node(vap->iv_bss);
+#ifdef IEEE80211_DEBUG_REFCNT
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+		__func__, __LINE__,
+		ni, ether_sprintf(ni->ni_macaddr),
+		ieee80211_node_refcnt(ni)+1);
+#endif
+		ieee80211_ref_node(ni);
+	}
+	else
+		ni = ieee80211_mesh_find_txnode(vap, da);
+
 	if (vap->iv_state == IEEE80211_S_CAC) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
 		    "block %s frame in CAC state", "HWMP action");
@@ -407,19 +617,7 @@
 	}
 
 	KASSERT(ni != NULL, ("null node"));
-	/*
-	 * Hold a reference on the node so it doesn't go away until after
-	 * the xmit is complete all the way in the driver.  On error we
-	 * will remove our reference.
-	 */
-#ifdef IEEE80211_DEBUG_REFCNT
-	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
-	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
-	    __func__, __LINE__,
-	    ni, ether_sprintf(ni->ni_macaddr),
-	    ieee80211_node_refcnt(ni)+1);
-#endif
-	ieee80211_ref_node(ni);
+	ic = ni->ni_ic;
 
 	m = ieee80211_getmgtframe(&frm,
 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -430,8 +628,8 @@
 		vap->iv_stats.is_tx_nobuf++;
 		return ENOMEM;
 	}
-	*frm++ = IEEE80211_ACTION_CAT_MESHPATH;
-	*frm++ = IEEE80211_ACTION_MESHPATH_SEL;
+	*frm++ = IEEE80211_ACTION_CAT_MESH;
+	*frm++ = IEEE80211_ACTION_MESH_HWMP;
 	switch (*ie) {
 	case IEEE80211_ELEMID_MESHPREQ:
 		frm = hwmp_add_meshpreq(frm,
@@ -452,15 +650,18 @@
 	}
 
 	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
 	if (m == NULL) {
 		ieee80211_free_node(ni);
 		vap->iv_stats.is_tx_nobuf++;
 		return ENOMEM;
 	}
+
+	IEEE80211_TX_LOCK(ic);
+
 	ieee80211_send_setup(ni, m,
 	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
-	    IEEE80211_NONQOS_TID, sa, da, sa);
+	    IEEE80211_NONQOS_TID, vap->iv_myaddr, da, vap->iv_myaddr);
 
 	m->m_flags |= M_ENCAP;		/* mark encapsulated */
 	IEEE80211_NODE_STAT(ni, tx_mgmt);
@@ -473,7 +674,9 @@
 	else
 		params.ibp_try0 = ni->ni_txparms->maxretry;
 	params.ibp_power = ni->ni_txpower;
-	return ic->ic_raw_xmit(ni, m, &params);
+	ret = ieee80211_raw_output(vap, ni, m, &params);
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 }
 
 #define ADDSHORT(frm, v) do {		\
@@ -488,6 +691,9 @@
 /*
  * Add a Mesh Path Request IE to a frame.
  */
+#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
+#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
+#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
 static uint8_t *
 hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
 {
@@ -494,8 +700,7 @@
 	int i;
 
 	*frm++ = IEEE80211_ELEMID_MESHPREQ;
-	*frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
-	    (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
+	*frm++ = preq->preq_len;	/* len already calculated */
 	*frm++ = preq->preq_flags;
 	*frm++ = preq->preq_hopcount;
 	*frm++ = preq->preq_ttl;
@@ -502,17 +707,24 @@
 	ADDWORD(frm, preq->preq_id);
 	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
 	ADDWORD(frm, preq->preq_origseq);
+	if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
+		IEEE80211_ADDR_COPY(frm, preq->preq_orig_ext_addr);
+		frm += 6;
+	}
 	ADDWORD(frm, preq->preq_lifetime);
 	ADDWORD(frm, preq->preq_metric);
 	*frm++ = preq->preq_tcount;
 	for (i = 0; i < preq->preq_tcount; i++) {
-		*frm++ = preq->preq_targets[i].target_flags;
-		IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr);
+		*frm++ = PREQ_TFLAGS(i);
+		IEEE80211_ADDR_COPY(frm, PREQ_TADDR(i));
 		frm += 6;
-		ADDWORD(frm, preq->preq_targets[i].target_seq);
+		ADDWORD(frm, PREQ_TSEQ(i));
 	}
 	return frm;
 }
+#undef	PREQ_TFLAGS
+#undef	PREQ_TADDR
+#undef	PREQ_TSEQ
 
 /*
  * Add a Mesh Path Reply IE to a frame.
@@ -521,12 +733,16 @@
 hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
 {
 	*frm++ = IEEE80211_ELEMID_MESHPREP;
-	*frm++ = sizeof(struct ieee80211_meshprep_ie) - 2;
+	*frm++ = prep->prep_len;	/* len already calculated */
 	*frm++ = prep->prep_flags;
 	*frm++ = prep->prep_hopcount;
 	*frm++ = prep->prep_ttl;
 	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
 	ADDWORD(frm, prep->prep_targetseq);
+	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
+		IEEE80211_ADDR_COPY(frm, prep->prep_target_ext_addr);
+		frm += 6;
+	}
 	ADDWORD(frm, prep->prep_lifetime);
 	ADDWORD(frm, prep->prep_metric);
 	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
@@ -537,6 +753,11 @@
 /*
  * Add a Mesh Path Error IE to a frame.
  */
+#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
+#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
+#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
+#define	PERR_EXTADDR(n)	perr->perr_dests[n].dest_ext_addr
+#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
 static uint8_t *
 hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
 {
@@ -543,19 +764,27 @@
 	int i;
 
 	*frm++ = IEEE80211_ELEMID_MESHPERR;
-	*frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
-	    (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
+	*frm++ = perr->perr_len;	/* len already calculated */
 	*frm++ = perr->perr_ttl;
 	*frm++ = perr->perr_ndests;
 	for (i = 0; i < perr->perr_ndests; i++) {
-		*frm++ = perr->perr_dests[i].dest_flags;
-		IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr);
+		*frm++ = PERR_DFLAGS(i);
+		IEEE80211_ADDR_COPY(frm, PERR_DADDR(i));
 		frm += 6;
-		ADDWORD(frm, perr->perr_dests[i].dest_seq);
-		ADDSHORT(frm, perr->perr_dests[i].dest_rcode);
+		ADDWORD(frm, PERR_DSEQ(i));
+		if (PERR_DFLAGS(i) & IEEE80211_MESHPERR_FLAGS_AE) {
+			IEEE80211_ADDR_COPY(frm, PERR_EXTADDR(i));
+			frm += 6;
+		}
+		ADDSHORT(frm, PERR_DRCODE(i));
 	}
 	return frm;
 }
+#undef	PERR_DFLAGS
+#undef	PERR_DADDR
+#undef	PERR_DSEQ
+#undef	PERR_EXTADDR
+#undef	PERR_DRCODE
 
 /*
  * Add a Root Annoucement IE to a frame.
@@ -564,12 +793,13 @@
 hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
 {
 	*frm++ = IEEE80211_ELEMID_MESHRANN;
-	*frm++ = sizeof(struct ieee80211_meshrann_ie) - 2;
+	*frm++ = rann->rann_len;
 	*frm++ = rann->rann_flags;
 	*frm++ = rann->rann_hopcount;
 	*frm++ = rann->rann_ttl;
 	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
 	ADDWORD(frm, rann->rann_seq);
+	ADDWORD(frm, rann->rann_interval);
 	ADDWORD(frm, rann->rann_metric);
 	return frm;
 }
@@ -578,19 +808,23 @@
 hwmp_rootmode_setup(struct ieee80211vap *vap)
 {
 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 
 	switch (hs->hs_rootmode) {
 	case IEEE80211_HWMP_ROOTMODE_DISABLED:
 		callout_drain(&hs->hs_roottimer);
+		ms->ms_flags &= ~IEEE80211_MESHFLAGS_ROOT;
 		break;
 	case IEEE80211_HWMP_ROOTMODE_NORMAL:
 	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
 		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
 		    hwmp_rootmode_cb, vap);
+		ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
 		break;
 	case IEEE80211_HWMP_ROOTMODE_RANN:
 		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
 		    hwmp_rootmode_rann_cb, vap);
+		ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
 		break;
 	}
 }
@@ -613,9 +847,9 @@
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
 	    "%s", "send broadcast PREQ");
 
-	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
-	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
-		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR;
+	preq.preq_flags = 0;
+	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
+		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_GATE;
 	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
 		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
 	preq.preq_hopcount = 0;
@@ -628,10 +862,11 @@
 	preq.preq_tcount = 1;
 	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
 	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
-	    IEEE80211_MESHPREQ_TFLAGS_RF;
+	    IEEE80211_MESHPREQ_TFLAGS_USN;
 	PREQ_TSEQ(0) = 0;
 	vap->iv_stats.is_hwmp_rootreqs++;
-	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
+	/* NB: we enforce rate check ourself */
+	hwmp_send_preq(vap, broadcastaddr, &preq, NULL, NULL);
 	hwmp_rootmode_setup(vap);
 }
 #undef	PREQ_TFLAGS
@@ -654,19 +889,59 @@
 	    "%s", "send broadcast RANN");
 
 	rann.rann_flags = 0;
-	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
-		rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
+	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
+		rann.rann_flags |= IEEE80211_MESHFLAGS_GATE;
 	rann.rann_hopcount = 0;
 	rann.rann_ttl = ms->ms_ttl;
 	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
 	rann.rann_seq = ++hs->hs_seq;
+	rann.rann_interval = ieee80211_hwmp_rannint;
 	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
 
 	vap->iv_stats.is_hwmp_rootrann++;
-	hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
+	hwmp_send_rann(vap, broadcastaddr, &rann);
 	hwmp_rootmode_setup(vap);
 }
 
+/*
+ * Update forwarding information to TA if metric improves.
+ */
+static void
+hwmp_update_transmitter(struct ieee80211vap *vap, struct ieee80211_node *ni,
+    const char *hwmp_frame)
+{
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_mesh_route *rttran = NULL;	/* Transmitter */
+	int metric = 0;
+
+	rttran = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
+	if (rttran == NULL) {
+		rttran = ieee80211_mesh_rt_add(vap, ni->ni_macaddr);
+		if (rttran == NULL) {
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "unable to add path to transmitter %6D of %s",
+			    ni->ni_macaddr, ":", hwmp_frame);
+			vap->iv_stats.is_mesh_rtaddfailed++;
+			return;
+		}
+	}
+	metric = ms->ms_pmetric->mpm_metric(ni);
+	if (!(rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) ||
+	    rttran->rt_metric > metric)
+	{
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "%s path to transmiter %6D of %s, metric %d:%d",
+		    rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
+		    "prefer" : "update", ni->ni_macaddr, ":", hwmp_frame,
+		    rttran->rt_metric, metric);
+		IEEE80211_ADDR_COPY(rttran->rt_nexthop, ni->ni_macaddr);
+		rttran->rt_metric = metric;
+		rttran->rt_nhops  = 1;
+		ieee80211_mesh_rt_update(rttran, ms->ms_ppath->mpp_inact);
+		rttran->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
+	}
+}
+
 #define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
 #define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
 #define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
@@ -675,15 +950,16 @@
     const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
 {
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
-	struct ieee80211_mesh_route *rt = NULL;
 	struct ieee80211_mesh_route *rtorig = NULL;
-	struct ieee80211_hwmp_route *hrorig;
+	struct ieee80211_mesh_route *rtorig_ext = NULL;
+	struct ieee80211_mesh_route *rttarg = NULL;
+	struct ieee80211_hwmp_route *hrorig = NULL;
+	struct ieee80211_hwmp_route *hrtarg = NULL;
 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 	struct ieee80211_meshprep_ie prep;
+	ieee80211_hwmp_seq preqid;	/* last seen preqid for orig */
+	uint32_t metric = 0;
 
-	if (ni == vap->iv_bss ||
-	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
-		return;
 	/*
 	 * Ignore PREQs from us. Could happen because someone forward it
 	 * back to us.
@@ -692,104 +968,194 @@
 		return;
 
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-	    "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
+	    "received PREQ, orig %6D, targ(0) %6D", preq->preq_origaddr, ":",
+	    PREQ_TADDR(0), ":");
 
 	/*
-	 * Acceptance criteria: if the PREQ is not for us and
-	 * forwarding is disabled, discard this PREQ.
+	 * Acceptance criteria: (if the PREQ is not for us or not broadcast,
+	 * or an external mac address not proxied by us),
+	 * AND forwarding is disabled, discard this PREQ.
 	 */
-	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
-	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
+	rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
+	if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD) &&
+	    (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
+	    !IEEE80211_IS_MULTICAST(PREQ_TADDR(0)) ||
+	    (rttarg != NULL &&
+	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
+	    IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate)))) {
 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
 		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
 		return;
 	}
+	/*
+	 * Acceptance criteria: if unicast addressed 
+	 * AND no valid forwarding for Target of PREQ, discard this PREQ.
+	 */
+	if(rttarg != NULL)
+		hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg,
+		    struct ieee80211_hwmp_route);
+	/* Address mode: ucast */
+	if(preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM &&
+	    rttarg == NULL &&
+	    !IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
+		    preq->preq_origaddr, NULL,
+		    "unicast addressed PREQ of unknown target %6D",
+		    PREQ_TADDR(0), ":");
+		return;
+	}
+
+	/* PREQ ACCEPTED */
+
 	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
-	if (rtorig == NULL)
+	if (rtorig == NULL) {
 		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
-	if (rtorig == NULL) {
-		/* XXX stat */
-		return;
+		if (rtorig == NULL) {
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "unable to add orig path to %6D",
+			    preq->preq_origaddr, ":");
+			vap->iv_stats.is_mesh_rtaddfailed++;
+			return;
+		}
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "adding originator %6D", preq->preq_origaddr, ":");
 	}
 	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
-	/*
-	 * Sequence number validation.
+
+	/* record last seen preqid */
+	preqid = hrorig->hr_preqid;
+	hrorig->hr_preqid = HWMP_SEQ_MAX(hrorig->hr_preqid, preq->preq_id);
+
+	/* Data creation and update of forwarding information
+	 * according to Table 11C-8 for originator mesh STA.
 	 */
-	if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
-	    HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
+	metric = preq->preq_metric + ms->ms_pmetric->mpm_metric(ni);
+	if (HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
+	    (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) &&
+	    metric < rtorig->rt_metric)) {
+		hrorig->hr_seq = preq->preq_origseq;
+		IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2);
+		rtorig->rt_metric = metric;
+		rtorig->rt_nhops  = preq->preq_hopcount + 1;
+		ieee80211_mesh_rt_update(rtorig, preq->preq_lifetime);
+		/* Path to orig is valid now.
+		 * NB: we know it can't be Proxy, and if it is GATE
+		 * it will be marked below.
+		 */
+		rtorig->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
+	} else if ((hrtarg != NULL &&
+	    !HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0))) ||
+	    (rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
+	    preqid >= preq->preq_id)) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "discard PREQ from %s, old seq no %u <= %u",
-		    ether_sprintf(preq->preq_origaddr),
-		    preq->preq_origseq, hrorig->hr_seq);
+		    "discard PREQ from %6D, old seqno %u <= %u,"
+		    " or old preqid %u < %u",
+		    preq->preq_origaddr, ":",
+		    preq->preq_origseq, hrorig->hr_seq,
+		    preq->preq_id, preqid);
 		return;
 	}
-	hrorig->hr_preqid = preq->preq_id;
-	hrorig->hr_seq = preq->preq_origseq;
 
+	/* Update forwarding information to TA if metric improves. */
+	hwmp_update_transmitter(vap, ni, "PREQ");
+
 	/*
 	 * Check if the PREQ is addressed to us.
+	 * or a Proxy currently gated by us.
 	 */
-	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
-		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "reply to %s", ether_sprintf(preq->preq_origaddr));
+	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
+	    (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
+	    rttarg != NULL &&
+	    IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate) &&
+	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
+	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
 		/*
-		 * Build and send a PREP frame.
+		 * When we are the target we shall update our own HWMP seq
+		 * number with max of (current and preq->seq) + 1
 		 */
+		hs->hs_seq = HWMP_SEQ_MAX(hs->hs_seq, PREQ_TSEQ(0)) + 1;
+
 		prep.prep_flags = 0;
 		prep.prep_hopcount = 0;
+		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
+		if (rttarg != NULL && /* if NULL it means we are the target */
+		    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "reply for proxy %6D", rttarg->rt_dest, ":");
+			prep.prep_flags |= IEEE80211_MESHPREP_FLAGS_AE;
+			IEEE80211_ADDR_COPY(prep.prep_target_ext_addr,
+			    rttarg->rt_dest);
+			/* update proxy seqno to HWMP seqno */
+			rttarg->rt_ext_seq = hs->hs_seq;
+			prep.prep_hopcount = rttarg->rt_nhops;
+			prep.prep_metric = rttarg->rt_metric;
+			IEEE80211_ADDR_COPY(prep.prep_targetaddr, rttarg->rt_mesh_gate);
+		}
+		/*
+		 * Build and send a PREP frame.
+		 */
 		prep.prep_ttl = ms->ms_ttl;
-		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
-		prep.prep_targetseq = ++hs->hs_seq;
+		prep.prep_targetseq = hs->hs_seq;
 		prep.prep_lifetime = preq->preq_lifetime;
-		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
 		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
 		prep.prep_origseq = preq->preq_origseq;
-		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
-		/*
-		 * Build the reverse path, if we don't have it already.
-		 */
-		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
-		if (rt == NULL)
-			hwmp_discover(vap, preq->preq_origaddr, NULL);
-		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
-			hwmp_discover(vap, rt->rt_dest, NULL);
+
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "reply to %6D", preq->preq_origaddr, ":");
+		hwmp_send_prep(vap, wh->i_addr2, &prep);
 		return;
 	}
+	/* we may update our proxy information for the orig external */
+	else if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
+		rtorig_ext =
+		    ieee80211_mesh_rt_find(vap, preq->preq_orig_ext_addr);
+		if (rtorig_ext == NULL) {
+			rtorig_ext = ieee80211_mesh_rt_add(vap,
+			    preq->preq_orig_ext_addr);
+			if (rtorig_ext == NULL) {
+				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+				    "unable to add orig ext proxy to %6D",
+				    preq->preq_orig_ext_addr, ":");
+				vap->iv_stats.is_mesh_rtaddfailed++;
+				return;
+			}
+			IEEE80211_ADDR_COPY(rtorig_ext->rt_mesh_gate,
+			    preq->preq_origaddr);
+		}
+		rtorig_ext->rt_ext_seq = preq->preq_origseq;
+		ieee80211_mesh_rt_update(rtorig_ext, preq->preq_lifetime);
+	}
 	/*
 	 * Proactive PREQ: reply with a proactive PREP to the
 	 * root STA if requested.
 	 */
 	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
-	    (PREQ_TFLAGS(0) &
-	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
-	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
-		uint8_t rootmac[IEEE80211_ADDR_LEN];
+	    (PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "root mesh station @ %6D", preq->preq_origaddr, ":");
 
-		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
-		rt = ieee80211_mesh_rt_find(vap, rootmac);
-		if (rt == NULL) {
-			rt = ieee80211_mesh_rt_add(vap, rootmac);
-			if (rt == NULL) {
-				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-				    "unable to add root mesh path to %s",
-				    ether_sprintf(rootmac));
-				vap->iv_stats.is_mesh_rtaddfailed++;
-				return;
-			}
+		/* Check if root is a mesh gate, mark it */
+		if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_GATE) {
+			struct ieee80211_mesh_gate_route *gr;
+
+			rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
+			gr = ieee80211_mesh_mark_gate(vap, preq->preq_origaddr,
+			    rtorig);
+			gr->gr_lastseq = 0; /* NOT GANN */
 		}
-		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "root mesh station @ %s", ether_sprintf(rootmac));
 
 		/*
 		 * Reply with a PREP if we don't have a path to the root
 		 * or if the root sent us a proactive PREQ.
 		 */
-		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
+		if ((rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
 		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
 			prep.prep_flags = 0;
 			prep.prep_hopcount = 0;
 			prep.prep_ttl = ms->ms_ttl;
-			IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
+			IEEE80211_ADDR_COPY(prep.prep_origaddr,
+			    preq->preq_origaddr);
 			prep.prep_origseq = preq->preq_origseq;
 			prep.prep_lifetime = preq->preq_lifetime;
 			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
@@ -796,108 +1162,70 @@
 			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
 			    vap->iv_myaddr);
 			prep.prep_targetseq = ++hs->hs_seq;
-			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
-			    broadcastaddr, &prep);
+			hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
 		}
-		hwmp_discover(vap, rootmac, NULL);
-		return;
 	}
-	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
 
 	/*
 	 * Forwarding and Intermediate reply for PREQs with 1 target.
 	 */
-	if (preq->preq_tcount == 1) {
+	if ((preq->preq_tcount == 1) && (preq->preq_ttl > 1) &&
+	    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
 		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
 
 		memcpy(&ppreq, preq, sizeof(ppreq));
+
 		/*
 		 * We have a valid route to this node.
+		 * NB: if target is proxy dont reply.
 		 */
-		if (rt != NULL &&
-		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
-			if (preq->preq_ttl > 1 &&
-			    preq->preq_hopcount < hs->hs_maxhops) {
-				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-				    "forward PREQ from %s",
-				    ether_sprintf(preq->preq_origaddr));
-				/*
-				 * Propagate the original PREQ.
-				 */
-				ppreq.preq_hopcount += 1;
-				ppreq.preq_ttl -= 1;
-				ppreq.preq_metric +=
-				    ms->ms_pmetric->mpm_metric(ni);
-				/*
-				 * Set TO and unset RF bits because we are going
-				 * to send a PREP next.
-				 */
-				ppreq.preq_targets[0].target_flags |=
-				    IEEE80211_MESHPREQ_TFLAGS_TO;
-				ppreq.preq_targets[0].target_flags &=
-				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
-				hwmp_send_preq(ni, vap->iv_myaddr,
-				    broadcastaddr, &ppreq);
-			}
+		if (rttarg != NULL &&
+		    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
+		    !(rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) {
 			/*
 			 * Check if we can send an intermediate Path Reply,
-			 * i.e., Target Only bit is not set.
+			 * i.e., Target Only bit is not set and target is not
+			 * the MAC broadcast address.
 			 */
-	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
+			if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO) &&
+			    !IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr)) {
 				struct ieee80211_meshprep_ie prep;
 
 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-				    "intermediate reply for PREQ from %s",
-				    ether_sprintf(preq->preq_origaddr));
+				    "intermediate reply for PREQ from %6D",
+				    preq->preq_origaddr, ":");
 				prep.prep_flags = 0;
-				prep.prep_hopcount = rt->rt_nhops + 1;
+				prep.prep_hopcount = rttarg->rt_nhops;
 				prep.prep_ttl = ms->ms_ttl;
 				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
 				    PREQ_TADDR(0));
-				prep.prep_targetseq = hrorig->hr_seq;
+				prep.prep_targetseq = hrtarg->hr_seq;
 				prep.prep_lifetime = preq->preq_lifetime;
-				prep.prep_metric = rt->rt_metric +
-				    ms->ms_pmetric->mpm_metric(ni);
+				prep.prep_metric =rttarg->rt_metric;
 				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
 				    preq->preq_origaddr);
 				prep.prep_origseq = hrorig->hr_seq;
-				hwmp_send_prep(ni, vap->iv_myaddr,
-				    broadcastaddr, &prep);
+				hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
+
+				/*
+				 * Set TO and unset RF bits because we have
+				 * sent a PREP.
+				 */
+				ppreq.preq_targets[0].target_flags |=
+				    IEEE80211_MESHPREQ_TFLAGS_TO;
 			}
-		/*
-		 * We have no information about this path,
-		 * propagate the PREQ.
-		 */
-		} else if (preq->preq_ttl > 1 &&
-		    preq->preq_hopcount < hs->hs_maxhops) {
-			if (rt == NULL) {
-				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
-				if (rt == NULL) {
-					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
-					    ni, "unable to add PREQ path to %s",
-					    ether_sprintf(PREQ_TADDR(0)));
-					vap->iv_stats.is_mesh_rtaddfailed++;
-					return;
-				}
-			}
-			rt->rt_metric = preq->preq_metric;
-			rt->rt_lifetime = preq->preq_lifetime;
-			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
-			    struct ieee80211_hwmp_route);
-			hrorig->hr_seq = preq->preq_origseq;
-			hrorig->hr_preqid = preq->preq_id;
+		}
 
-			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-			    "forward PREQ from %s",
-			    ether_sprintf(preq->preq_origaddr));
-			ppreq.preq_hopcount += 1;
-			ppreq.preq_ttl -= 1;
-			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
-			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
-			    &ppreq);
-		}
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "forward PREQ from %6D",
+		    preq->preq_origaddr, ":");
+		ppreq.preq_hopcount += 1;
+		ppreq.preq_ttl -= 1;
+		ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
+
+		/* don't do PREQ ratecheck when we propagate */
+		hwmp_send_preq(vap, broadcastaddr, &ppreq, NULL, NULL);
 	}
-
 }
 #undef	PREQ_TFLAGS
 #undef	PREQ_TADDR
@@ -904,24 +1232,26 @@
 #undef	PREQ_TSEQ
 
 static int
-hwmp_send_preq(struct ieee80211_node *ni,
-    const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_preq(struct ieee80211vap *vap,
     const uint8_t da[IEEE80211_ADDR_LEN],
-    struct ieee80211_meshpreq_ie *preq)
+    struct ieee80211_meshpreq_ie *preq,
+    struct timeval *last, struct timeval *minint)
 {
-	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
 
 	/*
 	 * Enforce PREQ interval.
+	 * NB: Proactive ROOT PREQs rate is handled by cb task.
 	 */
-	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
-		return EALREADY;
-	getmicrouptime(&hs->hs_lastpreq);
+	if (last != NULL && minint != NULL) {
+		if (ratecheck(last, minint) == 0)
+			return EALREADY; /* XXX: we should postpone */
+		getmicrouptime(last);
+	}
 
 	/*
 	 * mesh preq action frame format
 	 *     [6] da
-	 *     [6] sa 
+	 *     [6] sa
 	 *     [6] addr3 = sa
 	 *     [1] action
 	 *     [1] category
@@ -928,8 +1258,10 @@
 	 *     [tlv] mesh path request
 	 */
 	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
-	return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
-	    sizeof(struct ieee80211_meshpreq_ie));
+	preq->preq_len = (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE ?
+	    IEEE80211_MESHPREQ_BASE_SZ_AE : IEEE80211_MESHPREQ_BASE_SZ) +
+	    preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ;
+	return hwmp_send_action(vap, da, (uint8_t *)preq, preq->preq_len+2);
 }
 
 static void
@@ -936,127 +1268,180 @@
 hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
     const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
 {
+#define	IS_PROXY(rt)	(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)
+#define	PROXIED_BY_US(rt)		\
+    (IEEE80211_ADDR_EQ(vap->iv_myaddr, rt->rt_mesh_gate))
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 	struct ieee80211_mesh_route *rt = NULL;
+	struct ieee80211_mesh_route *rtorig = NULL;
+	struct ieee80211_mesh_route *rtext = NULL;
 	struct ieee80211_hwmp_route *hr;
 	struct ieee80211com *ic = vap->iv_ic;
-	struct ifnet *ifp = vap->iv_ifp;
 	struct mbuf *m, *next;
+	uint32_t metric = 0;
+	const uint8_t *addr;
 
+	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+	    "received PREP, orig %6D, targ %6D", prep->prep_origaddr, ":",
+	    prep->prep_targetaddr, ":");
+
 	/*
-	 * Acceptance criteria: if the corresponding PREQ was not generated
-	 * by us and forwarding is disabled, discard this PREP.
+	 * Acceptance criteria: (If the corresponding PREP was not generated
+	 * by us OR not generated by an external mac that is not proxied by us)
+	 * AND forwarding is disabled, discard this PREP.
 	 */
-	if (ni == vap->iv_bss ||
-	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
+	rtorig = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
+	if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) ||
+	    (rtorig != NULL && IS_PROXY(rtorig) && !PROXIED_BY_US(rtorig))) &&
+	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)){
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "discard PREP, orig(%6D) not proxied or generated by us",
+		    prep->prep_origaddr, ":");
 		return;
-	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
-	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
-		return;
+	}
 
-	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-	    "received PREP from %s", ether_sprintf(prep->prep_targetaddr));
+	/* PREP ACCEPTED */
 
+	/*
+	 * If accepted shall create or update the active forwarding information
+	 * it maintains for the target mesh STA of the PREP (according to the
+	 * rules defined in 13.10.8.4). If the conditions for creating or
+	 * updating the forwarding information have not been met in those
+	 * rules, no further steps are applied to the PREP.
+	 */
 	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
 	if (rt == NULL) {
-		/*
-		 * If we have no entry this could be a reply to a root PREQ.
-		 */
-		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
-			rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
-			if (rt == NULL) {
-				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
-				    ni, "unable to add PREP path to %s",
-				    ether_sprintf(prep->prep_targetaddr));
-				vap->iv_stats.is_mesh_rtaddfailed++;
-				return;
-			}
-			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
-			rt->rt_nhops = prep->prep_hopcount;
-			rt->rt_lifetime = prep->prep_lifetime;
-			rt->rt_metric = prep->prep_metric;
-			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
+		rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
+		if (rt == NULL) {
 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-			    "add root path to %s nhops %d metric %d (PREP)",
-			    ether_sprintf(prep->prep_targetaddr),
-			    rt->rt_nhops, rt->rt_metric);
+			    "unable to add PREP path to %6D",
+			    prep->prep_targetaddr, ":");
+			vap->iv_stats.is_mesh_rtaddfailed++;
 			return;
-		} 
-		return;
+		}
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "adding target %6D", prep->prep_targetaddr, ":");
 	}
-	/*
-	 * Sequence number validation.
-	 */
 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
-	if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) {
-		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "discard PREP from %s, old seq no %u <= %u",
-		    ether_sprintf(prep->prep_targetaddr),
-		    prep->prep_targetseq, hr->hr_seq);
-		return;
+	/* update path metric */
+	metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni);
+	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
+		if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) {
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "discard PREP from %6D, old seq no %u < %u",
+			    prep->prep_targetaddr, ":",
+			    prep->prep_targetseq, hr->hr_seq);
+			return;
+		} else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) &&
+		    metric > rt->rt_metric) {
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "discard PREP from %6D, new metric %u > %u",
+			    prep->prep_targetaddr, ":",
+			    metric, rt->rt_metric);
+			return;
+		}
 	}
+
+	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+	    "%s path to %6D, hopcount %d:%d metric %d:%d",
+	    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
+	    "prefer" : "update",
+	    prep->prep_targetaddr, ":",
+	    rt->rt_nhops, prep->prep_hopcount + 1,
+	    rt->rt_metric, metric);
+
 	hr->hr_seq = prep->prep_targetseq;
+	hr->hr_preqretries = 0;
+	IEEE80211_ADDR_COPY(rt->rt_nexthop, ni->ni_macaddr);
+	rt->rt_metric = metric;
+	rt->rt_nhops = prep->prep_hopcount + 1;
+	ieee80211_mesh_rt_update(rt, prep->prep_lifetime);
+	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
+		/* discovery complete */
+		rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_DISCOVER;
+	}
+	rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; /* mark valid */
+
+	/* Update forwarding information to TA if metric improves */
+	hwmp_update_transmitter(vap, ni, "PREP");
+
 	/*
-	 * If it's NOT for us, propagate the PREP.
+	 * If it's NOT for us, propagate the PREP
 	 */
 	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
-	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
+	    prep->prep_ttl > 1 &&
+	    prep->prep_hopcount < hs->hs_maxhops) {
 		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
+		/*
+		 * NB: We should already have setup the path to orig
+		 * mesh STA when we propagated PREQ to target mesh STA,
+		 * no PREP is generated without a corresponding PREQ.
+		 * XXX: for now just ignore.
+		 */
+		if (rtorig == NULL) {
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "received PREP for an unknown orig(%6D)",
+			    prep->prep_origaddr, ":");
+			return;
+		}
 
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "propagate PREP from %s",
-		    ether_sprintf(prep->prep_targetaddr));
+		    "propagate PREP from %6D",
+		    prep->prep_targetaddr, ":");
 
 		memcpy(&pprep, prep, sizeof(pprep));
 		pprep.prep_hopcount += 1;
 		pprep.prep_ttl -= 1;
 		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
-		IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr);
-		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
+		hwmp_send_prep(vap, rtorig->rt_nexthop, &pprep);
+
+		/* precursor list for the Target Mesh STA Address is updated */
 	}
-	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
-	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
-		/* NB: never clobber a proxy entry */;
+
+	/*
+	 * Check if we received a PREP w/ AE and store target external address.
+	 * We may store target external address if recevied PREP w/ AE
+	 * and we are not final destination
+	 */
+	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
+		rtext = ieee80211_mesh_rt_find(vap,
+			prep->prep_target_ext_addr);
+		if (rtext == NULL) {
+			rtext = ieee80211_mesh_rt_add(vap,
+				prep->prep_target_ext_addr);
+			if (rtext == NULL) {
+				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+				    "unable to add PREP path to proxy %6D",
+				    prep->prep_targetaddr, ":");
+				vap->iv_stats.is_mesh_rtaddfailed++;
+				return;
+			}
+		}
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "discard PREP for %s, route is marked PROXY",
-		    ether_sprintf(prep->prep_targetaddr));
-		vap->iv_stats.is_hwmp_proxy++;
-	} else if (prep->prep_origseq == hr->hr_origseq) {
+		    "%s path to %6D, hopcount %d:%d metric %d:%d",
+		    rtext->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
+		    "prefer" : "update",
+		    prep->prep_target_ext_addr, ":",
+		    rtext->rt_nhops, prep->prep_hopcount + 1,
+		    rtext->rt_metric, metric);
+
+		rtext->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
+			IEEE80211_MESHRT_FLAGS_VALID;
+		IEEE80211_ADDR_COPY(rtext->rt_dest,
+		    prep->prep_target_ext_addr);
+		IEEE80211_ADDR_COPY(rtext->rt_mesh_gate,
+		    prep->prep_targetaddr);
+		IEEE80211_ADDR_COPY(rtext->rt_nexthop, wh->i_addr2);
+		rtext->rt_metric = metric;
+		rtext->rt_lifetime = prep->prep_lifetime;
+		rtext->rt_nhops = prep->prep_hopcount + 1;
+		rtext->rt_ext_seq = prep->prep_origseq; /* new proxy seq */
 		/*
-		 * Check if we already have a path to this node.
-		 * If we do, check if this path reply contains a
-		 * better route.
+		 * XXX: proxy entries have no HWMP priv data,
+		 * nullify them to be sure?
 		 */
-		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
-		    (prep->prep_hopcount < rt->rt_nhops ||
-		     prep->prep_metric < rt->rt_metric)) {
-			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-			    "%s path to %s, hopcount %d:%d metric %d:%d",
-			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
-				"prefer" : "update",
-			    ether_sprintf(prep->prep_origaddr),
-			    rt->rt_nhops, prep->prep_hopcount,
-			    rt->rt_metric, prep->prep_metric);
-			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
-			rt->rt_nhops = prep->prep_hopcount;
-			rt->rt_lifetime = prep->prep_lifetime;
-			rt->rt_metric = prep->prep_metric;
-			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
-		} else {
-			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-			    "ignore PREP for %s, hopcount %d:%d metric %d:%d",
-			    ether_sprintf(prep->prep_targetaddr),
-			    rt->rt_nhops, prep->prep_hopcount,
-			    rt->rt_metric, prep->prep_metric);
-		}
-	} else {
-		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "discard PREP for %s, wrong seqno %u != %u",
-		    ether_sprintf(prep->prep_targetaddr), prep->prep_origseq,
-		    hr->hr_seq);
-		vap->iv_stats.is_hwmp_wrongseq++;
-	} 
+	}
 	/*
 	 * Check for frames queued awaiting path discovery.
 	 * XXX probably can tell exactly and avoid remove call
@@ -1064,21 +1449,33 @@
 	 *     stuck back on the stageq because there won't be
 	 *     a path.
 	 */
-	m = ieee80211_ageq_remove(&ic->ic_stageq, 
+	addr = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
+	    prep->prep_target_ext_addr : prep->prep_targetaddr;
+	m = ieee80211_ageq_remove(&ic->ic_stageq,
 	    (struct ieee80211_node *)(uintptr_t)
-		ieee80211_mac_hash(ic, rt->rt_dest));
+	    ieee80211_mac_hash(ic, addr)); /* either dest or ext_dest */
+
+	/*
+	 * All frames in the stageq here should be non-M_ENCAP; or things
+	 * will get very unhappy.
+	 */
 	for (; m != NULL; m = next) {
 		next = m->m_nextpkt;
 		m->m_nextpkt = NULL;
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
-		ifp->if_transmit(ifp, m);
+		/*
+		 * If the mbuf has M_ENCAP set, ensure we free it.
+		 * Note that after if_transmit() is called, m is invalid.
+		 */
+		(void) ieee80211_vap_xmitpkt(vap, m);
 	}
+#undef	IS_PROXY
+#undef	PROXIED_BY_US
 }
 
 static int
-hwmp_send_prep(struct ieee80211_node *ni,
-    const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_prep(struct ieee80211vap *vap,
     const uint8_t da[IEEE80211_ADDR_LEN],
     struct ieee80211_meshprep_ie *prep)
 {
@@ -1087,7 +1484,7 @@
 	/*
 	 * mesh prep action frame format
 	 *     [6] da
-	 *     [6] sa 
+	 *     [6] sa
 	 *     [6] addr3 = sa
 	 *     [1] action
 	 *     [1] category
@@ -1094,8 +1491,9 @@
 	 *     [tlv] mesh path reply
 	 */
 	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
-	return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
-	    sizeof(struct ieee80211_meshprep_ie));
+	prep->prep_len = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
+	    IEEE80211_MESHPREP_BASE_SZ_AE : IEEE80211_MESHPREP_BASE_SZ;
+	return hwmp_send_action(vap, da, (uint8_t *)prep, prep->prep_len + 2);
 }
 
 #define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
@@ -1124,11 +1522,11 @@
 		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
 	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
 	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
-	PERR_DSEQ(0) = hr->hr_seq;
+	PERR_DSEQ(0) = ++hr->hr_seq;
 	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
 	/* NB: flush everything passing through peer */
 	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
-	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
+	hwmp_send_perr(vap, broadcastaddr, &perr);
 }
 #undef	PERR_DFLAGS
 #undef	PERR_DADDR
@@ -1135,10 +1533,11 @@
 #undef	PERR_DSEQ
 #undef	PERR_DRCODE
 
-#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
-#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
-#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
-#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
+#define	PERR_DFLAGS(n)		perr->perr_dests[n].dest_flags
+#define	PERR_DADDR(n)		perr->perr_dests[n].dest_addr
+#define	PERR_DSEQ(n)		perr->perr_dests[n].dest_seq
+#define	PERR_DEXTADDR(n)	perr->perr_dests[n].dest_ext_addr
+#define	PERR_DRCODE(n)		perr->perr_dests[n].dest_rcode
 static void
 hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
     const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
@@ -1145,57 +1544,111 @@
 {
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211_mesh_route *rt = NULL;
+	struct ieee80211_mesh_route *rt_ext = NULL;
 	struct ieee80211_hwmp_route *hr;
- 	struct ieee80211_meshperr_ie pperr;
-	int i, forward = 0;
+	struct ieee80211_meshperr_ie *pperr = NULL;
+	int i, j = 0, forward = 0;
 
+	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+	    "received PERR from %6D", wh->i_addr2, ":");
+
 	/*
-	 * Acceptance criteria: check if we received a PERR from a
-	 * neighbor and forwarding is enabled.
+	 * if forwarding is true, prepare pperr
 	 */
-	if (ni == vap->iv_bss ||
-	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
-	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
-		return;
+	if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
+		forward = 1;
+		pperr = malloc(sizeof(*perr) + 31*sizeof(*perr->perr_dests),
+		    M_80211_MESH_PERR, M_NOWAIT); /* XXX: magic number, 32 err dests */
+	}
+
 	/*
-	 * Find all routing entries that match and delete them.
+	 * Acceptance criteria: check if we have forwarding information
+	 * stored about destination, and that nexthop == TA of this PERR.
+	 * NB: we also build a new PERR to propagate in case we should forward.
 	 */
 	for (i = 0; i < perr->perr_ndests; i++) {
 		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
 		if (rt == NULL)
 			continue;
+		if (!IEEE80211_ADDR_EQ(rt->rt_nexthop, wh->i_addr2))
+			continue;
+
+		/* found and accepted a PERR ndest element, process it... */
+		if (forward)
+			memcpy(&pperr->perr_dests[j], &perr->perr_dests[i],
+			    sizeof(*perr->perr_dests));
 		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
-		if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 
-		    HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
-			ieee80211_mesh_rt_del(vap, rt->rt_dest);
-			ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
-			rt = NULL;
-			forward = 1;
+		switch(PERR_DFLAGS(i)) {
+		case (IEEE80211_REASON_MESH_PERR_NO_FI):
+			if (PERR_DSEQ(i) == 0) {
+				hr->hr_seq++;
+				if (forward) {
+					pperr->perr_dests[j].dest_seq =
+					    hr->hr_seq;
+				}
+			} else {
+				hr->hr_seq = PERR_DSEQ(i);
+			}
+			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
+			j++;
+			break;
+		case (IEEE80211_REASON_MESH_PERR_DEST_UNREACH):
+			if(HWMP_SEQ_GT(PERR_DSEQ(i), hr->hr_seq)) {
+				hr->hr_seq = PERR_DSEQ(i);
+				rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
+				j++;
+			}
+			break;
+		case (IEEE80211_REASON_MESH_PERR_NO_PROXY):
+			rt_ext = ieee80211_mesh_rt_find(vap, PERR_DEXTADDR(i));
+			if (rt_ext != NULL) {
+				rt_ext->rt_flags &=
+				    ~IEEE80211_MESHRT_FLAGS_VALID;
+				j++;
+			}
+			break;
+		default:
+			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+			    "PERR, unknown reason code %u\n", PERR_DFLAGS(i));
+			goto done; /* XXX: stats?? */
 		}
+		ieee80211_mesh_rt_flush_peer(vap, PERR_DADDR(i));
+		KASSERT(j < 32, ("PERR, error ndest >= 32 (%u)", j));
 	}
+	if (j == 0) {
+		IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL, "%s",
+		    "PERR not accepted");
+		goto done; /* XXX: stats?? */
+	}
+
 	/*
 	 * Propagate the PERR if we previously found it on our routing table.
-	 * XXX handle ndest > 1
 	 */
 	if (forward && perr->perr_ttl > 1) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "propagate PERR from %s", ether_sprintf(wh->i_addr2));
-		memcpy(&pperr, perr, sizeof(*perr));
-		pperr.perr_ttl--;
-		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
-		    &pperr);
+		    "propagate PERR from %6D", wh->i_addr2, ":");
+		pperr->perr_ndests = j;
+		pperr->perr_ttl--;
+		hwmp_send_perr(vap, broadcastaddr, pperr);
 	}
+done:
+	if (pperr != NULL)
+		free(pperr, M_80211_MESH_PERR);
 }
-#undef	PEER_DADDR
+#undef	PERR_DFLAGS
+#undef	PERR_DADDR
 #undef	PERR_DSEQ
+#undef	PERR_DEXTADDR
+#undef	PERR_DRCODE
 
 static int
-hwmp_send_perr(struct ieee80211_node *ni,
-    const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_perr(struct ieee80211vap *vap,
     const uint8_t da[IEEE80211_ADDR_LEN],
     struct ieee80211_meshperr_ie *perr)
 {
-	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
+	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+	int i;
+	uint8_t length = 0;
 
 	/*
 	 * Enforce PERR interval.
@@ -1214,11 +1667,78 @@
 	 *     [tlv] mesh path error
 	 */
 	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
-	return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
-	    sizeof(struct ieee80211_meshperr_ie));
+	length = IEEE80211_MESHPERR_BASE_SZ;
+	for (i = 0; i<perr->perr_ndests; i++) {
+		if (perr->perr_dests[i].dest_flags &
+		    IEEE80211_MESHPERR_FLAGS_AE) {
+			length += IEEE80211_MESHPERR_DEST_SZ_AE;
+			continue ;
+		}
+		length += IEEE80211_MESHPERR_DEST_SZ;
+	}
+	perr->perr_len =length;
+	return hwmp_send_action(vap, da, (uint8_t *)perr, perr->perr_len+2);
 }
 
+/*
+ * Called from the rest of the net80211 code (mesh code for example).
+ * NB: IEEE80211_REASON_MESH_PERR_DEST_UNREACH can be trigger by the fact that
+ * a mesh STA is unable to forward an MSDU/MMPDU to a next-hop mesh STA.
+ */
+#define	PERR_DFLAGS(n)		perr.perr_dests[n].dest_flags
+#define	PERR_DADDR(n)		perr.perr_dests[n].dest_addr
+#define	PERR_DSEQ(n)		perr.perr_dests[n].dest_seq
+#define	PERR_DEXTADDR(n)	perr.perr_dests[n].dest_ext_addr
+#define	PERR_DRCODE(n)		perr.perr_dests[n].dest_rcode
 static void
+hwmp_senderror(struct ieee80211vap *vap,
+    const uint8_t addr[IEEE80211_ADDR_LEN],
+    struct ieee80211_mesh_route *rt, int rcode)
+{
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_hwmp_route *hr = NULL;
+	struct ieee80211_meshperr_ie perr;
+
+	if (rt != NULL)
+		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
+		    struct ieee80211_hwmp_route);
+
+	perr.perr_ndests = 1;
+	perr.perr_ttl = ms->ms_ttl;
+	PERR_DFLAGS(0) = 0;
+	PERR_DRCODE(0) = rcode;
+
+	switch (rcode) {
+	case IEEE80211_REASON_MESH_PERR_NO_FI:
+		IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
+		PERR_DSEQ(0) = 0; /* reserved */
+		break;
+	case IEEE80211_REASON_MESH_PERR_NO_PROXY:
+		KASSERT(rt != NULL, ("no proxy info for sending PERR"));
+		KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
+		    ("route is not marked proxy"));
+		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_FLAGS_AE;
+		IEEE80211_ADDR_COPY(PERR_DADDR(0), vap->iv_myaddr);
+		PERR_DSEQ(0) = rt->rt_ext_seq;
+		IEEE80211_ADDR_COPY(PERR_DEXTADDR(0), addr);
+		break;
+	case IEEE80211_REASON_MESH_PERR_DEST_UNREACH:
+		KASSERT(rt != NULL, ("no route info for sending PERR"));
+		IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
+		PERR_DSEQ(0) = hr->hr_seq;
+		break;
+	default:
+		KASSERT(0, ("unknown reason code for HWMP PERR (%u)", rcode));
+	}
+	hwmp_send_perr(vap, broadcastaddr, &perr);
+}
+#undef	PERR_DFLAGS
+#undef	PEER_DADDR
+#undef	PERR_DSEQ
+#undef	PERR_DEXTADDR
+#undef	PERR_DRCODE
+
+static void
 hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
     const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
 {
@@ -1226,41 +1746,95 @@
 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 	struct ieee80211_mesh_route *rt = NULL;
 	struct ieee80211_hwmp_route *hr;
+	struct ieee80211_meshpreq_ie preq;
 	struct ieee80211_meshrann_ie prann;
+	uint32_t metric = 0;
 
-	if (ni == vap->iv_bss ||
-	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
-	    IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
+	if (IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
 		return;
 
 	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
-	/*
-	 * Discover the path to the root mesh STA.
-	 * If we already know it, propagate the RANN element.
-	 */
+	if (rt != NULL && rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
+		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+
+		/* Acceptance criteria: if RANN.seq < stored seq, discard RANN */
+		if (HWMP_SEQ_LT(rann->rann_seq, hr->hr_seq)) {
+			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+			"RANN seq %u < %u", rann->rann_seq, hr->hr_seq);
+			return;
+		}
+
+		/* Acceptance criteria: if RANN.seq == stored seq AND
+		* RANN.metric > stored metric, discard RANN */
+		if (HWMP_SEQ_EQ(rann->rann_seq, hr->hr_seq) &&
+		rann->rann_metric > rt->rt_metric) {
+			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+			"RANN metric %u > %u", rann->rann_metric, rt->rt_metric);
+			return;
+		}
+	}
+
+	/* RANN ACCEPTED */
+
+	ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */
+	metric = rann->rann_metric + ms->ms_pmetric->mpm_metric(ni);
+
 	if (rt == NULL) {
-		hwmp_discover(vap, rann->rann_addr, NULL);
-		return;
+		rt = ieee80211_mesh_rt_add(vap, rann->rann_addr);
+		if (rt == NULL) {
+			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+			    "unable to add mac for RANN root %6D",
+			    rann->rann_addr, ":");
+			    vap->iv_stats.is_mesh_rtaddfailed++;
+			return;
+		}
 	}
 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
-	if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
+	/* Check if root is a mesh gate, mark it */
+	if (rann->rann_flags & IEEE80211_MESHRANN_FLAGS_GATE) {
+		struct ieee80211_mesh_gate_route *gr;
+
+		rt->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
+		gr = ieee80211_mesh_mark_gate(vap, rann->rann_addr,
+			rt);
+		gr->gr_lastseq = 0; /* NOT GANN */
+	}
+	/* discovery timeout */
+	ieee80211_mesh_rt_update(rt,
+	    ticks_to_msecs(ieee80211_hwmp_roottimeout));
+
+	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
+	preq.preq_hopcount = 0;
+	preq.preq_ttl = ms->ms_ttl;
+	preq.preq_id = 0; /* reserved */
+	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
+	preq.preq_origseq = ++hs->hs_seq;
+	preq.preq_lifetime = ieee80211_hwmp_roottimeout;
+	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+	preq.preq_tcount = 1;
+	preq.preq_targets[0].target_flags = IEEE80211_MESHPREQ_TFLAGS_TO;
+	/* NB: IEEE80211_MESHPREQ_TFLAGS_USN = 0 implicitly implied */
+	IEEE80211_ADDR_COPY(preq.preq_targets[0].target_addr, rann->rann_addr);
+	preq.preq_targets[0].target_seq = rann->rann_seq;
+	/* XXX: if rootconfint have not passed, we built this preq in vain */
+	hwmp_send_preq(vap, wh->i_addr2, &preq, &hr->hr_lastrootconf,
+	    &ieee80211_hwmp_rootconfint);
+
+	/* propagate a RANN */
+	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
+	    rann->rann_ttl > 1 &&
+	    ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
 		hr->hr_seq = rann->rann_seq;
-		if (rann->rann_ttl > 1 &&
-		    rann->rann_hopcount < hs->hs_maxhops &&
-		    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
-			memcpy(&prann, rann, sizeof(prann));
-			prann.rann_hopcount += 1;
-			prann.rann_ttl -= 1;
-			prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
-			hwmp_send_rann(vap->iv_bss, vap->iv_myaddr,
-			    broadcastaddr, &prann);
-		}
+		memcpy(&prann, rann, sizeof(prann));
+		prann.rann_hopcount += 1;
+		prann.rann_ttl -= 1;
+		prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
+		hwmp_send_rann(vap, broadcastaddr, &prann);
 	}
 }
 
 static int
-hwmp_send_rann(struct ieee80211_node *ni,
-    const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_rann(struct ieee80211vap *vap,
     const uint8_t da[IEEE80211_ADDR_LEN],
     struct ieee80211_meshrann_ie *rann)
 {
@@ -1267,7 +1841,7 @@
 	/*
 	 * mesh rann action frame format
 	 *     [6] da
-	 *     [6] sa 
+	 *     [6] sa
 	 *     [6] addr3 = sa
 	 *     [1] action
 	 *     [1] category
@@ -1274,13 +1848,69 @@
 	 *     [tlv] root annoucement
 	 */
 	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
-	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
-	    sizeof(struct ieee80211_meshrann_ie));
+	rann->rann_len = IEEE80211_MESHRANN_BASE_SZ;
+	return hwmp_send_action(vap, da, (uint8_t *)rann, rann->rann_len + 2);
 }
 
 #define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
 #define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
 #define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
+static void
+hwmp_rediscover_cb(void *arg)
+{
+	struct ieee80211_mesh_route *rt = arg;
+	struct ieee80211vap *vap = rt->rt_vap;
+	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_hwmp_route *hr;
+	struct ieee80211_meshpreq_ie preq; /* Optimize: storing first preq? */
+
+	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID))
+		return ; /* nothing to do */
+
+	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+	if (hr->hr_preqretries >=
+		ieee80211_hwmp_maxpreq_retries) {
+		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY,
+			rt->rt_dest, "%s",
+			"max number of discovery, send queued frames to GATE");
+		ieee80211_mesh_forward_to_gates(vap, rt);
+		vap->iv_stats.is_mesh_fwd_nopath++;
+		return ; /* XXX: flush queue? */
+	}
+
+	hr->hr_preqretries++;
+
+
+	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, rt->rt_dest,
+	    "start path rediscovery , target seq %u", hr->hr_seq);
+	/*
+	 * Try to discover the path for this node.
+	 * Group addressed PREQ Case A
+	 */
+	preq.preq_flags = 0;
+	preq.preq_hopcount = 0;
+	preq.preq_ttl = ms->ms_ttl;
+	preq.preq_id = ++hs->hs_preqid;
+	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
+	preq.preq_origseq = hr->hr_origseq;
+	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_pathtimeout);
+	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+	preq.preq_tcount = 1;
+	IEEE80211_ADDR_COPY(PREQ_TADDR(0), rt->rt_dest);
+	PREQ_TFLAGS(0) = 0;
+	if (ieee80211_hwmp_targetonly)
+		PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
+	PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
+	PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
+	/* XXX check return value */
+	hwmp_send_preq(vap, broadcastaddr, &preq, &hr->hr_lastpreq,
+	    &ieee80211_hwmp_preqminint);
+	callout_reset(&rt->rt_discovery,
+		ieee80211_hwmp_net_diameter_traversaltime * 2,
+		hwmp_rediscover_cb, rt);
+}
+
 static struct ieee80211_node *
 hwmp_discover(struct ieee80211vap *vap,
     const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
@@ -1306,8 +1936,8 @@
 			rt = ieee80211_mesh_rt_add(vap, dest);
 			if (rt == NULL) {
 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
-				    ni, "unable to add discovery path to %s",
-				    ether_sprintf(dest));
+				    ni, "unable to add discovery path to %6D",
+				    dest, ":");
 				vap->iv_stats.is_mesh_rtaddfailed++;
 				goto done;
 			}
@@ -1314,20 +1944,45 @@
 		}
 		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
 		    struct ieee80211_hwmp_route);
+		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
+			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
+			    "%s", "already discovering queue frame until path found");
+			sendpreq = 1;
+			goto done;
+		}
 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
+			if (hr->hr_lastdiscovery != 0 &&
+			    (ticks - hr->hr_lastdiscovery <
+			    (ieee80211_hwmp_net_diameter_traversaltime * 2))) {
+				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+			            dest, NULL, "%s",
+				    "too frequent discovery requeust");
+				sendpreq = 1;
+				goto done;
+			}
+			hr->hr_lastdiscovery = ticks;
+			if (hr->hr_preqretries >=
+			    ieee80211_hwmp_maxpreq_retries) {
+				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+			            dest, NULL, "%s",
+				    "no valid path , max number of discovery");
+				vap->iv_stats.is_mesh_fwd_nopath++;
+				goto done;
+			}
+			rt->rt_flags = IEEE80211_MESHRT_FLAGS_DISCOVER;
+			hr->hr_preqretries++;
 			if (hr->hr_origseq == 0)
 				hr->hr_origseq = ++hs->hs_seq;
 			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
-			rt->rt_lifetime =
-			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
-			/* XXX check preq retries */
 			sendpreq = 1;
 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
-			    "start path discovery (src %s)",
+			    "start path discovery (src %s), target seq %u",
 			    m == NULL ? "<none>" : ether_sprintf(
-				mtod(m, struct ether_header *)->ether_shost));
+			    mtod(m, struct ether_header *)->ether_shost),
+			    hr->hr_seq);
 			/*
 			 * Try to discover the path for this node.
+			 * Group addressed PREQ Case A
 			 */
 			preq.preq_flags = 0;
 			preq.preq_hopcount = 0;
@@ -1335,20 +1990,22 @@
 			preq.preq_id = ++hs->hs_preqid;
 			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
 			preq.preq_origseq = hr->hr_origseq;
-			preq.preq_lifetime = rt->rt_lifetime;
-			preq.preq_metric = rt->rt_metric;
+			preq.preq_lifetime =
+			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
+			preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
 			preq.preq_tcount = 1;
 			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
 			PREQ_TFLAGS(0) = 0;
 			if (ieee80211_hwmp_targetonly)
 				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
-			if (ieee80211_hwmp_replyforward)
-				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
 			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
-			PREQ_TSEQ(0) = 0;
+			PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
 			/* XXX check return value */
-			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
-			    broadcastaddr, &preq);
+			hwmp_send_preq(vap, broadcastaddr, &preq,
+			    &hr->hr_lastpreq, &ieee80211_hwmp_preqminint);
+			callout_reset(&rt->rt_discovery,
+			    ieee80211_hwmp_net_diameter_traversaltime * 2,
+			    hwmp_rediscover_cb, rt);
 		}
 		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
 			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
@@ -1391,7 +2048,7 @@
 {
 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 	int error;
- 
+
 	if (vap->iv_opmode != IEEE80211_M_MBSS)
 		return ENOSYS;
 	error = 0;

Modified: trunk/sys/net80211/ieee80211_input.c
===================================================================
--- trunk/sys/net80211/ieee80211_input.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_input.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_input.c 254640 2013-08-22 05:53:47Z adrian $");
 
 #include "opt_wlan.h"
 
@@ -131,7 +132,7 @@
 			 * Packet contents are changed by ieee80211_decap
 			 * so do a deep copy of the packet.
 			 */
-			mcopy = m_dup(m, M_DONTWAIT);
+			mcopy = m_dup(m, M_NOWAIT);
 			if (mcopy == NULL) {
 				/* XXX stat+msg */
 				continue;
@@ -250,7 +251,10 @@
 	struct ifnet *ifp = vap->iv_ifp;
 
 	/* clear driver/net80211 flags before passing up */
-	m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST);
+	m->m_flags &= ~(M_MCAST | M_BCAST);
+#if __FreeBSD_version >= 1000046
+	m_clrprotoflags(m);
+#endif
 
 	/* NB: see hostap_deliver_data, this path doesn't handle hostap */
 	KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap"));
@@ -323,13 +327,13 @@
 		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
 		break;
 	}
-#ifdef ALIGNED_POINTER
+#ifndef __NO_STRICT_ALIGNMENT
 	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
 		m = ieee80211_realign(vap, m, sizeof(*eh));
 		if (m == NULL)
 			return NULL;
 	}
-#endif /* ALIGNED_POINTER */
+#endif /* !__NO_STRICT_ALIGNMENT */
 	if (llc != NULL) {
 		eh = mtod(m, struct ether_header *);
 		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
@@ -522,6 +526,9 @@
 		case IEEE80211_ELEMID_CSA:
 			scan->csa = frm;
 			break;
+		case IEEE80211_ELEMID_QUIET:
+			scan->quiet = frm;
+			break;
 		case IEEE80211_ELEMID_FHPARMS:
 			if (ic->ic_phytype == IEEE80211_T_FH) {
 				scan->fhdwell = LE_READ_2(&frm[2]);
@@ -649,7 +656,8 @@
 	      scan->bintval <= IEEE80211_BINTVAL_MAX)) {
 		IEEE80211_DISCARD(vap,
 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
-		    wh, NULL, "bogus beacon interval", scan->bintval);
+		    wh, NULL, "bogus beacon interval (%d TU)",
+		    (int) scan->bintval);
 		vap->iv_stats.is_rx_badbintval++;
 		scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
 	}
@@ -756,6 +764,61 @@
 			break;
 		}
 		break;
+#ifdef IEEE80211_SUPPORT_MESH
+	case IEEE80211_ACTION_CAT_MESH:
+		switch (ia->ia_action) {
+		case IEEE80211_ACTION_MESH_LMETRIC:
+			/*
+			 * XXX: verification is true only if we are using
+			 * Airtime link metric (default)
+			 */
+			IEEE80211_VERIFY_LENGTH(efrm - frm,
+			    sizeof(struct ieee80211_meshlmetric_ie),
+			    return EINVAL);
+			break;
+		case IEEE80211_ACTION_MESH_HWMP:
+			/* verify something */
+			break;
+		case IEEE80211_ACTION_MESH_GANN:
+			IEEE80211_VERIFY_LENGTH(efrm - frm,
+			    sizeof(struct ieee80211_meshgann_ie),
+			    return EINVAL);
+			break;
+		case IEEE80211_ACTION_MESH_CC:
+		case IEEE80211_ACTION_MESH_MCCA_SREQ:
+		case IEEE80211_ACTION_MESH_MCCA_SREP:
+		case IEEE80211_ACTION_MESH_MCCA_AREQ:
+		case IEEE80211_ACTION_MESH_MCCA_ADVER:
+		case IEEE80211_ACTION_MESH_MCCA_TRDOWN:
+		case IEEE80211_ACTION_MESH_TBTT_REQ:
+		case IEEE80211_ACTION_MESH_TBTT_RES:
+			/* reject these early on, not implemented */
+			IEEE80211_DISCARD(vap,
+			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+			    wh, NULL, "not implemented yet, act=0x%02X",
+			    ia->ia_action);
+			return EINVAL;
+		}
+		break;
+	case IEEE80211_ACTION_CAT_SELF_PROT:
+		/* If TA or RA group address discard silently */
+		if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+			IEEE80211_IS_MULTICAST(wh->i_addr2))
+			return EINVAL;
+		/*
+		 * XXX: Should we verify complete length now or it is
+		 * to varying in sizes?
+		 */
+		switch (ia->ia_action) {
+		case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+		case IEEE80211_ACTION_MESHPEERING_CLOSE:
+			/* is not a peering candidate (yet) */
+			if (ni == vap->iv_bss)
+				return EINVAL;
+			break;
+		}
+		break;
+#endif
 	}
 	return 0;
 }

Modified: trunk/sys/net80211/ieee80211_input.h
===================================================================
--- trunk/sys/net80211/ieee80211_input.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_input.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_input.h 221418 2011-05-04 02:23:59Z adrian $
  */
 #ifndef _NET80211_IEEE80211_INPUT_H_
 #define _NET80211_IEEE80211_INPUT_H_

Modified: trunk/sys/net80211/ieee80211_ioctl.c
===================================================================
--- trunk/sys/net80211/ieee80211_ioctl.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ioctl.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_ioctl.c 322061 2017-08-04 20:24:23Z pfg $");
 
 /*
  * IEEE 802.11 ioctl support (FreeBSD-specific)
@@ -972,6 +973,21 @@
 	case IEEE80211_IOC_PUREG:
 		ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0;
 		break;
+	case IEEE80211_IOC_QUIET:
+		ireq->i_val = vap->iv_quiet;
+		break;
+	case IEEE80211_IOC_QUIET_COUNT:
+		ireq->i_val = vap->iv_quiet_count;
+		break;
+	case IEEE80211_IOC_QUIET_PERIOD:
+		ireq->i_val = vap->iv_quiet_period;
+		break;
+	case IEEE80211_IOC_QUIET_DUR:
+		ireq->i_val = vap->iv_quiet_duration;
+		break;
+	case IEEE80211_IOC_QUIET_OFFSET:
+		ireq->i_val = vap->iv_quiet_offset;
+		break;
 	case IEEE80211_IOC_BGSCAN:
 		ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0;
 		break;
@@ -1325,12 +1341,17 @@
 	if (!IEEE80211_ADDR_EQ(mac, ic->ic_ifp->if_broadcastaddr)) {
 		IEEE80211_NODE_LOCK(nt);
 		ni = ieee80211_find_node_locked(nt, mac);
+		IEEE80211_NODE_UNLOCK(nt);
+		/*
+		 * Don't do the node update inside the node
+		 * table lock.  This unfortunately causes LORs
+		 * with drivers and their TX paths.
+		 */
 		if (ni != NULL) {
 			domlme(mlmeop, ni);
 			ieee80211_free_node(ni);
 		} else
 			error = ENOENT;
-		IEEE80211_NODE_UNLOCK(nt);
 	} else {
 		ieee80211_iterate_nodes(nt, domlme, mlmeop);
 	}
@@ -1382,6 +1403,22 @@
 			    IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
 			ieee80211_free_node(ni);
 			break;
+		case IEEE80211_M_MBSS:
+			IEEE80211_NODE_LOCK(nt);
+			ni = ieee80211_find_node_locked(nt, mac);
+			/*
+			 * Don't do the node update inside the node
+			 * table lock.  This unfortunately causes LORs
+			 * with drivers and their TX paths.
+			 */
+			IEEE80211_NODE_UNLOCK(nt);
+			if (ni != NULL) {
+				ieee80211_node_leave(ni);
+				ieee80211_free_node(ni);
+			} else {
+				error = ENOENT;
+			}
+			break;
 		default:
 			error = EINVAL;
 			break;
@@ -1396,6 +1433,12 @@
 		}
 		IEEE80211_NODE_LOCK(nt);
 		ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+		/*
+		 * Don't do the node update inside the node
+		 * table lock.  This unfortunately causes LORs
+		 * with drivers and their TX paths.
+		 */
+		IEEE80211_NODE_UNLOCK(nt);
 		if (ni != NULL) {
 			mlmedebug(vap, mac, op, reason);
 			if (op == IEEE80211_MLME_AUTHORIZE)
@@ -1405,7 +1448,6 @@
 			ieee80211_free_node(ni);
 		} else
 			error = ENOENT;
-		IEEE80211_NODE_UNLOCK(nt);
 		break;
 	case IEEE80211_MLME_AUTH:
 		if (vap->iv_opmode != IEEE80211_M_HOSTAP) {
@@ -1414,6 +1456,12 @@
 		}
 		IEEE80211_NODE_LOCK(nt);
 		ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+		/*
+		 * Don't do the node update inside the node
+		 * table lock.  This unfortunately causes LORs
+		 * with drivers and their TX paths.
+		 */
+		IEEE80211_NODE_UNLOCK(nt);
 		if (ni != NULL) {
 			mlmedebug(vap, mac, op, reason);
 			if (reason == IEEE80211_STATUS_SUCCESS) {
@@ -1437,7 +1485,6 @@
 			ieee80211_free_node(ni);
 		} else
 			error = ENOENT;
-		IEEE80211_NODE_UNLOCK(nt);
 		break;
 	default:
 		error = EINVAL;
@@ -1543,7 +1590,9 @@
 	    mlme.im_op == IEEE80211_MLME_ASSOC)
 		return setmlme_assoc_sta(vap, mlme.im_macaddr,
 		    vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
-	else if (mlme.im_op == IEEE80211_MLME_ASSOC)
+	else if ((vap->iv_opmode == IEEE80211_M_IBSS || 
+	    vap->iv_opmode == IEEE80211_M_AHDEMO) && 
+	    mlme.im_op == IEEE80211_MLME_ASSOC)
 		return setmlme_assoc_adhoc(vap, mlme.im_macaddr,
 		    mlme.im_ssid_len, mlme.im_ssid);
 	else
@@ -1922,9 +1971,10 @@
 			/* XXX need state machine for other vap's to follow */
 			ieee80211_setcurchan(ic, vap->iv_des_chan);
 			vap->iv_bss->ni_chan = ic->ic_curchan;
-		} else
+		} else {
 			ic->ic_curchan = vap->iv_des_chan;
 			ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
+		}
 	} else {
 		/*
 		 * Need to go through the state machine in case we
@@ -2939,6 +2989,24 @@
 		if (isvap11g(vap))
 			error = ENETRESET;
 		break;
+	case IEEE80211_IOC_QUIET:
+		vap->iv_quiet= ireq->i_val;
+		break;
+	case IEEE80211_IOC_QUIET_COUNT:
+		vap->iv_quiet_count=ireq->i_val;
+		break;
+	case IEEE80211_IOC_QUIET_PERIOD:
+		vap->iv_quiet_period=ireq->i_val;
+		break;
+	case IEEE80211_IOC_QUIET_OFFSET:
+		vap->iv_quiet_offset=ireq->i_val;
+		break;
+	case IEEE80211_IOC_QUIET_DUR:
+		if(ireq->i_val < vap->iv_bss->ni_intval)
+			vap->iv_quiet_duration = ireq->i_val;
+		else
+			error = EINVAL;
+		break;
 	case IEEE80211_IOC_BGSCAN:
 		if (ireq->i_val) {
 			if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0)

Modified: trunk/sys/net80211/ieee80211_ioctl.h
===================================================================
--- trunk/sys/net80211/ieee80211_ioctl.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ioctl.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_ioctl.h 246501 2013-02-07 21:20:28Z monthadar $
  */
 #ifndef _NET80211_IEEE80211_IOCTL_H_
 #define _NET80211_IEEE80211_IOCTL_H_
@@ -241,8 +242,12 @@
 	uint32_t	is_mesh_notproxy;	/* dropped 'cuz not proxying */
 	uint32_t	is_rx_badalign;		/* dropped 'cuz misaligned */
 	uint32_t	is_hwmp_proxy;		/* PREP for proxy route */
-	
-	uint32_t	is_spare[11];
+	uint32_t	is_beacon_bad;		/* Number of bad beacons */
+	uint32_t	is_ampdu_bar_tx;	/* A-MPDU BAR frames TXed */
+	uint32_t	is_ampdu_bar_tx_retry;	/* A-MPDU BAR frames TX rtry */
+	uint32_t	is_ampdu_bar_tx_fail;	/* A-MPDU BAR frames TX fail */
+
+	uint32_t	is_spare[7];
 };
 
 /*
@@ -335,8 +340,10 @@
 
 struct ieee80211req_mesh_route {
 	uint8_t		imr_flags;
-#define	IEEE80211_MESHRT_FLAGS_VALID	0x01
-#define	IEEE80211_MESHRT_FLAGS_PROXY	0x02
+#define	IEEE80211_MESHRT_FLAGS_DISCOVER	0x01
+#define	IEEE80211_MESHRT_FLAGS_VALID	0x02
+#define	IEEE80211_MESHRT_FLAGS_PROXY	0x04
+#define	IEEE80211_MESHRT_FLAGS_GATE	0x08
 	uint8_t		imr_dest[IEEE80211_ADDR_LEN];
 	uint8_t		imr_nexthop[IEEE80211_ADDR_LEN];
 	uint16_t	imr_nhops;
@@ -705,6 +712,7 @@
 #define	IEEE80211_IOC_MESH_PR_SIG	178	/* mesh sig protocol */
 #define	IEEE80211_IOC_MESH_PR_CC	179	/* mesh congestion protocol */
 #define	IEEE80211_IOC_MESH_PR_AUTH	180	/* mesh auth protocol */
+#define	IEEE80211_IOC_MESH_GATE		181	/* mesh gate XXX: 173? */
 
 #define	IEEE80211_IOC_HWMP_ROOTMODE	190	/* HWMP root mode */
 #define	IEEE80211_IOC_HWMP_MAXHOPS	191	/* number of hops before drop */
@@ -715,6 +723,11 @@
 #define	IEEE80211_IOC_TDMA_SLOTLEN	203	/* TDMA: slot length (usecs) */
 #define	IEEE80211_IOC_TDMA_BINTERVAL	204	/* TDMA: beacon intvl (slots) */
 
+#define	IEEE80211_IOC_QUIET		205	/* Quiet Enable/Disable */
+#define	IEEE80211_IOC_QUIET_PERIOD	206	/* Quiet Period */
+#define	IEEE80211_IOC_QUIET_OFFSET	207	/* Quiet Offset */
+#define	IEEE80211_IOC_QUIET_DUR		208	/* Quiet Duration */
+#define	IEEE80211_IOC_QUIET_COUNT	209	/* Quiet Count */
 /*
  * Parameters for controlling a scan requested with
  * IEEE80211_IOC_SCAN_REQ.

Modified: trunk/sys/net80211/ieee80211_mesh.c
===================================================================
--- trunk/sys/net80211/ieee80211_mesh.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_mesh.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*- 
  * Copyright (c) 2009 The FreeBSD Foundation 
  * All rights reserved. 
@@ -28,7 +29,7 @@
  */ 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_mesh.c 322061 2017-08-04 20:24:23Z pfg $");
 #endif
 
 /*
@@ -52,6 +53,7 @@
 #include <sys/proc.h>
 #include <sys/sysctl.h>
 
+#include <net/bpf.h>
 #include <net/if.h>
 #include <net/if_media.h>
 #include <net/if_llc.h>
@@ -59,6 +61,9 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_action.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
 #include <net80211/ieee80211_input.h>
 #include <net80211/ieee80211_mesh.h>
 
@@ -68,6 +73,8 @@
 static void	mesh_vattach(struct ieee80211vap *);
 static int	mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int);
 static void	mesh_rt_cleanup_cb(void *);
+static void	mesh_gatemode_setup(struct ieee80211vap *);
+static void	mesh_gatemode_cb(void *);
 static void	mesh_linkchange(struct ieee80211_node *,
 		    enum ieee80211_mesh_mlstate);
 static void	mesh_checkid(void *, struct ieee80211_node *);
@@ -74,9 +81,8 @@
 static uint32_t	mesh_generateid(struct ieee80211vap *);
 static int	mesh_checkpseq(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN], uint32_t);
-static struct ieee80211_node *
-		mesh_find_txnode(struct ieee80211vap *,
-		    const uint8_t [IEEE80211_ADDR_LEN]);
+static void	mesh_transmit_to_gate(struct ieee80211vap *, struct mbuf *,
+		    struct ieee80211_mesh_route *);
 static void	mesh_forward(struct ieee80211vap *, struct mbuf *,
 		    const struct ieee80211_meshcntl *);
 static int	mesh_input(struct ieee80211_node *, struct mbuf *, int, int);
@@ -99,11 +105,16 @@
  */
 static SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0,
     "IEEE 802.11s parameters");
+static int	ieee80211_mesh_gateint = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, gateint, CTLTYPE_INT | CTLFLAG_RW,
+    &ieee80211_mesh_gateint, 0, ieee80211_sysctl_msecs_ticks, "I",
+    "mesh gate interval (ms)");
 static int ieee80211_mesh_retrytimeout = -1;
 SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW,
     &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
     "Retry timeout (msec)");
 static int ieee80211_mesh_holdingtimeout = -1;
+
 SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW,
     &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
     "Holding state timeout (msec)");
@@ -111,10 +122,20 @@
 SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW,
     &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
     "Confirm state timeout (msec)");
+static int ieee80211_mesh_backofftimeout = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, backofftimeout, CTLTYPE_INT | CTLFLAG_RW,
+    &ieee80211_mesh_backofftimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+    "Backoff timeout (msec). This is to throutles peering forever when "
+    "not receiving answer or is rejected by a neighbor");
 static int ieee80211_mesh_maxretries = 2;
-SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLFLAG_RW,
     &ieee80211_mesh_maxretries, 0,
     "Maximum retries during peer link establishment");
+static int ieee80211_mesh_maxholding = 2;
+SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxholding, CTLFLAG_RW,
+    &ieee80211_mesh_maxholding, 0,
+    "Maximum times we are allowed to transition to HOLDING state before "
+    "backinoff during peer link establishment");
 
 static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
 	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -122,14 +143,14 @@
 static	ieee80211_recv_action_func mesh_recv_action_meshpeering_open;
 static	ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm;
 static	ieee80211_recv_action_func mesh_recv_action_meshpeering_close;
-static	ieee80211_recv_action_func mesh_recv_action_meshlmetric_req;
-static	ieee80211_recv_action_func mesh_recv_action_meshlmetric_rep;
+static	ieee80211_recv_action_func mesh_recv_action_meshlmetric;
+static	ieee80211_recv_action_func mesh_recv_action_meshgate;
 
 static	ieee80211_send_action_func mesh_send_action_meshpeering_open;
 static	ieee80211_send_action_func mesh_send_action_meshpeering_confirm;
 static	ieee80211_send_action_func mesh_send_action_meshpeering_close;
-static	ieee80211_send_action_func mesh_send_action_meshlink_request;
-static	ieee80211_send_action_func mesh_send_action_meshlink_reply;
+static	ieee80211_send_action_func mesh_send_action_meshlmetric;
+static	ieee80211_send_action_func mesh_send_action_meshgate;
 
 static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
 	.mpm_descr	= "AIRTIME",
@@ -140,12 +161,24 @@
 static struct ieee80211_mesh_proto_path		mesh_proto_paths[4];
 static struct ieee80211_mesh_proto_metric	mesh_proto_metrics[4];
 
+#define	RT_ENTRY_LOCK(rt)	mtx_lock(&(rt)->rt_lock)
+#define	RT_ENTRY_LOCK_ASSERT(rt) mtx_assert(&(rt)->rt_lock, MA_OWNED)
+#define	RT_ENTRY_UNLOCK(rt)	mtx_unlock(&(rt)->rt_lock)
+
 #define	MESH_RT_LOCK(ms)	mtx_lock(&(ms)->ms_rt_lock)
 #define	MESH_RT_LOCK_ASSERT(ms)	mtx_assert(&(ms)->ms_rt_lock, MA_OWNED)
 #define	MESH_RT_UNLOCK(ms)	mtx_unlock(&(ms)->ms_rt_lock)
 
-MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table");
+MALLOC_DEFINE(M_80211_MESH_PREQ, "80211preq", "802.11 MESH Path Request frame");
+MALLOC_DEFINE(M_80211_MESH_PREP, "80211prep", "802.11 MESH Path Reply frame");
+MALLOC_DEFINE(M_80211_MESH_PERR, "80211perr", "802.11 MESH Path Error frame");
 
+/* The longer one of the lifetime should be stored as new lifetime */
+#define MESH_ROUTE_LIFETIME_MAX(a, b)	(a > b ? a : b)
+
+MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh_rt", "802.11s routing table");
+MALLOC_DEFINE(M_80211_MESH_GT_RT, "80211mesh_gt", "802.11s known gates table");
+
 /*
  * Helper functions to manipulate the Mesh routing table.
  */
@@ -166,9 +199,10 @@
 }
 
 static struct ieee80211_mesh_route *
-mesh_rt_add_locked(struct ieee80211_mesh_state *ms,
+mesh_rt_add_locked(struct ieee80211vap *vap,
     const uint8_t dest[IEEE80211_ADDR_LEN])
 {
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211_mesh_route *rt;
 
 	KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest),
@@ -179,9 +213,12 @@
 	rt = malloc(ALIGN(sizeof(struct ieee80211_mesh_route)) +
 	    ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, M_NOWAIT | M_ZERO);
 	if (rt != NULL) {
+		rt->rt_vap = vap;
 		IEEE80211_ADDR_COPY(rt->rt_dest, dest);
 		rt->rt_priv = (void *)ALIGN(&rt[1]);
-		rt->rt_crtime = ticks;
+		mtx_init(&rt->rt_lock, "MBSS_RT", "802.11s route entry", MTX_DEF);
+		callout_init(&rt->rt_discovery, 1);
+		rt->rt_updtime = ticks;	/* create time */
 		TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
 	}
 	return rt;
@@ -213,12 +250,56 @@
 	    ("%s: adding self to the routing table", __func__));
 
 	MESH_RT_LOCK(ms);
-	rt = mesh_rt_add_locked(ms, dest);
+	rt = mesh_rt_add_locked(vap, dest);
 	MESH_RT_UNLOCK(ms);
 	return rt;
 }
 
 /*
+ * Update the route lifetime and returns the updated lifetime.
+ * If new_lifetime is zero and route is timedout it will be invalidated.
+ * new_lifetime is in msec
+ */
+int
+ieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int new_lifetime)
+{
+	int timesince, now;
+	uint32_t lifetime = 0;
+
+	KASSERT(rt != NULL, ("route is NULL"));
+
+	now = ticks;
+	RT_ENTRY_LOCK(rt);
+
+	/* dont clobber a proxy entry gated by us */
+	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY && rt->rt_nhops == 0) {
+		RT_ENTRY_UNLOCK(rt);
+		return rt->rt_lifetime;
+	}
+
+	timesince = ticks_to_msecs(now - rt->rt_updtime);
+	rt->rt_updtime = now;
+	if (timesince >= rt->rt_lifetime) {
+		if (new_lifetime != 0) {
+			rt->rt_lifetime = new_lifetime;
+		}
+		else {
+			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
+			rt->rt_lifetime = 0;
+		}
+	} else {
+		/* update what is left of lifetime */
+		rt->rt_lifetime = rt->rt_lifetime - timesince;
+		rt->rt_lifetime  = MESH_ROUTE_LIFETIME_MAX(
+			new_lifetime, rt->rt_lifetime);
+	}
+	lifetime = rt->rt_lifetime;
+	RT_ENTRY_UNLOCK(rt);
+
+	return lifetime;
+}
+
+/*
  * Add a proxy route (as needed) for the specified destination.
  */
 void
@@ -231,7 +312,7 @@
 	MESH_RT_LOCK(ms);
 	rt = mesh_rt_find_locked(ms, dest);
 	if (rt == NULL) {
-		rt = mesh_rt_add_locked(ms, dest);
+		rt = mesh_rt_add_locked(vap, dest);
 		if (rt == NULL) {
 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
 			    "%s", "unable to add proxy entry");
@@ -239,12 +320,14 @@
 		} else {
 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
 			    "%s", "add proxy entry");
+			IEEE80211_ADDR_COPY(rt->rt_mesh_gate, vap->iv_myaddr);
 			IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
 			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
 				     |  IEEE80211_MESHRT_FLAGS_PROXY;
 		}
-	/* XXX assert PROXY? */
 	} else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
+		KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
+		    ("no proxy flag for poxy entry"));
 		struct ieee80211com *ic = vap->iv_ic;
 		/*
 		 * Fix existing entry created by received frames from
@@ -269,6 +352,13 @@
 mesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt)
 {
 	TAILQ_REMOVE(&ms->ms_routes, rt, rt_next);
+	/*
+	 * Grab the lock before destroying it, to be sure no one else
+	 * is holding the route.
+	 */
+	RT_ENTRY_LOCK(rt);
+	callout_drain(&rt->rt_discovery);
+	mtx_destroy(&rt->rt_lock);
 	free(rt, M_80211_MESH_RT);
 }
 
@@ -282,6 +372,13 @@
 	MESH_RT_LOCK(ms);
 	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
 		if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) {
+			if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+				ms->ms_ppath->mpp_senderror(vap, dest, rt,
+				    IEEE80211_REASON_MESH_PERR_NO_PROXY);
+			} else {
+				ms->ms_ppath->mpp_senderror(vap, dest, rt,
+				    IEEE80211_REASON_MESH_PERR_DEST_UNREACH);
+			}
 			mesh_rt_del(ms, rt);
 			MESH_RT_UNLOCK(ms);
 			return;
@@ -333,8 +430,11 @@
 		return;
 	MESH_RT_LOCK(ms);
 	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
-		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 &&
-		    ticks - rt->rt_crtime >= ms->ms_ppath->mpp_inact)
+		/* Discover paths will be deleted by their own callout */
+		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER)
+			continue;
+		ieee80211_mesh_rt_update(rt, 0);
+		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
 			mesh_rt_del(ms, rt);
 	}
 	MESH_RT_UNLOCK(ms);
@@ -412,6 +512,48 @@
 #undef	N
 
 static void
+mesh_gatemode_setup(struct ieee80211vap *vap)
+{
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+	/*
+	 * NB: When a mesh gate is running as a ROOT it shall
+	 * not send out periodic GANNs but instead mark the
+	 * mesh gate flag for the corresponding proactive PREQ
+	 * and RANN frames.
+	 */
+	if (ms->ms_flags & IEEE80211_MESHFLAGS_ROOT ||
+	    (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) == 0) {
+		callout_drain(&ms->ms_gatetimer);
+		return ;
+	}
+	callout_reset(&ms->ms_gatetimer, ieee80211_mesh_gateint,
+	    mesh_gatemode_cb, vap);
+}
+
+static void
+mesh_gatemode_cb(void *arg)
+{
+	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_meshgann_ie gann;
+
+	gann.gann_flags = 0; /* Reserved */
+	gann.gann_hopcount = 0;
+	gann.gann_ttl = ms->ms_ttl;
+	IEEE80211_ADDR_COPY(gann.gann_addr, vap->iv_myaddr);
+	gann.gann_seq = ms->ms_gateseq++;
+	gann.gann_interval = ieee80211_mesh_gateint;
+
+	IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, vap->iv_bss,
+	    "send broadcast GANN (seq %u)", gann.gann_seq);
+
+	ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_GANN, &gann);
+	mesh_gatemode_setup(vap);
+}
+
+static void
 ieee80211_mesh_init(void)
 {
 
@@ -421,42 +563,44 @@
 	/*
 	 * Setup mesh parameters that depends on the clock frequency.
 	 */
+	ieee80211_mesh_gateint = msecs_to_ticks(10000);
 	ieee80211_mesh_retrytimeout = msecs_to_ticks(40);
 	ieee80211_mesh_holdingtimeout = msecs_to_ticks(40);
 	ieee80211_mesh_confirmtimeout = msecs_to_ticks(40);
+	ieee80211_mesh_backofftimeout = msecs_to_ticks(5000);
 
 	/*
 	 * Register action frame handlers.
 	 */
-	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
 	    IEEE80211_ACTION_MESHPEERING_OPEN,
 	    mesh_recv_action_meshpeering_open);
-	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
 	    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 	    mesh_recv_action_meshpeering_confirm);
-	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
 	    IEEE80211_ACTION_MESHPEERING_CLOSE,
 	    mesh_recv_action_meshpeering_close);
-	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC,
-	    IEEE80211_ACTION_MESHLMETRIC_REQ, mesh_recv_action_meshlmetric_req);
-	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC,
-	    IEEE80211_ACTION_MESHLMETRIC_REP, mesh_recv_action_meshlmetric_rep);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_GANN, mesh_recv_action_meshgate);
 
-	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING, 
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
 	    IEEE80211_ACTION_MESHPEERING_OPEN,
 	    mesh_send_action_meshpeering_open);
-	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING, 
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
 	    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 	    mesh_send_action_meshpeering_confirm);
-	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING, 
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
 	    IEEE80211_ACTION_MESHPEERING_CLOSE,
 	    mesh_send_action_meshpeering_close);
-	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC, 
-	    IEEE80211_ACTION_MESHLMETRIC_REQ,
-	    mesh_send_action_meshlink_request);
-	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC, 
-	    IEEE80211_ACTION_MESHLMETRIC_REP,
-	    mesh_send_action_meshlink_reply);
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_LMETRIC,
+	    mesh_send_action_meshlmetric);
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_GANN,
+	    mesh_send_action_meshgate);
 
 	/*
 	 * Register Airtime Link Metric.
@@ -488,7 +632,7 @@
 		args[1] = ni->ni_mllid;
 		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
 		    args);
 	}
@@ -532,9 +676,12 @@
 	ms->ms_seq = 0;
 	ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD);
 	ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL;
+	TAILQ_INIT(&ms->ms_known_gates);
 	TAILQ_INIT(&ms->ms_routes);
 	mtx_init(&ms->ms_rt_lock, "MBSS", "802.11s routing table", MTX_DEF);
-	callout_init(&ms->ms_cleantimer, CALLOUT_MPSAFE);
+	callout_init(&ms->ms_cleantimer, 1);
+	callout_init(&ms->ms_gatetimer, 1);
+	ms->ms_gateseq = 0;
 	mesh_select_proto_metric(vap, "AIRTIME");
 	KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL"));
 	mesh_select_proto_path(vap, "HWMP");
@@ -563,8 +710,10 @@
 	if (ostate != IEEE80211_S_SCAN)
 		ieee80211_cancel_scan(vap);	/* background scan */
 	ni = vap->iv_bss;			/* NB: no reference held */
-	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
+	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) {
 		callout_drain(&ms->ms_cleantimer);
+		callout_drain(&ms->ms_gatetimer);
+	}
 	switch (nstate) {
 	case IEEE80211_S_INIT:
 		switch (ostate) {
@@ -689,6 +838,7 @@
 		ieee80211_node_authorize(vap->iv_bss);
 		callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
                     mesh_rt_cleanup_cb, vap);
+		mesh_gatemode_setup(vap);
 		break;
 	default:
 		break;
@@ -709,7 +859,44 @@
 	    mesh_rt_cleanup_cb, vap);
 }
 
+/*
+ * Mark a mesh STA as gate and return a pointer to it.
+ * If this is first time, we create a new gate route.
+ * Always update the path route to this mesh gate.
+ */
+struct ieee80211_mesh_gate_route *
+ieee80211_mesh_mark_gate(struct ieee80211vap *vap, const uint8_t *addr,
+    struct ieee80211_mesh_route *rt)
+{
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_mesh_gate_route *gr = NULL, *next;
+	int found = 0;
 
+	MESH_RT_LOCK(ms);
+	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
+		if (IEEE80211_ADDR_EQ(gr->gr_addr, addr)) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		/* New mesh gate add it to known table. */
+		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, addr,
+		    "%s", "stored new gate information from pro-PREQ.");
+		gr = malloc(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
+		    M_80211_MESH_GT_RT, M_NOWAIT | M_ZERO);
+		IEEE80211_ADDR_COPY(gr->gr_addr, addr);
+		TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
+	}
+	gr->gr_route = rt;
+	/* TODO: link from path route to gate route */
+	MESH_RT_UNLOCK(ms);
+
+	return gr;
+}
+
+
 /*
  * Helper function to note the Mesh Peer Link FSM change.
  */
@@ -820,8 +1007,8 @@
 /*
  * Iterate the routing table and locate the next hop.
  */
-static struct ieee80211_node *
-mesh_find_txnode(struct ieee80211vap *vap,
+struct ieee80211_node *
+ieee80211_mesh_find_txnode(struct ieee80211vap *vap,
     const uint8_t dest[IEEE80211_ADDR_LEN])
 {
 	struct ieee80211_mesh_route *rt;
@@ -829,17 +1016,130 @@
 	rt = ieee80211_mesh_rt_find(vap, dest);
 	if (rt == NULL)
 		return NULL;
-	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
-	    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) {
+	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
-		    "%s: !valid or proxy, flags 0x%x", __func__, rt->rt_flags);
+		    "%s: !valid, flags 0x%x", __func__, rt->rt_flags);
 		/* XXX stat */
 		return NULL;
 	}
+	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+		rt = ieee80211_mesh_rt_find(vap, rt->rt_mesh_gate);
+		if (rt == NULL) return NULL;
+		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
+			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
+			    "%s: meshgate !valid, flags 0x%x", __func__,
+			    rt->rt_flags);
+			/* XXX stat */
+			return NULL;
+		}
+	}
 	return ieee80211_find_txnode(vap, rt->rt_nexthop);
 }
 
+static void
+mesh_transmit_to_gate(struct ieee80211vap *vap, struct mbuf *m,
+    struct ieee80211_mesh_route *rt_gate)
+{
+	struct ifnet *ifp = vap->iv_ifp;
+	struct ieee80211_node *ni;
+
+	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+	ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest);
+	if (ni == NULL) {
+		ifp->if_oerrors++;
+		m_freem(m);
+		return;
+	}
+
+	/*
+	 * Send through the VAP packet transmit path.
+	 * This consumes the node ref grabbed above and
+	 * the mbuf, regardless of whether there's a problem
+	 * or not.
+	 */
+	(void) ieee80211_vap_pkt_send_dest(vap, m, ni);
+}
+
 /*
+ * Forward the queued frames to known valid mesh gates.
+ * Assume destination to be outside the MBSS (i.e. proxy entry),
+ * If no valid mesh gates are known silently discard queued frames.
+ * After transmitting frames to all known valid mesh gates, this route
+ * will be marked invalid, and a new path discovery will happen in the hopes
+ * that (at least) one of the mesh gates have a new proxy entry for us to use.
+ */
+void
+ieee80211_mesh_forward_to_gates(struct ieee80211vap *vap,
+    struct ieee80211_mesh_route *rt_dest)
+{
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_mesh_route *rt_gate;
+	struct ieee80211_mesh_gate_route *gr = NULL, *gr_next;
+	struct mbuf *m, *mcopy, *next;
+
+	IEEE80211_TX_UNLOCK_ASSERT(ic);
+
+	KASSERT( rt_dest->rt_flags == IEEE80211_MESHRT_FLAGS_DISCOVER,
+	    ("Route is not marked with IEEE80211_MESHRT_FLAGS_DISCOVER"));
+
+	/* XXX: send to more than one valid mash gate */
+	MESH_RT_LOCK(ms);
+
+	m = ieee80211_ageq_remove(&ic->ic_stageq,
+	    (struct ieee80211_node *)(uintptr_t)
+	    ieee80211_mac_hash(ic, rt_dest->rt_dest));
+
+	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, gr_next) {
+		rt_gate = gr->gr_route;
+		if (rt_gate == NULL) {
+			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
+				rt_dest->rt_dest,
+				"mesh gate with no path %6D",
+				gr->gr_addr, ":");
+			continue;
+		}
+		if ((rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
+			continue;
+		KASSERT(rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_GATE,
+		    ("route not marked as a mesh gate"));
+		KASSERT((rt_gate->rt_flags &
+			IEEE80211_MESHRT_FLAGS_PROXY) == 0,
+			("found mesh gate that is also marked porxy"));
+		/*
+		 * convert route to a proxy route gated by the current
+		 * mesh gate, this is needed so encap can built data
+		 * frame with correct address.
+		 */
+		rt_dest->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
+			IEEE80211_MESHRT_FLAGS_VALID;
+		rt_dest->rt_ext_seq = 1; /* random value */
+		IEEE80211_ADDR_COPY(rt_dest->rt_mesh_gate, rt_gate->rt_dest);
+		IEEE80211_ADDR_COPY(rt_dest->rt_nexthop, rt_gate->rt_nexthop);
+		rt_dest->rt_metric = rt_gate->rt_metric;
+		rt_dest->rt_nhops = rt_gate->rt_nhops;
+		ieee80211_mesh_rt_update(rt_dest, ms->ms_ppath->mpp_inact);
+		MESH_RT_UNLOCK(ms);
+		/* XXX: lock?? */
+		mcopy = m_dup(m, M_NOWAIT);
+		for (; mcopy != NULL; mcopy = next) {
+			next = mcopy->m_nextpkt;
+			mcopy->m_nextpkt = NULL;
+			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
+			    rt_dest->rt_dest,
+			    "flush queued frame %p len %d", mcopy,
+			    mcopy->m_pkthdr.len);
+			mesh_transmit_to_gate(vap, mcopy, rt_gate);
+		}
+		MESH_RT_LOCK(ms);
+	}
+	rt_dest->rt_flags = 0; /* Mark invalid */
+	m_freem(m);
+	MESH_RT_UNLOCK(ms);
+}
+
+/*
  * Forward the specified frame.
  * Decrement the TTL and set TA to our MAC address.
  */
@@ -850,7 +1150,6 @@
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ifnet *ifp = vap->iv_ifp;
-	struct ifnet *parent = ic->ic_ifp;
 	const struct ieee80211_frame *wh =
 	    mtod(m, const struct ieee80211_frame *);
 	struct mbuf *mcopy;
@@ -859,9 +1158,17 @@
 	struct ieee80211_node *ni;
 	int err;
 
-	if (mc->mc_ttl == 0) {
+	/* This is called from the RX path - don't hold this lock */
+	IEEE80211_TX_UNLOCK_ASSERT(ic);
+
+	/*
+	 * mesh ttl of 1 means we are the last one receving it,
+	 * according to amendment we decrement and then check if
+	 * 0, if so we dont forward.
+	 */
+	if (mc->mc_ttl < 1) {
 		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
-		    "%s", "frame not fwd'd, ttl 0");
+		    "%s", "frame not fwd'd, ttl 1");
 		vap->iv_stats.is_mesh_fwd_ttl++;
 		return;
 	}
@@ -871,7 +1178,7 @@
 		vap->iv_stats.is_mesh_fwd_disabled++;
 		return;
 	}
-	mcopy = m_dup(m, M_DONTWAIT);
+	mcopy = m_dup(m, M_NOWAIT);
 	if (mcopy == NULL) {
 		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
 		    "%s", "frame not fwd'd, cannot dup");
@@ -899,10 +1206,18 @@
 		ni = ieee80211_ref_node(vap->iv_bss);
 		mcopy->m_flags |= M_MCAST;
 	} else {
-		ni = mesh_find_txnode(vap, whcopy->i_addr3);
+		ni = ieee80211_mesh_find_txnode(vap, whcopy->i_addr3);
 		if (ni == NULL) {
+			/*
+			 * [Optional] any of the following three actions:
+			 * o silently discard
+			 * o trigger a path discovery
+			 * o inform TA that meshDA is unknown.
+			 */
 			IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
 			    "%s", "frame not fwd'd, no path");
+			ms->ms_ppath->mpp_senderror(vap, whcopy->i_addr3, NULL,
+			    IEEE80211_REASON_MESH_PERR_NO_FI);
 			vap->iv_stats.is_mesh_fwd_nopath++;
 			m_freem(mcopy);
 			return;
@@ -917,7 +1232,20 @@
 
 	/* XXX do we know m_nextpkt is NULL? */
 	mcopy->m_pkthdr.rcvif = (void *) ni;
-	err = parent->if_transmit(parent, mcopy);
+
+	/*
+	 * XXX this bypasses all of the VAP TX handling; it passes frames
+	 * directly to the parent interface.
+	 *
+	 * Because of this, there's no TX lock being held as there's no
+	 * encaps state being used.
+	 *
+	 * Doing a direct parent transmit may not be the correct thing
+	 * to do here; we'll have to re-think this soon.
+	 */
+	IEEE80211_TX_LOCK(ic);
+	err = ieee80211_parent_xmitpkt(ic, mcopy);
+	IEEE80211_TX_UNLOCK(ic);
 	if (err != 0) {
 		/* NB: IFQ_HANDOFF reclaims mbuf */
 		ieee80211_free_node(ni);
@@ -929,9 +1257,10 @@
 static struct mbuf *
 mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
 {
-#define	WHDIR(wh) ((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK)
+#define	WHDIR(wh)	((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK)
+#define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
 	uint8_t b[sizeof(struct ieee80211_qosframe_addr4) +
-		  sizeof(struct ieee80211_meshcntl_ae11)];
+		  sizeof(struct ieee80211_meshcntl_ae10)];
 	const struct ieee80211_qosframe_addr4 *wh;
 	const struct ieee80211_meshcntl_ae10 *mc;
 	struct ether_header *eh;
@@ -965,13 +1294,14 @@
 		m_adj(m, hdrlen - sizeof(*eh));
 	}
 	eh = mtod(m, struct ether_header *);
-	ae = mc->mc_flags & 3;
+	ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
 	if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) {
 		IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1);
-		if (ae == 0) {
+		if (ae == IEEE80211_MESH_AE_00) {
 			IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3);
-		} else if (ae == 1) {
-			IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr4);
+		} else if (ae == IEEE80211_MESH_AE_01) {
+			IEEE80211_ADDR_COPY(eh->ether_shost,
+			    MC01(mc)->mc_addr4);
 		} else {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
 			    (const struct ieee80211_frame *)wh, NULL,
@@ -981,12 +1311,12 @@
 			return NULL;
 		}
 	} else {
-		if (ae == 0) {
+		if (ae == IEEE80211_MESH_AE_00) {
 			IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3);
 			IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4);
-		} else if (ae == 2) {
-			IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr4);
-			IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr5);
+		} else if (ae == IEEE80211_MESH_AE_10) {
+			IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr5);
+			IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr6);
 		} else {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
 			    (const struct ieee80211_frame *)wh, NULL,
@@ -996,19 +1326,20 @@
 			return NULL;
 		}
 	}
-#ifdef ALIGNED_POINTER
+#ifndef __NO_STRICT_ALIGNMENT
 	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
 		m = ieee80211_realign(vap, m, sizeof(*eh));
 		if (m == NULL)
 			return NULL;
 	}
-#endif /* ALIGNED_POINTER */
+#endif /* !__NO_STRICT_ALIGNMENT */
 	if (llc != NULL) {
 		eh = mtod(m, struct ether_header *);
 		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
 	}
 	return m;
-#undef WDIR
+#undef	WDIR
+#undef	MC01
 }
 
 /*
@@ -1024,12 +1355,13 @@
 
 	KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS,
 	    ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
-	KASSERT(ae == 0 || ae == 2, ("bad AE %d", ae));
-	if (ae == 2) {				/* ucast w/ proxy */
+	KASSERT(ae == IEEE80211_MESH_AE_00 || ae == IEEE80211_MESH_AE_10,
+	    ("bad AE %d", ae));
+	if (ae == IEEE80211_MESH_AE_10) {	/* ucast w/ proxy */
 		const struct ieee80211_meshcntl_ae10 *mc10 =
 		    (const struct ieee80211_meshcntl_ae10 *) mc;
 		struct ieee80211_mesh_route *rt =
-		    ieee80211_mesh_rt_find(vap, mc10->mc_addr4);
+		    ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
 		/* check for proxy route to ourself */
 		return (rt != NULL &&
 		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY));
@@ -1037,19 +1369,184 @@
 		return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
 }
 
+/*
+ * Verifies transmitter, updates lifetime, precursor list and forwards data.
+ * > 0 means we have forwarded data and no need to process locally
+ * == 0 means we want to process locally (and we may have forwarded data
+ * < 0 means there was an error and data should be discarded
+ */
 static int
+mesh_recv_indiv_data_to_fwrd(struct ieee80211vap *vap, struct mbuf *m,
+    struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
+{
+	struct ieee80211_qosframe_addr4 *qwh;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_mesh_route *rt_meshda, *rt_meshsa;
+
+	/* This is called from the RX path - don't hold this lock */
+	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+	qwh = (struct ieee80211_qosframe_addr4 *)wh;
+
+	/*
+	 * TODO:
+	 * o verify addr2 is  a legitimate transmitter
+	 * o lifetime of precursor of addr3 (addr2) is max(init, curr)
+	 * o lifetime of precursor of addr4 (nexthop) is max(init, curr)
+	 */
+
+	/* set lifetime of addr3 (meshDA) to initial value */
+	rt_meshda = ieee80211_mesh_rt_find(vap, qwh->i_addr3);
+	if (rt_meshda == NULL) {
+		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2,
+		    "no route to meshDA(%6D)", qwh->i_addr3, ":");
+		/*
+		 * [Optional] any of the following three actions:
+		 * o silently discard 				[X]
+		 * o trigger a path discovery			[ ]
+		 * o inform TA that meshDA is unknown.		[ ]
+		 */
+		/* XXX: stats */
+		return (-1);
+	}
+
+	ieee80211_mesh_rt_update(rt_meshda, ticks_to_msecs(
+	    ms->ms_ppath->mpp_inact));
+
+	/* set lifetime of addr4 (meshSA) to initial value */
+	rt_meshsa = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
+	KASSERT(rt_meshsa != NULL, ("no route"));
+	ieee80211_mesh_rt_update(rt_meshsa, ticks_to_msecs(
+	    ms->ms_ppath->mpp_inact));
+
+	mesh_forward(vap, m, mc);
+	return (1); /* dont process locally */
+}
+
+/*
+ * Verifies transmitter, updates lifetime, precursor list and process data
+ * locally, if data is proxy with AE = 10 it could mean data should go
+ * on another mesh path or data should be forwarded to the DS.
+ *
+ * > 0 means we have forwarded data and no need to process locally
+ * == 0 means we want to process locally (and we may have forwarded data
+ * < 0 means there was an error and data should be discarded
+ */
+static int
+mesh_recv_indiv_data_to_me(struct ieee80211vap *vap, struct mbuf *m,
+    struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
+{
+	struct ieee80211_qosframe_addr4 *qwh;
+	const struct ieee80211_meshcntl_ae10 *mc10;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_mesh_route *rt;
+	int ae;
+
+	/* This is called from the RX path - don't hold this lock */
+	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+	qwh = (struct ieee80211_qosframe_addr4 *)wh;
+	mc10 = (const struct ieee80211_meshcntl_ae10 *)mc;
+
+	/*
+	 * TODO:
+	 * o verify addr2 is  a legitimate transmitter
+	 * o lifetime of precursor entry is max(init, curr)
+	 */
+
+	/* set lifetime of addr4 (meshSA) to initial value */
+	rt = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
+	KASSERT(rt != NULL, ("no route"));
+	ieee80211_mesh_rt_update(rt, ticks_to_msecs(ms->ms_ppath->mpp_inact));
+	rt = NULL;
+
+	ae = mc10->mc_flags & IEEE80211_MESH_AE_MASK;
+	KASSERT(ae == IEEE80211_MESH_AE_00 ||
+	    ae == IEEE80211_MESH_AE_10, ("bad AE %d", ae));
+	if (ae == IEEE80211_MESH_AE_10) {
+		if (IEEE80211_ADDR_EQ(mc10->mc_addr5, qwh->i_addr3)) {
+			return (0); /* process locally */
+		}
+
+		rt =  ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
+		if (rt != NULL &&
+		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
+		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) == 0) {
+			/*
+			 * Forward on another mesh-path, according to
+			 * amendment as specified in 9.32.4.1
+			 */
+			IEEE80211_ADDR_COPY(qwh->i_addr3, mc10->mc_addr5);
+			mesh_forward(vap, m,
+			    (const struct ieee80211_meshcntl *)mc10);
+			return (1); /* dont process locally */
+		}
+		/*
+		 * All other cases: forward of MSDUs from the MBSS to DS indiv.
+		 * addressed according to 13.11.3.2.
+		 */
+		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, qwh->i_addr2,
+		    "forward frame to DS, SA(%6D) DA(%6D)",
+		    mc10->mc_addr6, ":", mc10->mc_addr5, ":");
+	}
+	return (0); /* process locally */
+}
+
+/*
+ * Try to forward the group addressed data on to other mesh STAs, and
+ * also to the DS.
+ *
+ * > 0 means we have forwarded data and no need to process locally
+ * == 0 means we want to process locally (and we may have forwarded data
+ * < 0 means there was an error and data should be discarded
+ */
+static int
+mesh_recv_group_data(struct ieee80211vap *vap, struct mbuf *m,
+    struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
+{
+#define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+	/* This is called from the RX path - don't hold this lock */
+	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+	mesh_forward(vap, m, mc);
+
+	if(mc->mc_ttl > 0) {
+		if (mc->mc_flags & IEEE80211_MESH_AE_01) {
+			/*
+			 * Forward of MSDUs from the MBSS to DS group addressed
+			 * (according to 13.11.3.2)
+			 * This happens by delivering the packet, and a bridge
+			 * will sent it on another port member.
+			 */
+			if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
+			    ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
+				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH,
+				    MC01(mc)->mc_addr4, "%s",
+				    "forward from MBSS to the DS");
+		}
+	}
+	return (0); /* process locally */
+#undef	MC01
+}
+
+static int
 mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
 {
 #define	HAS_SEQ(type)	((type & 0x4) == 0)
+#define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
+#define	MC10(mc)	((const struct ieee80211_meshcntl_ae10 *)mc)
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ifnet *ifp = vap->iv_ifp;
 	struct ieee80211_frame *wh;
 	const struct ieee80211_meshcntl *mc;
-	int hdrspace, meshdrlen, need_tap;
-	uint8_t dir, type, subtype, qos;
+	int hdrspace, meshdrlen, need_tap, error;
+	uint8_t dir, type, subtype, ae;
 	uint32_t seq;
-	uint8_t *addr;
+	const uint8_t *addr;
+	uint8_t qos[2];
 	ieee80211_seq rxseq;
 
 	KASSERT(ni != NULL, ("null node"));
@@ -1058,6 +1555,9 @@
 	need_tap = 1;			/* mbuf need to be tapped. */
 	type = -1;			/* undefined */
 
+	/* This is called from the RX path - don't hold this lock */
+	IEEE80211_TX_UNLOCK_ASSERT(ic);
+
 	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
 		    ni->ni_macaddr, NULL,
@@ -1120,7 +1620,7 @@
 	 *
 	 * NB: this check is also done upon peering link initiation.
 	 */
-	if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+	if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
 		    wh, NULL, "%s", "disallowed by ACL");
 		vap->iv_stats.is_rx_acl++;
@@ -1138,7 +1638,7 @@
 			    ni->ni_mlstate);
 			vap->iv_stats.is_mesh_nolink++;
 			goto out;
-		}	
+		}
 		if (dir != IEEE80211_FC1_DIR_FROMDS &&
 		    dir != IEEE80211_FC1_DIR_DSTODS) {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
@@ -1146,8 +1646,64 @@
 			vap->iv_stats.is_rx_wrongdir++;
 			goto err;
 		}
+
+		/* All Mesh data frames are QoS subtype */
+		if (!HAS_SEQ(type)) {
+			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+			    wh, "data", "incorrect subtype 0x%x", subtype);
+			vap->iv_stats.is_rx_badsubtype++;
+			goto err;
+		}
+
+		/*
+		 * Next up, any fragmentation.
+		 * XXX: we defrag before we even try to forward,
+		 * Mesh Control field is not present in sub-sequent
+		 * fragmented frames. This is in contrast to Draft 4.0.
+		 */
+		hdrspace = ieee80211_hdrspace(ic, wh);
+		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+			m = ieee80211_defrag(ni, m, hdrspace);
+			if (m == NULL) {
+				/* Fragment dropped or frame not complete yet */
+				goto out;
+			}
+		}
+		wh = mtod(m, struct ieee80211_frame *); /* NB: after defrag */
+
+		/*
+		 * Now we have a complete Mesh Data frame.
+		 */
+
+		/*
+		 * Only fromDStoDS data frames use 4 address qos frames
+		 * as specified in amendment. Otherwise addr4 is located
+		 * in the Mesh Control field and a 3 address qos frame
+		 * is used.
+		 */
+		if (IEEE80211_IS_DSTODS(wh))
+			*(uint16_t *)qos = *(uint16_t *)
+			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos;
+		else
+			*(uint16_t *)qos = *(uint16_t *)
+			    ((struct ieee80211_qosframe *)wh)->i_qos;
+
+		/*
+		 * NB: The mesh STA sets the Mesh Control Present
+		 * subfield to 1 in the Mesh Data frame containing
+		 * an unfragmented MSDU, an A-MSDU, or the first
+		 * fragment of an MSDU.
+		 * After defrag it should always be present.
+		 */
+		if (!(qos[1] & IEEE80211_QOS_MC)) {
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+			    ni->ni_macaddr, NULL,
+			    "%s", "Mesh control field not present");
+			vap->iv_stats.is_rx_elem_missing++; /* XXX: kinda */
+			goto err;
+		}
+
 		/* pull up enough to get to the mesh control */
-		hdrspace = ieee80211_hdrspace(ic, wh);
 		if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) &&
 		    (m = m_pullup(m, hdrspace +
 		        sizeof(struct ieee80211_meshcntl))) == NULL) {
@@ -1164,12 +1720,28 @@
 		 */
 		mc = (const struct ieee80211_meshcntl *)
 		    (mtod(m, const uint8_t *) + hdrspace);
+		ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
 		meshdrlen = sizeof(struct ieee80211_meshcntl) +
-		    (mc->mc_flags & 3) * IEEE80211_ADDR_LEN;
+		    ae * IEEE80211_ADDR_LEN;
 		hdrspace += meshdrlen;
+
+		/* pull complete hdrspace = ieee80211_hdrspace + meshcontrol */
+		if ((meshdrlen > sizeof(struct ieee80211_meshcntl)) &&
+		    (m->m_len < hdrspace) &&
+		    ((m = m_pullup(m, hdrspace)) == NULL)) {
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, NULL,
+			    "data too short: expecting %u", hdrspace);
+			vap->iv_stats.is_rx_tooshort++;
+			goto out;		/* XXX */
+		}
+		/* XXX: are we sure there is no reallocating after m_pullup? */
+
 		seq = LE_READ_4(mc->mc_seq);
 		if (IEEE80211_IS_MULTICAST(wh->i_addr1))
 			addr = wh->i_addr3;
+		else if (ae == IEEE80211_MESH_AE_01)
+			addr = MC01(mc)->mc_addr4;
 		else
 			addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4;
 		if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) {
@@ -1183,38 +1755,22 @@
 			goto out;
 		}
 
-		/*
-		 * Potentially forward packet.  See table s36 (p140)
-		 * for the rules.  XXX tap fwd'd packets not for us?
-		 */
-		if (dir == IEEE80211_FC1_DIR_FROMDS ||
-		    !mesh_isucastforme(vap, wh, mc)) {
-			mesh_forward(vap, m, mc);
-			if (dir == IEEE80211_FC1_DIR_DSTODS)
-				goto out;
-			/* NB: fall thru to deliver mcast frames locally */
-		}
-
-		/*
-		 * Save QoS bits for use below--before we strip the header.
-		 */
-		if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
-			qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
-			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
-			    ((struct ieee80211_qosframe *)wh)->i_qos[0];
+		/* This code "routes" the frame to the right control path */
+		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+			if (IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr3))
+				error =
+				    mesh_recv_indiv_data_to_me(vap, m, wh, mc);
+			else if (IEEE80211_IS_MULTICAST(wh->i_addr3))
+				error = mesh_recv_group_data(vap, m, wh, mc);
+			else
+				error = mesh_recv_indiv_data_to_fwrd(vap, m,
+				    wh, mc);
 		} else
-			qos = 0;
-		/*
-		 * Next up, any fragmentation.
-		 */
-		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
-			m = ieee80211_defrag(ni, m, hdrspace);
-			if (m == NULL) {
-				/* Fragment dropped or frame not complete yet */
-				goto out;
-			}
-		}
-		wh = NULL;		/* no longer valid, catch any uses */
+			error = mesh_recv_group_data(vap, m, wh, mc);
+		if (error < 0)
+			goto err;
+		else if (error > 0)
+			goto out;
 
 		if (ieee80211_radiotap_active_vap(vap))
 			ieee80211_radiotap_rx(vap, m);
@@ -1236,7 +1792,7 @@
 			IEEE80211_NODE_STAT(ni, rx_decap);
 			goto err;
 		}
-		if (qos & IEEE80211_QOS_AMSDU) {
+		if (qos[0] & IEEE80211_QOS_AMSDU) {
 			m = ieee80211_decap_amsdu(ni, m);
 			if (m == NULL)
 				return IEEE80211_FC0_TYPE_DATA;
@@ -1269,7 +1825,7 @@
 			    ether_sprintf(wh->i_addr2), rssi);
 		}
 #endif
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
 			    wh, NULL, "%s", "WEP set but not permitted");
 			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
@@ -1296,6 +1852,9 @@
 		m_freem(m);
 	}
 	return type;
+#undef	HAS_SEQ
+#undef	MC01
+#undef	MC10
 }
 
 static void
@@ -1306,6 +1865,7 @@
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_frame *wh;
+	struct ieee80211_mesh_route *rt;
 	uint8_t *frm, *efrm;
 
 	wh = mtod(m0, struct ieee80211_frame *);
@@ -1378,8 +1938,7 @@
 		/*
 		 * Peer only based on the current ACL policy.
 		 */
-		if (vap->iv_acl != NULL &&
-		    !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+		if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
 			    wh, NULL, "%s", "disallowed by ACL");
 			vap->iv_stats.is_rx_acl++;
@@ -1396,23 +1955,47 @@
 		}
 		/*
 		 * Automatically peer with discovered nodes if possible.
-		 * XXX backoff on repeated failure
 		 */
 		if (ni != vap->iv_bss &&
-		    (ms->ms_flags & IEEE80211_MESHFLAGS_AP) &&
-		    ni->ni_mlstate == IEEE80211_NODE_MESH_IDLE) {
-			uint16_t args[1];
+		    (ms->ms_flags & IEEE80211_MESHFLAGS_AP)) {
+			switch (ni->ni_mlstate) {
+			case IEEE80211_NODE_MESH_IDLE:
+			{
+				uint16_t args[1];
 
-			ni->ni_mlpid = mesh_generateid(vap);
-			if (ni->ni_mlpid == 0)
-				return;
-			mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT);
-			args[0] = ni->ni_mlpid;
-			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
-			    IEEE80211_ACTION_MESHPEERING_OPEN, args);
-			ni->ni_mlrcnt = 0;
-			mesh_peer_timeout_setup(ni);
+				/* Wait for backoff callout to reset counter */
+				if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
+					return;
+
+				ni->ni_mlpid = mesh_generateid(vap);
+				if (ni->ni_mlpid == 0)
+					return;
+				mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT);
+				args[0] = ni->ni_mlpid;
+				ieee80211_send_action(ni,
+				IEEE80211_ACTION_CAT_SELF_PROT,
+				IEEE80211_ACTION_MESHPEERING_OPEN, args);
+				ni->ni_mlrcnt = 0;
+				mesh_peer_timeout_setup(ni);
+				break;
+			}
+			case IEEE80211_NODE_MESH_ESTABLISHED:
+			{
+				/*
+				 * Valid beacon from a peer mesh STA
+				 * bump TA lifetime
+				 */
+				rt = ieee80211_mesh_rt_find(vap, wh->i_addr2);
+				if(rt != NULL) {
+					ieee80211_mesh_rt_update(rt,
+					    ticks_to_msecs(
+					    ms->ms_ppath->mpp_inact));
+				}
+				break;
+			}
+			default:
+				break; /* ignore */
+			}
 		}
 		break;
 	}
@@ -1542,8 +2125,7 @@
 }
 
 /*
- * Parse meshpeering action ie's for open+confirm frames; the
- * important bits are returned in the supplied structure.
+ * Parse meshpeering action ie's for MPM frames
  */
 static const struct ieee80211_meshpeer_ie *
 mesh_parse_meshpeering_action(struct ieee80211_node *ni,
@@ -1553,7 +2135,9 @@
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 	const struct ieee80211_meshpeer_ie *mpie;
+	uint16_t args[3];
 	const uint8_t *meshid, *meshconf, *meshpeer;
+	uint8_t sendclose = 0; /* 1 = MPM frame rejected, close will be sent */
 
 	meshid = meshconf = meshpeer = NULL;
 	while (efrm - frm > 1) {
@@ -1569,15 +2153,28 @@
 			meshpeer = frm;
 			mpie = (const struct ieee80211_meshpeer_ie *) frm;
 			memset(mp, 0, sizeof(*mp));
+			mp->peer_len = mpie->peer_len;
+			mp->peer_proto = LE_READ_2(&mpie->peer_proto);
 			mp->peer_llinkid = LE_READ_2(&mpie->peer_llinkid);
-			/* NB: peer link ID is optional on these frames */
-			if (subtype == IEEE80211_MESH_PEER_LINK_CLOSE &&
-			    mpie->peer_len == 8) {
-				mp->peer_linkid = 0;
-				mp->peer_rcode = LE_READ_2(&mpie->peer_linkid);
-			} else {
-				mp->peer_linkid = LE_READ_2(&mpie->peer_linkid);
-				mp->peer_rcode = LE_READ_2(&mpie->peer_rcode);
+			switch (subtype) {
+			case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+				mp->peer_linkid =
+				    LE_READ_2(&mpie->peer_linkid);
+				break;
+			case IEEE80211_ACTION_MESHPEERING_CLOSE:
+				/* NB: peer link ID is optional */
+				if (mpie->peer_len ==
+				    (IEEE80211_MPM_BASE_SZ + 2)) {
+					mp->peer_linkid = 0;
+					mp->peer_rcode =
+					    LE_READ_2(&mpie->peer_linkid);
+				} else {
+					mp->peer_linkid =
+					    LE_READ_2(&mpie->peer_linkid);
+					mp->peer_rcode =
+					    LE_READ_2(&mpie->peer_rcode);
+				}
+				break;
 			}
 			break;
 		}
@@ -1585,22 +2182,46 @@
 	}
 
 	/*
-	 * Verify the contents of the frame. Action frames with
-	 * close subtype don't have a Mesh Configuration IE.
-	 * If if fails validation, close the peer link.
+	 * Verify the contents of the frame.
+	 * If it fails validation, close the peer link.
 	 */
-	KASSERT(meshpeer != NULL &&
-	    subtype != IEEE80211_ACTION_MESHPEERING_CLOSE,
-	    ("parsing close action"));
+	if (mesh_verify_meshpeer(vap, subtype, (const uint8_t *)mp)) {
+		sendclose = 1;
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+		    wh, NULL, "%s", "MPM validation failed");
+	}
 
-	if (mesh_verify_meshid(vap, meshid) ||
-	    mesh_verify_meshpeer(vap, subtype, meshpeer) ||
+	/* If meshid is not the same reject any frames type. */
+	if (sendclose == 0 && mesh_verify_meshid(vap, meshid)) {
+		sendclose = 1;
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+		    wh, NULL, "%s", "not for our mesh");
+		if (subtype == IEEE80211_ACTION_MESHPEERING_CLOSE) {
+			/*
+			 * Standard not clear about this, if we dont ignore
+			 * there will be an endless loop between nodes sending
+			 * CLOSE frames between each other with wrong meshid.
+			 * Discard and timers will bring FSM to IDLE state.
+			 */
+			return NULL;
+		}
+	}
+	
+	/*
+	 * Close frames are accepted if meshid is the same.
+	 * Verify the other two types.
+	 */
+	if (sendclose == 0 && subtype != IEEE80211_ACTION_MESHPEERING_CLOSE &&
 	    mesh_verify_meshconf(vap, meshconf)) {
-		uint16_t args[3];
-
+		sendclose = 1;
 		IEEE80211_DISCARD(vap,
 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
-		    wh, NULL, "%s", "not for our mesh");
+		    wh, NULL, "%s", "configuration missmatch");
+	}
+
+	if (sendclose) {
 		vap->iv_stats.is_rx_mgtdiscard++;
 		switch (ni->ni_mlstate) {
 		case IEEE80211_NODE_MESH_IDLE:
@@ -1613,9 +2234,17 @@
 		case IEEE80211_NODE_MESH_CONFIRMRCV:
 			args[0] = ni->ni_mlpid;
 			args[1] = ni->ni_mllid;
-			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+			/* Reason codes for rejection */
+			switch (subtype) {
+			case IEEE80211_ACTION_MESHPEERING_OPEN:
+				args[2] = IEEE80211_REASON_MESH_CPVIOLATION;
+				break;
+			case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+				args[2] = IEEE80211_REASON_MESH_INCONS_PARAMS;
+				break;
+			}
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
 			    args);
 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1624,6 +2253,7 @@
 		}
 		return NULL;
 	}
+	
 	return (const struct ieee80211_meshpeer_ie *) mp;
 }
 
@@ -1633,6 +2263,7 @@
 	const uint8_t *frm, const uint8_t *efrm)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211_meshpeer_ie ie;
 	const struct ieee80211_meshpeer_ie *meshpeer;
 	uint16_t args[3];
@@ -1650,6 +2281,19 @@
 
 	switch (ni->ni_mlstate) {
 	case IEEE80211_NODE_MESH_IDLE:
+		/* Reject open request if reached our maximum neighbor count */
+		if (ms->ms_neighbors >= IEEE80211_MESH_MAX_NEIGHBORS) {
+			args[0] = meshpeer->peer_llinkid;
+			args[1] = 0;
+			args[2] = IEEE80211_REASON_MESH_MAX_PEERS;
+			ieee80211_send_action(ni,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
+			    IEEE80211_ACTION_MESHPEERING_CLOSE,
+			    args);
+			/* stay in IDLE state */
+			return (0);
+		}
+		/* Open frame accepted */
 		mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
 		ni->ni_mllid = meshpeer->peer_llinkid;
 		ni->ni_mlpid = mesh_generateid(vap);
@@ -1658,13 +2302,13 @@
 		args[0] = ni->ni_mlpid;
 		/* Announce we're open too... */
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_OPEN, args);
 		/* ...and confirm the link. */
 		args[0] = ni->ni_mlpid;
 		args[1] = ni->ni_mllid;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 		    args);
 		mesh_peer_timeout_setup(ni);
@@ -1676,7 +2320,7 @@
 			args[1] = ni->ni_mlpid;
 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
 			    args);
 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1687,7 +2331,7 @@
 		args[0] = ni->ni_mlpid;
 		args[1] = ni->ni_mllid;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 		    args);
 		break;
@@ -1697,7 +2341,7 @@
 		args[0] = ni->ni_mlpid;
 		args[1] = ni->ni_mllid;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 		    args);
 		/* NB: don't setup/clear any timeout */
@@ -1709,7 +2353,7 @@
 			args[1] = ni->ni_mllid;
 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
 			    args);
 			mesh_linkchange(ni,
@@ -1722,7 +2366,7 @@
 		args[0] = ni->ni_mlpid;
 		args[1] = ni->ni_mllid;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 		    args);
 		mesh_peer_timeout_stop(ni);
@@ -1733,7 +2377,7 @@
 			args[1] = ni->ni_mlpid;
 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
 			    args);
 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1743,7 +2387,7 @@
 		args[0] = ni->ni_mlpid;
 		args[1] = ni->ni_mllid;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 		    args);
 		break;
@@ -1750,9 +2394,10 @@
 	case IEEE80211_NODE_MESH_HOLDING:
 		args[0] = ni->ni_mlpid;
 		args[1] = meshpeer->peer_llinkid;
-		args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
+		/* Standard not clear about what the reaason code should be */
+		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
 		    args);
 		break;
@@ -1788,13 +2433,15 @@
 		break;
 	case IEEE80211_NODE_MESH_OPENSNT:
 		mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV);
+		mesh_peer_timeout_setup(ni);
 		break;
 	case IEEE80211_NODE_MESH_HOLDING:
 		args[0] = ni->ni_mlpid;
 		args[1] = meshpeer->peer_llinkid;
-		args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
+		/* Standard not clear about what the reaason code should be */
+		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
 		    args);
 		break;
@@ -1804,7 +2451,7 @@
 			args[1] = ni->ni_mllid;
 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
 			    args);
 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1827,8 +2474,23 @@
 	const struct ieee80211_frame *wh,
 	const uint8_t *frm, const uint8_t *efrm)
 {
+	struct ieee80211_meshpeer_ie ie;
+	const struct ieee80211_meshpeer_ie *meshpeer;
 	uint16_t args[3];
 
+	/* +2 for action + code */
+	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2, efrm, &ie,
+	    IEEE80211_ACTION_MESHPEERING_CLOSE);
+	if (meshpeer == NULL) {
+		return 0;
+	}
+
+	/*
+	 * XXX: check reason code, for example we could receive
+	 * IEEE80211_REASON_MESH_MAX_PEERS then we should not attempt
+	 * to peer again.
+	 */
+
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
 	    ni, "%s", "recv PEER CLOSE");
 
@@ -1844,7 +2506,7 @@
 		args[1] = ni->ni_mllid;
 		args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD;
 		ieee80211_send_action(ni,
-		    IEEE80211_ACTION_CAT_MESHPEERING,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
 		    args);
 		mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1852,7 +2514,7 @@
 		break;
 	case IEEE80211_NODE_MESH_HOLDING:
 		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
-		mesh_peer_timeout_setup(ni);
+		mesh_peer_timeout_stop(ni);
 		break;
 	}
 	return 0;
@@ -1862,41 +2524,202 @@
  * Link Metric handling.
  */
 static int
-mesh_recv_action_meshlmetric_req(struct ieee80211_node *ni,
+mesh_recv_action_meshlmetric(struct ieee80211_node *ni,
 	const struct ieee80211_frame *wh,
 	const uint8_t *frm, const uint8_t *efrm)
 {
-	uint32_t metric;
+	const struct ieee80211_meshlmetric_ie *ie =
+	    (const struct ieee80211_meshlmetric_ie *)
+	    (frm+2); /* action + code */
+	struct ieee80211_meshlmetric_ie lm_rep;
+	
+	if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
+		lm_rep.lm_flags = 0;
+		lm_rep.lm_metric = mesh_airtime_calc(ni);
+		ieee80211_send_action(ni,
+		    IEEE80211_ACTION_CAT_MESH,
+		    IEEE80211_ACTION_MESH_LMETRIC,
+		    &lm_rep);
+	}
+	/* XXX: else do nothing for now */
+	return 0;
+}
 
-	metric = mesh_airtime_calc(ni);
-	ieee80211_send_action(ni,
-	    IEEE80211_ACTION_CAT_MESHLMETRIC,
-	    IEEE80211_ACTION_MESHLMETRIC_REP,
-	    &metric);
+/*
+ * Parse meshgate action ie's for GANN frames.
+ * Returns -1 if parsing fails, otherwise 0.
+ */
+static int
+mesh_parse_meshgate_action(struct ieee80211_node *ni,
+    const struct ieee80211_frame *wh,	/* XXX for VERIFY_LENGTH */
+    struct ieee80211_meshgann_ie *ie, const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	const struct ieee80211_meshgann_ie *gannie;
+
+	while (efrm - frm > 1) {
+		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return -1);
+		switch (*frm) {
+		case IEEE80211_ELEMID_MESHGANN:
+			gannie = (const struct ieee80211_meshgann_ie *) frm;
+			memset(ie, 0, sizeof(*ie));
+			ie->gann_ie = gannie->gann_ie;
+			ie->gann_len = gannie->gann_len;
+			ie->gann_flags = gannie->gann_flags;
+			ie->gann_hopcount = gannie->gann_hopcount;
+			ie->gann_ttl = gannie->gann_ttl;
+			IEEE80211_ADDR_COPY(ie->gann_addr, gannie->gann_addr);
+			ie->gann_seq = LE_READ_4(&gannie->gann_seq);
+			ie->gann_interval = LE_READ_2(&gannie->gann_interval);
+			break;
+		}
+		frm += frm[1] + 2;
+	}
+
 	return 0;
 }
 
+/*
+ * Mesh Gate Announcement handling.
+ */
 static int
-mesh_recv_action_meshlmetric_rep(struct ieee80211_node *ni,
+mesh_recv_action_meshgate(struct ieee80211_node *ni,
 	const struct ieee80211_frame *wh,
 	const uint8_t *frm, const uint8_t *efrm)
 {
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_mesh_state *ms = vap->iv_mesh;
+	struct ieee80211_mesh_gate_route *gr, *next;
+	struct ieee80211_mesh_route *rt_gate;
+	struct ieee80211_meshgann_ie pgann;
+	struct ieee80211_meshgann_ie ie;
+	int found = 0;
+
+	/* +2 for action + code */
+	if (mesh_parse_meshgate_action(ni, wh, &ie, frm+2, efrm) != 0) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+		    ni->ni_macaddr, NULL, "%s",
+		    "GANN parsing failed");
+		vap->iv_stats.is_rx_mgtdiscard++;
+		return (0);
+	}
+
+	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ie.gann_addr))
+		return 0;
+
+	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr,
+	    "received GANN, meshgate: %6D (seq %u)", ie.gann_addr, ":",
+	    ie.gann_seq);
+
+	if (ms == NULL)
+		return (0);
+	MESH_RT_LOCK(ms);
+	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
+		if (!IEEE80211_ADDR_EQ(gr->gr_addr, ie.gann_addr))
+			continue;
+		if (ie.gann_seq <= gr->gr_lastseq) {
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+			    ni->ni_macaddr, NULL,
+			    "GANN old seqno %u <= %u",
+			    ie.gann_seq, gr->gr_lastseq);
+			MESH_RT_UNLOCK(ms);
+			return (0);
+		}
+		/* corresponding mesh gate found & GANN accepted */
+		found = 1;
+		break;
+
+	}
+	if (found == 0) {
+		/* this GANN is from a new mesh Gate add it to known table. */
+		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
+		    "stored new GANN information, seq %u.", ie.gann_seq);
+		gr = malloc(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
+		    M_80211_MESH_GT_RT, M_NOWAIT | M_ZERO);
+		IEEE80211_ADDR_COPY(gr->gr_addr, ie.gann_addr);
+		TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
+	}
+	gr->gr_lastseq = ie.gann_seq;
+
+	/* check if we have a path to this gate */
+	rt_gate = mesh_rt_find_locked(ms, gr->gr_addr);
+	if (rt_gate != NULL &&
+	    rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
+		gr->gr_route = rt_gate;
+		rt_gate->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
+	}
+
+	MESH_RT_UNLOCK(ms);
+
+	/* popagate only if decremented ttl >= 1 && forwarding is enabled */
+	if ((ie.gann_ttl - 1) < 1 && !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
+		return 0;
+	pgann.gann_flags = ie.gann_flags; /* Reserved */
+	pgann.gann_hopcount = ie.gann_hopcount + 1;
+	pgann.gann_ttl = ie.gann_ttl - 1;
+	IEEE80211_ADDR_COPY(pgann.gann_addr, ie.gann_addr);
+	pgann.gann_seq = ie.gann_seq;
+	pgann.gann_interval = ie.gann_interval;
+
+	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
+	    "%s", "propagate GANN");
+
+	ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
+	    IEEE80211_ACTION_MESH_GANN, &pgann);
+
 	return 0;
 }
 
 static int
-mesh_send_action(struct ieee80211_node *ni, struct mbuf *m)
+mesh_send_action(struct ieee80211_node *ni,
+    const uint8_t sa[IEEE80211_ADDR_LEN],
+    const uint8_t da[IEEE80211_ADDR_LEN],
+    struct mbuf *m)
 {
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_bpf_params params;
+	struct ieee80211_frame *wh;
+	int ret;
 
+	KASSERT(ni != NULL, ("null node"));
+
+	if (vap->iv_state == IEEE80211_S_CAC) {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+		    "block %s frame in CAC state", "Mesh action");
+		vap->iv_stats.is_tx_badstate++;
+		ieee80211_free_node(ni);
+		m_freem(m);
+		return EIO;		/* XXX */
+	}
+
+	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+	if (m == NULL) {
+		ieee80211_free_node(ni);
+		return ENOMEM;
+	}
+
+	IEEE80211_TX_LOCK(ic);
+	wh = mtod(m, struct ieee80211_frame *);
+	ieee80211_send_setup(ni, m,
+	     IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
+	     IEEE80211_NONQOS_TID, sa, da, sa);
+	m->m_flags |= M_ENCAP;		/* mark encapsulated */
+
 	memset(&params, 0, sizeof(params));
 	params.ibp_pri = WME_AC_VO;
 	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
-	/* XXX ucast/mcast */
-	params.ibp_try0 = ni->ni_txparms->maxretry;
+	if (IEEE80211_IS_MULTICAST(da))
+		params.ibp_try0 = 1;
+	else
+		params.ibp_try0 = ni->ni_txparms->maxretry;
 	params.ibp_power = ni->ni_txpower;
-	return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
-	     &params);
+
+	IEEE80211_NODE_STAT(ni, tx_mgmt);
+
+	ret = ieee80211_raw_output(vap, ni, m, &params);
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 }
 
 #define	ADDSHORT(frm, v) do {			\
@@ -1935,10 +2758,10 @@
 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
 	    sizeof(uint16_t)	/* action+category */
 	    + sizeof(uint16_t)	/* capabilites */
-	    + 2 + IEEE80211_RATE_SIZE	 
-	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)	 
+	    + 2 + IEEE80211_RATE_SIZE
+	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 	    + 2 + IEEE80211_MESHID_LEN
-	    + sizeof(struct ieee80211_meshconf_ie)	 
+	    + sizeof(struct ieee80211_meshconf_ie)
 	    + sizeof(struct ieee80211_meshpeer_ie)
 	);
 	if (m != NULL) {
@@ -1961,10 +2784,10 @@
 		frm = ieee80211_add_xrates(frm, rs);
 		frm = ieee80211_add_meshid(frm, vap);
 		frm = ieee80211_add_meshconf(frm, vap);
-		frm = ieee80211_add_meshpeer(frm, IEEE80211_MESH_PEER_LINK_OPEN,
+		frm = ieee80211_add_meshpeer(frm, IEEE80211_ACTION_MESHPEERING_OPEN,
 		    args[0], 0, 0);
 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-		return mesh_send_action(ni, m);
+		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
 	} else {
 		vap->iv_stats.is_tx_nobuf++;
 		ieee80211_free_node(ni);
@@ -1998,10 +2821,10 @@
 	    + sizeof(uint16_t)	/* capabilites */
 	    + sizeof(uint16_t)	/* status code */
 	    + sizeof(uint16_t)	/* AID */
-	    + 2 + IEEE80211_RATE_SIZE	 
-	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)	 
+	    + 2 + IEEE80211_RATE_SIZE
+	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 	    + 2 + IEEE80211_MESHID_LEN
-	    + sizeof(struct ieee80211_meshconf_ie)	 
+	    + sizeof(struct ieee80211_meshconf_ie)
 	    + sizeof(struct ieee80211_meshpeer_ie)
 	);
 	if (m != NULL) {
@@ -2029,10 +2852,10 @@
 		frm = ieee80211_add_meshid(frm, vap);
 		frm = ieee80211_add_meshconf(frm, vap);
 		frm = ieee80211_add_meshpeer(frm,
-		    IEEE80211_MESH_PEER_LINK_CONFIRM,
+		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
 		    args[0], args[1], 0);
 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-		return mesh_send_action(ni, m);
+		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
 	} else {
 		vap->iv_stats.is_tx_nobuf++;
 		ieee80211_free_node(ni);
@@ -2064,7 +2887,7 @@
 	    sizeof(uint16_t)	/* action+category */
 	    + sizeof(uint16_t)	/* reason code */
 	    + 2 + IEEE80211_MESHID_LEN
-	    + sizeof(struct ieee80211_meshpeer_ie) 
+	    + sizeof(struct ieee80211_meshpeer_ie)
 	);
 	if (m != NULL) {
 		/*
@@ -2071,19 +2894,17 @@
 		 * mesh peer close action frame format:
 		 *   [1] category
 		 *   [1] action
-		 *   [2] reason code
 		 *   [tlv] mesh id
 		 *   [tlv] mesh peer link mgmt
 		 */
 		*frm++ = category;
 		*frm++ = action;
-		ADDSHORT(frm, args[2]);		/* reason code */
 		frm = ieee80211_add_meshid(frm, vap);
 		frm = ieee80211_add_meshpeer(frm,
-		    IEEE80211_MESH_PEER_LINK_CLOSE,
+		    IEEE80211_ACTION_MESHPEERING_CLOSE,
 		    args[0], args[1], args[2]);
 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-		return mesh_send_action(ni, m);
+		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
 	} else {
 		vap->iv_stats.is_tx_nobuf++;
 		ieee80211_free_node(ni);
@@ -2092,17 +2913,23 @@
 }
 
 static int
-mesh_send_action_meshlink_request(struct ieee80211_node *ni,
+mesh_send_action_meshlmetric(struct ieee80211_node *ni,
 	int category, int action, void *arg0)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_meshlmetric_ie *ie = arg0;
 	struct mbuf *m;
 	uint8_t *frm;
 
-	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
-	    "%s", "send LINK METRIC REQUEST action");
-
+	if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+		    ni, "%s", "send LINK METRIC REQUEST action");
+	} else {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+		    ni, "send LINK METRIC REPLY action: metric 0x%x",
+		    ie->lm_metric);
+	}
 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
@@ -2110,18 +2937,22 @@
 
 	m = ieee80211_getmgtframe(&frm,
 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
-	    sizeof(uint16_t)	/* action+category */
+	    sizeof(uint16_t) +	/* action+category */
+	    sizeof(struct ieee80211_meshlmetric_ie)
 	);
 	if (m != NULL) {
 		/*
-		 * mesh link metric request
+		 * mesh link metric
 		 *   [1] category
 		 *   [1] action
+		 *   [tlv] mesh link metric
 		 */
 		*frm++ = category;
 		*frm++ = action;
+		frm = ieee80211_add_meshlmetric(frm,
+		    ie->lm_flags, ie->lm_metric);
 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-		return mesh_send_action(ni, m);
+		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
 	} else {
 		vap->iv_stats.is_tx_nobuf++;
 		ieee80211_free_node(ni);
@@ -2130,18 +2961,15 @@
 }
 
 static int
-mesh_send_action_meshlink_reply(struct ieee80211_node *ni,
-	int category, int action, void *args0)
+mesh_send_action_meshgate(struct ieee80211_node *ni,
+	int category, int action, void *arg0)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
-	uint32_t *metric = args0;
+	struct ieee80211_meshgann_ie *ie = arg0;
 	struct mbuf *m;
 	uint8_t *frm;
 
-	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
-	    "send LINK METRIC REPLY action: metric 0x%x", *metric);
-
 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
@@ -2149,21 +2977,21 @@
 
 	m = ieee80211_getmgtframe(&frm,
 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
-	    sizeof(uint16_t)	/* action+category */
-	    + sizeof(struct ieee80211_meshlmetric_ie)
+	    sizeof(uint16_t) +	/* action+category */
+	    IEEE80211_MESHGANN_BASE_SZ
 	);
 	if (m != NULL) {
 		/*
-		 * mesh link metric reply
+		 * mesh link metric
 		 *   [1] category
 		 *   [1] action
-		 *   [tlv] mesh link metric
+		 *   [tlv] mesh gate annoucement
 		 */
 		*frm++ = category;
 		*frm++ = action;
-		frm = ieee80211_add_meshlmetric(frm, *metric);
+		frm = ieee80211_add_meshgate(frm, ie);
 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-		return mesh_send_action(ni, m);
+		return mesh_send_action(ni, vap->iv_myaddr, broadcastaddr, m);
 	} else {
 		vap->iv_stats.is_tx_nobuf++;
 		ieee80211_free_node(ni);
@@ -2213,6 +3041,15 @@
 	callout_drain(&ni->ni_mltimer);
 }
 
+static void
+mesh_peer_backoff_cb(void *arg)
+{
+	struct ieee80211_node *ni = (struct ieee80211_node *)arg;
+
+	/* After backoff timeout, try to peer automatically again. */
+	ni->ni_mlhcnt = 0;
+}
+
 /*
  * Mesh Peer Link Management FSM timeout handling.
  */
@@ -2236,7 +3073,7 @@
 			args[0] = ni->ni_mlpid;
 			args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
 			ni->ni_mlrcnt = 0;
 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -2244,7 +3081,7 @@
 		} else {
 			args[0] = ni->ni_mlpid;
 			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
+			    IEEE80211_ACTION_CAT_SELF_PROT,
 			    IEEE80211_ACTION_MESHPEERING_OPEN, args);
 			ni->ni_mlrcnt++;
 			mesh_peer_timeout_backoff(ni);
@@ -2251,21 +3088,20 @@
 		}
 		break;
 	case IEEE80211_NODE_MESH_CONFIRMRCV:
-		if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) {
-			args[0] = ni->ni_mlpid;
-			args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
-			ieee80211_send_action(ni,
-			    IEEE80211_ACTION_CAT_MESHPEERING,
-			    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
-			ni->ni_mlrcnt = 0;
-			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
-			mesh_peer_timeout_setup(ni);
-		} else {
-			ni->ni_mlrcnt++;
-			mesh_peer_timeout_setup(ni);
-		}
+		args[0] = ni->ni_mlpid;
+		args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
+		ieee80211_send_action(ni,
+		    IEEE80211_ACTION_CAT_SELF_PROT,
+		    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
+		mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+		mesh_peer_timeout_setup(ni);
 		break;
 	case IEEE80211_NODE_MESH_HOLDING:
+		ni->ni_mlhcnt++;
+		if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
+			callout_reset(&ni->ni_mlhtimer,
+			    ieee80211_mesh_backofftimeout,
+			    mesh_peer_backoff_cb, ni);
 		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
 		break;
 	}
@@ -2290,7 +3126,6 @@
 	const struct ieee80211_meshconf_ie *meshconf =
 	    (const struct ieee80211_meshconf_ie *) ie;
 	const struct ieee80211_mesh_state *ms = vap->iv_mesh;
-	uint16_t cap;
 
 	if (meshconf == NULL)
 		return 1;
@@ -2324,10 +3159,8 @@
 		    meshconf->conf_pselid);
 		return 1;
 	}
-	/* NB: conf_cap is only read correctly here */
-	cap = LE_READ_2(&meshconf->conf_cap);
 	/* Not accepting peers */
-	if (!(cap & IEEE80211_MESHCONF_CAP_AP)) {
+	if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) {
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
 		    "not accepting peers: 0x%x\n", meshconf->conf_cap);
 		return 1;
@@ -2342,22 +3175,31 @@
 	const struct ieee80211_meshpeer_ie *meshpeer =
 	    (const struct ieee80211_meshpeer_ie *) ie;
 
-	if (meshpeer == NULL || meshpeer->peer_len < 6 ||
-	    meshpeer->peer_len > 10)
+	if (meshpeer == NULL ||
+	    meshpeer->peer_len < IEEE80211_MPM_BASE_SZ ||
+	    meshpeer->peer_len > IEEE80211_MPM_MAX_SZ)
 		return 1;
+	if (meshpeer->peer_proto != IEEE80211_MPPID_MPM) {
+		IEEE80211_DPRINTF(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+		    "Only MPM protocol is supported (proto: 0x%02X)",
+		    meshpeer->peer_proto);
+		return 1;
+	}
 	switch (subtype) {
-	case IEEE80211_MESH_PEER_LINK_OPEN:
-		if (meshpeer->peer_len != 6)
+	case IEEE80211_ACTION_MESHPEERING_OPEN:
+		if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ)
 			return 1;
 		break;
-	case IEEE80211_MESH_PEER_LINK_CONFIRM:
-		if (meshpeer->peer_len != 8)
+	case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+		if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ + 2)
 			return 1;
 		break;
-	case IEEE80211_MESH_PEER_LINK_CLOSE:
-		if (meshpeer->peer_len < 8)
+	case IEEE80211_ACTION_MESHPEERING_CLOSE:
+		if (meshpeer->peer_len < IEEE80211_MPM_BASE_SZ + 2)
 			return 1;
-		if (meshpeer->peer_len == 8 && meshpeer->peer_linkid != 0)
+		if (meshpeer->peer_len == (IEEE80211_MPM_BASE_SZ + 2) &&
+		    meshpeer->peer_linkid != 0)
 			return 1;
 		if (meshpeer->peer_rcode == 0)
 			return 1;
@@ -2396,7 +3238,7 @@
 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
 
 	*frm++ = IEEE80211_ELEMID_MESHCONF;
-	*frm++ = sizeof(struct ieee80211_meshconf_ie) - 2;
+	*frm++ = IEEE80211_MESH_CONF_SZ;
 	*frm++ = ms->ms_ppath->mpp_ie;		/* path selection */
 	*frm++ = ms->ms_pmetric->mpm_ie;	/* link metric */
 	*frm++ = IEEE80211_MESHCONF_CC_DISABLED;
@@ -2403,9 +3245,10 @@
 	*frm++ = IEEE80211_MESHCONF_SYNC_NEIGHOFF;
 	*frm++ = IEEE80211_MESHCONF_AUTH_DISABLED;
 	/* NB: set the number of neighbors before the rest */
-	*frm = (ms->ms_neighbors > 15 ? 15 : ms->ms_neighbors) << 1;
-	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
-		*frm |= IEEE80211_MESHCONF_FORM_MP;
+	*frm = (ms->ms_neighbors > IEEE80211_MESH_MAX_NEIGHBORS ?
+	    IEEE80211_MESH_MAX_NEIGHBORS : ms->ms_neighbors) << 1;
+	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
+		*frm |= IEEE80211_MESHCONF_FORM_GATE;
 	frm += 1;
 	caps = 0;
 	if (ms->ms_flags & IEEE80211_MESHFLAGS_AP)
@@ -2412,7 +3255,7 @@
 		caps |= IEEE80211_MESHCONF_CAP_AP;
 	if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
 		caps |= IEEE80211_MESHCONF_CAP_FWRD;
-	ADDSHORT(frm, caps);
+	*frm++ = caps;
 	return frm;
 }
 
@@ -2423,34 +3266,29 @@
 ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid,
     uint16_t peerid, uint16_t reason)
 {
-	/* XXX change for AH */
-	static const uint8_t meshpeerproto[4] = IEEE80211_MESH_PEER_PROTO;
 
 	KASSERT(localid != 0, ("localid == 0"));
 
 	*frm++ = IEEE80211_ELEMID_MESHPEER;
 	switch (subtype) {
-	case IEEE80211_MESH_PEER_LINK_OPEN:
-		*frm++ = 6;		/* length */
-		memcpy(frm, meshpeerproto, 4);
-		frm += 4;
-		ADDSHORT(frm, localid);	/* local ID */
+	case IEEE80211_ACTION_MESHPEERING_OPEN:
+		*frm++ = IEEE80211_MPM_BASE_SZ;		/* length */
+		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
+		ADDSHORT(frm, localid);			/* local ID */
 		break;
-	case IEEE80211_MESH_PEER_LINK_CONFIRM:
+	case IEEE80211_ACTION_MESHPEERING_CONFIRM:
 		KASSERT(peerid != 0, ("sending peer confirm without peer id"));
-		*frm++ = 8;		/* length */
-		memcpy(frm, meshpeerproto, 4);
-		frm += 4;
-		ADDSHORT(frm, localid);	/* local ID */
-		ADDSHORT(frm, peerid);	/* peer ID */
+		*frm++ = IEEE80211_MPM_BASE_SZ + 2;	/* length */
+		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
+		ADDSHORT(frm, localid);			/* local ID */
+		ADDSHORT(frm, peerid);			/* peer ID */
 		break;
-	case IEEE80211_MESH_PEER_LINK_CLOSE:
+	case IEEE80211_ACTION_MESHPEERING_CLOSE:
 		if (peerid)
-			*frm++ = 10;	/* length */
+			*frm++ = IEEE80211_MPM_MAX_SZ;	/* length */
 		else
-			*frm++ = 8;	/* length */
-		memcpy(frm, meshpeerproto, 4);
-		frm += 4;
+			*frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
+		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
 		ADDSHORT(frm, localid);	/* local ID */
 		if (peerid)
 			ADDSHORT(frm, peerid);	/* peer ID */
@@ -2470,7 +3308,7 @@
  */
 #define IEEE80211_MESH_MAXOVERHEAD \
 	(sizeof(struct ieee80211_qosframe_addr4) \
-	 + sizeof(struct ieee80211_meshcntl_ae11) \
+	 + sizeof(struct ieee80211_meshcntl_ae10) \
 	+ sizeof(struct llc) \
 	+ IEEE80211_ADDR_LEN \
 	+ IEEE80211_WEP_IVLEN \
@@ -2509,13 +3347,32 @@
  * Add a Mesh Link Metric report IE to a frame.
  */
 uint8_t *
-ieee80211_add_meshlmetric(uint8_t *frm, uint32_t metric)
+ieee80211_add_meshlmetric(uint8_t *frm, uint8_t flags, uint32_t metric)
 {
 	*frm++ = IEEE80211_ELEMID_MESHLINK;
-	*frm++ = 4;
+	*frm++ = 5;
+	*frm++ = flags;
 	ADDWORD(frm, metric);
 	return frm;
 }
+
+/*
+ * Add a Mesh Gate Announcement IE to a frame.
+ */
+uint8_t *
+ieee80211_add_meshgate(uint8_t *frm, struct ieee80211_meshgann_ie *ie)
+{
+	*frm++ = IEEE80211_ELEMID_MESHGANN; /* ie */
+	*frm++ = IEEE80211_MESHGANN_BASE_SZ; /* len */
+	*frm++ = ie->gann_flags;
+	*frm++ = ie->gann_hopcount;
+	*frm++ = ie->gann_ttl;
+	IEEE80211_ADDR_COPY(frm, ie->gann_addr);
+	frm += 6;
+	ADDWORD(frm, ie->gann_seq);
+	ADDSHORT(frm, ie->gann_interval);
+	return frm;
+}
 #undef ADDSHORT
 #undef ADDWORD
 
@@ -2526,7 +3383,8 @@
 ieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni)
 {
 	ni->ni_flags |= IEEE80211_NODE_QOS;
-	callout_init(&ni->ni_mltimer, CALLOUT_MPSAFE);
+	callout_init(&ni->ni_mltimer, 1);
+	callout_init(&ni->ni_mlhtimer, 1);
 }
 
 /*
@@ -2539,6 +3397,7 @@
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 
 	callout_drain(&ni->ni_mltimer);
+	callout_drain(&ni->ni_mlhtimer);
 	/* NB: short-circuit callbacks after mesh_vdetach */
 	if (vap->iv_mesh != NULL)
 		ms->ms_ppath->mpp_peerdown(ni);
@@ -2601,6 +3460,9 @@
 	case IEEE80211_IOC_MESH_FWRD:
 		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0;
 		break;
+	case IEEE80211_IOC_MESH_GATE:
+		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) != 0;
+		break;
 	case IEEE80211_IOC_MESH_TTL:
 		ireq->i_val = ms->ms_ttl;
 		break;
@@ -2629,7 +3491,6 @@
 					break;
 				imr = (struct ieee80211req_mesh_route *)
 				    (p + off);
-				imr->imr_flags = rt->rt_flags;
 				IEEE80211_ADDR_COPY(imr->imr_dest,
 				    rt->rt_dest);
 				IEEE80211_ADDR_COPY(imr->imr_nexthop,
@@ -2636,8 +3497,10 @@
 				    rt->rt_nexthop);
 				imr->imr_metric = rt->rt_metric;
 				imr->imr_nhops = rt->rt_nhops;
-				imr->imr_lifetime = rt->rt_lifetime;
+				imr->imr_lifetime =
+				    ieee80211_mesh_rt_update(rt, 0);
 				imr->imr_lastmseq = rt->rt_lastmseq;
+				imr->imr_flags = rt->rt_flags; /* last */
 				off += sizeof(*imr);
 			}
 			MESH_RT_UNLOCK(ms);
@@ -2714,7 +3577,14 @@
 			ms->ms_flags |= IEEE80211_MESHFLAGS_FWD;
 		else
 			ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
+		mesh_gatemode_setup(vap);
 		break;
+	case IEEE80211_IOC_MESH_GATE:
+		if (ireq->i_val)
+			ms->ms_flags |= IEEE80211_MESHFLAGS_GATE;
+		else
+			ms->ms_flags &= ~IEEE80211_MESHFLAGS_GATE;
+		break;
 	case IEEE80211_IOC_MESH_TTL:
 		ms->ms_ttl = (uint8_t) ireq->i_val;
 		break;

Modified: trunk/sys/net80211/ieee80211_mesh.h
===================================================================
--- trunk/sys/net80211/ieee80211_mesh.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_mesh.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*- 
  * Copyright (c) 2009 The FreeBSD Foundation 
  * All rights reserved. 
@@ -26,12 +27,13 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
  * SUCH DAMAGE. 
  * 
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_mesh.h 246512 2013-02-07 21:26:40Z monthadar $
  */
 #ifndef _NET80211_IEEE80211_MESH_H_
 #define _NET80211_IEEE80211_MESH_H_
 
 #define	IEEE80211_MESH_DEFAULT_TTL	31
+#define	IEEE80211_MESH_MAX_NEIGHBORS	15
 
 /*
  * NB: all structures are __packed  so sizeof works on arm, et. al.
@@ -40,6 +42,7 @@
  * 802.11s Information Elements.
 */
 /* Mesh Configuration */
+#define IEEE80211_MESH_CONF_SZ		(7)
 struct ieee80211_meshconf_ie {
 	uint8_t		conf_ie;	/* IEEE80211_ELEMID_MESHCONF */
 	uint8_t		conf_len;
@@ -49,31 +52,66 @@
 	uint8_t		conf_syncid;	/* Sync. Protocol ID */
 	uint8_t		conf_authid;	/* Auth. Protocol ID */
 	uint8_t		conf_form;	/* Formation Information */
-	uint16_t	conf_cap;
+	uint8_t		conf_cap;
 } __packed;
 
 /* Hybrid Wireless Mesh Protocol */
-#define	IEEE80211_MESHCONF_PATH_HWMP		0x00
+enum {
+	/* 0 reserved */
+	IEEE80211_MESHCONF_PATH_HWMP		= 1,
+	/* 2-254 reserved */
+	IEEE80211_MESHCONF_PATH_VENDOR		= 255,
+};
+
 /* Airtime Link Metric */
-#define	IEEE80211_MESHCONF_METRIC_AIRTIME	0x00
+enum {
+	/* 0 reserved */
+	IEEE80211_MESHCONF_METRIC_AIRTIME	= 1,
+	/* 2-254 reserved */
+	IEEE80211_MESHCONF_METRIC_VENDOR	= 255,
+};
+
 /* Congestion Control */
-#define	IEEE80211_MESHCONF_CC_DISABLED		0x00
-#define	IEEE80211_MESHCONF_CC_SIG		0x01
+enum {
+	IEEE80211_MESHCONF_CC_DISABLED		= 0,
+	IEEE80211_MESHCONF_CC_SIG		= 1,
+	/* 2-254 reserved */
+	IEEE80211_MESHCONF_CC_VENDOR		= 255,
+};
+
 /* Neighbour Offset */
-#define	IEEE80211_MESHCONF_SYNC_NEIGHOFF	0x00
-#define	IEEE80211_MESHCONF_AUTH_DISABLED	0x00
-/* Simultaneous Authenticaction of Equals */
-#define	IEEE80211_MESHCONF_AUTH_SAE		0x01
-#define	IEEE80211_MESHCONF_FORM_MP		0x01 /* Connected to Portal */
-#define	IEEE80211_MESHCONF_FORM_NNEIGH_MASK	0x04 /* Number of Neighbours */
+enum {
+	/* 0 reserved */
+	IEEE80211_MESHCONF_SYNC_NEIGHOFF	= 1,
+	/* 2-254 rserved */
+	IEEE80211_MESHCONF_SYNC_VENDOR		= 255,
+};
+
+/* Authentication Protocol Identifier */
+enum {
+	
+	IEEE80211_MESHCONF_AUTH_DISABLED	= 0,
+	/* Simultaneous Authenticaction of Equals */
+	IEEE80211_MESHCONF_AUTH_SEA		= 1,
+	IEEE80211_MESHCONF_AUTH_8021X		= 2, /* IEEE 802.1X */
+	/* 3-254 reserved */
+	IEEE80211_MESHCONF_AUTH_VENDOR		= 255,
+};
+
+/* Mesh Formation Info */
+#define	IEEE80211_MESHCONF_FORM_GATE	0x01 	/* Connected to Gate */
+#define	IEEE80211_MESHCONF_FORM_NNEIGH_MASK 0x7E /* Number of Neighbours */
+#define	IEEE80211_MESHCONF_FORM_SA	0xF0 	/* indicating 802.1X auth */
+
+/* Mesh Capability */
 #define	IEEE80211_MESHCONF_CAP_AP	0x01	/* Accepting Peers */
 #define	IEEE80211_MESHCONF_CAP_MCCAS	0x02	/* MCCA supported */
 #define	IEEE80211_MESHCONF_CAP_MCCAE	0x04	/* MCCA enabled */
 #define	IEEE80211_MESHCONF_CAP_FWRD 	0x08	/* forwarding enabled */
 #define	IEEE80211_MESHCONF_CAP_BTR	0x10	/* Beacon Timing Report Enab */
-#define	IEEE80211_MESHCONF_CAP_TBTTA	0x20	/* TBTT Adj. Enabled */
-#define	IEEE80211_MESHCONF_CAP_TBTT	0x40	/* TBTT Adjusting  */
-#define	IEEE80211_MESHCONF_CAP_PSL	0x80	/* Power Save Level */
+#define	IEEE80211_MESHCONF_CAP_TBTT	0x20	/* TBTT Adjusting  */
+#define	IEEE80211_MESHCONF_CAP_PSL	0x40	/* Power Save Level */
+/* 0x80 reserved */
 
 /* Mesh Identifier */
 struct ieee80211_meshid_ie {
@@ -83,8 +121,14 @@
 
 /* Link Metric Report */
 struct ieee80211_meshlmetric_ie {
-	uint8_t		lm_ie;	/* IEEE80211_ELEMID_MESHLINK */
+	uint8_t		lm_ie;	/* IEEE80211_ACTION_MESH_LMETRIC */
 	uint8_t		lm_len;
+	uint8_t		lm_flags;
+#define	IEEE80211_MESH_LMETRIC_FLAGS_REQ	0x01	/* Request */
+	/*
+	 * XXX: this field should be variable in size and depend on
+	 * the active active path selection metric identifier
+	 */
 	uint32_t	lm_metric;
 #define	IEEE80211_MESHLMETRIC_INITIALVAL	0
 } __packed;
@@ -98,32 +142,24 @@
 } __packed;
 
 /* Peer Link Management */
+#define IEEE80211_MPM_BASE_SZ	(4)
+#define IEEE80211_MPM_MAX_SZ	(8)
 struct ieee80211_meshpeer_ie {
 	uint8_t		peer_ie;	/* IEEE80211_ELEMID_MESHPEER */
 	uint8_t		peer_len;
-	uint8_t		peer_proto[4];	/* Peer Management Protocol */
+	uint16_t	peer_proto;	/* Peer Management Protocol */
 	uint16_t	peer_llinkid;	/* Local Link ID */
 	uint16_t	peer_linkid;	/* Peer Link ID */
 	uint16_t	peer_rcode;
 } __packed;
 
+/* Mesh Peering Protocol Identifier field value */
 enum {
-	IEEE80211_MESH_PEER_LINK_OPEN		= 0,
-	IEEE80211_MESH_PEER_LINK_CONFIRM	= 1,
-	IEEE80211_MESH_PEER_LINK_CLOSE		= 2,
-	/* values 3-255 are reserved */
+	IEEE80211_MPPID_MPM		= 0,	/* Mesh peering management */
+	IEEE80211_MPPID_AUTH_MPM	= 1,	/* Auth. mesh peering exchange */
+	/* 2-65535 reserved */
 };
 
-/* Mesh Peering Management Protocol */
-#define	IEEE80211_MESH_PEER_PROTO_OUI		0x00, 0x0f, 0xac
-#define	IEEE80211_MESH_PEER_PROTO_VALUE		0x2a
-#define	IEEE80211_MESH_PEER_PROTO	{ IEEE80211_MESH_PEER_PROTO_OUI, \
-					  IEEE80211_MESH_PEER_PROTO_VALUE }
-/* Abbreviated Handshake Protocol */
-#define	IEEE80211_MESH_PEER_PROTO_AH_OUI	0x00, 0x0f, 0xac
-#define	IEEE80211_MESH_PEER_PROTO_AH_VALUE	0x2b
-#define	IEEE80211_MESH_PEER_PROTO_AH	{ IEEE80211_MESH_PEER_PROTO_AH_OUI, \
-					  IEEE80211_MESH_PEER_PROTO_AH_VALUE }
 #ifdef notyet
 /* Mesh Channel Switch Annoucement */
 struct ieee80211_meshcsa_ie {
@@ -158,37 +194,50 @@
 } __packed;
 #endif
 
-/* Portal (MP) Annoucement */
-struct ieee80211_meshpann_ie {
-	uint8_t		pann_ie;		/* IEEE80211_ELEMID_MESHPANN */
-	uint8_t		pann_len;
-	uint8_t		pann_flags;
-	uint8_t		pann_hopcount;
-	uint8_t		pann_ttl;
-	uint8_t		pann_addr[IEEE80211_ADDR_LEN];
-	uint8_t		pann_seq;		/* PANN Sequence Number */
+/* Gate (GANN) Annoucement */
+/*
+ * NB: these macros used for the length in the IEs does not include 2 bytes
+ * for _ie and _len fields as is defined by the standard.
+ */
+#define	IEEE80211_MESHGANN_BASE_SZ 	(15)
+struct ieee80211_meshgann_ie {
+	uint8_t		gann_ie;		/* IEEE80211_ELEMID_MESHGANN */
+	uint8_t		gann_len;
+	uint8_t		gann_flags;
+	uint8_t		gann_hopcount;
+	uint8_t		gann_ttl;
+	uint8_t		gann_addr[IEEE80211_ADDR_LEN];
+	uint32_t	gann_seq;		/* GANN Sequence Number */
+	uint16_t	gann_interval;		/* GANN Interval */
 } __packed;
 
 /* Root (MP) Annoucement */
+#define	IEEE80211_MESHRANN_BASE_SZ 	(21)
 struct ieee80211_meshrann_ie {
 	uint8_t		rann_ie;		/* IEEE80211_ELEMID_MESHRANN */
 	uint8_t		rann_len;
 	uint8_t		rann_flags;
-#define	IEEE80211_MESHRANN_FLAGS_PR	0x01	/* Portal Role */
+#define	IEEE80211_MESHRANN_FLAGS_GATE	0x01	/* Mesh Gate */
 	uint8_t		rann_hopcount;
 	uint8_t		rann_ttl;
 	uint8_t		rann_addr[IEEE80211_ADDR_LEN];
 	uint32_t	rann_seq;		/* HWMP Sequence Number */
+	uint32_t	rann_interval;
 	uint32_t	rann_metric;
 } __packed;
 
 /* Mesh Path Request */
+#define	IEEE80211_MESHPREQ_BASE_SZ 		(26)
+#define	IEEE80211_MESHPREQ_BASE_SZ_AE 		(32)
+#define	IEEE80211_MESHPREQ_TRGT_SZ 		(11)
+#define	IEEE80211_MESHPREQ_TCNT_OFFSET		(27)
+#define	IEEE80211_MESHPREQ_TCNT_OFFSET_AE	(33)
 struct ieee80211_meshpreq_ie {
 	uint8_t		preq_ie;	/* IEEE80211_ELEMID_MESHPREQ */
 	uint8_t		preq_len;
 	uint8_t		preq_flags;
-#define	IEEE80211_MESHPREQ_FLAGS_PR	0x01	/* Portal Role */
-#define	IEEE80211_MESHPREQ_FLAGS_AM	0x02	/* 0 = ucast / 1 = bcast */
+#define	IEEE80211_MESHPREQ_FLAGS_GATE	0x01	/* Mesh Gate */
+#define	IEEE80211_MESHPREQ_FLAGS_AM	0x02	/* 0 = bcast / 1 = ucast */
 #define	IEEE80211_MESHPREQ_FLAGS_PP	0x04	/* Proactive PREP */
 #define	IEEE80211_MESHPREQ_FLAGS_AE	0x40	/* Address Extension */
 	uint8_t		preq_hopcount;
@@ -196,7 +245,8 @@
 	uint32_t	preq_id;
 	uint8_t		preq_origaddr[IEEE80211_ADDR_LEN];
 	uint32_t	preq_origseq;	/* HWMP Sequence Number */
-	/* NB: may have Originator Proxied Address */
+	/* NB: may have Originator External Address */
+	uint8_t		preq_orig_ext_addr[IEEE80211_ADDR_LEN];
 	uint32_t	preq_lifetime;
 	uint32_t	preq_metric;
 	uint8_t		preq_tcount;	/* target count */
@@ -203,7 +253,6 @@
 	struct {
 		uint8_t		target_flags;
 #define	IEEE80211_MESHPREQ_TFLAGS_TO	0x01	/* Target Only */
-#define	IEEE80211_MESHPREQ_TFLAGS_RF	0x02	/* Reply and Forward */
 #define	IEEE80211_MESHPREQ_TFLAGS_USN	0x04	/* Unknown HWMP seq number */
 		uint8_t		target_addr[IEEE80211_ADDR_LEN];
 		uint32_t	target_seq;	/* HWMP Sequence Number */
@@ -211,15 +260,19 @@
 } __packed;
 
 /* Mesh Path Reply */
+#define	IEEE80211_MESHPREP_BASE_SZ 	(31)
+#define	IEEE80211_MESHPREP_BASE_SZ_AE 	(37)
 struct ieee80211_meshprep_ie {
 	uint8_t		prep_ie;	/* IEEE80211_ELEMID_MESHPREP */
 	uint8_t		prep_len;
 	uint8_t		prep_flags;
+#define	IEEE80211_MESHPREP_FLAGS_AE	0x40	/* Address Extension */
 	uint8_t		prep_hopcount;
 	uint8_t		prep_ttl;
 	uint8_t		prep_targetaddr[IEEE80211_ADDR_LEN];
 	uint32_t	prep_targetseq;
-	/* NB: May have Target Proxied Address */
+	/* NB: May have Target External Address */
+	uint8_t		prep_target_ext_addr[IEEE80211_ADDR_LEN];
 	uint32_t	prep_lifetime;
 	uint32_t	prep_metric;
 	uint8_t		prep_origaddr[IEEE80211_ADDR_LEN];
@@ -227,6 +280,11 @@
 } __packed;
 
 /* Mesh Path Error */
+#define	IEEE80211_MESHPERR_MAXDEST	(19)
+#define	IEEE80211_MESHPERR_NDEST_OFFSET	(3)
+#define	IEEE80211_MESHPERR_BASE_SZ 	(2)
+#define	IEEE80211_MESHPERR_DEST_SZ 	(13)
+#define	IEEE80211_MESHPERR_DEST_SZ_AE 	(19)
 struct ieee80211_meshperr_ie {
 	uint8_t		perr_ie;	/* IEEE80211_ELEMID_MESHPERR */
 	uint8_t		perr_len;
@@ -234,10 +292,13 @@
 	uint8_t		perr_ndests;	/* Number of Destinations */
 	struct {
 		uint8_t		dest_flags;
-#define	IEEE80211_MESHPERR_DFLAGS_USN	0x01
-#define	IEEE80211_MESHPERR_DFLAGS_RC	0x02
+#define	IEEE80211_MESHPERR_DFLAGS_USN	0x01	/* XXX: not part of standard */
+#define	IEEE80211_MESHPERR_DFLAGS_RC	0x02	/* XXX: not part of standard */
+#define	IEEE80211_MESHPERR_FLAGS_AE	0x40	/* Address Extension */
 		uint8_t		dest_addr[IEEE80211_ADDR_LEN];
 		uint32_t	dest_seq;	/* HWMP Sequence Number */
+		/* NB: May have Destination External Address */
+		uint8_t		dest_ext_addr[IEEE80211_ADDR_LEN];
 		uint16_t	dest_rcode;
 	} __packed perr_dests[1];		/* NB: variable size */
 } __packed;
@@ -269,10 +330,9 @@
 
 /*
  * 802.11s Action Frames
+ * XXX: these are wrong, and some of them should be
+ * under MESH category while PROXY is under MULTIHOP category.
  */
-#define	IEEE80211_ACTION_CAT_MESHPEERING	30	/* XXX Linux */
-#define	IEEE80211_ACTION_CAT_MESHLMETRIC	13
-#define	IEEE80211_ACTION_CAT_MESHPATH		32	/* XXX Linux */
 #define	IEEE80211_ACTION_CAT_INTERWORK		15
 #define	IEEE80211_ACTION_CAT_RESOURCE		16
 #define	IEEE80211_ACTION_CAT_PROXY		17
@@ -281,38 +341,32 @@
  * Mesh Peering Action codes.
  */
 enum {
-	IEEE80211_ACTION_MESHPEERING_OPEN	= 0,
-	IEEE80211_ACTION_MESHPEERING_CONFIRM	= 1,
-	IEEE80211_ACTION_MESHPEERING_CLOSE	= 2,
-	/* 3-255 reserved */
+	/* 0 reserved */
+	IEEE80211_ACTION_MESHPEERING_OPEN	= 1,
+	IEEE80211_ACTION_MESHPEERING_CONFIRM	= 2,
+	IEEE80211_ACTION_MESHPEERING_CLOSE	= 3,
+	/* 4-255 reserved */
 };
 
 /*
- * Mesh Path Selection Action code.
+ * Mesh Action code.
  */
 enum {
-	IEEE80211_ACTION_MESHPATH_SEL	= 0,
-	/* 1-255 reserved */
+	IEEE80211_ACTION_MESH_LMETRIC	= 0,	/* Mesh Link Metric Report */
+	IEEE80211_ACTION_MESH_HWMP	= 1,	/* HWMP Mesh Path Selection */
+	IEEE80211_ACTION_MESH_GANN	= 2,	/* Gate Announcement */
+	IEEE80211_ACTION_MESH_CC	= 3,	/* Congestion Control */
+	IEEE80211_ACTION_MESH_MCCA_SREQ	= 4,	/* MCCA Setup Request */
+	IEEE80211_ACTION_MESH_MCCA_SREP	= 5,	/* MCCA Setup Reply */
+	IEEE80211_ACTION_MESH_MCCA_AREQ	= 6,	/* MCCA Advertisement Req. */
+	IEEE80211_ACTION_MESH_MCCA_ADVER =7,	/* MCCA Advertisement */
+	IEEE80211_ACTION_MESH_MCCA_TRDOWN = 8,	/* MCCA Teardown */
+	IEEE80211_ACTION_MESH_TBTT_REQ	= 9,	/* TBTT Adjustment Request */
+	IEEE80211_ACTION_MESH_TBTT_RES	= 10,	/* TBTT Adjustment Response */
+	/* 11-255 reserved */
 };
 
 /*
- * Mesh Link Metric Action codes.
- */
-enum {
-	IEEE80211_ACTION_MESHLMETRIC_REQ = 0,	/* Link Metric Request */
-	IEEE80211_ACTION_MESHLMETRIC_REP = 1,	/* Link Metric Report */
-	/* 2-255 reserved */
-};
-
-/*
- * Mesh Portal Annoucement Action codes.
- */
-enum {
-	IEEE80211_ACTION_MESHPANN	= 0,
-	/* 1-255 reserved */
-};
-
-/*
  * Different mesh control structures based on the AE
  * (Address Extension) bits.
  */
@@ -334,37 +388,67 @@
 	uint8_t		mc_flags;	/* Address Extension 10 */
 	uint8_t		mc_ttl;		/* TTL */
 	uint8_t		mc_seq[4];	/* Sequence No. */
-	uint8_t		mc_addr4[IEEE80211_ADDR_LEN];
 	uint8_t		mc_addr5[IEEE80211_ADDR_LEN];
-} __packed;
-
-struct ieee80211_meshcntl_ae11 {
-	uint8_t		mc_flags;	/* Address Extension 11 */
-	uint8_t		mc_ttl;		/* TTL */
-	uint8_t		mc_seq[4];	/* Sequence No. */
-	uint8_t		mc_addr4[IEEE80211_ADDR_LEN];
-	uint8_t		mc_addr5[IEEE80211_ADDR_LEN];
 	uint8_t		mc_addr6[IEEE80211_ADDR_LEN];
 } __packed;
 
+#define IEEE80211_MESH_AE_MASK		0x03
+enum {
+	IEEE80211_MESH_AE_00		= 0,	/* MC has no AE subfield */
+	IEEE80211_MESH_AE_01		= 1,	/* MC contain addr4 */
+	IEEE80211_MESH_AE_10		= 2,	/* MC contain addr5 & addr6 */
+	IEEE80211_MESH_AE_11		= 3,	/* RESERVED */
+};
+
 #ifdef _KERNEL
+MALLOC_DECLARE(M_80211_MESH_PREQ);
+MALLOC_DECLARE(M_80211_MESH_PREP);
+MALLOC_DECLARE(M_80211_MESH_PERR);
+
 MALLOC_DECLARE(M_80211_MESH_RT);
+MALLOC_DECLARE(M_80211_MESH_GT_RT);
+/*
+ * Basic forwarding information:
+ * o Destination MAC
+ * o Next-hop MAC
+ * o Precursor list (not implemented yet)
+ * o Path timeout
+ * The rest is part of the active Mesh path selection protocol.
+ * XXX: to be moved out later.
+ */
 struct ieee80211_mesh_route {
 	TAILQ_ENTRY(ieee80211_mesh_route)	rt_next;
-	int			rt_crtime;	/* creation time */
+	struct ieee80211vap	*rt_vap;
+	struct mtx		rt_lock;	/* fine grained route lock */
+	struct callout		rt_discovery;	/* discovery timeout */
+	int			rt_updtime;	/* last update time */
 	uint8_t			rt_dest[IEEE80211_ADDR_LEN];
+	uint8_t			rt_mesh_gate[IEEE80211_ADDR_LEN]; /* meshDA */
 	uint8_t			rt_nexthop[IEEE80211_ADDR_LEN];
 	uint32_t		rt_metric;	/* path metric */
 	uint16_t		rt_nhops;	/* number of hops */
 	uint16_t		rt_flags;
-#define	IEEE80211_MESHRT_FLAGS_VALID	0x01	/* patch discovery complete */
-#define	IEEE80211_MESHRT_FLAGS_PROXY	0x02	/* proxy entry */
-	uint32_t		rt_lifetime;
+#define	IEEE80211_MESHRT_FLAGS_DISCOVER	0x01	/* path discovery */
+#define	IEEE80211_MESHRT_FLAGS_VALID	0x02	/* path discovery complete */
+#define	IEEE80211_MESHRT_FLAGS_PROXY	0x04	/* proxy entry */
+#define	IEEE80211_MESHRT_FLAGS_GATE	0x08	/* mesh gate entry */
+	uint32_t		rt_lifetime;	/* route timeout */
 	uint32_t		rt_lastmseq;	/* last seq# seen dest */
+	uint32_t		rt_ext_seq;	/* proxy seq number */
 	void			*rt_priv;	/* private data */
 };
 #define	IEEE80211_MESH_ROUTE_PRIV(rt, cast)	((cast *)rt->rt_priv)
 
+/*
+ * Stored information about known mesh gates.
+ */
+struct ieee80211_mesh_gate_route {
+	TAILQ_ENTRY(ieee80211_mesh_gate_route)	gr_next;
+	uint8_t				gr_addr[IEEE80211_ADDR_LEN];
+	uint32_t			gr_lastseq;
+	struct ieee80211_mesh_route 	*gr_route;
+};
+
 #define	IEEE80211_MESH_PROTO_DSZ	12	/* description size */
 /*
  * Mesh Path Selection Protocol.
@@ -379,6 +463,9 @@
 				const uint8_t [IEEE80211_ADDR_LEN],
 				struct mbuf *);
 	void		(*mpp_peerdown)(struct ieee80211_node *);
+	void		(*mpp_senderror)(struct ieee80211vap *,
+				const uint8_t [IEEE80211_ADDR_LEN],
+				struct ieee80211_mesh_route *, int);
 	void		(*mpp_vattach)(struct ieee80211vap *);
 	void		(*mpp_vdetach)(struct ieee80211vap *);
 	int		(*mpp_newstate)(struct ieee80211vap *,
@@ -425,11 +512,15 @@
 	uint16_t			ms_neighbors;
 	uint8_t				ms_ttl;	/* mesh ttl set in packets */
 #define IEEE80211_MESHFLAGS_AP		0x01	/* accept peers */
-#define IEEE80211_MESHFLAGS_PORTAL	0x02	/* mesh portal role */
+#define IEEE80211_MESHFLAGS_GATE	0x02	/* mesh gate role */
 #define IEEE80211_MESHFLAGS_FWD		0x04	/* forward packets */
+#define IEEE80211_MESHFLAGS_ROOT	0x08	/* configured as root */
 	uint8_t				ms_flags;
 	struct mtx			ms_rt_lock;
 	struct callout			ms_cleantimer;
+	struct callout			ms_gatetimer;
+	ieee80211_mesh_seq		ms_gateseq;
+	TAILQ_HEAD(, ieee80211_mesh_gate_route) ms_known_gates;
 	TAILQ_HEAD(, ieee80211_mesh_route)  ms_routes;
 	struct ieee80211_mesh_proto_metric *ms_pmetric;
 	struct ieee80211_mesh_proto_path   *ms_ppath;
@@ -448,6 +539,7 @@
 void		ieee80211_mesh_rt_flush(struct ieee80211vap *);
 void		ieee80211_mesh_rt_flush_peer(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN]);
+int		ieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int);
 void		ieee80211_mesh_proxy_check(struct ieee80211vap *,
 		    const uint8_t [IEEE80211_ADDR_LEN]);
 
@@ -460,7 +552,9 @@
 uint8_t *	ieee80211_add_meshconf(uint8_t *, struct ieee80211vap *);
 uint8_t *	ieee80211_add_meshpeer(uint8_t *, uint8_t, uint16_t, uint16_t,
 		    uint16_t);
-uint8_t *	ieee80211_add_meshlmetric(uint8_t *, uint32_t);
+uint8_t *	ieee80211_add_meshlmetric(uint8_t *, uint8_t, uint32_t);
+uint8_t *	ieee80211_add_meshgate(uint8_t *,
+		    struct ieee80211_meshgann_ie *);
 
 void		ieee80211_mesh_node_init(struct ieee80211vap *,
 		    struct ieee80211_node *);
@@ -473,6 +567,14 @@
 		   const struct ieee80211_scanparams *);
 void		ieee80211_mesh_update_beacon(struct ieee80211vap *,
 		    struct ieee80211_beacon_offsets *);
+struct ieee80211_mesh_gate_route *
+		ieee80211_mesh_mark_gate(struct ieee80211vap *,
+		    const uint8_t *, struct ieee80211_mesh_route *);
+void		ieee80211_mesh_forward_to_gates(struct ieee80211vap *,
+		    struct ieee80211_mesh_route *);
+struct ieee80211_node *
+		ieee80211_mesh_find_txnode(struct ieee80211vap *,
+		    const uint8_t [IEEE80211_ADDR_LEN]);
 
 /*
  * Return non-zero if proxy operation is enabled.
@@ -482,7 +584,7 @@
 {
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	return (ms->ms_flags &
-	    (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_PORTAL)) != 0;
+	    (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_GATE)) != 0;
 }
 
 /*

Modified: trunk/sys/net80211/ieee80211_monitor.c
===================================================================
--- trunk/sys/net80211/ieee80211_monitor.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_monitor.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_monitor.c 193287 2009-06-02 00:04:10Z sam $");
 #endif
 
 /*

Modified: trunk/sys/net80211/ieee80211_monitor.h
===================================================================
--- trunk/sys/net80211/ieee80211_monitor.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_monitor.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_monitor.h 178354 2008-04-20 20:35:46Z sam $
  */
 #ifndef _NET80211_IEEE80211_MONITOR_H_
 #define _NET80211_IEEE80211_MONITOR_H_

Modified: trunk/sys/net80211/ieee80211_node.c
===================================================================
--- trunk/sys/net80211/ieee80211_node.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_node.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_node.c 314667 2017-03-04 13:03:31Z avg $");
 
 #include "opt_wlan.h"
 
@@ -111,7 +112,7 @@
 	    "802.11 staging q");
 	ieee80211_node_table_init(ic, &ic->ic_sta, "station",
 		IEEE80211_INACT_INIT, ic->ic_max_keyix);
-	callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
+	callout_init(&ic->ic_inact, 1);
 	callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
 		ieee80211_node_timeout, ic);
 
@@ -685,6 +686,14 @@
 	ieee80211_runtask(ic, &ic->ic_chan_task);
 }
 
+void
+ieee80211_update_chw(struct ieee80211com *ic)
+{
+
+	ieee80211_setupcurchan(ic, ic->ic_curchan);
+	ieee80211_runtask(ic, &ic->ic_chw_task);
+}
+
 /*
  * Join the specified IBSS/BSS network.  The node is assumed to
  * be passed in with a held reference.
@@ -763,6 +772,7 @@
 		/* XXX msg */
 		return 0;
 	}
+
 	/*
 	 * Expand scan state into node's format.
 	 * XXX may not need all this stuff
@@ -813,6 +823,29 @@
 		IEEE80211_F_DOSORT);
 	if (ieee80211_iserp_rateset(&ni->ni_rates))
 		ni->ni_flags |= IEEE80211_NODE_ERP;
+
+	/*
+	 * Setup HT state for this node if it's available, otherwise
+	 * non-STA modes won't pick this state up.
+	 *
+	 * For IBSS and related modes that don't go through an
+	 * association request/response, the only appropriate place
+	 * to setup the HT state is here.
+	 */
+	if (ni->ni_ies.htinfo_ie != NULL &&
+	    ni->ni_ies.htcap_ie != NULL &&
+	    vap->iv_flags_ht & IEEE80211_FHT_HT) {
+		ieee80211_ht_node_init(ni);
+		ieee80211_ht_updateparams(ni,
+		    ni->ni_ies.htcap_ie,
+		    ni->ni_ies.htinfo_ie);
+		ieee80211_setup_htrates(ni, ni->ni_ies.htcap_ie,
+		    IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+		ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie);
+	}
+	/* XXX else check for ath FF? */
+	/* XXX QoS? Difficult given that WME config is specific to a master */
+
 	ieee80211_node_setuptxparms(ni);
 	ieee80211_ratectl_node_init(ni);
 
@@ -930,6 +963,9 @@
 		case IEEE80211_ELEMID_HTCAP:
 			ies->htcap_ie = ie;
 			break;
+		case IEEE80211_ELEMID_HTINFO:
+			ies->htinfo_ie = ie;
+			break;
 #ifdef IEEE80211_SUPPORT_MESH
 		case IEEE80211_ELEMID_MESHID:
 			ies->meshid_ie = ie;
@@ -950,7 +986,6 @@
 static void
 node_cleanup(struct ieee80211_node *ni)
 {
-#define	N(a)	(sizeof(a)/sizeof(a[0]))
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
 	int i;
@@ -1017,7 +1052,7 @@
 	 *
 	 * XXX does this leave us open to inheriting old state?
 	 */
-	for (i = 0; i < N(ni->ni_rxfrag); i++)
+	for (i = 0; i < nitems(ni->ni_rxfrag); i++)
 		if (ni->ni_rxfrag[i] != NULL) {
 			m_freem(ni->ni_rxfrag[i]);
 			ni->ni_rxfrag[i] = NULL;
@@ -1026,7 +1061,6 @@
 	 * Must be careful here to remove any key map entry w/o a LOR.
 	 */
 	ieee80211_node_delucastkey(ni);
-#undef N
 }
 
 static void
@@ -1396,7 +1430,7 @@
 {
 	struct ieee80211_node *ni;
 
-	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE | IEEE80211_MSG_ASSOC,
 	    "%s: mac<%s>\n", __func__, ether_sprintf(macaddr));
 	ni = ieee80211_dup_bss(vap, macaddr);
 	if (ni != NULL) {
@@ -1436,6 +1470,8 @@
 	const struct ieee80211_frame *wh,
 	const struct ieee80211_scanparams *sp)
 {
+	int do_ht_setup = 0;
+
 	ni->ni_esslen = sp->ssid[1];
 	memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
 	IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
@@ -1461,6 +1497,16 @@
 		if (ni->ni_ies.ath_ie != NULL)
 			ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
 #endif
+		if (ni->ni_ies.htcap_ie != NULL)
+			ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie);
+		if (ni->ni_ies.htinfo_ie != NULL)
+			ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie);
+
+		if ((ni->ni_ies.htcap_ie != NULL) &&
+		    (ni->ni_ies.htinfo_ie != NULL) &&
+		    (ni->ni_vap->iv_flags_ht & IEEE80211_FHT_HT)) {
+			do_ht_setup = 1;
+		}
 	}
 
 	/* NB: must be after ni_chan is setup */
@@ -1467,6 +1513,25 @@
 	ieee80211_setup_rates(ni, sp->rates, sp->xrates,
 		IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 		IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+
+	/*
+	 * If the neighbor is HT compatible, flip that on.
+	 */
+	if (do_ht_setup) {
+		IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
+		    "%s: doing HT setup\n", __func__);
+		ieee80211_ht_node_init(ni);
+		ieee80211_ht_updateparams(ni,
+		    ni->ni_ies.htcap_ie,
+		    ni->ni_ies.htinfo_ie);
+		ieee80211_setup_htrates(ni,
+		    ni->ni_ies.htcap_ie,
+		    IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+		ieee80211_setup_basic_htrates(ni,
+		    ni->ni_ies.htinfo_ie);
+		ieee80211_node_setuptxparms(ni);
+		ieee80211_ratectl_node_init(ni);
+	}
 }
 
 /*
@@ -1482,7 +1547,7 @@
 {
 	struct ieee80211_node *ni;
 
-	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
 	    "%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2));
 	ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */
 	if (ni != NULL) {
@@ -2148,33 +2213,134 @@
 		ieee80211_node_timeout, ic);
 }
 
-void
-ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
-	ieee80211_iter_func *f, void *arg)
+/*
+ * Iterate over the node table and return an array of ref'ed nodes.
+ *
+ * This is separated out from calling the actual node function so that
+ * no LORs will occur.
+ *
+ * If there are too many nodes (ie, the number of nodes doesn't fit
+ * within 'max_aid' entries) then the node references will be freed
+ * and an error will be returned.
+ *
+ * The responsibility of allocating and freeing "ni_arr" is up to
+ * the caller.
+ */
+int
+ieee80211_iterate_nt(struct ieee80211_node_table *nt,
+    struct ieee80211_node **ni_arr, uint16_t max_aid)
 {
+	u_int gen;
+	int i, j, ret;
 	struct ieee80211_node *ni;
-	u_int gen;
 
 	IEEE80211_NODE_ITERATE_LOCK(nt);
+	IEEE80211_NODE_LOCK(nt);
+
 	gen = ++nt->nt_scangen;
-restart:
-	IEEE80211_NODE_LOCK(nt);
+	i = ret = 0;
+
+	/*
+	 * We simply assume here that since the node
+	 * scan generation doesn't change (as
+	 * we are holding both the node table and
+	 * node table iteration locks), we can simply
+	 * assign it to the node here.
+	 */
 	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
-		if (ni->ni_scangen != gen) {
-			ni->ni_scangen = gen;
-			(void) ieee80211_ref_node(ni);
-			IEEE80211_NODE_UNLOCK(nt);
-			(*f)(arg, ni);
-			ieee80211_free_node(ni);
-			goto restart;
+		if (i >= max_aid) {
+			ret = E2BIG;
+			if_printf(nt->nt_ic->ic_ifp,
+			    "Node array overflow: max=%u", max_aid);
+			break;
 		}
+		ni_arr[i] = ieee80211_ref_node(ni);
+		ni_arr[i]->ni_scangen = gen;
+		i++;
 	}
+
+	/*
+	 * It's safe to unlock here.
+	 *
+	 * If we're successful, the list is returned.
+	 * If we're unsuccessful, the list is ignored
+	 * and we remove our references.
+	 *
+	 * This avoids any potential LOR with
+	 * ieee80211_free_node().
+	 */
 	IEEE80211_NODE_UNLOCK(nt);
+	IEEE80211_NODE_ITERATE_UNLOCK(nt);
 
-	IEEE80211_NODE_ITERATE_UNLOCK(nt);
+	/*
+	 * If ret is non-zero, we hit some kind of error.
+	 * Rather than walking some nodes, we'll walk none
+	 * of them.
+	 */
+	if (ret) {
+		for (j = 0; j < i; j++) {
+			/* ieee80211_free_node() locks by itself */
+			ieee80211_free_node(ni_arr[j]);
+		}
+	}
+
+	return (ret);
 }
 
+/*
+ * Just a wrapper, so we don't have to change every ieee80211_iterate_nodes()
+ * reference in the source.
+ *
+ * Note that this fetches 'max_aid' from the first VAP, rather than finding
+ * the largest max_aid from all VAPs.
+ */
 void
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
+	ieee80211_iter_func *f, void *arg)
+{
+	struct ieee80211_node **ni_arr;
+	size_t size;
+	int i;
+	uint16_t max_aid;
+	struct ieee80211vap *vap;
+
+	/* Overdoing it default */
+	max_aid = IEEE80211_AID_MAX;
+
+	/* Handle the case of there being no vaps just yet */
+	vap = TAILQ_FIRST(&nt->nt_ic->ic_vaps);
+	if (vap != NULL)
+		max_aid = vap->iv_max_aid;
+
+	size = max_aid * sizeof(struct ieee80211_node *);
+	ni_arr = (struct ieee80211_node **) malloc(size, M_80211_NODE,
+	    M_NOWAIT | M_ZERO);
+	if (ni_arr == NULL)
+		return;
+
+	/*
+	 * If this fails, the node table won't have any
+	 * valid entries - ieee80211_iterate_nt() frees
+	 * the references to them.  So don't try walking
+	 * the table; just skip to the end and free the
+	 * temporary memory.
+	 */
+	if (ieee80211_iterate_nt(nt, ni_arr, max_aid) != 0)
+		goto done;
+
+	for (i = 0; i < max_aid; i++) {
+		if (ni_arr[i] == NULL)	/* end of the list */
+			break;
+		(*f)(arg, ni_arr[i]);
+		/* ieee80211_free_node() locks by itself */
+		ieee80211_free_node(ni_arr[i]);
+	}
+
+done:
+	free(ni_arr, M_80211_NODE);
+}
+
+void
 ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
 {
 	printf("0x%p: mac %s refcnt %d\n", ni,

Modified: trunk/sys/net80211/ieee80211_node.h
===================================================================
--- trunk/sys/net80211/ieee80211_node.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_node.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_node.h 246497 2013-02-07 21:12:55Z monthadar $
  */
 #ifndef _NET80211_IEEE80211_NODE_H_
 #define _NET80211_IEEE80211_NODE_H_
@@ -204,6 +205,8 @@
 	struct callout		ni_mltimer;	/* link mesh timer */
 	uint8_t			ni_mlrcnt;	/* link mesh retry counter */
 	uint8_t			ni_mltval;	/* link mesh timer value */
+	struct callout		ni_mlhtimer;	/* link mesh backoff timer */
+	uint8_t			ni_mlhcnt;	/* link mesh holding counter */
 
 	/* 11n state */
 	uint16_t		ni_htcap;	/* HT capabilities */
@@ -214,7 +217,7 @@
 	uint8_t			ni_htstbc;	/* HT */
 	uint8_t			ni_chw;		/* negotiated channel width */
 	struct ieee80211_htrateset ni_htrates;	/* negotiated ht rate set */
-	struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_AC];
+	struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_TID];
 	struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
 
 	/* others */
@@ -299,8 +302,6 @@
 	*ni = NULL;			/* guard against use */
 }
 
-struct ieee80211com;
-
 void	ieee80211_node_attach(struct ieee80211com *);
 void	ieee80211_node_lateattach(struct ieee80211com *);
 void	ieee80211_node_detach(struct ieee80211com *);
@@ -326,6 +327,7 @@
 void	ieee80211_setupcurchan(struct ieee80211com *,
 	    struct ieee80211_channel *);
 void	ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
+void	ieee80211_update_chw(struct ieee80211com *);
 int	ieee80211_ibss_merge(struct ieee80211_node *);
 struct ieee80211_scan_entry;
 int	ieee80211_sta_join(struct ieee80211vap *, struct ieee80211_channel *,
@@ -439,6 +441,8 @@
 void	ieee80211_node_timeout(void *arg);
 
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
+int	ieee80211_iterate_nt(struct ieee80211_node_table *,
+		struct ieee80211_node **, uint16_t);
 void	ieee80211_iterate_nodes(struct ieee80211_node_table *,
 		ieee80211_iter_func *, void *);
 

Modified: trunk/sys/net80211/ieee80211_output.c
===================================================================
--- trunk/sys/net80211/ieee80211_output.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_output.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_output.c 283855 2015-05-31 23:29:04Z ae $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -110,31 +111,321 @@
 #endif
 
 /*
- * Start method for vap's.  All packets from the stack come
- * through here.  We handle common processing of the packets
- * before dispatching them to the underlying device.
+ * Transmit a frame to the given destination on the given VAP.
+ *
+ * It's up to the caller to figure out the details of who this
+ * is going to and resolving the node.
+ *
+ * This routine takes care of queuing it for power save,
+ * A-MPDU state stuff, fast-frames state stuff, encapsulation
+ * if required, then passing it up to the driver layer.
+ *
+ * This routine (for now) consumes the mbuf and frees the node
+ * reference; it ideally will return a TX status which reflects
+ * whether the mbuf was consumed or not, so the caller can
+ * free the mbuf (if appropriate) and the node reference (again,
+ * if appropriate.)
  */
-void
-ieee80211_start(struct ifnet *ifp)
+int
+ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m,
+    struct ieee80211_node *ni)
 {
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ifnet *ifp = vap->iv_ifp;
+	int error, len, mcast;
+
+	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+	    (m->m_flags & M_PWR_SAV) == 0) {
+		/*
+		 * Station in power save mode; pass the frame
+		 * to the 802.11 layer and continue.  We'll get
+		 * the frame back when the time is right.
+		 * XXX lose WDS vap linkage?
+		 */
+		if (ieee80211_pwrsave(ni, m) != 0)
+			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+		ieee80211_free_node(ni);
+
+		/*
+		 * We queued it fine, so tell the upper layer
+		 * that we consumed it.
+		 */
+		return (0);
+	}
+	/* calculate priority so drivers can find the tx queue */
+	if (ieee80211_classify(ni, m)) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+		    ni->ni_macaddr, NULL,
+		    "%s", "classification failure");
+		vap->iv_stats.is_tx_classify++;
+		ifp->if_oerrors++;
+		m_freem(m);
+		ieee80211_free_node(ni);
+
+		/* XXX better status? */
+		return (0);
+	}
+	/*
+	 * Stash the node pointer.  Note that we do this after
+	 * any call to ieee80211_dwds_mcast because that code
+	 * uses any existing value for rcvif to identify the
+	 * interface it (might have been) received on.
+	 */
+	m->m_pkthdr.rcvif = (void *)ni;
+	mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1: 0;
+	len = m->m_pkthdr.len;
+
+	BPF_MTAP(ifp, m);		/* 802.3 tx */
+
+	/*
+	 * Check if A-MPDU tx aggregation is setup or if we
+	 * should try to enable it.  The sta must be associated
+	 * with HT and A-MPDU enabled for use.  When the policy
+	 * routine decides we should enable A-MPDU we issue an
+	 * ADDBA request and wait for a reply.  The frame being
+	 * encapsulated will go out w/o using A-MPDU, or possibly
+	 * it might be collected by the driver and held/retransmit.
+	 * The default ic_ampdu_enable routine handles staggering
+	 * ADDBA requests in case the receiver NAK's us or we are
+	 * otherwise unable to establish a BA stream.
+	 */
+	if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
+	    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) &&
+	    (m->m_flags & M_EAPOL) == 0) {
+		int tid = WME_AC_TO_TID(M_WME_GETAC(m));
+		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
+
+		ieee80211_txampdu_count_packet(tap);
+		if (IEEE80211_AMPDU_RUNNING(tap)) {
+			/*
+			 * Operational, mark frame for aggregation.
+			 *
+			 * XXX do tx aggregation here
+			 */
+			m->m_flags |= M_AMPDU_MPDU;
+		} else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
+		    ic->ic_ampdu_enable(ni, tap)) {
+			/*
+			 * Not negotiated yet, request service.
+			 */
+			ieee80211_ampdu_request(ni, tap);
+			/* XXX hold frame for reply? */
+		}
+	}
+
+#ifdef IEEE80211_SUPPORT_SUPERG
+	else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) {
+		m = ieee80211_ff_check(ni, m);
+		if (m == NULL) {
+			/* NB: any ni ref held on stageq */
+			return (0);
+		}
+	}
+#endif /* IEEE80211_SUPPORT_SUPERG */
+
+	/*
+	 * Grab the TX lock - serialise the TX process from this
+	 * point (where TX state is being checked/modified)
+	 * through to driver queue.
+	 */
+	IEEE80211_TX_LOCK(ic);
+
+	if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) {
+		/*
+		 * Encapsulate the packet in prep for transmission.
+		 */
+		m = ieee80211_encap(vap, ni, m);
+		if (m == NULL) {
+			/* NB: stat+msg handled in ieee80211_encap */
+			IEEE80211_TX_UNLOCK(ic);
+			ieee80211_free_node(ni);
+			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+			return (ENOBUFS);
+		}
+	}
+	error = ieee80211_parent_xmitpkt(ic, m);
+
+	/*
+	 * Unlock at this point - no need to hold it across
+	 * ieee80211_free_node() (ie, the comlock)
+	 */
+	IEEE80211_TX_UNLOCK(ic);
+	if (error != 0) {
+		/* NB: IFQ_HANDOFF reclaims mbuf */
+		ieee80211_free_node(ni);
+		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+	} else {
+		ifp->if_opackets++;
+		if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast);
+		if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
+	}
+	ic->ic_lastdata = ticks;
+
+	return (0);
+}
+
+
+
+/*
+ * Send the given mbuf through the given vap.
+ *
+ * This consumes the mbuf regardless of whether the transmit
+ * was successful or not.
+ *
+ * This does none of the initial checks that ieee80211_start()
+ * does (eg CAC timeout, interface wakeup) - the caller must
+ * do this first.
+ */
+static int
+ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m)
+{
 #define	IS_DWDS(vap) \
 	(vap->iv_opmode == IEEE80211_M_WDS && \
 	 (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0)
-	struct ieee80211vap *vap = ifp->if_softc;
 	struct ieee80211com *ic = vap->iv_ic;
-	struct ifnet *parent = ic->ic_ifp;
+	struct ifnet *ifp = vap->iv_ifp;
 	struct ieee80211_node *ni;
-	struct mbuf *m;
 	struct ether_header *eh;
-	int error;
 
+	/*
+	 * Cancel any background scan.
+	 */
+	if (ic->ic_flags & IEEE80211_F_SCAN)
+		ieee80211_cancel_anyscan(vap);
+	/* 
+	 * Find the node for the destination so we can do
+	 * things like power save and fast frames aggregation.
+	 *
+	 * NB: past this point various code assumes the first
+	 *     mbuf has the 802.3 header present (and contiguous).
+	 */
+	ni = NULL;
+	if (m->m_len < sizeof(struct ether_header) &&
+	   (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+		    "discard frame, %s\n", "m_pullup failed");
+		vap->iv_stats.is_tx_nobuf++;	/* XXX */
+		ifp->if_oerrors++;
+		return (ENOBUFS);
+	}
+	eh = mtod(m, struct ether_header *);
+	if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+		if (IS_DWDS(vap)) {
+			/*
+			 * Only unicast frames from the above go out
+			 * DWDS vaps; multicast frames are handled by
+			 * dispatching the frame as it comes through
+			 * the AP vap (see below).
+			 */
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS,
+			    eh->ether_dhost, "mcast", "%s", "on DWDS");
+			vap->iv_stats.is_dwds_mcast++;
+			m_freem(m);
+			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+			/* XXX better status? */
+			return (ENOBUFS);
+		}
+		if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+			/*
+			 * Spam DWDS vap's w/ multicast traffic.
+			 */
+			/* XXX only if dwds in use? */
+			ieee80211_dwds_mcast(vap, m);
+		}
+	}
+#ifdef IEEE80211_SUPPORT_MESH
+	if (vap->iv_opmode != IEEE80211_M_MBSS) {
+#endif
+		ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+		if (ni == NULL) {
+			/* NB: ieee80211_find_txnode does stat+msg */
+			ifp->if_oerrors++;
+			m_freem(m);
+			/* XXX better status? */
+			return (ENOBUFS);
+		}
+		if (ni->ni_associd == 0 &&
+		    (ni->ni_flags & IEEE80211_NODE_ASSOCID)) {
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+			    eh->ether_dhost, NULL,
+			    "sta not associated (type 0x%04x)",
+			    htons(eh->ether_type));
+			vap->iv_stats.is_tx_notassoc++;
+			ifp->if_oerrors++;
+			m_freem(m);
+			ieee80211_free_node(ni);
+			/* XXX better status? */
+			return (ENOBUFS);
+		}
+#ifdef IEEE80211_SUPPORT_MESH
+	} else {
+		if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) {
+			/*
+			 * Proxy station only if configured.
+			 */
+			if (!ieee80211_mesh_isproxyena(vap)) {
+				IEEE80211_DISCARD_MAC(vap,
+				    IEEE80211_MSG_OUTPUT |
+				    IEEE80211_MSG_MESH,
+				    eh->ether_dhost, NULL,
+				    "%s", "proxy not enabled");
+				vap->iv_stats.is_mesh_notproxy++;
+				ifp->if_oerrors++;
+				m_freem(m);
+				/* XXX better status? */
+				return (ENOBUFS);
+			}
+			IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+			    "forward frame from DS SA(%6D), DA(%6D)\n",
+			    eh->ether_shost, ":",
+			    eh->ether_dhost, ":");
+			ieee80211_mesh_proxy_check(vap, eh->ether_shost);
+		}
+		ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m);
+		if (ni == NULL) {
+			/*
+			 * NB: ieee80211_mesh_discover holds/disposes
+			 * frame (e.g. queueing on path discovery).
+			 */
+			ifp->if_oerrors++;
+			/* XXX better status? */
+			return (ENOBUFS);
+		}
+	}
+#endif
+
+	/*
+	 * We've resolved the sender, so attempt to transmit it.
+	 */
+	if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0)
+		return (ENOBUFS);
+	return (0);
+#undef	IS_DWDS
+}
+
+/*
+ * Start method for vap's.  All packets from the stack come
+ * through here.  We handle common processing of the packets
+ * before dispatching them to the underlying device.
+ *
+ * if_transmit() requires that the mbuf be consumed by this call
+ * regardless of the return condition.
+ */
+int
+ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+	struct ieee80211vap *vap = ifp->if_softc;
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ifnet *parent = ic->ic_ifp;
+
 	/* NB: parent must be up and running */
 	if (!IFNET_IS_UP_RUNNING(parent)) {
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
 		    "%s: ignore queue, parent %s not up+running\n",
 		    __func__, parent->if_xname);
-		/* XXX stat */
-		return;
+		m_freem(m);
+		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+		return (ENETDOWN);
 	}
 	if (vap->iv_state == IEEE80211_S_SLEEP) {
 		/*
@@ -141,7 +432,8 @@
 		 * In power save, wakeup device for transmit.
 		 */
 		ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
-		return;
+		m_freem(m);
+		return (0);
 	}
 	/*
 	 * No data frames go out unless we're running.
@@ -157,237 +449,74 @@
 			    "%s: ignore queue, in %s state\n",
 			    __func__, ieee80211_state_name[vap->iv_state]);
 			vap->iv_stats.is_tx_badstate++;
+			IEEE80211_UNLOCK(ic);
 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
-			IEEE80211_UNLOCK(ic);
-			return;
+			m_freem(m);
+			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+			return (ENETDOWN);
 		}
 		IEEE80211_UNLOCK(ic);
 	}
-	for (;;) {
-		IFQ_DEQUEUE(&ifp->if_snd, m);
-		if (m == NULL)
-			break;
-		/*
-		 * Sanitize mbuf flags for net80211 use.  We cannot
-		 * clear M_PWR_SAV or M_MORE_DATA because these may
-		 * be set for frames that are re-submitted from the
-		 * power save queue.
-		 *
-		 * NB: This must be done before ieee80211_classify as
-		 *     it marks EAPOL in frames with M_EAPOL.
-		 */
-		m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA);
-		/*
-		 * Cancel any background scan.
-		 */
-		if (ic->ic_flags & IEEE80211_F_SCAN)
-			ieee80211_cancel_anyscan(vap);
-		/* 
-		 * Find the node for the destination so we can do
-		 * things like power save and fast frames aggregation.
-		 *
-		 * NB: past this point various code assumes the first
-		 *     mbuf has the 802.3 header present (and contiguous).
-		 */
-		ni = NULL;
-		if (m->m_len < sizeof(struct ether_header) &&
-		   (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
-			IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
-			    "discard frame, %s\n", "m_pullup failed");
-			vap->iv_stats.is_tx_nobuf++;	/* XXX */
-			ifp->if_oerrors++;
-			continue;
-		}
-		eh = mtod(m, struct ether_header *);
-		if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
-			if (IS_DWDS(vap)) {
-				/*
-				 * Only unicast frames from the above go out
-				 * DWDS vaps; multicast frames are handled by
-				 * dispatching the frame as it comes through
-				 * the AP vap (see below).
-				 */
-				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS,
-				    eh->ether_dhost, "mcast", "%s", "on DWDS");
-				vap->iv_stats.is_dwds_mcast++;
-				m_freem(m);
-				continue;
-			}
-			if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
-				/*
-				 * Spam DWDS vap's w/ multicast traffic.
-				 */
-				/* XXX only if dwds in use? */
-				ieee80211_dwds_mcast(vap, m);
-			}
-		}
-#ifdef IEEE80211_SUPPORT_MESH
-		if (vap->iv_opmode != IEEE80211_M_MBSS) {
-#endif
-			ni = ieee80211_find_txnode(vap, eh->ether_dhost);
-			if (ni == NULL) {
-				/* NB: ieee80211_find_txnode does stat+msg */
-				ifp->if_oerrors++;
-				m_freem(m);
-				continue;
-			}
-			if (ni->ni_associd == 0 &&
-			    (ni->ni_flags & IEEE80211_NODE_ASSOCID)) {
-				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
-				    eh->ether_dhost, NULL,
-				    "sta not associated (type 0x%04x)",
-				    htons(eh->ether_type));
-				vap->iv_stats.is_tx_notassoc++;
-				ifp->if_oerrors++;
-				m_freem(m);
-				ieee80211_free_node(ni);
-				continue;
-			}
-#ifdef IEEE80211_SUPPORT_MESH
-		} else {
-			if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) {
-				/*
-				 * Proxy station only if configured.
-				 */
-				if (!ieee80211_mesh_isproxyena(vap)) {
-					IEEE80211_DISCARD_MAC(vap,
-					    IEEE80211_MSG_OUTPUT |
-						IEEE80211_MSG_MESH,
-					    eh->ether_dhost, NULL,
-					    "%s", "proxy not enabled");
-					vap->iv_stats.is_mesh_notproxy++;
-					ifp->if_oerrors++;
-					m_freem(m);
-					continue;
-				}
-				ieee80211_mesh_proxy_check(vap, eh->ether_shost);
-			}
-			ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m);
-			if (ni == NULL) {
-				/*
-				 * NB: ieee80211_mesh_discover holds/disposes
-				 * frame (e.g. queueing on path discovery).
-				 */
-				ifp->if_oerrors++;
-				continue;
-			}
-		}
-#endif
-		if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
-		    (m->m_flags & M_PWR_SAV) == 0) {
-			/*
-			 * Station in power save mode; pass the frame
-			 * to the 802.11 layer and continue.  We'll get
-			 * the frame back when the time is right.
-			 * XXX lose WDS vap linkage?
-			 */
-			(void) ieee80211_pwrsave(ni, m);
-			ieee80211_free_node(ni);
-			continue;
-		}
-		/* calculate priority so drivers can find the tx queue */
-		if (ieee80211_classify(ni, m)) {
-			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
-			    eh->ether_dhost, NULL,
-			    "%s", "classification failure");
-			vap->iv_stats.is_tx_classify++;
-			ifp->if_oerrors++;
-			m_freem(m);
-			ieee80211_free_node(ni);
-			continue;
-		}
-		/*
-		 * Stash the node pointer.  Note that we do this after
-		 * any call to ieee80211_dwds_mcast because that code
-		 * uses any existing value for rcvif to identify the
-		 * interface it (might have been) received on.
-		 */
-		m->m_pkthdr.rcvif = (void *)ni;
 
-		BPF_MTAP(ifp, m);		/* 802.3 tx */
- 
-		/*
-		 * Check if A-MPDU tx aggregation is setup or if we
-		 * should try to enable it.  The sta must be associated
-		 * with HT and A-MPDU enabled for use.  When the policy
-		 * routine decides we should enable A-MPDU we issue an
-		 * ADDBA request and wait for a reply.  The frame being
-		 * encapsulated will go out w/o using A-MPDU, or possibly
-		 * it might be collected by the driver and held/retransmit.
-		 * The default ic_ampdu_enable routine handles staggering
-		 * ADDBA requests in case the receiver NAK's us or we are
-		 * otherwise unable to establish a BA stream.
-		 */
-		if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
-		    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) &&
-		    (m->m_flags & M_EAPOL) == 0) {
-			const int ac = M_WME_GETAC(m);
-			struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+	/*
+	 * Sanitize mbuf flags for net80211 use.  We cannot
+	 * clear M_PWR_SAV or M_MORE_DATA because these may
+	 * be set for frames that are re-submitted from the
+	 * power save queue.
+	 *
+	 * NB: This must be done before ieee80211_classify as
+	 *     it marks EAPOL in frames with M_EAPOL.
+	 */
+	m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA);
 
-			ieee80211_txampdu_count_packet(tap);
-			if (IEEE80211_AMPDU_RUNNING(tap)) {
-				/*
-				 * Operational, mark frame for aggregation.
-				 *
-				 * XXX do tx aggregation here
-				 */
-				m->m_flags |= M_AMPDU_MPDU;
-			} else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
-			    ic->ic_ampdu_enable(ni, tap)) {
-				/*
-				 * Not negotiated yet, request service.
-				 */
-				ieee80211_ampdu_request(ni, tap);
-				/* XXX hold frame for reply? */
-			}
-		}
-#ifdef IEEE80211_SUPPORT_SUPERG
-		else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) {
-			m = ieee80211_ff_check(ni, m);
-			if (m == NULL) {
-				/* NB: any ni ref held on stageq */
-				continue;
-			}
-		}
-#endif /* IEEE80211_SUPPORT_SUPERG */
-		if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) {
-			/*
-			 * Encapsulate the packet in prep for transmission.
-			 */
-			m = ieee80211_encap(vap, ni, m);
-			if (m == NULL) {
-				/* NB: stat+msg handled in ieee80211_encap */
-				ieee80211_free_node(ni);
-				continue;
-			}
-		}
+	/*
+	 * Bump to the packet transmission path.
+	 * The mbuf will be consumed here.
+	 */
+	return (ieee80211_start_pkt(vap, m));
+}
 
-		error = parent->if_transmit(parent, m);
-		if (error != 0) {
-			/* NB: IFQ_HANDOFF reclaims mbuf */
-			ieee80211_free_node(ni);
-		} else {
-			ifp->if_opackets++;
-		}
-		ic->ic_lastdata = ticks;
-	}
-#undef IS_DWDS
+void
+ieee80211_vap_qflush(struct ifnet *ifp)
+{
+
+	/* Empty for now */
 }
 
 /*
+ * 802.11 raw output routine.
+ */
+int
+ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni,
+    struct mbuf *m, const struct ieee80211_bpf_params *params)
+{
+	struct ieee80211com *ic = vap->iv_ic;
+
+	return (ic->ic_raw_xmit(ni, m, params));
+}
+
+/*
  * 802.11 output routine. This is (currently) used only to
  * connect bpf write calls to the 802.11 layer for injecting
  * raw 802.11 frames.
  */
+#if __FreeBSD_version >= 1000031
 int
 ieee80211_output(struct ifnet *ifp, struct mbuf *m,
+	const struct sockaddr *dst, struct route *ro)
+#else
+int
+ieee80211_output(struct ifnet *ifp, struct mbuf *m,
 	struct sockaddr *dst, struct route *ro)
+#endif
 {
 #define senderr(e) do { error = (e); goto bad;} while (0)
 	struct ieee80211_node *ni = NULL;
 	struct ieee80211vap *vap;
 	struct ieee80211_frame *wh;
+	struct ieee80211com *ic = NULL;
 	int error;
+	int ret;
 
 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
 		/*
@@ -401,6 +530,7 @@
 		senderr(ENETDOWN);
 	}
 	vap = ifp->if_softc;
+	ic = vap->iv_ic;
 	/*
 	 * Hand to the 802.3 code if not tagged as
 	 * a raw 802.11 frame.
@@ -481,6 +611,8 @@
 	/* NB: ieee80211_encap does not include 802.11 header */
 	IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len);
 
+	IEEE80211_TX_LOCK(ic);
+
 	/*
 	 * NB: DLT_IEEE802_11_RADIO identifies the parameters are
 	 * present by setting the sa_len field of the sockaddr (yes,
@@ -487,9 +619,11 @@
 	 * this is a hack).
 	 * NB: we assume sa_data is suitably aligned to cast.
 	 */
-	return vap->iv_ic->ic_raw_xmit(ni, m,
+	ret = ieee80211_raw_output(vap, ni, m,
 	    (const struct ieee80211_bpf_params *)(dst->sa_len ?
 		dst->sa_data : NULL));
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 bad:
 	if (m != NULL)
 		m_freem(m);
@@ -520,6 +654,8 @@
 	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
 	ieee80211_seq seqno;
 
+	IEEE80211_TX_LOCK_ASSERT(ni->ni_ic);
+
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
 	if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
 		switch (vap->iv_opmode) {
@@ -551,7 +687,6 @@
 			break;
 		case IEEE80211_M_MBSS:
 #ifdef IEEE80211_SUPPORT_MESH
-			/* XXX add support for proxied addresses */
 			if (IEEE80211_IS_MULTICAST(da)) {
 				wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
 				/* XXX next hop */
@@ -584,7 +719,7 @@
 	}
 	*(uint16_t *)&wh->i_dur[0] = 0;
 
-	tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)];
+	tap = &ni->ni_tx_ampdu[tid];
 	if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap))
 		m->m_flags |= M_AMPDU_MPDU;
 	else {
@@ -614,6 +749,7 @@
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_frame *wh;
+	int ret;
 
 	KASSERT(ni != NULL, ("null node"));
 
@@ -629,12 +765,14 @@
 		return EIO;		/* XXX */
 	}
 
-	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
 	if (m == NULL) {
 		ieee80211_free_node(ni);
 		return ENOMEM;
 	}
 
+	IEEE80211_TX_LOCK(ic);
+
 	wh = mtod(m, struct ieee80211_frame *);
 	ieee80211_send_setup(ni, m,
 	     IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID,
@@ -642,7 +780,7 @@
 	if (params->ibp_flags & IEEE80211_BPF_CRYPTO) {
 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1,
 		    "encrypting frame (%s)", __func__);
-		wh->i_fc[1] |= IEEE80211_FC1_WEP;
+		wh->i_fc[1] |= IEEE80211_FC1_PROTECTED;
 	}
 	m->m_flags |= M_ENCAP;		/* mark encapsulated */
 
@@ -663,7 +801,9 @@
 #endif
 	IEEE80211_NODE_STAT(ni, tx_mgmt);
 
-	return ic->ic_raw_xmit(ni, m, params);
+	ret = ieee80211_raw_output(vap, ni, m, params);
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 }
 
 /*
@@ -687,6 +827,7 @@
 	struct ieee80211_frame *wh;
 	int hdrlen;
 	uint8_t *frm;
+	int ret;
 
 	if (vap->iv_state == IEEE80211_S_CAC) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
@@ -715,7 +856,7 @@
 	}
 	KASSERT(M_LEADINGSPACE(m) >= hdrlen,
 	    ("leading space %zd", M_LEADINGSPACE(m)));
-	M_PREPEND(m, hdrlen, M_DONTWAIT);
+	M_PREPEND(m, hdrlen, M_NOWAIT);
 	if (m == NULL) {
 		/* NB: cannot happen */
 		ieee80211_free_node(ni);
@@ -722,6 +863,8 @@
 		return ENOMEM;
 	}
 
+	IEEE80211_TX_LOCK(ic);
+
 	wh = mtod(m, struct ieee80211_frame *);		/* NB: a little lie */
 	if (ni->ni_flags & IEEE80211_NODE_QOS) {
 		const int tid = WME_AC_TO_TID(WME_AC_BE);
@@ -764,7 +907,9 @@
 	    ieee80211_chan2ieee(ic, ic->ic_curchan),
 	    wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
 
-	return ic->ic_raw_xmit(ni, m, NULL);
+	ret = ieee80211_raw_output(vap, ni, m, NULL);
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 }
 
 /* 
@@ -932,7 +1077,7 @@
 			return NULL;
 		}
 		KASSERT(needed_space <= MHLEN,
-		    ("not enough room, need %u got %zu\n", needed_space, MHLEN));
+		    ("not enough room, need %u got %d\n", needed_space, MHLEN));
 		/*
 		 * Setup new mbuf to have leading space to prepend the
 		 * 802.11 header and any crypto header bits that are
@@ -1011,10 +1156,13 @@
     struct mbuf *m)
 {
 #define	WH4(wh)	((struct ieee80211_frame_addr4 *)(wh))
+#define MC01(mc)	((struct ieee80211_meshcntl_ae01 *)mc)
 	struct ieee80211com *ic = ni->ni_ic;
 #ifdef IEEE80211_SUPPORT_MESH
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211_meshcntl_ae10 *mc;
+	struct ieee80211_mesh_route *rt = NULL;
+	int dir = -1;
 #endif
 	struct ether_header eh;
 	struct ieee80211_frame *wh;
@@ -1024,6 +1172,8 @@
 	ieee80211_seq seqno;
 	int meshhdrsize, meshae;
 	uint8_t *qos;
+	
+	IEEE80211_TX_LOCK_ASSERT(ic);
 
 	/*
 	 * Copy existing Ethernet header to a safe place.  The
@@ -1069,9 +1219,11 @@
 	 * ap's require all data frames to be QoS-encapsulated
 	 * once negotiated in which case we'll need to make this
 	 * configurable.
+	 * NB: mesh data frames are QoS.
 	 */
-	addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
-		 (m->m_flags & M_EAPOL) == 0;
+	addqos = ((ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) ||
+	    (vap->iv_opmode == IEEE80211_M_MBSS)) &&
+	    (m->m_flags & M_EAPOL) == 0;
 	if (addqos)
 		hdrsize = sizeof(struct ieee80211_qosframe);
 	else
@@ -1093,21 +1245,40 @@
 		 *   w/ 4-address format and address extension mode 10
 		 */
 		is4addr = 0;		/* NB: don't use, disable */
-		if (!IEEE80211_IS_MULTICAST(eh.ether_dhost))
-			hdrsize += IEEE80211_ADDR_LEN;	/* unicast are 4-addr */
-		meshhdrsize = sizeof(struct ieee80211_meshcntl);
-		/* XXX defines for AE modes */
-		if (IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
-			if (!IEEE80211_IS_MULTICAST(eh.ether_dhost))
-				meshae = 0;
-			else
-				meshae = 4;		/* NB: pseudo */
-		} else if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
-			meshae = 1;
-			meshhdrsize += 1*IEEE80211_ADDR_LEN;
+		if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
+			rt = ieee80211_mesh_rt_find(vap, eh.ether_dhost);
+			KASSERT(rt != NULL, ("route is NULL"));
+			dir = IEEE80211_FC1_DIR_DSTODS;
+			hdrsize += IEEE80211_ADDR_LEN;
+			if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+				if (IEEE80211_ADDR_EQ(rt->rt_mesh_gate,
+				    vap->iv_myaddr)) {
+					IEEE80211_NOTE_MAC(vap,
+					    IEEE80211_MSG_MESH,
+					    eh.ether_dhost,
+					    "%s", "trying to send to ourself");
+					goto bad;
+				}
+				meshae = IEEE80211_MESH_AE_10;
+				meshhdrsize =
+				    sizeof(struct ieee80211_meshcntl_ae10);
+			} else {
+				meshae = IEEE80211_MESH_AE_00;
+				meshhdrsize =
+				    sizeof(struct ieee80211_meshcntl);
+			}
 		} else {
-			meshae = 2;
-			meshhdrsize += 2*IEEE80211_ADDR_LEN;
+			dir = IEEE80211_FC1_DIR_FROMDS;
+			if (!IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
+				/* proxy group */
+				meshae = IEEE80211_MESH_AE_01;
+				meshhdrsize =
+				    sizeof(struct ieee80211_meshcntl_ae01);
+			} else {
+				/* group */
+				meshae = IEEE80211_MESH_AE_00;
+				meshhdrsize = sizeof(struct ieee80211_meshcntl);
+			}
 		}
 	} else {
 #endif
@@ -1164,7 +1335,7 @@
 	}
 	datalen = m->m_pkthdr.len;		/* NB: w/o 802.11 header */
 
-	M_PREPEND(m, hdrspace + meshhdrsize, M_DONTWAIT);
+	M_PREPEND(m, hdrspace + meshhdrsize, M_NOWAIT);
 	if (m == NULL) {
 		vap->iv_stats.is_tx_nobuf++;
 		goto bad;
@@ -1208,44 +1379,52 @@
 		/* NB: offset by hdrspace to deal with DATAPAD */
 		mc = (struct ieee80211_meshcntl_ae10 *)
 		     (mtod(m, uint8_t *) + hdrspace);
+		wh->i_fc[1] = dir;
 		switch (meshae) {
-		case 0:			/* ucast, no proxy */
-			wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
-			IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
-			IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
-			IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
-			IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+		case IEEE80211_MESH_AE_00:	/* no proxy */
 			mc->mc_flags = 0;
-			qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
+			if (dir == IEEE80211_FC1_DIR_DSTODS) { /* ucast */
+				IEEE80211_ADDR_COPY(wh->i_addr1,
+				    ni->ni_macaddr);
+				IEEE80211_ADDR_COPY(wh->i_addr2,
+				    vap->iv_myaddr);
+				IEEE80211_ADDR_COPY(wh->i_addr3,
+				    eh.ether_dhost);
+				IEEE80211_ADDR_COPY(WH4(wh)->i_addr4,
+				    eh.ether_shost);
+				qos =((struct ieee80211_qosframe_addr4 *)
+				    wh)->i_qos;
+			} else if (dir == IEEE80211_FC1_DIR_FROMDS) {
+				 /* mcast */
+				IEEE80211_ADDR_COPY(wh->i_addr1,
+				    eh.ether_dhost);
+				IEEE80211_ADDR_COPY(wh->i_addr2,
+				    vap->iv_myaddr);
+				IEEE80211_ADDR_COPY(wh->i_addr3,
+				    eh.ether_shost);
+				qos = ((struct ieee80211_qosframe *)
+				    wh)->i_qos;
+			}
 			break;
-		case 4:			/* mcast, no proxy */
+		case IEEE80211_MESH_AE_01:	/* mcast, proxy */
 			wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
 			IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
 			IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
-			IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
-			mc->mc_flags = 0;		/* NB: AE is really 0 */
-			qos = ((struct ieee80211_qosframe *) wh)->i_qos;
-			break;
-		case 1:			/* mcast, proxy */
-			wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
-			IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
-			IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
 			IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr);
 			mc->mc_flags = 1;
-			IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_shost);
+			IEEE80211_ADDR_COPY(MC01(mc)->mc_addr4,
+			    eh.ether_shost);
 			qos = ((struct ieee80211_qosframe *) wh)->i_qos;
 			break;
-		case 2:			/* ucast, proxy */
-			wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
-			IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+		case IEEE80211_MESH_AE_10:	/* ucast, proxy */
+			KASSERT(rt != NULL, ("route is NULL"));
+			IEEE80211_ADDR_COPY(wh->i_addr1, rt->rt_nexthop);
 			IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
-			/* XXX not right, need MeshDA */
-			IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
-			/* XXX assume are MeshSA */
+			IEEE80211_ADDR_COPY(wh->i_addr3, rt->rt_mesh_gate);
 			IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr);
-			mc->mc_flags = 2;
-			IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_dhost);
-			IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_shost);
+			mc->mc_flags = IEEE80211_MESH_AE_10;
+			IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_dhost);
+			IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost);
 			qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
 			break;
 		default:
@@ -1277,7 +1456,12 @@
 		qos[0] = tid & IEEE80211_QOS_TID;
 		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
 			qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
-		qos[1] = 0;
+#ifdef IEEE80211_SUPPORT_MESH
+		if (vap->iv_opmode == IEEE80211_M_MBSS)
+			qos[1] = IEEE80211_QOS_MC;
+		else
+#endif
+			qos[1] = 0;
 		wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
 
 		if ((m->m_flags & M_AMPDU_MPDU) == 0) {
@@ -1321,7 +1505,7 @@
 		     (vap->iv_opmode == IEEE80211_M_STA ?
 		      !IEEE80211_KEY_UNDEFINED(key) :
 		      !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
-			wh->i_fc[1] |= IEEE80211_FC1_WEP;
+			wh->i_fc[1] |= IEEE80211_FC1_PROTECTED;
 			if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) {
 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT,
 				    eh.ether_dhost,
@@ -1351,6 +1535,7 @@
 		m_freem(m);
 	return NULL;
 #undef WH4
+#undef MC01
 }
 
 /*
@@ -1365,18 +1550,28 @@
 ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
 	u_int hdrsize, u_int ciphdrsize, u_int mtu)
 {
+	struct ieee80211com *ic = vap->iv_ic;
 	struct ieee80211_frame *wh, *whf;
 	struct mbuf *m, *prev, *next;
 	u_int totalhdrsize, fragno, fragsize, off, remainder, payload;
+	u_int hdrspace;
 
 	KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?"));
 	KASSERT(m0->m_pkthdr.len > mtu,
 		("pktlen %u mtu %u", m0->m_pkthdr.len, mtu));
 
+	/*
+	 * Honor driver DATAPAD requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		hdrspace = roundup(hdrsize, sizeof(uint32_t));
+	else
+		hdrspace = hdrsize;
+
 	wh = mtod(m0, struct ieee80211_frame *);
 	/* NB: mark the first frag; it will be propagated below */
 	wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG;
-	totalhdrsize = hdrsize + ciphdrsize;
+	totalhdrsize = hdrspace + ciphdrsize;
 	fragno = 1;
 	off = mtu - ciphdrsize;
 	remainder = m0->m_pkthdr.len - off;
@@ -1389,9 +1584,9 @@
 		KASSERT(fragsize < MCLBYTES,
 			("fragment size %u too big!", fragsize));
 		if (fragsize > MHLEN)
-			m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+			m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
 		else
-			m = m_gethdr(M_DONTWAIT, MT_DATA);
+			m = m_gethdr(M_NOWAIT, MT_DATA);
 		if (m == NULL)
 			goto bad;
 		/* leave room to prepend any cipher header */
@@ -1402,9 +1597,20 @@
 		 * we mark the first fragment with the MORE_FRAG bit
 		 * it automatically is propagated to each fragment; we
 		 * need only clear it on the last fragment (done below).
+		 * NB: frag 1+ dont have Mesh Control field present.
 		 */
 		whf = mtod(m, struct ieee80211_frame *);
 		memcpy(whf, wh, hdrsize);
+#ifdef IEEE80211_SUPPORT_MESH
+		if (vap->iv_opmode == IEEE80211_M_MBSS) {
+			if (IEEE80211_IS_DSTODS(wh))
+				((struct ieee80211_qosframe_addr4 *)
+				    whf)->i_qos[1] &= ~IEEE80211_QOS_MC;
+			else
+				((struct ieee80211_qosframe *)
+				    whf)->i_qos[1] &= ~IEEE80211_QOS_MC;
+		}
+#endif
 		*(uint16_t *)&whf->i_seq[0] |= htole16(
 			(fragno & IEEE80211_SEQ_FRAG_MASK) <<
 				IEEE80211_SEQ_FRAG_SHIFT);
@@ -1412,9 +1618,10 @@
 
 		payload = fragsize - totalhdrsize;
 		/* NB: destination is known to be contiguous */
-		m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize);
-		m->m_len = hdrsize + payload;
-		m->m_pkthdr.len = hdrsize + payload;
+
+		m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrspace);
+		m->m_len = hdrspace + payload;
+		m->m_pkthdr.len = hdrspace + payload;
 		m->m_flags |= M_FRAG;
 
 		/* chain up the fragment */
@@ -1661,6 +1868,33 @@
 }
 
 /*
+ * Add an 11h Quiet time element to a frame.
+ */
+static uint8_t *
+ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap)
+{
+	struct ieee80211_quiet_ie *quiet = (struct ieee80211_quiet_ie *) frm;
+
+	quiet->quiet_ie = IEEE80211_ELEMID_QUIET;
+	quiet->len = 6;
+	if (vap->iv_quiet_count_value == 1)
+		vap->iv_quiet_count_value = vap->iv_quiet_count;
+	else if (vap->iv_quiet_count_value > 1)
+		vap->iv_quiet_count_value--;
+
+	if (vap->iv_quiet_count_value == 0) {
+		/* value 0 is reserved as per 802.11h standerd */
+		vap->iv_quiet_count_value = 1;
+	}
+
+	quiet->tbttcount = vap->iv_quiet_count_value;
+	quiet->period = vap->iv_quiet_period;
+	quiet->duration = htole16(vap->iv_quiet_duration);
+	quiet->offset = htole16(vap->iv_quiet_offset);
+	return frm + sizeof(*quiet);
+}
+
+/*
  * Add an 11h Channel Switch Announcement element to a frame.
  * Note that we use the per-vap CSA count to adjust the global
  * counter so we can use this routine to form probe response
@@ -1704,6 +1938,40 @@
 	return add_appie(frm, ic->ic_countryie);
 }
 
+uint8_t *
+ieee80211_add_wpa(uint8_t *frm, const struct ieee80211vap *vap)
+{
+	if (vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL)
+		return (add_ie(frm, vap->iv_wpa_ie));
+	else {
+		/* XXX else complain? */
+		return (frm);
+	}
+}
+
+uint8_t *
+ieee80211_add_rsn(uint8_t *frm, const struct ieee80211vap *vap)
+{
+	if (vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL)
+		return (add_ie(frm, vap->iv_rsn_ie));
+	else {
+		/* XXX else complain? */
+		return (frm);
+	}
+}
+
+uint8_t *
+ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni)
+{
+	if (ni->ni_flags & IEEE80211_NODE_QOS) {
+		*frm++ = IEEE80211_ELEMID_QOS;
+		*frm++ = 1;
+		*frm++ = 0;
+	}
+
+	return (frm);
+}
+
 /*
  * Send a probe request frame with the specified ssid
  * and any optional information element data.
@@ -1723,6 +1991,7 @@
 	const struct ieee80211_rateset *rs;
 	struct mbuf *m;
 	uint8_t *frm;
+	int ret;
 
 	if (vap->iv_state == IEEE80211_S_CAC) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
@@ -1771,17 +2040,9 @@
 	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
 	rs = ieee80211_get_suprates(ic, ic->ic_curchan);
 	frm = ieee80211_add_rates(frm, rs);
-	if (vap->iv_flags & IEEE80211_F_WPA2) {
-		if (vap->iv_rsn_ie != NULL)
-			frm = add_ie(frm, vap->iv_rsn_ie);
-		/* XXX else complain? */
-	}
+	frm = ieee80211_add_rsn(frm, vap);
 	frm = ieee80211_add_xrates(frm, rs);
-	if (vap->iv_flags & IEEE80211_F_WPA1) {
-		if (vap->iv_wpa_ie != NULL)
-			frm = add_ie(frm, vap->iv_wpa_ie);
-		/* XXX else complain? */
-	}
+	frm = ieee80211_add_wpa(frm, vap);
 	if (vap->iv_appie_probereq != NULL)
 		frm = add_appie(frm, vap->iv_appie_probereq);
 	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
@@ -1788,7 +2049,7 @@
 
 	KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame),
 	    ("leading space %zd", M_LEADINGSPACE(m)));
-	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
 	if (m == NULL) {
 		/* NB: cannot happen */
 		ieee80211_free_node(ni);
@@ -1795,6 +2056,7 @@
 		return ENOMEM;
 	}
 
+	IEEE80211_TX_LOCK(ic);
 	wh = mtod(m, struct ieee80211_frame *);
 	ieee80211_send_setup(ni, m,
 	     IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
@@ -1822,7 +2084,9 @@
 	} else
 		params.ibp_try0 = tp->maxretry;
 	params.ibp_power = ni->ni_txpower;
-	return ic->ic_raw_xmit(ni, m, &params);
+	ret = ieee80211_raw_output(vap, ni, m, &params);
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 }
 
 /*
@@ -2044,11 +2308,7 @@
 
 		frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
 		frm = ieee80211_add_rates(frm, &ni->ni_rates);
-		if (vap->iv_flags & IEEE80211_F_WPA2) {
-			if (vap->iv_rsn_ie != NULL)
-				frm = add_ie(frm, vap->iv_rsn_ie);
-			/* XXX else complain? */
-		}
+		frm = ieee80211_add_rsn(frm, vap);
 		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
 		if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) {
 			frm = ieee80211_add_powercapability(frm,
@@ -2059,11 +2319,7 @@
 		    ni->ni_ies.htcap_ie != NULL &&
 		    ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
 			frm = ieee80211_add_htcap(frm, ni);
-		if (vap->iv_flags & IEEE80211_F_WPA1) {
-			if (vap->iv_wpa_ie != NULL)
-				frm = add_ie(frm, vap->iv_wpa_ie);
-			/* XXX else complain */
-		}
+		frm = ieee80211_add_wpa(frm, vap);
 		if ((ic->ic_flags & IEEE80211_F_WME) &&
 		    ni->ni_ies.wme_ie != NULL)
 			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
@@ -2253,6 +2509,7 @@
 	       + IEEE80211_COUNTRY_MAX_SIZE
 	       + 3
 	       + sizeof(struct ieee80211_csa_ie)
+	       + sizeof(struct ieee80211_quiet_ie)
 	       + 3
 	       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 	       + sizeof(struct ieee80211_ie_wpa)
@@ -2319,14 +2576,17 @@
 		if (ic->ic_flags & IEEE80211_F_CSAPENDING)
 			frm = ieee80211_add_csa(frm, vap);
 	}
+	if (vap->iv_flags & IEEE80211_F_DOTH) {
+		if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+		    (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
+			if (vap->iv_quiet)
+				frm = ieee80211_add_quiet(frm, vap);
+		}
+	}
 	if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
 		frm = ieee80211_add_erp(frm, ic);
 	frm = ieee80211_add_xrates(frm, rs);
-	if (vap->iv_flags & IEEE80211_F_WPA2) {
-		if (vap->iv_rsn_ie != NULL)
-			frm = add_ie(frm, vap->iv_rsn_ie);
-		/* XXX else complain? */
-	}
+	frm = ieee80211_add_rsn(frm, vap);
 	/*
 	 * NB: legacy 11b clients do not get certain ie's.
 	 *     The caller identifies such clients by passing
@@ -2338,11 +2598,7 @@
 		frm = ieee80211_add_htcap(frm, bss);
 		frm = ieee80211_add_htinfo(frm, bss);
 	}
-	if (vap->iv_flags & IEEE80211_F_WPA1) {
-		if (vap->iv_wpa_ie != NULL)
-			frm = add_ie(frm, vap->iv_wpa_ie);
-		/* XXX else complain? */
-	}
+	frm = ieee80211_add_wpa(frm, vap);
 	if (vap->iv_flags & IEEE80211_F_WME)
 		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
 	if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
@@ -2383,6 +2639,7 @@
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ieee80211_frame *wh;
 	struct mbuf *m;
+	int ret;
 
 	if (vap->iv_state == IEEE80211_S_CAC) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss,
@@ -2408,9 +2665,10 @@
 		return ENOMEM;
 	}
 
-	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
 	KASSERT(m != NULL, ("no room for header"));
 
+	IEEE80211_TX_LOCK(ic);
 	wh = mtod(m, struct ieee80211_frame *);
 	ieee80211_send_setup(bss, m,
 	     IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP,
@@ -2426,7 +2684,9 @@
 	    legacy ? " <legacy>" : "");
 	IEEE80211_NODE_STAT(bss, tx_mgmt);
 
-	return ic->ic_raw_xmit(bss, m, NULL);
+	ret = ieee80211_raw_output(vap, bss, m, NULL);
+	IEEE80211_TX_UNLOCK(ic);
+	return (ret);
 }
 
 /*
@@ -2442,7 +2702,7 @@
 	struct mbuf *m;
 
 	/* XXX honor ic_headroom */
-	m = m_gethdr(M_DONTWAIT, MT_DATA);
+	m = m_gethdr(M_NOWAIT, MT_DATA);
 	if (m != NULL) {
 		rts = mtod(m, struct ieee80211_frame_rts *);
 		rts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
@@ -2468,7 +2728,7 @@
 	struct mbuf *m;
 
 	/* XXX honor ic_headroom */
-	m = m_gethdr(M_DONTWAIT, MT_DATA);
+	m = m_gethdr(M_NOWAIT, MT_DATA);
 	if (m != NULL) {
 		cts = mtod(m, struct ieee80211_frame_cts *);
 		cts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
@@ -2485,9 +2745,9 @@
 static void
 ieee80211_tx_mgt_timeout(void *arg)
 {
-	struct ieee80211_node *ni = arg;
-	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211vap *vap = arg;
 
+	IEEE80211_LOCK(vap->iv_ic);
 	if (vap->iv_state != IEEE80211_S_INIT &&
 	    (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) {
 		/*
@@ -2494,11 +2754,26 @@
 		 * NB: it's safe to specify a timeout as the reason here;
 		 *     it'll only be used in the right state.
 		 */
-		ieee80211_new_state(vap, IEEE80211_S_SCAN,
+		ieee80211_new_state_locked(vap, IEEE80211_S_SCAN,
 			IEEE80211_SCAN_FAIL_TIMEOUT);
 	}
+	IEEE80211_UNLOCK(vap->iv_ic);
 }
 
+/*
+ * This is the callback set on net80211-sourced transmitted
+ * authentication request frames.
+ *
+ * This does a couple of things:
+ *
+ * + If the frame transmitted was a success, it schedules a future
+ *   event which will transition the interface to scan.
+ *   If a state transition _then_ occurs before that event occurs,
+ *   said state transition will cancel this callout.
+ *
+ * + If the frame transmit was a failure, it immediately schedules
+ *   the transition back to scan.
+ */
 static void
 ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
 {
@@ -2516,10 +2791,11 @@
 	 *
 	 * XXX what happens if !acked but response shows up before callback?
 	 */
-	if (vap->iv_state == ostate)
+	if (vap->iv_state == ostate) {
 		callout_reset(&vap->iv_mgtsend,
 			status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
-			ieee80211_tx_mgt_timeout, ni);
+			ieee80211_tx_mgt_timeout, vap);
+	}
 }
 
 static void
@@ -2617,29 +2893,32 @@
 			frm = ieee80211_add_powerconstraint(frm, vap);
 		bo->bo_csa = frm;
 		if (ic->ic_flags & IEEE80211_F_CSAPENDING)
-			frm = ieee80211_add_csa(frm, vap);
+			frm = ieee80211_add_csa(frm, vap);	
 	} else
 		bo->bo_csa = frm;
+
+	if (vap->iv_flags & IEEE80211_F_DOTH) {
+		bo->bo_quiet = frm;
+		if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+		    (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
+			if (vap->iv_quiet)
+				frm = ieee80211_add_quiet(frm,vap);
+		}
+	} else
+		bo->bo_quiet = frm;
+
 	if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
 		bo->bo_erp = frm;
 		frm = ieee80211_add_erp(frm, ic);
 	}
 	frm = ieee80211_add_xrates(frm, rs);
-	if (vap->iv_flags & IEEE80211_F_WPA2) {
-		if (vap->iv_rsn_ie != NULL)
-			frm = add_ie(frm, vap->iv_rsn_ie);
-		/* XXX else complain */
-	}
+	frm = ieee80211_add_rsn(frm, vap);
 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
 		frm = ieee80211_add_htcap(frm, ni);
 		bo->bo_htinfo = frm;
 		frm = ieee80211_add_htinfo(frm, ni);
 	}
-	if (vap->iv_flags & IEEE80211_F_WPA1) {
-		if (vap->iv_wpa_ie != NULL)
-			frm = add_ie(frm, vap->iv_wpa_ie);
-		/* XXX else complain */
-	}
+	frm = ieee80211_add_wpa(frm, vap);
 	if (vap->iv_flags & IEEE80211_F_WME) {
 		bo->bo_wme = frm;
 		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
@@ -2733,7 +3012,8 @@
 		 + 2 + 4 + vap->iv_tim_len		/* DTIM/IBSSPARMS */
 		 + IEEE80211_COUNTRY_MAX_SIZE		/* country */
 		 + 2 + 1				/* power control */
-	         + sizeof(struct ieee80211_csa_ie)	/* CSA */
+		 + sizeof(struct ieee80211_csa_ie)	/* CSA */
+		 + sizeof(struct ieee80211_quiet_ie)	/* Quiet */
 		 + 2 + 1				/* ERP */
 	         + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 		 + (vap->iv_caps & IEEE80211_C_WPA ?	/* WPA 1+2 */
@@ -2766,7 +3046,7 @@
 	}
 	ieee80211_beacon_construct(m, frm, bo, ni);
 
-	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
 	KASSERT(m != NULL, ("no space for 802.11 header?"));
 	wh = mtod(m, struct ieee80211_frame *);
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
@@ -2953,6 +3233,7 @@
 				bo->bo_appie += adjust;
 				bo->bo_wme += adjust;
 				bo->bo_csa += adjust;
+				bo->bo_quiet += adjust;
 				bo->bo_tim_len = timlen;
 
 				/* update information element */
@@ -3006,6 +3287,7 @@
 #endif
 				bo->bo_appie += sizeof(*csa);
 				bo->bo_csa_trailer_len += sizeof(*csa);
+				bo->bo_quiet += sizeof(*csa);
 				bo->bo_tim_trailer_len += sizeof(*csa);
 				m->m_len += sizeof(*csa);
 				m->m_pkthdr.len += sizeof(*csa);
@@ -3016,6 +3298,11 @@
 			vap->iv_csa_count++;
 			/* NB: don't clear IEEE80211_BEACON_CSA */
 		}
+		if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+		    (vap->iv_flags_ext & IEEE80211_FEXT_DFS) ){
+			if (vap->iv_quiet)
+				ieee80211_add_quiet(bo->bo_quiet, vap);
+		}
 		if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
 			/*
 			 * ERP element needs updating.
@@ -3058,3 +3345,71 @@
 
 	return len_changed;
 }
+
+/*
+ * Do Ethernet-LLC encapsulation for each payload in a fast frame
+ * tunnel encapsulation.  The frame is assumed to have an Ethernet
+ * header at the front that must be stripped before prepending the
+ * LLC followed by the Ethernet header passed in (with an Ethernet
+ * type that specifies the payload size).
+ */
+struct mbuf *
+ieee80211_ff_encap1(struct ieee80211vap *vap, struct mbuf *m,
+	const struct ether_header *eh)
+{
+	struct llc *llc;
+	uint16_t payload;
+
+	/* XXX optimize by combining m_adj+M_PREPEND */
+	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
+	llc = mtod(m, struct llc *);
+	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+	llc->llc_control = LLC_UI;
+	llc->llc_snap.org_code[0] = 0;
+	llc->llc_snap.org_code[1] = 0;
+	llc->llc_snap.org_code[2] = 0;
+	llc->llc_snap.ether_type = eh->ether_type;
+	payload = m->m_pkthdr.len;		/* NB: w/o Ethernet header */
+
+	M_PREPEND(m, sizeof(struct ether_header), M_NOWAIT);
+	if (m == NULL) {		/* XXX cannot happen */
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
+			"%s: no space for ether_header\n", __func__);
+		vap->iv_stats.is_tx_nobuf++;
+		return NULL;
+	}
+	ETHER_HEADER_COPY(mtod(m, void *), eh);
+	mtod(m, struct ether_header *)->ether_type = htons(payload);
+	return m;
+}
+
+/*
+ * Complete an mbuf transmission.
+ *
+ * For now, this simply processes a completed frame after the
+ * driver has completed it's transmission and/or retransmission.
+ * It assumes the frame is an 802.11 encapsulated frame.
+ *
+ * Later on it will grow to become the exit path for a given frame
+ * from the driver and, depending upon how it's been encapsulated
+ * and already transmitted, it may end up doing A-MPDU retransmission,
+ * power save requeuing, etc.
+ *
+ * In order for the above to work, the driver entry point to this
+ * must not hold any driver locks.  Thus, the driver needs to delay
+ * any actual mbuf completion until it can release said locks.
+ *
+ * This frees the mbuf and if the mbuf has a node reference,
+ * the node reference will be freed.
+ */
+void
+ieee80211_tx_complete(struct ieee80211_node *ni, struct mbuf *m, int status)
+{
+
+	if (ni != NULL) {
+		if (m->m_flags & M_TXCB)
+			ieee80211_process_callback(ni, m, status);
+		ieee80211_free_node(ni);
+	}
+	m_freem(m);
+}

Modified: trunk/sys/net80211/ieee80211_phy.c
===================================================================
--- trunk/sys/net80211/ieee80211_phy.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_phy.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_phy.c 254315 2013-08-14 04:24:25Z rpaulo $");
 
 /*
  * IEEE 802.11 PHY-related support.
@@ -60,8 +61,11 @@
 #define	TURBO	IEEE80211_T_TURBO
 #define	HALF	IEEE80211_T_OFDM_HALF
 #define	QUART	IEEE80211_T_OFDM_QUARTER
+#define	HT	IEEE80211_T_HT
+/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
+#define	N(r)	(IEEE80211_RATE_MCS | r)
 #define	PBCC	(IEEE80211_T_OFDM_QUARTER+1)		/* XXX */
-#define	B(r)	(0x80 | r)
+#define	B(r)	(IEEE80211_RATE_BASIC | r)
 #define	Mb(x)	(x*1000)
 
 static struct ieee80211_rate_table ieee80211_11b_table = {
@@ -176,6 +180,98 @@
     },
 };
 
+static struct ieee80211_rate_table ieee80211_11ng_table = {
+    .rateCount = 36,
+    .info = {
+/*                                   short            ctrl  */
+/*                                Preamble  dot11Rate Rate */
+     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
+     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
+     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
+     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
+     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
+     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
+     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
+     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
+     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
+     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
+    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
+    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 },
+
+    [12] = { .phy = HT,      6500,    0x00,      N(0),   4 },
+    [13] = { .phy = HT,     13000,    0x00,      N(1),   6 },
+    [14] = { .phy = HT,     19500,    0x00,      N(2),   6 },
+    [15] = { .phy = HT,     26000,    0x00,      N(3),   8 },
+    [16] = { .phy = HT,     39000,    0x00,      N(4),   8 },
+    [17] = { .phy = HT,     52000,    0x00,      N(5),   8 },
+    [18] = { .phy = HT,     58500,    0x00,      N(6),   8 },
+    [19] = { .phy = HT,     65000,    0x00,      N(7),   8 },
+
+    [20] = { .phy = HT,     13000,    0x00,      N(8),   4 },
+    [21] = { .phy = HT,     26000,    0x00,      N(9),   6 },
+    [22] = { .phy = HT,     39000,    0x00,     N(10),   6 },
+    [23] = { .phy = HT,     52000,    0x00,     N(11),   8 },
+    [24] = { .phy = HT,     78000,    0x00,     N(12),   8 },
+    [25] = { .phy = HT,    104000,    0x00,     N(13),   8 },
+    [26] = { .phy = HT,    117000,    0x00,     N(14),   8 },
+    [27] = { .phy = HT,    130000,    0x00,     N(15),   8 },
+
+    [28] = { .phy = HT,     19500,    0x00,     N(16),   4 },
+    [29] = { .phy = HT,     39000,    0x00,     N(17),   6 },
+    [30] = { .phy = HT,     58500,    0x00,     N(18),   6 },
+    [31] = { .phy = HT,     78000,    0x00,     N(19),   8 },
+    [32] = { .phy = HT,    117000,    0x00,     N(20),   8 },
+    [33] = { .phy = HT,    156000,    0x00,     N(21),   8 },
+    [34] = { .phy = HT,    175500,    0x00,     N(22),   8 },
+    [35] = { .phy = HT,    195000,    0x00,     N(23),   8 },
+
+    },
+};
+
+static struct ieee80211_rate_table ieee80211_11na_table = {
+    .rateCount = 32,
+    .info = {
+/*                                   short            ctrl  */
+/*                                Preamble  dot11Rate Rate */
+     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
+     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
+     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
+     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
+     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
+     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
+     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
+     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 },
+
+     [8] = { .phy = HT,      6500,    0x00,      N(0),   0 },
+     [9] = { .phy = HT,     13000,    0x00,      N(1),   2 },
+    [10] = { .phy = HT,     19500,    0x00,      N(2),   2 },
+    [11] = { .phy = HT,     26000,    0x00,      N(3),   4 },
+    [12] = { .phy = HT,     39000,    0x00,      N(4),   4 },
+    [13] = { .phy = HT,     52000,    0x00,      N(5),   4 },
+    [14] = { .phy = HT,     58500,    0x00,      N(6),   4 },
+    [15] = { .phy = HT,     65000,    0x00,      N(7),   4 },
+
+    [16] = { .phy = HT,     13000,    0x00,      N(8),   0 },
+    [17] = { .phy = HT,     26000,    0x00,      N(9),   2 },
+    [18] = { .phy = HT,     39000,    0x00,     N(10),   2 },
+    [19] = { .phy = HT,     52000,    0x00,     N(11),   4 },
+    [20] = { .phy = HT,     78000,    0x00,     N(12),   4 },
+    [21] = { .phy = HT,    104000,    0x00,     N(13),   4 },
+    [22] = { .phy = HT,    117000,    0x00,     N(14),   4 },
+    [23] = { .phy = HT,    130000,    0x00,     N(15),   4 },
+
+    [24] = { .phy = HT,     19500,    0x00,     N(16),   0 },
+    [25] = { .phy = HT,     39000,    0x00,     N(17),   2 },
+    [26] = { .phy = HT,     58500,    0x00,     N(18),   2 },
+    [27] = { .phy = HT,     78000,    0x00,     N(19),   4 },
+    [28] = { .phy = HT,    117000,    0x00,     N(20),   4 },
+    [29] = { .phy = HT,    156000,    0x00,     N(21),   4 },
+    [30] = { .phy = HT,    175500,    0x00,     N(22),   4 },
+    [31] = { .phy = HT,    195000,    0x00,     N(23),   4 },
+
+    },
+};
+
 #undef	Mb
 #undef	B
 #undef	OFDM
@@ -184,6 +280,8 @@
 #undef	CCK
 #undef	TURBO
 #undef	XR
+#undef	HT
+#undef	N
 
 /*
  * Setup a rate table's reverse lookup table and fill in
@@ -197,13 +295,12 @@
 static void
 ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
 {
-#define	N(a)	(sizeof(a)/sizeof(a[0]))
 #define	WLAN_CTRL_FRAME_SIZE \
 	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
 
 	int i;
 
-	for (i = 0; i < N(rt->rateCodeToIndex); i++)
+	for (i = 0; i < nitems(rt->rateCodeToIndex); i++)
 		rt->rateCodeToIndex[i] = (uint8_t) -1;
 	for (i = 0; i < rt->rateCount; i++) {
 		uint8_t code = rt->info[i].dot11Rate;
@@ -210,15 +307,23 @@
 		uint8_t cix = rt->info[i].ctlRateIndex;
 		uint8_t ctl_rate = rt->info[cix].dot11Rate;
 
-		rt->rateCodeToIndex[code] = i;
-		if (code & IEEE80211_RATE_BASIC) {
-			/*
-			 * Map w/o basic rate bit too.
-			 */
-			code &= IEEE80211_RATE_VAL;
-			rt->rateCodeToIndex[code] = i;
+		/*
+		 * Map without the basic rate bit.
+		 *
+		 * It's up to the caller to ensure that the basic
+		 * rate bit is stripped here.
+		 *
+		 * For HT, use the MCS rate bit.
+		 */
+		code &= IEEE80211_RATE_VAL;
+		if (rt->info[i].phy == IEEE80211_T_HT) {
+			code |= IEEE80211_RATE_MCS;
 		}
 
+		/* XXX assume the control rate is non-MCS? */
+		ctl_rate &= IEEE80211_RATE_VAL;
+		rt->rateCodeToIndex[code] = i;
+
 		/*
 		 * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
 		 *     depends on whether they are marked as basic rates;
@@ -236,7 +341,6 @@
 	}
 
 #undef WLAN_CTRL_FRAME_SIZE
-#undef N
 }
 
 /* Setup all rate tables */
@@ -243,15 +347,13 @@
 static void
 ieee80211_phy_init(void)
 {
-#define N(arr)	(int)(sizeof(arr) / sizeof(arr[0]))
 	static struct ieee80211_rate_table * const ratetables[] = {
 		&ieee80211_half_table,
 		&ieee80211_quarter_table,
-		&ieee80211_11a_table,
-		&ieee80211_11g_table,
+		&ieee80211_11na_table,
+		&ieee80211_11ng_table,
 		&ieee80211_turbog_table,
 		&ieee80211_turboa_table,
-		&ieee80211_turboa_table,
 		&ieee80211_11a_table,
 		&ieee80211_11g_table,
 		&ieee80211_11b_table
@@ -258,10 +360,9 @@
 	};
 	int i;
 
-	for (i = 0; i < N(ratetables); ++i)
+	for (i = 0; i < nitems(ratetables); ++i)
 		ieee80211_setup_ratetable(ratetables[i]);
 
-#undef N
 }
 SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
 
@@ -276,9 +377,9 @@
 	else if (IEEE80211_IS_CHAN_QUARTER(c))
 		rt = &ieee80211_quarter_table;
 	else if (IEEE80211_IS_CHAN_HTA(c))
-		rt = &ieee80211_11a_table;	/* XXX */
+		rt = &ieee80211_11na_table;
 	else if (IEEE80211_IS_CHAN_HTG(c))
-		rt = &ieee80211_11g_table;	/* XXX */
+		rt = &ieee80211_11ng_table;
 	else if (IEEE80211_IS_CHAN_108G(c))
 		rt = &ieee80211_turbog_table;
 	else if (IEEE80211_IS_CHAN_ST(c))
@@ -463,3 +564,66 @@
 	}
 	return txTime;
 }
+
+static const uint16_t ht20_bps[32] = {
+	26, 52, 78, 104, 156, 208, 234, 260,
+	52, 104, 156, 208, 312, 416, 468, 520,
+	78, 156, 234, 312, 468, 624, 702, 780,
+	104, 208, 312, 416, 624, 832, 936, 1040
+};
+static const uint16_t ht40_bps[32] = {
+	54, 108, 162, 216, 324, 432, 486, 540,
+	108, 216, 324, 432, 648, 864, 972, 1080,
+	162, 324, 486, 648, 972, 1296, 1458, 1620,
+	216, 432, 648, 864, 1296, 1728, 1944, 2160
+};
+
+
+#define	OFDM_PLCP_BITS	22
+#define	HT_L_STF	8
+#define	HT_L_LTF	8
+#define	HT_L_SIG	4
+#define	HT_SIG		8
+#define	HT_STF		4
+#define	HT_LTF(n)	((n) * 4)
+
+#define	HT_RC_2_MCS(_rc)	((_rc) & 0xf)
+#define	HT_RC_2_STREAMS(_rc)	((((_rc) & 0x78) >> 3) + 1)
+#define	IS_HT_RATE(_rc)		( (_rc) & IEEE80211_RATE_MCS)
+
+/*
+ * Calculate the transmit duration of an 11n frame.
+ */
+uint32_t
+ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
+    int streams, int isht40, int isShortGI)
+{
+	uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
+
+	KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
+	KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
+
+	if (isht40)
+		bitsPerSymbol = ht40_bps[rate & 0x1f];
+	else
+		bitsPerSymbol = ht20_bps[rate & 0x1f];
+	numBits = OFDM_PLCP_BITS + (frameLen << 3);
+	numSymbols = howmany(numBits, bitsPerSymbol);
+	if (isShortGI)
+		txTime = ((numSymbols * 18) + 4) / 5;   /* 3.6us */
+	else
+		txTime = numSymbols * 4;                /* 4us */
+	return txTime + HT_L_STF + HT_L_LTF +
+	    HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+}
+
+#undef	IS_HT_RATE
+#undef	HT_RC_2_STREAMS
+#undef	HT_RC_2_MCS
+#undef	HT_LTF
+#undef	HT_STF
+#undef	HT_SIG
+#undef	HT_L_SIG
+#undef	HT_L_LTF
+#undef	HT_L_STF
+#undef	OFDM_PLCP_BITS

Modified: trunk/sys/net80211/ieee80211_phy.h
===================================================================
--- trunk/sys/net80211/ieee80211_phy.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_phy.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_phy.h 252727 2013-07-04 21:16:49Z adrian $
  */
 
 #ifndef _NET80211_IEEE80211_PHY_H_
@@ -60,6 +61,8 @@
 
 struct ieee80211_channel;
 
+#define	IEEE80211_RATE_TABLE_SIZE	128
+
 struct ieee80211_rate_table {
 	int		rateCount;		/* NB: for proper padding */
 	uint8_t		rateCodeToIndex[256];	/* back mapping */
@@ -74,7 +77,7 @@
 						 * rate; used for dur. calcs */
 		uint16_t	lpAckDuration;	/* long preamble ACK dur. */
 		uint16_t	spAckDuration;	/* short preamble ACK dur. */
-	} info[32];
+	} info[IEEE80211_RATE_TABLE_SIZE];
 };
 
 const struct ieee80211_rate_table *ieee80211_get_ratetable(
@@ -83,7 +86,14 @@
 static __inline__ uint8_t
 ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
-	uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+	uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
 	KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
 	return rt->info[cix].dot11Rate;
 }
@@ -91,7 +101,14 @@
 static __inline__ uint8_t
 ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
-	uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+	uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
 	KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
 	return rt->info[cix].dot11Rate;
 }
@@ -99,7 +116,14 @@
 static __inline__ enum ieee80211_phytype
 ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
-	uint8_t rix = rt->rateCodeToIndex[rate];
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+	uint8_t rix = rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL];
 	KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
 	return rt->info[rix].phy;
 }
@@ -107,6 +131,13 @@
 static __inline__ int
 ieee80211_isratevalid(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
 	return rt->rateCodeToIndex[rate] != (uint8_t)-1;
 }
 
@@ -134,6 +165,14 @@
 	}
 }
 
+static __inline__ uint8_t
+ieee80211_legacy_rate_lookup(const struct ieee80211_rate_table *rt,
+    uint8_t rate)
+{
+
+	return (rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]);
+}
+
 /*
  * Compute the time to transmit a frame of length frameLen bytes
  * using the specified 802.11 rate code, phy, and short preamble
@@ -151,5 +190,10 @@
  * Convert 802.11 rate code to PLCP signal.
  */
 uint8_t		ieee80211_rate2plcp(int, enum ieee80211_phytype);
+
+uint32_t	ieee80211_compute_duration_ht(uint32_t frameLen,
+			uint16_t rate, int streams, int isht40,
+			int isShortGI);
+
 #endif	/* _KERNEL */
 #endif	/* !_NET80211_IEEE80211_PHY_H_ */

Modified: trunk/sys/net80211/ieee80211_power.c
===================================================================
--- trunk/sys/net80211/ieee80211_power.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_power.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD: src/sys/net80211/ieee80211_power.c,v 1.5 2013/01/17 23:29:38 laffer1 Exp $");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_power.c 254261 2013-08-12 22:27:53Z adrian $");
 
 /*
  * IEEE 802.11 power save support.
@@ -69,6 +70,8 @@
 		vap->iv_update_ps = ieee80211_update_ps;
 		vap->iv_set_tim = ieee80211_set_tim;
 	}
+	vap->iv_node_ps = ieee80211_node_pwrsave;
+	vap->iv_sta_ps = ieee80211_sta_pwrsave;
 }
 
 void
@@ -411,9 +414,12 @@
 pwrsave_flushq(struct ieee80211_node *ni)
 {
 	struct ieee80211_psq *psq = &ni->ni_psq;
+	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_psq_head *qhead;
 	struct ifnet *parent, *ifp;
+	struct mbuf *parent_q = NULL, *ifp_q = NULL;
+	struct mbuf *m;
 
 	IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
 	    "flush ps queue, %u packets queued", psq->psq_len);
@@ -425,8 +431,7 @@
 		parent = vap->iv_ic->ic_ifp;
 		/* XXX need different driver interface */
 		/* XXX bypasses q max and OACTIVE */
-		IF_PREPEND_LIST(&parent->if_snd, qhead->head, qhead->tail,
-		    qhead->len);
+		parent_q = qhead->head;
 		qhead->head = qhead->tail = NULL;
 		qhead->len = 0;
 	} else
@@ -437,8 +442,7 @@
 		ifp = vap->iv_ifp;
 		/* XXX need different driver interface */
 		/* XXX bypasses q max and OACTIVE */
-		IF_PREPEND_LIST(&ifp->if_snd, qhead->head, qhead->tail,
-		    qhead->len);
+		ifp_q = qhead->head;
 		qhead->head = qhead->tail = NULL;
 		qhead->len = 0;
 	} else
@@ -448,10 +452,36 @@
 
 	/* NB: do this outside the psq lock */
 	/* XXX packets might get reordered if parent is OACTIVE */
-	if (parent != NULL)
-		if_start(parent);
-	if (ifp != NULL)
-		if_start(ifp);
+	/* parent frames, should be encapsulated */
+	if (parent != NULL) {
+		while (parent_q != NULL) {
+			m = parent_q;
+			parent_q = m->m_nextpkt;
+			m->m_nextpkt = NULL;
+			/* must be encapsulated */
+			KASSERT((m->m_flags & M_ENCAP),
+			    ("%s: parentq with non-M_ENCAP frame!\n",
+			    __func__));
+			/*
+			 * For encaped frames, we need to free the node
+			 * reference upon failure.
+			 */
+			if (ieee80211_parent_xmitpkt(ic, m) != 0)
+				ieee80211_free_node(ni);
+		}
+	}
+
+	/* VAP frames, aren't encapsulated */
+	if (ifp != NULL) {
+		while (ifp_q != NULL) {
+			m = ifp_q;
+			ifp_q = m->m_nextpkt;
+			m->m_nextpkt = NULL;
+			KASSERT((!(m->m_flags & M_ENCAP)),
+			    ("%s: vapq with M_ENCAP frame!\n", __func__));
+			(void) ieee80211_vap_xmitpkt(vap, m);
+		}
+	}
 }
 
 /*

Modified: trunk/sys/net80211/ieee80211_power.h
===================================================================
--- trunk/sys/net80211/ieee80211_power.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_power.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_power.h 241138 2012-10-02 17:45:19Z adrian $
  */
 #ifndef _NET80211_IEEE80211_POWER_H_
 #define _NET80211_IEEE80211_POWER_H_
@@ -71,6 +72,11 @@
 struct mbuf *ieee80211_node_psq_dequeue(struct ieee80211_node *ni, int *qlen);
 int	ieee80211_node_psq_drain(struct ieee80211_node *);
 int	ieee80211_node_psq_age(struct ieee80211_node *);
+
+/*
+ * Don't call these directly from the stack; they are vap methods
+ * that should be overridden.
+ */
 int	ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
 void	ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
 void	ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);

Modified: trunk/sys/net80211/ieee80211_proto.c
===================================================================
--- trunk/sys/net80211/ieee80211_proto.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_proto.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_proto.c 314667 2017-03-04 13:03:31Z avg $");
 
 /*
  * IEEE 802.11 protocol support.
@@ -105,9 +106,8 @@
 static void update_mcast(void *, int);
 static void update_promisc(void *, int);
 static void update_channel(void *, int);
+static void update_chw(void *, int);
 static void ieee80211_newstate_cb(void *, int);
-static int ieee80211_new_state_locked(struct ieee80211vap *,
-	enum ieee80211_state, int);
 
 static int
 null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
@@ -144,6 +144,7 @@
 	TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic);
 	TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic);
 	TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic);
+	TASK_INIT(&ic->ic_chw_task, 0, update_chw, ic);
 
 	ic->ic_wme.wme_hipri_switch_hysteresis =
 		AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
@@ -193,8 +194,8 @@
 	vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT;
 	vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT;
 	vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
-	callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
-	callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
+	callout_init_mtx(&vap->iv_swbmiss, IEEE80211_LOCK_OBJ(ic), 0);
+	callout_init(&vap->iv_mgtsend, 1);
 	TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
 	TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
 	/*
@@ -447,7 +448,7 @@
 		printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID,
 			qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : "");
 	}
-	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 		int off;
 
 		off = ieee80211_anyhdrspace(ic, wh);
@@ -660,13 +661,12 @@
 int
 ieee80211_iserp_rateset(const struct ieee80211_rateset *rs)
 {
-#define N(a)	(sizeof(a) / sizeof(a[0]))
 	static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
 	int i, j;
 
-	if (rs->rs_nrates < N(rates))
+	if (rs->rs_nrates < nitems(rates))
 		return 0;
-	for (i = 0; i < N(rates); i++) {
+	for (i = 0; i < nitems(rates); i++) {
 		for (j = 0; j < rs->rs_nrates; j++) {
 			int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
 			if (rates[i] == r)
@@ -679,7 +679,6 @@
 		;
 	}
 	return 1;
-#undef N
 }
 
 /*
@@ -994,6 +993,7 @@
 	struct wmeParams *chanp, *bssp;
 	enum ieee80211_phymode mode;
 	int i;
+	int do_aggrmode = 0;
 
        	/*
 	 * Set up the channel access parameters for the physical
@@ -1034,11 +1034,38 @@
 	 * BE uses agressive params to optimize performance of
 	 * legacy/non-QoS traffic.
 	 */
-        if ((vap->iv_opmode == IEEE80211_M_HOSTAP &&
-	     (wme->wme_flags & WME_F_AGGRMODE) != 0) ||
-	    (vap->iv_opmode == IEEE80211_M_STA &&
-	     (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
-	    (vap->iv_flags & IEEE80211_F_WME) == 0) {
+
+	/* Hostap? Only if aggressive mode is enabled */
+        if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+	     (wme->wme_flags & WME_F_AGGRMODE) != 0)
+		do_aggrmode = 1;
+
+	/*
+	 * Station? Only if we're in a non-QoS BSS.
+	 */
+	else if ((vap->iv_opmode == IEEE80211_M_STA &&
+	     (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0))
+		do_aggrmode = 1;
+
+	/*
+	 * IBSS? Only if we we have WME enabled.
+	 */
+	else if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+	    (vap->iv_flags & IEEE80211_F_WME))
+		do_aggrmode = 1;
+
+	/*
+	 * If WME is disabled on this VAP, default to aggressive mode
+	 * regardless of the configuration.
+	 */
+	if ((vap->iv_flags & IEEE80211_F_WME) == 0)
+		do_aggrmode = 1;
+
+	/* XXX WDS? */
+
+	/* XXX MBSS? */
+	
+	if (do_aggrmode) {
 		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
 		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
 
@@ -1056,7 +1083,14 @@
 		    chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin,
 		    chanp->wmep_logcwmax, chanp->wmep_txopLimit);
 	}
-	
+
+
+	/*
+	 * Change the contention window based on the number of associated
+	 * stations.  If the number of associated stations is 1 and
+	 * aggressive mode is enabled, lower the contention window even
+	 * further.
+	 */
 	if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
 	    ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
 		static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
@@ -1080,8 +1114,15 @@
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
 		    "update %s (chan+bss) logcwmin %u\n",
 		    ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin);
-    	}	
-	if (vap->iv_opmode == IEEE80211_M_HOSTAP) {	/* XXX ibss? */
+	}
+
+	/*
+	 * Arrange for the beacon update.
+	 *
+	 * XXX what about MBSS, WDS?
+	 */
+	if (vap->iv_opmode == IEEE80211_M_HOSTAP
+	    || vap->iv_opmode == IEEE80211_M_IBSS) {
 		/*
 		 * Arrange for a beacon update and bump the parameter
 		 * set number so associated stations load the new values.
@@ -1147,6 +1188,17 @@
 	ieee80211_radiotap_chan_change(ic);
 }
 
+static void
+update_chw(void *arg, int npending)
+{
+	struct ieee80211com *ic = arg;
+
+	/*
+	 * XXX should we defer the channel width _config_ update until now?
+	 */
+	ic->ic_update_chw(ic);
+}
+
 /*
  * Block until the parent is in a known state.  This is
  * used after any operations that dispatch a task (e.g.
@@ -1161,6 +1213,7 @@
 	ieee80211_draintask(ic, &ic->ic_promisc_task);
 	ieee80211_draintask(ic, &ic->ic_chan_task);
 	ieee80211_draintask(ic, &ic->ic_bmiss_task);
+	ieee80211_draintask(ic, &ic->ic_chw_task);
 	taskqueue_unblock(ic->ic_tq);
 }
 
@@ -1403,7 +1456,7 @@
 	struct ieee80211com *ic = arg;
 	struct ieee80211vap *vap;
 
-	/* XXX locking */
+	IEEE80211_LOCK(ic);
 	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
 		/*
 		 * We only pass events through for sta vap's in RUN state;
@@ -1415,6 +1468,7 @@
 		    vap->iv_bmiss != NULL)
 			vap->iv_bmiss(vap);
 	}
+	IEEE80211_UNLOCK(ic);
 }
 
 static void
@@ -1421,12 +1475,14 @@
 beacon_swmiss(void *arg, int npending)
 {
 	struct ieee80211vap *vap = arg;
+	struct ieee80211com *ic = vap->iv_ic;
 
-	if (vap->iv_state != IEEE80211_S_RUN)
-		return;
-
-	/* XXX Call multiple times if npending > zero? */
-	vap->iv_bmiss(vap);
+	IEEE80211_LOCK(ic);
+	if (vap->iv_state == IEEE80211_S_RUN) {
+		/* XXX Call multiple times if npending > zero? */
+		vap->iv_bmiss(vap);
+	}
+	IEEE80211_UNLOCK(ic);
 }
 
 /*
@@ -1440,6 +1496,8 @@
 	struct ieee80211vap *vap = arg;
 	struct ieee80211com *ic = vap->iv_ic;
 
+	IEEE80211_LOCK_ASSERT(ic);
+
 	/* XXX sleep state? */
 	KASSERT(vap->iv_state == IEEE80211_S_RUN,
 	    ("wrong state %d", vap->iv_state));
@@ -1610,6 +1668,7 @@
 		if (vap->iv_state != IEEE80211_S_INIT) {
 			/* NB: iv_newstate may drop the lock */
 			vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+			IEEE80211_LOCK_ASSERT(ic);
 			vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
 		}
 	}
@@ -1644,6 +1703,7 @@
 			vap->iv_newstate(vap,
 			    vap->iv_opmode == IEEE80211_M_STA ?
 			        IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
+			IEEE80211_LOCK_ASSERT(ic);
 		}
 	}
 }
@@ -1673,6 +1733,7 @@
 		    ieee80211_state_name[vap->iv_state],
 		    ieee80211_state_name[IEEE80211_S_INIT], arg);
 		vap->iv_newstate(vap, IEEE80211_S_INIT, arg);
+		IEEE80211_LOCK_ASSERT(ic);
 		vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT;
 	}
 
@@ -1694,6 +1755,7 @@
 	    ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg);
 
 	rc = vap->iv_newstate(vap, nstate, arg);
+	IEEE80211_LOCK_ASSERT(ic);
 	vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT;
 	if (rc != 0) {
 		/* State transition failed */
@@ -1720,8 +1782,11 @@
 		 * (i.e. coming out of power save mode).
 		 */
 		vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-		if_start(vap->iv_ifp);
 
+		/*
+		 * XXX TODO Kick-start a VAP queue - this should be a method!
+		 */
+
 		/* bring up any vaps waiting on us */
 		wakeupwaiting(vap);
 	} else if (nstate == IEEE80211_S_INIT) {
@@ -1733,8 +1798,9 @@
 		 */
 		ieee80211_scan_flush(vap);
 
-		/* XXX NB: cast for altq */
-		ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
+		/*
+		 * XXX TODO: ic/vap queue flush
+		 */
 	}
 done:
 	IEEE80211_UNLOCK(ic);
@@ -1767,7 +1833,7 @@
  * is usually a mistake and indicates lack of proper integration
  * with the net80211 layer.
  */
-static int
+int
 ieee80211_new_state_locked(struct ieee80211vap *vap,
 	enum ieee80211_state nstate, int arg)
 {

Modified: trunk/sys/net80211/ieee80211_proto.h
===================================================================
--- trunk/sys/net80211/ieee80211_proto.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_proto.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_proto.h 259175 2013-12-10 13:42:59Z gavin $
  */
 #ifndef _NET80211_IEEE80211_PROTO_H_
 #define _NET80211_IEEE80211_PROTO_H_
@@ -96,12 +97,22 @@
 		struct ieee80211_bpf_params *);
 int	ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
 		const struct ieee80211_bpf_params *);
+#if __FreeBSD_version >= 1000031
 int	ieee80211_output(struct ifnet *, struct mbuf *,
+               const struct sockaddr *, struct route *ro);
+#else
+int	ieee80211_output(struct ifnet *, struct mbuf *,
                struct sockaddr *, struct route *ro);
+#endif
+int	ieee80211_vap_pkt_send_dest(struct ieee80211vap *, struct mbuf *,
+		struct ieee80211_node *);
+int	ieee80211_raw_output(struct ieee80211vap *, struct ieee80211_node *,
+		struct mbuf *, const struct ieee80211_bpf_params *);
 void	ieee80211_send_setup(struct ieee80211_node *, struct mbuf *, int, int,
         const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN],
         const uint8_t [IEEE80211_ADDR_LEN]);
-void	ieee80211_start(struct ifnet *);
+int	ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m);
+void	ieee80211_vap_qflush(struct ifnet *ifp);
 int	ieee80211_send_nulldata(struct ieee80211_node *);
 int	ieee80211_classify(struct ieee80211_node *, struct mbuf *m);
 struct mbuf *ieee80211_mbuf_adjust(struct ieee80211vap *, int,
@@ -115,6 +126,11 @@
 		const uint8_t da[IEEE80211_ADDR_LEN],
 		const uint8_t bssid[IEEE80211_ADDR_LEN],
 		const uint8_t *ssid, size_t ssidlen);
+struct mbuf *	ieee80211_ff_encap1(struct ieee80211vap *, struct mbuf *,
+		const struct ether_header *);
+void	ieee80211_tx_complete(struct ieee80211_node *,
+		struct mbuf *, int);
+
 /*
  * The formation of ProbeResponse frames requires guidance to
  * deal with legacy clients.  When the client is identified as
@@ -134,6 +150,9 @@
 
 uint8_t *ieee80211_add_rates(uint8_t *, const struct ieee80211_rateset *);
 uint8_t *ieee80211_add_xrates(uint8_t *, const struct ieee80211_rateset *);
+uint8_t *ieee80211_add_wpa(uint8_t *, const struct ieee80211vap *);
+uint8_t *ieee80211_add_rsn(uint8_t *, const struct ieee80211vap *);
+uint8_t *ieee80211_add_qos(uint8_t *, const struct ieee80211_node *);
 uint16_t ieee80211_getcapinfo(struct ieee80211vap *,
 		struct ieee80211_channel *);
 
@@ -215,7 +234,7 @@
 	int	(*iac_attach)(struct ieee80211vap *);
 	void	(*iac_detach)(struct ieee80211vap *);
 	int	(*iac_check)(struct ieee80211vap *,
-			const uint8_t mac[IEEE80211_ADDR_LEN]);
+			const struct ieee80211_frame *wh);
 	int	(*iac_add)(struct ieee80211vap *,
 			const uint8_t mac[IEEE80211_ADDR_LEN]);
 	int	(*iac_remove)(struct ieee80211vap *,
@@ -314,6 +333,8 @@
 void	ieee80211_swbmiss(void *arg);
 void	ieee80211_beacon_miss(struct ieee80211com *);
 int	ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int);
+int	ieee80211_new_state_locked(struct ieee80211vap *, enum ieee80211_state,
+		int);
 void	ieee80211_print_essid(const uint8_t *, int);
 void	ieee80211_dump_pkt(struct ieee80211com *,
 		const uint8_t *, int, int, int);
@@ -344,6 +365,7 @@
 	uint16_t	bo_appie_len;	/* AppIE length in bytes */
 	uint16_t	bo_csa_trailer_len;
 	uint8_t		*bo_csa;	/* start of CSA element */
+	uint8_t		*bo_quiet;	/* start of Quiet element */
 	uint8_t		*bo_meshconf;	/* start of MESHCONF element */
 	uint8_t		*bo_spare[3];
 };

Modified: trunk/sys/net80211/ieee80211_radiotap.c
===================================================================
--- trunk/sys/net80211/ieee80211_radiotap.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_radiotap.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_radiotap.c 237214 2012-06-18 02:08:04Z adrian $");
 
 /*
  * IEEE 802.11 radiotap support.
@@ -47,7 +48,7 @@
 
 #include <net80211/ieee80211_var.h>
 
-static int radiotap_offset(struct ieee80211_radiotap_header *, int);
+static int radiotap_offset(struct ieee80211_radiotap_header *, int, int);
 
 void
 ieee80211_radiotap_attach(struct ieee80211com *ic,
@@ -54,6 +55,17 @@
 	struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap,
 	struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap)
 {
+	ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap,
+	    rh, rlen, 0, rx_radiotap);
+}
+
+void
+ieee80211_radiotap_attachv(struct ieee80211com *ic,
+	struct ieee80211_radiotap_header *th,
+	int tlen, int n_tx_v, uint32_t tx_radiotap,
+	struct ieee80211_radiotap_header *rh,
+	int rlen, int n_rx_v, uint32_t rx_radiotap)
+{
 #define	B(_v)	(1<<(_v))
 	int off;
 
@@ -63,11 +75,11 @@
 	/* calculate offset to channel data */
 	off = -1;
 	if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
-		off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL);
+		off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_CHANNEL);
 	else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
-		off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL);
+		off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_XCHANNEL);
 	if (off == -1) {
-		if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x",
+		if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x\n",
 		    __func__, tx_radiotap);
 		/* NB: we handle this case but data will have no chan spec */
 	} else
@@ -79,11 +91,11 @@
 	/* calculate offset to channel data */
 	off = -1;
 	if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
-		off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL);
+		off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_CHANNEL);
 	else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
-		off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL);
+		off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_XCHANNEL);
 	if (off == -1) {
-		if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x",
+		if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x\n",
 		    __func__, rx_radiotap);
 		/* NB: we handle this case but data will have no chan spec */
 	} else
@@ -260,7 +272,8 @@
  * known -1 is returned.
  */
 static int
-radiotap_offset(struct ieee80211_radiotap_header *rh, int item)
+radiotap_offset(struct ieee80211_radiotap_header *rh,
+    int n_vendor_attributes, int item)
 {
 	static const struct {
 		size_t	align, width;
@@ -325,11 +338,17 @@
 		    .align	= sizeof(uint32_t),
 		    .width	= 2*sizeof(uint32_t),
 		},
+		[IEEE80211_RADIOTAP_MCS] = {
+		    .align	= sizeof(uint8_t),
+		    .width	= 3*sizeof(uint8_t),
+		},
 	};
 	uint32_t present = le32toh(rh->it_present);
 	int off, i;
 
 	off = sizeof(struct ieee80211_radiotap_header);
+	off += n_vendor_attributes * (sizeof(uint32_t));
+
 	for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) {
 		if ((present & (1<<i)) == 0)
 			continue;

Modified: trunk/sys/net80211/ieee80211_radiotap.h
===================================================================
--- trunk/sys/net80211/ieee80211_radiotap.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_radiotap.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,4 +1,5 @@
 /* $MidnightBSD$ */
+/* $FreeBSD: stable/10/sys/net80211/ieee80211_radiotap.h 245156 2013-01-08 06:59:21Z adrian $ */
 /* $NetBSD: ieee80211_radiotap.h,v 1.16 2007/01/06 05:51:15 dyoung Exp $ */
 
 /*-
@@ -54,6 +55,12 @@
 
 #define	IEEE80211_RADIOTAP_HDRLEN	64	/* XXX deprecated */
 
+struct ieee80211_radiotap_vendor_header {
+	uint8_t		vh_oui[3];	/* 3 byte vendor OUI */
+	uint8_t		vh_sub_ns;	/* Sub namespace of this section */
+	uint16_t	vh_skip_len;	/* Length of this vendor section */
+} __packed;
+
 /*
  * The radio capture header precedes the 802.11 header.
  *
@@ -188,8 +195,21 @@
 	IEEE80211_RADIOTAP_ANTENNA = 11,
 	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
 	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
-	/* NB: gap for netbsd definitions */
+	/*
+	 * 14-17 are from Linux, they overlap the netbsd-specific
+	 * fields.
+	 */
+	IEEE80211_RADIOTAP_RX_FLAGS = 14,
+	IEEE80211_RADIOTAP_TX_FLAGS = 15,
+	IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+	IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+
 	IEEE80211_RADIOTAP_XCHANNEL = 18,
+	IEEE80211_RADIOTAP_MCS = 19,
+	IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+
+        IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+	IEEE80211_RADIOTAP_VENDOREXT = 30,
 	IEEE80211_RADIOTAP_EXT = 31,
 };
 

Modified: trunk/sys/net80211/ieee80211_ratectl.c
===================================================================
--- trunk/sys/net80211/ieee80211_ratectl.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ratectl.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2010 Rui Paulo <rpaulo at FreeBSD.org>
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_ratectl.c 214894 2010-11-06 18:17:20Z bschmidt $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>

Modified: trunk/sys/net80211/ieee80211_ratectl.h
===================================================================
--- trunk/sys/net80211/ieee80211_ratectl.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ratectl.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2010 Rui Paulo <rpaulo at FreeBSD.org>
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_ratectl.h 215289 2010-11-14 09:59:52Z bschmidt $
  */
 
 enum ieee80211_ratealgs {

Modified: trunk/sys/net80211/ieee80211_ratectl_none.c
===================================================================
--- trunk/sys/net80211/ieee80211_ratectl_none.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_ratectl_none.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2010 Bernhard Schmidt <bschmidt at FreeBSD.org>
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_ratectl_none.c 215244 2010-11-13 14:59:54Z bschmidt $");
 
 #include "opt_wlan.h"
 

Modified: trunk/sys/net80211/ieee80211_regdomain.c
===================================================================
--- trunk/sys/net80211/ieee80211_regdomain.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_regdomain.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_regdomain.c 233845 2012-04-03 17:48:42Z bschmidt $");
 
 /*
  * IEEE 802.11 regdomain support.
@@ -105,7 +106,12 @@
 	c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
 	c->ic_ieee = ieee;
 	c->ic_flags = flags;
-	c->ic_extieee = 0;
+	if (flags & IEEE80211_CHAN_HT40U)
+		c->ic_extieee = ieee + 4;
+	else if (flags & IEEE80211_CHAN_HT40D)
+		c->ic_extieee = ieee - 4;
+	else
+		c->ic_extieee = 0;
 }
 
 /*
@@ -123,7 +129,8 @@
 	/* XXX just do something for now */
 	ic->ic_nchans = 0;
 	if (isset(bands, IEEE80211_MODE_11B) ||
-	    isset(bands, IEEE80211_MODE_11G)) {
+	    isset(bands, IEEE80211_MODE_11G) ||
+	    isset(bands, IEEE80211_MODE_11NG)) {
 		int maxchan = 11;
 		if (rd != NULL && rd->ecm)
 			maxchan = 14;
@@ -132,15 +139,67 @@
 				addchan(ic, i, IEEE80211_CHAN_B);
 			if (isset(bands, IEEE80211_MODE_11G))
 				addchan(ic, i, IEEE80211_CHAN_G);
+			if (isset(bands, IEEE80211_MODE_11NG)) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
+			}
+			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+				continue;
+			if (i <= 7) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
+				addchan(ic, i + 4,
+				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
+			}
 		}
 	}
-	if (isset(bands, IEEE80211_MODE_11A)) {
-		for (i = 36; i <= 64; i += 4)
+	if (isset(bands, IEEE80211_MODE_11A) ||
+	    isset(bands, IEEE80211_MODE_11NA)) {
+		for (i = 36; i <= 64; i += 4) {
 			addchan(ic, i, IEEE80211_CHAN_A);
-		for (i = 100; i <= 140; i += 4)
+			if (isset(bands, IEEE80211_MODE_11NA)) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
+			}
+			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+				continue;
+			if ((i % 8) == 4) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
+				addchan(ic, i + 4,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
+			}
+		}
+		for (i = 100; i <= 140; i += 4) {
 			addchan(ic, i, IEEE80211_CHAN_A);
-		for (i = 149; i <= 161; i += 4)
+			if (isset(bands, IEEE80211_MODE_11NA)) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
+			}
+			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+				continue;
+			if ((i % 8) == 4 && i != 140) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
+				addchan(ic, i + 4,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
+			}
+		}
+		for (i = 149; i <= 161; i += 4) {
 			addchan(ic, i, IEEE80211_CHAN_A);
+			if (isset(bands, IEEE80211_MODE_11NA)) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
+			}
+			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+				continue;
+			if ((i % 8) == 5) {
+				addchan(ic, i,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
+				addchan(ic, i + 4,
+				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
+			}
+		}
 	}
 	if (rd != NULL)
 		ic->ic_regdomain = *rd;

Modified: trunk/sys/net80211/ieee80211_regdomain.h
===================================================================
--- trunk/sys/net80211/ieee80211_regdomain.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_regdomain.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_regdomain.h 243974 2012-12-07 06:34:46Z adrian $
  */
 #ifndef _NET80211_IEEE80211_REGDOMAIN_H_
 #define _NET80211_IEEE80211_REGDOMAIN_H_
@@ -258,6 +259,17 @@
 	SKU_SR9			= 0x0298, /* Ubiquiti SR9 (900MHz/GSM) */
 	SKU_XR9			= 0x0299, /* Ubiquiti XR9 (900MHz/GSM) */
 	SKU_GZ901		= 0x029a, /* Zcomax GZ-901 (900MHz/GSM) */
+	SKU_XC900M		= 0x029b, /* Xagyl XC900M (900MHz/GSM) */
+					  /*
+					   * The XC900M by default uses the
+					   * same mapping as the XR9.  It
+					   * can optionally use a slightly
+					   * offset channel spacing (905MHz-
+					   * 925MHz) versus the XR9 (907MHz-
+					   * 922MHz), giving an extra channel.
+					   * This requires a jumper on the
+					   * NIC to be changed.
+					   */
 };
 
 #if defined(__KERNEL__) || defined(_KERNEL)

Modified: trunk/sys/net80211/ieee80211_rssadapt.c
===================================================================
--- trunk/sys/net80211/ieee80211_rssadapt.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_rssadapt.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,4 +1,5 @@
-/*	$MidnightBSD$	*/
+/* $MidnightBSD$ */
+/*	$FreeBSD: stable/10/sys/net80211/ieee80211_rssadapt.c 321725 2017-07-30 18:38:05Z avos $	*/
 /* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
 /*-
  * Copyright (c) 2010 Rui Paulo <rpaulo at FreeBSD.org>
@@ -127,7 +128,8 @@
 
 	KASSERT(vap->iv_rs == NULL, ("%s: iv_rs already initialized",
 	    __func__));
-	
+
+	nrefs++;		/* XXX locking */
 	vap->iv_rs = rs = malloc(sizeof(struct ieee80211_rssadapt),
 	    M_80211_RATECTL, M_NOWAIT|M_ZERO);
 	if (rs == NULL) {
@@ -143,6 +145,8 @@
 rssadapt_deinit(struct ieee80211vap *vap)
 {
 	free(vap->iv_rs, M_80211_RATECTL);
+	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+	nrefs--;		/* XXX locking */
 }
 
 static void

Modified: trunk/sys/net80211/ieee80211_rssadapt.h
===================================================================
--- trunk/sys/net80211/ieee80211_rssadapt.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_rssadapt.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,4 +1,5 @@
-/*	$MidnightBSD$	*/
+/* $MidnightBSD$ */
+/*	$FreeBSD: stable/10/sys/net80211/ieee80211_rssadapt.h 206358 2010-04-07 15:29:13Z rpaulo $	*/
 /* $NetBSD: ieee80211_rssadapt.h,v 1.4 2005/02/26 22:45:09 perry Exp $ */
 /*-
  * Copyright (c) 2003, 2004 David Young.  All rights reserved.

Modified: trunk/sys/net80211/ieee80211_scan.c
===================================================================
--- trunk/sys/net80211/ieee80211_scan.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_scan.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_scan.c 259173 2013-12-10 13:36:56Z gavin $");
 
 /*
  * IEEE 802.11 scanning support.
@@ -859,6 +860,7 @@
 	if (ss->ss_next == ss->ss_last) {
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
 			"%s: no channels to scan\n", __func__);
+		scandone = 1;
 		goto done;
 	}
 
@@ -866,7 +868,7 @@
 	    vap->iv_state == IEEE80211_S_RUN) {
 		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
 			/* Enable station power save mode */
-			ieee80211_sta_pwrsave(vap, 1);
+			vap->iv_sta_ps(vap, 1);
 			/*
 			 * Use an 1ms delay so the null data frame has a chance
 			 * to go out.
@@ -960,6 +962,19 @@
 	IEEE80211_LOCK(ic);
 
 	/*
+	 * Since a cancellation may have occured during one of the
+	 * driver calls (whilst unlocked), update scandone.
+	 */
+	if (scandone == 0 &&
+	    ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) {
+		/* XXX printf? */
+		if_printf(vap->iv_ifp,
+		    "%s: OOPS! scan cancelled during driver call!\n",
+		    __func__);
+	}
+	scandone |= ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0);
+
+	/*
 	 * Record scan complete time.  Note that we also do
 	 * this when canceled so any background scan will
 	 * not be restarted for a while.
@@ -1034,7 +1049,7 @@
 	 * waiting for us.
 	 */
 	if (scandone) {
-		ieee80211_sta_pwrsave(vap, 0);
+		vap->iv_sta_ps(vap, 0);
 		if (ss->ss_next >= ss->ss_last) {
 			ieee80211_notify_scan_done(vap);
 			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;

Modified: trunk/sys/net80211/ieee80211_scan.h
===================================================================
--- trunk/sys/net80211/ieee80211_scan.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_scan.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2005-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_scan.h 227331 2011-11-08 04:00:24Z adrian $
  */
 #ifndef _NET80211_IEEE80211_SCAN_H_
 #define _NET80211_IEEE80211_SCAN_H_
@@ -213,6 +214,7 @@
 	uint8_t		*ath;
 	uint8_t		*tdma;
 	uint8_t		*csa;
+	uint8_t		*quiet;
 	uint8_t		*meshid;
 	uint8_t		*meshconf;
 	uint8_t		*spare[3];

Modified: trunk/sys/net80211/ieee80211_scan_sta.c
===================================================================
--- trunk/sys/net80211/ieee80211_scan_sta.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_scan_sta.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_scan_sta.c 254315 2013-08-14 04:24:25Z rpaulo $");
 
 /*
  * IEEE 802.11 station scanning support.
@@ -51,6 +52,7 @@
 #ifdef IEEE80211_SUPPORT_MESH
 #include <net80211/ieee80211_mesh.h>
 #endif
+#include <net80211/ieee80211_ratectl.h>
 
 #include <net/bpf.h>
 
@@ -449,13 +451,12 @@
 	struct ieee80211_scan_state *ss,
 	enum ieee80211_phymode mode, const uint16_t freq[], int nfreq)
 {
-#define	N(a)	(sizeof(a) / sizeof(a[0]))
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ieee80211_channel *c, *cg;
 	u_int modeflags;
 	int i;
 
-	KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
+	KASSERT(mode < nitems(chanflags), ("Unexpected mode %u", mode));
 	modeflags = chanflags[mode];
 	for (i = 0; i < nfreq; i++) {
 		if (ss->ss_last >= IEEE80211_SCAN_MAX)
@@ -475,7 +476,6 @@
 		}
 		ss->ss_chans[ss->ss_last++] = c;
 	}
-#undef N
 }
 
 struct scanlist {
@@ -1567,6 +1567,7 @@
 	struct sta_table *st = ss->ss_priv;
 	struct sta_entry *selbs;
 	struct ieee80211_channel *chan;
+	struct ieee80211com *ic = vap->iv_ic;
 
 	KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
 		vap->iv_opmode == IEEE80211_M_AHDEMO ||
@@ -1612,15 +1613,19 @@
 			 */
 			if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
 			    IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
-				struct ieee80211com *ic = vap->iv_ic;
-
 				chan = adhoc_pick_channel(ss, 0);
-				if (chan != NULL)
-					chan = ieee80211_ht_adjust_channel(ic,
-					    chan, vap->iv_flags_ht);
 			} else
 				chan = vap->iv_des_chan;
 			if (chan != NULL) {
+				struct ieee80211com *ic = vap->iv_ic;
+				/*
+				 * Create a HT capable IBSS; the per-node
+				 * probe request/response will result in
+				 * "correct" rate control capabilities being
+				 * negotiated.
+				 */
+				chan = ieee80211_ht_adjust_channel(ic,
+				    chan, vap->iv_flags_ht);
 				ieee80211_create_ibss(vap, chan);
 				return 1;
 			}
@@ -1644,6 +1649,14 @@
 	chan = selbs->base.se_chan;
 	if (selbs->se_flags & STA_DEMOTE11B)
 		chan = demote11b(vap, chan);
+	/*
+	 * If HT is available, make it a possibility here.
+	 * The intent is to enable HT20/HT40 when joining a non-HT
+	 * IBSS node; we can then advertise HT IEs and speak HT
+	 * to any subsequent nodes that support it.
+	 */
+	chan = ieee80211_ht_adjust_channel(ic,
+	    chan, vap->iv_flags_ht);
 	if (!ieee80211_sta_join(vap, chan, &selbs->base))
 		goto notfound;
 	return 1;				/* terminate scan */

Modified: trunk/sys/net80211/ieee80211_sta.c
===================================================================
--- trunk/sys/net80211/ieee80211_sta.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_sta.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_sta.c 262007 2014-02-17 01:36:53Z kevlo $");
 #endif
 
 /*
@@ -50,6 +51,8 @@
 #include <net/if.h>
 #include <net/if_media.h>
 #include <net/if_llc.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
 #include <net/ethernet.h>
 
 #include <net/bpf.h>
@@ -61,6 +64,7 @@
 #include <net80211/ieee80211_superg.h>
 #endif
 #include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_sta.h>
 
 #define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
 
@@ -109,6 +113,8 @@
 {
 	struct ieee80211com *ic = vap->iv_ic;
 
+	IEEE80211_LOCK_ASSERT(ic);
+
 	KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
 	KASSERT(vap->iv_state >= IEEE80211_S_RUN,
 	    ("wrong state %s", ieee80211_state_name[vap->iv_state]));
@@ -398,7 +404,7 @@
 			    arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
 			break;
 		case IEEE80211_S_SLEEP:
-			ieee80211_sta_pwrsave(vap, 0);
+			vap->iv_sta_ps(vap, 0);
 			break;
 		default:
 			goto invalid;
@@ -434,7 +440,7 @@
 			goto invalid;
 		break;
 	case IEEE80211_S_SLEEP:
-		ieee80211_sta_pwrsave(vap, 1);
+		vap->iv_sta_ps(vap, 1);
 		break;
 	default:
 	invalid:
@@ -582,6 +588,30 @@
 			vap->iv_stats.is_rx_wrongbss++;
 			goto out;
 		}
+
+		/*
+		 * Some devices may be in a promiscuous mode
+		 * where they receive frames for multiple station
+		 * addresses.
+		 *
+		 * If we receive a data frame that isn't
+		 * destined to our VAP MAC, drop it.
+		 *
+		 * XXX TODO: This is only enforced when not scanning;
+		 * XXX it assumes a software-driven scan will put the NIC
+		 * XXX into a "no data frames" mode before setting this
+		 * XXX flag. Otherwise it may be possible that we'll still
+		 * XXX process data frames whilst scanning.
+		 */
+		if ((! IEEE80211_IS_MULTICAST(wh->i_addr1))
+		    && (! IEEE80211_ADDR_EQ(wh->i_addr1, IF_LLADDR(ifp)))) {
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+			    bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D",
+			    IF_LLADDR(ifp), ":", wh->i_addr1, ":");
+			vap->iv_stats.is_rx_wrongbss++;
+			goto out;
+		}
+
 		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
 		ni->ni_noise = nf;
 		if (HAS_SEQ(type) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
@@ -699,7 +729,7 @@
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -717,7 +747,7 @@
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
-			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -852,7 +882,7 @@
 			    ether_sprintf(wh->i_addr2), rssi);
 		}
 #endif
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
 				/*
 				 * Only shared key auth frames with a challenge
@@ -881,7 +911,7 @@
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
-			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		}
 		vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
 		goto out;
@@ -1058,7 +1088,7 @@
 		    IEEE80211_SCAN_FAIL_STATUS);
 }
 
-static int
+int
 ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
 	const struct ieee80211_frame *wh)
 {
@@ -1257,6 +1287,7 @@
 	uint8_t *frm, *efrm;
 	uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
 	uint8_t rate;
+	int ht_state_change = 0;
 
 	wh = mtod(m0, struct ieee80211_frame *);
 	frm = (uint8_t *)&wh[1];
@@ -1277,8 +1308,11 @@
 			return;
 		}
 		/* XXX probe response in sta mode when !scanning? */
-		if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+		if (ieee80211_parse_beacon(ni, m0, &scan) != 0) {
+			if (! (ic->ic_flags & IEEE80211_F_SCAN))
+				vap->iv_stats.is_beacon_bad++;
 			return;
+		}
 		/*
 		 * Count frame now that we know it's to be processed.
 		 */
@@ -1341,10 +1375,13 @@
 #endif
 			if (scan.htcap != NULL && scan.htinfo != NULL &&
 			    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
-				ieee80211_ht_updateparams(ni,
-				    scan.htcap, scan.htinfo);
 				/* XXX state changes? */
+				if (ieee80211_ht_updateparams(ni,
+				    scan.htcap, scan.htinfo))
+					ht_state_change = 1;
 			}
+			if (scan.quiet)
+				ic->ic_set_quiet(ni, scan.quiet);
 			if (scan.tim != NULL) {
 				struct ieee80211_tim_ie *tim =
 				    (struct ieee80211_tim_ie *) scan.tim;
@@ -1361,7 +1398,7 @@
 					 * we are expecting data.
 					 */
 					ic->ic_lastdata = ticks;
-					ieee80211_sta_pwrsave(vap, 0);
+					vap->iv_sta_ps(vap, 0);
 				}
 #endif
 				ni->ni_dtim_count = tim->tim_count;
@@ -1408,6 +1445,13 @@
 #endif
 				ieee80211_bg_scan(vap, 0);
 			}
+
+			/*
+			 * If we've had a channel width change (eg HT20<->HT40)
+			 * then schedule a delayed driver notification.
+			 */
+			if (ht_state_change)
+				ieee80211_update_chw(ic);
 			return;
 		}
 		/*

Modified: trunk/sys/net80211/ieee80211_sta.h
===================================================================
--- trunk/sys/net80211/ieee80211_sta.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_sta.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_sta.h 244060 2012-12-09 22:56:29Z adrian $
  */
 #ifndef _NET80211_IEEE80211_STA_H_
 #define _NET80211_IEEE80211_STA_H_
@@ -33,4 +34,10 @@
 void	ieee80211_sta_attach(struct ieee80211com *);
 void	ieee80211_sta_detach(struct ieee80211com *);
 void	ieee80211_sta_vattach(struct ieee80211vap *);
+
+/*
+ * Used by the adhoc/mesh/tdma paths.
+ */
+extern	int ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
+	    const struct ieee80211_frame *wh);
 #endif /* !_NET80211_IEEE80211_STA_H_ */

Modified: trunk/sys/net80211/ieee80211_superg.c
===================================================================
--- trunk/sys/net80211/ieee80211_superg.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_superg.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -24,10 +25,12 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_superg.c 273736 2014-10-27 14:38:00Z hselasky $");
 
 #include "opt_wlan.h"
 
+#ifdef	IEEE80211_SUPPORT_SUPERG
+
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/mbuf.h>   
@@ -84,7 +87,7 @@
 	memcpy(dst, src, sizeof(struct ether_header))
 
 static	int ieee80211_ffppsmin = 2;	/* pps threshold for ff aggregation */
-SYSCTL_INT(_net_wlan, OID_AUTO, ffppsmin, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_INT(_net_wlan, OID_AUTO, ffppsmin, CTLFLAG_RW,
 	&ieee80211_ffppsmin, 0, "min packet rate before fast-frame staging");
 static	int ieee80211_ffagemax = -1;	/* max time frames held on stage q */
 SYSCTL_PROC(_net_wlan, OID_AUTO, ffagemax, CTLTYPE_INT | CTLFLAG_RW,
@@ -328,43 +331,6 @@
 }
 
 /*
- * Do Ethernet-LLC encapsulation for each payload in a fast frame
- * tunnel encapsulation.  The frame is assumed to have an Ethernet
- * header at the front that must be stripped before prepending the
- * LLC followed by the Ethernet header passed in (with an Ethernet
- * type that specifies the payload size).
- */
-static struct mbuf *
-ff_encap1(struct ieee80211vap *vap, struct mbuf *m,
-	const struct ether_header *eh)
-{
-	struct llc *llc;
-	uint16_t payload;
-
-	/* XXX optimize by combining m_adj+M_PREPEND */
-	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
-	llc = mtod(m, struct llc *);
-	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
-	llc->llc_control = LLC_UI;
-	llc->llc_snap.org_code[0] = 0;
-	llc->llc_snap.org_code[1] = 0;
-	llc->llc_snap.org_code[2] = 0;
-	llc->llc_snap.ether_type = eh->ether_type;
-	payload = m->m_pkthdr.len;		/* NB: w/o Ethernet header */
-
-	M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
-	if (m == NULL) {		/* XXX cannot happen */
-		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
-			"%s: no space for ether_header\n", __func__);
-		vap->iv_stats.is_tx_nobuf++;
-		return NULL;
-	}
-	ETHER_HEADER_COPY(mtod(m, void *), eh);
-	mtod(m, struct ether_header *)->ether_type = htons(payload);
-	return m;
-}
-
-/*
  * Fast frame encapsulation.  There must be two packets
  * chained with m_nextpkt.  We do header adjustment for
  * each, add the tunnel encapsulation, and then concatenate
@@ -422,10 +388,10 @@
 	 * Now do tunnel encapsulation.  First, each
 	 * frame gets a standard encapsulation.
 	 */
-	m1 = ff_encap1(vap, m1, &eh1);
+	m1 = ieee80211_ff_encap1(vap, m1, &eh1);
 	if (m1 == NULL)
 		goto bad;
-	m2 = ff_encap1(vap, m2, &eh2);
+	m2 = ieee80211_ff_encap1(vap, m2, &eh2);
 	if (m2 == NULL)
 		goto bad;
 
@@ -460,7 +426,7 @@
 	 */
 	m->m_next = m2;			/* NB: last mbuf from above */
 	m1->m_pkthdr.len += m2->m_pkthdr.len;
-	M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
+	M_PREPEND(m1, sizeof(uint32_t)+2, M_NOWAIT);
 	if (m1 == NULL) {		/* XXX cannot happen */
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
 		    "%s: no space for tunnel header\n", __func__);
@@ -469,7 +435,7 @@
 	}
 	memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
 
-	M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
+	M_PREPEND(m1, sizeof(struct llc), M_NOWAIT);
 	if (m1 == NULL) {		/* XXX cannot happen */
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
 		    "%s: no space for llc header\n", __func__);
@@ -499,15 +465,17 @@
 ff_transmit(struct ieee80211_node *ni, struct mbuf *m)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = ni->ni_ic;
 	int error;
 
+	IEEE80211_TX_LOCK_ASSERT(vap->iv_ic);
+
 	/* encap and xmit */
 	m = ieee80211_encap(vap, ni, m);
 	if (m != NULL) {
 		struct ifnet *ifp = vap->iv_ifp;
-		struct ifnet *parent = ni->ni_ic->ic_ifp;
 
-		error = parent->if_transmit(parent, m);
+		error = ieee80211_parent_xmitpkt(ic, m);;
 		if (error != 0) {
 			/* NB: IFQ_HANDOFF reclaims mbuf */
 			ieee80211_free_node(ni);
@@ -547,30 +515,40 @@
 
 /*
  * Age frames on the staging queue.
+ *
+ * This is called without the comlock held, but it does all its work
+ * behind the comlock.  Because of this, it's possible that the
+ * staging queue will be serviced between the function which called
+ * it and now; thus simply checking that the queue has work in it
+ * may fail.
+ *
+ * See PR kern/174283 for more details.
  */
 void
 ieee80211_ff_age(struct ieee80211com *ic, struct ieee80211_stageq *sq,
     int quanta)
 {
-	struct ieee80211_superg *sg = ic->ic_superg;
 	struct mbuf *m, *head;
 	struct ieee80211_node *ni;
 	struct ieee80211_tx_ampdu *tap;
 
+#if 0
 	KASSERT(sq->head != NULL, ("stageq empty"));
+#endif
 
 	IEEE80211_LOCK(ic);
 	head = sq->head;
 	while ((m = sq->head) != NULL && M_AGE_GET(m) < quanta) {
+		int tid = WME_AC_TO_TID(M_WME_GETAC(m));
+
 		/* clear tap ref to frame */
 		ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
-		tap = &ni->ni_tx_ampdu[M_WME_GETAC(m)];
+		tap = &ni->ni_tx_ampdu[tid];
 		KASSERT(tap->txa_private == m, ("staging queue empty"));
 		tap->txa_private = NULL;
 
 		sq->head = m->m_nextpkt;
 		sq->depth--;
-		sg->ff_stageqdepth--;
 	}
 	if (m == NULL)
 		sq->tail = NULL;
@@ -578,13 +556,18 @@
 		M_AGE_SUB(m, quanta);
 	IEEE80211_UNLOCK(ic);
 
+	IEEE80211_TX_LOCK(ic);
 	ff_flush(head, m);
+	IEEE80211_TX_UNLOCK(ic);
 }
 
 static void
-stageq_add(struct ieee80211_stageq *sq, struct mbuf *m)
+stageq_add(struct ieee80211com *ic, struct ieee80211_stageq *sq, struct mbuf *m)
 {
 	int age = ieee80211_ffagemax;
+
+	IEEE80211_LOCK_ASSERT(ic);
+
 	if (sq->tail != NULL) {
 		sq->tail->m_nextpkt = m;
 		age -= M_AGE_GET(sq->head);
@@ -598,10 +581,12 @@
 }
 
 static void
-stageq_remove(struct ieee80211_stageq *sq, struct mbuf *mstaged)
+stageq_remove(struct ieee80211com *ic, struct ieee80211_stageq *sq, struct mbuf *mstaged)
 {
 	struct mbuf *m, *mprev;
 
+	IEEE80211_LOCK_ASSERT(ic);
+
 	mprev = NULL;
 	for (m = sq->head; m != NULL; m = m->m_nextpkt) {
 		if (m == mstaged) {
@@ -662,6 +647,8 @@
 	struct mbuf *mstaged;
 	uint32_t txtime, limit;
 
+	IEEE80211_TX_UNLOCK_ASSERT(ic);
+
 	/*
 	 * Check if the supplied frame can be aggregated.
 	 *
@@ -670,7 +657,7 @@
 	 *     be aggregated with other types of frames when encryption is on?
 	 */
 	IEEE80211_LOCK(ic);
-	tap = &ni->ni_tx_ampdu[pri];
+	tap = &ni->ni_tx_ampdu[WME_AC_TO_TID(pri)];
 	mstaged = tap->txa_private;		/* NB: we reuse AMPDU state */
 	ieee80211_txampdu_count_packet(tap);
 
@@ -713,14 +700,16 @@
 
 		tap->txa_private = NULL;
 		if (mstaged != NULL)
-			stageq_remove(sq, mstaged);
+			stageq_remove(ic, sq, mstaged);
 		IEEE80211_UNLOCK(ic);
 
 		if (mstaged != NULL) {
+			IEEE80211_TX_LOCK(ic);
 			IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
 			    "%s: flush staged frame", __func__);
 			/* encap and xmit */
 			ff_transmit(ni, mstaged);
+			IEEE80211_TX_UNLOCK(ic);
 		}
 		return m;		/* NB: original frame */
 	}
@@ -733,7 +722,7 @@
 	 */
 	if (mstaged != NULL) {
 		tap->txa_private = NULL;
-		stageq_remove(sq, mstaged);
+		stageq_remove(ic, sq, mstaged);
 		IEEE80211_UNLOCK(ic);
 
 		IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
@@ -754,8 +743,7 @@
 		    ("txa_private %p", tap->txa_private));
 		tap->txa_private = m;
 
-		stageq_add(sq, m);
-		sg->ff_stageqdepth++;
+		stageq_add(ic, sq, m);
 		IEEE80211_UNLOCK(ic);
 
 		IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
@@ -782,17 +770,19 @@
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_superg *sg = ic->ic_superg;
 	struct ieee80211_tx_ampdu *tap;
-	struct mbuf *m, *head;
-	int ac;
+	struct mbuf *m, *next_m, *head;
+	int tid;
 
 	IEEE80211_LOCK(ic);
 	head = NULL;
-	for (ac = 0; ac < WME_NUM_AC; ac++) {
-		tap = &ni->ni_tx_ampdu[ac];
+	for (tid = 0; tid < WME_NUM_TID; tid++) {
+		int ac = TID_TO_WME_AC(tid);
+
+		tap = &ni->ni_tx_ampdu[tid];
 		m = tap->txa_private;
 		if (m != NULL) {
 			tap->txa_private = NULL;
-			stageq_remove(&sg->ff_stageq[ac], m);
+			stageq_remove(ic, &sg->ff_stageq[ac], m);
 			m->m_nextpkt = head;
 			head = m;
 		}
@@ -799,9 +789,16 @@
 	}
 	IEEE80211_UNLOCK(ic);
 
-	for (m = head; m != NULL; m = m->m_nextpkt) {
+	/*
+	 * Free mbufs, taking care to not dereference the mbuf after
+	 * we free it (hence grabbing m_nextpkt before we free it.)
+	 */
+	m = head;
+	while (m != NULL) {
+		next_m = m->m_nextpkt;
 		m_freem(m);
 		ieee80211_free_node(ni);
+		m = next_m;
 	}
 }
 
@@ -898,3 +895,5 @@
 	return 0;
 }
 IEEE80211_IOCTL_SET(superg, superg_ioctl_set80211);
+
+#endif	/* IEEE80211_SUPPORT_SUPERG */

Modified: trunk/sys/net80211/ieee80211_superg.h
===================================================================
--- trunk/sys/net80211/ieee80211_superg.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_superg.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_superg.h 244051 2012-12-09 19:20:28Z adrian $
  */
 #ifndef _NET80211_IEEE80211_SUPERG_H_
 #define _NET80211_IEEE80211_SUPERG_H_
@@ -66,7 +67,6 @@
 struct ieee80211_superg {
 	/* fast-frames staging q */
 	struct ieee80211_stageq	ff_stageq[WME_NUM_AC];
-	int			ff_stageqdepth;	/* cumulative depth */
 };
 
 void	ieee80211_superg_attach(struct ieee80211com *);
@@ -87,6 +87,10 @@
 void	ieee80211_ff_age(struct ieee80211com *, struct ieee80211_stageq *,
 	     int quanta);
 
+/*
+ * See ieee80211_ff_age() for a description of the locking
+ * expectation here.
+ */
 static __inline void
 ieee80211_ff_flush(struct ieee80211com *ic, int ac)
 {
@@ -96,12 +100,16 @@
 		ieee80211_ff_age(ic, &sg->ff_stageq[ac], 0x7fffffff);
 }
 
+/*
+ * See ieee80211_ff_age() for a description of the locking
+ * expectation here.
+ */
 static __inline void
 ieee80211_ff_age_all(struct ieee80211com *ic, int quanta)
 {
 	struct ieee80211_superg *sg = ic->ic_superg;
 
-	if (sg != NULL && sg->ff_stageqdepth) {
+	if (sg != NULL) {
 		if (sg->ff_stageq[WME_AC_VO].depth)
 			ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_VO], quanta);
 		if (sg->ff_stageq[WME_AC_VI].depth)

Modified: trunk/sys/net80211/ieee80211_tdma.c
===================================================================
--- trunk/sys/net80211/ieee80211_tdma.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_tdma.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2007-2009 Intel Corporation
@@ -26,7 +27,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_tdma.c 254506 2013-08-18 23:40:30Z adrian $");
 #endif
 
 /*
@@ -36,6 +37,8 @@
 #include "opt_tdma.h"
 #include "opt_wlan.h"
 
+#ifdef	IEEE80211_SUPPORT_TDMA
+
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/mbuf.h>   
@@ -286,6 +289,8 @@
 {
 	struct ieee80211_tdma_state *ts = vap->iv_tdma;
 
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
 	KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
 	KASSERT(vap->iv_state == IEEE80211_S_RUN,
 	    ("wrong state %d", vap->iv_state));
@@ -740,7 +745,7 @@
 	struct ieee80211_tdma_state *ts = vap->iv_tdma;
 
 	if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
-		return EOPNOTSUPP;
+		return ENOSYS;
 
 	switch (ireq->i_type) {
 	case IEEE80211_IOC_TDMA_SLOT:
@@ -768,7 +773,7 @@
 	struct ieee80211_tdma_state *ts = vap->iv_tdma;
 
 	if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
-		return EOPNOTSUPP;
+		return ENOSYS;
 
 	switch (ireq->i_type) {
 	case IEEE80211_IOC_TDMA_SLOT:
@@ -818,3 +823,5 @@
 	return ERESTART;
 }
 IEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211);
+
+#endif	/* IEEE80211_SUPPORT_TDMA */

Modified: trunk/sys/net80211/ieee80211_tdma.h
===================================================================
--- trunk/sys/net80211/ieee80211_tdma.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_tdma.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2007-2009 Intel Corporation
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_tdma.h 192468 2009-05-20 20:00:40Z sam $
  */
 #ifndef _NET80211_IEEE80211_TDMA_H_
 #define _NET80211_IEEE80211_TDMA_H_

Modified: trunk/sys/net80211/ieee80211_var.h
===================================================================
--- trunk/sys/net80211/ieee80211_var.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_var.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2001 Atsushi Onoe
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
@@ -23,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_var.h 254076 2013-08-07 22:01:43Z adrian $
  */
 #ifndef _NET80211_IEEE80211_VAR_H_
 #define _NET80211_IEEE80211_VAR_H_
@@ -55,7 +56,7 @@
 #include <net80211/ieee80211_radiotap.h>
 #include <net80211/ieee80211_scan.h>
 
-#define	IEEE80211_TXPOWER_MAX	100	/* .5 dbM (XXX units?) */
+#define	IEEE80211_TXPOWER_MAX	100	/* .5 dBm (XXX units?) */
 #define	IEEE80211_TXPOWER_MIN	0	/* kill radio */
 
 #define	IEEE80211_DTIM_DEFAULT	1	/* default DTIM period */
@@ -118,6 +119,7 @@
 struct ieee80211com {
 	struct ifnet		*ic_ifp;	/* associated device */
 	ieee80211_com_lock_t	ic_comlock;	/* state update lock */
+	ieee80211_tx_lock_t	ic_txlock;	/* ic/vap TX lock */
 	TAILQ_HEAD(, ieee80211vap) ic_vaps;	/* list of vap instances */
 	int			ic_headroom;	/* driver tx headroom needs */
 	enum ieee80211_phytype	ic_phytype;	/* XXX wrong for multi-mode */
@@ -130,6 +132,7 @@
 	struct task		ic_mcast_task;	/* deferred mcast update */
 	struct task		ic_chan_task;	/* deferred channel change */
 	struct task		ic_bmiss_task;	/* deferred beacon miss hndlr */
+	struct task		ic_chw_task;	/* deferred HT CHW update */
 
 	uint32_t		ic_flags;	/* state flags */
 	uint32_t		ic_flags_ext;	/* extended state flags */
@@ -242,6 +245,10 @@
 	int			(*ic_setregdomain)(struct ieee80211com *,
 				    struct ieee80211_regdomain *,
 				    int, struct ieee80211_channel []);
+
+	int			(*ic_set_quiet)(struct ieee80211_node *,
+				    u_int8_t *quiet_elm);
+
 	/* send/recv 802.11 management frame */
 	int			(*ic_send_mgmt)(struct ieee80211_node *,
 				     int, int);
@@ -318,6 +325,10 @@
 				    int batimeout, int baseqctl);
 	void			(*ic_ampdu_rx_stop)(struct ieee80211_node *,
 				    struct ieee80211_rx_ampdu *);
+
+	/* The channel width has changed (20<->2040) */
+	void			(*ic_update_chw)(struct ieee80211com *);
+
 	uint64_t		ic_spare[7];
 };
 
@@ -403,6 +414,12 @@
 	uint8_t			iv_dtim_period;	/* DTIM period */
 	uint8_t			iv_dtim_count;	/* DTIM count from last bcn */
 						/* set/unset aid pwrsav state */
+	uint8_t			iv_quiet;	/* Quiet Element */
+	uint8_t			iv_quiet_count;	/* constant count for Quiet Element */
+	uint8_t			iv_quiet_count_value;	/* variable count for Quiet Element */
+	uint8_t			iv_quiet_period;	/* period for Quiet Element */
+	uint16_t		iv_quiet_duration;	/* duration for Quiet Element */
+	uint16_t		iv_quiet_offset;	/* offset for Quiet Element */
 	int			iv_csa_count;	/* count for doing CSA */
 
 	struct ieee80211_node	*iv_bss;	/* information for this node */
@@ -471,12 +488,22 @@
 	/* power save handling */
 	void			(*iv_update_ps)(struct ieee80211vap *, int);
 	int			(*iv_set_tim)(struct ieee80211_node *, int);
+	void			(*iv_node_ps)(struct ieee80211_node *, int);
+	void			(*iv_sta_ps)(struct ieee80211vap *, int);
+	void			(*iv_recv_pspoll)(struct ieee80211_node *,
+				    struct mbuf *);
+
 	/* state machine processing */
 	int			(*iv_newstate)(struct ieee80211vap *,
 				    enum ieee80211_state, int);
 	/* 802.3 output method for raw frame xmit */
+#if __FreeBSD_version >= 1000031
 	int			(*iv_output)(struct ifnet *, struct mbuf *,
+				    const struct sockaddr *, struct route *);
+#else
+	int			(*iv_output)(struct ifnet *, struct mbuf *,
 				    struct sockaddr *, struct route *);
+#endif
 	uint64_t		iv_spare[6];
 };
 MALLOC_DECLARE(M_80211_VAP);
@@ -690,6 +717,11 @@
 		uint32_t tx_radiotap,
 	    struct ieee80211_radiotap_header *rh, int rlen,
 		uint32_t rx_radiotap);
+void	ieee80211_radiotap_attachv(struct ieee80211com *,
+	    struct ieee80211_radiotap_header *th,
+	    int tlen, int n_tx_v, uint32_t tx_radiotap,
+	    struct ieee80211_radiotap_header *rh,
+	    int rlen, int n_rx_v, uint32_t rx_radiotap);
 void	ieee80211_radiotap_detach(struct ieee80211com *);
 void	ieee80211_radiotap_vattach(struct ieee80211vap *);
 void	ieee80211_radiotap_vdetach(struct ieee80211vap *);
@@ -796,6 +828,28 @@
 }
 
 /*
+ * Fetch the current TX power (cap) for the given node.
+ *
+ * This includes the node and ic/vap TX power limit as needed,
+ * but it doesn't take into account any per-rate limit.
+ */
+static __inline uint16_t
+ieee80211_get_node_txpower(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t txpower;
+
+	txpower = ni->ni_txpower;
+	txpower = MIN(txpower, ic->ic_txpowlimit);
+	if (ic->ic_curchan != NULL) {
+		txpower = MIN(txpower, 2 * ic->ic_curchan->ic_maxregpower);
+		txpower = MIN(txpower, ic->ic_curchan->ic_maxpower);
+	}
+
+	return (txpower);
+}
+
+/*
  * Debugging facilities compiled in when IEEE80211_DEBUG is defined.
  *
  * The intent is that any problem in the net80211 layer can be

Modified: trunk/sys/net80211/ieee80211_wds.c
===================================================================
--- trunk/sys/net80211/ieee80211_wds.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_wds.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -25,7 +26,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_wds.c 283855 2015-05-31 23:29:04Z ae $");
 #endif
 
 /*
@@ -232,7 +233,6 @@
 ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
 {
 	struct ieee80211com *ic = vap0->iv_ic;
-	struct ifnet *parent = ic->ic_ifp;
 	const struct ether_header *eh = mtod(m, const struct ether_header *);
 	struct ieee80211_node *ni;
 	struct ieee80211vap *vap;
@@ -256,7 +256,7 @@
 		/*
 		 * Duplicate the frame and send it.
 		 */
-		mcopy = m_copypacket(m, M_DONTWAIT);
+		mcopy = m_copypacket(m, M_NOWAIT);
 		if (mcopy == NULL) {
 			ifp->if_oerrors++;
 			/* XXX stat + msg */
@@ -296,13 +296,17 @@
 		mcopy->m_flags |= M_MCAST;
 		mcopy->m_pkthdr.rcvif = (void *) ni;
 
-		err = parent->if_transmit(parent, mcopy);
+		err = ieee80211_parent_xmitpkt(ic, mcopy);
 		if (err) {
 			/* NB: IFQ_HANDOFF reclaims mbuf */
 			ifp->if_oerrors++;
 			ieee80211_free_node(ni);
-		} else
+		} else {
 			ifp->if_opackets++;
+			if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
+			if_inc_counter(ifp, IFCOUNTER_OBYTES,
+			    m->m_pkthdr.len);
+		}
 	}
 }
 
@@ -557,7 +561,7 @@
 		 * crypto cipher modules used to do delayed update
 		 * of replay sequence numbers.
 		 */
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
 				/*
 				 * Discard encrypted frames when privacy is off.
@@ -575,7 +579,7 @@
 				goto out;
 			}
 			wh = mtod(m, struct ieee80211_frame *);
-			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+			wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
 		} else {
 			/* XXX M_WEP and IEEE80211_F_PRIVACY */
 			key = NULL;
@@ -709,7 +713,7 @@
 			    ether_sprintf(wh->i_addr2), rssi);
 		}
 #endif
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
 			    wh, NULL, "%s", "WEP set but not permitted");
 			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */

Modified: trunk/sys/net80211/ieee80211_wds.h
===================================================================
--- trunk/sys/net80211/ieee80211_wds.h	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_wds.h	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -22,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/net80211/ieee80211_wds.h 178354 2008-04-20 20:35:46Z sam $
  */
 #ifndef _NET80211_IEEE80211_WDS_H_
 #define _NET80211_IEEE80211_WDS_H_

Modified: trunk/sys/net80211/ieee80211_xauth.c
===================================================================
--- trunk/sys/net80211/ieee80211_xauth.c	2018-05-25 20:03:57 UTC (rev 9936)
+++ trunk/sys/net80211/ieee80211_xauth.c	2018-05-25 20:04:31 UTC (rev 9937)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2004 Video54 Technologies, Inc.
  * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
@@ -25,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/net80211/ieee80211_xauth.c 178354 2008-04-20 20:35:46Z sam $");
 
 /*
  * External authenticator placeholder module.



More information about the Midnightbsd-cvs mailing list