xref: /freebsd/sys/netinet/netdump/netdump_client.c (revision 8726929d67052e89f7849bdb4445f859e98038c8)
1e5054602SMark Johnston /*-
2e5054602SMark Johnston  * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved.
3e5054602SMark Johnston  * Copyright (c) 2000 Darrell Anderson
4e5054602SMark Johnston  * All rights reserved.
5e5054602SMark Johnston  *
6e5054602SMark Johnston  * Redistribution and use in source and binary forms, with or without
7e5054602SMark Johnston  * modification, are permitted provided that the following conditions
8e5054602SMark Johnston  * are met:
9e5054602SMark Johnston  * 1. Redistributions of source code must retain the above copyright
10e5054602SMark Johnston  *    notice, this list of conditions and the following disclaimer.
11e5054602SMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
12e5054602SMark Johnston  *    notice, this list of conditions and the following disclaimer in the
13e5054602SMark Johnston  *    documentation and/or other materials provided with the distribution.
14e5054602SMark Johnston  *
15e5054602SMark Johnston  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e5054602SMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e5054602SMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e5054602SMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19e5054602SMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e5054602SMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e5054602SMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e5054602SMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e5054602SMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e5054602SMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e5054602SMark Johnston  * SUCH DAMAGE.
26e5054602SMark Johnston  */
27e5054602SMark Johnston 
28e5054602SMark Johnston /*
29e5054602SMark Johnston  * netdump_client.c
30e5054602SMark Johnston  * FreeBSD subsystem supporting netdump network dumps.
31e5054602SMark Johnston  * A dedicated server must be running to accept client dumps.
32e5054602SMark Johnston  */
33e5054602SMark Johnston 
34e5054602SMark Johnston #include <sys/cdefs.h>
35e5054602SMark Johnston __FBSDID("$FreeBSD$");
36e5054602SMark Johnston 
37e5054602SMark Johnston #include <sys/param.h>
38e5054602SMark Johnston #include <sys/conf.h>
39e5054602SMark Johnston #include <sys/disk.h>
40e5054602SMark Johnston #include <sys/endian.h>
4104e0c883SConrad Meyer #include <sys/eventhandler.h>
42b35822d9SMark Johnston #include <sys/jail.h>
43e5054602SMark Johnston #include <sys/kernel.h>
44e5054602SMark Johnston #include <sys/kerneldump.h>
45e5054602SMark Johnston #include <sys/mbuf.h>
46e5054602SMark Johnston #include <sys/module.h>
47e5054602SMark Johnston #include <sys/priv.h>
48e5054602SMark Johnston #include <sys/proc.h>
49e5054602SMark Johnston #include <sys/protosw.h>
50e5054602SMark Johnston #include <sys/socket.h>
51e5054602SMark Johnston #include <sys/sysctl.h>
52e5054602SMark Johnston #include <sys/systm.h>
53e5054602SMark Johnston 
54e5054602SMark Johnston #include <net/ethernet.h>
55e5054602SMark Johnston #include <net/if.h>
56e5054602SMark Johnston #include <net/if_arp.h>
57e5054602SMark Johnston #include <net/if_dl.h>
58e5054602SMark Johnston #include <net/if_types.h>
59e5054602SMark Johnston #include <net/if_var.h>
60e5054602SMark Johnston 
61e5054602SMark Johnston #include <netinet/in.h>
62e5054602SMark Johnston #include <netinet/in_systm.h>
63e5054602SMark Johnston #include <netinet/in_var.h>
64e5054602SMark Johnston #include <netinet/ip.h>
65e5054602SMark Johnston #include <netinet/ip_var.h>
66e5054602SMark Johnston #include <netinet/ip_options.h>
67e5054602SMark Johnston #include <netinet/udp.h>
68e5054602SMark Johnston #include <netinet/udp_var.h>
69e5054602SMark Johnston #include <netinet/netdump/netdump.h>
70e5054602SMark Johnston 
71e5054602SMark Johnston #include <machine/in_cksum.h>
72e5054602SMark Johnston #include <machine/pcb.h>
73e5054602SMark Johnston 
74e5054602SMark Johnston #define	NETDDEBUG(f, ...) do {						\
75e5054602SMark Johnston 	if (nd_debug > 0)						\
76e5054602SMark Johnston 		printf(("%s: " f), __func__, ## __VA_ARGS__);		\
77e5054602SMark Johnston } while (0)
78e5054602SMark Johnston #define	NETDDEBUG_IF(i, f, ...) do {					\
79e5054602SMark Johnston 	if (nd_debug > 0)						\
80e5054602SMark Johnston 		if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__);	\
81e5054602SMark Johnston } while (0)
82e5054602SMark Johnston #define	NETDDEBUGV(f, ...) do {						\
83e5054602SMark Johnston 	if (nd_debug > 1)						\
84e5054602SMark Johnston 		printf(("%s: " f), __func__, ## __VA_ARGS__);		\
85e5054602SMark Johnston } while (0)
86e5054602SMark Johnston #define	NETDDEBUGV_IF(i, f, ...) do {					\
87e5054602SMark Johnston 	if (nd_debug > 1)						\
88e5054602SMark Johnston 		if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__);	\
89e5054602SMark Johnston } while (0)
90e5054602SMark Johnston 
91e5054602SMark Johnston static int	 netdump_arp_gw(void);
92e5054602SMark Johnston static void	 netdump_cleanup(void);
936b6e2954SConrad Meyer static int	 netdump_configure(struct diocskerneldump_arg *,
946b6e2954SConrad Meyer 		    struct thread *);
95e5054602SMark Johnston static int	 netdump_dumper(void *priv __unused, void *virtual,
96e5054602SMark Johnston 		    vm_offset_t physical __unused, off_t offset, size_t length);
9764e7d18fSConrad Meyer static bool	 netdump_enabled(void);
9864e7d18fSConrad Meyer static int	 netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS);
99e5054602SMark Johnston static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp,
100e5054602SMark Johnston 		    struct ether_addr dst, u_short etype);
101e5054602SMark Johnston static void	 netdump_handle_arp(struct mbuf **mb);
102e5054602SMark Johnston static void	 netdump_handle_ip(struct mbuf **mb);
103e5054602SMark Johnston static int	 netdump_ioctl(struct cdev *dev __unused, u_long cmd,
104e5054602SMark Johnston 		    caddr_t addr, int flags __unused, struct thread *td);
105e5054602SMark Johnston static int	 netdump_modevent(module_t mod, int type, void *priv);
106e5054602SMark Johnston static void	 netdump_network_poll(void);
107e5054602SMark Johnston static void	 netdump_pkt_in(struct ifnet *ifp, struct mbuf *m);
10864e7d18fSConrad Meyer static void	 netdump_reinit_internal(struct ifnet *ifp);
109e5054602SMark Johnston static int	 netdump_send(uint32_t type, off_t offset, unsigned char *data,
110e5054602SMark Johnston 		    uint32_t datalen);
111e5054602SMark Johnston static int	 netdump_send_arp(in_addr_t dst);
112e5054602SMark Johnston static int	 netdump_start(struct dumperinfo *di);
113e5054602SMark Johnston static int	 netdump_udp_output(struct mbuf *m);
11464e7d18fSConrad Meyer static void	 netdump_unconfigure(void);
115e5054602SMark Johnston 
116e5054602SMark Johnston /* Must be at least as big as the chunks dumpsys() gives us. */
117e5054602SMark Johnston static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
118e5054602SMark Johnston static uint32_t nd_seqno;
119e5054602SMark Johnston static int dump_failed, have_gw_mac;
120e5054602SMark Johnston static void (*drv_if_input)(struct ifnet *, struct mbuf *);
121e5054602SMark Johnston static int restore_gw_addr;
122e5054602SMark Johnston 
123e5054602SMark Johnston static uint64_t rcvd_acks;
124e5054602SMark Johnston CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT);
125e5054602SMark Johnston 
126e5054602SMark Johnston /* Configuration parameters. */
1276144b50fSConrad Meyer static struct {
1286144b50fSConrad Meyer 	char		 ndc_iface[IFNAMSIZ];
1296144b50fSConrad Meyer 	union kd_ip	 ndc_server;
1306144b50fSConrad Meyer 	union kd_ip	 ndc_client;
1316144b50fSConrad Meyer 	union kd_ip	 ndc_gateway;
1326144b50fSConrad Meyer 	uint8_t		 ndc_af;
133*8726929dSMark Johnston 	/* Runtime State */
134*8726929dSMark Johnston 	off_t		 nd_tx_off;
135*8726929dSMark Johnston 	size_t		 nd_buf_len;
1366144b50fSConrad Meyer } nd_conf;
1376144b50fSConrad Meyer #define	nd_server	nd_conf.ndc_server.in4
1386144b50fSConrad Meyer #define	nd_client	nd_conf.ndc_client.in4
1396144b50fSConrad Meyer #define	nd_gateway	nd_conf.ndc_gateway.in4
140e5054602SMark Johnston 
141e5054602SMark Johnston /* General dynamic settings. */
14264e7d18fSConrad Meyer static struct sx nd_conf_lk;
14364e7d18fSConrad Meyer SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock");
14464e7d18fSConrad Meyer #define NETDUMP_WLOCK()			sx_xlock(&nd_conf_lk)
14564e7d18fSConrad Meyer #define NETDUMP_WUNLOCK()		sx_xunlock(&nd_conf_lk)
14664e7d18fSConrad Meyer #define NETDUMP_RLOCK()			sx_slock(&nd_conf_lk)
14764e7d18fSConrad Meyer #define NETDUMP_RUNLOCK()		sx_sunlock(&nd_conf_lk)
14864e7d18fSConrad Meyer #define NETDUMP_ASSERT_WLOCKED()	sx_assert(&nd_conf_lk, SA_XLOCKED)
14964e7d18fSConrad Meyer #define NETDUMP_ASSERT_LOCKED()		sx_assert(&nd_conf_lk, SA_LOCKED)
150e5054602SMark Johnston static struct ether_addr nd_gw_mac;
151e5054602SMark Johnston static struct ifnet *nd_ifp;
15264e7d18fSConrad Meyer static eventhandler_tag nd_detach_cookie;
153e5054602SMark Johnston static uint16_t nd_server_port = NETDUMP_PORT;
154e5054602SMark Johnston 
155e5054602SMark Johnston FEATURE(netdump, "Netdump client support");
156e5054602SMark Johnston 
157e5054602SMark Johnston static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD, NULL,
158e5054602SMark Johnston     "netdump parameters");
159e5054602SMark Johnston 
160e5054602SMark Johnston static int nd_debug;
161e5054602SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
162e5054602SMark Johnston     &nd_debug, 0,
163e5054602SMark Johnston     "Debug message verbosity");
16464e7d18fSConrad Meyer SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT,
16564e7d18fSConrad Meyer     &nd_ifp, 0, netdump_enabled_sysctl, "I", "netdump configuration status");
166e5054602SMark Johnston static char nd_path[MAXPATHLEN];
167e5054602SMark Johnston SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW,
168e5054602SMark Johnston     nd_path, sizeof(nd_path),
169e5054602SMark Johnston     "Server path for output files");
170da7d7778SMark Johnston static int nd_polls = 2000;
171da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN,
172da7d7778SMark Johnston     &nd_polls, 0,
173da7d7778SMark Johnston     "Number of times to poll before assuming packet loss (0.5ms per poll)");
174da7d7778SMark Johnston static int nd_retries = 10;
175da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN,
176da7d7778SMark Johnston     &nd_retries, 0,
177da7d7778SMark Johnston     "Number of retransmit attempts before giving up");
178da7d7778SMark Johnston static int nd_arp_retries = 3;
179da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN,
180da7d7778SMark Johnston     &nd_arp_retries, 0,
181da7d7778SMark Johnston     "Number of ARP attempts before giving up");
182e5054602SMark Johnston 
18364e7d18fSConrad Meyer static bool
18464e7d18fSConrad Meyer netdump_enabled(void)
18564e7d18fSConrad Meyer {
18664e7d18fSConrad Meyer 
18764e7d18fSConrad Meyer 	NETDUMP_ASSERT_LOCKED();
18864e7d18fSConrad Meyer 	return (nd_ifp != NULL);
18964e7d18fSConrad Meyer }
19064e7d18fSConrad Meyer 
19164e7d18fSConrad Meyer static int
19264e7d18fSConrad Meyer netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)
19364e7d18fSConrad Meyer {
19464e7d18fSConrad Meyer 	int en, error;
19564e7d18fSConrad Meyer 
19664e7d18fSConrad Meyer 	NETDUMP_RLOCK();
19764e7d18fSConrad Meyer 	en = netdump_enabled();
19864e7d18fSConrad Meyer 	NETDUMP_RUNLOCK();
19964e7d18fSConrad Meyer 
20064e7d18fSConrad Meyer 	error = SYSCTL_OUT(req, &en, sizeof(en));
20164e7d18fSConrad Meyer 	if (error != 0 || req->newptr == NULL)
20264e7d18fSConrad Meyer 		return (error);
20364e7d18fSConrad Meyer 	return (EPERM);
20464e7d18fSConrad Meyer }
20564e7d18fSConrad Meyer 
206e5054602SMark Johnston /*
207e5054602SMark Johnston  * Checks for netdump support on a network interface
208e5054602SMark Johnston  *
209e5054602SMark Johnston  * Parameters:
210e5054602SMark Johnston  *	ifp	The network interface that is being tested for support
211e5054602SMark Johnston  *
212e5054602SMark Johnston  * Returns:
213e5054602SMark Johnston  *	int	1 if the interface is supported, 0 if not
214e5054602SMark Johnston  */
215e5054602SMark Johnston static bool
216e5054602SMark Johnston netdump_supported_nic(struct ifnet *ifp)
217e5054602SMark Johnston {
218e5054602SMark Johnston 
219e5054602SMark Johnston 	return (ifp->if_netdump_methods != NULL);
220e5054602SMark Johnston }
221e5054602SMark Johnston 
222e5054602SMark Johnston /*-
223e5054602SMark Johnston  * Network specific primitives.
224e5054602SMark Johnston  * Following down the code they are divided ordered as:
225e5054602SMark Johnston  * - Packet buffer primitives
226e5054602SMark Johnston  * - Output primitives
227e5054602SMark Johnston  * - Input primitives
228e5054602SMark Johnston  * - Polling primitives
229e5054602SMark Johnston  */
230e5054602SMark Johnston 
231e5054602SMark Johnston /*
232e5054602SMark Johnston  * Handles creation of the ethernet header, then places outgoing packets into
233e5054602SMark Johnston  * the tx buffer for the NIC
234e5054602SMark Johnston  *
235e5054602SMark Johnston  * Parameters:
236e5054602SMark Johnston  *	m	The mbuf containing the packet to be sent (will be freed by
237e5054602SMark Johnston  *		this function or the NIC driver)
238e5054602SMark Johnston  *	ifp	The interface to send on
239e5054602SMark Johnston  *	dst	The destination ethernet address (source address will be looked
240e5054602SMark Johnston  *		up using ifp)
241e5054602SMark Johnston  *	etype	The ETHERTYPE_* value for the protocol that is being sent
242e5054602SMark Johnston  *
243e5054602SMark Johnston  * Returns:
244e5054602SMark Johnston  *	int	see errno.h, 0 for success
245e5054602SMark Johnston  */
246e5054602SMark Johnston static int
247e5054602SMark Johnston netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst,
248e5054602SMark Johnston     u_short etype)
249e5054602SMark Johnston {
250e5054602SMark Johnston 	struct ether_header *eh;
251e5054602SMark Johnston 
252e5054602SMark Johnston 	if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) ||
253e5054602SMark Johnston 	    (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) {
254e5054602SMark Johnston 		if_printf(ifp, "netdump_ether_output: interface isn't up\n");
255e5054602SMark Johnston 		m_freem(m);
256e5054602SMark Johnston 		return (ENETDOWN);
257e5054602SMark Johnston 	}
258e5054602SMark Johnston 
259e5054602SMark Johnston 	/* Fill in the ethernet header. */
260e5054602SMark Johnston 	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
261e5054602SMark Johnston 	if (m == NULL) {
262e5054602SMark Johnston 		printf("%s: out of mbufs\n", __func__);
263e5054602SMark Johnston 		return (ENOBUFS);
264e5054602SMark Johnston 	}
265e5054602SMark Johnston 	eh = mtod(m, struct ether_header *);
266e5054602SMark Johnston 	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
267e5054602SMark Johnston 	memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN);
268e5054602SMark Johnston 	eh->ether_type = htons(etype);
269e5054602SMark Johnston 	return ((ifp->if_netdump_methods->nd_transmit)(ifp, m));
270e5054602SMark Johnston }
271e5054602SMark Johnston 
272e5054602SMark Johnston /*
273e5054602SMark Johnston  * Unreliable transmission of an mbuf chain to the netdump server
274e5054602SMark Johnston  * Note: can't handle fragmentation; fails if the packet is larger than
275e5054602SMark Johnston  *	 nd_ifp->if_mtu after adding the UDP/IP headers
276e5054602SMark Johnston  *
277e5054602SMark Johnston  * Parameters:
278e5054602SMark Johnston  *	m	mbuf chain
279e5054602SMark Johnston  *
280e5054602SMark Johnston  * Returns:
281e5054602SMark Johnston  *	int	see errno.h, 0 for success
282e5054602SMark Johnston  */
283e5054602SMark Johnston static int
284e5054602SMark Johnston netdump_udp_output(struct mbuf *m)
285e5054602SMark Johnston {
286e5054602SMark Johnston 	struct udpiphdr *ui;
287e5054602SMark Johnston 	struct ip *ip;
288e5054602SMark Johnston 
28964e7d18fSConrad Meyer 	MPASS(netdump_enabled());
290e5054602SMark Johnston 
291e5054602SMark Johnston 	M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
292e5054602SMark Johnston 	if (m == NULL) {
293e5054602SMark Johnston 		printf("%s: out of mbufs\n", __func__);
294e5054602SMark Johnston 		return (ENOBUFS);
295e5054602SMark Johnston 	}
296e5054602SMark Johnston 
297e5054602SMark Johnston 	if (m->m_pkthdr.len > nd_ifp->if_mtu) {
298e5054602SMark Johnston 		printf("netdump_udp_output: Packet is too big: %d > MTU %u\n",
299e5054602SMark Johnston 		    m->m_pkthdr.len, nd_ifp->if_mtu);
300e5054602SMark Johnston 		m_freem(m);
301e5054602SMark Johnston 		return (ENOBUFS);
302e5054602SMark Johnston 	}
303e5054602SMark Johnston 
304e5054602SMark Johnston 	ui = mtod(m, struct udpiphdr *);
305e5054602SMark Johnston 	bzero(ui->ui_x1, sizeof(ui->ui_x1));
306e5054602SMark Johnston 	ui->ui_pr = IPPROTO_UDP;
307e5054602SMark Johnston 	ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip));
308e5054602SMark Johnston 	ui->ui_ulen = ui->ui_len;
309e5054602SMark Johnston 	ui->ui_src = nd_client;
310e5054602SMark Johnston 	ui->ui_dst = nd_server;
311e5054602SMark Johnston 	/* Use this src port so that the server can connect() the socket */
312e5054602SMark Johnston 	ui->ui_sport = htons(NETDUMP_ACKPORT);
313e5054602SMark Johnston 	ui->ui_dport = htons(nd_server_port);
314e5054602SMark Johnston 	ui->ui_sum = 0;
315e5054602SMark Johnston 	if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0)
316e5054602SMark Johnston 		ui->ui_sum = 0xffff;
317e5054602SMark Johnston 
318e5054602SMark Johnston 	ip = mtod(m, struct ip *);
319e5054602SMark Johnston 	ip->ip_v = IPVERSION;
320e5054602SMark Johnston 	ip->ip_hl = sizeof(struct ip) >> 2;
321e5054602SMark Johnston 	ip->ip_tos = 0;
322e5054602SMark Johnston 	ip->ip_len = htons(m->m_pkthdr.len);
323e5054602SMark Johnston 	ip->ip_id = 0;
324e5054602SMark Johnston 	ip->ip_off = htons(IP_DF);
325e5054602SMark Johnston 	ip->ip_ttl = 255;
326e5054602SMark Johnston 	ip->ip_sum = 0;
327e5054602SMark Johnston 	ip->ip_sum = in_cksum(m, sizeof(struct ip));
328e5054602SMark Johnston 
329e5054602SMark Johnston 	return (netdump_ether_output(m, nd_ifp, nd_gw_mac, ETHERTYPE_IP));
330e5054602SMark Johnston }
331e5054602SMark Johnston 
332e5054602SMark Johnston /*
333e5054602SMark Johnston  * Builds and sends a single ARP request to locate the server
334e5054602SMark Johnston  *
335e5054602SMark Johnston  * Return value:
336e5054602SMark Johnston  *	0 on success
337e5054602SMark Johnston  *	errno on error
338e5054602SMark Johnston  */
339e5054602SMark Johnston static int
340e5054602SMark Johnston netdump_send_arp(in_addr_t dst)
341e5054602SMark Johnston {
342e5054602SMark Johnston 	struct ether_addr bcast;
343e5054602SMark Johnston 	struct mbuf *m;
344e5054602SMark Johnston 	struct arphdr *ah;
345e5054602SMark Johnston 	int pktlen;
346e5054602SMark Johnston 
34764e7d18fSConrad Meyer 	MPASS(netdump_enabled());
348e5054602SMark Johnston 
349e5054602SMark Johnston 	/* Fill-up a broadcast address. */
350e5054602SMark Johnston 	memset(&bcast, 0xFF, ETHER_ADDR_LEN);
351e5054602SMark Johnston 	m = m_gethdr(M_NOWAIT, MT_DATA);
352e5054602SMark Johnston 	if (m == NULL) {
353e5054602SMark Johnston 		printf("netdump_send_arp: Out of mbufs\n");
354e5054602SMark Johnston 		return (ENOBUFS);
355e5054602SMark Johnston 	}
356e5054602SMark Johnston 	pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
357e5054602SMark Johnston 	m->m_len = pktlen;
358e5054602SMark Johnston 	m->m_pkthdr.len = pktlen;
359e5054602SMark Johnston 	MH_ALIGN(m, pktlen);
360e5054602SMark Johnston 	ah = mtod(m, struct arphdr *);
361e5054602SMark Johnston 	ah->ar_hrd = htons(ARPHRD_ETHER);
362e5054602SMark Johnston 	ah->ar_pro = htons(ETHERTYPE_IP);
363e5054602SMark Johnston 	ah->ar_hln = ETHER_ADDR_LEN;
364e5054602SMark Johnston 	ah->ar_pln = sizeof(struct in_addr);
365e5054602SMark Johnston 	ah->ar_op = htons(ARPOP_REQUEST);
366e5054602SMark Johnston 	memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN);
367e5054602SMark Johnston 	((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr;
368e5054602SMark Johnston 	bzero(ar_tha(ah), ETHER_ADDR_LEN);
369e5054602SMark Johnston 	((struct in_addr *)ar_tpa(ah))->s_addr = dst;
370e5054602SMark Johnston 	return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP));
371e5054602SMark Johnston }
372e5054602SMark Johnston 
373e5054602SMark Johnston /*
374e5054602SMark Johnston  * Sends ARP requests to locate the server and waits for a response.
375e5054602SMark Johnston  * We first try to ARP the server itself, and fall back to the provided
376e5054602SMark Johnston  * gateway if the server appears to be off-link.
377e5054602SMark Johnston  *
378e5054602SMark Johnston  * Return value:
379e5054602SMark Johnston  *	0 on success
380e5054602SMark Johnston  *	errno on error
381e5054602SMark Johnston  */
382e5054602SMark Johnston static int
383e5054602SMark Johnston netdump_arp_gw(void)
384e5054602SMark Johnston {
385e5054602SMark Johnston 	in_addr_t dst;
386e5054602SMark Johnston 	int error, polls, retries;
387e5054602SMark Johnston 
388e5054602SMark Johnston 	dst = nd_server.s_addr;
389e5054602SMark Johnston restart:
390e5054602SMark Johnston 	for (retries = 0; retries < nd_arp_retries && have_gw_mac == 0;
391e5054602SMark Johnston 	    retries++) {
392e5054602SMark Johnston 		error = netdump_send_arp(dst);
393e5054602SMark Johnston 		if (error != 0)
394e5054602SMark Johnston 			return (error);
395e5054602SMark Johnston 		for (polls = 0; polls < nd_polls && have_gw_mac == 0; polls++) {
396e5054602SMark Johnston 			netdump_network_poll();
397e5054602SMark Johnston 			DELAY(500);
398e5054602SMark Johnston 		}
399e5054602SMark Johnston 		if (have_gw_mac == 0)
400e5054602SMark Johnston 			printf("(ARP retry)");
401e5054602SMark Johnston 	}
402e5054602SMark Johnston 	if (have_gw_mac != 0)
403e5054602SMark Johnston 		return (0);
404e5054602SMark Johnston 	if (dst == nd_server.s_addr && nd_server.s_addr != nd_gateway.s_addr) {
405e5054602SMark Johnston 		printf("Failed to ARP server, trying to reach gateway...\n");
406e5054602SMark Johnston 		dst = nd_gateway.s_addr;
407e5054602SMark Johnston 		goto restart;
408e5054602SMark Johnston 	}
409e5054602SMark Johnston 
410e5054602SMark Johnston 	printf("\nARP timed out.\n");
411e5054602SMark Johnston 	return (ETIMEDOUT);
412e5054602SMark Johnston }
413e5054602SMark Johnston 
414e5054602SMark Johnston /*
415e5054602SMark Johnston  * Dummy free function for netdump clusters.
416e5054602SMark Johnston  */
417e5054602SMark Johnston static void
418e5054602SMark Johnston netdump_mbuf_free(struct mbuf *m __unused)
419e5054602SMark Johnston {
420e5054602SMark Johnston }
421e5054602SMark Johnston 
422e5054602SMark Johnston /*
423e5054602SMark Johnston  * Construct and reliably send a netdump packet.  May fail from a resource
424e5054602SMark Johnston  * shortage or extreme number of unacknowledged retransmissions.  Wait for
425e5054602SMark Johnston  * an acknowledgement before returning.  Splits packets into chunks small
426e5054602SMark Johnston  * enough to be sent without fragmentation (looks up the interface MTU)
427e5054602SMark Johnston  *
428e5054602SMark Johnston  * Parameters:
429e5054602SMark Johnston  *	type	netdump packet type (HERALD, FINISHED, or VMCORE)
430e5054602SMark Johnston  *	offset	vmcore data offset (bytes)
431e5054602SMark Johnston  *	data	vmcore data
432e5054602SMark Johnston  *	datalen	vmcore data size (bytes)
433e5054602SMark Johnston  *
434e5054602SMark Johnston  * Returns:
435e5054602SMark Johnston  *	int see errno.h, 0 for success
436e5054602SMark Johnston  */
437e5054602SMark Johnston static int
438e5054602SMark Johnston netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen)
439e5054602SMark Johnston {
440e5054602SMark Johnston 	struct netdump_msg_hdr *nd_msg_hdr;
441e5054602SMark Johnston 	struct mbuf *m, *m2;
442e5054602SMark Johnston 	uint64_t want_acks;
443e5054602SMark Johnston 	uint32_t i, pktlen, sent_so_far;
444e5054602SMark Johnston 	int retries, polls, error;
445e5054602SMark Johnston 
446e5054602SMark Johnston 	want_acks = 0;
447e5054602SMark Johnston 	rcvd_acks = 0;
448e5054602SMark Johnston 	retries = 0;
449e5054602SMark Johnston 
45064e7d18fSConrad Meyer 	MPASS(netdump_enabled());
451e5054602SMark Johnston 
452e5054602SMark Johnston retransmit:
453e5054602SMark Johnston 	/* Chunks can be too big to fit in packets. */
454e5054602SMark Johnston 	for (i = sent_so_far = 0; sent_so_far < datalen ||
455e5054602SMark Johnston 	    (i == 0 && datalen == 0); i++) {
456e5054602SMark Johnston 		pktlen = datalen - sent_so_far;
457e5054602SMark Johnston 
458e5054602SMark Johnston 		/* First bound: the packet structure. */
459e5054602SMark Johnston 		pktlen = min(pktlen, NETDUMP_DATASIZE);
460e5054602SMark Johnston 
461e5054602SMark Johnston 		/* Second bound: the interface MTU (assume no IP options). */
462e5054602SMark Johnston 		pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) -
463e5054602SMark Johnston 		    sizeof(struct netdump_msg_hdr));
464e5054602SMark Johnston 
465e5054602SMark Johnston 		/*
466e5054602SMark Johnston 		 * Check if it is retransmitting and this has been ACKed
467e5054602SMark Johnston 		 * already.
468e5054602SMark Johnston 		 */
469e5054602SMark Johnston 		if ((rcvd_acks & (1 << i)) != 0) {
470e5054602SMark Johnston 			sent_so_far += pktlen;
471e5054602SMark Johnston 			continue;
472e5054602SMark Johnston 		}
473e5054602SMark Johnston 
474e5054602SMark Johnston 		/*
475e5054602SMark Johnston 		 * Get and fill a header mbuf, then chain data as an extended
476e5054602SMark Johnston 		 * mbuf.
477e5054602SMark Johnston 		 */
478e5054602SMark Johnston 		m = m_gethdr(M_NOWAIT, MT_DATA);
479e5054602SMark Johnston 		if (m == NULL) {
480e5054602SMark Johnston 			printf("netdump_send: Out of mbufs\n");
481e5054602SMark Johnston 			return (ENOBUFS);
482e5054602SMark Johnston 		}
483e5054602SMark Johnston 		m->m_len = sizeof(struct netdump_msg_hdr);
484e5054602SMark Johnston 		m->m_pkthdr.len = sizeof(struct netdump_msg_hdr);
485e5054602SMark Johnston 		MH_ALIGN(m, sizeof(struct netdump_msg_hdr));
486e5054602SMark Johnston 		nd_msg_hdr = mtod(m, struct netdump_msg_hdr *);
487e5054602SMark Johnston 		nd_msg_hdr->mh_seqno = htonl(nd_seqno + i);
488e5054602SMark Johnston 		nd_msg_hdr->mh_type = htonl(type);
489e5054602SMark Johnston 		nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far);
490e5054602SMark Johnston 		nd_msg_hdr->mh_len = htonl(pktlen);
491e5054602SMark Johnston 		nd_msg_hdr->mh__pad = 0;
492e5054602SMark Johnston 
493e5054602SMark Johnston 		if (pktlen != 0) {
494e5054602SMark Johnston 			m2 = m_get(M_NOWAIT, MT_DATA);
495e5054602SMark Johnston 			if (m2 == NULL) {
496e5054602SMark Johnston 				m_freem(m);
497e5054602SMark Johnston 				printf("netdump_send: Out of mbufs\n");
498e5054602SMark Johnston 				return (ENOBUFS);
499e5054602SMark Johnston 			}
500e5054602SMark Johnston 			MEXTADD(m2, data + sent_so_far, pktlen,
501e5054602SMark Johnston 			    netdump_mbuf_free, NULL, NULL, 0, EXT_DISPOSABLE);
502e5054602SMark Johnston 			m2->m_len = pktlen;
503e5054602SMark Johnston 
504e5054602SMark Johnston 			m_cat(m, m2);
505e5054602SMark Johnston 			m->m_pkthdr.len += pktlen;
506e5054602SMark Johnston 		}
507e5054602SMark Johnston 		error = netdump_udp_output(m);
508e5054602SMark Johnston 		if (error != 0)
509e5054602SMark Johnston 			return (error);
510e5054602SMark Johnston 
511e5054602SMark Johnston 		/* Note that we're waiting for this packet in the bitfield. */
512e5054602SMark Johnston 		want_acks |= (1 << i);
513e5054602SMark Johnston 		sent_so_far += pktlen;
514e5054602SMark Johnston 	}
515e5054602SMark Johnston 	if (i >= NETDUMP_MAX_IN_FLIGHT)
516e5054602SMark Johnston 		printf("Warning: Sent more than %d packets (%d). "
517e5054602SMark Johnston 		    "Acknowledgements will fail unless the size of "
518e5054602SMark Johnston 		    "rcvd_acks/want_acks is increased.\n",
519e5054602SMark Johnston 		    NETDUMP_MAX_IN_FLIGHT, i);
520e5054602SMark Johnston 
521e5054602SMark Johnston 	/*
522e5054602SMark Johnston 	 * Wait for acks.  A *real* window would speed things up considerably.
523e5054602SMark Johnston 	 */
524e5054602SMark Johnston 	polls = 0;
525e5054602SMark Johnston 	while (rcvd_acks != want_acks) {
526e5054602SMark Johnston 		if (polls++ > nd_polls) {
527e5054602SMark Johnston 			if (retries++ > nd_retries)
528e5054602SMark Johnston 				return (ETIMEDOUT);
529e5054602SMark Johnston 			printf(". ");
530e5054602SMark Johnston 			goto retransmit;
531e5054602SMark Johnston 		}
532e5054602SMark Johnston 		netdump_network_poll();
533e5054602SMark Johnston 		DELAY(500);
534e5054602SMark Johnston 	}
535e5054602SMark Johnston 	nd_seqno += i;
536e5054602SMark Johnston 	return (0);
537e5054602SMark Johnston }
538e5054602SMark Johnston 
539e5054602SMark Johnston /*
540e5054602SMark Johnston  * Handler for IP packets: checks their sanity and then processes any netdump
541e5054602SMark Johnston  * ACK packets it finds.
542e5054602SMark Johnston  *
543e5054602SMark Johnston  * It needs to replicate partially the behaviour of ip_input() and
544e5054602SMark Johnston  * udp_input().
545e5054602SMark Johnston  *
546e5054602SMark Johnston  * Parameters:
547e5054602SMark Johnston  *	mb	a pointer to an mbuf * containing the packet received
548e5054602SMark Johnston  *		Updates *mb if m_pullup et al change the pointer
549e5054602SMark Johnston  *		Assumes the calling function will take care of freeing the mbuf
550e5054602SMark Johnston  */
551e5054602SMark Johnston static void
552e5054602SMark Johnston netdump_handle_ip(struct mbuf **mb)
553e5054602SMark Johnston {
554e5054602SMark Johnston 	struct ip *ip;
555e5054602SMark Johnston 	struct udpiphdr *udp;
556e5054602SMark Johnston 	struct netdump_ack *nd_ack;
557e5054602SMark Johnston 	struct mbuf *m;
558e5054602SMark Johnston 	int rcv_ackno;
559e5054602SMark Johnston 	unsigned short hlen;
560e5054602SMark Johnston 
561e5054602SMark Johnston 	/* IP processing. */
562e5054602SMark Johnston 	m = *mb;
563e5054602SMark Johnston 	if (m->m_pkthdr.len < sizeof(struct ip)) {
564e5054602SMark Johnston 		NETDDEBUG("dropping packet too small for IP header\n");
565e5054602SMark Johnston 		return;
566e5054602SMark Johnston 	}
567e5054602SMark Johnston 	if (m->m_len < sizeof(struct ip)) {
568e5054602SMark Johnston 		m = m_pullup(m, sizeof(struct ip));
569e5054602SMark Johnston 		*mb = m;
570e5054602SMark Johnston 		if (m == NULL) {
571e5054602SMark Johnston 			NETDDEBUG("m_pullup failed\n");
572e5054602SMark Johnston 			return;
573e5054602SMark Johnston 		}
574e5054602SMark Johnston 	}
575e5054602SMark Johnston 	ip = mtod(m, struct ip *);
576e5054602SMark Johnston 
577e5054602SMark Johnston 	/* IP version. */
578e5054602SMark Johnston 	if (ip->ip_v != IPVERSION) {
579e5054602SMark Johnston 		NETDDEBUG("bad IP version %d\n", ip->ip_v);
580e5054602SMark Johnston 		return;
581e5054602SMark Johnston 	}
582e5054602SMark Johnston 
583e5054602SMark Johnston 	/* Header length. */
584e5054602SMark Johnston 	hlen = ip->ip_hl << 2;
585e5054602SMark Johnston 	if (hlen < sizeof(struct ip)) {
586e5054602SMark Johnston 		NETDDEBUG("bad IP header length (%hu)\n", hlen);
587e5054602SMark Johnston 		return;
588e5054602SMark Johnston 	}
589e5054602SMark Johnston 	if (hlen > m->m_len) {
590e5054602SMark Johnston 		m = m_pullup(m, hlen);
591e5054602SMark Johnston 		*mb = m;
592e5054602SMark Johnston 		if (m == NULL) {
593e5054602SMark Johnston 			NETDDEBUG("m_pullup failed\n");
594e5054602SMark Johnston 			return;
595e5054602SMark Johnston 		}
596e5054602SMark Johnston 		ip = mtod(m, struct ip *);
597e5054602SMark Johnston 	}
598e5054602SMark Johnston 	/* Ignore packets with IP options. */
599e5054602SMark Johnston 	if (hlen > sizeof(struct ip)) {
600e5054602SMark Johnston 		NETDDEBUG("drop packet with IP options\n");
601e5054602SMark Johnston 		return;
602e5054602SMark Johnston 	}
603e5054602SMark Johnston 
604e5054602SMark Johnston #ifdef INVARIANTS
6056c1c6ae5SRodney W. Grimes 	if ((IN_LOOPBACK(ntohl(ip->ip_dst.s_addr)) ||
6066c1c6ae5SRodney W. Grimes 	    IN_LOOPBACK(ntohl(ip->ip_src.s_addr))) &&
607e5054602SMark Johnston 	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
608e5054602SMark Johnston 		NETDDEBUG("Bad IP header (RFC1122)\n");
609e5054602SMark Johnston 		return;
610e5054602SMark Johnston 	}
611e5054602SMark Johnston #endif
612e5054602SMark Johnston 
613e5054602SMark Johnston 	/* Checksum. */
614e5054602SMark Johnston 	if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) {
615e5054602SMark Johnston 		if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) {
616e5054602SMark Johnston 			NETDDEBUG("bad IP checksum\n");
617e5054602SMark Johnston 			return;
618e5054602SMark Johnston 		}
619e5054602SMark Johnston 	} else {
620e5054602SMark Johnston 		/* XXX */ ;
621e5054602SMark Johnston 	}
622e5054602SMark Johnston 
623e5054602SMark Johnston 	/* Convert fields to host byte order. */
624e5054602SMark Johnston 	ip->ip_len = ntohs(ip->ip_len);
625e5054602SMark Johnston 	if (ip->ip_len < hlen) {
626e5054602SMark Johnston 		NETDDEBUG("IP packet smaller (%hu) than header (%hu)\n",
627e5054602SMark Johnston 		    ip->ip_len, hlen);
628e5054602SMark Johnston 		return;
629e5054602SMark Johnston 	}
630e5054602SMark Johnston 	if (m->m_pkthdr.len < ip->ip_len) {
631e5054602SMark Johnston 		NETDDEBUG("IP packet bigger (%hu) than ethernet packet (%d)\n",
632e5054602SMark Johnston 		    ip->ip_len, m->m_pkthdr.len);
633e5054602SMark Johnston 		return;
634e5054602SMark Johnston 	}
635e5054602SMark Johnston 	if (m->m_pkthdr.len > ip->ip_len) {
636e5054602SMark Johnston 
637e5054602SMark Johnston 		/* Truncate the packet to the IP length. */
638e5054602SMark Johnston 		if (m->m_len == m->m_pkthdr.len) {
639e5054602SMark Johnston 			m->m_len = ip->ip_len;
640e5054602SMark Johnston 			m->m_pkthdr.len = ip->ip_len;
641e5054602SMark Johnston 		} else
642e5054602SMark Johnston 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
643e5054602SMark Johnston 	}
644e5054602SMark Johnston 
645e5054602SMark Johnston 	ip->ip_off = ntohs(ip->ip_off);
646e5054602SMark Johnston 
647e5054602SMark Johnston 	/* Check that the source is the server's IP. */
648e5054602SMark Johnston 	if (ip->ip_src.s_addr != nd_server.s_addr) {
649e5054602SMark Johnston 		NETDDEBUG("drop packet not from server (from 0x%x)\n",
650e5054602SMark Johnston 		    ip->ip_src.s_addr);
651e5054602SMark Johnston 		return;
652e5054602SMark Johnston 	}
653e5054602SMark Johnston 
654e5054602SMark Johnston 	/* Check if the destination IP is ours. */
655e5054602SMark Johnston 	if (ip->ip_dst.s_addr != nd_client.s_addr) {
656e5054602SMark Johnston 		NETDDEBUGV("drop packet not to our IP\n");
657e5054602SMark Johnston 		return;
658e5054602SMark Johnston 	}
659e5054602SMark Johnston 
660e5054602SMark Johnston 	if (ip->ip_p != IPPROTO_UDP) {
661e5054602SMark Johnston 		NETDDEBUG("drop non-UDP packet\n");
662e5054602SMark Johnston 		return;
663e5054602SMark Johnston 	}
664e5054602SMark Johnston 
665e5054602SMark Johnston 	/* Do not deal with fragments. */
666e5054602SMark Johnston 	if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) {
667e5054602SMark Johnston 		NETDDEBUG("drop fragmented packet\n");
668e5054602SMark Johnston 		return;
669e5054602SMark Johnston 	}
670e5054602SMark Johnston 
671e5054602SMark Johnston 	/* UDP custom is to have packet length not include IP header. */
672e5054602SMark Johnston 	ip->ip_len -= hlen;
673e5054602SMark Johnston 
674e5054602SMark Johnston 	/* UDP processing. */
675e5054602SMark Johnston 
676e5054602SMark Johnston 	/* Get IP and UDP headers together, along with the netdump packet. */
677e5054602SMark Johnston 	if (m->m_pkthdr.len <
678e5054602SMark Johnston 	    sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
679e5054602SMark Johnston 		NETDDEBUG("ignoring small packet\n");
680e5054602SMark Johnston 		return;
681e5054602SMark Johnston 	}
682e5054602SMark Johnston 	if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
683e5054602SMark Johnston 		m = m_pullup(m, sizeof(struct udpiphdr) +
684e5054602SMark Johnston 		    sizeof(struct netdump_ack));
685e5054602SMark Johnston 		*mb = m;
686e5054602SMark Johnston 		if (m == NULL) {
687e5054602SMark Johnston 			NETDDEBUG("m_pullup failed\n");
688e5054602SMark Johnston 			return;
689e5054602SMark Johnston 		}
690e5054602SMark Johnston 	}
691e5054602SMark Johnston 	udp = mtod(m, struct udpiphdr *);
692e5054602SMark Johnston 
693e5054602SMark Johnston 	if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) {
694e5054602SMark Johnston 		NETDDEBUG("not on the netdump port.\n");
695e5054602SMark Johnston 		return;
696e5054602SMark Johnston 	}
697e5054602SMark Johnston 
698e5054602SMark Johnston 	/* Netdump processing. */
699e5054602SMark Johnston 
700e5054602SMark Johnston 	/*
701e5054602SMark Johnston 	 * Packet is meant for us.  Extract the ack sequence number and the
702e5054602SMark Johnston 	 * port number if necessary.
703e5054602SMark Johnston 	 */
704e5054602SMark Johnston 	nd_ack = (struct netdump_ack *)(mtod(m, caddr_t) +
705e5054602SMark Johnston 	    sizeof(struct udpiphdr));
706e5054602SMark Johnston 	rcv_ackno = ntohl(nd_ack->na_seqno);
707e5054602SMark Johnston 	if (nd_server_port == NETDUMP_PORT)
708e5054602SMark Johnston 		nd_server_port = ntohs(udp->ui_u.uh_sport);
709e5054602SMark Johnston 	if (rcv_ackno >= nd_seqno + NETDUMP_MAX_IN_FLIGHT)
710e5054602SMark Johnston 		printf("%s: ACK %d too far in future!\n", __func__, rcv_ackno);
711e5054602SMark Johnston 	else if (rcv_ackno >= nd_seqno) {
712e5054602SMark Johnston 		/* We're interested in this ack. Record it. */
713e5054602SMark Johnston 		rcvd_acks |= 1 << (rcv_ackno - nd_seqno);
714e5054602SMark Johnston 	}
715e5054602SMark Johnston }
716e5054602SMark Johnston 
717e5054602SMark Johnston /*
718e5054602SMark Johnston  * Handler for ARP packets: checks their sanity and then
719e5054602SMark Johnston  * 1. If the ARP is a request for our IP, respond with our MAC address
720e5054602SMark Johnston  * 2. If the ARP is a response from our server, record its MAC address
721e5054602SMark Johnston  *
722e5054602SMark Johnston  * It needs to replicate partially the behaviour of arpintr() and
723e5054602SMark Johnston  * in_arpinput().
724e5054602SMark Johnston  *
725e5054602SMark Johnston  * Parameters:
726e5054602SMark Johnston  *	mb	a pointer to an mbuf * containing the packet received
727e5054602SMark Johnston  *		Updates *mb if m_pullup et al change the pointer
728e5054602SMark Johnston  *		Assumes the calling function will take care of freeing the mbuf
729e5054602SMark Johnston  */
730e5054602SMark Johnston static void
731e5054602SMark Johnston netdump_handle_arp(struct mbuf **mb)
732e5054602SMark Johnston {
733e5054602SMark Johnston 	char buf[INET_ADDRSTRLEN];
734e5054602SMark Johnston 	struct in_addr isaddr, itaddr, myaddr;
735e5054602SMark Johnston 	struct ether_addr dst;
736e5054602SMark Johnston 	struct mbuf *m;
737e5054602SMark Johnston 	struct arphdr *ah;
738e5054602SMark Johnston 	struct ifnet *ifp;
739e5054602SMark Johnston 	uint8_t *enaddr;
740e5054602SMark Johnston 	int req_len, op;
741e5054602SMark Johnston 
742e5054602SMark Johnston 	m = *mb;
743e5054602SMark Johnston 	ifp = m->m_pkthdr.rcvif;
744e5054602SMark Johnston 	if (m->m_len < sizeof(struct arphdr)) {
745e5054602SMark Johnston 		m = m_pullup(m, sizeof(struct arphdr));
746e5054602SMark Johnston 		*mb = m;
747e5054602SMark Johnston 		if (m == NULL) {
748e5054602SMark Johnston 			NETDDEBUG("runt packet: m_pullup failed\n");
749e5054602SMark Johnston 			return;
750e5054602SMark Johnston 		}
751e5054602SMark Johnston 	}
752e5054602SMark Johnston 
753e5054602SMark Johnston 	ah = mtod(m, struct arphdr *);
754e5054602SMark Johnston 	if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) {
755e5054602SMark Johnston 		NETDDEBUG("unknown hardware address 0x%2D)\n",
756e5054602SMark Johnston 		    (unsigned char *)&ah->ar_hrd, "");
757e5054602SMark Johnston 		return;
758e5054602SMark Johnston 	}
759e5054602SMark Johnston 	if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
760e5054602SMark Johnston 		NETDDEBUG("drop ARP for unknown protocol %d\n",
761e5054602SMark Johnston 		    ntohs(ah->ar_pro));
762e5054602SMark Johnston 		return;
763e5054602SMark Johnston 	}
764e5054602SMark Johnston 	req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
765e5054602SMark Johnston 	if (m->m_len < req_len) {
766e5054602SMark Johnston 		m = m_pullup(m, req_len);
767e5054602SMark Johnston 		*mb = m;
768e5054602SMark Johnston 		if (m == NULL) {
769e5054602SMark Johnston 			NETDDEBUG("runt packet: m_pullup failed\n");
770e5054602SMark Johnston 			return;
771e5054602SMark Johnston 		}
772e5054602SMark Johnston 	}
773e5054602SMark Johnston 	ah = mtod(m, struct arphdr *);
774e5054602SMark Johnston 
775e5054602SMark Johnston 	op = ntohs(ah->ar_op);
776e5054602SMark Johnston 	memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
777e5054602SMark Johnston 	memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
778e5054602SMark Johnston 	enaddr = (uint8_t *)IF_LLADDR(ifp);
779e5054602SMark Johnston 	myaddr = nd_client;
780e5054602SMark Johnston 
781e5054602SMark Johnston 	if (memcmp(ar_sha(ah), enaddr, ifp->if_addrlen) == 0) {
782e5054602SMark Johnston 		NETDDEBUG("ignoring ARP from myself\n");
783e5054602SMark Johnston 		return;
784e5054602SMark Johnston 	}
785e5054602SMark Johnston 
786e5054602SMark Johnston 	if (isaddr.s_addr == nd_client.s_addr) {
787e5054602SMark Johnston 		printf("%s: %*D is using my IP address %s!\n", __func__,
788e5054602SMark Johnston 		    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
789e5054602SMark Johnston 		    inet_ntoa_r(isaddr, buf));
790e5054602SMark Johnston 		return;
791e5054602SMark Johnston 	}
792e5054602SMark Johnston 
793e5054602SMark Johnston 	if (memcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen) == 0) {
794e5054602SMark Johnston 		NETDDEBUG("ignoring ARP from broadcast address\n");
795e5054602SMark Johnston 		return;
796e5054602SMark Johnston 	}
797e5054602SMark Johnston 
798e5054602SMark Johnston 	if (op == ARPOP_REPLY) {
799e5054602SMark Johnston 		if (isaddr.s_addr != nd_gateway.s_addr &&
800e5054602SMark Johnston 		    isaddr.s_addr != nd_server.s_addr) {
801e5054602SMark Johnston 			inet_ntoa_r(isaddr, buf);
802e5054602SMark Johnston 			NETDDEBUG(
803e5054602SMark Johnston 			    "ignoring ARP reply from %s (not netdump server)\n",
804e5054602SMark Johnston 			    buf);
805e5054602SMark Johnston 			return;
806e5054602SMark Johnston 		}
807e5054602SMark Johnston 		memcpy(nd_gw_mac.octet, ar_sha(ah),
808e5054602SMark Johnston 		    min(ah->ar_hln, ETHER_ADDR_LEN));
809e5054602SMark Johnston 		have_gw_mac = 1;
810e5054602SMark Johnston 		NETDDEBUG("got server MAC address %6D\n", nd_gw_mac.octet, ":");
811e5054602SMark Johnston 		return;
812e5054602SMark Johnston 	}
813e5054602SMark Johnston 
814e5054602SMark Johnston 	if (op != ARPOP_REQUEST) {
815e5054602SMark Johnston 		NETDDEBUG("ignoring ARP non-request/reply\n");
816e5054602SMark Johnston 		return;
817e5054602SMark Johnston 	}
818e5054602SMark Johnston 
819e5054602SMark Johnston 	if (itaddr.s_addr != nd_client.s_addr) {
820e5054602SMark Johnston 		NETDDEBUG("ignoring ARP not to our IP\n");
821e5054602SMark Johnston 		return;
822e5054602SMark Johnston 	}
823e5054602SMark Johnston 
824e5054602SMark Johnston 	memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
825e5054602SMark Johnston 	memcpy(ar_sha(ah), enaddr, ah->ar_hln);
826e5054602SMark Johnston 	memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln);
827e5054602SMark Johnston 	memcpy(ar_spa(ah), &itaddr, ah->ar_pln);
828e5054602SMark Johnston 	ah->ar_op = htons(ARPOP_REPLY);
829e5054602SMark Johnston 	ah->ar_pro = htons(ETHERTYPE_IP);
830e5054602SMark Johnston 	m->m_flags &= ~(M_BCAST|M_MCAST);
831e5054602SMark Johnston 	m->m_len = arphdr_len(ah);
832e5054602SMark Johnston 	m->m_pkthdr.len = m->m_len;
833e5054602SMark Johnston 
834e5054602SMark Johnston 	memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
835e5054602SMark Johnston 	netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
836e5054602SMark Johnston 	*mb = NULL;
837e5054602SMark Johnston }
838e5054602SMark Johnston 
839e5054602SMark Johnston /*
840e5054602SMark Johnston  * Handler for incoming packets directly from the network adapter
841e5054602SMark Johnston  * Identifies the packet type (IP or ARP) and passes it along to one of the
842e5054602SMark Johnston  * helper functions netdump_handle_ip or netdump_handle_arp.
843e5054602SMark Johnston  *
844e5054602SMark Johnston  * It needs to replicate partially the behaviour of ether_input() and
845e5054602SMark Johnston  * ether_demux().
846e5054602SMark Johnston  *
847e5054602SMark Johnston  * Parameters:
848e5054602SMark Johnston  *	ifp	the interface the packet came from (should be nd_ifp)
849e5054602SMark Johnston  *	m	an mbuf containing the packet received
850e5054602SMark Johnston  */
851e5054602SMark Johnston static void
852e5054602SMark Johnston netdump_pkt_in(struct ifnet *ifp, struct mbuf *m)
853e5054602SMark Johnston {
854e5054602SMark Johnston 	struct ifreq ifr;
855e5054602SMark Johnston 	struct ether_header *eh;
856e5054602SMark Johnston 	u_short etype;
857e5054602SMark Johnston 
858e5054602SMark Johnston 	/* Ethernet processing. */
859e5054602SMark Johnston 	if ((m->m_flags & M_PKTHDR) == 0) {
860e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "discard frame without packet header\n");
861e5054602SMark Johnston 		goto done;
862e5054602SMark Johnston 	}
863e5054602SMark Johnston 	if (m->m_len < ETHER_HDR_LEN) {
864e5054602SMark Johnston 		NETDDEBUG_IF(ifp,
865e5054602SMark Johnston 	    "discard frame without leading eth header (len %u pktlen %u)\n",
866e5054602SMark Johnston 		    m->m_len, m->m_pkthdr.len);
867e5054602SMark Johnston 		goto done;
868e5054602SMark Johnston 	}
869e5054602SMark Johnston 	if ((m->m_flags & M_HASFCS) != 0) {
870e5054602SMark Johnston 		m_adj(m, -ETHER_CRC_LEN);
871e5054602SMark Johnston 		m->m_flags &= ~M_HASFCS;
872e5054602SMark Johnston 	}
873e5054602SMark Johnston 	eh = mtod(m, struct ether_header *);
874e5054602SMark Johnston 	etype = ntohs(eh->ether_type);
875e5054602SMark Johnston 	if ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) {
876e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "ignoring vlan packets\n");
877e5054602SMark Johnston 		goto done;
878e5054602SMark Johnston 	}
879e5054602SMark Johnston 	if (if_gethwaddr(ifp, &ifr) != 0) {
880e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "failed to get hw addr for interface\n");
881e5054602SMark Johnston 		goto done;
882e5054602SMark Johnston 	}
883e5054602SMark Johnston 	if (memcmp(ifr.ifr_addr.sa_data, eh->ether_dhost,
884e5054602SMark Johnston 	    ETHER_ADDR_LEN) != 0) {
885e5054602SMark Johnston 		NETDDEBUG_IF(ifp,
886e5054602SMark Johnston 		    "discard frame with incorrect destination addr\n");
887e5054602SMark Johnston 		goto done;
888e5054602SMark Johnston 	}
889e5054602SMark Johnston 
890e5054602SMark Johnston 	/* Done ethernet processing. Strip off the ethernet header. */
891e5054602SMark Johnston 	m_adj(m, ETHER_HDR_LEN);
892e5054602SMark Johnston 	switch (etype) {
893e5054602SMark Johnston 	case ETHERTYPE_ARP:
894e5054602SMark Johnston 		netdump_handle_arp(&m);
895e5054602SMark Johnston 		break;
896e5054602SMark Johnston 	case ETHERTYPE_IP:
897e5054602SMark Johnston 		netdump_handle_ip(&m);
898e5054602SMark Johnston 		break;
899e5054602SMark Johnston 	default:
900e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "dropping unknown ethertype %hu\n", etype);
901e5054602SMark Johnston 		break;
902e5054602SMark Johnston 	}
903e5054602SMark Johnston done:
904e5054602SMark Johnston 	if (m != NULL)
905e5054602SMark Johnston 		m_freem(m);
906e5054602SMark Johnston }
907e5054602SMark Johnston 
908e5054602SMark Johnston /*
909e5054602SMark Johnston  * After trapping, instead of assuming that most of the network stack is sane,
910e5054602SMark Johnston  * we just poll the driver directly for packets.
911e5054602SMark Johnston  */
912e5054602SMark Johnston static void
913e5054602SMark Johnston netdump_network_poll(void)
914e5054602SMark Johnston {
915e5054602SMark Johnston 
91664e7d18fSConrad Meyer 	MPASS(netdump_enabled());
917e5054602SMark Johnston 
918e5054602SMark Johnston 	nd_ifp->if_netdump_methods->nd_poll(nd_ifp, 1000);
919e5054602SMark Johnston }
920e5054602SMark Johnston 
921e5054602SMark Johnston /*-
922e5054602SMark Johnston  * Dumping specific primitives.
923e5054602SMark Johnston  */
924e5054602SMark Johnston 
925e5054602SMark Johnston /*
926e5054602SMark Johnston  * Callback from dumpsys() to dump a chunk of memory.
927e5054602SMark Johnston  * Copies it out to our static buffer then sends it across the network.
928e5054602SMark Johnston  * Detects the initial KDH and makes sure it is given a special packet type.
929e5054602SMark Johnston  *
930e5054602SMark Johnston  * Parameters:
931e5054602SMark Johnston  *	priv	 Unused. Optional private pointer.
932e5054602SMark Johnston  *	virtual  Virtual address (where to read the data from)
933e5054602SMark Johnston  *	physical Unused. Physical memory address.
934e5054602SMark Johnston  *	offset	 Offset from start of core file
935e5054602SMark Johnston  *	length	 Data length
936e5054602SMark Johnston  *
937e5054602SMark Johnston  * Return value:
938e5054602SMark Johnston  *	0 on success
939e5054602SMark Johnston  *	errno on error
940e5054602SMark Johnston  */
941e5054602SMark Johnston static int
942e5054602SMark Johnston netdump_dumper(void *priv __unused, void *virtual,
943e5054602SMark Johnston     vm_offset_t physical __unused, off_t offset, size_t length)
944e5054602SMark Johnston {
945e5054602SMark Johnston 	int error;
946e5054602SMark Johnston 
947e5054602SMark Johnston 	NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n",
948e5054602SMark Johnston 	    virtual, (uintmax_t)offset, length);
949e5054602SMark Johnston 
950e5054602SMark Johnston 	if (virtual == NULL) {
951*8726929dSMark Johnston 		if (nd_conf.nd_buf_len != 0) {
952*8726929dSMark Johnston 			error = netdump_send(NETDUMP_VMCORE, nd_conf.nd_tx_off, nd_buf,
953*8726929dSMark Johnston 			    nd_conf.nd_buf_len);
954*8726929dSMark Johnston 			if (error != 0) {
955*8726929dSMark Johnston 				dump_failed = 1;
956*8726929dSMark Johnston 			}
957*8726929dSMark Johnston 		}
958*8726929dSMark Johnston 
959e5054602SMark Johnston 		if (dump_failed != 0)
960e5054602SMark Johnston 			printf("failed to dump the kernel core\n");
961e5054602SMark Johnston 		else if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0)
962e5054602SMark Johnston 			printf("failed to close the transaction\n");
963e5054602SMark Johnston 		else
964e5054602SMark Johnston 			printf("\nnetdump finished.\n");
965e5054602SMark Johnston 		netdump_cleanup();
966e5054602SMark Johnston 		return (0);
967e5054602SMark Johnston 	}
968e5054602SMark Johnston 	if (length > sizeof(nd_buf))
969e5054602SMark Johnston 		return (ENOSPC);
970e5054602SMark Johnston 
971*8726929dSMark Johnston 	if (nd_conf.nd_buf_len + length > sizeof(nd_buf) ||
972*8726929dSMark Johnston 	    (nd_conf.nd_buf_len != 0 && nd_conf.nd_tx_off +
973*8726929dSMark Johnston 	    nd_conf.nd_buf_len != offset)) {
974*8726929dSMark Johnston 		error = netdump_send(NETDUMP_VMCORE, nd_conf.nd_tx_off, nd_buf,
975*8726929dSMark Johnston 		    nd_conf.nd_buf_len);
976e5054602SMark Johnston 		if (error != 0) {
977e5054602SMark Johnston 			dump_failed = 1;
978e5054602SMark Johnston 			return (error);
979e5054602SMark Johnston 		}
980*8726929dSMark Johnston 		nd_conf.nd_buf_len = 0;
981*8726929dSMark Johnston 		nd_conf.nd_tx_off = offset;
982*8726929dSMark Johnston 	}
983*8726929dSMark Johnston 
984*8726929dSMark Johnston 	memmove(nd_buf + nd_conf.nd_buf_len, virtual, length);
985*8726929dSMark Johnston 	nd_conf.nd_buf_len += length;
986*8726929dSMark Johnston 
987e5054602SMark Johnston 	return (0);
988e5054602SMark Johnston }
989e5054602SMark Johnston 
990e5054602SMark Johnston /*
991e5054602SMark Johnston  * Perform any initalization needed prior to transmitting the kernel core.
992e5054602SMark Johnston  */
993e5054602SMark Johnston static int
994e5054602SMark Johnston netdump_start(struct dumperinfo *di)
995e5054602SMark Johnston {
996e5054602SMark Johnston 	char *path;
997e5054602SMark Johnston 	char buf[INET_ADDRSTRLEN];
998e5054602SMark Johnston 	uint32_t len;
999e5054602SMark Johnston 	int error;
1000e5054602SMark Johnston 
1001e5054602SMark Johnston 	error = 0;
1002e5054602SMark Johnston 
1003e5054602SMark Johnston 	/* Check if the dumping is allowed to continue. */
100464e7d18fSConrad Meyer 	if (!netdump_enabled())
1005e5054602SMark Johnston 		return (EINVAL);
1006e5054602SMark Johnston 
1007e5054602SMark Johnston 	if (panicstr == NULL) {
1008e5054602SMark Johnston 		printf(
1009e5054602SMark Johnston 		    "netdump_start: netdump may only be used after a panic\n");
1010e5054602SMark Johnston 		return (EINVAL);
1011e5054602SMark Johnston 	}
1012e5054602SMark Johnston 
1013e5054602SMark Johnston 	if (nd_server.s_addr == INADDR_ANY) {
1014e5054602SMark Johnston 		printf("netdump_start: can't netdump; no server IP given\n");
1015e5054602SMark Johnston 		return (EINVAL);
1016e5054602SMark Johnston 	}
1017e5054602SMark Johnston 	if (nd_client.s_addr == INADDR_ANY) {
1018e5054602SMark Johnston 		printf("netdump_start: can't netdump; no client IP given\n");
1019e5054602SMark Johnston 		return (EINVAL);
1020e5054602SMark Johnston 	}
1021e5054602SMark Johnston 
1022e5054602SMark Johnston 	/* We start dumping at offset 0. */
1023e5054602SMark Johnston 	di->dumpoff = 0;
1024e5054602SMark Johnston 
1025e5054602SMark Johnston 	nd_seqno = 1;
1026e5054602SMark Johnston 
1027e5054602SMark Johnston 	/*
1028e5054602SMark Johnston 	 * nd_server_port could have switched after the first ack the
1029e5054602SMark Johnston 	 * first time it gets called.  Adjust it accordingly.
1030e5054602SMark Johnston 	 */
1031e5054602SMark Johnston 	nd_server_port = NETDUMP_PORT;
1032e5054602SMark Johnston 
1033e5054602SMark Johnston 	/* Switch to the netdump mbuf zones. */
1034e5054602SMark Johnston 	netdump_mbuf_dump();
1035e5054602SMark Johnston 
1036e5054602SMark Johnston 	nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_START);
1037e5054602SMark Johnston 
1038e5054602SMark Johnston 	/* Make the card use *our* receive callback. */
1039e5054602SMark Johnston 	drv_if_input = nd_ifp->if_input;
1040e5054602SMark Johnston 	nd_ifp->if_input = netdump_pkt_in;
1041e5054602SMark Johnston 
1042e5054602SMark Johnston 	if (nd_gateway.s_addr == INADDR_ANY) {
1043e5054602SMark Johnston 		restore_gw_addr = 1;
1044e5054602SMark Johnston 		nd_gateway.s_addr = nd_server.s_addr;
1045e5054602SMark Johnston 	}
1046e5054602SMark Johnston 
1047e5054602SMark Johnston 	printf("netdump in progress. searching for server...\n");
1048e5054602SMark Johnston 	if (netdump_arp_gw()) {
1049e5054602SMark Johnston 		printf("failed to locate server MAC address\n");
1050e5054602SMark Johnston 		error = EINVAL;
1051e5054602SMark Johnston 		goto trig_abort;
1052e5054602SMark Johnston 	}
1053e5054602SMark Johnston 
1054e5054602SMark Johnston 	if (nd_path[0] != '\0') {
1055e5054602SMark Johnston 		path = nd_path;
1056e5054602SMark Johnston 		len = strlen(path) + 1;
1057e5054602SMark Johnston 	} else {
1058e5054602SMark Johnston 		path = NULL;
1059e5054602SMark Johnston 		len = 0;
1060e5054602SMark Johnston 	}
1061e5054602SMark Johnston 	if (netdump_send(NETDUMP_HERALD, 0, path, len) != 0) {
1062e5054602SMark Johnston 		printf("failed to contact netdump server\n");
1063e5054602SMark Johnston 		error = EINVAL;
1064e5054602SMark Johnston 		goto trig_abort;
1065e5054602SMark Johnston 	}
1066e5054602SMark Johnston 	printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf),
1067e5054602SMark Johnston 	    nd_gw_mac.octet, ":");
1068e5054602SMark Johnston 	return (0);
1069e5054602SMark Johnston 
1070e5054602SMark Johnston trig_abort:
1071e5054602SMark Johnston 	netdump_cleanup();
1072e5054602SMark Johnston 	return (error);
1073e5054602SMark Johnston }
1074e5054602SMark Johnston 
1075e5054602SMark Johnston static int
1076e5054602SMark Johnston netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh,
1077e5054602SMark Johnston     void *key, uint32_t keysize)
1078e5054602SMark Johnston {
1079e5054602SMark Johnston 	int error;
1080e5054602SMark Johnston 
1081e5054602SMark Johnston 	memcpy(nd_buf, kdh, sizeof(*kdh));
1082e5054602SMark Johnston 	error = netdump_send(NETDUMP_KDH, 0, nd_buf, sizeof(*kdh));
1083e5054602SMark Johnston 	if (error == 0 && keysize > 0) {
1084e5054602SMark Johnston 		if (keysize > sizeof(nd_buf))
1085e5054602SMark Johnston 			return (EINVAL);
1086e5054602SMark Johnston 		memcpy(nd_buf, key, keysize);
1087e5054602SMark Johnston 		error = netdump_send(NETDUMP_EKCD_KEY, 0, nd_buf, keysize);
1088e5054602SMark Johnston 	}
1089e5054602SMark Johnston 	return (error);
1090e5054602SMark Johnston }
1091e5054602SMark Johnston 
1092e5054602SMark Johnston /*
1093e5054602SMark Johnston  * Cleanup routine for a possibly failed netdump.
1094e5054602SMark Johnston  */
1095e5054602SMark Johnston static void
1096e5054602SMark Johnston netdump_cleanup(void)
1097e5054602SMark Johnston {
1098e5054602SMark Johnston 
1099e5054602SMark Johnston 	if (restore_gw_addr != 0) {
1100e5054602SMark Johnston 		nd_gateway.s_addr = INADDR_ANY;
1101e5054602SMark Johnston 		restore_gw_addr = 0;
1102e5054602SMark Johnston 	}
1103e5054602SMark Johnston 	if (drv_if_input != NULL) {
1104e5054602SMark Johnston 		nd_ifp->if_input = drv_if_input;
1105e5054602SMark Johnston 		drv_if_input = NULL;
1106e5054602SMark Johnston 	}
1107e5054602SMark Johnston 	nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_END);
1108e5054602SMark Johnston }
1109e5054602SMark Johnston 
1110e5054602SMark Johnston /*-
1111e5054602SMark Johnston  * KLD specific code.
1112e5054602SMark Johnston  */
1113e5054602SMark Johnston 
1114e5054602SMark Johnston static struct cdevsw netdump_cdevsw = {
1115e5054602SMark Johnston 	.d_version =	D_VERSION,
1116e5054602SMark Johnston 	.d_ioctl =	netdump_ioctl,
1117e5054602SMark Johnston 	.d_name =	"netdump",
1118e5054602SMark Johnston };
1119e5054602SMark Johnston 
1120e5054602SMark Johnston static struct cdev *netdump_cdev;
1121e5054602SMark Johnston 
112264e7d18fSConrad Meyer static void
112364e7d18fSConrad Meyer netdump_unconfigure(void)
112464e7d18fSConrad Meyer {
112564e7d18fSConrad Meyer 	struct diocskerneldump_arg kda;
112664e7d18fSConrad Meyer 
112764e7d18fSConrad Meyer 	NETDUMP_ASSERT_WLOCKED();
112864e7d18fSConrad Meyer 	KASSERT(netdump_enabled(), ("%s: nd_ifp NULL", __func__));
112964e7d18fSConrad Meyer 
113064e7d18fSConrad Meyer 	bzero(&kda, sizeof(kda));
113164e7d18fSConrad Meyer 	kda.kda_index = KDA_REMOVE_DEV;
113264e7d18fSConrad Meyer 	(void)dumper_remove(nd_conf.ndc_iface, &kda);
113364e7d18fSConrad Meyer 
113464e7d18fSConrad Meyer 	netdump_mbuf_drain();
113564e7d18fSConrad Meyer 
113664e7d18fSConrad Meyer 	if_rele(nd_ifp);
113764e7d18fSConrad Meyer 	nd_ifp = NULL;
113864e7d18fSConrad Meyer 
113964e7d18fSConrad Meyer 	bzero(&nd_conf, sizeof(nd_conf));
114064e7d18fSConrad Meyer }
114164e7d18fSConrad Meyer 
114264e7d18fSConrad Meyer static void
114364e7d18fSConrad Meyer netdump_ifdetach(void *arg __unused, struct ifnet *ifp)
114464e7d18fSConrad Meyer {
114564e7d18fSConrad Meyer 
114664e7d18fSConrad Meyer 	NETDUMP_WLOCK();
114764e7d18fSConrad Meyer 	if (ifp == nd_ifp)
114864e7d18fSConrad Meyer 		netdump_unconfigure();
114964e7d18fSConrad Meyer 	NETDUMP_WUNLOCK();
115064e7d18fSConrad Meyer }
115164e7d18fSConrad Meyer 
1152e5054602SMark Johnston static int
11536b6e2954SConrad Meyer netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
1154e5054602SMark Johnston {
1155e5054602SMark Johnston 	struct ifnet *ifp;
1156e5054602SMark Johnston 
115764e7d18fSConrad Meyer 	NETDUMP_ASSERT_WLOCKED();
115864e7d18fSConrad Meyer 
1159b35822d9SMark Johnston 	CURVNET_SET(TD_TO_VNET(td));
1160b35822d9SMark Johnston 	if (!IS_DEFAULT_VNET(curvnet)) {
1161b35822d9SMark Johnston 		CURVNET_RESTORE();
1162b35822d9SMark Johnston 		return (EINVAL);
1163b35822d9SMark Johnston 	}
116464e7d18fSConrad Meyer 	ifp = ifunit_ref(conf->kda_iface);
1165b35822d9SMark Johnston 	CURVNET_RESTORE();
1166e5054602SMark Johnston 
1167e5054602SMark Johnston 	if (ifp == NULL)
1168e5054602SMark Johnston 		return (ENOENT);
116964e7d18fSConrad Meyer 	if ((if_getflags(ifp) & IFF_UP) == 0) {
117064e7d18fSConrad Meyer 		if_rele(ifp);
1171e5054602SMark Johnston 		return (ENXIO);
117264e7d18fSConrad Meyer 	}
117364e7d18fSConrad Meyer 	if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER) {
117464e7d18fSConrad Meyer 		if_rele(ifp);
11756b6e2954SConrad Meyer 		return (ENODEV);
117664e7d18fSConrad Meyer 	}
1177e5054602SMark Johnston 
117864e7d18fSConrad Meyer 	if (netdump_enabled())
117964e7d18fSConrad Meyer 		if_rele(nd_ifp);
1180e5054602SMark Johnston 	nd_ifp = ifp;
11816144b50fSConrad Meyer 
118264e7d18fSConrad Meyer 	netdump_reinit_internal(ifp);
118364e7d18fSConrad Meyer 
11846144b50fSConrad Meyer #define COPY_SIZED(elm) do {	\
11856144b50fSConrad Meyer 	_Static_assert(sizeof(nd_conf.ndc_ ## elm) ==			\
11866144b50fSConrad Meyer 	    sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
11876144b50fSConrad Meyer 	memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm,		\
11886144b50fSConrad Meyer 	    sizeof(nd_conf.ndc_ ## elm));				\
11896144b50fSConrad Meyer } while (0)
11906144b50fSConrad Meyer 	COPY_SIZED(iface);
11916144b50fSConrad Meyer 	COPY_SIZED(server);
11926144b50fSConrad Meyer 	COPY_SIZED(client);
11936144b50fSConrad Meyer 	COPY_SIZED(gateway);
11946144b50fSConrad Meyer 	COPY_SIZED(af);
11956144b50fSConrad Meyer #undef COPY_SIZED
119664e7d18fSConrad Meyer 
1197e5054602SMark Johnston 	return (0);
1198e5054602SMark Johnston }
1199e5054602SMark Johnston 
1200e5054602SMark Johnston /*
1201e5054602SMark Johnston  * Reinitialize the mbuf pool used by drivers while dumping. This is called
120264e7d18fSConrad Meyer  * from the generic ioctl handler for SIOCSIFMTU after any NIC driver has
120364e7d18fSConrad Meyer  * reconfigured itself.  (I.e., it may not be a configured netdump interface.)
1204e5054602SMark Johnston  */
1205e5054602SMark Johnston void
1206e5054602SMark Johnston netdump_reinit(struct ifnet *ifp)
1207e5054602SMark Johnston {
120864e7d18fSConrad Meyer 
120964e7d18fSConrad Meyer 	NETDUMP_WLOCK();
121064e7d18fSConrad Meyer 	if (ifp != nd_ifp) {
121164e7d18fSConrad Meyer 		NETDUMP_WUNLOCK();
121264e7d18fSConrad Meyer 		return;
121364e7d18fSConrad Meyer 	}
121464e7d18fSConrad Meyer 	netdump_reinit_internal(ifp);
121564e7d18fSConrad Meyer 	NETDUMP_WUNLOCK();
121664e7d18fSConrad Meyer }
121764e7d18fSConrad Meyer 
121864e7d18fSConrad Meyer static void
121964e7d18fSConrad Meyer netdump_reinit_internal(struct ifnet *ifp)
122064e7d18fSConrad Meyer {
1221e5054602SMark Johnston 	int clsize, nmbuf, ncl, nrxr;
1222e5054602SMark Johnston 
122364e7d18fSConrad Meyer 	NETDUMP_ASSERT_WLOCKED();
1224e5054602SMark Johnston 
1225e5054602SMark Johnston 	ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize);
1226e5054602SMark Johnston 	KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr));
1227e5054602SMark Johnston 
1228e5054602SMark Johnston 	/*
1229e5054602SMark Johnston 	 * We need two headers per message on the transmit side. Multiply by
1230e5054602SMark Johnston 	 * four to give us some breathing room.
1231e5054602SMark Johnston 	 */
1232e5054602SMark Johnston 	nmbuf = ncl * (4 + nrxr);
1233e5054602SMark Johnston 	ncl *= nrxr;
1234e5054602SMark Johnston 	netdump_mbuf_reinit(nmbuf, ncl, clsize);
1235e5054602SMark Johnston }
1236e5054602SMark Johnston 
1237e5054602SMark Johnston /*
1238e5054602SMark Johnston  * ioctl(2) handler for the netdump device. This is currently only used to
1239e5054602SMark Johnston  * register netdump as a dump device.
1240e5054602SMark Johnston  *
1241e5054602SMark Johnston  * Parameters:
1242e5054602SMark Johnston  *     dev, Unused.
1243e5054602SMark Johnston  *     cmd, The ioctl to be handled.
1244e5054602SMark Johnston  *     addr, The parameter for the ioctl.
1245e5054602SMark Johnston  *     flags, Unused.
1246e5054602SMark Johnston  *     td, The thread invoking this ioctl.
1247e5054602SMark Johnston  *
1248e5054602SMark Johnston  * Returns:
1249e5054602SMark Johnston  *     0 on success, and an errno value on failure.
1250e5054602SMark Johnston  */
1251e5054602SMark Johnston static int
1252e5054602SMark Johnston netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
1253e5054602SMark Johnston     int flags __unused, struct thread *td)
1254e5054602SMark Johnston {
12556b6e2954SConrad Meyer 	struct diocskerneldump_arg kda_copy, *conf;
1256e5054602SMark Johnston 	struct dumperinfo dumper;
1257e5054602SMark Johnston 	uint8_t *encryptedkey;
1258e5054602SMark Johnston 	int error;
1259a9f7f192SConrad Meyer #ifdef COMPAT_FREEBSD11
1260e5054602SMark Johnston 	u_int u;
1261a9f7f192SConrad Meyer #endif
12626b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12
12636b6e2954SConrad Meyer 	struct diocskerneldump_arg_freebsd12 *kda12;
12646b6e2954SConrad Meyer 	struct netdump_conf_freebsd12 *conf12;
12656b6e2954SConrad Meyer #endif
1266e5054602SMark Johnston 
12676b6e2954SConrad Meyer 	conf = NULL;
1268e5054602SMark Johnston 	error = 0;
126964e7d18fSConrad Meyer 	NETDUMP_WLOCK();
127064e7d18fSConrad Meyer 
1271e5054602SMark Johnston 	switch (cmd) {
127260ade167SConrad Meyer #ifdef COMPAT_FREEBSD11
127360ade167SConrad Meyer 	case DIOCSKERNELDUMP_FREEBSD11:
12746b6e2954SConrad Meyer 		gone_in(13, "11.x ABI compatibility");
1275e5054602SMark Johnston 		u = *(u_int *)addr;
1276e5054602SMark Johnston 		if (u != 0) {
1277e5054602SMark Johnston 			error = ENXIO;
1278e5054602SMark Johnston 			break;
1279e5054602SMark Johnston 		}
128064e7d18fSConrad Meyer 		if (netdump_enabled())
128164e7d18fSConrad Meyer 			netdump_unconfigure();
128260ade167SConrad Meyer 		break;
128360ade167SConrad Meyer #endif
12846b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12
12856b6e2954SConrad Meyer 		/*
12866b6e2954SConrad Meyer 		 * Used by dumpon(8) in 12.x for clearing previous
12876b6e2954SConrad Meyer 		 * configuration -- then NETDUMPSCONF_FREEBSD12 is used to
12886b6e2954SConrad Meyer 		 * actually configure netdump.
12896b6e2954SConrad Meyer 		 */
12906b6e2954SConrad Meyer 	case DIOCSKERNELDUMP_FREEBSD12:
12916b6e2954SConrad Meyer 		gone_in(14, "12.x ABI compatibility");
12926b6e2954SConrad Meyer 
12936b6e2954SConrad Meyer 		kda12 = (void *)addr;
12946b6e2954SConrad Meyer 		if (kda12->kda12_enable) {
129560ade167SConrad Meyer 			error = ENXIO;
129660ade167SConrad Meyer 			break;
129760ade167SConrad Meyer 		}
129864e7d18fSConrad Meyer 		if (netdump_enabled())
129964e7d18fSConrad Meyer 			netdump_unconfigure();
1300e5054602SMark Johnston 		break;
13016b6e2954SConrad Meyer 
13026b6e2954SConrad Meyer 	case NETDUMPGCONF_FREEBSD12:
13036b6e2954SConrad Meyer 		gone_in(14, "FreeBSD 12.x ABI compat");
13046b6e2954SConrad Meyer 		conf12 = (void *)addr;
13056b6e2954SConrad Meyer 
130664e7d18fSConrad Meyer 		if (!netdump_enabled()) {
1307e5054602SMark Johnston 			error = ENXIO;
1308e5054602SMark Johnston 			break;
1309e5054602SMark Johnston 		}
13106144b50fSConrad Meyer 		if (nd_conf.ndc_af != AF_INET) {
13116b6e2954SConrad Meyer 			error = EOPNOTSUPP;
1312e5054602SMark Johnston 			break;
13136b6e2954SConrad Meyer 		}
1314e5054602SMark Johnston 
13156b6e2954SConrad Meyer 		strlcpy(conf12->ndc12_iface, nd_ifp->if_xname,
13166b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_iface));
13176b6e2954SConrad Meyer 		memcpy(&conf12->ndc12_server, &nd_server,
13186b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_server));
13196b6e2954SConrad Meyer 		memcpy(&conf12->ndc12_client, &nd_client,
13206b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_client));
13216b6e2954SConrad Meyer 		memcpy(&conf12->ndc12_gateway, &nd_gateway,
13226b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_gateway));
13236b6e2954SConrad Meyer 		break;
13246b6e2954SConrad Meyer #endif
13256b6e2954SConrad Meyer 	case DIOCGKERNELDUMP:
13266b6e2954SConrad Meyer 		conf = (void *)addr;
13276b6e2954SConrad Meyer 		/*
13286b6e2954SConrad Meyer 		 * For now, index is ignored; netdump doesn't support multiple
13296b6e2954SConrad Meyer 		 * configurations (yet).
13306b6e2954SConrad Meyer 		 */
133164e7d18fSConrad Meyer 		if (!netdump_enabled()) {
13326b6e2954SConrad Meyer 			error = ENXIO;
13336b6e2954SConrad Meyer 			conf = NULL;
13346b6e2954SConrad Meyer 			break;
13356b6e2954SConrad Meyer 		}
13366b6e2954SConrad Meyer 
13376b6e2954SConrad Meyer 		strlcpy(conf->kda_iface, nd_ifp->if_xname,
13386b6e2954SConrad Meyer 		    sizeof(conf->kda_iface));
13396b6e2954SConrad Meyer 		memcpy(&conf->kda_server, &nd_server, sizeof(nd_server));
13406b6e2954SConrad Meyer 		memcpy(&conf->kda_client, &nd_client, sizeof(nd_client));
13416b6e2954SConrad Meyer 		memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway));
13426144b50fSConrad Meyer 		conf->kda_af = nd_conf.ndc_af;
13436b6e2954SConrad Meyer 		conf = NULL;
13446b6e2954SConrad Meyer 		break;
13456b6e2954SConrad Meyer 
13466b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12
13476b6e2954SConrad Meyer 	case NETDUMPSCONF_FREEBSD12:
13486b6e2954SConrad Meyer 		gone_in(14, "FreeBSD 12.x ABI compat");
13496b6e2954SConrad Meyer 
13506b6e2954SConrad Meyer 		conf12 = (struct netdump_conf_freebsd12 *)addr;
13516b6e2954SConrad Meyer 
13526b6e2954SConrad Meyer 		_Static_assert(offsetof(struct diocskerneldump_arg, kda_server)
13536b6e2954SConrad Meyer 		    == offsetof(struct netdump_conf_freebsd12, ndc12_server),
13546b6e2954SConrad Meyer 		    "simplifying assumption");
13556b6e2954SConrad Meyer 
13566b6e2954SConrad Meyer 		memset(&kda_copy, 0, sizeof(kda_copy));
13576b6e2954SConrad Meyer 		memcpy(&kda_copy, conf12,
13586b6e2954SConrad Meyer 		    offsetof(struct diocskerneldump_arg, kda_server));
13596b6e2954SConrad Meyer 
13606b6e2954SConrad Meyer 		/* 12.x ABI could only configure IPv4 (INET) netdump. */
13616b6e2954SConrad Meyer 		kda_copy.kda_af = AF_INET;
13626b6e2954SConrad Meyer 		memcpy(&kda_copy.kda_server.in4, &conf12->ndc12_server,
13636b6e2954SConrad Meyer 		    sizeof(struct in_addr));
13646b6e2954SConrad Meyer 		memcpy(&kda_copy.kda_client.in4, &conf12->ndc12_client,
13656b6e2954SConrad Meyer 		    sizeof(struct in_addr));
13666b6e2954SConrad Meyer 		memcpy(&kda_copy.kda_gateway.in4, &conf12->ndc12_gateway,
13676b6e2954SConrad Meyer 		    sizeof(struct in_addr));
13686b6e2954SConrad Meyer 
13696b6e2954SConrad Meyer 		kda_copy.kda_index =
13706b6e2954SConrad Meyer 		    (conf12->ndc12_kda.kda12_enable ? 0 : KDA_REMOVE_ALL);
13716b6e2954SConrad Meyer 
13726b6e2954SConrad Meyer 		conf = &kda_copy;
13736b6e2954SConrad Meyer 		explicit_bzero(conf12, sizeof(*conf12));
13746b6e2954SConrad Meyer 		/* FALLTHROUGH */
13756b6e2954SConrad Meyer #endif
13766b6e2954SConrad Meyer 	case DIOCSKERNELDUMP:
13776b6e2954SConrad Meyer 		encryptedkey = NULL;
13786b6e2954SConrad Meyer 		if (cmd == DIOCSKERNELDUMP) {
13796b6e2954SConrad Meyer 			conf = (void *)addr;
13806b6e2954SConrad Meyer 			memcpy(&kda_copy, conf, sizeof(kda_copy));
13816b6e2954SConrad Meyer 		}
13826b6e2954SConrad Meyer 		/* Netdump only supports IP4 at this time. */
13836b6e2954SConrad Meyer 		if (conf->kda_af != AF_INET) {
13846b6e2954SConrad Meyer 			error = EPROTONOSUPPORT;
13856b6e2954SConrad Meyer 			break;
13866b6e2954SConrad Meyer 		}
13876b6e2954SConrad Meyer 
13886b6e2954SConrad Meyer 		conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0';
13896b6e2954SConrad Meyer 		if (conf->kda_index == KDA_REMOVE ||
13906b6e2954SConrad Meyer 		    conf->kda_index == KDA_REMOVE_DEV ||
13916b6e2954SConrad Meyer 		    conf->kda_index == KDA_REMOVE_ALL) {
139264e7d18fSConrad Meyer 			if (netdump_enabled())
139364e7d18fSConrad Meyer 				netdump_unconfigure();
139464e7d18fSConrad Meyer 			if (conf->kda_index == KDA_REMOVE_ALL)
139564e7d18fSConrad Meyer 				error = dumper_remove(NULL, conf);
1396e5054602SMark Johnston 			break;
1397e5054602SMark Johnston 		}
1398e5054602SMark Johnston 
1399b35822d9SMark Johnston 		error = netdump_configure(conf, td);
1400e5054602SMark Johnston 		if (error != 0)
1401e5054602SMark Johnston 			break;
1402e5054602SMark Johnston 
14036b6e2954SConrad Meyer 		if (conf->kda_encryption != KERNELDUMP_ENC_NONE) {
14046b6e2954SConrad Meyer 			if (conf->kda_encryptedkeysize <= 0 ||
14056b6e2954SConrad Meyer 			    conf->kda_encryptedkeysize >
14066b6e2954SConrad Meyer 			    KERNELDUMP_ENCKEY_MAX_SIZE) {
14076b6e2954SConrad Meyer 				error = EINVAL;
14086b6e2954SConrad Meyer 				break;
14096b6e2954SConrad Meyer 			}
14106b6e2954SConrad Meyer 			encryptedkey = malloc(conf->kda_encryptedkeysize,
14116b6e2954SConrad Meyer 			    M_TEMP, M_WAITOK);
14126b6e2954SConrad Meyer 			error = copyin(conf->kda_encryptedkey, encryptedkey,
14136b6e2954SConrad Meyer 			    conf->kda_encryptedkeysize);
1414e5054602SMark Johnston 			if (error != 0) {
1415e5054602SMark Johnston 				free(encryptedkey, M_TEMP);
14166b6e2954SConrad Meyer 				break;
1417e5054602SMark Johnston 			}
14186b6e2954SConrad Meyer 
14196b6e2954SConrad Meyer 			conf->kda_encryptedkey = encryptedkey;
1420e5054602SMark Johnston 		}
1421e5054602SMark Johnston 
14229f78e2b8SMark Johnston 		memset(&dumper, 0, sizeof(dumper));
1423e5054602SMark Johnston 		dumper.dumper_start = netdump_start;
1424e5054602SMark Johnston 		dumper.dumper_hdr = netdump_write_headers;
1425e5054602SMark Johnston 		dumper.dumper = netdump_dumper;
1426e5054602SMark Johnston 		dumper.priv = NULL;
1427e5054602SMark Johnston 		dumper.blocksize = NETDUMP_DATASIZE;
1428e5054602SMark Johnston 		dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE;
1429e5054602SMark Johnston 		dumper.mediaoffset = 0;
1430e5054602SMark Johnston 		dumper.mediasize = 0;
1431e5054602SMark Johnston 
14326b6e2954SConrad Meyer 		error = dumper_insert(&dumper, conf->kda_iface, conf);
1433e5054602SMark Johnston 		if (encryptedkey != NULL) {
14346b6e2954SConrad Meyer 			explicit_bzero(encryptedkey,
14356b6e2954SConrad Meyer 			    conf->kda_encryptedkeysize);
1436e5054602SMark Johnston 			free(encryptedkey, M_TEMP);
1437e5054602SMark Johnston 		}
143864e7d18fSConrad Meyer 		if (error != 0)
143964e7d18fSConrad Meyer 			netdump_unconfigure();
1440e5054602SMark Johnston 		break;
1441e5054602SMark Johnston 	default:
14426b6e2954SConrad Meyer 		error = ENOTTY;
1443e5054602SMark Johnston 		break;
1444e5054602SMark Johnston 	}
14456b6e2954SConrad Meyer 	explicit_bzero(&kda_copy, sizeof(kda_copy));
14466b6e2954SConrad Meyer 	if (conf != NULL)
14476b6e2954SConrad Meyer 		explicit_bzero(conf, sizeof(*conf));
144864e7d18fSConrad Meyer 	NETDUMP_WUNLOCK();
1449e5054602SMark Johnston 	return (error);
1450e5054602SMark Johnston }
1451e5054602SMark Johnston 
1452e5054602SMark Johnston /*
1453e5054602SMark Johnston  * Called upon system init or kld load.  Initializes the netdump parameters to
1454e5054602SMark Johnston  * sane defaults (locates the first available NIC and uses the first IPv4 IP on
1455e5054602SMark Johnston  * that card as the client IP).  Leaves the server IP unconfigured.
1456e5054602SMark Johnston  *
1457e5054602SMark Johnston  * Parameters:
1458e5054602SMark Johnston  *	mod, Unused.
1459e5054602SMark Johnston  *	what, The module event type.
1460e5054602SMark Johnston  *	priv, Unused.
1461e5054602SMark Johnston  *
1462e5054602SMark Johnston  * Returns:
1463e5054602SMark Johnston  *	int, An errno value if an error occured, 0 otherwise.
1464e5054602SMark Johnston  */
1465e5054602SMark Johnston static int
1466e5054602SMark Johnston netdump_modevent(module_t mod __unused, int what, void *priv __unused)
1467e5054602SMark Johnston {
14686b6e2954SConrad Meyer 	struct diocskerneldump_arg conf;
1469e5054602SMark Johnston 	char *arg;
1470e5054602SMark Johnston 	int error;
1471e5054602SMark Johnston 
1472e5054602SMark Johnston 	error = 0;
1473e5054602SMark Johnston 	switch (what) {
1474e5054602SMark Johnston 	case MOD_LOAD:
1475e5054602SMark Johnston 		error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev,
1476e5054602SMark Johnston 		    &netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump");
1477e5054602SMark Johnston 		if (error != 0)
1478e5054602SMark Johnston 			return (error);
1479e5054602SMark Johnston 
148064e7d18fSConrad Meyer 		nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
148164e7d18fSConrad Meyer 		    netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
148264e7d18fSConrad Meyer 
1483e5054602SMark Johnston 		if ((arg = kern_getenv("net.dump.iface")) != NULL) {
14846b6e2954SConrad Meyer 			strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
1485e5054602SMark Johnston 			freeenv(arg);
1486e5054602SMark Johnston 
1487e5054602SMark Johnston 			if ((arg = kern_getenv("net.dump.server")) != NULL) {
14886b6e2954SConrad Meyer 				inet_aton(arg, &conf.kda_server.in4);
1489e5054602SMark Johnston 				freeenv(arg);
1490e5054602SMark Johnston 			}
1491e5054602SMark Johnston 			if ((arg = kern_getenv("net.dump.client")) != NULL) {
1492070e7bf9SConrad Meyer 				inet_aton(arg, &conf.kda_client.in4);
1493e5054602SMark Johnston 				freeenv(arg);
1494e5054602SMark Johnston 			}
1495e5054602SMark Johnston 			if ((arg = kern_getenv("net.dump.gateway")) != NULL) {
1496070e7bf9SConrad Meyer 				inet_aton(arg, &conf.kda_gateway.in4);
1497e5054602SMark Johnston 				freeenv(arg);
1498e5054602SMark Johnston 			}
14996b6e2954SConrad Meyer 			conf.kda_af = AF_INET;
1500e5054602SMark Johnston 
1501e5054602SMark Johnston 			/* Ignore errors; we print a message to the console. */
150264e7d18fSConrad Meyer 			NETDUMP_WLOCK();
1503b35822d9SMark Johnston 			(void)netdump_configure(&conf, curthread);
150464e7d18fSConrad Meyer 			NETDUMP_WUNLOCK();
1505e5054602SMark Johnston 		}
1506e5054602SMark Johnston 		break;
1507e5054602SMark Johnston 	case MOD_UNLOAD:
150864e7d18fSConrad Meyer 		NETDUMP_WLOCK();
150964e7d18fSConrad Meyer 		if (netdump_enabled()) {
1510e5054602SMark Johnston 			printf("netdump: disabling dump device for unload\n");
151164e7d18fSConrad Meyer 			netdump_unconfigure();
1512e5054602SMark Johnston 		}
151364e7d18fSConrad Meyer 		NETDUMP_WUNLOCK();
15146b6e2954SConrad Meyer 		destroy_dev(netdump_cdev);
151564e7d18fSConrad Meyer 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
151664e7d18fSConrad Meyer 		    nd_detach_cookie);
1517e5054602SMark Johnston 		break;
1518e5054602SMark Johnston 	default:
1519e5054602SMark Johnston 		error = EOPNOTSUPP;
1520e5054602SMark Johnston 		break;
1521e5054602SMark Johnston 	}
1522e5054602SMark Johnston 	return (error);
1523e5054602SMark Johnston }
1524e5054602SMark Johnston 
1525e5054602SMark Johnston static moduledata_t netdump_mod = {
1526e5054602SMark Johnston 	"netdump",
1527e5054602SMark Johnston 	netdump_modevent,
1528e5054602SMark Johnston 	NULL,
1529e5054602SMark Johnston };
1530e5054602SMark Johnston 
1531e5054602SMark Johnston MODULE_VERSION(netdump, 1);
1532e5054602SMark Johnston DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
1533