[Zrouter-src-freebsd] ZRouter.org: push to FreeBSD HEAD tree

zrouter-src-freebsd at zrouter.org zrouter-src-freebsd at zrouter.org
Wed Jul 25 14:36:15 UTC 2012


details:   http://zrouter.org/hg/FreeBSD/head//rev/4abb4d69799e
changeset: 497:4abb4d69799e
user:      Aleksandr Rybalko <ray at ddteam.net>
date:      Wed Jul 25 16:47:10 2012 +0300
description:
Lazy update

diffstat:

 head/sys/libkern/iconv.c               |   13 +-
 head/sys/libkern/iconv_ucs.c           |    4 +-
 head/sys/net/bpf.c                     |  565 +++++++++++++-------
 head/sys/net/bpf.h                     |  127 ++++-
 head/sys/net/bpf_buffer.c              |   53 +-
 head/sys/net/bpf_buffer.h              |    3 +-
 head/sys/net/bpf_zerocopy.c            |   12 +-
 head/sys/net/bpfdesc.h                 |   15 +-
 head/sys/net/flowtable.c               |    5 +-
 head/sys/net/ieee8023ad_lacp.c         |   82 +-
 head/sys/net/if.h                      |    7 +-
 head/sys/net/if_bridge.c               |   46 +-
 head/sys/net/if_dl.h                   |    3 +-
 head/sys/net/if_epair.c                |   40 +-
 head/sys/net/if_ethersubr.c            |    2 +-
 head/sys/net/if_gif.c                  |  139 +++-
 head/sys/net/if_lagg.c                 |   28 +-
 head/sys/net/if_loop.c                 |   27 +-
 head/sys/net/if_stf.c                  |   18 +-
 head/sys/net/if_tap.c                  |   41 +-
 head/sys/net/if_var.h                  |    4 +-
 head/sys/net/if_vlan.c                 |   22 +-
 head/sys/net/route.h                   |   17 +-
 head/sys/net/rtsock.c                  |    6 +-
 head/sys/net80211/_ieee80211.h         |    4 +-
 head/sys/net80211/ieee80211.h          |    6 +-
 head/sys/net80211/ieee80211_action.c   |   10 +-
 head/sys/net80211/ieee80211_ddb.c      |    6 +-
 head/sys/net80211/ieee80211_ht.c       |   30 +-
 head/sys/net80211/ieee80211_hwmp.c     |  878 ++++++++++++++++++++++----------
 head/sys/net80211/ieee80211_input.c    |   20 +-
 head/sys/net80211/ieee80211_ioctl.h    |    8 +-
 head/sys/net80211/ieee80211_mesh.c     |  680 +++++++++++++++++++-----
 head/sys/net80211/ieee80211_mesh.h     |  109 ++-
 head/sys/net80211/ieee80211_output.c   |  123 ++-
 head/sys/net80211/ieee80211_radiotap.c |   28 +-
 head/sys/net80211/ieee80211_var.h      |    7 +-
 head/sys/netgraph/netflow/netflow.c    |  164 +++--
 head/sys/netgraph/netflow/netflow_v9.c |   16 +-
 head/sys/netgraph/netflow/ng_netflow.c |   83 +--
 head/sys/netgraph/netflow/ng_netflow.h |   16 +-
 head/sys/netgraph/ng_ksocket.c         |    6 +-
 head/sys/netgraph/ng_mppc.c            |    2 +-
 head/sys/netgraph/ng_patch.c           |    4 +-
 head/sys/netinet6/frag6.c              |   15 +-
 head/sys/netinet6/icmp6.c              |    6 +-
 head/sys/netinet6/in6.c                |   57 +-
 head/sys/netinet6/in6.h                |    4 +-
 head/sys/netinet6/in6_cksum.c          |  120 +++-
 head/sys/netinet6/in6_src.c            |    6 +-
 head/sys/netinet6/ip6_forward.c        |   20 +-
 head/sys/netinet6/ip6_input.c          |  268 ++++++---
 head/sys/netinet6/ip6_ipsec.c          |    3 +-
 head/sys/netinet6/ip6_mroute.c         |   32 +-
 head/sys/netinet6/ip6_mroute.h         |    3 +-
 head/sys/netinet6/ip6_output.c         |  124 +++-
 head/sys/netinet6/ip6_var.h            |    6 +-
 head/sys/netinet6/mld6.c               |   13 +-
 head/sys/netinet6/nd6.c                |  216 +++----
 head/sys/netinet6/nd6.h                |    4 +-
 head/sys/netinet6/nd6_nbr.c            |   14 +-
 head/sys/netinet6/route6.c             |    4 +-
 head/sys/netinet6/scope6.c             |   15 +-
 head/sys/netinet6/scope6_var.h         |    3 +-
 head/sys/netinet6/sctp6_usrreq.c       |  326 ++++--------
 head/sys/netinet6/sctp6_var.h          |   11 +-
 head/sys/netinet6/udp6_usrreq.c        |   23 +-
 head/sys/netipsec/ipsec_output.c       |    5 +-
 head/sys/netncp/ncp_nls.h              |    6 +-
 head/sys/netsmb/smb_dev.c              |    4 +-
 head/sys/netsmb/smb_trantcp.c          |    6 +-
 head/sys/nfsclient/nfs_bio.c           |   11 +-
 head/sys/nfsclient/nfs_node.c          |    4 +-
 head/sys/nfsclient/nfs_subs.c          |    3 +-
 head/sys/nfsclient/nfs_vfsops.c        |    3 +-
 head/sys/nfsclient/nfs_vnops.c         |   18 +-
 head/sys/nfsclient/nfsnode.h           |    3 +-
 77 files changed, 3082 insertions(+), 1753 deletions(-)

diffs (9405 lines):

diff -r 58b18bd1429e -r 4abb4d69799e head/sys/libkern/iconv.c
--- a/head/sys/libkern/iconv.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/libkern/iconv.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/libkern/iconv.c 227650 2011-11-18 03:05:20Z kevlo $");
+__FBSDID("$FreeBSD: head/sys/libkern/iconv.c 236899 2012-06-11 17:42:39Z mjg $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -84,9 +84,11 @@
 	struct iconv_cspair *csp;
 
 	sx_xlock(&iconv_lock);
-	while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL) {
-		if (csp->cp_refcount)
+	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
+		if (csp->cp_refcount) {
+			sx_xunlock(&iconv_lock);
 			return EBUSY;
+		}
 	}
 
 	while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL)
