4a711b8d04
In addition to reducing lines of code, this also ensures that the full allocation is always zeroed avoiding possible bugs with incorrect lengths passed to explicit_bzero(). Suggested by: cem Reviewed by: cem, delphij Approved by: csprng (cem) Sponsored by: Chelsio Communications Differential Revision: https://reviews.freebsd.org/D25435
816 lines
21 KiB
C
816 lines
21 KiB
C
/*-
|
|
* Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved.
|
|
* Copyright (c) 2000 Darrell Anderson
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* netdump_client.c
|
|
* FreeBSD subsystem supporting netdump network dumps.
|
|
* A dedicated server must be running to accept client dumps.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_ddb.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/eventhandler.h>
|
|
#include <sys/jail.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/kerneldump.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/module.h>
|
|
#include <sys/priv.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/systm.h>
|
|
|
|
#ifdef DDB
|
|
#include <ddb/ddb.h>
|
|
#include <ddb/db_lex.h>
|
|
#endif
|
|
|
|
#include <net/ethernet.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_var.h>
|
|
#include <net/debugnet.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/ip_options.h>
|
|
#include <netinet/udp.h>
|
|
#include <netinet/udp_var.h>
|
|
#include <netinet/netdump/netdump.h>
|
|
|
|
#include <machine/in_cksum.h>
|
|
#include <machine/pcb.h>
|
|
|
|
#define NETDDEBUGV(f, ...) do { \
|
|
if (nd_debug > 1) \
|
|
printf(("%s: " f), __func__, ## __VA_ARGS__); \
|
|
} while (0)
|
|
|
|
static int netdump_configure(struct diocskerneldump_arg *,
|
|
struct thread *);
|
|
static int netdump_dumper(void *priv __unused, void *virtual,
|
|
vm_offset_t physical __unused, off_t offset, size_t length);
|
|
static bool netdump_enabled(void);
|
|
static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS);
|
|
static int netdump_ioctl(struct cdev *dev __unused, u_long cmd,
|
|
caddr_t addr, int flags __unused, struct thread *td);
|
|
static int netdump_modevent(module_t mod, int type, void *priv);
|
|
static int netdump_start(struct dumperinfo *di);
|
|
static void netdump_unconfigure(void);
|
|
|
|
/* Must be at least as big as the chunks dumpsys() gives us. */
|
|
static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
|
|
static int dump_failed;
|
|
|
|
/* Configuration parameters. */
|
|
static struct {
|
|
char ndc_iface[IFNAMSIZ];
|
|
union kd_ip ndc_server;
|
|
union kd_ip ndc_client;
|
|
union kd_ip ndc_gateway;
|
|
uint8_t ndc_af;
|
|
/* Runtime State */
|
|
struct debugnet_pcb *nd_pcb;
|
|
off_t nd_tx_off;
|
|
size_t nd_buf_len;
|
|
} nd_conf;
|
|
#define nd_server nd_conf.ndc_server.in4
|
|
#define nd_client nd_conf.ndc_client.in4
|
|
#define nd_gateway nd_conf.ndc_gateway.in4
|
|
|
|
/* General dynamic settings. */
|
|
static struct sx nd_conf_lk;
|
|
SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock");
|
|
#define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk)
|
|
#define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk)
|
|
#define NETDUMP_RLOCK() sx_slock(&nd_conf_lk)
|
|
#define NETDUMP_RUNLOCK() sx_sunlock(&nd_conf_lk)
|
|
#define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED)
|
|
#define NETDUMP_ASSERT_LOCKED() sx_assert(&nd_conf_lk, SA_LOCKED)
|
|
static struct ifnet *nd_ifp;
|
|
static eventhandler_tag nd_detach_cookie;
|
|
|
|
FEATURE(netdump, "Netdump client support");
|
|
|
|
static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
|
|
"netdump parameters");
|
|
|
|
static int nd_debug;
|
|
SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
|
|
&nd_debug, 0,
|
|
"Debug message verbosity");
|
|
SYSCTL_PROC(_net_netdump, OID_AUTO, enabled,
|
|
CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE, NULL, 0,
|
|
netdump_enabled_sysctl, "I",
|
|
"netdump configuration status");
|
|
static char nd_path[MAXPATHLEN];
|
|
SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW,
|
|
nd_path, sizeof(nd_path),
|
|
"Server path for output files");
|
|
/*
|
|
* The following three variables were moved to debugnet(4), but these knobs
|
|
* were retained as aliases.
|
|
*/
|
|
SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN,
|
|
&debugnet_npolls, 0,
|
|
"Number of times to poll before assuming packet loss (0.5ms per poll)");
|
|
SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN,
|
|
&debugnet_nretries, 0,
|
|
"Number of retransmit attempts before giving up");
|
|
SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN,
|
|
&debugnet_arp_nretries, 0,
|
|
"Number of ARP attempts before giving up");
|
|
|
|
static bool nd_is_enabled;
|
|
static bool
|
|
netdump_enabled(void)
|
|
{
|
|
|
|
NETDUMP_ASSERT_LOCKED();
|
|
return (nd_is_enabled);
|
|
}
|
|
|
|
static void
|
|
netdump_set_enabled(bool status)
|
|
{
|
|
|
|
NETDUMP_ASSERT_LOCKED();
|
|
nd_is_enabled = status;
|
|
}
|
|
|
|
static int
|
|
netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
int en, error;
|
|
|
|
NETDUMP_RLOCK();
|
|
en = netdump_enabled();
|
|
NETDUMP_RUNLOCK();
|
|
|
|
error = SYSCTL_OUT(req, &en, sizeof(en));
|
|
if (error != 0 || req->newptr == NULL)
|
|
return (error);
|
|
return (EPERM);
|
|
}
|
|
|
|
/*-
|
|
* Dumping specific primitives.
|
|
*/
|
|
|
|
/*
|
|
* Flush any buffered vmcore data.
|
|
*/
|
|
static int
|
|
netdump_flush_buf(void)
|
|
{
|
|
int error;
|
|
|
|
error = 0;
|
|
if (nd_conf.nd_buf_len != 0) {
|
|
struct debugnet_proto_aux auxdata = {
|
|
.dp_offset_start = nd_conf.nd_tx_off,
|
|
};
|
|
error = debugnet_send(nd_conf.nd_pcb, DEBUGNET_DATA, nd_buf,
|
|
nd_conf.nd_buf_len, &auxdata);
|
|
if (error == 0)
|
|
nd_conf.nd_buf_len = 0;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Callback from dumpsys() to dump a chunk of memory.
|
|
* Copies it out to our static buffer then sends it across the network.
|
|
* Detects the initial KDH and makes sure it is given a special packet type.
|
|
*
|
|
* Parameters:
|
|
* priv Unused. Optional private pointer.
|
|
* virtual Virtual address (where to read the data from)
|
|
* physical Unused. Physical memory address.
|
|
* offset Offset from start of core file
|
|
* length Data length
|
|
*
|
|
* Return value:
|
|
* 0 on success
|
|
* errno on error
|
|
*/
|
|
static int
|
|
netdump_dumper(void *priv __unused, void *virtual,
|
|
vm_offset_t physical __unused, off_t offset, size_t length)
|
|
{
|
|
int error;
|
|
|
|
NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n",
|
|
virtual, (uintmax_t)offset, length);
|
|
|
|
if (virtual == NULL) {
|
|
error = netdump_flush_buf();
|
|
if (error != 0)
|
|
dump_failed = 1;
|
|
|
|
if (dump_failed != 0)
|
|
printf("failed to dump the kernel core\n");
|
|
else if (
|
|
debugnet_sendempty(nd_conf.nd_pcb, DEBUGNET_FINISHED) != 0)
|
|
printf("failed to close the transaction\n");
|
|
else
|
|
printf("\nnetdump finished.\n");
|
|
debugnet_free(nd_conf.nd_pcb);
|
|
nd_conf.nd_pcb = NULL;
|
|
return (0);
|
|
}
|
|
if (length > sizeof(nd_buf))
|
|
return (ENOSPC);
|
|
|
|
if (nd_conf.nd_buf_len + length > sizeof(nd_buf) ||
|
|
(nd_conf.nd_buf_len != 0 && nd_conf.nd_tx_off +
|
|
nd_conf.nd_buf_len != offset)) {
|
|
error = netdump_flush_buf();
|
|
if (error != 0) {
|
|
dump_failed = 1;
|
|
return (error);
|
|
}
|
|
nd_conf.nd_tx_off = offset;
|
|
}
|
|
|
|
memmove(nd_buf + nd_conf.nd_buf_len, virtual, length);
|
|
nd_conf.nd_buf_len += length;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Perform any initialization needed prior to transmitting the kernel core.
|
|
*/
|
|
static int
|
|
netdump_start(struct dumperinfo *di)
|
|
{
|
|
struct debugnet_conn_params dcp;
|
|
struct debugnet_pcb *pcb;
|
|
char buf[INET_ADDRSTRLEN];
|
|
int error;
|
|
|
|
error = 0;
|
|
|
|
/* Check if the dumping is allowed to continue. */
|
|
if (!netdump_enabled())
|
|
return (EINVAL);
|
|
|
|
if (!KERNEL_PANICKED()) {
|
|
printf(
|
|
"netdump_start: netdump may only be used after a panic\n");
|
|
return (EINVAL);
|
|
}
|
|
|
|
memset(&dcp, 0, sizeof(dcp));
|
|
|
|
if (nd_server.s_addr == INADDR_ANY) {
|
|
printf("netdump_start: can't netdump; no server IP given\n");
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* We start dumping at offset 0. */
|
|
di->dumpoff = 0;
|
|
|
|
dcp.dc_ifp = nd_ifp;
|
|
|
|
dcp.dc_client = nd_client.s_addr;
|
|
dcp.dc_server = nd_server.s_addr;
|
|
dcp.dc_gateway = nd_gateway.s_addr;
|
|
|
|
dcp.dc_herald_port = NETDUMP_PORT;
|
|
dcp.dc_client_port = NETDUMP_ACKPORT;
|
|
|
|
dcp.dc_herald_data = nd_path;
|
|
dcp.dc_herald_datalen = (nd_path[0] == 0) ? 0 : strlen(nd_path) + 1;
|
|
|
|
error = debugnet_connect(&dcp, &pcb);
|
|
if (error != 0) {
|
|
printf("failed to contact netdump server\n");
|
|
/* Squash debugnet to something the dumper code understands. */
|
|
return (EINVAL);
|
|
}
|
|
|
|
printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf),
|
|
debugnet_get_gw_mac(pcb), ":");
|
|
nd_conf.nd_pcb = pcb;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh,
|
|
void *key, uint32_t keysize)
|
|
{
|
|
int error;
|
|
|
|
error = netdump_flush_buf();
|
|
if (error != 0)
|
|
return (error);
|
|
memcpy(nd_buf, kdh, sizeof(*kdh));
|
|
error = debugnet_send(nd_conf.nd_pcb, NETDUMP_KDH, nd_buf,
|
|
sizeof(*kdh), NULL);
|
|
if (error == 0 && keysize > 0) {
|
|
if (keysize > sizeof(nd_buf))
|
|
return (EINVAL);
|
|
memcpy(nd_buf, key, keysize);
|
|
error = debugnet_send(nd_conf.nd_pcb, NETDUMP_EKCD_KEY, nd_buf,
|
|
keysize, NULL);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*-
|
|
* KLD specific code.
|
|
*/
|
|
|
|
static struct cdevsw netdump_cdevsw = {
|
|
.d_version = D_VERSION,
|
|
.d_ioctl = netdump_ioctl,
|
|
.d_name = "netdump",
|
|
};
|
|
|
|
static struct cdev *netdump_cdev;
|
|
|
|
static void
|
|
netdump_unconfigure(void)
|
|
{
|
|
struct diocskerneldump_arg kda;
|
|
|
|
NETDUMP_ASSERT_WLOCKED();
|
|
KASSERT(netdump_enabled(), ("%s: not enabled", __func__));
|
|
|
|
bzero(&kda, sizeof(kda));
|
|
kda.kda_index = KDA_REMOVE_DEV;
|
|
(void)dumper_remove(nd_conf.ndc_iface, &kda);
|
|
|
|
if (nd_ifp != NULL)
|
|
if_rele(nd_ifp);
|
|
nd_ifp = NULL;
|
|
netdump_set_enabled(false);
|
|
|
|
log(LOG_WARNING, "netdump: Lost configured interface %s\n",
|
|
nd_conf.ndc_iface);
|
|
|
|
bzero(&nd_conf, sizeof(nd_conf));
|
|
}
|
|
|
|
static void
|
|
netdump_ifdetach(void *arg __unused, struct ifnet *ifp)
|
|
{
|
|
|
|
NETDUMP_WLOCK();
|
|
if (ifp == nd_ifp)
|
|
netdump_unconfigure();
|
|
NETDUMP_WUNLOCK();
|
|
}
|
|
|
|
/*
|
|
* td of NULL is a sentinel value that indicates a kernel caller (ddb(4) or
|
|
* modload-based tunable parameters).
|
|
*/
|
|
static int
|
|
netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
|
|
{
|
|
struct ifnet *ifp;
|
|
|
|
NETDUMP_ASSERT_WLOCKED();
|
|
|
|
if (conf->kda_iface[0] != 0) {
|
|
if (td != NULL && !IS_DEFAULT_VNET(TD_TO_VNET(td)))
|
|
return (EINVAL);
|
|
CURVNET_SET(vnet0);
|
|
ifp = ifunit_ref(conf->kda_iface);
|
|
CURVNET_RESTORE();
|
|
} else
|
|
ifp = NULL;
|
|
|
|
if (nd_ifp != NULL)
|
|
if_rele(nd_ifp);
|
|
nd_ifp = ifp;
|
|
netdump_set_enabled(true);
|
|
|
|
#define COPY_SIZED(elm) do { \
|
|
_Static_assert(sizeof(nd_conf.ndc_ ## elm) == \
|
|
sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
|
|
memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm, \
|
|
sizeof(nd_conf.ndc_ ## elm)); \
|
|
} while (0)
|
|
COPY_SIZED(iface);
|
|
COPY_SIZED(server);
|
|
COPY_SIZED(client);
|
|
COPY_SIZED(gateway);
|
|
COPY_SIZED(af);
|
|
#undef COPY_SIZED
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ioctl(2) handler for the netdump device. This is currently only used to
|
|
* register netdump as a dump device.
|
|
*
|
|
* Parameters:
|
|
* dev, Unused.
|
|
* cmd, The ioctl to be handled.
|
|
* addr, The parameter for the ioctl.
|
|
* flags, Unused.
|
|
* td, The thread invoking this ioctl.
|
|
*
|
|
* Returns:
|
|
* 0 on success, and an errno value on failure.
|
|
*/
|
|
static int
|
|
netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
|
|
int flags __unused, struct thread *td)
|
|
{
|
|
struct diocskerneldump_arg kda_copy, *conf;
|
|
struct dumperinfo dumper;
|
|
uint8_t *encryptedkey;
|
|
int error;
|
|
#ifdef COMPAT_FREEBSD11
|
|
u_int u;
|
|
#endif
|
|
#ifdef COMPAT_FREEBSD12
|
|
struct diocskerneldump_arg_freebsd12 *kda12;
|
|
struct netdump_conf_freebsd12 *conf12;
|
|
#endif
|
|
|
|
conf = NULL;
|
|
error = 0;
|
|
NETDUMP_WLOCK();
|
|
|
|
switch (cmd) {
|
|
#ifdef COMPAT_FREEBSD11
|
|
case DIOCSKERNELDUMP_FREEBSD11:
|
|
gone_in(13, "11.x ABI compatibility");
|
|
u = *(u_int *)addr;
|
|
if (u != 0) {
|
|
error = ENXIO;
|
|
break;
|
|
}
|
|
if (netdump_enabled())
|
|
netdump_unconfigure();
|
|
break;
|
|
#endif
|
|
#ifdef COMPAT_FREEBSD12
|
|
/*
|
|
* Used by dumpon(8) in 12.x for clearing previous
|
|
* configuration -- then NETDUMPSCONF_FREEBSD12 is used to
|
|
* actually configure netdump.
|
|
*/
|
|
case DIOCSKERNELDUMP_FREEBSD12:
|
|
gone_in(14, "12.x ABI compatibility");
|
|
|
|
kda12 = (void *)addr;
|
|
if (kda12->kda12_enable) {
|
|
error = ENXIO;
|
|
break;
|
|
}
|
|
if (netdump_enabled())
|
|
netdump_unconfigure();
|
|
break;
|
|
|
|
case NETDUMPGCONF_FREEBSD12:
|
|
gone_in(14, "FreeBSD 12.x ABI compat");
|
|
conf12 = (void *)addr;
|
|
|
|
if (!netdump_enabled()) {
|
|
error = ENXIO;
|
|
break;
|
|
}
|
|
if (nd_conf.ndc_af != AF_INET) {
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
|
|
if (nd_ifp != NULL)
|
|
strlcpy(conf12->ndc12_iface, nd_ifp->if_xname,
|
|
sizeof(conf12->ndc12_iface));
|
|
memcpy(&conf12->ndc12_server, &nd_server,
|
|
sizeof(conf12->ndc12_server));
|
|
memcpy(&conf12->ndc12_client, &nd_client,
|
|
sizeof(conf12->ndc12_client));
|
|
memcpy(&conf12->ndc12_gateway, &nd_gateway,
|
|
sizeof(conf12->ndc12_gateway));
|
|
break;
|
|
#endif
|
|
case DIOCGKERNELDUMP:
|
|
conf = (void *)addr;
|
|
/*
|
|
* For now, index is ignored; netdump doesn't support multiple
|
|
* configurations (yet).
|
|
*/
|
|
if (!netdump_enabled()) {
|
|
error = ENXIO;
|
|
conf = NULL;
|
|
break;
|
|
}
|
|
|
|
if (nd_ifp != NULL)
|
|
strlcpy(conf->kda_iface, nd_ifp->if_xname,
|
|
sizeof(conf->kda_iface));
|
|
memcpy(&conf->kda_server, &nd_server, sizeof(nd_server));
|
|
memcpy(&conf->kda_client, &nd_client, sizeof(nd_client));
|
|
memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway));
|
|
conf->kda_af = nd_conf.ndc_af;
|
|
conf = NULL;
|
|
break;
|
|
|
|
#ifdef COMPAT_FREEBSD12
|
|
case NETDUMPSCONF_FREEBSD12:
|
|
gone_in(14, "FreeBSD 12.x ABI compat");
|
|
|
|
conf12 = (struct netdump_conf_freebsd12 *)addr;
|
|
|
|
_Static_assert(offsetof(struct diocskerneldump_arg, kda_server)
|
|
== offsetof(struct netdump_conf_freebsd12, ndc12_server),
|
|
"simplifying assumption");
|
|
|
|
memset(&kda_copy, 0, sizeof(kda_copy));
|
|
memcpy(&kda_copy, conf12,
|
|
offsetof(struct diocskerneldump_arg, kda_server));
|
|
|
|
/* 12.x ABI could only configure IPv4 (INET) netdump. */
|
|
kda_copy.kda_af = AF_INET;
|
|
memcpy(&kda_copy.kda_server.in4, &conf12->ndc12_server,
|
|
sizeof(struct in_addr));
|
|
memcpy(&kda_copy.kda_client.in4, &conf12->ndc12_client,
|
|
sizeof(struct in_addr));
|
|
memcpy(&kda_copy.kda_gateway.in4, &conf12->ndc12_gateway,
|
|
sizeof(struct in_addr));
|
|
|
|
kda_copy.kda_index =
|
|
(conf12->ndc12_kda.kda12_enable ? 0 : KDA_REMOVE_ALL);
|
|
|
|
conf = &kda_copy;
|
|
explicit_bzero(conf12, sizeof(*conf12));
|
|
/* FALLTHROUGH */
|
|
#endif
|
|
case DIOCSKERNELDUMP:
|
|
encryptedkey = NULL;
|
|
if (cmd == DIOCSKERNELDUMP) {
|
|
conf = (void *)addr;
|
|
memcpy(&kda_copy, conf, sizeof(kda_copy));
|
|
}
|
|
/* Netdump only supports IP4 at this time. */
|
|
if (conf->kda_af != AF_INET) {
|
|
error = EPROTONOSUPPORT;
|
|
break;
|
|
}
|
|
|
|
conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0';
|
|
if (conf->kda_index == KDA_REMOVE ||
|
|
conf->kda_index == KDA_REMOVE_DEV ||
|
|
conf->kda_index == KDA_REMOVE_ALL) {
|
|
if (netdump_enabled())
|
|
netdump_unconfigure();
|
|
if (conf->kda_index == KDA_REMOVE_ALL)
|
|
error = dumper_remove(NULL, conf);
|
|
break;
|
|
}
|
|
|
|
error = netdump_configure(conf, td);
|
|
if (error != 0)
|
|
break;
|
|
|
|
if (conf->kda_encryption != KERNELDUMP_ENC_NONE) {
|
|
if (conf->kda_encryptedkeysize <= 0 ||
|
|
conf->kda_encryptedkeysize >
|
|
KERNELDUMP_ENCKEY_MAX_SIZE) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
encryptedkey = malloc(conf->kda_encryptedkeysize,
|
|
M_TEMP, M_WAITOK);
|
|
error = copyin(conf->kda_encryptedkey, encryptedkey,
|
|
conf->kda_encryptedkeysize);
|
|
if (error != 0) {
|
|
free(encryptedkey, M_TEMP);
|
|
break;
|
|
}
|
|
|
|
conf->kda_encryptedkey = encryptedkey;
|
|
}
|
|
|
|
memset(&dumper, 0, sizeof(dumper));
|
|
dumper.dumper_start = netdump_start;
|
|
dumper.dumper_hdr = netdump_write_headers;
|
|
dumper.dumper = netdump_dumper;
|
|
dumper.priv = NULL;
|
|
dumper.blocksize = NETDUMP_DATASIZE;
|
|
dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE;
|
|
dumper.mediaoffset = 0;
|
|
dumper.mediasize = 0;
|
|
|
|
error = dumper_insert(&dumper, conf->kda_iface, conf);
|
|
zfree(encryptedkey, M_TEMP);
|
|
if (error != 0)
|
|
netdump_unconfigure();
|
|
break;
|
|
default:
|
|
error = ENOTTY;
|
|
break;
|
|
}
|
|
explicit_bzero(&kda_copy, sizeof(kda_copy));
|
|
if (conf != NULL)
|
|
explicit_bzero(conf, sizeof(*conf));
|
|
NETDUMP_WUNLOCK();
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Called upon system init or kld load. Initializes the netdump parameters to
|
|
* sane defaults (locates the first available NIC and uses the first IPv4 IP on
|
|
* that card as the client IP). Leaves the server IP unconfigured.
|
|
*
|
|
* Parameters:
|
|
* mod, Unused.
|
|
* what, The module event type.
|
|
* priv, Unused.
|
|
*
|
|
* Returns:
|
|
* int, An errno value if an error occured, 0 otherwise.
|
|
*/
|
|
static int
|
|
netdump_modevent(module_t mod __unused, int what, void *priv __unused)
|
|
{
|
|
struct diocskerneldump_arg conf;
|
|
char *arg;
|
|
int error;
|
|
|
|
error = 0;
|
|
switch (what) {
|
|
case MOD_LOAD:
|
|
error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev,
|
|
&netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump");
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
|
|
netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
|
|
|
|
if ((arg = kern_getenv("net.dump.iface")) != NULL) {
|
|
strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
|
|
freeenv(arg);
|
|
|
|
if ((arg = kern_getenv("net.dump.server")) != NULL) {
|
|
inet_aton(arg, &conf.kda_server.in4);
|
|
freeenv(arg);
|
|
}
|
|
if ((arg = kern_getenv("net.dump.client")) != NULL) {
|
|
inet_aton(arg, &conf.kda_client.in4);
|
|
freeenv(arg);
|
|
}
|
|
if ((arg = kern_getenv("net.dump.gateway")) != NULL) {
|
|
inet_aton(arg, &conf.kda_gateway.in4);
|
|
freeenv(arg);
|
|
}
|
|
conf.kda_af = AF_INET;
|
|
|
|
/* Ignore errors; we print a message to the console. */
|
|
NETDUMP_WLOCK();
|
|
(void)netdump_configure(&conf, NULL);
|
|
NETDUMP_WUNLOCK();
|
|
}
|
|
break;
|
|
case MOD_UNLOAD:
|
|
NETDUMP_WLOCK();
|
|
if (netdump_enabled()) {
|
|
printf("netdump: disabling dump device for unload\n");
|
|
netdump_unconfigure();
|
|
}
|
|
NETDUMP_WUNLOCK();
|
|
destroy_dev(netdump_cdev);
|
|
EVENTHANDLER_DEREGISTER(ifnet_departure_event,
|
|
nd_detach_cookie);
|
|
break;
|
|
default:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
static moduledata_t netdump_mod = {
|
|
"netdump",
|
|
netdump_modevent,
|
|
NULL,
|
|
};
|
|
|
|
MODULE_VERSION(netdump, 1);
|
|
DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
|
|
|
|
#ifdef DDB
|
|
/*
|
|
* Usage: netdump -s <server> [-g <gateway] -c <localip> -i <interface>
|
|
*
|
|
* Order is not significant.
|
|
*
|
|
* Currently, this command does not support configuring encryption or
|
|
* compression.
|
|
*/
|
|
DB_FUNC(netdump, db_netdump_cmd, db_cmd_table, CS_OWN, NULL)
|
|
{
|
|
static struct diocskerneldump_arg conf;
|
|
static char blockbuf[NETDUMP_DATASIZE];
|
|
static union {
|
|
struct dumperinfo di;
|
|
/* For valid di_devname. */
|
|
char di_buf[sizeof(struct dumperinfo) + 1];
|
|
} u;
|
|
|
|
struct debugnet_ddb_config params;
|
|
int error;
|
|
|
|
error = debugnet_parse_ddb_cmd("netdump", ¶ms);
|
|
if (error != 0) {
|
|
db_printf("Error configuring netdump: %d\n", error);
|
|
return;
|
|
}
|
|
|
|
/* Translate to a netdump dumper config. */
|
|
memset(&conf, 0, sizeof(conf));
|
|
|
|
if (params.dd_ifp != NULL)
|
|
strlcpy(conf.kda_iface, if_name(params.dd_ifp),
|
|
sizeof(conf.kda_iface));
|
|
|
|
conf.kda_af = AF_INET;
|
|
conf.kda_server.in4 = (struct in_addr) { params.dd_server };
|
|
if (params.dd_has_client)
|
|
conf.kda_client.in4 = (struct in_addr) { params.dd_client };
|
|
else
|
|
conf.kda_client.in4 = (struct in_addr) { INADDR_ANY };
|
|
if (params.dd_has_gateway)
|
|
conf.kda_gateway.in4 = (struct in_addr) { params.dd_gateway };
|
|
else
|
|
conf.kda_gateway.in4 = (struct in_addr) { INADDR_ANY };
|
|
|
|
/* Set the global netdump config to these options. */
|
|
error = netdump_configure(&conf, NULL);
|
|
if (error != 0) {
|
|
db_printf("Error enabling netdump: %d\n", error);
|
|
return;
|
|
}
|
|
|
|
/* Fake the generic dump configuration list entry to avoid malloc. */
|
|
memset(&u.di_buf, 0, sizeof(u.di_buf));
|
|
u.di.dumper_start = netdump_start;
|
|
u.di.dumper_hdr = netdump_write_headers;
|
|
u.di.dumper = netdump_dumper;
|
|
u.di.priv = NULL;
|
|
u.di.blocksize = NETDUMP_DATASIZE;
|
|
u.di.maxiosize = MAXDUMPPGS * PAGE_SIZE;
|
|
u.di.mediaoffset = 0;
|
|
u.di.mediasize = 0;
|
|
u.di.blockbuf = blockbuf;
|
|
|
|
dumper_ddb_insert(&u.di);
|
|
|
|
error = doadump(false);
|
|
|
|
dumper_ddb_remove(&u.di);
|
|
if (error != 0)
|
|
db_printf("Cannot dump: %d\n", error);
|
|
}
|
|
#endif /* DDB */
|