Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 16 Sep 2011 12:00:38 +0200
From:      Monthadar Al Jaberi <monthadar@gmail.com>
To:        freebsd-wireless@freebsd.org
Subject:   FreeBSD net80211s HWMP code
Message-ID:  <CA%2BsBSoJwkVMTAbybPbHFdz6CHbjDzF5PRgxpXoWHVESpAfEQKg@mail.gmail.com>

index | next in thread | raw e-mail

[-- Attachment #1 --]
Hi,

I am attaching my first update of the IEEE80211s HWMP code based on Draft 8.0.

It is not complete, some stuff works better, others are broken and
there are more things to todo.

So I would like it if some are interesting in testing the code.

Broken:
For now only use ondemand routing and not proactive (HWMPROOTMODE=disabled).

Works better:
PERR frames: if an MP is removed PERR frames will make sure that all
neighbour mesh nodes tables will be updated correctly.
PREQ/PREP: had some errors in code and did not follow standard correct.

Things todo:
External address support (called Proxy in freebsd code, will change it
to reflect standard naming)
Lifetime of paths don't decrement (but because we have better PERR
frames its not a big problem for now)
And much more!!

Attaching a diff of the code.

In the hope it will be useful for someone.
br,
-- 
//Monthadar Al Jaberi

[-- Attachment #2 --]
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 1d16ae6..e73db40 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -968,9 +968,13 @@ discard:
 char *
 ether_sprintf(const u_char *ap)
 {
-	static char etherbuf[18];
-	snprintf(etherbuf, sizeof (etherbuf), "%6D", ap, ":");
-	return (etherbuf);
+	static char etherbuf[6][18];
+	static int i = 0;
+	char *buf;
+	snprintf(etherbuf[i], sizeof (etherbuf[0]), "%6D", ap, ":");
+	buf = etherbuf[i];
+	i = (i+1)%6;
+	return (buf);
 }
 
 /*
diff --git a/sys/net80211/ieee80211_hwmp.c b/sys/net80211/ieee80211_hwmp.c
index 78729fc..8987ff7 100644
--- a/sys/net80211/ieee80211_hwmp.c
+++ b/sys/net80211/ieee80211_hwmp.c
@@ -1,6 +1,7 @@
 /*- 
  * Copyright (c) 2009 The FreeBSD Foundation 
- * All rights reserved. 
+ * Copyright (c) 2011 Monthadar Al Jaberi, TerraNet AB.
+ * All rights reserved.
  * 
  * This software was developed by Rui Paulo under sponsorship from the
  * FreeBSD Foundation. 
@@ -119,6 +120,10 @@ static void	hwmp_peerdown(struct ieee80211_node *);
 static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
 static struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
 
+MALLOC_DEFINE(M_80211_HWMP_PREQ, "80211preq", "802.11 HWMP Path Request frame");
+MALLOC_DEFINE(M_80211_HWMP_PREP, "80211prep", "802.11 HWMP Path Reply frame");
+MALLOC_DEFINE(M_80211_HWMP_PERR, "80211perr", "802.11 HWMP Path Error frame");
+
 /* unalligned little endian access */
 #define LE_WRITE_2(p, v) do {				\
 	((uint8_t *)(p))[0] = (v) & 0xff;		\
@@ -139,9 +144,17 @@ static const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
 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)
 
+/* The longer one of the lifetime should be stored as new lifetime */
+#define MESH_ROUTE_LIFETIME_MAX(a, b)	(a > b ? a : b)
+
+#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)
+
 /*
  * Private extension of ieee80211_mesh_route.
  */
@@ -187,6 +200,7 @@ SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
     "root announcement interval (ms)");
 
 #define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
+#define	IEEE80211_HWMP_PERR_MAXDEST	10
 
 static	ieee80211_recv_action_func hwmp_recv_action_meshpath;
 