@@ -133,6 +135,7 @@
 static int
 iconv_unregister_converter(struct iconv_converter_class *dcp)
 {
+	dcp->refs--;
 	if (dcp->refs > 1) {
 		ICDEBUG("converter have %d referenses left\n", dcp->refs);
 		return EBUSY;
@@ -549,9 +552,7 @@
 iconv_lookupcp(char **cpp, const char *s)
 {
 	if (cpp == NULL) {
-		ICDEBUG("warning a NULL list passed\n", ""); /* XXX ISO variadic								macros cannot
-								leave out the
-								variadic args */
+		ICDEBUG("warning a NULL list passed\n", "");
 		return ENOENT;
 	}
 	for (; *cpp; cpp++)
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/libkern/iconv_ucs.c
--- a/head/sys/libkern/iconv_ucs.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/libkern/iconv_ucs.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/libkern/iconv_ucs.c 227650 2011-11-18 03:05:20Z kevlo $");
+__FBSDID("$FreeBSD: head/sys/libkern/iconv_ucs.c 235713 2012-05-21 02:45:47Z kevlo $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -457,8 +457,6 @@
 		/* out of utf-16 range or having illegal bits */
 		return (0);
 	}
-	if (w == 0)
-		return (0);
 
 	if (srclen < w)
 		return (0);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/bpf.c
--- a/head/sys/net/bpf.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/bpf.c	Wed Jul 25 16:47:10 2012 +0300
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/bpf.c 233946 2012-04-06 13:34:19Z melifaro $");
+__FBSDID("$FreeBSD: head/sys/net/bpf.c 236806 2012-06-09 10:04:40Z melifaro $");
 
 #include "opt_bpf.h"
 #include "opt_compat.h"
@@ -147,6 +147,7 @@
 
 static void	bpf_attachd(struct bpf_d *, struct bpf_if *);
 static void	bpf_detachd(struct bpf_d *);
+static void	bpf_detachd_locked(struct bpf_d *);
 static void	bpf_freed(struct bpf_d *);
 static int	bpf_movein(struct uio *, int, struct ifnet *, struct mbuf **,
 		    struct sockaddr *, int *, struct bpf_insn *);
@@ -158,7 +159,7 @@
 		    void (*)(struct bpf_d *, caddr_t, u_int, void *, u_int),
 		    struct bintime *);
 static void	reset_d(struct bpf_d *);
-static int	 bpf_setf(struct bpf_d *, struct bpf_program *, u_long cmd);
+static int	bpf_setf(struct bpf_d *, struct bpf_program *, u_long cmd);
 static int	bpf_getdltlist(struct bpf_d *, struct bpf_dltlist *);
 static int	bpf_setdlt(struct bpf_d *, u_int);
 static void	filt_bpfdetach(struct knote *);
@@ -206,6 +207,37 @@
 	.f_event = filt_bpfread,
 };
 
+eventhandler_tag	bpf_ifdetach_cookie = NULL;
+
+/*
+ * LOCKING MODEL USED BY BPF:
+ * Locks:
+ * 1) global lock (BPF_LOCK). Mutex, used to protect interface addition/removal,
+ * some global counters and every bpf_if reference.
+ * 2) Interface lock. Rwlock, used to protect list of BPF descriptors and their filters.
+ * 3) Descriptor lock. Mutex, used to protect BPF buffers and various structure fields
+ *   used by bpf_mtap code.
+ *
+ * Lock order:
+ *
+ * Global lock, interface lock, descriptor lock
+ *
+ * We have to acquire interface lock before descriptor main lock due to BPF_MTAP[2]
+ * working model. In many places (like bpf_detachd) we start with BPF descriptor
+ * (and we need to at least rlock it to get reliable interface pointer). This
+ * gives us potential LOR. As a result, we use global lock to protect from bpf_if
+ * change in every such place.
+ *
+ * Changing d->bd_bif is protected by 1) global lock, 2) interface lock and
+ * 3) descriptor main wlock.
+ * Reading bd_bif can be protected by any of these locks, typically global lock.
+ *
+ * Changing read/write BPF filter is protected by the same three locks,
+ * the same applies for reading.
+ *
+ * Sleeping in global lock is not allowed due to bpfdetach() using it.
+ */
+
 /*
  * Wrapper functions for various buffering methods.  If the set of buffer
  * modes expands, we will probably want to introduce a switch data structure
@@ -216,7 +248,7 @@
     u_int len)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	switch (d->bd_bufmode) {
 	case BPF_BUFMODE_BUFFER:
@@ -236,7 +268,7 @@
     u_int len)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	switch (d->bd_bufmode) {
 	case BPF_BUFMODE_BUFFER:
@@ -258,7 +290,7 @@
 bpf_buf_reclaimed(struct bpf_d *d)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	switch (d->bd_bufmode) {
 	case BPF_BUFMODE_BUFFER:
@@ -317,7 +349,7 @@
 bpf_buffull(struct bpf_d *d)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	switch (d->bd_bufmode) {
 	case BPF_BUFMODE_ZBUF:
@@ -333,7 +365,7 @@
 bpf_bufheld(struct bpf_d *d)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	switch (d->bd_bufmode) {
 	case BPF_BUFMODE_ZBUF:
@@ -577,6 +609,18 @@
 static void
 bpf_attachd(struct bpf_d *d, struct bpf_if *bp)
 {
+	int op_w;
+
+	BPF_LOCK_ASSERT();
+
+	/*
+	 * Save sysctl value to protect from sysctl change
+	 * between reads
+	 */
+	op_w = V_bpf_optimize_writers;
+
+	if (d->bd_bif != NULL)
+		bpf_detachd_locked(d);
 	/*
 	 * Point d at bp, and add d to the interface's list.
 	 * Since there are many applicaiotns using BPF for
@@ -584,11 +628,13 @@
 	 * we can delay adding d to the list of active listeners until
 	 * some filter is configured.
 	 */
+
+	BPFIF_WLOCK(bp);
+	BPFD_LOCK(d);
+
 	d->bd_bif = bp;
 
-	BPFIF_WLOCK(bp);
-
-	if (V_bpf_optimize_writers != 0) {
+	if (op_w != 0) {
 		/* Add to writers-only list */
 		LIST_INSERT_HEAD(&bp->bif_wlist, d, bd_next);
 		/*
@@ -600,16 +646,15 @@
 	} else
 		LIST_INSERT_HEAD(&bp->bif_dlist, d, bd_next);
 
+	BPFD_UNLOCK(d);
 	BPFIF_WUNLOCK(bp);
 
-	BPF_LOCK();
 	bpf_bpfd_cnt++;
-	BPF_UNLOCK();
 
 	CTR3(KTR_NET, "%s: bpf_attach called by pid %d, adding to %s list",
 	    __func__, d->bd_pid, d->bd_writer ? "writer" : "active");
 
-	if (V_bpf_optimize_writers == 0)
+	if (op_w == 0)
 		EVENTHANDLER_INVOKE(bpf_track, bp->bif_ifp, bp->bif_dlt, 1);
 }
 
@@ -622,10 +667,23 @@
 {
 	struct bpf_if *bp;
 
+	BPF_LOCK_ASSERT();
+
 	bp = d->bd_bif;
 
+	/*
+	 * Filter can be set several times without specifying interface.
+	 * Mark d as reader and exit.
+	 */
+	if (bp == NULL) {
+		BPFD_LOCK(d);
+		d->bd_writer = 0;
+		BPFD_UNLOCK(d);
+		return;
+	}
+
 	BPFIF_WLOCK(bp);
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 
 	/* Remove from writers-only list */
 	LIST_REMOVE(d, bd_next);
@@ -633,7 +691,7 @@
 	/* Mark d as reader */
 	d->bd_writer = 0;
 
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	BPFIF_WUNLOCK(bp);
 
 	CTR2(KTR_NET, "%s: upgrade required by pid %d", __func__, d->bd_pid);
@@ -647,6 +705,14 @@
 static void
 bpf_detachd(struct bpf_d *d)
 {
+	BPF_LOCK();
+	bpf_detachd_locked(d);
+	BPF_UNLOCK();
+}
+
+static void
+bpf_detachd_locked(struct bpf_d *d)
+{
 	int error;
 	struct bpf_if *bp;
 	struct ifnet *ifp;
@@ -655,9 +721,12 @@
 
 	BPF_LOCK_ASSERT();
 
-	bp = d->bd_bif;
+	/* Check if descriptor is attached */
+	if ((bp = d->bd_bif) == NULL)
+		return;
+
 	BPFIF_WLOCK(bp);
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 
 	/* Save bd_writer value */
 	error = d->bd_writer;
@@ -669,10 +738,9 @@
 
 	ifp = bp->bif_ifp;
 	d->bd_bif = NULL;
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	BPFIF_WUNLOCK(bp);
 
-	/* We're already protected by global lock. */
 	bpf_bpfd_cnt--;
 
 	/* Call event handler iff d is attached */
@@ -710,16 +778,13 @@
 {
 	struct bpf_d *d = data;
 
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	if (d->bd_state == BPF_WAITING)
 		callout_stop(&d->bd_callout);
 	d->bd_state = BPF_IDLE;
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	funsetown(&d->bd_sigio);
-	BPF_LOCK();
-	if (d->bd_bif)
-		bpf_detachd(d);
-	BPF_UNLOCK();
+	bpf_detachd(d);
 #ifdef MAC
 	mac_bpfdesc_destroy(d);
 #endif /* MAC */
@@ -739,7 +804,7 @@
 bpfopen(struct cdev *dev, int flags, int fmt, struct thread *td)
 {
 	struct bpf_d *d;
-	int error;
+	int error, size;
 
 	d = malloc(sizeof(*d), M_BPF, M_WAITOK | M_ZERO);
 	error = devfs_set_cdevpriv(d, bpf_dtor);
@@ -762,9 +827,13 @@
 	mac_bpfdesc_init(d);
 	mac_bpfdesc_create(td->td_ucred, d);
 #endif
-	rw_init(&d->bd_lock, "bpf cdev lock");
-	callout_init_rw(&d->bd_callout, &d->bd_lock, 0);
-	knlist_init_rw_reader(&d->bd_sel.si_note, &d->bd_lock);
+	mtx_init(&d->bd_lock, devtoname(dev), "bpf cdev lock", MTX_DEF);
+	callout_init_mtx(&d->bd_callout, &d->bd_lock, 0);
+	knlist_init_mtx(&d->bd_sel.si_note, &d->bd_lock);
+
+	/* Allocate default buffers */
+	size = d->bd_bufsize;
+	bpf_buffer_ioctl_sblen(d, &size);
 
 	return (0);
 }
@@ -793,10 +862,10 @@
 
 	non_block = ((ioflag & O_NONBLOCK) != 0);
 
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	BPF_PID_REFRESH_CUR(d);
 	if (d->bd_bufmode != BPF_BUFMODE_BUFFER) {
-		BPFD_WUNLOCK(d);
+		BPFD_UNLOCK(d);
 		return (EOPNOTSUPP);
 	}
 	if (d->bd_state == BPF_WAITING)
@@ -832,18 +901,18 @@
 		 * it before using it again.
 		 */
 		if (d->bd_bif == NULL) {
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 			return (ENXIO);
 		}
 
 		if (non_block) {
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 			return (EWOULDBLOCK);
 		}
-		error = rw_sleep(d, &d->bd_lock, PRINET|PCATCH,
+		error = msleep(d, &d->bd_lock, PRINET|PCATCH,
 		     "bpf", d->bd_rtout);
 		if (error == EINTR || error == ERESTART) {
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 			return (error);
 		}
 		if (error == EWOULDBLOCK) {
@@ -861,7 +930,7 @@
 				break;
 
 			if (d->bd_slen == 0) {
-				BPFD_WUNLOCK(d);
+				BPFD_UNLOCK(d);
 				return (0);
 			}
 			ROTATE_BUFFERS(d);
@@ -871,7 +940,7 @@
 	/*
 	 * At this point, we know we have something in the hold slot.
 	 */
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 
 	/*
 	 * Move data from hold buffer into user space.
@@ -884,12 +953,12 @@
 	 */
 	error = bpf_uiomove(d, d->bd_hbuf, d->bd_hlen, uio);
 
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	d->bd_fbuf = d->bd_hbuf;
 	d->bd_hbuf = NULL;
 	d->bd_hlen = 0;
 	bpf_buf_reclaimed(d);
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 
 	return (error);
 }
@@ -901,7 +970,7 @@
 bpf_wakeup(struct bpf_d *d)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 	if (d->bd_state == BPF_WAITING) {
 		callout_stop(&d->bd_callout);
 		d->bd_state = BPF_IDLE;
@@ -919,7 +988,7 @@
 {
 	struct bpf_d *d = (struct bpf_d *)arg;
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	if (callout_pending(&d->bd_callout) || !callout_active(&d->bd_callout))
 		return;
@@ -934,7 +1003,7 @@
 bpf_ready(struct bpf_d *d)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	if (!bpf_canfreebuf(d) && d->bd_hlen != 0)
 		return (1);
@@ -959,6 +1028,7 @@
 
 	BPF_PID_REFRESH_CUR(d);
 	d->bd_wcount++;
+	/* XXX: locking required */
 	if (d->bd_bif == NULL) {
 		d->bd_wdcount++;
 		return (ENXIO);
@@ -979,6 +1049,7 @@
 	bzero(&dst, sizeof(dst));
 	m = NULL;
 	hlen = 0;
+	/* XXX: bpf_movein() can sleep */
 	error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, ifp,
 	    &m, &dst, &hlen, d->bd_wfilter);
 	if (error) {
@@ -1005,11 +1076,11 @@
 
 	CURVNET_SET(ifp->if_vnet);
 #ifdef MAC
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	mac_bpfdesc_create_mbuf(d, m);
 	if (mc != NULL)
 		mac_bpfdesc_create_mbuf(d, mc);
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 #endif
 
 	error = (*ifp->if_output)(ifp, m, &dst, NULL);
@@ -1038,7 +1109,7 @@
 reset_d(struct bpf_d *d)
 {
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	if ((d->bd_hbuf != NULL) &&
 	    (d->bd_bufmode != BPF_BUFMODE_ZBUF || bpf_canfreebuf(d))) {
@@ -1105,12 +1176,12 @@
 	/*
 	 * Refresh PID associated with this descriptor.
 	 */
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	BPF_PID_REFRESH(d, td);
 	if (d->bd_state == BPF_WAITING)
 		callout_stop(&d->bd_callout);
 	d->bd_state = BPF_IDLE;
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 
 	if (d->bd_locked == 1) {
 		switch (cmd) {
@@ -1158,7 +1229,9 @@
 	case BIOCGDLTLIST32:
 	case BIOCGRTIMEOUT32:
 	case BIOCSRTIMEOUT32:
+		BPFD_LOCK(d);
 		d->bd_compat32 = 1;
+		BPFD_UNLOCK(d);
 	}
 #endif
 
@@ -1176,11 +1249,11 @@
 		{
 			int n;
 
-			BPFD_WLOCK(d);
+			BPFD_LOCK(d);
 			n = d->bd_slen;
 			if (d->bd_hbuf)
 				n += d->bd_hlen;
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 
 			*(int *)addr = n;
 			break;
@@ -1203,7 +1276,9 @@
 	 * Get buffer len [for read()].
 	 */
 	case BIOCGBLEN:
+		BPFD_LOCK(d);
 		*(u_int *)addr = d->bd_bufsize;
+		BPFD_UNLOCK(d);
 		break;
 
 	/*
@@ -1231,9 +1306,9 @@
 	 * Flush read packet buffer.
 	 */
 	case BIOCFLUSH:
-		BPFD_WLOCK(d);
+		BPFD_LOCK(d);
 		reset_d(d);
-		BPFD_WUNLOCK(d);
+		BPFD_UNLOCK(d);
 		break;
 
 	/*
@@ -1258,10 +1333,12 @@
 	 * Get current data link type.
 	 */
 	case BIOCGDLT:
+		BPF_LOCK();
 		if (d->bd_bif == NULL)
 			error = EINVAL;
 		else
 			*(u_int *)addr = d->bd_bif->bif_dlt;
+		BPF_UNLOCK();
 		break;
 
 	/*
@@ -1276,6 +1353,7 @@
 			list32 = (struct bpf_dltlist32 *)addr;
 			dltlist.bfl_len = list32->bfl_len;
 			dltlist.bfl_list = PTRIN(list32->bfl_list);
+			BPF_LOCK();
 			if (d->bd_bif == NULL)
 				error = EINVAL;
 			else {
@@ -1283,31 +1361,37 @@
 				if (error == 0)
 					list32->bfl_len = dltlist.bfl_len;
 			}
+			BPF_UNLOCK();
 			break;
 		}
 #endif
 
 	case BIOCGDLTLIST:
+		BPF_LOCK();
 		if (d->bd_bif == NULL)
 			error = EINVAL;
 		else
 			error = bpf_getdltlist(d, (struct bpf_dltlist *)addr);
+		BPF_UNLOCK();
 		break;
 
 	/*
 	 * Set data link type.
 	 */
 	case BIOCSDLT:
+		BPF_LOCK();
 		if (d->bd_bif == NULL)
 			error = EINVAL;
 		else
 			error = bpf_setdlt(d, *(u_int *)addr);
+		BPF_UNLOCK();
 		break;
 
 	/*
 	 * Get interface name.
 	 */
 	case BIOCGETIF:
+		BPF_LOCK();
 		if (d->bd_bif == NULL)
 			error = EINVAL;
 		else {
@@ -1317,13 +1401,16 @@
 			strlcpy(ifr->ifr_name, ifp->if_xname,
 			    sizeof(ifr->ifr_name));
 		}
+		BPF_UNLOCK();
 		break;
 
 	/*
 	 * Set interface.
 	 */
 	case BIOCSETIF:
+		BPF_LOCK();
 		error = bpf_setif(d, (struct ifreq *)addr);
+		BPF_UNLOCK();
 		break;
 
 	/*
@@ -1406,7 +1493,9 @@
 	 * Set immediate mode.
 	 */
 	case BIOCIMMEDIATE:
+		BPFD_LOCK(d);
 		d->bd_immediate = *(u_int *)addr;
+		BPFD_UNLOCK(d);
 		break;
 
 	case BIOCVERSION:
@@ -1422,21 +1511,27 @@
 	 * Get "header already complete" flag
 	 */
 	case BIOCGHDRCMPLT:
+		BPFD_LOCK(d);
 		*(u_int *)addr = d->bd_hdrcmplt;
+		BPFD_UNLOCK(d);
 		break;
 
 	/*
 	 * Set "header already complete" flag
 	 */
 	case BIOCSHDRCMPLT:
+		BPFD_LOCK(d);
 		d->bd_hdrcmplt = *(u_int *)addr ? 1 : 0;
+		BPFD_UNLOCK(d);
 		break;
 
 	/*
 	 * Get packet direction flag
 	 */
 	case BIOCGDIRECTION:
+		BPFD_LOCK(d);
 		*(u_int *)addr = d->bd_direction;
+		BPFD_UNLOCK(d);
 		break;
 
 	/*
@@ -1451,7 +1546,9 @@
 			case BPF_D_IN:
 			case BPF_D_INOUT:
 			case BPF_D_OUT:
+				BPFD_LOCK(d);
 				d->bd_direction = direction;
+				BPFD_UNLOCK(d);
 				break;
 			default:
 				error = EINVAL;
@@ -1463,7 +1560,9 @@
 	 * Get packet timestamp format and resolution.
 	 */
 	case BIOCGTSTAMP:
+		BPFD_LOCK(d);
 		*(u_int *)addr = d->bd_tstamp;
+		BPFD_UNLOCK(d);
 		break;
 
 	/*
@@ -1482,26 +1581,38 @@
 		break;
 
 	case BIOCFEEDBACK:
+		BPFD_LOCK(d);
 		d->bd_feedback = *(u_int *)addr;
+		BPFD_UNLOCK(d);
 		break;
 
 	case BIOCLOCK:
+		BPFD_LOCK(d);
 		d->bd_locked = 1;
+		BPFD_UNLOCK(d);
 		break;
 
 	case FIONBIO:		/* Non-blocking I/O */
 		break;
 
 	case FIOASYNC:		/* Send signal on receive packets */
+		BPFD_LOCK(d);
 		d->bd_async = *(int *)addr;
+		BPFD_UNLOCK(d);
 		break;
 
 	case FIOSETOWN:
+		/*
+		 * XXX: Add some sort of locking here?
+		 * fsetown() can sleep.
+		 */
 		error = fsetown(*(int *)addr, &d->bd_sigio);
 		break;
 
 	case FIOGETOWN:
+		BPFD_LOCK(d);
 		*(int *)addr = fgetown(&d->bd_sigio);
+		BPFD_UNLOCK(d);
 		break;
 
 	/* This is deprecated, FIOSETOWN should be used instead. */
@@ -1522,16 +1633,23 @@
 
 			if (sig >= NSIG)
 				error = EINVAL;
-			else
+			else {
+				BPFD_LOCK(d);
 				d->bd_sig = sig;
+				BPFD_UNLOCK(d);
+			}
 			break;
 		}
 	case BIOCGRSIG:
+		BPFD_LOCK(d);
 		*(u_int *)addr = d->bd_sig;
+		BPFD_UNLOCK(d);
 		break;
 
 	case BIOCGETBUFMODE:
+		BPFD_LOCK(d);
 		*(u_int *)addr = d->bd_bufmode;
+		BPFD_UNLOCK(d);
 		break;
 
 	case BIOCSETBUFMODE:
@@ -1556,15 +1674,15 @@
 			return (EINVAL);
 		}
 
-		BPFD_WLOCK(d);
+		BPFD_LOCK(d);
 		if (d->bd_sbuf != NULL || d->bd_hbuf != NULL ||
 		    d->bd_fbuf != NULL || d->bd_bif != NULL) {
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 			CURVNET_RESTORE();
 			return (EBUSY);
 		}
 		d->bd_bufmode = *(u_int *)addr;
-		BPFD_WUNLOCK(d);
+		BPFD_UNLOCK(d);
 		break;
 
 	case BIOCGETZMAX:
@@ -1586,123 +1704,130 @@
 /*
  * Set d's packet filter program to fp.  If this file already has a filter,
  * free it and replace it.  Returns EINVAL for bogus requests.
+ *
+ * Note we need global lock here to serialize bpf_setf() and bpf_setif() calls
+ * since reading d->bd_bif can't be protected by d or interface lock due to
+ * lock order.
+ *
+ * Additionally, we have to acquire interface write lock due to bpf_mtap() uses
+ * interface read lock to read all filers.
+ *
  */
 static int
 bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
 {
+#ifdef COMPAT_FREEBSD32
+	struct bpf_program fp_swab;
+	struct bpf_program32 *fp32;
+#endif
 	struct bpf_insn *fcode, *old;
-	u_int wfilter, flen, size;
 #ifdef BPF_JITTER
-	bpf_jit_filter *ofunc;
+	bpf_jit_filter *jfunc, *ofunc;
 #endif
+	size_t size;
+	u_int flen;
 	int need_upgrade;
+
 #ifdef COMPAT_FREEBSD32
-	struct bpf_program32 *fp32;
-	struct bpf_program fp_swab;
-
-	if (cmd == BIOCSETWF32 || cmd == BIOCSETF32 || cmd == BIOCSETFNR32) {
+	switch (cmd) {
+	case BIOCSETF32:
+	case BIOCSETWF32:
+	case BIOCSETFNR32:
 		fp32 = (struct bpf_program32 *)fp;
 		fp_swab.bf_len = fp32->bf_len;
 		fp_swab.bf_insns = (struct bpf_insn *)(uintptr_t)fp32->bf_insns;
 		fp = &fp_swab;
-		if (cmd == BIOCSETWF32)
+		switch (cmd) {
+		case BIOCSETF32:
+			cmd = BIOCSETF;
+			break;
+		case BIOCSETWF32:
 			cmd = BIOCSETWF;
+			break;
+		}
+		break;
 	}
 #endif
+
+	fcode = NULL;
+#ifdef BPF_JITTER
+	jfunc = ofunc = NULL;
+#endif
+	need_upgrade = 0;
+
+	/*
+	 * Check new filter validness before acquiring any locks.
+	 * Allocate memory for new filter, if needed.
+	 */
+	flen = fp->bf_len;
+	if (flen > bpf_maxinsns || (fp->bf_insns == NULL && flen != 0))
+		return (EINVAL);
+	size = flen * sizeof(*fp->bf_insns);
+	if (size > 0) {
+		/* We're setting up new filter.  Copy and check actual data. */
+		fcode = malloc(size, M_BPF, M_WAITOK);
+		if (copyin(fp->bf_insns, fcode, size) != 0 ||
+		    !bpf_validate(fcode, flen)) {
+			free(fcode, M_BPF);
+			return (EINVAL);
+		}
+#ifdef BPF_JITTER
+		/* Filter is copied inside fcode and is perfectly valid. */
+		jfunc = bpf_jitter(fcode, flen);
+#endif
+	}
+
+	BPF_LOCK();
+
+	/*
+	 * Set up new filter.
+	 * Protect filter change by interface lock.
+	 * Additionally, we are protected by global lock here.
+	 */
+	if (d->bd_bif != NULL)
+		BPFIF_WLOCK(d->bd_bif);
+	BPFD_LOCK(d);
 	if (cmd == BIOCSETWF) {
 		old = d->bd_wfilter;
-		wfilter = 1;
-#ifdef BPF_JITTER
-		ofunc = NULL;
-#endif
+		d->bd_wfilter = fcode;
 	} else {
-		wfilter = 0;
 		old = d->bd_rfilter;
+		d->bd_rfilter = fcode;
 #ifdef BPF_JITTER
 		ofunc = d->bd_bfilter;
+		d->bd_bfilter = jfunc;
 #endif
-	}
-	if (fp->bf_insns == NULL) {
-		if (fp->bf_len != 0)
-			return (EINVAL);
-		/* 
-		 * Protect filter change by interface lock, too.
-		 * The same lock order is used by bpf_detachd().
-		 */
-		BPFIF_WLOCK(d->bd_bif);
-		BPFD_WLOCK(d);
-		if (wfilter)
-			d->bd_wfilter = NULL;
-		else {
-			d->bd_rfilter = NULL;
-#ifdef BPF_JITTER
-			d->bd_bfilter = NULL;
-#endif
-			if (cmd == BIOCSETF)
-				reset_d(d);
-		}
-		BPFD_WUNLOCK(d);
-		BPFIF_WUNLOCK(d->bd_bif);
-		if (old != NULL)
-			free((caddr_t)old, M_BPF);
-#ifdef BPF_JITTER
-		if (ofunc != NULL)
-			bpf_destroy_jit_filter(ofunc);
-#endif
-		return (0);
-	}
-	flen = fp->bf_len;
-	if (flen > bpf_maxinsns)
-		return (EINVAL);
-
-	need_upgrade = 0;
-	size = flen * sizeof(*fp->bf_insns);
-	fcode = (struct bpf_insn *)malloc(size, M_BPF, M_WAITOK);
-	if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 &&
-	    bpf_validate(fcode, (int)flen)) {
-		/* 
-		 * Protect filter change by interface lock, too
-		 * The same lock order is used by bpf_detachd().
-		 */
-		BPFIF_WLOCK(d->bd_bif);
-		BPFD_WLOCK(d);
-		if (wfilter)
-			d->bd_wfilter = fcode;
-		else {
-			d->bd_rfilter = fcode;
-#ifdef BPF_JITTER
-			d->bd_bfilter = bpf_jitter(fcode, flen);
-#endif
-			if (cmd == BIOCSETF)
-				reset_d(d);
-
+		if (cmd == BIOCSETF)
+			reset_d(d);
+
+		if (fcode != NULL) {
 			/*
 			 * Do not require upgrade by first BIOCSETF
-			 * (used to set snaplen) by pcap_open_live()
+			 * (used to set snaplen) by pcap_open_live().
 			 */
-			if ((d->bd_writer != 0) && (--d->bd_writer == 0))
+			if (d->bd_writer != 0 && --d->bd_writer == 0)
 				need_upgrade = 1;
 			CTR4(KTR_NET, "%s: filter function set by pid %d, "
 			    "bd_writer counter %d, need_upgrade %d",
 			    __func__, d->bd_pid, d->bd_writer, need_upgrade);
 		}
-		BPFD_WUNLOCK(d);
+	}
+	BPFD_UNLOCK(d);
+	if (d->bd_bif != NULL)
 		BPFIF_WUNLOCK(d->bd_bif);
-		if (old != NULL)
-			free((caddr_t)old, M_BPF);
+	if (old != NULL)
+		free(old, M_BPF);
 #ifdef BPF_JITTER
-		if (ofunc != NULL)
-			bpf_destroy_jit_filter(ofunc);
+	if (ofunc != NULL)
+		bpf_destroy_jit_filter(ofunc);
 #endif
 
-		/* Move d to active readers list */
-		if (need_upgrade != 0)
-			bpf_upgraded(d);
-
-		return (0);
-	}
-	free((caddr_t)fcode, M_BPF);
-	return (EINVAL);
+	/* Move d to active readers list. */
+	if (need_upgrade)
+		bpf_upgraded(d);
+
+	BPF_UNLOCK();
+	return (0);
 }
 
 /*
@@ -1716,28 +1841,30 @@
 	struct bpf_if *bp;
 	struct ifnet *theywant;
 
+	BPF_LOCK_ASSERT();
+
 	theywant = ifunit(ifr->ifr_name);
 	if (theywant == NULL || theywant->if_bpf == NULL)
 		return (ENXIO);
 
 	bp = theywant->if_bpf;
 
+	/* Check if interface is not being detached from BPF */
+	BPFIF_RLOCK(bp);
+	if (bp->flags & BPFIF_FLAG_DYING) {
+		BPFIF_RUNLOCK(bp);
+		return (ENXIO);
+	}
+	BPFIF_RUNLOCK(bp);
+
 	/*
 	 * Behavior here depends on the buffering model.  If we're using
 	 * kernel memory buffers, then we can allocate them here.  If we're
 	 * using zero-copy, then the user process must have registered
 	 * buffers by the time we get here.  If not, return an error.
-	 *
-	 * XXXRW: There are locking issues here with multi-threaded use: what
-	 * if two threads try to set the interface at once?
 	 */
 	switch (d->bd_bufmode) {
 	case BPF_BUFMODE_BUFFER:
-		if (d->bd_sbuf == NULL)
-			bpf_buffer_alloc(d);
-		KASSERT(d->bd_sbuf != NULL, ("bpf_setif: bd_sbuf NULL"));
-		break;
-
 	case BPF_BUFMODE_ZBUF:
 		if (d->bd_sbuf == NULL)
 			return (EINVAL);
@@ -1746,18 +1873,11 @@
 	default:
 		panic("bpf_setif: bufmode %d", d->bd_bufmode);
 	}
-	if (bp != d->bd_bif) {
-		if (d->bd_bif)
-			/*
-			 * Detach if attached to something else.
-			 */
-			bpf_detachd(d);
-
+	if (bp != d->bd_bif)
 		bpf_attachd(d, bp);
-	}
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	reset_d(d);
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	return (0);
 }
 
@@ -1781,7 +1901,7 @@
 	 * Refresh PID associated with this descriptor.
 	 */
 	revents = events & (POLLOUT | POLLWRNORM);
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	BPF_PID_REFRESH(d, td);
 	if (events & (POLLIN | POLLRDNORM)) {
 		if (bpf_ready(d))
@@ -1796,7 +1916,7 @@
 			}
 		}
 	}
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	return (revents);
 }
 
@@ -1816,12 +1936,12 @@
 	/*
 	 * Refresh PID associated with this descriptor.
 	 */
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	BPF_PID_REFRESH_CUR(d);
 	kn->kn_fop = &bpfread_filtops;
 	kn->kn_hook = d;
 	knlist_add(&d->bd_sel.si_note, kn, 1);
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 
 	return (0);
 }
@@ -1840,7 +1960,7 @@
 	struct bpf_d *d = (struct bpf_d *)kn->kn_hook;
 	int ready;
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 	ready = bpf_ready(d);
 	if (ready) {
 		kn->kn_data = d->bd_slen;
@@ -1946,7 +2066,7 @@
 			/*
 			 * Filter matches. Let's to acquire write lock.
 			 */
-			BPFD_WLOCK(d);
+			BPFD_LOCK(d);
 
 			d->bd_fcount++;
 			if (gottime < bpf_ts_quality(d->bd_tstamp))
@@ -1956,7 +2076,7 @@
 #endif
 				catchpacket(d, pkt, pktlen, slen,
 				    bpf_append_bytes, &bt);
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 		}
 	}
 	BPFIF_RUNLOCK(bp);
@@ -2005,7 +2125,7 @@
 #endif
 		slen = bpf_filter(d->bd_rfilter, (u_char *)m, pktlen, 0);
 		if (slen != 0) {
-			BPFD_WLOCK(d);
+			BPFD_LOCK(d);
 
 			d->bd_fcount++;
 			if (gottime < bpf_ts_quality(d->bd_tstamp))
@@ -2015,7 +2135,7 @@
 #endif
 				catchpacket(d, (u_char *)m, pktlen, slen,
 				    bpf_append_mbuf, &bt);
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 		}
 	}
 	BPFIF_RUNLOCK(bp);
@@ -2061,7 +2181,7 @@
 		++d->bd_rcount;
 		slen = bpf_filter(d->bd_rfilter, (u_char *)&mb, pktlen, 0);
 		if (slen != 0) {
-			BPFD_WLOCK(d);
+			BPFD_LOCK(d);
 
 			d->bd_fcount++;
 			if (gottime < bpf_ts_quality(d->bd_tstamp))
@@ -2071,7 +2191,7 @@
 #endif
 				catchpacket(d, (u_char *)&mb, pktlen, slen,
 				    bpf_append_mbuf, &bt);
-			BPFD_WUNLOCK(d);
+			BPFD_UNLOCK(d);
 		}
 	}
 	BPFIF_RUNLOCK(bp);
@@ -2166,7 +2286,7 @@
 	int do_timestamp;
 	int tstype;
 
-	BPFD_WLOCK_ASSERT(d);
+	BPFD_LOCK_ASSERT(d);
 
 	/*
 	 * Detect whether user space has released a buffer back to us, and if
@@ -2313,7 +2433,7 @@
 	}
 	if (d->bd_wfilter != NULL)
 		free((caddr_t)d->bd_wfilter, M_BPF);
-	rw_destroy(&d->bd_lock);
+	mtx_destroy(&d->bd_lock);
 }
 
 /*
@@ -2361,10 +2481,9 @@
 }
 
 /*
- * Detach bpf from an interface.  This involves detaching each descriptor
- * associated with the interface, and leaving bd_bif NULL.  Notify each
- * descriptor as it's detached so that any sleepers wake up and get
- * ENXIO.
+ * Detach bpf from an interface. This involves detaching each descriptor
+ * associated with the interface. Notify each descriptor as it's detached
+ * so that any sleepers wake up and get ENXIO.
  */
 void
 bpfdetach(struct ifnet *ifp)
@@ -2377,31 +2496,45 @@
 	ndetached = 0;
 #endif
 
+	BPF_LOCK();
 	/* Find all bpf_if struct's which reference ifp and detach them. */
 	do {
-		BPF_LOCK();
 		LIST_FOREACH(bp, &bpf_iflist, bif_next) {
 			if (ifp == bp->bif_ifp)
 				break;
 		}
 		if (bp != NULL)
 			LIST_REMOVE(bp, bif_next);
-		BPF_UNLOCK();
 
 		if (bp != NULL) {
 #ifdef INVARIANTS
 			ndetached++;
 #endif
 			while ((d = LIST_FIRST(&bp->bif_dlist)) != NULL) {
-				bpf_detachd(d);
-				BPFD_WLOCK(d);
+				bpf_detachd_locked(d);
+				BPFD_LOCK(d);
 				bpf_wakeup(d);
-				BPFD_WUNLOCK(d);
+				BPFD_UNLOCK(d);
 			}
-			rw_destroy(&bp->bif_lock);
-			free(bp, M_BPF);
+			/* Free writer-only descriptors */
+			while ((d = LIST_FIRST(&bp->bif_wlist)) != NULL) {
+				bpf_detachd_locked(d);
+				BPFD_LOCK(d);
+				bpf_wakeup(d);
+				BPFD_UNLOCK(d);
+			}
+
+			/*
+			 * Delay freing bp till interface is detached
+			 * and all routes through this interface are removed.
+			 * Mark bp as detached to restrict new consumers.
+			 */
+			BPFIF_WLOCK(bp);
+			bp->flags |= BPFIF_FLAG_DYING;
+			BPFIF_WUNLOCK(bp);
 		}
 	} while (bp != NULL);
+	BPF_UNLOCK();
 
 #ifdef INVARIANTS
 	if (ndetached == 0)
@@ -2410,6 +2543,37 @@
 }
 
 /*
+ * Interface departure handler.
+ * Note departure event does not guarantee interface is going down.
+ */
+static void
+bpf_ifdetach(void *arg __unused, struct ifnet *ifp)
+{
+	struct bpf_if *bp;
+
+	BPF_LOCK();
+	if ((bp = ifp->if_bpf) == NULL) {
+		BPF_UNLOCK();
+		return;
+	}
+
+	/* Check if bpfdetach() was called previously */
+	if ((bp->flags & BPFIF_FLAG_DYING) == 0) {
+		BPF_UNLOCK();
+		return;
+	}
+
+	CTR3(KTR_NET, "%s: freing BPF instance %p for interface %p",
+	    __func__, bp, ifp);
+
+	ifp->if_bpf = NULL;
+	BPF_UNLOCK();
+
+	rw_destroy(&bp->bif_lock);
+	free(bp, M_BPF);
+}
+
+/*
  * Get a list of available data link type of the interface.
  */
 static int
@@ -2419,24 +2583,22 @@
 	struct ifnet *ifp;
 	struct bpf_if *bp;
 
+	BPF_LOCK_ASSERT();
+
 	ifp = d->bd_bif->bif_ifp;
 	n = 0;
 	error = 0;
-	BPF_LOCK();
 	LIST_FOREACH(bp, &bpf_iflist, bif_next) {
 		if (bp->bif_ifp != ifp)
 			continue;
 		if (bfl->bfl_list != NULL) {
-			if (n >= bfl->bfl_len) {
-				BPF_UNLOCK();
+			if (n >= bfl->bfl_len)
 				return (ENOMEM);
-			}
 			error = copyout(&bp->bif_dlt,
 			    bfl->bfl_list + n, sizeof(u_int));
 		}
 		n++;
 	}
-	BPF_UNLOCK();
 	bfl->bfl_len = n;
 	return (error);
 }
@@ -2451,22 +2613,23 @@
 	struct ifnet *ifp;
 	struct bpf_if *bp;
 
+	BPF_LOCK_ASSERT();
+
 	if (d->bd_bif->bif_dlt == dlt)
 		return (0);
 	ifp = d->bd_bif->bif_ifp;
-	BPF_LOCK();
+
 	LIST_FOREACH(bp, &bpf_iflist, bif_next) {
 		if (bp->bif_ifp == ifp && bp->bif_dlt == dlt)
 			break;
 	}
-	BPF_UNLOCK();
+
 	if (bp != NULL) {
 		opromisc = d->bd_promisc;
-		bpf_detachd(d);
 		bpf_attachd(d, bp);
-		BPFD_WLOCK(d);
+		BPFD_LOCK(d);
 		reset_d(d);
-		BPFD_WUNLOCK(d);
+		BPFD_UNLOCK(d);
 		if (opromisc) {
 			error = ifpromisc(bp->bif_ifp, 1);
 			if (error)
@@ -2491,6 +2654,11 @@
 	dev = make_dev(&bpf_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "bpf");
 	/* For compatibility */
 	make_dev_alias(dev, "bpf0");
+
+	/* Register interface departure handler */
+	bpf_ifdetach_cookie = EVENTHANDLER_REGISTER(
+		    ifnet_departure_event, bpf_ifdetach, NULL,
+		    EVENTHANDLER_PRI_ANY);
 }
 
 /*
@@ -2508,20 +2676,23 @@
 	LIST_FOREACH(bp, &bpf_iflist, bif_next) {
 		BPFIF_RLOCK(bp);
 		LIST_FOREACH(bd, &bp->bif_dlist, bd_next) {
-			BPFD_WLOCK(bd);
+			BPFD_LOCK(bd);
 			bd->bd_rcount = 0;
 			bd->bd_dcount = 0;
 			bd->bd_fcount = 0;
 			bd->bd_wcount = 0;
 			bd->bd_wfcount = 0;
 			bd->bd_zcopy = 0;
-			BPFD_WUNLOCK(bd);
+			BPFD_UNLOCK(bd);
 		}
 		BPFIF_RUNLOCK(bp);
 	}
 	BPF_UNLOCK();
 }
 
+/*
+ * Fill filter statistics
+ */
 static void
 bpfstats_fill_xbpf(struct xbpf_d *d, struct bpf_d *bd)
 {
@@ -2529,6 +2700,7 @@
 	bzero(d, sizeof(*d));
 	BPFD_LOCK_ASSERT(bd);
 	d->bd_structsize = sizeof(*d);
+	/* XXX: reading should be protected by global lock */
 	d->bd_immediate = bd->bd_immediate;
 	d->bd_promisc = bd->bd_promisc;
 	d->bd_hdrcmplt = bd->bd_hdrcmplt;
@@ -2553,6 +2725,9 @@
 	d->bd_bufmode = bd->bd_bufmode;
 }
 
+/*
+ * Handle `netstat -B' stats request
+ */
 static int
 bpf_stats_sysctl(SYSCTL_HANDLER_ARGS)
 {
@@ -2602,15 +2777,15 @@
 		/* Send writers-only first */
 		LIST_FOREACH(bd, &bp->bif_wlist, bd_next) {
 			xbd = &xbdbuf[index++];
-			BPFD_RLOCK(bd);
+			BPFD_LOCK(bd);
 			bpfstats_fill_xbpf(xbd, bd);
-			BPFD_RUNLOCK(bd);
+			BPFD_UNLOCK(bd);
 		}
 		LIST_FOREACH(bd, &bp->bif_dlist, bd_next) {
 			xbd = &xbdbuf[index++];
-			BPFD_RLOCK(bd);
+			BPFD_LOCK(bd);
 			bpfstats_fill_xbpf(xbd, bd);
-			BPFD_RUNLOCK(bd);
+			BPFD_UNLOCK(bd);
 		}
 		BPFIF_RUNLOCK(bp);
 	}
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/bpf.h
--- a/head/sys/net/bpf.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/bpf.h	Wed Jul 25 16:47:10 2012 +0300
@@ -34,7 +34,7 @@
  *      @(#)bpf.h	8.1 (Berkeley) 6/10/93
  *	@(#)bpf.h	1.34 (LBL)     6/16/96
  *
- * $FreeBSD: head/sys/net/bpf.h 233938 2012-04-06 06:55:21Z melifaro $
+ * $FreeBSD: head/sys/net/bpf.h 235745 2012-05-21 22:17:29Z melifaro $
  */
 
 #ifndef _NET_BPF_H_
@@ -279,6 +279,24 @@
  */
 #define DLT_SYMANTEC_FIREWALL	99
 
+/*
+ * Values between 100 and 103 are used in capture file headers as
+ * link-layer header type LINKTYPE_ values corresponding to DLT_ types
+ * that differ between platforms; don't use those values for new DLT_
+ * new types.
+ */
+
+/*
+ * Values starting with 104 are used for newly-assigned link-layer
+ * header type values; for those link-layer header types, the DLT_
+ * value returned by pcap_datalink() and passed to pcap_open_dead(),
+ * and the LINKTYPE_ value that appears in capture files, are the
+ * same.
+ *
+ * DLT_MATCHING_MIN is the lowest such value; DLT_MATCHING_MAX is
+ * the highest such value.
+ */
+#define DLT_MATCHING_MIN	104
 
 /*
  * This value was defined by libpcap 0.5; platforms that have defined
@@ -978,8 +996,110 @@
  * Raw IPv4/IPv6; different from DLT_RAW in that the DLT_ value specifies
  * whether it's v4 or v6.  Requested by Darren Reed <Darren.Reed at Sun.COM>.
  */
-#define	DLT_IPV4			228
-#define	DLT_IPV6			229
+#define DLT_IPV4		228
+#define DLT_IPV6		229
+
+/*
+ * IEEE 802.15.4, exactly as it appears in the spec (no padding, no
+ * nothing), and with no FCS at the end of the frame; requested by
+ * Jon Smirl <jonsmirl at gmail.com>.
+ */
+#define DLT_IEEE802_15_4_NOFCS	230
+
+/*
+ * Raw D-Bus:
+ *
+ *	http://www.freedesktop.org/wiki/Software/dbus
+ *
+ * messages:
+ *
+ *	http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
+ *
+ * starting with the endianness flag, followed by the message type, etc.,
+ * but without the authentication handshake before the message sequence:
+ *
+ *	http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
+ *
+ * Requested by Martin Vidner <martin at vidner.net>.
+ */
+#define DLT_DBUS		231
+
+/*
+ * Juniper-private data link type, as per request from
+ * Hannes Gredler <hannes at juniper.net>.
+ */
+#define DLT_JUNIPER_VS			232
+#define DLT_JUNIPER_SRX_E2E		233
+#define DLT_JUNIPER_FIBRECHANNEL	234
+
+/*
+ * DVB-CI (DVB Common Interface for communication between a PC Card
+ * module and a DVB receiver).  See
+ *
+ *	http://www.kaiser.cx/pcap-dvbci.html
+ *
+ * for the specification.
+ *
+ * Requested by Martin Kaiser <martin at kaiser.cx>.
+ */
+#define DLT_DVB_CI		235
+
+/*
+ * Variant of 3GPP TS 27.010 multiplexing protocol (similar to, but
+ * *not* the same as, 27.010).  Requested by Hans-Christoph Schemmel
+ * <hans-christoph.schemmel at cinterion.com>.
+ */
+#define DLT_MUX27010		236
+
+/*
+ * STANAG 5066 D_PDUs.  Requested by M. Baris Demiray
+ * <barisdemiray at gmail.com>.
+ */
+#define DLT_STANAG_5066_D_PDU	237
+
+/*
+ * Juniper-private data link type, as per request from
+ * Hannes Gredler <hannes at juniper.net>.
+ */
+#define DLT_JUNIPER_ATM_CEMIC	238
+
+/*
+ * NetFilter LOG messages 
+ * (payload of netlink NFNL_SUBSYS_ULOG/NFULNL_MSG_PACKET packets)
+ *
+ * Requested by Jakub Zawadzki <darkjames-ws at darkjames.pl>
+ */
+#define DLT_NFLOG		239
+
+/*
+ * Hilscher Gesellschaft fuer Systemautomation mbH link-layer type
+ * for Ethernet packets with a 4-byte pseudo-header and always
+ * with the payload including the FCS, as supplied by their
+ * netANALYZER hardware and software.
+ *
+ * Requested by Holger P. Frommer <HPfrommer at hilscher.com>
+ */
+#define DLT_NETANALYZER		240
+
+/*
+ * Hilscher Gesellschaft fuer Systemautomation mbH link-layer type
+ * for Ethernet packets with a 4-byte pseudo-header and FCS and
+ * with the Ethernet header preceded by 7 bytes of preamble and
+ * 1 byte of SFD, as supplied by their netANALYZER hardware and
+ * software.
+ *
+ * Requested by Holger P. Frommer <HPfrommer at hilscher.com>
+ */
+#define DLT_NETANALYZER_TRANSPARENT	241
+
+/*
+ * IP-over-Infiniband, as specified by RFC 4391.
+ *
+ * Requested by Petr Sumbera <petr.sumbera at oracle.com>.
+ */
+#define DLT_IPOIB		242
+
+#define DLT_MATCHING_MAX	242	/* highest value in the "matching" range */
 
 /*
  * DLT and savefile link type values are split into a class and
@@ -1105,6 +1225,7 @@
 	struct ifnet *bif_ifp;		/* corresponding interface */
 	struct rwlock bif_lock;		/* interface lock */
 	LIST_HEAD(, bpf_d)	bif_wlist;	/* writer-only list */
+	int flags;			/* Interface flags */
 #endif
 };
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/bpf_buffer.c
--- a/head/sys/net/bpf_buffer.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/bpf_buffer.c	Wed Jul 25 16:47:10 2012 +0300
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/bpf_buffer.c 233937 2012-04-06 06:53:58Z melifaro $");
+__FBSDID("$FreeBSD: head/sys/net/bpf_buffer.c 235746 2012-05-21 22:19:19Z melifaro $");
 
 #include "opt_bpf.h"
 
@@ -93,21 +93,6 @@
 SYSCTL_INT(_net_bpf, OID_AUTO, maxbufsize, CTLFLAG_RW,
     &bpf_maxbufsize, 0, "Maximum capture buffer in bytes");
 
-void
-bpf_buffer_alloc(struct bpf_d *d)
-{
-
-	KASSERT(d->bd_fbuf == NULL, ("bpf_buffer_alloc: bd_fbuf != NULL"));
-	KASSERT(d->bd_sbuf == NULL, ("bpf_buffer_alloc: bd_sbuf != NULL"));
-	KASSERT(d->bd_hbuf == NULL, ("bpf_buffer_alloc: bd_hbuf != NULL"));
-
-	d->bd_fbuf = (caddr_t)malloc(d->bd_bufsize, M_BPF, M_WAITOK);
-	d->bd_sbuf = (caddr_t)malloc(d->bd_bufsize, M_BPF, M_WAITOK);
-	d->bd_hbuf = NULL;
-	d->bd_slen = 0;
-	d->bd_hlen = 0;
-}
-
 /*
  * Simple data copy to the current kernel buffer.
  */
@@ -183,19 +168,43 @@
 bpf_buffer_ioctl_sblen(struct bpf_d *d, u_int *i)
 {
 	u_int size;
+	caddr_t fbuf, sbuf;
 
-	BPFD_WLOCK(d);
-	if (d->bd_bif != NULL) {
-		BPFD_WUNLOCK(d);
-		return (EINVAL);
-	}
 	size = *i;
 	if (size > bpf_maxbufsize)
 		*i = size = bpf_maxbufsize;
 	else if (size < BPF_MINBUFSIZE)
 		*i = size = BPF_MINBUFSIZE;
+
+	/* Allocate buffers immediately */
+	fbuf = (caddr_t)malloc(size, M_BPF, M_WAITOK);
+	sbuf = (caddr_t)malloc(size, M_BPF, M_WAITOK);
+
+	BPFD_LOCK(d);
+	if (d->bd_bif != NULL) {
+		/* Interface already attached, unable to change buffers */
+		BPFD_UNLOCK(d);
+		free(fbuf, M_BPF);
+		free(sbuf, M_BPF);
+		return (EINVAL);
+	}
+
+	/* Free old buffers if set */
+	if (d->bd_fbuf != NULL)
+		free(d->bd_fbuf, M_BPF);
+	if (d->bd_sbuf != NULL)
+		free(d->bd_sbuf, M_BPF);
+
+	/* Fill in new data */
 	d->bd_bufsize = size;
-	BPFD_WUNLOCK(d);
+	d->bd_fbuf = fbuf;
+	d->bd_sbuf = sbuf;
+
+	d->bd_hbuf = NULL;
+	d->bd_slen = 0;
+	d->bd_hlen = 0;
+
+	BPFD_UNLOCK(d);
 	return (0);
 }
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/bpf_buffer.h
--- a/head/sys/net/bpf_buffer.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/bpf_buffer.h	Wed Jul 25 16:47:10 2012 +0300
@@ -26,7 +26,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: head/sys/net/bpf_buffer.h 230108 2012-01-14 17:07:52Z eadler $
+ * $FreeBSD: head/sys/net/bpf_buffer.h 235746 2012-05-21 22:19:19Z melifaro $
  */
 
 #ifndef _NET_BPF_BUFFER_H_
@@ -36,7 +36,6 @@
 #error "no user-serviceable parts inside"
 #endif
 
-void	bpf_buffer_alloc(struct bpf_d *d);
 void	bpf_buffer_append_bytes(struct bpf_d *d, caddr_t buf, u_int offset,
 	    void *src, u_int len);
 void	bpf_buffer_append_mbuf(struct bpf_d *d, caddr_t buf, u_int offset,
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/bpf_zerocopy.c
--- a/head/sys/net/bpf_zerocopy.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/bpf_zerocopy.c	Wed Jul 25 16:47:10 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/bpf_zerocopy.c 233937 2012-04-06 06:53:58Z melifaro $");
+__FBSDID("$FreeBSD: head/sys/net/bpf_zerocopy.c 235745 2012-05-21 22:17:29Z melifaro $");
 
 #include "opt_bpf.h"
 
@@ -515,14 +515,14 @@
 	struct zbuf *bzh;
 
 	bzero(bz, sizeof(*bz));
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	if (d->bd_hbuf == NULL && d->bd_slen != 0) {
 		ROTATE_BUFFERS(d);
 		bzh = (struct zbuf *)d->bd_hbuf;
 		bz->bz_bufa = (void *)bzh->zb_uaddr;
 		bz->bz_buflen = d->bd_hlen;
 	}
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	return (0);
 }
 
@@ -570,10 +570,10 @@
 	 * We only allow buffers to be installed once, so atomically check
 	 * that no buffers are currently installed and install new buffers.
 	 */
-	BPFD_WLOCK(d);
+	BPFD_LOCK(d);
 	if (d->bd_hbuf != NULL || d->bd_sbuf != NULL || d->bd_fbuf != NULL ||
 	    d->bd_bif != NULL) {
-		BPFD_WUNLOCK(d);
+		BPFD_UNLOCK(d);
 		zbuf_free(zba);
 		zbuf_free(zbb);
 		return (EINVAL);
@@ -593,6 +593,6 @@
 	 * shared management region.
 	 */
 	d->bd_bufsize = bz->bz_buflen - sizeof(struct bpf_zbuf_header);
-	BPFD_WUNLOCK(d);
+	BPFD_UNLOCK(d);
 	return (0);
 }
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/bpfdesc.h
--- a/head/sys/net/bpfdesc.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/bpfdesc.h	Wed Jul 25 16:47:10 2012 +0300
@@ -33,7 +33,7 @@
  *
  *      @(#)bpfdesc.h	8.1 (Berkeley) 6/10/93
  *
- * $FreeBSD: head/sys/net/bpfdesc.h 233938 2012-04-06 06:55:21Z melifaro $
+ * $FreeBSD: head/sys/net/bpfdesc.h 235745 2012-05-21 22:17:29Z melifaro $
  */
 
 #ifndef _NET_BPFDESC_H_
@@ -88,7 +88,7 @@
 	int		bd_sig;		/* signal to send upon packet reception */
 	struct sigio *	bd_sigio;	/* information for async I/O */
 	struct selinfo	bd_sel;		/* bsd select info */
-	struct rwlock	bd_lock;	/* per-descriptor lock */
+	struct mtx	bd_lock;	/* per-descriptor lock */
 	struct callout	bd_callout;	/* for BPF timeouts with select */
 	struct label	*bd_label;	/* MAC label for descriptor */
 	u_int64_t	bd_fcount;	/* number of packets which matched filter */
@@ -107,12 +107,9 @@
 #define BPF_WAITING	1		/* waiting for read timeout in select */
 #define BPF_TIMED_OUT	2		/* read timeout has expired in select */
 
-#define BPFD_RLOCK(bd)		rw_rlock(&(bd)->bd_lock)
-#define BPFD_RUNLOCK(bd)	rw_runlock(&(bd)->bd_lock)
-#define BPFD_WLOCK(bd)		rw_wlock(&(bd)->bd_lock)
-#define BPFD_WUNLOCK(bd)	rw_wunlock(&(bd)->bd_lock)
-#define BPFD_WLOCK_ASSERT(bd)	rw_assert(&(bd)->bd_lock, RA_WLOCKED)
-#define BPFD_LOCK_ASSERT(bd)	rw_assert(&(bd)->bd_lock, RA_LOCKED)
+#define BPFD_LOCK(bd)		mtx_lock(&(bd)->bd_lock)
+#define BPFD_UNLOCK(bd)		mtx_unlock(&(bd)->bd_lock)
+#define BPFD_LOCK_ASSERT(bd)	mtx_assert(&(bd)->bd_lock, MA_OWNED)
 
 #define BPF_PID_REFRESH(bd, td)	(bd)->bd_pid = (td)->td_proc->p_pid
 #define BPF_PID_REFRESH_CUR(bd)	(bd)->bd_pid = curthread->td_proc->p_pid
@@ -159,4 +156,6 @@
 #define BPFIF_WLOCK(bif)	rw_wlock(&(bif)->bif_lock)
 #define BPFIF_WUNLOCK(bif)	rw_wunlock(&(bif)->bif_lock)
 
+#define BPFIF_FLAG_DYING	1	/* Reject new bpf consumers */
+
 #endif
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/flowtable.c
--- a/head/sys/net/flowtable.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/flowtable.c	Wed Jul 25 16:47:10 2012 +0300
@@ -34,7 +34,7 @@
 #include "opt_inet6.h"
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/flowtable.c 231852 2012-02-17 02:39:58Z bz $");
+__FBSDID("$FreeBSD: head/sys/net/flowtable.c 238092 2012-07-04 07:37:53Z glebius $");
 
 #include <sys/param.h>  
 #include <sys/types.h>
@@ -619,6 +619,7 @@
 	sin->sin_addr.s_addr = hashkey[2];
 	ro->ro_rt = __DEVOLATILE(struct rtentry *, fle->f_rt);
 	ro->ro_lle = __DEVOLATILE(struct llentry *, fle->f_lle);
+	ro->ro_flags |= RT_NORTREF;
 }
 #endif /* INET */
 
@@ -826,7 +827,7 @@
 	memcpy(&sin6->sin6_addr, &hashkey[5], sizeof (struct in6_addr));
 	ro->ro_rt = __DEVOLATILE(struct rtentry *, fle->f_rt);
 	ro->ro_lle = __DEVOLATILE(struct llentry *, fle->f_lle);
-
+	ro->ro_flags |= RT_NORTREF;
 }
 #endif /* INET6 */
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/ieee8023ad_lacp.c
--- a/head/sys/net/ieee8023ad_lacp.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/ieee8023ad_lacp.c	Wed Jul 25 16:47:10 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/ieee8023ad_lacp.c 232629 2012-03-06 22:58:13Z thompsa $");
+__FBSDID("$FreeBSD: head/sys/net/ieee8023ad_lacp.c 236062 2012-05-26 08:09:01Z thompsa $");
 
 #include <sys/param.h>
 #include <sys/callout.h>
@@ -38,6 +38,7 @@
 #include <sys/kernel.h> /* hz */
 #include <sys/socket.h> /* for net/if.h */
 #include <sys/sockio.h>
+#include <sys/sysctl.h>
 #include <machine/stdarg.h>
 #include <sys/lock.h>
 #include <sys/rwlock.h>
@@ -168,7 +169,8 @@
 static int	lacp_xmit_lacpdu(struct lacp_port *);
 static int	lacp_xmit_marker(struct lacp_port *);
 
-#if defined(LACP_DEBUG)
+/* Debugging */
+
 static void	lacp_dump_lacpdu(const struct lacpdu *);
 static const char *lacp_format_partner(const struct lacp_peerinfo *, char *,
 		    size_t);
@@ -184,10 +186,14 @@
 		    size_t);
 static void	lacp_dprintf(const struct lacp_port *, const char *, ...)
 		    __attribute__((__format__(__printf__, 2, 3)));
-#define	LACP_DPRINTF(a)	lacp_dprintf a
-#else
-#define LACP_DPRINTF(a) /* nothing */
-#endif
+
+static int lacp_debug = 0;
+SYSCTL_INT(_net, OID_AUTO, lacp_debug, CTLFLAG_RW | CTLFLAG_TUN,
+    &lacp_debug, 0, "Enable LACP debug logging (1=debug, 2=trace)");
+TUNABLE_INT("net.lacp_debug", &lacp_debug);
+
+#define LACP_DPRINTF(a) if (lacp_debug > 0) { lacp_dprintf a ; }
+#define LACP_TRACE(a) if (lacp_debug > 1) { lacp_dprintf(a,"%s\n",__func__); }
 
 /*
  * partner administration variables.
@@ -290,10 +296,10 @@
 		goto bad;
 	}
 
-#if defined(LACP_DEBUG)
-	LACP_DPRINTF((lp, "lacpdu receive\n"));
-	lacp_dump_lacpdu(du);
-#endif /* defined(LACP_DEBUG) */
+        if (lacp_debug > 0) {
+		lacp_dprintf(lp, "lacpdu receive\n");
+		lacp_dump_lacpdu(du);
+	}
 
 	LACP_LOCK(lsc);
 	lacp_sm_rx(lp, du);
@@ -370,10 +376,10 @@
 	    sizeof(du->ldu_collector));
 	du->ldu_collector.lci_maxdelay = 0;
 
