Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 3 Apr 2014 12:23:05 -0700
From:      Sean Fagan <sef@ixsystems.com>
To:        freebsd-net <freebsd-net@freebsd.org>
Subject:   Re: More on the whole interruptless networking
Message-ID:  <16E0C844-B302-4B61-A250-CA12CABDE476@ixsystems.com>

next in thread | raw e-mail | index | archive | help

--Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=us-ascii

No, that icon is not attach, it's send.  Grrr.

Attached diffs that I promised and didn't attach before hitting the =
wrong button.


--Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3
Content-Disposition: attachment;
	filename=netdump-10-diffs.txt
Content-Type: text/plain;
	name="netdump-10-diffs.txt"
Content-Transfer-Encoding: quoted-printable

diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf
index a34f0c1..e455200 100644
--- a/etc/defaults/rc.conf
+++ b/etc/defaults/rc.conf
@@ -274,6 +274,14 @@ ctld_enable=3D"NO"		# CAM Target Layer / =
iSCSI target daemon.
 local_unbound_enable=3D"NO"	# local caching resolver
=20
 #
+# netdumpsrv configuration.
+#
+netdumpsrv_enable=3D"NO"		# Run netdumpsrv.
+netdumpsrv_program=3D"/usr/sbin/netdumpsrv" # Path to netdumpsrv.
+netdumpsrv_pidfile=3D"/var/run/netdumpsrv.pid" # Path to pidfile for =
netdumpsrv.
+#netdumpsrv_flags=3D""		# Use this for flags -a, -D, -d, -i
+
+#
 # kerberos. Do not run the admin daemons on slave servers
 #
 kerberos5_server_enable=3D"NO"	# Run a kerberos 5 master server (or =
NO).
diff --git a/etc/rc.d/netdumpsrv b/etc/rc.d/netdumpsrv
new file mode 100644
index 0000000..3ae47e6
--- /dev/null
+++ b/etc/rc.d/netdumpsrv
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# PROVIDE: netdumpsrv
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=3D"netdumpsrv"
+rcvar=3D`set_rcvar`
+
+command=3D"/usr/sbin/${name}"
+pidfile=3D"/var/run/${name}.pid"
+
+load_rc_config $name
+
+run_rc_command "$1"
+#!/bin/sh
+#
+# PROVIDE: netdumpsrv
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=3D"netdumpsrv"
+rcvar=3D`set_rcvar`
+
+command=3D"/usr/sbin/${name}"
+pidfile=3D"/var/run/${name}.pid"
+
+load_rc_config $name
+
+run_rc_command "$1"
diff --git a/sys/amd64/amd64/minidump_machdep.c =
b/sys/amd64/amd64/minidump_machdep.c
index 0ee8bcf..1e94bfc 100644
--- a/sys/amd64/amd64/minidump_machdep.c
+++ b/sys/amd64/amd64/minidump_machdep.c
@@ -319,13 +319,20 @@ minidumpsys(struct dumperinfo *di)
 	}
 	dumpsize +=3D PAGE_SIZE;
=20
-	/* Determine dump offset on device. */
-	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * =
2) {
-		error =3D E2BIG;
-		goto fail;
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+
+		/* Determine dump offset on device. */
+		if (di->mediasize <
+		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+			error =3D E2BIG;
+			goto fail;
+		}
+		dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
+		dumplo -=3D sizeof(kdh) * 2;
 	}
-	dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
-	dumplo -=3D sizeof(kdh) * 2;
 	progress =3D dumpsize;
=20
 	/* Initialize mdhdr */
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 3b48f0f..e1aabb4 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -76,7 +76,8 @@ options 	INCLUDE_CONFIG_FILE     # Include this =
file in kernel
 # Debugging support.  Always need this:
 options 	KDB			# Enable kernel debugger =
support.
 options 	KDB_TRACE		# Print a stack trace for a =
panic.
-
+options		DDB			# Support DDB.
+options		NETDUMP_CLIENT		# Network core-dump
 # Make an SMP-capable kernel by default
 options 	SMP			# Symmetric MultiProcessor =
Kernel
=20
diff --git a/sys/arm/arm/dump_machdep.c b/sys/arm/arm/dump_machdep.c
index e8ba5768..0a88114 100644
--- a/sys/arm/arm/dump_machdep.c
+++ b/sys/arm/arm/dump_machdep.c
@@ -311,13 +311,20 @@ dumpsys(struct dumperinfo *di)
 	dumpsize +=3D fileofs;
 	hdrgap =3D fileofs - DEV_ALIGN(hdrsz);
=20
-	/* Determine dump offset on device. */
-	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * =
2) {
-		error =3D ENOSPC;
-		goto fail;
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+
+		/* Determine dump offset on device. */
+		if (di->mediasize <
+		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+			error =3D ENOSPC;
+			goto fail;
+		}
+		dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
+		dumplo -=3D sizeof(kdh) * 2;
 	}
-	dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
-	dumplo -=3D sizeof(kdh) * 2;
=20
 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, =
dumpsize, di->blocksize);
=20
diff --git a/sys/arm/arm/minidump_machdep.c =
b/sys/arm/arm/minidump_machdep.c
index a858709..4779a54 100644
--- a/sys/arm/arm/minidump_machdep.c
+++ b/sys/arm/arm/minidump_machdep.c
@@ -284,14 +284,21 @@ minidumpsys(struct dumperinfo *di)
=20
 	dumpsize +=3D PAGE_SIZE;
=20
-	/* Determine dump offset on device. */
-	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * =
2) {
-		error =3D ENOSPC;
-		goto fail;
-	}
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+
+		/* Determine dump offset on device. */
+		if (di->mediasize <
+		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+			error =3D ENOSPC;
+			goto fail;
+		}
=20
-	dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
-	dumplo -=3D sizeof(kdh) * 2;
+		dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
+		dumplo -=3D sizeof(kdh) * 2;
+	}
 	progress =3D dumpsize;
=20
 	/* Initialize mdhdr */
diff --git a/sys/conf/files b/sys/conf/files
index 27d9c7e..46d0d27 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3261,6 +3261,7 @@ netinet/ip_ipsec.c		optional inet =
ipsec
 netinet/ip_mroute.c		optional mrouting inet
 netinet/ip_options.c		optional inet
 netinet/ip_output.c		optional inet
+netinet/netdump_client.c	optional inet netdump_client
 netinet/raw_ip.c		optional inet | inet6
 netinet/cc/cc.c			optional inet | inet6
 netinet/cc/cc_newreno.c		optional inet | inet6
diff --git a/sys/conf/options b/sys/conf/options
index a4c785e..5cc03db 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -295,6 +295,10 @@ NFS_ROOT	opt_nfsroot.h
 # SMB/CIFS requester
 NETSMB		opt_netsmb.h
=20
+# Netdump client kernel support
+NETDUMP_CLIENT		opt_netdump.h
+NETDUMP_CLIENT_DEBUG	opt_netdump.h
+
 # Options used only in subr_param.c.
 HZ		opt_param.h
 MAXFILES	opt_param.h
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 16e1d6f..b375388 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -75,6 +75,9 @@
 #include <netinet/if_ether.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
+#ifdef NETDUMP_CLIENT
+#include <netinet/netdump.h>
+#endif
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
=20
@@ -87,6 +90,27 @@
 #include "e1000_82571.h"
 #include "if_em.h"
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define	EM_CORE_LOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		EM_CORE_LOCK(adapter);					=
\
+} while (0)
+#define	EM_CORE_UNLOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		EM_CORE_UNLOCK(adapter);				=
\
+} while (0)
+#define	EM_TX_LOCK_COND(txr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		EM_TX_LOCK(txr);					=
\
+} while (0)
+#define	EM_TX_UNLOCK_COND(txr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		EM_TX_UNLOCK(txr);					=
\
+} while (0)
+
+#endif
+
 /*********************************************************************
  *  Set this to one to display debug statistics
  *********************************************************************/
@@ -300,14 +324,32 @@ static int	=
em_sysctl_eee(SYSCTL_HANDLER_ARGS);
=20
 static __inline void em_rx_discard(struct rx_ring *, int);
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+static int	_em_poll_generic(struct ifnet *ifp, enum poll_cmd cmd,
+		    int count, int locking);
 static poll_handler_t em_poll;
-#endif /* POLLING */
+#endif
+#ifdef NETDUMP_CLIENT
+static poll_handler_t em_poll_unlocked;
+static ndumplock_handler_t em_ndump_disable_intr;
+static ndumplock_handler_t em_ndump_enable_intr;
+#endif
=20
 /*********************************************************************
  *  FreeBSD Device Interface Entry Points
  *********************************************************************/
=20
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods em_ndump_methods =3D {
+	.ne_poll_locked =3D em_poll,
+	.ne_poll_unlocked =3D em_poll_unlocked,
+	.ne_disable_intr =3D em_ndump_disable_intr,
+	.ne_enable_intr =3D em_ndump_enable_intr
+};
+
+#endif
+
 static device_method_t em_methods[] =3D {
 	/* Device interface */
 	DEVMETHOD(device_probe, em_probe),
@@ -1416,14 +1458,14 @@ em_init(void *arg)
 }
=20
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
 /*********************************************************************
  *
  *  Legacy polling routine: note this only works with single queue
  *
  *********************************************************************/
 static int
-em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+_em_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int =
locking)
 {
 	struct adapter *adapter =3D ifp->if_softc;
 	struct tx_ring	*txr =3D adapter->tx_rings;
@@ -1431,9 +1473,9 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, int =
count)
 	u32		reg_icr;
 	int		rx_done;
=20
-	EM_CORE_LOCK(adapter);
+	EM_CORE_LOCK_COND(adapter, locking);
 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) {
-		EM_CORE_UNLOCK(adapter);
+		EM_CORE_UNLOCK_COND(adapter, locking);
 		return (0);
 	}
=20
@@ -1447,11 +1489,11 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, =
int count)
 			    em_local_timer, adapter);
 		}
 	}
-	EM_CORE_UNLOCK(adapter);
+	EM_CORE_UNLOCK_COND(adapter, locking);
=20
 	em_rxeof(rxr, count, &rx_done);
=20
-	EM_TX_LOCK(txr);
+	EM_TX_LOCK_COND(txr, locking);
 	em_txeof(txr);
 #ifdef EM_MULTIQUEUE
 	if (!drbr_empty(ifp, txr->br))
@@ -1460,12 +1502,49 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, =
int count)
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 		em_start_locked(ifp, txr);
 #endif
-	EM_TX_UNLOCK(txr);
+	EM_TX_UNLOCK_COND(txr, locking);
=20
 	return (rx_done);
 }
-#endif /* DEVICE_POLLING */
=20
+static int
+em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_em_poll_generic(ifp, cmd, count, 1));
+}
+#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */
+
+#ifdef NETDUMP_CLIENT
+static int
+em_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_em_poll_generic(ifp, cmd, count, 0));
+}
+
+static void
+em_ndump_disable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	EM_CORE_LOCK(adapter);
+	em_disable_intr(adapter);
+	EM_CORE_UNLOCK(adapter);
+}
+
+static void
+em_ndump_enable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	EM_CORE_LOCK(adapter);
+	em_enable_intr(adapter);
+	EM_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
=20
 /*********************************************************************
  *
@@ -2989,6 +3068,9 @@ em_setup_interface(device_t dev, struct adapter =
*adapter)
 	ifp->if_qflush =3D em_qflush;
 #else
 	ifp->if_start =3D em_start;
+#ifdef NETDUMP_CLIENT
+	ifp->if_ndumpfuncs =3D &em_ndump_methods;
+#endif
 	IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1);
 	ifp->if_snd.ifq_drv_maxlen =3D adapter->num_tx_desc - 1;
 	IFQ_SET_READY(&ifp->if_snd);
diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c
index 07a9d3e..93d1add 100644
--- a/sys/dev/e1000/if_igb.c
+++ b/sys/dev/e1000/if_igb.c
@@ -39,6 +39,7 @@
 #ifdef HAVE_KERNEL_OPTION_HEADERS
 #include "opt_device_polling.h"
 #include "opt_altq.h"
+#include "opt_netdump.h"
 #endif
=20
 #include <sys/param.h>
@@ -80,6 +81,9 @@
 #include <netinet/if_ether.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
+#ifdef NETDUMP_CLIENT
+#include <netinet/netdump.h>
+#endif
 #include <netinet/tcp.h>
 #include <netinet/tcp_lro.h>
 #include <netinet/udp.h>
@@ -93,6 +97,35 @@
 #include "e1000_82575.h"
 #include "if_igb.h"
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define	IGB_CORE_LOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		IGB_CORE_LOCK(adapter);					=
\
+} while (0)
+#define	IGB_CORE_UNLOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		IGB_CORE_UNLOCK(adapter);				=
\
+} while (0)
+#define	IGB_RX_LOCK_COND(rxr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IGB_RX_LOCK(rxr);					=
\
+} while (0)
+#define	IGB_RX_UNLOCK_COND(rxr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IGB_RX_UNLOCK(rxr);					=
\
+} while (0)
+#define	IGB_TX_LOCK_COND(txr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IGB_TX_LOCK(txr);					=
\
+} while (0)
+#define	IGB_TX_UNLOCK_COND(txr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IGB_TX_UNLOCK(txr);					=
\
+} while (0)
+
+#endif
+
 /*********************************************************************
  *  Set this to one to display debug statistics
  *********************************************************************/
@@ -240,7 +273,8 @@ static __inline	void igb_rx_discard(struct =
rx_ring *, int);
 static __inline void igb_rx_input(struct rx_ring *,
 		    struct ifnet *, struct mbuf *, u32);
=20
-static bool	igb_rxeof(struct igb_queue *, int, int *);
+static bool	_igb_rxeof_generic(struct igb_queue *, int, int *, int);
+#define	igb_rxeof(q, c, d)	_igb_rxeof_generic(q, c, d, 1)
 static void	igb_rx_checksum(u32, struct mbuf *, u32);
 static int	igb_tx_ctx_setup(struct tx_ring *,
 		    struct mbuf *, u32 *, u32 *);
@@ -289,14 +323,32 @@ static int	=
igb_set_flowcntl(SYSCTL_HANDLER_ARGS);
 static int	igb_sysctl_dmac(SYSCTL_HANDLER_ARGS);
 static int	igb_sysctl_eee(SYSCTL_HANDLER_ARGS);
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+static int	_igb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd,
+		    int count, int locking);
 static poll_handler_t igb_poll;
-#endif /* POLLING */
+#endif
+#ifdef NETDUMP_CLIENT
+static poll_handler_t igb_poll_unlocked;
+static ndumplock_handler_t igb_ndump_disable_intr;
+static ndumplock_handler_t igb_ndump_enable_intr;
+#endif
=20
 /*********************************************************************
  *  FreeBSD Device Interface Entry Points
  *********************************************************************/
=20
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods igb_ndump_methods =3D {
+	.ne_poll_locked =3D igb_poll,
+	.ne_poll_unlocked =3D igb_poll_unlocked,
+	.ne_disable_intr =3D igb_ndump_disable_intr,
+	.ne_enable_intr =3D igb_ndump_enable_intr
+};
+
+#endif
+
 static device_method_t igb_methods[] =3D {
 	/* Device interface */
 	DEVMETHOD(device_probe, igb_probe),
@@ -1524,7 +1576,7 @@ igb_irq_fast(void *arg)
 	return FILTER_HANDLED;
 }
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
 #if __FreeBSD_version >=3D 800000
 #define POLL_RETURN_COUNT(a) (a)
 static int
@@ -1532,7 +1584,7 @@ static int
 #define POLL_RETURN_COUNT(a)
 static void
 #endif
-igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+_igb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int =
locking)
 {
 	struct adapter		*adapter =3D ifp->if_softc;
 	struct igb_queue	*que;
@@ -1541,9 +1593,9 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int =
count)
 	u32			loop =3D IGB_MAX_LOOP;
 	bool			more;
=20
-	IGB_CORE_LOCK(adapter);
+	IGB_CORE_LOCK_COND(adapter, locking);
 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) {
-		IGB_CORE_UNLOCK(adapter);
+		IGB_CORE_UNLOCK_COND(adapter, locking);
 		return POLL_RETURN_COUNT(rx_done);
 	}
=20
@@ -1556,15 +1608,15 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, =
int count)
 		if (reg_icr & E1000_ICR_RXO)
 			adapter->rx_overruns++;
 	}
-	IGB_CORE_UNLOCK(adapter);
+	IGB_CORE_UNLOCK_COND(adapter, locking);
=20
 	for (int i =3D 0; i < adapter->num_queues; i++) {
 		que =3D &adapter->queues[i];
 		txr =3D que->txr;
=20
-		igb_rxeof(que, count, &rx_done);
+		_igb_rxeof_generic(que, count, &rx_done, locking);
=20
-		IGB_TX_LOCK(txr);
+		IGB_TX_LOCK_COND(txr, locking);
 		do {
 			more =3D igb_txeof(txr);
 		} while (loop-- && more);
@@ -1575,12 +1627,50 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, =
int count)
 		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 			igb_start_locked(txr, ifp);
 #endif
-		IGB_TX_UNLOCK(txr);
+		IGB_TX_UNLOCK_COND(txr, locking);
 	}
=20
 	return POLL_RETURN_COUNT(rx_done);
 }
-#endif /* DEVICE_POLLING */
+#endif /* DEVICE_POLLING || NETDUMP_CLIENT */
+
+static int
+igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_igb_poll_generic(ifp, cmd, count, 1));
+}
+
+#ifdef NETDUMP_CLIENT
+static int
+igb_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_igb_poll_generic(ifp, cmd, count, 0));
+}
+
+static void
+igb_ndump_disable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	IGB_CORE_LOCK(adapter);
+	igb_disable_intr(adapter);
+	IGB_CORE_UNLOCK(adapter);
+}
+
+static void
+igb_ndump_enable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	IGB_CORE_LOCK(adapter);
+	igb_enable_intr(adapter);
+	IGB_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
=20
 /*********************************************************************
  *
@@ -3123,6 +3213,10 @@ igb_setup_interface(device_t dev, struct adapter =
*adapter)
 	IFQ_SET_READY(&ifp->if_snd);
 #endif
=20
+#ifdef NETDUMP_CLIENT
+	ifp->if_ndumpfuncs =3D &igb_ndump_methods;
+#endif
+
 	ether_ifattach(ifp, adapter->hw.mac.addr);
=20
 	ifp->if_capabilities =3D ifp->if_capenable =3D 0;
@@ -4811,7 +4905,7 @@ igb_rx_input(struct rx_ring *rxr, struct ifnet =
*ifp, struct mbuf *m, u32 ptype)
  *  Return TRUE if more to clean, FALSE otherwise
  *********************************************************************/
 static bool
-igb_rxeof(struct igb_queue *que, int count, int *done)
+_igb_rxeof_generic(struct igb_queue *que, int count, int *done, int =
locking)
 {
 	struct adapter		*adapter =3D que->adapter;
 	struct rx_ring		*rxr =3D que->rxr;
@@ -4822,7 +4916,7 @@ igb_rxeof(struct igb_queue *que, int count, int =
*done)
 	u32			ptype, staterr =3D 0;
 	union e1000_adv_rx_desc	*cur;
=20
-	IGB_RX_LOCK(rxr);
+	IGB_RX_LOCK_COND(rxr, locking);
 	/* Sync the ring. */
 	bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
@@ -5007,7 +5101,7 @@ next_desc:
 	if (done !=3D NULL)
 		*done +=3D rxdone;
=20
-	IGB_RX_UNLOCK(rxr);
+	IGB_RX_UNLOCK_COND(rxr, locking);
 	return ((staterr & E1000_RXD_STAT_DD) ? TRUE : FALSE);
 }
=20
diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c
index 57e88a4..d05dd00 100644
--- a/sys/dev/e1000/if_lem.c
+++ b/sys/dev/e1000/if_lem.c
@@ -34,6 +34,7 @@
=20
 #include "opt_inet.h"
 #include "opt_inet6.h"
+#include "opt_netdump.h"
=20
 #ifdef HAVE_KERNEL_OPTION_HEADERS
 #include "opt_device_polling.h"
@@ -72,6 +73,7 @@
 #include <netinet/if_ether.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
+#include <netinet/netdump.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
=20
@@ -83,6 +85,35 @@
 #include "e1000_api.h"
 #include "if_lem.h"
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define	EM_CORE_LOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		EM_CORE_LOCK(adapter);					=
\
+} while (0)
+#define	EM_CORE_UNLOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		EM_CORE_UNLOCK(adapter);				=
\
+} while (0)
+#define	EM_RX_LOCK_COND(adapter, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		EM_RX_LOCK(adapter);					=
\
+} while (0)
+#define	EM_RX_UNLOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		EM_RX_UNLOCK(adapter);					=
\
+} while (0)
+#define	EM_TX_LOCK_COND(adapter, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		EM_TX_LOCK(adapter);					=
\
+} while (0)
+#define	EM_TX_UNLOCK_COND(adapter, locking) do {			=
\
+	if ((locking) !=3D 0)						=
\
+		EM_TX_UNLOCK(adapter);					=
\
+} while (0)
+
+#endif
+
 /*********************************************************************
  *  Legacy Em Driver version:
  *********************************************************************/
@@ -195,7 +226,8 @@ static void	lem_txeof(struct adapter *);
 static void	lem_tx_purge(struct adapter *);
 static int	lem_allocate_receive_structures(struct adapter *);
 static int	lem_allocate_transmit_structures(struct adapter *);
-static bool	lem_rxeof(struct adapter *, int, int *);
+static bool	_lem_rxeof_generic(struct adapter *, int, int *, int);
+#define	lem_rxeof(a, c, d)	_lem_rxeof_generic(a, c, d, 1)
 #ifndef __NO_STRICT_ALIGNMENT
 static int	lem_fixup_rx(struct adapter *);
 #endif
@@ -247,14 +279,32 @@ static void	lem_handle_link(void *context, =
int pending);
 static void	lem_add_rx_process_limit(struct adapter *, const char *,
 		    const char *, int *, int);
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+static int	_lem_poll_generic(struct ifnet *ifp, enum poll_cmd cmd,
+		    int count, int locking);
 static poll_handler_t lem_poll;
-#endif /* POLLING */
+#endif
+#ifdef NETDUMP_CLIENT
+static poll_handler_t lem_poll_unlocked;
+static ndumplock_handler_t lem_ndump_disable_intr;
+static ndumplock_handler_t lem_ndump_enable_intr;
+#endif
=20
 /*********************************************************************
  *  FreeBSD Device Interface Entry Points
  *********************************************************************/
=20
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods lem_ndump_methods =3D {
+	.ne_poll_locked =3D lem_poll,
+	.ne_poll_unlocked =3D lem_poll_unlocked,
+	.ne_disable_intr =3D lem_ndump_disable_intr,
+	.ne_enable_intr =3D lem_ndump_enable_intr
+};
+
+#endif
+
 static device_method_t lem_methods[] =3D {
 	/* Device interface */
 	DEVMETHOD(device_probe, lem_probe),
@@ -1225,21 +1275,21 @@ lem_init(void *arg)
 }
=20
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
 /*********************************************************************
  *
  *  Legacy polling routine =20
  *
  *********************************************************************/
 static int
-lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+_lem_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int =
locking)
 {
 	struct adapter *adapter =3D ifp->if_softc;
 	u32		reg_icr, rx_done =3D 0;
=20
-	EM_CORE_LOCK(adapter);
+	EM_CORE_LOCK_COND(adapter, locking);
 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) {
-		EM_CORE_UNLOCK(adapter);
+		EM_CORE_UNLOCK_COND(adapter, locking);
 		return (rx_done);
 	}
=20
@@ -1253,18 +1303,56 @@ lem_poll(struct ifnet *ifp, enum poll_cmd cmd, =
int count)
 			    lem_local_timer, adapter);
 		}
 	}
-	EM_CORE_UNLOCK(adapter);
+	EM_CORE_UNLOCK_COND(adapter, locking);
=20
-	lem_rxeof(adapter, count, &rx_done);
+	_lem_rxeof_generic(adapter, count, &rx_done, locking);
=20
-	EM_TX_LOCK(adapter);
+	EM_TX_LOCK_COND(adapter, locking);
 	lem_txeof(adapter);
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 		lem_start_locked(ifp);
-	EM_TX_UNLOCK(adapter);
+	EM_TX_UNLOCK_COND(adapter, locking);
 	return (rx_done);
 }
-#endif /* DEVICE_POLLING */
+
+static int
+lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_lem_poll_generic(ifp, cmd, count, 1));
+}
+#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */
+
+#ifdef NETDUMP_CLIENT
+static int
+lem_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_lem_poll_generic(ifp, cmd, count, 0));
+}
+
+static void
+lem_ndump_disable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	EM_CORE_LOCK(adapter);
+	lem_disable_intr(adapter);
+	EM_CORE_UNLOCK(adapter);
+}
+
+static void
+lem_ndump_enable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	EM_CORE_LOCK(adapter);
+	lem_enable_intr(adapter);
+	EM_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
=20
 /*********************************************************************
  *
@@ -2363,6 +2451,9 @@ lem_setup_interface(device_t dev, struct adapter =
*adapter)
 	ifp->if_flags =3D IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl =3D lem_ioctl;
 	ifp->if_start =3D lem_start;
+#ifdef NETDUMP_CLIENT
+	ifp->if_ndumpfuncs =3D &lem_ndump_methods;
+#endif
 	IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1);
 	ifp->if_snd.ifq_drv_maxlen =3D adapter->num_tx_desc - 1;
 	IFQ_SET_READY(&ifp->if_snd);
@@ -3438,7 +3529,7 @@ lem_free_receive_structures(struct adapter =
*adapter)
  *  For polling we also now return the number of cleaned packets
  *********************************************************************/
 static bool