@@ -281,9 +295,9 @@ hwmp_recv_action_meshpath(struct ieee80211_node *ni,
 	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 */
 	int found = 0;
@@ -293,66 +307,112 @@ hwmp_recv_action_meshpath(struct ieee80211_node *ni,
 		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) {
+			int i = 0;
+			preq = malloc(sizeof(struct ieee80211_meshpreq_ie) +
+						(iefrm[27]-1)*sizeof(*preq->preq_targets),
+						M_80211_HWMP_PREQ, M_NOWAIT | M_ZERO);
+			KASSERT(preq != NULL, ("preq == NULL"));
+			
+			preq->preq_ie = *iefrm++;
+			preq->preq_len = *iefrm++;
+			preq->preq_flags = *iefrm++;
+			preq->preq_hopcount = *iefrm++;
+			preq->preq_ttl = *iefrm++;
+			preq->preq_id = LE_READ_4(iefrm); iefrm += 4;
+			IEEE80211_ADDR_COPY(preq->preq_origaddr, iefrm); iefrm += 6;
+			preq->preq_origseq = LE_READ_4(iefrm); iefrm += 4;
+			/* NB: may have Originator Proxied Address */
+			preq->preq_lifetime = LE_READ_4(iefrm); iefrm += 4;
+			preq->preq_metric = LE_READ_4(iefrm); iefrm += 4;
+			preq->preq_tcount = *iefrm++;
+			
+			if (preq->preq_len != IEEE80211_MESHPREQ_BASE_SZ +
+					preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ) {
 				IEEE80211_DISCARD(vap,
 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
 				    wh, NULL, "%s", "PREQ with wrong len");
 				vap->iv_stats.is_rx_mgtdiscard++;
+				free(preq, M_80211_HWMP_PREQ);
 				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);
+			
+			for (i = 0; i < preq->preq_tcount; i++) {
+				preq->preq_targets[i].target_flags = *iefrm++;
+				IEEE80211_ADDR_COPY(preq->preq_targets[i].target_addr, iefrm); iefrm += 6;
+				preq->preq_targets[i].target_seq = LE_READ_4(iefrm); iefrm += 4;
+			}
+
+			hwmp_recv_preq(vap, ni, wh, preq);
+			free(preq, M_80211_HWMP_PREQ);
 			found++;
 			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) {
+			prep = malloc(sizeof(struct ieee80211_meshprep_ie),
+					M_80211_HWMP_PREP, M_NOWAIT | M_ZERO);
+			KASSERT(prep != NULL, ("prep == NULL"));
+			
+			prep->prep_ie = *iefrm++;
+			prep->prep_len = *iefrm++;
+			
+			if (prep->prep_len != IEEE80211_MESHPREP_BASE_SZ) {
 				IEEE80211_DISCARD(vap,
 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
 				    wh, NULL, "%s", "PREP with wrong len");
 				vap->iv_stats.is_rx_mgtdiscard++;
+				free(prep, M_80211_HWMP_PREP);
 				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->prep_flags = *iefrm++;
+			prep->prep_hopcount = *iefrm++;
+			prep->prep_ttl = *iefrm++;
+			IEEE80211_ADDR_COPY(prep->prep_targetaddr, iefrm); iefrm += 6;
+			prep->prep_targetseq = LE_READ_4(iefrm); iefrm += 4;
+			/* NB: May have Target Proxied Address */
+			prep->prep_lifetime = LE_READ_4(iefrm); iefrm += 4;
+			prep->prep_metric = LE_READ_4(iefrm); iefrm += 4;
+			IEEE80211_ADDR_COPY(prep->prep_origaddr, iefrm); iefrm += 6;
+			prep->prep_origseq = LE_READ_4(iefrm); iefrm += 4;
+
+			hwmp_recv_prep(vap, ni, wh, prep);
+			free(prep, M_80211_HWMP_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) {
+			int i = 0;
+			perr = malloc(sizeof(struct ieee80211_meshperr_ie) + 
+					(iefrm[3]-1)*sizeof(*perr->perr_dests),
+					M_80211_HWMP_PERR, M_NOWAIT | M_ZERO);
+			KASSERT(perr != NULL, ("perr == NULL"));
+			perr->perr_ie = *iefrm++;
+			perr->perr_len = *iefrm++;
+			perr->perr_ttl = *iefrm++;
+			perr->perr_ndests = *iefrm++;
+
+			if (perr->perr_len != (IEEE80211_MESHPERR_BASE_SZ +
+						perr->perr_ndests * IEEE80211_MESHPERR_DEST_SZ)) {
 				IEEE80211_DISCARD(vap,
 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
-				    wh, NULL, "%s", "PERR with wrong len");
+				    wh, NULL, "%s", "PERR with wrong len, %s", perr->perr_len);
 				vap->iv_stats.is_rx_mgtdiscard++;
+				free(perr, M_80211_HWMP_PERR);
 				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);
+
+			for(i = 0; i<perr->perr_ndests; i++) {
+				perr->perr_dests[i].dest_flags = *iefrm++;
+				IEEE80211_ADDR_COPY(perr->perr_dests[i].dest_addr, iefrm); iefrm += 6;
+				perr->perr_dests[i].dest_seq = LE_READ_4(iefrm); iefrm += 4;
+				/* NB: May have Target Proxied Address */
+				perr->perr_dests[i].dest_rcode = LE_READ_2(iefrm); iefrm += 2;
+			}
+
+			hwmp_recv_perr(vap, ni, wh, perr);
+			free(perr, M_80211_HWMP_PERR);
 			found++;
 			break;
 		}
@@ -376,7 +436,7 @@ hwmp_recv_action_meshpath(struct ieee80211_node *ni,
 			break;
 		}
 		}
-		iefrm += iefrm[1] + 2;
+// 		iefrm += iefrm[1] + 2;
 	}
 	if (!found) {
 		IEEE80211_DISCARD(vap,
@@ -493,9 +553,8 @@ hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
 {
 	int i;
 
-	*frm++ = IEEE80211_ELEMID_MESHPREQ;
-	*frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
-	    (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
+	*frm++ = preq->preq_ie;
+	*frm++ = preq->preq_len;
 	*frm++ = preq->preq_flags;
 	*frm++ = preq->preq_hopcount;
 	*frm++ = preq->preq_ttl;
@@ -520,8 +579,8 @@ hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
 static uint8_t *
 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_ie;
+	*frm++ = prep->prep_len;
 	*frm++ = prep->prep_flags;
 	*frm++ = prep->prep_hopcount;
 	*frm++ = prep->prep_ttl;
@@ -542,9 +601,8 @@ hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
 {
 	int i;
 
-	*frm++ = IEEE80211_ELEMID_MESHPERR;
-	*frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
-	    (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
+	*frm++ = perr->perr_ie;
+	*frm++ = perr->perr_len;
 	*frm++ = perr->perr_ttl;
 	*frm++ = perr->perr_ndests;
 	for (i = 0; i < perr->perr_ndests; i++) {
@@ -675,9 +733,10 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
     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 *rt = NULL; // Target route
+	struct ieee80211_mesh_route *rtorig = NULL; // Originator route
+	struct ieee80211_hwmp_route *hrorig = NULL;
+	struct ieee80211_hwmp_route *hr = NULL;
 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 	struct ieee80211_meshprep_ie prep;
 
@@ -692,46 +751,86 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 		return;
 
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-	    "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
+	    "received PREQ, originator %s, target %s", ether_sprintf(preq->preq_origaddr), ether_sprintf(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
+	 * AND forwarding is disabled, discard this PREQ.
 	 */
-	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
+	if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) || /* XXX: test for proxy address*/
+		!IEEE80211_IS_MULTICAST(PREQ_TADDR(0))) &&
 	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
-		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
+		    ni->ni_macaddr, NULL, "not accepting PREQ, originator %s", ether_sprintf(preq->preq_origaddr));
 		return;
 	}
+	/*
+	 * Acceptance criteria: if unicast addressed 
+	 * AND no valid forwarding for Target of PREQ, discard this PREQ.
+	 */
+	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
+	if(rt != NULL)
+		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+	if((preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM) == 0 && /* address mode: ucast */
+		rt == NULL &&
+		!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, ni->ni_macaddr,
+		    NULL, "unicast addressed PREQ, originator %s, unknown target %s",
+				      ether_sprintf(preq->preq_origaddr),
+				      ether_sprintf(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);
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			"adding originator %s", ether_sprintf(preq->preq_origaddr));
+	}
 	if (rtorig == NULL) {
 		/* XXX stat */
 		return;
 	}
 	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
-	/*
-	 * Sequence number validation.
+
+	/* 
+	 * 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)) {
+	if(HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
+		(HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) && preq->preq_metric < rtorig->rt_metric)){
+		hrorig->hr_seq = preq->preq_origseq;
+		IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2);
+		rtorig->rt_metric = preq->preq_metric +
+			ms->ms_pmetric->mpm_metric(ni);
+		rtorig->rt_nhops  = preq->preq_hopcount + 1;
+		rtorig->rt_lifetime  = MESH_ROUTE_LIFETIME_MAX(preq->preq_lifetime, rtorig->rt_lifetime);
+		rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; /* path to orig is valid now */
+	}else if(hr != NULL && 
+		HWMP_SEQ_EQ(hr->hr_seq, PREQ_TSEQ(0)) &&
+		(rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0){
 		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);
+			"received PREQ, originator %s", "unknown");
+	}else{
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, ni->ni_macaddr, 
+			NULL, "PREQ, originator %s, last seen orig.seq %d, preq orig.seq %d", 
+				      ether_sprintf(preq->preq_origaddr), hrorig->hr_seq, preq->preq_origseq);
 		return;
 	}