-#if defined(LACP_DEBUG)
-	LACP_DPRINTF((lp, "lacpdu transmit\n"));
-	lacp_dump_lacpdu(du);
-#endif /* defined(LACP_DEBUG) */
+	if (lacp_debug > 0) {
+		lacp_dprintf(lp, "lacpdu transmit\n");
+		lacp_dump_lacpdu(du);
+	}
 
 	m->m_flags |= M_MCAST;
 
@@ -647,9 +653,7 @@
 {
 	struct lacp_aggregator *la = lp->lp_aggregator;
 	struct lacp_softc *lsc = lp->lp_lsc;
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif /* defined(LACP_DEBUG) */
 
 	LACP_LOCK_ASSERT(lsc);
 
@@ -684,9 +688,7 @@
 {
 	struct lacp_aggregator *la = lp->lp_aggregator;
 	struct lacp_softc *lsc = lp->lp_lsc;
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif /* defined(LACP_DEBUG) */
 
 	LACP_LOCK_ASSERT(lsc);
 
@@ -720,7 +722,8 @@
 
 	LACP_LOCK_ASSERT(lsc);
 
-	LACP_DPRINTF((NULL, "%s\n", __func__));
+	LACP_TRACE(NULL);
+
 	lsc->lsc_suppress_distributing = FALSE;
 }
 
@@ -838,7 +841,8 @@
 		return;
 	}
 
-	LACP_DPRINTF((NULL, "%s\n", __func__));
+	LACP_TRACE(NULL);
+
 	lsc->lsc_suppress_distributing = TRUE;
 
 	/* send a marker frame down each port to verify the queues are empty */
@@ -908,11 +912,9 @@
 	struct lacp_aggregator *la;
 	struct lacp_aggregator *best_la = NULL;
 	uint64_t best_speed = 0;
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif /* defined(LACP_DEBUG) */
 
-	LACP_DPRINTF((NULL, "%s:\n", __func__));
+	LACP_TRACE(NULL);
 
 	TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) {
 		uint64_t speed;
@@ -946,7 +948,6 @@
 	KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports),
 	    ("invalid aggregator list"));
 
-#if defined(LACP_DEBUG)
 	if (lsc->lsc_active_aggregator != best_la) {
 		LACP_DPRINTF((NULL, "active aggregator changed\n"));
 		LACP_DPRINTF((NULL, "old %s\n",
@@ -957,7 +958,6 @@
 	}
 	LACP_DPRINTF((NULL, "new %s\n",
 	    lacp_format_lagid_aggregator(best_la, buf, sizeof(buf))));
-#endif /* defined(LACP_DEBUG) */
 
 	if (lsc->lsc_active_aggregator != best_la) {
 		sc->sc_ifp->if_baudrate = best_speed;
@@ -1040,9 +1040,7 @@
 static void
 lacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la)
 {
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif
 
 	LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n",
 	    __func__,
@@ -1058,9 +1056,7 @@
 static void
 lacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la)
 {
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif
 
 	LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n",
 	    __func__,
@@ -1195,9 +1191,7 @@
 {
 	struct lacp_softc *lsc = lp->lp_lsc;
 	struct lacp_aggregator *la;
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif
 
 	if (lp->lp_aggregator) {
 		return;
@@ -1278,7 +1272,8 @@
 	enum lacp_selected selected = lp->lp_selected;
 	struct lacp_aggregator *la;
 
-	/* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */
+	if (lacp_debug > 1)
+		lacp_dprintf(lp, "%s: state %d\n", __func__, lp->lp_mux_state);
 
 re_eval:
 	la = lp->lp_aggregator;
@@ -1387,9 +1382,7 @@
 lacp_sm_mux_timer(struct lacp_port *lp)
 {
 	struct lacp_aggregator *la = lp->lp_aggregator;
-#if defined(LACP_DEBUG)
 	char buf[LACP_LAGIDSTR_MAX+1];
-#endif
 
 	KASSERT(la->la_pending > 0, ("no pending event"));
 
@@ -1537,11 +1530,9 @@
 {
 	boolean_t active;
 	uint8_t oldpstate;
-#if defined(LACP_DEBUG)
 	char buf[LACP_STATESTR_MAX+1];
-#endif
 
-	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
+	LACP_TRACE(lp);
 
 	oldpstate = lp->lp_partner.lip_state;
 
@@ -1576,7 +1567,8 @@
 static void
 lacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du)
 {
-	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
+
+	LACP_TRACE(lp);
 
 	if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) ||
 	    !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state,
@@ -1591,7 +1583,7 @@
 {
 	uint8_t oldpstate;
 
-	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
+	LACP_TRACE(lp);
 
 	oldpstate = lp->lp_partner.lip_state;
 	lp->lp_partner = lacp_partner_admin;
@@ -1603,7 +1595,8 @@
 lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp,
     const struct lacp_peerinfo *info)
 {
-	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
+
+	LACP_TRACE(lp);
 
 	if (lacp_compare_peerinfo(&lp->lp_partner, info) ||
 	    !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state,
@@ -1616,7 +1609,8 @@
 static void
 lacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du)
 {
-	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
+
+	LACP_TRACE(lp);
 
 	lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor);
 }
@@ -1624,7 +1618,8 @@
 static void
 lacp_sm_rx_update_default_selected(struct lacp_port *lp)
 {
-	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
+
+	LACP_TRACE(lp);
 
 	lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin);
 }
@@ -1812,7 +1807,7 @@
 	return (0);
 }
 
-#if defined(LACP_DEBUG)
+/* Debugging */
 const char *
 lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen)
 {
@@ -1942,4 +1937,3 @@
 	vprintf(fmt, va);
 	va_end(va);
 }
-#endif
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if.h
--- a/head/sys/net/if.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)if.h	8.1 (Berkeley) 6/10/93
- * $FreeBSD: head/sys/net/if.h 231505 2012-02-11 06:02:16Z bz $
+ * $FreeBSD: head/sys/net/if.h 237787 2012-06-29 15:21:34Z jhb $
  */
 
 #ifndef _NET_IF_H_
@@ -153,7 +153,6 @@
 #define	IFF_STATICARP	0x80000		/* (n) static ARP */
 #define	IFF_DYING	0x200000	/* (n) interface is winding down */
 #define	IFF_RENAMING	0x400000	/* (n) interface is being renamed */
-
 /*
  * Old names for driver flags so that user space tools can continue to use
  * the old (portable) names.
@@ -230,6 +229,10 @@
 #define	IFCAP_VLAN_HWTSO	0x40000 /* can do IFCAP_TSO on VLANs */
 #define	IFCAP_LINKSTATE		0x80000 /* the runtime link state is dynamic */
 #define	IFCAP_NETMAP		0x100000 /* netmap mode supported/enabled */
+#define	IFCAP_RXCSUM_IPV6	0x200000  /* can offload checksum on IPv6 RX */
+#define	IFCAP_TXCSUM_IPV6	0x400000  /* can offload checksum on IPv6 TX */
+
+#define IFCAP_HWCSUM_IPV6	(IFCAP_RXCSUM_IPV6 | IFCAP_TXCSUM_IPV6)
 
 #define IFCAP_HWCSUM	(IFCAP_RXCSUM | IFCAP_TXCSUM)
 #define	IFCAP_TSO	(IFCAP_TSO4 | IFCAP_TSO6)
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_bridge.c
--- a/head/sys/net/if_bridge.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_bridge.c	Wed Jul 25 16:47:10 2012 +0300
@@ -75,7 +75,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/if_bridge.c 234487 2012-04-20 09:55:50Z thompsa $");
+__FBSDID("$FreeBSD: head/sys/net/if_bridge.c 238355 2012-07-10 20:59:35Z emaste $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -334,6 +334,7 @@
 static int	bridge_fragment(struct ifnet *, struct mbuf *,
 		    struct ether_header *, int, struct llc *);
 static void	bridge_linkstate(struct ifnet *ifp);
+static void	bridge_linkcheck(struct bridge_softc *sc);
 
 extern void (*bridge_linkstate_p)(struct ifnet *ifp);
 
@@ -964,6 +965,7 @@
 		EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp);
 	}
 
+	bridge_linkcheck(sc);
 	bridge_mutecaps(sc);	/* recalcuate now this interface is removed */
 	bridge_rtdelete(sc, ifs, IFBF_FLUSHALL);
 	KASSERT(bif->bif_addrcnt == 0,
@@ -993,7 +995,6 @@
 		bridge_set_ifcap(sc, bif, bif->bif_savedcaps);
 	}
 	bstp_destroy(&bif->bif_stp);	/* prepare to free */
-	bridge_linkstate(ifs);
 	BRIDGE_LOCK(sc);
 	free(bif, M_DEVBUF);
 }
@@ -1092,18 +1093,17 @@
 
 	/* Set interface capabilities to the intersection set of all members */
 	bridge_mutecaps(sc);
-
-	BRIDGE_UNLOCK(sc);
-	/* Update the linkstate for the bridge */
-	bridge_linkstate(ifs);
+	bridge_linkcheck(sc);
+
 	/* Place the interface into promiscuous mode */
 	switch (ifs->if_type) {
 		case IFT_ETHER:
 		case IFT_L2VLAN:
+			BRIDGE_UNLOCK(sc);
 			error = ifpromisc(ifs, 1);
+			BRIDGE_LOCK(sc);
 			break;
 	}
-	BRIDGE_LOCK(sc);
 	if (error)
 		bridge_delete_member(sc, bif, 0);
 out:
@@ -1811,8 +1811,10 @@
 			m->m_flags &= ~M_VLANTAG;
 		}
 
-		if (err == 0)
-			dst_ifp->if_transmit(dst_ifp, m);
+		if ((err = dst_ifp->if_transmit(dst_ifp, m))) {
+			m_freem(m0);
+			break;
+		}
 	}
 
 	if (err == 0) {
@@ -3486,8 +3488,7 @@
 bridge_linkstate(struct ifnet *ifp)
 {
 	struct bridge_softc *sc = ifp->if_bridge;
-	struct bridge_iflist *bif, *bif2;
-	int new_link, hasls;
+	struct bridge_iflist *bif;
 
 	BRIDGE_LOCK(sc);
 	bif = bridge_lookup_member_if(sc, ifp);
@@ -3495,13 +3496,26 @@
 		BRIDGE_UNLOCK(sc);
 		return;
 	}
+	bridge_linkcheck(sc);
+	BRIDGE_UNLOCK(sc);
+
+	bstp_linkstate(&bif->bif_stp);
+}
+
+static void
+bridge_linkcheck(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif;
+	int new_link, hasls;
+
+	BRIDGE_LOCK_ASSERT(sc);
 	new_link = LINK_STATE_DOWN;
 	hasls = 0;
 	/* Our link is considered up if at least one of our ports is active */
-	LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) {
-		if (bif2->bif_ifp->if_capabilities & IFCAP_LINKSTATE)
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE)
 			hasls++;
-		if (bif2->bif_ifp->if_link_state == LINK_STATE_UP) {
+		if (bif->bif_ifp->if_link_state == LINK_STATE_UP) {
 			new_link = LINK_STATE_UP;
 			break;
 		}
@@ -3511,8 +3525,4 @@
 		new_link = LINK_STATE_UP;
 	}
 	if_link_state_change(sc->sc_ifp, new_link);
-	BRIDGE_UNLOCK(sc);
-
-	bstp_linkstate(&bif->bif_stp);
 }
-
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_dl.h
--- a/head/sys/net/if_dl.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_dl.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)if_dl.h	8.1 (Berkeley) 6/10/93
- * $FreeBSD$
+ * $FreeBSD: head/sys/net/if_dl.h 235640 2012-05-19 02:39:43Z marcel $
  */
 
 #ifndef _NET_IF_DL_H_
@@ -67,6 +67,7 @@
 };
 
 #define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))
+#define LLINDEX(s) ((s)->sdl_index)
 
 #ifndef _KERNEL
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_epair.c
--- a/head/sys/net/if_epair.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_epair.c	Wed Jul 25 16:47:10 2012 +0300
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/if_epair.c 227309 2011-11-07 15:43:11Z ed $");
+__FBSDID("$FreeBSD: head/sys/net/if_epair.c 238309 2012-07-09 20:38:18Z trociny $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -904,39 +904,41 @@
 	if_link_state_change(oifp, LINK_STATE_DOWN);
 	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 	oifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+	/*
+	 * Get rid of our second half. As the other of the two
+	 * interfaces may reside in a different vnet, we need to
+	 * switch before freeing them.
+	 */
+	CURVNET_SET_QUIET(oifp->if_vnet);
 	ether_ifdetach(oifp);
-	ether_ifdetach(ifp);
 	/*
 	 * Wait for all packets to be dispatched to if_input.
-	 * The numbers can only go down as the interfaces are
+	 * The numbers can only go down as the interface is
 	 * detached so there is no need to use atomics.
 	 */
-	DPRINTF("sca refcnt=%u scb refcnt=%u\n", sca->refcount, scb->refcount);
-	EPAIR_REFCOUNT_ASSERT(sca->refcount == 1 && scb->refcount == 1,
-	    ("%s: ifp=%p sca->refcount!=1: %d || ifp=%p scb->refcount!=1: %d",
-	    __func__, ifp, sca->refcount, oifp, scb->refcount));
-
-	/*
-	 * Get rid of our second half.
-	 */
+	DPRINTF("scb refcnt=%u\n", scb->refcount);
+	EPAIR_REFCOUNT_ASSERT(scb->refcount == 1,
+	    ("%s: ifp=%p scb->refcount!=1: %d", __func__, oifp, scb->refcount));
 	oifp->if_softc = NULL;
 	error = if_clone_destroyif(ifc, oifp);
 	if (error)
 		panic("%s: if_clone_destroyif() for our 2nd iface failed: %d",
 		    __func__, error);
+	if_free(oifp);
+	ifmedia_removeall(&scb->media);
+	free(scb, M_EPAIR);
+	CURVNET_RESTORE();
 
+	ether_ifdetach(ifp);
 	/*
-	 * Finish cleaning up. Free them and release the unit.
-	 * As the other of the two interfaces my reside in a different vnet,
-	 * we need to switch before freeing them.
+	 * Wait for all packets to be dispatched to if_input.
 	 */
-	CURVNET_SET_QUIET(oifp->if_vnet);
-	if_free(oifp);
-	CURVNET_RESTORE();
+	DPRINTF("sca refcnt=%u\n", sca->refcount);
+	EPAIR_REFCOUNT_ASSERT(sca->refcount == 1,
+	    ("%s: ifp=%p sca->refcount!=1: %d", __func__, ifp, sca->refcount));
 	if_free(ifp);
 	ifmedia_removeall(&sca->media);
-	ifmedia_removeall(&scb->media);
-	free(scb, M_EPAIR);
 	free(sca, M_EPAIR);
 	ifc_free_unit(ifc, unit);
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_ethersubr.c
--- a/head/sys/net/if_ethersubr.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_ethersubr.c	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)if_ethersubr.c	8.1 (Berkeley) 6/10/93
- * $FreeBSD: head/sys/net/if_ethersubr.c 232487 2012-03-04 11:11:03Z zec $
+ * $FreeBSD: head/sys/net/if_ethersubr.c 234946 2012-05-03 08:56:43Z melifaro $
  */
 
 #include "opt_atalk.h"
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_gif.c
--- a/head/sys/net/if_gif.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_gif.c	Wed Jul 25 16:47:10 2012 +0300
@@ -1,4 +1,4 @@
-/*	$FreeBSD: head/sys/net/if_gif.c 227309 2011-11-07 15:43:11Z ed $	*/
+/*	$FreeBSD: head/sys/net/if_gif.c 237787 2012-06-29 15:21:34Z jhb $	*/
 /*	$KAME: if_gif.c,v 1.87 2001/10/19 08:50:27 itojun Exp $	*/
 
 /*-
@@ -342,26 +342,82 @@
 		return 0;
 	}
 }
+#ifdef INET
+#define GIF_HDR_LEN (ETHER_HDR_LEN + sizeof (struct ip))
+#endif
+#ifdef INET6
+#define GIF_HDR_LEN6 (ETHER_HDR_LEN + sizeof (struct ip6_hdr))
+#endif
 
 static void
 gif_start(struct ifnet *ifp)
 {
 	struct gif_softc *sc;
 	struct mbuf *m;
+	uint32_t af;
+	int error = 0;
 
 	sc = ifp->if_softc;
+	GIF_LOCK(sc);
+	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
 
-	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
-	for (;;) {
-		IFQ_DEQUEUE(&ifp->if_snd, m);
+		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
 		if (m == 0)
 			break;
 
-		gif_output(ifp, m, sc->gif_pdst, NULL);
+#ifdef ALTQ
+		/* Take out those altq bytes we add in gif_output  */
+#ifdef INET
+		if (sc->gif_psrc->sa_family == AF_INET) 
+			m->m_pkthdr.len -= GIF_HDR_LEN;
+#endif
+#ifdef INET6
+		if (sc->gif_psrc->sa_family == AF_INET6) 
+		    m->m_pkthdr.len -= GIF_HDR_LEN6;
+#endif
+#endif
+		/* 
+		 * Now pull back the af that we
+		 * stashed in the csum_data.
+		 */
+		af = m->m_pkthdr.csum_data;
+		
+		if (ifp->if_bridge)
+			af = AF_LINK;
+
+		BPF_MTAP2(ifp, &af, sizeof(af), m);
+		ifp->if_opackets++;	
+
+/*              Done by IFQ_HANDOFF */
+/* 		ifp->if_obytes += m->m_pkthdr.len;*/
+		/* override to IPPROTO_ETHERIP for bridged traffic */
+
+		M_SETFIB(m, sc->gif_fibnum);
+		/* inner AF-specific encapsulation */
+		/* XXX should we check if our outer source is legal? */
+		/* dispatch to output logic based on outer AF */
+		switch (sc->gif_psrc->sa_family) {
+#ifdef INET
+		case AF_INET:
+			error = in_gif_output(ifp, af, m);
+			break;
+#endif
+#ifdef INET6
+		case AF_INET6:
+			error = in6_gif_output(ifp, af, m);
+			break;
+#endif
+		default:
+			m_freem(m);		
+			error = ENETDOWN;
+		}
+		if (error)
+			ifp->if_oerrors++;
 
 	}
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-
+	GIF_UNLOCK(sc);
 	return;
 }
 
@@ -376,8 +432,7 @@
 	struct m_tag *mtag;
 	int error = 0;
 	int gif_called;