-lem_rxeof(struct adapter *adapter, int count, int *done)
+_lem_rxeof_generic(struct adapter *adapter, int count, int *done, int =
locking)
 {
 	struct ifnet	*ifp =3D adapter->ifp;
 	struct mbuf	*mp;
@@ -3447,7 +3538,7 @@ lem_rxeof(struct adapter *adapter, int count, int =
*done)
 	int		i, rx_sent =3D 0;
 	struct e1000_rx_desc   *current_desc;
=20
-	EM_RX_LOCK(adapter);
+	EM_RX_LOCK_COND(adapter, locking);
 	i =3D adapter->next_rx_desc_to_check;
 	current_desc =3D &adapter->rx_desc_base[i];
 	bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
@@ -3461,7 +3552,7 @@ lem_rxeof(struct adapter *adapter, int count, int =
*done)
 	if (!((current_desc->status) & E1000_RXD_STAT_DD)) {
 		if (done !=3D NULL)
 			*done =3D rx_sent;
-		EM_RX_UNLOCK(adapter);
+		EM_RX_UNLOCK_COND(adapter, locking);
 		return (FALSE);
 	}
=20
@@ -3601,9 +3692,9 @@ discard:
 		/* Call into the stack */
 		if (m !=3D NULL) {
 			adapter->next_rx_desc_to_check =3D i;
-			EM_RX_UNLOCK(adapter);
+			EM_RX_UNLOCK_COND(adapter, locking);
 			(*ifp->if_input)(ifp, m);
-			EM_RX_LOCK(adapter);
+			EM_RX_LOCK_COND(adapter, locking);
 			rx_sent++;
 			i =3D adapter->next_rx_desc_to_check;
 		}
@@ -3617,7 +3708,7 @@ discard:
 	E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), i);
 	if (done !=3D NULL)
 		*done =3D rx_sent;
-	EM_RX_UNLOCK(adapter);
+	EM_RX_UNLOCK_COND(adapter, locking);
 	return ((status & E1000_RXD_STAT_DD) ? TRUE : FALSE);
 }
=20
diff --git a/sys/dev/ixgb/if_ixgb.c b/sys/dev/ixgb/if_ixgb.c
index 2e62c1c..32b154a 100644
--- a/sys/dev/ixgb/if_ixgb.c
+++ b/sys/dev/ixgb/if_ixgb.c
@@ -35,10 +35,24 @@ POSSIBILITY OF SUCH DAMAGE.
=20
 #ifdef HAVE_KERNEL_OPTION_HEADERS
 #include "opt_device_polling.h"
+#include "opt_netdump.h"
 #endif
=20
 #include <dev/ixgb/if_ixgb.h>
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define	IXGB_LOCK_COND(adapter, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IXGB_LOCK(adapter);					=
\
+} while (0)
+#define	IXGB_UNLOCK_COND(adapter, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IXGB_UNLOCK(adapter);					=
\
+} while (0)
+
+#endif
+
 /*********************************************************************
  *  Set this to one to display debug statistics
  *********************************************************************/
@@ -145,14 +159,32 @@ static int
 ixgb_dma_malloc(struct adapter *, bus_size_t,
 		struct ixgb_dma_alloc *, int);
 static void     ixgb_dma_free(struct adapter *, struct ixgb_dma_alloc =
*);
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+static int      _ixgb_poll_generic(struct ifnet *ifp, enum poll_cmd =
cmd,
+		    int count, int locking);
 static poll_handler_t ixgb_poll;
 #endif
+#ifdef NETDUMP_CLIENT
+static poll_handler_t ixgb_poll_unlocked;
+static ndumplock_handler_t ixgb_ndump_disable_intr;
+static ndumplock_handler_t ixgb_ndump_enable_intr;
+#endif
=20
 /*********************************************************************
  *  FreeBSD Device Interface Entry Points
  *********************************************************************/
=20
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods ixgb_ndump_methods =3D {
+	.ne_poll_locked =3D ixgb_poll,
+	.ne_poll_unlocked =3D ixgb_poll_unlocked,
+	.ne_disable_intr =3D ixgb_ndump_disable_intr,
+	.ne_enable_intr =3D ixgb_ndump_enable_intr
+};
+
+#endif
+
 static device_method_t ixgb_methods[] =3D {
 	/* Device interface */
 	DEVMETHOD(device_probe, ixgb_probe),
@@ -751,7 +783,7 @@ ixgb_init(void *arg)
 	return;
 }
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
 static int
 ixgb_poll_locked(struct ifnet * ifp, enum poll_cmd cmd, int count)
 {
@@ -777,18 +809,57 @@ ixgb_poll_locked(struct ifnet * ifp, enum poll_cmd =
cmd, int count)
 }
=20
 static int
-ixgb_poll(struct ifnet * ifp, enum poll_cmd cmd, int count)
+_ixgb_poll_generic(struct ifnet * ifp, enum poll_cmd cmd, int count,
+    int locking)
 {
 	struct adapter *adapter =3D ifp->if_softc;
 	int rx_npkts =3D 0;
=20
-	IXGB_LOCK(adapter);
+	IXGB_LOCK_COND(adapter, locking);
 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
 		rx_npkts =3D ixgb_poll_locked(ifp, cmd, count);
-	IXGB_UNLOCK(adapter);
+	IXGB_UNLOCK_COND(adapter, locking);
 	return (rx_npkts);
 }
-#endif /* DEVICE_POLLING */
+
+static int
+ixgb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_ixgb_poll_generic(ifp, cmd, count, 1));
+}
+#endif /* !DEVICE_POLLING  && !NETDUMP_CLIENT */
+
+#ifdef NETDUMP_CLIENT
+static int
+ixgb_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_ixgb_poll_generic(ifp, cmd, count, 0));
+}
+
+static void
+ixgb_ndump_disable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	IXGB_LOCK(adapter);
+	ixgb_disable_intr(adapter);
+	IXGB_UNLOCK(adapter);
+}
+
+static void
+ixgb_ndump_enable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	IXGB_LOCK(adapter);
+	ixgb_enable_intr(adapter);
+	IXGB_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
=20
 /*********************************************************************
  *
@@ -1356,6 +1427,9 @@ ixgb_setup_interface(device_t dev, struct adapter =
* adapter)
 	ifp->if_ioctl =3D ixgb_ioctl;
 	ifp->if_start =3D ixgb_start;
 	ifp->if_snd.ifq_maxlen =3D adapter->num_tx_desc - 1;
+#ifdef NETDUMP_CLIENT
+	ifp->if_ndumpfuncs =3D &ixgb_ndump_methods;
+#endif
=20
 #if __FreeBSD_version < 500000
 	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
diff --git a/sys/dev/ixgb/if_ixgb.h b/sys/dev/ixgb/if_ixgb.h
index 4e88db7..a75b7b7 100644
--- a/sys/dev/ixgb/if_ixgb.h
+++ b/sys/dev/ixgb/if_ixgb.h
@@ -60,6 +60,9 @@ POSSIBILITY OF SUCH DAMAGE.
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#ifdef NETDUMP_CLIENT
+#include <netinet/netdump.h>
+#endif
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
=20
diff --git a/sys/dev/ixgbe/ixgbe.c b/sys/dev/ixgbe/ixgbe.c
index 581dcc6..c1e88c7 100644
--- a/sys/dev/ixgbe/ixgbe.c
+++ b/sys/dev/ixgbe/ixgbe.c
@@ -35,8 +35,34 @@
=20
 #include "opt_inet.h"
 #include "opt_inet6.h"
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_device_polling.h"
+#include "opt_netdump.h"
+#endif
+
 #include "ixgbe.h"
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define	IXGBE_RX_LOCK_COND(rxr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IXGBE_RX_LOCK(rxr);					=
\
+} while (0)
+#define	IXGBE_RX_UNLOCK_COND(rxr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IXGBE_RX_UNLOCK(rxr);					=
\
+} while (0)
+#define	IXGBE_TX_LOCK_COND(txr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IXGBE_TX_LOCK(txr);					=
\
+} while (0)
+#define	IXGBE_TX_UNLOCK_COND(txr, locking) do {				=
\
+	if ((locking) !=3D 0)						=
\
+		IXGBE_TX_UNLOCK(txr);					=
\
+} while (0)
+
+#endif
+
 /*********************************************************************
  *  Set this to one to display debug statistics
  *********************************************************************/
@@ -147,8 +173,9 @@ static void	ixgbe_setup_hw_rsc(struct rx_ring *);
 static void     ixgbe_enable_intr(struct adapter *);
 static void     ixgbe_disable_intr(struct adapter *);
 static void     ixgbe_update_stats_counters(struct adapter *);
-static void	ixgbe_txeof(struct tx_ring *);
-static bool	ixgbe_rxeof(struct ix_queue *);
+static bool	ixgbe_txeof(struct tx_ring *);
+static bool	_ixgbe_rxeof_generic(struct ix_queue *, int, int *, =
int);
+#define	ixgbe_rxeof(a)	_ixgbe_rxeof_generic(a, =
(a)->rxr->process_limit, NULL, 1)
 static void	ixgbe_rx_checksum(u32, struct mbuf *, u32);
 static void     ixgbe_set_promisc(struct adapter *);
 static void     ixgbe_set_multi(struct adapter *);
@@ -207,10 +234,32 @@ static void	ixgbe_reinit_fdir(void *, int);
 /* Missing shared code prototype */
 extern void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw);
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+static int	_ixgbe_poll_generic(struct ifnet *ifp, enum poll_cmd =
cmd,
+		    int count, int locking);
+static poll_handler_t ixgbe_poll;
+#endif
+#ifdef NETDUMP_CLIENT
+static poll_handler_t ixgbe_poll_unlocked;
+static ndumplock_handler_t ixgbe_ndump_disable_intr;
+static ndumplock_handler_t ixgbe_ndump_enable_intr;
+#endif
+
 /*********************************************************************
  *  FreeBSD Device Interface Entry Points
  *********************************************************************/
=20
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods ixgbe_ndump_methods =3D {
+	.ne_poll_locked =3D ixgbe_poll,
+	.ne_poll_unlocked =3D ixgbe_poll_unlocked,
+	.ne_disable_intr =3D ixgbe_ndump_disable_intr,
+	.ne_enable_intr =3D ixgbe_ndump_enable_intr
+};
+
+#endif
+
 static device_method_t ixgbe_methods[] =3D {
 	/* Device interface */
 	DEVMETHOD(device_probe, ixgbe_probe),
@@ -667,6 +716,11 @@ ixgbe_detach(device_t dev)
 		return (EBUSY);
 	}
=20
+#ifdef DEVICE_POLLING
+	if ((adapter->ifp->if_capenable & IFCAP_POLLING) !=3D 0)
+		ether_poll_deregister(adapter->ifp);
+#endif
+
 	IXGBE_CORE_LOCK(adapter);
 	ixgbe_stop(adapter);
 	IXGBE_CORE_UNLOCK(adapter);
@@ -1026,6 +1080,25 @@ ixgbe_ioctl(struct ifnet * ifp, u_long command, =
caddr_t data)
 	{
 		int mask =3D ifr->ifr_reqcap ^ ifp->if_capenable;
 		IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)");
+#ifdef DEVICE_POLLING
+		if ((mask & IFCAP_POLLING) !=3D 0) {
+			if ((ifr->ifr_reqcap & IFCAP_POLLING) !=3D 0) {
+				error =3D =
ether_poll_register(ixgbe_poll, ifp);
+				if (error !=3D 0)
+					return (error);
+				IXGBE_CORE_LOCK(adapter);
+				ixgbe_disable_intr(adapter);
+				ifp->if_capenable |=3D IFCAP_POLLING;
+				IXGBE_CORE_UNLOCK(adapter);
+			} else {
+				error =3D ether_poll_deregister(ifp);
+				IXGBE_CORE_LOCK(adapter);
+				ixgbe_enable_intr(adapter);
+				ifp->if_capenable &=3D ~IFCAP_POLLING;
+				IXGBE_CORE_UNLOCK(adapter);
+			}
+		}
+#endif /* !DEVICE_POLLING */
 		if (mask & IFCAP_HWCSUM)
 			ifp->if_capenable ^=3D IFCAP_HWCSUM;
 		if (mask & IFCAP_TSO4)
@@ -1339,8 +1412,13 @@ ixgbe_init_locked(struct adapter *adapter)
 	/* Set up VLAN support and filter */
 	ixgbe_setup_vlan_hw_support(adapter);
=20
-	/* And now turn on interrupts */
-	ixgbe_enable_intr(adapter);
+#ifdef DEVICE_POLLING
+	/* Disable interrupts if polling is on, enable otherwise. */
+	if ((ifp->if_capenable & IFCAP_POLLING) !=3D 0)
+		ixgbe_disable_intr(adapter);
+	else
+#endif
+		ixgbe_enable_intr(adapter);
=20
 	/* Now inform the stack we're ready */
 	ifp->if_drv_flags |=3D IFF_DRV_RUNNING;
@@ -1437,6 +1515,10 @@ ixgbe_handle_que(void *context, int pending)
 	return;
 }
=20
+#ifdef DEVICE_POLLING
+	if ((adapter->ifp->if_capenable & IFCAP_POLLING) !=3D 0)
+		return;
+#endif
=20
 /*********************************************************************
  *
@@ -2646,6 +2728,9 @@ ixgbe_setup_interface(device_t dev, struct adapter =
*adapter)
 	ifp->if_snd.ifq_drv_maxlen =3D adapter->num_tx_desc - 2;
 	IFQ_SET_READY(&ifp->if_snd);
 #endif
+#ifdef NETDUMP_CLIENT
+	ifp->if_ndumpfuncs =3D &ixgbe_ndump_methods;
+#endif
=20
 	ether_ifattach(ifp, adapter->hw.mac.addr);
=20
@@ -2665,6 +2750,9 @@ ixgbe_setup_interface(device_t dev, struct adapter =
*adapter)
 			     |  IFCAP_VLAN_MTU
 			     |  IFCAP_HWSTATS;
 	ifp->if_capenable =3D ifp->if_capabilities;
+#ifdef DEVICE_POLLING
+	ifp->if_capabilities |=3D IFCAP_POLLING;
+#endif
=20
 	/*
 	** Don't turn this on by default, if vlans are
@@ -3582,6 +3670,91 @@ ixgbe_atr(struct tx_ring *txr, struct mbuf *mp)
 }
 #endif /* IXGBE_FDIR */
=20
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+static int
+_ixgbe_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count,
+    int locking)
+{
+	struct adapter *adapter;
+	struct tx_ring *txr;
+	struct ix_queue *que;
+	struct ixgbe_hw *hw;
+	u32 loop, reg_eicr;
+	int rx_npkts;
+	bool more_tx;
+
+	adapter =3D ifp->if_softc;
+	txr =3D adapter->tx_rings;
+	que =3D adapter->queues;
+	hw =3D &adapter->hw;
+	loop =3D MAX_LOOP;
+	rx_npkts =3D 0;
+
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0)
+		return (rx_npkts);
+
+	if (cmd =3D=3D POLL_AND_CHECK_STATUS) {
+		reg_eicr =3D IXGBE_READ_REG(hw, IXGBE_EICR);
+
+		/* Link status change */
+		if ((reg_eicr & IXGBE_EICR_LSC) !=3D 0)
+			taskqueue_enqueue(adapter->tq, =
&adapter->link_task);
+	}
+	_ixgbe_rxeof_generic(que, count, &rx_npkts, locking);
+	IXGBE_TX_LOCK_COND(txr, locking);
+	do {
+		more_tx =3D ixgbe_txeof(txr);
+	} while (loop-- && more_tx);
+#if __FreeBSD_version >=3D 800000
+	if (!drbr_empty(ifp, txr->br))
+		ixgbe_mq_start_locked(ifp, txr);
+#else
+	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		ixgbe_start_locked(txr, ifp);
+#endif
+	IXGBE_TX_UNLOCK_COND(txr, locking);
+	return (rx_npkts);
+}
+
+static int
+ixgbe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_ixgbe_poll_generic(ifp, cmd, count, 1));
+}
+#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */
+
+#ifdef NETDUMP_CLIENT
+static int
+ixgbe_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+
+	return (_ixgbe_poll_generic(ifp, cmd, count, 0));
+}
+
+static void
+ixgbe_ndump_disable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	IXGBE_CORE_LOCK(adapter);
+	ixgbe_disable_intr(adapter);
+	IXGBE_CORE_UNLOCK(adapter);
+}
+
+static void
+ixgbe_ndump_enable_intr(struct ifnet *ifp)
+{
+	struct adapter *adapter;
+
+	adapter =3D ifp->if_softc;
+	IXGBE_CORE_LOCK(adapter);
+	ixgbe_enable_intr(adapter);
+	IXGBE_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
+
 /**********************************************************************
  *
  *  Examine each tx_buffer in the used queue. If the hardware is done
@@ -3589,7 +3762,7 @@ ixgbe_atr(struct tx_ring *txr, struct mbuf *mp)
  *  tx_buffer is put back on the free queue.
  *
  =
**********************************************************************/
-static void
+static bool
 ixgbe_txeof(struct tx_ring *txr)
 {
 	struct adapter		*adapter =3D txr->adapter;
@@ -3632,13 +3805,13 @@ ixgbe_txeof(struct tx_ring *txr)
 			netmap_tx_irq(ifp, txr->me |
 			    (NETMAP_LOCKED_ENTER|NETMAP_LOCKED_EXIT));
 		}
-		return;
+		return FALSE;
 	}
 #endif /* DEV_NETMAP */
=20
 	if (txr->tx_avail =3D=3D txr->num_desc) {
 		txr->queue_status =3D IXGBE_QUEUE_IDLE;
-		return;
+		return FALSE;
 	}
=20
 	/* Get work starting point */
@@ -3735,7 +3908,7 @@ ixgbe_txeof(struct tx_ring *txr)
 	if (txr->tx_avail =3D=3D txr->num_desc)
 		txr->queue_status =3D IXGBE_QUEUE_IDLE;
=20
-	return;
+	return TRUE;
 }
=20
 /*********************************************************************
@@ -4405,20 +4578,20 @@ ixgbe_rx_discard(struct rx_ring *rxr, int i)
  *  Return TRUE for more work, FALSE for all clean.
  *********************************************************************/
 static bool
-ixgbe_rxeof(struct ix_queue *que)
+_ixgbe_rxeof_generic(struct ix_queue *que, int count, int *rx_npktsp,
+			     int locking)
 {
 	struct adapter		*adapter =3D que->adapter;
 	struct rx_ring		*rxr =3D que->rxr;
 	struct ifnet		*ifp =3D adapter->ifp;
 	struct lro_ctrl		*lro =3D &rxr->lro;
 	struct lro_entry	*queued;
-	int			i, nextp, processed =3D 0;
+	int			i, nextp, processed =3D 0, rx_npkts =3D =
0;
 	u32			staterr =3D 0;
-	u16			count =3D rxr->process_limit;
 	union ixgbe_adv_rx_desc	*cur;
 	struct ixgbe_rx_buf	*rbuf, *nbuf;
=20
-	IXGBE_RX_LOCK(rxr);
+	IXGBE_RX_LOCK_COND(rxr, locking);
=20
 #ifdef DEV_NETMAP
 	/* Same as the txeof routine: wakeup clients on intr. */
@@ -4588,6 +4761,7 @@ next_desc:
 		if (sendmp !=3D NULL) {
 			rxr->next_to_check =3D i;
 			ixgbe_rx_input(rxr, ifp, sendmp, ptype);
+			rx_npkts++;
 			i =3D rxr->next_to_check;
 		}
=20
@@ -4612,15 +4786,18 @@ next_desc:
 		tcp_lro_flush(lro, queued);
 	}
=20
-	IXGBE_RX_UNLOCK(rxr);
+	IXGBE_RX_UNLOCK_COND(rxr, locking);
=20
+	if (rx_npktsp !=3D NULL)
+		*rx_npktsp =3D rx_npkts;
 	/*
 	** Still have cleaning to do?
 	*/
-	if ((staterr & IXGBE_RXD_STAT_DD) !=3D 0)
+	if ((staterr & IXGBE_RXD_STAT_DD) !=3D 0) {
 		return (TRUE);
-	else
+	} else {
 		return (FALSE);
+	}
 }
=20
=20
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 77b72ed..f7278b4 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -66,6 +66,9 @@
 #include <netinet/if_ether.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
+#ifdef NETDUMP_CLIENT
+#include <netinet/netdump.h>
+#endif
 #include <netinet/tcp.h>
 #include <netinet/tcp_lro.h>
 #include <netinet/udp.h>
diff --git a/sys/i386/i386/minidump_machdep.c =
b/sys/i386/i386/minidump_machdep.c
index e0cd1ff..417d9a9 100644
--- a/sys/i386/i386/minidump_machdep.c
+++ b/sys/i386/i386/minidump_machdep.c
@@ -248,13 +248,19 @@ minidumpsys(struct dumperinfo *di)
 	}
 	dumpsize +=3D PAGE_SIZE;
=20
-	/* Determine dump offset on device. */
-	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * =
2) {
-		error =3D ENOSPC;
-		goto fail;
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+
+		/* Determine dump offset on device. */
+		if (di->mediasize <
+		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+			error =3D ENOSPC;
+			goto fail;
+		}
+		dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
+		dumplo -=3D sizeof(kdh) * 2;
 	}
-	dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
-	dumplo -=3D sizeof(kdh) * 2;
 	progress =3D dumpsize;
=20
 	/* Initialize mdhdr */
diff --git a/sys/ia64/ia64/dump_machdep.c b/sys/ia64/ia64/dump_machdep.c
index 6fa8608..716c111 100644
--- a/sys/ia64/ia64/dump_machdep.c
+++ b/sys/ia64/ia64/dump_machdep.c
@@ -245,13 +245,20 @@ dumpsys(struct dumperinfo *di)
 	dumpsize +=3D fileofs;
 	hdrgap =3D fileofs - DEV_ALIGN(hdrsz);
=20
-	/* Determine dump offset on device. */
-	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * =
2) {
-		error =3D ENOSPC;
-		goto fail;
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+
+		/* Determine dump offset on device. */
+		if (di->mediasize <
+		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+			error =3D ENOSPC;
+			goto fail;
+		}
+		dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
+		dumplo -=3D sizeof(kdh) * 2;
 	}
-	dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
-	dumplo -=3D sizeof(kdh) * 2;
=20
 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, =
dumpsize, di->blocksize);
=20
diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c
index b120263..df9dd69 100644
--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
=20
 #include "opt_ddb.h"
 #include "opt_kdb.h"
+#include "opt_netdump.h"
 #include "opt_panic.h"
 #include "opt_sched.h"
 #include "opt_watchdog.h"
@@ -84,6 +85,15 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm_pager.h>
 #include <vm/swap_pager.h>
=20
+#ifdef NETDUMP_CLIENT
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/netdump.h>
+#endif
+
 #include <sys/signalvar.h>
=20
 #ifndef PANIC_REBOOT_WAIT_TIME
@@ -754,6 +764,17 @@ vpanic(const char *fmt, va_list ap)
 	kern_reboot(bootopt);
 }
=20
+#ifdef DDB
+DB_COMMAND(netdump, ddb_force_netdump)
+{
+
+	if (nd_enable)
+		netdumpsys();
+	else
+		db_printf("netdump is not enabled\n");
+}
+#endif
+
 /*
  * Support for poweroff delay.
  *
@@ -861,7 +882,9 @@ dump_write(struct dumperinfo *di, void *virtual, =
vm_offset_t physical,
     off_t offset, size_t length)
 {
=20
-	if (length !=3D 0 && (offset < di->mediaoffset ||
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) !=3D 0 && length !=3D 0 &&
+	    (offset < di->mediaoffset ||
 	    offset - di->mediaoffset + length > di->mediasize)) {
 		printf("Attempt to write outside dump device =
boundaries.\n"
 	    "offset(%jd), mediaoffset(%jd), length(%ju), =
mediasize(%jd).\n",
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 3288a4f..38fb118 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -73,6 +73,7 @@ struct	carp_softc;
 struct  ifvlantrunk;
 struct	route;
 struct	vnet;
+struct	netdump_methods;
 #endif
=20
 #include <sys/queue.h>		/* get TAILQ macros */
