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>
358270d35eSConrad Meyer #include "opt_ddb.h"
368270d35eSConrad Meyer
37e5054602SMark Johnston #include <sys/param.h>
38e5054602SMark Johnston #include <sys/conf.h>
39e5054602SMark Johnston #include <sys/disk.h>
40e5054602SMark Johnston #include <sys/endian.h>
4104e0c883SConrad Meyer #include <sys/eventhandler.h>
42b35822d9SMark Johnston #include <sys/jail.h>
43e5054602SMark Johnston #include <sys/kernel.h>
44e5054602SMark Johnston #include <sys/kerneldump.h>
45e5054602SMark Johnston #include <sys/mbuf.h>
46e5054602SMark Johnston #include <sys/module.h>
47e5054602SMark Johnston #include <sys/priv.h>
48e5054602SMark Johnston #include <sys/proc.h>
49e5054602SMark Johnston #include <sys/protosw.h>
50e5054602SMark Johnston #include <sys/socket.h>
51e5054602SMark Johnston #include <sys/sysctl.h>
527790c8c1SConrad Meyer #include <sys/syslog.h>
53e5054602SMark Johnston #include <sys/systm.h>
54e5054602SMark Johnston
558270d35eSConrad Meyer #ifdef DDB
568270d35eSConrad Meyer #include <ddb/ddb.h>
578270d35eSConrad Meyer #include <ddb/db_lex.h>
588270d35eSConrad Meyer #endif
598270d35eSConrad Meyer
60e5054602SMark Johnston #include <net/ethernet.h>
61e5054602SMark Johnston #include <net/if.h>
62e5054602SMark Johnston #include <net/if_arp.h>
63e5054602SMark Johnston #include <net/if_dl.h>
64e5054602SMark Johnston #include <net/if_types.h>
65e5054602SMark Johnston #include <net/if_var.h>
663d0d5b21SJustin Hibbits #include <net/if_private.h>
677790c8c1SConrad Meyer #include <net/debugnet.h>
68e5054602SMark Johnston
69e5054602SMark Johnston #include <netinet/in.h>
70e5054602SMark Johnston #include <netinet/in_systm.h>
71e5054602SMark Johnston #include <netinet/in_var.h>
72e5054602SMark Johnston #include <netinet/ip.h>
73e5054602SMark Johnston #include <netinet/ip_var.h>
74e5054602SMark Johnston #include <netinet/ip_options.h>
75e5054602SMark Johnston #include <netinet/udp.h>
76e5054602SMark Johnston #include <netinet/udp_var.h>
77e5054602SMark Johnston #include <netinet/netdump/netdump.h>
78e5054602SMark Johnston
79e5054602SMark Johnston #include <machine/in_cksum.h>
80e5054602SMark Johnston #include <machine/pcb.h>
81e5054602SMark Johnston
82e5054602SMark Johnston #define NETDDEBUGV(f, ...) do { \
83e5054602SMark Johnston if (nd_debug > 1) \
84e5054602SMark Johnston printf(("%s: " f), __func__, ## __VA_ARGS__); \
85e5054602SMark Johnston } while (0)
86e5054602SMark Johnston
87a5732433SBryan Drewery static void netdump_cleanup(void);
886b6e2954SConrad Meyer static int netdump_configure(struct diocskerneldump_arg *,
896b6e2954SConrad Meyer struct thread *);
90e5054602SMark Johnston static int netdump_dumper(void *priv __unused, void *virtual,
91489ba222SMitchell Horne off_t offset, size_t length);
9264e7d18fSConrad Meyer static bool netdump_enabled(void);
9364e7d18fSConrad Meyer static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS);
94e5054602SMark Johnston static int netdump_ioctl(struct cdev *dev __unused, u_long cmd,
95e5054602SMark Johnston caddr_t addr, int flags __unused, struct thread *td);
96e5054602SMark Johnston static int netdump_modevent(module_t mod, int type, void *priv);
9713a58148SEric van Gyzen static int netdump_start(struct dumperinfo *di, void *key,
9813a58148SEric van Gyzen uint32_t keysize);
9964e7d18fSConrad Meyer static void netdump_unconfigure(void);
100e5054602SMark Johnston
101e5054602SMark Johnston /* Must be at least as big as the chunks dumpsys() gives us. */
102e5054602SMark Johnston static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
1037790c8c1SConrad Meyer static int dump_failed;
104e5054602SMark Johnston
105e5054602SMark Johnston /* Configuration parameters. */
1066144b50fSConrad Meyer static struct {
1076144b50fSConrad Meyer char ndc_iface[IFNAMSIZ];
1086144b50fSConrad Meyer union kd_ip ndc_server;
1096144b50fSConrad Meyer union kd_ip ndc_client;
1106144b50fSConrad Meyer union kd_ip ndc_gateway;
1116144b50fSConrad Meyer uint8_t ndc_af;
1128726929dSMark Johnston /* Runtime State */
1137790c8c1SConrad Meyer struct debugnet_pcb *nd_pcb;
1148726929dSMark Johnston off_t nd_tx_off;
1158726929dSMark Johnston size_t nd_buf_len;
1166144b50fSConrad Meyer } nd_conf;
1176144b50fSConrad Meyer #define nd_server nd_conf.ndc_server.in4
1186144b50fSConrad Meyer #define nd_client nd_conf.ndc_client.in4
1196144b50fSConrad Meyer #define nd_gateway nd_conf.ndc_gateway.in4
120e5054602SMark Johnston
121e5054602SMark Johnston /* General dynamic settings. */
12264e7d18fSConrad Meyer static struct sx nd_conf_lk;
12364e7d18fSConrad Meyer SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock");
12464e7d18fSConrad Meyer #define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk)
12564e7d18fSConrad Meyer #define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk)
12664e7d18fSConrad Meyer #define NETDUMP_RLOCK() sx_slock(&nd_conf_lk)
12764e7d18fSConrad Meyer #define NETDUMP_RUNLOCK() sx_sunlock(&nd_conf_lk)
12864e7d18fSConrad Meyer #define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED)
12964e7d18fSConrad Meyer #define NETDUMP_ASSERT_LOCKED() sx_assert(&nd_conf_lk, SA_LOCKED)
130e5054602SMark Johnston static struct ifnet *nd_ifp;
13164e7d18fSConrad Meyer static eventhandler_tag nd_detach_cookie;
132e5054602SMark Johnston
133e5054602SMark Johnston FEATURE(netdump, "Netdump client support");
134e5054602SMark Johnston
1357029da5cSPawel Biernacki static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
136e5054602SMark Johnston "netdump parameters");
137e5054602SMark Johnston
138e5054602SMark Johnston static int nd_debug;
139e5054602SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
140e5054602SMark Johnston &nd_debug, 0,
141e5054602SMark Johnston "Debug message verbosity");
1427029da5cSPawel Biernacki SYSCTL_PROC(_net_netdump, OID_AUTO, enabled,
1437029da5cSPawel Biernacki CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE, NULL, 0,
1447029da5cSPawel Biernacki netdump_enabled_sysctl, "I",
1457029da5cSPawel Biernacki "netdump configuration status");
146e5054602SMark Johnston static char nd_path[MAXPATHLEN];
147e5054602SMark Johnston SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW,
148e5054602SMark Johnston nd_path, sizeof(nd_path),
149e5054602SMark Johnston "Server path for output files");
1507790c8c1SConrad Meyer /*
1517790c8c1SConrad Meyer * The following three variables were moved to debugnet(4), but these knobs
1527790c8c1SConrad Meyer * were retained as aliases.
1537790c8c1SConrad Meyer */
154da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN,
1557790c8c1SConrad Meyer &debugnet_npolls, 0,
156da7d7778SMark Johnston "Number of times to poll before assuming packet loss (0.5ms per poll)");
157da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN,
1587790c8c1SConrad Meyer &debugnet_nretries, 0,
159da7d7778SMark Johnston "Number of retransmit attempts before giving up");
160da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN,
1617790c8c1SConrad Meyer &debugnet_arp_nretries, 0,
162da7d7778SMark Johnston "Number of ARP attempts before giving up");
163e5054602SMark Johnston
164fde2cf65SConrad Meyer static bool nd_is_enabled;
16564e7d18fSConrad Meyer static bool
netdump_enabled(void)16664e7d18fSConrad Meyer netdump_enabled(void)
16764e7d18fSConrad Meyer {
16864e7d18fSConrad Meyer
16964e7d18fSConrad Meyer NETDUMP_ASSERT_LOCKED();
170fde2cf65SConrad Meyer return (nd_is_enabled);
171fde2cf65SConrad Meyer }
172fde2cf65SConrad Meyer
173fde2cf65SConrad Meyer static void
netdump_set_enabled(bool status)174fde2cf65SConrad Meyer netdump_set_enabled(bool status)
175fde2cf65SConrad Meyer {
176fde2cf65SConrad Meyer
177fde2cf65SConrad Meyer NETDUMP_ASSERT_LOCKED();
178fde2cf65SConrad Meyer nd_is_enabled = status;
17964e7d18fSConrad Meyer }
18064e7d18fSConrad Meyer
18164e7d18fSConrad Meyer static int
netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)18264e7d18fSConrad Meyer netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)
18364e7d18fSConrad Meyer {
18464e7d18fSConrad Meyer int en, error;
18564e7d18fSConrad Meyer
18664e7d18fSConrad Meyer NETDUMP_RLOCK();
18764e7d18fSConrad Meyer en = netdump_enabled();
18864e7d18fSConrad Meyer NETDUMP_RUNLOCK();
18964e7d18fSConrad Meyer
19064e7d18fSConrad Meyer error = SYSCTL_OUT(req, &en, sizeof(en));
19164e7d18fSConrad Meyer if (error != 0 || req->newptr == NULL)
19264e7d18fSConrad Meyer return (error);
19364e7d18fSConrad Meyer return (EPERM);
19464e7d18fSConrad Meyer }
19564e7d18fSConrad Meyer
196e5054602SMark Johnston /*-
197e5054602SMark Johnston * Dumping specific primitives.
198e5054602SMark Johnston */
199e5054602SMark Johnston
200e5054602SMark Johnston /*
201ccdc9866SMark Johnston * Flush any buffered vmcore data.
202ccdc9866SMark Johnston */
203ccdc9866SMark Johnston static int
netdump_flush_buf(void)204ccdc9866SMark Johnston netdump_flush_buf(void)
205ccdc9866SMark Johnston {
206ccdc9866SMark Johnston int error;
207ccdc9866SMark Johnston
208ccdc9866SMark Johnston error = 0;
209ccdc9866SMark Johnston if (nd_conf.nd_buf_len != 0) {
2107790c8c1SConrad Meyer struct debugnet_proto_aux auxdata = {
2117790c8c1SConrad Meyer .dp_offset_start = nd_conf.nd_tx_off,
2127790c8c1SConrad Meyer };
2137790c8c1SConrad Meyer error = debugnet_send(nd_conf.nd_pcb, DEBUGNET_DATA, nd_buf,
2147790c8c1SConrad Meyer nd_conf.nd_buf_len, &auxdata);
215ccdc9866SMark Johnston if (error == 0)
216ccdc9866SMark Johnston nd_conf.nd_buf_len = 0;
217ccdc9866SMark Johnston }
218ccdc9866SMark Johnston return (error);
219ccdc9866SMark Johnston }
220ccdc9866SMark Johnston
221ccdc9866SMark Johnston /*
222e5054602SMark Johnston * Callback from dumpsys() to dump a chunk of memory.
223e5054602SMark Johnston * Copies it out to our static buffer then sends it across the network.
224e5054602SMark Johnston * Detects the initial KDH and makes sure it is given a special packet type.
225e5054602SMark Johnston *
226e5054602SMark Johnston * Parameters:
227e5054602SMark Johnston * priv Unused. Optional private pointer.
228e5054602SMark Johnston * virtual Virtual address (where to read the data from)
229e5054602SMark Johnston * offset Offset from start of core file
230e5054602SMark Johnston * length Data length
231e5054602SMark Johnston *
232e5054602SMark Johnston * Return value:
233e5054602SMark Johnston * 0 on success
234e5054602SMark Johnston * errno on error
235e5054602SMark Johnston */
236e5054602SMark Johnston static int
netdump_dumper(void * priv __unused,void * virtual,off_t offset,size_t length)237489ba222SMitchell Horne netdump_dumper(void *priv __unused, void *virtual, off_t offset, size_t length)
238e5054602SMark Johnston {
239e5054602SMark Johnston int error;
240e5054602SMark Johnston
241e5054602SMark Johnston NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n",
242e5054602SMark Johnston virtual, (uintmax_t)offset, length);
243e5054602SMark Johnston
244e5054602SMark Johnston if (virtual == NULL) {
245ccdc9866SMark Johnston error = netdump_flush_buf();
246ccdc9866SMark Johnston if (error != 0)
2478726929dSMark Johnston dump_failed = 1;
2488726929dSMark Johnston
249e5054602SMark Johnston if (dump_failed != 0)
250e5054602SMark Johnston printf("failed to dump the kernel core\n");
2517790c8c1SConrad Meyer else if (
2527790c8c1SConrad Meyer debugnet_sendempty(nd_conf.nd_pcb, DEBUGNET_FINISHED) != 0)
253e5054602SMark Johnston printf("failed to close the transaction\n");
254e5054602SMark Johnston else
255e5054602SMark Johnston printf("\nnetdump finished.\n");
256a5732433SBryan Drewery netdump_cleanup();
257e5054602SMark Johnston return (0);
258e5054602SMark Johnston }
259a5732433SBryan Drewery if (length > sizeof(nd_buf)) {
260a5732433SBryan Drewery netdump_cleanup();
261e5054602SMark Johnston return (ENOSPC);
262a5732433SBryan Drewery }
263e5054602SMark Johnston
2648726929dSMark Johnston if (nd_conf.nd_buf_len + length > sizeof(nd_buf) ||
2658726929dSMark Johnston (nd_conf.nd_buf_len != 0 && nd_conf.nd_tx_off +
2668726929dSMark Johnston nd_conf.nd_buf_len != offset)) {
267ccdc9866SMark Johnston error = netdump_flush_buf();
268e5054602SMark Johnston if (error != 0) {
269e5054602SMark Johnston dump_failed = 1;
270a5732433SBryan Drewery netdump_cleanup();
271e5054602SMark Johnston return (error);
272e5054602SMark Johnston }
2738726929dSMark Johnston nd_conf.nd_tx_off = offset;
2748726929dSMark Johnston }
2758726929dSMark Johnston
2768726929dSMark Johnston memmove(nd_buf + nd_conf.nd_buf_len, virtual, length);
2778726929dSMark Johnston nd_conf.nd_buf_len += length;
2788726929dSMark Johnston
279e5054602SMark Johnston return (0);
280e5054602SMark Johnston }
281e5054602SMark Johnston
282e5054602SMark Johnston /*
2835aa0576bSEd Maste * Perform any initialization needed prior to transmitting the kernel core.
284e5054602SMark Johnston */
285e5054602SMark Johnston static int
netdump_start(struct dumperinfo * di,void * key,uint32_t keysize)28613a58148SEric van Gyzen netdump_start(struct dumperinfo *di, void *key, uint32_t keysize)
287e5054602SMark Johnston {
2887790c8c1SConrad Meyer struct debugnet_conn_params dcp;
2897790c8c1SConrad Meyer struct debugnet_pcb *pcb;
290e5054602SMark Johnston char buf[INET_ADDRSTRLEN];
291e5054602SMark Johnston int error;
292e5054602SMark Johnston
293e5054602SMark Johnston error = 0;
294e5054602SMark Johnston
295e5054602SMark Johnston /* Check if the dumping is allowed to continue. */
29664e7d18fSConrad Meyer if (!netdump_enabled())
297e5054602SMark Johnston return (EINVAL);
298e5054602SMark Johnston
299879e0604SMateusz Guzik if (!KERNEL_PANICKED()) {
300e5054602SMark Johnston printf(
301e5054602SMark Johnston "netdump_start: netdump may only be used after a panic\n");
302e5054602SMark Johnston return (EINVAL);
303e5054602SMark Johnston }
304e5054602SMark Johnston
3057790c8c1SConrad Meyer memset(&dcp, 0, sizeof(dcp));
3067790c8c1SConrad Meyer
307e5054602SMark Johnston if (nd_server.s_addr == INADDR_ANY) {
308e5054602SMark Johnston printf("netdump_start: can't netdump; no server IP given\n");
309e5054602SMark Johnston return (EINVAL);
310e5054602SMark Johnston }
311e5054602SMark Johnston
312e5054602SMark Johnston /* We start dumping at offset 0. */
313e5054602SMark Johnston di->dumpoff = 0;
314e5054602SMark Johnston
3157790c8c1SConrad Meyer dcp.dc_ifp = nd_ifp;
316e5054602SMark Johnston
3177790c8c1SConrad Meyer dcp.dc_client = nd_client.s_addr;
3187790c8c1SConrad Meyer dcp.dc_server = nd_server.s_addr;
3197790c8c1SConrad Meyer dcp.dc_gateway = nd_gateway.s_addr;
320e5054602SMark Johnston
3217790c8c1SConrad Meyer dcp.dc_herald_port = NETDUMP_PORT;
322e9c69625SConrad Meyer dcp.dc_client_port = NETDUMP_ACKPORT;
323e5054602SMark Johnston
3247790c8c1SConrad Meyer dcp.dc_herald_data = nd_path;
3257790c8c1SConrad Meyer dcp.dc_herald_datalen = (nd_path[0] == 0) ? 0 : strlen(nd_path) + 1;
326e5054602SMark Johnston
3277790c8c1SConrad Meyer error = debugnet_connect(&dcp, &pcb);
3287790c8c1SConrad Meyer if (error != 0) {
329e5054602SMark Johnston printf("failed to contact netdump server\n");
3307790c8c1SConrad Meyer /* Squash debugnet to something the dumper code understands. */
3317790c8c1SConrad Meyer return (EINVAL);
332e5054602SMark Johnston }
333e5054602SMark Johnston
3347790c8c1SConrad Meyer printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf),
3357790c8c1SConrad Meyer debugnet_get_gw_mac(pcb), ":");
3367790c8c1SConrad Meyer nd_conf.nd_pcb = pcb;
33713a58148SEric van Gyzen
33813a58148SEric van Gyzen /* Send the key before the dump so a partial dump is still usable. */
33913a58148SEric van Gyzen if (keysize > 0) {
34013a58148SEric van Gyzen if (keysize > sizeof(nd_buf)) {
34113a58148SEric van Gyzen printf("crypto key is too large (%u)\n", keysize);
34213a58148SEric van Gyzen error = EINVAL;
34313a58148SEric van Gyzen goto out;
34413a58148SEric van Gyzen }
34513a58148SEric van Gyzen memcpy(nd_buf, key, keysize);
34613a58148SEric van Gyzen error = debugnet_send(pcb, NETDUMP_EKCD_KEY, nd_buf, keysize,
34713a58148SEric van Gyzen NULL);
34813a58148SEric van Gyzen if (error != 0) {
34913a58148SEric van Gyzen printf("error %d sending crypto key\n", error);
35013a58148SEric van Gyzen goto out;
35113a58148SEric van Gyzen }
35213a58148SEric van Gyzen }
35313a58148SEric van Gyzen
35413a58148SEric van Gyzen out:
35513a58148SEric van Gyzen if (error != 0) {
35613a58148SEric van Gyzen /* As above, squash errors. */
35713a58148SEric van Gyzen error = EINVAL;
35813a58148SEric van Gyzen netdump_cleanup();
35913a58148SEric van Gyzen }
36013a58148SEric van Gyzen return (error);
361e5054602SMark Johnston }
362e5054602SMark Johnston
363e5054602SMark Johnston static int
netdump_write_headers(struct dumperinfo * di,struct kerneldumpheader * kdh)36413a58148SEric van Gyzen netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh)
365e5054602SMark Johnston {
366e5054602SMark Johnston int error;
367e5054602SMark Johnston
368ccdc9866SMark Johnston error = netdump_flush_buf();
369ccdc9866SMark Johnston if (error != 0)
370a5732433SBryan Drewery goto out;
371e5054602SMark Johnston memcpy(nd_buf, kdh, sizeof(*kdh));
3727790c8c1SConrad Meyer error = debugnet_send(nd_conf.nd_pcb, NETDUMP_KDH, nd_buf,
3737790c8c1SConrad Meyer sizeof(*kdh), NULL);
374a5732433SBryan Drewery out:
375a5732433SBryan Drewery if (error != 0)
376a5732433SBryan Drewery netdump_cleanup();
377e5054602SMark Johnston return (error);
378e5054602SMark Johnston }
379e5054602SMark Johnston
380a5732433SBryan Drewery /*
381a5732433SBryan Drewery * Cleanup routine for a possibly failed netdump.
382a5732433SBryan Drewery */
383a5732433SBryan Drewery static void
netdump_cleanup(void)384a5732433SBryan Drewery netdump_cleanup(void)
385a5732433SBryan Drewery {
386a5732433SBryan Drewery if (nd_conf.nd_pcb != NULL) {
387a5732433SBryan Drewery debugnet_free(nd_conf.nd_pcb);
388a5732433SBryan Drewery nd_conf.nd_pcb = NULL;
389a5732433SBryan Drewery }
390a5732433SBryan Drewery }
391a5732433SBryan Drewery
392e5054602SMark Johnston /*-
393e5054602SMark Johnston * KLD specific code.
394e5054602SMark Johnston */
395e5054602SMark Johnston
396e5054602SMark Johnston static struct cdevsw netdump_cdevsw = {
397e5054602SMark Johnston .d_version = D_VERSION,
398e5054602SMark Johnston .d_ioctl = netdump_ioctl,
399e5054602SMark Johnston .d_name = "netdump",
400e5054602SMark Johnston };
401e5054602SMark Johnston
402e5054602SMark Johnston static struct cdev *netdump_cdev;
403e5054602SMark Johnston
40464e7d18fSConrad Meyer static void
netdump_unconfigure(void)40564e7d18fSConrad Meyer netdump_unconfigure(void)
40664e7d18fSConrad Meyer {
40764e7d18fSConrad Meyer struct diocskerneldump_arg kda;
40864e7d18fSConrad Meyer
40964e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED();
410fde2cf65SConrad Meyer KASSERT(netdump_enabled(), ("%s: not enabled", __func__));
41164e7d18fSConrad Meyer
41264e7d18fSConrad Meyer bzero(&kda, sizeof(kda));
41364e7d18fSConrad Meyer kda.kda_index = KDA_REMOVE_DEV;
41464e7d18fSConrad Meyer (void)dumper_remove(nd_conf.ndc_iface, &kda);
41564e7d18fSConrad Meyer
416fde2cf65SConrad Meyer if (nd_ifp != NULL)
41764e7d18fSConrad Meyer if_rele(nd_ifp);
41864e7d18fSConrad Meyer nd_ifp = NULL;
419fde2cf65SConrad Meyer netdump_set_enabled(false);
42064e7d18fSConrad Meyer
4217790c8c1SConrad Meyer log(LOG_WARNING, "netdump: Lost configured interface %s\n",
4227790c8c1SConrad Meyer nd_conf.ndc_iface);
4237790c8c1SConrad Meyer
42464e7d18fSConrad Meyer bzero(&nd_conf, sizeof(nd_conf));
42564e7d18fSConrad Meyer }
42664e7d18fSConrad Meyer
42764e7d18fSConrad Meyer static void
netdump_ifdetach(void * arg __unused,struct ifnet * ifp)42864e7d18fSConrad Meyer netdump_ifdetach(void *arg __unused, struct ifnet *ifp)
42964e7d18fSConrad Meyer {
43064e7d18fSConrad Meyer
43164e7d18fSConrad Meyer NETDUMP_WLOCK();
43264e7d18fSConrad Meyer if (ifp == nd_ifp)
43364e7d18fSConrad Meyer netdump_unconfigure();
43464e7d18fSConrad Meyer NETDUMP_WUNLOCK();
43564e7d18fSConrad Meyer }
43664e7d18fSConrad Meyer
4378270d35eSConrad Meyer /*
4388270d35eSConrad Meyer * td of NULL is a sentinel value that indicates a kernel caller (ddb(4) or
4398270d35eSConrad Meyer * modload-based tunable parameters).
4408270d35eSConrad Meyer */
441e5054602SMark Johnston static int
netdump_configure(struct diocskerneldump_arg * conf,struct thread * td)4426b6e2954SConrad Meyer netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
443e5054602SMark Johnston {
444e5054602SMark Johnston struct ifnet *ifp;
445e5054602SMark Johnston
44664e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED();
44764e7d18fSConrad Meyer
448fde2cf65SConrad Meyer if (conf->kda_iface[0] != 0) {
4494ad8cb68SMichael Tuexen if (td != NULL && !IS_DEFAULT_VNET(TD_TO_VNET(td)))
450b35822d9SMark Johnston return (EINVAL);
4514ad8cb68SMichael Tuexen CURVNET_SET(vnet0);
45264e7d18fSConrad Meyer ifp = ifunit_ref(conf->kda_iface);
453b35822d9SMark Johnston CURVNET_RESTORE();
454*d94d07d5SMark Johnston if (ifp == NULL)
455*d94d07d5SMark Johnston return (ENODEV);
45638a36057SMitchell Horne if (!DEBUGNET_SUPPORTED_NIC(ifp)) {
45738a36057SMitchell Horne if_rele(ifp);
45838a36057SMitchell Horne return (ENODEV);
45938a36057SMitchell Horne }
460fde2cf65SConrad Meyer } else
461fde2cf65SConrad Meyer ifp = NULL;
462e5054602SMark Johnston
463fde2cf65SConrad Meyer if (nd_ifp != NULL)
46464e7d18fSConrad Meyer if_rele(nd_ifp);
465e5054602SMark Johnston nd_ifp = ifp;
466fde2cf65SConrad Meyer netdump_set_enabled(true);
4676144b50fSConrad Meyer
4686144b50fSConrad Meyer #define COPY_SIZED(elm) do { \
4696144b50fSConrad Meyer _Static_assert(sizeof(nd_conf.ndc_ ## elm) == \
4706144b50fSConrad Meyer sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
4716144b50fSConrad Meyer memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm, \
4726144b50fSConrad Meyer sizeof(nd_conf.ndc_ ## elm)); \
4736144b50fSConrad Meyer } while (0)
4746144b50fSConrad Meyer COPY_SIZED(iface);
4756144b50fSConrad Meyer COPY_SIZED(server);
4766144b50fSConrad Meyer COPY_SIZED(client);
4776144b50fSConrad Meyer COPY_SIZED(gateway);
4786144b50fSConrad Meyer COPY_SIZED(af);
4796144b50fSConrad Meyer #undef COPY_SIZED
48064e7d18fSConrad Meyer
481e5054602SMark Johnston return (0);
482e5054602SMark Johnston }
483e5054602SMark Johnston
484e5054602SMark Johnston /*
485e5054602SMark Johnston * ioctl(2) handler for the netdump device. This is currently only used to
486e5054602SMark Johnston * register netdump as a dump device.
487e5054602SMark Johnston *
488e5054602SMark Johnston * Parameters:
489e5054602SMark Johnston * dev, Unused.
490e5054602SMark Johnston * cmd, The ioctl to be handled.
491e5054602SMark Johnston * addr, The parameter for the ioctl.
492e5054602SMark Johnston * flags, Unused.
493e5054602SMark Johnston * td, The thread invoking this ioctl.
494e5054602SMark Johnston *
495e5054602SMark Johnston * Returns:
496e5054602SMark Johnston * 0 on success, and an errno value on failure.
497e5054602SMark Johnston */
498e5054602SMark Johnston static int
netdump_ioctl(struct cdev * dev __unused,u_long cmd,caddr_t addr,int flags __unused,struct thread * td)499e5054602SMark Johnston netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
500e5054602SMark Johnston int flags __unused, struct thread *td)
501e5054602SMark Johnston {
5020a90043eSMitchell Horne struct diocskerneldump_arg *conf;
503e5054602SMark Johnston struct dumperinfo dumper;
504e5054602SMark Johnston uint8_t *encryptedkey;
505e5054602SMark Johnston int error;
506e5054602SMark Johnston
5076b6e2954SConrad Meyer conf = NULL;
508e5054602SMark Johnston error = 0;
50964e7d18fSConrad Meyer NETDUMP_WLOCK();
51064e7d18fSConrad Meyer
511e5054602SMark Johnston switch (cmd) {
5126b6e2954SConrad Meyer case DIOCGKERNELDUMP:
5136b6e2954SConrad Meyer conf = (void *)addr;
5146b6e2954SConrad Meyer /*
5156b6e2954SConrad Meyer * For now, index is ignored; netdump doesn't support multiple
5166b6e2954SConrad Meyer * configurations (yet).
5176b6e2954SConrad Meyer */
51864e7d18fSConrad Meyer if (!netdump_enabled()) {
5196b6e2954SConrad Meyer error = ENXIO;
5206b6e2954SConrad Meyer conf = NULL;
5216b6e2954SConrad Meyer break;
5226b6e2954SConrad Meyer }
5236b6e2954SConrad Meyer
524fde2cf65SConrad Meyer if (nd_ifp != NULL)
5256b6e2954SConrad Meyer strlcpy(conf->kda_iface, nd_ifp->if_xname,
5266b6e2954SConrad Meyer sizeof(conf->kda_iface));
5276b6e2954SConrad Meyer memcpy(&conf->kda_server, &nd_server, sizeof(nd_server));
5286b6e2954SConrad Meyer memcpy(&conf->kda_client, &nd_client, sizeof(nd_client));
5296b6e2954SConrad Meyer memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway));
5306144b50fSConrad Meyer conf->kda_af = nd_conf.ndc_af;
5316b6e2954SConrad Meyer conf = NULL;
5326b6e2954SConrad Meyer break;
5336b6e2954SConrad Meyer case DIOCSKERNELDUMP:
5346b6e2954SConrad Meyer encryptedkey = NULL;
5356b6e2954SConrad Meyer conf = (void *)addr;
5360a90043eSMitchell Horne
5376b6e2954SConrad Meyer /* Netdump only supports IP4 at this time. */
5386b6e2954SConrad Meyer if (conf->kda_af != AF_INET) {
5396b6e2954SConrad Meyer error = EPROTONOSUPPORT;
5406b6e2954SConrad Meyer break;
5416b6e2954SConrad Meyer }
5426b6e2954SConrad Meyer
5436b6e2954SConrad Meyer conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0';
5446b6e2954SConrad Meyer if (conf->kda_index == KDA_REMOVE ||
5456b6e2954SConrad Meyer conf->kda_index == KDA_REMOVE_DEV ||
5466b6e2954SConrad Meyer conf->kda_index == KDA_REMOVE_ALL) {
54764e7d18fSConrad Meyer if (netdump_enabled())
54864e7d18fSConrad Meyer netdump_unconfigure();
54964e7d18fSConrad Meyer if (conf->kda_index == KDA_REMOVE_ALL)
55064e7d18fSConrad Meyer error = dumper_remove(NULL, conf);
551e5054602SMark Johnston break;
552e5054602SMark Johnston }
553e5054602SMark Johnston
554b35822d9SMark Johnston error = netdump_configure(conf, td);
555e5054602SMark Johnston if (error != 0)
556e5054602SMark Johnston break;
557e5054602SMark Johnston
5586b6e2954SConrad Meyer if (conf->kda_encryption != KERNELDUMP_ENC_NONE) {
5596b6e2954SConrad Meyer if (conf->kda_encryptedkeysize <= 0 ||
5606b6e2954SConrad Meyer conf->kda_encryptedkeysize >
5616b6e2954SConrad Meyer KERNELDUMP_ENCKEY_MAX_SIZE) {
5626b6e2954SConrad Meyer error = EINVAL;
5636b6e2954SConrad Meyer break;
5646b6e2954SConrad Meyer }
5656b6e2954SConrad Meyer encryptedkey = malloc(conf->kda_encryptedkeysize,
5666b6e2954SConrad Meyer M_TEMP, M_WAITOK);
5676b6e2954SConrad Meyer error = copyin(conf->kda_encryptedkey, encryptedkey,
5686b6e2954SConrad Meyer conf->kda_encryptedkeysize);
569e5054602SMark Johnston if (error != 0) {
570e5054602SMark Johnston free(encryptedkey, M_TEMP);
5716b6e2954SConrad Meyer break;
572e5054602SMark Johnston }
5736b6e2954SConrad Meyer
5746b6e2954SConrad Meyer conf->kda_encryptedkey = encryptedkey;
575e5054602SMark Johnston }
576e5054602SMark Johnston
5779f78e2b8SMark Johnston memset(&dumper, 0, sizeof(dumper));
578e5054602SMark Johnston dumper.dumper_start = netdump_start;
579e5054602SMark Johnston dumper.dumper_hdr = netdump_write_headers;
580e5054602SMark Johnston dumper.dumper = netdump_dumper;
581e5054602SMark Johnston dumper.priv = NULL;
582e5054602SMark Johnston dumper.blocksize = NETDUMP_DATASIZE;
583e5054602SMark Johnston dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE;
584e5054602SMark Johnston dumper.mediaoffset = 0;
585e5054602SMark Johnston dumper.mediasize = 0;
586e5054602SMark Johnston
5876b6e2954SConrad Meyer error = dumper_insert(&dumper, conf->kda_iface, conf);
5884a711b8dSJohn Baldwin zfree(encryptedkey, M_TEMP);
58964e7d18fSConrad Meyer if (error != 0)
59064e7d18fSConrad Meyer netdump_unconfigure();
591e5054602SMark Johnston break;
592e5054602SMark Johnston default:
5936b6e2954SConrad Meyer error = ENOTTY;
594e5054602SMark Johnston break;
595e5054602SMark Johnston }
5966b6e2954SConrad Meyer if (conf != NULL)
5976b6e2954SConrad Meyer explicit_bzero(conf, sizeof(*conf));
59864e7d18fSConrad Meyer NETDUMP_WUNLOCK();
599e5054602SMark Johnston return (error);
600e5054602SMark Johnston }
601e5054602SMark Johnston
602e5054602SMark Johnston /*
603e5054602SMark Johnston * Called upon system init or kld load. Initializes the netdump parameters to
604e5054602SMark Johnston * sane defaults (locates the first available NIC and uses the first IPv4 IP on
605e5054602SMark Johnston * that card as the client IP). Leaves the server IP unconfigured.
606e5054602SMark Johnston *
607e5054602SMark Johnston * Parameters:
608e5054602SMark Johnston * mod, Unused.
609e5054602SMark Johnston * what, The module event type.
610e5054602SMark Johnston * priv, Unused.
611e5054602SMark Johnston *
612e5054602SMark Johnston * Returns:
613347b1991SGordon Bergling * int, An errno value if an error occurred, 0 otherwise.
614e5054602SMark Johnston */
615e5054602SMark Johnston static int
netdump_modevent(module_t mod __unused,int what,void * priv __unused)616e5054602SMark Johnston netdump_modevent(module_t mod __unused, int what, void *priv __unused)
617e5054602SMark Johnston {
6186b6e2954SConrad Meyer struct diocskerneldump_arg conf;
619e5054602SMark Johnston char *arg;
620e5054602SMark Johnston int error;
621e5054602SMark Johnston
622e5054602SMark Johnston error = 0;
623e5054602SMark Johnston switch (what) {
624e5054602SMark Johnston case MOD_LOAD:
625e5054602SMark Johnston error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev,
626e5054602SMark Johnston &netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump");
627e5054602SMark Johnston if (error != 0)
628e5054602SMark Johnston return (error);
629e5054602SMark Johnston
63064e7d18fSConrad Meyer nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
63164e7d18fSConrad Meyer netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
63264e7d18fSConrad Meyer
633e5054602SMark Johnston if ((arg = kern_getenv("net.dump.iface")) != NULL) {
6346b6e2954SConrad Meyer strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
635e5054602SMark Johnston freeenv(arg);
636e5054602SMark Johnston
637e5054602SMark Johnston if ((arg = kern_getenv("net.dump.server")) != NULL) {
6386b6e2954SConrad Meyer inet_aton(arg, &conf.kda_server.in4);
639e5054602SMark Johnston freeenv(arg);
640e5054602SMark Johnston }
641e5054602SMark Johnston if ((arg = kern_getenv("net.dump.client")) != NULL) {
642070e7bf9SConrad Meyer inet_aton(arg, &conf.kda_client.in4);
643e5054602SMark Johnston freeenv(arg);
644e5054602SMark Johnston }
645e5054602SMark Johnston if ((arg = kern_getenv("net.dump.gateway")) != NULL) {
646070e7bf9SConrad Meyer inet_aton(arg, &conf.kda_gateway.in4);
647e5054602SMark Johnston freeenv(arg);
648e5054602SMark Johnston }
6496b6e2954SConrad Meyer conf.kda_af = AF_INET;
650e5054602SMark Johnston
651e5054602SMark Johnston /* Ignore errors; we print a message to the console. */
65264e7d18fSConrad Meyer NETDUMP_WLOCK();
6538270d35eSConrad Meyer (void)netdump_configure(&conf, NULL);
65464e7d18fSConrad Meyer NETDUMP_WUNLOCK();
655e5054602SMark Johnston }
656e5054602SMark Johnston break;
657e5054602SMark Johnston case MOD_UNLOAD:
65864e7d18fSConrad Meyer NETDUMP_WLOCK();
65964e7d18fSConrad Meyer if (netdump_enabled()) {
660e5054602SMark Johnston printf("netdump: disabling dump device for unload\n");
66164e7d18fSConrad Meyer netdump_unconfigure();
662e5054602SMark Johnston }
66364e7d18fSConrad Meyer NETDUMP_WUNLOCK();
6646b6e2954SConrad Meyer destroy_dev(netdump_cdev);
66564e7d18fSConrad Meyer EVENTHANDLER_DEREGISTER(ifnet_departure_event,
66664e7d18fSConrad Meyer nd_detach_cookie);
667e5054602SMark Johnston break;
668e5054602SMark Johnston default:
669e5054602SMark Johnston error = EOPNOTSUPP;
670e5054602SMark Johnston break;
671e5054602SMark Johnston }
672e5054602SMark Johnston return (error);
673e5054602SMark Johnston }
674e5054602SMark Johnston
675e5054602SMark Johnston static moduledata_t netdump_mod = {
676e5054602SMark Johnston "netdump",
677e5054602SMark Johnston netdump_modevent,
678e5054602SMark Johnston NULL,
679e5054602SMark Johnston };
680e5054602SMark Johnston
681e5054602SMark Johnston MODULE_VERSION(netdump, 1);
682e5054602SMark Johnston DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
6838270d35eSConrad Meyer
6848270d35eSConrad Meyer #ifdef DDB
6858270d35eSConrad Meyer /*
6868270d35eSConrad Meyer * Usage: netdump -s <server> [-g <gateway] -c <localip> -i <interface>
6878270d35eSConrad Meyer *
6888270d35eSConrad Meyer * Order is not significant.
6898270d35eSConrad Meyer *
6908270d35eSConrad Meyer * Currently, this command does not support configuring encryption or
6918270d35eSConrad Meyer * compression.
6928270d35eSConrad Meyer */
DB_COMMAND_FLAGS(netdump,db_netdump_cmd,CS_OWN)693258958b3SMitchell Horne DB_COMMAND_FLAGS(netdump, db_netdump_cmd, CS_OWN)
6948270d35eSConrad Meyer {
6958270d35eSConrad Meyer static struct diocskerneldump_arg conf;
6968270d35eSConrad Meyer static char blockbuf[NETDUMP_DATASIZE];
6978270d35eSConrad Meyer static union {
6988270d35eSConrad Meyer struct dumperinfo di;
6998270d35eSConrad Meyer /* For valid di_devname. */
7008270d35eSConrad Meyer char di_buf[sizeof(struct dumperinfo) + 1];
7018270d35eSConrad Meyer } u;
7028270d35eSConrad Meyer
7038270d35eSConrad Meyer struct debugnet_ddb_config params;
7048270d35eSConrad Meyer int error;
7058270d35eSConrad Meyer
7068270d35eSConrad Meyer error = debugnet_parse_ddb_cmd("netdump", ¶ms);
7078270d35eSConrad Meyer if (error != 0) {
7088270d35eSConrad Meyer db_printf("Error configuring netdump: %d\n", error);
7098270d35eSConrad Meyer return;
7108270d35eSConrad Meyer }
7118270d35eSConrad Meyer
7128270d35eSConrad Meyer /* Translate to a netdump dumper config. */
7138270d35eSConrad Meyer memset(&conf, 0, sizeof(conf));
714fde2cf65SConrad Meyer
715fde2cf65SConrad Meyer if (params.dd_ifp != NULL)
716fde2cf65SConrad Meyer strlcpy(conf.kda_iface, if_name(params.dd_ifp),
717fde2cf65SConrad Meyer sizeof(conf.kda_iface));
7188270d35eSConrad Meyer
7198270d35eSConrad Meyer conf.kda_af = AF_INET;
7208270d35eSConrad Meyer conf.kda_server.in4 = (struct in_addr) { params.dd_server };
721fde2cf65SConrad Meyer if (params.dd_has_client)
7228270d35eSConrad Meyer conf.kda_client.in4 = (struct in_addr) { params.dd_client };
723fde2cf65SConrad Meyer else
724fde2cf65SConrad Meyer conf.kda_client.in4 = (struct in_addr) { INADDR_ANY };
7258270d35eSConrad Meyer if (params.dd_has_gateway)
7268270d35eSConrad Meyer conf.kda_gateway.in4 = (struct in_addr) { params.dd_gateway };
7278270d35eSConrad Meyer else
7288270d35eSConrad Meyer conf.kda_gateway.in4 = (struct in_addr) { INADDR_ANY };
7298270d35eSConrad Meyer
7308270d35eSConrad Meyer /* Set the global netdump config to these options. */
7318270d35eSConrad Meyer error = netdump_configure(&conf, NULL);
7328270d35eSConrad Meyer if (error != 0) {
7338270d35eSConrad Meyer db_printf("Error enabling netdump: %d\n", error);
7348270d35eSConrad Meyer return;
7358270d35eSConrad Meyer }
7368270d35eSConrad Meyer
7378270d35eSConrad Meyer /* Fake the generic dump configuration list entry to avoid malloc. */
7388270d35eSConrad Meyer memset(&u.di_buf, 0, sizeof(u.di_buf));
7398270d35eSConrad Meyer u.di.dumper_start = netdump_start;
7408270d35eSConrad Meyer u.di.dumper_hdr = netdump_write_headers;
7418270d35eSConrad Meyer u.di.dumper = netdump_dumper;
7428270d35eSConrad Meyer u.di.priv = NULL;
7438270d35eSConrad Meyer u.di.blocksize = NETDUMP_DATASIZE;
7448270d35eSConrad Meyer u.di.maxiosize = MAXDUMPPGS * PAGE_SIZE;
7458270d35eSConrad Meyer u.di.mediaoffset = 0;
7468270d35eSConrad Meyer u.di.mediasize = 0;
7478270d35eSConrad Meyer u.di.blockbuf = blockbuf;
7488270d35eSConrad Meyer
7498270d35eSConrad Meyer dumper_ddb_insert(&u.di);
7508270d35eSConrad Meyer
7518270d35eSConrad Meyer error = doadump(false);
7528270d35eSConrad Meyer
7538270d35eSConrad Meyer dumper_ddb_remove(&u.di);
7548270d35eSConrad Meyer if (error != 0)
7558270d35eSConrad Meyer db_printf("Cannot dump: %d\n", error);
7568270d35eSConrad Meyer }
7578270d35eSConrad Meyer #endif /* DDB */
758