-	u_int32_t af;
-
+	uint32_t af;
 #ifdef MAC
 	error = mac_ifnet_check_transmit(ifp, m);
 	if (error) {
@@ -426,55 +481,45 @@
 	m_tag_prepend(m, mtag);
 
 	m->m_flags &= ~(M_BCAST|M_MCAST);
-
-	GIF_LOCK(sc);
-
-	if (!(ifp->if_flags & IFF_UP) ||
-	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
-		GIF_UNLOCK(sc);
-		m_freem(m);
-		error = ENETDOWN;
-		goto end;
-	}
-
 	/* BPF writes need to be handled specially. */
 	if (dst->sa_family == AF_UNSPEC) {
 		bcopy(dst->sa_data, &af, sizeof(af));
 		dst->sa_family = af;
 	}
-
 	af = dst->sa_family;
-	BPF_MTAP2(ifp, &af, sizeof(af), m);
-	ifp->if_opackets++;	
-	ifp->if_obytes += m->m_pkthdr.len;
-
-	/* override to IPPROTO_ETHERIP for bridged traffic */
-	if (ifp->if_bridge)
-		af = AF_LINK;
-
-	M_SETFIB(m, sc->gif_fibnum);
-	/* inner AF-specific encapsulation */
-
-	/* XXX should we check if our outer source is legal? */
-
-	/* dispatch to output logic based on outer AF */
-	switch (sc->gif_psrc->sa_family) {
+	/* 
+	 * Now save the af in the inbound pkt csum
+	 * data, this is a cheat since we are using
+	 * the inbound csum_data field to carry the
+	 * af over to the gif_start() routine, avoiding
+	 * using yet another mtag. 
+	 */
+	m->m_pkthdr.csum_data = af;
+	if (!(ifp->if_flags & IFF_UP) ||
+	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+		m_freem(m);
+		error = ENETDOWN;
+		goto end;
+	}
+#ifdef ALTQ
+	/*
+	 * Make altq aware of the bytes we will add 
+	 * when we actually send it.
+	 */
 #ifdef INET
-	case AF_INET:
-		error = in_gif_output(ifp, af, m);
-		break;
+	if (sc->gif_psrc->sa_family == AF_INET) 
+		m->m_pkthdr.len += GIF_HDR_LEN;
 #endif
 #ifdef INET6
-	case AF_INET6:
-		error = in6_gif_output(ifp, af, m);
-		break;
+	if (sc->gif_psrc->sa_family == AF_INET6) 
+		m->m_pkthdr.len += GIF_HDR_LEN6;
 #endif
-	default:
-		m_freem(m);		
-		error = ENETDOWN;
-	}
-
-	GIF_UNLOCK(sc);
+#endif
+	/*
+	 * Queue message on interface, update output statistics if
+	 * successful, and start output if interface not yet active.
+	 */
+	IFQ_HANDOFF(ifp, m, error);
   end:
 	if (error)
 		ifp->if_oerrors++;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_lagg.c
--- a/head/sys/net/if_lagg.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_lagg.c	Wed Jul 25 16:47:10 2012 +0300
@@ -18,7 +18,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/if_lagg.c 234163 2012-04-12 01:07:17Z thompsa $");
+__FBSDID("$FreeBSD: head/sys/net/if_lagg.c 237852 2012-06-30 19:09:02Z thompsa $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -516,8 +516,13 @@
 		return (ENOSPC);
 
 	/* Check if port has already been associated to a lagg */
-	if (ifp->if_lagg != NULL)
+	if (ifp->if_lagg != NULL) {
+		/* Port is already in the current lagg? */
+		lp = (struct lagg_port *)ifp->if_lagg;
+		if (lp->lp_softc == sc)
+			return (EEXIST);
 		return (EBUSY);
+	}
 
 	/* XXX Disallow non-ethernet interfaces (this should be any of 802) */
 	if (ifp->if_type != IFT_ETHER)
@@ -764,28 +769,18 @@
 	return (EINVAL);
 }
 
+/*
+ * For direct output to child ports.
+ */
 static int
 lagg_port_output(struct ifnet *ifp, struct mbuf *m,
 	struct sockaddr *dst, struct route *ro)
 {
 	struct lagg_port *lp = ifp->if_lagg;
-	struct ether_header *eh;
-	short type = 0;
 
 	switch (dst->sa_family) {
 		case pseudo_AF_HDRCMPLT:
 		case AF_UNSPEC:
-			eh = (struct ether_header *)dst->sa_data;
-			type = eh->ether_type;
-			break;
-	}
-
-	/*
-	 * Only allow ethernet types required to initiate or maintain the link,
-	 * aggregated frames take a different path.
-	 */
-	switch (ntohs(type)) {
-		case ETHERTYPE_PAE:	/* EAPOL PAE/802.1x */
 			return ((*lp->lp_output)(ifp, m, dst, ro));
 	}
 
@@ -802,6 +797,9 @@
 
 	if ((lp = ifp->if_lagg) == NULL)
 		return;
+	/* If the ifnet is just being renamed, don't do anything. */
+	if (ifp->if_flags & IFF_RENAMING)
+		return;
 
 	sc = lp->lp_softc;
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_loop.c
--- a/head/sys/net/if_loop.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_loop.c	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)if_loop.c	8.2 (Berkeley) 1/9/95
- * $FreeBSD$
+ * $FreeBSD: head/sys/net/if_loop.c 236332 2012-05-30 20:56:07Z tuexen $
  */
 
 /*
@@ -92,7 +92,9 @@
 #endif
 
 #define	LO_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP)
-#define	LO_CSUM_SET		(CSUM_DATA_VALID | CSUM_PSEUDO_HDR | \
+#define	LO_CSUM_FEATURES6	(CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | CSUM_SCTP_IPV6)
+#define	LO_CSUM_SET		(CSUM_DATA_VALID | CSUM_DATA_VALID_IPV6 | \
+				    CSUM_PSEUDO_HDR | \
 				    CSUM_IP_CHECKED | CSUM_IP_VALID | \
 				    CSUM_SCTP_VALID)
 
@@ -143,8 +145,9 @@
 	ifp->if_ioctl = loioctl;
 	ifp->if_output = looutput;
 	ifp->if_snd.ifq_maxlen = ifqmaxlen;
-	ifp->if_capabilities = ifp->if_capenable = IFCAP_HWCSUM;
-	ifp->if_hwassist = LO_CSUM_FEATURES;
+	ifp->if_capabilities = ifp->if_capenable =
+	    IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6;
+	ifp->if_hwassist = LO_CSUM_FEATURES | LO_CSUM_FEATURES6;
 	if_attach(ifp);
 	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
 	if (V_loif == NULL)
@@ -252,7 +255,14 @@
 			m->m_pkthdr.csum_flags = LO_CSUM_SET;
 		}
 		m->m_pkthdr.csum_flags &= ~LO_CSUM_FEATURES;
+		break;
 	case AF_INET6:
+		if (ifp->if_capenable & IFCAP_RXCSUM_IPV6) {
+			m->m_pkthdr.csum_data = 0xffff;
+			m->m_pkthdr.csum_flags = LO_CSUM_SET;
+		}
+		m->m_pkthdr.csum_flags &= ~LO_CSUM_FEATURES6;
+		break;
 	case AF_IPX:
 	case AF_APPLETALK:
 		break;
@@ -436,10 +446,15 @@
 			ifp->if_capenable ^= IFCAP_RXCSUM;
 		if ((mask & IFCAP_TXCSUM) != 0)
 			ifp->if_capenable ^= IFCAP_TXCSUM;
+		if ((mask & IFCAP_RXCSUM_IPV6) != 0)
+			ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
+		if ((mask & IFCAP_TXCSUM_IPV6) != 0)
+			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
+		ifp->if_hwassist = 0;
 		if (ifp->if_capenable & IFCAP_TXCSUM)
 			ifp->if_hwassist = LO_CSUM_FEATURES;
-		else
-			ifp->if_hwassist = 0;
+		if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
+			ifp->if_hwassist |= LO_CSUM_FEATURES6;
 		break;
 
 	default:
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_stf.c
--- a/head/sys/net/if_stf.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_stf.c	Wed Jul 25 16:47:10 2012 +0300
@@ -1,4 +1,4 @@
-/*	$FreeBSD: head/sys/net/if_stf.c 227309 2011-11-07 15:43:11Z ed $	*/
+/*	$FreeBSD: head/sys/net/if_stf.c 238492 2012-07-15 17:44:27Z melifaro $	*/
 /*	$KAME: if_stf.c,v 1.73 2001/12/03 11:08:30 keiichi Exp $	*/
 
 /*-
@@ -793,7 +793,7 @@
 	struct rt_addrinfo *info;
 {
 	RT_LOCK_ASSERT(rt);
-	rt->rt_rmx.rmx_mtu = IPV6_MMTU;
+	rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
 }
 
 static int
@@ -806,7 +806,7 @@
 	struct ifreq *ifr;
 	struct sockaddr_in6 *sin6;
 	struct in_addr addr;
-	int error;
+	int error, mtu;
 
 	error = 0;
 	switch (cmd) {
@@ -840,6 +840,18 @@
 			error = EAFNOSUPPORT;
 		break;
 
+	case SIOCGIFMTU:
+		break;
+
+	case SIOCSIFMTU:
+		ifr = (struct ifreq *)data;
+		mtu = ifr->ifr_mtu;
+		/* RFC 4213 3.2 ideal world MTU */
+		if (mtu < IPV6_MINMTU || mtu > IF_MAXMTU - 20)
+			return (EINVAL);
+		ifp->if_mtu = mtu;
+		break;
+
 	default:
 		error = EINVAL;
 		break;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_tap.c
--- a/head/sys/net/if_tap.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_tap.c	Wed Jul 25 16:47:10 2012 +0300
@@ -31,7 +31,7 @@
  */
 
 /*
- * $FreeBSD: head/sys/net/if_tap.c 227459 2011-11-11 22:57:52Z brooks $
+ * $FreeBSD: head/sys/net/if_tap.c 238183 2012-07-06 23:17:30Z emaste $
  * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $
  */
 
@@ -42,6 +42,7 @@
 #include <sys/conf.h>
 #include <sys/fcntl.h>
 #include <sys/filio.h>
+#include <sys/jail.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
@@ -64,8 +65,10 @@
 #include <net/if.h>
 #include <net/if_clone.h>
 #include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
 #include <net/route.h>
-#include <net/if_types.h>
+#include <net/vnet.h>
 
 #include <netinet/in.h>
 
@@ -214,6 +217,7 @@
 	KASSERT(!(tp->tap_flags & TAP_OPEN),
 		("%s flags is out of sync", ifp->if_xname));
 
+	CURVNET_SET(ifp->if_vnet);
 	seldrain(&tp->tap_rsel);
 	knlist_destroy(&tp->tap_rsel.si_note);
 	destroy_dev(tp->tap_dev);
@@ -222,6 +226,7 @@
 
 	mtx_destroy(&tp->tap_mtx);
 	free(tp, M_TAP);
+	CURVNET_RESTORE();
 }
 
 static void
@@ -363,6 +368,7 @@
 	if (unit == -1)
 		append_unit = 1;
 
+	CURVNET_SET(CRED_TO_VNET(cred));
 	/* find any existing device, or allocate new unit number */
 	i = clone_create(&tapclones, &tap_cdevsw, &unit, dev, extra);
 	if (i) {
@@ -381,6 +387,7 @@
 	}
 
 	if_clone_create(name, namelen, NULL);
+	CURVNET_RESTORE();
 } /* tapclone */
 
 
@@ -521,6 +528,7 @@
 
 	/* junk all pending output */
 	mtx_lock(&tp->tap_mtx);
+	CURVNET_SET(ifp->if_vnet);
 	IF_DRAIN(&ifp->if_snd);
 
 	/*
@@ -544,6 +552,8 @@
 	}
 
 	if_link_state_change(ifp, LINK_STATE_DOWN);
+	CURVNET_RESTORE();
+
 	funsetown(&tp->tap_sigio);
 	selwakeuppri(&tp->tap_rsel, PZERO+1);
 	KNOTE_LOCKED(&tp->tap_rsel.si_note, 0);
@@ -593,7 +603,8 @@
 	struct tap_softc	*tp = ifp->if_softc;
 	struct ifreq		*ifr = (struct ifreq *)data;
 	struct ifstat		*ifs = NULL;
-	int			 dummy;
+	struct ifmediareq	*ifmr = NULL;
+	int			 dummy, error = 0;
 
 	switch (cmd) {
 		case SIOCSIFFLAGS: /* XXX -- just like vmnet does */
@@ -601,6 +612,22 @@
 		case SIOCDELMULTI:
 			break;
 
+		case SIOCGIFMEDIA:
+			ifmr = (struct ifmediareq *)data;
+			dummy = ifmr->ifm_count;
+			ifmr->ifm_count = 1;
+			ifmr->ifm_status = IFM_AVALID;
+			ifmr->ifm_active = IFM_ETHER;
+			if (tp->tap_flags & TAP_OPEN)
+				ifmr->ifm_status |= IFM_ACTIVE;
+			ifmr->ifm_current = ifmr->ifm_active;
+			if (dummy >= 1) {
+				int media = IFM_ETHER;
+				error = copyout(&media, ifmr->ifm_ulist,
+				    sizeof(int));
+			}
+			break;
+
 		case SIOCSIFMTU:
 			ifp->if_mtu = ifr->ifr_mtu;
 			break;
@@ -617,11 +644,11 @@
 			break;
 
 		default:
-			return (ether_ioctl(ifp, cmd, data));
-			/* NOT REACHED */
+			error = ether_ioctl(ifp, cmd, data);
+			break;
 	}
 
-	return (0);
+	return (error);
 } /* tapifioctl */
 
 
@@ -945,7 +972,9 @@
 	}
 
 	/* Pass packet up to parent. */
+	CURVNET_SET(ifp->if_vnet);
 	(*ifp->if_input)(ifp, m);
+	CURVNET_RESTORE();
 	ifp->if_ipackets ++; /* ibytes are counted in parent */
 
 	return (0);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_var.h
--- a/head/sys/net/if_var.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_var.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	From: @(#)if.h	8.1 (Berkeley) 6/10/93
- * $FreeBSD: head/sys/net/if_var.h 233202 2012-03-19 21:09:12Z jhb $
+ * $FreeBSD: head/sys/net/if_var.h 237263 2012-06-19 07:34:13Z np $
  */
 
 #ifndef	_NET_IF_VAR_H_
@@ -712,6 +712,8 @@
 #define	IF_MINMTU	72
 #define	IF_MAXMTU	65535
 
+#define	TOEDEV(ifp)	((ifp)->if_llsoftc)
+
 #endif /* _KERNEL */
 
 /*
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/if_vlan.c
--- a/head/sys/net/if_vlan.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/if_vlan.c	Wed Jul 25 16:47:10 2012 +0300
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net/if_vlan.c 230026 2012-01-12 18:39:37Z rwatson $");
+__FBSDID("$FreeBSD: head/sys/net/if_vlan.c 237263 2012-06-19 07:34:13Z np $");
 
 #include "opt_vlan.h"
 
@@ -746,8 +746,8 @@
 		vlan_trunk_cap_p = NULL;
 		vlan_trunkdev_p = NULL;
 		vlan_tag_p = NULL;
-		vlan_cookie_p = vlan_cookie;
-		vlan_setcookie_p = vlan_setcookie;
+		vlan_cookie_p = NULL;
+		vlan_setcookie_p = NULL;
 		vlan_devat_p = NULL;
 		VLAN_LOCK_DESTROY();
 		if (bootverbose)
@@ -1503,6 +1503,22 @@
 		ifp->if_capenable &= ~(p->if_capenable & IFCAP_TSO);
 		ifp->if_hwassist &= ~(p->if_hwassist & CSUM_TSO);
 	}
+
+	/*
+	 * If the parent interface can offload TCP connections over VLANs then
+	 * propagate its TOE capability to the VLAN interface.
+	 *
+	 * All TOE drivers in the tree today can deal with VLANs.  If this
+	 * changes then IFCAP_VLAN_TOE should be promoted to a full capability
+	 * with its own bit.
+	 */
+#define	IFCAP_VLAN_TOE IFCAP_TOE
+	if (p->if_capabilities & IFCAP_VLAN_TOE)
+		ifp->if_capabilities |= p->if_capabilities & IFCAP_TOE;
+	if (p->if_capenable & IFCAP_VLAN_TOE) {
+		TOEDEV(ifp) = TOEDEV(p);
+		ifp->if_capenable |= p->if_capenable & IFCAP_TOE;
+	}
 }
 
 static void
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/route.h
--- a/head/sys/net/route.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/route.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)route.h	8.4 (Berkeley) 1/9/95
- * $FreeBSD: head/sys/net/route.h 233113 2012-03-18 11:23:40Z bz $
+ * $FreeBSD: head/sys/net/route.h 238092 2012-07-04 07:37:53Z glebius $
  */
 
 #ifndef _NET_ROUTE_H_
@@ -54,7 +54,8 @@
 	struct	sockaddr ro_dst;
 };
 
-#define RT_CACHING_CONTEXT	0x1
+#define	RT_CACHING_CONTEXT	0x1	/* XXX: not used anywhere */
+#define	RT_NORTREF		0x2	/* doesn't hold reference on ro_rt */
 
 /*
  * These numbers are used by reliable protocols for determining
@@ -341,6 +342,18 @@
 	RTFREE_LOCKED(_rt);					\
 } while (0)
 
+#define	RO_RTFREE(_ro) do {					\
+	if ((_ro)->ro_rt) {					\
+		if ((_ro)->ro_flags & RT_NORTREF) {		\
+			(_ro)->ro_flags &= ~RT_NORTREF;		\
+			(_ro)->ro_rt = NULL;			\
+		} else {					\
+			RT_LOCK((_ro)->ro_rt);			\
+			RTFREE_LOCKED((_ro)->ro_rt);		\
+		}						\
+	}							\
+} while (0)
+
 struct radix_node_head *rt_tables_get_rnh(int, int);
 
 struct ifmultiaddr;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net/rtsock.c
--- a/head/sys/net/rtsock.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net/rtsock.c	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)rtsock.c	8.7 (Berkeley) 10/12/95
- * $FreeBSD: head/sys/net/rtsock.c 231505 2012-02-11 06:02:16Z bz $
+ * $FreeBSD: head/sys/net/rtsock.c 234572 2012-04-22 16:13:23Z melifaro $
  */
 #include "opt_compat.h"
 #include "opt_sctp.h"
@@ -1884,10 +1884,10 @@
 		for (error = 0; error == 0 && i <= lim; i++) {
 			rnh = rt_tables_get_rnh(req->td->td_proc->p_fibnum, i);
 			if (rnh != NULL) {
-				RADIX_NODE_HEAD_LOCK(rnh); 
+				RADIX_NODE_HEAD_RLOCK(rnh); 
 			    	error = rnh->rnh_walktree(rnh,
 				    sysctl_dumpentry, &w);
-				RADIX_NODE_HEAD_UNLOCK(rnh);
+				RADIX_NODE_HEAD_RUNLOCK(rnh);
 			} else if (af != 0)
 				error = EAFNOSUPPORT;
 		}
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/_ieee80211.h
--- a/head/sys/net80211/_ieee80211.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/_ieee80211.h	Wed Jul 25 16:47:10 2012 +0300
@@ -23,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.
  *
- * $FreeBSD: head/sys/net80211/_ieee80211.h 220935 2011-04-22 00:44:27Z adrian $
+ * $FreeBSD: head/sys/net80211/_ieee80211.h 237871 2012-07-01 04:25:49Z adrian $
  */
 #ifndef _NET80211__IEEE80211_H_
 #define _NET80211__IEEE80211_H_
@@ -242,6 +242,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) \
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211.h
--- a/head/sys/net80211/ieee80211.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211.h	Wed Jul 25 16:47:10 2012 +0300
@@ -23,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.
  *
- * $FreeBSD: head/sys/net80211/ieee80211.h 232480 2012-03-04 05:52:26Z adrian $
+ * $FreeBSD: head/sys/net80211/ieee80211.h 234892 2012-05-01 16:14:18Z monthadar $
  */
 #ifndef _NET80211_IEEE80211_H_
 #define _NET80211_IEEE80211_H_
@@ -333,6 +333,8 @@
 #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*/
@@ -732,7 +734,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 */
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_action.c
--- a/head/sys/net80211/ieee80211_action.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_action.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_action.c 232479 2012-03-04 05:49:39Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_action.c 234874 2012-05-01 15:35:10Z monthadar $");
 #endif
 
 /*
@@ -94,7 +94,7 @@
 			break;
 		ht_send_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
+	case IEEE80211_ACTION_CAT_SELF_PROT:
 		if (act >= N(meshpl_send_action))
 			break;
 		meshpl_send_action[act] = f;
@@ -144,7 +144,7 @@
 		if (act < N(ht_send_action))
 			f = ht_send_action[act];
 		break;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
+	case IEEE80211_ACTION_CAT_SELF_PROT:
 		if (act < N(meshpl_send_action))
 			f = meshpl_send_action[act];
 		break;
@@ -215,7 +215,7 @@
 			break;
 		ht_recv_action[act] = f;
 		return 0;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
+	case IEEE80211_ACTION_CAT_SELF_PROT:
 		if (act >= N(meshpl_recv_action))
 			break;
 		meshpl_recv_action[act] = f;
@@ -269,7 +269,7 @@
 		if (ia->ia_action < N(ht_recv_action))
 			f = ht_recv_action[ia->ia_action];
 		break;
-	case IEEE80211_ACTION_CAT_MESHPEERING:
+	case IEEE80211_ACTION_CAT_SELF_PROT:
 		if (ia->ia_action < N(meshpl_recv_action))
 			f = meshpl_recv_action[ia->ia_action];
 		break;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_ddb.c
--- a/head/sys/net80211/ieee80211_ddb.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_ddb.c	Wed Jul 25 16:47:10 2012 +0300
@@ -24,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ddb.c 234324 2012-04-15 20:29:39Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ddb.c 234877 2012-05-01 15:47:30Z monthadar $");
 
 #include "opt_ddb.h"
 #include "opt_wlan.h"
@@ -870,8 +870,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++;
 	}
 }
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_ht.c
--- a/head/sys/net80211/ieee80211_ht.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_ht.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 234324 2012-04-15 20:29:39Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 235801 2012-05-22 19:37:12Z adrian $");
 #endif
 
 /*
@@ -2166,6 +2166,9 @@
 	}
 }
 
+/* XXX */
+static void bar_start_timer(struct ieee80211_tx_ampdu *tap);
+
 static void
 bar_timeout(void *arg)
 {
@@ -2184,11 +2187,34 @@
 		return;
 	/* XXX ? */
 	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 {
 		ni->ni_vap->iv_stats.is_ampdu_bar_tx_retry++;
-		ieee80211_send_bar(ni, tap, tap->txa_seqpending);
+		if (ieee80211_send_bar(ni, tap, tap->txa_seqpending) != 0) {
+			/*
+			 * 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);
+		}
 	}
 }
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_hwmp.c
--- a/head/sys/net80211/ieee80211_hwmp.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_hwmp.c	Wed Jul 25 16:47:10 2012 +0300
@@ -28,7 +28,7 @@
  */ 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_hwmp.c 232625 2012-03-06 21:20:16Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_hwmp.c 234896 2012-05-01 16:18:38Z monthadar $");
 #endif
 
 /*
@@ -89,7 +89,8 @@
 static int	hwmp_send_preq(struct ieee80211_node *,
 		    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 *);
@@ -104,6 +105,9 @@
 		    const uint8_t [IEEE80211_ADDR_LEN],
 		    const uint8_t [IEEE80211_ADDR_LEN],
 		    struct ieee80211_meshperr_ie *);
+static void	hwmp_senderror(struct ieee80211vap *,
+		    const uint8_t [IEEE80211_ADDR_LEN],
+		    struct ieee80211_mesh_route *, int);
 static void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
 		   const struct ieee80211_frame *,
 		   const struct ieee80211_meshrann_ie *);
@@ -143,8 +147,7 @@
 #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 HWMP_SEQ_MAX(a, b)	(a > b ? a : b)
 
 /*
  * Private extension of ieee80211_mesh_route.
@@ -153,14 +156,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 */
 };
@@ -170,13 +175,19 @@
 static int	ieee80211_hwmp_targetonly = 0;
 SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | 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",
@@ -189,6 +200,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
 
@@ -199,6 +215,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,
@@ -212,10 +229,25 @@
 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.
@@ -537,6 +569,7 @@
 			}
 			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++;
@@ -808,8 +841,8 @@
 	    "%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;
+	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;
@@ -822,10 +855,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);
+	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq,
+	    NULL, NULL);	/* NB: we enforce rate check ourself */
 	hwmp_rootmode_setup(vap);
 }
 #undef	PREQ_TFLAGS
@@ -848,12 +882,13 @@
 	    "%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++;
@@ -869,13 +904,14 @@
     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_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 */
 
 	if (ni == vap->iv_bss ||
 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
@@ -888,16 +924,21 @@
 		return;
 
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-	    "received PREQ, source %6D", 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 or not broadcast
+	 * 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.
-	 * XXX: need to check PROXY
 	 */
-	if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
-	    !IEEE80211_IS_MULTICAST(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;
@@ -906,7 +947,6 @@
 	 * Acceptance criteria: if unicast addressed 
 	 * AND no valid forwarding for Target of PREQ, discard this PREQ.
 	 */
-	rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
 	if(rttarg != NULL)
 		hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg,
 		    struct ieee80211_hwmp_route);
@@ -926,19 +966,26 @@
 	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
 	if (rtorig == NULL) {
 		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
+		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, ":");
 	}
-	if (rtorig == NULL) {
-		/* XXX stat */
-		return;
-	}
 	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
 
+	/* 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_GT(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;
@@ -946,17 +993,19 @@
 		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);
+		ieee80211_mesh_rt_update(rtorig, preq->preq_lifetime);
 		/* path to orig is valid now */
 		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) == 0) {
+	}else if ((hrtarg != NULL &&
+	    HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0)) &&
+	    ((rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)) ||
+	    preqid >= preq->preq_id) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "discard PREQ from %6D, old seq no %u <= %u",
+		    "discard PREQ from %6D, old seqno %u <= %u,"
+		    " or old preqid %u < %u",
 		    preq->preq_origaddr, ":",
-		    preq->preq_origseq, hrorig->hr_seq);
+		    preq->preq_origseq, hrorig->hr_seq,
+		    preq->preq_id, preqid);
 		return;
 	}
 
@@ -967,68 +1016,86 @@
 
 	/*
 	 * Check if the PREQ is addressed to us.
+	 * or a Proxy currently supplied by us.
 	 */
-	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
-		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "reply to %6D", preq->preq_origaddr, ":");
+	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
+	    (rttarg != NULL &&
+	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
+	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
+		/*
+		 * 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;
+		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;
+		}
 		/*
 		 * Build and send a PREP frame.
 		 */
-		prep.prep_flags = 0;
 		prep.prep_hopcount = 0;
 		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;
+
+		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+		    "reply to %6D", preq->preq_origaddr, ":");
 		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);
 		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];
-
-		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 %6D",
-				    rootmac, ":");
-				vap->iv_stats.is_mesh_rtaddfailed++;
-				return;
-			}
-		}
+	    (PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-		    "root mesh station @ %6D", rootmac, ":");
+		    "root mesh station @ %6D", preq->preq_origaddr, ":");
 
 		/*
 		 * 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;
@@ -1036,109 +1103,69 @@
 			    vap->iv_myaddr);
 			prep.prep_targetseq = ++hs->hs_seq;
 			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
-			    broadcastaddr, &prep);
+			    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.
 		 */
-		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 %6D",
-				    preq->preq_origaddr, ":");
-				/*
-				 * 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 +=
-				    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,
-				    rt->rt_nexthop, &ppreq);
-			}
+		if (rttarg != NULL &&
+		    (rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
 			/*
 			 * 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 %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);
+				    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 %6D",
-					    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 %6D",
-			    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(ni, vap->iv_myaddr, broadcastaddr,
+			&ppreq, NULL, NULL);
 	}
 }
 #undef	PREQ_TFLAGS
@@ -1149,16 +1176,19 @@
 hwmp_send_preq(struct ieee80211_node *ni,
     const uint8_t sa[IEEE80211_ADDR_LEN],
     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
@@ -1180,62 +1210,70 @@
 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;
 
-	/*
-	 * Acceptance criteria: if the corresponding PREQ 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))
-		return;
 
 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-	    "received PREP from %6D", prep->prep_targetaddr, ":");
+	    "received PREP, orig %6D, targ %6D", prep->prep_origaddr, ":",
+	    prep->prep_targetaddr, ":");
 
+	/*
+	 * 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.
+	 */
+	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;
+	}
+
+	/* 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.
+	 * [OPTIONAL]: update forwarding information to TA if metric improves.
+	 */
 	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.
-		 * XXX: not true anymore cause we dont create entry for target
-		 *  when propagating PREQs like the old code did.
-		 */
-		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 %6D",
-				    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 %6D nhops %d metric %lu (PREP)",
-			    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);
+	/* 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,
@@ -1244,7 +1282,7 @@
 			    prep->prep_targetseq, hr->hr_seq);
 			return;
 		} else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) &&
-		    prep->prep_metric > rt->rt_metric) {
+		    metric > rt->rt_metric) {
 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 			    "discard PREP from %6D, new metric %u > %u",
 			    prep->prep_targetaddr, ":",
@@ -1253,13 +1291,46 @@
 		}
 	}
 
+	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,
+	    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 */
+
 	/*
-	 * 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 %6D",
 		    prep->prep_targetaddr, ":");
@@ -1268,54 +1339,53 @@
 		pprep.prep_hopcount += 1;
 		pprep.prep_ttl -= 1;
 		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
-		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
+		hwmp_send_prep(ni, vap->iv_myaddr, 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 %6D, route is marked PROXY",
-		    prep->prep_targetaddr, ":");
-		vap->iv_stats.is_hwmp_proxy++;
-	/* NB: first path discovery always fails */
-	} else if (hr->hr_origseq == 0 ||
-	    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,
+		    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)) {
-			hr->hr_origseq = prep->prep_origseq;
-			metric = prep->prep_metric +
-			    ms->ms_pmetric->mpm_metric(ni);
-			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_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 + 1;
-			rt->rt_lifetime = prep->prep_lifetime;
-			rt->rt_metric = metric;
-			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
-		} else {
-			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
-			    "ignore PREP for %6D, hopcount %d:%d metric %d:%d",
-			    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 %6D, wrong orig seqno %u != %u",
-		    prep->prep_targetaddr, ":", prep->prep_origseq,
-		    hr->hr_origseq);
-		vap->iv_stats.is_hwmp_wrongseq++;
 	}
 	/*
 	 * Check for frames queued awaiting path discovery.
@@ -1324,9 +1394,11 @@
 	 *     stuck back on the stageq because there won't be
 	 *     a path.
 	 */
+	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 */
 	for (; m != NULL; m = next) {
 		next = m->m_nextpkt;
 		m->m_nextpkt = NULL;
@@ -1334,6 +1406,8 @@
 		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
 		ifp->if_transmit(ifp, m);
 	}
+#undef	IS_PROXY
+#undef	PROXIED_BY_US
 }
 
 static int
@@ -1386,7 +1460,7 @@
 		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);
@@ -1397,60 +1471,117 @@
 #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)
 {
 	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;
+
+	if (ni == vap->iv_bss ||
+	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
+		return;
+
+	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)
+		if (rt == NULL || rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
 			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, rt->rt_dest);
+		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 %6D", wh->i_addr2, ":");
-		memcpy(&pperr, perr, sizeof(*perr));
-		pperr.perr_ttl--;
+		pperr->perr_ndests = j;
+		pperr->perr_ttl--;
 		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
-		    &pperr);
+		    pperr);
 	}
+done:
+	if (pperr != NULL)
+		free(pperr, M_80211_MESH_PERR);
 }
 #undef	PERR_DFLAGS
-#undef	PEER_DADDR
+#undef	PERR_DADDR
 #undef	PERR_DSEQ
+#undef	PERR_DEXTADDR
 #undef	PERR_DRCODE
 
 static int
@@ -1493,6 +1624,64 @@
 	return hwmp_send_action(ni, sa, 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->iv_bss, vap->iv_myaddr, 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)
@@ -1501,7 +1690,9 @@
 	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 ||
@@ -1509,27 +1700,74 @@
 		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)) {
+	/* 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->iv_bss, vap->iv_myaddr, 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->iv_bss, vap->iv_myaddr,
+		    broadcastaddr, &prann);
 	}
 }
 
@@ -1557,6 +1795,64 @@
 #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_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+			rt->rt_dest, NULL, "%s",
+			"no valid path , max number of discovery, send GATE");
+		/* TODO: send to known gates */
+		vap->iv_stats.is_mesh_fwd_nopath++;
+		rt->rt_flags = 0; /* Mark invalid */
+		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->iv_bss, vap->iv_myaddr,
+		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)
@@ -1591,12 +1887,29 @@
 		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
 		    struct ieee80211_hwmp_route);
 		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");
+				/* XXX: stats? */
+				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), target seq %u",
@@ -1607,26 +1920,29 @@
 			 * Try to discover the path for this node.
 			 * Group addressed PREQ Case A
 			 */
-			preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
+			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 = 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) = hr->hr_seq;
+			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);
+			    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);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_input.c
--- a/head/sys/net80211/ieee80211_input.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_input.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_input.c 232484 2012-03-04 09:45:43Z glebius $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_input.c 234874 2012-05-01 15:35:10Z monthadar $");
 
 #include "opt_wlan.h"
 
@@ -792,6 +792,24 @@
 			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;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_ioctl.h
--- a/head/sys/net80211/ieee80211_ioctl.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_ioctl.h	Wed Jul 25 16:47:10 2012 +0300
@@ -23,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.
  *
- * $FreeBSD: head/sys/net80211/ieee80211_ioctl.h 234018 2012-04-08 04:51:25Z adrian $
+ * $FreeBSD: head/sys/net80211/ieee80211_ioctl.h 234894 2012-05-01 16:16:20Z monthadar $
  */
 #ifndef _NET80211_IEEE80211_IOCTL_H_
 #define _NET80211_IEEE80211_IOCTL_H_
@@ -339,8 +339,9 @@
 
 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
 	uint8_t		imr_dest[IEEE80211_ADDR_LEN];
 	uint8_t		imr_nexthop[IEEE80211_ADDR_LEN];
 	uint16_t	imr_nhops;