-	hrorig->hr_preqid = preq->preq_id;
-	hrorig->hr_seq = preq->preq_origseq;
+	
+	/* 
+	 * Forwarding information for transmitter mesh STA 
+	 * [OPTIONAL: if metric improved]
+	 */
 
 	/*
 	 * Check if the PREQ is addressed to 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));
+		    "reply PREP, originator %s", ether_sprintf(preq->preq_origaddr));
 		/*
 		 * Build and send a PREP frame.
 		 */
@@ -755,54 +854,6 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 			hwmp_discover(vap, rt->rt_dest, NULL);
 		return;
 	}
-	/*
-	 * 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];
-
-		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;
-			}
-		}
-		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 ||
-		    (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);
-			prep.prep_origseq = preq->preq_origseq;
-			prep.prep_lifetime = preq->preq_lifetime;
-			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
-			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_discover(vap, rootmac, NULL);
-		return;
-	}
-	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
 
 	/*
 	 * Forwarding and Intermediate reply for PREQs with 1 target.
@@ -810,7 +861,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 	if (preq->preq_tcount == 1) {
 		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
 
-		memcpy(&ppreq, preq, sizeof(ppreq));
+		memcpy(&ppreq, preq, sizeof(struct ieee80211_meshpreq_ie));
 		/*
 		 * We have a valid route to this node.
 		 */
