Date: Sun, 6 May 2018 00:38:29 +0000 (UTC) From: Mark Johnston <markj@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r333283 - in head: etc/mtree include share/man/man4 sys/conf sys/net sys/netinet/netdump Message-ID: <201805060038.w460cUTZ079439@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: markj Date: Sun May 6 00:38:29 2018 New Revision: 333283 URL: https://svnweb.freebsd.org/changeset/base/333283 Log: Import the netdump client code. This is a component of a system which lets the kernel dump core to a remote host after a panic, rather than to a local storage device. The server component is available in the ports tree. netdump is particularly useful on diskless systems. The netdump(4) man page contains some details describing the protocol. Support for configuring netdump will be added to dumpon(8) in a future commit. To use netdump, the kernel must have been compiled with the NETDUMP option. The initial revision of netdump was written by Darrell Anderson and was integrated into Sandvine's OS, from which this version was derived. Reviewed by: bdrewery, cem (earlier versions), julian, sbruno MFC after: 1 month X-MFC note: use a spare field in struct ifnet Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D15253 Added: head/share/man/man4/netdump.4 (contents, props changed) head/sys/netinet/netdump/ head/sys/netinet/netdump/netdump.h (contents, props changed) head/sys/netinet/netdump/netdump_client.c (contents, props changed) Modified: head/etc/mtree/BSD.include.dist head/include/Makefile head/share/man/man4/Makefile head/sys/conf/NOTES head/sys/conf/files head/sys/conf/options head/sys/net/if.c head/sys/net/if_var.h Modified: head/etc/mtree/BSD.include.dist ============================================================================== --- head/etc/mtree/BSD.include.dist Sun May 6 00:22:38 2018 (r333282) +++ head/etc/mtree/BSD.include.dist Sun May 6 00:38:29 2018 (r333283) @@ -290,6 +290,8 @@ netinet cc .. + netdump + .. .. netinet6 .. Modified: head/include/Makefile ============================================================================== --- head/include/Makefile Sun May 6 00:22:38 2018 (r333282) +++ head/include/Makefile Sun May 6 00:38:29 2018 (r333283) @@ -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: head/share/man/man4/Makefile ============================================================================== --- head/share/man/man4/Makefile Sun May 6 00:22:38 2018 (r333282) +++ head/share/man/man4/Makefile Sun May 6 00:38:29 2018 (r333283) @@ -319,6 +319,7 @@ MAN= aac.4 \ ncv.4 \ ${_ndis.4} \ net80211.4 \ + netdump.4 \ netfpga10g_nf10bmac.4 \ netgraph.4 \ netintro.4 \ Added: head/share/man/man4/netdump.4 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/share/man/man4/netdump.4 Sun May 6 00:38:29 2018 (r333283) @@ -0,0 +1,127 @@ +.\"- +.\" Copyright (c) 2018 Mark Johnston <markj@FreeBSD.org> +.\" +.\" 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 May 4, 2018 +.Dt NETDUMP 4 +.Os +.Sh NAME +.Nm netdump +.Nd protocol for transmitting kernel dumps to a remote server +.Sh SYNOPSIS +To compile netdump client support into the kernel, place the following line in +your kernel configuration file: +.Bd -ragged -offset indent +.Cd "options NETDUMP" +.Ed +.Pp +Debug output can be enabled by adding the following line: +.Bd -ragged -offset indent +.Cd "options NETDUMP_DEBUG" +.Ed +.Sh DESCRIPTION +netdump is a UDP-based protocol for transmitting kernel dumps to a remote host. +A netdump client is a panicking kernel, and a netdump server is a host +running the +.Nm +daemon, available in ports as +.Pa ports/ftp/netdumpd . +.Nm +clients are configured using the +.Xr dumpon 8 +utility. +.Pp +.Nm +client messages consist of a fixed-size header followed by a variable-sized +payload. +The header contains the message type, a sequence number, the offset of +the payload data in the kernel dump, and the length of the payload data +(not including the header). +The message types are +.Dv HERALD , FINISHED , KDH , VMCORE , +and +.Dv EKCD_KEY . +.Nm +server messages have a fixed size and contain only the sequence number of +the client message. +These messages indicate that the server has successfully processed the +client message with the corresponding sequence number. +All client messages are acknowledged this way. +Server messages are always sent to port 20024 of the client. +.Pp +To initiate a +.Nm , +the client sends a +.Dv HERALD +message to the server at port 20023. +The client may include a relative path in its payload, in which case the +.Nm +server should attempt to save the dump at that path relative to its configured +dump directory. +The server will acknowledge the +.Dv HERALD +using a random source port, and the client must send all subsequent messages +to that port. +.Pp +The +.Dv KDH , VMCORE , +and +.Dv EKCD_KEY +message payloads contain the kernel dump header, dump contents, and +dump encryption key respectively. +The offset in the message header should be treated as a seek offset +in the corresponding file. +There are no ordering requirements for these messages. +.Pp +A +.Nm +is completed by sending the +.Dv FINISHED +message to the server. +.Pp +The following network drivers support netdump: +.Xr alc 4 , +.Xr bge 4 , +.Xr bxe 4 , +.Xr cxgb 4 , +.Xr em 4 , +.Xr igb 4 , +.Xr ix 4 , +.Xr mlx4en 4 , +.Xr re 4 , +.Xr vtnet 4 . +.Sh SEE ALSO +.Xr decryptcore 8 , +.Xr dumpon 8 , +.Xr savecore 8 +.Sh HISTORY +.Nm +client support first appeared in +.Fx 12.0 . +.Sh BUGS +Only IPv4 is supported. +.Pp +.Nm +may only be used after the kernel has panicked. Modified: head/sys/conf/NOTES ============================================================================== --- head/sys/conf/NOTES Sun May 6 00:22:38 2018 (r333282) +++ head/sys/conf/NOTES Sun May 6 00:38:29 2018 (r333283) @@ -1025,6 +1025,10 @@ options TCP_SIGNATURE #include support for RFC 2385 # a smooth scheduling of the traffic. options DUMMYNET +# The NETDUMP option enables netdump(4) client support in the kernel. +# This allows a panicking kernel to transmit a kernel dump to a remote host. +options NETDUMP + ##################################################################### # FILESYSTEM OPTIONS Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Sun May 6 00:22:38 2018 (r333282) +++ head/sys/conf/files Sun May 6 00:38:29 2018 (r333283) @@ -4372,6 +4372,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: head/sys/conf/options ============================================================================== --- head/sys/conf/options Sun May 6 00:22:38 2018 (r333282) +++ head/sys/conf/options Sun May 6 00:38:29 2018 (r333283) @@ -313,6 +313,9 @@ NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h +# Enable netdump(4) client support. +NETDUMP opt_global.h + # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h Modified: head/sys/net/if.c ============================================================================== --- head/sys/net/if.c Sun May 6 00:22:38 2018 (r333282) +++ head/sys/net/if.c Sun May 6 00:38:29 2018 (r333283) @@ -87,6 +87,7 @@ #include <netinet/ip_carp.h> #ifdef INET #include <netinet/if_ether.h> +#include <netinet/netdump/netdump.h> #endif /* INET */ #ifdef INET6 #include <netinet6/in6_var.h> @@ -2769,6 +2770,9 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, if (error == 0) { getmicrotime(&ifp->if_lastchange); rt_ifmsg(ifp); +#ifdef INET + NETDUMP_REINIT(ifp); +#endif } /* * If the link MTU changed, do network layer specific procedure. Modified: head/sys/net/if_var.h ============================================================================== --- head/sys/net/if_var.h Sun May 6 00:22:38 2018 (r333282) +++ head/sys/net/if_var.h Sun May 6 00:38:29 2018 (r333283) @@ -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? */ @@ -367,6 +368,11 @@ struct ifnet { /* Ethernet PCP */ uint8_t if_pcp; + + /* + * 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: head/sys/netinet/netdump/netdump.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/netinet/netdump/netdump.h Sun May 6 00:38:29 2018 (r333283) @@ -0,0 +1,130 @@ +/*- + * 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/disk.h> +#include <sys/ioccom.h> + +#include <net/if.h> +#include <netinet/in.h> + +#define NETDUMP_PORT 20023 /* Server UDP port for heralds. */ +#define NETDUMP_ACKPORT 20024 /* Client UDP port 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_EKCD_KEY 5 /* Contains kernel dump key. */ + +#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 { + struct diocskerneldump_arg ndc_kda; + 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; + +void netdump_reinit(struct ifnet *); + +typedef void netdump_init_t(struct ifnet *, int *nrxr, int *ncl, int *clsize); +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_REINIT(ifp) netdump_reinit(ifp) + +#define NETDUMP_SET(ifp, driver) \ + (ifp)->if_netdump_methods = &driver##_netdump_methods + +#else /* !NETDUMP */ + +#define NETDUMP_DEFINE(driver) +#define NETDUMP_REINIT(ifp) +#define NETDUMP_SET(ifp, driver) + +#endif /* NETDUMP */ +#endif /* _KERNEL */ + +#endif /* _NETINET_NETDUMP_H_ */ Added: head/sys/netinet/netdump/netdump_client.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/netinet/netdump/netdump_client.c Sun May 6 00:38:29 2018 (r333283) @@ -0,0 +1,1296 @@ +/*- + * Copyright (c) 2005-2014 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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> + +#define NETDDEBUG(f, ...) do { \ + if (nd_debug > 0) \ + printf(("%s: " f), __func__, ## __VA_ARGS__); \ +} while (0) +#define NETDDEBUG_IF(i, f, ...) do { \ + if (nd_debug > 0) \ + if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__); \ +} while (0) +#define NETDDEBUGV(f, ...) do { \ + if (nd_debug > 1) \ + printf(("%s: " f), __func__, ## __VA_ARGS__); \ +} while (0) +#define NETDDEBUGV_IF(i, f, ...) do { \ + if (nd_debug > 1) \ + if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__); \ +} while (0) + +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_handle_arp(struct mbuf **mb); +static void netdump_handle_ip(struct mbuf **mb); +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_start(struct dumperinfo *di); +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; + +FEATURE(netdump, "Netdump client support"); + +static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD, NULL, + "netdump parameters"); + +static int nd_debug; +SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN, + &nd_debug, 0, + "Debug message verbosity"); +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 error, polls, retries; + + dst = nd_server.s_addr; +restart: + for (retries = 0; retries < nd_arp_retries && have_gw_mac == 0; + retries++) { + error = netdump_send_arp(dst); + if (error != 0) + return (error); + 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 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_DISPOSABLE); + 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", + NETDUMP_MAX_IN_FLIGHT, i); + + /* + * Wait for acks. A *real* window would speed things up considerably. + */ + polls = 0; + while (rcvd_acks != want_acks) { + if (polls++ > nd_polls) { + if (retries++ > nd_retries) + return (ETIMEDOUT); + printf(". "); + goto retransmit; + } + netdump_network_poll(); + DELAY(500); + } + nd_seqno += i; + return (0); +} + +/* + * Handler for IP packets: checks their sanity and then processes any netdump + * ACK packets it finds. + * + * It needs to replicate partially the behaviour of ip_input() and + * udp_input(). + * + * Parameters: + * mb a pointer to an mbuf * containing the packet received + * Updates *mb if m_pullup et al change the pointer + * Assumes the calling function will take care of freeing the mbuf + */ +static void +netdump_handle_ip(struct mbuf **mb) +{ + struct ip *ip; + struct udpiphdr *udp; + struct netdump_ack *nd_ack; + struct mbuf *m; + int rcv_ackno; + unsigned short hlen; + + /* IP processing. */ + m = *mb; + if (m->m_pkthdr.len < sizeof(struct ip)) { + NETDDEBUG("dropping packet too small for IP header\n"); + return; + } + if (m->m_len < sizeof(struct ip)) { + m = m_pullup(m, sizeof(struct ip)); + *mb = m; + if (m == NULL) { + NETDDEBUG("m_pullup failed\n"); + return; + } + } + ip = mtod(m, struct ip *); + + /* IP version. */ + if (ip->ip_v != IPVERSION) { + NETDDEBUG("bad IP version %d\n", ip->ip_v); + return; + } + + /* Header length. */ + hlen = ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) { + NETDDEBUG("bad IP header length (%hu)\n", hlen); + return; + } + if (hlen > m->m_len) { + m = m_pullup(m, hlen); + *mb = m; + if (m == NULL) { + NETDDEBUG("m_pullup failed\n"); + return; + } + ip = mtod(m, struct ip *); + } + /* Ignore packets with IP options. */ + if (hlen > sizeof(struct ip)) { + NETDDEBUG("drop packet with IP options\n"); + return; + } + +#ifdef INVARIANTS + if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || + (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) && + (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { + NETDDEBUG("Bad IP header (RFC1122)\n"); + return; + } +#endif + + /* Checksum. */ + if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) { + if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) { + NETDDEBUG("bad IP checksum\n"); + return; + } + } else { + /* XXX */ ; + } + + /* Convert fields to host byte order. */ + ip->ip_len = ntohs(ip->ip_len); + if (ip->ip_len < hlen) { + NETDDEBUG("IP packet smaller (%hu) than header (%hu)\n", + ip->ip_len, hlen); + return; + } + if (m->m_pkthdr.len < ip->ip_len) { + NETDDEBUG("IP packet bigger (%hu) than ethernet packet (%d)\n", + ip->ip_len, m->m_pkthdr.len); + return; + } + if (m->m_pkthdr.len > ip->ip_len) { + + /* Truncate the packet to the IP length. */ + if (m->m_len == m->m_pkthdr.len) { + m->m_len = ip->ip_len; + m->m_pkthdr.len = ip->ip_len; + } else + m_adj(m, ip->ip_len - m->m_pkthdr.len); + } + + ip->ip_off = ntohs(ip->ip_off); + + /* Check that the source is the server's IP. */ + if (ip->ip_src.s_addr != nd_server.s_addr) { + NETDDEBUG("drop packet not from server (from 0x%x)\n", + ip->ip_src.s_addr); + return; + } + + /* Check if the destination IP is ours. */ + if (ip->ip_dst.s_addr != nd_client.s_addr) { + NETDDEBUGV("drop packet not to our IP\n"); + return; + } + + if (ip->ip_p != IPPROTO_UDP) { + NETDDEBUG("drop non-UDP packet\n"); + return; + } + + /* Do not deal with fragments. */ + if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) { + NETDDEBUG("drop fragmented packet\n"); + return; + } + + /* UDP custom is to have packet length not include IP header. */ + ip->ip_len -= hlen; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201805060038.w460cUTZ079439>