@@ -709,6 +710,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 */
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_mesh.c
--- a/head/sys/net80211/ieee80211_mesh.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_mesh.c	Wed Jul 25 16:47:10 2012 +0300
@@ -28,7 +28,7 @@
  */ 
 #include <sys/cdefs.h>
 #ifdef __FreeBSD__
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_mesh.c 232625 2012-03-06 21:20:16Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_mesh.c 234894 2012-05-01 16:16:20Z monthadar $");
 #endif
 
 /*
@@ -138,6 +138,10 @@
 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)
@@ -146,6 +150,9 @@
 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", "802.11s routing table");
 
 /*
@@ -168,9 +175,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),
@@ -181,9 +189,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, CALLOUT_MPSAFE);
+		rt->rt_updtime = ticks;	/* create time */
 		TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
 	}
 	return rt;
@@ -215,12 +226,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
@@ -233,7 +288,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");
@@ -241,12 +296,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
@@ -271,6 +328,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);
 }
 
@@ -284,6 +348,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;
@@ -335,8 +406,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);
@@ -430,25 +504,25 @@
 	/*
 	 * 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_MESH,
 	    IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric);
 
-	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_MESH,
@@ -485,7 +559,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);
 	}
@@ -856,9 +930,14 @@
 	struct ieee80211_node *ni;
 	int err;
 
-	if (mc->mc_ttl == 0) {
+	/*
+	 * 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;
 	}
@@ -898,8 +977,16 @@
 	} else {
 		ni = 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;
@@ -926,9 +1013,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;
@@ -962,13 +1050,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,
@@ -978,12 +1067,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,
@@ -1005,7 +1094,8 @@
 		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
 	}
 	return m;
-#undef WDIR
+#undef	WDIR
+#undef	MC01
 }
 
 /*
@@ -1021,12 +1111,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));
@@ -1034,19 +1125,172 @@
 		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;
+
+	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 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;
+
+	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.
+		 */
+	}
+	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;
+
+	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;
+	int hdrspace, meshdrlen, need_tap, error;
+	uint8_t dir, type, subtype, ae;
 	uint32_t seq;
-	uint8_t *addr, qos[2];
+	const uint8_t *addr;
+	uint8_t qos[2];
 	ieee80211_seq rxseq;
 
 	KASSERT(ni != NULL, ("null node"));
@@ -1135,7 +1379,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,
@@ -1217,12 +1461,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)) {
@@ -1236,17 +1496,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 */
-		}
+		/* 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
+			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);
@@ -1328,6 +1593,9 @@
 		m_freem(m);
 	}
 	return type;
+#undef	HAS_SEQ
+#undef	MC01
+#undef	MC10
 }
 
 static void
@@ -1338,6 +1606,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 *);
@@ -1430,20 +1699,41 @@
 		 * 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);
+				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;
 	}
@@ -1573,8 +1863,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,
@@ -1584,7 +1873,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) {
@@ -1600,15 +1891,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;
 		}
@@ -1616,22 +1920,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) ||
-	    mesh_verify_meshconf(vap, meshconf)) {
-		uint16_t args[3];
-
+	/* 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)) {
+		sendclose = 1;
+		IEEE80211_DISCARD(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+		    wh, NULL, "%s", "configuration missmatch");
+	}
+
+	if (sendclose) {
 		vap->iv_stats.is_rx_mgtdiscard++;
 		switch (ni->ni_mlstate) {
 		case IEEE80211_NODE_MESH_IDLE:
@@ -1644,9 +1972,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);
@@ -1655,6 +1991,7 @@
 		}
 		return NULL;
 	}
+	
 	return (const struct ieee80211_meshpeer_ie *) mp;
 }
 
@@ -1664,6 +2001,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];
@@ -1681,6 +2019,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);
@@ -1689,13 +2040,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);
@@ -1707,7 +2058,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);
@@ -1718,7 +2069,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;
@@ -1728,7 +2079,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 */
@@ -1740,7 +2091,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,
@@ -1753,7 +2104,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);
@@ -1764,7 +2115,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);
@@ -1774,16 +2125,17 @@
 		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;
 	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;
@@ -1819,13 +2171,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;
@@ -1835,7 +2189,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);
@@ -1858,8 +2212,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");
 
@@ -1875,7 +2244,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);
@@ -1883,7 +2252,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;
@@ -1991,7 +2360,7 @@
 		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);
@@ -2059,7 +2428,7 @@
 		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);
@@ -2101,16 +2470,14 @@
 		 * 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);
@@ -2234,7 +2601,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);
@@ -2242,26 +2609,20 @@
 		} 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);
 		}
 		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:
 		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
@@ -2337,22 +2698,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;
@@ -2398,9 +2768,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)
@@ -2418,34 +2789,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 */
@@ -2465,7 +2831,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 \
@@ -2597,6 +2963,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;
@@ -2625,15 +2994,16 @@
 					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,
 				    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);
@@ -2711,6 +3081,12 @@
 		else
 			ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
 		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;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_mesh.h
--- a/head/sys/net80211/ieee80211_mesh.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_mesh.h	Wed Jul 25 16:47:10 2012 +0300
@@ -26,12 +26,13 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
  * SUCH DAMAGE. 
  * 
- * $FreeBSD: head/sys/net80211/ieee80211_mesh.h 232625 2012-03-06 21:20:16Z adrian $
+ * $FreeBSD: head/sys/net80211/ieee80211_mesh.h 234894 2012-05-01 16:16:20Z 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.
@@ -97,7 +98,7 @@
 };
 
 /* Mesh Formation Info */
-#define	IEEE80211_MESHCONF_FORM_MP	0x01 	/* Connected to Portal */
+#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 */
 
@@ -140,32 +141,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 {
@@ -200,9 +193,9 @@
 } __packed;
 #endif
 
-/* Portal (MP) Annoucement */
-struct ieee80211_meshpann_ie {
-	uint8_t		pann_ie;		/* IEEE80211_ELEMID_MESHPANN */
+/* Gate (GANN) Annoucement */
+struct ieee80211_meshgann_ie {
+	uint8_t		pann_ie;		/* IEEE80211_ELEMID_MESHGANN */
 	uint8_t		pann_len;
 	uint8_t		pann_flags;
 	uint8_t		pann_hopcount;
@@ -217,7 +210,7 @@
 	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];
@@ -236,8 +229,8 @@
 	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;
@@ -253,7 +246,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 */
@@ -331,9 +323,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 */
-/* XXX: these need to be looked into */
 #define	IEEE80211_ACTION_CAT_INTERWORK		15
 #define	IEEE80211_ACTION_CAT_RESOURCE		16
 #define	IEEE80211_ACTION_CAT_PROXY		17
@@ -342,10 +334,11 @@
  * 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 */
 };
 
 /*
@@ -367,14 +360,6 @@
 };
 
 /*
- * Mesh Portal Annoucement Action codes.
- */
-enum {
-	IEEE80211_ACTION_MESHPANN	= 0,
-	/* 1-255 reserved */
-};
-
-/*
  * Different mesh control structures based on the AE
  * (Address Extension) bits.
  */
@@ -396,37 +381,51 @@
 	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);
+/*
+ * 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 */
+	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)
@@ -445,6 +444,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 *,
@@ -491,7 +493,7 @@
 	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 */
 	uint8_t				ms_flags;
 	struct mtx			ms_rt_lock;
@@ -514,6 +516,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]);
 
@@ -548,7 +551,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;
 }
 
 /*
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_output.c
--- a/head/sys/net80211/ieee80211_output.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_output.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_output.c 234324 2012-04-15 20:29:39Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_output.c 237561 2012-06-25 11:52:26Z monthadar $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -254,7 +254,7 @@
 				if (!ieee80211_mesh_isproxyena(vap)) {
 					IEEE80211_DISCARD_MAC(vap,
 					    IEEE80211_MSG_OUTPUT |
-						IEEE80211_MSG_MESH,
+					    IEEE80211_MSG_MESH,
 					    eh->ether_dhost, NULL,
 					    "%s", "proxy not enabled");
 					vap->iv_stats.is_mesh_notproxy++;
@@ -363,7 +363,6 @@
 				continue;
 			}
 		}
-
 		error = parent->if_transmit(parent, m);
 		if (error != 0) {
 			/* NB: IFQ_HANDOFF reclaims mbuf */
@@ -556,7 +555,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 */
@@ -1016,10 +1014,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;
@@ -1100,21 +1101,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
@@ -1215,44 +1235,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 */
-			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 */
+		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, 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:
@@ -1285,9 +1313,9 @@
 		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
 			qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
 #ifdef IEEE80211_SUPPORT_MESH
-		if (vap->iv_opmode == IEEE80211_M_MBSS) {
-			qos[1] |= IEEE80211_QOS_MC;
-		} else
+		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;
@@ -1363,6 +1391,7 @@
 		m_freem(m);
 	return NULL;
 #undef WH4
+#undef MC01
 }
 
 /*
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_radiotap.c
--- a/head/sys/net80211/ieee80211_radiotap.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_radiotap.c	Wed Jul 25 16:47:10 2012 +0300
@@ -24,7 +24,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_radiotap.c 232705 2012-03-08 23:46:42Z adrian $");
+__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_radiotap.c 237214 2012-06-18 02:08:04Z adrian $");
 
 /*
  * IEEE 802.11 radiotap support.
@@ -47,13 +47,24 @@
 
 #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,
 	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,9 +74,9 @@
 	/* 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\n",
 		    __func__, tx_radiotap);
@@ -79,9 +90,9 @@
 	/* 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\n",
 		    __func__, rx_radiotap);
@@ -260,7 +271,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;
@@ -334,6 +346,8 @@
 	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;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/net80211/ieee80211_var.h
--- a/head/sys/net80211/ieee80211_var.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/net80211/ieee80211_var.h	Wed Jul 25 16:47:10 2012 +0300
@@ -23,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.
  *
- * $FreeBSD: head/sys/net80211/ieee80211_var.h 233452 2012-03-25 03:11:57Z adrian $
+ * $FreeBSD: head/sys/net80211/ieee80211_var.h 237214 2012-06-18 02:08:04Z adrian $
  */
 #ifndef _NET80211_IEEE80211_VAR_H_
 #define _NET80211_IEEE80211_VAR_H_
@@ -705,6 +705,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 *);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/netflow/netflow.c
--- a/head/sys/netgraph/netflow/netflow.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/netflow/netflow.c	Wed Jul 25 16:47:10 2012 +0300
@@ -29,7 +29,7 @@
  */
 
 static const char rcs_id[] =
-    "@(#) $FreeBSD: head/sys/netgraph/netflow/netflow.c 232921 2012-03-13 11:08:40Z melifaro $";
+    "@(#) $FreeBSD: head/sys/netgraph/netflow/netflow.c 237227 2012-06-18 13:56:36Z melifaro $";
 
 #include "opt_inet6.h"
 #include "opt_route.h"
@@ -98,9 +98,9 @@
 static int export_add(item_p, struct flow_entry *);
 static int export_send(priv_p, fib_export_p, item_p, int);
 
-static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *, int, uint8_t);
+static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *, int, uint8_t, uint8_t);
 #ifdef INET6
-static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *, int, uint8_t);
+static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *, int, uint8_t, uint8_t);
 #endif
 
 static __inline void expire_flow(priv_p, fib_export_p, struct flow_entry *, int);
@@ -325,9 +325,9 @@
  * as this was done in previous version. Need to test & profile
  * to be sure.
  */
-static __inline int
+static int
 hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r,
-	int plen, uint8_t tcp_flags)
+	int plen, uint8_t flags, uint8_t tcp_flags)
 {
 	struct flow_entry *fle;
 	struct sockaddr_in sin;
@@ -358,44 +358,48 @@
 	 * First we do route table lookup on destination address. So we can
 	 * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
 	 */
-	bzero(&sin, sizeof(sin));
-	sin.sin_len = sizeof(struct sockaddr_in);
-	sin.sin_family = AF_INET;
-	sin.sin_addr = fle->f.r.r_dst;
-	rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
-	if (rt != NULL) {
-		fle->f.fle_o_ifx = rt->rt_ifp->if_index;
+	if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0) {
+		bzero(&sin, sizeof(sin));
+		sin.sin_len = sizeof(struct sockaddr_in);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = fle->f.r.r_dst;
+		rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
+		if (rt != NULL) {
+			fle->f.fle_o_ifx = rt->rt_ifp->if_index;
 
-		if (rt->rt_flags & RTF_GATEWAY &&
-		    rt->rt_gateway->sa_family == AF_INET)
-			fle->f.next_hop =
-			    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
+			if (rt->rt_flags & RTF_GATEWAY &&
+			    rt->rt_gateway->sa_family == AF_INET)
+				fle->f.next_hop =
+				    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
 
-		if (rt_mask(rt))
-			fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
-			    rt_mask(rt))->sin_addr.s_addr);
-		else if (rt->rt_flags & RTF_HOST)
-			/* Give up. We can't determine mask :( */
-			fle->f.dst_mask = 32;
+			if (rt_mask(rt))
+				fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST)
+				/* Give up. We can't determine mask :( */
+				fle->f.dst_mask = 32;
 
-		RTFREE_LOCKED(rt);
+			RTFREE_LOCKED(rt);
+		}
 	}
 
 	/* Do route lookup on source address, to fill in src_mask. */
-	bzero(&sin, sizeof(sin));
-	sin.sin_len = sizeof(struct sockaddr_in);
-	sin.sin_family = AF_INET;
-	sin.sin_addr = fle->f.r.r_src;
-	rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
-	if (rt != NULL) {
-		if (rt_mask(rt))
-			fle->f.src_mask = bitcount32(((struct sockaddr_in *)
-			    rt_mask(rt))->sin_addr.s_addr);
-		else if (rt->rt_flags & RTF_HOST)
-			/* Give up. We can't determine mask :( */
-			fle->f.src_mask = 32;
+	if ((flags & NG_NETFLOW_CONF_NOSRCLOOKUP) == 0) {
+		bzero(&sin, sizeof(sin));
+		sin.sin_len = sizeof(struct sockaddr_in);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = fle->f.r.r_src;
+		rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
+		if (rt != NULL) {
+			if (rt_mask(rt))
+				fle->f.src_mask = bitcount32(((struct sockaddr_in *)
+				    rt_mask(rt))->sin_addr.s_addr);
+			else if (rt->rt_flags & RTF_HOST)
+				/* Give up. We can't determine mask :( */
+				fle->f.src_mask = 32;
 
-		RTFREE_LOCKED(rt);
+			RTFREE_LOCKED(rt);
+		}
 	}
 
 	/* Push new flow at the and of hash. */
@@ -410,10 +414,10 @@
 				bitcount32((x).__u6_addr.__u6_addr32[1]) + \
 				bitcount32((x).__u6_addr.__u6_addr32[2]) + \
 				bitcount32((x).__u6_addr.__u6_addr32[3])
-/* XXX: Do we need inline here ? */
-static __inline int
+#define RT_MASK6(x)	(ipv6_masklen(((struct sockaddr_in6 *)rt_mask(x))->sin6_addr))
+static int
 hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r,
-	int plen, uint8_t tcp_flags)
+	int plen, uint8_t flags, uint8_t tcp_flags)
 {
 	struct flow6_entry *fle6;
 	struct sockaddr_in6 *src, *dst;
@@ -445,49 +449,55 @@
 	 * First we do route table lookup on destination address. So we can
 	 * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
 	 */
-	bzero(&rin6, sizeof(struct route_in6));
-	dst = (struct sockaddr_in6 *)&rin6.ro_dst;
-	dst->sin6_len = sizeof(struct sockaddr_in6);
-	dst->sin6_family = AF_INET6;
-	dst->sin6_addr = r->dst.r_dst6;
+	if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0)
+	{
+		bzero(&rin6, sizeof(struct route_in6));
+		dst = (struct sockaddr_in6 *)&rin6.ro_dst;
+		dst->sin6_len = sizeof(struct sockaddr_in6);
+		dst->sin6_family = AF_INET6;
+		dst->sin6_addr = r->dst.r_dst6;
 
-	rin6.ro_rt = rtalloc1_fib((struct sockaddr *)dst, 0, 0, r->fib);
+		rin6.ro_rt = rtalloc1_fib((struct sockaddr *)dst, 0, 0, r->fib);
 
-	if (rin6.ro_rt != NULL) {
-		rt = rin6.ro_rt;
-		fle6->f.fle_o_ifx = rt->rt_ifp->if_index;
+		if (rin6.ro_rt != NULL) {
+			rt = rin6.ro_rt;
+			fle6->f.fle_o_ifx = rt->rt_ifp->if_index;
 
-		if (rt->rt_flags & RTF_GATEWAY &&
-		    rt->rt_gateway->sa_family == AF_INET6)
-			fle6->f.n.next_hop6 =
-			    ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
+			if (rt->rt_flags & RTF_GATEWAY &&
+			    rt->rt_gateway->sa_family == AF_INET6)
+				fle6->f.n.next_hop6 =
+				    ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
 
-		if (rt_mask(rt))
-			fle6->f.dst_mask = ipv6_masklen(((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr);
-		else 
-			fle6->f.dst_mask = 128;
+			if (rt_mask(rt))
+				fle6->f.dst_mask = RT_MASK6(rt);
+			else
+				fle6->f.dst_mask = 128;
 
-		RTFREE_LOCKED(rt);
+			RTFREE_LOCKED(rt);
+		}
 	}
 
-	/* Do route lookup on source address, to fill in src_mask. */
-	bzero(&rin6, sizeof(struct route_in6));
-	src = (struct sockaddr_in6 *)&rin6.ro_dst;
-	src->sin6_len = sizeof(struct sockaddr_in6);
-	src->sin6_family = AF_INET6;
-	src->sin6_addr = r->src.r_src6;
+	if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0)
+	{
+		/* Do route lookup on source address, to fill in src_mask. */
+		bzero(&rin6, sizeof(struct route_in6));
+		src = (struct sockaddr_in6 *)&rin6.ro_dst;
+		src->sin6_len = sizeof(struct sockaddr_in6);
+		src->sin6_family = AF_INET6;
+		src->sin6_addr = r->src.r_src6;
 
-	rin6.ro_rt = rtalloc1_fib((struct sockaddr *)src, 0, 0, r->fib);
+		rin6.ro_rt = rtalloc1_fib((struct sockaddr *)src, 0, 0, r->fib);
 
-	if (rin6.ro_rt != NULL) {
-		rt = rin6.ro_rt;
+		if (rin6.ro_rt != NULL) {
+			rt = rin6.ro_rt;
 
-		if (rt_mask(rt))
-			fle6->f.src_mask = ipv6_masklen(((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr);
-		else 
-			fle6->f.src_mask = 128;
+			if (rt_mask(rt))
+				fle6->f.src_mask = RT_MASK6(rt);
+			else
+				fle6->f.src_mask = 128;
 
-		RTFREE_LOCKED(rt);
+			RTFREE_LOCKED(rt);
+		}
 	}
 
 	/* Push new flow at the and of hash. */
@@ -495,6 +505,8 @@
 
 	return (0);
 }
+#undef ipv6_masklen
+#undef RT_MASK6
 #endif
 
 
@@ -651,7 +663,7 @@
 /* Insert packet from into flow cache. */
 int
 ng_netflow_flow_add(priv_p priv, fib_export_p fe, struct ip *ip, caddr_t upper_ptr, uint8_t upper_proto, 
-		uint8_t is_frag, unsigned int src_if_index)
+		uint8_t flags, unsigned int src_if_index)
 {
 	register struct flow_entry	*fle, *fle1;
 	struct flow_hash_entry	*hsh;
@@ -770,7 +782,7 @@
 			}
 		}
 	} else				/* A new flow entry. */
-		error = hash_insert(priv, hsh, &r, plen, tcp_flags);
+		error = hash_insert(priv, hsh, &r, plen, flags, tcp_flags);
 
 	mtx_unlock(&hsh->mtx);
 
@@ -781,7 +793,7 @@
 /* Insert IPv6 packet from into flow cache. */
 int
 ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6, caddr_t upper_ptr, uint8_t upper_proto, 
-		uint8_t is_frag, unsigned int src_if_index)
+		uint8_t flags, unsigned int src_if_index)
 {
 	register struct flow_entry	*fle = NULL, *fle1;
 	register struct flow6_entry	*fle6;
@@ -811,7 +823,7 @@
 #if 0
 	r.r_tos = ip->ip_tos;
 #endif
-	if (is_frag == 0) {
+	if ((flags & NG_NETFLOW_IS_FRAG) == 0) {
 		switch(upper_proto) {
 		case IPPROTO_TCP:
 		{
@@ -896,7 +908,7 @@
 			}
 		}
 	} else				/* A new flow entry. */
-		error = hash6_insert(priv, hsh, &r, plen, tcp_flags);
+		error = hash6_insert(priv, hsh, &r, plen, flags, tcp_flags);
 
 	mtx_unlock(&hsh->mtx);
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/netflow/netflow_v9.c
--- a/head/sys/netgraph/netflow/netflow_v9.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/netflow/netflow_v9.c	Wed Jul 25 16:47:10 2012 +0300
@@ -23,11 +23,11 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * 	$FreeBSD: head/sys/netgraph/netflow/netflow_v9.c 223706 2011-07-01 08:27:03Z glebius $
+ * 	$FreeBSD: head/sys/netgraph/netflow/netflow_v9.c 237164 2012-06-16 13:55:31Z melifaro $
  */
 
 static const char rcs_id[] =
-    "@(#) $FreeBSD: head/sys/netgraph/netflow/netflow_v9.c 223706 2011-07-01 08:27:03Z glebius $";
+    "@(#) $FreeBSD: head/sys/netgraph/netflow/netflow_v9.c 237164 2012-06-16 13:55:31Z melifaro $";
 
 #include "opt_inet6.h"
 #include "opt_route.h"
@@ -222,7 +222,7 @@
 	header->unix_secs  = htonl(ts.tv_sec);
 	header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1));
 	header->count = htons(t->count);
-	header->source_id = htonl(NG_NODE_ID(priv->node));
+	header->source_id = htonl(fe->domain_id);
 
 	if (priv->export9 != NULL)
 		NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags);
@@ -416,16 +416,14 @@
 		 * Check if we need to insert templates into packet
 		 */
 		
-		struct timespec ts;
 		struct netflow_v9_flowset_header	*fl;
 	
-		getnanotime(&ts);
-		if ((ts.tv_sec >= priv->templ_time + fe->templ_last_ts) ||
+		if ((time_uptime >= priv->templ_time + fe->templ_last_ts) ||
 				(fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) {
 
-			atomic_store_rel_32(&fe->templ_last_ts, ts.tv_sec);
-			atomic_store_rel_32(&fe->templ_last_pkt, fe->sent_packets);
-	
+			fe->templ_last_ts = time_uptime;
+			fe->templ_last_pkt = fe->sent_packets;
+
 			fl = priv->v9_flowsets[0];
 			m_append(m, ntohs(fl->length), (void *)fl);
 			t->flow_header = m->m_len;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/netflow/ng_netflow.c
--- a/head/sys/netgraph/netflow/ng_netflow.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/netflow/ng_netflow.c	Wed Jul 25 16:47:10 2012 +0300
@@ -29,7 +29,7 @@
  */
 
 static const char rcs_id[] =
-    "@(#) $FreeBSD: head/sys/netgraph/netflow/ng_netflow.c 232921 2012-03-13 11:08:40Z melifaro $";
+    "@(#) $FreeBSD: head/sys/netgraph/netflow/ng_netflow.c 237227 2012-06-18 13:56:36Z melifaro $";
 
 #include "opt_inet6.h"
 #include "opt_route.h"
@@ -560,8 +560,8 @@
 	struct ip6_hdr *ip6 = NULL;
 	struct m_tag *mtag;
 	int pullup_len = 0, off;
-	uint8_t upper_proto = 0, is_frag = 0;
-	int error = 0, bypass = 0, acct = 0;
+	uint8_t acct = 0, bypass = 0, flags = 0, upper_proto = 0;
+	int error = 0, l3_off = 0;
 	unsigned int src_if_index;
 	caddr_t upper_ptr = NULL;
 	fib_export_p fe;	
@@ -619,6 +619,9 @@
 		}
 	}
 
+	/* Import configuration flags related to flow creation */
+	flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS;
+
 	NGI_GET_M(item, m);
 	m_old = m;
 
@@ -666,6 +669,7 @@
 			M_CHECK(sizeof(struct ip));
 			eh = mtod(m, struct ether_header *);
 			ip = (struct ip *)(eh + 1);
+			l3_off = sizeof(struct ether_header);
 			break;
 #ifdef INET6
 		case ETHERTYPE_IPV6:
@@ -676,6 +680,7 @@
 			M_CHECK(sizeof(struct ip6_hdr));
 			eh = mtod(m, struct ether_header *);
 			ip6 = (struct ip6_hdr *)(eh + 1);
+			l3_off = sizeof(struct ether_header);
 			break;
 #endif
 		case ETHERTYPE_VLAN:
@@ -686,6 +691,7 @@
 			    sizeof(struct ether_header));
 			evh = mtod(m, struct ether_vlan_header *);
 			etype = ntohs(evh->evl_proto);
+			l3_off = sizeof(struct ether_vlan_header);
 
 			if (etype == ETHERTYPE_IP) {
 				M_CHECK(sizeof(struct ip));
@@ -707,12 +713,13 @@
 	case DLT_RAW:		/* IP packets */
 		M_CHECK(sizeof(struct ip));
 		ip = mtod(m, struct ip *);
+		/* l3_off is already zero */
 #ifdef INET6
 		/* If INET6 is not defined IPv6 packets will be discarded in ng_netflow_flow_add() */
 		if (ip->ip_v == IP6VERSION) {
 			/* IPv6 packet */
 			ip = NULL;
-			M_CHECK(sizeof(struct ip6_hdr));
+			M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip));
 			ip6 = mtod(m, struct ip6_hdr *);
 		}
 #endif
@@ -755,7 +762,7 @@
 		}
 	} else if (ip != NULL) {
 		/* Nothing to save except upper layer proto, since this is packet fragment */
-		is_frag = 1;
+		flags |= NG_NETFLOW_IS_FRAG;
 		upper_proto = ip->ip_p;
 		if ((ip->ip_v != IPVERSION) ||
 		    ((ip->ip_hl << 2) < sizeof(struct ip)))
@@ -817,14 +824,17 @@
 				upper_proto = ip6f->ip6f_nxt;
 				hdr_off = sizeof(struct ip6_frag);
 				off += hdr_off;
-				is_frag = 1;
+				flags |= NG_NETFLOW_IS_FRAG;
 				goto loopend;
 
 #if 0				
 			case IPPROTO_NONE:
 				goto loopend;
 #endif
-			/* Any unknow header (new extension or IPv6/IPv4 header for tunnels) */
+			/*
+			 * Any unknow header (new extension or IPv6/IPv4
+			 * header for tunnels) ends loop.
+			 */
 			default:
 				goto loopend;
 			}
@@ -842,56 +852,11 @@
 	/* Just in case of real reallocation in M_CHECK() / m_pullup() */
 	if (m != m_old) {
 		atomic_fetchadd_32(&priv->info.nfinfo_realloc_mbuf, 1);
-		ip = NULL;
-		ip6 = NULL;
-		switch (iface->info.ifinfo_dlt) {
-		case DLT_EN10MB:	/* Ethernet */
-		    {
-			struct ether_header *eh;
-	
-			eh = mtod(m, struct ether_header *);
-			switch (ntohs(eh->ether_type)) {
-			case ETHERTYPE_IP:
-				ip = (struct ip *)(eh + 1);
-				break;
-#ifdef INET6
-			case ETHERTYPE_IPV6:
-				ip6 = (struct ip6_hdr *)(eh + 1);
-				break;
-#endif
-			case ETHERTYPE_VLAN:
-			    {
-				struct ether_vlan_header *evh;
-	
-				evh = mtod(m, struct ether_vlan_header *);
-				if (ntohs(evh->evl_proto) == ETHERTYPE_IP) {
-					ip = (struct ip *)(evh + 1);
-					break;
-#ifdef INET6
-				} else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) {
-					ip6 = (struct ip6_hdr *)(evh + 1);
-					break;
-#endif					
-				}
-			    }
-			default:
-				panic("ng_netflow entered deadcode");
-			}
-			break;
-		    }
-		case DLT_RAW:		/* IP packets */
-			ip = mtod(m, struct ip *);
-#ifdef INET6			
-			if (ip->ip_v == IP6VERSION) {
-				/* IPv6 packet */
-				ip = NULL;
-				ip6 = mtod(m, struct ip6_hdr *);
-			}
-#endif			
- 			break;
- 		default:
- 			panic("ng_netflow entered deadcode");
- 		}
+		/* Restore ip/ipv6 pointer */
+		if (ip != NULL)
+			ip = (struct ip *)(mtod(m, caddr_t) + l3_off);
+		else if (ip6 != NULL)
+			ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off);
  	}
 
 	upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);
@@ -924,10 +889,10 @@
 	}
 
 	if (ip != NULL)
-		error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, upper_proto, is_frag, src_if_index);
+		error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, upper_proto, flags, src_if_index);
 #ifdef INET6		
 	else if (ip6 != NULL)
-		error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, upper_proto, is_frag, src_if_index);
+		error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, upper_proto, flags, src_if_index);
 #endif
 	else
 		goto bypass;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/netflow/ng_netflow.h
--- a/head/sys/netgraph/netflow/ng_netflow.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/netflow/ng_netflow.h	Wed Jul 25 16:47:10 2012 +0300
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  *
  *	 $SourceForge: ng_netflow.h,v 1.26 2004/09/04 15:44:55 glebius Exp $
- *	 $FreeBSD: head/sys/netgraph/netflow/ng_netflow.h 232921 2012-03-13 11:08:40Z melifaro $
+ *	 $FreeBSD: head/sys/netgraph/netflow/ng_netflow.h 237227 2012-06-18 13:56:36Z melifaro $
  */
 
 #ifndef	_NG_NETFLOW_H_
@@ -111,10 +111,16 @@
 	uint32_t	active_timeout;		/* flow active timeout */
 };
 