@@ -819,11 +870,15 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 			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));
+				    "forward PREQ, originator %s, target %s, to %s",
+					       ether_sprintf(preq->preq_origaddr),
+					       ether_sprintf(PREQ_TADDR(0)),
+					       ether_sprintf(rt->rt_nexthop));
 				/*
 				 * Propagate the original PREQ.
+				 * PREQ is unicast now to rt->rt_nexthop
 				 */
+				ppreq.preq_flags &= ~IEEE80211_MESHPREQ_FLAGS_AM;
 				ppreq.preq_hopcount += 1;
 				ppreq.preq_ttl -= 1;
 				ppreq.preq_metric +=
@@ -837,20 +892,23 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 				ppreq.preq_targets[0].target_flags &=
 				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
 				hwmp_send_preq(ni, vap->iv_myaddr,
-				    broadcastaddr, &ppreq);
+				    rt->rt_nexthop, &ppreq);
 			}
 			/*
 			 * 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 HWMP sequence number 
+			 * greater or equal to Target HWMP sequence number of PREQ.
 			 */
-	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
+	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO) &&
+				HWMP_SEQ_GEQ(hr->hr_seq, PREQ_TSEQ(0))) {
 				struct ieee80211_meshprep_ie prep;
 
 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-				    "intermediate reply for PREQ from %s",
-				    ether_sprintf(preq->preq_origaddr));
+				    "intermediate PREP, originator %s, target %s",
+					       ether_sprintf(preq->preq_origaddr),
+					       ether_sprintf(PREQ_TADDR(0)));
 				prep.prep_flags = 0;