@@ -203,7 +204,7 @@ struct ifnet {
 	char	*if_description;	/* interface description */
 	u_int	if_fib;			/* interface FIB */
 	u_char	if_alloctype;		/* if_type at time of allocation =
*/
-
+	struct netdump_methods	*if_ndumpfuncs;	/* netdump virtual =
methods */
 	u_int	if_hw_tsomax;		/* tso burst length limit, the =
minimum
 					 * is (IP_MAXPACKET / 8).
 					 * XXXAO: Have to find a better =
place
@@ -957,13 +958,13 @@ void	if_deregister_com_alloc(u_char type);
 #define IF_LLADDR(ifp)							=
\
     LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr))
=20
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
 enum poll_cmd {	POLL_ONLY, POLL_AND_CHECK_STATUS };
=20
 typedef	int poll_handler_t(struct ifnet *ifp, enum poll_cmd cmd, =
int count);
 int    ether_poll_register(poll_handler_t *h, struct ifnet *ifp);
 int    ether_poll_deregister(struct ifnet *ifp);
-#endif /* DEVICE_POLLING */
+#endif /* DEVICE_POLLING || NETDUMP_CLIENT */
=20
 #endif /* _KERNEL */
=20
diff --git a/sys/netinet/netdump.h b/sys/netinet/netdump.h
new file mode 100644
index 0000000..fb0510b
--- /dev/null
+++ b/sys/netinet/netdump.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2005-2011 Sandvine Incorporated
+ * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in =
the
+ *    documentation and/or other materials provided with the =
distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' =
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS =
INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+#ifndef _NETINET_NETDUMP_H_
+#define	_NETINET_NETDUMP_H_
+
+#include <sys/types.h>
+
+#define	NETDUMP_PORT		20023	/* Server udp port =
number for data. */
+#define	NETDUMP_ACKPORT		20024	/* Client udp port =
number for acks. */
+
+#define	NETDUMP_HERALD		1	/* Broadcast before =
starting a dump. */
+#define	NETDUMP_FINISHED	2	/* Send after finishing =
a dump. */
+#define	NETDUMP_VMCORE		3	/* Contains dump datas. =
*/
+#define	NETDUMP_KDH		4	/* Contains kernel dump =
header. */
+
+#define	NETDUMP_DATASIZE	8192	/* Packets payload. */
+
+struct netdump_msg_hdr {
+	uint32_t	mh_type; /* NETDUMP_HERALD, _FINISHED, _VMCORE, =
_KDH. */
+	uint32_t	mh_seqno;	/* Match acks with msgs. */
+	uint64_t	mh_offset;	/* vmcore offset (bytes). */
+	uint32_t	mh_len;		/* Attached data (bytes). */
+	uint32_t	mh__pad; /* Pad space matching 32- and 64-bits =
archs. */
+};
+
+struct netdump_ack {
+	uint32_t	na_seqno;	/* Match acks with msgs. */
+};
+
+struct netdump_msg {
+	struct netdump_msg_hdr nm_hdr;
+	uint8_t		nm_data[NETDUMP_DATASIZE];
+};
+
+#ifdef _KERNEL
+
+typedef void ndumplock_handler_t(struct ifnet *);
+
+struct netdump_methods {
+	poll_handler_t		*ne_poll_locked;
+	poll_handler_t		*ne_poll_unlocked;
+	ndumplock_handler_t	*ne_disable_intr;
+	ndumplock_handler_t	*ne_enable_intr;
+};
+
+int	 netdumpsys(void);
+
+extern int nd_enable;
+
+#endif
+
+#endif /* !_NETINET_NETDUMP_H_ */
+/*
+ * Copyright (c) 2005-2011 Sandvine Incorporated
+ * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in =
the
+ *    documentation and/or other materials provided with the =
distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' =
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS =
INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+#ifndef _NETINET_NETDUMP_H_
+#define	_NETINET_NETDUMP_H_
+
+#include <sys/types.h>
+
+#define	NETDUMP_PORT		20023	/* Server udp port =
number for data. */
+#define	NETDUMP_ACKPORT		20024	/* Client udp port =
number for acks. */
+
+#define	NETDUMP_HERALD		1	/* Broadcast before =
starting a dump. */
+#define	NETDUMP_FINISHED	2	/* Send after finishing =
a dump. */
+#define	NETDUMP_VMCORE		3	/* Contains dump datas. =
*/
+#define	NETDUMP_KDH		4	/* Contains kernel dump =
header. */
+
+#define	NETDUMP_DATASIZE	8192	/* Packets payload. */
+
+struct netdump_msg_hdr {
+	uint32_t	mh_type; /* NETDUMP_HERALD, _FINISHED, _VMCORE, =
_KDH. */
+	uint32_t	mh_seqno;	/* Match acks with msgs. */
+	uint64_t	mh_offset;	/* vmcore offset (bytes). */
+	uint32_t	mh_len;		/* Attached data (bytes). */
+	uint32_t	mh__pad; /* Pad space matching 32- and 64-bits =
archs. */
+};
+
+struct netdump_ack {
+	uint32_t	na_seqno;	/* Match acks with msgs. */
+};
+
+struct netdump_msg {
+	struct netdump_msg_hdr nm_hdr;
+	uint8_t		nm_data[NETDUMP_DATASIZE];
+};
+
+#ifdef _KERNEL
+
+typedef void ndumplock_handler_t(struct ifnet *);
+
+struct netdump_methods {
+	poll_handler_t		*ne_poll_locked;
+	poll_handler_t		*ne_poll_unlocked;
+	ndumplock_handler_t	*ne_disable_intr;
+	ndumplock_handler_t	*ne_enable_intr;
+};
+
+int	 netdumpsys(void);
+
+extern int nd_enable;
+
+#endif
+
+#endif /* !_NETINET_NETDUMP_H_ */
diff --git a/sys/netinet/netdump_client.c b/sys/netinet/netdump_client.c
new file mode 100644
index 0000000..1b73e1b
--- /dev/null
+++ b/sys/netinet/netdump_client.c
@@ -0,0 +1,1201 @@
+/*-
+ * Copyright (c) 2005-2011 Sandvine Incorporated. All rights reserved.
+ * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in =
the
+ *    documentation and/or other materials provided with the =
distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' =
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS =
INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN =
ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY =
OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * netdump_client.c
+ * FreeBSD subsystem supporting netdump network dumps.
+ * A dedicated server must be running to accept client dumps.
+ * XXX: This should be split into machdep and non-machdep parts
+ *
+*/
+
+#include "opt_ddb.h"
+#include "opt_kdb.h"
+#include "opt_netdump.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kerneldump.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/reboot.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_options.h>
+#include <netinet/netdump.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <machine/in_cksum.h>
+#include <machine/pcb.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#ifdef NETDUMP_CLIENT_DEBUG
+#define	NETDDEBUG(f, ...)		printf((f), ## =
__VA_ARGS__)
+#define	NETDDEBUG_IF(i, f, ...)		if_printf((i), (f), ## =
__VA_ARGS__)
+#if NETDUMP_CLIENT_DEBUG > 1
+#define	NETDDEBUGV(f, ...)		printf((f), ## =
__VA_ARGS__)
+#define	NETDDEBUGV_IF(i, f, ...)	if_printf((i), (f), ## =
__VA_ARGS__)
+#else
+#define	NETDDEBUGV(f, ...)
+#define	NETDDEBUGV_IF(i, f, ...)
+#endif
+#else
+#define	NETDDEBUG(f, ...)
+#define	NETDDEBUG_IF(i, f, ...)
+#define	NETDDEBUGV(f, ...)
+#define	NETDDEBUGV_IF(i, f, ...)
+#endif
+
+static void	 nd_handle_arp(struct mbuf **mb);
+static void	 nd_handle_ip(struct mbuf **mb);
+static int	 netdump_arp_server(void);
+static void	 netdump_config_defaults(void *dummy __unused);
+static int	 netdump_dumper(void *priv __unused, void *virtual,
+		    vm_offset_t physical __unused, off_t offset, size_t =
length);
+static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp,=20=