-#define NG_NETFLOW_CONF_INGRESS		1
-#define NG_NETFLOW_CONF_EGRESS		2
-#define NG_NETFLOW_CONF_ONCE		4
-#define NG_NETFLOW_CONF_THISONCE	8
+#define NG_NETFLOW_CONF_INGRESS		0x01	/* Account on ingress */
+#define NG_NETFLOW_CONF_EGRESS		0x02	/* Account on egress */
+#define NG_NETFLOW_CONF_ONCE		0x04	/* Add tag to account only once */
+#define NG_NETFLOW_CONF_THISONCE	0x08	/* Account once in current node */
+#define NG_NETFLOW_CONF_NOSRCLOOKUP	0x10	/* No radix lookup on src */
+#define NG_NETFLOW_CONF_NODSTLOOKUP	0x20	/* No radix lookup on dst */
+
+#define NG_NETFLOW_IS_FRAG		0x01
+#define NG_NETFLOW_FLOW_FLAGS		(NG_NETFLOW_CONF_NOSRCLOOKUP|\
+					NG_NETFLOW_CONF_NODSTLOOKUP)
 
 /* This structure is passed to NGM_NETFLOW_SETCONFIG */
 struct ng_netflow_setconfig {
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/ng_ksocket.c
--- a/head/sys/netgraph/ng_ksocket.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/ng_ksocket.c	Wed Jul 25 16:47:10 2012 +0300
@@ -37,7 +37,7 @@
  *
  * Author: Archie Cobbs <archie at freebsd.org>
  *
- * $FreeBSD: head/sys/netgraph/ng_ksocket.c 229272 2012-01-02 12:12:10Z ed $
+ * $FreeBSD: head/sys/netgraph/ng_ksocket.c 235923 2012-05-24 18:22:57Z glebius $
  * $Whistle: ng_ksocket.c,v 1.1 1999/11/16 20:04:40 archie Exp $
  */
 
@@ -524,7 +524,9 @@
 	priv_p priv;
 
 	/* Allocate private structure */
-	priv = malloc(sizeof(*priv), M_NETGRAPH_KSOCKET, M_WAITOK | M_ZERO);
+	priv = malloc(sizeof(*priv), M_NETGRAPH_KSOCKET, M_NOWAIT | M_ZERO);
+	if (priv == NULL)
+		return (ENOMEM);
 
 	LIST_INIT(&priv->embryos);
 	/* cross link them */
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/ng_mppc.c
--- a/head/sys/netgraph/ng_mppc.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/ng_mppc.c	Wed Jul 25 16:47:10 2012 +0300
@@ -38,7 +38,7 @@
  * Author: Archie Cobbs <archie at freebsd.org>
  *
  * $Whistle: ng_mppc.c,v 1.4 1999/11/25 00:10:12 archie Exp $
- * $FreeBSD: head/sys/netgraph/ng_mppc.c 227293 2011-11-07 06:44:47Z ed $
+ * $FreeBSD: head/sys/netgraph/ng_mppc.c 235979 2012-05-25 07:46:24Z glebius $
  */
 
 /*
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netgraph/ng_patch.c
--- a/head/sys/netgraph/ng_patch.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netgraph/ng_patch.c	Wed Jul 25 16:47:10 2012 +0300
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netgraph/ng_patch.c 220767 2011-04-18 09:10:27Z ae $");
+__FBSDID("$FreeBSD: head/sys/netgraph/ng_patch.c 234574 2012-04-22 17:00:52Z melifaro $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -517,7 +517,7 @@
 			return (ENOMEM);
 		}
 		do_patch(priv, m);
-		m->m_flags |= priv->config->csum_flags;
+		m->m_pkthdr.csum_flags |= priv->config->csum_flags;
 	}
 
 	target = NULL;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/frag6.c
--- a/head/sys/netinet6/frag6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/frag6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/netinet6/frag6.c 238248 2012-07-08 15:30:24Z bz $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -221,6 +221,19 @@
 	/* offset now points to data portion */
 	offset += sizeof(struct ip6_frag);
 
+	/*
+	 * XXX-BZ RFC XXXX (draft-gont-6man-ipv6-atomic-fragments)
+	 * Handle "atomic" fragments (offset and m bit set to 0) upfront,
+	 * unrelated to any reassembly.  Just skip the fragment header.
+	 */
+	if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) {
+		/* XXX-BZ we want dedicated counters for this. */
+		V_ip6stat.ip6s_reassembled++;
+		in6_ifstat_inc(dstifp, ifs6_reass_ok);
+		*offp = offset;
+		return (ip6f->ip6f_nxt);
+	}
+
 	IP6Q_LOCK();
 
 	/*
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/icmp6.c
--- a/head/sys/netinet6/icmp6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/icmp6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/icmp6.c 231852 2012-02-17 02:39:58Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/icmp6.c 235953 2012-05-25 01:42:48Z bz $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -2418,23 +2418,23 @@
 	if (rt) {
 		if (rt->rt_gateway == NULL ||
 		    rt->rt_gateway->sa_family != AF_INET6) {
+			RTFREE_LOCKED(rt);
 			nd6log((LOG_ERR,
 			    "ICMP6 redirect rejected; no route "
 			    "with inet6 gateway found for redirect dst: %s\n",
 			    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
-			RTFREE_LOCKED(rt);
 			goto bad;
 		}
 
 		gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
 		if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
+			RTFREE_LOCKED(rt);
 			nd6log((LOG_ERR,
 			    "ICMP6 redirect rejected; "
 			    "not equal to gw-for-src=%s (must be same): "
 			    "%s\n",
 			    ip6_sprintf(ip6buf, gw6),
 			    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
-			RTFREE_LOCKED(rt);
 			goto bad;
 		}
 	} else {
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/in6.c
--- a/head/sys/netinet6/in6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/in6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/in6.c 232054 2012-02-23 18:21:37Z kmacy $");
+__FBSDID("$FreeBSD: head/sys/netinet6/in6.c 238222 2012-07-08 08:49:37Z bz $");
 
 #include "opt_compat.h"
 #include "opt_inet.h"
@@ -1330,6 +1330,7 @@
 	struct sockaddr_in6 mltaddr, mltmask;
 	struct in6_multi_mship *imm;
 	struct rtentry *rt;
+	struct sockaddr_in6 sin6;
 	int error;
 
 	/*
@@ -1356,6 +1357,21 @@
 	if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0)
 		return (error);
 
+	/*
+	 * As for the mltaddr above, proactively prepare the sin6 to avoid
+	 * rtentry un- and re-locking.
+	 */
+	if (ifa0 != NULL) {
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_len = sizeof(sin6);
+		sin6.sin6_family = AF_INET6;
+		memcpy(&sin6.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, 
+		    sizeof(sin6.sin6_addr));
+		error = in6_setscope(&sin6.sin6_addr, ifa0->ifa_ifp, NULL);
+		if (error != 0)
+			return (error);
+	}
+
 	rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB);
 	if (rt != NULL && rt->rt_gateway != NULL &&
 	    (memcmp(&satosin6(rt->rt_gateway)->sin6_addr, 
@@ -1382,15 +1398,7 @@
 			/*
 			 * Replace the gateway of the route.
 			 */
-			struct sockaddr_in6 sa;
-
-			bzero(&sa, sizeof(sa));
-			sa.sin6_len = sizeof(struct sockaddr_in6);
-			sa.sin6_family = AF_INET6;
-			memcpy(&sa.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, 
-			       sizeof(sa.sin6_addr));
-			in6_setscope(&sa.sin6_addr, ifa0->ifa_ifp, NULL);
-			memcpy(rt->rt_gateway, &sa, sizeof(sa));
+			memcpy(rt->rt_gateway, &sin6, sizeof(sin6));
 			RTFREE_LOCKED(rt);
 		}
 	} else {
@@ -1432,15 +1440,7 @@
 			/*
 			 * Replace the gateway of the route.
 			 */
-			struct sockaddr_in6 sa;
-
-			bzero(&sa, sizeof(sa));
-			sa.sin6_len = sizeof(struct sockaddr_in6);
-			sa.sin6_family = AF_INET6;
-			memcpy(&sa.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, 
-			       sizeof(sa.sin6_addr));
-			in6_setscope(&sa.sin6_addr, ifa0->ifa_ifp, NULL);
-			memcpy(rt->rt_gateway, &sa, sizeof(sa));
+			memcpy(rt->rt_gateway, &sin6, sizeof(sin6));
 			RTFREE_LOCKED(rt);
 		}
 	} else {
@@ -1667,14 +1667,19 @@
 			hostid = IFA_IN6(ifa);
 
 			/* prefixlen must be <= 64. */
-			if (64 < iflr->prefixlen)
+			if (64 < iflr->prefixlen) {
+				if (ifa != NULL)
+					ifa_free(ifa);
 				return EINVAL;
+			}
 			prefixlen = iflr->prefixlen;
 
 			/* hostid part must be zero. */
 			sin6 = (struct sockaddr_in6 *)&iflr->addr;
 			if (sin6->sin6_addr.s6_addr32[2] != 0 ||
 			    sin6->sin6_addr.s6_addr32[3] != 0) {
+				if (ifa != NULL)
+					ifa_free(ifa);
 				return EINVAL;
 			}
 		} else
@@ -2265,14 +2270,20 @@
 		IF_ADDR_RUNLOCK(ifp);
 		return (struct in6_ifaddr *)ifa;
 	}
-	IF_ADDR_RUNLOCK(ifp);
 
 	/* use the last-resort values, that are, deprecated addresses */
-	if (dep[0])
+	if (dep[0]) {
+		ifa_ref((struct ifaddr *)dep[0]);
+		IF_ADDR_RUNLOCK(ifp);
 		return dep[0];
-	if (dep[1])
+	}
+	if (dep[1]) {
+		ifa_ref((struct ifaddr *)dep[1]);
+		IF_ADDR_RUNLOCK(ifp);
 		return dep[1];
+	}
 
+	IF_ADDR_RUNLOCK(ifp);
 	return NULL;
 }
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/in6.h
--- a/head/sys/netinet6/in6.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/in6.h	Wed Jul 25 16:47:10 2012 +0300
@@ -58,7 +58,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)in.h	8.3 (Berkeley) 1/3/94
- * $FreeBSD: head/sys/netinet6/in6.h 230584 2012-01-26 12:04:19Z glebius $
+ * $FreeBSD: head/sys/netinet6/in6.h 235924 2012-05-24 18:25:09Z bz $
  */
 
 #ifndef __KAME_NETINET_IN_H_INCLUDED_
@@ -632,7 +632,9 @@
 
 #ifdef _KERNEL
 struct cmsghdr;
+struct ip6_hdr;
 
+int	in6_cksum_pseudo(struct ip6_hdr *, uint32_t, uint8_t, uint16_t);
 int	in6_cksum __P((struct mbuf *, u_int8_t, u_int32_t, u_int32_t));
 int	in6_localaddr __P((struct in6_addr *));
 int	in6_localip(struct in6_addr *);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/in6_cksum.c
--- a/head/sys/netinet6/in6_cksum.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/in6_cksum.c	Wed Jul 25 16:47:10 2012 +0300
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/netinet6/in6_cksum.c 235924 2012-05-24 18:25:09Z bz $");
 
 #include <sys/param.h>
 #include <sys/mbuf.h>
@@ -80,6 +80,66 @@
 #define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
 #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);}
 
+static int
+_in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
+{
+	int sum;
+	uint16_t scope, *w;
+	union {
+		u_int16_t phs[4];
+		struct {
+			u_int32_t	ph_len;
+			u_int8_t	ph_zero[3];
+			u_int8_t	ph_nxt;
+		} __packed ph;
+	} uph;
+
+	sum = csum;
+
+	/*
+	 * First create IP6 pseudo header and calculate a summary.
+	 */
+	uph.ph.ph_len = htonl(len);
+	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
+	uph.ph.ph_nxt = nxt;
+
+	/* Payload length and upper layer identifier. */
+	sum += uph.phs[0];  sum += uph.phs[1];
+	sum += uph.phs[2];  sum += uph.phs[3];
+
+	/* IPv6 source address. */
+	scope = in6_getscope(&ip6->ip6_src);
+	w = (u_int16_t *)&ip6->ip6_src;
+	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+	if (scope != 0)
+		sum -= scope;
+
+	/* IPv6 destination address. */
+	scope = in6_getscope(&ip6->ip6_dst);
+	w = (u_int16_t *)&ip6->ip6_dst;
+	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+	if (scope != 0)
+		sum -= scope;
+
+	return (sum);
+}
+
+int
+in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
+{
+	int sum;
+	union {
+		u_int16_t s[2];
+		u_int32_t l;
+	} l_util;
+
+	sum = _in6_cksum_pseudo(ip6, len, nxt, csum);
+	REDUCE;
+	return (sum);
+}
+
 /*
  * m MUST contain a contiguous IP6 header.
  * off is an offset where TCP/UDP/ICMP6 header starts.
@@ -89,12 +149,10 @@
 int
 in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len)
 {
-	u_int16_t *w;
-	int sum = 0;
-	int mlen = 0;
-	int byte_swapped = 0;
 	struct ip6_hdr *ip6;
-	struct in6_addr in6;
+	u_int16_t *w, scope;
+	int byte_swapped, mlen;
+	int sum;
 	union {
 		u_int16_t phs[4];
 		struct {
@@ -112,42 +170,38 @@
 		u_int32_t l;
 	} l_util;
 
-	/* sanity check */
-	if (m->m_pkthdr.len < off + len) {
-		panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)",
-			m->m_pkthdr.len, off, len);
-	}
-
-	bzero(&uph, sizeof(uph));
+	/* Sanity check. */
+	KASSERT(m->m_pkthdr.len >= off + len, ("%s: mbuf len (%d) < off(%d)+"
+	    "len(%d)", __func__, m->m_pkthdr.len, off, len));
 
 	/*
 	 * First create IP6 pseudo header and calculate a summary.
 	 */
-	ip6 = mtod(m, struct ip6_hdr *);
 	uph.ph.ph_len = htonl(len);
+	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
 	uph.ph.ph_nxt = nxt;
 
-	/*
-	 * IPv6 source address.
-	 * XXX: we'd like to avoid copying the address, but we can't due to
-	 * the possibly embedded scope zone ID.
-	 */
-	in6 = ip6->ip6_src;
-	in6_clearscope(&in6);
-	w = (u_int16_t *)&in6;
+	/* Payload length and upper layer identifier. */
+	sum = uph.phs[0];  sum += uph.phs[1];
+	sum += uph.phs[2];  sum += uph.phs[3];
+
+	ip6 = mtod(m, struct ip6_hdr *);
+
+	/* IPv6 source address. */
+	scope = in6_getscope(&ip6->ip6_src);
+	w = (u_int16_t *)&ip6->ip6_src;
 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+	if (scope != 0)
+		sum -= scope;
 
-	/* IPv6 destination address */
-	in6 = ip6->ip6_dst;
-	in6_clearscope(&in6);
-	w = (u_int16_t *)&in6;
+	/* IPv6 destination address. */
+	scope = in6_getscope(&ip6->ip6_dst);
+	w = (u_int16_t *)&ip6->ip6_dst;
 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
-
-	/* Payload length and upper layer identifier */
-	sum += uph.phs[0];  sum += uph.phs[1];
-	sum += uph.phs[2];  sum += uph.phs[3];
+	if (scope != 0)
+		sum -= scope;
 
 	/*
 	 * Secondly calculate a summary of the first mbuf excluding offset.
@@ -167,14 +221,16 @@
 	/*
 	 * Force to even boundary.
 	 */
-	if ((1 & (long) w) && (mlen > 0)) {
+	if ((1 & (long)w) && (mlen > 0)) {
 		REDUCE;
 		sum <<= 8;
 		s_util.c[0] = *(u_char *)w;
 		w = (u_int16_t *)((char *)w + 1);
 		mlen--;
 		byte_swapped = 1;
-	}
+	} else
+		byte_swapped = 0;
+	
 	/*
 	 * Unroll the loop to make overhead from
 	 * branches &c small.
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/in6_src.c
--- a/head/sys/netinet6/in6_src.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/in6_src.c	Wed Jul 25 16:47:10 2012 +0300
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/in6_src.c 232127 2012-02-24 20:06:04Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/in6_src.c 237459 2012-06-22 21:26:35Z bz $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -597,6 +597,7 @@
 		if (ron->ro_rt == NULL) {
 			in6_rtalloc(ron, fibnum); /* multi path case? */
 			if (ron->ro_rt == NULL) {
+				/* XXX-BZ WT.? */
 				if (ron->ro_rt) {
 					RTFREE(ron->ro_rt);
 					ron->ro_rt = NULL;
@@ -873,8 +874,7 @@
 			RTFREE(ro6.ro_rt);
 			if (lifp)
 				return (ND_IFINFO(lifp)->chlim);
-		} else
-			return (V_ip6_defhlim);
+		}
 	}
 	return (V_ip6_defhlim);
 }
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_forward.c
--- a/head/sys/netinet6/ip6_forward.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_forward.c	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/ip6_forward.c 231852 2012-02-17 02:39:58Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/ip6_forward.c 236332 2012-05-30 20:56:07Z tuexen $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -581,15 +581,13 @@
 			m->m_flags |= M_FASTFWD_OURS;
 			if (m->m_pkthdr.rcvif == NULL)
 				m->m_pkthdr.rcvif = V_loif;
-			if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+			if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
 				m->m_pkthdr.csum_flags |=
-				    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+				    CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
 				m->m_pkthdr.csum_data = 0xffff;
 			}
-			m->m_pkthdr.csum_flags |=
-			    CSUM_IP_CHECKED | CSUM_IP_VALID;
 #ifdef SCTP
-			if (m->m_pkthdr.csum_flags & CSUM_SCTP)
+			if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
 				m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
 #endif
 			error = netisr_queue(NETISR_IPV6, m);
@@ -603,15 +601,15 @@
 	if (m->m_flags & M_FASTFWD_OURS) {
 		if (m->m_pkthdr.rcvif == NULL)
 			m->m_pkthdr.rcvif = V_loif;
-		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
 			m->m_pkthdr.csum_flags |=
-			    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+			    CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
 			m->m_pkthdr.csum_data = 0xffff;
 		}
 #ifdef SCTP
-		if (m->m_pkthdr.csum_flags & CSUM_SCTP)
-		m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
-#endif   
+		if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
+			m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+#endif
 		error = netisr_queue(NETISR_IPV6, m);
 		goto out;
 	}
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_input.c
--- a/head/sys/netinet6/ip6_input.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_input.c	Wed Jul 25 16:47:10 2012 +0300
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/ip6_input.c 232379 2012-03-02 07:23:28Z hrs $");
+__FBSDID("$FreeBSD: head/sys/netinet6/ip6_input.c 236958 2012-06-12 13:57:56Z tuexen $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -146,6 +146,9 @@
 
 static void ip6_init2(void *);
 static struct ip6aux *ip6_setdstifaddr(struct mbuf *, struct in6_ifaddr *);
+static struct ip6aux *ip6_addaux(struct mbuf *);
+static struct ip6aux *ip6_findaux(struct mbuf *m);
+static void ip6_delaux (struct mbuf *);
 static int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *);
 #ifdef PULLDOWN_TEST
 static struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int);
@@ -328,6 +331,83 @@
 /* This must be after route_init(), which is now SI_ORDER_THIRD */
 SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL);
 
+static int
+ip6_input_hbh(struct mbuf *m, uint32_t *plen, uint32_t *rtalert, int *off,
+    int *nxt, int *ours)
+{
+	struct ip6_hdr *ip6;
+	struct ip6_hbh *hbh;
+
+	if (ip6_hopopts_input(plen, rtalert, &m, off)) {
+#if 0	/*touches NULL pointer*/
+		in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+#endif
+		goto out;	/* m have already been freed */
+	}
+
+	/* adjust pointer */
+	ip6 = mtod(m, struct ip6_hdr *);
+
+	/*
+	 * if the payload length field is 0 and the next header field
+	 * indicates Hop-by-Hop Options header, then a Jumbo Payload
+	 * option MUST be included.
+	 */
+	if (ip6->ip6_plen == 0 && *plen == 0) {
+		/*
+		 * Note that if a valid jumbo payload option is
+		 * contained, ip6_hopopts_input() must set a valid
+		 * (non-zero) payload length to the variable plen.
+		 */
+		V_ip6stat.ip6s_badoptions++;
+		in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+		in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
+		icmp6_error(m, ICMP6_PARAM_PROB,
+			    ICMP6_PARAMPROB_HEADER,
+			    (caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
+		goto out;
+	}
+#ifndef PULLDOWN_TEST
+	/* ip6_hopopts_input() ensures that mbuf is contiguous */
+	hbh = (struct ip6_hbh *)(ip6 + 1);
+#else
+	IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr),
+		sizeof(struct ip6_hbh));
+	if (hbh == NULL) {
+		V_ip6stat.ip6s_tooshort++;
+		goto out;
+	}
+#endif
+	*nxt = hbh->ip6h_nxt;
+
+	/*
+	 * If we are acting as a router and the packet contains a
+	 * router alert option, see if we know the option value.
+	 * Currently, we only support the option value for MLD, in which
+	 * case we should pass the packet to the multicast routing
+	 * daemon.
+	 */
+	if (*rtalert != ~0) {
+		switch (*rtalert) {
+		case IP6OPT_RTALERT_MLD:
+			if (V_ip6_forwarding)
+				*ours = 1;
+			break;
+		default:
+			/*
+			 * RFC2711 requires unrecognized values must be
+			 * silently ignored.
+			 */
+			break;
+		}
+	}
+
+	return (0);
+
+out:
+	return (1);
+}
+
 void
 ip6_input(struct mbuf *m)
 {
@@ -799,19 +879,23 @@
 	 * as our interface address (e.g. multicast addresses, addresses
 	 * within FAITH prefixes and such).
 	 */
-	if (deliverifp && !ip6_getdstifaddr(m)) {
+	if (deliverifp) {
 		struct in6_ifaddr *ia6;
 
-		ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst);
-		if (ia6) {
-			if (!ip6_setdstifaddr(m, ia6)) {
-				/*
-				 * XXX maybe we should drop the packet here,
-				 * as we could not provide enough information
-				 * to the upper layers.
-				 */
+ 		if ((ia6 = ip6_getdstifaddr(m)) != NULL) {
+			ifa_free(&ia6->ia_ifa);
+		} else {
+			ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst);
+			if (ia6) {
+				if (!ip6_setdstifaddr(m, ia6)) {
+					/*
+					 * XXX maybe we should drop the packet here,
+					 * as we could not provide enough information
+					 * to the upper layers.
+					 */
+				}
+				ifa_free(&ia6->ia_ifa);
 			}
-			ifa_free(&ia6->ia_ifa);
 		}
 	}
 
@@ -822,71 +906,11 @@
 	 */
 	plen = (u_int32_t)ntohs(ip6->ip6_plen);
 	if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
-		struct ip6_hbh *hbh;
+		int error;
 
-		if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) {
-#if 0	/*touches NULL pointer*/
-			in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
-#endif
-			goto out;	/* m have already been freed */
-		}
-
-		/* adjust pointer */
-		ip6 = mtod(m, struct ip6_hdr *);
-
-		/*
-		 * if the payload length field is 0 and the next header field
-		 * indicates Hop-by-Hop Options header, then a Jumbo Payload
-		 * option MUST be included.
-		 */
-		if (ip6->ip6_plen == 0 && plen == 0) {
-			/*
-			 * Note that if a valid jumbo payload option is
-			 * contained, ip6_hopopts_input() must set a valid
-			 * (non-zero) payload length to the variable plen.
-			 */
-			V_ip6stat.ip6s_badoptions++;
-			in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
-			in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
-			icmp6_error(m, ICMP6_PARAM_PROB,
-				    ICMP6_PARAMPROB_HEADER,
-				    (caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
+		error = ip6_input_hbh(m, &plen, &rtalert, &off, &nxt, &ours);
+		if (error != 0)
 			goto out;
-		}
-#ifndef PULLDOWN_TEST
-		/* ip6_hopopts_input() ensures that mbuf is contiguous */
-		hbh = (struct ip6_hbh *)(ip6 + 1);
-#else
-		IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr),
-			sizeof(struct ip6_hbh));
-		if (hbh == NULL) {
-			V_ip6stat.ip6s_tooshort++;
-			goto out;
-		}
-#endif
-		nxt = hbh->ip6h_nxt;
-
-		/*
-		 * If we are acting as a router and the packet contains a
-		 * router alert option, see if we know the option value.
-		 * Currently, we only support the option value for MLD, in which
-		 * case we should pass the packet to the multicast routing
-		 * daemon.
-		 */
-		if (rtalert != ~0) {
-			switch (rtalert) {
-			case IP6OPT_RTALERT_MLD:
-				if (V_ip6_forwarding)
-					ours = 1;
-				break;
-			default:
-				/*
-				 * RFC2711 requires unrecognized values must be
-				 * silently ignored.
-				 */
-				break;
-			}
-		}
 	} else
 		nxt = ip6->ip6_nxt;
 
@@ -1297,19 +1321,28 @@
 	}
 #endif
 
-	if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
-		if (v4only != NULL)
-			*v4only = 1;
-		return (mp);
-	}
-
 #define IS2292(inp, x, y)	(((inp)->inp_flags & IN6P_RFC2292) ? (x) : (y))
 	/* RFC 2292 sec. 5 */
 	if ((inp->inp_flags & IN6P_PKTINFO) != 0) {
 		struct in6_pktinfo pi6;
 
-		bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr));
-		in6_clearscope(&pi6.ipi6_addr);	/* XXX */
+		if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+#ifdef INET
+			struct ip *ip;
+
+			ip = mtod(m, struct ip *);
+			pi6.ipi6_addr.s6_addr32[0] = 0;
+			pi6.ipi6_addr.s6_addr32[1] = 0;
+			pi6.ipi6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
+			pi6.ipi6_addr.s6_addr32[3] = ip->ip_dst.s_addr;
+#else
+			/* We won't hit this code */
+			bzero(&pi6.ipi6_addr, sizeof(struct in6_addr));
+#endif
+		} else {	
+			bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr));
+			in6_clearscope(&pi6.ipi6_addr);	/* XXX */
+		}
 		pi6.ipi6_ifindex =
 		    (m && m->m_pkthdr.rcvif) ? m->m_pkthdr.rcvif->if_index : 0;
 
@@ -1321,8 +1354,21 @@
 	}
 
 	if ((inp->inp_flags & IN6P_HOPLIMIT) != 0) {
-		int hlim = ip6->ip6_hlim & 0xff;
+		int hlim;
 
+		if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+#ifdef INET
+			struct ip *ip;
+
+			ip = mtod(m, struct ip *);
+			hlim = ip->ip_ttl;
+#else
+			/* We won't hit this code */
+			hlim = 0;
+#endif
+		} else {
+			hlim = ip6->ip6_hlim & 0xff;
+		}
 		*mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int),
 		    IS2292(inp, IPV6_2292HOPLIMIT, IPV6_HOPLIMIT),
 		    IPPROTO_IPV6);
@@ -1330,8 +1376,40 @@
 			mp = &(*mp)->m_next;
 	}
 
-	if (v4only != NULL)
-		*v4only = 0;
+	if ((inp->inp_flags & IN6P_TCLASS) != 0) {
+		int tclass;
+
+		if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+#ifdef INET
+			struct ip *ip;
+
+			ip = mtod(m, struct ip *);
+			tclass = ip->ip_tos;
+#else
+			/* We won't hit this code */
+			tclass = 0;
+#endif
+		} else {
+			u_int32_t flowinfo;
+
+			flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
+			flowinfo >>= 20;
+			tclass = flowinfo & 0xff;
+		}
+		*mp = sbcreatecontrol((caddr_t) &tclass, sizeof(int),
+		    IPV6_TCLASS, IPPROTO_IPV6);
+		if (*mp)
+			mp = &(*mp)->m_next;
+	}
+
+	if (v4only != NULL) {
+		if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+			*v4only = 1;
+		} else {
+			*v4only = 0;
+		}
+	}
+
 	return (mp);
 }
 
@@ -1345,20 +1423,6 @@
 	if (v4only)
 		return;
 