-				prep.prep_hopcount = rt->rt_nhops + 1;
+				prep.prep_hopcount = rt->rt_nhops;
 				prep.prep_ttl = ms->ms_ttl;
 				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
 				    PREQ_TADDR(0));
@@ -862,7 +920,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 				    preq->preq_origaddr);
 				prep.prep_origseq = hrorig->hr_seq;
 				hwmp_send_prep(ni, vap->iv_myaddr,
-				    broadcastaddr, &prep);
+				    wh->i_addr2, &prep);
 			}
 		/*
 		 * We have no information about this path,
@@ -870,26 +928,11 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
 		 */
 		} 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));
+			    "broadcast PREQ, originator %s, target %s",
+			    ether_sprintf(preq->preq_origaddr),
+			    ether_sprintf(PREQ_TADDR(0)));
+			ppreq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_AM;
 			ppreq.preq_hopcount += 1;
 			ppreq.preq_ttl -= 1;
 			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
@@ -928,8 +971,8 @@ hwmp_send_preq(struct ieee80211_node *ni,
 	 *     [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 = IEEE80211_MESHPREQ_BASE_SZ + preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ;
+	return hwmp_send_action(ni, sa, da, (uint8_t *)preq, preq->preq_len+2);
 }
 
 static void
@@ -939,81 +982,102 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
 	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_hwmp_route *hr;
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ifnet *ifp = vap->iv_ifp;
 	struct mbuf *m, *next;
+	uint32_t metric = 0;
 
 	/*
-	 * Acceptance criteria: if the corresponding PREQ was not generated
+	 * Acceptance criteria: if the corresponding PREP was not generated
 	 * by us and forwarding is disabled, discard this PREP.
 	 */
 	if (ni == vap->iv_bss ||
 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
 		return;
-	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
-	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
+	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && /* XXX: test for proxy address */
+	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)){
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
+		    ni->ni_macaddr, NULL, "not accepting PREP, originator %s, forwarding disabled", ether_sprintf(prep->prep_origaddr));
 		return;
+	}
+
+	/* PREP ACCEPTED */
 
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-	    "received PREP from %s", ether_sprintf(prep->prep_targetaddr));
+	    "received PREP, originator %s, target %s", ether_sprintf(prep->prep_origaddr), ether_sprintf(prep->prep_targetaddr));
 
 	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;
-			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);
+		rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			"adding target %s", ether_sprintf(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;
-		} 
-		return;
+		}
 	}
-	/*
-	 * Sequence number validation.
+
+	/* 
+	 * Data creation and update of forwarding information
+	 * according to Table 11C-8 for originator mesh STA.
 	 */
 	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);
+	if(HWMP_SEQ_GT(prep->prep_targetseq, hr->hr_seq) ||
+		(HWMP_SEQ_EQ(prep->prep_targetseq, hr->hr_seq) && prep->prep_metric < rt->rt_metric)){
+		metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni);
+		if((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0){
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "create path to %s, hopcount %d metric %d",
+			    ether_sprintf(prep->prep_targetaddr),
+			    prep->prep_hopcount + 1,
+			    metric);
+		}else{
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+			    "prefer path to %s, hopcount %d:[%d] metric %d:[%d]",
+			    ether_sprintf(prep->prep_targetaddr),
+			    prep->prep_hopcount + 1, rt->rt_nhops,
+			    metric, rt->rt_metric);
+		}
+		hr->hr_seq = prep->prep_targetseq;
+		IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
+		rt->rt_metric = metric;
+		rt->rt_nhops  = prep->prep_hopcount + 1;
+		rt->rt_lifetime  = MESH_ROUTE_LIFETIME_MAX(prep->prep_lifetime, rt->rt_lifetime);
+		rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; /* path to target is valid now */
+	}else{
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, ni->ni_macaddr, 
+			NULL, "PREP, originator %s, last seen orig.seq %d, preq orig.seq %d", 
+				      ether_sprintf(prep->prep_origaddr), hr->hr_seq, prep->prep_origseq);
 		return;
 	}
