From owner-freebsd-net@FreeBSD.ORG Thu Apr 3 19:23:10 2014 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id DE1A73BE for ; Thu, 3 Apr 2014 19:23:10 +0000 (UTC) Received: from mail.iXsystems.com (newknight.ixsystems.com [206.40.55.70]) (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 5C5F537D for ; Thu, 3 Apr 2014 19:23:09 +0000 (UTC) Received: from localhost (mail.ixsystems.com [10.2.55.1]) by mail.iXsystems.com (Postfix) with ESMTP id 671B872D97 for ; Thu, 3 Apr 2014 12:23:09 -0700 (PDT) Received: from mail.iXsystems.com ([10.2.55.1]) by localhost (mail.ixsystems.com [10.2.55.1]) (maiad, port 10024) with ESMTP id 16614-03 for ; Thu, 3 Apr 2014 12:23:09 -0700 (PDT) Received: from [10.0.1.7] (base.kithrup.com [173.164.180.195]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by mail.iXsystems.com (Postfix) with ESMTPSA id 6BD1D72D86 for ; Thu, 3 Apr 2014 12:23:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ixsystems.com; s=newknight0; t=1396552988; bh=e4TCrbhZMKVervkXzfGL1RM8XQEP3PUhWzxU/+qG1yk=; h=From:Subject:Date:To; b=bgyFinXDh2T9l47rZPkcc/yZ8HREzB8zNs6nslDv8wuzdSisbEtX7kyY7PBgaRySL NtCxuNnCZY8xg8gXHzQoIRdNVgPm3AQzBrxnXu2xMyrcZPRh+Mrq7/msSF1uQTybyh jvQ69fIhyQg1UrYBqAK0RsEjhvwRJDbPNGbjPOfQ= From: Sean Fagan Content-Type: multipart/mixed; boundary="Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3" Subject: Re: More on the whole interruptless networking Message-Id: <16E0C844-B302-4B61-A250-CA12CABDE476@ixsystems.com> Date: Thu, 3 Apr 2014 12:23:05 -0700 To: freebsd-net Mime-Version: 1.0 (Mac OS X Mail 7.2 \(1874\)) X-Mailer: Apple Mail (2.1874) X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 03 Apr 2014 19:23:10 -0000 --Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii No, that icon is not attach, it's send. Grrr. Attached diffs that I promised and didn't attach before hitting the = wrong button. --Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3 Content-Disposition: attachment; filename=netdump-10-diffs.txt Content-Type: text/plain; name="netdump-10-diffs.txt" Content-Transfer-Encoding: quoted-printable diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index a34f0c1..e455200 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -274,6 +274,14 @@ ctld_enable=3D"NO" # CAM Target Layer / = iSCSI target daemon. local_unbound_enable=3D"NO" # local caching resolver =20 # +# netdumpsrv configuration. +# +netdumpsrv_enable=3D"NO" # Run netdumpsrv. +netdumpsrv_program=3D"/usr/sbin/netdumpsrv" # Path to netdumpsrv. +netdumpsrv_pidfile=3D"/var/run/netdumpsrv.pid" # Path to pidfile for = netdumpsrv. +#netdumpsrv_flags=3D"" # Use this for flags -a, -D, -d, -i + +# # kerberos. Do not run the admin daemons on slave servers # kerberos5_server_enable=3D"NO" # Run a kerberos 5 master server (or = NO). diff --git a/etc/rc.d/netdumpsrv b/etc/rc.d/netdumpsrv new file mode 100644 index 0000000..3ae47e6 --- /dev/null +++ b/etc/rc.d/netdumpsrv @@ -0,0 +1,34 @@ +#!/bin/sh +# +# PROVIDE: netdumpsrv +# BEFORE: LOGIN +# KEYWORD: shutdown + +. /etc/rc.subr + +name=3D"netdumpsrv" +rcvar=3D`set_rcvar` + +command=3D"/usr/sbin/${name}" +pidfile=3D"/var/run/${name}.pid" + +load_rc_config $name + +run_rc_command "$1" +#!/bin/sh +# +# PROVIDE: netdumpsrv +# BEFORE: LOGIN +# KEYWORD: shutdown + +. /etc/rc.subr + +name=3D"netdumpsrv" +rcvar=3D`set_rcvar` + +command=3D"/usr/sbin/${name}" +pidfile=3D"/var/run/${name}.pid" + +load_rc_config $name + +run_rc_command "$1" diff --git a/sys/amd64/amd64/minidump_machdep.c = b/sys/amd64/amd64/minidump_machdep.c index 0ee8bcf..1e94bfc 100644 --- a/sys/amd64/amd64/minidump_machdep.c +++ b/sys/amd64/amd64/minidump_machdep.c @@ -319,13 +319,20 @@ minidumpsys(struct dumperinfo *di) } dumpsize +=3D PAGE_SIZE; =20 - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * = 2) { - error =3D E2BIG; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error =3D E2BIG; + goto fail; + } + dumplo =3D di->mediaoffset + di->mediasize - dumpsize; + dumplo -=3D sizeof(kdh) * 2; } - dumplo =3D di->mediaoffset + di->mediasize - dumpsize; - dumplo -=3D sizeof(kdh) * 2; progress =3D dumpsize; =20 /* Initialize mdhdr */ diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 3b48f0f..e1aabb4 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -76,7 +76,8 @@ options INCLUDE_CONFIG_FILE # Include this = file in kernel # Debugging support. Always need this: options KDB # Enable kernel debugger = support. options KDB_TRACE # Print a stack trace for a = panic. - +options DDB # Support DDB. +options NETDUMP_CLIENT # Network core-dump # Make an SMP-capable kernel by default options SMP # Symmetric MultiProcessor = Kernel =20 diff --git a/sys/arm/arm/dump_machdep.c b/sys/arm/arm/dump_machdep.c index e8ba5768..0a88114 100644 --- a/sys/arm/arm/dump_machdep.c +++ b/sys/arm/arm/dump_machdep.c @@ -311,13 +311,20 @@ dumpsys(struct dumperinfo *di) dumpsize +=3D fileofs; hdrgap =3D fileofs - DEV_ALIGN(hdrsz); =20 - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * = 2) { - error =3D ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error =3D ENOSPC; + goto fail; + } + dumplo =3D di->mediaoffset + di->mediasize - dumpsize; + dumplo -=3D sizeof(kdh) * 2; } - dumplo =3D di->mediaoffset + di->mediasize - dumpsize; - dumplo -=3D sizeof(kdh) * 2; =20 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, = dumpsize, di->blocksize); =20 diff --git a/sys/arm/arm/minidump_machdep.c = b/sys/arm/arm/minidump_machdep.c index a858709..4779a54 100644 --- a/sys/arm/arm/minidump_machdep.c +++ b/sys/arm/arm/minidump_machdep.c @@ -284,14 +284,21 @@ minidumpsys(struct dumperinfo *di) =20 dumpsize +=3D PAGE_SIZE; =20 - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * = 2) { - error =3D ENOSPC; - goto fail; - } + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error =3D ENOSPC; + goto fail; + } =20 - dumplo =3D di->mediaoffset + di->mediasize - dumpsize; - dumplo -=3D sizeof(kdh) * 2; + dumplo =3D di->mediaoffset + di->mediasize - dumpsize; + dumplo -=3D sizeof(kdh) * 2; + } progress =3D dumpsize; =20 /* Initialize mdhdr */ diff --git a/sys/conf/files b/sys/conf/files index 27d9c7e..46d0d27 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3261,6 +3261,7 @@ netinet/ip_ipsec.c optional inet = ipsec netinet/ip_mroute.c optional mrouting inet netinet/ip_options.c optional inet netinet/ip_output.c optional inet +netinet/netdump_client.c optional inet netdump_client netinet/raw_ip.c optional inet | inet6 netinet/cc/cc.c optional inet | inet6 netinet/cc/cc_newreno.c optional inet | inet6 diff --git a/sys/conf/options b/sys/conf/options index a4c785e..5cc03db 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -295,6 +295,10 @@ NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h =20 +# Netdump client kernel support +NETDUMP_CLIENT opt_netdump.h +NETDUMP_CLIENT_DEBUG opt_netdump.h + # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index 16e1d6f..b375388 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -75,6 +75,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include =20 @@ -87,6 +90,27 @@ #include "e1000_82571.h" #include "if_em.h" =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define EM_CORE_LOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_CORE_LOCK(adapter); = \ +} while (0) +#define EM_CORE_UNLOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_CORE_UNLOCK(adapter); = \ +} while (0) +#define EM_TX_LOCK_COND(txr, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_TX_LOCK(txr); = \ +} while (0) +#define EM_TX_UNLOCK_COND(txr, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_TX_UNLOCK(txr); = \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -300,14 +324,32 @@ static int = em_sysctl_eee(SYSCTL_HANDLER_ARGS); =20 static __inline void em_rx_discard(struct rx_ring *, int); =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _em_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t em_poll; -#endif /* POLLING */ +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t em_poll_unlocked; +static ndumplock_handler_t em_ndump_disable_intr; +static ndumplock_handler_t em_ndump_enable_intr; +#endif =20 /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ =20 +#ifdef NETDUMP_CLIENT + +static struct netdump_methods em_ndump_methods =3D { + .ne_poll_locked =3D em_poll, + .ne_poll_unlocked =3D em_poll_unlocked, + .ne_disable_intr =3D em_ndump_disable_intr, + .ne_enable_intr =3D em_ndump_enable_intr +}; + +#endif + static device_method_t em_methods[] =3D { /* Device interface */ DEVMETHOD(device_probe, em_probe), @@ -1416,14 +1458,14 @@ em_init(void *arg) } =20 =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) /********************************************************************* * * Legacy polling routine: note this only works with single queue * *********************************************************************/ static int -em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +_em_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int = locking) { struct adapter *adapter =3D ifp->if_softc; struct tx_ring *txr =3D adapter->tx_rings; @@ -1431,9 +1473,9 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, int = count) u32 reg_icr; int rx_done; =20 - EM_CORE_LOCK(adapter); + EM_CORE_LOCK_COND(adapter, locking); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) { - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); return (0); } =20 @@ -1447,11 +1489,11 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, = int count) em_local_timer, adapter); } } - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); =20 em_rxeof(rxr, count, &rx_done); =20 - EM_TX_LOCK(txr); + EM_TX_LOCK_COND(txr, locking); em_txeof(txr); #ifdef EM_MULTIQUEUE if (!drbr_empty(ifp, txr->br)) @@ -1460,12 +1502,49 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, = int count) if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) em_start_locked(ifp, txr); #endif - EM_TX_UNLOCK(txr); + EM_TX_UNLOCK_COND(txr, locking); =20 return (rx_done); } -#endif /* DEVICE_POLLING */ =20 +static int +em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_em_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +em_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_em_poll_generic(ifp, cmd, count, 0)); +} + +static void +em_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + EM_CORE_LOCK(adapter); + em_disable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} + +static void +em_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + EM_CORE_LOCK(adapter); + em_enable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ =20 /********************************************************************* * @@ -2989,6 +3068,9 @@ em_setup_interface(device_t dev, struct adapter = *adapter) ifp->if_qflush =3D em_qflush; #else ifp->if_start =3D em_start; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs =3D &em_ndump_methods; +#endif IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1); ifp->if_snd.ifq_drv_maxlen =3D adapter->num_tx_desc - 1; IFQ_SET_READY(&ifp->if_snd); diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c index 07a9d3e..93d1add 100644 --- a/sys/dev/e1000/if_igb.c +++ b/sys/dev/e1000/if_igb.c @@ -39,6 +39,7 @@ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #include "opt_altq.h" +#include "opt_netdump.h" #endif =20 #include @@ -80,6 +81,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include #include @@ -93,6 +97,35 @@ #include "e1000_82575.h" #include "if_igb.h" =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define IGB_CORE_LOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + IGB_CORE_LOCK(adapter); = \ +} while (0) +#define IGB_CORE_UNLOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + IGB_CORE_UNLOCK(adapter); = \ +} while (0) +#define IGB_RX_LOCK_COND(rxr, locking) do { = \ + if ((locking) !=3D 0) = \ + IGB_RX_LOCK(rxr); = \ +} while (0) +#define IGB_RX_UNLOCK_COND(rxr, locking) do { = \ + if ((locking) !=3D 0) = \ + IGB_RX_UNLOCK(rxr); = \ +} while (0) +#define IGB_TX_LOCK_COND(txr, locking) do { = \ + if ((locking) !=3D 0) = \ + IGB_TX_LOCK(txr); = \ +} while (0) +#define IGB_TX_UNLOCK_COND(txr, locking) do { = \ + if ((locking) !=3D 0) = \ + IGB_TX_UNLOCK(txr); = \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -240,7 +273,8 @@ static __inline void igb_rx_discard(struct = rx_ring *, int); static __inline void igb_rx_input(struct rx_ring *, struct ifnet *, struct mbuf *, u32); =20 -static bool igb_rxeof(struct igb_queue *, int, int *); +static bool _igb_rxeof_generic(struct igb_queue *, int, int *, int); +#define igb_rxeof(q, c, d) _igb_rxeof_generic(q, c, d, 1) static void igb_rx_checksum(u32, struct mbuf *, u32); static int igb_tx_ctx_setup(struct tx_ring *, struct mbuf *, u32 *, u32 *); @@ -289,14 +323,32 @@ static int = igb_set_flowcntl(SYSCTL_HANDLER_ARGS); static int igb_sysctl_dmac(SYSCTL_HANDLER_ARGS); static int igb_sysctl_eee(SYSCTL_HANDLER_ARGS); =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _igb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t igb_poll; -#endif /* POLLING */ +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t igb_poll_unlocked; +static ndumplock_handler_t igb_ndump_disable_intr; +static ndumplock_handler_t igb_ndump_enable_intr; +#endif =20 /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ =20 +#ifdef NETDUMP_CLIENT + +static struct netdump_methods igb_ndump_methods =3D { + .ne_poll_locked =3D igb_poll, + .ne_poll_unlocked =3D igb_poll_unlocked, + .ne_disable_intr =3D igb_ndump_disable_intr, + .ne_enable_intr =3D igb_ndump_enable_intr +}; + +#endif + static device_method_t igb_methods[] =3D { /* Device interface */ DEVMETHOD(device_probe, igb_probe), @@ -1524,7 +1576,7 @@ igb_irq_fast(void *arg) return FILTER_HANDLED; } =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) #if __FreeBSD_version >=3D 800000 #define POLL_RETURN_COUNT(a) (a) static int @@ -1532,7 +1584,7 @@ static int #define POLL_RETURN_COUNT(a) static void #endif -igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +_igb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int = locking) { struct adapter *adapter =3D ifp->if_softc; struct igb_queue *que; @@ -1541,9 +1593,9 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int = count) u32 loop =3D IGB_MAX_LOOP; bool more; =20 - IGB_CORE_LOCK(adapter); + IGB_CORE_LOCK_COND(adapter, locking); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) { - IGB_CORE_UNLOCK(adapter); + IGB_CORE_UNLOCK_COND(adapter, locking); return POLL_RETURN_COUNT(rx_done); } =20 @@ -1556,15 +1608,15 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, = int count) if (reg_icr & E1000_ICR_RXO) adapter->rx_overruns++; } - IGB_CORE_UNLOCK(adapter); + IGB_CORE_UNLOCK_COND(adapter, locking); =20 for (int i =3D 0; i < adapter->num_queues; i++) { que =3D &adapter->queues[i]; txr =3D que->txr; =20 - igb_rxeof(que, count, &rx_done); + _igb_rxeof_generic(que, count, &rx_done, locking); =20 - IGB_TX_LOCK(txr); + IGB_TX_LOCK_COND(txr, locking); do { more =3D igb_txeof(txr); } while (loop-- && more); @@ -1575,12 +1627,50 @@ igb_poll(struct ifnet *ifp, enum poll_cmd cmd, = int count) if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) igb_start_locked(txr, ifp); #endif - IGB_TX_UNLOCK(txr); + IGB_TX_UNLOCK_COND(txr, locking); } =20 return POLL_RETURN_COUNT(rx_done); } -#endif /* DEVICE_POLLING */ +#endif /* DEVICE_POLLING || NETDUMP_CLIENT */ + +static int +igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_igb_poll_generic(ifp, cmd, count, 1)); +} + +#ifdef NETDUMP_CLIENT +static int +igb_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_igb_poll_generic(ifp, cmd, count, 0)); +} + +static void +igb_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + IGB_CORE_LOCK(adapter); + igb_disable_intr(adapter); + IGB_CORE_UNLOCK(adapter); +} + +static void +igb_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + IGB_CORE_LOCK(adapter); + igb_enable_intr(adapter); + IGB_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ =20 /********************************************************************* * @@ -3123,6 +3213,10 @@ igb_setup_interface(device_t dev, struct adapter = *adapter) IFQ_SET_READY(&ifp->if_snd); #endif =20 +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs =3D &igb_ndump_methods; +#endif + ether_ifattach(ifp, adapter->hw.mac.addr); =20 ifp->if_capabilities =3D ifp->if_capenable =3D 0; @@ -4811,7 +4905,7 @@ igb_rx_input(struct rx_ring *rxr, struct ifnet = *ifp, struct mbuf *m, u32 ptype) * Return TRUE if more to clean, FALSE otherwise *********************************************************************/ static bool -igb_rxeof(struct igb_queue *que, int count, int *done) +_igb_rxeof_generic(struct igb_queue *que, int count, int *done, int = locking) { struct adapter *adapter =3D que->adapter; struct rx_ring *rxr =3D que->rxr; @@ -4822,7 +4916,7 @@ igb_rxeof(struct igb_queue *que, int count, int = *done) u32 ptype, staterr =3D 0; union e1000_adv_rx_desc *cur; =20 - IGB_RX_LOCK(rxr); + IGB_RX_LOCK_COND(rxr, locking); /* Sync the ring. */ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); @@ -5007,7 +5101,7 @@ next_desc: if (done !=3D NULL) *done +=3D rxdone; =20 - IGB_RX_UNLOCK(rxr); + IGB_RX_UNLOCK_COND(rxr, locking); return ((staterr & E1000_RXD_STAT_DD) ? TRUE : FALSE); } =20 diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c index 57e88a4..d05dd00 100644 --- a/sys/dev/e1000/if_lem.c +++ b/sys/dev/e1000/if_lem.c @@ -34,6 +34,7 @@ =20 #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_netdump.h" =20 #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" @@ -72,6 +73,7 @@ #include #include #include +#include #include #include =20 @@ -83,6 +85,35 @@ #include "e1000_api.h" #include "if_lem.h" =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define EM_CORE_LOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_CORE_LOCK(adapter); = \ +} while (0) +#define EM_CORE_UNLOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_CORE_UNLOCK(adapter); = \ +} while (0) +#define EM_RX_LOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_RX_LOCK(adapter); = \ +} while (0) +#define EM_RX_UNLOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_RX_UNLOCK(adapter); = \ +} while (0) +#define EM_TX_LOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_TX_LOCK(adapter); = \ +} while (0) +#define EM_TX_UNLOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + EM_TX_UNLOCK(adapter); = \ +} while (0) + +#endif + /********************************************************************* * Legacy Em Driver version: *********************************************************************/ @@ -195,7 +226,8 @@ static void lem_txeof(struct adapter *); static void lem_tx_purge(struct adapter *); static int lem_allocate_receive_structures(struct adapter *); static int lem_allocate_transmit_structures(struct adapter *); -static bool lem_rxeof(struct adapter *, int, int *); +static bool _lem_rxeof_generic(struct adapter *, int, int *, int); +#define lem_rxeof(a, c, d) _lem_rxeof_generic(a, c, d, 1) #ifndef __NO_STRICT_ALIGNMENT static int lem_fixup_rx(struct adapter *); #endif @@ -247,14 +279,32 @@ static void lem_handle_link(void *context, = int pending); static void lem_add_rx_process_limit(struct adapter *, const char *, const char *, int *, int); =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _lem_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t lem_poll; -#endif /* POLLING */ +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t lem_poll_unlocked; +static ndumplock_handler_t lem_ndump_disable_intr; +static ndumplock_handler_t lem_ndump_enable_intr; +#endif =20 /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ =20 +#ifdef NETDUMP_CLIENT + +static struct netdump_methods lem_ndump_methods =3D { + .ne_poll_locked =3D lem_poll, + .ne_poll_unlocked =3D lem_poll_unlocked, + .ne_disable_intr =3D lem_ndump_disable_intr, + .ne_enable_intr =3D lem_ndump_enable_intr +}; + +#endif + static device_method_t lem_methods[] =3D { /* Device interface */ DEVMETHOD(device_probe, lem_probe), @@ -1225,21 +1275,21 @@ lem_init(void *arg) } =20 =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) /********************************************************************* * * Legacy polling routine =20 * *********************************************************************/ static int -lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +_lem_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int = locking) { struct adapter *adapter =3D ifp->if_softc; u32 reg_icr, rx_done =3D 0; =20 - EM_CORE_LOCK(adapter); + EM_CORE_LOCK_COND(adapter, locking); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) { - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); return (rx_done); } =20 @@ -1253,18 +1303,56 @@ lem_poll(struct ifnet *ifp, enum poll_cmd cmd, = int count) lem_local_timer, adapter); } } - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); =20 - lem_rxeof(adapter, count, &rx_done); + _lem_rxeof_generic(adapter, count, &rx_done, locking); =20 - EM_TX_LOCK(adapter); + EM_TX_LOCK_COND(adapter, locking); lem_txeof(adapter); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) lem_start_locked(ifp); - EM_TX_UNLOCK(adapter); + EM_TX_UNLOCK_COND(adapter, locking); return (rx_done); } -#endif /* DEVICE_POLLING */ + +static int +lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_lem_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +lem_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_lem_poll_generic(ifp, cmd, count, 0)); +} + +static void +lem_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + EM_CORE_LOCK(adapter); + lem_disable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} + +static void +lem_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + EM_CORE_LOCK(adapter); + lem_enable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ =20 /********************************************************************* * @@ -2363,6 +2451,9 @@ lem_setup_interface(device_t dev, struct adapter = *adapter) ifp->if_flags =3D IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl =3D lem_ioctl; ifp->if_start =3D lem_start; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs =3D &lem_ndump_methods; +#endif IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1); ifp->if_snd.ifq_drv_maxlen =3D adapter->num_tx_desc - 1; IFQ_SET_READY(&ifp->if_snd); @@ -3438,7 +3529,7 @@ lem_free_receive_structures(struct adapter = *adapter) * For polling we also now return the number of cleaned packets *********************************************************************/ static bool -lem_rxeof(struct adapter *adapter, int count, int *done) +_lem_rxeof_generic(struct adapter *adapter, int count, int *done, int = locking) { struct ifnet *ifp =3D adapter->ifp; struct mbuf *mp; @@ -3447,7 +3538,7 @@ lem_rxeof(struct adapter *adapter, int count, int = *done) int i, rx_sent =3D 0; struct e1000_rx_desc *current_desc; =20 - EM_RX_LOCK(adapter); + EM_RX_LOCK_COND(adapter, locking); i =3D adapter->next_rx_desc_to_check; current_desc =3D &adapter->rx_desc_base[i]; bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, @@ -3461,7 +3552,7 @@ lem_rxeof(struct adapter *adapter, int count, int = *done) if (!((current_desc->status) & E1000_RXD_STAT_DD)) { if (done !=3D NULL) *done =3D rx_sent; - EM_RX_UNLOCK(adapter); + EM_RX_UNLOCK_COND(adapter, locking); return (FALSE); } =20 @@ -3601,9 +3692,9 @@ discard: /* Call into the stack */ if (m !=3D NULL) { adapter->next_rx_desc_to_check =3D i; - EM_RX_UNLOCK(adapter); + EM_RX_UNLOCK_COND(adapter, locking); (*ifp->if_input)(ifp, m); - EM_RX_LOCK(adapter); + EM_RX_LOCK_COND(adapter, locking); rx_sent++; i =3D adapter->next_rx_desc_to_check; } @@ -3617,7 +3708,7 @@ discard: E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), i); if (done !=3D NULL) *done =3D rx_sent; - EM_RX_UNLOCK(adapter); + EM_RX_UNLOCK_COND(adapter, locking); return ((status & E1000_RXD_STAT_DD) ? TRUE : FALSE); } =20 diff --git a/sys/dev/ixgb/if_ixgb.c b/sys/dev/ixgb/if_ixgb.c index 2e62c1c..32b154a 100644 --- a/sys/dev/ixgb/if_ixgb.c +++ b/sys/dev/ixgb/if_ixgb.c @@ -35,10 +35,24 @@ POSSIBILITY OF SUCH DAMAGE. =20 #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" +#include "opt_netdump.h" #endif =20 #include =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define IXGB_LOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + IXGB_LOCK(adapter); = \ +} while (0) +#define IXGB_UNLOCK_COND(adapter, locking) do { = \ + if ((locking) !=3D 0) = \ + IXGB_UNLOCK(adapter); = \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -145,14 +159,32 @@ static int ixgb_dma_malloc(struct adapter *, bus_size_t, struct ixgb_dma_alloc *, int); static void ixgb_dma_free(struct adapter *, struct ixgb_dma_alloc = *); -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _ixgb_poll_generic(struct ifnet *ifp, enum poll_cmd = cmd, + int count, int locking); static poll_handler_t ixgb_poll; #endif +#ifdef NETDUMP_CLIENT +static poll_handler_t ixgb_poll_unlocked; +static ndumplock_handler_t ixgb_ndump_disable_intr; +static ndumplock_handler_t ixgb_ndump_enable_intr; +#endif =20 /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ =20 +#ifdef NETDUMP_CLIENT + +static struct netdump_methods ixgb_ndump_methods =3D { + .ne_poll_locked =3D ixgb_poll, + .ne_poll_unlocked =3D ixgb_poll_unlocked, + .ne_disable_intr =3D ixgb_ndump_disable_intr, + .ne_enable_intr =3D ixgb_ndump_enable_intr +}; + +#endif + static device_method_t ixgb_methods[] =3D { /* Device interface */ DEVMETHOD(device_probe, ixgb_probe), @@ -751,7 +783,7 @@ ixgb_init(void *arg) return; } =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) static int ixgb_poll_locked(struct ifnet * ifp, enum poll_cmd cmd, int count) { @@ -777,18 +809,57 @@ ixgb_poll_locked(struct ifnet * ifp, enum poll_cmd = cmd, int count) } =20 static int -ixgb_poll(struct ifnet * ifp, enum poll_cmd cmd, int count) +_ixgb_poll_generic(struct ifnet * ifp, enum poll_cmd cmd, int count, + int locking) { struct adapter *adapter =3D ifp->if_softc; int rx_npkts =3D 0; =20 - IXGB_LOCK(adapter); + IXGB_LOCK_COND(adapter, locking); if (ifp->if_drv_flags & IFF_DRV_RUNNING) rx_npkts =3D ixgb_poll_locked(ifp, cmd, count); - IXGB_UNLOCK(adapter); + IXGB_UNLOCK_COND(adapter, locking); return (rx_npkts); } -#endif /* DEVICE_POLLING */ + +static int +ixgb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgb_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +ixgb_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgb_poll_generic(ifp, cmd, count, 0)); +} + +static void +ixgb_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + IXGB_LOCK(adapter); + ixgb_disable_intr(adapter); + IXGB_UNLOCK(adapter); +} + +static void +ixgb_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + IXGB_LOCK(adapter); + ixgb_enable_intr(adapter); + IXGB_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ =20 /********************************************************************* * @@ -1356,6 +1427,9 @@ ixgb_setup_interface(device_t dev, struct adapter = * adapter) ifp->if_ioctl =3D ixgb_ioctl; ifp->if_start =3D ixgb_start; ifp->if_snd.ifq_maxlen =3D adapter->num_tx_desc - 1; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs =3D &ixgb_ndump_methods; +#endif =20 #if __FreeBSD_version < 500000 ether_ifattach(ifp, ETHER_BPF_SUPPORTED); diff --git a/sys/dev/ixgb/if_ixgb.h b/sys/dev/ixgb/if_ixgb.h index 4e88db7..a75b7b7 100644 --- a/sys/dev/ixgb/if_ixgb.h +++ b/sys/dev/ixgb/if_ixgb.h @@ -60,6 +60,9 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include =20 diff --git a/sys/dev/ixgbe/ixgbe.c b/sys/dev/ixgbe/ixgbe.c index 581dcc6..c1e88c7 100644 --- a/sys/dev/ixgbe/ixgbe.c +++ b/sys/dev/ixgbe/ixgbe.c @@ -35,8 +35,34 @@ =20 #include "opt_inet.h" #include "opt_inet6.h" +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#include "opt_netdump.h" +#endif + #include "ixgbe.h" =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define IXGBE_RX_LOCK_COND(rxr, locking) do { = \ + if ((locking) !=3D 0) = \ + IXGBE_RX_LOCK(rxr); = \ +} while (0) +#define IXGBE_RX_UNLOCK_COND(rxr, locking) do { = \ + if ((locking) !=3D 0) = \ + IXGBE_RX_UNLOCK(rxr); = \ +} while (0) +#define IXGBE_TX_LOCK_COND(txr, locking) do { = \ + if ((locking) !=3D 0) = \ + IXGBE_TX_LOCK(txr); = \ +} while (0) +#define IXGBE_TX_UNLOCK_COND(txr, locking) do { = \ + if ((locking) !=3D 0) = \ + IXGBE_TX_UNLOCK(txr); = \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -147,8 +173,9 @@ static void ixgbe_setup_hw_rsc(struct rx_ring *); static void ixgbe_enable_intr(struct adapter *); static void ixgbe_disable_intr(struct adapter *); static void ixgbe_update_stats_counters(struct adapter *); -static void ixgbe_txeof(struct tx_ring *); -static bool ixgbe_rxeof(struct ix_queue *); +static bool ixgbe_txeof(struct tx_ring *); +static bool _ixgbe_rxeof_generic(struct ix_queue *, int, int *, = int); +#define ixgbe_rxeof(a) _ixgbe_rxeof_generic(a, = (a)->rxr->process_limit, NULL, 1) static void ixgbe_rx_checksum(u32, struct mbuf *, u32); static void ixgbe_set_promisc(struct adapter *); static void ixgbe_set_multi(struct adapter *); @@ -207,10 +234,32 @@ static void ixgbe_reinit_fdir(void *, int); /* Missing shared code prototype */ extern void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw); =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _ixgbe_poll_generic(struct ifnet *ifp, enum poll_cmd = cmd, + int count, int locking); +static poll_handler_t ixgbe_poll; +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t ixgbe_poll_unlocked; +static ndumplock_handler_t ixgbe_ndump_disable_intr; +static ndumplock_handler_t ixgbe_ndump_enable_intr; +#endif + /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ =20 +#ifdef NETDUMP_CLIENT + +static struct netdump_methods ixgbe_ndump_methods =3D { + .ne_poll_locked =3D ixgbe_poll, + .ne_poll_unlocked =3D ixgbe_poll_unlocked, + .ne_disable_intr =3D ixgbe_ndump_disable_intr, + .ne_enable_intr =3D ixgbe_ndump_enable_intr +}; + +#endif + static device_method_t ixgbe_methods[] =3D { /* Device interface */ DEVMETHOD(device_probe, ixgbe_probe), @@ -667,6 +716,11 @@ ixgbe_detach(device_t dev) return (EBUSY); } =20 +#ifdef DEVICE_POLLING + if ((adapter->ifp->if_capenable & IFCAP_POLLING) !=3D 0) + ether_poll_deregister(adapter->ifp); +#endif + IXGBE_CORE_LOCK(adapter); ixgbe_stop(adapter); IXGBE_CORE_UNLOCK(adapter); @@ -1026,6 +1080,25 @@ ixgbe_ioctl(struct ifnet * ifp, u_long command, = caddr_t data) { int mask =3D ifr->ifr_reqcap ^ ifp->if_capenable; IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)"); +#ifdef DEVICE_POLLING + if ((mask & IFCAP_POLLING) !=3D 0) { + if ((ifr->ifr_reqcap & IFCAP_POLLING) !=3D 0) { + error =3D = ether_poll_register(ixgbe_poll, ifp); + if (error !=3D 0) + return (error); + IXGBE_CORE_LOCK(adapter); + ixgbe_disable_intr(adapter); + ifp->if_capenable |=3D IFCAP_POLLING; + IXGBE_CORE_UNLOCK(adapter); + } else { + error =3D ether_poll_deregister(ifp); + IXGBE_CORE_LOCK(adapter); + ixgbe_enable_intr(adapter); + ifp->if_capenable &=3D ~IFCAP_POLLING; + IXGBE_CORE_UNLOCK(adapter); + } + } +#endif /* !DEVICE_POLLING */ if (mask & IFCAP_HWCSUM) ifp->if_capenable ^=3D IFCAP_HWCSUM; if (mask & IFCAP_TSO4) @@ -1339,8 +1412,13 @@ ixgbe_init_locked(struct adapter *adapter) /* Set up VLAN support and filter */ ixgbe_setup_vlan_hw_support(adapter); =20 - /* And now turn on interrupts */ - ixgbe_enable_intr(adapter); +#ifdef DEVICE_POLLING + /* Disable interrupts if polling is on, enable otherwise. */ + if ((ifp->if_capenable & IFCAP_POLLING) !=3D 0) + ixgbe_disable_intr(adapter); + else +#endif + ixgbe_enable_intr(adapter); =20 /* Now inform the stack we're ready */ ifp->if_drv_flags |=3D IFF_DRV_RUNNING; @@ -1437,6 +1515,10 @@ ixgbe_handle_que(void *context, int pending) return; } =20 +#ifdef DEVICE_POLLING + if ((adapter->ifp->if_capenable & IFCAP_POLLING) !=3D 0) + return; +#endif =20 /********************************************************************* * @@ -2646,6 +2728,9 @@ ixgbe_setup_interface(device_t dev, struct adapter = *adapter) ifp->if_snd.ifq_drv_maxlen =3D adapter->num_tx_desc - 2; IFQ_SET_READY(&ifp->if_snd); #endif +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs =3D &ixgbe_ndump_methods; +#endif =20 ether_ifattach(ifp, adapter->hw.mac.addr); =20 @@ -2665,6 +2750,9 @@ ixgbe_setup_interface(device_t dev, struct adapter = *adapter) | IFCAP_VLAN_MTU | IFCAP_HWSTATS; ifp->if_capenable =3D ifp->if_capabilities; +#ifdef DEVICE_POLLING + ifp->if_capabilities |=3D IFCAP_POLLING; +#endif =20 /* ** Don't turn this on by default, if vlans are @@ -3582,6 +3670,91 @@ ixgbe_atr(struct tx_ring *txr, struct mbuf *mp) } #endif /* IXGBE_FDIR */ =20 +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int +_ixgbe_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, + int locking) +{ + struct adapter *adapter; + struct tx_ring *txr; + struct ix_queue *que; + struct ixgbe_hw *hw; + u32 loop, reg_eicr; + int rx_npkts; + bool more_tx; + + adapter =3D ifp->if_softc; + txr =3D adapter->tx_rings; + que =3D adapter->queues; + hw =3D &adapter->hw; + loop =3D MAX_LOOP; + rx_npkts =3D 0; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) =3D=3D 0) + return (rx_npkts); + + if (cmd =3D=3D POLL_AND_CHECK_STATUS) { + reg_eicr =3D IXGBE_READ_REG(hw, IXGBE_EICR); + + /* Link status change */ + if ((reg_eicr & IXGBE_EICR_LSC) !=3D 0) + taskqueue_enqueue(adapter->tq, = &adapter->link_task); + } + _ixgbe_rxeof_generic(que, count, &rx_npkts, locking); + IXGBE_TX_LOCK_COND(txr, locking); + do { + more_tx =3D ixgbe_txeof(txr); + } while (loop-- && more_tx); +#if __FreeBSD_version >=3D 800000 + if (!drbr_empty(ifp, txr->br)) + ixgbe_mq_start_locked(ifp, txr); +#else + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + ixgbe_start_locked(txr, ifp); +#endif + IXGBE_TX_UNLOCK_COND(txr, locking); + return (rx_npkts); +} + +static int +ixgbe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgbe_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +ixgbe_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgbe_poll_generic(ifp, cmd, count, 0)); +} + +static void +ixgbe_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + IXGBE_CORE_LOCK(adapter); + ixgbe_disable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); +} + +static void +ixgbe_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter =3D ifp->if_softc; + IXGBE_CORE_LOCK(adapter); + ixgbe_enable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ + /********************************************************************** * * Examine each tx_buffer in the used queue. If the hardware is done @@ -3589,7 +3762,7 @@ ixgbe_atr(struct tx_ring *txr, struct mbuf *mp) * tx_buffer is put back on the free queue. * = **********************************************************************/ -static void +static bool ixgbe_txeof(struct tx_ring *txr) { struct adapter *adapter =3D txr->adapter; @@ -3632,13 +3805,13 @@ ixgbe_txeof(struct tx_ring *txr) netmap_tx_irq(ifp, txr->me | (NETMAP_LOCKED_ENTER|NETMAP_LOCKED_EXIT)); } - return; + return FALSE; } #endif /* DEV_NETMAP */ =20 if (txr->tx_avail =3D=3D txr->num_desc) { txr->queue_status =3D IXGBE_QUEUE_IDLE; - return; + return FALSE; } =20 /* Get work starting point */ @@ -3735,7 +3908,7 @@ ixgbe_txeof(struct tx_ring *txr) if (txr->tx_avail =3D=3D txr->num_desc) txr->queue_status =3D IXGBE_QUEUE_IDLE; =20 - return; + return TRUE; } =20 /********************************************************************* @@ -4405,20 +4578,20 @@ ixgbe_rx_discard(struct rx_ring *rxr, int i) * Return TRUE for more work, FALSE for all clean. *********************************************************************/ static bool -ixgbe_rxeof(struct ix_queue *que) +_ixgbe_rxeof_generic(struct ix_queue *que, int count, int *rx_npktsp, + int locking) { struct adapter *adapter =3D que->adapter; struct rx_ring *rxr =3D que->rxr; struct ifnet *ifp =3D adapter->ifp; struct lro_ctrl *lro =3D &rxr->lro; struct lro_entry *queued; - int i, nextp, processed =3D 0; + int i, nextp, processed =3D 0, rx_npkts =3D = 0; u32 staterr =3D 0; - u16 count =3D rxr->process_limit; union ixgbe_adv_rx_desc *cur; struct ixgbe_rx_buf *rbuf, *nbuf; =20 - IXGBE_RX_LOCK(rxr); + IXGBE_RX_LOCK_COND(rxr, locking); =20 #ifdef DEV_NETMAP /* Same as the txeof routine: wakeup clients on intr. */ @@ -4588,6 +4761,7 @@ next_desc: if (sendmp !=3D NULL) { rxr->next_to_check =3D i; ixgbe_rx_input(rxr, ifp, sendmp, ptype); + rx_npkts++; i =3D rxr->next_to_check; } =20 @@ -4612,15 +4786,18 @@ next_desc: tcp_lro_flush(lro, queued); } =20 - IXGBE_RX_UNLOCK(rxr); + IXGBE_RX_UNLOCK_COND(rxr, locking); =20 + if (rx_npktsp !=3D NULL) + *rx_npktsp =3D rx_npkts; /* ** Still have cleaning to do? */ - if ((staterr & IXGBE_RXD_STAT_DD) !=3D 0) + if ((staterr & IXGBE_RXD_STAT_DD) !=3D 0) { return (TRUE); - else + } else { return (FALSE); + } } =20 =20 diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h index 77b72ed..f7278b4 100644 --- a/sys/dev/ixgbe/ixgbe.h +++ b/sys/dev/ixgbe/ixgbe.h @@ -66,6 +66,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include #include diff --git a/sys/i386/i386/minidump_machdep.c = b/sys/i386/i386/minidump_machdep.c index e0cd1ff..417d9a9 100644 --- a/sys/i386/i386/minidump_machdep.c +++ b/sys/i386/i386/minidump_machdep.c @@ -248,13 +248,19 @@ minidumpsys(struct dumperinfo *di) } dumpsize +=3D PAGE_SIZE; =20 - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * = 2) { - error =3D ENOSPC; - goto fail; + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error =3D ENOSPC; + goto fail; + } + dumplo =3D di->mediaoffset + di->mediasize - dumpsize; + dumplo -=3D sizeof(kdh) * 2; } - dumplo =3D di->mediaoffset + di->mediasize - dumpsize; - dumplo -=3D sizeof(kdh) * 2; progress =3D dumpsize; =20 /* Initialize mdhdr */ diff --git a/sys/ia64/ia64/dump_machdep.c b/sys/ia64/ia64/dump_machdep.c index 6fa8608..716c111 100644 --- a/sys/ia64/ia64/dump_machdep.c +++ b/sys/ia64/ia64/dump_machdep.c @@ -245,13 +245,20 @@ dumpsys(struct dumperinfo *di) dumpsize +=3D fileofs; hdrgap =3D fileofs - DEV_ALIGN(hdrsz); =20 - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * = 2) { - error =3D ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error =3D ENOSPC; + goto fail; + } + dumplo =3D di->mediaoffset + di->mediasize - dumpsize; + dumplo -=3D sizeof(kdh) * 2; } - dumplo =3D di->mediaoffset + di->mediasize - dumpsize; - dumplo -=3D sizeof(kdh) * 2; =20 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, = dumpsize, di->blocksize); =20 diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c index b120263..df9dd69 100644 --- a/sys/kern/kern_shutdown.c +++ b/sys/kern/kern_shutdown.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); =20 #include "opt_ddb.h" #include "opt_kdb.h" +#include "opt_netdump.h" #include "opt_panic.h" #include "opt_sched.h" #include "opt_watchdog.h" @@ -84,6 +85,15 @@ __FBSDID("$FreeBSD$"); #include #include =20 +#ifdef NETDUMP_CLIENT +#include + +#include +#include + +#include +#endif + #include =20 #ifndef PANIC_REBOOT_WAIT_TIME @@ -754,6 +764,17 @@ vpanic(const char *fmt, va_list ap) kern_reboot(bootopt); } =20 +#ifdef DDB +DB_COMMAND(netdump, ddb_force_netdump) +{ + + if (nd_enable) + netdumpsys(); + else + db_printf("netdump is not enabled\n"); +} +#endif + /* * Support for poweroff delay. * @@ -861,7 +882,9 @@ dump_write(struct dumperinfo *di, void *virtual, = vm_offset_t physical, off_t offset, size_t length) { =20 - if (length !=3D 0 && (offset < di->mediaoffset || + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) !=3D 0 && length !=3D 0 && + (offset < di->mediaoffset || offset - di->mediaoffset + length > di->mediasize)) { printf("Attempt to write outside dump device = boundaries.\n" "offset(%jd), mediaoffset(%jd), length(%ju), = mediasize(%jd).\n", diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 3288a4f..38fb118 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -73,6 +73,7 @@ struct carp_softc; struct ifvlantrunk; struct route; struct vnet; +struct netdump_methods; #endif =20 #include /* get TAILQ macros */ @@ -203,7 +204,7 @@ struct ifnet { char *if_description; /* interface description */ u_int if_fib; /* interface FIB */ u_char if_alloctype; /* if_type at time of allocation = */ - + struct netdump_methods *if_ndumpfuncs; /* netdump virtual = methods */ u_int if_hw_tsomax; /* tso burst length limit, the = minimum * is (IP_MAXPACKET / 8). * XXXAO: Have to find a better = place @@ -957,13 +958,13 @@ void if_deregister_com_alloc(u_char type); #define IF_LLADDR(ifp) = \ LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr)) =20 -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS }; =20 typedef int poll_handler_t(struct ifnet *ifp, enum poll_cmd cmd, = int count); int ether_poll_register(poll_handler_t *h, struct ifnet *ifp); int ether_poll_deregister(struct ifnet *ifp); -#endif /* DEVICE_POLLING */ +#endif /* DEVICE_POLLING || NETDUMP_CLIENT */ =20 #endif /* _KERNEL */ =20 diff --git a/sys/netinet/netdump.h b/sys/netinet/netdump.h new file mode 100644 index 0000000..fb0510b --- /dev/null +++ b/sys/netinet/netdump.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2005-2011 Sandvine Incorporated + * Copyright (c) 2000 Darrell Anderson + * 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 + +#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 + * 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 + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef DDB +#include +#endif + +#ifdef NETDUMP_CLIENT_DEBUG +#define NETDDEBUG(f, ...) printf((f), ## = __VA_ARGS__) +#define NETDDEBUG_IF(i, f, ...) if_printf((i), (f), ## = __VA_ARGS__) +#if NETDUMP_CLIENT_DEBUG > 1 +#define NETDDEBUGV(f, ...) printf((f), ## = __VA_ARGS__) +#define NETDDEBUGV_IF(i, f, ...) if_printf((i), (f), ## = __VA_ARGS__) +#else +#define NETDDEBUGV(f, ...) +#define NETDDEBUGV_IF(i, f, ...) +#endif +#else +#define NETDDEBUG(f, ...) +#define NETDDEBUG_IF(i, f, ...) +#define NETDDEBUGV(f, ...) +#define NETDDEBUGV_IF(i, f, ...) +#endif + +static void nd_handle_arp(struct mbuf **mb); +static void nd_handle_ip(struct mbuf **mb); +static int netdump_arp_server(void); +static void netdump_config_defaults(void *dummy __unused); +static int netdump_dumper(void *priv __unused, void *virtual, + vm_offset_t physical __unused, off_t offset, size_t = length); +static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp,=20= + struct ether_addr dst, u_short etype); +static int netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr = __unused, void *opt_args __unused); +static void netdump_network_poll(void); +static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m); +static int netdump_send(uint32_t type, off_t offset, unsigned char = *data, + uint32_t datalen); +static int netdump_send_arp(void); +static int netdump_udp_output(struct mbuf *m); + +static int sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS); +static int sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS); + +/* Must be at least as big as the chunks dumpsys() gives us. */ +static unsigned char buf[MAXDUMPPGS * PAGE_SIZE]; +static uint64_t rcvd_acks; +static uint32_t nd_seqno =3D 1; +static int dump_failed, have_server_mac; + +/* + * Times to poll the NIC (0.5ms each poll) before assuming packetloss + * occurred (default to 5s). + */ +static int nd_polls =3D 10000; + +/* Times to retransmit lost packets. */ +static int nd_retries =3D 10; + +/* General dynamic settings. */ +static char nd_ifp_str[IFNAMSIZ]; +static struct ether_addr nd_gw_mac; +static struct in_addr nd_server =3D {INADDR_ANY}; +static struct in_addr nd_client =3D {INADDR_ANY}; +static struct in_addr nd_gw =3D {INADDR_ANY}; +struct ifnet *nd_ifp; +int nd_enable =3D 0; +static uint16_t nd_server_port =3D NETDUMP_PORT; + +/* Tunables storages. */ +static char nd_server_tun[INET_ADDRSTRLEN]; +static char nd_client_tun[INET_ADDRSTRLEN]; +static char nd_gw_tun[INET_ADDRSTRLEN]; +static char nd_nic_tun[IFNAMSIZ]; + +/* + * Checks for netdump support on a network interface + * + * Parameters: + * ifp The network interface that is being tested for support + * + * Returns: + * int 1 if the interface is supported, 0 if not + */ +static __inline int +netdump_supported_nic(struct ifnet *ifp) +{ + + return (ifp->if_ndumpfuncs !=3D NULL); +} + +/*- + * Sysctls specific code. + */ + +/* + * Sysctl handler converting a string sysctl to/from an ifaddr name. + * + * Parameters: + * SYSCTL_HANDLER_ARGS + * - arg1 is a pointer to the if structure name + * - arg2 is unused + * + * Returns: + * int see errno.h, 0 for success + */ +static int +sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS) +{ + char buf[IFNAMSIZ]; + struct ifnet *ifp; + int error, found; + + strlcpy(buf, arg1, sizeof(buf)); + error =3D sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error !=3D 0 || req->newptr =3D=3D NULL) + return (error); + found =3D 0; + IFNET_RLOCK_NOSLEEP(); + TAILQ_FOREACH(ifp, &V_ifnet, if_link) { + if (!strncmp(ifp->if_xname, buf, strlen(ifp->if_xname)) = && + netdump_supported_nic(ifp)) { + found =3D 1; + break; + } + } + IFNET_RUNLOCK_NOSLEEP(); + if (found =3D=3D 0) + return (EINVAL); + strlcpy(arg1, buf, strlen(buf) + 1); + return (error); +} + +/* + * Sysctl handler converting a string sysctl to/from an in_addr. + * + * Parameters: + * SYSCTL_HANDLER_ARGS + * - arg1 is a pointer to the struct in_addr holding the IP + * - arg2 is unused + * + * Returns: + * int see errno.h, 0 for success + */ +static int +sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS) +{ + struct in_addr addr; + char buf[INET_ADDRSTRLEN]; + int error; + + inet_ntoa_r(*(struct in_addr *)arg1, buf); + error =3D sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error =3D=3D 0) { + if (!inet_aton(buf, &addr)) + error =3D EINVAL; + else + *(struct in_addr *)arg1 =3D addr; + } + return (error); +} + +SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, = "netdump"); +SYSCTL_PROC(_net_dump, OID_AUTO, server, CTLTYPE_STRING | CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_server, 0, sysctl_handle_inaddr, "A", "dump = server"); +SYSCTL_PROC(_net_dump, OID_AUTO, client, CTLTYPE_STRING |CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_client, 0, sysctl_handle_inaddr, "A", "dump = client"); +SYSCTL_PROC(_net_dump, OID_AUTO, gateway, CTLTYPE_STRING | CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_gw, 0, sysctl_handle_inaddr, "A", + "dump default gateway"); +SYSCTL_PROC(_net_dump, OID_AUTO, nic, CTLTYPE_STRING | CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_ifp_str, 0, sysctl_handle_ifxname, "A", + "dumping interface name"); +SYSCTL_INT(_net_dump, OID_AUTO, polls, CTLTYPE_INT | CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_polls, 0, "times to poll NIC per retry"); +SYSCTL_INT(_net_dump, OID_AUTO, retries, CTLTYPE_INT | CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_retries, 0, "times to retransmit lost = packets"); +SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT | CTLFLAG_RW | + CTLFLAG_MPSAFE, &nd_enable, 0, "enable network dump"); + +TUNABLE_STR("net.dump.server", nd_server_tun, sizeof(nd_server_tun)); +TUNABLE_STR("net.dump.client", nd_client_tun, sizeof(nd_client_tun)); +TUNABLE_STR("net.dump.gateway", nd_gw_tun, sizeof(nd_gw_tun)); +TUNABLE_STR("net.dump.nic", nd_nic_tun, sizeof(nd_nic_tun)); +TUNABLE_INT("net.dump.enable", &nd_enable); + +/*- + * Network specific primitives. + * Following down the code they are divided ordered as: + * - Output primitives + * - Input primitives + * - Polling primitives + */ + +/* + * Netdump wraps external mbufs around address ranges. unlike most = sane + * counterparts, netdump uses a stop-and-wait approach to flow control = and + * retransmission, so the ack obviates the need for mbuf reference + * counting. We still need to tell other mbuf handlers not to do = anything + * special with our mbufs, so specify this nop handler. + * + * Parameters: + * ptr data to free (ignored) + * opt_args callback pointer (ignored) + * + * Returns: + * void + */ +static int +netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr __unused, void = *opt_args __unused) +{ + return EXT_FREE_OK; +} + +/* + * Handles creation of the ethernet header, then places outgoing = packets into + * the tx buffer for the NIC + * + * Parameters: + * m The mbuf containing the packet to be sent (will be freed = by + * this function or the NIC driver) + * ifp The interface to send on + * dst The destination ethernet address (source address will be = looked + * up using ifp) + * etype The ETHERTYPE_* value for the protocol that is being = sent + * + * Returns: + * int see errno.h, 0 for success + */ +static int +netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct = ether_addr dst, + u_short etype) +{ + struct ether_header *eh; + + /* Fill in the ethernet header. */ + M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); + if (m =3D=3D NULL) { + printf("netdump_ether_output: Out of mbufs\n"); + return (ENOBUFS); + } + eh =3D mtod(m, struct ether_header *); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); + eh->ether_type =3D htons(etype); + + if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) !=3D IFF_UP) || + (ifp->if_drv_flags & IFF_DRV_RUNNING) !=3D IFF_DRV_RUNNING) = { + if_printf(ifp, "netdump_ether_output: Interface isn't = up\n"); + m_freem(m); + return (ENETDOWN); + } + return ((ifp->if_transmit)(ifp, m)); +} + +/* + * Unreliable transmission of an mbuf chain to the netdump server + * Note: can't handle fragmentation; fails if the packet is larger than + * nd_ifp->if_mtu after adding the UDP/IP headers + * + * Parameters: + * m mbuf chain + * + * Returns: + * int see errno.h, 0 for success + */ +static int +netdump_udp_output(struct mbuf *m) +{ + struct udpiphdr *ui; + struct ip *ip; + + MPASS(nd_ifp !=3D NULL); + + M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT); + if (m =3D=3D NULL) { + printf("netdump_udp_output: Out of mbufs\n"); + return (ENOBUFS); + } + ui =3D mtod(m, struct udpiphdr *); + bzero(ui->ui_x1, sizeof(ui->ui_x1)); + ui->ui_pr =3D IPPROTO_UDP; + ui->ui_len =3D htons(m->m_pkthdr.len - sizeof(struct ip)); + ui->ui_ulen =3D ui->ui_len; + ui->ui_src =3D nd_client; + ui->ui_dst =3D nd_server; + /* Use this src port so that the server can connect() the socket = */ + ui->ui_sport =3D htons(NETDUMP_ACKPORT); + ui->ui_dport =3D htons(nd_server_port); + ui->ui_sum =3D 0; + if ((ui->ui_sum =3D in_cksum(m, m->m_pkthdr.len)) =3D=3D 0) + ui->ui_sum =3D 0xffff; + + ip =3D mtod(m, struct ip *); + ip->ip_v =3D IPVERSION; + ip->ip_hl =3D sizeof(struct ip) >> 2; + ip->ip_tos =3D 0; + ip->ip_len =3D htons(m->m_pkthdr.len); + ip->ip_id =3D 0; + ip->ip_off =3D htons(IP_DF); + ip->ip_ttl =3D 32; + ip->ip_sum =3D 0; + ip->ip_sum =3D in_cksum(m, sizeof(struct ip)); + + if (m->m_pkthdr.len > nd_ifp->if_mtu) { + printf("netdump_udp_output: Packet is too big: %u > MTU = %lu\n", + m->m_pkthdr.len, (unsigned long)nd_ifp->if_mtu); + m_freem(m); + return (ENOBUFS); + } + return (netdump_ether_output(m, nd_ifp, nd_gw_mac, = ETHERTYPE_IP)); +} + +/* + * Builds and sends a single ARP request to locate the server + * + * Parameters: + * void + * + * Return value: + * 0 on success + * errno on error + */ +static int +netdump_send_arp() +{ + struct ether_addr bcast; + struct mbuf *m; + struct arphdr *ah; + int pktlen; + + MPASS(nd_ifp !=3D NULL); + + /* Fill-up a broadcast address. */ + memset(&bcast, 0xFF, ETHER_ADDR_LEN); + MGETHDR(m, M_NOWAIT, MT_DATA); + if (m =3D=3D NULL) { + printf("netdump_send_arp: Out of mbufs"); + return (ENOBUFS); + } + pktlen =3D arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); + m->m_len =3D pktlen; + m->m_pkthdr.len =3D pktlen; + MH_ALIGN(m, pktlen); + ah =3D mtod(m, struct arphdr *); + ah->ar_hrd =3D htons(ARPHRD_ETHER); + ah->ar_pro =3D htons(ETHERTYPE_IP); + ah->ar_hln =3D ETHER_ADDR_LEN; + ah->ar_pln =3D sizeof(struct in_addr); + ah->ar_op =3D htons(ARPOP_REQUEST); + memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN); + memcpy(ar_spa(ah), &nd_client.s_addr, sizeof(nd_client.s_addr)); + bzero(ar_tha(ah), ETHER_ADDR_LEN); + memcpy(ar_tpa(ah), &nd_gw.s_addr, sizeof(nd_gw.s_addr)); + + return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP)); +} + +/* + * Sends ARP requests to locate the server and waits for a response + * + * Parameters: + * void + * + * Return value: + * 0 on success + * errno on error + */ +static int +netdump_arp_server() +{ + int err, polls, retries; + + for (retries =3D 0; retries < nd_retries && have_server_mac =3D=3D= 0; + retries++) { + err =3D netdump_send_arp(); + if (err !=3D 0) + return (err); + for (polls =3D 0; polls < nd_polls && have_server_mac =3D=3D= 0; + polls++) { + netdump_network_poll(); + DELAY(500); + } + if (have_server_mac =3D=3D 0) + printf("(ARP retry)"); + } + if (have_server_mac !=3D 0) + return (0); + + printf("\nARP timed out.\n"); + return (ETIMEDOUT); +} + +/* + * Construct and reliably send a netdump packet. May fail from a = resource + * shortage or extreme number of unacknowledged retransmissions. Wait = for + * an acknowledgement before returning. Splits packets into chunks = small + * enough to be sent without fragmentation (looks up the interface MTU) + * + * Parameters: + * type netdump packet type (HERALD, FINISHED, or VMCORE) + * offset vmcore data offset (bytes) + * data vmcore data + * datalen vmcore data size (bytes) + * + * Returns: + * int see errno.h, 0 for success + */ +static int +netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t = datalen) +{ + uint64_t want_acks; + struct netdump_msg_hdr *nd_msg_hdr; + struct mbuf *m, *m2; + uint32_t i, pktlen, sent_so_far; + int retries, polls, error; + + want_acks =3D 0; + rcvd_acks =3D 0; + retries =3D 0; + + MPASS(nd_ifp !=3D NULL); + +retransmit: + + /* Chunks can be too big to fit in packets. */ + for (i =3D sent_so_far =3D 0; sent_so_far < + datalen || (i =3D=3D 0 && datalen =3D=3D 0); i++) { + pktlen =3D datalen - sent_so_far; + + /* First bound: the packet structure. */ + pktlen =3D min(pktlen, NETDUMP_DATASIZE); + + /* Second bound: the interface MTU (assume no IP = options). */ + pktlen =3D min(pktlen, nd_ifp->if_mtu - sizeof(struct = udpiphdr) - + sizeof(struct netdump_msg_hdr)); + + /* + * Check if it is retransmitting and this has been ACKed + * already. + */ + if ((rcvd_acks & (1 << i)) !=3D 0) { + sent_so_far +=3D pktlen; + continue; + } + + /* + * Get and fill a header mbuf, then chain data as an = extended + * mbuf. + */ + MGETHDR(m, M_NOWAIT, MT_DATA); + if (m =3D=3D NULL) { + printf("netdump_send: Out of mbufs!\n"); + return (ENOBUFS); + } + m->m_len =3D sizeof(struct netdump_msg_hdr); + m->m_pkthdr.len =3D sizeof(struct netdump_msg_hdr); + MH_ALIGN(m, sizeof(struct netdump_msg_hdr)); + nd_msg_hdr =3D mtod(m, struct netdump_msg_hdr *); + nd_msg_hdr->mh_seqno =3D htonl(nd_seqno+i); + nd_msg_hdr->mh_type =3D htonl(type); + nd_msg_hdr->mh_offset =3D htobe64(offset + sent_so_far); + nd_msg_hdr->mh_len =3D htonl(pktlen); + nd_msg_hdr->mh__pad =3D 0; + + if (pktlen !=3D 0) { + m2 =3D m_get(M_NOWAIT, MT_DATA); + if (m2 =3D=3D NULL) { + m_freem(m); + printf("netdump_send: Out of mbufs!\n"); + return (ENOBUFS); + } + MEXTADD(m2, data+sent_so_far, pktlen, = netdump_mbuf_nop, + NULL, NULL, M_RDONLY, EXT_MOD_TYPE); + m2->m_len =3D pktlen; + m->m_next =3D m2; + m->m_pkthdr.len +=3D m2->m_len; + } + error =3D netdump_udp_output(m); + if (error !=3D 0) + return (error); + + /* Note that we're waiting for this packet in the = bitfield. */ + want_acks |=3D 1 << i; + sent_so_far +=3D pktlen; + } + if (i >=3D sizeof(want_acks) * 8) + printf("Warning: Sent more than %zd packets (%d). " + "Acknowledgements will fail unless the size of " + "rcvd_acks/want_acks is increased.\n", + sizeof(want_acks) * 8, i); + + /* + * Wait for acks. A *real* window would speed things up = considerably. + */ + polls =3D 0; + while (rcvd_acks !=3D want_acks) { =09 + if (polls++ > nd_polls) { + if (retries++ > nd_retries) + return (ETIMEDOUT); + printf(". "); + goto retransmit; + } + netdump_network_poll(); + DELAY(500); + } + nd_seqno +=3D i; + return (0); +} + +/* + * Handler for IP packets: checks their sanity and then processes any = netdump + * ACK packets it finds. + * + * It needs to replicate partially the behaviour of ip_input() and + * udp_input(). + * + * Parameters: + * mb a pointer to an mbuf * containing the packet received + * Updates *mb if m_pullup et al change the pointer + * Assumes the calling function will take care of freeing = the mbuf + * + * Return value: + * void + */ +static void +nd_handle_ip(struct mbuf **mb) +{ + struct ip *ip; + struct udpiphdr *udp; + struct netdump_ack *nd_ack; + struct mbuf *m; + int rcv_ackno; + unsigned short hlen; + + /* IP processing. */ + m =3D *mb; + if (m->m_pkthdr.len < sizeof(struct ip)) { + NETDDEBUG("nd_handle_ip: dropping packet too small for = IP " + "header\n"); + return; + } + if (m->m_len < sizeof(struct ip)) { + m =3D m_pullup(m, sizeof(struct ip)); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("nd_handle_ip: m_pullup failed\n"); + return; + } + } + ip =3D mtod(m, struct ip *); + + /* IP version. */ + if (ip->ip_v !=3D IPVERSION) { + NETDDEBUG("nd_handle_ip: Bad IP version %d\n", = ip->ip_v); + return; + } + + /* Header length. */ + hlen =3D ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) { + NETDDEBUG("nd_handle_ip: Bad IP header length (%hu)\n", = hlen); + return; + } + if (hlen > m->m_len) { + m =3D m_pullup(m, hlen); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("nd_handle_ip: m_pullup failed\n"); + return; + } + ip =3D mtod(m, struct ip *); + } + +#ifdef INVARIANTS + if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D = IN_LOOPBACKNET || + (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D = IN_LOOPBACKNET) && + (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) =3D=3D 0) { + NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n"); + return; + } +#endif + + /* Checksum. */ + if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) !=3D 0) { + if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) =3D=3D 0) { + NETDDEBUG("nd_handle_ip: Bad IP checksum\n"); + return; + } + } else + NETDDEBUG("nd_handle_ip: HW didn't check IP cksum\n"); + + /* Convert fields to host byte order. */ + ip->ip_len =3D ntohs(ip->ip_len); + if (ip->ip_len < hlen) { + NETDDEBUG("nd_handle_ip: IP packet smaller (%hu) than header = (%hu)\n", + ip->ip_len, hlen); + return; + } + ip->ip_off =3D ntohs(ip->ip_off); + + if (m->m_pkthdr.len < ip->ip_len) { +NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) than ethernet packet = (%hu)\n", + ip->ip_len, m->m_pkthdr.len); + return; + } + if (m->m_pkthdr.len > ip->ip_len) { + + /* Truncate the packet to the IP length. */ + if (m->m_len =3D=3D m->m_pkthdr.len) { + m->m_len =3D ip->ip_len; + m->m_pkthdr.len =3D ip->ip_len; + } else + m_adj(m, ip->ip_len - m->m_pkthdr.len); + } + + /* Ignore packets with IP options. */ + if (hlen > sizeof(struct ip)) { + NETDDEBUG("nd_handle_ip: Drop packet with IP = options\n"); + return; + } + + /* Check that the source is the server's IP. */ + if (ip->ip_src.s_addr !=3D nd_server.s_addr) { + NETDDEBUG("nd_handle_ip: Drop packet not from = server\n"); + return; + } + + /* Check if the destination IP is ours. */ + if (ip->ip_dst.s_addr !=3D nd_client.s_addr) { + NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n"); + return; + } + + if (ip->ip_p !=3D IPPROTO_UDP) { + NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n"); + return; + } + + /* Do not deal with fragments. */ + if ((ip->ip_off & (IP_MF | IP_OFFMASK)) !=3D 0) { + NETDDEBUG("nd_handle_ip: Drop fragmented packet\n"); + return; + } + + /* UDP custom is to have packet length not include IP header. */ + ip->ip_len -=3D hlen; + + /* UDP processing. */ + + /* Get IP and UDP headers together, along with the netdump = packet. */ + if (m->m_pkthdr.len < + sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { + NETDDEBUG("nd_handle_ip: Ignoring small packet\n"); + return; + } + if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct = netdump_ack)) { + m =3D m_pullup(m, sizeof(struct udpiphdr) + + sizeof(struct netdump_ack)); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("nd_handle_ip: m_pullup failed\n"); + return; + } + } + udp =3D mtod(m, struct udpiphdr *); + + if (ntohs(udp->ui_u.uh_dport) !=3D NETDUMP_ACKPORT) { + NETDDEBUG("not on the netdump port.\n"); + return; + } + + /* Netdump processing. */ + + /* + * Packet is meant for us. Extract the ack sequence number and = the + * port number if necessary. + */ + nd_ack =3D (struct netdump_ack *)(mtod(m, caddr_t) + + sizeof(struct udpiphdr)); + rcv_ackno =3D ntohl(nd_ack->na_seqno); + if (nd_server_port =3D=3D NETDUMP_PORT) + nd_server_port =3D ntohs(udp->ui_u.uh_sport); + if (rcv_ackno >=3D nd_seqno + 64) + printf("nd_handle_ip: ACK %d too far in future!\n", = rcv_ackno); + else if (rcv_ackno >=3D nd_seqno) { + + /* We're interested in this ack. Record it. */ + rcvd_acks |=3D 1 << (rcv_ackno-nd_seqno); + } +} + +/* + * Handler for ARP packets: checks their sanity and then + * 1. If the ARP is a request for our IP, respond with our MAC address + * 2. If the ARP is a response from our server, record its MAC address + * + * It needs to replicate partially the behaviour of arpintr() and + * in_arpinput(). + * + * Parameters: + * mb a pointer to an mbuf * containing the packet received + * Updates *mb if m_pullup et al change the pointer + * Assumes the calling function will take care of freeing = the mbuf + * + * Return value: + * void + */ +static void +nd_handle_arp(struct mbuf **mb) +{ + char buf[INET_ADDRSTRLEN]; + struct in_addr isaddr, itaddr, myaddr; + struct ether_addr dst; + struct mbuf *m; + struct arphdr *ah; + struct ifnet *ifp; + uint8_t *enaddr; + int req_len, op; + + m =3D *mb; + ifp =3D m->m_pkthdr.rcvif; + if (m->m_len < sizeof(struct arphdr)) { + m =3D m_pullup(m, sizeof(struct arphdr)); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("nd_handle_arp: runt packet: m_pullup = failed\n"); + return; + } + } + ah =3D mtod(m, struct arphdr *); + + if (ntohs(ah->ar_hrd) !=3D ARPHRD_ETHER) { + NETDDEBUG("nd_handle_arp: unknown hardware address = 0x%2D)\n", + (unsigned char *)&ah->ar_hrd, ""); + return; + } + if (ntohs(ah->ar_pro) !=3D ETHERTYPE_IP) { + NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol = %d\n", + ntohs(ah->ar_pro)); + return; + } + req_len =3D arphdr_len2(ifp->if_addrlen, sizeof(struct = in_addr)); + if (m->m_len < req_len) { + m =3D m_pullup(m, req_len); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("nd_handle_arp: runt packet: m_pullup = failed\n"); + return; + } + } + ah =3D mtod(m, struct arphdr *); + + op =3D ntohs(ah->ar_op); + memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); + memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr)); + enaddr =3D (uint8_t *)IF_LLADDR(ifp); + myaddr =3D nd_client; + + if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) { + NETDDEBUG("nd_handle_arp: ignoring ARP from myself\n"); + return; + } + + if (isaddr.s_addr =3D=3D nd_client.s_addr) { + printf("nd_handle_arp: %*D is using my IP address = %s!\n", + ifp->if_addrlen, (u_char *)ar_sha(ah), ":", + inet_ntoa(isaddr)); + return; + } + + if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) { + NETDDEBUG("nd_handle_arp: ignoring ARP from broadcast = address\n"); + return; + } + + if (op =3D=3D ARPOP_REPLY) { + if (isaddr.s_addr !=3D nd_gw.s_addr) { + inet_ntoa_r(isaddr, buf); +NETDDEBUG("nd_handle_arp: ignoring ARP reply from %s (not netdump = server)\n", + buf); + return; + } + memcpy(nd_gw_mac.octet, ar_sha(ah), + min(ah->ar_hln, ETHER_ADDR_LEN)); + have_server_mac =3D 1; + NETDDEBUG("\nnd_handle_arp: Got server MAC address = %6D\n", + nd_gw_mac.octet, ":"); + return; + } + + if (op !=3D ARPOP_REQUEST) { + NETDDEBUG("nd_handle_arp: Ignoring ARP = non-request/reply\n"); + return; + } + + if (itaddr.s_addr !=3D nd_client.s_addr) { + NETDDEBUG("nd_handle_arp: ignoring ARP not to our = IP\n"); + return; + } + + memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); + memcpy(ar_sha(ah), enaddr, ah->ar_hln); + memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); + memcpy(ar_spa(ah), &itaddr, ah->ar_pln); + ah->ar_op =3D htons(ARPOP_REPLY); + ah->ar_pro =3D htons(ETHERTYPE_IP); + m->m_flags &=3D ~(M_BCAST|M_MCAST); + m->m_len =3D sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln); + m->m_pkthdr.len =3D m->m_len; + + memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN); + netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP); + *mb =3D NULL; +} + +/* + * Handler for incoming packets directly from the network adapter + * Identifies the packet type (IP or ARP) and passes it along to one of = the + * helper functions nd_handle_ip or nd_handle_arp. + * + * It needs to replicate partially the behaviour of ether_input() and + * ether_demux(). + * + * Parameters: + * ifp the interface the packet came from (should be nd_ifp) + * m an mbuf containing the packet received + * + * Return value: + * void + */ +static void +netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) +{ + struct ether_header *eh; + u_short etype; + + /* Ethernet processing. */ + if ((m->m_flags & M_PKTHDR) =3D=3D 0) { + NETDDEBUG_IF(ifp, + "netdump_pkt_in: Discard frame without packet = header\n"); + goto done; + } + if (m->m_len < ETHER_HDR_LEN) { + NETDDEBUG_IF(ifp, +"netdump_pkt_in: Discard frame without leading eth header (len %u = pktlen %u)\n", + m->m_len, m->m_pkthdr.len); + goto done; + } + if ((m->m_flags & M_HASFCS) !=3D 0) { + m_adj(m, -ETHER_CRC_LEN); + m->m_flags &=3D ~M_HASFCS; + } + eh =3D mtod(m, struct ether_header *); + m->m_pkthdr.PH_loc.ptr =3D eh; + etype =3D ntohs(eh->ether_type); + if ((m->m_flags & M_VLANTAG) !=3D 0 || etype =3D=3D = ETHERTYPE_VLAN) { + NETDDEBUG_IF(ifp, "netdump_pkt_in: Ignoring vlan = packets\n"); + goto done; + } + + /* XXX: Probably must also check if we're the recipient MAC = address. */ + + /* Done ethernet processing. Strip off the ethernet header. */ + m_adj(m, ETHER_HDR_LEN); + switch (etype) { + case ETHERTYPE_ARP: + nd_handle_arp(&m); + break; + case ETHERTYPE_IP: + nd_handle_ip(&m); + break; + default: + NETDDEBUG_IF(ifp, + "netdump_pkt_in: Dropping unknown ethertype = %hu\n", + etype); + break; + } +done: + if (m !=3D NULL) + m_freem(m); +} + +/* + * After trapping, instead of assuming that most of the network stack = is sane + * just poll the driver directly for packets. + * + * Parameters: + * void + * + * Returns: + * void + */ +static void +netdump_network_poll() +{ + + MPASS(nd_ifp !=3D NULL); + +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr !=3D NULL) + nd_ifp->if_ndumpfuncs->ne_poll_unlocked(nd_ifp, + POLL_AND_CHECK_STATUS, 1000); + else +#endif + nd_ifp->if_ndumpfuncs->ne_poll_locked(nd_ifp, + POLL_AND_CHECK_STATUS, 1000); +} + +/*- + * Dumping specific primitives. + */ + +/* + * Callback from dumpsys() to dump a chunk of memory. + * Copies it out to our static buffer then sends it across the network. + * Detects the initial KDH and makes sure it is given a special packet = type. + * + * Parameters: + * priv Unused. Optional private pointer. + * virtual Virtual address (where to read the data from) + * physical Unused. Physical memory address. + * offset Offset from start of core file + * length Data length + * + * Return value: + * 0 on success + * errno on error + */ +static int +netdump_dumper(void *priv __unused, void *virtual, + vm_offset_t physical __unused, off_t offset, size_t length) +{ + int err, msgtype; + + NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n", + virtual, (uintmax_t)offset, length); + + if (length > sizeof(buf)) + return (ENOSPC); + + /* + * The first write (at offset 0) is the kernel dump header. = Flag it + * for the server to treat specially. + * XXX: This doesn't strip out the footer KDH, although it + * should not hurt anything. + */ + msgtype =3D NETDUMP_VMCORE; + if (offset =3D=3D 0 && length > 0) + msgtype =3D NETDUMP_KDH; + else if (offset > 0) + offset -=3D sizeof(struct kerneldumpheader); + memcpy(buf, virtual, length); + err =3D netdump_send(msgtype, offset, buf, length); + if (err !=3D 0) { + dump_failed =3D 1; + return (err); + } + return (0); +} + +/* + * Dumper routine, specular to dumpsys(). + * + * Parameters: + * void + * + * Returns: + * int see errno.h, 0 for success + */ +int +netdumpsys() +{ + struct dumperinfo dumper; + void (*old_if_input)(struct ifnet *, struct mbuf *); + int error, found, must_lock, nd_gw_unset; + + old_if_input =3D NULL; + error =3D 0; + found =3D 0; + nd_gw_unset =3D 0; + must_lock =3D 1; +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr !=3D NULL) + must_lock =3D 0; +#endif + + /* Check if the dumping is allowed to continue. */ + if (nd_enable =3D=3D 0) + return (EINVAL); + + /* Lookup the right if device to be used in the dump. */ + if (must_lock !=3D 0) + IFNET_RLOCK_NOSLEEP(); + TAILQ_FOREACH(nd_ifp, &V_ifnet, if_link) { + if (!strncmp(nd_ifp->if_xname, nd_ifp_str, + strlen(nd_ifp->if_xname)) && + netdump_supported_nic(nd_ifp)) { + found =3D 1; + break; + } + } + if (must_lock !=3D 0) + IFNET_RUNLOCK_NOSLEEP(); + if (found =3D=3D 0) { + printf("netdumpsys: Can't netdump: no valid NIC = given\n"); + return (EINVAL); + } + + MPASS(nd_ifp !=3D NULL); + + if (nd_server.s_addr =3D=3D INADDR_ANY) { + printf("netdumpsys: Can't netdump; no server IP = given\n"); + return (EINVAL); + } + if (nd_client.s_addr =3D=3D INADDR_ANY) { + printf("netdumpsys: Can't netdump; no client IP = given\n"); + return (EINVAL); + } + + /* + * nd_server_port could have switched after the first ack the + * first time it gets called. Adjust it accordingly. + */ + nd_server_port =3D NETDUMP_PORT; + if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock = !=3D 0) + nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp); + + /* Make the card use *our* receive callback. */ + old_if_input =3D nd_ifp->if_input; + nd_ifp->if_input =3D netdump_pkt_in; + + if (nd_gw.s_addr =3D=3D INADDR_ANY) { + nd_gw.s_addr =3D nd_server.s_addr; + nd_gw_unset =3D 1; + } + printf("\n-----------------------------------\n"); + printf("netdump in progress. searching for server.. "); + if (netdump_arp_server()) { + printf("Failed to locate server MAC address\n"); + error =3D EINVAL; + goto trig_abort; + } + if (netdump_send(NETDUMP_HERALD, 0, NULL, 0) !=3D 0) { + printf("Failed to contact netdump server\n"); + error =3D EINVAL; + goto trig_abort; + } + printf("dumping to %s (%6D)\n", inet_ntoa(nd_server), = nd_gw_mac.octet, + ":"); + printf("-----------------------------------\n"); + + /* Call the dumping routine. */ + dumper.dumper =3D netdump_dumper; + dumper.priv =3D NULL; + dumper.blocksize =3D NETDUMP_DATASIZE; + dumper.mediasize =3D 0; + dumper.mediaoffset =3D 0; + dumpsys(&dumper); + if (dump_failed !=3D 0) { + printf("Failed to dump the actual raw datas\n"); + error =3D EINVAL; + goto trig_abort; + } + if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) !=3D 0) { + printf("Failed to close the transaction\n"); + error =3D EINVAL; + goto trig_abort; + } + printf("\nnetdump finished.\n"); +trig_abort: + if (nd_gw_unset !=3D 0) + nd_gw.s_addr =3D INADDR_ANY; + if (old_if_input) + nd_ifp->if_input =3D old_if_input; + if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock = !=3D 0) + nd_ifp->if_ndumpfuncs->ne_enable_intr(nd_ifp); + return (error); +} + +/*- + * KLD specific code. + */ + +/* + * Called upon system init. Initializes the sysctl variables to sane = defaults + * (locates the first available NIC and uses the first IPv4 IP on that = card as + * the client IP). Leaves the server IP unconfigured. + * + * Parameters: + * void *, unused + * + * Returns: + * void + */ +static void +netdump_config_defaults(void *dummy __unused) +{ + struct ifnet *ifp; + int found; + + nd_ifp =3D NULL; + nd_server.s_addr =3D INADDR_ANY; + nd_client.s_addr =3D INADDR_ANY; + nd_gw.s_addr =3D INADDR_ANY; + + if (nd_server_tun[0] !=3D '\0') + inet_aton(nd_server_tun, &nd_server); + if (nd_client_tun[0] !=3D '\0') + inet_aton(nd_client_tun, &nd_client); + if (nd_gw_tun[0] !=3D '\0') + inet_aton(nd_gw_tun, &nd_gw); + if (nd_nic_tun[0] !=3D '\0') { + found =3D 0; + IFNET_RLOCK_NOSLEEP(); + TAILQ_FOREACH(ifp, &V_ifnet, if_link) { + if (!strncmp(ifp->if_xname, nd_nic_tun, + strlen(ifp->if_xname))) { + found =3D 1; + break; + } + } + IFNET_RUNLOCK_NOSLEEP(); + if (found !=3D 0 && netdump_supported_nic(ifp)) + nd_ifp =3D ifp; + } +} +SYSINIT(netdump, SI_SUB_KLD, SI_ORDER_ANY, netdump_config_defaults, = NULL); diff --git a/sys/sparc64/sparc64/dump_machdep.c = b/sys/sparc64/sparc64/dump_machdep.c index d5409ac..5d0ad9e 100644 --- a/sys/sparc64/sparc64/dump_machdep.c +++ b/sys/sparc64/sparc64/dump_machdep.c @@ -159,17 +159,22 @@ dumpsys(struct dumperinfo *di) DEV_BSIZE); size +=3D hdrsize; =20 - totsize =3D size + 2 * sizeof(kdh); - if (totsize > di->mediasize) { - printf("Insufficient space on device (need %ld, have = %ld), " - "refusing to dump.\n", (long)totsize, - (long)di->mediasize); - error =3D ENOSPC; - goto fail; - } + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + totsize =3D size + 2 * sizeof(kdh); + if (totsize > di->mediasize) { + printf("Insufficient space on device (need %ld, = " + "have %ld), refusing to dump.\n", = (long)totsize, + (long)di->mediasize); + error =3D ENOSPC; + goto fail; + } =20 - /* Determine dump offset on device. */ - dumplo =3D di->mediaoffset + di->mediasize - totsize; + /* Determine dump offset on device. */ + dumplo =3D di->mediaoffset + di->mediasize - totsize; + } =20 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_SPARC64_VERSION, = size, di->blocksize); diff --git a/sys/x86/x86/dump_machdep.c b/sys/x86/x86/dump_machdep.c index 5c874f4..c75b1ff 100644 --- a/sys/x86/x86/dump_machdep.c +++ b/sys/x86/x86/dump_machdep.c @@ -311,13 +311,20 @@ dumpsys(struct dumperinfo *di) dumpsize +=3D fileofs; hdrgap =3D fileofs - DEV_ALIGN(hdrsz); =20 - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * = 2) { - error =3D ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) =3D=3D 0) + dumplo =3D 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error =3D ENOSPC; + goto fail; + } + dumplo =3D di->mediaoffset + di->mediasize - dumpsize; + dumplo -=3D sizeof(kdh) * 2; } - dumplo =3D di->mediaoffset + di->mediasize - dumpsize; - dumplo -=3D sizeof(kdh) * 2; =20 mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_VERSION, = dumpsize, di->blocksize); diff --git a/usr.sbin/netdumpsrv/Makefile b/usr.sbin/netdumpsrv/Makefile new file mode 100644 index 0000000..08ea0ae --- /dev/null +++ b/usr.sbin/netdumpsrv/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +PROG=3D netdumpsrv +MAN=3D netdumpsrv.8 + +CFLAGS+=3D -DNDEBUG -I${.CURDIR} + +WARNS?=3D 6 + +DPADD=3D ${LIBUTIL} +LDADD=3D -lutil + +.include 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_DUMPS 256 /* Dumps per IP before to be = cleaned out. */ +#define CLIENT_TIMEOUT 600 /* Clients timeout (secs). */ +#define CLIENT_TPASS 10 /* Clients timeout pass (secs). = */ + +#define PFLAGS_ABIND 0x01 +#define PFLAGS_DDIR 0x02 +#define PFLAGS_DEBUG 0x04 +#define PFLAGS_SCRIPT 0x08 + +#define LOGERR(m, ...) = \ + (*phook)(LOG_ERR | LOG_DAEMON, (m), ## __VA_ARGS__) +#define LOGERR_PERROR(m) = \ + (*phook)(LOG_ERR | LOG_DAEMON, "%s: %s\n", m, strerror(errno)) +#define LOGINFO(m, ...) = \ + (*phook)(LOG_INFO | LOG_DAEMON, (m), ## __VA_ARGS__) +#define LOGWARN(m, ...) = \ + (*phook)(LOG_WARNING | LOG_DAEMON, (m), ## __VA_ARGS__) + +#define client_ntoa(cl) = \ + inet_ntoa((cl)->ip) +#define client_pinfo(cl, f, ...) = \ + fprintf((cl)->infofile, (f), ## __VA_ARGS__) + +struct netdump_client { + char infofilename[MAXPATHLEN]; + char corefilename[MAXPATHLEN]; + char hostname[NI_MAXHOST]; + time_t last_msg; + SLIST_ENTRY(netdump_client) iter; + struct in_addr ip; + FILE *infofile; + int corefd; + int sock; + unsigned short printed_port_warning: 1; + unsigned short any_data_rcvd: 1; +}; + +/* Clients list. */ +static SLIST_HEAD(, netdump_client) clients =3D = SLIST_HEAD_INITIALIZER(clients); + +/* Program arguments handlers. */ +static uint32_t pflags; +static char dumpdir[MAXPATHLEN]; +static char *handler_script; +static struct in_addr bindip; + +/* Miscellaneous handlers. */ +static struct pidfh *pfh; +static time_t now; +static time_t last_timeout_check; +static int do_shutdown; +static int sock; + +/* Daemon print functions hook. */ +static void (*phook)(int, const char *, ...); + +static struct netdump_client *alloc_client(struct sockaddr_in *sip); +static void eventloop(void); +static void exec_handler(struct netdump_client *client, + const char *reason); +static void free_client(struct netdump_client *client); +static void handle_finish(struct netdump_client *client, + struct netdump_msg *msg); +static void handle_herald(struct sockaddr_in *from, + struct netdump_client *client, + struct netdump_msg *msg); +static void handle_kdh(struct netdump_client *client, + struct netdump_msg *msg); +static void handle_packet(struct netdump_client *client, + struct sockaddr_in *from, const char = *fromstr, + struct netdump_msg *msg); +static void handle_timeout(struct netdump_client *client); +static void handle_vmcore(struct netdump_client *client, + struct netdump_msg *msg); +static void phook_printf(int priority, const char *message, = ...); +static int receive_message(int isock, struct sockaddr_in = *from, + char *fromstr, size_t fromstrlen, + struct netdump_msg *msg); +static void send_ack(struct netdump_client *client, + struct netdump_msg *msg); +static void signal_shutdown(int sig __unused); +static void timeout_clients(void); +static void usage(const char *cmd); + +static void +usage(const char *cmd) +{ + + fprintf(stderr, "Usage: %s [-a bind_addr] [-d dump_dir] [-i = script]\n", + cmd); +} + +static void +phook_printf(int priority, const char *message, ...) +{ + va_list ap; + + va_start(ap, message); + if ((priority & LOG_INFO) !=3D 0) { + assert((priority & (LOG_WARNING | LOG_ERR)) =3D=3D 0); + vprintf(message, ap); + } else + vfprintf(stderr, message, ap); +} + +static struct netdump_client * +alloc_client(struct sockaddr_in *sip) +{ + struct sockaddr_in saddr; + struct netdump_client *client; + struct in_addr *ip; + char *firstdot; + int i, ecode, fd, bufsz; + + assert(sip !=3D NULL); + + client =3D calloc(1, sizeof(*client)); + if (client =3D=3D NULL) { + LOGERR_PERROR("calloc()"); + return (NULL); + } + ip =3D &sip->sin_addr; + bcopy(ip, &client->ip, sizeof(*ip)); + client->corefd =3D -1; + client->sock =3D -1; + client->last_msg =3D now; + + ecode =3D getnameinfo((struct sockaddr *)sip, sip->sin_len, + client->hostname, sizeof(client->hostname), NULL, 0, = NI_NAMEREQD); + if (ecode !=3D 0) { + + /* Can't resolve, try with a numeric IP. */ + ecode =3D getnameinfo((struct sockaddr *)sip, = sip->sin_len, + client->hostname, sizeof(client->hostname), NULL, 0, = 0); + if (ecode !=3D 0) { + LOGERR("getnameinfo(): %s\n", = gai_strerror(ecode)); + free(client); + return (NULL); + } + } else { + + /* Strip off the domain name */ + firstdot =3D strchr(client->hostname, '.'); + if (firstdot) + *firstdot =3D '\0'; + } + + client->sock =3D socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (client->sock =3D=3D -1) { + LOGERR_PERROR("socket()"); + free(client); + return (NULL); + } + if (fcntl(client->sock, F_SETFL, O_NONBLOCK) =3D=3D -1) { + LOGERR_PERROR("fcntl()"); + close(client->sock); + free(client); + return (NULL); + } + bzero(&saddr, sizeof(saddr)); + saddr.sin_len =3D sizeof(saddr); + saddr.sin_family =3D AF_INET; + saddr.sin_addr.s_addr =3D bindip.s_addr; + saddr.sin_port =3D htons(0); + if (bind(client->sock, (struct sockaddr *)&saddr, = sizeof(saddr))) { + LOGERR_PERROR("bind()"); + close(client->sock); + free(client); + return (NULL); + } + bzero(&saddr, sizeof(saddr)); + saddr.sin_len =3D sizeof(saddr); + saddr.sin_family =3D AF_INET; + saddr.sin_addr.s_addr =3D ip->s_addr; + saddr.sin_port =3D htons(NETDUMP_ACKPORT); + if (connect(client->sock, (struct sockaddr *)&saddr, = sizeof(saddr))) { + LOGERR_PERROR("connect()"); + close(client->sock); + free(client); + return (NULL); + } + + /* It should be enough to hold approximatively twize the chunk = size. */ + bufsz =3D 131072; + if (setsockopt(client->sock, SOL_SOCKET, SO_RCVBUF, &bufsz, + sizeof(bufsz))) { + LOGERR_PERROR("setsockopt()"); + LOGWARN("May drop packets from %s due to small receive = buffer\n", + client->hostname); + } + + /* Try info.host.0 through info.host.255 in sequence. */ + for (i =3D 0; i < MAX_DUMPS; i++) { + snprintf(client->infofilename, = sizeof(client->infofilename), + "%s/info.%s.%d", dumpdir, client->hostname, i); + snprintf(client->corefilename, = sizeof(client->corefilename), + "%s/vmcore.%s.%d", dumpdir, client->hostname, i); + + /* Try the info file first. */ + fd =3D open(client->infofilename, O_WRONLY | O_CREAT | = O_EXCL, + DEFFILEMODE); + if (fd =3D=3D -1) { + if (errno !=3D EEXIST) + LOGERR("open(\"%s\"): %s\n", + client->infofilename, = strerror(errno)); + continue; + } + client->infofile =3D fdopen(fd, "w"); + if (client->infofile =3D=3D NULL) { + LOGERR_PERROR("fdopen()"); + close(fd); + unlink(client->infofilename); + continue; + } + + /* Next make the core file. */ + fd =3D open(client->corefilename, O_RDWR | O_CREAT | = O_EXCL, + DEFFILEMODE); + if (fd =3D=3D -1) { + + /* Failed. Keep the numbers in sync. */ + fclose(client->infofile); + unlink(client->infofilename); + client->infofile =3D NULL; + if (errno !=3D EEXIST) + LOGERR("open(\"%s\"): %s\n", + client->corefilename, = strerror(errno)); + continue; + } + client->corefd =3D fd; + break; + } + + if (client->infofile =3D=3D NULL || client->corefd =3D=3D -1) { + LOGERR("Can't create output files for new client %s = [%s]\n", + client->hostname, client_ntoa(client)); + if (client->infofile) + fclose(client->infofile); + if (client->corefd !=3D -1) + close(client->corefd); + if (client->sock !=3D -1) + close(client->sock); + free(client); + return (NULL); + } + SLIST_INSERT_HEAD(&clients, client, iter); + return (client); +} + +static void +free_client(struct netdump_client *client) +{ + + assert(client !=3D NULL); + + /* Remove from the list. Ignore errors from close() routines. = */ + SLIST_REMOVE(&clients, client, netdump_client, iter); + fclose(client->infofile); + close(client->corefd); + close(client->sock); + free(client); +} + +static void +exec_handler(struct netdump_client *client, const char *reason) +{ + int pid; + + assert(client !=3D NULL); + + /* If no script is specified this is a no-op. */ + if ((pflags & PFLAGS_SCRIPT) =3D=3D 0) + return; + + pid =3D fork(); + + /* + * The function is invoked in critical conditions, thus just = exiting + * without reporting errors is fine. + */ + if (pid =3D=3D -1) { + LOGERR_PERROR("fork()"); + return; + } else if (pid !=3D 0) { + close(sock); + pidfile_close(pfh); + if (execl(handler_script, handler_script, reason, + client_ntoa(client), client->hostname, + client->infofilename, client->corefilename, NULL) =3D=3D= -1) { + LOGERR_PERROR("fork()"); + _exit(1); + } + } +} + +static void +handle_timeout(struct netdump_client *client) +{ + + assert(client !=3D NULL); + + LOGINFO("Client %s timed out\n", client_ntoa(client)); + client_pinfo(client, "Dump incomplete: client timed out\n"); + exec_handler(client, "timeout"); + free_client(client); +} + +static void +timeout_clients(void) +{ + struct netdump_client *client, *tmp; + =20 + /* Only time out clients every 10 seconds. */ + if (now - last_timeout_check < CLIENT_TPASS) + return; + last_timeout_check =3D now; + + /* Traverse the list looking for stale clients. */ + SLIST_FOREACH_SAFE(client, &clients, iter, tmp) { + if (client->last_msg + CLIENT_TIMEOUT < now) { + LOGINFO("Timingout with such values: %jd + %jd < = %jd\n", + (intmax_t)client->last_msg, + (intmax_t)CLIENT_TIMEOUT, (intmax_t)now); + handle_timeout(client); + } + } +} + +static void +send_ack(struct netdump_client *client, struct netdump_msg *msg) +{ + struct netdump_ack ack; + int tryagain; + =20 + assert(client !=3D NULL && msg !=3D NULL); + + bzero(&ack, sizeof(ack)); + ack.na_seqno =3D htonl(msg->nm_hdr.mh_seqno); + do { + tryagain =3D 0; + if (send(client->sock, &ack, sizeof(ack), 0) =3D=3D -1) = { + if (errno =3D=3D EINTR) { + tryagain =3D 1; + continue; + } + + /* + * XXX: On EAGAIN, we should probably queue the = packet + * to be sent when the socket is writable but + * that is too much effort, since it is mostly + * harmless to wait for the client to = retransmit. + */ + LOGERR_PERROR("send()"); + } + } while (tryagain); +} + +static void +handle_herald(struct sockaddr_in *from, struct netdump_client *client, + struct netdump_msg *msg) +{ + + assert(from !=3D NULL && msg !=3D NULL); + + if (client !=3D NULL) { + if (client->any_data_rcvd =3D=3D 0) { + + /* Must be a retransmit of the herald packet. */ + send_ack(client, msg); + return; + } + + /* An old connection must have timed out. Clean it up = first. */ + handle_timeout(client); + } + + client =3D alloc_client(from); + if (client =3D=3D NULL) { + LOGERR("handle_herald(): new client allocation = failure\n"); + return; + } + client_pinfo(client, "Dump from %s [%s]\n", client->hostname, + client_ntoa(client)); + LOGINFO("New dump from client %s [%s] (to %s)\n", = client->hostname, + client_ntoa(client), client->corefilename); + send_ack(client, msg); +} + +static void +handle_kdh(struct netdump_client *client, struct netdump_msg *msg) +{ + time_t t; + uint64_t dumplen; + struct kerneldumpheader *h; + int parity_check; + + assert(msg !=3D NULL); + + if (client =3D=3D NULL) + return; + + client->any_data_rcvd =3D 1; + h =3D (struct kerneldumpheader *)msg->nm_data; + if (msg->nm_hdr.mh_len < sizeof(struct kerneldumpheader)) { + LOGERR("Bad KDH from %s [%s]: packet too small\n", + client->hostname, client_ntoa(client)); + client_pinfo(client, "Bad KDH: packet too small\n"); + fflush(client->infofile); + send_ack(client, msg); + return; + } + parity_check =3D kerneldump_parity(h); + + /* Make sure all the strings are null-terminated. */ + h->architecture[sizeof(h->architecture) - 1] =3D '\0'; + h->hostname[sizeof(h->hostname) - 1] =3D '\0'; + h->versionstring[sizeof(h->versionstring) - 1] =3D '\0'; + h->panicstring[sizeof(h->panicstring) - 1] =3D '\0'; + + client_pinfo(client, " Architecture: %s\n", h->architecture); + client_pinfo(client, " Architecture version: %d\n", + dtoh32(h->architectureversion)); + dumplen =3D dtoh64(h->dumplength); + client_pinfo(client, " Dump length: %lldB (%lld MB)\n", + (long long)dumplen, (long long)(dumplen >> 20)); + client_pinfo(client, " blocksize: %d\n", dtoh32(h->blocksize)); + t =3D dtoh64(h->dumptime); + client_pinfo(client, " Dumptime: %s", ctime(&t)); + client_pinfo(client, " Hostname: %s\n", h->hostname); + client_pinfo(client, " Versionstring: %s", h->versionstring); + client_pinfo(client, " Panicstring: %s\n", h->panicstring); + client_pinfo(client, " Header parity check: %s\n", + parity_check ? "Fail" : "Pass"); + fflush(client->infofile); + + LOGINFO("(KDH from %s [%s])", client->hostname, = client_ntoa(client)); + send_ack(client, msg); +} + +static void +handle_vmcore(struct netdump_client *client, struct netdump_msg *msg) +{ + + assert(msg !=3D NULL); + + if (client =3D=3D NULL) + return; + + client->any_data_rcvd =3D 1; + if (msg->nm_hdr.mh_seqno % 11523 =3D=3D 0) { + + /* Approximately every 16MB with MTU of 1500 */ + LOGINFO("."); + } + if (pwrite(client->corefd, msg->nm_data, msg->nm_hdr.mh_len, + msg->nm_hdr.mh_offset) =3D=3D -1) { + LOGERR("pwrite (for client %s [%s]): %s\n", = client->hostname, + client_ntoa(client), strerror(errno)); + client_pinfo(client, + "Dump unsuccessful: write error @ offset = %08"PRIx64": %s\n", + msg->nm_hdr.mh_offset, strerror(errno)); + exec_handler(client, "error"); + free_client(client); + return; + } + send_ack(client, msg); +} + +static void +handle_finish(struct netdump_client *client, struct netdump_msg *msg) +{ + + assert(msg !=3D NULL); + + if (client =3D=3D NULL) + return; + + LOGINFO("\nCompleted dump from client %s [%s]\n", = client->hostname, + client_ntoa(client)); + client_pinfo(client, "Dump complete\n"); + send_ack(client, msg); + exec_handler(client, "success"); + free_client(client); +} + + +static int +receive_message(int isock, struct sockaddr_in *from, char *fromstr, + size_t fromstrlen, struct netdump_msg *msg) +{ + socklen_t fromlen; + ssize_t len; + + assert(from !=3D NULL && fromstr !=3D NULL && msg !=3D NULL); + + bzero(from, sizeof(*from)); + from->sin_family =3D AF_INET; + from->sin_len =3D fromlen =3D sizeof(*from); + from->sin_port =3D 0; + from->sin_addr.s_addr =3D INADDR_ANY; + + len =3D recvfrom(isock, msg, sizeof(*msg), 0, (struct sockaddr = *)from, + &fromlen); + if (len =3D=3D -1) { + + /* + * As long as some callers may discard the errors = printing + * in defined circumstances, leave them the choice and = avoid + * any error reporting. + */ + return (-1); + } + + snprintf(fromstr, fromstrlen, "%s:%hu", = inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + if ((size_t)len < sizeof(struct netdump_msg_hdr)) { + LOGERR("Ignoring runt packet from %s (got %zu)\n", = fromstr, + (size_t)len); + return (0); + } + + /* Convert byte order. */ + msg->nm_hdr.mh_type =3D ntohl(msg->nm_hdr.mh_type); + msg->nm_hdr.mh_seqno =3D ntohl(msg->nm_hdr.mh_seqno); + msg->nm_hdr.mh_offset =3D be64toh(msg->nm_hdr.mh_offset); + msg->nm_hdr.mh_len =3D ntohl(msg->nm_hdr.mh_len); + + if ((size_t)len < sizeof(struct netdump_msg_hdr) + = msg->nm_hdr.mh_len) { + LOGERR("Packet too small from %s (got %zu, expected = %zu)\n", + fromstr, (size_t)len, + sizeof(struct netdump_msg_hdr) + = msg->nm_hdr.mh_len); + return (0); + } + return (len); +} + +static void +handle_packet(struct netdump_client *client, struct sockaddr_in *from, + const char *fromstr, struct netdump_msg *msg) +{ + + assert(from !=3D NULL && fromstr !=3D NULL && msg !=3D NULL); + + if (client !=3D NULL) + client->last_msg =3D time(NULL); + + switch (msg->nm_hdr.mh_type) { + case NETDUMP_HERALD: + handle_herald(from, client, msg); + break; + case NETDUMP_KDH: + handle_kdh(client, msg); + break; + case NETDUMP_VMCORE: + handle_vmcore(client, msg); + break; + case NETDUMP_FINISHED: + handle_finish(client, msg); + break; + default: + LOGERR("Received unknown message type %d from %s\n", + msg->nm_hdr.mh_type, fromstr); + } +} + +static void +eventloop(void) +{ + struct netdump_msg msg; + char fromstr[INET_ADDRSTRLEN + 6]; + fd_set readfds; + struct sockaddr_in from; + struct timeval tv; + struct netdump_client *client, *tmp; + int len, maxfd; + + while (do_shutdown =3D=3D 0) { + maxfd =3D sock + 1; + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + SLIST_FOREACH(client, &clients, iter) { + FD_SET(client->sock, &readfds); + if (maxfd <=3D client->sock) + maxfd =3D client->sock+1; + } + + /* So that we time out clients regularly. */ + tv.tv_sec =3D CLIENT_TPASS; + tv.tv_usec =3D 0; + if (select(maxfd, &readfds, NULL, NULL, &tv) =3D=3D -1) = { + if (errno =3D=3D EINTR) + continue; + LOGERR_PERROR("select()"); + + /* + * Errors with select() probably will not go = away + * with simple retrying. + */ + pidfile_remove(pfh); + exit(1); + } + now =3D time(NULL); + if (FD_ISSET(sock, &readfds)) { + len =3D receive_message(sock, &from, fromstr, + sizeof(fromstr), &msg); + if (len =3D=3D -1) { + if (errno =3D=3D EINTR) + continue; + if (errno !=3D EAGAIN) { + pidfile_remove(pfh); + LOGERR_PERROR("recvfrom()"); + exit(1); + } + } else if (len !=3D 0) { + + /* + * With len =3D=3D 0 the packet was = rejected + * (probably because it was too small) = so just + * ignore this case. + */ + + /* Check if they are on the clients = list. */ + SLIST_FOREACH(client, &clients, iter) + if (client->ip.s_addr =3D=3D + from.sin_addr.s_addr) + break; + + /* + * Technically, clients should not be + * responding on the server port, so = client + * should be NULL, however, if they = insist on + * doing so, it's not really going to = hurt + * anything (except maybe fill up the = server + * socket's receive buffer), so still + * accept it. The only possibly = legitimate case + * is if there's a new dump starting and = the + * previous one didn't finish cleanly. = Handle + * this by suppressing the error on = HERALD + * packets. + */ + if (client !=3D NULL && + msg.nm_hdr.mh_type !=3D = NETDUMP_HERALD && + client->printed_port_warning =3D=3D = 0) { + LOGWARN("Client %s responding on server = port\n", + client->hostname); + client->printed_port_warning =3D = 1; + } + handle_packet(client, &from, fromstr, = &msg); + } + } + + /* + * handle_packet() and handle_timeout() may free the = client, + * handle stale pointers. + */ + SLIST_FOREACH_SAFE(client, &clients, iter, tmp) { + if (FD_ISSET(client->sock, &readfds)) { + len =3D receive_message(client->sock, = &from, + fromstr, sizeof(fromstr), &msg); + if (len =3D=3D -1) { + if (errno =3D=3D EINTR || errno = =3D=3D EAGAIN) + continue; + LOGERR_PERROR("recvfrom()"); + + /* + * Client socket is broken for + * some reason. + */ + handle_timeout(client); + } else if (len !=3D 0) { + + /* + * With len =3D=3D 0 the packet = was + * rejected (probably because it = was + * too small) so just ignore = this case. + */ + + FD_CLR(client->sock, &readfds); + handle_packet(client, &from, = fromstr, + &msg); + } + } + } + timeout_clients(); + } + LOGINFO("Shutting down..."); + + /* + * Clients is the head of the list, so clients !=3D NULL iff the = list + * is not empty. Call it a timeout so that the scripts get run. + */ + while (!SLIST_EMPTY(&clients)) + handle_timeout(SLIST_FIRST(&clients)); +} + +static void +signal_shutdown(int sig __unused) +{ + + do_shutdown =3D 1; +} + +int +main(int argc, char **argv) +{ + struct stat statbuf; + struct sockaddr_in bindaddr; + struct sigaction sa; + int ch; + + pfh =3D pidfile_open(NULL, 0600, NULL); + if (pfh =3D=3D NULL) { + if (errno =3D=3D EEXIST) + printf("Instance of netdump already running\n"); + else + printf("Impossible to open the pid file\n"); + exit(1); + } + + while ((ch =3D getopt(argc, argv, "a:Dd:i:")) !=3D -1) { + switch (ch) { + case 'a': + pflags |=3D PFLAGS_ABIND; + if (!inet_aton(optarg, &bindip)) { + pidfile_remove(pfh); + fprintf(stderr, "Invalid bind IP = specified\n"); + exit(1); + } + printf("Listening on IP %s\n", optarg); + break; + case 'D': + pflags |=3D PFLAGS_DEBUG; + break; + case 'd': + pflags |=3D PFLAGS_DDIR; + assert(dumpdir[0] =3D=3D '\0'); + strncpy(dumpdir, optarg, sizeof(dumpdir) - 1); + break; + case 'i': + pflags |=3D PFLAGS_SCRIPT; + + /* + * When suddenly closing the process for an = error, + * it is unuseful to take care of handler_script + * deallocation as long as the process will = _exit(2) + * anyway. + */ + handler_script =3D strdup(optarg); + if (handler_script =3D=3D NULL) { + pidfile_remove(pfh); + perror("strdup()"); + fprintf(stderr, "Unable to set script = file\n"); + exit(1); + } + if (access(handler_script, F_OK | X_OK)) { + pidfile_remove(pfh); + perror("access()"); + fprintf(stderr, + "Unable to access script file\n"); + exit(1); + } + break; + default: + pidfile_remove(pfh); + usage(argv[0]); + exit(1); + } + } + if ((pflags & PFLAGS_ABIND) =3D=3D 0) { + bindip.s_addr =3D INADDR_ANY; + printf("Default: listening on all interfaces\n"); + } + if ((pflags & PFLAGS_DDIR) =3D=3D 0) { + strcpy(dumpdir, "/var/crash"); + printf("Default: dumping on /var/crash/\n"); + } + if ((pflags & PFLAGS_DEBUG) =3D=3D 0) + phook =3D syslog; + else + phook =3D phook_printf; + + /* Further sanity checks on dump location. */ + if (stat(dumpdir, &statbuf)) { + pidfile_remove(pfh); + perror("stat()"); + fprintf(stderr, "Invalid dump location specified\n"); + exit(1); + } + if ((statbuf.st_mode & S_IFMT) !=3D S_IFDIR) { + pidfile_remove(pfh); + fprintf(stderr, "Dump location is not a directory\n"); + exit(1); + } + if (access(dumpdir, F_OK | W_OK)) { + fprintf(stderr, + "Warning: May be unable to write into dump location: = %s\n", + strerror(errno)); + } + + if ((pflags & PFLAGS_DEBUG) =3D=3D 0 && daemon(0, 0) =3D=3D -1) = { + pidfile_remove(pfh); + perror("daemon()"); + fprintf(stderr, "Impossible to demonize the process\n"); + exit(1); + } + pidfile_write(pfh); + + /* Set up the server socket. */ + sock =3D socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock =3D=3D -1) { + pidfile_remove(pfh); + LOGERR_PERROR("socket()"); + exit(1); + } + bzero(&bindaddr, sizeof(bindaddr)); + bindaddr.sin_len =3D sizeof(bindaddr); + bindaddr.sin_family =3D AF_INET; + bindaddr.sin_addr.s_addr =3D bindip.s_addr; + bindaddr.sin_port =3D htons(NETDUMP_PORT); + if (bind(sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) = { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("bind()"); + exit(1); + } + if (fcntl(sock, F_SETFL, O_NONBLOCK) =3D=3D -1) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("fcntl()"); + exit(1); + } + + /* Override some signal handlers. */ + bzero(&sa, sizeof(sa)); + sa.sa_handler =3D signal_shutdown; + if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, = NULL)) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("sigaction(SIGINT | SIGTERM)"); + exit(1); + } + bzero(&sa, sizeof(sa)); + sa.sa_handler =3D SIG_IGN; + sa.sa_flags =3D SA_NOCLDWAIT; + if (sigaction(SIGCHLD, &sa, NULL)) { + pidfile_remove(pfh); + LOGERR_PERROR("sigaction(SIGCHLD)"); + close(sock); + exit(1); + } + + LOGINFO("Waiting for clients.\n"); + eventloop(); + + pidfile_remove(pfh); + return (0); +} --Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii --Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3 Content-Disposition: attachment; filename=kernel-netdump-diffs.txt Content-Type: text/plain; name="kernel-netdump-diffs.txt" Content-Transfer-Encoding: quoted-printable diff --git a/sys/conf/files b/sys/conf/files index 46d0d27..b910b1a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3262,6 +3264,7 @@ netinet/ip_mroute.c optional = mrouting inet netinet/ip_options.c optional inet netinet/ip_output.c optional inet netinet/netdump_client.c optional inet netdump_client +netinet/network_debug.c optional inet netdump_client netinet/raw_ip.c optional inet | inet6 netinet/cc/cc.c optional inet | inet6 netinet/cc/cc_newreno.c optional inet | inet6 diff --git a/sys/netinet/netdump_client.c b/sys/netinet/netdump_client.c index 1b73e1b..ad4c347 100644 --- a/sys/netinet/netdump_client.c +++ b/sys/netinet/netdump_client.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include =20 @@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include =20 @@ -96,20 +98,15 @@ __FBSDID("$FreeBSD$"); #define NETDDEBUGV_IF(i, f, ...) #endif =20 -static void nd_handle_arp(struct mbuf **mb); -static void nd_handle_ip(struct mbuf **mb); +static void nd_handle_ip(struct network_debug *, struct mbuf **mb); +static int nd_packet_wanted(struct network_debug *, struct mbuf *); static int netdump_arp_server(void); static void netdump_config_defaults(void *dummy __unused); static int netdump_dumper(void *priv __unused, void *virtual, vm_offset_t physical __unused, off_t offset, size_t = length); -static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp,=20= - struct ether_addr dst, u_short etype); -static int netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr = __unused, void *opt_args __unused); -static void netdump_network_poll(void); static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m); static int netdump_send(uint32_t type, off_t offset, unsigned char = *data, uint32_t datalen); -static int netdump_send_arp(void); static int netdump_udp_output(struct mbuf *m); =20 static int sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS); @@ -119,49 +116,30 @@ static int = sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS); static unsigned char buf[MAXDUMPPGS * PAGE_SIZE]; static uint64_t rcvd_acks; static uint32_t nd_seqno =3D 1; -static int dump_failed, have_server_mac; - -/* - * Times to poll the NIC (0.5ms each poll) before assuming packetloss - * occurred (default to 5s). - */ -static int nd_polls =3D 10000; - -/* Times to retransmit lost packets. */ -static int nd_retries =3D 10; - -/* General dynamic settings. */ -static char nd_ifp_str[IFNAMSIZ]; -static struct ether_addr nd_gw_mac; -static struct in_addr nd_server =3D {INADDR_ANY}; -static struct in_addr nd_client =3D {INADDR_ANY}; -static struct in_addr nd_gw =3D {INADDR_ANY}; -struct ifnet *nd_ifp; -int nd_enable =3D 0; -static uint16_t nd_server_port =3D NETDUMP_PORT; +static int dump_failed; + +static struct network_debug __unused +nd_networking =3D { + .poll_time =3D 10000, + .retries =3D 10, + .if_name =3D { 0 }, + .ifp =3D NULL, + .remote =3D {INADDR_ANY}, + .local =3D {INADDR_ANY}, + .gw =3D {INADDR_ANY}, + .ip_handler =3D nd_handle_ip, + .wanted =3D nd_packet_wanted, +}; + +int nd_enable =3D 0; // Used in kern_shutdown, so can't be static +static uint16_t nd_remote_port =3D NETDUMP_PORT; =20 /* Tunables storages. */ -static char nd_server_tun[INET_ADDRSTRLEN]; -static char nd_client_tun[INET_ADDRSTRLEN]; +static char nd_remote_tun[INET_ADDRSTRLEN]; +static char nd_local_tun[INET_ADDRSTRLEN]; static char nd_gw_tun[INET_ADDRSTRLEN]; static char nd_nic_tun[IFNAMSIZ]; =20 -/* - * Checks for netdump support on a network interface - * - * Parameters: - * ifp The network interface that is being tested for support - * - * Returns: - * int 1 if the interface is supported, 0 if not - */ -static __inline int -netdump_supported_nic(struct ifnet *ifp) -{ - - return (ifp->if_ndumpfuncs !=3D NULL); -} - /*- * Sysctls specific code. */ @@ -192,7 +170,7 @@ sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS) IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (!strncmp(ifp->if_xname, buf, strlen(ifp->if_xname)) = && - netdump_supported_nic(ifp)) { + network_debug_supported_nic(ifp)) { found =3D 1; break; } @@ -235,24 +213,24 @@ sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS) =20 SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, = "netdump"); SYSCTL_PROC(_net_dump, OID_AUTO, server, CTLTYPE_STRING | CTLFLAG_RW | - CTLFLAG_MPSAFE, &nd_server, 0, sysctl_handle_inaddr, "A", "dump = server"); + CTLFLAG_MPSAFE, &nd_networking.remote, 0, sysctl_handle_inaddr, = "A", "dump server"); SYSCTL_PROC(_net_dump, OID_AUTO, client, CTLTYPE_STRING |CTLFLAG_RW | - CTLFLAG_MPSAFE, &nd_client, 0, sysctl_handle_inaddr, "A", "dump = client"); + CTLFLAG_MPSAFE, &nd_networking.local, 0, sysctl_handle_inaddr, "A", = "dump client"); SYSCTL_PROC(_net_dump, OID_AUTO, gateway, CTLTYPE_STRING | CTLFLAG_RW | - CTLFLAG_MPSAFE, &nd_gw, 0, sysctl_handle_inaddr, "A", + CTLFLAG_MPSAFE, &nd_networking.gw, 0, sysctl_handle_inaddr, "A", "dump default gateway"); SYSCTL_PROC(_net_dump, OID_AUTO, nic, CTLTYPE_STRING | CTLFLAG_RW | - CTLFLAG_MPSAFE, &nd_ifp_str, 0, sysctl_handle_ifxname, "A", + CTLFLAG_MPSAFE, &nd_networking.if_name, 0, sysctl_handle_ifxname, = "A", "dumping interface name"); SYSCTL_INT(_net_dump, OID_AUTO, polls, CTLTYPE_INT | CTLFLAG_RW | - CTLFLAG_MPSAFE, &nd_polls, 0, "times to poll NIC per retry"); + CTLFLAG_MPSAFE, &nd_networking.poll_time, 0, "times to poll NIC per = retry"); SYSCTL_INT(_net_dump, OID_AUTO, retries, CTLTYPE_INT | CTLFLAG_RW | - CTLFLAG_MPSAFE, &nd_retries, 0, "times to retransmit lost = packets"); + CTLFLAG_MPSAFE, &nd_networking.retries, 0, "times to retransmit = lost packets"); SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_enable, 0, "enable network dump"); =20 -TUNABLE_STR("net.dump.server", nd_server_tun, sizeof(nd_server_tun)); -TUNABLE_STR("net.dump.client", nd_client_tun, sizeof(nd_client_tun)); +TUNABLE_STR("net.dump.server", nd_remote_tun, sizeof(nd_remote_tun)); +TUNABLE_STR("net.dump.client", nd_local_tun, sizeof(nd_local_tun)); TUNABLE_STR("net.dump.gateway", nd_gw_tun, sizeof(nd_gw_tun)); TUNABLE_STR("net.dump.nic", nd_nic_tun, sizeof(nd_nic_tun)); TUNABLE_INT("net.dump.enable", &nd_enable); @@ -265,66 +243,7 @@ TUNABLE_INT("net.dump.enable", &nd_enable); * - Polling primitives */ =20 -/* - * Netdump wraps external mbufs around address ranges. unlike most = sane - * counterparts, netdump uses a stop-and-wait approach to flow control = and - * retransmission, so the ack obviates the need for mbuf reference - * counting. We still need to tell other mbuf handlers not to do = anything - * special with our mbufs, so specify this nop handler. - * - * Parameters: - * ptr data to free (ignored) - * opt_args callback pointer (ignored) - * - * Returns: - * void - */ -static int -netdump_mbuf_nop(struct mbuf *mp __unused, void *ptr __unused, void = *opt_args __unused) -{ - return EXT_FREE_OK; -} - -/* - * Handles creation of the ethernet header, then places outgoing = packets into - * the tx buffer for the NIC - * - * Parameters: - * m The mbuf containing the packet to be sent (will be freed = by - * this function or the NIC driver) - * ifp The interface to send on - * dst The destination ethernet address (source address will be = looked - * up using ifp) - * etype The ETHERTYPE_* value for the protocol that is being = sent - * - * Returns: - * int see errno.h, 0 for success - */ -static int -netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct = ether_addr dst, - u_short etype) -{ - struct ether_header *eh; =20 - /* Fill in the ethernet header. */ - M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); - if (m =3D=3D NULL) { - printf("netdump_ether_output: Out of mbufs\n"); - return (ENOBUFS); - } - eh =3D mtod(m, struct ether_header *); - memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); - memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); - eh->ether_type =3D htons(etype); - - if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) !=3D IFF_UP) || - (ifp->if_drv_flags & IFF_DRV_RUNNING) !=3D IFF_DRV_RUNNING) = { - if_printf(ifp, "netdump_ether_output: Interface isn't = up\n"); - m_freem(m); - return (ENETDOWN); - } - return ((ifp->if_transmit)(ifp, m)); -} =20 /* * Unreliable transmission of an mbuf chain to the netdump server @@ -343,7 +262,7 @@ netdump_udp_output(struct mbuf *m) struct udpiphdr *ui; struct ip *ip; =20 - MPASS(nd_ifp !=3D NULL); + MPASS(nd_networking.ifp !=3D NULL); =20 M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT); if (m =3D=3D NULL) { @@ -355,11 +274,11 @@ netdump_udp_output(struct mbuf *m) ui->ui_pr =3D IPPROTO_UDP; ui->ui_len =3D htons(m->m_pkthdr.len - sizeof(struct ip)); ui->ui_ulen =3D ui->ui_len; - ui->ui_src =3D nd_client; - ui->ui_dst =3D nd_server; + ui->ui_src =3D nd_networking.local; + ui->ui_dst =3D nd_networking.remote; /* Use this src port so that the server can connect() the socket = */ ui->ui_sport =3D htons(NETDUMP_ACKPORT); - ui->ui_dport =3D htons(nd_server_port); + ui->ui_dport =3D htons(nd_remote_port); ui->ui_sum =3D 0; if ((ui->ui_sum =3D in_cksum(m, m->m_pkthdr.len)) =3D=3D 0) ui->ui_sum =3D 0xffff; @@ -375,59 +294,15 @@ netdump_udp_output(struct mbuf *m) ip->ip_sum =3D 0; ip->ip_sum =3D in_cksum(m, sizeof(struct ip)); =20 - if (m->m_pkthdr.len > nd_ifp->if_mtu) { + if (m->m_pkthdr.len > nd_networking.ifp->if_mtu) { printf("netdump_udp_output: Packet is too big: %u > MTU = %lu\n", - m->m_pkthdr.len, (unsigned long)nd_ifp->if_mtu); + m->m_pkthdr.len, (unsigned = long)nd_networking.ifp->if_mtu); m_freem(m); return (ENOBUFS); } - return (netdump_ether_output(m, nd_ifp, nd_gw_mac, = ETHERTYPE_IP)); + return (network_debug_ether_output(m, nd_networking.ifp, = nd_networking.remote_mac, ETHERTYPE_IP)); } =20 -/* - * Builds and sends a single ARP request to locate the server - * - * Parameters: - * void - * - * Return value: - * 0 on success - * errno on error - */ -static int -netdump_send_arp() -{ - struct ether_addr bcast; - struct mbuf *m; - struct arphdr *ah; - int pktlen; - - MPASS(nd_ifp !=3D NULL); - - /* Fill-up a broadcast address. */ - memset(&bcast, 0xFF, ETHER_ADDR_LEN); - MGETHDR(m, M_NOWAIT, MT_DATA); - if (m =3D=3D NULL) { - printf("netdump_send_arp: Out of mbufs"); - return (ENOBUFS); - } - pktlen =3D arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); - m->m_len =3D pktlen; - m->m_pkthdr.len =3D pktlen; - MH_ALIGN(m, pktlen); - ah =3D mtod(m, struct arphdr *); - ah->ar_hrd =3D htons(ARPHRD_ETHER); - ah->ar_pro =3D htons(ETHERTYPE_IP); - ah->ar_hln =3D ETHER_ADDR_LEN; - ah->ar_pln =3D sizeof(struct in_addr); - ah->ar_op =3D htons(ARPOP_REQUEST); - memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN); - memcpy(ar_spa(ah), &nd_client.s_addr, sizeof(nd_client.s_addr)); - bzero(ar_tha(ah), ETHER_ADDR_LEN); - memcpy(ar_tpa(ah), &nd_gw.s_addr, sizeof(nd_gw.s_addr)); - - return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP)); -} =20 /* * Sends ARP requests to locate the server and waits for a response @@ -444,20 +319,20 @@ netdump_arp_server() { int err, polls, retries; =20 - for (retries =3D 0; retries < nd_retries && have_server_mac =3D=3D= 0; + for (retries =3D 0; (retries < nd_networking.retries) && = (nd_networking.have_remote_mac =3D=3D 0); retries++) { - err =3D netdump_send_arp(); + err =3D network_debug_send_arp(&nd_networking); if (err !=3D 0) return (err); - for (polls =3D 0; polls < nd_polls && have_server_mac =3D=3D= 0; + for (polls =3D 0; (polls < nd_networking.poll_time) && = (nd_networking.have_remote_mac =3D=3D 0); polls++) { - netdump_network_poll(); + network_debug_poll(&nd_networking); DELAY(500); } - if (have_server_mac =3D=3D 0) + if (nd_networking.have_remote_mac =3D=3D 0) printf("(ARP retry)"); } - if (have_server_mac !=3D 0) + if (nd_networking.have_remote_mac !=3D 0) return (0); =20 printf("\nARP timed out.\n"); @@ -492,7 +367,7 @@ netdump_send(uint32_t type, off_t offset, unsigned = char *data, uint32_t datalen) rcvd_acks =3D 0; retries =3D 0; =20 - MPASS(nd_ifp !=3D NULL); + MPASS(nd_networking.ifp !=3D NULL); =20 retransmit: =20 @@ -505,7 +380,7 @@ retransmit: pktlen =3D min(pktlen, NETDUMP_DATASIZE); =20 /* Second bound: the interface MTU (assume no IP = options). */ - pktlen =3D min(pktlen, nd_ifp->if_mtu - sizeof(struct = udpiphdr) - + pktlen =3D min(pktlen, nd_networking.ifp->if_mtu - = sizeof(struct udpiphdr) - sizeof(struct netdump_msg_hdr)); =20 /* @@ -543,7 +418,7 @@ retransmit: printf("netdump_send: Out of mbufs!\n"); return (ENOBUFS); } - MEXTADD(m2, data+sent_so_far, pktlen, = netdump_mbuf_nop, + MEXTADD(m2, data+sent_so_far, pktlen, = network_debug_mbuf_nop, NULL, NULL, M_RDONLY, EXT_MOD_TYPE); m2->m_len =3D pktlen; m->m_next =3D m2; @@ -568,13 +443,13 @@ retransmit: */ polls =3D 0; while (rcvd_acks !=3D want_acks) { =09 - if (polls++ > nd_polls) { - if (retries++ > nd_retries) + if (polls++ > nd_networking.poll_time) { + if (retries++ > nd_networking.retries) return (ETIMEDOUT); printf(". "); goto retransmit; } - netdump_network_poll(); + network_debug_poll(&nd_networking); DELAY(500); } nd_seqno +=3D i; @@ -582,6 +457,97 @@ retransmit: } =20 /* + * Determine if we want this IP packet. + */ +static int +nd_packet_wanted(struct network_debug *ndp, struct mbuf *m) +{ + int retval =3D 0; + struct udpiphdr *udp; + struct ip *ip; + unsigned short hlen; + + ip =3D mtod(m, struct ip*); + + /* IP version. */ + if (ip->ip_v !=3D IPVERSION) { + NETDDEBUG("%s: Bad IP version %d\n", __FUNCTION__, = ip->ip_v); + goto done; + } + +#ifdef INVARIANTS + if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D = IN_LOOPBACKNET || + (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D = IN_LOOPBACKNET) && + (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) =3D=3D 0) { + NETDDEBUG("%s: Bad IP header (RFC1122)\n", = __FUNCTION__); + goto done; + } +#endif + + /* Header length. */ + hlen =3D ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) { + NETDDEBUG("%s: Bad IP header length (%hu)\n", = __FUNCTION__, hlen); + goto done; + } + /* Checksum. */ + if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) !=3D 0) { + if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) =3D=3D 0) { + NETDDEBUG("%s: Bad IP checksum\n", = __FUNCTION__); + goto done; + } + } + + if (ntohs(ip->ip_len) < hlen) { + NETDDEBUG("%s: IP packet smaller (%hu) than header = (%hu)\n", + __FUNCTION__, ntohs(ip->ip_len), hlen); + goto done; + } + if (m->m_pkthdr.len < ntohs(ip->ip_len)) { + NETDDEBUG("%s: IP packet bigger (%hu) than ethernet = packet (%hu)\n", + __FUNCTION__, ntohs(ip->ip_len), = m->m_pkthdr.len); + goto done; + } + /* Ignore packets with IP options. */ + if (hlen > sizeof(struct ip)) { + NETDDEBUG("%s: Drop packet with IP options\n", = __FUNCTION__); + goto done; + } + if (ip->ip_p !=3D IPPROTO_UDP) { + NETDDEBUG("%s: Drop non-UDP packet\n", __FUNCTION__); + goto done; + } + + /* Do not deal with fragments. */ + if ((ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) !=3D 0) { + NETDDEBUG("%s: Drop fragmented packet\n", __FUNCTION__); + goto done; + } + /* Check that the source is the server's IP. */ + if (ip->ip_src.s_addr !=3D ndp->remote.s_addr) { + NETDDEBUG("%s: Drop packet not from server\n", = __FUNCTION__); + goto done; + } + + /* Check if the destination IP is ours. */ + if (ip->ip_dst.s_addr !=3D ndp->local.s_addr) { + NETDDEBUGV("%s: Drop packet not to our IP\n", = __FUNCTION__); + goto done; + } + udp =3D mtod(m, struct udpiphdr *); + + if (ntohs(udp->ui_u.uh_dport) !=3D NETDUMP_ACKPORT) { + NETDDEBUG("%s: not on the netdump port.\n", = __FUNCTION__); + goto done; + } +=09 + retval =3D 1; + +done: + return retval; +} + +/* * Handler for IP packets: checks their sanity and then processes any = netdump * ACK packets it finds. * @@ -589,6 +555,7 @@ retransmit: * udp_input(). * * Parameters: + * ndp A pointer to a network_debug struct * mb a pointer to an mbuf * containing the packet received * Updates *mb if m_pullup et al change the pointer * Assumes the calling function will take care of freeing = the mbuf @@ -597,27 +564,26 @@ retransmit: * void */ static void -nd_handle_ip(struct mbuf **mb) -{ - struct ip *ip; +nd_handle_ip(struct network_debug *ndp, struct mbuf **mb) +{=09 struct udpiphdr *udp; - struct netdump_ack *nd_ack; + struct ip *ip; struct mbuf *m; + struct netdump_ack *nd_ack; int rcv_ackno; unsigned short hlen; =20 /* IP processing. */ m =3D *mb; if (m->m_pkthdr.len < sizeof(struct ip)) { - NETDDEBUG("nd_handle_ip: dropping packet too small for = IP " - "header\n"); + NETDDEBUG("%s: dropping packet too small for IP = header\n", __FUNCTION__); return; } if (m->m_len < sizeof(struct ip)) { m =3D m_pullup(m, sizeof(struct ip)); *mb =3D m; if (m =3D=3D NULL) { - NETDDEBUG("nd_handle_ip: m_pullup failed\n"); + NETDDEBUG("%s: m_pullup failed\n", = __FUNCTION__); return; } } @@ -625,21 +591,21 @@ nd_handle_ip(struct mbuf **mb) =20 /* IP version. */ if (ip->ip_v !=3D IPVERSION) { - NETDDEBUG("nd_handle_ip: Bad IP version %d\n", = ip->ip_v); + NETDDEBUG("%s: Bad IP version %d\n", __FUNCTION__, = ip->ip_v); return; } =20 /* Header length. */ hlen =3D ip->ip_hl << 2; if (hlen < sizeof(struct ip)) { - NETDDEBUG("nd_handle_ip: Bad IP header length (%hu)\n", = hlen); + NETDDEBUG("%s: Bad IP header length (%hu)\n", = __FUNCTION__, hlen); return; } if (hlen > m->m_len) { m =3D m_pullup(m, hlen); *mb =3D m; if (m =3D=3D NULL) { - NETDDEBUG("nd_handle_ip: m_pullup failed\n"); + NETDDEBUG("%s: m_pullup failed\n", = __FUNCTION__); return; } ip =3D mtod(m, struct ip *); @@ -649,7 +615,7 @@ nd_handle_ip(struct mbuf **mb) if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D = IN_LOOPBACKNET || (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) =3D=3D = IN_LOOPBACKNET) && (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) =3D=3D 0) { - NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n"); + NETDDEBUG("%s: Bad IP header (RFC1122)\n", = __FUNCTION__); return; } #endif @@ -657,24 +623,24 @@ nd_handle_ip(struct mbuf **mb) /* Checksum. */ if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) !=3D 0) { if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) =3D=3D 0) { - NETDDEBUG("nd_handle_ip: Bad IP checksum\n"); + NETDDEBUG("%s: Bad IP checksum\n", = __FUNCTION__); return; } } else - NETDDEBUG("nd_handle_ip: HW didn't check IP cksum\n"); + NETDDEBUG("%s: HW didn't check IP cksum\n", = __FUNCTION__); =20 /* Convert fields to host byte order. */ ip->ip_len =3D ntohs(ip->ip_len); if (ip->ip_len < hlen) { - NETDDEBUG("nd_handle_ip: IP packet smaller (%hu) than header = (%hu)\n", - ip->ip_len, hlen); + NETDDEBUG("%s: IP packet smaller (%hu) than header = (%hu)\n", + __FUNCTION__, ip->ip_len, hlen); return; } ip->ip_off =3D ntohs(ip->ip_off); - +=09 if (m->m_pkthdr.len < ip->ip_len) { -NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) than ethernet packet = (%hu)\n", - ip->ip_len, m->m_pkthdr.len); + NETDDEBUG("%s: IP packet bigger (%hu) than ethernet = packet (%hu)\n", + __FUNCTION__, ip->ip_len, m->m_pkthdr.len); return; } if (m->m_pkthdr.len > ip->ip_len) { @@ -689,42 +655,48 @@ NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) = than ethernet packet (%hu)\n", =20 /* Ignore packets with IP options. */ if (hlen > sizeof(struct ip)) { - NETDDEBUG("nd_handle_ip: Drop packet with IP = options\n"); - return; - } - - /* Check that the source is the server's IP. */ - if (ip->ip_src.s_addr !=3D nd_server.s_addr) { - NETDDEBUG("nd_handle_ip: Drop packet not from = server\n"); - return; - } - - /* Check if the destination IP is ours. */ - if (ip->ip_dst.s_addr !=3D nd_client.s_addr) { - NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n"); + NETDDEBUG("%s: Drop packet with IP options\n", = __FUNCTION__); return; } =20 if (ip->ip_p !=3D IPPROTO_UDP) { - NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n"); + NETDDEBUG("%s: Drop non-UDP packet\n", __FUNCTION__); return; } =20 /* Do not deal with fragments. */ if ((ip->ip_off & (IP_MF | IP_OFFMASK)) !=3D 0) { - NETDDEBUG("nd_handle_ip: Drop fragmented packet\n"); + NETDDEBUG("%s: Drop fragmented packet\n", __FUNCTION__); return; } =20 /* UDP custom is to have packet length not include IP header. */ ip->ip_len -=3D hlen; =20 + /* UDP processing. */ =20 + /* + * Note that the rest of the code below is hard-wired for + * network dump, and cannot be used for other purposes. + */ + + /* Check that the source is the server's IP. */ + if (ip->ip_src.s_addr !=3D ndp->remote.s_addr) { + NETDDEBUG("%s: Drop packet not from server\n", = __FUNCTION__); + return; + } + + /* Check if the destination IP is ours. */ + if (ip->ip_dst.s_addr !=3D ndp->local.s_addr) { + NETDDEBUGV("%s: Drop packet not to our IP\n", = __FUNCTION__); + return; + } + /* Get IP and UDP headers together, along with the netdump = packet. */ if (m->m_pkthdr.len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { - NETDDEBUG("nd_handle_ip: Ignoring small packet\n"); + NETDDEBUG("%s: Ignoring small packet\n", __FUNCTION__); return; } if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct = netdump_ack)) { @@ -732,19 +704,17 @@ NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) = than ethernet packet (%hu)\n", sizeof(struct netdump_ack)); *mb =3D m; if (m =3D=3D NULL) { - NETDDEBUG("nd_handle_ip: m_pullup failed\n"); + NETDDEBUG("%s: m_pullup failed\n", = __FUNCTION__); return; } } udp =3D mtod(m, struct udpiphdr *); =20 if (ntohs(udp->ui_u.uh_dport) !=3D NETDUMP_ACKPORT) { - NETDDEBUG("not on the netdump port.\n"); + NETDDEBUG("%s: not on the netdump port.\n", = __FUNCTION__); return; } =20 - /* Netdump processing. */ - /* * Packet is meant for us. Extract the ack sequence number and = the * port number if necessary. @@ -752,233 +722,30 @@ NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) = than ethernet packet (%hu)\n", nd_ack =3D (struct netdump_ack *)(mtod(m, caddr_t) + sizeof(struct udpiphdr)); rcv_ackno =3D ntohl(nd_ack->na_seqno); - if (nd_server_port =3D=3D NETDUMP_PORT) - nd_server_port =3D ntohs(udp->ui_u.uh_sport); + if (nd_remote_port =3D=3D NETDUMP_PORT) + nd_remote_port =3D ntohs(udp->ui_u.uh_sport); if (rcv_ackno >=3D nd_seqno + 64) - printf("nd_handle_ip: ACK %d too far in future!\n", = rcv_ackno); + printf("%s: ACK %d too far in future!\n", __FUNCTION__, = rcv_ackno); else if (rcv_ackno >=3D nd_seqno) { - /* We're interested in this ack. Record it. */ rcvd_acks |=3D 1 << (rcv_ackno-nd_seqno); } -} - -/* - * Handler for ARP packets: checks their sanity and then - * 1. If the ARP is a request for our IP, respond with our MAC address - * 2. If the ARP is a response from our server, record its MAC address - * - * It needs to replicate partially the behaviour of arpintr() and - * in_arpinput(). - * - * Parameters: - * mb a pointer to an mbuf * containing the packet received - * Updates *mb if m_pullup et al change the pointer - * Assumes the calling function will take care of freeing = the mbuf - * - * Return value: - * void - */ -static void -nd_handle_arp(struct mbuf **mb) -{ - char buf[INET_ADDRSTRLEN]; - struct in_addr isaddr, itaddr, myaddr; - struct ether_addr dst; - struct mbuf *m; - struct arphdr *ah; - struct ifnet *ifp; - uint8_t *enaddr; - int req_len, op; - - m =3D *mb; - ifp =3D m->m_pkthdr.rcvif; - if (m->m_len < sizeof(struct arphdr)) { - m =3D m_pullup(m, sizeof(struct arphdr)); - *mb =3D m; - if (m =3D=3D NULL) { - NETDDEBUG("nd_handle_arp: runt packet: m_pullup = failed\n"); - return; - } - } - ah =3D mtod(m, struct arphdr *); - - if (ntohs(ah->ar_hrd) !=3D ARPHRD_ETHER) { - NETDDEBUG("nd_handle_arp: unknown hardware address = 0x%2D)\n", - (unsigned char *)&ah->ar_hrd, ""); - return; - } - if (ntohs(ah->ar_pro) !=3D ETHERTYPE_IP) { - NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol = %d\n", - ntohs(ah->ar_pro)); - return; - } - req_len =3D arphdr_len2(ifp->if_addrlen, sizeof(struct = in_addr)); - if (m->m_len < req_len) { - m =3D m_pullup(m, req_len); - *mb =3D m; - if (m =3D=3D NULL) { - NETDDEBUG("nd_handle_arp: runt packet: m_pullup = failed\n"); - return; - } - } - ah =3D mtod(m, struct arphdr *); - - op =3D ntohs(ah->ar_op); - memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); - memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr)); - enaddr =3D (uint8_t *)IF_LLADDR(ifp); - myaddr =3D nd_client; - - if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) { - NETDDEBUG("nd_handle_arp: ignoring ARP from myself\n"); - return; - } - - if (isaddr.s_addr =3D=3D nd_client.s_addr) { - printf("nd_handle_arp: %*D is using my IP address = %s!\n", - ifp->if_addrlen, (u_char *)ar_sha(ah), ":", - inet_ntoa(isaddr)); - return; - } =20 - if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) { - NETDDEBUG("nd_handle_arp: ignoring ARP from broadcast = address\n"); - return; - } - - if (op =3D=3D ARPOP_REPLY) { - if (isaddr.s_addr !=3D nd_gw.s_addr) { - inet_ntoa_r(isaddr, buf); -NETDDEBUG("nd_handle_arp: ignoring ARP reply from %s (not netdump = server)\n", - buf); - return; - } - memcpy(nd_gw_mac.octet, ar_sha(ah), - min(ah->ar_hln, ETHER_ADDR_LEN)); - have_server_mac =3D 1; - NETDDEBUG("\nnd_handle_arp: Got server MAC address = %6D\n", - nd_gw_mac.octet, ":"); - return; - } - - if (op !=3D ARPOP_REQUEST) { - NETDDEBUG("nd_handle_arp: Ignoring ARP = non-request/reply\n"); - return; - } - - if (itaddr.s_addr !=3D nd_client.s_addr) { - NETDDEBUG("nd_handle_arp: ignoring ARP not to our = IP\n"); - return; - } - - memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); - memcpy(ar_sha(ah), enaddr, ah->ar_hln); - memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); - memcpy(ar_spa(ah), &itaddr, ah->ar_pln); - ah->ar_op =3D htons(ARPOP_REPLY); - ah->ar_pro =3D htons(ETHERTYPE_IP); - m->m_flags &=3D ~(M_BCAST|M_MCAST); - m->m_len =3D sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln); - m->m_pkthdr.len =3D m->m_len; - - memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN); - netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP); - *mb =3D NULL; } =20 /* - * Handler for incoming packets directly from the network adapter - * Identifies the packet type (IP or ARP) and passes it along to one of = the - * helper functions nd_handle_ip or nd_handle_arp. - * - * It needs to replicate partially the behaviour of ether_input() and - * ether_demux(). - * - * Parameters: - * ifp the interface the packet came from (should be nd_ifp) - * m an mbuf containing the packet received - * - * Return value: - * void + * netdump_pkt_in + * Simple wrapper for network_debug_pkt_in; this is called + * as ifp->if_input() by the ethernet driver. We simply let + * network_debug_pkt_in() procss it, and decide which helper + * function to call. */ static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) { - struct ether_header *eh; - u_short etype; - - /* Ethernet processing. */ - if ((m->m_flags & M_PKTHDR) =3D=3D 0) { - NETDDEBUG_IF(ifp, - "netdump_pkt_in: Discard frame without packet = header\n"); - goto done; - } - if (m->m_len < ETHER_HDR_LEN) { - NETDDEBUG_IF(ifp, -"netdump_pkt_in: Discard frame without leading eth header (len %u = pktlen %u)\n", - m->m_len, m->m_pkthdr.len); - goto done; - } - if ((m->m_flags & M_HASFCS) !=3D 0) { - m_adj(m, -ETHER_CRC_LEN); - m->m_flags &=3D ~M_HASFCS; - } - eh =3D mtod(m, struct ether_header *); - m->m_pkthdr.PH_loc.ptr =3D eh; - etype =3D ntohs(eh->ether_type); - if ((m->m_flags & M_VLANTAG) !=3D 0 || etype =3D=3D = ETHERTYPE_VLAN) { - NETDDEBUG_IF(ifp, "netdump_pkt_in: Ignoring vlan = packets\n"); - goto done; - } - - /* XXX: Probably must also check if we're the recipient MAC = address. */ - - /* Done ethernet processing. Strip off the ethernet header. */ - m_adj(m, ETHER_HDR_LEN); - switch (etype) { - case ETHERTYPE_ARP: - nd_handle_arp(&m); - break; - case ETHERTYPE_IP: - nd_handle_ip(&m); - break; - default: - NETDDEBUG_IF(ifp, - "netdump_pkt_in: Dropping unknown ethertype = %hu\n", - etype); - break; - } -done: - if (m !=3D NULL) - m_freem(m); + network_debug_pkt_in(&nd_networking, m); } =20 -/* - * After trapping, instead of assuming that most of the network stack = is sane - * just poll the driver directly for packets. - * - * Parameters: - * void - * - * Returns: - * void - */ -static void -netdump_network_poll() -{ - - MPASS(nd_ifp !=3D NULL); - -#if defined(KDB) && !defined(KDB_UNATTENDED) - if (panicstr !=3D NULL) - nd_ifp->if_ndumpfuncs->ne_poll_unlocked(nd_ifp, - POLL_AND_CHECK_STATUS, 1000); - else -#endif - nd_ifp->if_ndumpfuncs->ne_poll_locked(nd_ifp, - POLL_AND_CHECK_STATUS, 1000); -} =20 /*- * Dumping specific primitives. @@ -1033,6 +800,25 @@ netdump_dumper(void *priv __unused, void *virtual, } =20 /* + * Preparation routine. This sets some things up, and also ensures + * there we have an interface and address. + */ +static int +nd_prepare(struct network_debug *ndp) +{ + int error =3D 0; + + if (ndp->remote.s_addr =3D=3D INADDR_ANY) { + printf("%s: Can't netdump; no server IP given\n", = __FUNCTION__); + return (EINVAL); + } + + error =3D network_debug_prepare(ndp); + return error; +} + + +/* * Dumper routine, specular to dumpsys(). * * Parameters: @@ -1045,13 +831,11 @@ int netdumpsys() { struct dumperinfo dumper; - void (*old_if_input)(struct ifnet *, struct mbuf *); - int error, found, must_lock, nd_gw_unset; + int error, found, must_lock; + void (*old_if_input)(struct ifnet *, struct mbuf *) =3D NULL; =20 - old_if_input =3D NULL; error =3D 0; found =3D 0; - nd_gw_unset =3D 0; must_lock =3D 1; #if defined(KDB) && !defined(KDB_UNATTENDED) if (panicstr !=3D NULL) @@ -1062,51 +846,21 @@ netdumpsys() if (nd_enable =3D=3D 0) return (EINVAL); =20 - /* Lookup the right if device to be used in the dump. */ - if (must_lock !=3D 0) - IFNET_RLOCK_NOSLEEP(); - TAILQ_FOREACH(nd_ifp, &V_ifnet, if_link) { - if (!strncmp(nd_ifp->if_xname, nd_ifp_str, - strlen(nd_ifp->if_xname)) && - netdump_supported_nic(nd_ifp)) { - found =3D 1; - break; - } - } - if (must_lock !=3D 0) - IFNET_RUNLOCK_NOSLEEP(); - if (found =3D=3D 0) { - printf("netdumpsys: Can't netdump: no valid NIC = given\n"); - return (EINVAL); - } - - MPASS(nd_ifp !=3D NULL); - - if (nd_server.s_addr =3D=3D INADDR_ANY) { - printf("netdumpsys: Can't netdump; no server IP = given\n"); - return (EINVAL); - } - if (nd_client.s_addr =3D=3D INADDR_ANY) { - printf("netdumpsys: Can't netdump; no client IP = given\n"); - return (EINVAL); + error =3D nd_prepare(&nd_networking); + if (error) { + goto trig_abort; } =20 /* * nd_server_port could have switched after the first ack the * first time it gets called. Adjust it accordingly. */ - nd_server_port =3D NETDUMP_PORT; - if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock = !=3D 0) - nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp); + nd_remote_port =3D NETDUMP_PORT; =20 /* Make the card use *our* receive callback. */ - old_if_input =3D nd_ifp->if_input; - nd_ifp->if_input =3D netdump_pkt_in; + old_if_input =3D nd_networking.ifp->if_input; + nd_networking.ifp->if_input =3D netdump_pkt_in; =20 - if (nd_gw.s_addr =3D=3D INADDR_ANY) { - nd_gw.s_addr =3D nd_server.s_addr; - nd_gw_unset =3D 1; - } printf("\n-----------------------------------\n"); printf("netdump in progress. searching for server.. "); if (netdump_arp_server()) { @@ -1119,7 +873,7 @@ netdumpsys() error =3D EINVAL; goto trig_abort; } - printf("dumping to %s (%6D)\n", inet_ntoa(nd_server), = nd_gw_mac.octet, + printf("dumping to %s (%6D)\n", inet_ntoa(nd_networking.remote), = nd_networking.remote_mac.octet, ":"); printf("-----------------------------------\n"); =20 @@ -1142,12 +896,9 @@ netdumpsys() } printf("\nnetdump finished.\n"); trig_abort: - if (nd_gw_unset !=3D 0) - nd_gw.s_addr =3D INADDR_ANY; + network_debug_finish(&nd_networking); if (old_if_input) - nd_ifp->if_input =3D old_if_input; - if ((nd_ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && must_lock = !=3D 0) - nd_ifp->if_ndumpfuncs->ne_enable_intr(nd_ifp); + nd_networking.ifp->if_input =3D old_if_input; return (error); } =20 @@ -1160,6 +911,10 @@ trig_abort: * (locates the first available NIC and uses the first IPv4 IP on that = card as * the client IP). Leaves the server IP unconfigured. * + * The parenthical comment above is completely wrong. Not only does it = not do anything + * w.r.t. IP address, but it is called during kernel initialization -- = so no NICs + * are configured at the time. We instead do this in nd_prepare(). + * * Parameters: * void *, unused * @@ -1169,22 +924,21 @@ trig_abort: static void netdump_config_defaults(void *dummy __unused) { - struct ifnet *ifp; - int found; - - nd_ifp =3D NULL; - nd_server.s_addr =3D INADDR_ANY; - nd_client.s_addr =3D INADDR_ANY; - nd_gw.s_addr =3D INADDR_ANY; - - if (nd_server_tun[0] !=3D '\0') - inet_aton(nd_server_tun, &nd_server); - if (nd_client_tun[0] !=3D '\0') - inet_aton(nd_client_tun, &nd_client); + nd_networking.ifp =3D NULL; + nd_networking.remote.s_addr =3D INADDR_ANY; + nd_networking.local.s_addr =3D INADDR_ANY; + nd_networking.gw.s_addr =3D INADDR_ANY; + + if (nd_remote_tun[0] !=3D '\0') + inet_aton(nd_remote_tun, &nd_networking.remote); + if (nd_local_tun[0] !=3D '\0') + inet_aton(nd_local_tun, &nd_networking.local); if (nd_gw_tun[0] !=3D '\0') - inet_aton(nd_gw_tun, &nd_gw); + inet_aton(nd_gw_tun, &nd_networking.gw); if (nd_nic_tun[0] !=3D '\0') { - found =3D 0; +#if 0 + int found =3D 0; + struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (!strncmp(ifp->if_xname, nd_nic_tun, @@ -1194,8 +948,13 @@ netdump_config_defaults(void *dummy __unused) } } IFNET_RUNLOCK_NOSLEEP(); - if (found !=3D 0 && netdump_supported_nic(ifp)) - nd_ifp =3D ifp; + if (found !=3D 0 && network_debug_supported_nic(ifp)) + nd_networking.ifp =3D ifp; +#else + strlcpy(nd_networking.if_name, nd_nic_tun, = sizeof(nd_networking.if_name)); +#endif } } + SYSINIT(netdump, SI_SUB_KLD, SI_ORDER_ANY, netdump_config_defaults, = NULL); + diff --git a/sys/netinet/network_debug.c b/sys/netinet/network_debug.c new file mode 100644 index 0000000..933aec5 --- /dev/null +++ b/sys/netinet/network_debug.c @@ -0,0 +1,574 @@ +/*- + * + * Copyright (c) 2014 iXSystems, Inc. All rights reserved. + * + * Copyright (c) 2005-2011 Sandvine Incorporated. All rights reserved. + * Copyright (c) 2000 Darrell Anderson + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef DDB +#include +#endif + +//#define NETWORK_DEBUG_DEBUG 10 + +#ifdef NETWORK_DEBUG_DEBUG +# define NETDDEBUG(f, ...) printf((f), ## = __VA_ARGS__) +# define NETDDEBUG_IF(i, f, ...) if_printf((i), (f), ## = __VA_ARGS__) +# if NETWORK_DEBUG_DEBUG > 1 +# define NETDDEBUGV(f, ...) printf((f), ## = __VA_ARGS__) +# define NETDDEBUGV_IF(i, f, ...) if_printf((i), (f), ## = __VA_ARGS__) +# else +# define NETDDEBUGV(f, ...) +# define NETDDEBUGV_IF(i, f, ...) +# endif +#else +# define NETDDEBUG(f, ...) +# define NETDDEBUG_IF(i, f, ...) +# define NETDDEBUGV(f, ...) +# define NETDDEBUGV_IF(i, f, ...) +#endif + +/* + * Handles creation of the ethernet header, then places outgoing = packets into + * the tx buffer for the NIC + * + * Parameters: + * m The mbuf containing the packet to be sent (will be freed = by + * this function or the NIC driver) + * ifp The interface to send on + * dst The destination ethernet address (source address will be = looked + * up using ifp) + * etype The ETHERTYPE_* value for the protocol that is being = sent + * + * Returns: + * int see errno.h, 0 for success + */ +int +network_debug_ether_output(struct mbuf *m, struct ifnet *ifp, struct = ether_addr dst, + u_short etype) +{ + struct ether_header *eh; + + /* Fill in the ethernet header. */ + M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); + if (m =3D=3D NULL) { + NETDDEBUG("%s: Out of mbufs\n", __FUNCTION__); + return (ENOBUFS); + } + eh =3D mtod(m, struct ether_header *); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); + eh->ether_type =3D htons(etype); + + if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) !=3D IFF_UP) || + (ifp->if_drv_flags & IFF_DRV_RUNNING) !=3D IFF_DRV_RUNNING) = { + if_printf(ifp, "%s: Interface isn't up\n", = __FUNCTION__); + m_freem(m); + return (ENETDOWN); + } + return ((ifp->if_transmit)(ifp, m)); +} + +/* + * Netdump wraps external mbufs around address ranges. unlike most = sane + * counterparts, netdump uses a stop-and-wait approach to flow control = and + * retransmission, so the ack obviates the need for mbuf reference + * counting. We still need to tell other mbuf handlers not to do = anything + * special with our mbufs, so specify this nop handler. + * + * Parameters: + * ptr data to free (ignored) + * opt_args callback pointer (ignored) + * + * Returns: + * void + */ +int +network_debug_mbuf_nop(struct mbuf *mp __unused, void *ptr __unused, = void *opt_args __unused) +{ + return EXT_FREE_OK; +} +/* + * Handler for ARP packets: checks their sanity and then + * 1. If the ARP is a request for our IP, respond with our MAC address + * 2. If the ARP is a response from our server, record its MAC address + * + * It needs to replicate partially the behaviour of arpintr() and + * in_arpinput(). + * + * Parameters: + * mb a pointer to an mbuf * containing the packet received + * Updates *mb if m_pullup et al change the pointer + * Assumes the calling function will take care of freeing = the mbuf + * + * Return value: + * void + */ +void +network_debug_handle_arp(struct network_debug *ndp, struct mbuf **mb) +{ + char buf[INET_ADDRSTRLEN]; + struct in_addr isaddr, itaddr, myaddr; + struct ether_addr dst; + struct mbuf *m; + struct arphdr *ah; + struct ifnet *ifp; + uint8_t *enaddr; + int req_len, op; + + NETDDEBUG("%s(%d)\n", __FUNCTION__, __LINE__); + + m =3D *mb; + ifp =3D m->m_pkthdr.rcvif; + if (m->m_len < sizeof(struct arphdr)) { + m =3D m_pullup(m, sizeof(struct arphdr)); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("%s: runt packet: m_pullup failed\n", = __FUNCTION__); + return; + } + } + ah =3D mtod(m, struct arphdr *); + + if (ntohs(ah->ar_hrd) !=3D ARPHRD_ETHER) { + NETDDEBUG("%s: unknown hardware address 0x%2D)\n", + __FUNCTION__, + (unsigned char *)&ah->ar_hrd, ""); + return; + } + if (ntohs(ah->ar_pro) !=3D ETHERTYPE_IP) { + NETDDEBUG("%s: Drop ARP for unknown protocol %d\n", + __FUNCTION__, + ntohs(ah->ar_pro)); + return; + } + req_len =3D arphdr_len2(ifp->if_addrlen, sizeof(struct = in_addr)); + if (m->m_len < req_len) { + m =3D m_pullup(m, req_len); + *mb =3D m; + if (m =3D=3D NULL) { + NETDDEBUG("%s: runt packet: m_pullup failed\n", = __FUNCTION__); + return; + } + } + ah =3D mtod(m, struct arphdr *); + + op =3D ntohs(ah->ar_op); + memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); + memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr)); + enaddr =3D (uint8_t *)IF_LLADDR(ifp); + myaddr =3D ndp->local; + + if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) { + NETDDEBUG("%s: ignoring ARP from myself\n", = __FUNCTION__); + return; + } + + if (ndp->local.s_addr !=3D INADDR_ANY && + isaddr.s_addr =3D=3D ndp->local.s_addr) { + NETDDEBUG("%s: %*D is using my IP address %s!\n", + __FUNCTION__, + ifp->if_addrlen, (u_char *)ar_sha(ah), ":", + inet_ntoa(isaddr)); + return; + } + + if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) { + NETDDEBUG("%s: ignoring ARP from broadcast address\n", = __FUNCTION__); + return; + } + + if (op =3D=3D ARPOP_REPLY) { + if (ndp->gw.s_addr =3D=3D INADDR_ANY && = ndp->remote.s_addr =3D=3D INADDR_ANY) { + NETDDEBUG("%s: Ignoring ARP because we do not = have an address yet", __FUNCTION__); + return; + } + if (isaddr.s_addr !=3D (ndp->gw.s_addr =3D=3D INADDR_ANY = ? ndp->remote.s_addr : ndp->gw.s_addr)) { + inet_ntoa_r(isaddr, buf); + NETDDEBUG("%sp: ignoring ARP reply from %s (not = desired remote)\n", + __FUNCTION__, buf); + return; + } + memcpy(ndp->remote_mac.octet, ar_sha(ah), + min(ah->ar_hln, ETHER_ADDR_LEN)); + ndp->have_remote_mac =3D 1; + NETDDEBUG("\n%s: Got remote MAC address %6D\n", = __FUNCTION__, + ndp->remote_mac.octet, ":"); + return; + } + + if (op !=3D ARPOP_REQUEST) { + NETDDEBUG("%s: Ignoring ARP non-request/reply\n", = __FUNCTION__); + return; + } + + if (itaddr.s_addr !=3D ndp->local.s_addr) { + NETDDEBUG("%s: ignoring ARP not to our IP\n", = __FUNCTION__); + return; + } + + memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); + memcpy(ar_sha(ah), enaddr, ah->ar_hln); + memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); + memcpy(ar_spa(ah), &itaddr, ah->ar_pln); + ah->ar_op =3D htons(ARPOP_REPLY); + ah->ar_pro =3D htons(ETHERTYPE_IP); + m->m_flags &=3D ~(M_BCAST|M_MCAST); + m->m_len =3D sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln); + m->m_pkthdr.len =3D m->m_len; + + memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN); + network_debug_ether_output(m, ifp, dst, ETHERTYPE_ARP); + *mb =3D NULL; +} +/* + * Builds and sends a single ARP request to locate the server + * + * Parameters: + * void + * + * Return value: + * 0 on success + * errno on error + */ +int +network_debug_send_arp(struct network_debug *ndp) +{ + struct ether_addr bcast; + struct mbuf *m; + struct arphdr *ah; + int pktlen; + + MPASS(ndp->ifp !=3D NULL); + + /* Fill-up a broadcast address. */ + memset(&bcast, 0xFF, ETHER_ADDR_LEN); + MGETHDR(m, M_NOWAIT, MT_DATA); + if (m =3D=3D NULL) { + NETDDEBUG("%s: Out of mbufs", __FUNCTION__); + return (ENOBUFS); + } + pktlen =3D arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); + m->m_len =3D pktlen; + m->m_pkthdr.len =3D pktlen; + MH_ALIGN(m, pktlen); + ah =3D mtod(m, struct arphdr *); + ah->ar_hrd =3D htons(ARPHRD_ETHER); + ah->ar_pro =3D htons(ETHERTYPE_IP); + ah->ar_hln =3D ETHER_ADDR_LEN; + ah->ar_pln =3D sizeof(struct in_addr); + ah->ar_op =3D htons(ARPOP_REQUEST); + memcpy(ar_sha(ah), IF_LLADDR(ndp->ifp), ETHER_ADDR_LEN); + memcpy(ar_spa(ah), &ndp->local.s_addr, = sizeof(ndp->local.s_addr)); + bzero(ar_tha(ah), ETHER_ADDR_LEN); + memcpy(ar_tpa(ah), (ndp->gw.s_addr =3D=3D INADDR_ANY) ? = &ndp->remote.s_addr : &ndp->gw.s_addr, sizeof(ndp->gw.s_addr)); + + return (network_debug_ether_output(m, ndp->ifp, bcast, = ETHERTYPE_ARP)); +} +/* + * After trapping, instead of assuming that most of the network stack = is sane + * just poll the driver directly for packets. + * + * Parameters: + * void + * + * Returns: + * void + */ +void +network_debug_poll(struct network_debug *ndp) +{ + + MPASS(ndp->ifp !=3D NULL); + +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr !=3D NULL) + ndp->ifp->if_ndumpfuncs->ne_poll_unlocked(ndp->ifp, + POLL_AND_CHECK_STATUS, 1000); + else +#endif + ndp->ifp->if_ndumpfuncs->ne_poll_locked(ndp->ifp, + POLL_AND_CHECK_STATUS, 1000); +} + +/* + * Handler for incoming packets directly from the network adapter + * Identifies the packet type (IP or ARP) and passes it along to one of = the + * helper functions. + * + * Note: Each subsystem that wants to use this will need to set the + * interface if_input pointer to something that calls this. E.g. + * static void packet_in(struct ifnet *ifp, struct mbuf *m) { + * network_debug_pkt_in(&my_netdeb, m); + * } + * and + * ndp->ifp->if_input =3D packet_in; + * + * It needs to replicate partially the behaviour of ether_input() and + * ether_demux(). + * + * Parameters: + * ndp The network_debug structure for our purposes. + * m an mbuf containing the packet received + * + * Return value: + * void + */ + +void +network_debug_pkt_in(struct network_debug *ndp, struct mbuf *m) +{ + static const u_char no_mac[ETHER_ADDR_LEN] =3D { 0 }; + static const u_char broadcast_mac[ETHER_ADDR_LEN] =3D { 0xff, = 0xff, 0xff, 0xff, 0xff, 0xff }; + + struct ether_header *eh, temp_eh; + u_short etype; + int send_through =3D 1; + + NETDDEBUG("%s(%d): Got an mbuf\n", __FUNCTION__, __LINE__); + + /* Ethernet processing. */ + if ((m->m_flags & M_PKTHDR) =3D=3D 0) { + NETDDEBUG_IF(ndp->ifp, "%s: Discard frame without packet = header\n", __FUNCTION__); + goto done; + } + if (m->m_len < ETHER_HDR_LEN) { + NETDDEBUG_IF(ndp->ifp, + "%s: Discard frame without leading eth = header (len %u pktlen %u)\n", + __FUNCTION__, m->m_len, m->m_pkthdr.len); + goto done; + } + if ((m->m_flags & M_HASFCS) !=3D 0) { + m_adj(m, -ETHER_CRC_LEN); + m->m_flags &=3D ~M_HASFCS; + } + eh =3D mtod(m, struct ether_header *); + temp_eh =3D *eh; // Not a big structure, so should be = safe to have on stack and copy + m->m_pkthdr.PH_loc.ptr =3D eh; + etype =3D ntohs(eh->ether_type); + if ((m->m_flags & M_VLANTAG) !=3D 0 || etype =3D=3D = ETHERTYPE_VLAN) { + NETDDEBUG_IF(ndp->ifp, "netdump_pkt_in: Ignoring vlan = packets\n"); + goto done; + } + + /* + * XXX: Probably must also check if we're the recipient MAC = address. + * But we need to also check for broadcast, and whether or not = we have + * a MAC address set. + */ + + if (bcmp(&ndp->local_mac, no_mac, sizeof(no_mac)) !=3D 0 && + bcmp(&eh->ether_dhost, broadcast_mac, sizeof(broadcast_mac)) = !=3D 0 && + bcmp(&ndp->local_mac, &eh->ether_dhost, = sizeof(ndp->local_mac)) !=3D 0) { + NETDDEBUG("%s: Ethernet packet for destination %6D is = not for me (%6D)\n", + __FUNCTION__, + eh->ether_dhost, ":", + ndp->local_mac.octet, ":"); + goto done; + } + + /* Done ethernet processing. Strip off the ethernet header. */ + m_adj(m, ETHER_HDR_LEN); + switch (etype) { + case ETHERTYPE_ARP: + NETDDEBUG("%s(%d): ETHERTYPE_ARP\n", __FUNCTION__, = __LINE__); + network_debug_handle_arp(ndp, &m); + break; + case ETHERTYPE_IP: + NETDDEBUG("%s(%d: ETHERTYPE_IP\n", __FUNCTION__, = __LINE__); + if (ndp->wanted) { + send_through =3D (ndp->wanted)(ndp, m); + if (send_through) { + /* + * If this packet is wanted, then we = stash + * the source information into ndp + */ + NETDDEBUG("%s(%d): Setting = ndp->remote_mac to %6D\n", __FUNCTION__, __LINE__, eh->ether_shost, = ":"); + bcopy(&eh->ether_shost, = &ndp->remote_mac, sizeof(ndp->remote_mac)); + ndp->remote =3D (mtod(m, struct = udpiphdr*))->ui_src; + NETDDEBUG("%s(%d): Set ndp->remote to = %#x\n", __FUNCTION__, __LINE__, ndp->remote.s_addr); + } + } + if (send_through && ndp->ip_handler) + (*ndp->ip_handler)(ndp, &m); + NETDDEBUG("%s(%d): Done with ip_handler\n", = __FUNCTION__, __LINE__); + break; + default: + NETDDEBUG_IF(ndp->ifp, + "%s: Dropping unknown ethertype %hu\n", + __FUNCTION__, + etype); + break; + } +done: + if (m !=3D NULL) + m_freem(m); + NETDDEBUG("%s(%d): Done here\n", __FUNCTION__, __LINE__); +} + +int +network_debug_prepare(struct network_debug *ndp) +{ + int must_lock =3D 1; + int error =3D 0; + +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr !=3D NULL) + must_lock =3D 0; +#endif + /* Lookup the right if device to be used */ + if (must_lock !=3D 0) + IFNET_RLOCK_NOSLEEP(); + + if (ndp->ifp =3D=3D NULL) { + struct ifnet *ifp =3D NULL; + /* + * Look for an interface. If one was specified via = sysctl + * or tunable, look for that name; otherwise, pick the = first + * one that claims to support netdump. + */ + TAILQ_FOREACH(ifp, &V_ifnet, if_link) { + if ((ndp->if_name[0] =3D=3D 0 || + !strcmp(ifp->if_xname, ndp->if_name)) && + network_debug_supported_nic(ifp)) { + ndp->ifp =3D ifp; + break; + } + } + if (must_lock !=3D 0) + IFNET_RUNLOCK_NOSLEEP(); + if (ndp->ifp =3D=3D NULL) { + NETDDEBUG("%s: No valid NIC found\n", = __FUNCTION__); + return (EINVAL); + } + } + + MPASS(ndp->ifp !=3D NULL); +=09 + bcopy((void*)IF_LLADDR(ndp->ifp), (void*)&ndp->local_mac, = sizeof(ndp->local_mac)); + + if (ndp->local.s_addr =3D=3D INADDR_ANY) { + /* + * If we don't have an address yet, let's try to + * find it. + */ + struct ifreq req; + struct sockaddr_in *sin; + if (ndp->ifp->if_addr =3D=3D NULL || + ndp->ifp->if_addr->ifa_addr =3D=3D NULL) { + NETDDEBUG("%s: Can't netdump; no local IP given, = and can't figure it out\n", __FUNCTION__); + return (EINVAL); + } + bzero(&req, sizeof(req)); + if ((error =3D in_control(NULL, SIOCGIFADDR, = (caddr_t)&req, ndp->ifp, NULL)) !=3D 0) { + NETDDEBUG("%s: Can't ask interface for IP = address to do error %d\n", __FUNCTION__, error); + return (error); + } + sin =3D (struct sockaddr_in*)&req.ifr_addr; + if (sin->sin_family !=3D AF_INET) { + NETDDEBUG("%s: Address family for interface = %s%u is %d, not AF_INET (%d)\n", __FUNCTION__, ndp->ifp->if_dname, = ndp->ifp->if_dunit, sin->sin_family, AF_INET); + return (EINVAL); + } + if (sin->sin_addr.s_addr =3D=3D INADDR_ANY) { + NETDDEBUG("%s: Address for interface is = INADDR_ANY\n", __FUNCTION__); + return (EINVAL); + } + ndp->local.s_addr =3D sin->sin_addr.s_addr; + NETDDEBUG("%s(%d): ndp->local.s_addr =3D %#x\n", = __FUNCTION__, __LINE__, ndp->local.s_addr); + } + + if ((ndp->ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && = must_lock !=3D 0) + ndp->ifp->if_ndumpfuncs->ne_disable_intr(ndp->ifp); + + return 0; +} + +void +network_debug_finish(struct network_debug *ndp) +{ + int must_lock =3D 1; + +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr !=3D NULL) + must_lock =3D 0; +#endif + + if (ndp->local.s_addr !=3D INADDR_ANY) { + if ((ndp->ifp->if_capenable & IFCAP_POLLING) =3D=3D 0 && = must_lock !=3D 0) + = ndp->ifp->if_ndumpfuncs->ne_enable_intr(ndp->ifp); + } + return; +} diff --git a/sys/netinet/network_debug.h b/sys/netinet/network_debug.h new file mode 100644 index 0000000..af7c7b7 --- /dev/null +++ b/sys/netinet/network_debug.h @@ -0,0 +1,89 @@ +#ifndef _NETINET_NETWORK_DEBUG_H +# define _NETINET_NETWORK_DEBUG_H + +# include + +struct network_debug { + int poll_time; // Time to poll the nic = (0.5ms for each poll) + int retries; // Number of times to = try retransmitting un-acked packets + int have_remote_mac; // Indicates whether we = got the gw / remote mac yet (via ARP) + char if_name[IFNAMSIZ]; // The interface name to = use + struct ifnet *ifp; // Interface to use + struct in_addr gw; // Address of gateway, = if any + struct ether_addr remote_mac; // MAC address to which = to send packets (may be remote, may be router) + struct in_addr remote; // IP address for remote = end + struct ether_addr local_mac; // Our MAC address + struct in_addr local; // Our IP address! + + /* + * If non-NULL, this function will be called on the receipt of + * a packet to determine if it is a desired packet. The mbuf* + * will be the start of the IP packet -- the ethernet header + * will have been stripped off by this point. The function may + * want to check the source address/port and/or the destination + * port. (If the destination address or MAC don't match the + * ones in this structure, it won't get that far.) The caller + * should not alter the mbuf in any way. + * + * If the function returns non-zero, then remote_mac and + * remote will be set appropriately based on the packet. + * + * This function is called from network_debug_pkt_in, and only + * for IP packets. + * + * Saying the packet is wanted doesn't guarantee it'll be = processed + * by the ip_handler. But if a wanted function is set, it = should + * make an effort to indicate whether it really wants the = packet. + */ + int (*wanted)(struct network_debug *, struct mbuf = *); + + /* + * If non-NULL, this function will be called to process any IP = packets. + * Currently, only UDP is supported. The mbuf** will refer to = the + * UDP packet. + * + * The caller is allowed to manipulate the mbuf. + */ + void (*ip_handler)(struct network_debug *, struct = mbuf **); +}; + +/* + * Checks for netdump support on a network interface + * + * Parameters: + * ifp The network interface that is being tested for support + * + * Returns: + * int 1 if the interface is supported, 0 if not + */ +static __inline int +network_debug_supported_nic(struct ifnet *ifp) +{ + + return (ifp->if_ndumpfuncs !=3D NULL); +} + +int network_debug_ether_output(struct mbuf *, struct ifnet *, struct = ether_addr dst, u_short etype); + +int network_debug_mbuf_nop(struct mbuf *mp, void *, void *); + +void network_debug_handle_arp(struct network_debug *ndp, struct mbuf = **); + +int network_debug_send_arp(struct network_debug *ndp); + +void network_debug_poll(struct network_debug *ndp); + +void network_debug_pkt_in(struct network_debug *, struct mbuf *); + +/* + * Call to initialize the network_debug structure -- it will look for = an interface, + * IP address, etc. + */ +int network_debug_prepare(struct network_debug *ndp); + +/* + * Call when done. It does less than the previous function. + */ +void network_debug_finish(struct network_debug *ndp); + +#endif /* _NETINET_NETWORK_DEBUG_H */ diff --git a/usr.sbin/netdumpsrv/netdumpsrv.c = b/usr.sbin/netdumpsrv/netdumpsrv.c index af6ac6d..283a003 100644 --- a/usr.sbin/netdumpsrv/netdumpsrv.c +++ b/usr.sbin/netdumpsrv/netdumpsrv.c @@ -454,7 +454,7 @@ handle_kdh(struct netdump_client *client, struct = netdump_msg *msg) return; =20 client->any_data_rcvd =3D 1; - h =3D (struct kerneldumpheader *)msg->nm_data; + h =3D (void*)msg->nm_data; if (msg->nm_hdr.mh_len < sizeof(struct kerneldumpheader)) { LOGERR("Bad KDH from %s [%s]: packet too small\n", client->hostname, client_ntoa(client)); --Apple-Mail=_B5ABEE4A-F6E8-4BE7-9CF0-3958B48D3DE3--