xref: /freebsd/sys/netinet/netdump/netdump_client.c (revision 6144b50f8bcbeaf2ba9351ed0cc0ea75915d15db)
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>
41b35822d9SMark Johnston #include <sys/jail.h>
42e5054602SMark Johnston #include <sys/kernel.h>
43e5054602SMark Johnston #include <sys/kerneldump.h>
44e5054602SMark Johnston #include <sys/mbuf.h>
45e5054602SMark Johnston #include <sys/module.h>
46e5054602SMark Johnston #include <sys/priv.h>
47e5054602SMark Johnston #include <sys/proc.h>
48e5054602SMark Johnston #include <sys/protosw.h>
49e5054602SMark Johnston #include <sys/socket.h>
50e5054602SMark Johnston #include <sys/sysctl.h>
51e5054602SMark Johnston #include <sys/systm.h>
52e5054602SMark Johnston 
53e5054602SMark Johnston #include <net/ethernet.h>
54e5054602SMark Johnston #include <net/if.h>
55e5054602SMark Johnston #include <net/if_arp.h>
56e5054602SMark Johnston #include <net/if_dl.h>
57e5054602SMark Johnston #include <net/if_types.h>
58e5054602SMark Johnston #include <net/if_var.h>
59e5054602SMark Johnston 
60e5054602SMark Johnston #include <netinet/in.h>
61e5054602SMark Johnston #include <netinet/in_systm.h>
62e5054602SMark Johnston #include <netinet/in_var.h>
63e5054602SMark Johnston #include <netinet/ip.h>
64e5054602SMark Johnston #include <netinet/ip_var.h>
65e5054602SMark Johnston #include <netinet/ip_options.h>
66e5054602SMark Johnston #include <netinet/udp.h>
67e5054602SMark Johnston #include <netinet/udp_var.h>
68e5054602SMark Johnston #include <netinet/netdump/netdump.h>
69e5054602SMark Johnston 
70e5054602SMark Johnston #include <machine/in_cksum.h>
71e5054602SMark Johnston #include <machine/pcb.h>
72e5054602SMark Johnston 
73e5054602SMark Johnston #define	NETDDEBUG(f, ...) do {						\
74e5054602SMark Johnston 	if (nd_debug > 0)						\
75e5054602SMark Johnston 		printf(("%s: " f), __func__, ## __VA_ARGS__);		\
76e5054602SMark Johnston } while (0)
77e5054602SMark Johnston #define	NETDDEBUG_IF(i, f, ...) do {					\
78e5054602SMark Johnston 	if (nd_debug > 0)						\
79e5054602SMark Johnston 		if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__);	\
80e5054602SMark Johnston } while (0)
81e5054602SMark Johnston #define	NETDDEBUGV(f, ...) do {						\
82e5054602SMark Johnston 	if (nd_debug > 1)						\
83e5054602SMark Johnston 		printf(("%s: " f), __func__, ## __VA_ARGS__);		\
84e5054602SMark Johnston } while (0)
85e5054602SMark Johnston #define	NETDDEBUGV_IF(i, f, ...) do {					\
86e5054602SMark Johnston 	if (nd_debug > 1)						\
87e5054602SMark Johnston 		if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__);	\
88e5054602SMark Johnston } while (0)
89e5054602SMark Johnston 
90e5054602SMark Johnston static int	 netdump_arp_gw(void);
91e5054602SMark Johnston static void	 netdump_cleanup(void);
926b6e2954SConrad Meyer static int	 netdump_configure(struct diocskerneldump_arg *,
936b6e2954SConrad Meyer 		    struct thread *);
94e5054602SMark Johnston static int	 netdump_dumper(void *priv __unused, void *virtual,
95e5054602SMark Johnston 		    vm_offset_t physical __unused, off_t offset, size_t length);
96e5054602SMark Johnston static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp,
97e5054602SMark Johnston 		    struct ether_addr dst, u_short etype);
98e5054602SMark Johnston static void	 netdump_handle_arp(struct mbuf **mb);
99e5054602SMark Johnston static void	 netdump_handle_ip(struct mbuf **mb);
100e5054602SMark Johnston static int	 netdump_ioctl(struct cdev *dev __unused, u_long cmd,
101e5054602SMark Johnston 		    caddr_t addr, int flags __unused, struct thread *td);
102e5054602SMark Johnston static int	 netdump_modevent(module_t mod, int type, void *priv);
103e5054602SMark Johnston static void	 netdump_network_poll(void);
104e5054602SMark Johnston static void	 netdump_pkt_in(struct ifnet *ifp, struct mbuf *m);
105e5054602SMark Johnston static int	 netdump_send(uint32_t type, off_t offset, unsigned char *data,
106e5054602SMark Johnston 		    uint32_t datalen);
107e5054602SMark Johnston static int	 netdump_send_arp(in_addr_t dst);
108e5054602SMark Johnston static int	 netdump_start(struct dumperinfo *di);
109e5054602SMark Johnston static int	 netdump_udp_output(struct mbuf *m);
110e5054602SMark Johnston 
111e5054602SMark Johnston /* Must be at least as big as the chunks dumpsys() gives us. */
112e5054602SMark Johnston static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
113e5054602SMark Johnston static uint32_t nd_seqno;
114e5054602SMark Johnston static int dump_failed, have_gw_mac;
115e5054602SMark Johnston static void (*drv_if_input)(struct ifnet *, struct mbuf *);
116e5054602SMark Johnston static int restore_gw_addr;
117e5054602SMark Johnston 
118e5054602SMark Johnston static uint64_t rcvd_acks;
119e5054602SMark Johnston CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT);
120e5054602SMark Johnston 
121e5054602SMark Johnston /* Configuration parameters. */
122*6144b50fSConrad Meyer static struct {
123*6144b50fSConrad Meyer 	char		 ndc_iface[IFNAMSIZ];
124*6144b50fSConrad Meyer 	union kd_ip	 ndc_server;
125*6144b50fSConrad Meyer 	union kd_ip	 ndc_client;
126*6144b50fSConrad Meyer 	union kd_ip	 ndc_gateway;
127*6144b50fSConrad Meyer 	uint8_t		 ndc_af;
128*6144b50fSConrad Meyer } nd_conf;
129*6144b50fSConrad Meyer #define	nd_server	nd_conf.ndc_server.in4
130*6144b50fSConrad Meyer #define	nd_client	nd_conf.ndc_client.in4
131*6144b50fSConrad Meyer #define	nd_gateway	nd_conf.ndc_gateway.in4
132e5054602SMark Johnston 
133e5054602SMark Johnston /* General dynamic settings. */
134e5054602SMark Johnston static struct ether_addr nd_gw_mac;
135e5054602SMark Johnston static struct ifnet *nd_ifp;
136e5054602SMark Johnston static uint16_t nd_server_port = NETDUMP_PORT;
137e5054602SMark Johnston 
138e5054602SMark Johnston FEATURE(netdump, "Netdump client support");
139e5054602SMark Johnston 
140e5054602SMark Johnston static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD, NULL,
141e5054602SMark Johnston     "netdump parameters");
142e5054602SMark Johnston 
143e5054602SMark Johnston static int nd_debug;
144e5054602SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
145e5054602SMark Johnston     &nd_debug, 0,
146e5054602SMark Johnston     "Debug message verbosity");
147e5054602SMark Johnston static int nd_enabled;
148e5054602SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD,
149e5054602SMark Johnston     &nd_enabled, 0,
150e5054602SMark Johnston     "netdump configuration status");
151e5054602SMark Johnston static char nd_path[MAXPATHLEN];
152e5054602SMark Johnston SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW,
153e5054602SMark Johnston     nd_path, sizeof(nd_path),
154e5054602SMark Johnston     "Server path for output files");
155da7d7778SMark Johnston static int nd_polls = 2000;
156da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN,
157da7d7778SMark Johnston     &nd_polls, 0,
158da7d7778SMark Johnston     "Number of times to poll before assuming packet loss (0.5ms per poll)");
159da7d7778SMark Johnston static int nd_retries = 10;
160da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN,
161da7d7778SMark Johnston     &nd_retries, 0,
162da7d7778SMark Johnston     "Number of retransmit attempts before giving up");
163da7d7778SMark Johnston static int nd_arp_retries = 3;
164da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN,
165da7d7778SMark Johnston     &nd_arp_retries, 0,
166da7d7778SMark Johnston     "Number of ARP attempts before giving up");
167e5054602SMark Johnston 
168e5054602SMark Johnston /*
169e5054602SMark Johnston  * Checks for netdump support on a network interface
170e5054602SMark Johnston  *
171e5054602SMark Johnston  * Parameters:
172e5054602SMark Johnston  *	ifp	The network interface that is being tested for support
173e5054602SMark Johnston  *
174e5054602SMark Johnston  * Returns:
175e5054602SMark Johnston  *	int	1 if the interface is supported, 0 if not
176e5054602SMark Johnston  */
177e5054602SMark Johnston static bool
178e5054602SMark Johnston netdump_supported_nic(struct ifnet *ifp)
179e5054602SMark Johnston {
180e5054602SMark Johnston 
181e5054602SMark Johnston 	return (ifp->if_netdump_methods != NULL);
182e5054602SMark Johnston }
183e5054602SMark Johnston 
184e5054602SMark Johnston /*-
185e5054602SMark Johnston  * Network specific primitives.
186e5054602SMark Johnston  * Following down the code they are divided ordered as:
187e5054602SMark Johnston  * - Packet buffer primitives
188e5054602SMark Johnston  * - Output primitives
189e5054602SMark Johnston  * - Input primitives
190e5054602SMark Johnston  * - Polling primitives
191e5054602SMark Johnston  */
192e5054602SMark Johnston 
193e5054602SMark Johnston /*
194e5054602SMark Johnston  * Handles creation of the ethernet header, then places outgoing packets into
195e5054602SMark Johnston  * the tx buffer for the NIC
196e5054602SMark Johnston  *
197e5054602SMark Johnston  * Parameters:
198e5054602SMark Johnston  *	m	The mbuf containing the packet to be sent (will be freed by
199e5054602SMark Johnston  *		this function or the NIC driver)
200e5054602SMark Johnston  *	ifp	The interface to send on
201e5054602SMark Johnston  *	dst	The destination ethernet address (source address will be looked
202e5054602SMark Johnston  *		up using ifp)
203e5054602SMark Johnston  *	etype	The ETHERTYPE_* value for the protocol that is being sent
204e5054602SMark Johnston  *
205e5054602SMark Johnston  * Returns:
206e5054602SMark Johnston  *	int	see errno.h, 0 for success
207e5054602SMark Johnston  */
208e5054602SMark Johnston static int
209e5054602SMark Johnston netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst,
210e5054602SMark Johnston     u_short etype)
211e5054602SMark Johnston {
212e5054602SMark Johnston 	struct ether_header *eh;
213e5054602SMark Johnston 
214e5054602SMark Johnston 	if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) ||
215e5054602SMark Johnston 	    (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) {
216e5054602SMark Johnston 		if_printf(ifp, "netdump_ether_output: interface isn't up\n");
217e5054602SMark Johnston 		m_freem(m);
218e5054602SMark Johnston 		return (ENETDOWN);
219e5054602SMark Johnston 	}
220e5054602SMark Johnston 
221e5054602SMark Johnston 	/* Fill in the ethernet header. */
222e5054602SMark Johnston 	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
223e5054602SMark Johnston 	if (m == NULL) {
224e5054602SMark Johnston 		printf("%s: out of mbufs\n", __func__);
225e5054602SMark Johnston 		return (ENOBUFS);
226e5054602SMark Johnston 	}
227e5054602SMark Johnston 	eh = mtod(m, struct ether_header *);
228e5054602SMark Johnston 	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
229e5054602SMark Johnston 	memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN);
230e5054602SMark Johnston 	eh->ether_type = htons(etype);
231e5054602SMark Johnston 	return ((ifp->if_netdump_methods->nd_transmit)(ifp, m));
232e5054602SMark Johnston }
233e5054602SMark Johnston 
234e5054602SMark Johnston /*
235e5054602SMark Johnston  * Unreliable transmission of an mbuf chain to the netdump server
236e5054602SMark Johnston  * Note: can't handle fragmentation; fails if the packet is larger than
237e5054602SMark Johnston  *	 nd_ifp->if_mtu after adding the UDP/IP headers
238e5054602SMark Johnston  *
239e5054602SMark Johnston  * Parameters:
240e5054602SMark Johnston  *	m	mbuf chain
241e5054602SMark Johnston  *
242e5054602SMark Johnston  * Returns:
243e5054602SMark Johnston  *	int	see errno.h, 0 for success
244e5054602SMark Johnston  */
245e5054602SMark Johnston static int
246e5054602SMark Johnston netdump_udp_output(struct mbuf *m)
247e5054602SMark Johnston {
248e5054602SMark Johnston 	struct udpiphdr *ui;
249e5054602SMark Johnston 	struct ip *ip;
250e5054602SMark Johnston 
251e5054602SMark Johnston 	MPASS(nd_ifp != NULL);
252e5054602SMark Johnston 
253e5054602SMark Johnston 	M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
254e5054602SMark Johnston 	if (m == NULL) {
255e5054602SMark Johnston 		printf("%s: out of mbufs\n", __func__);
256e5054602SMark Johnston 		return (ENOBUFS);
257e5054602SMark Johnston 	}
258e5054602SMark Johnston 
259e5054602SMark Johnston 	if (m->m_pkthdr.len > nd_ifp->if_mtu) {
260e5054602SMark Johnston 		printf("netdump_udp_output: Packet is too big: %d > MTU %u\n",
261e5054602SMark Johnston 		    m->m_pkthdr.len, nd_ifp->if_mtu);
262e5054602SMark Johnston 		m_freem(m);
263e5054602SMark Johnston 		return (ENOBUFS);
264e5054602SMark Johnston 	}
265e5054602SMark Johnston 
266e5054602SMark Johnston 	ui = mtod(m, struct udpiphdr *);
267e5054602SMark Johnston 	bzero(ui->ui_x1, sizeof(ui->ui_x1));
268e5054602SMark Johnston 	ui->ui_pr = IPPROTO_UDP;
269e5054602SMark Johnston 	ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip));
270e5054602SMark Johnston 	ui->ui_ulen = ui->ui_len;
271e5054602SMark Johnston 	ui->ui_src = nd_client;
272e5054602SMark Johnston 	ui->ui_dst = nd_server;
273e5054602SMark Johnston 	/* Use this src port so that the server can connect() the socket */
274e5054602SMark Johnston 	ui->ui_sport = htons(NETDUMP_ACKPORT);
275e5054602SMark Johnston 	ui->ui_dport = htons(nd_server_port);
276e5054602SMark Johnston 	ui->ui_sum = 0;
277e5054602SMark Johnston 	if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0)
278e5054602SMark Johnston 		ui->ui_sum = 0xffff;
279e5054602SMark Johnston 
280e5054602SMark Johnston 	ip = mtod(m, struct ip *);
281e5054602SMark Johnston 	ip->ip_v = IPVERSION;
282e5054602SMark Johnston 	ip->ip_hl = sizeof(struct ip) >> 2;
283e5054602SMark Johnston 	ip->ip_tos = 0;
284e5054602SMark Johnston 	ip->ip_len = htons(m->m_pkthdr.len);
285e5054602SMark Johnston 	ip->ip_id = 0;
286e5054602SMark Johnston 	ip->ip_off = htons(IP_DF);
287e5054602SMark Johnston 	ip->ip_ttl = 255;
288e5054602SMark Johnston 	ip->ip_sum = 0;
289e5054602SMark Johnston 	ip->ip_sum = in_cksum(m, sizeof(struct ip));
290e5054602SMark Johnston 
291e5054602SMark Johnston 	return (netdump_ether_output(m, nd_ifp, nd_gw_mac, ETHERTYPE_IP));
292e5054602SMark Johnston }
293e5054602SMark Johnston 
294e5054602SMark Johnston /*
295e5054602SMark Johnston  * Builds and sends a single ARP request to locate the server
296e5054602SMark Johnston  *
297e5054602SMark Johnston  * Return value:
298e5054602SMark Johnston  *	0 on success
299e5054602SMark Johnston  *	errno on error
300e5054602SMark Johnston  */
301e5054602SMark Johnston static int
302e5054602SMark Johnston netdump_send_arp(in_addr_t dst)
303e5054602SMark Johnston {
304e5054602SMark Johnston 	struct ether_addr bcast;
305e5054602SMark Johnston 	struct mbuf *m;
306e5054602SMark Johnston 	struct arphdr *ah;
307e5054602SMark Johnston 	int pktlen;
308e5054602SMark Johnston 
309e5054602SMark Johnston 	MPASS(nd_ifp != NULL);
310e5054602SMark Johnston 
311e5054602SMark Johnston 	/* Fill-up a broadcast address. */
312e5054602SMark Johnston 	memset(&bcast, 0xFF, ETHER_ADDR_LEN);
313e5054602SMark Johnston 	m = m_gethdr(M_NOWAIT, MT_DATA);
314e5054602SMark Johnston 	if (m == NULL) {
315e5054602SMark Johnston 		printf("netdump_send_arp: Out of mbufs\n");
316e5054602SMark Johnston 		return (ENOBUFS);
317e5054602SMark Johnston 	}
318e5054602SMark Johnston 	pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
319e5054602SMark Johnston 	m->m_len = pktlen;
320e5054602SMark Johnston 	m->m_pkthdr.len = pktlen;
321e5054602SMark Johnston 	MH_ALIGN(m, pktlen);
322e5054602SMark Johnston 	ah = mtod(m, struct arphdr *);
323e5054602SMark Johnston 	ah->ar_hrd = htons(ARPHRD_ETHER);
324e5054602SMark Johnston 	ah->ar_pro = htons(ETHERTYPE_IP);
325e5054602SMark Johnston 	ah->ar_hln = ETHER_ADDR_LEN;
326e5054602SMark Johnston 	ah->ar_pln = sizeof(struct in_addr);
327e5054602SMark Johnston 	ah->ar_op = htons(ARPOP_REQUEST);
328e5054602SMark Johnston 	memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN);
329e5054602SMark Johnston 	((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr;
330e5054602SMark Johnston 	bzero(ar_tha(ah), ETHER_ADDR_LEN);
331e5054602SMark Johnston 	((struct in_addr *)ar_tpa(ah))->s_addr = dst;
332e5054602SMark Johnston 	return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP));
333e5054602SMark Johnston }
334e5054602SMark Johnston 
335e5054602SMark Johnston /*
336e5054602SMark Johnston  * Sends ARP requests to locate the server and waits for a response.
337e5054602SMark Johnston  * We first try to ARP the server itself, and fall back to the provided
338e5054602SMark Johnston  * gateway if the server appears to be off-link.
339e5054602SMark Johnston  *
340e5054602SMark Johnston  * Return value:
341e5054602SMark Johnston  *	0 on success
342e5054602SMark Johnston  *	errno on error
343e5054602SMark Johnston  */
344e5054602SMark Johnston static int
345e5054602SMark Johnston netdump_arp_gw(void)
346e5054602SMark Johnston {
347e5054602SMark Johnston 	in_addr_t dst;
348e5054602SMark Johnston 	int error, polls, retries;
349e5054602SMark Johnston 
350e5054602SMark Johnston 	dst = nd_server.s_addr;
351e5054602SMark Johnston restart:
352e5054602SMark Johnston 	for (retries = 0; retries < nd_arp_retries && have_gw_mac == 0;
353e5054602SMark Johnston 	    retries++) {
354e5054602SMark Johnston 		error = netdump_send_arp(dst);
355e5054602SMark Johnston 		if (error != 0)
356e5054602SMark Johnston 			return (error);
357e5054602SMark Johnston 		for (polls = 0; polls < nd_polls && have_gw_mac == 0; polls++) {
358e5054602SMark Johnston 			netdump_network_poll();
359e5054602SMark Johnston 			DELAY(500);
360e5054602SMark Johnston 		}
361e5054602SMark Johnston 		if (have_gw_mac == 0)
362e5054602SMark Johnston 			printf("(ARP retry)");
363e5054602SMark Johnston 	}
364e5054602SMark Johnston 	if (have_gw_mac != 0)
365e5054602SMark Johnston 		return (0);
366e5054602SMark Johnston 	if (dst == nd_server.s_addr && nd_server.s_addr != nd_gateway.s_addr) {
367e5054602SMark Johnston 		printf("Failed to ARP server, trying to reach gateway...\n");
368e5054602SMark Johnston 		dst = nd_gateway.s_addr;
369e5054602SMark Johnston 		goto restart;
370e5054602SMark Johnston 	}
371e5054602SMark Johnston 
372e5054602SMark Johnston 	printf("\nARP timed out.\n");
373e5054602SMark Johnston 	return (ETIMEDOUT);
374e5054602SMark Johnston }
375e5054602SMark Johnston 
376e5054602SMark Johnston /*
377e5054602SMark Johnston  * Dummy free function for netdump clusters.
378e5054602SMark Johnston  */
379e5054602SMark Johnston static void
380e5054602SMark Johnston netdump_mbuf_free(struct mbuf *m __unused)
381e5054602SMark Johnston {
382e5054602SMark Johnston }
383e5054602SMark Johnston 
384e5054602SMark Johnston /*
385e5054602SMark Johnston  * Construct and reliably send a netdump packet.  May fail from a resource
386e5054602SMark Johnston  * shortage or extreme number of unacknowledged retransmissions.  Wait for
387e5054602SMark Johnston  * an acknowledgement before returning.  Splits packets into chunks small
388e5054602SMark Johnston  * enough to be sent without fragmentation (looks up the interface MTU)
389e5054602SMark Johnston  *
390e5054602SMark Johnston  * Parameters:
391e5054602SMark Johnston  *	type	netdump packet type (HERALD, FINISHED, or VMCORE)
392e5054602SMark Johnston  *	offset	vmcore data offset (bytes)
393e5054602SMark Johnston  *	data	vmcore data
394e5054602SMark Johnston  *	datalen	vmcore data size (bytes)
395e5054602SMark Johnston  *
396e5054602SMark Johnston  * Returns:
397e5054602SMark Johnston  *	int see errno.h, 0 for success
398e5054602SMark Johnston  */
399e5054602SMark Johnston static int
400e5054602SMark Johnston netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen)
401e5054602SMark Johnston {
402e5054602SMark Johnston 	struct netdump_msg_hdr *nd_msg_hdr;
403e5054602SMark Johnston 	struct mbuf *m, *m2;
404e5054602SMark Johnston 	uint64_t want_acks;
405e5054602SMark Johnston 	uint32_t i, pktlen, sent_so_far;
406e5054602SMark Johnston 	int retries, polls, error;
407e5054602SMark Johnston 
408e5054602SMark Johnston 	want_acks = 0;
409e5054602SMark Johnston 	rcvd_acks = 0;
410e5054602SMark Johnston 	retries = 0;
411e5054602SMark Johnston 
412e5054602SMark Johnston 	MPASS(nd_ifp != NULL);
413e5054602SMark Johnston 
414e5054602SMark Johnston retransmit:
415e5054602SMark Johnston 	/* Chunks can be too big to fit in packets. */
416e5054602SMark Johnston 	for (i = sent_so_far = 0; sent_so_far < datalen ||
417e5054602SMark Johnston 	    (i == 0 && datalen == 0); i++) {
418e5054602SMark Johnston 		pktlen = datalen - sent_so_far;
419e5054602SMark Johnston 
420e5054602SMark Johnston 		/* First bound: the packet structure. */
421e5054602SMark Johnston 		pktlen = min(pktlen, NETDUMP_DATASIZE);
422e5054602SMark Johnston 
423e5054602SMark Johnston 		/* Second bound: the interface MTU (assume no IP options). */
424e5054602SMark Johnston 		pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) -
425e5054602SMark Johnston 		    sizeof(struct netdump_msg_hdr));
426e5054602SMark Johnston 
427e5054602SMark Johnston 		/*
428e5054602SMark Johnston 		 * Check if it is retransmitting and this has been ACKed
429e5054602SMark Johnston 		 * already.
430e5054602SMark Johnston 		 */
431e5054602SMark Johnston 		if ((rcvd_acks & (1 << i)) != 0) {
432e5054602SMark Johnston 			sent_so_far += pktlen;
433e5054602SMark Johnston 			continue;
434e5054602SMark Johnston 		}
435e5054602SMark Johnston 
436e5054602SMark Johnston 		/*
437e5054602SMark Johnston 		 * Get and fill a header mbuf, then chain data as an extended
438e5054602SMark Johnston 		 * mbuf.
439e5054602SMark Johnston 		 */
440e5054602SMark Johnston 		m = m_gethdr(M_NOWAIT, MT_DATA);
441e5054602SMark Johnston 		if (m == NULL) {
442e5054602SMark Johnston 			printf("netdump_send: Out of mbufs\n");
443e5054602SMark Johnston 			return (ENOBUFS);
444e5054602SMark Johnston 		}
445e5054602SMark Johnston 		m->m_len = sizeof(struct netdump_msg_hdr);
446e5054602SMark Johnston 		m->m_pkthdr.len = sizeof(struct netdump_msg_hdr);
447e5054602SMark Johnston 		MH_ALIGN(m, sizeof(struct netdump_msg_hdr));
448e5054602SMark Johnston 		nd_msg_hdr = mtod(m, struct netdump_msg_hdr *);
449e5054602SMark Johnston 		nd_msg_hdr->mh_seqno = htonl(nd_seqno + i);
450e5054602SMark Johnston 		nd_msg_hdr->mh_type = htonl(type);
451e5054602SMark Johnston 		nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far);
452e5054602SMark Johnston 		nd_msg_hdr->mh_len = htonl(pktlen);
453e5054602SMark Johnston 		nd_msg_hdr->mh__pad = 0;
454e5054602SMark Johnston 
455e5054602SMark Johnston 		if (pktlen != 0) {
456e5054602SMark Johnston 			m2 = m_get(M_NOWAIT, MT_DATA);
457e5054602SMark Johnston 			if (m2 == NULL) {
458e5054602SMark Johnston 				m_freem(m);
459e5054602SMark Johnston 				printf("netdump_send: Out of mbufs\n");
460e5054602SMark Johnston 				return (ENOBUFS);
461e5054602SMark Johnston 			}
462e5054602SMark Johnston 			MEXTADD(m2, data + sent_so_far, pktlen,
463e5054602SMark Johnston 			    netdump_mbuf_free, NULL, NULL, 0, EXT_DISPOSABLE);
464e5054602SMark Johnston 			m2->m_len = pktlen;
465e5054602SMark Johnston 
466e5054602SMark Johnston 			m_cat(m, m2);
467e5054602SMark Johnston 			m->m_pkthdr.len += pktlen;
468e5054602SMark Johnston 		}
469e5054602SMark Johnston 		error = netdump_udp_output(m);
470e5054602SMark Johnston 		if (error != 0)
471e5054602SMark Johnston 			return (error);
472e5054602SMark Johnston 
473e5054602SMark Johnston 		/* Note that we're waiting for this packet in the bitfield. */
474e5054602SMark Johnston 		want_acks |= (1 << i);
475e5054602SMark Johnston 		sent_so_far += pktlen;
476e5054602SMark Johnston 	}
477e5054602SMark Johnston 	if (i >= NETDUMP_MAX_IN_FLIGHT)
478e5054602SMark Johnston 		printf("Warning: Sent more than %d packets (%d). "
479e5054602SMark Johnston 		    "Acknowledgements will fail unless the size of "
480e5054602SMark Johnston 		    "rcvd_acks/want_acks is increased.\n",
481e5054602SMark Johnston 		    NETDUMP_MAX_IN_FLIGHT, i);
482e5054602SMark Johnston 
483e5054602SMark Johnston 	/*
484e5054602SMark Johnston 	 * Wait for acks.  A *real* window would speed things up considerably.
485e5054602SMark Johnston 	 */
486e5054602SMark Johnston 	polls = 0;
487e5054602SMark Johnston 	while (rcvd_acks != want_acks) {
488e5054602SMark Johnston 		if (polls++ > nd_polls) {
489e5054602SMark Johnston 			if (retries++ > nd_retries)
490e5054602SMark Johnston 				return (ETIMEDOUT);
491e5054602SMark Johnston 			printf(". ");
492e5054602SMark Johnston 			goto retransmit;
493e5054602SMark Johnston 		}
494e5054602SMark Johnston 		netdump_network_poll();
495e5054602SMark Johnston 		DELAY(500);
496e5054602SMark Johnston 	}
497e5054602SMark Johnston 	nd_seqno += i;
498e5054602SMark Johnston 	return (0);
499e5054602SMark Johnston }
500e5054602SMark Johnston 
501e5054602SMark Johnston /*
502e5054602SMark Johnston  * Handler for IP packets: checks their sanity and then processes any netdump
503e5054602SMark Johnston  * ACK packets it finds.
504e5054602SMark Johnston  *
505e5054602SMark Johnston  * It needs to replicate partially the behaviour of ip_input() and
506e5054602SMark Johnston  * udp_input().
507e5054602SMark Johnston  *
508e5054602SMark Johnston  * Parameters:
509e5054602SMark Johnston  *	mb	a pointer to an mbuf * containing the packet received
510e5054602SMark Johnston  *		Updates *mb if m_pullup et al change the pointer
511e5054602SMark Johnston  *		Assumes the calling function will take care of freeing the mbuf
512e5054602SMark Johnston  */
513e5054602SMark Johnston static void
514e5054602SMark Johnston netdump_handle_ip(struct mbuf **mb)
515e5054602SMark Johnston {
516e5054602SMark Johnston 	struct ip *ip;
517e5054602SMark Johnston 	struct udpiphdr *udp;
518e5054602SMark Johnston 	struct netdump_ack *nd_ack;
519e5054602SMark Johnston 	struct mbuf *m;
520e5054602SMark Johnston 	int rcv_ackno;
521e5054602SMark Johnston 	unsigned short hlen;
522e5054602SMark Johnston 
523e5054602SMark Johnston 	/* IP processing. */
524e5054602SMark Johnston 	m = *mb;
525e5054602SMark Johnston 	if (m->m_pkthdr.len < sizeof(struct ip)) {
526e5054602SMark Johnston 		NETDDEBUG("dropping packet too small for IP header\n");
527e5054602SMark Johnston 		return;
528e5054602SMark Johnston 	}
529e5054602SMark Johnston 	if (m->m_len < sizeof(struct ip)) {
530e5054602SMark Johnston 		m = m_pullup(m, sizeof(struct ip));
531e5054602SMark Johnston 		*mb = m;
532e5054602SMark Johnston 		if (m == NULL) {
533e5054602SMark Johnston 			NETDDEBUG("m_pullup failed\n");
534e5054602SMark Johnston 			return;
535e5054602SMark Johnston 		}
536e5054602SMark Johnston 	}
537e5054602SMark Johnston 	ip = mtod(m, struct ip *);
538e5054602SMark Johnston 
539e5054602SMark Johnston 	/* IP version. */
540e5054602SMark Johnston 	if (ip->ip_v != IPVERSION) {
541e5054602SMark Johnston 		NETDDEBUG("bad IP version %d\n", ip->ip_v);
542e5054602SMark Johnston 		return;
543e5054602SMark Johnston 	}
544e5054602SMark Johnston 
545e5054602SMark Johnston 	/* Header length. */
546e5054602SMark Johnston 	hlen = ip->ip_hl << 2;
547e5054602SMark Johnston 	if (hlen < sizeof(struct ip)) {
548e5054602SMark Johnston 		NETDDEBUG("bad IP header length (%hu)\n", hlen);
549e5054602SMark Johnston 		return;
550e5054602SMark Johnston 	}
551e5054602SMark Johnston 	if (hlen > m->m_len) {
552e5054602SMark Johnston 		m = m_pullup(m, hlen);
553e5054602SMark Johnston 		*mb = m;
554e5054602SMark Johnston 		if (m == NULL) {
555e5054602SMark Johnston 			NETDDEBUG("m_pullup failed\n");
556e5054602SMark Johnston 			return;
557e5054602SMark Johnston 		}
558e5054602SMark Johnston 		ip = mtod(m, struct ip *);
559e5054602SMark Johnston 	}
560e5054602SMark Johnston 	/* Ignore packets with IP options. */
561e5054602SMark Johnston 	if (hlen > sizeof(struct ip)) {
562e5054602SMark Johnston 		NETDDEBUG("drop packet with IP options\n");
563e5054602SMark Johnston 		return;
564e5054602SMark Johnston 	}
565e5054602SMark Johnston 
566e5054602SMark Johnston #ifdef INVARIANTS
5676c1c6ae5SRodney W. Grimes 	if ((IN_LOOPBACK(ntohl(ip->ip_dst.s_addr)) ||
5686c1c6ae5SRodney W. Grimes 	    IN_LOOPBACK(ntohl(ip->ip_src.s_addr))) &&
569e5054602SMark Johnston 	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
570e5054602SMark Johnston 		NETDDEBUG("Bad IP header (RFC1122)\n");
571e5054602SMark Johnston 		return;
572e5054602SMark Johnston 	}
573e5054602SMark Johnston #endif
574e5054602SMark Johnston 
575e5054602SMark Johnston 	/* Checksum. */
576e5054602SMark Johnston 	if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) {
577e5054602SMark Johnston 		if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) {
578e5054602SMark Johnston 			NETDDEBUG("bad IP checksum\n");
579e5054602SMark Johnston 			return;
580e5054602SMark Johnston 		}
581e5054602SMark Johnston 	} else {
582e5054602SMark Johnston 		/* XXX */ ;
583e5054602SMark Johnston 	}
584e5054602SMark Johnston 
585e5054602SMark Johnston 	/* Convert fields to host byte order. */
586e5054602SMark Johnston 	ip->ip_len = ntohs(ip->ip_len);
587e5054602SMark Johnston 	if (ip->ip_len < hlen) {
588e5054602SMark Johnston 		NETDDEBUG("IP packet smaller (%hu) than header (%hu)\n",
589e5054602SMark Johnston 		    ip->ip_len, hlen);
590e5054602SMark Johnston 		return;
591e5054602SMark Johnston 	}
592e5054602SMark Johnston 	if (m->m_pkthdr.len < ip->ip_len) {
593e5054602SMark Johnston 		NETDDEBUG("IP packet bigger (%hu) than ethernet packet (%d)\n",
594e5054602SMark Johnston 		    ip->ip_len, m->m_pkthdr.len);
595e5054602SMark Johnston 		return;
596e5054602SMark Johnston 	}
597e5054602SMark Johnston 	if (m->m_pkthdr.len > ip->ip_len) {
598e5054602SMark Johnston 
599e5054602SMark Johnston 		/* Truncate the packet to the IP length. */
600e5054602SMark Johnston 		if (m->m_len == m->m_pkthdr.len) {
601e5054602SMark Johnston 			m->m_len = ip->ip_len;
602e5054602SMark Johnston 			m->m_pkthdr.len = ip->ip_len;
603e5054602SMark Johnston 		} else
604e5054602SMark Johnston 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
605e5054602SMark Johnston 	}
606e5054602SMark Johnston 
607e5054602SMark Johnston 	ip->ip_off = ntohs(ip->ip_off);
608e5054602SMark Johnston 
609e5054602SMark Johnston 	/* Check that the source is the server's IP. */
610e5054602SMark Johnston 	if (ip->ip_src.s_addr != nd_server.s_addr) {
611e5054602SMark Johnston 		NETDDEBUG("drop packet not from server (from 0x%x)\n",
612e5054602SMark Johnston 		    ip->ip_src.s_addr);
613e5054602SMark Johnston 		return;
614e5054602SMark Johnston 	}
615e5054602SMark Johnston 
616e5054602SMark Johnston 	/* Check if the destination IP is ours. */
617e5054602SMark Johnston 	if (ip->ip_dst.s_addr != nd_client.s_addr) {
618e5054602SMark Johnston 		NETDDEBUGV("drop packet not to our IP\n");
619e5054602SMark Johnston 		return;
620e5054602SMark Johnston 	}
621e5054602SMark Johnston 
622e5054602SMark Johnston 	if (ip->ip_p != IPPROTO_UDP) {
623e5054602SMark Johnston 		NETDDEBUG("drop non-UDP packet\n");
624e5054602SMark Johnston 		return;
625e5054602SMark Johnston 	}
626e5054602SMark Johnston 
627e5054602SMark Johnston 	/* Do not deal with fragments. */
628e5054602SMark Johnston 	if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) {
629e5054602SMark Johnston 		NETDDEBUG("drop fragmented packet\n");
630e5054602SMark Johnston 		return;
631e5054602SMark Johnston 	}
632e5054602SMark Johnston 
633e5054602SMark Johnston 	/* UDP custom is to have packet length not include IP header. */
634e5054602SMark Johnston 	ip->ip_len -= hlen;
635e5054602SMark Johnston 
636e5054602SMark Johnston 	/* UDP processing. */
637e5054602SMark Johnston 
638e5054602SMark Johnston 	/* Get IP and UDP headers together, along with the netdump packet. */
639e5054602SMark Johnston 	if (m->m_pkthdr.len <
640e5054602SMark Johnston 	    sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
641e5054602SMark Johnston 		NETDDEBUG("ignoring small packet\n");
642e5054602SMark Johnston 		return;
643e5054602SMark Johnston 	}
644e5054602SMark Johnston 	if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
645e5054602SMark Johnston 		m = m_pullup(m, sizeof(struct udpiphdr) +
646e5054602SMark Johnston 		    sizeof(struct netdump_ack));
647e5054602SMark Johnston 		*mb = m;
648e5054602SMark Johnston 		if (m == NULL) {
649e5054602SMark Johnston 			NETDDEBUG("m_pullup failed\n");
650e5054602SMark Johnston 			return;
651e5054602SMark Johnston 		}
652e5054602SMark Johnston 	}
653e5054602SMark Johnston 	udp = mtod(m, struct udpiphdr *);
654e5054602SMark Johnston 
655e5054602SMark Johnston 	if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) {
656e5054602SMark Johnston 		NETDDEBUG("not on the netdump port.\n");
657e5054602SMark Johnston 		return;
658e5054602SMark Johnston 	}
659e5054602SMark Johnston 
660e5054602SMark Johnston 	/* Netdump processing. */
661e5054602SMark Johnston 
662e5054602SMark Johnston 	/*
663e5054602SMark Johnston 	 * Packet is meant for us.  Extract the ack sequence number and the
664e5054602SMark Johnston 	 * port number if necessary.
665e5054602SMark Johnston 	 */
666e5054602SMark Johnston 	nd_ack = (struct netdump_ack *)(mtod(m, caddr_t) +
667e5054602SMark Johnston 	    sizeof(struct udpiphdr));
668e5054602SMark Johnston 	rcv_ackno = ntohl(nd_ack->na_seqno);
669e5054602SMark Johnston 	if (nd_server_port == NETDUMP_PORT)
670e5054602SMark Johnston 		nd_server_port = ntohs(udp->ui_u.uh_sport);
671e5054602SMark Johnston 	if (rcv_ackno >= nd_seqno + NETDUMP_MAX_IN_FLIGHT)
672e5054602SMark Johnston 		printf("%s: ACK %d too far in future!\n", __func__, rcv_ackno);
673e5054602SMark Johnston 	else if (rcv_ackno >= nd_seqno) {
674e5054602SMark Johnston 		/* We're interested in this ack. Record it. */
675e5054602SMark Johnston 		rcvd_acks |= 1 << (rcv_ackno - nd_seqno);
676e5054602SMark Johnston 	}
677e5054602SMark Johnston }
678e5054602SMark Johnston 
679e5054602SMark Johnston /*
680e5054602SMark Johnston  * Handler for ARP packets: checks their sanity and then
681e5054602SMark Johnston  * 1. If the ARP is a request for our IP, respond with our MAC address
682e5054602SMark Johnston  * 2. If the ARP is a response from our server, record its MAC address
683e5054602SMark Johnston  *
684e5054602SMark Johnston  * It needs to replicate partially the behaviour of arpintr() and
685e5054602SMark Johnston  * in_arpinput().
686e5054602SMark Johnston  *
687e5054602SMark Johnston  * Parameters:
688e5054602SMark Johnston  *	mb	a pointer to an mbuf * containing the packet received
689e5054602SMark Johnston  *		Updates *mb if m_pullup et al change the pointer
690e5054602SMark Johnston  *		Assumes the calling function will take care of freeing the mbuf
691e5054602SMark Johnston  */
692e5054602SMark Johnston static void
693e5054602SMark Johnston netdump_handle_arp(struct mbuf **mb)
694e5054602SMark Johnston {
695e5054602SMark Johnston 	char buf[INET_ADDRSTRLEN];
696e5054602SMark Johnston 	struct in_addr isaddr, itaddr, myaddr;
697e5054602SMark Johnston 	struct ether_addr dst;
698e5054602SMark Johnston 	struct mbuf *m;
699e5054602SMark Johnston 	struct arphdr *ah;
700e5054602SMark Johnston 	struct ifnet *ifp;
701e5054602SMark Johnston 	uint8_t *enaddr;
702e5054602SMark Johnston 	int req_len, op;
703e5054602SMark Johnston 
704e5054602SMark Johnston 	m = *mb;
705e5054602SMark Johnston 	ifp = m->m_pkthdr.rcvif;
706e5054602SMark Johnston 	if (m->m_len < sizeof(struct arphdr)) {
707e5054602SMark Johnston 		m = m_pullup(m, sizeof(struct arphdr));
708e5054602SMark Johnston 		*mb = m;
709e5054602SMark Johnston 		if (m == NULL) {
710e5054602SMark Johnston 			NETDDEBUG("runt packet: m_pullup failed\n");
711e5054602SMark Johnston 			return;
712e5054602SMark Johnston 		}
713e5054602SMark Johnston 	}
714e5054602SMark Johnston 
715e5054602SMark Johnston 	ah = mtod(m, struct arphdr *);
716e5054602SMark Johnston 	if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) {
717e5054602SMark Johnston 		NETDDEBUG("unknown hardware address 0x%2D)\n",
718e5054602SMark Johnston 		    (unsigned char *)&ah->ar_hrd, "");
719e5054602SMark Johnston 		return;
720e5054602SMark Johnston 	}
721e5054602SMark Johnston 	if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
722e5054602SMark Johnston 		NETDDEBUG("drop ARP for unknown protocol %d\n",
723e5054602SMark Johnston 		    ntohs(ah->ar_pro));
724e5054602SMark Johnston 		return;
725e5054602SMark Johnston 	}
726e5054602SMark Johnston 	req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
727e5054602SMark Johnston 	if (m->m_len < req_len) {
728e5054602SMark Johnston 		m = m_pullup(m, req_len);
729e5054602SMark Johnston 		*mb = m;
730e5054602SMark Johnston 		if (m == NULL) {
731e5054602SMark Johnston 			NETDDEBUG("runt packet: m_pullup failed\n");
732e5054602SMark Johnston 			return;
733e5054602SMark Johnston 		}
734e5054602SMark Johnston 	}
735e5054602SMark Johnston 	ah = mtod(m, struct arphdr *);
736e5054602SMark Johnston 
737e5054602SMark Johnston 	op = ntohs(ah->ar_op);
738e5054602SMark Johnston 	memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
739e5054602SMark Johnston 	memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
740e5054602SMark Johnston 	enaddr = (uint8_t *)IF_LLADDR(ifp);
741e5054602SMark Johnston 	myaddr = nd_client;
742e5054602SMark Johnston 
743e5054602SMark Johnston 	if (memcmp(ar_sha(ah), enaddr, ifp->if_addrlen) == 0) {
744e5054602SMark Johnston 		NETDDEBUG("ignoring ARP from myself\n");
745e5054602SMark Johnston 		return;
746e5054602SMark Johnston 	}
747e5054602SMark Johnston 
748e5054602SMark Johnston 	if (isaddr.s_addr == nd_client.s_addr) {
749e5054602SMark Johnston 		printf("%s: %*D is using my IP address %s!\n", __func__,
750e5054602SMark Johnston 		    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
751e5054602SMark Johnston 		    inet_ntoa_r(isaddr, buf));
752e5054602SMark Johnston 		return;
753e5054602SMark Johnston 	}
754e5054602SMark Johnston 
755e5054602SMark Johnston 	if (memcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen) == 0) {
756e5054602SMark Johnston 		NETDDEBUG("ignoring ARP from broadcast address\n");
757e5054602SMark Johnston 		return;
758e5054602SMark Johnston 	}
759e5054602SMark Johnston 
760e5054602SMark Johnston 	if (op == ARPOP_REPLY) {
761e5054602SMark Johnston 		if (isaddr.s_addr != nd_gateway.s_addr &&
762e5054602SMark Johnston 		    isaddr.s_addr != nd_server.s_addr) {
763e5054602SMark Johnston 			inet_ntoa_r(isaddr, buf);
764e5054602SMark Johnston 			NETDDEBUG(
765e5054602SMark Johnston 			    "ignoring ARP reply from %s (not netdump server)\n",
766e5054602SMark Johnston 			    buf);
767e5054602SMark Johnston 			return;
768e5054602SMark Johnston 		}
769e5054602SMark Johnston 		memcpy(nd_gw_mac.octet, ar_sha(ah),
770e5054602SMark Johnston 		    min(ah->ar_hln, ETHER_ADDR_LEN));
771e5054602SMark Johnston 		have_gw_mac = 1;
772e5054602SMark Johnston 		NETDDEBUG("got server MAC address %6D\n", nd_gw_mac.octet, ":");
773e5054602SMark Johnston 		return;
774e5054602SMark Johnston 	}
775e5054602SMark Johnston 
776e5054602SMark Johnston 	if (op != ARPOP_REQUEST) {
777e5054602SMark Johnston 		NETDDEBUG("ignoring ARP non-request/reply\n");
778e5054602SMark Johnston 		return;
779e5054602SMark Johnston 	}
780e5054602SMark Johnston 
781e5054602SMark Johnston 	if (itaddr.s_addr != nd_client.s_addr) {
782e5054602SMark Johnston 		NETDDEBUG("ignoring ARP not to our IP\n");
783e5054602SMark Johnston 		return;
784e5054602SMark Johnston 	}
785e5054602SMark Johnston 
786e5054602SMark Johnston 	memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
787e5054602SMark Johnston 	memcpy(ar_sha(ah), enaddr, ah->ar_hln);
788e5054602SMark Johnston 	memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln);
789e5054602SMark Johnston 	memcpy(ar_spa(ah), &itaddr, ah->ar_pln);
790e5054602SMark Johnston 	ah->ar_op = htons(ARPOP_REPLY);
791e5054602SMark Johnston 	ah->ar_pro = htons(ETHERTYPE_IP);
792e5054602SMark Johnston 	m->m_flags &= ~(M_BCAST|M_MCAST);
793e5054602SMark Johnston 	m->m_len = arphdr_len(ah);
794e5054602SMark Johnston 	m->m_pkthdr.len = m->m_len;
795e5054602SMark Johnston 
796e5054602SMark Johnston 	memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
797e5054602SMark Johnston 	netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
798e5054602SMark Johnston 	*mb = NULL;
799e5054602SMark Johnston }
800e5054602SMark Johnston 
801e5054602SMark Johnston /*
802e5054602SMark Johnston  * Handler for incoming packets directly from the network adapter
803e5054602SMark Johnston  * Identifies the packet type (IP or ARP) and passes it along to one of the
804e5054602SMark Johnston  * helper functions netdump_handle_ip or netdump_handle_arp.
805e5054602SMark Johnston  *
806e5054602SMark Johnston  * It needs to replicate partially the behaviour of ether_input() and
807e5054602SMark Johnston  * ether_demux().
808e5054602SMark Johnston  *
809e5054602SMark Johnston  * Parameters:
810e5054602SMark Johnston  *	ifp	the interface the packet came from (should be nd_ifp)
811e5054602SMark Johnston  *	m	an mbuf containing the packet received
812e5054602SMark Johnston  */
813e5054602SMark Johnston static void
814e5054602SMark Johnston netdump_pkt_in(struct ifnet *ifp, struct mbuf *m)
815e5054602SMark Johnston {
816e5054602SMark Johnston 	struct ifreq ifr;
817e5054602SMark Johnston 	struct ether_header *eh;
818e5054602SMark Johnston 	u_short etype;
819e5054602SMark Johnston 
820e5054602SMark Johnston 	/* Ethernet processing. */
821e5054602SMark Johnston 	if ((m->m_flags & M_PKTHDR) == 0) {
822e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "discard frame without packet header\n");
823e5054602SMark Johnston 		goto done;
824e5054602SMark Johnston 	}
825e5054602SMark Johnston 	if (m->m_len < ETHER_HDR_LEN) {
826e5054602SMark Johnston 		NETDDEBUG_IF(ifp,
827e5054602SMark Johnston 	    "discard frame without leading eth header (len %u pktlen %u)\n",
828e5054602SMark Johnston 		    m->m_len, m->m_pkthdr.len);
829e5054602SMark Johnston 		goto done;
830e5054602SMark Johnston 	}
831e5054602SMark Johnston 	if ((m->m_flags & M_HASFCS) != 0) {
832e5054602SMark Johnston 		m_adj(m, -ETHER_CRC_LEN);
833e5054602SMark Johnston 		m->m_flags &= ~M_HASFCS;
834e5054602SMark Johnston 	}
835e5054602SMark Johnston 	eh = mtod(m, struct ether_header *);
836e5054602SMark Johnston 	etype = ntohs(eh->ether_type);
837e5054602SMark Johnston 	if ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) {
838e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "ignoring vlan packets\n");
839e5054602SMark Johnston 		goto done;
840e5054602SMark Johnston 	}
841e5054602SMark Johnston 	if (if_gethwaddr(ifp, &ifr) != 0) {
842e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "failed to get hw addr for interface\n");
843e5054602SMark Johnston 		goto done;
844e5054602SMark Johnston 	}
845e5054602SMark Johnston 	if (memcmp(ifr.ifr_addr.sa_data, eh->ether_dhost,
846e5054602SMark Johnston 	    ETHER_ADDR_LEN) != 0) {
847e5054602SMark Johnston 		NETDDEBUG_IF(ifp,
848e5054602SMark Johnston 		    "discard frame with incorrect destination addr\n");
849e5054602SMark Johnston 		goto done;
850e5054602SMark Johnston 	}
851e5054602SMark Johnston 
852e5054602SMark Johnston 	/* Done ethernet processing. Strip off the ethernet header. */
853e5054602SMark Johnston 	m_adj(m, ETHER_HDR_LEN);
854e5054602SMark Johnston 	switch (etype) {
855e5054602SMark Johnston 	case ETHERTYPE_ARP:
856e5054602SMark Johnston 		netdump_handle_arp(&m);
857e5054602SMark Johnston 		break;
858e5054602SMark Johnston 	case ETHERTYPE_IP:
859e5054602SMark Johnston 		netdump_handle_ip(&m);
860e5054602SMark Johnston 		break;
861e5054602SMark Johnston 	default:
862e5054602SMark Johnston 		NETDDEBUG_IF(ifp, "dropping unknown ethertype %hu\n", etype);
863e5054602SMark Johnston 		break;
864e5054602SMark Johnston 	}
865e5054602SMark Johnston done:
866e5054602SMark Johnston 	if (m != NULL)
867e5054602SMark Johnston 		m_freem(m);
868e5054602SMark Johnston }
869e5054602SMark Johnston 
870e5054602SMark Johnston /*
871e5054602SMark Johnston  * After trapping, instead of assuming that most of the network stack is sane,
872e5054602SMark Johnston  * we just poll the driver directly for packets.
873e5054602SMark Johnston  */
874e5054602SMark Johnston static void
875e5054602SMark Johnston netdump_network_poll(void)
876e5054602SMark Johnston {
877e5054602SMark Johnston 
878e5054602SMark Johnston 	MPASS(nd_ifp != NULL);
879e5054602SMark Johnston 
880e5054602SMark Johnston 	nd_ifp->if_netdump_methods->nd_poll(nd_ifp, 1000);
881e5054602SMark Johnston }
882e5054602SMark Johnston 
883e5054602SMark Johnston /*-
884e5054602SMark Johnston  * Dumping specific primitives.
885e5054602SMark Johnston  */
886e5054602SMark Johnston 
887e5054602SMark Johnston /*
888e5054602SMark Johnston  * Callback from dumpsys() to dump a chunk of memory.
889e5054602SMark Johnston  * Copies it out to our static buffer then sends it across the network.
890e5054602SMark Johnston  * Detects the initial KDH and makes sure it is given a special packet type.
891e5054602SMark Johnston  *
892e5054602SMark Johnston  * Parameters:
893e5054602SMark Johnston  *	priv	 Unused. Optional private pointer.
894e5054602SMark Johnston  *	virtual  Virtual address (where to read the data from)
895e5054602SMark Johnston  *	physical Unused. Physical memory address.
896e5054602SMark Johnston  *	offset	 Offset from start of core file
897e5054602SMark Johnston  *	length	 Data length
898e5054602SMark Johnston  *
899e5054602SMark Johnston  * Return value:
900e5054602SMark Johnston  *	0 on success
901e5054602SMark Johnston  *	errno on error
902e5054602SMark Johnston  */
903e5054602SMark Johnston static int
904e5054602SMark Johnston netdump_dumper(void *priv __unused, void *virtual,
905e5054602SMark Johnston     vm_offset_t physical __unused, off_t offset, size_t length)
906e5054602SMark Johnston {
907e5054602SMark Johnston 	int error;
908e5054602SMark Johnston 
909e5054602SMark Johnston 	NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n",
910e5054602SMark Johnston 	    virtual, (uintmax_t)offset, length);
911e5054602SMark Johnston 
912e5054602SMark Johnston 	if (virtual == NULL) {
913e5054602SMark Johnston 		if (dump_failed != 0)
914e5054602SMark Johnston 			printf("failed to dump the kernel core\n");
915e5054602SMark Johnston 		else if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0)
916e5054602SMark Johnston 			printf("failed to close the transaction\n");
917e5054602SMark Johnston 		else
918e5054602SMark Johnston 			printf("\nnetdump finished.\n");
919e5054602SMark Johnston 		netdump_cleanup();
920e5054602SMark Johnston 		return (0);
921e5054602SMark Johnston 	}
922e5054602SMark Johnston 	if (length > sizeof(nd_buf))
923e5054602SMark Johnston 		return (ENOSPC);
924e5054602SMark Johnston 
925e5054602SMark Johnston 	memmove(nd_buf, virtual, length);
926e5054602SMark Johnston 	error = netdump_send(NETDUMP_VMCORE, offset, nd_buf, length);
927e5054602SMark Johnston 	if (error != 0) {
928e5054602SMark Johnston 		dump_failed = 1;
929e5054602SMark Johnston 		return (error);
930e5054602SMark Johnston 	}
931e5054602SMark Johnston 	return (0);
932e5054602SMark Johnston }
933e5054602SMark Johnston 
934e5054602SMark Johnston /*
935e5054602SMark Johnston  * Perform any initalization needed prior to transmitting the kernel core.
936e5054602SMark Johnston  */
937e5054602SMark Johnston static int
938e5054602SMark Johnston netdump_start(struct dumperinfo *di)
939e5054602SMark Johnston {
940e5054602SMark Johnston 	char *path;
941e5054602SMark Johnston 	char buf[INET_ADDRSTRLEN];
942e5054602SMark Johnston 	uint32_t len;
943e5054602SMark Johnston 	int error;
944e5054602SMark Johnston 
945e5054602SMark Johnston 	error = 0;
946e5054602SMark Johnston 
947e5054602SMark Johnston 	/* Check if the dumping is allowed to continue. */
948e5054602SMark Johnston 	if (nd_enabled == 0)
949e5054602SMark Johnston 		return (EINVAL);
950e5054602SMark Johnston 
951e5054602SMark Johnston 	if (panicstr == NULL) {
952e5054602SMark Johnston 		printf(
953e5054602SMark Johnston 		    "netdump_start: netdump may only be used after a panic\n");
954e5054602SMark Johnston 		return (EINVAL);
955e5054602SMark Johnston 	}
956e5054602SMark Johnston 
957e5054602SMark Johnston 	MPASS(nd_ifp != NULL);
958e5054602SMark Johnston 
959e5054602SMark Johnston 	if (nd_server.s_addr == INADDR_ANY) {
960e5054602SMark Johnston 		printf("netdump_start: can't netdump; no server IP given\n");
961e5054602SMark Johnston 		return (EINVAL);
962e5054602SMark Johnston 	}
963e5054602SMark Johnston 	if (nd_client.s_addr == INADDR_ANY) {
964e5054602SMark Johnston 		printf("netdump_start: can't netdump; no client IP given\n");
965e5054602SMark Johnston 		return (EINVAL);
966e5054602SMark Johnston 	}
967e5054602SMark Johnston 
968e5054602SMark Johnston 	/* We start dumping at offset 0. */
969e5054602SMark Johnston 	di->dumpoff = 0;
970e5054602SMark Johnston 
971e5054602SMark Johnston 	nd_seqno = 1;
972e5054602SMark Johnston 
973e5054602SMark Johnston 	/*
974e5054602SMark Johnston 	 * nd_server_port could have switched after the first ack the
975e5054602SMark Johnston 	 * first time it gets called.  Adjust it accordingly.
976e5054602SMark Johnston 	 */
977e5054602SMark Johnston 	nd_server_port = NETDUMP_PORT;
978e5054602SMark Johnston 
979e5054602SMark Johnston 	/* Switch to the netdump mbuf zones. */
980e5054602SMark Johnston 	netdump_mbuf_dump();
981e5054602SMark Johnston 
982e5054602SMark Johnston 	nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_START);
983e5054602SMark Johnston 
984e5054602SMark Johnston 	/* Make the card use *our* receive callback. */
985e5054602SMark Johnston 	drv_if_input = nd_ifp->if_input;
986e5054602SMark Johnston 	nd_ifp->if_input = netdump_pkt_in;
987e5054602SMark Johnston 
988e5054602SMark Johnston 	if (nd_gateway.s_addr == INADDR_ANY) {
989e5054602SMark Johnston 		restore_gw_addr = 1;
990e5054602SMark Johnston 		nd_gateway.s_addr = nd_server.s_addr;
991e5054602SMark Johnston 	}
992e5054602SMark Johnston 
993e5054602SMark Johnston 	printf("netdump in progress. searching for server...\n");
994e5054602SMark Johnston 	if (netdump_arp_gw()) {
995e5054602SMark Johnston 		printf("failed to locate server MAC address\n");
996e5054602SMark Johnston 		error = EINVAL;
997e5054602SMark Johnston 		goto trig_abort;
998e5054602SMark Johnston 	}
999e5054602SMark Johnston 
1000e5054602SMark Johnston 	if (nd_path[0] != '\0') {
1001e5054602SMark Johnston 		path = nd_path;
1002e5054602SMark Johnston 		len = strlen(path) + 1;
1003e5054602SMark Johnston 	} else {
1004e5054602SMark Johnston 		path = NULL;
1005e5054602SMark Johnston 		len = 0;
1006e5054602SMark Johnston 	}
1007e5054602SMark Johnston 	if (netdump_send(NETDUMP_HERALD, 0, path, len) != 0) {
1008e5054602SMark Johnston 		printf("failed to contact netdump server\n");
1009e5054602SMark Johnston 		error = EINVAL;
1010e5054602SMark Johnston 		goto trig_abort;
1011e5054602SMark Johnston 	}
1012e5054602SMark Johnston 	printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf),
1013e5054602SMark Johnston 	    nd_gw_mac.octet, ":");
1014e5054602SMark Johnston 	return (0);
1015e5054602SMark Johnston 
1016e5054602SMark Johnston trig_abort:
1017e5054602SMark Johnston 	netdump_cleanup();
1018e5054602SMark Johnston 	return (error);
1019e5054602SMark Johnston }
1020e5054602SMark Johnston 
1021e5054602SMark Johnston static int
1022e5054602SMark Johnston netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh,
1023e5054602SMark Johnston     void *key, uint32_t keysize)
1024e5054602SMark Johnston {
1025e5054602SMark Johnston 	int error;
1026e5054602SMark Johnston 
1027e5054602SMark Johnston 	memcpy(nd_buf, kdh, sizeof(*kdh));
1028e5054602SMark Johnston 	error = netdump_send(NETDUMP_KDH, 0, nd_buf, sizeof(*kdh));
1029e5054602SMark Johnston 	if (error == 0 && keysize > 0) {
1030e5054602SMark Johnston 		if (keysize > sizeof(nd_buf))
1031e5054602SMark Johnston 			return (EINVAL);
1032e5054602SMark Johnston 		memcpy(nd_buf, key, keysize);
1033e5054602SMark Johnston 		error = netdump_send(NETDUMP_EKCD_KEY, 0, nd_buf, keysize);
1034e5054602SMark Johnston 	}
1035e5054602SMark Johnston 	return (error);
1036e5054602SMark Johnston }
1037e5054602SMark Johnston 
1038e5054602SMark Johnston /*
1039e5054602SMark Johnston  * Cleanup routine for a possibly failed netdump.
1040e5054602SMark Johnston  */
1041e5054602SMark Johnston static void
1042e5054602SMark Johnston netdump_cleanup(void)
1043e5054602SMark Johnston {
1044e5054602SMark Johnston 
1045e5054602SMark Johnston 	if (restore_gw_addr != 0) {
1046e5054602SMark Johnston 		nd_gateway.s_addr = INADDR_ANY;
1047e5054602SMark Johnston 		restore_gw_addr = 0;
1048e5054602SMark Johnston 	}
1049e5054602SMark Johnston 	if (drv_if_input != NULL) {
1050e5054602SMark Johnston 		nd_ifp->if_input = drv_if_input;
1051e5054602SMark Johnston 		drv_if_input = NULL;
1052e5054602SMark Johnston 	}
1053e5054602SMark Johnston 	nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_END);
1054e5054602SMark Johnston }
1055e5054602SMark Johnston 
1056e5054602SMark Johnston /*-
1057e5054602SMark Johnston  * KLD specific code.
1058e5054602SMark Johnston  */
1059e5054602SMark Johnston 
1060e5054602SMark Johnston static struct cdevsw netdump_cdevsw = {
1061e5054602SMark Johnston 	.d_version =	D_VERSION,
1062e5054602SMark Johnston 	.d_ioctl =	netdump_ioctl,
1063e5054602SMark Johnston 	.d_name =	"netdump",
1064e5054602SMark Johnston };
1065e5054602SMark Johnston 
1066e5054602SMark Johnston static struct cdev *netdump_cdev;
1067e5054602SMark Johnston 
1068e5054602SMark Johnston static int
10696b6e2954SConrad Meyer netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
1070e5054602SMark Johnston {
1071a68cc388SGleb Smirnoff 	struct epoch_tracker et;
1072e5054602SMark Johnston 	struct ifnet *ifp;
1073e5054602SMark Johnston 
1074b35822d9SMark Johnston 	CURVNET_SET(TD_TO_VNET(td));
1075b35822d9SMark Johnston 	if (!IS_DEFAULT_VNET(curvnet)) {
1076b35822d9SMark Johnston 		CURVNET_RESTORE();
1077b35822d9SMark Johnston 		return (EINVAL);
1078b35822d9SMark Johnston 	}
1079a68cc388SGleb Smirnoff 	NET_EPOCH_ENTER(et);
10804f6c66ccSMatt Macy 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
10816b6e2954SConrad Meyer 		if (strcmp(ifp->if_xname, conf->kda_iface) == 0)
1082e5054602SMark Johnston 			break;
1083e5054602SMark Johnston 	}
1084e5054602SMark Johnston 	/* XXX ref */
1085a68cc388SGleb Smirnoff 	NET_EPOCH_EXIT(et);
1086b35822d9SMark Johnston 	CURVNET_RESTORE();
1087e5054602SMark Johnston 
1088e5054602SMark Johnston 	if (ifp == NULL)
1089e5054602SMark Johnston 		return (ENOENT);
1090e5054602SMark Johnston 	if ((if_getflags(ifp) & IFF_UP) == 0)
1091e5054602SMark Johnston 		return (ENXIO);
1092e5054602SMark Johnston 	if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER)
10936b6e2954SConrad Meyer 		return (ENODEV);
1094e5054602SMark Johnston 
1095e5054602SMark Johnston 	nd_ifp = ifp;
1096*6144b50fSConrad Meyer 
1097e5054602SMark Johnston 	netdump_reinit(ifp);
1098*6144b50fSConrad Meyer #define COPY_SIZED(elm) do {	\
1099*6144b50fSConrad Meyer 	_Static_assert(sizeof(nd_conf.ndc_ ## elm) ==			\
1100*6144b50fSConrad Meyer 	    sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
1101*6144b50fSConrad Meyer 	memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm,		\
1102*6144b50fSConrad Meyer 	    sizeof(nd_conf.ndc_ ## elm));				\
1103*6144b50fSConrad Meyer } while (0)
1104*6144b50fSConrad Meyer 	COPY_SIZED(iface);
1105*6144b50fSConrad Meyer 	COPY_SIZED(server);
1106*6144b50fSConrad Meyer 	COPY_SIZED(client);
1107*6144b50fSConrad Meyer 	COPY_SIZED(gateway);
1108*6144b50fSConrad Meyer 	COPY_SIZED(af);
1109*6144b50fSConrad Meyer #undef COPY_SIZED
1110e5054602SMark Johnston 	nd_enabled = 1;
1111e5054602SMark Johnston 	return (0);
1112e5054602SMark Johnston }
1113e5054602SMark Johnston 
1114e5054602SMark Johnston /*
1115e5054602SMark Johnston  * Reinitialize the mbuf pool used by drivers while dumping. This is called
1116e5054602SMark Johnston  * from the generic ioctl handler for SIOCSIFMTU after the driver has
1117e5054602SMark Johnston  * reconfigured itself.
1118e5054602SMark Johnston  */
1119e5054602SMark Johnston void
1120e5054602SMark Johnston netdump_reinit(struct ifnet *ifp)
1121e5054602SMark Johnston {
1122e5054602SMark Johnston 	int clsize, nmbuf, ncl, nrxr;
1123e5054602SMark Johnston 
1124e5054602SMark Johnston 	if (ifp != nd_ifp)
1125e5054602SMark Johnston 		return;
1126e5054602SMark Johnston 
1127e5054602SMark Johnston 	ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize);
1128e5054602SMark Johnston 	KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr));
1129e5054602SMark Johnston 
1130e5054602SMark Johnston 	/*
1131e5054602SMark Johnston 	 * We need two headers per message on the transmit side. Multiply by
1132e5054602SMark Johnston 	 * four to give us some breathing room.
1133e5054602SMark Johnston 	 */
1134e5054602SMark Johnston 	nmbuf = ncl * (4 + nrxr);
1135e5054602SMark Johnston 	ncl *= nrxr;
1136e5054602SMark Johnston 	netdump_mbuf_reinit(nmbuf, ncl, clsize);
1137e5054602SMark Johnston }
1138e5054602SMark Johnston 
1139e5054602SMark Johnston /*
1140e5054602SMark Johnston  * ioctl(2) handler for the netdump device. This is currently only used to
1141e5054602SMark Johnston  * register netdump as a dump device.
1142e5054602SMark Johnston  *
1143e5054602SMark Johnston  * Parameters:
1144e5054602SMark Johnston  *     dev, Unused.
1145e5054602SMark Johnston  *     cmd, The ioctl to be handled.
1146e5054602SMark Johnston  *     addr, The parameter for the ioctl.
1147e5054602SMark Johnston  *     flags, Unused.
1148e5054602SMark Johnston  *     td, The thread invoking this ioctl.
1149e5054602SMark Johnston  *
1150e5054602SMark Johnston  * Returns:
1151e5054602SMark Johnston  *     0 on success, and an errno value on failure.
1152e5054602SMark Johnston  */
1153e5054602SMark Johnston static int
1154e5054602SMark Johnston netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
1155e5054602SMark Johnston     int flags __unused, struct thread *td)
1156e5054602SMark Johnston {
11576b6e2954SConrad Meyer 	struct diocskerneldump_arg kda_copy, *conf;
1158e5054602SMark Johnston 	struct dumperinfo dumper;
1159e5054602SMark Johnston 	uint8_t *encryptedkey;
1160e5054602SMark Johnston 	int error;
1161a9f7f192SConrad Meyer #ifdef COMPAT_FREEBSD11
1162e5054602SMark Johnston 	u_int u;
1163a9f7f192SConrad Meyer #endif
11646b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12
11656b6e2954SConrad Meyer 	struct diocskerneldump_arg_freebsd12 *kda12;
11666b6e2954SConrad Meyer 	struct netdump_conf_freebsd12 *conf12;
11676b6e2954SConrad Meyer #endif
1168e5054602SMark Johnston 
11696b6e2954SConrad Meyer 	conf = NULL;
1170e5054602SMark Johnston 	error = 0;
1171e5054602SMark Johnston 	switch (cmd) {
117260ade167SConrad Meyer #ifdef COMPAT_FREEBSD11
117360ade167SConrad Meyer 	case DIOCSKERNELDUMP_FREEBSD11:
11746b6e2954SConrad Meyer 		gone_in(13, "11.x ABI compatibility");
1175e5054602SMark Johnston 		u = *(u_int *)addr;
1176e5054602SMark Johnston 		if (u != 0) {
1177e5054602SMark Johnston 			error = ENXIO;
1178e5054602SMark Johnston 			break;
1179e5054602SMark Johnston 		}
118060ade167SConrad Meyer 		if (nd_enabled) {
118160ade167SConrad Meyer 			nd_enabled = 0;
118260ade167SConrad Meyer 			netdump_mbuf_drain();
118360ade167SConrad Meyer 		}
118460ade167SConrad Meyer 		break;
118560ade167SConrad Meyer #endif
11866b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12
11876b6e2954SConrad Meyer 		/*
11886b6e2954SConrad Meyer 		 * Used by dumpon(8) in 12.x for clearing previous
11896b6e2954SConrad Meyer 		 * configuration -- then NETDUMPSCONF_FREEBSD12 is used to
11906b6e2954SConrad Meyer 		 * actually configure netdump.
11916b6e2954SConrad Meyer 		 */
11926b6e2954SConrad Meyer 	case DIOCSKERNELDUMP_FREEBSD12:
11936b6e2954SConrad Meyer 		gone_in(14, "12.x ABI compatibility");
11946b6e2954SConrad Meyer 
11956b6e2954SConrad Meyer 		kda12 = (void *)addr;
11966b6e2954SConrad Meyer 		if (kda12->kda12_enable) {
119760ade167SConrad Meyer 			error = ENXIO;
119860ade167SConrad Meyer 			break;
119960ade167SConrad Meyer 		}
1200e5054602SMark Johnston 		if (nd_enabled) {
1201e5054602SMark Johnston 			nd_enabled = 0;
1202e5054602SMark Johnston 			netdump_mbuf_drain();
1203e5054602SMark Johnston 		}
1204e5054602SMark Johnston 		break;
12056b6e2954SConrad Meyer 
12066b6e2954SConrad Meyer 	case NETDUMPGCONF_FREEBSD12:
12076b6e2954SConrad Meyer 		gone_in(14, "FreeBSD 12.x ABI compat");
12086b6e2954SConrad Meyer 		conf12 = (void *)addr;
12096b6e2954SConrad Meyer 
1210e5054602SMark Johnston 		if (!nd_enabled) {
1211e5054602SMark Johnston 			error = ENXIO;
1212e5054602SMark Johnston 			break;
1213e5054602SMark Johnston 		}
1214*6144b50fSConrad Meyer 		if (nd_conf.ndc_af != AF_INET) {
12156b6e2954SConrad Meyer 			error = EOPNOTSUPP;
1216e5054602SMark Johnston 			break;
12176b6e2954SConrad Meyer 		}
1218e5054602SMark Johnston 
12196b6e2954SConrad Meyer 		strlcpy(conf12->ndc12_iface, nd_ifp->if_xname,
12206b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_iface));
12216b6e2954SConrad Meyer 		memcpy(&conf12->ndc12_server, &nd_server,
12226b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_server));
12236b6e2954SConrad Meyer 		memcpy(&conf12->ndc12_client, &nd_client,
12246b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_client));
12256b6e2954SConrad Meyer 		memcpy(&conf12->ndc12_gateway, &nd_gateway,
12266b6e2954SConrad Meyer 		    sizeof(conf12->ndc12_gateway));
12276b6e2954SConrad Meyer 		break;
12286b6e2954SConrad Meyer #endif
12296b6e2954SConrad Meyer 	case DIOCGKERNELDUMP:
12306b6e2954SConrad Meyer 		conf = (void *)addr;
12316b6e2954SConrad Meyer 		/*
12326b6e2954SConrad Meyer 		 * For now, index is ignored; netdump doesn't support multiple
12336b6e2954SConrad Meyer 		 * configurations (yet).
12346b6e2954SConrad Meyer 		 */
12356b6e2954SConrad Meyer 		if (!nd_enabled) {
12366b6e2954SConrad Meyer 			error = ENXIO;
12376b6e2954SConrad Meyer 			conf = NULL;
12386b6e2954SConrad Meyer 			break;
12396b6e2954SConrad Meyer 		}
12406b6e2954SConrad Meyer 
12416b6e2954SConrad Meyer 		strlcpy(conf->kda_iface, nd_ifp->if_xname,
12426b6e2954SConrad Meyer 		    sizeof(conf->kda_iface));
12436b6e2954SConrad Meyer 		memcpy(&conf->kda_server, &nd_server, sizeof(nd_server));
12446b6e2954SConrad Meyer 		memcpy(&conf->kda_client, &nd_client, sizeof(nd_client));
12456b6e2954SConrad Meyer 		memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway));
1246*6144b50fSConrad Meyer 		conf->kda_af = nd_conf.ndc_af;
12476b6e2954SConrad Meyer 		conf = NULL;
12486b6e2954SConrad Meyer 		break;
12496b6e2954SConrad Meyer 
12506b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12
12516b6e2954SConrad Meyer 	case NETDUMPSCONF_FREEBSD12:
12526b6e2954SConrad Meyer 		gone_in(14, "FreeBSD 12.x ABI compat");
12536b6e2954SConrad Meyer 
12546b6e2954SConrad Meyer 		conf12 = (struct netdump_conf_freebsd12 *)addr;
12556b6e2954SConrad Meyer 
12566b6e2954SConrad Meyer 		_Static_assert(offsetof(struct diocskerneldump_arg, kda_server)
12576b6e2954SConrad Meyer 		    == offsetof(struct netdump_conf_freebsd12, ndc12_server),
12586b6e2954SConrad Meyer 		    "simplifying assumption");
12596b6e2954SConrad Meyer 
12606b6e2954SConrad Meyer 		memset(&kda_copy, 0, sizeof(kda_copy));
12616b6e2954SConrad Meyer 		memcpy(&kda_copy, conf12,
12626b6e2954SConrad Meyer 		    offsetof(struct diocskerneldump_arg, kda_server));
12636b6e2954SConrad Meyer 
12646b6e2954SConrad Meyer 		/* 12.x ABI could only configure IPv4 (INET) netdump. */
12656b6e2954SConrad Meyer 		kda_copy.kda_af = AF_INET;
12666b6e2954SConrad Meyer 		memcpy(&kda_copy.kda_server.in4, &conf12->ndc12_server,
12676b6e2954SConrad Meyer 		    sizeof(struct in_addr));
12686b6e2954SConrad Meyer 		memcpy(&kda_copy.kda_client.in4, &conf12->ndc12_client,
12696b6e2954SConrad Meyer 		    sizeof(struct in_addr));
12706b6e2954SConrad Meyer 		memcpy(&kda_copy.kda_gateway.in4, &conf12->ndc12_gateway,
12716b6e2954SConrad Meyer 		    sizeof(struct in_addr));
12726b6e2954SConrad Meyer 
12736b6e2954SConrad Meyer 		kda_copy.kda_index =
12746b6e2954SConrad Meyer 		    (conf12->ndc12_kda.kda12_enable ? 0 : KDA_REMOVE_ALL);
12756b6e2954SConrad Meyer 
12766b6e2954SConrad Meyer 		conf = &kda_copy;
12776b6e2954SConrad Meyer 		explicit_bzero(conf12, sizeof(*conf12));
12786b6e2954SConrad Meyer 		/* FALLTHROUGH */
12796b6e2954SConrad Meyer #endif
12806b6e2954SConrad Meyer 	case DIOCSKERNELDUMP:
12816b6e2954SConrad Meyer 		encryptedkey = NULL;
12826b6e2954SConrad Meyer 		if (cmd == DIOCSKERNELDUMP) {
12836b6e2954SConrad Meyer 			conf = (void *)addr;
12846b6e2954SConrad Meyer 			memcpy(&kda_copy, conf, sizeof(kda_copy));
12856b6e2954SConrad Meyer 		}
12866b6e2954SConrad Meyer 		/* Netdump only supports IP4 at this time. */
12876b6e2954SConrad Meyer 		if (conf->kda_af != AF_INET) {
12886b6e2954SConrad Meyer 			error = EPROTONOSUPPORT;
12896b6e2954SConrad Meyer 			break;
12906b6e2954SConrad Meyer 		}
12916b6e2954SConrad Meyer 
12926b6e2954SConrad Meyer 		conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0';
12936b6e2954SConrad Meyer 		if (conf->kda_index == KDA_REMOVE ||
12946b6e2954SConrad Meyer 		    conf->kda_index == KDA_REMOVE_DEV ||
12956b6e2954SConrad Meyer 		    conf->kda_index == KDA_REMOVE_ALL) {
12966b6e2954SConrad Meyer 			if (nd_enabled || conf->kda_index == KDA_REMOVE_ALL) {
12976b6e2954SConrad Meyer 				error = dumper_remove(conf->kda_iface, conf);
1298b35822d9SMark Johnston 				if (error == 0) {
1299e5054602SMark Johnston 					nd_enabled = 0;
1300b35822d9SMark Johnston 					netdump_mbuf_drain();
1301b35822d9SMark Johnston 				}
1302e5054602SMark Johnston 			}
1303e5054602SMark Johnston 			break;
1304e5054602SMark Johnston 		}
1305e5054602SMark Johnston 
1306b35822d9SMark Johnston 		error = netdump_configure(conf, td);
1307e5054602SMark Johnston 		if (error != 0)
1308e5054602SMark Johnston 			break;
1309e5054602SMark Johnston 
13106b6e2954SConrad Meyer 		if (conf->kda_encryption != KERNELDUMP_ENC_NONE) {
13116b6e2954SConrad Meyer 			if (conf->kda_encryptedkeysize <= 0 ||
13126b6e2954SConrad Meyer 			    conf->kda_encryptedkeysize >
13136b6e2954SConrad Meyer 			    KERNELDUMP_ENCKEY_MAX_SIZE) {
13146b6e2954SConrad Meyer 				error = EINVAL;
13156b6e2954SConrad Meyer 				break;
13166b6e2954SConrad Meyer 			}
13176b6e2954SConrad Meyer 			encryptedkey = malloc(conf->kda_encryptedkeysize,
13186b6e2954SConrad Meyer 			    M_TEMP, M_WAITOK);
13196b6e2954SConrad Meyer 			error = copyin(conf->kda_encryptedkey, encryptedkey,
13206b6e2954SConrad Meyer 			    conf->kda_encryptedkeysize);
1321e5054602SMark Johnston 			if (error != 0) {
1322e5054602SMark Johnston 				free(encryptedkey, M_TEMP);
13236b6e2954SConrad Meyer 				break;
1324e5054602SMark Johnston 			}
13256b6e2954SConrad Meyer 
13266b6e2954SConrad Meyer 			conf->kda_encryptedkey = encryptedkey;
1327e5054602SMark Johnston 		}
1328e5054602SMark Johnston 
13299f78e2b8SMark Johnston 		memset(&dumper, 0, sizeof(dumper));
1330e5054602SMark Johnston 		dumper.dumper_start = netdump_start;
1331e5054602SMark Johnston 		dumper.dumper_hdr = netdump_write_headers;
1332e5054602SMark Johnston 		dumper.dumper = netdump_dumper;
1333e5054602SMark Johnston 		dumper.priv = NULL;
1334e5054602SMark Johnston 		dumper.blocksize = NETDUMP_DATASIZE;
1335e5054602SMark Johnston 		dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE;
1336e5054602SMark Johnston 		dumper.mediaoffset = 0;
1337e5054602SMark Johnston 		dumper.mediasize = 0;
1338e5054602SMark Johnston 
13396b6e2954SConrad Meyer 		error = dumper_insert(&dumper, conf->kda_iface, conf);
1340e5054602SMark Johnston 		if (encryptedkey != NULL) {
13416b6e2954SConrad Meyer 			explicit_bzero(encryptedkey,
13426b6e2954SConrad Meyer 			    conf->kda_encryptedkeysize);
1343e5054602SMark Johnston 			free(encryptedkey, M_TEMP);
1344e5054602SMark Johnston 		}
1345b35822d9SMark Johnston 		if (error != 0) {
1346e5054602SMark Johnston 			nd_enabled = 0;
1347b35822d9SMark Johnston 			netdump_mbuf_drain();
1348b35822d9SMark Johnston 		}
1349e5054602SMark Johnston 		break;
1350e5054602SMark Johnston 	default:
13516b6e2954SConrad Meyer 		error = ENOTTY;
1352e5054602SMark Johnston 		break;
1353e5054602SMark Johnston 	}
13546b6e2954SConrad Meyer 	explicit_bzero(&kda_copy, sizeof(kda_copy));
13556b6e2954SConrad Meyer 	if (conf != NULL)
13566b6e2954SConrad Meyer 		explicit_bzero(conf, sizeof(*conf));
1357e5054602SMark Johnston 	return (error);
1358e5054602SMark Johnston }
1359e5054602SMark Johnston 
1360e5054602SMark Johnston /*
1361e5054602SMark Johnston  * Called upon system init or kld load.  Initializes the netdump parameters to
1362e5054602SMark Johnston  * sane defaults (locates the first available NIC and uses the first IPv4 IP on
1363e5054602SMark Johnston  * that card as the client IP).  Leaves the server IP unconfigured.
1364e5054602SMark Johnston  *
1365e5054602SMark Johnston  * Parameters:
1366e5054602SMark Johnston  *	mod, Unused.
1367e5054602SMark Johnston  *	what, The module event type.
1368e5054602SMark Johnston  *	priv, Unused.
1369e5054602SMark Johnston  *
1370e5054602SMark Johnston  * Returns:
1371e5054602SMark Johnston  *	int, An errno value if an error occured, 0 otherwise.
1372e5054602SMark Johnston  */
1373e5054602SMark Johnston static int
1374e5054602SMark Johnston netdump_modevent(module_t mod __unused, int what, void *priv __unused)
1375e5054602SMark Johnston {
13766b6e2954SConrad Meyer 	struct diocskerneldump_arg conf;
1377e5054602SMark Johnston 	char *arg;
1378e5054602SMark Johnston 	int error;
1379e5054602SMark Johnston 
1380e5054602SMark Johnston 	error = 0;
1381e5054602SMark Johnston 	switch (what) {
1382e5054602SMark Johnston 	case MOD_LOAD:
1383e5054602SMark Johnston 		error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev,
1384e5054602SMark Johnston 		    &netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump");
1385e5054602SMark Johnston 		if (error != 0)
1386e5054602SMark Johnston 			return (error);
1387e5054602SMark Johnston 
1388e5054602SMark Johnston 		if ((arg = kern_getenv("net.dump.iface")) != NULL) {
13896b6e2954SConrad Meyer 			strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
1390e5054602SMark Johnston 			freeenv(arg);
1391e5054602SMark Johnston 
1392e5054602SMark Johnston 			if ((arg = kern_getenv("net.dump.server")) != NULL) {
13936b6e2954SConrad Meyer 				inet_aton(arg, &conf.kda_server.in4);
1394e5054602SMark Johnston 				freeenv(arg);
1395e5054602SMark Johnston 			}
1396e5054602SMark Johnston 			if ((arg = kern_getenv("net.dump.client")) != NULL) {
13976b6e2954SConrad Meyer 				inet_aton(arg, &conf.kda_server.in4);
1398e5054602SMark Johnston 				freeenv(arg);
1399e5054602SMark Johnston 			}
1400e5054602SMark Johnston 			if ((arg = kern_getenv("net.dump.gateway")) != NULL) {
14016b6e2954SConrad Meyer 				inet_aton(arg, &conf.kda_server.in4);
1402e5054602SMark Johnston 				freeenv(arg);
1403e5054602SMark Johnston 			}
14046b6e2954SConrad Meyer 			conf.kda_af = AF_INET;
1405e5054602SMark Johnston 
1406e5054602SMark Johnston 			/* Ignore errors; we print a message to the console. */
1407b35822d9SMark Johnston 			(void)netdump_configure(&conf, curthread);
1408e5054602SMark Johnston 		}
1409e5054602SMark Johnston 		break;
1410e5054602SMark Johnston 	case MOD_UNLOAD:
1411e5054602SMark Johnston 		if (nd_enabled) {
14126b6e2954SConrad Meyer 			struct diocskerneldump_arg kda;
14136b6e2954SConrad Meyer 
1414e5054602SMark Johnston 			printf("netdump: disabling dump device for unload\n");
14156b6e2954SConrad Meyer 
14166b6e2954SConrad Meyer 			bzero(&kda, sizeof(kda));
14176b6e2954SConrad Meyer 			kda.kda_index = KDA_REMOVE_DEV;
1418*6144b50fSConrad Meyer 			(void)dumper_remove(nd_conf.ndc_iface, &kda);
14196b6e2954SConrad Meyer 
14206b6e2954SConrad Meyer 			netdump_mbuf_drain();
1421e5054602SMark Johnston 			nd_enabled = 0;
1422e5054602SMark Johnston 		}
14236b6e2954SConrad Meyer 		destroy_dev(netdump_cdev);
1424e5054602SMark Johnston 		break;
1425e5054602SMark Johnston 	default:
1426e5054602SMark Johnston 		error = EOPNOTSUPP;
1427e5054602SMark Johnston 		break;
1428e5054602SMark Johnston 	}
1429e5054602SMark Johnston 	return (error);
1430e5054602SMark Johnston }
1431e5054602SMark Johnston 
1432e5054602SMark Johnston static moduledata_t netdump_mod = {
1433e5054602SMark Johnston 	"netdump",
1434e5054602SMark Johnston 	netdump_modevent,
1435e5054602SMark Johnston 	NULL,
1436e5054602SMark Johnston };
1437e5054602SMark Johnston 
1438e5054602SMark Johnston MODULE_VERSION(netdump, 1);
1439e5054602SMark Johnston DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
1440