+		    struct ether_addr dst, u_short etype);
+static int	 netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr =
__unused, void *opt_args __unused);
+static void	 netdump_network_poll(void);
+static void	 netdump_pkt_in(struct ifnet *ifp, struct mbuf *m);
+static int	 netdump_send(uint32_t type, off_t offset, unsigned char =
*data,
+		    uint32_t datalen);
+static int	 netdump_send_arp(void);
+static int	 netdump_udp_output(struct mbuf *m);
+
+static int	 sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS);
+static int	 sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS);
+
+/* Must be at least as big as the chunks dumpsys() gives us. */
+static unsigned char buf[MAXDUMPPGS * PAGE_SIZE];
+static uint64_t rcvd_acks;
+static uint32_t nd_seqno =3D 1;
+static int dump_failed, have_server_mac;
+
+/*
+ * Times to poll the NIC (0.5ms each poll) before assuming packetloss
+ * occurred (default to 5s).
+ */
+static int nd_polls =3D 10000;
+
+/* Times to retransmit lost packets. */
+static int nd_retries =3D 10;
+
+/* General dynamic settings. */
+static char nd_ifp_str[IFNAMSIZ];
+static struct ether_addr nd_gw_mac;
+static struct in_addr nd_server =3D {INADDR_ANY};
+static struct in_addr nd_client =3D {INADDR_ANY};
+static struct in_addr nd_gw =3D {INADDR_ANY};
+struct ifnet *nd_ifp;
+int nd_enable =3D 0;
+static uint16_t nd_server_port =3D NETDUMP_PORT;
+
+/* Tunables storages. */
+static char nd_server_tun[INET_ADDRSTRLEN];
+static char nd_client_tun[INET_ADDRSTRLEN];
+static char nd_gw_tun[INET_ADDRSTRLEN];
+static char nd_nic_tun[IFNAMSIZ];
+
+/*
+ * Checks for netdump support on a network interface
+ *
+ * Parameters:
+ *	ifp	The network interface that is being tested for support
+ *
+ * Returns:
+ *	int	1 if the interface is supported, 0 if not
+ */
+static __inline int
+netdump_supported_nic(struct ifnet *ifp)
+{
+
+	return (ifp->if_ndumpfuncs !=3D NULL);
+}
+
+/*-
+ * Sysctls specific code.
+ */
+
+/*
+ * Sysctl handler converting a string sysctl to/from an ifaddr name.
+ *
+ * Parameters:
+ *	SYSCTL_HANDLER_ARGS
+ *	 - arg1 is a pointer to the if structure name
+ *	 - arg2 is unused
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS)
+{
+	char buf[IFNAMSIZ];
+	struct ifnet *ifp;
+	int error, found;
+
+	strlcpy(buf, arg1, sizeof(buf));
+	error =3D sysctl_handle_string(oidp, buf, sizeof(buf), req);
+	if (error !=3D 0 || req->newptr =3D=3D NULL)
+		return (error);
+	found =3D 0;
+	IFNET_RLOCK_NOSLEEP();
+	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+		if (!strncmp(ifp->if_xname, buf, strlen(ifp->if_xname)) =
&&
+		    netdump_supported_nic(ifp)) {
+			found =3D 1;
+			break;
+		}
+	}
+	IFNET_RUNLOCK_NOSLEEP();
+	if (found =3D=3D 0)
+		return (EINVAL);
+	strlcpy(arg1, buf, strlen(buf) + 1);
+	return (error);
+}
+
+/*
+ * Sysctl handler converting a string sysctl to/from an in_addr.
+ *
+ * Parameters:
+ *	SYSCTL_HANDLER_ARGS
+ *	 - arg1 is a pointer to the struct in_addr holding the IP
+ *	 - arg2 is unused
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS)
+{
+	struct in_addr addr;
+	char buf[INET_ADDRSTRLEN];
+	int error;
+
+	inet_ntoa_r(*(struct in_addr *)arg1, buf);
+	error =3D sysctl_handle_string(oidp, buf, sizeof(buf), req);
+	if (error =3D=3D 0) {
+		if (!inet_aton(buf, &addr))
+			error =3D EINVAL;
+		else
+			*(struct in_addr *)arg1 =3D addr;
+	}
+	return (error);
+}
+
+SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, =
"netdump");
+SYSCTL_PROC(_net_dump, OID_AUTO, server, CTLTYPE_STRING | CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_server, 0, sysctl_handle_inaddr, "A", "dump =
server");
+SYSCTL_PROC(_net_dump, OID_AUTO, client, CTLTYPE_STRING |CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_client, 0, sysctl_handle_inaddr, "A", "dump =
client");
+SYSCTL_PROC(_net_dump, OID_AUTO, gateway, CTLTYPE_STRING | CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_gw, 0, sysctl_handle_inaddr, "A",
+    "dump default gateway");
+SYSCTL_PROC(_net_dump, OID_AUTO, nic, CTLTYPE_STRING | CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_ifp_str, 0, sysctl_handle_ifxname, "A",
+    "dumping interface name");
+SYSCTL_INT(_net_dump, OID_AUTO, polls, CTLTYPE_INT | CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_polls, 0, "times to poll NIC per retry");
+SYSCTL_INT(_net_dump, OID_AUTO, retries, CTLTYPE_INT | CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_retries, 0, "times to retransmit lost =
packets");
+SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT | CTLFLAG_RW |
+    CTLFLAG_MPSAFE, &nd_enable, 0, "enable network dump");
+
+TUNABLE_STR("net.dump.server", nd_server_tun, sizeof(nd_server_tun));
+TUNABLE_STR("net.dump.client", nd_client_tun, sizeof(nd_client_tun));
+TUNABLE_STR("net.dump.gateway", nd_gw_tun, sizeof(nd_gw_tun));
+TUNABLE_STR("net.dump.nic", nd_nic_tun, sizeof(nd_nic_tun));
+TUNABLE_INT("net.dump.enable", &nd_enable);
+
+/*-
+ * Network specific primitives.
+ * Following down the code they are divided ordered as:
+ * - Output primitives
+ * - Input primitives
+ * - Polling primitives
+ */
+
+/*
+ * Netdump wraps external mbufs around address ranges.  unlike most =
sane
+ * counterparts, netdump uses a stop-and-wait approach to flow control =
and
+ * retransmission, so the ack obviates the need for mbuf reference
+ * counting.  We still need to tell other mbuf handlers not to do =
anything
+ * special with our mbufs, so specify this nop handler.
+ *
+ * Parameters:
+ *	ptr	 data to free (ignored)
+ *	opt_args callback pointer (ignored)
+ *
+ * Returns:
+ *	void
+ */
+static int
+netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr __unused, void =
*opt_args __unused)
+{
+	return EXT_FREE_OK;
+}
+
+/*
+ * Handles creation of the ethernet header, then places outgoing =
packets into
+ * the tx buffer for the NIC
+ *
+ * Parameters:
+ *	m	The mbuf containing the packet to be sent (will be freed =
by
+ *		this function or the NIC driver)
+ *	ifp	The interface to send on
+ *	dst	The destination ethernet address (source address will be =
looked
+ *		up using ifp)
+ *	etype	The ETHERTYPE_* value for the protocol that is being =
sent
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct =
ether_addr dst,
+    u_short etype)
+{
+	struct ether_header *eh;
+
+	/* Fill in the ethernet header. */
+	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
+	if (m =3D=3D NULL) {
+		printf("netdump_ether_output: Out of mbufs\n");
+		return (ENOBUFS);
+	}
+	eh =3D mtod(m, struct ether_header *);
+	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+	memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN);
+	eh->ether_type =3D htons(etype);
+
+	if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) !=3D IFF_UP) ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) !=3D IFF_DRV_RUNNING) =
{
+		if_printf(ifp, "netdump_ether_output: Interface isn't =
up\n");
+		m_freem(m);
+		return (ENETDOWN);
+	}
+	return ((ifp->if_transmit)(ifp, m));
+}
+
+/*
+ * Unreliable transmission of an mbuf chain to the netdump server
+ * Note: can't handle fragmentation; fails if the packet is larger than
+ *	 nd_ifp->if_mtu after adding the UDP/IP headers
+ *
+ * Parameters:
+ *	m	mbuf chain
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+netdump_udp_output(struct mbuf *m)
+{
+	struct udpiphdr *ui;
+	struct ip *ip;
+
+	MPASS(nd_ifp !=3D NULL);
+
+	M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
+	if (m =3D=3D NULL) {
+		printf("netdump_udp_output: Out of mbufs\n");
+		return (ENOBUFS);
+	}
+	ui =3D mtod(m, struct udpiphdr *);
+	bzero(ui->ui_x1, sizeof(ui->ui_x1));
+	ui->ui_pr =3D IPPROTO_UDP;
+	ui->ui_len =3D htons(m->m_pkthdr.len - sizeof(struct ip));
+	ui->ui_ulen =3D ui->ui_len;
+	ui->ui_src =3D nd_client;
+	ui->ui_dst =3D nd_server;
+	/* Use this src port so that the server can connect() the socket =
*/
+	ui->ui_sport =3D htons(NETDUMP_ACKPORT);
+	ui->ui_dport =3D htons(nd_server_port);
+	ui->ui_sum =3D 0;
+	if ((ui->ui_sum =3D in_cksum(m, m->m_pkthdr.len)) =3D=3D 0)
+		ui->ui_sum =3D 0xffff;
+
+	ip =3D mtod(m, struct ip *);
+	ip->ip_v =3D IPVERSION;
+	ip->ip_hl =3D sizeof(struct ip) >> 2;
+	ip->ip_tos =3D 0;
+	ip->ip_len =3D htons(m->m_pkthdr.len);
+	ip->ip_id =3D 0;
+	ip->ip_off =3D htons(IP_DF);
+	ip->ip_ttl =3D 32;
+	ip->ip_sum =3D 0;
+	ip->ip_sum =3D in_cksum(m, sizeof(struct ip));
+
+	if (m->m_pkthdr.len > nd_ifp->if_mtu) {
+		printf("netdump_udp_output: Packet is too big: %u > MTU =
%lu\n",
+		       m->m_pkthdr.len, (unsigned long)nd_ifp->if_mtu);
+		m_freem(m);
+		return (ENOBUFS);
+	}
+	return (netdump_ether_output(m, nd_ifp, nd_gw_mac, =
ETHERTYPE_IP));
+}
+
+/*
+ * Builds and sends a single ARP request to locate the server
+ *
+ * Parameters:
+ *	void
+ *
+ * Return value:
+ *	0 on success
+ *	errno on error
+ */
+static int
+netdump_send_arp()
+{
+	struct ether_addr bcast;
+	struct mbuf *m;
+	struct arphdr *ah;
+	int pktlen;
+
+	MPASS(nd_ifp !=3D NULL);
+
+	/* Fill-up a broadcast address. */
+	memset(&bcast, 0xFF, ETHER_ADDR_LEN);
+	MGETHDR(m, M_NOWAIT, MT_DATA);
+	if (m =3D=3D NULL) {
+		printf("netdump_send_arp: Out of mbufs");
+		return (ENOBUFS);
+	}
+	pktlen =3D arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
+	m->m_len =3D pktlen;
+	m->m_pkthdr.len =3D pktlen;
+	MH_ALIGN(m, pktlen);
+	ah =3D mtod(m, struct arphdr *);
+	ah->ar_hrd =3D htons(ARPHRD_ETHER);
+	ah->ar_pro =3D htons(ETHERTYPE_IP);
+	ah->ar_hln =3D ETHER_ADDR_LEN;
+	ah->ar_pln =3D sizeof(struct in_addr);
+	ah->ar_op =3D htons(ARPOP_REQUEST);
+	memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN);
+	memcpy(ar_spa(ah), &nd_client.s_addr, sizeof(nd_client.s_addr));
+	bzero(ar_tha(ah), ETHER_ADDR_LEN);
+	memcpy(ar_tpa(ah), &nd_gw.s_addr, sizeof(nd_gw.s_addr));
+
+	return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP));
+}
+
+/*
+ * Sends ARP requests to locate the server and waits for a response
+ *
+ * Parameters:
+ *	void
+ *
+ * Return value:
+ *	0 on success
+ *	errno on error
+ */
+static int
+netdump_arp_server()
+{
+	int err, polls, retries;
+
+	for (retries =3D 0; retries < nd_retries && have_server_mac =3D=3D=
 0;
+	    retries++) {
+		err =3D netdump_send_arp();
+		if (err !=3D 0)
+			return (err);
+		for (polls =3D 0; polls < nd_polls && have_server_mac =3D=3D=
 0;
+		    polls++) {
+			netdump_network_poll();
+			DELAY(500);
+		}
+		if (have_server_mac =3D=3D 0)
+			printf("(ARP retry)");
+	}
+	if (have_server_mac !=3D 0)
+		return (0);
+
+	printf("\nARP timed out.\n");
+	return (ETIMEDOUT);
+}
+
+/*
+ * Construct and reliably send a netdump packet.  May fail from a =
resource
+ * shortage or extreme number of unacknowledged retransmissions.  Wait =
for
+ * an acknowledgement before returning.  Splits packets into chunks =
small
+ * enough to be sent without fragmentation (looks up the interface MTU)
+ *
+ * Parameters:
+ *	type	netdump packet type (HERALD, FINISHED, or VMCORE)
+ *	offset	vmcore data offset (bytes)
+ *	data	vmcore data
+ *	datalen	vmcore data size (bytes)
+ *
+ * Returns:
+ *	int see errno.h, 0 for success
+ */
+static int
+netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t =
datalen)
+{
+	uint64_t want_acks;
+	struct netdump_msg_hdr *nd_msg_hdr;
+	struct mbuf *m, *m2;
+	uint32_t i, pktlen, sent_so_far;
+	int retries, polls, error;
+
+	want_acks =3D 0;
+	rcvd_acks =3D 0;
+	retries =3D 0;
+
+	MPASS(nd_ifp !=3D NULL);
+
+retransmit:
+
+	/* Chunks can be too big to fit in packets. */
+	for (i =3D sent_so_far =3D 0; sent_so_far <
+	    datalen || (i =3D=3D 0 && datalen =3D=3D 0); i++) {
+		pktlen =3D datalen - sent_so_far;
+
+		/* First bound: the packet structure. */
+		pktlen =3D min(pktlen, NETDUMP_DATASIZE);
+
+		/* Second bound: the interface MTU (assume no IP =
options). */
+		pktlen =3D min(pktlen, nd_ifp->if_mtu - sizeof(struct =
udpiphdr) -
+		    sizeof(struct netdump_msg_hdr));
+
+		/*
+		 * Check if it is retransmitting and this has been ACKed
+		 * already.
+		 */
+		if ((rcvd_acks & (1 << i)) !=3D 0) {
+			sent_so_far +=3D pktlen;
+			continue;
+		}
+
+		/*
+		 * Get and fill a header mbuf, then chain data as an =
extended
+		 * mbuf.
+		 */
+		MGETHDR(m, M_NOWAIT, MT_DATA);
+		if (m =3D=3D NULL) {
+			printf("netdump_send: Out of mbufs!\n");
+			return (ENOBUFS);
+		}
+		m->m_len =3D sizeof(struct netdump_msg_hdr);
+		m->m_pkthdr.len =3D sizeof(struct netdump_msg_hdr);
+		MH_ALIGN(m, sizeof(struct netdump_msg_hdr));
+		nd_msg_hdr =3D mtod(m, struct netdump_msg_hdr *);
+		nd_msg_hdr->mh_seqno =3D htonl(nd_seqno+i);
+		nd_msg_hdr->mh_type =3D htonl(type);
+		nd_msg_hdr->mh_offset =3D htobe64(offset + sent_so_far);
+		nd_msg_hdr->mh_len =3D htonl(pktlen);
+		nd_msg_hdr->mh__pad =3D 0;
+
+		if (pktlen !=3D 0) {
+			m2 =3D m_get(M_NOWAIT, MT_DATA);
+			if (m2 =3D=3D NULL) {
+				m_freem(m);
+				printf("netdump_send: Out of mbufs!\n");
+				return (ENOBUFS);
+			}
+			MEXTADD(m2, data+sent_so_far, pktlen, =
netdump_mbuf_nop,
+			    NULL, NULL, M_RDONLY, EXT_MOD_TYPE);
+			m2->m_len =3D pktlen;
+			m->m_next =3D m2;
+			m->m_pkthdr.len +=3D m2->m_len;
+		}
+		error =3D netdump_udp_output(m);
+		if (error !=3D 0)
+			return (error);
+
+		/* Note that we're waiting for this packet in the =
bitfield. */
+		want_acks |=3D 1 << i;
+		sent_so_far +=3D pktlen;
+	}
+	if (i >=3D sizeof(want_acks) * 8)
+		printf("Warning: Sent more than %zd packets (%d). "
+		    "Acknowledgements will fail unless the size of "
+		    "rcvd_acks/want_acks is increased.\n",
+		    sizeof(want_acks) * 8, i);
+
+	/*
+	 * Wait for acks.  A *real* window would speed things up =
considerably.
+	 */
+	polls =3D 0;
+	while (rcvd_acks !=3D want_acks) {	=09
+		if (polls++ > nd_polls) {
+			if (retries++ > nd_retries)
+				return (ETIMEDOUT);
+			printf(". ");
+			goto retransmit;
+		}
+		netdump_network_poll();
+		DELAY(500);
+	}
+	nd_seqno +=3D i;
+	return (0);
+}
+
+/*
+ * Handler for IP packets: checks their sanity and then processes any =
netdump
+ * ACK packets it finds.
+ *
+ * It needs to replicate partially the behaviour of ip_input() and
+ * udp_input().
+ *
+ * Parameters:
+ *	mb	a pointer to an mbuf * containing the packet received
+ *		Updates *mb if m_pullup et al change the pointer
+ *		Assumes the calling function will take care of freeing =
the mbuf
+ *
+ * Return value:
+ *	void
+ */
+static void
+nd_handle_ip(struct mbuf **mb)
+{
+	struct ip *ip;
+	struct udpiphdr *udp;
+	struct netdump_ack *nd_ack;
+	struct mbuf *m;
+	int rcv_ackno;
+	unsigned short hlen;
+
+	/* IP processing. */
+	m =3D *mb;
+	if (m->m_pkthdr.len < sizeof(struct ip)) {
+		NETDDEBUG("nd_handle_ip: dropping packet too small for =
IP "
+		    "header\n");
+		return;
+	}
+	if (m->m_len < sizeof(struct ip)) {
+		m =3D m_pullup(m, sizeof(struct ip));
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			return;
+		}
+	}
+	ip =3D mtod(m, struct ip *);
+
+	/* IP version. */
+	if (ip->ip_v !=3D IPVERSION) {
+		NETDDEBUG("nd_handle_ip: Bad IP version %d\n", =
ip->ip_v);
+		return;
+	}
+
+	/* Header length. */
+	hlen =3D ip->ip_hl << 2;
+	if (hlen < sizeof(struct ip)) {
+		NETDDEBUG("nd_handle_ip: Bad IP header length (%hu)\n", =
hlen);
+		return;
+	}
+	if (hlen > m->m_len) {
+		m =3D m_pullup(m, hlen);
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			return;
+		}
+		ip =3D mtod(m, struct ip *);
+	}
+
+#ifdef INVARIANTS
+	if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D =
IN_LOOPBACKNET ||
+	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D =
IN_LOOPBACKNET) &&
+	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) =3D=3D 0) {
+		NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n");
+		return;
+	}
+#endif
+
+	/* Checksum. */
+	if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) !=3D 0) {
+		if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) =3D=3D 0) {
+			NETDDEBUG("nd_handle_ip: Bad IP checksum\n");
+			return;
+		}
+	} else
+		NETDDEBUG("nd_handle_ip: HW didn't check IP cksum\n");
+
+	/* Convert fields to host byte order. */
+	ip->ip_len =3D ntohs(ip->ip_len);
+	if (ip->ip_len < hlen) {
+	NETDDEBUG("nd_handle_ip: IP packet smaller (%hu) than header =
(%hu)\n",
+		    ip->ip_len, hlen);
+		return;
+	}
+	ip->ip_off =3D ntohs(ip->ip_off);
+
+	if (m->m_pkthdr.len < ip->ip_len) {
+NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) than ethernet packet =
(%hu)\n",
+		    ip->ip_len, m->m_pkthdr.len);
+		return;
+	}
+	if (m->m_pkthdr.len > ip->ip_len) {
+
+		/* Truncate the packet to the IP length. */
+		if (m->m_len =3D=3D m->m_pkthdr.len) {
+			m->m_len =3D ip->ip_len;
+			m->m_pkthdr.len =3D ip->ip_len;
+		} else
+			m_adj(m, ip->ip_len - m->m_pkthdr.len);
+	}
+
+	/* Ignore packets with IP options. */
+	if (hlen > sizeof(struct ip)) {
+		NETDDEBUG("nd_handle_ip: Drop packet with IP =
options\n");
+		return;
+	}
+
+	/* Check that the source is the server's IP. */
+	if (ip->ip_src.s_addr !=3D nd_server.s_addr) {
+		NETDDEBUG("nd_handle_ip: Drop packet not from =
server\n");
+		return;
+	}
+
+	/* Check if the destination IP is ours. */
+	if (ip->ip_dst.s_addr !=3D nd_client.s_addr) {
+		NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n");
+		return;
+	}
+
+	if (ip->ip_p !=3D IPPROTO_UDP) {
+		NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n");
+		return;
+	}
+
+	/* Do not deal with fragments. */
+	if ((ip->ip_off & (IP_MF | IP_OFFMASK)) !=3D 0) {
+		NETDDEBUG("nd_handle_ip: Drop fragmented packet\n");
+		return;
+	}
+
+	/* UDP custom is to have packet length not include IP header. */
+	ip->ip_len -=3D hlen;
+
+	/* UDP processing. */
+
+	/* Get IP and UDP headers together, along with the netdump =
packet. */
+	if (m->m_pkthdr.len <
+	    sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
+		NETDDEBUG("nd_handle_ip: Ignoring small packet\n");
+		return;
+	}
+	if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct =
netdump_ack)) {
+		m =3D m_pullup(m, sizeof(struct udpiphdr) +
+		    sizeof(struct netdump_ack));
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			return;
+		}
+	}
+	udp =3D mtod(m, struct udpiphdr *);
+
+	if (ntohs(udp->ui_u.uh_dport) !=3D NETDUMP_ACKPORT) {
+		NETDDEBUG("not on the netdump port.\n");
+		return;
+	}
+
+	/* Netdump processing. */
+
+	/*
+	 * Packet is meant for us.  Extract the ack sequence number and =
the
+	 * port number if necessary.
+	 */
+	nd_ack =3D (struct netdump_ack *)(mtod(m, caddr_t) +
+	    sizeof(struct udpiphdr));
+	rcv_ackno =3D ntohl(nd_ack->na_seqno);
+	if (nd_server_port =3D=3D NETDUMP_PORT)
+	    nd_server_port =3D ntohs(udp->ui_u.uh_sport);
+	if (rcv_ackno >=3D nd_seqno + 64)
+		printf("nd_handle_ip: ACK %d too far in future!\n", =
rcv_ackno);
+	else if (rcv_ackno >=3D nd_seqno) {
+
+		/* We're interested in this ack. Record it. */
+		rcvd_acks |=3D 1 << (rcv_ackno-nd_seqno);
+	}
+}
+
+/*
+ * Handler for ARP packets: checks their sanity and then
+ * 1. If the ARP is a request for our IP, respond with our MAC address
+ * 2. If the ARP is a response from our server, record its MAC address
+ *
+ * It needs to replicate partially the behaviour of arpintr() and
+ * in_arpinput().
+ *
+ * Parameters:
+ *	mb	a pointer to an mbuf * containing the packet received
+ *		Updates *mb if m_pullup et al change the pointer
+ *		Assumes the calling function will take care of freeing =
the mbuf
+ *
+ * Return value:
+ *	void
+ */
+static void
+nd_handle_arp(struct mbuf **mb)
+{
+	char buf[INET_ADDRSTRLEN];
+	struct in_addr isaddr, itaddr, myaddr;
+	struct ether_addr dst;
+	struct mbuf *m;
+	struct arphdr *ah;
+	struct ifnet *ifp;
+	uint8_t *enaddr;
+	int req_len, op;
+
+	m =3D *mb;
+	ifp =3D m->m_pkthdr.rcvif;
+	if (m->m_len < sizeof(struct arphdr)) {
+		m =3D m_pullup(m, sizeof(struct arphdr));
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+		NETDDEBUG("nd_handle_arp: runt packet: m_pullup =
failed\n");
+			return;
+		}
+	}
+	ah =3D mtod(m, struct arphdr *);
+
+	if (ntohs(ah->ar_hrd) !=3D ARPHRD_ETHER) {
+		NETDDEBUG("nd_handle_arp: unknown hardware address =
0x%2D)\n",
+		    (unsigned char *)&ah->ar_hrd, "");
+		return;
+	}
+	if (ntohs(ah->ar_pro) !=3D ETHERTYPE_IP) {
+		NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol =
%d\n",
+		    ntohs(ah->ar_pro));
+		return;
+	}
+	req_len =3D arphdr_len2(ifp->if_addrlen, sizeof(struct =
in_addr));
+	if (m->m_len < req_len) {
+		m =3D m_pullup(m, req_len);
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+		NETDDEBUG("nd_handle_arp: runt packet: m_pullup =
failed\n");
+			return;
+		}
+	}
+	ah =3D mtod(m, struct arphdr *);
+
+	op =3D ntohs(ah->ar_op);
+	memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
+	memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
+	enaddr =3D (uint8_t *)IF_LLADDR(ifp);
+	myaddr =3D nd_client;
+
+	if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) {
+		NETDDEBUG("nd_handle_arp: ignoring ARP from myself\n");
+		return;
+	}
+
+	if (isaddr.s_addr =3D=3D nd_client.s_addr) {
+		printf("nd_handle_arp: %*D is using my IP address =
%s!\n",
+		    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+		    inet_ntoa(isaddr));
+		return;
+	}
+
+	if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) {
+	NETDDEBUG("nd_handle_arp: ignoring ARP from broadcast =
address\n");
+		return;
+	}
+
+	if (op =3D=3D ARPOP_REPLY) {
+		if (isaddr.s_addr !=3D nd_gw.s_addr) {
+			inet_ntoa_r(isaddr, buf);
+NETDDEBUG("nd_handle_arp: ignoring ARP reply from %s (not netdump =
server)\n",
+			    buf);
+			return;
+		}
+		memcpy(nd_gw_mac.octet, ar_sha(ah),
+		    min(ah->ar_hln, ETHER_ADDR_LEN));
+		have_server_mac =3D 1;
+		NETDDEBUG("\nnd_handle_arp: Got server MAC address =
%6D\n",
+		    nd_gw_mac.octet, ":");
+		return;
+	}
+
+	if (op !=3D ARPOP_REQUEST) {
+		NETDDEBUG("nd_handle_arp: Ignoring ARP =
non-request/reply\n");
+		return;
+	}
+
+	if (itaddr.s_addr !=3D nd_client.s_addr) {
+		NETDDEBUG("nd_handle_arp: ignoring ARP not to our =
IP\n");
+		return;
+	}
+
+	memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
+	memcpy(ar_sha(ah), enaddr, ah->ar_hln);
+	memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln);
+	memcpy(ar_spa(ah), &itaddr, ah->ar_pln);
+	ah->ar_op =3D htons(ARPOP_REPLY);
+	ah->ar_pro =3D htons(ETHERTYPE_IP);
+	m->m_flags &=3D ~(M_BCAST|M_MCAST);
+	m->m_len =3D sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
+	m->m_pkthdr.len =3D m->m_len;
+
+	memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
+	netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
+	*mb =3D NULL;
+}
+
+/*
+ * Handler for incoming packets directly from the network adapter
+ * Identifies the packet type (IP or ARP) and passes it along to one of =
the
+ * helper functions nd_handle_ip or nd_handle_arp.
+ *
+ * It needs to replicate partially the behaviour of ether_input() and
+ * ether_demux().
+ *
+ * Parameters:
+ *	ifp	the interface the packet came from (should be nd_ifp)
+ *	m	an mbuf containing the packet received
+ *
+ * Return value:
+ *	void
+ */
+static void
+netdump_pkt_in(struct ifnet *ifp, struct mbuf *m)
+{
+	struct ether_header *eh;
+	u_short etype;
+
+	/* Ethernet processing. */
+	if ((m->m_flags & M_PKTHDR) =3D=3D 0) {
+		NETDDEBUG_IF(ifp,
+		    "netdump_pkt_in: Discard frame without packet =
header\n");
+		goto done;
+	}
+	if (m->m_len < ETHER_HDR_LEN) {
+		NETDDEBUG_IF(ifp,
+"netdump_pkt_in: Discard frame without leading eth header (len %u =
pktlen %u)\n",
+		    m->m_len, m->m_pkthdr.len);
+		goto done;
+	}
+	if ((m->m_flags & M_HASFCS) !=3D 0) {
+		m_adj(m, -ETHER_CRC_LEN);
+		m->m_flags &=3D ~M_HASFCS;
+	}
+	eh =3D mtod(m, struct ether_header *);
+	m->m_pkthdr.PH_loc.ptr =3D eh;
+	etype =3D ntohs(eh->ether_type);
+	if ((m->m_flags & M_VLANTAG) !=3D 0 || etype =3D=3D =
ETHERTYPE_VLAN) {
+		NETDDEBUG_IF(ifp, "netdump_pkt_in: Ignoring vlan =
packets\n");
+		goto done;
+	}
+
+	/* XXX: Probably must also check if we're the recipient MAC =
address. */
+
+	/* Done ethernet processing. Strip off the ethernet header. */
+	m_adj(m, ETHER_HDR_LEN);
+	switch (etype) {
+		case ETHERTYPE_ARP:
+			nd_handle_arp(&m);
+			break;
+		case ETHERTYPE_IP:
+			nd_handle_ip(&m);
+			break;
+		default:
+			NETDDEBUG_IF(ifp,
+			    "netdump_pkt_in: Dropping unknown ethertype =
%hu\n",
+			    etype);
+			break;
+	}
+done:
+	if (m !=3D NULL)
+		m_freem(m);
+}
+
+/*
+ * After trapping, instead of assuming that most of the network stack =
is sane
+ * just poll the driver directly for packets.
+ *
+ * Parameters:
+ *	void
+ *
+ * Returns:
+ *	void
+ */
+static void
+netdump_network_poll()
+{
+
+	MPASS(nd_ifp !=3D NULL);
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+	if (panicstr !=3D NULL)
+		nd_ifp->if_ndumpfuncs->ne_poll_unlocked(nd_ifp,
+		    POLL_AND_CHECK_STATUS, 1000);
+	else
+#endif
+		nd_ifp->if_ndumpfuncs->ne_poll_locked(nd_ifp,
+		    POLL_AND_CHECK_STATUS, 1000);
+}
+
+/*-
+ * Dumping specific primitives.
+ */
+
+/*
+ * Callback from dumpsys() to dump a chunk of memory.
+ * Copies it out to our static buffer then sends it across the network.
+ * Detects the initial KDH and makes sure it is given a special packet =
type.
+ *
+ * Parameters:
+ *	priv	 Unused. Optional private pointer.
+ *	virtual  Virtual address (where to read the data from)
+ *	physical Unused. Physical memory address.
+ *	offset	 Offset from start of core file
+ *	length	 Data length
+ *
+ * Return value:
+ *	0 on success
+ *	errno on error
+ */
+static int
+netdump_dumper(void *priv __unused, void *virtual,
+    vm_offset_t physical __unused, off_t offset, size_t length)
+{
+	int err, msgtype;
+
+	NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n",
+	    virtual, (uintmax_t)offset, length);
+
+	if (length > sizeof(buf))
+		return (ENOSPC);
+
+	/*
+	 * The first write (at offset 0) is the kernel dump header.  =
Flag it
+	 * for the server to treat specially.
+	 * XXX: This doesn't strip out the footer KDH, although it
+	 * should not hurt anything.
+	 */
+	msgtype =3D NETDUMP_VMCORE;
+	if (offset =3D=3D 0 && length > 0)
+		msgtype =3D NETDUMP_KDH;
+	else if (offset > 0)
+		offset -=3D sizeof(struct kerneldumpheader);
+	memcpy(buf, virtual, length);
+	err =3D netdump_send(msgtype, offset, buf, length);
+	if (err !=3D 0) {
+		dump_failed =3D 1;
+		return (err);
+	}
+	return (0);
+}
+
+/*
+ * Dumper routine, specular to dumpsys().
+ *
+ * Parameters:
+ *	void
+ *
+ * Returns:
+ *	int see errno.h, 0 for success
+ */
+int
+netdumpsys()
+{
+	struct dumperinfo dumper;
+	void (*old_if_input)(struct ifnet *, struct mbuf *);
+	int error, found, must_lock, nd_gw_unset;
+
+	old_if_input =3D NULL;
+	error =3D 0;
+	found =3D 0;
+	nd_gw_unset =3D 0;
+	must_lock =3D 1;
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+	if (panicstr !=3D NULL)
+		must_lock =3D 0;
+#endif
+
+	/* Check if the dumping is allowed to continue. */
+	if (nd_enable =3D=3D 0)
+		return (EINVAL);
+
+	/* Lookup the right if device to be used in the dump. */
+	if (must_lock !=3D 0)
+		IFNET_RLOCK_NOSLEEP();
+	TAILQ_FOREACH(nd_ifp, &V_ifnet, if_link) {
+		if (!strncmp(nd_ifp->if_xname, nd_ifp_str,
+		    strlen(nd_ifp->if_xname)) &&
+		    netdump_supported_nic(nd_ifp)) {
+			found =3D 1;
+			break;
+		}
+	}
+	if (must_lock !=3D 0)
+		IFNET_RUNLOCK_NOSLEEP();
+	if (found =3D=3D 0) {
+		printf("netdumpsys: Can't netdump: no valid NIC =
given\n");
+		return (EINVAL);
+	}
+
+	MPASS(nd_ifp !=3D NULL);
+
+	if (nd_server.s_addr =3D=3D INADDR_ANY) {
+		printf("netdumpsys: Can't netdump; no server IP =
given\n");
+		return (EINVAL);
+	}
+	if (nd_client.s_addr =3D=3D INADDR_ANY) {
+		printf("netdumpsys: Can't netdump; no client IP =
given\n");
+		return (EINVAL);
+	}
+
+	/*
+	 * nd_server_port could have switched after the first ack the
+	 * first time it gets called.  Adjust it accordingly.
+	 */
+	nd_server_port =3D NETDUMP_PORT;
+	if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock =
!=3D 0)
+		nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp);
+
+	/* Make the card use *our* receive callback. */
+	old_if_input =3D nd_ifp->if_input;
+	nd_ifp->if_input =3D netdump_pkt_in;
+
+	if (nd_gw.s_addr =3D=3D INADDR_ANY) {
+		nd_gw.s_addr =3D nd_server.s_addr;
+		nd_gw_unset =3D 1;
+	}
+	printf("\n-----------------------------------\n");
+	printf("netdump in progress. searching for server.. ");
+	if (netdump_arp_server()) {
+		printf("Failed to locate server MAC address\n");
+		error =3D EINVAL;
+		goto trig_abort;
+	}
+	if (netdump_send(NETDUMP_HERALD, 0, NULL, 0) !=3D 0) {
+		printf("Failed to contact netdump server\n");
+		error =3D EINVAL;
+		goto trig_abort;
+	}
+	printf("dumping to %s (%6D)\n", inet_ntoa(nd_server), =
nd_gw_mac.octet,
+	    ":");
+	printf("-----------------------------------\n");
+
+	/* Call the dumping routine. */
+	dumper.dumper =3D netdump_dumper;
+	dumper.priv =3D NULL;
+	dumper.blocksize =3D NETDUMP_DATASIZE;
+	dumper.mediasize =3D 0;
+	dumper.mediaoffset =3D 0;
+	dumpsys(&dumper);
+	if (dump_failed !=3D 0) {
+		printf("Failed to dump the actual raw datas\n");
+		error =3D EINVAL;
+		goto trig_abort;
+	}
+	if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) !=3D 0) {
+		printf("Failed to close the transaction\n");
+		error =3D EINVAL;
+		goto trig_abort;
+	}
+	printf("\nnetdump finished.\n");
+trig_abort:
+	if (nd_gw_unset !=3D 0)
+		nd_gw.s_addr =3D INADDR_ANY;
+	if (old_if_input)
+		nd_ifp->if_input =3D old_if_input;
+	if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock =
!=3D 0)
+		nd_ifp->if_ndumpfuncs->ne_enable_intr(nd_ifp);
+	return (error);
+}
+
+/*-
+ * KLD specific code.
+ */
+
+/*
+ * Called upon system init. Initializes the sysctl variables to sane =
defaults
+ * (locates the first available NIC and uses the first IPv4 IP on that =
card as
+ * the client IP).  Leaves the server IP unconfigured.
+ *
+ * Parameters:
+ *	void *, unused
+ *
+ * Returns:
+ *	void
+ */
+static void
+netdump_config_defaults(void *dummy __unused)
+{
+	struct ifnet *ifp;
+	int found;
+
+	nd_ifp =3D NULL;
+	nd_server.s_addr =3D INADDR_ANY;
+	nd_client.s_addr =3D INADDR_ANY;
+	nd_gw.s_addr =3D INADDR_ANY;
+
+	if (nd_server_tun[0] !=3D '\0')
+		inet_aton(nd_server_tun, &nd_server);
+	if (nd_client_tun[0] !=3D '\0')
+		inet_aton(nd_client_tun, &nd_client);
+	if (nd_gw_tun[0] !=3D '\0')
+		inet_aton(nd_gw_tun, &nd_gw);
+	if (nd_nic_tun[0] !=3D '\0') {
+		found =3D 0;
+		IFNET_RLOCK_NOSLEEP();
+		TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+			if (!strncmp(ifp->if_xname, nd_nic_tun,
+			    strlen(ifp->if_xname))) {
+				found =3D 1;
+				break;
+			}
+		}
+		IFNET_RUNLOCK_NOSLEEP();
+		if (found !=3D 0 && netdump_supported_nic(ifp))
+			nd_ifp =3D ifp;
+	}
+}
+SYSINIT(netdump, SI_SUB_KLD, SI_ORDER_ANY, netdump_config_defaults, =
NULL);
diff --git a/sys/sparc64/sparc64/dump_machdep.c =
b/sys/sparc64/sparc64/dump_machdep.c
index d5409ac..5d0ad9e 100644
--- a/sys/sparc64/sparc64/dump_machdep.c
+++ b/sys/sparc64/sparc64/dump_machdep.c
@@ -159,17 +159,22 @@ dumpsys(struct dumperinfo *di)
 	    DEV_BSIZE);
 	size +=3D hdrsize;
=20
-	totsize =3D size + 2 * sizeof(kdh);
-	if (totsize > di->mediasize) {
-		printf("Insufficient space on device (need %ld, have =
%ld), "
-		    "refusing to dump.\n", (long)totsize,
-		    (long)di->mediasize);
-		error =3D ENOSPC;
-		goto fail;
-	}
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+		totsize =3D size + 2 * sizeof(kdh);
+		if (totsize > di->mediasize) {
+			printf("Insufficient space on device (need %ld, =
"
+			    "have %ld), refusing to dump.\n", =
(long)totsize,
+			    (long)di->mediasize);
+			error =3D ENOSPC;
+			goto fail;
+		}
=20
-	/* Determine dump offset on device. */
-	dumplo =3D di->mediaoffset + di->mediasize - totsize;
+		/* Determine dump offset on device. */
+		dumplo =3D di->mediaoffset + di->mediasize - totsize;
+	}
=20
 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_SPARC64_VERSION, =
size,
 	    di->blocksize);
diff --git a/sys/x86/x86/dump_machdep.c b/sys/x86/x86/dump_machdep.c
index 5c874f4..c75b1ff 100644
--- a/sys/x86/x86/dump_machdep.c
+++ b/sys/x86/x86/dump_machdep.c
@@ -311,13 +311,20 @@ dumpsys(struct dumperinfo *di)
 	dumpsize +=3D fileofs;
 	hdrgap =3D fileofs - DEV_ALIGN(hdrsz);
=20
-	/* Determine dump offset on device. */
-	if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * =
2) {
-		error =3D ENOSPC;
-		goto fail;
+	/* If the upper bound is 0, dumper likely will not use disks. */
+	if ((di->mediaoffset + di->mediasize) =3D=3D 0)
+		dumplo =3D 0;
+	else {
+
+		/* Determine dump offset on device. */
+		if (di->mediasize <
+		    SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+			error =3D ENOSPC;
+			goto fail;
+		}
+		dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
+		dumplo -=3D sizeof(kdh) * 2;
 	}
-	dumplo =3D di->mediaoffset + di->mediasize - dumpsize;
-	dumplo -=3D sizeof(kdh) * 2;
=20
 	mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_VERSION, =
dumpsize,
 	    di->blocksize);