-	hr->hr_seq = prep->prep_targetseq;
+	
+	
 	/*
 	 * 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) {
 		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
-
+		rtorig = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
+		if(rtorig == NULL){
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, ni->ni_macaddr, 
+				NULL, "PREP, originator %s unknown", ether_sprintf(prep->prep_origaddr));
+			return;
+		}
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "propagate PREP from %s",
-		    ether_sprintf(prep->prep_targetaddr));
+		    "propagate PREP, originator %s, target %s, to %s",
+		    ether_sprintf(prep->prep_origaddr), ether_sprintf(prep->prep_targetaddr), ether_sprintf(rtorig->rt_nexthop));
 
-		memcpy(&pprep, prep, sizeof(pprep));
+		memcpy(&pprep, prep, sizeof(struct ieee80211_meshprep_ie));
 		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(ni, vap->iv_myaddr, rtorig->rt_nexthop, &pprep);
 	}
 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
@@ -1022,40 +1086,6 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *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) {
-		/*
-		 * Check if we already have a path to this node.
-		 * If we do, check if this path reply contains a
-		 * better route.
-		 */
-		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.
@@ -1094,22 +1124,28 @@ hwmp_send_prep(struct ieee80211_node *ni,
 	 *     [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 = IEEE80211_MESHPREP_BASE_SZ;
+	return hwmp_send_action(ni, sa, da, (uint8_t *)prep, prep->prep_len + 2);
 }
 
-#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_DRCODE(n)	perr->perr_dests[n].dest_rcode
 static void
 hwmp_peerdown(struct ieee80211_node *ni)
 {
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
-	struct ieee80211_meshperr_ie perr;
-	struct ieee80211_mesh_route *rt;
+	struct ieee80211_meshperr_ie *perr;
+	struct ieee80211_mesh_route *rt, *next;
 	struct ieee80211_hwmp_route *hr;
+	int i = 0;
+	
+	perr = malloc(sizeof(struct ieee80211_meshperr_ie) + 
+			IEEE80211_HWMP_PERR_MAXDEST*sizeof(*perr->perr_dests),
+			M_80211_HWMP_PERR, M_NOWAIT | M_ZERO);
+	KASSERT(perr != NULL, ("error allocationg PERR"));
 
 	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
 	if (rt == NULL)
@@ -1117,18 +1153,33 @@ hwmp_peerdown(struct ieee80211_node *ni)
 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 	    "%s", "delete route entry");
-	perr.perr_ttl = ms->ms_ttl;
-	perr.perr_ndests = 1;
-	PERR_DFLAGS(0) = 0;
-	if (hr->hr_seq == 0)
-		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_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
+	perr->perr_ttl = ms->ms_ttl;
+	
+	/* find all destinations that have ni->ni_macaddr as nexthop and add to PERR*/
+	MESH_RT_LOCK(ms);
+	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
+		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
+		    IEEE80211_ADDR_EQ(rt->rt_nexthop, ni->ni_macaddr)){
+			hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+			KASSERT(i < IEEE80211_HWMP_PERR_MAXDEST, ("PERR max destination overflow"));
+			PERR_DFLAGS(i) = 0;
+			if (hr->hr_seq == 0)
+				PERR_DFLAGS(i) |= IEEE80211_MESHPERR_DFLAGS_USN;
+			IEEE80211_ADDR_COPY(PERR_DADDR(i), rt->rt_dest);
+			PERR_DSEQ(i) = ++hr->hr_seq;
+			PERR_DRCODE(i) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, "PERR: found unrechable destination %s", ether_sprintf(PERR_DADDR(i)));
+			++i;
+		}
+	}
+	MESH_RT_UNLOCK(ms);
+	
+	perr->perr_ndests = i;
+	
 	/* 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->iv_bss, vap->iv_myaddr, broadcastaddr, perr);
+	free(perr, M_80211_HWMP_PERR);
 }
 #undef	PERR_DFLAGS
 #undef	PERR_DADDR
@@ -1146,44 +1197,87 @@ hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
 	struct ieee80211_mesh_route *rt = NULL;
 	struct ieee80211_hwmp_route *hr;
- 	struct ieee80211_meshperr_ie pperr;
-	int i, forward = 0;
+	struct ieee80211_meshperr_ie *pperr;
+	int i, k, forward = 0;
+	int num_unrchbl_dest = 0; // number of unreachable destination relevant to us. Zero means to reject PERR.
+	int unrchbl_dest[IEEE80211_HWMP_PERR_MAXDEST];
 
 	/*
 	 * Acceptance criteria: check if we received a PERR from a
-	 * neighbor and forwarding is enabled.
+	 * neighbor.
 	 */
 	if (ni == vap->iv_bss ||
-	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
-	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
+	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
 		return;
+		
 	/*
-	 * Find all routing entries that match and delete them.
+	 * Acceptance criteria: check if one of the destination addresses in PERR
+	 * is in forwarding information with next hop equals to transmitter address.
+	 * Also build a list of unreachble destination relavant for us, for when PERR gets accepted.
 	 */
+	k = 0;
 	for (i = 0; i < perr->perr_ndests; i++) {
 		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
 		if (rt == NULL)
 			continue;
+		if((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
+			IEEE80211_ADDR_EQ(rt->rt_nexthop, wh->i_addr2)){
+			unrchbl_dest[k++] = i;
+			++num_unrchbl_dest;
+		}
+	}
+	if(!num_unrchbl_dest){
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, ni->ni_macaddr, 
+				NULL, "%s", "PERR unrelevant");
+		return;
+	}
+	
+	/* PERR ACCEPTED */
+	
+	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, "received PERR, number of destinations %d, unreachable %d", perr->perr_ndests, num_unrchbl_dest);
+	
+	for(i = 0; i<num_unrchbl_dest; i++) {
+		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
+		if (rt == NULL) //XXX: we  know it is not null!
+			continue;
 		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;
+		
+		if((PERR_DRCODE(i) & IEEE80211_REASON_MESH_PERR_NO_FI) &&
+			PERR_DSEQ(i) == 0){
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+				       "PERR: no forwarding information for destination %s",
+				       ether_sprintf(PERR_DADDR(unrchbl_dest[i])));
+			hr->hr_seq++; // 11C.9.11.4.3 effect of receipt case b)
+		}else if(HWMP_SEQ_GT(PERR_DSEQ(i), hr->hr_seq)){ //case c)
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+				       "PERR: unrechable destination %s with perr seq %d > stored seq %d",
+				       ether_sprintf(PERR_DADDR(unrchbl_dest[i])), PERR_DSEQ(i), hr->hr_seq);
+			hr->hr_seq = PERR_DSEQ(i);
+			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID; //invlidate forwarding information
+			++forward;
+		}else{
+			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+				       "PERR: unrechable destination %s with perr seq %d < stored seq %d",
+				       ether_sprintf(PERR_DADDR(unrchbl_dest[i])), PERR_DSEQ(i), hr->hr_seq);
 		}
 	}