-	if ((in6p->inp_flags & IN6P_TCLASS) != 0) {
-		u_int32_t flowinfo;
-		int tclass;
-
-		flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
-		flowinfo >>= 20;
-
-		tclass = flowinfo & 0xff;
-		*mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass),
-		    IPV6_TCLASS, IPPROTO_IPV6);
-		if (*mp)
-			mp = &(*mp)->m_next;
-	}
-
 	/*
 	 * IPV6_HOPOPTS socket option.  Recall that we required super-user
 	 * privilege for the option (see ip6_ctloutput), but it might be too
@@ -1772,7 +1836,7 @@
 	}
 }
 
-struct ip6aux *
+static struct ip6aux *
 ip6_addaux(struct mbuf *m)
 {
 	struct m_tag *mtag;
@@ -1789,7 +1853,7 @@
 	return mtag ? (struct ip6aux *)(mtag + 1) : NULL;
 }
 
-struct ip6aux *
+static struct ip6aux *
 ip6_findaux(struct mbuf *m)
 {
 	struct m_tag *mtag;
@@ -1798,7 +1862,7 @@
 	return mtag ? (struct ip6aux *)(mtag + 1) : NULL;
 }
 
-void
+static void
 ip6_delaux(struct mbuf *m)
 {
 	struct m_tag *mtag;
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_ipsec.c
--- a/head/sys/netinet6/ip6_ipsec.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_ipsec.c	Wed Jul 25 16:47:10 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/ip6_ipsec.c 230442 2012-01-22 02:13:19Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/ip6_ipsec.c 236170 2012-05-28 09:30:13Z bz $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -291,6 +291,7 @@
 		/*
 		 * Do delayed checksums now because we send before
 		 * this is done in the normal processing path.
+		 * XXX-BZ CSUM_DELAY_DATA_IPV6?
 		 */
 		if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
 			ipseclog((LOG_DEBUG,
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_mroute.c
--- a/head/sys/netinet6/ip6_mroute.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_mroute.c	Wed Jul 25 16:47:10 2012 +0300
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/ip6_mroute.c 228700 2011-12-19 05:50:34Z maxim $");
+__FBSDID("$FreeBSD: head/sys/netinet6/ip6_mroute.c 238016 2012-07-02 19:44:18Z glebius $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -717,7 +717,6 @@
 	mifp->m6_pkt_out   = 0;
 	mifp->m6_bytes_in  = 0;
 	mifp->m6_bytes_out = 0;
-	bzero(&mifp->m6_route, sizeof(mifp->m6_route));
 
 	/* Adjust nummifs up if the mifi is higher than nummifs */
 	if (nummifs <= mifcp->mif6c_mifi)
@@ -1576,11 +1575,8 @@
 	struct mbuf *mb_copy;
 	struct ifnet *ifp = mifp->m6_ifp;
 	int error = 0;
-	struct sockaddr_in6 *dst6;
 	u_long linkmtu;
 
-	dst6 = &mifp->m6_route.ro_dst;
-
 	/*
 	 * Make a new reference to the packet; make sure that
 	 * the IPv6 header is actually copied, not just referenced,
@@ -1610,8 +1606,8 @@
 		/* XXX: ip6_output will override ip6->ip6_hlim */
 		im6o.im6o_multicast_hlim = ip6->ip6_hlim;
 		im6o.im6o_multicast_loop = 1;
-		error = ip6_output(mb_copy, NULL, &mifp->m6_route,
-				   IPV6_FORWARDING, &im6o, NULL, NULL);
+		error = ip6_output(mb_copy, NULL, NULL, IPV6_FORWARDING, &im6o,
+		    NULL, NULL);
 
 #ifdef MRT6DEBUG
 		if (V_mrt6debug & DEBUG_XMIT)
@@ -1626,10 +1622,13 @@
 	 * loop back a copy now.
 	 */
 	if (in6_mcast_loop) {
-		dst6->sin6_len = sizeof(struct sockaddr_in6);
-		dst6->sin6_family = AF_INET6;
-		dst6->sin6_addr = ip6->ip6_dst;
-		ip6_mloopback(ifp, m, &mifp->m6_route.ro_dst);
+		struct sockaddr_in6 dst6;
+
+		bzero(&dst6, sizeof(dst6));
+		dst6.sin6_len = sizeof(struct sockaddr_in6);
+		dst6.sin6_family = AF_INET6;
+		dst6.sin6_addr = ip6->ip6_dst;
+		ip6_mloopback(ifp, m, &dst6);
 	}
 
 	/*
@@ -1638,15 +1637,18 @@
 	 */
 	linkmtu = IN6_LINKMTU(ifp);
 	if (mb_copy->m_pkthdr.len <= linkmtu || linkmtu < IPV6_MMTU) {
-		dst6->sin6_len = sizeof(struct sockaddr_in6);
-		dst6->sin6_family = AF_INET6;
-		dst6->sin6_addr = ip6->ip6_dst;
+		struct sockaddr_in6 dst6;
+
+		bzero(&dst6, sizeof(dst6));
+		dst6.sin6_len = sizeof(struct sockaddr_in6);
+		dst6.sin6_family = AF_INET6;
+		dst6.sin6_addr = ip6->ip6_dst;
 		/*
 		 * We just call if_output instead of nd6_output here, since
 		 * we need no ND for a multicast forwarded packet...right?
 		 */
 		error = (*ifp->if_output)(ifp, mb_copy,
-		    (struct sockaddr *)&mifp->m6_route.ro_dst, NULL);
+		    (struct sockaddr *)&dst6, NULL);
 #ifdef MRT6DEBUG
 		if (V_mrt6debug & DEBUG_XMIT)
 			log(LOG_DEBUG, "phyint_send on mif %d err %d\n",
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_mroute.h
--- a/head/sys/netinet6/ip6_mroute.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_mroute.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	$KAME: ip6_mroute.h,v 1.19 2001/06/14 06:12:55 suz Exp $
- * $FreeBSD$
+ * $FreeBSD: head/sys/netinet6/ip6_mroute.h 238016 2012-07-02 19:44:18Z glebius $
  */
 
 /*	BSDI ip_mroute.h,v 2.5 1996/10/11 16:01:48 pjd Exp	*/
@@ -212,7 +212,6 @@
 	u_quad_t	m6_pkt_out;	/* # pkts out on interface           */
 	u_quad_t	m6_bytes_in;	/* # bytes in on interface	     */
 	u_quad_t	m6_bytes_out;	/* # bytes out on interface	     */
-	struct route_in6 m6_route;	/* cached route */
 #ifdef notyet
 	u_int		m6_rsvp_on;	/* RSVP listening on this vif */
 	struct socket   *m6_rsvpd;	/* RSVP daemon socket */
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_output.c
--- a/head/sys/netinet6/ip6_output.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_output.c	Wed Jul 25 16:47:10 2012 +0300
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/ip6_output.c 232127 2012-02-24 20:06:04Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/ip6_output.c 238092 2012-07-04 07:37:53Z glebius $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -83,6 +83,8 @@
 #include <sys/syslog.h>
 #include <sys/ucred.h>
 
+#include <machine/in_cksum.h>
+
 #include <net/if.h>
 #include <net/netisr.h>
 #include <net/route.h>
@@ -182,12 +184,38 @@
 	}\
     } while (/*CONSTCOND*/ 0)
 
+static void
+in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset)
+{
+	u_short csum;
+
+	csum = in_cksum_skip(m, offset + plen, offset);
+	if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6 && csum == 0)
+		csum = 0xffff;
+	offset += m->m_pkthdr.csum_data;	/* checksum offset */
+
+	if (offset + sizeof(u_short) > m->m_len) {
+		printf("%s: delayed m_pullup, m->len: %d off: %d\n",
+		    __func__, m->m_len, offset);
+		/*
+		 * XXX this should not happen, but if it does, the correct
+		 * behavior may be to insert the checksum in the appropriate
+		 * next mbuf in the chain.
+		 */
+		return;
+	}
+	*(u_short *)(m->m_data + offset) = csum;
+}
+
 /*
  * IP6 output. The packet in mbuf chain m contains a skeletal IP6
  * header (with pri, len, nxt, hlim, src, dst).
  * This function may modify ver and hlim only.
  * The mbuf chain containing the packet will be freed.
  * The mbuf opt, if present, will not be freed.
+ * If route_in6 ro is present and has ro_rt initialized, route lookup would be
+ * skipped and ro->ro_rt would be used. If ro is present but ro->ro_rt is NULL,
+ * then result of route lookup is stored in ro->ro_rt.
  *
  * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and
  * nd_ifinfo.linkmtu is u_int32_t.  so we use u_long to hold largest one,
@@ -218,12 +246,9 @@
 	struct in6_addr finaldst, src0, dst0;
 	u_int32_t zone;
 	struct route_in6 *ro_pmtu = NULL;
-	int flevalid = 0;
 	int hdrsplit = 0;
 	int needipsec = 0;
-#ifdef SCTP
-	int sw_csum;
-#endif
+	int sw_csum, tso;
 #ifdef IPSEC
 	struct ipsec_output_state state;
 	struct ip6_rthdr *rh = NULL;
@@ -483,22 +508,20 @@
 		ro = &opt->ip6po_route;
 	dst = (struct sockaddr_in6 *)&ro->ro_dst;
 #ifdef FLOWTABLE
-	if (ro == &ip6route) {
+	if (ro->ro_rt == NULL) {
 		struct flentry *fle;
-		
+
 		/*
 		 * The flow table returns route entries valid for up to 30
 		 * seconds; we rely on the remainder of ip_output() taking no
 		 * longer than that long for the stability of ro_rt.  The
 		 * flow ID assignment must have happened before this point.
 		 */
-		if ((fle = flowtable_lookup_mbuf(V_ip6_ft, m, AF_INET6)) != NULL) {
+		fle = flowtable_lookup_mbuf(V_ip6_ft, m, AF_INET6);
+		if (fle != NULL)
 			flow_to_route_in6(fle, ro);
-			if (ro->ro_rt != NULL && ro->ro_lle != NULL)
-				flevalid = 1;
-		}
 	}
-#endif	
+#endif
 again:
 	/*
 	 * if specified, try to fill in the traffic class field.
@@ -604,7 +627,7 @@
 	dst_sa.sin6_family = AF_INET6;
 	dst_sa.sin6_len = sizeof(dst_sa);
 	dst_sa.sin6_addr = ip6->ip6_dst;
-	if (flevalid) {
+	if (ro->ro_rt) {
 		rt = ro->ro_rt;
 		ifp = ro->ro_rt->rt_ifp;
 	} else if ((error = in6_selectroute_fib(&dst_sa, opt, im6o, ro,
@@ -644,7 +667,7 @@
 
 	/*
 	 * The outgoing interface must be in the zone of source and
-	 * destination addresses.  
+	 * destination addresses.
 	 */
 	origifp = ifp;
 
@@ -670,7 +693,7 @@
 		goto badscope;
 	}
 
-	/* We should use ia_ifp to support the case of 
+	/* We should use ia_ifp to support the case of
 	 * sending packets to an address of our own.
 	 */
 	if (ia != NULL && ia->ia_ifp)
@@ -862,15 +885,13 @@
 			m->m_flags |= M_FASTFWD_OURS;
 			if (m->m_pkthdr.rcvif == NULL)
 				m->m_pkthdr.rcvif = V_loif;
-			if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+			if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
 				m->m_pkthdr.csum_flags |=
-				    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+				    CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
 				m->m_pkthdr.csum_data = 0xffff;
 			}
-			m->m_pkthdr.csum_flags |=
-			    CSUM_IP_CHECKED | CSUM_IP_VALID;
 #ifdef SCTP
-			if (m->m_pkthdr.csum_flags & CSUM_SCTP)
+			if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
 				m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
 #endif
 			error = netisr_queue(NETISR_IPV6, m);
@@ -884,15 +905,15 @@
 	if (m->m_flags & M_FASTFWD_OURS) {
 		if (m->m_pkthdr.rcvif == NULL)
 			m->m_pkthdr.rcvif = V_loif;
-		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
 			m->m_pkthdr.csum_flags |=
-			    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+			    CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
 			m->m_pkthdr.csum_data = 0xffff;
 		}
 #ifdef SCTP
-		if (m->m_pkthdr.csum_flags & CSUM_SCTP)
-		m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
-#endif   
+		if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
+			m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+#endif
 		error = netisr_queue(NETISR_IPV6, m);
 		goto done;
 	}
@@ -927,16 +948,32 @@
 	 * 4: if dontfrag == 1 && alwaysfrag == 1
 	 *	error, as we cannot handle this conflicting request
 	 */
+	sw_csum = m->m_pkthdr.csum_flags;
+	if (!hdrsplit) {
+		tso = ((sw_csum & ifp->if_hwassist & CSUM_TSO) != 0) ? 1 : 0;
+		sw_csum &= ~ifp->if_hwassist;
+	} else
+		tso = 0;
+	/*
+	 * If we added extension headers, we will not do TSO and calculate the
+	 * checksums ourselves for now.
+	 * XXX-BZ  Need a framework to know when the NIC can handle it, even
+	 * with ext. hdrs.
+	 */
+	if (sw_csum & CSUM_DELAY_DATA_IPV6) {
+		sw_csum &= ~CSUM_DELAY_DATA_IPV6;
+		in6_delayed_cksum(m, plen, sizeof(struct ip6_hdr));
+	}
 #ifdef SCTP
-	sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;
-	if (sw_csum & CSUM_SCTP) {
+	if (sw_csum & CSUM_SCTP_IPV6) {
+		sw_csum &= ~CSUM_SCTP_IPV6;
 		sctp_delayed_cksum(m, sizeof(struct ip6_hdr));
-		sw_csum &= ~CSUM_SCTP;
 	}
 #endif
+	m->m_pkthdr.csum_flags &= ifp->if_hwassist;
 	tlen = m->m_pkthdr.len;
 
-	if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
+	if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso)
 		dontfrag = 1;
 	else
 		dontfrag = 0;
@@ -945,7 +982,7 @@
 		error = EMSGSIZE;
 		goto bad;
 	}
-	if (dontfrag && tlen > IN6_LINKMTU(ifp)) {	/* case 2-b */
+	if (dontfrag && tlen > IN6_LINKMTU(ifp) && !tso) {	/* case 2-b */
 		/*
 		 * Even if the DONTFRAG option is specified, we cannot send the
 		 * packet when the data length is larger than the MTU of the
@@ -1033,6 +1070,22 @@
 			goto bad;
 		}
 
+
+		/*
+		 * If the interface will not calculate checksums on
+		 * fragmented packets, then do it here.
+		 * XXX-BZ handle the hw offloading case.  Need flags.
+		 */
+		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
+			in6_delayed_cksum(m, plen, hlen);
+			m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
+		}
+#ifdef SCTP
+		if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) {
+			sctp_delayed_cksum(m, hlen);
+			m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
+		}
+#endif
 		mnext = &m->m_nextpkt;
 
 		/*
@@ -1129,13 +1182,10 @@
 		V_ip6stat.ip6s_fragmented++;
 
 done:
-	if (ro == &ip6route && ro->ro_rt && flevalid == 0) {
-                /* brace necessary for RTFREE */
-		RTFREE(ro->ro_rt);
-	} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt &&
-	    ((flevalid == 0) || (ro_pmtu != ro))) {
-		RTFREE(ro_pmtu->ro_rt);
-	}
+	if (ro == &ip6route)
+		RO_RTFREE(ro);
+	if (ro_pmtu == &ip6route)
+		RO_RTFREE(ro_pmtu);
 #ifdef IPSEC
 	if (sp != NULL)
 		KEY_FREESP(&sp);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/ip6_var.h
--- a/head/sys/netinet6/ip6_var.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/ip6_var.h	Wed Jul 25 16:47:10 2012 +0300
@@ -58,7 +58,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)ip_var.h	8.1 (Berkeley) 6/10/93
- * $FreeBSD: head/sys/netinet6/ip6_var.h 232127 2012-02-24 20:06:04Z bz $
+ * $FreeBSD: head/sys/netinet6/ip6_var.h 235956 2012-05-25 01:48:15Z bz $
  */
 
 #ifndef _NETINET6_IP6_VAR_H_
@@ -388,9 +388,9 @@
 int	ip6_nexthdr __P((struct mbuf *, int, int, int *));
 int	ip6_lasthdr __P((struct mbuf *, int, int, int *));
 
-struct ip6aux *ip6_addaux __P((struct mbuf *));
+#ifdef __notyet__
 struct ip6aux *ip6_findaux __P((struct mbuf *));
-void	ip6_delaux __P((struct mbuf *));
+#endif
 
 extern int	(*ip6_mforward)(struct ip6_hdr *, struct ifnet *,
     struct mbuf *);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/mld6.c
--- a/head/sys/netinet6/mld6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/mld6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -64,7 +64,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/mld6.c 229621 2012-01-05 19:00:36Z jhb $");
+__FBSDID("$FreeBSD: head/sys/netinet6/mld6.c 237736 2012-06-28 23:48:40Z bms $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -833,7 +833,7 @@
 	mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
 
 	maxdelay = ntohs(mld->mld_maxdelay);	/* in 1/10ths of a second */
-	if (maxdelay >= 32678) {
+	if (maxdelay >= 32768) {
 		maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
 			   (MLD_MRC_EXP(maxdelay) + 3);
 	}
@@ -867,16 +867,10 @@
 	 */
 	if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
 		/*
-		 * General Queries SHOULD be directed to ff02::1.
 		 * A general query with a source list has undefined
 		 * behaviour; discard it.
 		 */
-		struct in6_addr		 dst;
-
-		dst = ip6->ip6_dst;
-		in6_clearscope(&dst);
-		if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes) ||
-		    nsrc > 0)
+		if (nsrc > 0)
 			return (EINVAL);
 		is_general_query = 1;
 	} else {
@@ -2203,6 +2197,7 @@
 #endif
 			mld_v1_transmit_report(inm, MLD_LISTENER_DONE);
 			inm->in6m_state = MLD_NOT_MEMBER;
+			V_current_state_timers_running6 = 1;
 		} else if (mli->mli_version == MLD_VERSION_2) {
 			/*
 			 * Stop group timer and all pending reports.
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/nd6.c
--- a/head/sys/netinet6/nd6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/nd6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/nd6.c 232514 2012-03-04 18:51:45Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/nd6.c 235986 2012-05-25 09:27:16Z bz $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -174,9 +174,7 @@
 {
 	struct nd_ifinfo *nd;
 
-	nd = (struct nd_ifinfo *)malloc(sizeof(*nd), M_IP6NDP, M_WAITOK);
-	bzero(nd, sizeof(*nd));
-
+	nd = (struct nd_ifinfo *)malloc(sizeof(*nd), M_IP6NDP, M_WAITOK|M_ZERO);
 	nd->initialized = 1;
 
 	nd->chlim = IPV6_DEFHLIM;
@@ -284,10 +282,9 @@
 	struct nd_opt_hdr *nd_opt;
 	int olen;
 
-	if (ndopts == NULL)
-		panic("ndopts == NULL in nd6_option");
-	if (ndopts->nd_opts_last == NULL)
-		panic("uninitialized ndopts in nd6_option");
+	KASSERT(ndopts != NULL, ("%s: ndopts == NULL", __func__));
+	KASSERT(ndopts->nd_opts_last != NULL, ("%s: uninitialized ndopts",
+	    __func__));
 	if (ndopts->nd_opts_search == NULL)
 		return NULL;
 	if (ndopts->nd_opts_done)
@@ -335,10 +332,9 @@
 	struct nd_opt_hdr *nd_opt;
 	int i = 0;
 
-	if (ndopts == NULL)
-		panic("ndopts == NULL in nd6_options");
-	if (ndopts->nd_opts_last == NULL)
-		panic("uninitialized ndopts in nd6_options");
+	KASSERT(ndopts != NULL, ("%s: ndopts == NULL", __func__));
+	KASSERT(ndopts->nd_opts_last != NULL, ("%s: uninitialized ndopts",
+	    __func__));
 	if (ndopts->nd_opts_search == NULL)
 		return 0;
 
@@ -1174,11 +1170,13 @@
 void
 nd6_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info)
 {
-	struct sockaddr_in6 *gateway = (struct sockaddr_in6 *)rt->rt_gateway;
+	struct sockaddr_in6 *gateway;
 	struct nd_defrouter *dr;
-	struct ifnet *ifp = rt->rt_ifp;
+	struct ifnet *ifp;
 
 	RT_LOCK_ASSERT(rt);
+	gateway = (struct sockaddr_in6 *)rt->rt_gateway;
+	ifp = rt->rt_ifp;
 
 	switch (req) {
 	case RTM_ADD:
@@ -1547,10 +1545,8 @@
 
 	IF_AFDATA_UNLOCK_ASSERT(ifp);
 
-	if (ifp == NULL)
-		panic("ifp == NULL in nd6_cache_lladdr");
-	if (from == NULL)
-		panic("from == NULL in nd6_cache_lladdr");
+	KASSERT(ifp != NULL, ("%s: ifp == NULL", __func__));
+	KASSERT(from != NULL, ("%s: from == NULL", __func__));
 
 	/* nothing must be updated for unspecified address */
 	if (IN6_IS_ADDR_UNSPECIFIED(from))
@@ -2074,6 +2070,8 @@
 		}
 		return (error);
 	}
+	/* Reset layer specific mbuf flags to avoid confusing lower layers. */
+	m->m_flags &= ~(M_PROTOFLAGS);  
 	if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
 		return ((*ifp->if_output)(origifp, m, (struct sockaddr *)dst,
 		    NULL));
@@ -2239,7 +2237,6 @@
 
 	for (m_hold = ln->la_hold; m_hold; m_hold = m_hold_next) {
 		m_hold_next = m_hold->m_nextpkt;
-		m_hold->m_nextpkt = NULL;
 		m_freem(m_hold);
 	}
 
@@ -2262,128 +2259,101 @@
 static int
 nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
 {
+	struct in6_defrouter d;
+	struct nd_defrouter *dr;
 	int error;
-	char buf[1024] __aligned(4);
-	struct in6_defrouter *d, *de;
-	struct nd_defrouter *dr;
 
 	if (req->newptr)
-		return EPERM;
-	error = 0;
+		return (EPERM);
 
+	bzero(&d, sizeof(d));
+	d.rtaddr.sin6_family = AF_INET6;
+	d.rtaddr.sin6_len = sizeof(d.rtaddr);
+
+	/*
+	 * XXX locking
+	 */
 	TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
-		d = (struct in6_defrouter *)buf;
-		de = (struct in6_defrouter *)(buf + sizeof(buf));
-
-		if (d + 1 <= de) {
-			bzero(d, sizeof(*d));
-			d->rtaddr.sin6_family = AF_INET6;
-			d->rtaddr.sin6_len = sizeof(d->rtaddr);
-			d->rtaddr.sin6_addr = dr->rtaddr;
-			error = sa6_recoverscope(&d->rtaddr);
-			if (error != 0)
-				return (error);
-			d->flags = dr->flags;
-			d->rtlifetime = dr->rtlifetime;
-			d->expire = dr->expire;
-			d->if_index = dr->ifp->if_index;
-		} else
-			panic("buffer too short");
-
-		error = SYSCTL_OUT(req, buf, sizeof(*d));
-		if (error)
-			break;
+		d.rtaddr.sin6_addr = dr->rtaddr;
+		error = sa6_recoverscope(&d.rtaddr);
+		if (error != 0)
+			return (error);
+		d.flags = dr->flags;
+		d.rtlifetime = dr->rtlifetime;
+		d.expire = dr->expire;
+		d.if_index = dr->ifp->if_index;
+		error = SYSCTL_OUT(req, &d, sizeof(d));
+		if (error != 0)
+			return (error);
 	}
-
-	return (error);
+	return (0);
 }
 
 static int
 nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
 {
+	struct in6_prefix p;
+	struct sockaddr_in6 s6;
+	struct nd_prefix *pr;
+	struct nd_pfxrouter *pfr;
+	time_t maxexpire;
 	int error;
-	char buf[1024] __aligned(4);
-	struct in6_prefix *p, *pe;
-	struct nd_prefix *pr;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	if (req->newptr)
-		return EPERM;
-	error = 0;
+		return (EPERM);
 
+	bzero(&p, sizeof(p));
+	p.origin = PR_ORIG_RA;
+	bzero(&s6, sizeof(s6));
+	s6.sin6_family = AF_INET6;
+	s6.sin6_len = sizeof(s6);
+
+	/*
+	 * XXX locking
+	 */
 	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
-		u_short advrtrs;
-		size_t advance;
-		struct sockaddr_in6 *sin6, *s6;
-		struct nd_pfxrouter *pfr;
-
-		p = (struct in6_prefix *)buf;
-		pe = (struct in6_prefix *)(buf + sizeof(buf));
-
-		if (p + 1 <= pe) {
-			bzero(p, sizeof(*p));
-			sin6 = (struct sockaddr_in6 *)(p + 1);
-
-			p->prefix = pr->ndpr_prefix;
-			if (sa6_recoverscope(&p->prefix)) {
+		p.prefix = pr->ndpr_prefix;
+		if (sa6_recoverscope(&p.prefix)) {
+			log(LOG_ERR, "scope error in prefix list (%s)\n",
+			    ip6_sprintf(ip6buf, &p.prefix.sin6_addr));
+			/* XXX: press on... */
+		}
+		p.raflags = pr->ndpr_raf;
+		p.prefixlen = pr->ndpr_plen;
+		p.vltime = pr->ndpr_vltime;
+		p.pltime = pr->ndpr_pltime;
+		p.if_index = pr->ndpr_ifp->if_index;
+		if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
+			p.expire = 0;
+		else {
+			/* XXX: we assume time_t is signed. */
+			maxexpire = (-1) &
+			    ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1));
+			if (pr->ndpr_vltime < maxexpire - pr->ndpr_lastupdate)
+				p.expire = pr->ndpr_lastupdate +
+				    pr->ndpr_vltime;
+			else
+				p.expire = maxexpire;
+		}
+		p.refcnt = pr->ndpr_refcnt;
+		p.flags = pr->ndpr_stateflags;
+		p.advrtrs = 0;
+		LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry)
+			p.advrtrs++;
+		error = SYSCTL_OUT(req, &p, sizeof(p));
+		if (error != 0)
+			return (error);
+		LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) {
+			s6.sin6_addr = pfr->router->rtaddr;
+			if (sa6_recoverscope(&s6))
 				log(LOG_ERR,
 				    "scope error in prefix list (%s)\n",
-				    ip6_sprintf(ip6buf, &p->prefix.sin6_addr));
-				/* XXX: press on... */
-			}
-			p->raflags = pr->ndpr_raf;
-			p->prefixlen = pr->ndpr_plen;
-			p->vltime = pr->ndpr_vltime;
-			p->pltime = pr->ndpr_pltime;
-			p->if_index = pr->ndpr_ifp->if_index;
-			if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
-				p->expire = 0;
-			else {
-				time_t maxexpire;
-
-				/* XXX: we assume time_t is signed. */
-				maxexpire = (-1) &
-				    ~((time_t)1 <<
-				    ((sizeof(maxexpire) * 8) - 1));
-				if (pr->ndpr_vltime <
-				    maxexpire - pr->ndpr_lastupdate) {
-				    p->expire = pr->ndpr_lastupdate +
-				        pr->ndpr_vltime;
-				} else
-					p->expire = maxexpire;
-			}
-			p->refcnt = pr->ndpr_refcnt;
-			p->flags = pr->ndpr_stateflags;
-			p->origin = PR_ORIG_RA;
-			advrtrs = 0;
-			LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) {
-				if ((void *)&sin6[advrtrs + 1] > (void *)pe) {
-					advrtrs++;
-					continue;
-				}
-				s6 = &sin6[advrtrs];
-				bzero(s6, sizeof(*s6));
-				s6->sin6_family = AF_INET6;
-				s6->sin6_len = sizeof(*sin6);
-				s6->sin6_addr = pfr->router->rtaddr;
-				if (sa6_recoverscope(s6)) {
-					log(LOG_ERR,
-					    "scope error in "
-					    "prefix list (%s)\n",
-					    ip6_sprintf(ip6buf,
-						    &pfr->router->rtaddr));
-				}
-				advrtrs++;
-			}
-			p->advrtrs = advrtrs;
-		} else
-			panic("buffer too short");
-
-		advance = sizeof(*p) + sizeof(*sin6) * advrtrs;
-		error = SYSCTL_OUT(req, buf, advance);
-		if (error)
-			break;
+				    ip6_sprintf(ip6buf, &pfr->router->rtaddr));
+			error = SYSCTL_OUT(req, &s6, sizeof(s6));
+			if (error != 0)
+				return (error);
+		}
 	}
-
-	return (error);
+	return (0);
 }
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/nd6.h
--- a/head/sys/netinet6/nd6.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/nd6.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	$KAME: nd6.h,v 1.76 2001/12/18 02:10:31 itojun Exp $
- * $FreeBSD: head/sys/netinet6/nd6.h 229547 2012-01-05 01:14:35Z bz $
+ * $FreeBSD: head/sys/netinet6/nd6.h 238273 2012-07-09 06:21:46Z hrs $
  */
 
 #ifndef _NETINET6_ND6_H_
@@ -79,7 +79,7 @@
 
 #define ND6_IFF_PERFORMNUD	0x1
 #define ND6_IFF_ACCEPT_RTADV	0x2
-#define ND6_IFF_PREFER_SOURCE	0x4 /* XXX: not related to ND. */
+#define ND6_IFF_PREFER_SOURCE	0x4 /* Not used in FreeBSD. */
 #define ND6_IFF_IFDISABLED	0x8 /* IPv6 operation is disabled due to
 				     * DAD failure.  (XXX: not ND-specific)
 				     */
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/nd6_nbr.c
--- a/head/sys/netinet6/nd6_nbr.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/nd6_nbr.c	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/nd6_nbr.c 231852 2012-02-17 02:39:58Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/nd6_nbr.c 238092 2012-07-04 07:37:53Z glebius $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -595,9 +595,9 @@
 	icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
 	ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]);
 
-	if (ro.ro_rt) {		/* we don't cache this route. */
-		RTFREE(ro.ro_rt);
-	}
+	/* We don't cache this route. */
+	RO_RTFREE(&ro);
+
 	return;
 
   bad:
@@ -1124,9 +1124,9 @@
 	icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
 	ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_ADVERT]);
 
-	if (ro.ro_rt) {		/* we don't cache this route. */
-		RTFREE(ro.ro_rt);
-	}
+	/* We don't cache this route. */
+	RO_RTFREE(&ro);
+
 	return;
 
   bad:
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/route6.c
--- a/head/sys/netinet6/route6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/route6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/netinet6/route6.c 235954 2012-05-25 01:43:52Z bz $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -62,6 +62,7 @@
 	struct mbuf *m = *mp;
 	struct ip6_rthdr *rh;
 	int off = *offp, rhlen;
+#ifdef __notyet__
 	struct ip6aux *ip6a;
 
 	ip6a = ip6_findaux(m);
@@ -73,6 +74,7 @@
 			return IPPROTO_DONE;
 		}
 	}
+#endif
 
 #ifndef PULLDOWN_TEST
 	IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/scope6.c
--- a/head/sys/netinet6/scope6.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/scope6.c	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/netinet6/scope6.c 235916 2012-05-24 16:30:13Z bz $");
 
 #include <sys/param.h>
 #include <sys/malloc.h>
@@ -494,3 +494,16 @@
 
 	return (modified);
 }
+
+/*
+ * Return the scope identifier or zero.
+ */
+uint16_t
+in6_getscope(struct in6_addr *in6)
+{
+
+	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
+		return (in6->s6_addr16[1]);
+
+	return (0);
+}
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/scope6_var.h
--- a/head/sys/netinet6/scope6_var.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/scope6_var.h	Wed Jul 25 16:47:10 2012 +0300
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *	$KAME: scope6_var.h,v 1.4 2000/05/18 15:03:27 jinmei Exp $
- * $FreeBSD: head/sys/netinet6/scope6_var.h 229127 2011-12-31 16:19:22Z bz $
+ * $FreeBSD: head/sys/netinet6/scope6_var.h 235916 2012-05-24 16:30:13Z bz $
  */
 
 #ifndef _NETINET6_SCOPE6_VAR_H_
@@ -54,6 +54,7 @@
 int	sa6_recoverscope __P((struct sockaddr_in6 *));
 int	in6_setscope __P((struct in6_addr *, struct ifnet *, u_int32_t *));
 int	in6_clearscope __P((struct in6_addr *));