diff --git a/usr.sbin/netdumpsrv/Makefile b/usr.sbin/netdumpsrv/Makefile
new file mode 100644
index 0000000..08ea0ae
--- /dev/null
+++ b/usr.sbin/netdumpsrv/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG=3D	netdumpsrv
+MAN=3D	netdumpsrv.8
+
+CFLAGS+=3D	-DNDEBUG -I${.CURDIR}
+
+WARNS?=3D	6
+
+DPADD=3D	${LIBUTIL}
+LDADD=3D	-lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/netdumpsrv/netdumpsrv.8 =
b/usr.sbin/netdumpsrv/netdumpsrv.8
new file mode 100644
index 0000000..767f087
--- /dev/null
+++ b/usr.sbin/netdumpsrv/netdumpsrv.8
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2011 Sandvine Incorporated. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above =
copyright
+.\"    notice, this list of conditions and the following disclaimer in =
the
+.\"    documentation and/or other materials provided with the =
distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' =
AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS =
INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
+.\" LIABILITY, OR TORT (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$
+.\"
+.Dd August 23, 2010
+.Dt NETDUMPSRV 8
+.Os
+.Sh NAME
+.Nm netdumpsrv
+.Nd receive kernel core dumps over the network
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar addr
+.Op Fl D
+.Op Fl d Ar dumpdir
+.Op Fl i Ar script
+.Sh DESCRIPTION
+The
+.Nm
+utility listens on a UDP socket for incoming connections
+from a
+.Fx
+kernel core dumping over the network.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Bind the daemon to the given address
+.Dq Pa addr .
+.It Fl D
+Run the utility in debugging mode.
+The daemon version is not entered while the output
+is printed entirely on the console.
+.It Fl d
+Save the core dumps to the specified
+.Dq Pa dumpdir
+directory.
+.It Fl i
+Execute the script
+.Dq Pa script
+after each dump received.
+The script accepts the following strings as parameters:
+its name,
+reasons for invokation,
+the client address,
+the client hostname,
+the info file name and the core dump file name.
+.El
+.Sh SEE ALSO
+.Xr dumpon 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 9.0 .
diff --git a/usr.sbin/netdumpsrv/netdumpsrv.c =
b/usr.sbin/netdumpsrv/netdumpsrv.c
new file mode 100644
index 0000000..af6ac6d
--- /dev/null
+++ b/usr.sbin/netdumpsrv/netdumpsrv.c
@@ -0,0 +1,911 @@
+/*-
+ * Copyright (c) 2005-2011 Sandvine Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in =
the
+ *    documentation and/or other materials provided with the =
distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' =
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS =
INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN =
ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY =
OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/kerneldump.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/netdump.h>
+
+#include <fcntl.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define	MAX_DUMPS	256	/* Dumps per IP before to be =
cleaned out. */
+#define	CLIENT_TIMEOUT	600	/* Clients timeout (secs). */
+#define	CLIENT_TPASS	10	/* Clients timeout pass (secs). =
*/
+
+#define	PFLAGS_ABIND	0x01
+#define	PFLAGS_DDIR	0x02
+#define	PFLAGS_DEBUG	0x04
+#define	PFLAGS_SCRIPT	0x08
+
+#define	LOGERR(m, ...)							=
\
+	(*phook)(LOG_ERR | LOG_DAEMON, (m), ## __VA_ARGS__)
+#define	LOGERR_PERROR(m)						=
\
+	(*phook)(LOG_ERR | LOG_DAEMON, "%s: %s\n", m, strerror(errno))
+#define	LOGINFO(m, ...)							=
\
+	(*phook)(LOG_INFO | LOG_DAEMON, (m), ## __VA_ARGS__)
+#define	LOGWARN(m, ...)							=
\
+	(*phook)(LOG_WARNING | LOG_DAEMON, (m), ## __VA_ARGS__)
+
+#define	client_ntoa(cl)							=
\
+	inet_ntoa((cl)->ip)
+#define	client_pinfo(cl, f, ...)					=
\
+	fprintf((cl)->infofile, (f), ## __VA_ARGS__)
+
+struct netdump_client {
+	char		infofilename[MAXPATHLEN];
+	char		corefilename[MAXPATHLEN];
+	char		hostname[NI_MAXHOST];
+	time_t		last_msg;
+	SLIST_ENTRY(netdump_client) iter;
+	struct in_addr	ip;
+	FILE		*infofile;
+	int		corefd;
+	int		sock;
+	unsigned short	printed_port_warning: 1;
+	unsigned short	any_data_rcvd: 1;
+};
+
+/* Clients list. */
+static SLIST_HEAD(, netdump_client) clients =3D =
SLIST_HEAD_INITIALIZER(clients);
+
+/* Program arguments handlers. */
+static uint32_t pflags;
+static char dumpdir[MAXPATHLEN];
+static char *handler_script;
+static struct in_addr bindip;
+
+/* Miscellaneous handlers. */
+static struct pidfh *pfh;
+static time_t now;
+static time_t last_timeout_check;
+static int do_shutdown;
+static int sock;
+
+/* Daemon print functions hook. */
+static void (*phook)(int, const char *, ...);
+
+static struct netdump_client	*alloc_client(struct sockaddr_in *sip);
+static void		 eventloop(void);
+static void		 exec_handler(struct netdump_client *client,
+			    const char *reason);
+static void		 free_client(struct netdump_client *client);
+static void		 handle_finish(struct netdump_client *client,
+			    struct netdump_msg *msg);
+static void		 handle_herald(struct sockaddr_in *from,
+			    struct netdump_client *client,
+			    struct netdump_msg *msg);
+static void		 handle_kdh(struct netdump_client *client,
+			    struct netdump_msg *msg);
+static void		 handle_packet(struct netdump_client *client,
+			    struct sockaddr_in *from, const char =
*fromstr,
+			    struct netdump_msg *msg);
+static void		 handle_timeout(struct netdump_client *client);
+static void		 handle_vmcore(struct netdump_client *client,
+			    struct netdump_msg *msg);
+static void		 phook_printf(int priority, const char *message, =
...);
+static int		 receive_message(int isock, struct sockaddr_in =
*from,
+			    char *fromstr, size_t fromstrlen,
+			    struct netdump_msg *msg);
+static void		 send_ack(struct netdump_client *client,
+			    struct netdump_msg *msg);
+static void		 signal_shutdown(int sig __unused);
+static void		 timeout_clients(void);
+static void		 usage(const char *cmd);
+
+static void
+usage(const char *cmd)
+{
+
+	fprintf(stderr, "Usage: %s [-a bind_addr] [-d dump_dir] [-i =
script]\n",
+	    cmd);
+}
+
+static void
+phook_printf(int priority, const char *message, ...)
+{
+	va_list ap;
+
+	va_start(ap, message);
+	if ((priority & LOG_INFO) !=3D 0) {
+		assert((priority & (LOG_WARNING | LOG_ERR)) =3D=3D 0);
+		vprintf(message, ap);
+	} else
+		vfprintf(stderr, message, ap);
+}
+
+static struct netdump_client *
+alloc_client(struct sockaddr_in *sip)
+{
+	struct sockaddr_in saddr;
+	struct netdump_client *client;
+	struct in_addr *ip;
+	char *firstdot;
+	int i, ecode, fd, bufsz;
+
+	assert(sip !=3D NULL);
+
+	client =3D calloc(1, sizeof(*client));
+	if (client =3D=3D NULL) {
+		LOGERR_PERROR("calloc()");
+		return (NULL);
+	}
+	ip =3D &sip->sin_addr;
+	bcopy(ip, &client->ip, sizeof(*ip));
+	client->corefd =3D -1;
+	client->sock =3D -1;
+	client->last_msg =3D now;
+
+	ecode =3D getnameinfo((struct sockaddr *)sip, sip->sin_len,
+	    client->hostname, sizeof(client->hostname), NULL, 0, =
NI_NAMEREQD);
+	if (ecode !=3D 0) {
+
+		/* Can't resolve, try with a numeric IP. */
+		ecode =3D getnameinfo((struct sockaddr *)sip, =
sip->sin_len,
+		    client->hostname, sizeof(client->hostname), NULL, 0, =
0);
+		if (ecode !=3D 0) {
+			LOGERR("getnameinfo(): %s\n", =
gai_strerror(ecode));
+			free(client);
+			return (NULL);
+		}
+	} else {
+
+		/* Strip off the domain name */
+		firstdot =3D strchr(client->hostname, '.');
+		if (firstdot)
+			*firstdot =3D '\0';
+	}
+
+	client->sock =3D socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (client->sock =3D=3D -1) {
+		LOGERR_PERROR("socket()");
+		free(client);
+		return (NULL);
+	}
+	if (fcntl(client->sock, F_SETFL, O_NONBLOCK) =3D=3D -1) {
+		LOGERR_PERROR("fcntl()");
+		close(client->sock);
+		free(client);
+		return (NULL);
+	}
+	bzero(&saddr, sizeof(saddr));
+	saddr.sin_len =3D sizeof(saddr);
+	saddr.sin_family =3D AF_INET;
+	saddr.sin_addr.s_addr =3D bindip.s_addr;
+	saddr.sin_port =3D htons(0);
+	if (bind(client->sock, (struct sockaddr *)&saddr, =
sizeof(saddr))) {
+		LOGERR_PERROR("bind()");
+		close(client->sock);
+		free(client);
+		return (NULL);
+	}
+	bzero(&saddr, sizeof(saddr));
+	saddr.sin_len =3D sizeof(saddr);
+	saddr.sin_family =3D AF_INET;
+	saddr.sin_addr.s_addr =3D ip->s_addr;
+	saddr.sin_port =3D htons(NETDUMP_ACKPORT);
+	if (connect(client->sock, (struct sockaddr *)&saddr, =
sizeof(saddr))) {
+		LOGERR_PERROR("connect()");
+		close(client->sock);
+		free(client);
+		return (NULL);
+	}
+
+	/* It should be enough to hold approximatively twize the chunk =
size. */
+	bufsz =3D 131072;
+	if (setsockopt(client->sock, SOL_SOCKET, SO_RCVBUF, &bufsz,
+	    sizeof(bufsz))) {
+		LOGERR_PERROR("setsockopt()");
+	LOGWARN("May drop packets from %s due to small receive =
buffer\n",
+		    client->hostname);
+	}
+
+	/* Try info.host.0 through info.host.255 in sequence. */
+	for (i =3D 0; i < MAX_DUMPS; i++) {
+		snprintf(client->infofilename, =
sizeof(client->infofilename),
+		    "%s/info.%s.%d", dumpdir, client->hostname, i);
+		snprintf(client->corefilename, =
sizeof(client->corefilename),
+		    "%s/vmcore.%s.%d", dumpdir, client->hostname, i);
+
+		/* Try the info file first. */
+		fd =3D open(client->infofilename, O_WRONLY | O_CREAT | =
O_EXCL,
+		    DEFFILEMODE);
+		if (fd =3D=3D -1) {
+			if (errno !=3D EEXIST)
+				LOGERR("open(\"%s\"): %s\n",
+				    client->infofilename, =
strerror(errno));
+			continue;
+		}
+		client->infofile =3D fdopen(fd, "w");
+		if (client->infofile =3D=3D NULL) {
+			LOGERR_PERROR("fdopen()");
+			close(fd);
+			unlink(client->infofilename);
+			continue;
+		}
+
+		/* Next make the core file. */
+		fd =3D open(client->corefilename, O_RDWR | O_CREAT | =
O_EXCL,
+		    DEFFILEMODE);
+		if (fd =3D=3D -1) {
+
+			/* Failed. Keep the numbers in sync. */
+			fclose(client->infofile);
+			unlink(client->infofilename);
+			client->infofile =3D NULL;
+			if (errno !=3D EEXIST)
+				LOGERR("open(\"%s\"): %s\n",
+				    client->corefilename, =
strerror(errno));
+			continue;
+		}
+		client->corefd =3D fd;
+		break;
+	}
+
+	if (client->infofile =3D=3D NULL || client->corefd =3D=3D -1) {
+		LOGERR("Can't create output files for new client %s =
[%s]\n",
+		    client->hostname, client_ntoa(client));
+		if (client->infofile)
+			fclose(client->infofile);
+		if (client->corefd !=3D -1)
+			close(client->corefd);
+		if (client->sock !=3D -1)
+			close(client->sock);
+		free(client);
+		return (NULL);
+	}
+	SLIST_INSERT_HEAD(&clients, client, iter);
+	return (client);
+}
+
+static void
+free_client(struct netdump_client *client)
+{
+
+	assert(client !=3D NULL);
+
+	/* Remove from the list.  Ignore errors from close() routines. =
*/
+	SLIST_REMOVE(&clients, client, netdump_client, iter);
+	fclose(client->infofile);
+	close(client->corefd);
+	close(client->sock);
+	free(client);
+}
+
+static void
+exec_handler(struct netdump_client *client, const char *reason)
+{
+	int pid;
+
+	assert(client !=3D NULL);
+
+	/* If no script is specified this is a no-op. */
+	if ((pflags & PFLAGS_SCRIPT) =3D=3D 0)
+		return;
+
+	pid =3D fork();
+
+	/*
+	 * The function is invoked in critical conditions, thus just =
exiting
+	 * without reporting errors is fine.
+	 */
+	if (pid =3D=3D -1) {
+		LOGERR_PERROR("fork()");
+		return;
+	} else if (pid !=3D 0) {
+		close(sock);
+		pidfile_close(pfh);
+		if (execl(handler_script, handler_script, reason,
+		    client_ntoa(client), client->hostname,
+		    client->infofilename, client->corefilename, NULL) =3D=3D=
 -1) {
+			LOGERR_PERROR("fork()");
+			_exit(1);
+		}
+	}
+}
+
+static void
+handle_timeout(struct netdump_client *client)
+{
+
+	assert(client !=3D NULL);
+
+	LOGINFO("Client %s timed out\n", client_ntoa(client));
+	client_pinfo(client, "Dump incomplete: client timed out\n");
+	exec_handler(client, "timeout");
+	free_client(client);
+}
+
+static void
+timeout_clients(void)
+{
+	struct netdump_client *client, *tmp;
+   =20
+	/* Only time out clients every 10 seconds. */
+	if (now - last_timeout_check < CLIENT_TPASS)
+		return;
+	last_timeout_check =3D now;
+
+	/* Traverse the list looking for stale clients. */
+	SLIST_FOREACH_SAFE(client, &clients, iter, tmp) {
+		if (client->last_msg + CLIENT_TIMEOUT < now) {
+			LOGINFO("Timingout with such values: %jd + %jd < =
%jd\n",
+			    (intmax_t)client->last_msg,
+			    (intmax_t)CLIENT_TIMEOUT, (intmax_t)now);
+			handle_timeout(client);
+		}
+	}
+}
+
+static void
+send_ack(struct netdump_client *client, struct netdump_msg *msg)
+{
+	struct netdump_ack ack;
+	int tryagain;
+   =20
+	assert(client !=3D NULL && msg !=3D NULL);
+
+	bzero(&ack, sizeof(ack));
+	ack.na_seqno =3D htonl(msg->nm_hdr.mh_seqno);
+	do {
+		tryagain =3D 0;
+		if (send(client->sock, &ack, sizeof(ack), 0) =3D=3D -1) =
{
+			if (errno =3D=3D EINTR) {
+				tryagain =3D 1;
+				continue;
+			}
+
+			/*
+			 * XXX: On EAGAIN, we should probably queue the =
packet
+			 * to be sent when the socket is writable but
+			 * that is too much effort, since it is mostly
+			 * harmless to wait for the client to =
retransmit.
+			 */
+			LOGERR_PERROR("send()");
+		}
+	} while (tryagain);
+}
+
+static void
+handle_herald(struct sockaddr_in *from, struct netdump_client *client,
+    struct netdump_msg *msg)
+{
+
+	assert(from !=3D NULL && msg !=3D NULL);
+
+	if (client !=3D NULL) {
+		if (client->any_data_rcvd =3D=3D 0) {
+
+			/* Must be a retransmit of the herald packet. */
+			send_ack(client, msg);
+			return;
+		}
+
+		/* An old connection must have timed out. Clean it up =
first. */
+		handle_timeout(client);
+	}
+
+	client =3D alloc_client(from);
+	if (client =3D=3D NULL) {
+		LOGERR("handle_herald(): new client allocation =
failure\n");
+		return;
+	}
+	client_pinfo(client, "Dump from %s [%s]\n", client->hostname,
+	    client_ntoa(client));
+	LOGINFO("New dump from client %s [%s] (to %s)\n", =
client->hostname,
+	    client_ntoa(client), client->corefilename);
+	send_ack(client, msg);
+}
+
+static void
+handle_kdh(struct netdump_client *client, struct netdump_msg *msg)
+{
+	time_t t;
+	uint64_t dumplen;
+	struct kerneldumpheader *h;
+	int parity_check;
+
+	assert(msg !=3D NULL);
+
+	if (client =3D=3D NULL)
+		return;
+
+	client->any_data_rcvd =3D 1;
+	h =3D (struct kerneldumpheader *)msg->nm_data;
+	if (msg->nm_hdr.mh_len < sizeof(struct kerneldumpheader)) {
+		LOGERR("Bad KDH from %s [%s]: packet too small\n",
+		    client->hostname, client_ntoa(client));
+		client_pinfo(client, "Bad KDH: packet too small\n");
+		fflush(client->infofile);
+		send_ack(client, msg);
+		return;
+	}
+	parity_check =3D kerneldump_parity(h);
+
+	/* Make sure all the strings are null-terminated. */
+	h->architecture[sizeof(h->architecture) - 1] =3D '\0';
+	h->hostname[sizeof(h->hostname) - 1] =3D '\0';
+	h->versionstring[sizeof(h->versionstring) - 1] =3D '\0';
+	h->panicstring[sizeof(h->panicstring) - 1] =3D '\0';
+
+	client_pinfo(client, "  Architecture: %s\n", h->architecture);
+	client_pinfo(client, "  Architecture version: %d\n",
+	    dtoh32(h->architectureversion));
+	dumplen =3D dtoh64(h->dumplength);
+	client_pinfo(client, "  Dump length: %lldB (%lld MB)\n",
+	    (long long)dumplen, (long long)(dumplen >> 20));
+	client_pinfo(client, "  blocksize: %d\n", dtoh32(h->blocksize));
+	t =3D dtoh64(h->dumptime);
+	client_pinfo(client, "  Dumptime: %s", ctime(&t));
+	client_pinfo(client, "  Hostname: %s\n", h->hostname);
+	client_pinfo(client, "  Versionstring: %s", h->versionstring);
+	client_pinfo(client, "  Panicstring: %s\n", h->panicstring);
+	client_pinfo(client, "  Header parity check: %s\n",
+	    parity_check ? "Fail" : "Pass");
+	fflush(client->infofile);
+
+	LOGINFO("(KDH from %s [%s])", client->hostname, =
client_ntoa(client));
+	send_ack(client, msg);
+}
+
+static void
+handle_vmcore(struct netdump_client *client, struct netdump_msg *msg)
+{
+
+	assert(msg !=3D NULL);
+
+	if (client =3D=3D NULL)
+		return;
+
+	client->any_data_rcvd =3D 1;
+	if (msg->nm_hdr.mh_seqno % 11523 =3D=3D 0) {
+
+		/* Approximately every 16MB with MTU of 1500 */
+		LOGINFO(".");
+	}
+	if (pwrite(client->corefd, msg->nm_data, msg->nm_hdr.mh_len,
+	    msg->nm_hdr.mh_offset) =3D=3D -1) {
+		LOGERR("pwrite (for client %s [%s]): %s\n", =
client->hostname,
+		    client_ntoa(client), strerror(errno));
+		client_pinfo(client,
+		    "Dump unsuccessful: write error @ offset =
%08"PRIx64": %s\n",
+		    msg->nm_hdr.mh_offset, strerror(errno));
+		exec_handler(client, "error");
+		free_client(client);
+		return;
+	}
+	send_ack(client, msg);
+}
+
+static void
+handle_finish(struct netdump_client *client, struct netdump_msg *msg)
+{
+
+	assert(msg !=3D NULL);
+
+	if (client =3D=3D NULL)
+		return;
+
+	LOGINFO("\nCompleted dump from client %s [%s]\n", =
client->hostname,
+	    client_ntoa(client));
+	client_pinfo(client, "Dump complete\n");
+	send_ack(client, msg);
+	exec_handler(client, "success");
+	free_client(client);
+}
+
+
+static int
+receive_message(int isock, struct sockaddr_in *from, char *fromstr,
+    size_t fromstrlen, struct netdump_msg *msg)
+{
+	socklen_t fromlen;
+	ssize_t len;
+
+	assert(from !=3D NULL && fromstr !=3D NULL && msg !=3D NULL);
+
+	bzero(from, sizeof(*from));
+	from->sin_family =3D AF_INET;
+	from->sin_len =3D fromlen =3D sizeof(*from);
+	from->sin_port =3D 0;
+	from->sin_addr.s_addr =3D INADDR_ANY;
+
+	len =3D recvfrom(isock, msg, sizeof(*msg), 0, (struct sockaddr =
*)from,
+	    &fromlen);
+	if (len =3D=3D -1) {
+
+		/*
+		 * As long as some callers may discard the errors =
printing
+		 * in defined circumstances, leave them the choice and =
avoid
+		 * any error reporting.
+		 */
+		return (-1);
+	}
+
+	snprintf(fromstr, fromstrlen, "%s:%hu", =
inet_ntoa(from->sin_addr),
+	    ntohs(from->sin_port));
+	if ((size_t)len < sizeof(struct netdump_msg_hdr)) {
+		LOGERR("Ignoring runt packet from %s (got %zu)\n", =
fromstr,
+		    (size_t)len);
+		return (0);
+	}
+
+	/* Convert byte order. */
+	msg->nm_hdr.mh_type =3D ntohl(msg->nm_hdr.mh_type);
+	msg->nm_hdr.mh_seqno =3D ntohl(msg->nm_hdr.mh_seqno);
+	msg->nm_hdr.mh_offset =3D be64toh(msg->nm_hdr.mh_offset);
+	msg->nm_hdr.mh_len =3D ntohl(msg->nm_hdr.mh_len);
+
+	if ((size_t)len < sizeof(struct netdump_msg_hdr) + =
msg->nm_hdr.mh_len) {
+		LOGERR("Packet too small from %s (got %zu, expected =
%zu)\n",
+		    fromstr, (size_t)len,
+		    sizeof(struct netdump_msg_hdr) + =
msg->nm_hdr.mh_len);
+		return (0);
+	}
+	return (len);
+}
+
+static void
+handle_packet(struct netdump_client *client, struct sockaddr_in *from,
+    const char *fromstr, struct netdump_msg *msg)
+{
+
+	assert(from !=3D NULL && fromstr !=3D NULL && msg !=3D NULL);
+
+	if (client !=3D NULL)
+		client->last_msg =3D time(NULL);
+
+	switch (msg->nm_hdr.mh_type) {
+	case NETDUMP_HERALD:
+		handle_herald(from, client, msg);
+		break;
+	case NETDUMP_KDH:
+		handle_kdh(client, msg);
+		break;
+	case NETDUMP_VMCORE:
+		handle_vmcore(client, msg);
+		break;
+	case NETDUMP_FINISHED:
+		handle_finish(client, msg);
+		break;
+	default:
+		LOGERR("Received unknown message type %d from %s\n",
+		    msg->nm_hdr.mh_type, fromstr);
+	}
+}
+
+static void
+eventloop(void)
+{
+	struct netdump_msg msg;
+	char fromstr[INET_ADDRSTRLEN + 6];
+	fd_set readfds;
+	struct sockaddr_in from;
+	struct timeval tv;
+	struct netdump_client *client, *tmp;
+	int len, maxfd;
+
+	while (do_shutdown =3D=3D 0) {
+		maxfd =3D sock + 1;
+		FD_ZERO(&readfds);
+		FD_SET(sock, &readfds);
+		SLIST_FOREACH(client, &clients, iter) {
+			FD_SET(client->sock, &readfds);
+			if (maxfd <=3D client->sock)
+				maxfd =3D client->sock+1;
+		}
+
+		/* So that we time out clients regularly. */
+		tv.tv_sec =3D CLIENT_TPASS;
+		tv.tv_usec =3D 0;
+		if (select(maxfd, &readfds, NULL, NULL, &tv) =3D=3D -1) =
{
+			if (errno =3D=3D EINTR)
+				continue;
+			LOGERR_PERROR("select()");
+
+			/*
+			 * Errors with select() probably will not go =
away
+			 * with simple retrying.
+			 */
+			pidfile_remove(pfh);
+			exit(1);
+		}
+		now =3D time(NULL);
+		if (FD_ISSET(sock, &readfds)) {
+			len =3D receive_message(sock, &from, fromstr,
+			    sizeof(fromstr), &msg);
+			if (len =3D=3D -1) {
+				if (errno =3D=3D EINTR)
+					continue;
+				if (errno !=3D EAGAIN) {
+					pidfile_remove(pfh);
+					LOGERR_PERROR("recvfrom()");
+					exit(1);
+				}
+			} else if (len !=3D 0) {
+
+				/*
+				 * With len =3D=3D 0 the packet was =
rejected
+				 * (probably because it was too small) =
so just
+				 * ignore this case.
+				 */
+
+				/* Check if they are on the clients =
list. */
+				SLIST_FOREACH(client, &clients, iter)
+					if (client->ip.s_addr =3D=3D
+					    from.sin_addr.s_addr)
+						break;
+
+				/*
+				 * Technically, clients should not be
+				 * responding on the server port, so =
client
+				 * should be NULL, however, if they =
insist on
+				 * doing so, it's not really going to =
hurt
+				 * anything (except maybe fill up the =
server
+				 * socket's receive buffer), so still
+				 * accept it. The only possibly =
legitimate case
+				 * is if there's a new dump starting and =
the
+				 * previous one didn't finish cleanly. =
Handle
+				 * this by suppressing the error on =
HERALD
+				 * packets.
+				 */
+				if (client !=3D NULL &&
+				    msg.nm_hdr.mh_type !=3D =
NETDUMP_HERALD &&
+				    client->printed_port_warning =3D=3D =
0) {
+			    LOGWARN("Client %s responding on server =
port\n",
+					    client->hostname);
+					client->printed_port_warning =3D =
1;
+				}
+				handle_packet(client, &from, fromstr, =
&msg);
+			}
+		}
+
+		/*
+		 * handle_packet() and handle_timeout() may free the =
client,
+		 * handle stale pointers.
+		 */
+		SLIST_FOREACH_SAFE(client, &clients, iter, tmp) {
+			if (FD_ISSET(client->sock, &readfds)) {
+				len =3D receive_message(client->sock, =
&from,
+				    fromstr, sizeof(fromstr), &msg);
+				if (len =3D=3D -1) {
+					if (errno =3D=3D EINTR || errno =
=3D=3D EAGAIN)
+						continue;
+					LOGERR_PERROR("recvfrom()");
+
+					/*
+					 * Client socket is broken for
+					 * some reason.
+					 */
+					handle_timeout(client);
+				} else if (len !=3D 0) {
+
+					/*
+					 * With len =3D=3D 0 the packet =
was
+					 * rejected (probably because it =
was
+					 * too small) so just ignore =
this case.
+					 */
+
+					FD_CLR(client->sock, &readfds);
+					handle_packet(client, &from, =
fromstr,
+					    &msg);
+				}
+			}
+		}
+		timeout_clients();
+	}
+	LOGINFO("Shutting down...");
+
+	/*
+	 * Clients is the head of the list, so clients !=3D NULL iff the =
list
+	 * is not empty. Call it a timeout so that the scripts get run.
+	 */
+	while (!SLIST_EMPTY(&clients))
+		handle_timeout(SLIST_FIRST(&clients));
+}
+
+static void
+signal_shutdown(int sig __unused)
+{
+
+    do_shutdown =3D 1;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct stat statbuf;
+	struct sockaddr_in bindaddr;
+	struct sigaction sa;
+	int ch;
+
+	pfh =3D pidfile_open(NULL, 0600, NULL);
+	if (pfh =3D=3D NULL) {
+		if (errno =3D=3D EEXIST)
+			printf("Instance of netdump already running\n");
+		else
+			printf("Impossible to open the pid file\n");
+		exit(1);
+	}
+
+	while ((ch =3D getopt(argc, argv, "a:Dd:i:")) !=3D -1) {
+		switch (ch) {
+		case 'a':
+			pflags |=3D PFLAGS_ABIND;
+			if (!inet_aton(optarg, &bindip)) {
+				pidfile_remove(pfh);
+				fprintf(stderr, "Invalid bind IP =
specified\n");
+				exit(1);
+			}
+			printf("Listening on IP %s\n", optarg);
+			break;
+		case 'D':
+			pflags |=3D PFLAGS_DEBUG;
+			break;
+		case 'd':
+			pflags |=3D PFLAGS_DDIR;
+			assert(dumpdir[0] =3D=3D '\0');
+			strncpy(dumpdir, optarg, sizeof(dumpdir) - 1);
+			break;
+		case 'i':
+			pflags |=3D PFLAGS_SCRIPT;
+
+			/*
+			 * When suddenly closing the process for an =
error,
+			 * it is unuseful to take care of handler_script
+			 * deallocation as long as the process will =
_exit(2)
+			 * anyway.
+			 */
+			handler_script =3D strdup(optarg);
+			if (handler_script =3D=3D NULL) {
+				pidfile_remove(pfh);
+				perror("strdup()");
+				fprintf(stderr, "Unable to set script =
file\n");
+				exit(1);
+			}
+			if (access(handler_script, F_OK | X_OK)) {
+				pidfile_remove(pfh);
+				perror("access()");
+				fprintf(stderr,
+				    "Unable to access script file\n");
+				exit(1);
+			}
+			break;
+		default:
+			pidfile_remove(pfh);
+			usage(argv[0]);
+			exit(1);
+		}
+	}
+	if ((pflags & PFLAGS_ABIND) =3D=3D 0) {
+		bindip.s_addr =3D INADDR_ANY;
+		printf("Default: listening on all interfaces\n");
+	}
+	if ((pflags & PFLAGS_DDIR) =3D=3D 0) {
+		strcpy(dumpdir, "/var/crash");
+		printf("Default: dumping on /var/crash/\n");
+	}
+	if ((pflags & PFLAGS_DEBUG) =3D=3D 0)
+		phook =3D syslog;
+	else
+		phook =3D phook_printf;
+
+	/* Further sanity checks on dump location. */
+	if (stat(dumpdir, &statbuf)) {
+		pidfile_remove(pfh);
+		perror("stat()");
+		fprintf(stderr, "Invalid dump location specified\n");
+		exit(1);
+	}
+	if ((statbuf.st_mode & S_IFMT) !=3D S_IFDIR) {
+		pidfile_remove(pfh);
+		fprintf(stderr, "Dump location is not a directory\n");
+		exit(1);
+	}
+	if (access(dumpdir, F_OK | W_OK)) {
+		fprintf(stderr,
+		    "Warning: May be unable to write into dump location: =
%s\n",
+		    strerror(errno));
+	}
+
+	if ((pflags & PFLAGS_DEBUG) =3D=3D 0 && daemon(0, 0) =3D=3D -1) =
{
+		pidfile_remove(pfh);
+		perror("daemon()");
+		fprintf(stderr, "Impossible to demonize the process\n");
+		exit(1);
+	}
+	pidfile_write(pfh);
+
+	/* Set up the server socket. */
+	sock =3D socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (sock =3D=3D -1) {
+		pidfile_remove(pfh);
+		LOGERR_PERROR("socket()");
+		exit(1);
+	}
+	bzero(&bindaddr, sizeof(bindaddr));
+	bindaddr.sin_len =3D sizeof(bindaddr);
+	bindaddr.sin_family =3D AF_INET;
+	bindaddr.sin_addr.s_addr =3D bindip.s_addr;
+	bindaddr.sin_port =3D htons(NETDUMP_PORT);
+	if (bind(sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) =
{
+		pidfile_remove(pfh);
+		close(sock);
+		LOGERR_PERROR("bind()");
+		exit(1);
+	}
+	if (fcntl(sock, F_SETFL, O_NONBLOCK) =3D=3D -1) {
+		pidfile_remove(pfh);
+		close(sock);
+		LOGERR_PERROR("fcntl()");
+		exit(1);
+	}
+
+	/* Override some signal handlers. */
+	bzero(&sa, sizeof(sa));
+	sa.sa_handler =3D signal_shutdown;
+	if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, =
NULL)) {
+		pidfile_remove(pfh);
+		close(sock);
+		LOGERR_PERROR("sigaction(SIGINT | SIGTERM)");
+		exit(1);
+	}
+	bzero(&sa, sizeof(sa));
+	sa.sa_handler =3D SIG_IGN;
+	sa.sa_flags =3D SA_NOCLDWAIT;
+	if (sigaction(SIGCHLD, &sa, NULL)) {
+		pidfile_remove(pfh);
+		LOGERR_PERROR("sigaction(SIGCHLD)");
+		close(sock);
+		exit(1);
+	}
+
+	LOGINFO("Waiting for clients.\n");
+	eventloop();
+
+	pidfile_remove(pfh);
+	return (0);
+}

--Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=us-ascii



--Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3
Content-Disposition: attachment;
	filename=kernel-netdump-diffs.txt
Content-Type: text/plain;
	name="kernel-netdump-diffs.txt"
Content-Transfer-Encoding: quoted-printable

diff --git a/sys/conf/files b/sys/conf/files
index 46d0d27..b910b1a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3262,6 +3264,7 @@ netinet/ip_mroute.c		optional =
mrouting inet
 netinet/ip_options.c		optional inet
 netinet/ip_output.c		optional inet
 netinet/netdump_client.c	optional inet netdump_client
+netinet/network_debug.c		optional inet netdump_client
 netinet/raw_ip.c		optional inet | inet6
 netinet/cc/cc.c			optional inet | inet6
 netinet/cc/cc_newreno.c		optional inet | inet6
diff --git a/sys/netinet/netdump_client.c b/sys/netinet/netdump_client.c
index 1b73e1b..ad4c347 100644
--- a/sys/netinet/netdump_client.c
+++ b/sys/netinet/netdump_client.c
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/protosw.h>
 #include <sys/reboot.h>
 #include <sys/socket.h>
+#include <sys/sockio.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
=20
@@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/ip_var.h>
 #include <netinet/ip_options.h>
 #include <netinet/netdump.h>
+#include <netinet/network_debug.h>
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
=20
@@ -96,20 +98,15 @@ __FBSDID("$FreeBSD$");
 #define	NETDDEBUGV_IF(i, f, ...)
 #endif
=20
-static void	 nd_handle_arp(struct mbuf **mb);
-static void	 nd_handle_ip(struct mbuf **mb);
+static void	nd_handle_ip(struct network_debug *, struct mbuf **mb);
+static int	nd_packet_wanted(struct network_debug *, struct mbuf *);
 static int	 netdump_arp_server(void);
 static void	 netdump_config_defaults(void *dummy __unused);
 static int	 netdump_dumper(void *priv __unused, void *virtual,
 		    vm_offset_t physical __unused, off_t offset, size_t =
length);
-static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp,=20=

-		    struct ether_addr dst, u_short etype);
-static int	 netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr =
__unused, void *opt_args __unused);
-static void	 netdump_network_poll(void);
 static void	 netdump_pkt_in(struct ifnet *ifp, struct mbuf *m);
 static int	 netdump_send(uint32_t type, off_t offset, unsigned char =
*data,
 		    uint32_t datalen);
-static int	 netdump_send_arp(void);
 static int	 netdump_udp_output(struct mbuf *m);
=20
 static int	 sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS);
@@ -119,49 +116,30 @@ static int	 =
sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS);
 static unsigned char buf[MAXDUMPPGS * PAGE_SIZE];
 static uint64_t rcvd_acks;
 static uint32_t nd_seqno =3D 1;