+
 	/*
 	 * Propagate the PERR if we previously found it on our routing table.
-	 * XXX handle ndest > 1
 	 */
-	if (forward && perr->perr_ttl > 1) {
+	if (forward > 100 && perr->perr_ttl > 1 &&
+	    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
+		pperr = malloc(sizeof(struct ieee80211_meshperr_ie) + 
+		    (perr->perr_ndests-1)*sizeof(*perr->perr_dests),
+		    M_80211_HWMP_PERR, M_NOWAIT | M_ZERO);
+		KASSERT(pperr != NULL, ("error allocationg PERR"));
+		memcpy(pperr, perr, sizeof(struct ieee80211_meshperr_ie) + 
+		    (perr->perr_ndests-1)*sizeof(*perr->perr_dests));
 		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);
+		pperr->perr_ttl--;
+		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, pperr);
+		free(pperr, M_80211_HWMP_PERR);
 	}
 }
 #undef	PEER_DADDR
@@ -1214,8 +1308,8 @@ hwmp_send_perr(struct ieee80211_node *ni,
 	 *     [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));
+	perr->perr_len = IEEE80211_MESHPERR_BASE_SZ + perr->perr_ndests * IEEE80211_MESHPERR_DEST_SZ;
+	return hwmp_send_action(ni, sa, da, (uint8_t *)perr, perr->perr_len+2);
 }
 
 static void
