aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2019-05-06 18:24:07 +0000
committerConrad Meyer <cem@FreeBSD.org>2019-05-06 18:24:07 +0000
commit6b6e2954dd65ec08e73efb0f2e8bfb2278a07dc6 (patch)
tree0a05af7fff614a4db1f169f9b92918086e7d676f
parent46068e86c3d22eb492cf9de0cb3e43e58be4422f (diff)
downloadsrc-6b6e2954dd65ec08e73efb0f2e8bfb2278a07dc6.tar.gz
src-6b6e2954dd65ec08e73efb0f2e8bfb2278a07dc6.zip
List-ify kernel dump device configuration
Allow users to specify multiple dump configurations in a prioritized list. This enables fallback to secondary device(s) if primary dump fails. E.g., one might configure a preference for netdump, but fallback to disk dump as a second choice if netdump is unavailable. This change does not list-ify netdump configuration, which is tracked separately from ordinary disk dumps internally; only one netdump configuration can be made at a time, for now. It also does not implement IPv6 netdump. savecore(8) is already capable of scanning and iterating multiple devices from /etc/fstab or passed on the command line. This change doesn't update the rc or loader variables 'dumpdev' in any way; it can still be set to configure a single dump device, and rc.d/savecore still uses it as a single device. Only dumpon(8) is updated to be able to configure the more complicated configurations for now. As part of revving the ABI, unify netdump and disk dump configuration ioctl / structure, and leave room for ipv6 netdump as a future possibility. Backwards-compatibility ioctls are added to smooth ABI transition, especially for developers who may not keep kernel and userspace perfectly synced. Reviewed by: markj, scottl (earlier version) Relnotes: maybe Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D19996
Notes
Notes: svn path=/head/; revision=347192
-rw-r--r--sbin/dumpon/dumpon.838
-rw-r--r--sbin/dumpon/dumpon.c157
-rw-r--r--sys/dev/null/null.c13
-rw-r--r--sys/geom/geom_dev.c67
-rw-r--r--sys/geom/raid/g_raid.h2
-rw-r--r--sys/kern/kern_shutdown.c246
-rw-r--r--sys/netinet/netdump/netdump.h18
-rw-r--r--sys/netinet/netdump/netdump_client.c198
-rw-r--r--sys/sys/conf.h12
-rw-r--r--sys/sys/disk.h57
-rw-r--r--sys/sys/param.h2
11 files changed, 604 insertions, 206 deletions
diff --git a/sbin/dumpon/dumpon.8 b/sbin/dumpon/dumpon.8
index 1ab3c1650adc..a62bd366dfb4 100644
--- a/sbin/dumpon/dumpon.8
+++ b/sbin/dumpon/dumpon.8
@@ -28,7 +28,7 @@
.\" From: @(#)swapon.8 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
-.Dd November 17, 2018
+.Dd May 6, 2019
.Dt DUMPON 8
.Os
.Sh NAME
@@ -36,12 +36,16 @@
.Nd "specify a device for crash dumps"
.Sh SYNOPSIS
.Nm
+.Op Fl i Ar index
+.Op Fl r
.Op Fl v
.Op Fl k Ar pubkey
.Op Fl Z
.Op Fl z
.Ar device
.Nm
+.Op Fl i Ar index
+.Op Fl r
.Op Fl v
.Op Fl k Ar pubkey
.Op Fl Z
@@ -72,8 +76,38 @@ and
.Va dumpon_flags .
For more information on this usage, see
.Xr rc.conf 5 .
+.Pp
+Starting in
+.Fx 13.0 ,
+.Nm
+can configure a series of fallback dump devices.
+For example, an administrator may prefer
+.Xr netdump 4
+by default, but if the
+.Xr netdump 4
+service cannot be reached or some other failure occurs, they might choose a
+local disk dump as a second choice option.
.Ss General options
.Bl -tag -width _k_pubkey
+.It Fl i Ar index
+Insert the specified dump configuration into the prioritized fallback dump
+device list at the specified index, starting at zero.
+.Pp
+If
+.Fl i
+is not specified, the configured dump device is appended to the prioritized
+list.
+.It Fl r
+Remove the specified dump device configuration or configurations from the
+fallback dump device list rather than inserting or appending it.
+In contrast,
+.Do
+.Nm
+off
+.Dc
+removes all configured devices.
+Conflicts with
+.Fl i .
.It Fl k Ar pubkey
Configure encrypted kernel dumps.
.Pp
@@ -96,7 +130,7 @@ The
.Va pubkey
file should be a PEM-formatted RSA key of at least 1024 bits.
.It Fl l
-List the currently configured dump device, or /dev/null if no device is
+List the currently configured dump device(s), or /dev/null if no devices are
configured.
.It Fl v
Enable verbose mode.
diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c
index c9805c47d14c..3eec6495b215 100644
--- a/sbin/dumpon/dumpon.c
+++ b/sbin/dumpon/dumpon.c
@@ -86,8 +86,8 @@ static void _Noreturn
usage(void)
{
fprintf(stderr,
- "usage: dumpon [-v] [-k <pubkey>] [-Zz] <device>\n"
- " dumpon [-v] [-k <pubkey>] [-Zz]\n"
+ "usage: dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz] <device>\n"
+ " dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz]\n"
" [-g <gateway>] -s <server> -c <client> <iface>\n"
" dumpon [-v] off\n"
" dumpon [-v] -l\n");
@@ -290,8 +290,10 @@ genkey(const char *pubkeyfile, struct diocskerneldump_arg *kdap)
static void
listdumpdev(void)
{
+ static char ip[200];
+
char dumpdev[PATH_MAX];
- struct netdump_conf ndconf;
+ struct diocskerneldump_arg ndconf;
size_t len;
const char *sysctlname = "kern.shutdown.dumpdevname";
int fd;
@@ -308,9 +310,17 @@ listdumpdev(void)
if (strlen(dumpdev) == 0)
(void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev));
- if (verbose)
- printf("kernel dumps on ");
- printf("%s\n", dumpdev);
+ if (verbose) {
+ char *ctx, *dd;
+ unsigned idx;
+
+ printf("kernel dumps on priority: device\n");
+ idx = 0;
+ ctx = dumpdev;
+ while ((dd = strsep(&ctx, ",")) != NULL)
+ printf("%u: %s\n", idx++, dd);
+ } else
+ printf("%s\n", dumpdev);
/* If netdump is enabled, print the configuration parameters. */
if (verbose) {
@@ -320,16 +330,22 @@ listdumpdev(void)
err(EX_OSERR, "opening %s", _PATH_NETDUMP);
return;
}
- if (ioctl(fd, NETDUMPGCONF, &ndconf) != 0) {
+ if (ioctl(fd, DIOCGKERNELDUMP, &ndconf) != 0) {
if (errno != ENXIO)
- err(EX_OSERR, "ioctl(NETDUMPGCONF)");
+ err(EX_OSERR, "ioctl(DIOCGKERNELDUMP)");
(void)close(fd);
return;
}
- printf("server address: %s\n", inet_ntoa(ndconf.ndc_server));
- printf("client address: %s\n", inet_ntoa(ndconf.ndc_client));
- printf("gateway address: %s\n", inet_ntoa(ndconf.ndc_gateway));
+ printf("server address: %s\n",
+ inet_ntop(ndconf.kda_af, &ndconf.kda_server, ip,
+ sizeof(ip)));
+ printf("client address: %s\n",
+ inet_ntop(ndconf.kda_af, &ndconf.kda_client, ip,
+ sizeof(ip)));
+ printf("gateway address: %s\n",
+ inet_ntop(ndconf.kda_af, &ndconf.kda_gateway, ip,
+ sizeof(ip)));
(void)close(fd);
}
}
@@ -359,19 +375,20 @@ int
main(int argc, char *argv[])
{
char dumpdev[PATH_MAX];
- struct diocskerneldump_arg _kda, *kdap;
- struct netdump_conf ndconf;
+ struct diocskerneldump_arg ndconf, *kdap;
struct addrinfo hints, *res;
const char *dev, *pubkeyfile, *server, *client, *gateway;
int ch, error, fd;
- bool enable, gzip, list, netdump, zstd;
+ bool gzip, list, netdump, zstd, insert, rflag;
+ uint8_t ins_idx;
- gzip = list = netdump = zstd = false;
+ gzip = list = netdump = zstd = insert = rflag = false;
kdap = NULL;
pubkeyfile = NULL;
server = client = gateway = NULL;
+ ins_idx = KDA_APPEND;
- while ((ch = getopt(argc, argv, "c:g:k:ls:vZz")) != -1)
+ while ((ch = getopt(argc, argv, "c:g:i:k:lrs:vZz")) != -1)
switch ((char)ch) {
case 'c':
client = optarg;
@@ -379,12 +396,28 @@ main(int argc, char *argv[])
case 'g':
gateway = optarg;
break;
+ case 'i':
+ {
+ int i;
+
+ i = atoi(optarg);
+ if (i < 0 || i >= KDA_APPEND - 1)
+ errx(EX_USAGE,
+ "-i index must be between zero and %d.",
+ (int)KDA_APPEND - 2);
+ insert = true;
+ ins_idx = i;
+ }
+ break;
case 'k':
pubkeyfile = optarg;
break;
case 'l':
list = true;
break;
+ case 'r':
+ rflag = true;
+ break;
case 's':
server = optarg;
break;
@@ -404,6 +437,9 @@ main(int argc, char *argv[])
if (gzip && zstd)
errx(EX_USAGE, "The -z and -Z options are mutually exclusive.");
+ if (insert && rflag)
+ errx(EX_USAGE, "The -i and -r options are mutually exclusive.");
+
argc -= optind;
argv += optind;
@@ -422,31 +458,30 @@ main(int argc, char *argv[])
#endif
if (server != NULL && client != NULL) {
- enable = true;
dev = _PATH_NETDUMP;
netdump = true;
- kdap = &ndconf.ndc_kda;
} else if (server == NULL && client == NULL && argc > 0) {
- enable = strcmp(argv[0], "off") != 0;
- dev = enable ? argv[0] : _PATH_DEVNULL;
+ if (strcmp(argv[0], "off") == 0) {
+ rflag = true;
+ dev = _PATH_DEVNULL;
+ } else
+ dev = argv[0];
netdump = false;
- kdap = &_kda;
} else
usage();
fd = opendumpdev(dev, dumpdev);
- if (!netdump && !gzip)
+ if (!netdump && !gzip && !rflag)
check_size(fd, dumpdev);
+ kdap = &ndconf;
bzero(kdap, sizeof(*kdap));
- kdap->kda_enable = 0;
- if (ioctl(fd, DIOCSKERNELDUMP, kdap) != 0)
- err(EX_OSERR, "ioctl(DIOCSKERNELDUMP)");
- if (!enable)
- exit(EX_OK);
- explicit_bzero(kdap, sizeof(*kdap));
- kdap->kda_enable = 1;
+ if (rflag)
+ kdap->kda_index = KDA_REMOVE;
+ else
+ kdap->kda_index = ins_idx;
+
kdap->kda_compression = KERNELDUMP_COMP_NONE;
if (zstd)
kdap->kda_compression = KERNELDUMP_COMP_ZSTD;
@@ -467,12 +502,12 @@ main(int argc, char *argv[])
((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr);
freeaddrinfo(res);
- if (strlcpy(ndconf.ndc_iface, argv[0],
- sizeof(ndconf.ndc_iface)) >= sizeof(ndconf.ndc_iface))
+ if (strlcpy(ndconf.kda_iface, argv[0],
+ sizeof(ndconf.kda_iface)) >= sizeof(ndconf.kda_iface))
errx(EX_USAGE, "invalid interface name '%s'", argv[0]);
- if (inet_aton(server, &ndconf.ndc_server) == 0)
+ if (inet_aton(server, &ndconf.kda_server.in4) == 0)
errx(EX_USAGE, "invalid server address '%s'", server);
- if (inet_aton(client, &ndconf.ndc_client) == 0)
+ if (inet_aton(client, &ndconf.kda_client.in4) == 0)
errx(EX_USAGE, "invalid client address '%s'", client);
if (gateway == NULL) {
@@ -485,39 +520,41 @@ main(int argc, char *argv[])
gateway = server;
}
}
- if (inet_aton(gateway, &ndconf.ndc_gateway) == 0)
+ if (inet_aton(gateway, &ndconf.kda_gateway.in4) == 0)
errx(EX_USAGE, "invalid gateway address '%s'", gateway);
+ ndconf.kda_af = AF_INET;
+ }
#ifdef HAVE_CRYPTO
- if (pubkeyfile != NULL)
- genkey(pubkeyfile, kdap);
-#endif
- error = ioctl(fd, NETDUMPSCONF, &ndconf);
- if (error != 0)
- error = errno;
- explicit_bzero(kdap->kda_encryptedkey,
- kdap->kda_encryptedkeysize);
- free(kdap->kda_encryptedkey);
- explicit_bzero(kdap, sizeof(*kdap));
- if (error != 0)
- errc(EX_OSERR, error, "ioctl(NETDUMPSCONF)");
- } else {
-#ifdef HAVE_CRYPTO
- if (pubkeyfile != NULL)
- genkey(pubkeyfile, kdap);
+ if (pubkeyfile != NULL)
+ genkey(pubkeyfile, kdap);
#endif
- error = ioctl(fd, DIOCSKERNELDUMP, kdap);
- if (error != 0)
- error = errno;
- explicit_bzero(kdap->kda_encryptedkey,
- kdap->kda_encryptedkeysize);
- free(kdap->kda_encryptedkey);
- explicit_bzero(kdap, sizeof(*kdap));
- if (error != 0)
- errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
+ error = ioctl(fd, DIOCSKERNELDUMP, kdap);
+ if (error != 0)
+ error = errno;
+ explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize);
+ free(kdap->kda_encryptedkey);
+ explicit_bzero(kdap, sizeof(*kdap));
+ if (error != 0) {
+ if (netdump) {
+ /*
+ * Be slightly less user-hostile for some common
+ * errors, especially as users don't have any great
+ * discoverability into which NICs support netdump.
+ */
+ if (error == ENXIO)
+ errx(EX_OSERR, "Unable to configure netdump "
+ "because the interface's link is down.");
+ else if (error == ENODEV)
+ errx(EX_OSERR, "Unable to configure netdump "
+ "because the interface driver does not yet "
+ "support netdump.");
+ }
+ errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
}
+
if (verbose)
- printf("kernel dumps on %s\n", dumpdev);
+ listdumpdev();
exit(EX_OK);
}
diff --git a/sys/dev/null/null.c b/sys/dev/null/null.c
index c1e81ed24024..6ec127ba2718 100644
--- a/sys/dev/null/null.c
+++ b/sys/dev/null/null.c
@@ -106,15 +106,26 @@ static int
null_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
int flags __unused, struct thread *td)
{
+ struct diocskerneldump_arg kda;
int error;
error = 0;
switch (cmd) {
#ifdef COMPAT_FREEBSD11
case DIOCSKERNELDUMP_FREEBSD11:
+ gone_in(13, "FreeBSD 11.x ABI compat");
+ /* FALLTHROUGH */
+#endif
+#ifdef COMPAT_FREEBSD12
+ case DIOCSKERNELDUMP_FREEBSD12:
+ if (cmd == DIOCSKERNELDUMP_FREEBSD12)
+ gone_in(14, "FreeBSD 12.x ABI compat");
+ /* FALLTHROUGH */
#endif
case DIOCSKERNELDUMP:
- error = clear_dumper(td);
+ bzero(&kda, sizeof(kda));
+ kda.kda_index = KDA_REMOVE_ALL;
+ error = dumper_remove(NULL, &kda);
break;
case FIONBIO:
break;
diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c
index 8c3b3a1c4bde..a68f9de4bd5b 100644
--- a/sys/geom/geom_dev.c
+++ b/sys/geom/geom_dev.c
@@ -135,15 +135,14 @@ g_dev_fini(struct g_class *mp)
}
static int
-g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda,
- struct thread *td)
+g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda)
{
struct g_kerneldump kd;
struct g_consumer *cp;
int error, len;
- if (dev == NULL || kda == NULL)
- return (clear_dumper(td));
+ MPASS(dev != NULL && kda != NULL);
+ MPASS(kda->kda_index != KDA_REMOVE);
cp = dev->si_drv2;
len = sizeof(kd);
@@ -154,9 +153,7 @@ g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda,
if (error != 0)
return (error);
- error = set_dumper(&kd.di, devtoname(dev), td, kda->kda_compression,
- kda->kda_encryption, kda->kda_key, kda->kda_encryptedkeysize,
- kda->kda_encryptedkey);
+ error = dumper_insert(&kd.di, devtoname(dev), kda);
if (error == 0)
dev->si_flags |= SI_DUMPDEV;
@@ -173,7 +170,7 @@ init_dumpdev(struct cdev *dev)
size_t len;
bzero(&kda, sizeof(kda));
- kda.kda_enable = 1;
+ kda.kda_index = KDA_APPEND;
if (dumpdev == NULL)
return (0);
@@ -190,7 +187,7 @@ init_dumpdev(struct cdev *dev)
if (error != 0)
return (error);
- error = g_dev_setdumpdev(dev, &kda, curthread);
+ error = g_dev_setdumpdev(dev, &kda);
if (error == 0) {
freeenv(dumpdev);
dumpdev = NULL;
@@ -509,6 +506,9 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread
struct g_provider *pp;
off_t offset, length, chunk, odd;
int i, error;
+#ifdef COMPAT_FREEBSD12
+ struct diocskerneldump_arg kda_copy;
+#endif
cp = dev->si_drv2;
pp = cp->provider;
@@ -547,31 +547,55 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread
{
struct diocskerneldump_arg kda;
+ gone_in(13, "FreeBSD 11.x ABI compat");
+
bzero(&kda, sizeof(kda));
kda.kda_encryption = KERNELDUMP_ENC_NONE;
- kda.kda_enable = (uint8_t)*(u_int *)data;
- if (kda.kda_enable == 0)
- error = g_dev_setdumpdev(NULL, NULL, td);
+ kda.kda_index = (*(u_int *)data ? 0 : KDA_REMOVE_ALL);
+ if (kda.kda_index == KDA_REMOVE_ALL)
+ error = dumper_remove(devtoname(dev), &kda);
else
- error = g_dev_setdumpdev(dev, &kda, td);
+ error = g_dev_setdumpdev(dev, &kda);
break;
}
#endif
+#ifdef COMPAT_FREEBSD12
+ case DIOCSKERNELDUMP_FREEBSD12:
+ {
+ struct diocskerneldump_arg_freebsd12 *kda12;
+
+ gone_in(14, "FreeBSD 12.x ABI compat");
+
+ kda12 = (void *)data;
+ memcpy(&kda_copy, kda12, sizeof(kda_copy));
+ kda_copy.kda_index = (kda12->kda12_enable ?
+ 0 : KDA_REMOVE_ALL);
+
+ explicit_bzero(kda12, sizeof(*kda12));
+ /* Kludge to pass kda_copy to kda in fallthrough. */
+ data = (void *)&kda_copy;
+ }
+ /* FALLTHROUGH */
+#endif
case DIOCSKERNELDUMP:
{
struct diocskerneldump_arg *kda;
uint8_t *encryptedkey;
kda = (struct diocskerneldump_arg *)data;
- if (kda->kda_enable == 0) {
- error = g_dev_setdumpdev(NULL, NULL, td);
+ if (kda->kda_index == KDA_REMOVE_ALL ||
+ kda->kda_index == KDA_REMOVE_DEV ||
+ kda->kda_index == KDA_REMOVE) {
+ error = dumper_remove(devtoname(dev), kda);
+ explicit_bzero(kda, sizeof(*kda));
break;
}
if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
- if (kda->kda_encryptedkeysize <= 0 ||
+ if (kda->kda_encryptedkeysize == 0 ||
kda->kda_encryptedkeysize >
KERNELDUMP_ENCKEY_MAX_SIZE) {
+ explicit_bzero(kda, sizeof(*kda));
return (EINVAL);
}
encryptedkey = malloc(kda->kda_encryptedkeysize, M_TEMP,
@@ -583,7 +607,7 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread
}
if (error == 0) {
kda->kda_encryptedkey = encryptedkey;
- error = g_dev_setdumpdev(dev, kda, td);
+ error = g_dev_setdumpdev(dev, kda);
}
if (encryptedkey != NULL) {
explicit_bzero(encryptedkey, kda->kda_encryptedkeysize);
@@ -859,8 +883,13 @@ g_dev_orphan(struct g_consumer *cp)
g_trace(G_T_TOPOLOGY, "g_dev_orphan(%p(%s))", cp, cp->geom->name);
/* Reset any dump-area set on this device */
- if (dev->si_flags & SI_DUMPDEV)
- (void)clear_dumper(curthread);
+ if (dev->si_flags & SI_DUMPDEV) {
+ struct diocskerneldump_arg kda;
+
+ bzero(&kda, sizeof(kda));
+ kda.kda_index = KDA_REMOVE_DEV;
+ (void)dumper_remove(devtoname(dev), &kda);
+ }
/* Destroy the struct cdev *so we get no more requests */
delist_dev(dev);
diff --git a/sys/geom/raid/g_raid.h b/sys/geom/raid/g_raid.h
index d295ed32577e..e693e3c00504 100644
--- a/sys/geom/raid/g_raid.h
+++ b/sys/geom/raid/g_raid.h
@@ -155,7 +155,6 @@ struct g_raid_disk {
struct g_raid_softc *d_softc; /* Back-pointer to softc. */
struct g_consumer *d_consumer; /* GEOM disk consumer. */
void *d_md_data; /* Disk's metadata storage. */
- struct g_kerneldump d_kd; /* Kernel dumping method/args. */
int d_candelete; /* BIO_DELETE supported. */
uint64_t d_flags; /* Additional flags. */
u_int d_state; /* Disk state. */
@@ -164,6 +163,7 @@ struct g_raid_disk {
int d_read_errs; /* Count of the read errors */
TAILQ_HEAD(, g_raid_subdisk) d_subdisks; /* List of subdisks. */
TAILQ_ENTRY(g_raid_disk) d_next; /* Next disk in the node. */
+ struct g_kerneldump d_kd; /* Kernel dumping method/args. */
};
#define G_RAID_SUBDISK_S_NONE 0x00 /* Absent. */
diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c
index 2ac99440e783..f2e98144e38b 100644
--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include "opt_ekcd.h"
#include "opt_kdb.h"
#include "opt_panic.h"
+#include "opt_printf.h"
#include "opt_sched.h"
#include "opt_watchdog.h"
@@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/compressor.h>
#include <sys/cons.h>
+#include <sys/disk.h>
#include <sys/eventhandler.h>
#include <sys/filedesc.h>
#include <sys/jail.h>
@@ -69,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include <sys/reboot.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
+#include <sys/sbuf.h>
#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
@@ -209,7 +212,16 @@ const char *panicstr;
int dumping; /* system is dumping */
int rebooting; /* system is rebooting */
-static struct dumperinfo dumper; /* our selected dumper */
+/*
+ * Used to serialize between sysctl kern.shutdown.dumpdevname and list
+ * modifications via ioctl.
+ */
+static struct mtx dumpconf_list_lk;
+MTX_SYSINIT(dumper_configs, &dumpconf_list_lk, "dumper config list", MTX_DEF);
+
+/* Our selected dumper(s). */
+static TAILQ_HEAD(dumpconflist, dumperinfo) dumper_configs =
+ TAILQ_HEAD_INITIALIZER(dumper_configs);
/* Context information for dump-debuggers. */
static struct pcb dumppcb; /* Registers. */
@@ -364,7 +376,7 @@ doadump(boolean_t textdump)
error = 0;
if (dumping)
return (EBUSY);
- if (dumper.dumper == NULL)
+ if (TAILQ_EMPTY(&dumper_configs))
return (ENXIO);
savectx(&dumppcb);
@@ -375,11 +387,18 @@ doadump(boolean_t textdump)
#ifdef DDB
if (textdump && textdump_pending) {
coredump = FALSE;
- textdump_dumpsys(&dumper);
+ textdump_dumpsys(TAILQ_FIRST(&dumper_configs));
}
#endif
- if (coredump)
- error = dumpsys(&dumper);
+ if (coredump) {
+ struct dumperinfo *di;
+
+ TAILQ_FOREACH(di, &dumper_configs, di_next) {
+ error = dumpsys(di);
+ if (error == 0)
+ break;
+ }
+ }
dumping--;
return (error);
@@ -952,9 +971,35 @@ kthread_shutdown(void *arg, int howto)
printf("done\n");
}
-static char dumpdevname[sizeof(((struct cdev*)NULL)->si_name)];
-SYSCTL_STRING(_kern_shutdown, OID_AUTO, dumpdevname, CTLFLAG_RD,
- dumpdevname, 0, "Device for kernel dumps");
+static int
+dumpdevname_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+ char buf[256];
+ struct dumperinfo *di;
+ struct sbuf sb;
+ int error;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error != 0)
+ return (error);
+
+ sbuf_new_for_sysctl(&sb, buf, sizeof(buf), req);
+
+ mtx_lock(&dumpconf_list_lk);
+ TAILQ_FOREACH(di, &dumper_configs, di_next) {
+ if (di != TAILQ_FIRST(&dumper_configs))
+ sbuf_putc(&sb, ',');
+ sbuf_cat(&sb, di->di_devname);
+ }
+ mtx_unlock(&dumpconf_list_lk);
+
+ error = sbuf_finish(&sb);
+ sbuf_delete(&sb);
+ return (error);
+}
+SYSCTL_PROC(_kern_shutdown, OID_AUTO, dumpdevname, CTLTYPE_STRING | CTLFLAG_RD,
+ &dumper_configs, 0, dumpdevname_sysctl_handler, "A",
+ "Device(s) for kernel dumps");
static int _dump_append(struct dumperinfo *di, void *virtual,
vm_offset_t physical, size_t length);
@@ -1092,31 +1137,67 @@ kerneldumpcomp_destroy(struct dumperinfo *di)
free(kdcomp, M_DUMPER);
}
+/*
+ * Must not be present on global list.
+ */
+static void
+free_single_dumper(struct dumperinfo *di)
+{
+
+ if (di == NULL)
+ return;
+
+ if (di->blockbuf != NULL) {
+ explicit_bzero(di->blockbuf, di->blocksize);
+ free(di->blockbuf, M_DUMPER);
+ }
+
+ kerneldumpcomp_destroy(di);
+
+#ifdef EKCD
+ if (di->kdcrypto != NULL) {
+ explicit_bzero(di->kdcrypto, sizeof(*di->kdcrypto) +
+ di->kdcrypto->kdc_dumpkeysize);
+ free(di->kdcrypto, M_EKCD);
+ }
+#endif
+
+ explicit_bzero(di, sizeof(*di));
+ free(di, M_DUMPER);
+}
+
/* Registration of dumpers */
int
-set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
- uint8_t compression, uint8_t encryption, const uint8_t *key,
- uint32_t encryptedkeysize, const uint8_t *encryptedkey)
+dumper_insert(const struct dumperinfo *di_template, const char *devname,
+ const struct diocskerneldump_arg *kda)
{
- size_t wantcopy;
+ struct dumperinfo *newdi, *listdi;
+ bool inserted;
+ uint8_t index;
int error;
- error = priv_check(td, PRIV_SETDUMPER);
+ index = kda->kda_index;
+ MPASS(index != KDA_REMOVE && index != KDA_REMOVE_DEV &&
+ index != KDA_REMOVE_ALL);
+
+ error = priv_check(curthread, PRIV_SETDUMPER);
if (error != 0)
return (error);
- if (dumper.dumper != NULL)
- return (EBUSY);
- dumper = *di;
- dumper.blockbuf = NULL;
- dumper.kdcrypto = NULL;
- dumper.kdcomp = NULL;
+ newdi = malloc(sizeof(*newdi) + strlen(devname) + 1, M_DUMPER, M_WAITOK
+ | M_ZERO);
+ memcpy(newdi, di_template, sizeof(*newdi));
+ newdi->blockbuf = NULL;
+ newdi->kdcrypto = NULL;
+ newdi->kdcomp = NULL;
+ strcpy(newdi->di_devname, devname);
- if (encryption != KERNELDUMP_ENC_NONE) {
+ if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
#ifdef EKCD
- dumper.kdcrypto = kerneldumpcrypto_create(di->blocksize,
- encryption, key, encryptedkeysize, encryptedkey);
- if (dumper.kdcrypto == NULL) {
+ newdi->kdcrypto = kerneldumpcrypto_create(di_template->blocksize,
+ kda->kda_encryption, kda->kda_key,
+ kda->kda_encryptedkeysize, kda->kda_encryptedkey);
+ if (newdi->kdcrypto == NULL) {
error = EINVAL;
goto cleanup;
}
@@ -1125,66 +1206,117 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
goto cleanup;
#endif
}
-
- wantcopy = strlcpy(dumpdevname, devname, sizeof(dumpdevname));
- if (wantcopy >= sizeof(dumpdevname)) {
- printf("set_dumper: device name truncated from '%s' -> '%s'\n",
- devname, dumpdevname);
- }
-
- if (compression != KERNELDUMP_COMP_NONE) {
+ if (kda->kda_compression != KERNELDUMP_COMP_NONE) {
/*
* We currently can't support simultaneous encryption and
- * compression.
+ * compression because our only encryption mode is an unpadded
+ * block cipher, go figure. This is low hanging fruit to fix.
*/
- if (encryption != KERNELDUMP_ENC_NONE) {
+ if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
error = EOPNOTSUPP;
goto cleanup;
}
- dumper.kdcomp = kerneldumpcomp_create(&dumper, compression);
- if (dumper.kdcomp == NULL) {
+ newdi->kdcomp = kerneldumpcomp_create(newdi,
+ kda->kda_compression);
+ if (newdi->kdcomp == NULL) {
error = EINVAL;
goto cleanup;
}
}
- dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO);
+ newdi->blockbuf = malloc(newdi->blocksize, M_DUMPER, M_WAITOK | M_ZERO);
+
+ /* Add the new configuration to the queue */
+ mtx_lock(&dumpconf_list_lk);
+ inserted = false;
+ TAILQ_FOREACH(listdi, &dumper_configs, di_next) {
+ if (index == 0) {
+ TAILQ_INSERT_BEFORE(listdi, newdi, di_next);
+ inserted = true;
+ break;
+ }
+ index--;
+ }
+ if (!inserted)
+ TAILQ_INSERT_TAIL(&dumper_configs, newdi, di_next);
+ mtx_unlock(&dumpconf_list_lk);
+
return (0);
cleanup:
- (void)clear_dumper(td);
+ free_single_dumper(newdi);
return (error);
}
-int
-clear_dumper(struct thread *td)
+static bool
+dumper_config_match(const struct dumperinfo *di, const char *devname,
+ const struct diocskerneldump_arg *kda)
{
- int error;
+ if (kda->kda_index == KDA_REMOVE_ALL)
+ return (true);
- error = priv_check(td, PRIV_SETDUMPER);
- if (error != 0)
- return (error);
+ if (strcmp(di->di_devname, devname) != 0)
+ return (false);
-#ifdef NETDUMP
- netdump_mbuf_drain();
-#endif
+ /*
+ * Allow wildcard removal of configs matching a device on g_dev_orphan.
+ */
+ if (kda->kda_index == KDA_REMOVE_DEV)
+ return (true);
+ if (di->kdcomp != NULL) {
+ if (di->kdcomp->kdc_format != kda->kda_compression)
+ return (false);
+ } else if (kda->kda_compression != KERNELDUMP_COMP_NONE)
+ return (false);
#ifdef EKCD
- if (dumper.kdcrypto != NULL) {
- explicit_bzero(dumper.kdcrypto, sizeof(*dumper.kdcrypto) +
- dumper.kdcrypto->kdc_dumpkeysize);
- free(dumper.kdcrypto, M_EKCD);
- }
+ if (di->kdcrypto != NULL) {
+ if (di->kdcrypto->kdc_encryption != kda->kda_encryption)
+ return (false);
+ /*
+ * Do we care to verify keys match to delete? It seems weird
+ * to expect multiple fallback dump configurations on the same
+ * device that only differ in crypto key.
+ */
+ } else
#endif
+ if (kda->kda_encryption != KERNELDUMP_ENC_NONE)
+ return (false);
- kerneldumpcomp_destroy(&dumper);
+ return (true);
+}
+
+int
+dumper_remove(const char *devname, const struct diocskerneldump_arg *kda)
+{
+ struct dumperinfo *di, *sdi;
+ bool found;
+ int error;
- if (dumper.blockbuf != NULL) {
- explicit_bzero(dumper.blockbuf, dumper.blocksize);
- free(dumper.blockbuf, M_DUMPER);
+ error = priv_check(curthread, PRIV_SETDUMPER);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Try to find a matching configuration, and kill it.
+ *
+ * NULL 'kda' indicates remove any configuration matching 'devname',
+ * which may remove multiple configurations in atypical configurations.
+ */
+ found = false;
+ mtx_lock(&dumpconf_list_lk);
+ TAILQ_FOREACH_SAFE(di, &dumper_configs, di_next, sdi) {
+ if (dumper_config_match(di, devname, kda)) {
+ found = true;
+ TAILQ_REMOVE(&dumper_configs, di, di_next);
+ free_single_dumper(di);
+ }
}
- explicit_bzero(&dumper, sizeof(dumper));
- dumpdevname[0] = '\0';
+ mtx_unlock(&dumpconf_list_lk);
+
+ /* Only produce ENOENT if a more targeted match didn't match. */
+ if (!found && kda->kda_index == KDA_REMOVE)
+ return (ENOENT);
return (0);
}
diff --git a/sys/netinet/netdump/netdump.h b/sys/netinet/netdump/netdump.h
index 7575afecd1b7..cdf53b78c50e 100644
--- a/sys/netinet/netdump/netdump.h
+++ b/sys/netinet/netdump/netdump.h
@@ -60,18 +60,18 @@ struct netdump_ack {
uint32_t na_seqno; /* Match acks with msgs. */
} __packed;
-struct netdump_conf {
- struct diocskerneldump_arg ndc_kda;
- char ndc_iface[IFNAMSIZ];
- struct in_addr ndc_server;
- struct in_addr ndc_client;
- struct in_addr ndc_gateway;
+struct netdump_conf_freebsd12 {
+ struct diocskerneldump_arg_freebsd12 ndc12_kda;
+ char ndc12_iface[IFNAMSIZ];
+ struct in_addr ndc12_server;
+ struct in_addr ndc12_client;
+ struct in_addr ndc12_gateway;
};
-#define _PATH_NETDUMP "/dev/netdump"
+#define NETDUMPGCONF_FREEBSD12 _IOR('n', 1, struct netdump_conf_freebsd12)
+#define NETDUMPSCONF_FREEBSD12 _IOW('n', 2, struct netdump_conf_freebsd12)
-#define NETDUMPGCONF _IOR('n', 1, struct netdump_conf)
-#define NETDUMPSCONF _IOW('n', 2, struct netdump_conf)
+#define _PATH_NETDUMP "/dev/netdump"
#ifdef _KERNEL
#ifdef NETDUMP
diff --git a/sys/netinet/netdump/netdump_client.c b/sys/netinet/netdump/netdump_client.c
index 2503491ca131..7aff10609c39 100644
--- a/sys/netinet/netdump/netdump_client.c
+++ b/sys/netinet/netdump/netdump_client.c
@@ -89,7 +89,8 @@ __FBSDID("$FreeBSD$");
static int netdump_arp_gw(void);
static void netdump_cleanup(void);
-static int netdump_configure(struct netdump_conf *, struct thread *);
+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 int netdump_ether_output(struct mbuf *m, struct ifnet *ifp,
@@ -118,10 +119,10 @@ static uint64_t rcvd_acks;
CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT);
/* Configuration parameters. */
-static struct netdump_conf nd_conf;
-#define nd_server nd_conf.ndc_server
-#define nd_client nd_conf.ndc_client
-#define nd_gateway nd_conf.ndc_gateway
+static struct diocskerneldump_arg nd_conf;
+#define nd_server nd_conf.kda_server.in4
+#define nd_client nd_conf.kda_client.in4
+#define nd_gateway nd_conf.kda_gateway.in4
/* General dynamic settings. */
static struct ether_addr nd_gw_mac;
@@ -1059,7 +1060,7 @@ static struct cdevsw netdump_cdevsw = {
static struct cdev *netdump_cdev;
static int
-netdump_configure(struct netdump_conf *conf, struct thread *td)
+netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
{
struct epoch_tracker et;
struct ifnet *ifp;
@@ -1071,7 +1072,7 @@ netdump_configure(struct netdump_conf *conf, struct thread *td)
}
NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
- if (strcmp(ifp->if_xname, conf->ndc_iface) == 0)
+ if (strcmp(ifp->if_xname, conf->kda_iface) == 0)
break;
}
/* XXX ref */
@@ -1083,7 +1084,7 @@ netdump_configure(struct netdump_conf *conf, struct thread *td)
if ((if_getflags(ifp) & IFF_UP) == 0)
return (ENXIO);
if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER)
- return (EINVAL);
+ return (ENODEV);
nd_ifp = ifp;
netdump_reinit(ifp);
@@ -1135,19 +1136,24 @@ static int
netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
int flags __unused, struct thread *td)
{
- struct diocskerneldump_arg *kda;
+ struct diocskerneldump_arg kda_copy, *conf;
struct dumperinfo dumper;
- struct netdump_conf *conf;
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;
switch (cmd) {
#ifdef COMPAT_FREEBSD11
case DIOCSKERNELDUMP_FREEBSD11:
+ gone_in(13, "11.x ABI compatibility");
u = *(u_int *)addr;
if (u != 0) {
error = ENXIO;
@@ -1159,9 +1165,17 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
}
break;
#endif
- case DIOCSKERNELDUMP:
- kda = (void *)addr;
- if (kda->kda_enable != 0) {
+#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;
}
@@ -1170,28 +1184,99 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
netdump_mbuf_drain();
}
break;
- case NETDUMPGCONF:
- conf = (struct netdump_conf *)addr;
+
+ case NETDUMPGCONF_FREEBSD12:
+ gone_in(14, "FreeBSD 12.x ABI compat");
+ conf12 = (void *)addr;
+
+ if (!nd_enabled) {
+ error = ENXIO;
+ break;
+ }
+ if (nd_conf.kda_af != AF_INET) {
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ 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 (!nd_enabled) {
error = ENXIO;
+ conf = NULL;
break;
}
- strlcpy(conf->ndc_iface, nd_ifp->if_xname,
- sizeof(conf->ndc_iface));
- memcpy(&conf->ndc_server, &nd_server, sizeof(nd_server));
- memcpy(&conf->ndc_client, &nd_client, sizeof(nd_client));
- memcpy(&conf->ndc_gateway, &nd_gateway, sizeof(nd_gateway));
+ 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.kda_af;
+ conf = NULL;
break;
- case NETDUMPSCONF:
- conf = (struct netdump_conf *)addr;
+
+#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;
- kda = &conf->ndc_kda;
+ 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->ndc_iface[sizeof(conf->ndc_iface) - 1] = '\0';
- if (kda->kda_enable == 0) {
- if (nd_enabled) {
- error = clear_dumper(td);
+ 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 (nd_enabled || conf->kda_index == KDA_REMOVE_ALL) {
+ error = dumper_remove(conf->kda_iface, conf);
if (error == 0) {
nd_enabled = 0;
netdump_mbuf_drain();
@@ -1204,19 +1289,23 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
if (error != 0)
break;
- if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
- if (kda->kda_encryptedkeysize <= 0 ||
- kda->kda_encryptedkeysize >
- KERNELDUMP_ENCKEY_MAX_SIZE)
- return (EINVAL);
- encryptedkey = malloc(kda->kda_encryptedkeysize, M_TEMP,
- M_WAITOK);
- error = copyin(kda->kda_encryptedkey, encryptedkey,
- kda->kda_encryptedkeysize);
+ 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);
- return (error);
+ break;
}
+
+ conf->kda_encryptedkey = encryptedkey;
}
memset(&dumper, 0, sizeof(dumper));
@@ -1229,12 +1318,10 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
dumper.mediaoffset = 0;
dumper.mediasize = 0;
- error = set_dumper(&dumper, conf->ndc_iface, td,
- kda->kda_compression, kda->kda_encryption,
- kda->kda_key, kda->kda_encryptedkeysize,
- encryptedkey);
+ error = dumper_insert(&dumper, conf->kda_iface, conf);
if (encryptedkey != NULL) {
- explicit_bzero(encryptedkey, kda->kda_encryptedkeysize);
+ explicit_bzero(encryptedkey,
+ conf->kda_encryptedkeysize);
free(encryptedkey, M_TEMP);
}
if (error != 0) {
@@ -1243,9 +1330,12 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
}
break;
default:
- error = EINVAL;
+ error = ENOTTY;
break;
}
+ explicit_bzero(&kda_copy, sizeof(kda_copy));
+ if (conf != NULL)
+ explicit_bzero(conf, sizeof(*conf));
return (error);
}
@@ -1265,7 +1355,7 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
static int
netdump_modevent(module_t mod __unused, int what, void *priv __unused)
{
- struct netdump_conf conf;
+ struct diocskerneldump_arg conf;
char *arg;
int error;
@@ -1278,33 +1368,41 @@ netdump_modevent(module_t mod __unused, int what, void *priv __unused)
return (error);
if ((arg = kern_getenv("net.dump.iface")) != NULL) {
- strlcpy(conf.ndc_iface, arg, sizeof(conf.ndc_iface));
+ strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
freeenv(arg);
if ((arg = kern_getenv("net.dump.server")) != NULL) {
- inet_aton(arg, &conf.ndc_server);
+ inet_aton(arg, &conf.kda_server.in4);
freeenv(arg);
}
if ((arg = kern_getenv("net.dump.client")) != NULL) {
- inet_aton(arg, &conf.ndc_server);
+ inet_aton(arg, &conf.kda_server.in4);
freeenv(arg);
}
if ((arg = kern_getenv("net.dump.gateway")) != NULL) {
- inet_aton(arg, &conf.ndc_server);
+ inet_aton(arg, &conf.kda_server.in4);
freeenv(arg);
}
+ conf.kda_af = AF_INET;
/* Ignore errors; we print a message to the console. */
(void)netdump_configure(&conf, curthread);
}
break;
case MOD_UNLOAD:
- destroy_dev(netdump_cdev);
if (nd_enabled) {
+ struct diocskerneldump_arg kda;
+
printf("netdump: disabling dump device for unload\n");
- (void)clear_dumper(curthread);
+
+ bzero(&kda, sizeof(kda));
+ kda.kda_index = KDA_REMOVE_DEV;
+ (void)dumper_remove(nd_conf.kda_iface, &kda);
+
+ netdump_mbuf_drain();
nd_enabled = 0;
}
+ destroy_dev(netdump_cdev);
break;
default:
error = EOPNOTSUPP;
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 1e004ebebcef..5741e66c5522 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -352,15 +352,19 @@ struct dumperinfo {
off_t origdumpoff; /* Starting dump offset. */
struct kerneldumpcrypto *kdcrypto; /* Kernel dump crypto. */
struct kerneldumpcomp *kdcomp; /* Kernel dump compression. */
+
+ TAILQ_ENTRY(dumperinfo) di_next;
+
+ char di_devname[];
};
extern int dumping; /* system is dumping */
int doadump(boolean_t);
-int set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
- uint8_t compression, uint8_t encryption, const uint8_t *key,
- uint32_t encryptedkeysize, const uint8_t *encryptedkey);
-int clear_dumper(struct thread *td);
+struct diocskerneldump_arg;
+int dumper_insert(const struct dumperinfo *di_template, const char *devname,
+ const struct diocskerneldump_arg *kda);
+int dumper_remove(const char *devname, const struct diocskerneldump_arg *kda);
int dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh);
int dump_append(struct dumperinfo *, void *, vm_offset_t, size_t);
diff --git a/sys/sys/disk.h b/sys/sys/disk.h
index 4338b03e924a..4fb0f8b61904 100644
--- a/sys/sys/disk.h
+++ b/sys/sys/disk.h
@@ -19,6 +19,10 @@
#include <sys/kerneldump.h>
#include <sys/types.h>
#include <sys/disk_zone.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
#ifdef _KERNEL
@@ -143,17 +147,66 @@ struct diocgattr_arg {
#define DIOCZONECMD _IOWR('d', 143, struct disk_zone_args)
+struct diocskerneldump_arg_freebsd12 {
+ uint8_t kda12_enable;
+ uint8_t kda12_compression;
+ uint8_t kda12_encryption;
+ uint8_t kda12_key[KERNELDUMP_KEY_MAX_SIZE];
+ uint32_t kda12_encryptedkeysize;
+ uint8_t *kda12_encryptedkey;
+};
+#define DIOCSKERNELDUMP_FREEBSD12 \
+ _IOW('d', 144, struct diocskerneldump_arg_freebsd12)
+
+union kd_ip {
+ struct in_addr in4;
+ struct in6_addr in6;
+};
+
+/*
+ * Sentinel values for kda_index.
+ *
+ * If kda_index is KDA_REMOVE_ALL, all dump configurations are cleared.
+ *
+ * If kda_index is KDA_REMOVE_DEV, all dump configurations for the specified
+ * device are cleared.
+ *
+ * If kda_index is KDA_REMOVE, only the specified dump configuration for the
+ * given device is removed from the list of fallback dump configurations.
+ *
+ * If kda_index is KDA_APPEND, the dump configuration is added after all
+ * existing dump configurations.
+ *
+ * Otherwise, the new configuration is inserted into the fallback dump list at
+ * index 'kda_index'.
+ */
+#define KDA_REMOVE UINT8_MAX
+#define KDA_REMOVE_ALL (UINT8_MAX - 1)
+#define KDA_REMOVE_DEV (UINT8_MAX - 2)
+#define KDA_APPEND (UINT8_MAX - 3)
struct diocskerneldump_arg {
- uint8_t kda_enable;
+ uint8_t kda_index;
uint8_t kda_compression;
uint8_t kda_encryption;
uint8_t kda_key[KERNELDUMP_KEY_MAX_SIZE];
uint32_t kda_encryptedkeysize;
uint8_t *kda_encryptedkey;
+ char kda_iface[IFNAMSIZ];
+ union kd_ip kda_server;
+ union kd_ip kda_client;
+ union kd_ip kda_gateway;
+ uint8_t kda_af;
};
-#define DIOCSKERNELDUMP _IOW('d', 144, struct diocskerneldump_arg)
+_Static_assert(__offsetof(struct diocskerneldump_arg, kda_iface) ==
+ sizeof(struct diocskerneldump_arg_freebsd12), "simplifying assumption");
+#define DIOCSKERNELDUMP _IOW('d', 145, struct diocskerneldump_arg)
/*
* Enable/Disable the device for kernel core dumps.
*/
+#define DIOCGKERNELDUMP _IOWR('d', 146, struct diocskerneldump_arg)
+ /*
+ * Get current kernel netdump configuration details for a given index.
+ */
+
#endif /* _SYS_DISK_H_ */
diff --git a/sys/sys/param.h b/sys/sys/param.h
index c4d6c6a5e420..ac6201144bd3 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -60,7 +60,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 1300022 /* Master, propagated to newvers */
+#define __FreeBSD_version 1300023 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,