-static int dump_failed, have_server_mac;
-
-/*
- * Times to poll the NIC (0.5ms each poll) before assuming packetloss
- * occurred (default to 5s).
- */
-static int nd_polls =3D 10000;
-
-/* Times to retransmit lost packets. */
-static int nd_retries =3D 10;
-
-/* General dynamic settings. */
-static char nd_ifp_str[IFNAMSIZ];
-static struct ether_addr nd_gw_mac;
-static struct in_addr nd_server =3D {INADDR_ANY};
-static struct in_addr nd_client =3D {INADDR_ANY};
-static struct in_addr nd_gw =3D {INADDR_ANY};
-struct ifnet *nd_ifp;
-int nd_enable =3D 0;
-static uint16_t nd_server_port =3D NETDUMP_PORT;
+static int dump_failed;
+
+static struct network_debug __unused
+nd_networking =3D {
+	.poll_time =3D 10000,
+	.retries =3D 10,
+	.if_name =3D { 0 },
+	.ifp =3D NULL,
+	.remote =3D {INADDR_ANY},
+	.local =3D {INADDR_ANY},
+	.gw =3D {INADDR_ANY},
+	.ip_handler =3D nd_handle_ip,
+	.wanted =3D nd_packet_wanted,
+};
+
+int nd_enable =3D 0;	// Used in kern_shutdown, so can't be static
+static uint16_t nd_remote_port =3D NETDUMP_PORT;
=20
 /* Tunables storages. */
-static char nd_server_tun[INET_ADDRSTRLEN];
-static char nd_client_tun[INET_ADDRSTRLEN];
+static char nd_remote_tun[INET_ADDRSTRLEN];
+static char nd_local_tun[INET_ADDRSTRLEN];
 static char nd_gw_tun[INET_ADDRSTRLEN];
 static char nd_nic_tun[IFNAMSIZ];
=20
-/*
- * Checks for netdump support on a network interface
- *
- * Parameters:
- *	ifp	The network interface that is being tested for support
- *
- * Returns:
- *	int	1 if the interface is supported, 0 if not
- */
-static __inline int
-netdump_supported_nic(struct ifnet *ifp)
-{
-
-	return (ifp->if_ndumpfuncs !=3D NULL);
-}
-
 /*-
  * Sysctls specific code.
  */
@@ -192,7 +170,7 @@ sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS)
 	IFNET_RLOCK_NOSLEEP();
 	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		if (!strncmp(ifp->if_xname, buf, strlen(ifp->if_xname)) =
&&
-		    netdump_supported_nic(ifp)) {
+		    network_debug_supported_nic(ifp)) {
 			found =3D 1;
 			break;
 		}
@@ -235,24 +213,24 @@ sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS)
=20
 SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, =
"netdump");
 SYSCTL_PROC(_net_dump, OID_AUTO, server, CTLTYPE_STRING | CTLFLAG_RW |
-    CTLFLAG_MPSAFE, &nd_server, 0, sysctl_handle_inaddr, "A", "dump =
server");
+    CTLFLAG_MPSAFE, &nd_networking.remote, 0, sysctl_handle_inaddr, =
"A", "dump server");
 SYSCTL_PROC(_net_dump, OID_AUTO, client, CTLTYPE_STRING |CTLFLAG_RW |
-    CTLFLAG_MPSAFE, &nd_client, 0, sysctl_handle_inaddr, "A", "dump =
client");
+    CTLFLAG_MPSAFE, &nd_networking.local, 0, sysctl_handle_inaddr, "A", =
"dump client");
 SYSCTL_PROC(_net_dump, OID_AUTO, gateway, CTLTYPE_STRING | CTLFLAG_RW |
-    CTLFLAG_MPSAFE, &nd_gw, 0, sysctl_handle_inaddr, "A",
+    CTLFLAG_MPSAFE, &nd_networking.gw, 0, sysctl_handle_inaddr, "A",
     "dump default gateway");
 SYSCTL_PROC(_net_dump, OID_AUTO, nic, CTLTYPE_STRING | CTLFLAG_RW |
-    CTLFLAG_MPSAFE, &nd_ifp_str, 0, sysctl_handle_ifxname, "A",
+    CTLFLAG_MPSAFE, &nd_networking.if_name, 0, sysctl_handle_ifxname, =
"A",
     "dumping interface name");
 SYSCTL_INT(_net_dump, OID_AUTO, polls, CTLTYPE_INT | CTLFLAG_RW |
-    CTLFLAG_MPSAFE, &nd_polls, 0, "times to poll NIC per retry");
+    CTLFLAG_MPSAFE, &nd_networking.poll_time, 0, "times to poll NIC per =
retry");
 SYSCTL_INT(_net_dump, OID_AUTO, retries, CTLTYPE_INT | CTLFLAG_RW |
-    CTLFLAG_MPSAFE, &nd_retries, 0, "times to retransmit lost =
packets");
+    CTLFLAG_MPSAFE, &nd_networking.retries, 0, "times to retransmit =
lost packets");
 SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT | CTLFLAG_RW |
     CTLFLAG_MPSAFE, &nd_enable, 0, "enable network dump");
=20
-TUNABLE_STR("net.dump.server", nd_server_tun, sizeof(nd_server_tun));
-TUNABLE_STR("net.dump.client", nd_client_tun, sizeof(nd_client_tun));
+TUNABLE_STR("net.dump.server", nd_remote_tun, sizeof(nd_remote_tun));
+TUNABLE_STR("net.dump.client", nd_local_tun, sizeof(nd_local_tun));
 TUNABLE_STR("net.dump.gateway", nd_gw_tun, sizeof(nd_gw_tun));
 TUNABLE_STR("net.dump.nic", nd_nic_tun, sizeof(nd_nic_tun));
 TUNABLE_INT("net.dump.enable", &nd_enable);
@@ -265,66 +243,7 @@ TUNABLE_INT("net.dump.enable", &nd_enable);
  * - Polling primitives
  */
=20
-/*
- * Netdump wraps external mbufs around address ranges.  unlike most =
sane
- * counterparts, netdump uses a stop-and-wait approach to flow control =
and
- * retransmission, so the ack obviates the need for mbuf reference
- * counting.  We still need to tell other mbuf handlers not to do =
anything
- * special with our mbufs, so specify this nop handler.
- *
- * Parameters:
- *	ptr	 data to free (ignored)
- *	opt_args callback pointer (ignored)
- *
- * Returns:
- *	void
- */
-static int
-netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr __unused, void =
*opt_args __unused)
-{
-	return EXT_FREE_OK;
-}
-
-/*
- * Handles creation of the ethernet header, then places outgoing =
packets into
- * the tx buffer for the NIC
- *
- * Parameters:
- *	m	The mbuf containing the packet to be sent (will be freed =
by
- *		this function or the NIC driver)
- *	ifp	The interface to send on
- *	dst	The destination ethernet address (source address will be =
looked
- *		up using ifp)
- *	etype	The ETHERTYPE_* value for the protocol that is being =
sent
- *
- * Returns:
- *	int	see errno.h, 0 for success
- */
-static int
-netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct =
ether_addr dst,
-    u_short etype)
-{
-	struct ether_header *eh;
=20
-	/* Fill in the ethernet header. */
-	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
-	if (m =3D=3D NULL) {
-		printf("netdump_ether_output: Out of mbufs\n");
-		return (ENOBUFS);
-	}
-	eh =3D mtod(m, struct ether_header *);
-	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
-	memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN);
-	eh->ether_type =3D htons(etype);
-
-	if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) !=3D IFF_UP) ||
-	    (ifp->if_drv_flags & IFF_DRV_RUNNING) !=3D IFF_DRV_RUNNING) =
{
-		if_printf(ifp, "netdump_ether_output: Interface isn't =
up\n");
-		m_freem(m);
-		return (ENETDOWN);
-	}
-	return ((ifp->if_transmit)(ifp, m));
-}
=20
 /*
  * Unreliable transmission of an mbuf chain to the netdump server
@@ -343,7 +262,7 @@ netdump_udp_output(struct mbuf *m)
 	struct udpiphdr *ui;
 	struct ip *ip;
=20
-	MPASS(nd_ifp !=3D NULL);
+	MPASS(nd_networking.ifp !=3D NULL);
=20
 	M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
 	if (m =3D=3D NULL) {
@@ -355,11 +274,11 @@ netdump_udp_output(struct mbuf *m)
 	ui->ui_pr =3D IPPROTO_UDP;
 	ui->ui_len =3D htons(m->m_pkthdr.len - sizeof(struct ip));
 	ui->ui_ulen =3D ui->ui_len;
-	ui->ui_src =3D nd_client;
-	ui->ui_dst =3D nd_server;
+	ui->ui_src =3D nd_networking.local;
+	ui->ui_dst =3D nd_networking.remote;
 	/* Use this src port so that the server can connect() the socket =
*/
 	ui->ui_sport =3D htons(NETDUMP_ACKPORT);
-	ui->ui_dport =3D htons(nd_server_port);
+	ui->ui_dport =3D htons(nd_remote_port);
 	ui->ui_sum =3D 0;
 	if ((ui->ui_sum =3D in_cksum(m, m->m_pkthdr.len)) =3D=3D 0)
 		ui->ui_sum =3D 0xffff;
@@ -375,59 +294,15 @@ netdump_udp_output(struct mbuf *m)
 	ip->ip_sum =3D 0;
 	ip->ip_sum =3D in_cksum(m, sizeof(struct ip));
=20
-	if (m->m_pkthdr.len > nd_ifp->if_mtu) {
+	if (m->m_pkthdr.len > nd_networking.ifp->if_mtu) {
 		printf("netdump_udp_output: Packet is too big: %u > MTU =
%lu\n",
-		       m->m_pkthdr.len, (unsigned long)nd_ifp->if_mtu);
+		       m->m_pkthdr.len, (unsigned =
long)nd_networking.ifp->if_mtu);
 		m_freem(m);
 		return (ENOBUFS);
 	}
-	return (netdump_ether_output(m, nd_ifp, nd_gw_mac, =
ETHERTYPE_IP));
+	return (network_debug_ether_output(m, nd_networking.ifp, =
nd_networking.remote_mac, ETHERTYPE_IP));
 }
=20
-/*
- * Builds and sends a single ARP request to locate the server
- *
- * Parameters:
- *	void
- *
- * Return value:
- *	0 on success
- *	errno on error
- */
-static int
-netdump_send_arp()
-{
-	struct ether_addr bcast;
-	struct mbuf *m;
-	struct arphdr *ah;
-	int pktlen;
-
-	MPASS(nd_ifp !=3D NULL);
-
-	/* Fill-up a broadcast address. */
-	memset(&bcast, 0xFF, ETHER_ADDR_LEN);
-	MGETHDR(m, M_NOWAIT, MT_DATA);
-	if (m =3D=3D NULL) {
-		printf("netdump_send_arp: Out of mbufs");
-		return (ENOBUFS);
-	}
-	pktlen =3D arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
-	m->m_len =3D pktlen;
-	m->m_pkthdr.len =3D pktlen;
-	MH_ALIGN(m, pktlen);
-	ah =3D mtod(m, struct arphdr *);
-	ah->ar_hrd =3D htons(ARPHRD_ETHER);
-	ah->ar_pro =3D htons(ETHERTYPE_IP);
-	ah->ar_hln =3D ETHER_ADDR_LEN;
-	ah->ar_pln =3D sizeof(struct in_addr);
-	ah->ar_op =3D htons(ARPOP_REQUEST);
-	memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN);
-	memcpy(ar_spa(ah), &nd_client.s_addr, sizeof(nd_client.s_addr));
-	bzero(ar_tha(ah), ETHER_ADDR_LEN);
-	memcpy(ar_tpa(ah), &nd_gw.s_addr, sizeof(nd_gw.s_addr));
-
-	return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP));
-}
=20
 /*
  * Sends ARP requests to locate the server and waits for a response
@@ -444,20 +319,20 @@ netdump_arp_server()
 {
 	int err, polls, retries;
=20
-	for (retries =3D 0; retries < nd_retries && have_server_mac =3D=3D=
 0;
+	for (retries =3D 0; (retries < nd_networking.retries) && =
(nd_networking.have_remote_mac =3D=3D 0);
 	    retries++) {
-		err =3D netdump_send_arp();
+		err =3D network_debug_send_arp(&nd_networking);
 		if (err !=3D 0)
 			return (err);
-		for (polls =3D 0; polls < nd_polls && have_server_mac =3D=3D=
 0;
+		for (polls =3D 0; (polls < nd_networking.poll_time) && =
(nd_networking.have_remote_mac =3D=3D 0);
 		    polls++) {
-			netdump_network_poll();
+			network_debug_poll(&nd_networking);
 			DELAY(500);
 		}
-		if (have_server_mac =3D=3D 0)
+		if (nd_networking.have_remote_mac =3D=3D 0)
 			printf("(ARP retry)");
 	}
-	if (have_server_mac !=3D 0)
+	if (nd_networking.have_remote_mac !=3D 0)
 		return (0);
=20
 	printf("\nARP timed out.\n");
@@ -492,7 +367,7 @@ netdump_send(uint32_t type, off_t offset, unsigned =
char *data, uint32_t datalen)
 	rcvd_acks =3D 0;
 	retries =3D 0;
=20
-	MPASS(nd_ifp !=3D NULL);
+	MPASS(nd_networking.ifp !=3D NULL);
=20
 retransmit:
=20
@@ -505,7 +380,7 @@ retransmit:
 		pktlen =3D min(pktlen, NETDUMP_DATASIZE);
=20
 		/* Second bound: the interface MTU (assume no IP =
options). */
-		pktlen =3D min(pktlen, nd_ifp->if_mtu - sizeof(struct =
udpiphdr) -
+		pktlen =3D min(pktlen, nd_networking.ifp->if_mtu - =
sizeof(struct udpiphdr) -
 		    sizeof(struct netdump_msg_hdr));
=20
 		/*
@@ -543,7 +418,7 @@ retransmit:
 				printf("netdump_send: Out of mbufs!\n");
 				return (ENOBUFS);
 			}
-			MEXTADD(m2, data+sent_so_far, pktlen, =
netdump_mbuf_nop,
+			MEXTADD(m2, data+sent_so_far, pktlen, =
network_debug_mbuf_nop,
 			    NULL, NULL, M_RDONLY, EXT_MOD_TYPE);
 			m2->m_len =3D pktlen;
 			m->m_next =3D m2;
@@ -568,13 +443,13 @@ retransmit:
 	 */
 	polls =3D 0;
 	while (rcvd_acks !=3D want_acks) {	=09
-		if (polls++ > nd_polls) {
-			if (retries++ > nd_retries)
+		if (polls++ > nd_networking.poll_time) {
+			if (retries++ > nd_networking.retries)
 				return (ETIMEDOUT);
 			printf(". ");
 			goto retransmit;
 		}
-		netdump_network_poll();
+		network_debug_poll(&nd_networking);
 		DELAY(500);
 	}
 	nd_seqno +=3D i;
@@ -582,6 +457,97 @@ retransmit:
 }
=20
 /*
+ * Determine if we want this IP packet.
+ */
+static int
+nd_packet_wanted(struct network_debug *ndp, struct mbuf *m)
+{
+	int retval =3D 0;
+	struct udpiphdr *udp;
+	struct ip *ip;
+	unsigned short hlen;
+
+	ip =3D  mtod(m, struct ip*);
+
+	/* IP version. */
+	if (ip->ip_v !=3D IPVERSION) {
+		NETDDEBUG("%s: Bad IP version %d\n", __FUNCTION__, =
ip->ip_v);
+		goto done;
+	}
+
+#ifdef INVARIANTS
+	if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D =
IN_LOOPBACKNET ||
+	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D =
IN_LOOPBACKNET) &&
+	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) =3D=3D 0) {
+		NETDDEBUG("%s: Bad IP header (RFC1122)\n", =
__FUNCTION__);
+		goto done;
+	}
+#endif
+
+	/* Header length. */
+	hlen =3D ip->ip_hl << 2;
+	if (hlen < sizeof(struct ip)) {
+		NETDDEBUG("%s: Bad IP header length (%hu)\n", =
__FUNCTION__, hlen);
+		goto done;
+	}
+	/* Checksum. */
+	if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) !=3D 0) {
+		if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) =3D=3D 0) {
+			NETDDEBUG("%s: Bad IP checksum\n", =
__FUNCTION__);
+			goto done;
+		}
+	}
+
+	if (ntohs(ip->ip_len) < hlen) {
+		NETDDEBUG("%s: IP packet smaller (%hu) than header =
(%hu)\n",
+			  __FUNCTION__, ntohs(ip->ip_len), hlen);
+		goto done;
+	}
+	if (m->m_pkthdr.len < ntohs(ip->ip_len)) {
+		NETDDEBUG("%s: IP packet bigger (%hu) than ethernet =
packet (%hu)\n",
+			  __FUNCTION__, ntohs(ip->ip_len), =
m->m_pkthdr.len);
+		goto done;
+	}
+	/* Ignore packets with IP options. */
+	if (hlen > sizeof(struct ip)) {
+		NETDDEBUG("%s: Drop packet with IP options\n", =
__FUNCTION__);
+		goto done;
+	}
+	if (ip->ip_p !=3D IPPROTO_UDP) {
+		NETDDEBUG("%s: Drop non-UDP packet\n", __FUNCTION__);
+		goto done;
+	}
+
+	/* Do not deal with fragments. */
+	if ((ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) !=3D 0) {
+		NETDDEBUG("%s: Drop fragmented packet\n", __FUNCTION__);
+		goto done;
+	}
+	/* Check that the source is the server's IP. */
+	if (ip->ip_src.s_addr !=3D ndp->remote.s_addr) {
+		NETDDEBUG("%s: Drop packet not from server\n", =
__FUNCTION__);
+		goto done;
+	}
+
+	/* Check if the destination IP is ours. */
+	if (ip->ip_dst.s_addr !=3D ndp->local.s_addr) {
+		NETDDEBUGV("%s: Drop packet not to our IP\n", =
__FUNCTION__);
+		goto done;
+	}
+	udp =3D mtod(m, struct udpiphdr *);
+
+	if (ntohs(udp->ui_u.uh_dport) !=3D NETDUMP_ACKPORT) {
+		NETDDEBUG("%s:  not on the netdump port.\n", =
__FUNCTION__);
+		goto done;
+	}
+=09
+	retval =3D 1;
+
+done:
+	return retval;
+}
+
+/*
  * Handler for IP packets: checks their sanity and then processes any =
netdump
  * ACK packets it finds.
  *
@@ -589,6 +555,7 @@ retransmit:
  * udp_input().
  *
  * Parameters:
+ *	ndp	A pointer to a network_debug struct
  *	mb	a pointer to an mbuf * containing the packet received
  *		Updates *mb if m_pullup et al change the pointer
  *		Assumes the calling function will take care of freeing =
the mbuf
@@ -597,27 +564,26 @@ retransmit:
  *	void
  */
 static void
