Date: Mon, 15 Jan 2018 00:50:08 +0000 (UTC) From: Mark Johnston <markj@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r327982 - in user/markj/netdump: etc/mtree include sys/conf sys/kern sys/net sys/netinet/netdump sys/sys Message-ID: <201801150050.w0F0o8Fe043137@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: markj Date: Mon Jan 15 00:50:08 2018 New Revision: 327982 URL: https://svnweb.freebsd.org/changeset/base/327982 Log: Add netdump client code and mbuf allocator hooks. Added: user/markj/netdump/sys/netinet/netdump/ user/markj/netdump/sys/netinet/netdump/netdump.h user/markj/netdump/sys/netinet/netdump/netdump_client.c Modified: user/markj/netdump/etc/mtree/BSD.include.dist user/markj/netdump/include/Makefile user/markj/netdump/sys/conf/NOTES user/markj/netdump/sys/conf/files user/markj/netdump/sys/conf/options user/markj/netdump/sys/kern/kern_mbuf.c user/markj/netdump/sys/kern/kern_shutdown.c user/markj/netdump/sys/net/if_var.h user/markj/netdump/sys/sys/conf.h user/markj/netdump/sys/sys/mbuf.h Modified: user/markj/netdump/etc/mtree/BSD.include.dist ============================================================================== --- user/markj/netdump/etc/mtree/BSD.include.dist Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/etc/mtree/BSD.include.dist Mon Jan 15 00:50:08 2018 (r327982) @@ -284,6 +284,8 @@ netinet cc .. + netdump + .. .. netinet6 .. Modified: user/markj/netdump/include/Makefile ============================================================================== --- user/markj/netdump/include/Makefile Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/include/Makefile Mon Jan 15 00:50:08 2018 (r327982) @@ -56,6 +56,7 @@ LSUBDIRS= cam/ata cam/mmc cam/nvme cam/scsi \ net/altq \ netgraph/atm netgraph/netflow \ netinet/cc \ + netinet/netdump \ security/audit \ security/mac_biba security/mac_bsdextended security/mac_lomac \ security/mac_mls security/mac_partition \ Modified: user/markj/netdump/sys/conf/NOTES ============================================================================== --- user/markj/netdump/sys/conf/NOTES Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/sys/conf/NOTES Mon Jan 15 00:50:08 2018 (r327982) @@ -1032,6 +1032,9 @@ options TCP_SIGNATURE #include support for RFC 2385 # a smooth scheduling of the traffic. options DUMMYNET +options NETDUMP +options NETDUMP_DEBUG + ##################################################################### # FILESYSTEM OPTIONS Modified: user/markj/netdump/sys/conf/files ============================================================================== --- user/markj/netdump/sys/conf/files Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/sys/conf/files Mon Jan 15 00:50:08 2018 (r327982) @@ -4307,6 +4307,7 @@ netinet/libalias/alias_mod.c optional libalias | netgr netinet/libalias/alias_proxy.c optional libalias inet | netgraph_nat inet netinet/libalias/alias_util.c optional libalias inet | netgraph_nat inet netinet/libalias/alias_sctp.c optional libalias inet | netgraph_nat inet +netinet/netdump/netdump_client.c optional inet netdump netinet6/dest6.c optional inet6 netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 Modified: user/markj/netdump/sys/conf/options ============================================================================== --- user/markj/netdump/sys/conf/options Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/sys/conf/options Mon Jan 15 00:50:08 2018 (r327982) @@ -310,6 +310,9 @@ NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h +NETDUMP opt_global.h +NETDUMP_DEBUG opt_netdump.h + # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h Modified: user/markj/netdump/sys/kern/kern_mbuf.c ============================================================================== --- user/markj/netdump/sys/kern/kern_mbuf.c Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/sys/kern/kern_mbuf.c Mon Jan 15 00:50:08 2018 (r327982) @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/domain.h> #include <sys/eventhandler.h> #include <sys/kernel.h> +#include <sys/limits.h> #include <sys/lock.h> #include <sys/mutex.h> #include <sys/protosw.h> @@ -379,7 +380,189 @@ mbuf_init(void *dummy) } SYSINIT(mbuf, SI_SUB_MBUF, SI_ORDER_FIRST, mbuf_init, NULL); +#ifdef NETDUMP +/* External functions invoked from the netdump code. */ +void netdump_mbuf_init(int, int); +void netdump_mbuf_drain(void); +void netdump_mbuf_dump(void); + +static struct mbufq nd_mbufq; +static struct mbufq nd_clustq; + +static uma_zone_t nd_zone_mbuf; +static uma_zone_t nd_zone_clust; +static uma_zone_t nd_zone_pack; + +static int +nd_buf_import(void *arg, void **store, int count, int domain __unused, + int flags) +{ + struct mbufq *q; + struct mbuf *m; + int i; + + q = arg; + + for (i = 0; i < count; i++) { + m = mbufq_dequeue(q); + if (m == NULL) + break; + trash_init(m, q == &nd_mbufq ? MSIZE : MCLBYTES, flags); + store[i] = m; + } + return (i); +} + +static void +nd_buf_release(void *arg, void **store, int count) +{ + struct mbufq *q; + struct mbuf *m; + int i; + + q = arg; + + for (i = 0; i < count; i++) { + m = store[i]; + (void)mbufq_enqueue(q, m); + } +} + +static int +nd_pack_import(void *arg, void **store, int count, int domain __unused, + int flags __unused) +{ + struct mbuf *m; + void *clust; + int i; + + for (i = 0; i < count; i++) { + m = m_get(MT_DATA, M_NOWAIT); + if (m == NULL) + break; + clust = uma_zalloc(nd_zone_clust, M_NOWAIT); + if (clust == NULL) { + m_free(m); + break; + } + + mb_ctor_clust(clust, MCLBYTES, m, M_NOWAIT); + store[i] = m; + } + return (i); +} + +static void +nd_pack_release(void *arg, void **store, int count) +{ + struct mbuf *m; + void *clust; + int i; + + for (i = 0; i < count; i++) { + m = store[i]; + clust = m->m_ext.ext_buf; + uma_zfree(nd_zone_clust, clust); + uma_zfree(nd_zone_mbuf, m); + } +} + /* + * Initialize zones used to cache netdump packet buffers. At panic-time, we + * swap out the regular mbuf/cluster zones with these, ensuring that drivers and + * the protocol code can allocate buffers from a preallocated pool, rather than + * relying on memory allocation to succeed after a panic. + * + * We keep mbufs and clusters in a pair of mbuf queues. In particular, for the + * purpose of caching clusters, we treat them as mbufs. + */ +void +netdump_mbuf_init(int nmbuf, int nclust) +{ + struct mbuf *m; + void *item; + + mbufq_init(&nd_mbufq, INT_MAX); + mbufq_init(&nd_clustq, INT_MAX); + + nd_zone_mbuf = uma_zcache_create("netdump_" MBUF_MEM_NAME, + MSIZE, mb_ctor_mbuf, mb_dtor_mbuf, +#ifdef INVARIANTS + trash_init, trash_fini, +#else + NULL, NULL, +#endif + nd_buf_import, nd_buf_release, + &nd_mbufq, UMA_ZONE_NOBUCKET); + + nd_zone_clust = uma_zcache_create("netdump_" MBUF_CLUSTER_MEM_NAME, + MCLBYTES, mb_ctor_clust, +#ifdef INVARIANTS + trash_dtor, trash_init, trash_fini, +#else + NULL, NULL, NULL, +#endif + nd_buf_import, nd_buf_release, + &nd_clustq, UMA_ZONE_NOBUCKET); + + nd_zone_pack = uma_zcache_create("netdump_" MBUF_PACKET_MEM_NAME, + MCLBYTES, mb_ctor_pack, mb_dtor_pack, NULL, NULL, + nd_pack_import, nd_pack_release, + NULL, UMA_ZONE_NOBUCKET); + + while (nmbuf-- > 0) { + m = m_get(MT_DATA, M_WAITOK); + uma_zfree(nd_zone_mbuf, m); + } + while (nclust-- > 0) { + item = uma_zalloc(zone_clust, M_WAITOK); + uma_zfree(nd_zone_clust, item); + } +} + +/* + * Free preallocated mbufs and clusters. + */ +void +netdump_mbuf_drain(void) +{ + struct mbuf *m; + void *item; + + while ((m = mbufq_dequeue(&nd_mbufq)) != NULL) + m_free(m); + while ((item = mbufq_dequeue(&nd_clustq)) != NULL) + uma_zfree(zone_clust, item); + + uma_zdestroy(nd_zone_mbuf); + uma_zdestroy(nd_zone_clust); + uma_zdestroy(nd_zone_pack); +} + +/* + * Callback invoked immediately prior to starting a netdump. + */ +void +netdump_mbuf_dump(void) +{ + + /* + * All cluster zones return 2KB buffers. It's up to the per-driver + * netdump hooks to ensure that no attempts are made to use larger + * clusters. netdump ACKs fit easily within an mbuf, let alone a 2KB + * cluster, so there's no need to preallocate larger buffers. + */ + printf("netdump: overwriting mbuf zone pointers\n"); + zone_mbuf = nd_zone_mbuf; + zone_clust = nd_zone_clust; + zone_pack = nd_zone_pack; + zone_jumbop = nd_zone_clust; + zone_jumbo9 = nd_zone_clust; + zone_jumbo16 = nd_zone_clust; +} +#endif /* NETDUMP */ + +/* * UMA backend page allocator for the jumbo frame zones. * * Allocates kernel virtual memory that is backed by contiguous physical @@ -681,19 +864,20 @@ mb_free_ext(struct mbuf *m) case EXT_NET_DRV: case EXT_MOD_TYPE: case EXT_DISPOSABLE: + case EXT_NETDUMP: KASSERT(mref->m_ext.ext_free != NULL, - ("%s: ext_free not set", __func__)); + ("%s: ext_free not set", __func__)); mref->m_ext.ext_free(mref); uma_zfree(zone_mbuf, mref); break; case EXT_EXTREF: KASSERT(m->m_ext.ext_free != NULL, - ("%s: ext_free not set", __func__)); + ("%s: ext_free not set", __func__)); m->m_ext.ext_free(m); break; default: KASSERT(m->m_ext.ext_type == 0, - ("%s: unknown ext_type", __func__)); + ("%s: unknown ext_type", __func__)); } } Modified: user/markj/netdump/sys/kern/kern_shutdown.c ============================================================================== --- user/markj/netdump/sys/kern/kern_shutdown.c Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/sys/kern/kern_shutdown.c Mon Jan 15 00:50:08 2018 (r327982) @@ -1101,7 +1101,7 @@ static int dump_check_bounds(struct dumperinfo *di, off_t offset, size_t length) { - if (length != 0 && (offset < di->mediaoffset || + if (di->mediasize > 0 && length != 0 && (offset < di->mediaoffset || offset - di->mediaoffset + length > di->mediasize)) { printf("Attempt to write outside dump device boundaries.\n" "offset(%jd), mediaoffset(%jd), length(%ju), mediasize(%jd).\n", @@ -1274,9 +1274,10 @@ dump_start(struct dumperinfo *di, struct kerneldumphea { uint64_t dumpextent; uint32_t keysize; + int error; #ifdef EKCD - int error = kerneldumpcrypto_init(di->kdcrypto); + error = kerneldumpcrypto_init(di->kdcrypto); if (error != 0) return (error); keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto); @@ -1284,8 +1285,15 @@ dump_start(struct dumperinfo *di, struct kerneldumphea keysize = 0; #endif + if (di->dumper_init != NULL) { + error = di->dumper_init(di->priv); + if (error != 0) + return (error); + } + dumpextent = dtoh64(kdh->dumpextent); - if (di->mediasize < SIZEOF_METADATA + dumpextent + 2 * di->blocksize + + if (di->mediasize > 0 && + di->mediasize < SIZEOF_METADATA + dumpextent + 2 * di->blocksize + keysize) { if (di->kdcomp != NULL) { /* @@ -1304,8 +1312,12 @@ dump_start(struct dumperinfo *di, struct kerneldumphea } /* The offset at which to begin writing the dump. */ - di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize - - dumpextent; + /* XXXMJ ugly */ + if (di->mediasize == 0) + di->dumpoff = di->blocksize; + else + di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize - + dumpextent; return (0); } @@ -1413,10 +1425,14 @@ dump_finish(struct dumperinfo *di, struct kerneldumphe /* * Write kerneldump headers at the beginning and end of the dump extent. * Write the key after the leading header. + * XXXMJ quite ugly */ - error = dump_write_header(di, kdh, - di->mediaoffset + di->mediasize - 2 * di->blocksize - extent - - keysize); + if (di->mediasize == 0) + error = dump_write_header(di, kdh, 0); + else + error = dump_write_header(di, kdh, + di->mediaoffset + di->mediasize - 2 * di->blocksize - extent - + keysize); if (error != 0) return (error); @@ -1427,10 +1443,15 @@ dump_finish(struct dumperinfo *di, struct kerneldumphe return (error); #endif - error = dump_write_header(di, kdh, - di->mediaoffset + di->mediasize - di->blocksize); - if (error != 0) - return (error); + /* XXX comment */ + if (di->dumper_fini != NULL) + di->dumper_fini(di->priv); + else { + error = dump_write_header(di, kdh, + di->mediaoffset + di->mediasize - di->blocksize); + if (error != 0) + return (error); + } (void)dump_write(di, NULL, 0, 0, 0); return (0); Modified: user/markj/netdump/sys/net/if_var.h ============================================================================== --- user/markj/netdump/sys/net/if_var.h Mon Jan 15 00:47:33 2018 (r327981) +++ user/markj/netdump/sys/net/if_var.h Mon Jan 15 00:50:08 2018 (r327982) @@ -70,6 +70,7 @@ struct route; /* if_output */ struct vnet; struct ifmedia; struct netmap_adapter; +struct netdump_methods; #ifdef _KERNEL #include <sys/mbuf.h> /* ifqueue only? */ @@ -364,6 +365,11 @@ struct ifnet { if_snd_tag_modify_t *if_snd_tag_modify; if_snd_tag_query_t *if_snd_tag_query; if_snd_tag_free_t *if_snd_tag_free; + + /* + * Netdump hooks to be called while dumping. + */ + struct netdump_methods *if_netdump_methods; /* * Spare fields to be added before branching a stable branch, so Added: user/markj/netdump/sys/netinet/netdump/netdump.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/markj/netdump/sys/netinet/netdump/netdump.h Mon Jan 15 00:50:08 2018 (r327982) @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2005-2014 Sandvine Incorporated + * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET_NETDUMP_H_ +#define _NETINET_NETDUMP_H_ + +#include <sys/types.h> +#include <sys/ioccom.h> + +#include <net/if.h> +#include <netinet/in.h> + +#define NETDUMP_PORT 20023 /* Server udp port number for data. */ +#define NETDUMP_ACKPORT 20024 /* Client udp port number for acks. */ + +#define NETDUMP_HERALD 1 /* Broadcast before starting a dump. */ +#define NETDUMP_FINISHED 2 /* Send after finishing a dump. */ +#define NETDUMP_VMCORE 3 /* Contains dump data. */ +#define NETDUMP_KDH 4 /* Contains kernel dump header. */ + +#define NETDUMP_DATASIZE 4096 /* Arbitrary packet size limit. */ + +struct netdump_msg_hdr { + uint32_t mh_type; /* Netdump message type. */ + 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; +} __packed; + +struct netdump_ack { + uint32_t na_seqno; /* Match acks with msgs. */ +} __packed; + +struct netdump_conf { + char ndc_iface[IFNAMSIZ]; + struct in_addr ndc_server; + struct in_addr ndc_client; + struct in_addr ndc_gateway; +}; + +#define _PATH_NETDUMP "/dev/netdump" + +#define NETDUMPGCONF _IOR('n', 1, struct netdump_conf) +#define NETDUMPSCONF _IOW('n', 2, struct netdump_conf) + +#ifdef _KERNEL +#ifdef NETDUMP + +#define NETDUMP_MAX_IN_FLIGHT 64 + +enum netdump_ev { + NETDUMP_START, + NETDUMP_END, +}; + +struct ifnet; +struct mbuf; + +typedef void netdump_init_t(struct ifnet *, int *nmbufp, int *nclustp); +typedef void netdump_event_t(struct ifnet *, enum netdump_ev); +typedef int netdump_transmit_t(struct ifnet *, struct mbuf *); +typedef int netdump_poll_t(struct ifnet *, int); + +struct netdump_methods { + netdump_init_t *nd_init; + netdump_event_t *nd_event; + netdump_transmit_t *nd_transmit; + netdump_poll_t *nd_poll; +}; + +#define NETDUMP_DEFINE(driver) \ + static netdump_init_t driver##_netdump_init; \ + static netdump_event_t driver##_netdump_event; \ + static netdump_transmit_t driver##_netdump_transmit; \ + static netdump_poll_t driver##_netdump_poll; \ + \ + static struct netdump_methods driver##_netdump_methods = { \ + .nd_init = driver##_netdump_init, \ + .nd_event = driver##_netdump_event, \ + .nd_transmit = driver##_netdump_transmit, \ + .nd_poll = driver##_netdump_poll, \ + } + +#define NETDUMP_SET(ifp, driver) \ + (ifp)->if_netdump_methods = &driver##_netdump_methods + +#else /* !NETDUMP */ + +#define NETDUMP_DEFINE(driver) +#define NETDUMP_SET(ifp, driver) + +#endif /* NETDUMP */ +#endif /* _KERNEL */ + +#endif /* _NETINET_NETDUMP_H_ */ Added: user/markj/netdump/sys/netinet/netdump/netdump_client.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/markj/netdump/sys/netinet/netdump/netdump_client.c Mon Jan 15 00:50:08 2018 (r327982) @@ -0,0 +1,1244 @@ +/*- + * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved. + * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * netdump_client.c + * FreeBSD subsystem supporting netdump network dumps. + * A dedicated server must be running to accept client dumps. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_netdump.h" + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/disk.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <sys/kerneldump.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/priv.h> +#include <sys/proc.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/ip_options.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/netdump/netdump.h> + +#include <machine/in_cksum.h> +#include <machine/pcb.h> + +#ifdef NETDUMP_DEBUG +#define NETDDEBUG(f, ...) \ + printf(("%s: " f), __func__, ## __VA_ARGS__) +#define NETDDEBUG_IF(i, f, ...) \ + if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__) +#if NETDUMP_DEBUG > 1 +#define NETDDEBUGV(f, ...) \ + printf(("%s: " f), __func__, ## __VA_ARGS__) +#define NETDDEBUGV_IF(i, f, ...) \ + if_printf((i), ("%s: " f), __func__, ## __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 + +/* Defined in kern_mbuf.c. */ +void netdump_mbuf_init(int nmbuf, int nclust); +void netdump_mbuf_drain(void); +void netdump_mbuf_dump(void); + +static int netdump_arp_gw(void); +static void netdump_cleanup(void); +static int netdump_configure(struct netdump_conf *); +static int netdump_dumper(void *priv __unused, void *virtual, + vm_offset_t physical __unused, off_t offset, size_t length); +static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp, + struct ether_addr dst, u_short etype); +static void netdump_fini(void *priv __unused); +static void netdump_handle_arp(struct mbuf **mb); +static void netdump_handle_ip(struct mbuf **mb); +static int netdump_init(void *priv __unused); +static int netdump_ioctl(struct cdev *dev __unused, u_long cmd, + caddr_t addr, int flags __unused, struct thread *td); +static int netdump_modevent(module_t mod, int type, void *priv); +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(in_addr_t dst); +static int netdump_udp_output(struct mbuf *m); + +/* Must be at least as big as the chunks dumpsys() gives us. */ +static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE]; +static uint32_t nd_seqno; +static int dump_failed, have_gw_mac; +static void (*drv_if_input)(struct ifnet *, struct mbuf *); +static int restore_gw_addr; + +static uint64_t rcvd_acks; +CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT); + +/* + * Times to poll the NIC (0.5ms each poll) before assuming packetloss + * occurred (default to 1s). + */ +static int nd_polls = 2000; + +/* Times to retransmit lost packets. */ +static int nd_retries = 10; + +/* Number of ARP retries. */ +static int nd_arp_retries = 3; + +/* Configuration parameters. */ +static struct netdump_conf nd_conf; +#define nd_server nd_conf.ndc_server +#define nd_client nd_conf.ndc_client +#define nd_gateway nd_conf.ndc_gateway + +/* General dynamic settings. */ +static struct ether_addr nd_gw_mac; +static struct ifnet *nd_ifp; +static uint16_t nd_server_port = NETDUMP_PORT; + +static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD, NULL, + "netdump parameters"); + +static int nd_enabled; +SYSCTL_INT(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD, + &nd_enabled, 0, + "netdump configuration status"); +static char nd_path[MAXPATHLEN]; +SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW, + nd_path, sizeof(nd_path), + "Server path for output files"); + +/* + * 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 bool +netdump_supported_nic(struct ifnet *ifp) +{ + + return (ifp->if_netdump_methods != NULL); +} + +/*- + * Network specific primitives. + * Following down the code they are divided ordered as: + * - Packet buffer primitives + * - Output primitives + * - Input primitives + * - Polling primitives + */ + +/* + * 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; + + if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) || + (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) { + if_printf(ifp, "netdump_ether_output: interface isn't up\n"); + m_freem(m); + return (ENETDOWN); + } + + /* Fill in the ethernet header. */ + M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); + if (m == NULL) { + printf("%s: out of mbufs\n", __func__); + return (ENOBUFS); + } + eh = mtod(m, struct ether_header *); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); + eh->ether_type = htons(etype); + return ((ifp->if_netdump_methods->nd_transmit)(ifp, m)); +} + +/* + * Unreliable transmission of an mbuf chain to the netdump server + * Note: can't handle fragmentation; fails if the packet is larger than + * nd_ifp->if_mtu after adding the UDP/IP headers + * + * Parameters: + * m mbuf chain + * + * Returns: + * int see errno.h, 0 for success + */ +static int +netdump_udp_output(struct mbuf *m) +{ + struct udpiphdr *ui; + struct ip *ip; + + MPASS(nd_ifp != NULL); + + M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT); + if (m == NULL) { + printf("%s: out of mbufs\n", __func__); + return (ENOBUFS); + } + + if (m->m_pkthdr.len > nd_ifp->if_mtu) { + printf("netdump_udp_output: Packet is too big: %d > MTU %u\n", + m->m_pkthdr.len, nd_ifp->if_mtu); + m_freem(m); + return (ENOBUFS); + } + + ui = mtod(m, struct udpiphdr *); + bzero(ui->ui_x1, sizeof(ui->ui_x1)); + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip)); + ui->ui_ulen = ui->ui_len; + ui->ui_src = nd_client; + ui->ui_dst = nd_server; + /* Use this src port so that the server can connect() the socket */ + ui->ui_sport = htons(NETDUMP_ACKPORT); + ui->ui_dport = htons(nd_server_port); + ui->ui_sum = 0; + if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0) + ui->ui_sum = 0xffff; + + ip = mtod(m, struct ip *); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_tos = 0; + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_id = 0; + ip->ip_off = htons(IP_DF); + ip->ip_ttl = 255; + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, sizeof(struct ip)); + + return (netdump_ether_output(m, nd_ifp, nd_gw_mac, ETHERTYPE_IP)); +} + +/* + * Builds and sends a single ARP request to locate the server + * + * Return value: + * 0 on success + * errno on error + */ +static int +netdump_send_arp(in_addr_t dst) +{ + struct ether_addr bcast; + struct mbuf *m; + struct arphdr *ah; + int pktlen; + + MPASS(nd_ifp != NULL); + + /* Fill-up a broadcast address. */ + memset(&bcast, 0xFF, ETHER_ADDR_LEN); + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + printf("netdump_send_arp: Out of mbufs\n"); + return (ENOBUFS); + } + pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); + m->m_len = pktlen; + m->m_pkthdr.len = pktlen; + MH_ALIGN(m, pktlen); + ah = mtod(m, struct arphdr *); + ah->ar_hrd = htons(ARPHRD_ETHER); + ah->ar_pro = htons(ETHERTYPE_IP); + ah->ar_hln = ETHER_ADDR_LEN; + ah->ar_pln = sizeof(struct in_addr); + ah->ar_op = htons(ARPOP_REQUEST); + memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN); + ((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr; + bzero(ar_tha(ah), ETHER_ADDR_LEN); + ((struct in_addr *)ar_tpa(ah))->s_addr = dst; + return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP)); +} + +/* + * Sends ARP requests to locate the server and waits for a response. + * We first try to ARP the server itself, and fall back to the provided + * gateway if the server appears to be off-link. + * + * Return value: + * 0 on success + * errno on error + */ +static int +netdump_arp_gw(void) +{ + in_addr_t dst; + int err, polls, retries; + + dst = nd_server.s_addr; +restart: + for (retries = 0; retries < nd_arp_retries && have_gw_mac == 0; + retries++) { + err = netdump_send_arp(dst); + if (err != 0) + return (err); + for (polls = 0; polls < nd_polls && have_gw_mac == 0; polls++) { + netdump_network_poll(); + DELAY(500); + } + if (have_gw_mac == 0) + printf("(ARP retry)"); + } + if (have_gw_mac != 0) + return (0); + if (dst == nd_server.s_addr && nd_server.s_addr != nd_gateway.s_addr) { + printf("Failed to ARP server, trying to reach gateway...\n"); + dst = nd_gateway.s_addr; + goto restart; + } + + printf("\nARP timed out.\n"); + return (ETIMEDOUT); +} + +/* + * Dummy free function for EXT_NETDUMP clusters. + */ +static void +netdump_mbuf_free(struct mbuf *m __unused) +{ +} + +/* + * 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) +{ + struct netdump_msg_hdr *nd_msg_hdr; + struct mbuf *m, *m2; + uint64_t want_acks; + uint32_t i, pktlen, sent_so_far; + int retries, polls, error; + + want_acks = 0; + rcvd_acks = 0; + retries = 0; + + MPASS(nd_ifp != NULL); + +retransmit: + /* Chunks can be too big to fit in packets. */ + for (i = sent_so_far = 0; sent_so_far < datalen || + (i == 0 && datalen == 0); i++) { + pktlen = datalen - sent_so_far; + + /* First bound: the packet structure. */ + pktlen = min(pktlen, NETDUMP_DATASIZE); + + /* Second bound: the interface MTU (assume no IP options). */ + pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) - + sizeof(struct netdump_msg_hdr)); + + /* + * Check if it is retransmitting and this has been ACKed + * already. + */ + if ((rcvd_acks & (1 << i)) != 0) { + sent_so_far += pktlen; + continue; + } + + /* + * Get and fill a header mbuf, then chain data as an extended + * mbuf. + */ + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + printf("netdump_send: Out of mbufs\n"); + return (ENOBUFS); + } + m->m_len = sizeof(struct netdump_msg_hdr); + m->m_pkthdr.len = sizeof(struct netdump_msg_hdr); + MH_ALIGN(m, sizeof(struct netdump_msg_hdr)); + nd_msg_hdr = mtod(m, struct netdump_msg_hdr *); + nd_msg_hdr->mh_seqno = htonl(nd_seqno + i); + nd_msg_hdr->mh_type = htonl(type); + nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far); + nd_msg_hdr->mh_len = htonl(pktlen); + nd_msg_hdr->mh__pad = 0; + + if (pktlen != 0) { + m2 = m_get(M_NOWAIT, MT_DATA); + if (m2 == NULL) { + m_freem(m); + printf("netdump_send: Out of mbufs\n"); + return (ENOBUFS); + } + MEXTADD(m2, data + sent_so_far, pktlen, + netdump_mbuf_free, NULL, NULL, 0, EXT_NETDUMP); + m2->m_len = pktlen; + + m_cat(m, m2); + m->m_pkthdr.len += pktlen; + } + error = netdump_udp_output(m); + if (error != 0) + return (error); + + /* Note that we're waiting for this packet in the bitfield. */ + want_acks |= (1 << i); + sent_so_far += pktlen; + } + if (i >= NETDUMP_MAX_IN_FLIGHT) + printf("Warning: Sent more than %d packets (%d). " + "Acknowledgements will fail unless the size of " + "rcvd_acks/want_acks is increased.\n", *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201801150050.w0F0o8Fe043137>