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 378270d35eSConrad Meyer #include "opt_ddb.h" 388270d35eSConrad Meyer 39e5054602SMark Johnston #include <sys/param.h> 40e5054602SMark Johnston #include <sys/conf.h> 41e5054602SMark Johnston #include <sys/disk.h> 42e5054602SMark Johnston #include <sys/endian.h> 4304e0c883SConrad Meyer #include <sys/eventhandler.h> 44b35822d9SMark Johnston #include <sys/jail.h> 45e5054602SMark Johnston #include <sys/kernel.h> 46e5054602SMark Johnston #include <sys/kerneldump.h> 47e5054602SMark Johnston #include <sys/mbuf.h> 48e5054602SMark Johnston #include <sys/module.h> 49e5054602SMark Johnston #include <sys/priv.h> 50e5054602SMark Johnston #include <sys/proc.h> 51e5054602SMark Johnston #include <sys/protosw.h> 52e5054602SMark Johnston #include <sys/socket.h> 53e5054602SMark Johnston #include <sys/sysctl.h> 547790c8c1SConrad Meyer #include <sys/syslog.h> 55e5054602SMark Johnston #include <sys/systm.h> 56e5054602SMark Johnston 578270d35eSConrad Meyer #ifdef DDB 588270d35eSConrad Meyer #include <ddb/ddb.h> 598270d35eSConrad Meyer #include <ddb/db_lex.h> 608270d35eSConrad Meyer #endif 618270d35eSConrad Meyer 62e5054602SMark Johnston #include <net/ethernet.h> 63e5054602SMark Johnston #include <net/if.h> 64e5054602SMark Johnston #include <net/if_arp.h> 65e5054602SMark Johnston #include <net/if_dl.h> 66e5054602SMark Johnston #include <net/if_types.h> 67e5054602SMark Johnston #include <net/if_var.h> 68*3d0d5b21SJustin Hibbits #include <net/if_private.h> 697790c8c1SConrad Meyer #include <net/debugnet.h> 70e5054602SMark Johnston 71e5054602SMark Johnston #include <netinet/in.h> 72e5054602SMark Johnston #include <netinet/in_systm.h> 73e5054602SMark Johnston #include <netinet/in_var.h> 74e5054602SMark Johnston #include <netinet/ip.h> 75e5054602SMark Johnston #include <netinet/ip_var.h> 76e5054602SMark Johnston #include <netinet/ip_options.h> 77e5054602SMark Johnston #include <netinet/udp.h> 78e5054602SMark Johnston #include <netinet/udp_var.h> 79e5054602SMark Johnston #include <netinet/netdump/netdump.h> 80e5054602SMark Johnston 81e5054602SMark Johnston #include <machine/in_cksum.h> 82e5054602SMark Johnston #include <machine/pcb.h> 83e5054602SMark Johnston 84e5054602SMark Johnston #define NETDDEBUGV(f, ...) do { \ 85e5054602SMark Johnston if (nd_debug > 1) \ 86e5054602SMark Johnston printf(("%s: " f), __func__, ## __VA_ARGS__); \ 87e5054602SMark Johnston } while (0) 88e5054602SMark Johnston 89a5732433SBryan Drewery static void netdump_cleanup(void); 906b6e2954SConrad Meyer static int netdump_configure(struct diocskerneldump_arg *, 916b6e2954SConrad Meyer struct thread *); 92e5054602SMark Johnston static int netdump_dumper(void *priv __unused, void *virtual, 93489ba222SMitchell Horne off_t offset, size_t length); 9464e7d18fSConrad Meyer static bool netdump_enabled(void); 9564e7d18fSConrad Meyer static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS); 96e5054602SMark Johnston static int netdump_ioctl(struct cdev *dev __unused, u_long cmd, 97e5054602SMark Johnston caddr_t addr, int flags __unused, struct thread *td); 98e5054602SMark Johnston static int netdump_modevent(module_t mod, int type, void *priv); 9913a58148SEric van Gyzen static int netdump_start(struct dumperinfo *di, void *key, 10013a58148SEric van Gyzen uint32_t keysize); 10164e7d18fSConrad Meyer static void netdump_unconfigure(void); 102e5054602SMark Johnston 103e5054602SMark Johnston /* Must be at least as big as the chunks dumpsys() gives us. */ 104e5054602SMark Johnston static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE]; 1057790c8c1SConrad Meyer static int dump_failed; 106e5054602SMark Johnston 107e5054602SMark Johnston /* Configuration parameters. */ 1086144b50fSConrad Meyer static struct { 1096144b50fSConrad Meyer char ndc_iface[IFNAMSIZ]; 1106144b50fSConrad Meyer union kd_ip ndc_server; 1116144b50fSConrad Meyer union kd_ip ndc_client; 1126144b50fSConrad Meyer union kd_ip ndc_gateway; 1136144b50fSConrad Meyer uint8_t ndc_af; 1148726929dSMark Johnston /* Runtime State */ 1157790c8c1SConrad Meyer struct debugnet_pcb *nd_pcb; 1168726929dSMark Johnston off_t nd_tx_off; 1178726929dSMark Johnston size_t nd_buf_len; 1186144b50fSConrad Meyer } nd_conf; 1196144b50fSConrad Meyer #define nd_server nd_conf.ndc_server.in4 1206144b50fSConrad Meyer #define nd_client nd_conf.ndc_client.in4 1216144b50fSConrad Meyer #define nd_gateway nd_conf.ndc_gateway.in4 122e5054602SMark Johnston 123e5054602SMark Johnston /* General dynamic settings. */ 12464e7d18fSConrad Meyer static struct sx nd_conf_lk; 12564e7d18fSConrad Meyer SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock"); 12664e7d18fSConrad Meyer #define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk) 12764e7d18fSConrad Meyer #define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk) 12864e7d18fSConrad Meyer #define NETDUMP_RLOCK() sx_slock(&nd_conf_lk) 12964e7d18fSConrad Meyer #define NETDUMP_RUNLOCK() sx_sunlock(&nd_conf_lk) 13064e7d18fSConrad Meyer #define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED) 13164e7d18fSConrad Meyer #define NETDUMP_ASSERT_LOCKED() sx_assert(&nd_conf_lk, SA_LOCKED) 132e5054602SMark Johnston static struct ifnet *nd_ifp; 13364e7d18fSConrad Meyer static eventhandler_tag nd_detach_cookie; 134e5054602SMark Johnston 135e5054602SMark Johnston FEATURE(netdump, "Netdump client support"); 136e5054602SMark Johnston 1377029da5cSPawel Biernacki static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 138e5054602SMark Johnston "netdump parameters"); 139e5054602SMark Johnston 140e5054602SMark Johnston static int nd_debug; 141e5054602SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN, 142e5054602SMark Johnston &nd_debug, 0, 143e5054602SMark Johnston "Debug message verbosity"); 1447029da5cSPawel Biernacki SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, 1457029da5cSPawel Biernacki CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE, NULL, 0, 1467029da5cSPawel Biernacki netdump_enabled_sysctl, "I", 1477029da5cSPawel Biernacki "netdump configuration status"); 148e5054602SMark Johnston static char nd_path[MAXPATHLEN]; 149e5054602SMark Johnston SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW, 150e5054602SMark Johnston nd_path, sizeof(nd_path), 151e5054602SMark Johnston "Server path for output files"); 1527790c8c1SConrad Meyer /* 1537790c8c1SConrad Meyer * The following three variables were moved to debugnet(4), but these knobs 1547790c8c1SConrad Meyer * were retained as aliases. 1557790c8c1SConrad Meyer */ 156da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN, 1577790c8c1SConrad Meyer &debugnet_npolls, 0, 158da7d7778SMark Johnston "Number of times to poll before assuming packet loss (0.5ms per poll)"); 159da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN, 1607790c8c1SConrad Meyer &debugnet_nretries, 0, 161da7d7778SMark Johnston "Number of retransmit attempts before giving up"); 162da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN, 1637790c8c1SConrad Meyer &debugnet_arp_nretries, 0, 164da7d7778SMark Johnston "Number of ARP attempts before giving up"); 165e5054602SMark Johnston 166fde2cf65SConrad Meyer static bool nd_is_enabled; 16764e7d18fSConrad Meyer static bool 16864e7d18fSConrad Meyer netdump_enabled(void) 16964e7d18fSConrad Meyer { 17064e7d18fSConrad Meyer 17164e7d18fSConrad Meyer NETDUMP_ASSERT_LOCKED(); 172fde2cf65SConrad Meyer return (nd_is_enabled); 173fde2cf65SConrad Meyer } 174fde2cf65SConrad Meyer 175fde2cf65SConrad Meyer static void 176fde2cf65SConrad Meyer netdump_set_enabled(bool status) 177fde2cf65SConrad Meyer { 178fde2cf65SConrad Meyer 179fde2cf65SConrad Meyer NETDUMP_ASSERT_LOCKED(); 180fde2cf65SConrad Meyer nd_is_enabled = status; 18164e7d18fSConrad Meyer } 18264e7d18fSConrad Meyer 18364e7d18fSConrad Meyer static int 18464e7d18fSConrad Meyer netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS) 18564e7d18fSConrad Meyer { 18664e7d18fSConrad Meyer int en, error; 18764e7d18fSConrad Meyer 18864e7d18fSConrad Meyer NETDUMP_RLOCK(); 18964e7d18fSConrad Meyer en = netdump_enabled(); 19064e7d18fSConrad Meyer NETDUMP_RUNLOCK(); 19164e7d18fSConrad Meyer 19264e7d18fSConrad Meyer error = SYSCTL_OUT(req, &en, sizeof(en)); 19364e7d18fSConrad Meyer if (error != 0 || req->newptr == NULL) 19464e7d18fSConrad Meyer return (error); 19564e7d18fSConrad Meyer return (EPERM); 19664e7d18fSConrad Meyer } 19764e7d18fSConrad Meyer 198e5054602SMark Johnston /*- 199e5054602SMark Johnston * Dumping specific primitives. 200e5054602SMark Johnston */ 201e5054602SMark Johnston 202e5054602SMark Johnston /* 203ccdc9866SMark Johnston * Flush any buffered vmcore data. 204ccdc9866SMark Johnston */ 205ccdc9866SMark Johnston static int 206ccdc9866SMark Johnston netdump_flush_buf(void) 207ccdc9866SMark Johnston { 208ccdc9866SMark Johnston int error; 209ccdc9866SMark Johnston 210ccdc9866SMark Johnston error = 0; 211ccdc9866SMark Johnston if (nd_conf.nd_buf_len != 0) { 2127790c8c1SConrad Meyer struct debugnet_proto_aux auxdata = { 2137790c8c1SConrad Meyer .dp_offset_start = nd_conf.nd_tx_off, 2147790c8c1SConrad Meyer }; 2157790c8c1SConrad Meyer error = debugnet_send(nd_conf.nd_pcb, DEBUGNET_DATA, nd_buf, 2167790c8c1SConrad Meyer nd_conf.nd_buf_len, &auxdata); 217ccdc9866SMark Johnston if (error == 0) 218ccdc9866SMark Johnston nd_conf.nd_buf_len = 0; 219ccdc9866SMark Johnston } 220ccdc9866SMark Johnston return (error); 221ccdc9866SMark Johnston } 222ccdc9866SMark Johnston 223ccdc9866SMark Johnston /* 224e5054602SMark Johnston * Callback from dumpsys() to dump a chunk of memory. 225e5054602SMark Johnston * Copies it out to our static buffer then sends it across the network. 226e5054602SMark Johnston * Detects the initial KDH and makes sure it is given a special packet type. 227e5054602SMark Johnston * 228e5054602SMark Johnston * Parameters: 229e5054602SMark Johnston * priv Unused. Optional private pointer. 230e5054602SMark Johnston * virtual Virtual address (where to read the data from) 231e5054602SMark Johnston * offset Offset from start of core file 232e5054602SMark Johnston * length Data length 233e5054602SMark Johnston * 234e5054602SMark Johnston * Return value: 235e5054602SMark Johnston * 0 on success 236e5054602SMark Johnston * errno on error 237e5054602SMark Johnston */ 238e5054602SMark Johnston static int 239489ba222SMitchell Horne netdump_dumper(void *priv __unused, void *virtual, off_t offset, size_t length) 240e5054602SMark Johnston { 241e5054602SMark Johnston int error; 242e5054602SMark Johnston 243e5054602SMark Johnston NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n", 244e5054602SMark Johnston virtual, (uintmax_t)offset, length); 245e5054602SMark Johnston 246e5054602SMark Johnston if (virtual == NULL) { 247ccdc9866SMark Johnston error = netdump_flush_buf(); 248ccdc9866SMark Johnston if (error != 0) 2498726929dSMark Johnston dump_failed = 1; 2508726929dSMark Johnston 251e5054602SMark Johnston if (dump_failed != 0) 252e5054602SMark Johnston printf("failed to dump the kernel core\n"); 2537790c8c1SConrad Meyer else if ( 2547790c8c1SConrad Meyer debugnet_sendempty(nd_conf.nd_pcb, DEBUGNET_FINISHED) != 0) 255e5054602SMark Johnston printf("failed to close the transaction\n"); 256e5054602SMark Johnston else 257e5054602SMark Johnston printf("\nnetdump finished.\n"); 258a5732433SBryan Drewery netdump_cleanup(); 259e5054602SMark Johnston return (0); 260e5054602SMark Johnston } 261a5732433SBryan Drewery if (length > sizeof(nd_buf)) { 262a5732433SBryan Drewery netdump_cleanup(); 263e5054602SMark Johnston return (ENOSPC); 264a5732433SBryan Drewery } 265e5054602SMark Johnston 2668726929dSMark Johnston if (nd_conf.nd_buf_len + length > sizeof(nd_buf) || 2678726929dSMark Johnston (nd_conf.nd_buf_len != 0 && nd_conf.nd_tx_off + 2688726929dSMark Johnston nd_conf.nd_buf_len != offset)) { 269ccdc9866SMark Johnston error = netdump_flush_buf(); 270e5054602SMark Johnston if (error != 0) { 271e5054602SMark Johnston dump_failed = 1; 272a5732433SBryan Drewery netdump_cleanup(); 273e5054602SMark Johnston return (error); 274e5054602SMark Johnston } 2758726929dSMark Johnston nd_conf.nd_tx_off = offset; 2768726929dSMark Johnston } 2778726929dSMark Johnston 2788726929dSMark Johnston memmove(nd_buf + nd_conf.nd_buf_len, virtual, length); 2798726929dSMark Johnston nd_conf.nd_buf_len += length; 2808726929dSMark Johnston 281e5054602SMark Johnston return (0); 282e5054602SMark Johnston } 283e5054602SMark Johnston 284e5054602SMark Johnston /* 2855aa0576bSEd Maste * Perform any initialization needed prior to transmitting the kernel core. 286e5054602SMark Johnston */ 287e5054602SMark Johnston static int 28813a58148SEric van Gyzen netdump_start(struct dumperinfo *di, void *key, uint32_t keysize) 289e5054602SMark Johnston { 2907790c8c1SConrad Meyer struct debugnet_conn_params dcp; 2917790c8c1SConrad Meyer struct debugnet_pcb *pcb; 292e5054602SMark Johnston char buf[INET_ADDRSTRLEN]; 293e5054602SMark Johnston int error; 294e5054602SMark Johnston 295e5054602SMark Johnston error = 0; 296e5054602SMark Johnston 297e5054602SMark Johnston /* Check if the dumping is allowed to continue. */ 29864e7d18fSConrad Meyer if (!netdump_enabled()) 299e5054602SMark Johnston return (EINVAL); 300e5054602SMark Johnston 301879e0604SMateusz Guzik if (!KERNEL_PANICKED()) { 302e5054602SMark Johnston printf( 303e5054602SMark Johnston "netdump_start: netdump may only be used after a panic\n"); 304e5054602SMark Johnston return (EINVAL); 305e5054602SMark Johnston } 306e5054602SMark Johnston 3077790c8c1SConrad Meyer memset(&dcp, 0, sizeof(dcp)); 3087790c8c1SConrad Meyer 309e5054602SMark Johnston if (nd_server.s_addr == INADDR_ANY) { 310e5054602SMark Johnston printf("netdump_start: can't netdump; no server IP given\n"); 311e5054602SMark Johnston return (EINVAL); 312e5054602SMark Johnston } 313e5054602SMark Johnston 314e5054602SMark Johnston /* We start dumping at offset 0. */ 315e5054602SMark Johnston di->dumpoff = 0; 316e5054602SMark Johnston 3177790c8c1SConrad Meyer dcp.dc_ifp = nd_ifp; 318e5054602SMark Johnston 3197790c8c1SConrad Meyer dcp.dc_client = nd_client.s_addr; 3207790c8c1SConrad Meyer dcp.dc_server = nd_server.s_addr; 3217790c8c1SConrad Meyer dcp.dc_gateway = nd_gateway.s_addr; 322e5054602SMark Johnston 3237790c8c1SConrad Meyer dcp.dc_herald_port = NETDUMP_PORT; 324e9c69625SConrad Meyer dcp.dc_client_port = NETDUMP_ACKPORT; 325e5054602SMark Johnston 3267790c8c1SConrad Meyer dcp.dc_herald_data = nd_path; 3277790c8c1SConrad Meyer dcp.dc_herald_datalen = (nd_path[0] == 0) ? 0 : strlen(nd_path) + 1; 328e5054602SMark Johnston 3297790c8c1SConrad Meyer error = debugnet_connect(&dcp, &pcb); 3307790c8c1SConrad Meyer if (error != 0) { 331e5054602SMark Johnston printf("failed to contact netdump server\n"); 3327790c8c1SConrad Meyer /* Squash debugnet to something the dumper code understands. */ 3337790c8c1SConrad Meyer return (EINVAL); 334e5054602SMark Johnston } 335e5054602SMark Johnston 3367790c8c1SConrad Meyer printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf), 3377790c8c1SConrad Meyer debugnet_get_gw_mac(pcb), ":"); 3387790c8c1SConrad Meyer nd_conf.nd_pcb = pcb; 33913a58148SEric van Gyzen 34013a58148SEric van Gyzen /* Send the key before the dump so a partial dump is still usable. */ 34113a58148SEric van Gyzen if (keysize > 0) { 34213a58148SEric van Gyzen if (keysize > sizeof(nd_buf)) { 34313a58148SEric van Gyzen printf("crypto key is too large (%u)\n", keysize); 34413a58148SEric van Gyzen error = EINVAL; 34513a58148SEric van Gyzen goto out; 34613a58148SEric van Gyzen } 34713a58148SEric van Gyzen memcpy(nd_buf, key, keysize); 34813a58148SEric van Gyzen error = debugnet_send(pcb, NETDUMP_EKCD_KEY, nd_buf, keysize, 34913a58148SEric van Gyzen NULL); 35013a58148SEric van Gyzen if (error != 0) { 35113a58148SEric van Gyzen printf("error %d sending crypto key\n", error); 35213a58148SEric van Gyzen goto out; 35313a58148SEric van Gyzen } 35413a58148SEric van Gyzen } 35513a58148SEric van Gyzen 35613a58148SEric van Gyzen out: 35713a58148SEric van Gyzen if (error != 0) { 35813a58148SEric van Gyzen /* As above, squash errors. */ 35913a58148SEric van Gyzen error = EINVAL; 36013a58148SEric van Gyzen netdump_cleanup(); 36113a58148SEric van Gyzen } 36213a58148SEric van Gyzen return (error); 363e5054602SMark Johnston } 364e5054602SMark Johnston 365e5054602SMark Johnston static int 36613a58148SEric van Gyzen netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh) 367e5054602SMark Johnston { 368e5054602SMark Johnston int error; 369e5054602SMark Johnston 370ccdc9866SMark Johnston error = netdump_flush_buf(); 371ccdc9866SMark Johnston if (error != 0) 372a5732433SBryan Drewery goto out; 373e5054602SMark Johnston memcpy(nd_buf, kdh, sizeof(*kdh)); 3747790c8c1SConrad Meyer error = debugnet_send(nd_conf.nd_pcb, NETDUMP_KDH, nd_buf, 3757790c8c1SConrad Meyer sizeof(*kdh), NULL); 376a5732433SBryan Drewery out: 377a5732433SBryan Drewery if (error != 0) 378a5732433SBryan Drewery netdump_cleanup(); 379e5054602SMark Johnston return (error); 380e5054602SMark Johnston } 381e5054602SMark Johnston 382a5732433SBryan Drewery /* 383a5732433SBryan Drewery * Cleanup routine for a possibly failed netdump. 384a5732433SBryan Drewery */ 385a5732433SBryan Drewery static void 386a5732433SBryan Drewery netdump_cleanup(void) 387a5732433SBryan Drewery { 388a5732433SBryan Drewery if (nd_conf.nd_pcb != NULL) { 389a5732433SBryan Drewery debugnet_free(nd_conf.nd_pcb); 390a5732433SBryan Drewery nd_conf.nd_pcb = NULL; 391a5732433SBryan Drewery } 392a5732433SBryan Drewery } 393a5732433SBryan Drewery 394e5054602SMark Johnston /*- 395e5054602SMark Johnston * KLD specific code. 396e5054602SMark Johnston */ 397e5054602SMark Johnston 398e5054602SMark Johnston static struct cdevsw netdump_cdevsw = { 399e5054602SMark Johnston .d_version = D_VERSION, 400e5054602SMark Johnston .d_ioctl = netdump_ioctl, 401e5054602SMark Johnston .d_name = "netdump", 402e5054602SMark Johnston }; 403e5054602SMark Johnston 404e5054602SMark Johnston static struct cdev *netdump_cdev; 405e5054602SMark Johnston 40664e7d18fSConrad Meyer static void 40764e7d18fSConrad Meyer netdump_unconfigure(void) 40864e7d18fSConrad Meyer { 40964e7d18fSConrad Meyer struct diocskerneldump_arg kda; 41064e7d18fSConrad Meyer 41164e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED(); 412fde2cf65SConrad Meyer KASSERT(netdump_enabled(), ("%s: not enabled", __func__)); 41364e7d18fSConrad Meyer 41464e7d18fSConrad Meyer bzero(&kda, sizeof(kda)); 41564e7d18fSConrad Meyer kda.kda_index = KDA_REMOVE_DEV; 41664e7d18fSConrad Meyer (void)dumper_remove(nd_conf.ndc_iface, &kda); 41764e7d18fSConrad Meyer 418fde2cf65SConrad Meyer if (nd_ifp != NULL) 41964e7d18fSConrad Meyer if_rele(nd_ifp); 42064e7d18fSConrad Meyer nd_ifp = NULL; 421fde2cf65SConrad Meyer netdump_set_enabled(false); 42264e7d18fSConrad Meyer 4237790c8c1SConrad Meyer log(LOG_WARNING, "netdump: Lost configured interface %s\n", 4247790c8c1SConrad Meyer nd_conf.ndc_iface); 4257790c8c1SConrad Meyer 42664e7d18fSConrad Meyer bzero(&nd_conf, sizeof(nd_conf)); 42764e7d18fSConrad Meyer } 42864e7d18fSConrad Meyer 42964e7d18fSConrad Meyer static void 43064e7d18fSConrad Meyer netdump_ifdetach(void *arg __unused, struct ifnet *ifp) 43164e7d18fSConrad Meyer { 43264e7d18fSConrad Meyer 43364e7d18fSConrad Meyer NETDUMP_WLOCK(); 43464e7d18fSConrad Meyer if (ifp == nd_ifp) 43564e7d18fSConrad Meyer netdump_unconfigure(); 43664e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 43764e7d18fSConrad Meyer } 43864e7d18fSConrad Meyer 4398270d35eSConrad Meyer /* 4408270d35eSConrad Meyer * td of NULL is a sentinel value that indicates a kernel caller (ddb(4) or 4418270d35eSConrad Meyer * modload-based tunable parameters). 4428270d35eSConrad Meyer */ 443e5054602SMark Johnston static int 4446b6e2954SConrad Meyer netdump_configure(struct diocskerneldump_arg *conf, struct thread *td) 445e5054602SMark Johnston { 446e5054602SMark Johnston struct ifnet *ifp; 447e5054602SMark Johnston 44864e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED(); 44964e7d18fSConrad Meyer 450fde2cf65SConrad Meyer if (conf->kda_iface[0] != 0) { 4514ad8cb68SMichael Tuexen if (td != NULL && !IS_DEFAULT_VNET(TD_TO_VNET(td))) 452b35822d9SMark Johnston return (EINVAL); 4534ad8cb68SMichael Tuexen CURVNET_SET(vnet0); 45464e7d18fSConrad Meyer ifp = ifunit_ref(conf->kda_iface); 455b35822d9SMark Johnston CURVNET_RESTORE(); 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 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 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 */ 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