xref: /freebsd/sys/netinet/netdump/netdump_client.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
1 /*-
2  * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved.
3  * Copyright (c) 2000 Darrell Anderson
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * netdump_client.c
30  * FreeBSD subsystem supporting netdump network dumps.
31  * A dedicated server must be running to accept client dumps.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include "opt_ddb.h"
38 
39 #include <sys/param.h>
40 #include <sys/conf.h>
41 #include <sys/disk.h>
42 #include <sys/endian.h>
43 #include <sys/eventhandler.h>
44 #include <sys/jail.h>
45 #include <sys/kernel.h>
46 #include <sys/kerneldump.h>
47 #include <sys/mbuf.h>
48 #include <sys/module.h>
49 #include <sys/priv.h>
50 #include <sys/proc.h>
51 #include <sys/protosw.h>
52 #include <sys/socket.h>
53 #include <sys/sysctl.h>
54 #include <sys/syslog.h>
55 #include <sys/systm.h>
56 
57 #ifdef DDB
58 #include <ddb/ddb.h>
59 #include <ddb/db_lex.h>
60 #endif
61 
62 #include <net/ethernet.h>
63 #include <net/if.h>
64 #include <net/if_arp.h>
65 #include <net/if_dl.h>
66 #include <net/if_types.h>
67 #include <net/if_var.h>
68 #include <net/debugnet.h>
69 
70 #include <netinet/in.h>
71 #include <netinet/in_systm.h>
72 #include <netinet/in_var.h>
73 #include <netinet/ip.h>
74 #include <netinet/ip_var.h>
75 #include <netinet/ip_options.h>
76 #include <netinet/udp.h>
77 #include <netinet/udp_var.h>
78 #include <netinet/netdump/netdump.h>
79 
80 #include <machine/in_cksum.h>
81 #include <machine/pcb.h>
82 
83 #define	NETDDEBUGV(f, ...) do {						\
84 	if (nd_debug > 1)						\
85 		printf(("%s: " f), __func__, ## __VA_ARGS__);		\
86 } while (0)
87 
88 static int	 netdump_configure(struct diocskerneldump_arg *,
89 		    struct thread *);
90 static int	 netdump_dumper(void *priv __unused, void *virtual,
91 		    vm_offset_t physical __unused, off_t offset, size_t length);
92 static bool	 netdump_enabled(void);
93 static int	 netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS);
94 static int	 netdump_ioctl(struct cdev *dev __unused, u_long cmd,
95 		    caddr_t addr, int flags __unused, struct thread *td);
96 static int	 netdump_modevent(module_t mod, int type, void *priv);
97 static int	 netdump_start(struct dumperinfo *di);
98 static void	 netdump_unconfigure(void);
99 
100 /* Must be at least as big as the chunks dumpsys() gives us. */
101 static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
102 static int dump_failed;
103 
104 /* Configuration parameters. */
105 static struct {
106 	char		 ndc_iface[IFNAMSIZ];
107 	union kd_ip	 ndc_server;
108 	union kd_ip	 ndc_client;
109 	union kd_ip	 ndc_gateway;
110 	uint8_t		 ndc_af;
111 	/* Runtime State */
112 	struct debugnet_pcb *nd_pcb;
113 	off_t		 nd_tx_off;
114 	size_t		 nd_buf_len;
115 } nd_conf;
116 #define	nd_server	nd_conf.ndc_server.in4
117 #define	nd_client	nd_conf.ndc_client.in4
118 #define	nd_gateway	nd_conf.ndc_gateway.in4
119 
120 /* General dynamic settings. */
121 static struct sx nd_conf_lk;
122 SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock");
123 #define NETDUMP_WLOCK()			sx_xlock(&nd_conf_lk)
124 #define NETDUMP_WUNLOCK()		sx_xunlock(&nd_conf_lk)
125 #define NETDUMP_RLOCK()			sx_slock(&nd_conf_lk)
126 #define NETDUMP_RUNLOCK()		sx_sunlock(&nd_conf_lk)
127 #define NETDUMP_ASSERT_WLOCKED()	sx_assert(&nd_conf_lk, SA_XLOCKED)
128 #define NETDUMP_ASSERT_LOCKED()		sx_assert(&nd_conf_lk, SA_LOCKED)
129 static struct ifnet *nd_ifp;
130 static eventhandler_tag nd_detach_cookie;
131 
132 FEATURE(netdump, "Netdump client support");
133 
134 static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
135     "netdump parameters");
136 
137 static int nd_debug;
138 SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
139     &nd_debug, 0,
140     "Debug message verbosity");
141 SYSCTL_PROC(_net_netdump, OID_AUTO, enabled,
142     CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE, NULL, 0,
143     netdump_enabled_sysctl, "I",
144     "netdump configuration status");
145 static char nd_path[MAXPATHLEN];
146 SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW,
147     nd_path, sizeof(nd_path),
148     "Server path for output files");
149 /*
150  * The following three variables were moved to debugnet(4), but these knobs
151  * were retained as aliases.
152  */
153 SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN,
154     &debugnet_npolls, 0,
155     "Number of times to poll before assuming packet loss (0.5ms per poll)");
156 SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN,
157     &debugnet_nretries, 0,
158     "Number of retransmit attempts before giving up");
159 SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN,
160     &debugnet_arp_nretries, 0,
161     "Number of ARP attempts before giving up");
162 
163 static bool nd_is_enabled;
164 static bool
165 netdump_enabled(void)
166 {
167 
168 	NETDUMP_ASSERT_LOCKED();
169 	return (nd_is_enabled);
170 }
171 
172 static void
173 netdump_set_enabled(bool status)
174 {
175 
176 	NETDUMP_ASSERT_LOCKED();
177 	nd_is_enabled = status;
178 }
179 
180 static int
181 netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)
182 {
183 	int en, error;
184 
185 	NETDUMP_RLOCK();
186 	en = netdump_enabled();
187 	NETDUMP_RUNLOCK();
188 
189 	error = SYSCTL_OUT(req, &en, sizeof(en));
190 	if (error != 0 || req->newptr == NULL)
191 		return (error);
192 	return (EPERM);
193 }
194 
195 /*-
196  * Dumping specific primitives.
197  */
198 
199 /*
200  * Flush any buffered vmcore data.
201  */
202 static int
203 netdump_flush_buf(void)
204 {
205 	int error;
206 
207 	error = 0;
208 	if (nd_conf.nd_buf_len != 0) {
209 		struct debugnet_proto_aux auxdata = {
210 			.dp_offset_start = nd_conf.nd_tx_off,
211 		};
212 		error = debugnet_send(nd_conf.nd_pcb, DEBUGNET_DATA, nd_buf,
213 		    nd_conf.nd_buf_len, &auxdata);
214 		if (error == 0)
215 			nd_conf.nd_buf_len = 0;
216 	}
217 	return (error);
218 }
219 
220 /*
221  * Callback from dumpsys() to dump a chunk of memory.
222  * Copies it out to our static buffer then sends it across the network.
223  * Detects the initial KDH and makes sure it is given a special packet type.
224  *
225  * Parameters:
226  *	priv	 Unused. Optional private pointer.
227  *	virtual  Virtual address (where to read the data from)
228  *	physical Unused. Physical memory address.
229  *	offset	 Offset from start of core file
230  *	length	 Data length
231  *
232  * Return value:
233  *	0 on success
234  *	errno on error
235  */
236 static int
237 netdump_dumper(void *priv __unused, void *virtual,
238     vm_offset_t physical __unused, off_t offset, size_t length)
239 {
240 	int error;
241 
242 	NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n",
243 	    virtual, (uintmax_t)offset, length);
244 
245 	if (virtual == NULL) {
246 		error = netdump_flush_buf();
247 		if (error != 0)
248 			dump_failed = 1;
249 
250 		if (dump_failed != 0)
251 			printf("failed to dump the kernel core\n");
252 		else if (
253 		    debugnet_sendempty(nd_conf.nd_pcb, DEBUGNET_FINISHED) != 0)
254 			printf("failed to close the transaction\n");
255 		else
256 			printf("\nnetdump finished.\n");
257 		debugnet_free(nd_conf.nd_pcb);
258 		nd_conf.nd_pcb = NULL;
259 		return (0);
260 	}
261 	if (length > sizeof(nd_buf))
262 		return (ENOSPC);
263 
264 	if (nd_conf.nd_buf_len + length > sizeof(nd_buf) ||
265 	    (nd_conf.nd_buf_len != 0 && nd_conf.nd_tx_off +
266 	    nd_conf.nd_buf_len != offset)) {
267 		error = netdump_flush_buf();
268 		if (error != 0) {
269 			dump_failed = 1;
270 			return (error);
271 		}
272 		nd_conf.nd_tx_off = offset;
273 	}
274 
275 	memmove(nd_buf + nd_conf.nd_buf_len, virtual, length);
276 	nd_conf.nd_buf_len += length;
277 
278 	return (0);
279 }
280 
281 /*
282  * Perform any initialization needed prior to transmitting the kernel core.
283  */
284 static int
285 netdump_start(struct dumperinfo *di)
286 {
287 	struct debugnet_conn_params dcp;
288 	struct debugnet_pcb *pcb;
289 	char buf[INET_ADDRSTRLEN];
290 	int error;
291 
292 	error = 0;
293 
294 	/* Check if the dumping is allowed to continue. */
295 	if (!netdump_enabled())
296 		return (EINVAL);
297 
298 	if (!KERNEL_PANICKED()) {
299 		printf(
300 		    "netdump_start: netdump may only be used after a panic\n");
301 		return (EINVAL);
302 	}
303 
304 	memset(&dcp, 0, sizeof(dcp));
305 
306 	if (nd_server.s_addr == INADDR_ANY) {
307 		printf("netdump_start: can't netdump; no server IP given\n");
308 		return (EINVAL);
309 	}
310 
311 	/* We start dumping at offset 0. */
312 	di->dumpoff = 0;
313 
314 	dcp.dc_ifp = nd_ifp;
315 
316 	dcp.dc_client = nd_client.s_addr;
317 	dcp.dc_server = nd_server.s_addr;
318 	dcp.dc_gateway = nd_gateway.s_addr;
319 
320 	dcp.dc_herald_port = NETDUMP_PORT;
321 	dcp.dc_client_port = NETDUMP_ACKPORT;
322 
323 	dcp.dc_herald_data = nd_path;
324 	dcp.dc_herald_datalen = (nd_path[0] == 0) ? 0 : strlen(nd_path) + 1;
325 
326 	error = debugnet_connect(&dcp, &pcb);
327 	if (error != 0) {
328 		printf("failed to contact netdump server\n");
329 		/* Squash debugnet to something the dumper code understands. */
330 		return (EINVAL);
331 	}
332 
333 	printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf),
334 	    debugnet_get_gw_mac(pcb), ":");
335 	nd_conf.nd_pcb = pcb;
336 	return (0);
337 }
338 
339 static int
340 netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh,
341     void *key, uint32_t keysize)
342 {
343 	int error;
344 
345 	error = netdump_flush_buf();
346 	if (error != 0)
347 		return (error);
348 	memcpy(nd_buf, kdh, sizeof(*kdh));
349 	error = debugnet_send(nd_conf.nd_pcb, NETDUMP_KDH, nd_buf,
350 	    sizeof(*kdh), NULL);
351 	if (error == 0 && keysize > 0) {
352 		if (keysize > sizeof(nd_buf))
353 			return (EINVAL);
354 		memcpy(nd_buf, key, keysize);
355 		error = debugnet_send(nd_conf.nd_pcb, NETDUMP_EKCD_KEY, nd_buf,
356 		    keysize, NULL);
357 	}
358 	return (error);
359 }
360 
361 /*-
362  * KLD specific code.
363  */
364 
365 static struct cdevsw netdump_cdevsw = {
366 	.d_version =	D_VERSION,
367 	.d_ioctl =	netdump_ioctl,
368 	.d_name =	"netdump",
369 };
370 
371 static struct cdev *netdump_cdev;
372 
373 static void
374 netdump_unconfigure(void)
375 {
376 	struct diocskerneldump_arg kda;
377 
378 	NETDUMP_ASSERT_WLOCKED();
379 	KASSERT(netdump_enabled(), ("%s: not enabled", __func__));
380 
381 	bzero(&kda, sizeof(kda));
382 	kda.kda_index = KDA_REMOVE_DEV;
383 	(void)dumper_remove(nd_conf.ndc_iface, &kda);
384 
385 	if (nd_ifp != NULL)
386 		if_rele(nd_ifp);
387 	nd_ifp = NULL;
388 	netdump_set_enabled(false);
389 
390 	log(LOG_WARNING, "netdump: Lost configured interface %s\n",
391 	    nd_conf.ndc_iface);
392 
393 	bzero(&nd_conf, sizeof(nd_conf));
394 }
395 
396 static void
397 netdump_ifdetach(void *arg __unused, struct ifnet *ifp)
398 {
399 
400 	NETDUMP_WLOCK();
401 	if (ifp == nd_ifp)
402 		netdump_unconfigure();
403 	NETDUMP_WUNLOCK();
404 }
405 
406 /*
407  * td of NULL is a sentinel value that indicates a kernel caller (ddb(4) or
408  * modload-based tunable parameters).
409  */
410 static int
411 netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
412 {
413 	struct ifnet *ifp;
414 
415 	NETDUMP_ASSERT_WLOCKED();
416 
417 	if (conf->kda_iface[0] != 0) {
418 		if (td != NULL && !IS_DEFAULT_VNET(TD_TO_VNET(td)))
419 			return (EINVAL);
420 		CURVNET_SET(vnet0);
421 		ifp = ifunit_ref(conf->kda_iface);
422 		CURVNET_RESTORE();
423 	} else
424 		ifp = NULL;
425 
426 	if (nd_ifp != NULL)
427 		if_rele(nd_ifp);
428 	nd_ifp = ifp;
429 	netdump_set_enabled(true);
430 
431 #define COPY_SIZED(elm) do {	\
432 	_Static_assert(sizeof(nd_conf.ndc_ ## elm) ==			\
433 	    sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
434 	memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm,		\
435 	    sizeof(nd_conf.ndc_ ## elm));				\
436 } while (0)
437 	COPY_SIZED(iface);
438 	COPY_SIZED(server);
439 	COPY_SIZED(client);
440 	COPY_SIZED(gateway);
441 	COPY_SIZED(af);
442 #undef COPY_SIZED
443 
444 	return (0);
445 }
446 
447 /*
448  * ioctl(2) handler for the netdump device. This is currently only used to
449  * register netdump as a dump device.
450  *
451  * Parameters:
452  *     dev, Unused.
453  *     cmd, The ioctl to be handled.
454  *     addr, The parameter for the ioctl.
455  *     flags, Unused.
456  *     td, The thread invoking this ioctl.
457  *
458  * Returns:
459  *     0 on success, and an errno value on failure.
460  */
461 static int
462 netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
463     int flags __unused, struct thread *td)
464 {
465 	struct diocskerneldump_arg kda_copy, *conf;
466 	struct dumperinfo dumper;
467 	uint8_t *encryptedkey;
468 	int error;
469 #ifdef COMPAT_FREEBSD11
470 	u_int u;
471 #endif
472 #ifdef COMPAT_FREEBSD12
473 	struct diocskerneldump_arg_freebsd12 *kda12;
474 	struct netdump_conf_freebsd12 *conf12;
475 #endif
476 
477 	conf = NULL;
478 	error = 0;
479 	NETDUMP_WLOCK();
480 
481 	switch (cmd) {
482 #ifdef COMPAT_FREEBSD11
483 	case DIOCSKERNELDUMP_FREEBSD11:
484 		gone_in(13, "11.x ABI compatibility");
485 		u = *(u_int *)addr;
486 		if (u != 0) {
487 			error = ENXIO;
488 			break;
489 		}
490 		if (netdump_enabled())
491 			netdump_unconfigure();
492 		break;
493 #endif
494 #ifdef COMPAT_FREEBSD12
495 		/*
496 		 * Used by dumpon(8) in 12.x for clearing previous
497 		 * configuration -- then NETDUMPSCONF_FREEBSD12 is used to
498 		 * actually configure netdump.
499 		 */
500 	case DIOCSKERNELDUMP_FREEBSD12:
501 		gone_in(14, "12.x ABI compatibility");
502 
503 		kda12 = (void *)addr;
504 		if (kda12->kda12_enable) {
505 			error = ENXIO;
506 			break;
507 		}
508 		if (netdump_enabled())
509 			netdump_unconfigure();
510 		break;
511 
512 	case NETDUMPGCONF_FREEBSD12:
513 		gone_in(14, "FreeBSD 12.x ABI compat");
514 		conf12 = (void *)addr;
515 
516 		if (!netdump_enabled()) {
517 			error = ENXIO;
518 			break;
519 		}
520 		if (nd_conf.ndc_af != AF_INET) {
521 			error = EOPNOTSUPP;
522 			break;
523 		}
524 
525 		if (nd_ifp != NULL)
526 			strlcpy(conf12->ndc12_iface, nd_ifp->if_xname,
527 			    sizeof(conf12->ndc12_iface));
528 		memcpy(&conf12->ndc12_server, &nd_server,
529 		    sizeof(conf12->ndc12_server));
530 		memcpy(&conf12->ndc12_client, &nd_client,
531 		    sizeof(conf12->ndc12_client));
532 		memcpy(&conf12->ndc12_gateway, &nd_gateway,
533 		    sizeof(conf12->ndc12_gateway));
534 		break;
535 #endif
536 	case DIOCGKERNELDUMP:
537 		conf = (void *)addr;
538 		/*
539 		 * For now, index is ignored; netdump doesn't support multiple
540 		 * configurations (yet).
541 		 */
542 		if (!netdump_enabled()) {
543 			error = ENXIO;
544 			conf = NULL;
545 			break;
546 		}
547 
548 		if (nd_ifp != NULL)
549 			strlcpy(conf->kda_iface, nd_ifp->if_xname,
550 			    sizeof(conf->kda_iface));
551 		memcpy(&conf->kda_server, &nd_server, sizeof(nd_server));
552 		memcpy(&conf->kda_client, &nd_client, sizeof(nd_client));
553 		memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway));
554 		conf->kda_af = nd_conf.ndc_af;
555 		conf = NULL;
556 		break;
557 
558 #ifdef COMPAT_FREEBSD12
559 	case NETDUMPSCONF_FREEBSD12:
560 		gone_in(14, "FreeBSD 12.x ABI compat");
561 
562 		conf12 = (struct netdump_conf_freebsd12 *)addr;
563 
564 		_Static_assert(offsetof(struct diocskerneldump_arg, kda_server)
565 		    == offsetof(struct netdump_conf_freebsd12, ndc12_server),
566 		    "simplifying assumption");
567 
568 		memset(&kda_copy, 0, sizeof(kda_copy));
569 		memcpy(&kda_copy, conf12,
570 		    offsetof(struct diocskerneldump_arg, kda_server));
571 
572 		/* 12.x ABI could only configure IPv4 (INET) netdump. */
573 		kda_copy.kda_af = AF_INET;
574 		memcpy(&kda_copy.kda_server.in4, &conf12->ndc12_server,
575 		    sizeof(struct in_addr));
576 		memcpy(&kda_copy.kda_client.in4, &conf12->ndc12_client,
577 		    sizeof(struct in_addr));
578 		memcpy(&kda_copy.kda_gateway.in4, &conf12->ndc12_gateway,
579 		    sizeof(struct in_addr));
580 
581 		kda_copy.kda_index =
582 		    (conf12->ndc12_kda.kda12_enable ? 0 : KDA_REMOVE_ALL);
583 
584 		conf = &kda_copy;
585 		explicit_bzero(conf12, sizeof(*conf12));
586 		/* FALLTHROUGH */
587 #endif
588 	case DIOCSKERNELDUMP:
589 		encryptedkey = NULL;
590 		if (cmd == DIOCSKERNELDUMP) {
591 			conf = (void *)addr;
592 			memcpy(&kda_copy, conf, sizeof(kda_copy));
593 		}
594 		/* Netdump only supports IP4 at this time. */
595 		if (conf->kda_af != AF_INET) {
596 			error = EPROTONOSUPPORT;
597 			break;
598 		}
599 
600 		conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0';
601 		if (conf->kda_index == KDA_REMOVE ||
602 		    conf->kda_index == KDA_REMOVE_DEV ||
603 		    conf->kda_index == KDA_REMOVE_ALL) {
604 			if (netdump_enabled())
605 				netdump_unconfigure();
606 			if (conf->kda_index == KDA_REMOVE_ALL)
607 				error = dumper_remove(NULL, conf);
608 			break;
609 		}
610 
611 		error = netdump_configure(conf, td);
612 		if (error != 0)
613 			break;
614 
615 		if (conf->kda_encryption != KERNELDUMP_ENC_NONE) {
616 			if (conf->kda_encryptedkeysize <= 0 ||
617 			    conf->kda_encryptedkeysize >
618 			    KERNELDUMP_ENCKEY_MAX_SIZE) {
619 				error = EINVAL;
620 				break;
621 			}
622 			encryptedkey = malloc(conf->kda_encryptedkeysize,
623 			    M_TEMP, M_WAITOK);
624 			error = copyin(conf->kda_encryptedkey, encryptedkey,
625 			    conf->kda_encryptedkeysize);
626 			if (error != 0) {
627 				free(encryptedkey, M_TEMP);
628 				break;
629 			}
630 
631 			conf->kda_encryptedkey = encryptedkey;
632 		}
633 
634 		memset(&dumper, 0, sizeof(dumper));
635 		dumper.dumper_start = netdump_start;
636 		dumper.dumper_hdr = netdump_write_headers;
637 		dumper.dumper = netdump_dumper;
638 		dumper.priv = NULL;
639 		dumper.blocksize = NETDUMP_DATASIZE;
640 		dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE;
641 		dumper.mediaoffset = 0;
642 		dumper.mediasize = 0;
643 
644 		error = dumper_insert(&dumper, conf->kda_iface, conf);
645 		zfree(encryptedkey, M_TEMP);
646 		if (error != 0)
647 			netdump_unconfigure();
648 		break;
649 	default:
650 		error = ENOTTY;
651 		break;
652 	}
653 	explicit_bzero(&kda_copy, sizeof(kda_copy));
654 	if (conf != NULL)
655 		explicit_bzero(conf, sizeof(*conf));
656 	NETDUMP_WUNLOCK();
657 	return (error);
658 }
659 
660 /*
661  * Called upon system init or kld load.  Initializes the netdump parameters to
662  * sane defaults (locates the first available NIC and uses the first IPv4 IP on
663  * that card as the client IP).  Leaves the server IP unconfigured.
664  *
665  * Parameters:
666  *	mod, Unused.
667  *	what, The module event type.
668  *	priv, Unused.
669  *
670  * Returns:
671  *	int, An errno value if an error occured, 0 otherwise.
672  */
673 static int
674 netdump_modevent(module_t mod __unused, int what, void *priv __unused)
675 {
676 	struct diocskerneldump_arg conf;
677 	char *arg;
678 	int error;
679 
680 	error = 0;
681 	switch (what) {
682 	case MOD_LOAD:
683 		error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev,
684 		    &netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump");
685 		if (error != 0)
686 			return (error);
687 
688 		nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
689 		    netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
690 
691 		if ((arg = kern_getenv("net.dump.iface")) != NULL) {
692 			strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
693 			freeenv(arg);
694 
695 			if ((arg = kern_getenv("net.dump.server")) != NULL) {
696 				inet_aton(arg, &conf.kda_server.in4);
697 				freeenv(arg);
698 			}
699 			if ((arg = kern_getenv("net.dump.client")) != NULL) {
700 				inet_aton(arg, &conf.kda_client.in4);
701 				freeenv(arg);
702 			}
703 			if ((arg = kern_getenv("net.dump.gateway")) != NULL) {
704 				inet_aton(arg, &conf.kda_gateway.in4);
705 				freeenv(arg);
706 			}
707 			conf.kda_af = AF_INET;
708 
709 			/* Ignore errors; we print a message to the console. */
710 			NETDUMP_WLOCK();
711 			(void)netdump_configure(&conf, NULL);
712 			NETDUMP_WUNLOCK();
713 		}
714 		break;
715 	case MOD_UNLOAD:
716 		NETDUMP_WLOCK();
717 		if (netdump_enabled()) {
718 			printf("netdump: disabling dump device for unload\n");
719 			netdump_unconfigure();
720 		}
721 		NETDUMP_WUNLOCK();
722 		destroy_dev(netdump_cdev);
723 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
724 		    nd_detach_cookie);
725 		break;
726 	default:
727 		error = EOPNOTSUPP;
728 		break;
729 	}
730 	return (error);
731 }
732 
733 static moduledata_t netdump_mod = {
734 	"netdump",
735 	netdump_modevent,
736 	NULL,
737 };
738 
739 MODULE_VERSION(netdump, 1);
740 DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
741 
742 #ifdef DDB
743 /*
744  * Usage: netdump -s <server> [-g <gateway] -c <localip> -i <interface>
745  *
746  * Order is not significant.
747  *
748  * Currently, this command does not support configuring encryption or
749  * compression.
750  */
751 DB_FUNC(netdump, db_netdump_cmd, db_cmd_table, CS_OWN, NULL)
752 {
753 	static struct diocskerneldump_arg conf;
754 	static char blockbuf[NETDUMP_DATASIZE];
755 	static union {
756 		struct dumperinfo di;
757 		/* For valid di_devname. */
758 		char di_buf[sizeof(struct dumperinfo) + 1];
759 	} u;
760 
761 	struct debugnet_ddb_config params;
762 	int error;
763 
764 	error = debugnet_parse_ddb_cmd("netdump", &params);
765 	if (error != 0) {
766 		db_printf("Error configuring netdump: %d\n", error);
767 		return;
768 	}
769 
770 	/* Translate to a netdump dumper config. */
771 	memset(&conf, 0, sizeof(conf));
772 
773 	if (params.dd_ifp != NULL)
774 		strlcpy(conf.kda_iface, if_name(params.dd_ifp),
775 		    sizeof(conf.kda_iface));
776 
777 	conf.kda_af = AF_INET;
778 	conf.kda_server.in4 = (struct in_addr) { params.dd_server };
779 	if (params.dd_has_client)
780 		conf.kda_client.in4 = (struct in_addr) { params.dd_client };
781 	else
782 		conf.kda_client.in4 = (struct in_addr) { INADDR_ANY };
783 	if (params.dd_has_gateway)
784 		conf.kda_gateway.in4 = (struct in_addr) { params.dd_gateway };
785 	else
786 		conf.kda_gateway.in4 = (struct in_addr) { INADDR_ANY };
787 
788 	/* Set the global netdump config to these options. */
789 	error = netdump_configure(&conf, NULL);
790 	if (error != 0) {
791 		db_printf("Error enabling netdump: %d\n", error);
792 		return;
793 	}
794 
795 	/* Fake the generic dump configuration list entry to avoid malloc. */
796 	memset(&u.di_buf, 0, sizeof(u.di_buf));
797 	u.di.dumper_start = netdump_start;
798 	u.di.dumper_hdr = netdump_write_headers;
799 	u.di.dumper = netdump_dumper;
800 	u.di.priv = NULL;
801 	u.di.blocksize = NETDUMP_DATASIZE;
802 	u.di.maxiosize = MAXDUMPPGS * PAGE_SIZE;
803 	u.di.mediaoffset = 0;
804 	u.di.mediasize = 0;
805 	u.di.blockbuf = blockbuf;
806 
807 	dumper_ddb_insert(&u.di);
808 
809 	error = doadump(false);
810 
811 	dumper_ddb_remove(&u.di);
812 	if (error != 0)
813 		db_printf("Cannot dump: %d\n", error);
814 }
815 #endif /* DDB */
816