-nd_handle_ip(struct mbuf **mb)
-{
-	struct ip *ip;
+nd_handle_ip(struct network_debug *ndp, struct mbuf **mb)
+{=09
 	struct udpiphdr *udp;
-	struct netdump_ack *nd_ack;
+	struct ip *ip;
 	struct mbuf *m;
+	struct netdump_ack *nd_ack;
 	int rcv_ackno;
 	unsigned short hlen;
=20
 	/* IP processing. */
 	m =3D *mb;
 	if (m->m_pkthdr.len < sizeof(struct ip)) {
-		NETDDEBUG("nd_handle_ip: dropping packet too small for =
IP "
-		    "header\n");
+		NETDDEBUG("%s: dropping packet too small for IP =
header\n", __FUNCTION__);
 		return;
 	}
 	if (m->m_len < sizeof(struct ip)) {
 		m =3D m_pullup(m, sizeof(struct ip));
 		*mb =3D m;
 		if (m =3D=3D NULL) {
-			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			NETDDEBUG("%s: m_pullup failed\n", =
__FUNCTION__);
 			return;
 		}
 	}
@@ -625,21 +591,21 @@ nd_handle_ip(struct mbuf **mb)
=20
 	/* IP version. */
 	if (ip->ip_v !=3D IPVERSION) {
-		NETDDEBUG("nd_handle_ip: Bad IP version %d\n", =
ip->ip_v);
+		NETDDEBUG("%s: Bad IP version %d\n", __FUNCTION__, =
ip->ip_v);
 		return;
 	}
=20
 	/* Header length. */
 	hlen =3D ip->ip_hl << 2;
 	if (hlen < sizeof(struct ip)) {
-		NETDDEBUG("nd_handle_ip: Bad IP header length (%hu)\n", =
hlen);
+		NETDDEBUG("%s: Bad IP header length (%hu)\n", =
__FUNCTION__, hlen);
 		return;
 	}
 	if (hlen > m->m_len) {
 		m =3D m_pullup(m, hlen);
 		*mb =3D m;
 		if (m =3D=3D NULL) {
-			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			NETDDEBUG("%s: m_pullup failed\n", =
__FUNCTION__);
 			return;
 		}
 		ip =3D mtod(m, struct ip *);
@@ -649,7 +615,7 @@ nd_handle_ip(struct mbuf **mb)
 	if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D =
IN_LOOPBACKNET ||
 	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D =
IN_LOOPBACKNET) &&
 	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) =3D=3D 0) {
-		NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n");
+		NETDDEBUG("%s: Bad IP header (RFC1122)\n", =
__FUNCTION__);
 		return;
 	}
 #endif
@@ -657,24 +623,24 @@ nd_handle_ip(struct mbuf **mb)
 	/* Checksum. */
 	if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) !=3D 0) {
 		if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) =3D=3D 0) {
-			NETDDEBUG("nd_handle_ip: Bad IP checksum\n");
+			NETDDEBUG("%s: Bad IP checksum\n", =
__FUNCTION__);
 			return;
 		}
 	} else
-		NETDDEBUG("nd_handle_ip: HW didn't check IP cksum\n");
+		NETDDEBUG("%s: HW didn't check IP cksum\n", =
__FUNCTION__);
=20
 	/* Convert fields to host byte order. */
 	ip->ip_len =3D ntohs(ip->ip_len);
 	if (ip->ip_len < hlen) {
-	NETDDEBUG("nd_handle_ip: IP packet smaller (%hu) than header =
(%hu)\n",
-		    ip->ip_len, hlen);
+		NETDDEBUG("%s: IP packet smaller (%hu) than header =
(%hu)\n",
+			  __FUNCTION__, ip->ip_len, hlen);
 		return;
 	}
 	ip->ip_off =3D ntohs(ip->ip_off);
-
+=09
 	if (m->m_pkthdr.len < ip->ip_len) {
-NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) than ethernet packet =
(%hu)\n",
-		    ip->ip_len, m->m_pkthdr.len);
+		NETDDEBUG("%s: IP packet bigger (%hu) than ethernet =
packet (%hu)\n",
+			  __FUNCTION__, ip->ip_len, m->m_pkthdr.len);
 		return;
 	}
 	if (m->m_pkthdr.len > ip->ip_len) {
@@ -689,42 +655,48 @@ NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) =
than ethernet packet (%hu)\n",
=20
 	/* Ignore packets with IP options. */
 	if (hlen > sizeof(struct ip)) {
-		NETDDEBUG("nd_handle_ip: Drop packet with IP =
options\n");
-		return;
-	}
-
-	/* Check that the source is the server's IP. */
-	if (ip->ip_src.s_addr !=3D nd_server.s_addr) {
-		NETDDEBUG("nd_handle_ip: Drop packet not from =
server\n");
-		return;
-	}
-
-	/* Check if the destination IP is ours. */
-	if (ip->ip_dst.s_addr !=3D nd_client.s_addr) {
-		NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n");
+		NETDDEBUG("%s: Drop packet with IP options\n", =
__FUNCTION__);
 		return;
 	}
=20
 	if (ip->ip_p !=3D IPPROTO_UDP) {
-		NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n");
+		NETDDEBUG("%s: Drop non-UDP packet\n", __FUNCTION__);
 		return;
 	}
=20
 	/* Do not deal with fragments. */
 	if ((ip->ip_off & (IP_MF | IP_OFFMASK)) !=3D 0) {
-		NETDDEBUG("nd_handle_ip: Drop fragmented packet\n");
+		NETDDEBUG("%s: Drop fragmented packet\n", __FUNCTION__);
 		return;
 	}
=20
 	/* UDP custom is to have packet length not include IP header. */
 	ip->ip_len -=3D hlen;
=20
+
 	/* UDP processing. */
=20
+	/*
+	 * Note that the rest of the code below is hard-wired for
+	 * network dump, and cannot be used for other purposes.
+	 */
+
+	/* Check that the source is the server's IP. */
+	if (ip->ip_src.s_addr !=3D ndp->remote.s_addr) {
+		NETDDEBUG("%s: Drop packet not from server\n", =
__FUNCTION__);
+		return;
+	}
+
+	/* Check if the destination IP is ours. */
+	if (ip->ip_dst.s_addr !=3D ndp->local.s_addr) {
+		NETDDEBUGV("%s: Drop packet not to our IP\n", =
__FUNCTION__);
+		return;
+	}
+
 	/* Get IP and UDP headers together, along with the netdump =
packet. */
 	if (m->m_pkthdr.len <
 	    sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
-		NETDDEBUG("nd_handle_ip: Ignoring small packet\n");
+		NETDDEBUG("%s: Ignoring small packet\n", __FUNCTION__);
 		return;
 	}
 	if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct =
netdump_ack)) {
@@ -732,19 +704,17 @@ NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) =
than ethernet packet (%hu)\n",
 		    sizeof(struct netdump_ack));
 		*mb =3D m;
 		if (m =3D=3D NULL) {
-			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			NETDDEBUG("%s: m_pullup failed\n", =
__FUNCTION__);
 			return;
 		}
 	}
 	udp =3D mtod(m, struct udpiphdr *);
=20
 	if (ntohs(udp->ui_u.uh_dport) !=3D NETDUMP_ACKPORT) {
-		NETDDEBUG("not on the netdump port.\n");
+		NETDDEBUG("%s:  not on the netdump port.\n", =
__FUNCTION__);
 		return;
 	}
=20
-	/* Netdump processing. */
-
 	/*
 	 * Packet is meant for us.  Extract the ack sequence number and =
the
 	 * port number if necessary.
@@ -752,233 +722,30 @@ NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) =
than ethernet packet (%hu)\n",
 	nd_ack =3D (struct netdump_ack *)(mtod(m, caddr_t) +
 	    sizeof(struct udpiphdr));
 	rcv_ackno =3D ntohl(nd_ack->na_seqno);
-	if (nd_server_port =3D=3D NETDUMP_PORT)
-	    nd_server_port =3D ntohs(udp->ui_u.uh_sport);
+	if (nd_remote_port =3D=3D NETDUMP_PORT)
+	    nd_remote_port =3D ntohs(udp->ui_u.uh_sport);
 	if (rcv_ackno >=3D nd_seqno + 64)
-		printf("nd_handle_ip: ACK %d too far in future!\n", =
rcv_ackno);
+		printf("%s: ACK %d too far in future!\n", __FUNCTION__, =
rcv_ackno);
 	else if (rcv_ackno >=3D nd_seqno) {
-
 		/* We're interested in this ack. Record it. */
 		rcvd_acks |=3D 1 << (rcv_ackno-nd_seqno);
 	}
-}
-
-/*
- * Handler for ARP packets: checks their sanity and then
- * 1. If the ARP is a request for our IP, respond with our MAC address
- * 2. If the ARP is a response from our server, record its MAC address
- *
- * It needs to replicate partially the behaviour of arpintr() and
- * in_arpinput().
- *
- * Parameters:
- *	mb	a pointer to an mbuf * containing the packet received
- *		Updates *mb if m_pullup et al change the pointer
- *		Assumes the calling function will take care of freeing =
the mbuf
- *
- * Return value:
- *	void
- */
-static void
-nd_handle_arp(struct mbuf **mb)
-{
-	char buf[INET_ADDRSTRLEN];
-	struct in_addr isaddr, itaddr, myaddr;
-	struct ether_addr dst;
-	struct mbuf *m;
-	struct arphdr *ah;
-	struct ifnet *ifp;
-	uint8_t *enaddr;
-	int req_len, op;
-
-	m =3D *mb;
-	ifp =3D m->m_pkthdr.rcvif;
-	if (m->m_len < sizeof(struct arphdr)) {
-		m =3D m_pullup(m, sizeof(struct arphdr));
-		*mb =3D m;
-		if (m =3D=3D NULL) {
-		NETDDEBUG("nd_handle_arp: runt packet: m_pullup =
failed\n");
-			return;
-		}
-	}
-	ah =3D mtod(m, struct arphdr *);
-
-	if (ntohs(ah->ar_hrd) !=3D ARPHRD_ETHER) {
-		NETDDEBUG("nd_handle_arp: unknown hardware address =
0x%2D)\n",
-		    (unsigned char *)&ah->ar_hrd, "");
-		return;
-	}
-	if (ntohs(ah->ar_pro) !=3D ETHERTYPE_IP) {
-		NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol =
%d\n",
-		    ntohs(ah->ar_pro));
-		return;
-	}
-	req_len =3D arphdr_len2(ifp->if_addrlen, sizeof(struct =
in_addr));
-	if (m->m_len < req_len) {
-		m =3D m_pullup(m, req_len);
-		*mb =3D m;
-		if (m =3D=3D NULL) {
-		NETDDEBUG("nd_handle_arp: runt packet: m_pullup =
failed\n");
-			return;
-		}
-	}
-	ah =3D mtod(m, struct arphdr *);
-
-	op =3D ntohs(ah->ar_op);
-	memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
-	memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
-	enaddr =3D (uint8_t *)IF_LLADDR(ifp);
-	myaddr =3D nd_client;
-
-	if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) {
-		NETDDEBUG("nd_handle_arp: ignoring ARP from myself\n");
-		return;
-	}
-
-	if (isaddr.s_addr =3D=3D nd_client.s_addr) {
-		printf("nd_handle_arp: %*D is using my IP address =
%s!\n",
-		    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
-		    inet_ntoa(isaddr));
-		return;
-	}
=20
-	if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) {
-	NETDDEBUG("nd_handle_arp: ignoring ARP from broadcast =
address\n");
-		return;
-	}
-
-	if (op =3D=3D ARPOP_REPLY) {
-		if (isaddr.s_addr !=3D nd_gw.s_addr) {
-			inet_ntoa_r(isaddr, buf);
-NETDDEBUG("nd_handle_arp: ignoring ARP reply from %s (not netdump =
server)\n",
-			    buf);
-			return;
-		}
-		memcpy(nd_gw_mac.octet, ar_sha(ah),
-		    min(ah->ar_hln, ETHER_ADDR_LEN));
-		have_server_mac =3D 1;
-		NETDDEBUG("\nnd_handle_arp: Got server MAC address =
%6D\n",
-		    nd_gw_mac.octet, ":");
-		return;
-	}
-
-	if (op !=3D ARPOP_REQUEST) {
-		NETDDEBUG("nd_handle_arp: Ignoring ARP =
non-request/reply\n");
-		return;
-	}
-
-	if (itaddr.s_addr !=3D nd_client.s_addr) {
-		NETDDEBUG("nd_handle_arp: ignoring ARP not to our =
IP\n");
-		return;
-	}
-
-	memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
-	memcpy(ar_sha(ah), enaddr, ah->ar_hln);
-	memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln);
-	memcpy(ar_spa(ah), &itaddr, ah->ar_pln);
-	ah->ar_op =3D htons(ARPOP_REPLY);
-	ah->ar_pro =3D htons(ETHERTYPE_IP);
-	m->m_flags &=3D ~(M_BCAST|M_MCAST);
-	m->m_len =3D sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
-	m->m_pkthdr.len =3D m->m_len;
-
-	memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
-	netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
-	*mb =3D NULL;
 }