@@ -1323,13 +1417,13 @@ hwmp_discover(struct ieee80211vap *vap,
 			/* 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.
 			 */
-			preq.preq_flags = 0;
+			preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; /* Group addressed PREQ Case A */
 			preq.preq_hopcount = 0;
 			preq.preq_ttl = ms->ms_ttl;
 			preq.preq_id = ++hs->hs_preqid;
@@ -1345,7 +1439,7 @@ hwmp_discover(struct ieee80211vap *vap,
 			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) = hr->hr_seq;
 			/* XXX check return value */
 			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
 			    broadcastaddr, &preq);
diff --git a/sys/net80211/ieee80211_mesh.h b/sys/net80211/ieee80211_mesh.h
index ad1b02a..a67f900 100644
--- a/sys/net80211/ieee80211_mesh.h
+++ b/sys/net80211/ieee80211_mesh.h
@@ -183,6 +183,9 @@ struct ieee80211_meshrann_ie {
 } __packed;
 
 /* Mesh Path Request */
+#define IEEE80211_MESHPREQ_BASE_SZ 	(26)
+#define IEEE80211_MESHPREQ_BASE_SZ_AE 	(32)
+#define IEEE80211_MESHPREQ_TRGT_SZ 	(11)
 struct ieee80211_meshpreq_ie {
 	uint8_t		preq_ie;	/* IEEE80211_ELEMID_MESHPREQ */
 	uint8_t		preq_len;
@@ -196,7 +199,8 @@ struct ieee80211_meshpreq_ie {
 	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 */
@@ -209,8 +213,11 @@ struct ieee80211_meshpreq_ie {
 		uint32_t	target_seq;	/* HWMP Sequence Number */
 	} __packed preq_targets[1];		/* NB: variable size */
 } __packed;
+MALLOC_DECLARE(M_80211_HWMP_PREQ);
 
 /* 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;
@@ -219,14 +226,19 @@ struct ieee80211_meshprep_ie {
 	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];
 	uint32_t	prep_origseq;	/* HWMP Sequence Number */
 } __packed;
+MALLOC_DECLARE(M_80211_HWMP_PREP);
 
 /* Mesh Path Error */
+#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;
@@ -238,9 +250,12 @@ struct ieee80211_meshperr_ie {
 #define	IEEE80211_MESHPERR_DFLAGS_RC	0x02
 		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;
+MALLOC_DECLARE(M_80211_HWMP_PERR);
 
 #ifdef notyet
 /* Mesh Proxy Update */
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index c1fc006..daaf74c 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -47,7 +47,7 @@
 #define	IEEE80211_INACT_WAIT	15		/* inactivity interval (secs) */
 #define	IEEE80211_INACT_INIT	(30/IEEE80211_INACT_WAIT)	/* initial */
 #define	IEEE80211_INACT_AUTH	(180/IEEE80211_INACT_WAIT)	/* associated but not authorized */
-#define	IEEE80211_INACT_RUN	(300/IEEE80211_INACT_WAIT)	/* authorized */
+#define	IEEE80211_INACT_RUN	(30/IEEE80211_INACT_WAIT)	/* authorized */
 #define	IEEE80211_INACT_PROBE	(30/IEEE80211_INACT_WAIT)	/* probe */
 #define	IEEE80211_INACT_SCAN	(300/IEEE80211_INACT_WAIT)	/* scanned */
 
help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CA%2BsBSoJwkVMTAbybPbHFdz6CHbjDzF5PRgxpXoWHVESpAfEQKg>