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
[-- Attachment #1 --]
No, that icon is not attach, it's send. Grrr.
Attached diffs that I promised and didn't attach before hitting the wrong button.
[-- Attachment #2 --]
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="NO" # CAM Target Layer / iSCSI target daemon.
local_unbound_enable="NO" # local caching resolver
#
+# netdumpsrv configuration.
+#
+netdumpsrv_enable="NO" # Run netdumpsrv.
+netdumpsrv_program="/usr/sbin/netdumpsrv" # Path to netdumpsrv.
+netdumpsrv_pidfile="/var/run/netdumpsrv.pid" # Path to pidfile for netdumpsrv.
+#netdumpsrv_flags="" # Use this for flags -a, -D, -d, -i
+
+#
# kerberos. Do not run the admin daemons on slave servers
#
kerberos5_server_enable="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="netdumpsrv"
+rcvar=`set_rcvar`
+
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+
+load_rc_config $name
+
+run_rc_command "$1"
+#!/bin/sh
+#
+# PROVIDE: netdumpsrv
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="netdumpsrv"
+rcvar=`set_rcvar`
+
+command="/usr/sbin/${name}"
+pidfile="/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 += PAGE_SIZE;
- /* Determine dump offset on device. */
- if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
- error = E2BIG;
- goto fail;
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+
+ /* Determine dump offset on device. */
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = E2BIG;
+ goto fail;
+ }
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
}
- dumplo = di->mediaoffset + di->mediasize - dumpsize;
- dumplo -= sizeof(kdh) * 2;
progress = dumpsize;
/* 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
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 += fileofs;
hdrgap = fileofs - DEV_ALIGN(hdrsz);
- /* Determine dump offset on device. */
- if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
- error = ENOSPC;
- goto fail;
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+
+ /* Determine dump offset on device. */
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = ENOSPC;
+ goto fail;
+ }
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
}
- dumplo = di->mediaoffset + di->mediasize - dumpsize;
- dumplo -= sizeof(kdh) * 2;
mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize, di->blocksize);
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)
dumpsize += PAGE_SIZE;
- /* Determine dump offset on device. */
- if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
- error = ENOSPC;
- goto fail;
- }
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+
+ /* Determine dump offset on device. */
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = ENOSPC;
+ goto fail;
+ }
- dumplo = di->mediaoffset + di->mediasize - dumpsize;
- dumplo -= sizeof(kdh) * 2;
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
+ }
progress = dumpsize;
/* 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
+# 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>
@@ -87,6 +90,27 @@
#include "e1000_82571.h"
#include "if_em.h"
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define EM_CORE_LOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_CORE_LOCK(adapter); \
+} while (0)
+#define EM_CORE_UNLOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_CORE_UNLOCK(adapter); \
+} while (0)
+#define EM_TX_LOCK_COND(txr, locking) do { \
+ if ((locking) != 0) \
+ EM_TX_LOCK(txr); \
+} while (0)
+#define EM_TX_UNLOCK_COND(txr, locking) do { \
+ if ((locking) != 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);
static __inline void em_rx_discard(struct rx_ring *, int);
-#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
/*********************************************************************
* FreeBSD Device Interface Entry Points
*********************************************************************/
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods em_ndump_methods = {
+ .ne_poll_locked = em_poll,
+ .ne_poll_unlocked = em_poll_unlocked,
+ .ne_disable_intr = em_ndump_disable_intr,
+ .ne_enable_intr = em_ndump_enable_intr
+};
+
+#endif
+
static device_method_t em_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, em_probe),
@@ -1416,14 +1458,14 @@ em_init(void *arg)
}
-#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 = ifp->if_softc;
struct tx_ring *txr = adapter->tx_rings;
@@ -1431,9 +1473,9 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
u32 reg_icr;
int rx_done;
- EM_CORE_LOCK(adapter);
+ EM_CORE_LOCK_COND(adapter, locking);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
- EM_CORE_UNLOCK(adapter);
+ EM_CORE_UNLOCK_COND(adapter, locking);
return (0);
}
@@ -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);
em_rxeof(rxr, count, &rx_done);
- 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);
return (rx_done);
}
-#endif /* DEVICE_POLLING */
+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 = 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 = ifp->if_softc;
+ EM_CORE_LOCK(adapter);
+ em_enable_intr(adapter);
+ EM_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
/*********************************************************************
*
@@ -2989,6 +3068,9 @@ em_setup_interface(device_t dev, struct adapter *adapter)
ifp->if_qflush = em_qflush;
#else
ifp->if_start = em_start;
+#ifdef NETDUMP_CLIENT
+ ifp->if_ndumpfuncs = &em_ndump_methods;
+#endif
IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1);
ifp->if_snd.ifq_drv_maxlen = 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
#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"
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define IGB_CORE_LOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ IGB_CORE_LOCK(adapter); \
+} while (0)
+#define IGB_CORE_UNLOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ IGB_CORE_UNLOCK(adapter); \
+} while (0)
+#define IGB_RX_LOCK_COND(rxr, locking) do { \
+ if ((locking) != 0) \
+ IGB_RX_LOCK(rxr); \
+} while (0)
+#define IGB_RX_UNLOCK_COND(rxr, locking) do { \
+ if ((locking) != 0) \
+ IGB_RX_UNLOCK(rxr); \
+} while (0)
+#define IGB_TX_LOCK_COND(txr, locking) do { \
+ if ((locking) != 0) \
+ IGB_TX_LOCK(txr); \
+} while (0)
+#define IGB_TX_UNLOCK_COND(txr, locking) do { \
+ if ((locking) != 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);
-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);
-#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
/*********************************************************************
* FreeBSD Device Interface Entry Points
*********************************************************************/
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods igb_ndump_methods = {
+ .ne_poll_locked = igb_poll,
+ .ne_poll_unlocked = igb_poll_unlocked,
+ .ne_disable_intr = igb_ndump_disable_intr,
+ .ne_enable_intr = igb_ndump_enable_intr
+};
+
+#endif
+
static device_method_t igb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, igb_probe),
@@ -1524,7 +1576,7 @@ igb_irq_fast(void *arg)
return FILTER_HANDLED;
}
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
#if __FreeBSD_version >= 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 = ifp->if_softc;
struct igb_queue *que;
@@ -1541,9 +1593,9 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
u32 loop = IGB_MAX_LOOP;
bool more;
- IGB_CORE_LOCK(adapter);
+ IGB_CORE_LOCK_COND(adapter, locking);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
- IGB_CORE_UNLOCK(adapter);
+ IGB_CORE_UNLOCK_COND(adapter, locking);
return POLL_RETURN_COUNT(rx_done);
}
@@ -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);
for (int i = 0; i < adapter->num_queues; i++) {
que = &adapter->queues[i];
txr = que->txr;
- igb_rxeof(que, count, &rx_done);
+ _igb_rxeof_generic(que, count, &rx_done, locking);
- IGB_TX_LOCK(txr);
+ IGB_TX_LOCK_COND(txr, locking);
do {
more = 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);
}
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 = 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 = ifp->if_softc;
+ IGB_CORE_LOCK(adapter);
+ igb_enable_intr(adapter);
+ IGB_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
/*********************************************************************
*
@@ -3123,6 +3213,10 @@ igb_setup_interface(device_t dev, struct adapter *adapter)
IFQ_SET_READY(&ifp->if_snd);
#endif
+#ifdef NETDUMP_CLIENT
+ ifp->if_ndumpfuncs = &igb_ndump_methods;
+#endif
+
ether_ifattach(ifp, adapter->hw.mac.addr);
ifp->if_capabilities = ifp->if_capenable = 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 = que->adapter;
struct rx_ring *rxr = que->rxr;
@@ -4822,7 +4916,7 @@ igb_rxeof(struct igb_queue *que, int count, int *done)
u32 ptype, staterr = 0;
union e1000_adv_rx_desc *cur;
- 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 != NULL)
*done += rxdone;
- IGB_RX_UNLOCK(rxr);
+ IGB_RX_UNLOCK_COND(rxr, locking);
return ((staterr & E1000_RXD_STAT_DD) ? TRUE : FALSE);
}
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 @@
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_netdump.h"
#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>
@@ -83,6 +85,35 @@
#include "e1000_api.h"
#include "if_lem.h"
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define EM_CORE_LOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_CORE_LOCK(adapter); \
+} while (0)
+#define EM_CORE_UNLOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_CORE_UNLOCK(adapter); \
+} while (0)
+#define EM_RX_LOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_RX_LOCK(adapter); \
+} while (0)
+#define EM_RX_UNLOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_RX_UNLOCK(adapter); \
+} while (0)
+#define EM_TX_LOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ EM_TX_LOCK(adapter); \
+} while (0)
+#define EM_TX_UNLOCK_COND(adapter, locking) do { \
+ if ((locking) != 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);
-#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
/*********************************************************************
* FreeBSD Device Interface Entry Points
*********************************************************************/
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods lem_ndump_methods = {
+ .ne_poll_locked = lem_poll,
+ .ne_poll_unlocked = lem_poll_unlocked,
+ .ne_disable_intr = lem_ndump_disable_intr,
+ .ne_enable_intr = lem_ndump_enable_intr
+};
+
+#endif
+
static device_method_t lem_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, lem_probe),
@@ -1225,21 +1275,21 @@ lem_init(void *arg)
}
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
/*********************************************************************
*
* Legacy polling routine
*
*********************************************************************/
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 = ifp->if_softc;
u32 reg_icr, rx_done = 0;
- EM_CORE_LOCK(adapter);
+ EM_CORE_LOCK_COND(adapter, locking);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
- EM_CORE_UNLOCK(adapter);
+ EM_CORE_UNLOCK_COND(adapter, locking);
return (rx_done);
}
@@ -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);
- lem_rxeof(adapter, count, &rx_done);
+ _lem_rxeof_generic(adapter, count, &rx_done, locking);
- 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 = 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 = ifp->if_softc;
+ EM_CORE_LOCK(adapter);
+ lem_enable_intr(adapter);
+ EM_CORE_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
/*********************************************************************
*
@@ -2363,6 +2451,9 @@ lem_setup_interface(device_t dev, struct adapter *adapter)
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = lem_ioctl;
ifp->if_start = lem_start;
+#ifdef NETDUMP_CLIENT
+ ifp->if_ndumpfuncs = &lem_ndump_methods;
+#endif
IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1);
ifp->if_snd.ifq_drv_maxlen = 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 = adapter->ifp;
struct mbuf *mp;
@@ -3447,7 +3538,7 @@ lem_rxeof(struct adapter *adapter, int count, int *done)
int i, rx_sent = 0;
struct e1000_rx_desc *current_desc;
- EM_RX_LOCK(adapter);
+ EM_RX_LOCK_COND(adapter, locking);
i = adapter->next_rx_desc_to_check;
current_desc = &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 != NULL)
*done = rx_sent;
- EM_RX_UNLOCK(adapter);
+ EM_RX_UNLOCK_COND(adapter, locking);
return (FALSE);
}
@@ -3601,9 +3692,9 @@ discard:
/* Call into the stack */
if (m != NULL) {
adapter->next_rx_desc_to_check = 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 = adapter->next_rx_desc_to_check;
}
@@ -3617,7 +3708,7 @@ discard:
E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), i);
if (done != NULL)
*done = rx_sent;
- EM_RX_UNLOCK(adapter);
+ EM_RX_UNLOCK_COND(adapter, locking);
return ((status & E1000_RXD_STAT_DD) ? TRUE : FALSE);
}
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.
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_device_polling.h"
+#include "opt_netdump.h"
#endif
#include <dev/ixgb/if_ixgb.h>
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define IXGB_LOCK_COND(adapter, locking) do { \
+ if ((locking) != 0) \
+ IXGB_LOCK(adapter); \
+} while (0)
+#define IXGB_UNLOCK_COND(adapter, locking) do { \
+ if ((locking) != 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
/*********************************************************************
* FreeBSD Device Interface Entry Points
*********************************************************************/
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods ixgb_ndump_methods = {
+ .ne_poll_locked = ixgb_poll,
+ .ne_poll_unlocked = ixgb_poll_unlocked,
+ .ne_disable_intr = ixgb_ndump_disable_intr,
+ .ne_enable_intr = ixgb_ndump_enable_intr
+};
+
+#endif
+
static device_method_t ixgb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ixgb_probe),
@@ -751,7 +783,7 @@ ixgb_init(void *arg)
return;
}
-#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)
}
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 = ifp->if_softc;
int rx_npkts = 0;
- IXGB_LOCK(adapter);
+ IXGB_LOCK_COND(adapter, locking);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
rx_npkts = 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 = 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 = ifp->if_softc;
+ IXGB_LOCK(adapter);
+ ixgb_enable_intr(adapter);
+ IXGB_UNLOCK(adapter);
+}
+#endif /* !NETDUMP_CLIENT */
/*********************************************************************
*
@@ -1356,6 +1427,9 @@ ixgb_setup_interface(device_t dev, struct adapter * adapter)
ifp->if_ioctl = ixgb_ioctl;
ifp->if_start = ixgb_start;
ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 1;
+#ifdef NETDUMP_CLIENT
+ ifp->if_ndumpfuncs = &ixgb_ndump_methods;
+#endif
#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>
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 @@
#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"
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
+
+#define IXGBE_RX_LOCK_COND(rxr, locking) do { \
+ if ((locking) != 0) \
+ IXGBE_RX_LOCK(rxr); \
+} while (0)
+#define IXGBE_RX_UNLOCK_COND(rxr, locking) do { \
+ if ((locking) != 0) \
+ IXGBE_RX_UNLOCK(rxr); \
+} while (0)
+#define IXGBE_TX_LOCK_COND(txr, locking) do { \
+ if ((locking) != 0) \
+ IXGBE_TX_LOCK(txr); \
+} while (0)
+#define IXGBE_TX_UNLOCK_COND(txr, locking) do { \
+ if ((locking) != 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);
+#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
*********************************************************************/
+#ifdef NETDUMP_CLIENT
+
+static struct netdump_methods ixgbe_ndump_methods = {
+ .ne_poll_locked = ixgbe_poll,
+ .ne_poll_unlocked = ixgbe_poll_unlocked,
+ .ne_disable_intr = ixgbe_ndump_disable_intr,
+ .ne_enable_intr = ixgbe_ndump_enable_intr
+};
+
+#endif
+
static device_method_t ixgbe_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ixgbe_probe),
@@ -667,6 +716,11 @@ ixgbe_detach(device_t dev)
return (EBUSY);
}
+#ifdef DEVICE_POLLING
+ if ((adapter->ifp->if_capenable & IFCAP_POLLING) != 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 = ifr->ifr_reqcap ^ ifp->if_capenable;
IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)");
+#ifdef DEVICE_POLLING
+ if ((mask & IFCAP_POLLING) != 0) {
+ if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) {
+ error = ether_poll_register(ixgbe_poll, ifp);
+ if (error != 0)
+ return (error);
+ IXGBE_CORE_LOCK(adapter);
+ ixgbe_disable_intr(adapter);
+ ifp->if_capenable |= IFCAP_POLLING;
+ IXGBE_CORE_UNLOCK(adapter);
+ } else {
+ error = ether_poll_deregister(ifp);
+ IXGBE_CORE_LOCK(adapter);
+ ixgbe_enable_intr(adapter);
+ ifp->if_capenable &= ~IFCAP_POLLING;
+ IXGBE_CORE_UNLOCK(adapter);
+ }
+ }
+#endif /* !DEVICE_POLLING */
if (mask & IFCAP_HWCSUM)
ifp->if_capenable ^= 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);
- /* 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) != 0)
+ ixgbe_disable_intr(adapter);
+ else
+#endif
+ ixgbe_enable_intr(adapter);
/* Now inform the stack we're ready */
ifp->if_drv_flags |= IFF_DRV_RUNNING;
@@ -1437,6 +1515,10 @@ ixgbe_handle_que(void *context, int pending)
return;
}
+#ifdef DEVICE_POLLING
+ if ((adapter->ifp->if_capenable & IFCAP_POLLING) != 0)
+ return;
+#endif
/*********************************************************************
*
@@ -2646,6 +2728,9 @@ ixgbe_setup_interface(device_t dev, struct adapter *adapter)
ifp->if_snd.ifq_drv_maxlen = adapter->num_tx_desc - 2;
IFQ_SET_READY(&ifp->if_snd);
#endif
+#ifdef NETDUMP_CLIENT
+ ifp->if_ndumpfuncs = &ixgbe_ndump_methods;
+#endif
ether_ifattach(ifp, adapter->hw.mac.addr);
@@ -2665,6 +2750,9 @@ ixgbe_setup_interface(device_t dev, struct adapter *adapter)
| IFCAP_VLAN_MTU
| IFCAP_HWSTATS;
ifp->if_capenable = ifp->if_capabilities;
+#ifdef DEVICE_POLLING
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
/*
** 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 */
+#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 = ifp->if_softc;
+ txr = adapter->tx_rings;
+ que = adapter->queues;
+ hw = &adapter->hw;
+ loop = MAX_LOOP;
+ rx_npkts = 0;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return (rx_npkts);
+
+ if (cmd == POLL_AND_CHECK_STATUS) {
+ reg_eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
+
+ /* Link status change */
+ if ((reg_eicr & IXGBE_EICR_LSC) != 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 = ixgbe_txeof(txr);
+ } while (loop-- && more_tx);
+#if __FreeBSD_version >= 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 = 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 = 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 = 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 */
if (txr->tx_avail == txr->num_desc) {
txr->queue_status = IXGBE_QUEUE_IDLE;
- return;
+ return FALSE;
}
/* Get work starting point */
@@ -3735,7 +3908,7 @@ ixgbe_txeof(struct tx_ring *txr)
if (txr->tx_avail == txr->num_desc)
txr->queue_status = IXGBE_QUEUE_IDLE;
- return;
+ return TRUE;
}
/*********************************************************************
@@ -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 = que->adapter;
struct rx_ring *rxr = que->rxr;
struct ifnet *ifp = adapter->ifp;
struct lro_ctrl *lro = &rxr->lro;
struct lro_entry *queued;
- int i, nextp, processed = 0;
+ int i, nextp, processed = 0, rx_npkts = 0;
u32 staterr = 0;
- u16 count = rxr->process_limit;
union ixgbe_adv_rx_desc *cur;
struct ixgbe_rx_buf *rbuf, *nbuf;
- IXGBE_RX_LOCK(rxr);
+ IXGBE_RX_LOCK_COND(rxr, locking);
#ifdef DEV_NETMAP
/* Same as the txeof routine: wakeup clients on intr. */
@@ -4588,6 +4761,7 @@ next_desc:
if (sendmp != NULL) {
rxr->next_to_check = i;
ixgbe_rx_input(rxr, ifp, sendmp, ptype);
+ rx_npkts++;
i = rxr->next_to_check;
}
@@ -4612,15 +4786,18 @@ next_desc:
tcp_lro_flush(lro, queued);
}
- IXGBE_RX_UNLOCK(rxr);
+ IXGBE_RX_UNLOCK_COND(rxr, locking);
+ if (rx_npktsp != NULL)
+ *rx_npktsp = rx_npkts;
/*
** Still have cleaning to do?
*/
- if ((staterr & IXGBE_RXD_STAT_DD) != 0)
+ if ((staterr & IXGBE_RXD_STAT_DD) != 0) {
return (TRUE);
- else
+ } else {
return (FALSE);
+ }
}
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 += PAGE_SIZE;
- /* Determine dump offset on device. */
- if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
- error = ENOSPC;
- goto fail;
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+
+ /* Determine dump offset on device. */
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = ENOSPC;
+ goto fail;
+ }
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
}
- dumplo = di->mediaoffset + di->mediasize - dumpsize;
- dumplo -= sizeof(kdh) * 2;
progress = dumpsize;
/* 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 += fileofs;
hdrgap = fileofs - DEV_ALIGN(hdrsz);
- /* Determine dump offset on device. */
- if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
- error = ENOSPC;
- goto fail;
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+
+ /* Determine dump offset on device. */
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = ENOSPC;
+ goto fail;
+ }
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
}
- dumplo = di->mediaoffset + di->mediasize - dumpsize;
- dumplo -= sizeof(kdh) * 2;
mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize);
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$");
#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>
+#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>
#ifndef PANIC_REBOOT_WAIT_TIME
@@ -754,6 +764,17 @@ vpanic(const char *fmt, va_list ap)
kern_reboot(bootopt);
}
+#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)
{
- if (length != 0 && (offset < di->mediaoffset ||
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) != 0 && length != 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
#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))
-#ifdef DEVICE_POLLING
+#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT)
enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS };
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 */
#endif /* _KERNEL */
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,
+ 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 = 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 = 10000;
+
+/* Times to retransmit lost packets. */
+static int nd_retries = 10;
+
+/* General dynamic settings. */
+static char nd_ifp_str[IFNAMSIZ];
+static struct ether_addr nd_gw_mac;
+static struct in_addr nd_server = {INADDR_ANY};
+static struct in_addr nd_client = {INADDR_ANY};
+static struct in_addr nd_gw = {INADDR_ANY};
+struct ifnet *nd_ifp;
+int nd_enable = 0;
+static uint16_t nd_server_port = 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 != 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 = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ found = 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 = 1;
+ break;
+ }
+ }
+ IFNET_RUNLOCK_NOSLEEP();
+ if (found == 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 = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error == 0) {
+ if (!inet_aton(buf, &addr))
+ error = EINVAL;
+ else
+ *(struct in_addr *)arg1 = 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 == NULL) {
+ printf("netdump_ether_output: Out of mbufs\n");
+ return (ENOBUFS);
+ }
+ eh = 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 = htons(etype);
+
+ if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) != 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 != NULL);
+
+ M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
+ if (m == NULL) {
+ printf("netdump_udp_output: Out of mbufs\n");
+ return (ENOBUFS);
+ }
+ ui = mtod(m, struct udpiphdr *);
+ bzero(ui->ui_x1, sizeof(ui->ui_x1));
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip));
+ ui->ui_ulen = ui->ui_len;
+ ui->ui_src = nd_client;
+ ui->ui_dst = nd_server;
+ /* Use this src port so that the server can connect() the socket */
+ ui->ui_sport = htons(NETDUMP_ACKPORT);
+ ui->ui_dport = htons(nd_server_port);
+ ui->ui_sum = 0;
+ if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0)
+ ui->ui_sum = 0xffff;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0;
+ ip->ip_len = htons(m->m_pkthdr.len);
+ ip->ip_id = 0;
+ ip->ip_off = htons(IP_DF);
+ ip->ip_ttl = 32;
+ ip->ip_sum = 0;
+ ip->ip_sum = 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 != NULL);
+
+ /* Fill-up a broadcast address. */
+ memset(&bcast, 0xFF, ETHER_ADDR_LEN);
+ MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("netdump_send_arp: Out of mbufs");
+ return (ENOBUFS);
+ }
+ pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
+ m->m_len = pktlen;
+ m->m_pkthdr.len = pktlen;
+ MH_ALIGN(m, pktlen);
+ ah = mtod(m, struct arphdr *);
+ ah->ar_hrd = htons(ARPHRD_ETHER);
+ ah->ar_pro = htons(ETHERTYPE_IP);
+ ah->ar_hln = ETHER_ADDR_LEN;
+ ah->ar_pln = sizeof(struct in_addr);
+ ah->ar_op = 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 = 0; retries < nd_retries && have_server_mac == 0;
+ retries++) {
+ err = netdump_send_arp();
+ if (err != 0)
+ return (err);
+ for (polls = 0; polls < nd_polls && have_server_mac == 0;
+ polls++) {
+ netdump_network_poll();
+ DELAY(500);
+ }
+ if (have_server_mac == 0)
+ printf("(ARP retry)");
+ }
+ if (have_server_mac != 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 = 0;
+ rcvd_acks = 0;
+ retries = 0;
+
+ MPASS(nd_ifp != NULL);
+
+retransmit:
+
+ /* Chunks can be too big to fit in packets. */
+ for (i = sent_so_far = 0; sent_so_far <
+ datalen || (i == 0 && datalen == 0); i++) {
+ pktlen = datalen - sent_so_far;
+
+ /* First bound: the packet structure. */
+ pktlen = min(pktlen, NETDUMP_DATASIZE);
+
+ /* Second bound: the interface MTU (assume no IP options). */
+ pktlen = 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)) != 0) {
+ sent_so_far += pktlen;
+ continue;
+ }
+
+ /*
+ * Get and fill a header mbuf, then chain data as an extended
+ * mbuf.
+ */
+ MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("netdump_send: Out of mbufs!\n");
+ return (ENOBUFS);
+ }
+ m->m_len = sizeof(struct netdump_msg_hdr);
+ m->m_pkthdr.len = sizeof(struct netdump_msg_hdr);
+ MH_ALIGN(m, sizeof(struct netdump_msg_hdr));
+ nd_msg_hdr = mtod(m, struct netdump_msg_hdr *);
+ nd_msg_hdr->mh_seqno = htonl(nd_seqno+i);
+ nd_msg_hdr->mh_type = htonl(type);
+ nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far);
+ nd_msg_hdr->mh_len = htonl(pktlen);
+ nd_msg_hdr->mh__pad = 0;
+
+ if (pktlen != 0) {
+ m2 = m_get(M_NOWAIT, MT_DATA);
+ if (m2 == 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 = pktlen;
+ m->m_next = m2;
+ m->m_pkthdr.len += m2->m_len;
+ }
+ error = netdump_udp_output(m);
+ if (error != 0)
+ return (error);
+
+ /* Note that we're waiting for this packet in the bitfield. */
+ want_acks |= 1 << i;
+ sent_so_far += pktlen;
+ }
+ if (i >= 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 = 0;
+ while (rcvd_acks != want_acks) {
+ if (polls++ > nd_polls) {
+ if (retries++ > nd_retries)
+ return (ETIMEDOUT);
+ printf(". ");
+ goto retransmit;
+ }
+ netdump_network_poll();
+ DELAY(500);
+ }
+ nd_seqno += 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 = *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 = m_pullup(m, sizeof(struct ip));
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+ return;
+ }
+ }
+ ip = mtod(m, struct ip *);
+
+ /* IP version. */
+ if (ip->ip_v != IPVERSION) {
+ NETDDEBUG("nd_handle_ip: Bad IP version %d\n", ip->ip_v);
+ return;
+ }
+
+ /* Header length. */
+ hlen = 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 = m_pullup(m, hlen);
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ }
+
+#ifdef INVARIANTS
+ if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
+ (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) &&
+ (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
+ NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n");
+ return;
+ }
+#endif
+
+ /* Checksum. */
+ if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) {
+ if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 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 = 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 = 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 == m->m_pkthdr.len) {
+ m->m_len = ip->ip_len;
+ m->m_pkthdr.len = 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 != 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 != nd_client.s_addr) {
+ NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n");
+ return;
+ }
+
+ if (ip->ip_p != 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)) != 0) {
+ NETDDEBUG("nd_handle_ip: Drop fragmented packet\n");
+ return;
+ }
+
+ /* UDP custom is to have packet length not include IP header. */
+ ip->ip_len -= 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 = m_pullup(m, sizeof(struct udpiphdr) +
+ sizeof(struct netdump_ack));
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+ return;
+ }
+ }
+ udp = mtod(m, struct udpiphdr *);
+
+ if (ntohs(udp->ui_u.uh_dport) != 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 = (struct netdump_ack *)(mtod(m, caddr_t) +
+ sizeof(struct udpiphdr));
+ rcv_ackno = ntohl(nd_ack->na_seqno);
+ if (nd_server_port == NETDUMP_PORT)
+ nd_server_port = ntohs(udp->ui_u.uh_sport);
+ if (rcv_ackno >= nd_seqno + 64)
+ printf("nd_handle_ip: ACK %d too far in future!\n", rcv_ackno);
+ else if (rcv_ackno >= nd_seqno) {
+
+ /* We're interested in this ack. Record it. */
+ rcvd_acks |= 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 = *mb;
+ ifp = m->m_pkthdr.rcvif;
+ if (m->m_len < sizeof(struct arphdr)) {
+ m = m_pullup(m, sizeof(struct arphdr));
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n");
+ return;
+ }
+ }
+ ah = mtod(m, struct arphdr *);
+
+ if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) {
+ NETDDEBUG("nd_handle_arp: unknown hardware address 0x%2D)\n",
+ (unsigned char *)&ah->ar_hrd, "");
+ return;
+ }
+ if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
+ NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol %d\n",
+ ntohs(ah->ar_pro));
+ return;
+ }
+ req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
+ if (m->m_len < req_len) {
+ m = m_pullup(m, req_len);
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n");
+ return;
+ }
+ }
+ ah = mtod(m, struct arphdr *);
+
+ op = ntohs(ah->ar_op);
+ memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
+ memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
+ enaddr = (uint8_t *)IF_LLADDR(ifp);
+ myaddr = 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 == 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 == ARPOP_REPLY) {
+ if (isaddr.s_addr != 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 = 1;
+ NETDDEBUG("\nnd_handle_arp: Got server MAC address %6D\n",
+ nd_gw_mac.octet, ":");
+ return;
+ }
+
+ if (op != ARPOP_REQUEST) {
+ NETDDEBUG("nd_handle_arp: Ignoring ARP non-request/reply\n");
+ return;
+ }
+
+ if (itaddr.s_addr != 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 = htons(ARPOP_REPLY);
+ ah->ar_pro = htons(ETHERTYPE_IP);
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+ m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
+ m->m_pkthdr.len = m->m_len;
+
+ memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
+ netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
+ *mb = 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) == 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) != 0) {
+ m_adj(m, -ETHER_CRC_LEN);
+ m->m_flags &= ~M_HASFCS;
+ }
+ eh = mtod(m, struct ether_header *);
+ m->m_pkthdr.PH_loc.ptr = eh;
+ etype = ntohs(eh->ether_type);
+ if ((m->m_flags & M_VLANTAG) != 0 || etype == 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 != 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 != NULL);
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+ if (panicstr != 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 = NETDUMP_VMCORE;
+ if (offset == 0 && length > 0)
+ msgtype = NETDUMP_KDH;
+ else if (offset > 0)
+ offset -= sizeof(struct kerneldumpheader);
+ memcpy(buf, virtual, length);
+ err = netdump_send(msgtype, offset, buf, length);
+ if (err != 0) {
+ dump_failed = 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 = NULL;
+ error = 0;
+ found = 0;
+ nd_gw_unset = 0;
+ must_lock = 1;
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+ if (panicstr != NULL)
+ must_lock = 0;
+#endif
+
+ /* Check if the dumping is allowed to continue. */
+ if (nd_enable == 0)
+ return (EINVAL);
+
+ /* Lookup the right if device to be used in the dump. */
+ if (must_lock != 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 = 1;
+ break;
+ }
+ }
+ if (must_lock != 0)
+ IFNET_RUNLOCK_NOSLEEP();
+ if (found == 0) {
+ printf("netdumpsys: Can't netdump: no valid NIC given\n");
+ return (EINVAL);
+ }
+
+ MPASS(nd_ifp != NULL);
+
+ if (nd_server.s_addr == INADDR_ANY) {
+ printf("netdumpsys: Can't netdump; no server IP given\n");
+ return (EINVAL);
+ }
+ if (nd_client.s_addr == 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 = NETDUMP_PORT;
+ if ((nd_ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 0)
+ nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp);
+
+ /* Make the card use *our* receive callback. */
+ old_if_input = nd_ifp->if_input;
+ nd_ifp->if_input = netdump_pkt_in;
+
+ if (nd_gw.s_addr == INADDR_ANY) {
+ nd_gw.s_addr = nd_server.s_addr;
+ nd_gw_unset = 1;
+ }
+ printf("\n-----------------------------------\n");
+ printf("netdump in progress. searching for server.. ");
+ if (netdump_arp_server()) {
+ printf("Failed to locate server MAC address\n");
+ error = EINVAL;
+ goto trig_abort;
+ }
+ if (netdump_send(NETDUMP_HERALD, 0, NULL, 0) != 0) {
+ printf("Failed to contact netdump server\n");
+ error = 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 = netdump_dumper;
+ dumper.priv = NULL;
+ dumper.blocksize = NETDUMP_DATASIZE;
+ dumper.mediasize = 0;
+ dumper.mediaoffset = 0;
+ dumpsys(&dumper);
+ if (dump_failed != 0) {
+ printf("Failed to dump the actual raw datas\n");
+ error = EINVAL;
+ goto trig_abort;
+ }
+ if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0) {
+ printf("Failed to close the transaction\n");
+ error = EINVAL;
+ goto trig_abort;
+ }
+ printf("\nnetdump finished.\n");
+trig_abort:
+ if (nd_gw_unset != 0)
+ nd_gw.s_addr = INADDR_ANY;
+ if (old_if_input)
+ nd_ifp->if_input = old_if_input;
+ if ((nd_ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 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 = NULL;
+ nd_server.s_addr = INADDR_ANY;
+ nd_client.s_addr = INADDR_ANY;
+ nd_gw.s_addr = INADDR_ANY;
+
+ if (nd_server_tun[0] != '\0')
+ inet_aton(nd_server_tun, &nd_server);
+ if (nd_client_tun[0] != '\0')
+ inet_aton(nd_client_tun, &nd_client);
+ if (nd_gw_tun[0] != '\0')
+ inet_aton(nd_gw_tun, &nd_gw);
+ if (nd_nic_tun[0] != '\0') {
+ found = 0;
+ IFNET_RLOCK_NOSLEEP();
+ TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+ if (!strncmp(ifp->if_xname, nd_nic_tun,
+ strlen(ifp->if_xname))) {
+ found = 1;
+ break;
+ }
+ }
+ IFNET_RUNLOCK_NOSLEEP();
+ if (found != 0 && netdump_supported_nic(ifp))
+ nd_ifp = 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 += hdrsize;
- totsize = 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 = ENOSPC;
- goto fail;
- }
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+ totsize = 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 = ENOSPC;
+ goto fail;
+ }
- /* Determine dump offset on device. */
- dumplo = di->mediaoffset + di->mediasize - totsize;
+ /* Determine dump offset on device. */
+ dumplo = di->mediaoffset + di->mediasize - totsize;
+ }
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 += fileofs;
hdrgap = fileofs - DEV_ALIGN(hdrsz);
- /* Determine dump offset on device. */
- if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
- error = ENOSPC;
- goto fail;
+ /* If the upper bound is 0, dumper likely will not use disks. */
+ if ((di->mediaoffset + di->mediasize) == 0)
+ dumplo = 0;
+ else {
+
+ /* Determine dump offset on device. */
+ if (di->mediasize <
+ SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+ error = ENOSPC;
+ goto fail;
+ }
+ dumplo = di->mediaoffset + di->mediasize - dumpsize;
+ dumplo -= sizeof(kdh) * 2;
}
- dumplo = di->mediaoffset + di->mediasize - dumpsize;
- dumplo -= sizeof(kdh) * 2;
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= netdumpsrv
+MAN= netdumpsrv.8
+
+CFLAGS+= -DNDEBUG -I${.CURDIR}
+
+WARNS?= 6
+
+DPADD= ${LIBUTIL}
+LDADD= -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 = 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) != 0) {
+ assert((priority & (LOG_WARNING | LOG_ERR)) == 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 != NULL);
+
+ client = calloc(1, sizeof(*client));
+ if (client == NULL) {
+ LOGERR_PERROR("calloc()");
+ return (NULL);
+ }
+ ip = &sip->sin_addr;
+ bcopy(ip, &client->ip, sizeof(*ip));
+ client->corefd = -1;
+ client->sock = -1;
+ client->last_msg = now;
+
+ ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len,
+ client->hostname, sizeof(client->hostname), NULL, 0, NI_NAMEREQD);
+ if (ecode != 0) {
+
+ /* Can't resolve, try with a numeric IP. */
+ ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len,
+ client->hostname, sizeof(client->hostname), NULL, 0, 0);
+ if (ecode != 0) {
+ LOGERR("getnameinfo(): %s\n", gai_strerror(ecode));
+ free(client);
+ return (NULL);
+ }
+ } else {
+
+ /* Strip off the domain name */
+ firstdot = strchr(client->hostname, '.');
+ if (firstdot)
+ *firstdot = '\0';
+ }
+
+ client->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (client->sock == -1) {
+ LOGERR_PERROR("socket()");
+ free(client);
+ return (NULL);
+ }
+ if (fcntl(client->sock, F_SETFL, O_NONBLOCK) == -1) {
+ LOGERR_PERROR("fcntl()");
+ close(client->sock);
+ free(client);
+ return (NULL);
+ }
+ bzero(&saddr, sizeof(saddr));
+ saddr.sin_len = sizeof(saddr);
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = bindip.s_addr;
+ saddr.sin_port = 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 = sizeof(saddr);
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = ip->s_addr;
+ saddr.sin_port = 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 = 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 = 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 = open(client->infofilename, O_WRONLY | O_CREAT | O_EXCL,
+ DEFFILEMODE);
+ if (fd == -1) {
+ if (errno != EEXIST)
+ LOGERR("open(\"%s\"): %s\n",
+ client->infofilename, strerror(errno));
+ continue;
+ }
+ client->infofile = fdopen(fd, "w");
+ if (client->infofile == NULL) {
+ LOGERR_PERROR("fdopen()");
+ close(fd);
+ unlink(client->infofilename);
+ continue;
+ }
+
+ /* Next make the core file. */
+ fd = open(client->corefilename, O_RDWR | O_CREAT | O_EXCL,
+ DEFFILEMODE);
+ if (fd == -1) {
+
+ /* Failed. Keep the numbers in sync. */
+ fclose(client->infofile);
+ unlink(client->infofilename);
+ client->infofile = NULL;
+ if (errno != EEXIST)
+ LOGERR("open(\"%s\"): %s\n",
+ client->corefilename, strerror(errno));
+ continue;
+ }
+ client->corefd = fd;
+ break;
+ }
+
+ if (client->infofile == NULL || client->corefd == -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 != -1)
+ close(client->corefd);
+ if (client->sock != -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 != 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 != NULL);
+
+ /* If no script is specified this is a no-op. */
+ if ((pflags & PFLAGS_SCRIPT) == 0)
+ return;
+
+ pid = fork();
+
+ /*
+ * The function is invoked in critical conditions, thus just exiting
+ * without reporting errors is fine.
+ */
+ if (pid == -1) {
+ LOGERR_PERROR("fork()");
+ return;
+ } else if (pid != 0) {
+ close(sock);
+ pidfile_close(pfh);
+ if (execl(handler_script, handler_script, reason,
+ client_ntoa(client), client->hostname,
+ client->infofilename, client->corefilename, NULL) == -1) {
+ LOGERR_PERROR("fork()");
+ _exit(1);
+ }
+ }
+}
+
+static void
+handle_timeout(struct netdump_client *client)
+{
+
+ assert(client != 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;
+
+ /* Only time out clients every 10 seconds. */
+ if (now - last_timeout_check < CLIENT_TPASS)
+ return;
+ last_timeout_check = 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;
+
+ assert(client != NULL && msg != NULL);
+
+ bzero(&ack, sizeof(ack));
+ ack.na_seqno = htonl(msg->nm_hdr.mh_seqno);
+ do {
+ tryagain = 0;
+ if (send(client->sock, &ack, sizeof(ack), 0) == -1) {
+ if (errno == EINTR) {
+ tryagain = 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 != NULL && msg != NULL);
+
+ if (client != NULL) {
+ if (client->any_data_rcvd == 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 = alloc_client(from);
+ if (client == 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 != NULL);
+
+ if (client == NULL)
+ return;
+
+ client->any_data_rcvd = 1;
+ h = (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 = kerneldump_parity(h);
+
+ /* Make sure all the strings are null-terminated. */
+ h->architecture[sizeof(h->architecture) - 1] = '\0';
+ h->hostname[sizeof(h->hostname) - 1] = '\0';
+ h->versionstring[sizeof(h->versionstring) - 1] = '\0';
+ h->panicstring[sizeof(h->panicstring) - 1] = '\0';
+
+ client_pinfo(client, " Architecture: %s\n", h->architecture);
+ client_pinfo(client, " Architecture version: %d\n",
+ dtoh32(h->architectureversion));
+ dumplen = 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 = 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 != NULL);
+
+ if (client == NULL)
+ return;
+
+ client->any_data_rcvd = 1;
+ if (msg->nm_hdr.mh_seqno % 11523 == 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) == -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 != NULL);
+
+ if (client == 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 != NULL && fromstr != NULL && msg != NULL);
+
+ bzero(from, sizeof(*from));
+ from->sin_family = AF_INET;
+ from->sin_len = fromlen = sizeof(*from);
+ from->sin_port = 0;
+ from->sin_addr.s_addr = INADDR_ANY;
+
+ len = recvfrom(isock, msg, sizeof(*msg), 0, (struct sockaddr *)from,
+ &fromlen);
+ if (len == -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 = ntohl(msg->nm_hdr.mh_type);
+ msg->nm_hdr.mh_seqno = ntohl(msg->nm_hdr.mh_seqno);
+ msg->nm_hdr.mh_offset = be64toh(msg->nm_hdr.mh_offset);
+ msg->nm_hdr.mh_len = 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 != NULL && fromstr != NULL && msg != NULL);
+
+ if (client != NULL)
+ client->last_msg = 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 == 0) {
+ maxfd = sock + 1;
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+ SLIST_FOREACH(client, &clients, iter) {
+ FD_SET(client->sock, &readfds);
+ if (maxfd <= client->sock)
+ maxfd = client->sock+1;
+ }
+
+ /* So that we time out clients regularly. */
+ tv.tv_sec = CLIENT_TPASS;
+ tv.tv_usec = 0;
+ if (select(maxfd, &readfds, NULL, NULL, &tv) == -1) {
+ if (errno == EINTR)
+ continue;
+ LOGERR_PERROR("select()");
+
+ /*
+ * Errors with select() probably will not go away
+ * with simple retrying.
+ */
+ pidfile_remove(pfh);
+ exit(1);
+ }
+ now = time(NULL);
+ if (FD_ISSET(sock, &readfds)) {
+ len = receive_message(sock, &from, fromstr,
+ sizeof(fromstr), &msg);
+ if (len == -1) {
+ if (errno == EINTR)
+ continue;
+ if (errno != EAGAIN) {
+ pidfile_remove(pfh);
+ LOGERR_PERROR("recvfrom()");
+ exit(1);
+ }
+ } else if (len != 0) {
+
+ /*
+ * With len == 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 ==
+ 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 != NULL &&
+ msg.nm_hdr.mh_type != NETDUMP_HERALD &&
+ client->printed_port_warning == 0) {
+ LOGWARN("Client %s responding on server port\n",
+ client->hostname);
+ client->printed_port_warning = 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 = receive_message(client->sock, &from,
+ fromstr, sizeof(fromstr), &msg);
+ if (len == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ LOGERR_PERROR("recvfrom()");
+
+ /*
+ * Client socket is broken for
+ * some reason.
+ */
+ handle_timeout(client);
+ } else if (len != 0) {
+
+ /*
+ * With len == 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 != 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 = 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct stat statbuf;
+ struct sockaddr_in bindaddr;
+ struct sigaction sa;
+ int ch;
+
+ pfh = pidfile_open(NULL, 0600, NULL);
+ if (pfh == NULL) {
+ if (errno == EEXIST)
+ printf("Instance of netdump already running\n");
+ else
+ printf("Impossible to open the pid file\n");
+ exit(1);
+ }
+
+ while ((ch = getopt(argc, argv, "a:Dd:i:")) != -1) {
+ switch (ch) {
+ case 'a':
+ pflags |= 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 |= PFLAGS_DEBUG;
+ break;
+ case 'd':
+ pflags |= PFLAGS_DDIR;
+ assert(dumpdir[0] == '\0');
+ strncpy(dumpdir, optarg, sizeof(dumpdir) - 1);
+ break;
+ case 'i':
+ pflags |= 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 = strdup(optarg);
+ if (handler_script == 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) == 0) {
+ bindip.s_addr = INADDR_ANY;
+ printf("Default: listening on all interfaces\n");
+ }
+ if ((pflags & PFLAGS_DDIR) == 0) {
+ strcpy(dumpdir, "/var/crash");
+ printf("Default: dumping on /var/crash/\n");
+ }
+ if ((pflags & PFLAGS_DEBUG) == 0)
+ phook = syslog;
+ else
+ phook = 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) != 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) == 0 && daemon(0, 0) == -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 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock == -1) {
+ pidfile_remove(pfh);
+ LOGERR_PERROR("socket()");
+ exit(1);
+ }
+ bzero(&bindaddr, sizeof(bindaddr));
+ bindaddr.sin_len = sizeof(bindaddr);
+ bindaddr.sin_family = AF_INET;
+ bindaddr.sin_addr.s_addr = bindip.s_addr;
+ bindaddr.sin_port = 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) == -1) {
+ pidfile_remove(pfh);
+ close(sock);
+ LOGERR_PERROR("fcntl()");
+ exit(1);
+ }
+
+ /* Override some signal handlers. */
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = 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 = SIG_IGN;
+ sa.sa_flags = 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);
+}
[-- Attachment #3 --]
[-- Attachment #4 --]
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>
@@ -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>
@@ -96,20 +98,15 @@ __FBSDID("$FreeBSD$");
#define NETDDEBUGV_IF(i, f, ...)
#endif
-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,
- 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);
@@ -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 = 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 = 10000;
-
-/* Times to retransmit lost packets. */
-static int nd_retries = 10;
-
-/* General dynamic settings. */
-static char nd_ifp_str[IFNAMSIZ];
-static struct ether_addr nd_gw_mac;
-static struct in_addr nd_server = {INADDR_ANY};
-static struct in_addr nd_client = {INADDR_ANY};
-static struct in_addr nd_gw = {INADDR_ANY};
-struct ifnet *nd_ifp;
-int nd_enable = 0;
-static uint16_t nd_server_port = NETDUMP_PORT;
+static int dump_failed;
+
+static struct network_debug __unused
+nd_networking = {
+ .poll_time = 10000,
+ .retries = 10,
+ .if_name = { 0 },
+ .ifp = NULL,
+ .remote = {INADDR_ANY},
+ .local = {INADDR_ANY},
+ .gw = {INADDR_ANY},
+ .ip_handler = nd_handle_ip,
+ .wanted = nd_packet_wanted,
+};
+
+int nd_enable = 0; // Used in kern_shutdown, so can't be static
+static uint16_t nd_remote_port = NETDUMP_PORT;
/* 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];
-/*
- * 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 != 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 = 1;
break;
}
@@ -235,24 +213,24 @@ sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS)
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");
-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
*/
-/*
- * 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 == NULL) {
- printf("netdump_ether_output: Out of mbufs\n");
- return (ENOBUFS);
- }
- eh = 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 = htons(etype);
-
- if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) ||
- (ifp->if_drv_flags & IFF_DRV_RUNNING) != 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
@@ -343,7 +262,7 @@ netdump_udp_output(struct mbuf *m)
struct udpiphdr *ui;
struct ip *ip;
- MPASS(nd_ifp != NULL);
+ MPASS(nd_networking.ifp != NULL);
M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
if (m == NULL) {
@@ -355,11 +274,11 @@ netdump_udp_output(struct mbuf *m)
ui->ui_pr = IPPROTO_UDP;
ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip));
ui->ui_ulen = ui->ui_len;
- ui->ui_src = nd_client;
- ui->ui_dst = nd_server;
+ ui->ui_src = nd_networking.local;
+ ui->ui_dst = nd_networking.remote;
/* Use this src port so that the server can connect() the socket */
ui->ui_sport = htons(NETDUMP_ACKPORT);
- ui->ui_dport = htons(nd_server_port);
+ ui->ui_dport = htons(nd_remote_port);
ui->ui_sum = 0;
if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0)
ui->ui_sum = 0xffff;
@@ -375,59 +294,15 @@ netdump_udp_output(struct mbuf *m)
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, sizeof(struct ip));
- 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));
}
-/*
- * 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 != NULL);
-
- /* Fill-up a broadcast address. */
- memset(&bcast, 0xFF, ETHER_ADDR_LEN);
- MGETHDR(m, M_NOWAIT, MT_DATA);
- if (m == NULL) {
- printf("netdump_send_arp: Out of mbufs");
- return (ENOBUFS);
- }
- pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
- m->m_len = pktlen;
- m->m_pkthdr.len = pktlen;
- MH_ALIGN(m, pktlen);
- ah = mtod(m, struct arphdr *);
- ah->ar_hrd = htons(ARPHRD_ETHER);
- ah->ar_pro = htons(ETHERTYPE_IP);
- ah->ar_hln = ETHER_ADDR_LEN;
- ah->ar_pln = sizeof(struct in_addr);
- ah->ar_op = 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
@@ -444,20 +319,20 @@ netdump_arp_server()
{
int err, polls, retries;
- for (retries = 0; retries < nd_retries && have_server_mac == 0;
+ for (retries = 0; (retries < nd_networking.retries) && (nd_networking.have_remote_mac == 0);
retries++) {
- err = netdump_send_arp();
+ err = network_debug_send_arp(&nd_networking);
if (err != 0)
return (err);
- for (polls = 0; polls < nd_polls && have_server_mac == 0;
+ for (polls = 0; (polls < nd_networking.poll_time) && (nd_networking.have_remote_mac == 0);
polls++) {
- netdump_network_poll();
+ network_debug_poll(&nd_networking);
DELAY(500);
}
- if (have_server_mac == 0)
+ if (nd_networking.have_remote_mac == 0)
printf("(ARP retry)");
}
- if (have_server_mac != 0)
+ if (nd_networking.have_remote_mac != 0)
return (0);
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 = 0;
retries = 0;
- MPASS(nd_ifp != NULL);
+ MPASS(nd_networking.ifp != NULL);
retransmit:
@@ -505,7 +380,7 @@ retransmit:
pktlen = min(pktlen, NETDUMP_DATASIZE);
/* Second bound: the interface MTU (assume no IP options). */
- pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) -
+ pktlen = min(pktlen, nd_networking.ifp->if_mtu - sizeof(struct udpiphdr) -
sizeof(struct netdump_msg_hdr));
/*
@@ -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 = pktlen;
m->m_next = m2;
@@ -568,13 +443,13 @@ retransmit:
*/
polls = 0;
while (rcvd_acks != want_acks) {
- 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 += i;
@@ -582,6 +457,97 @@ retransmit:
}
/*
+ * Determine if we want this IP packet.
+ */
+static int
+nd_packet_wanted(struct network_debug *ndp, struct mbuf *m)
+{
+ int retval = 0;
+ struct udpiphdr *udp;
+ struct ip *ip;
+ unsigned short hlen;
+
+ ip = mtod(m, struct ip*);
+
+ /* IP version. */
+ if (ip->ip_v != 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) == IN_LOOPBACKNET ||
+ (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) &&
+ (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
+ NETDDEBUG("%s: Bad IP header (RFC1122)\n", __FUNCTION__);
+ goto done;
+ }
+#endif
+
+ /* Header length. */
+ hlen = 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) != 0) {
+ if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 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 != 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)) != 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 != 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 != ndp->local.s_addr) {
+ NETDDEBUGV("%s: Drop packet not to our IP\n", __FUNCTION__);
+ goto done;
+ }
+ udp = mtod(m, struct udpiphdr *);
+
+ if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) {
+ NETDDEBUG("%s: not on the netdump port.\n", __FUNCTION__);
+ goto done;
+ }
+
+ retval = 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)
+{
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;
/* IP processing. */
m = *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 = m_pullup(m, sizeof(struct ip));
*mb = m;
if (m == 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)
/* IP version. */
if (ip->ip_v != 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;
}
/* Header length. */
hlen = 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 = m_pullup(m, hlen);
*mb = m;
if (m == NULL) {
- NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+ NETDDEBUG("%s: m_pullup failed\n", __FUNCTION__);
return;
}
ip = mtod(m, struct ip *);
@@ -649,7 +615,7 @@ nd_handle_ip(struct mbuf **mb)
if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
(ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) &&
(m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 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) != 0) {
if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 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__);
/* Convert fields to host byte order. */
ip->ip_len = 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 = 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);
+ 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",
/* 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 != 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 != 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;
}
if (ip->ip_p != IPPROTO_UDP) {
- NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n");
+ NETDDEBUG("%s: Drop non-UDP packet\n", __FUNCTION__);
return;
}
/* Do not deal with fragments. */
if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) {
- NETDDEBUG("nd_handle_ip: Drop fragmented packet\n");
+ NETDDEBUG("%s: Drop fragmented packet\n", __FUNCTION__);
return;
}
/* UDP custom is to have packet length not include IP header. */
ip->ip_len -= hlen;
+
/* UDP processing. */
+ /*
+ * 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 != 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 != 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 = m;
if (m == NULL) {
- NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+ NETDDEBUG("%s: m_pullup failed\n", __FUNCTION__);
return;
}
}
udp = mtod(m, struct udpiphdr *);
if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) {
- NETDDEBUG("not on the netdump port.\n");
+ NETDDEBUG("%s: not on the netdump port.\n", __FUNCTION__);
return;
}
- /* 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 = (struct netdump_ack *)(mtod(m, caddr_t) +
sizeof(struct udpiphdr));
rcv_ackno = ntohl(nd_ack->na_seqno);
- if (nd_server_port == NETDUMP_PORT)
- nd_server_port = ntohs(udp->ui_u.uh_sport);
+ if (nd_remote_port == NETDUMP_PORT)
+ nd_remote_port = ntohs(udp->ui_u.uh_sport);
if (rcv_ackno >= 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 >= nd_seqno) {
-
/* We're interested in this ack. Record it. */
rcvd_acks |= 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 = *mb;
- ifp = m->m_pkthdr.rcvif;
- if (m->m_len < sizeof(struct arphdr)) {
- m = m_pullup(m, sizeof(struct arphdr));
- *mb = m;
- if (m == NULL) {
- NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n");
- return;
- }
- }
- ah = mtod(m, struct arphdr *);
-
- if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) {
- NETDDEBUG("nd_handle_arp: unknown hardware address 0x%2D)\n",
- (unsigned char *)&ah->ar_hrd, "");
- return;
- }
- if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
- NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol %d\n",
- ntohs(ah->ar_pro));
- return;
- }
- req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
- if (m->m_len < req_len) {
- m = m_pullup(m, req_len);
- *mb = m;
- if (m == NULL) {
- NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n");
- return;
- }
- }
- ah = mtod(m, struct arphdr *);
-
- op = ntohs(ah->ar_op);
- memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
- memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
- enaddr = (uint8_t *)IF_LLADDR(ifp);
- myaddr = 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 == 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 == ARPOP_REPLY) {
- if (isaddr.s_addr != 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 = 1;
- NETDDEBUG("\nnd_handle_arp: Got server MAC address %6D\n",
- nd_gw_mac.octet, ":");
- return;
- }
-
- if (op != ARPOP_REQUEST) {
- NETDDEBUG("nd_handle_arp: Ignoring ARP non-request/reply\n");
- return;
- }
-
- if (itaddr.s_addr != 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 = htons(ARPOP_REPLY);
- ah->ar_pro = htons(ETHERTYPE_IP);
- m->m_flags &= ~(M_BCAST|M_MCAST);
- m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
- m->m_pkthdr.len = m->m_len;
-
- memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
- netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
- *mb = 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
+ * 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) == 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) != 0) {
- m_adj(m, -ETHER_CRC_LEN);
- m->m_flags &= ~M_HASFCS;
- }
- eh = mtod(m, struct ether_header *);
- m->m_pkthdr.PH_loc.ptr = eh;
- etype = ntohs(eh->ether_type);
- if ((m->m_flags & M_VLANTAG) != 0 || etype == 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 != NULL)
- m_freem(m);
+ network_debug_pkt_in(&nd_networking, 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 != NULL);
-
-#if defined(KDB) && !defined(KDB_UNATTENDED)
- if (panicstr != 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.
@@ -1033,6 +800,25 @@ netdump_dumper(void *priv __unused, void *virtual,
}
/*
+ * 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 = 0;
+
+ if (ndp->remote.s_addr == INADDR_ANY) {
+ printf("%s: Can't netdump; no server IP given\n", __FUNCTION__);
+ return (EINVAL);
+ }
+
+ error = 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 *) = NULL;
- old_if_input = NULL;
error = 0;
found = 0;
- nd_gw_unset = 0;
must_lock = 1;
#if defined(KDB) && !defined(KDB_UNATTENDED)
if (panicstr != NULL)
@@ -1062,51 +846,21 @@ netdumpsys()
if (nd_enable == 0)
return (EINVAL);
- /* Lookup the right if device to be used in the dump. */
- if (must_lock != 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 = 1;
- break;
- }
- }
- if (must_lock != 0)
- IFNET_RUNLOCK_NOSLEEP();
- if (found == 0) {
- printf("netdumpsys: Can't netdump: no valid NIC given\n");
- return (EINVAL);
- }
-
- MPASS(nd_ifp != NULL);
-
- if (nd_server.s_addr == INADDR_ANY) {
- printf("netdumpsys: Can't netdump; no server IP given\n");
- return (EINVAL);
- }
- if (nd_client.s_addr == INADDR_ANY) {
- printf("netdumpsys: Can't netdump; no client IP given\n");
- return (EINVAL);
+ error = nd_prepare(&nd_networking);
+ if (error) {
+ goto trig_abort;
}
/*
* nd_server_port could have switched after the first ack the
* first time it gets called. Adjust it accordingly.
*/
- nd_server_port = NETDUMP_PORT;
- if ((nd_ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 0)
- nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp);
+ nd_remote_port = NETDUMP_PORT;
/* Make the card use *our* receive callback. */
- old_if_input = nd_ifp->if_input;
- nd_ifp->if_input = netdump_pkt_in;
+ old_if_input = nd_networking.ifp->if_input;
+ nd_networking.ifp->if_input = netdump_pkt_in;
- if (nd_gw.s_addr == INADDR_ANY) {
- nd_gw.s_addr = nd_server.s_addr;
- nd_gw_unset = 1;
- }
printf("\n-----------------------------------\n");
printf("netdump in progress. searching for server.. ");
if (netdump_arp_server()) {
@@ -1119,7 +873,7 @@ netdumpsys()
error = 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");
@@ -1142,12 +896,9 @@ netdumpsys()
}
printf("\nnetdump finished.\n");
trig_abort:
- if (nd_gw_unset != 0)
- nd_gw.s_addr = INADDR_ANY;
+ network_debug_finish(&nd_networking);
if (old_if_input)
- nd_ifp->if_input = old_if_input;
- if ((nd_ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 0)
- nd_ifp->if_ndumpfuncs->ne_enable_intr(nd_ifp);
+ nd_networking.ifp->if_input = old_if_input;
return (error);
}
@@ -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 = NULL;
- nd_server.s_addr = INADDR_ANY;
- nd_client.s_addr = INADDR_ANY;
- nd_gw.s_addr = INADDR_ANY;
-
- if (nd_server_tun[0] != '\0')
- inet_aton(nd_server_tun, &nd_server);
- if (nd_client_tun[0] != '\0')
- inet_aton(nd_client_tun, &nd_client);
+ nd_networking.ifp = NULL;
+ nd_networking.remote.s_addr = INADDR_ANY;
+ nd_networking.local.s_addr = INADDR_ANY;
+ nd_networking.gw.s_addr = INADDR_ANY;
+
+ if (nd_remote_tun[0] != '\0')
+ inet_aton(nd_remote_tun, &nd_networking.remote);
+ if (nd_local_tun[0] != '\0')
+ inet_aton(nd_local_tun, &nd_networking.local);
if (nd_gw_tun[0] != '\0')
- inet_aton(nd_gw_tun, &nd_gw);
+ inet_aton(nd_gw_tun, &nd_networking.gw);
if (nd_nic_tun[0] != '\0') {
- found = 0;
+#if 0
+ int found = 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 != 0 && netdump_supported_nic(ifp))
- nd_ifp = ifp;
+ if (found != 0 && network_debug_supported_nic(ifp))
+ nd_networking.ifp = 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 == NULL) {
+ NETDDEBUG("%s: Out of mbufs\n", __FUNCTION__);
+ return (ENOBUFS);
+ }
+ eh = 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 = htons(etype);
+
+ if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) != 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 = *mb;
+ ifp = m->m_pkthdr.rcvif;
+ if (m->m_len < sizeof(struct arphdr)) {
+ m = m_pullup(m, sizeof(struct arphdr));
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("%s: runt packet: m_pullup failed\n", __FUNCTION__);
+ return;
+ }
+ }
+ ah = mtod(m, struct arphdr *);
+
+ if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) {
+ NETDDEBUG("%s: unknown hardware address 0x%2D)\n",
+ __FUNCTION__,
+ (unsigned char *)&ah->ar_hrd, "");
+ return;
+ }
+ if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
+ NETDDEBUG("%s: Drop ARP for unknown protocol %d\n",
+ __FUNCTION__,
+ ntohs(ah->ar_pro));
+ return;
+ }
+ req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
+ if (m->m_len < req_len) {
+ m = m_pullup(m, req_len);
+ *mb = m;
+ if (m == NULL) {
+ NETDDEBUG("%s: runt packet: m_pullup failed\n", __FUNCTION__);
+ return;
+ }
+ }
+ ah = mtod(m, struct arphdr *);
+
+ op = ntohs(ah->ar_op);
+ memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
+ memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
+ enaddr = (uint8_t *)IF_LLADDR(ifp);
+ myaddr = 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 != INADDR_ANY &&
+ isaddr.s_addr == 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 == ARPOP_REPLY) {
+ if (ndp->gw.s_addr == INADDR_ANY && ndp->remote.s_addr == INADDR_ANY) {
+ NETDDEBUG("%s: Ignoring ARP because we do not have an address yet", __FUNCTION__);
+ return;
+ }
+ if (isaddr.s_addr != (ndp->gw.s_addr == 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 = 1;
+ NETDDEBUG("\n%s: Got remote MAC address %6D\n", __FUNCTION__,
+ ndp->remote_mac.octet, ":");
+ return;
+ }
+
+ if (op != ARPOP_REQUEST) {
+ NETDDEBUG("%s: Ignoring ARP non-request/reply\n", __FUNCTION__);
+ return;
+ }
+
+ if (itaddr.s_addr != 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 = htons(ARPOP_REPLY);
+ ah->ar_pro = htons(ETHERTYPE_IP);
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+ m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
+ m->m_pkthdr.len = m->m_len;
+
+ memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
+ network_debug_ether_output(m, ifp, dst, ETHERTYPE_ARP);
+ *mb = 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 != NULL);
+
+ /* Fill-up a broadcast address. */
+ memset(&bcast, 0xFF, ETHER_ADDR_LEN);
+ MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ NETDDEBUG("%s: Out of mbufs", __FUNCTION__);
+ return (ENOBUFS);
+ }
+ pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
+ m->m_len = pktlen;
+ m->m_pkthdr.len = pktlen;
+ MH_ALIGN(m, pktlen);
+ ah = mtod(m, struct arphdr *);
+ ah->ar_hrd = htons(ARPHRD_ETHER);
+ ah->ar_pro = htons(ETHERTYPE_IP);
+ ah->ar_hln = ETHER_ADDR_LEN;
+ ah->ar_pln = sizeof(struct in_addr);
+ ah->ar_op = 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 == 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 != NULL);
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+ if (panicstr != 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 = 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] = { 0 };
+ static const u_char broadcast_mac[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ struct ether_header *eh, temp_eh;
+ u_short etype;
+ int send_through = 1;
+
+ NETDDEBUG("%s(%d): Got an mbuf\n", __FUNCTION__, __LINE__);
+
+ /* Ethernet processing. */
+ if ((m->m_flags & M_PKTHDR) == 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) != 0) {
+ m_adj(m, -ETHER_CRC_LEN);
+ m->m_flags &= ~M_HASFCS;
+ }
+ eh = mtod(m, struct ether_header *);
+ temp_eh = *eh; // Not a big structure, so should be safe to have on stack and copy
+ m->m_pkthdr.PH_loc.ptr = eh;
+ etype = ntohs(eh->ether_type);
+ if ((m->m_flags & M_VLANTAG) != 0 || etype == 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)) != 0 &&
+ bcmp(&eh->ether_dhost, broadcast_mac, sizeof(broadcast_mac)) != 0 &&
+ bcmp(&ndp->local_mac, &eh->ether_dhost, sizeof(ndp->local_mac)) != 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 = (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 = (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 != NULL)
+ m_freem(m);
+ NETDDEBUG("%s(%d): Done here\n", __FUNCTION__, __LINE__);
+}
+
+int
+network_debug_prepare(struct network_debug *ndp)
+{
+ int must_lock = 1;
+ int error = 0;
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+ if (panicstr != NULL)
+ must_lock = 0;
+#endif
+ /* Lookup the right if device to be used */
+ if (must_lock != 0)
+ IFNET_RLOCK_NOSLEEP();
+
+ if (ndp->ifp == NULL) {
+ struct ifnet *ifp = 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] == 0 ||
+ !strcmp(ifp->if_xname, ndp->if_name)) &&
+ network_debug_supported_nic(ifp)) {
+ ndp->ifp = ifp;
+ break;
+ }
+ }
+ if (must_lock != 0)
+ IFNET_RUNLOCK_NOSLEEP();
+ if (ndp->ifp == NULL) {
+ NETDDEBUG("%s: No valid NIC found\n", __FUNCTION__);
+ return (EINVAL);
+ }
+ }
+
+ MPASS(ndp->ifp != NULL);
+
+ bcopy((void*)IF_LLADDR(ndp->ifp), (void*)&ndp->local_mac, sizeof(ndp->local_mac));
+
+ if (ndp->local.s_addr == 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 == NULL ||
+ ndp->ifp->if_addr->ifa_addr == 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 = in_control(NULL, SIOCGIFADDR, (caddr_t)&req, ndp->ifp, NULL)) != 0) {
+ NETDDEBUG("%s: Can't ask interface for IP address to do error %d\n", __FUNCTION__, error);
+ return (error);
+ }
+ sin = (struct sockaddr_in*)&req.ifr_addr;
+ if (sin->sin_family != 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 == INADDR_ANY) {
+ NETDDEBUG("%s: Address for interface is INADDR_ANY\n", __FUNCTION__);
+ return (EINVAL);
+ }
+ ndp->local.s_addr = sin->sin_addr.s_addr;
+ NETDDEBUG("%s(%d): ndp->local.s_addr = %#x\n", __FUNCTION__, __LINE__, ndp->local.s_addr);
+ }
+
+ if ((ndp->ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 0)
+ ndp->ifp->if_ndumpfuncs->ne_disable_intr(ndp->ifp);
+
+ return 0;
+}
+
+void
+network_debug_finish(struct network_debug *ndp)
+{
+ int must_lock = 1;
+
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+ if (panicstr != NULL)
+ must_lock = 0;
+#endif
+
+ if (ndp->local.s_addr != INADDR_ANY) {
+ if ((ndp->ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 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 != 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;
client->any_data_rcvd = 1;
- h = (struct kerneldumpheader *)msg->nm_data;
+ h = (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));
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?16E0C844-B302-4B61-A250-CA12CABDE476>