=20
 /*
- * Handler for incoming packets directly from the network adapter
- * Identifies the packet type (IP or ARP) and passes it along to one of =
the
- * helper functions nd_handle_ip or nd_handle_arp.
- *
- * It needs to replicate partially the behaviour of ether_input() and
- * ether_demux().
- *
- * Parameters:
- *	ifp	the interface the packet came from (should be nd_ifp)
- *	m	an mbuf containing the packet received
- *
- * Return value:
- *	void
+ * netdump_pkt_in
+ * Simple wrapper for network_debug_pkt_in; this is called
+ * as ifp->if_input() by the ethernet driver.  We simply let
+ * network_debug_pkt_in() procss it, and decide which helper
+ * function to call.
  */
 static void
 netdump_pkt_in(struct ifnet *ifp, struct mbuf *m)
 {
-	struct ether_header *eh;
-	u_short etype;
-
-	/* Ethernet processing. */
-	if ((m->m_flags & M_PKTHDR) =3D=3D 0) {
-		NETDDEBUG_IF(ifp,
-		    "netdump_pkt_in: Discard frame without packet =
header\n");
-		goto done;
-	}
-	if (m->m_len < ETHER_HDR_LEN) {
-		NETDDEBUG_IF(ifp,
-"netdump_pkt_in: Discard frame without leading eth header (len %u =
pktlen %u)\n",
-		    m->m_len, m->m_pkthdr.len);
-		goto done;
-	}
-	if ((m->m_flags & M_HASFCS) !=3D 0) {
-		m_adj(m, -ETHER_CRC_LEN);
-		m->m_flags &=3D ~M_HASFCS;
-	}
-	eh =3D mtod(m, struct ether_header *);
-	m->m_pkthdr.PH_loc.ptr =3D eh;
-	etype =3D ntohs(eh->ether_type);
-	if ((m->m_flags & M_VLANTAG) !=3D 0 || etype =3D=3D =
ETHERTYPE_VLAN) {
-		NETDDEBUG_IF(ifp, "netdump_pkt_in: Ignoring vlan =
packets\n");
-		goto done;
-	}
-
-	/* XXX: Probably must also check if we're the recipient MAC =
address. */
-
-	/* Done ethernet processing. Strip off the ethernet header. */
-	m_adj(m, ETHER_HDR_LEN);
-	switch (etype) {
-		case ETHERTYPE_ARP:
-			nd_handle_arp(&m);
-			break;
-		case ETHERTYPE_IP:
-			nd_handle_ip(&m);
-			break;
-		default:
-			NETDDEBUG_IF(ifp,
-			    "netdump_pkt_in: Dropping unknown ethertype =
%hu\n",
-			    etype);
-			break;
-	}
-done:
-	if (m !=3D NULL)
-		m_freem(m);
+	network_debug_pkt_in(&nd_networking, m);
 }
=20
-/*
- * After trapping, instead of assuming that most of the network stack =
is sane
- * just poll the driver directly for packets.
- *
- * Parameters:
- *	void
- *
- * Returns:
- *	void
- */
-static void
-netdump_network_poll()
-{
-
-	MPASS(nd_ifp !=3D NULL);
-
-#if defined(KDB) && !defined(KDB_UNATTENDED)
-	if (panicstr !=3D NULL)
-		nd_ifp->if_ndumpfuncs->ne_poll_unlocked(nd_ifp,
-		    POLL_AND_CHECK_STATUS, 1000);
-	else
-#endif
-		nd_ifp->if_ndumpfuncs->ne_poll_locked(nd_ifp,
-		    POLL_AND_CHECK_STATUS, 1000);
-}
=20
 /*-
  * Dumping specific primitives.
@@ -1033,6 +800,25 @@ netdump_dumper(void *priv __unused, void *virtual,
 }
=20
 /*
+ * Preparation routine.  This sets some things up, and also ensures
+ * there we have an interface and address.
+ */
+static int
+nd_prepare(struct network_debug *ndp)
+{
+	int error =3D 0;
+
+	if (ndp->remote.s_addr =3D=3D INADDR_ANY) {
+		printf("%s: Can't netdump; no server IP given\n", =
__FUNCTION__);
+		return (EINVAL);
+	}
+
+	error =3D network_debug_prepare(ndp);
+	return error;
+}
+
+
+/*
  * Dumper routine, specular to dumpsys().
  *
  * Parameters:
@@ -1045,13 +831,11 @@ int
 netdumpsys()
 {
 	struct dumperinfo dumper;
-	void (*old_if_input)(struct ifnet *, struct mbuf *);
-	int error, found, must_lock, nd_gw_unset;
+	int error, found, must_lock;
+	void (*old_if_input)(struct ifnet *, struct mbuf *) =3D NULL;
=20
-	old_if_input =3D NULL;
 	error =3D 0;
 	found =3D 0;
-	nd_gw_unset =3D 0;
 	must_lock =3D 1;
 #if defined(KDB) && !defined(KDB_UNATTENDED)
 	if (panicstr !=3D NULL)
@@ -1062,51 +846,21 @@ netdumpsys()
 	if (nd_enable =3D=3D 0)
 		return (EINVAL);
=20
-	/* Lookup the right if device to be used in the dump. */
-	if (must_lock !=3D 0)
-		IFNET_RLOCK_NOSLEEP();
-	TAILQ_FOREACH(nd_ifp, &V_ifnet, if_link) {
-		if (!strncmp(nd_ifp->if_xname, nd_ifp_str,
-		    strlen(nd_ifp->if_xname)) &&
-		    netdump_supported_nic(nd_ifp)) {
-			found =3D 1;
-			break;
-		}
-	}
-	if (must_lock !=3D 0)
-		IFNET_RUNLOCK_NOSLEEP();
-	if (found =3D=3D 0) {
-		printf("netdumpsys: Can't netdump: no valid NIC =
given\n");
-		return (EINVAL);
-	}
-
-	MPASS(nd_ifp !=3D NULL);
-
-	if (nd_server.s_addr =3D=3D INADDR_ANY) {
-		printf("netdumpsys: Can't netdump; no server IP =
given\n");
-		return (EINVAL);
-	}
-	if (nd_client.s_addr =3D=3D INADDR_ANY) {
-		printf("netdumpsys: Can't netdump; no client IP =
given\n");
-		return (EINVAL);
+	error =3D nd_prepare(&nd_networking);
+	if (error) {
+		goto trig_abort;
 	}
=20
 	/*
 	 * nd_server_port could have switched after the first ack the
 	 * first time it gets called.  Adjust it accordingly.
 	 */
-	nd_server_port =3D NETDUMP_PORT;
-	if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock =
!=3D 0)
-		nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp);
+	nd_remote_port =3D NETDUMP_PORT;
=20
 	/* Make the card use *our* receive callback. */
-	old_if_input =3D nd_ifp->if_input;
-	nd_ifp->if_input =3D netdump_pkt_in;
+	old_if_input =3D nd_networking.ifp->if_input;
+	nd_networking.ifp->if_input =3D netdump_pkt_in;
=20
-	if (nd_gw.s_addr =3D=3D INADDR_ANY) {
-		nd_gw.s_addr =3D nd_server.s_addr;
-		nd_gw_unset =3D 1;
-	}
 	printf("\n-----------------------------------\n");
 	printf("netdump in progress. searching for server.. ");
 	if (netdump_arp_server()) {
@@ -1119,7 +873,7 @@ netdumpsys()
 		error =3D EINVAL;
 		goto trig_abort;
 	}
-	printf("dumping to %s (%6D)\n", inet_ntoa(nd_server), =
nd_gw_mac.octet,
+	printf("dumping to %s (%6D)\n", inet_ntoa(nd_networking.remote), =
nd_networking.remote_mac.octet,
 	    ":");
 	printf("-----------------------------------\n");
=20
@@ -1142,12 +896,9 @@ netdumpsys()
 	}
 	printf("\nnetdump finished.\n");
 trig_abort:
-	if (nd_gw_unset !=3D 0)
-		nd_gw.s_addr =3D INADDR_ANY;
+	network_debug_finish(&nd_networking);
 	if (old_if_input)
-		nd_ifp->if_input =3D old_if_input;
-	if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock =
!=3D 0)
-		nd_ifp->if_ndumpfuncs->ne_enable_intr(nd_ifp);
+		nd_networking.ifp->if_input =3D old_if_input;
 	return (error);
 }
=20
@@ -1160,6 +911,10 @@ trig_abort:
  * (locates the first available NIC and uses the first IPv4 IP on that =
card as
  * the client IP).  Leaves the server IP unconfigured.
  *
+ * The parenthical comment above is completely wrong.  Not only does it =
not do anything
+ * w.r.t. IP address, but it is called during kernel initialization -- =
so no NICs
+ * are configured at the time.  We instead do this in nd_prepare().
+ *
  * Parameters:
  *	void *, unused
  *
@@ -1169,22 +924,21 @@ trig_abort:
 static void
 netdump_config_defaults(void *dummy __unused)
 {
-	struct ifnet *ifp;
-	int found;
-
-	nd_ifp =3D NULL;
-	nd_server.s_addr =3D INADDR_ANY;
-	nd_client.s_addr =3D INADDR_ANY;
-	nd_gw.s_addr =3D INADDR_ANY;
-
-	if (nd_server_tun[0] !=3D '\0')
-		inet_aton(nd_server_tun, &nd_server);
-	if (nd_client_tun[0] !=3D '\0')
-		inet_aton(nd_client_tun, &nd_client);
+	nd_networking.ifp =3D NULL;
+	nd_networking.remote.s_addr =3D INADDR_ANY;
+	nd_networking.local.s_addr =3D INADDR_ANY;
+	nd_networking.gw.s_addr =3D INADDR_ANY;
+
+	if (nd_remote_tun[0] !=3D '\0')
+		inet_aton(nd_remote_tun, &nd_networking.remote);
+	if (nd_local_tun[0] !=3D '\0')
+		inet_aton(nd_local_tun, &nd_networking.local);
 	if (nd_gw_tun[0] !=3D '\0')
-		inet_aton(nd_gw_tun, &nd_gw);
+		inet_aton(nd_gw_tun, &nd_networking.gw);
 	if (nd_nic_tun[0] !=3D '\0') {
-		found =3D 0;
+#if 0
+		int found =3D 0;
+		struct ifnet *ifp;
 		IFNET_RLOCK_NOSLEEP();
 		TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 			if (!strncmp(ifp->if_xname, nd_nic_tun,
@@ -1194,8 +948,13 @@ netdump_config_defaults(void *dummy __unused)
 			}
 		}
 		IFNET_RUNLOCK_NOSLEEP();
-		if (found !=3D 0 && netdump_supported_nic(ifp))
-			nd_ifp =3D ifp;
+		if (found !=3D 0 && network_debug_supported_nic(ifp))
+			nd_networking.ifp =3D ifp;
+#else
+		strlcpy(nd_networking.if_name, nd_nic_tun, =
sizeof(nd_networking.if_name));
+#endif
 	}
 }
+
 SYSINIT(netdump, SI_SUB_KLD, SI_ORDER_ANY, netdump_config_defaults, =
NULL);
+
diff --git a/sys/netinet/network_debug.c b/sys/netinet/network_debug.c
new file mode 100644
index 0000000..933aec5
--- /dev/null
+++ b/sys/netinet/network_debug.c
@@ -0,0 +1,574 @@
+/*-
+ *
+ * Copyright (c) 2014 iXSystems, Inc.  All rights reserved.
+ *
+ * Copyright (c) 2005-2011 Sandvine Incorporated. All rights reserved.
+ * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in =
the
+ *    documentation and/or other materials provided with the =
distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' =
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR =
CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS =
INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN =
ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY =
OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * network_debug.c
+ * A FreeBSD subsystem supporting network I/O in a minimal kernel =
environment --
+ * specifically, to do a network coredump, or debugging over ethernet.
+ */
+
+#include "opt_ddb.h"
+#include "opt_kdb.h"
+#include "opt_netdump.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kerneldump.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/reboot.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_options.h>
+#include <netinet/netdump.h>
+#include <netinet/network_debug.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <machine/in_cksum.h>
+#include <machine/pcb.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+//#define NETWORK_DEBUG_DEBUG 10
+
+#ifdef NETWORK_DEBUG_DEBUG
+# define	NETDDEBUG(f, ...)		printf((f), ## =
__VA_ARGS__)
+# define	NETDDEBUG_IF(i, f, ...)		if_printf((i), (f), ## =
__VA_ARGS__)
+# if NETWORK_DEBUG_DEBUG > 1
+#  define	NETDDEBUGV(f, ...)		printf((f), ## =
__VA_ARGS__)
+#  define	NETDDEBUGV_IF(i, f, ...)	if_printf((i), (f), ## =
__VA_ARGS__)
+# else
+#  define	NETDDEBUGV(f, ...)
+#  define	NETDDEBUGV_IF(i, f, ...)
+# endif
+#else
+# define	NETDDEBUG(f, ...)
+# define	NETDDEBUG_IF(i, f, ...)
+# define	NETDDEBUGV(f, ...)
+# define	NETDDEBUGV_IF(i, f, ...)
+#endif
+
+/*
+ * Handles creation of the ethernet header, then places outgoing =
packets into
+ * the tx buffer for the NIC
+ *
+ * Parameters:
+ *	m	The mbuf containing the packet to be sent (will be freed =
by
+ *		this function or the NIC driver)
+ *	ifp	The interface to send on
+ *	dst	The destination ethernet address (source address will be =
looked
+ *		up using ifp)
+ *	etype	The ETHERTYPE_* value for the protocol that is being =
sent
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+int
+network_debug_ether_output(struct mbuf *m, struct ifnet *ifp, struct =
ether_addr dst,
+    u_short etype)
+{
+	struct ether_header *eh;
+
+	/* Fill in the ethernet header. */
+	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
+	if (m =3D=3D NULL) {
+		NETDDEBUG("%s: Out of mbufs\n", __FUNCTION__);
+		return (ENOBUFS);
+	}
+	eh =3D mtod(m, struct ether_header *);
+	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+	memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN);
+	eh->ether_type =3D htons(etype);
+
+	if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) !=3D IFF_UP) ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) !=3D IFF_DRV_RUNNING) =
{
+		if_printf(ifp, "%s: Interface isn't up\n", =
__FUNCTION__);
+		m_freem(m);
+		return (ENETDOWN);
+	}
+	return ((ifp->if_transmit)(ifp, m));
+}
+
+/*
+ * Netdump wraps external mbufs around address ranges.  unlike most =
sane
+ * counterparts, netdump uses a stop-and-wait approach to flow control =
and
+ * retransmission, so the ack obviates the need for mbuf reference
+ * counting.  We still need to tell other mbuf handlers not to do =
anything
+ * special with our mbufs, so specify this nop handler.
+ *
+ * Parameters:
+ *	ptr	 data to free (ignored)
+ *	opt_args callback pointer (ignored)
+ *
+ * Returns:
+ *	void
+ */
+int
+network_debug_mbuf_nop(struct mbuf *mp __unused, void *ptr __unused, =
void *opt_args __unused)
+{
+	return EXT_FREE_OK;
+}
+/*
+ * Handler for ARP packets: checks their sanity and then
+ * 1. If the ARP is a request for our IP, respond with our MAC address
+ * 2. If the ARP is a response from our server, record its MAC address
+ *
+ * It needs to replicate partially the behaviour of arpintr() and
+ * in_arpinput().
+ *
+ * Parameters:
+ *	mb	a pointer to an mbuf * containing the packet received
+ *		Updates *mb if m_pullup et al change the pointer
+ *		Assumes the calling function will take care of freeing =
the mbuf
+ *
+ * Return value:
+ *	void
+ */
+void
+network_debug_handle_arp(struct network_debug *ndp, struct mbuf **mb)
+{
+	char buf[INET_ADDRSTRLEN];
+	struct in_addr isaddr, itaddr, myaddr;
+	struct ether_addr dst;
+	struct mbuf *m;
+	struct arphdr *ah;
+	struct ifnet *ifp;
+	uint8_t *enaddr;
+	int req_len, op;
+
+	NETDDEBUG("%s(%d)\n", __FUNCTION__, __LINE__);
+
+	m =3D *mb;
+	ifp =3D m->m_pkthdr.rcvif;
+	if (m->m_len < sizeof(struct arphdr)) {
+		m =3D m_pullup(m, sizeof(struct arphdr));
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+			NETDDEBUG("%s: runt packet: m_pullup failed\n", =
__FUNCTION__);
+			return;
+		}
+	}
+	ah =3D mtod(m, struct arphdr *);
+
+	if (ntohs(ah->ar_hrd) !=3D ARPHRD_ETHER) {
+		NETDDEBUG("%s: unknown hardware address 0x%2D)\n",
+			  __FUNCTION__,
+			  (unsigned char *)&ah->ar_hrd, "");
+		return;
+	}
+	if (ntohs(ah->ar_pro) !=3D ETHERTYPE_IP) {
+		NETDDEBUG("%s: Drop ARP for unknown protocol %d\n",
+			  __FUNCTION__,
+			  ntohs(ah->ar_pro));
+		return;
+	}
+	req_len =3D arphdr_len2(ifp->if_addrlen, sizeof(struct =
in_addr));
+	if (m->m_len < req_len) {
+		m =3D m_pullup(m, req_len);
+		*mb =3D m;
+		if (m =3D=3D NULL) {
+			NETDDEBUG("%s: runt packet: m_pullup failed\n", =
__FUNCTION__);
+			return;
+		}
+	}
+	ah =3D mtod(m, struct arphdr *);
+
+	op =3D ntohs(ah->ar_op);
+	memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
+	memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
+	enaddr =3D (uint8_t *)IF_LLADDR(ifp);
+	myaddr =3D ndp->local;
+
+	if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) {
+		NETDDEBUG("%s: ignoring ARP from myself\n", =
__FUNCTION__);
+		return;
+	}
+
+	if (ndp->local.s_addr !=3D INADDR_ANY &&
+	    isaddr.s_addr =3D=3D ndp->local.s_addr) {
+		NETDDEBUG("%s: %*D is using my IP address %s!\n",
+		       __FUNCTION__,
+		       ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+		       inet_ntoa(isaddr));
+		return;
+	}
+
+	if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) {
+		NETDDEBUG("%s: ignoring ARP from broadcast address\n", =
__FUNCTION__);
+		return;
+	}
+
+	if (op =3D=3D ARPOP_REPLY) {
+		if (ndp->gw.s_addr =3D=3D INADDR_ANY && =
ndp->remote.s_addr =3D=3D INADDR_ANY) {
+			NETDDEBUG("%s:  Ignoring ARP because we do not =
have an address yet", __FUNCTION__);
+			return;
+		}
+		if (isaddr.s_addr !=3D (ndp->gw.s_addr =3D=3D INADDR_ANY =
? ndp->remote.s_addr : ndp->gw.s_addr)) {
+			inet_ntoa_r(isaddr, buf);
+			NETDDEBUG("%sp: ignoring ARP reply from %s (not =
desired remote)\n",
+				  __FUNCTION__, buf);
+			return;
+		}
+		memcpy(ndp->remote_mac.octet, ar_sha(ah),
+		       min(ah->ar_hln, ETHER_ADDR_LEN));
+		ndp->have_remote_mac =3D 1;
+		NETDDEBUG("\n%s: Got remote MAC address %6D\n", =
__FUNCTION__,
+			  ndp->remote_mac.octet, ":");
+		return;
+	}
+
+	if (op !=3D ARPOP_REQUEST) {
+		NETDDEBUG("%s: Ignoring ARP non-request/reply\n", =
__FUNCTION__);
+		return;
+	}
+
+	if (itaddr.s_addr !=3D ndp->local.s_addr) {
+		NETDDEBUG("%s: ignoring ARP not to our IP\n", =
__FUNCTION__);
+		return;
+	}
+
+	memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
+	memcpy(ar_sha(ah), enaddr, ah->ar_hln);
+	memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln);
+	memcpy(ar_spa(ah), &itaddr, ah->ar_pln);
+	ah->ar_op =3D htons(ARPOP_REPLY);
+	ah->ar_pro =3D htons(ETHERTYPE_IP);
+	m->m_flags &=3D ~(M_BCAST|M_MCAST);
+	m->m_len =3D sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
+	m->m_pkthdr.len =3D m->m_len;
+
+	memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
+	network_debug_ether_output(m, ifp, dst, ETHERTYPE_ARP);
+	*mb =3D NULL;
+}
+/*
+ * Builds and sends a single ARP request to locate the server
+ *
+ * Parameters:
+ *	void
+ *
+ * Return value:
+ *	0 on success
+ *	errno on error
+ */
+int
+network_debug_send_arp(struct network_debug *ndp)
+{
+	struct ether_addr bcast;
+	struct mbuf *m;
+	struct arphdr *ah;
+	int pktlen;
+
+	MPASS(ndp->ifp !=3D NULL);
+
+	/* Fill-up a broadcast address. */
+	memset(&bcast, 0xFF, ETHER_ADDR_LEN);
+	MGETHDR(m, M_NOWAIT, MT_DATA);
+	if (m =3D=3D NULL) {
+		NETDDEBUG("%s: Out of mbufs", __FUNCTION__);
+		return (ENOBUFS);
+	}
+	pktlen =3D arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
+	m->m_len =3D pktlen;
+	m->m_pkthdr.len =3D pktlen;
+	MH_ALIGN(m, pktlen);
+	ah =3D mtod(m, struct arphdr *);
+	ah->ar_hrd =3D htons(ARPHRD_ETHER);
+	ah->ar_pro =3D htons(ETHERTYPE_IP);
+	ah->ar_hln =3D ETHER_ADDR_LEN;
+	ah->ar_pln =3D sizeof(struct in_addr);
+	ah->ar_op =3D htons(ARPOP_REQUEST);
+	memcpy(ar_sha(ah), IF_LLADDR(ndp->ifp), ETHER_ADDR_LEN);
+	memcpy(ar_spa(ah), &ndp->local.s_addr, =
sizeof(ndp->local.s_addr));
+	bzero(ar_tha(ah), ETHER_ADDR_LEN);
+	memcpy(ar_tpa(ah), (ndp->gw.s_addr =3D=3D INADDR_ANY) ? =
&ndp->remote.s_addr : &ndp->gw.s_addr, sizeof(ndp->gw.s_addr));
+
+	return (network_debug_ether_output(m, ndp->ifp, bcast, =
ETHERTYPE_ARP));
+}
+/*
+ * After trapping, instead of assuming that most of the network stack =
is sane
+ * just poll the driver directly for packets.
+ *
+ * Parameters:
+ *	void
+ *
+ * Returns:
+ *	void
+ */
+void
+network_debug_poll(struct network_debug *ndp)
+{
+
+	MPASS(ndp->ifp !=3D NULL);
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+	if (panicstr !=3D NULL)
+		ndp->ifp->if_ndumpfuncs->ne_poll_unlocked(ndp->ifp,
+		    POLL_AND_CHECK_STATUS, 1000);
+	else
+#endif
+		ndp->ifp->if_ndumpfuncs->ne_poll_locked(ndp->ifp,
+		    POLL_AND_CHECK_STATUS, 1000);
+}
+
+/*
+ * Handler for incoming packets directly from the network adapter
+ * Identifies the packet type (IP or ARP) and passes it along to one of =
the
+ * helper functions.
+ *
+ * Note:  Each subsystem that wants to use this will need to set the
+ * interface if_input pointer to something that calls this.  E.g.
+ * static void packet_in(struct ifnet *ifp, struct mbuf *m) {
+ *	network_debug_pkt_in(&my_netdeb, m);
+ * }
+ * and
+ * ndp->ifp->if_input =3D packet_in;
+ *
+ * It needs to replicate partially the behaviour of ether_input() and
+ * ether_demux().
+ *
+ * Parameters:
+ *	ndp	The network_debug structure for our purposes.
+ *	m	an mbuf containing the packet received
+ *
+ * Return value:
+ *	void
+ */
+
+void
+network_debug_pkt_in(struct network_debug *ndp, struct mbuf *m)
+{
+	static const u_char no_mac[ETHER_ADDR_LEN] =3D { 0 };
+	static const u_char broadcast_mac[ETHER_ADDR_LEN] =3D { 0xff, =
0xff, 0xff, 0xff, 0xff, 0xff };
+
+	struct ether_header *eh, temp_eh;
+	u_short etype;
+	int send_through =3D 1;
+
+	NETDDEBUG("%s(%d):  Got an mbuf\n", __FUNCTION__, __LINE__);
+
+	/* Ethernet processing. */
+	if ((m->m_flags & M_PKTHDR) =3D=3D 0) {
+		NETDDEBUG_IF(ndp->ifp, "%s: Discard frame without packet =
header\n", __FUNCTION__);
+		goto done;
+	}
+	if (m->m_len < ETHER_HDR_LEN) {
+		NETDDEBUG_IF(ndp->ifp,
+			     "%s: Discard frame without leading eth =
header (len %u pktlen %u)\n",
+			     __FUNCTION__, m->m_len, m->m_pkthdr.len);
+		goto done;
+	}
+	if ((m->m_flags & M_HASFCS) !=3D 0) {
+		m_adj(m, -ETHER_CRC_LEN);
+		m->m_flags &=3D ~M_HASFCS;
+	}
+	eh =3D mtod(m, struct ether_header *);
+	temp_eh =3D *eh;	// Not a big structure, so should be =
safe to have on stack and copy
+	m->m_pkthdr.PH_loc.ptr =3D eh;
+	etype =3D ntohs(eh->ether_type);
+	if ((m->m_flags & M_VLANTAG) !=3D 0 || etype =3D=3D =
ETHERTYPE_VLAN) {
+		NETDDEBUG_IF(ndp->ifp, "netdump_pkt_in: Ignoring vlan =
packets\n");
+		goto done;
+	}
+
+	/*
+	 * XXX: Probably must also check if we're the recipient MAC =
address.
+	 * But we need to also check for broadcast, and whether or not =
we have
+	 * a MAC address set.
+	 */
+
+	if (bcmp(&ndp->local_mac, no_mac, sizeof(no_mac)) !=3D 0 &&
+	    bcmp(&eh->ether_dhost, broadcast_mac, sizeof(broadcast_mac)) =
!=3D 0 &&
+	    bcmp(&ndp->local_mac, &eh->ether_dhost, =
sizeof(ndp->local_mac)) !=3D 0) {
+		NETDDEBUG("%s: Ethernet packet for destination %6D is =
not for me (%6D)\n",
+			  __FUNCTION__,
+			  eh->ether_dhost, ":",
+			  ndp->local_mac.octet, ":");
+		goto done;
+	}
+
+	/* Done ethernet processing. Strip off the ethernet header. */
+	m_adj(m, ETHER_HDR_LEN);
+	switch (etype) {
+	case ETHERTYPE_ARP:
+		NETDDEBUG("%s(%d):  ETHERTYPE_ARP\n", __FUNCTION__, =
__LINE__);
+		network_debug_handle_arp(ndp, &m);
+		break;
+	case ETHERTYPE_IP:
+		NETDDEBUG("%s(%d:  ETHERTYPE_IP\n", __FUNCTION__, =
__LINE__);
+		if (ndp->wanted) {
+			send_through =3D (ndp->wanted)(ndp, m);
+			if (send_through) {
+				/*
+				 * If this packet is wanted, then we =
stash
+				 * the source information into ndp
+				 */
+				NETDDEBUG("%s(%d):  Setting =
ndp->remote_mac to %6D\n", __FUNCTION__, __LINE__, eh->ether_shost, =
":");
+				bcopy(&eh->ether_shost, =
&ndp->remote_mac, sizeof(ndp->remote_mac));
+				ndp->remote =3D (mtod(m, struct =
udpiphdr*))->ui_src;
+				NETDDEBUG("%s(%d):  Set ndp->remote to =
%#x\n", __FUNCTION__, __LINE__, ndp->remote.s_addr);
+			}
+		}
+		if (send_through && ndp->ip_handler)
+			(*ndp->ip_handler)(ndp, &m);
+		NETDDEBUG("%s(%d):  Done with ip_handler\n", =
__FUNCTION__, __LINE__);
+		break;
+	default:
+		NETDDEBUG_IF(ndp->ifp,
+			     "%s: Dropping unknown ethertype %hu\n",
+			     __FUNCTION__,
+			     etype);
+		break;
+	}
+done:
+	if (m !=3D NULL)
+		m_freem(m);
+	NETDDEBUG("%s(%d):  Done here\n", __FUNCTION__, __LINE__);
+}
+
+int
+network_debug_prepare(struct network_debug *ndp)
+{
+	int must_lock =3D 1;
+	int error =3D 0;
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+	if (panicstr !=3D NULL)
+		must_lock =3D 0;
+#endif
+	/* Lookup the right if device to be used */
+	if (must_lock !=3D 0)
+		IFNET_RLOCK_NOSLEEP();
+
+	if (ndp->ifp =3D=3D NULL) {
+		struct ifnet *ifp =3D NULL;
+		/*
+		 * Look for an interface.  If one was specified via =
sysctl
+		 * or tunable, look for that name; otherwise, pick the =
first
+		 * one that claims to support netdump.
+		 */
+		TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+			if ((ndp->if_name[0] =3D=3D 0 ||
+			     !strcmp(ifp->if_xname, ndp->if_name)) &&
+			    network_debug_supported_nic(ifp)) {
+				ndp->ifp =3D ifp;
+				break;
+			}
+		}
+		if (must_lock !=3D 0)
+			IFNET_RUNLOCK_NOSLEEP();
+		if (ndp->ifp =3D=3D NULL) {
+			NETDDEBUG("%s: No valid NIC found\n", =
__FUNCTION__);
+			return (EINVAL);
+		}
+	}
+
+	MPASS(ndp->ifp !=3D NULL);
+=09
+	bcopy((void*)IF_LLADDR(ndp->ifp), (void*)&ndp->local_mac, =
sizeof(ndp->local_mac));
+
+	if (ndp->local.s_addr =3D=3D INADDR_ANY) {
+		/*
+		 * If we don't have an address yet, let's try to
+		 * find it.
+		 */
+		struct ifreq req;
+		struct sockaddr_in *sin;
+		if (ndp->ifp->if_addr =3D=3D NULL ||
+		    ndp->ifp->if_addr->ifa_addr =3D=3D NULL) {
+			NETDDEBUG("%s: Can't netdump; no local IP given, =
and can't figure it out\n", __FUNCTION__);
+			return (EINVAL);
+		}
+		bzero(&req, sizeof(req));
+		if ((error =3D in_control(NULL, SIOCGIFADDR, =
(caddr_t)&req, ndp->ifp, NULL)) !=3D 0) {
+			NETDDEBUG("%s:  Can't ask interface for IP =
address to do error %d\n", __FUNCTION__, error);
+			return (error);
+		}
+		sin =3D (struct sockaddr_in*)&req.ifr_addr;
+		if (sin->sin_family !=3D AF_INET) {
+			NETDDEBUG("%s:  Address family for interface =
%s%u is %d, not AF_INET (%d)\n", __FUNCTION__, ndp->ifp->if_dname, =
ndp->ifp->if_dunit, sin->sin_family, AF_INET);
+			return (EINVAL);
+		}
+		if (sin->sin_addr.s_addr =3D=3D INADDR_ANY) {
+			NETDDEBUG("%s:  Address for interface is =
INADDR_ANY\n", __FUNCTION__);
+			return (EINVAL);
+		}
+		ndp->local.s_addr =3D sin->sin_addr.s_addr;
+		NETDDEBUG("%s(%d):  ndp->local.s_addr =3D %#x\n", =
__FUNCTION__, __LINE__, ndp->local.s_addr);
+	}
+
+	if ((ndp->ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && =
must_lock !=3D 0)
+		ndp->ifp->if_ndumpfuncs->ne_disable_intr(ndp->ifp);
+
+	return 0;
+}
+
+void
+network_debug_finish(struct network_debug *ndp)
+{
+	int must_lock =3D 1;
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+	if (panicstr !=3D NULL)
+		must_lock =3D 0;
+#endif
+
+	if (ndp->local.s_addr !=3D INADDR_ANY) {
+		if ((ndp->ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && =
must_lock !=3D 0)
+			=
ndp->ifp->if_ndumpfuncs->ne_enable_intr(ndp->ifp);
+	}
+	return;
+}
diff --git a/sys/netinet/network_debug.h b/sys/netinet/network_debug.h
new file mode 100644
index 0000000..af7c7b7
--- /dev/null
+++ b/sys/netinet/network_debug.h
@@ -0,0 +1,89 @@
+#ifndef _NETINET_NETWORK_DEBUG_H
+# define _NETINET_NETWORK_DEBUG_H
+
+# include <sys/types.h>
+
+struct network_debug {
+	int		poll_time;		// Time to poll the nic =
(0.5ms for each poll)
+	int		retries;		// Number of times to =
try retransmitting un-acked packets
+	int		have_remote_mac;	// Indicates whether we =
got the gw / remote mac yet (via ARP)
+	char		if_name[IFNAMSIZ];	// The interface name to =
use
+	struct ifnet	*ifp;			// Interface to use
+	struct in_addr	gw;			// Address of gateway, =
if any
+	struct ether_addr	remote_mac;	// MAC address to which =
to send packets (may be remote, may be router)
+	struct in_addr	remote;			// IP address for remote =
end
+	struct ether_addr	local_mac;	// Our MAC address
+	struct in_addr	local;			// Our IP address!
+
+	/*
+	 * If non-NULL, this function will be called on the receipt of
+	 * a packet to determine if it is a desired packet.  The mbuf*
+	 * will be the start of the IP packet -- the ethernet header
+	 * will have been stripped off by this point.  The function may
+	 * want to check the source address/port and/or the destination
+	 * port.  (If the destination address or MAC don't match the
+	 * ones in this structure, it won't get that far.)  The caller
+	 * should not alter the mbuf in any way.
+	 *
+	 * If the function returns non-zero, then remote_mac and
+	 * remote will be set appropriately based on the packet.
+	 *
+	 * This function is called from network_debug_pkt_in, and only
+	 * for IP packets.
+	 *
+	 * Saying the packet is wanted doesn't guarantee it'll be =
processed
+	 * by the ip_handler.  But if a wanted function is set, it =
should
+	 * make an effort to indicate whether it really wants the =
packet.
+	 */
+	int		(*wanted)(struct network_debug *, struct mbuf =
*);
+
+	/*
+	 * If non-NULL, this function will be called to process any IP =
packets.
+	 * Currently, only UDP is supported.  The mbuf** will refer to =
the
+	 * UDP packet.
+	 *
+	 * The caller is allowed to manipulate the mbuf.
+	 */
+	void		(*ip_handler)(struct network_debug *, struct =
mbuf **);
+};
+
+/*
+ * Checks for netdump support on a network interface
+ *
+ * Parameters:
+ *	ifp	The network interface that is being tested for support
+ *
+ * Returns:
+ *	int	1 if the interface is supported, 0 if not
+ */
+static __inline int
+network_debug_supported_nic(struct ifnet *ifp)
+{
+
+	return (ifp->if_ndumpfuncs !=3D NULL);
+}
+
+int network_debug_ether_output(struct mbuf *, struct ifnet *, struct =
ether_addr dst, u_short etype);
+
+int network_debug_mbuf_nop(struct mbuf *mp, void *, void *);
+
+void network_debug_handle_arp(struct network_debug *ndp, struct mbuf =
**);
+
+int network_debug_send_arp(struct network_debug *ndp);
+
+void network_debug_poll(struct network_debug *ndp);
+
+void network_debug_pkt_in(struct network_debug *, struct mbuf *);
+
+/*
+ * Call to initialize the network_debug structure -- it will look for =
an interface,
+ * IP address, etc.
+ */
+int network_debug_prepare(struct network_debug *ndp);
+
+/*
+ * Call when done.  It does less than the previous function.
+ */
+void network_debug_finish(struct network_debug *ndp);
+
+#endif /* _NETINET_NETWORK_DEBUG_H */
diff --git a/usr.sbin/netdumpsrv/netdumpsrv.c =
b/usr.sbin/netdumpsrv/netdumpsrv.c
index af6ac6d..283a003 100644
--- a/usr.sbin/netdumpsrv/netdumpsrv.c
+++ b/usr.sbin/netdumpsrv/netdumpsrv.c
@@ -454,7 +454,7 @@ handle_kdh(struct netdump_client *client, struct =
netdump_msg *msg)
 		return;
=20
 	client->any_data_rcvd =3D 1;
-	h =3D (struct kerneldumpheader *)msg->nm_data;
+	h =3D (void*)msg->nm_data;
 	if (msg->nm_hdr.mh_len < sizeof(struct kerneldumpheader)) {
 		LOGERR("Bad KDH from %s [%s]: packet too small\n",
 		    client->hostname, client_ntoa(client));

--Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?16E0C844-B302-4B61-A250-CA12CABDE476>