+uint16_t in6_getscope(struct in6_addr *);
 #endif /* _KERNEL */
 
 #endif /* _NETINET6_SCOPE6_VAR_H_ */
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/sctp6_usrreq.c
--- a/head/sys/netinet6/sctp6_usrreq.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/sctp6_usrreq.c	Wed Jul 25 16:47:10 2012 +0300
@@ -1,7 +1,7 @@
 /*-
  * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
- * Copyright (c) 2008-2011, by Randall Stewart. All rights reserved.
- * Copyright (c) 2008-2011, by Michael Tuexen. All rights reserved.
+ * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
+ * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -29,17 +29,17 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
-/*	$KAME: sctp6_usrreq.c,v 1.38 2005/08/24 08:08:56 suz Exp $	*/
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/sctp6_usrreq.c 233005 2012-03-15 14:22:05Z tuexen $");
+__FBSDID("$FreeBSD: head/sys/netinet6/sctp6_usrreq.c 238501 2012-07-15 20:16:17Z tuexen $");
 
 #include <netinet/sctp_os.h>
+#ifdef INET6
 #include <sys/proc.h>
 #include <netinet/sctp_pcb.h>
 #include <netinet/sctp_header.h>
 #include <netinet/sctp_var.h>
-#if defined(INET6)
+#ifdef INET6
 #include <netinet6/sctp6_var.h>
 #endif
 #include <netinet/sctp_sysctl.h>
@@ -58,7 +58,7 @@
 
 #ifdef IPSEC
 #include <netipsec/ipsec.h>
-#if defined(INET6)
+#ifdef INET6
 #include <netipsec/ipsec6.h>
 #endif				/* INET6 */
 #endif				/* IPSEC */
@@ -69,232 +69,128 @@
 sctp6_input(struct mbuf **i_pak, int *offp, int proto)
 {
 	struct mbuf *m;
+	int iphlen;
+	uint32_t vrf_id;
+	uint8_t ecn_bits;
+	struct sockaddr_in6 src, dst;
 	struct ip6_hdr *ip6;
 	struct sctphdr *sh;
-	struct sctp_inpcb *in6p = NULL;
-	struct sctp_nets *net;
-	int refcount_up = 0;
-	uint32_t vrf_id = 0;
+	struct sctp_chunkhdr *ch;
+	int length, offset;
 
-#ifdef IPSEC
-	struct inpcb *in6p_ip;
+#if !defined(SCTP_WITH_NO_CSUM)
+	uint8_t compute_crc;
 
 #endif
-	struct sctp_chunkhdr *ch;
-	int length, offset, iphlen;
-	uint8_t ecn_bits;
-	struct sctp_tcb *stcb = NULL;
-	int pkt_len = 0;
-
-#if !defined(SCTP_WITH_NO_CSUM)
-	uint32_t check, calc_check;
-
-#endif
-	int off = *offp;
+	uint32_t mflowid;
+	uint8_t use_mflowid;
 	uint16_t port = 0;
 
-	/* get the VRF and table id's */
+	iphlen = *offp;
 	if (SCTP_GET_PKT_VRFID(*i_pak, vrf_id)) {
 		SCTP_RELEASE_PKT(*i_pak);
-		return (-1);
+		return (IPPROTO_DONE);
 	}
 	m = SCTP_HEADER_TO_CHAIN(*i_pak);
-	pkt_len = SCTP_HEADER_LEN((*i_pak));
+#ifdef SCTP_MBUF_LOGGING
+	/* Log in any input mbufs */
+	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) {
+		struct mbuf *mat;
 
-#ifdef  SCTP_PACKET_LOGGING
-	sctp_packet_log(m, pkt_len);
+		for (mat = m; mat; mat = SCTP_BUF_NEXT(mat)) {
+			if (SCTP_BUF_IS_EXTENDED(mat)) {
+				sctp_log_mb(mat, SCTP_MBUF_INPUT);
+			}
+		}
+	}
 #endif
+#ifdef SCTP_PACKET_LOGGING
+	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) {
+		sctp_packet_log(m);
+	}
+#endif
+	SCTPDBG(SCTP_DEBUG_CRCOFFLOAD,
+	    "sctp6_input(): Packet of length %d received on %s with csum_flags 0x%x.\n",
+	    m->m_pkthdr.len,
+	    if_name(m->m_pkthdr.rcvif),
+	    m->m_pkthdr.csum_flags);
+	if (m->m_flags & M_FLOWID) {
+		mflowid = m->m_pkthdr.flowid;
+		use_mflowid = 1;
+	} else {
+		mflowid = 0;
+		use_mflowid = 0;
+	}
+	SCTP_STAT_INCR(sctps_recvpackets);
+	SCTP_STAT_INCR_COUNTER64(sctps_inpackets);
+	/* Get IP, SCTP, and first chunk header together in the first mbuf. */
+	offset = iphlen + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr);
 	ip6 = mtod(m, struct ip6_hdr *);
-	/* Ensure that (sctphdr + sctp_chunkhdr) in a row. */
-	IP6_EXTHDR_GET(sh, struct sctphdr *, m, off,
-	    (int)(sizeof(*sh) + sizeof(*ch)));
+	IP6_EXTHDR_GET(sh, struct sctphdr *, m, iphlen,
+	    (int)(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr)));
 	if (sh == NULL) {
 		SCTP_STAT_INCR(sctps_hdrops);
 		return (IPPROTO_DONE);
 	}
 	ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr));
-	iphlen = off;
-	offset = iphlen + sizeof(*sh) + sizeof(*ch);
-	SCTPDBG(SCTP_DEBUG_INPUT1,
-	    "sctp6_input() length:%d iphlen:%d\n", pkt_len, iphlen);
-
-
-#if defined(NFAITH) && NFAITH > 0
-
-	if (faithprefix_p != NULL && (*faithprefix_p) (&ip6->ip6_dst)) {
+	offset -= sizeof(struct sctp_chunkhdr);
+	memset(&src, 0, sizeof(struct sockaddr_in6));
+	src.sin6_family = AF_INET6;
+	src.sin6_len = sizeof(struct sockaddr_in6);
+	src.sin6_port = sh->src_port;
+	src.sin6_addr = ip6->ip6_src;
+	if (in6_setscope(&src.sin6_addr, m->m_pkthdr.rcvif, NULL) != 0) {
+		goto out;
+	}
+	memset(&dst, 0, sizeof(struct sockaddr_in6));
+	dst.sin6_family = AF_INET6;
+	dst.sin6_len = sizeof(struct sockaddr_in6);
+	dst.sin6_port = sh->dest_port;
+	dst.sin6_addr = ip6->ip6_dst;
+	if (in6_setscope(&dst.sin6_addr, m->m_pkthdr.rcvif, NULL) != 0) {
+		goto out;
+	}
+	if (faithprefix_p != NULL && (*faithprefix_p) (&dst.sin6_addr)) {
 		/* XXX send icmp6 host/port unreach? */
-		goto bad;
+		goto out;
 	}
-#endif				/* NFAITH defined and > 0 */
-	SCTP_STAT_INCR(sctps_recvpackets);
-	SCTP_STAT_INCR_COUNTER64(sctps_inpackets);
-	SCTPDBG(SCTP_DEBUG_INPUT1, "V6 input gets a packet iphlen:%d pktlen:%d\n",
-	    iphlen, pkt_len);
+	length = ntohs(ip6->ip6_plen) + iphlen;
+	/* Validate mbuf chain length with IP payload length. */
+	if (SCTP_HEADER_LEN(m) != length) {
+		SCTPDBG(SCTP_DEBUG_INPUT1,
+		    "sctp6_input() length:%d reported length:%d\n", length, SCTP_HEADER_LEN(m));
+		SCTP_STAT_INCR(sctps_hdrops);
+		goto out;
+	}
 	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
-		/* No multi-cast support in SCTP */
-		goto bad;
+		goto out;
 	}
-	/* destination port of 0 is illegal, based on RFC2960. */
-	if (sh->dest_port == 0)
-		goto bad;
-
-	SCTPDBG(SCTP_DEBUG_CRCOFFLOAD,
-	    "sctp_input(): Packet of length %d received on %s with csum_flags 0x%x.\n",
-	    m->m_pkthdr.len,
-	    if_name(m->m_pkthdr.rcvif),
-	    m->m_pkthdr.csum_flags);
+	ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff);
 #if defined(SCTP_WITH_NO_CSUM)
 	SCTP_STAT_INCR(sctps_recvnocrc);
 #else
 	if (m->m_pkthdr.csum_flags & CSUM_SCTP_VALID) {
 		SCTP_STAT_INCR(sctps_recvhwcrc);
-		goto sctp_skip_csum;
+		compute_crc = 0;
+	} else {
+		SCTP_STAT_INCR(sctps_recvswcrc);
+		compute_crc = 1;
 	}
-	check = sh->checksum;	/* save incoming checksum */
-	if ((check == 0) && (SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback)) &&
-	    (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &ip6->ip6_dst))) {
-		SCTP_STAT_INCR(sctps_recvnocrc);
-		goto sctp_skip_csum;
+#endif
+	sctp_common_input_processing(&m, iphlen, offset, length,
+	    (struct sockaddr *)&src,
+	    (struct sockaddr *)&dst,
+	    sh, ch,
+#if !defined(SCTP_WITH_NO_CSUM)
+	    compute_crc,
+#endif
+	    ecn_bits,
+	    use_mflowid, mflowid,
+	    vrf_id, port);
+out:
+	if (m) {
+		sctp_m_freem(m);
 	}
-	sh->checksum = 0;	/* prepare for calc */
-	calc_check = sctp_calculate_cksum(m, iphlen);
-	SCTP_STAT_INCR(sctps_recvswcrc);
-	if (calc_check != check) {
-		SCTPDBG(SCTP_DEBUG_INPUT1, "Bad CSUM on SCTP packet calc_check:%x check:%x  m:%p phlen:%d\n",
-		    calc_check, check, m, iphlen);
-		stcb = sctp_findassociation_addr(m, offset - sizeof(*ch),
-		    sh, ch, &in6p, &net, vrf_id);
-		if ((net) && (port)) {
-			if (net->port == 0) {
-				sctp_pathmtu_adjustment(stcb, net->mtu - sizeof(struct udphdr));
-			}
-			net->port = port;
-		}
-		if ((net != NULL) && (m->m_flags & M_FLOWID)) {
-			net->flowid = m->m_pkthdr.flowid;
-#ifdef INVARIANTS
-			net->flowidset = 1;
-#endif
-		}
-		/* in6p's ref-count increased && stcb locked */
-		if ((in6p) && (stcb)) {
-			sctp_send_packet_dropped(stcb, net, m, iphlen, 1);
-			sctp_chunk_output((struct sctp_inpcb *)in6p, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED);
-		} else if ((in6p != NULL) && (stcb == NULL)) {
-			refcount_up = 1;
-		}
-		SCTP_STAT_INCR(sctps_badsum);
-		SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors);
-		goto bad;
-	}
-	sh->checksum = calc_check;
-
-sctp_skip_csum:
-#endif
-	net = NULL;
-	/*
-	 * Locate pcb and tcb for datagram sctp_findassociation_addr() wants
-	 * IP/SCTP/first chunk header...
-	 */
-	stcb = sctp_findassociation_addr(m, offset - sizeof(*ch),
-	    sh, ch, &in6p, &net, vrf_id);
-	if ((net) && (port)) {
-		if (net->port == 0) {
-			sctp_pathmtu_adjustment(stcb, net->mtu - sizeof(struct udphdr));
-		}
-		net->port = port;
-	}
-	if ((net != NULL) && (m->m_flags & M_FLOWID)) {
-		net->flowid = m->m_pkthdr.flowid;
-#ifdef INVARIANTS
-		net->flowidset = 1;
-#endif
-	}
-	/* in6p's ref-count increased */
-	if (in6p == NULL) {
-		struct sctp_init_chunk *init_chk, chunk_buf;
-
-		SCTP_STAT_INCR(sctps_noport);
-		if (ch->chunk_type == SCTP_INITIATION) {
-			/*
-			 * we do a trick here to get the INIT tag, dig in
-			 * and get the tag from the INIT and put it in the
-			 * common header.
-			 */
-			init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
-			    iphlen + sizeof(*sh), sizeof(*init_chk),
-			    (uint8_t *) & chunk_buf);
-			if (init_chk)
-				sh->v_tag = init_chk->init.initiate_tag;
-			else
-				sh->v_tag = 0;
-		}
-		if (ch->chunk_type == SCTP_SHUTDOWN_ACK) {
-			sctp_send_shutdown_complete2(m, sh, vrf_id, port);
-			goto bad;
-		}
-		if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) {
-			goto bad;
-		}
-		if (ch->chunk_type != SCTP_ABORT_ASSOCIATION) {
-			if ((SCTP_BASE_SYSCTL(sctp_blackhole) == 0) ||
-			    ((SCTP_BASE_SYSCTL(sctp_blackhole) == 1) &&
-			    (ch->chunk_type != SCTP_INIT))) {
-				sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id, port);
-			}
-		}
-		goto bad;
-	} else if (stcb == NULL) {
-		refcount_up = 1;
-	}
-#ifdef IPSEC
-	/*
-	 * Check AH/ESP integrity.
-	 */
-	in6p_ip = (struct inpcb *)in6p;
-	if (in6p_ip && (ipsec6_in_reject(m, in6p_ip))) {
-/* XXX */
-		MODULE_GLOBAL(ipsec6stat).in_polvio++;
-		goto bad;
-	}
-#endif				/* IPSEC */
-
-	/*
-	 * CONTROL chunk processing
-	 */
-	offset -= sizeof(*ch);
-	ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff);
-
-	/* Length now holds the total packet length payload + iphlen */
-	length = ntohs(ip6->ip6_plen) + iphlen;
-
-	/* sa_ignore NO_NULL_CHK */
-	sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
-	    in6p, stcb, net, ecn_bits, vrf_id, port);
-	/* inp's ref-count reduced && stcb unlocked */
-	/* XXX this stuff below gets moved to appropriate parts later... */
-	if (m)
-		sctp_m_freem(m);
-	if ((in6p) && refcount_up) {
-		/* reduce ref-count */
-		SCTP_INP_WLOCK(in6p);
-		SCTP_INP_DECR_REF(in6p);
-		SCTP_INP_WUNLOCK(in6p);
-	}
-	return (IPPROTO_DONE);
-
-bad:
-	if (stcb) {
-		SCTP_TCB_UNLOCK(stcb);
-	}
-	if ((in6p) && refcount_up) {
-		/* reduce ref-count */
-		SCTP_INP_WLOCK(in6p);
-		SCTP_INP_DECR_REF(in6p);
-		SCTP_INP_WUNLOCK(in6p);
-	}
-	if (m)
-		sctp_m_freem(m);
 	return (IPPROTO_DONE);
 }
 
@@ -384,14 +280,12 @@
     struct sctp_tcb *stcb,
     struct sctp_nets *net)
 {
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
 	struct socket *so;
 
 #endif
+
 	/* protection */
-	int reason;
-
-
 	if ((inp == NULL) || (stcb == NULL) || (net == NULL) ||
 	    (sh == NULL) || (to == NULL)) {
 		if (stcb)
@@ -428,8 +322,7 @@
 			net->dest_state &= ~SCTP_ADDR_REACHABLE;
 			net->dest_state &= ~SCTP_ADDR_PF;
 			sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN,
-			    stcb, SCTP_FAILED_THRESHOLD,
-			    (void *)net, SCTP_SO_NOT_LOCKED);
+			    stcb, 0, (void *)net, SCTP_SO_NOT_LOCKED);
 		}
 		SCTP_TCB_UNLOCK(stcb);
 	} else if ((icmph->icmp6_code == ICMP_UNREACH_PROTOCOL) ||
@@ -441,9 +334,8 @@
 		 * now is dead. In either case treat it like a OOTB abort
 		 * with no TCB
 		 */
-		reason = SCTP_PEER_FAULTY;
-		sctp_abort_notification(stcb, reason, SCTP_SO_NOT_LOCKED);
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+		sctp_abort_notification(stcb, 1, 0, NULL, SCTP_SO_NOT_LOCKED);
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
 		so = SCTP_INP_SO(inp);
 		atomic_add_int(&stcb->asoc.refcnt, 1);
 		SCTP_TCB_UNLOCK(stcb);
@@ -452,7 +344,7 @@
 		atomic_subtract_int(&stcb->asoc.refcnt, 1);
 #endif
 		(void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_2);
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
 		SCTP_SOCKET_UNLOCK(so, 1);
 		/* SCTP_TCB_UNLOCK(stcb); MT: I think this is not needed. */
 #endif
@@ -516,8 +408,8 @@
 		final.sin6_family = AF_INET6;
 		final.sin6_addr = ((struct sockaddr_in6 *)pktdst)->sin6_addr;
 		final.sin6_port = sh.dest_port;
-		stcb = sctp_findassociation_addr_sa((struct sockaddr *)ip6cp->ip6c_src,
-		    (struct sockaddr *)&final,
+		stcb = sctp_findassociation_addr_sa((struct sockaddr *)&final,
+		    (struct sockaddr *)ip6cp->ip6c_src,
 		    &inp, &net, 1, vrf_id);
 		/* inp's ref-count increased && stcb locked */
 		if (stcb != NULL && inp && (inp->sctp_socket != NULL)) {
@@ -584,8 +476,8 @@
 	if (error)
 		return (error);
 
-	stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[0]),
-	    sin6tosa(&addrs[1]),
+	stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[1]),
+	    sin6tosa(&addrs[0]),
 	    &inp, &net, 1, vrf_id);
 	if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
 		if ((inp != NULL) && (stcb == NULL)) {
@@ -1349,3 +1241,5 @@
 	.pru_sosend = sctp_sosend,
 	.pru_soreceive = sctp_soreceive
 };
+
+#endif
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/sctp6_var.h
--- a/head/sys/netinet6/sctp6_var.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/sctp6_var.h	Wed Jul 25 16:47:10 2012 +0300
@@ -1,7 +1,7 @@
 /*-
  * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
- * Copyright (c) 2008-2011, by Randall Stewart. All rights reserved.
- * Copyright (c) 2008-2011, by Michael Tuexen. All rights reserved.
+ * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
+ * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -29,14 +29,13 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
-/*	$KAME: sctp6_var.h,v 1.7 2004/08/17 04:06:22 itojun Exp $	*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/netinet6/sctp6_var.h 235828 2012-05-23 11:26:28Z tuexen $");
 
 #ifndef _NETINET6_SCTP6_VAR_H_
 #define _NETINET6_SCTP6_VAR_H_
 
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/sctp6_var.h 230138 2012-01-15 14:03:05Z tuexen $");
-
 #if defined(_KERNEL)
 
 SYSCTL_DECL(_net_inet6_sctp6);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netinet6/udp6_usrreq.c
--- a/head/sys/netinet6/udp6_usrreq.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netinet6/udp6_usrreq.c	Wed Jul 25 16:47:10 2012 +0300
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet6/udp6_usrreq.c 225044 2011-08-20 17:05:11Z bz $");
+__FBSDID("$FreeBSD: head/sys/netinet6/udp6_usrreq.c 236170 2012-05-28 09:30:13Z bz $");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
@@ -185,6 +185,7 @@
 #ifdef IPFIREWALL_FORWARD
 	struct m_tag *fwd_tag;
 #endif
+	uint16_t uh_sum;
 
 	ifp = m->m_pkthdr.rcvif;
 	ip6 = mtod(m, struct ip6_hdr *);
@@ -228,7 +229,18 @@
 		UDPSTAT_INC(udps_nosum);
 		goto badunlocked;
 	}
-	if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
+
+	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) {
+		if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
+			uh_sum = m->m_pkthdr.csum_data;
+		else
+			uh_sum = in6_cksum_pseudo(ip6, ulen,
+			    IPPROTO_UDP, m->m_pkthdr.csum_data);
+		uh_sum ^= 0xffff;
+	} else
+		uh_sum = in6_cksum(m, IPPROTO_UDP, off, ulen);
+
+	if (uh_sum != 0) {
 		UDPSTAT_INC(udps_badsum);
 		goto badunlocked;
 	}
@@ -771,10 +783,9 @@
 		ip6->ip6_src	= *laddr;
 		ip6->ip6_dst	= *faddr;
 
-		if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
-				sizeof(struct ip6_hdr), plen)) == 0) {
-			udp6->uh_sum = 0xffff;
-		}
+		udp6->uh_sum = in6_cksum_pseudo(ip6, plen, IPPROTO_UDP, 0);
+		m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
+		m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
 
 		flags = 0;
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netipsec/ipsec_output.c
--- a/head/sys/netipsec/ipsec_output.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netipsec/ipsec_output.c	Wed Jul 25 16:47:10 2012 +0300
@@ -23,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: head/sys/netipsec/ipsec_output.c 231852 2012-02-17 02:39:58Z bz $
+ * $FreeBSD: head/sys/netipsec/ipsec_output.c 238700 2012-07-22 17:46:05Z bz $
  */
 
 /*
@@ -165,8 +165,7 @@
 	 */
 	if (isr->next) {
 		V_ipsec4stat.ips_out_bundlesa++;
-		sav = isr->next->sav;
-		saidx = &sav->sah->saidx;
+		/* XXX-BZ currently only support same AF bundles. */
 		switch (saidx->dst.sa.sa_family) {
 #ifdef INET
 		case AF_INET:
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netncp/ncp_nls.h
--- a/head/sys/netncp/ncp_nls.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netncp/ncp_nls.h	Wed Jul 25 16:47:10 2012 +0300
@@ -23,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: head/sys/netncp/ncp_nls.h 236376 2012-06-01 03:59:08Z eadler $
  */
 #ifndef _NETNCP_NCP_NLS_H_
 #define _NETNCP_NCP_NLS_H_
@@ -52,9 +52,11 @@
 #define	NCP_NLS_AS_IS		1
 #define	NCP_NLS_AS_IS_NAME	"asis"
 #define	NCP_NLS_KOI_866		2
+#define	NCP_NLS_KOI_866_NAME	"koi2cp866"
 #define	NCP_NLS_SE		3
-#define	NCP_NLS_KOI_866_NAME	"koi2cp866"
 #define	NCP_NLS_SE_NAME		"se"
+#define	NCP_NLS_DE		4
+#define	NCP_NLS_DE_NAME		"de"
 
 extern struct ncp_nlstables ncp_nls;	/* active nls */
 
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netsmb/smb_dev.c
--- a/head/sys/netsmb/smb_dev.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netsmb/smb_dev.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/netsmb/smb_dev.c 237036 2012-06-13 22:12:10Z pjd $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -375,7 +375,7 @@
 	struct file* fp;
 
 	FILEDESC_SLOCK(fdp);
-	if (((u_int)fd) >= fdp->fd_nfiles ||
+	if (fd < 0 || fd >= fdp->fd_nfiles ||
 	    (fp = fdp->fd_ofiles[fd]) == NULL ||
 	    (fp->f_flag & flag) == 0) {
 		FILEDESC_SUNLOCK(fdp);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/netsmb/smb_trantcp.c
--- a/head/sys/netsmb/smb_trantcp.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/netsmb/smb_trantcp.c	Wed Jul 25 16:47:10 2012 +0300
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/netsmb/smb_trantcp.c 238356 2012-07-10 21:02:59Z brueffer $");
 
 #include <sys/param.h>
 #include <sys/condvar.h>
@@ -523,8 +523,10 @@
 		return error;
 	getnanotime(&ts2);
 	timespecsub(&ts2, &ts1);
-	if (ts2.tv_sec == 0 && ts2.tv_sec == 0)
+	if (ts2.tv_sec == 0) {
 		ts2.tv_sec = 1;
+		ts2.tv_nsec = 0;
+	}
 	nbp->nbp_timo = ts2;
 	timespecadd(&nbp->nbp_timo, &ts2);
 	timespecadd(&nbp->nbp_timo, &ts2);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/nfsclient/nfs_bio.c
--- a/head/sys/nfsclient/nfs_bio.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/nfsclient/nfs_bio.c	Wed Jul 25 16:47:10 2012 +0300
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_bio.c 232327 2012-03-01 03:53:07Z rmacklem $");
+__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_bio.c 235332 2012-05-12 12:02:51Z rmacklem $");
 
 #include "opt_kdtrace.h"
 
@@ -275,7 +275,11 @@
 	vp = ap->a_vp;
 	np = VTONFS(vp);
 	td = curthread;				/* XXX */
-	cred = curthread->td_ucred;		/* XXX */
+	/* Set the cred to n_writecred for the write rpcs. */
+	if (np->n_writecred != NULL)
+		cred = crhold(np->n_writecred);
+	else
+		cred = crhold(curthread->td_ucred);	/* XXX */
 	nmp = VFSTONFS(vp->v_mount);
 	pages = ap->a_m;
 	count = ap->a_count;
@@ -339,6 +343,7 @@
 	    iomode = NFSV3WRITE_FILESYNC;
 
 	error = (nmp->nm_rpcops->nr_writerpc)(vp, &uio, cred, &iomode, &must_commit);
+	crfree(cred);
 
 	pmap_qremove(kva, npages);
 	relpbuf(bp, &nfs_pbuf_freecnt);
@@ -1796,7 +1801,7 @@
 		 * truncation point.  We may have a B_DELWRI and/or B_CACHE
 		 * buffer that now needs to be truncated.
 		 */
-		error = vtruncbuf(vp, cred, td, nsize, biosize);
+		error = vtruncbuf(vp, cred, nsize, biosize);
 		lbn = nsize / biosize;
 		bufsize = nsize & (biosize - 1);
 		bp = nfs_getcacheblk(vp, lbn, bufsize, td);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/nfsclient/nfs_node.c
--- a/head/sys/nfsclient/nfs_node.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/nfsclient/nfs_node.c	Wed Jul 25 16:47:10 2012 +0300
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_node.c 224604 2011-08-02 11:24:42Z rmacklem $");
+__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_node.c 235332 2012-05-12 12:02:51Z rmacklem $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -270,6 +270,8 @@
 			free((caddr_t)dp2, M_NFSDIROFF);
 		}
 	}
+	if (np->n_writecred != NULL)
+		crfree(np->n_writecred);
 	if (np->n_fhsize > NFS_SMALLFH) {
 		free((caddr_t)np->n_fhp, M_NFSBIGFH);
 	}
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/nfsclient/nfs_subs.c
--- a/head/sys/nfsclient/nfs_subs.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/nfsclient/nfs_subs.c	Wed Jul 25 16:47:10 2012 +0300
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_subs.c 234386 2012-04-17 16:28:22Z mckusick $");
+__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_subs.c 235246 2012-05-10 21:38:48Z mckusick $");
 
 /*
  * These functions support the macros and help fiddle mbuf chains for
@@ -865,7 +865,6 @@
 	struct buf *bp, *nbp;
 	struct bufobj *bo;
 
-	MNT_ILOCK(mp);
 	MNT_VNODE_FOREACH_ALL(vp, mp, nvp) {
 		bo = &vp->v_bufobj;
 		vholdl(vp);
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/nfsclient/nfs_vfsops.c
--- a/head/sys/nfsclient/nfs_vfsops.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/nfsclient/nfs_vfsops.c	Wed Jul 25 16:47:10 2012 +0300
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_vfsops.c 234386 2012-04-17 16:28:22Z mckusick $");
+__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_vfsops.c 235052 2012-05-05 09:34:35Z pluknet $");
 
 
 #include "opt_bootp.h"
@@ -1452,6 +1452,7 @@
 		MNT_IUNLOCK(mp);
 		return (EBADF);
 	}
+	MNT_IUNLOCK(mp);
 
 	/*
 	 * Force stale buffer cache information to be flushed.
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/nfsclient/nfs_vnops.c
--- a/head/sys/nfsclient/nfs_vnops.c	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/nfsclient/nfs_vnops.c	Wed Jul 25 16:47:10 2012 +0300
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_vnops.c 232821 2012-03-11 12:19:58Z kib $");
+__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_vnops.c 235332 2012-05-12 12:02:51Z rmacklem $");
 
 /*
  * vnode op calls for Sun NFS version 2 and 3
@@ -507,6 +507,7 @@
 	struct vattr vattr;
 	int error;
 	int fmode = ap->a_mode;
+	struct ucred *cred;
 
 	if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
 		return (EOPNOTSUPP);
@@ -563,7 +564,22 @@
 		}
 		np->n_directio_opens++;
 	}
+
+	/*
+	 * If this is an open for writing, capture a reference to the
+	 * credentials, so they can be used by nfs_putpages(). Using
+	 * these write credentials is preferable to the credentials of
+	 * whatever thread happens to be doing the VOP_PUTPAGES() since
+	 * the write RPCs are less likely to fail with EACCES.
+	 */
+	if ((fmode & FWRITE) != 0) {
+		cred = np->n_writecred;
+		np->n_writecred = crhold(ap->a_cred);
+	} else
+		cred = NULL;
 	mtx_unlock(&np->n_mtx);
+	if (cred != NULL)
+		crfree(cred);
 	vnode_create_vobject(vp, vattr.va_size, ap->a_td);
 	return (0);
 }
diff -r 58b18bd1429e -r 4abb4d69799e head/sys/nfsclient/nfsnode.h
--- a/head/sys/nfsclient/nfsnode.h	Wed Jul 25 16:45:04 2012 +0300
+++ b/head/sys/nfsclient/nfsnode.h	Wed Jul 25 16:47:10 2012 +0300
@@ -30,7 +30,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)nfsnode.h	8.9 (Berkeley) 5/14/95
- * $FreeBSD: head/sys/nfsclient/nfsnode.h 230394 2012-01-20 20:02:01Z jhb $
+ * $FreeBSD: head/sys/nfsclient/nfsnode.h 235332 2012-05-12 12:02:51Z rmacklem $
  */
 
 #ifndef _NFSCLIENT_NFSNODE_H_
@@ -128,6 +128,7 @@
 	uint32_t		n_namelen;
 	int			n_directio_opens;
 	int                     n_directio_asyncwr;
+	struct ucred		*n_writecred;	/* Cred. for putpages */
 };
 
 #define n_atim		n_un1.nf_atim



More information about the Zrouter-src-freebsd mailing list