mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
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
This commit is contained in:
parent
46068e86c3
commit
6b6e2954dd
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=347192
|
@ -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.
|
||||
|
|
|
@ -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,8 +310,16 @@ listdumpdev(void)
|
|||
if (strlen(dumpdev) == 0)
|
||||
(void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev));
|
||||
|
||||
if (verbose)
|
||||
printf("kernel dumps on ");
|
||||
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. */
|
||||
|
@ -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,23 +520,11 @@ 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);
|
||||
|
@ -509,15 +532,29 @@ main(int argc, char *argv[])
|
|||
error = ioctl(fd, DIOCSKERNELDUMP, kdap);
|
||||
if (error != 0)
|
||||
error = errno;
|
||||
explicit_bzero(kdap->kda_encryptedkey,
|
||||
kdap->kda_encryptedkeysize);
|
||||
explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize);
|
||||
free(kdap->kda_encryptedkey);
|
||||
explicit_bzero(kdap, sizeof(*kdap));
|
||||
if (error != 0)
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,15 +547,35 @@ 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:
|
||||
{
|
||||
|
@ -563,15 +583,19 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread
|
|||
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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (kda->kda_index == KDA_REMOVE_ALL)
|
||||
return (true);
|
||||
|
||||
if (strcmp(di->di_devname, devname) != 0)
|
||||
return (false);
|
||||
|
||||
/*
|
||||
* 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 (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);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
int
|
||||
dumper_remove(const char *devname, const struct diocskerneldump_arg *kda)
|
||||
{
|
||||
struct dumperinfo *di, *sdi;
|
||||
bool found;
|
||||
int error;
|
||||
|
||||
error = priv_check(td, PRIV_SETDUMPER);
|
||||
error = priv_check(curthread, PRIV_SETDUMPER);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
#ifdef NETDUMP
|
||||
netdump_mbuf_drain();
|
||||
#endif
|
||||
|
||||
#ifdef EKCD
|
||||
if (dumper.kdcrypto != NULL) {
|
||||
explicit_bzero(dumper.kdcrypto, sizeof(*dumper.kdcrypto) +
|
||||
dumper.kdcrypto->kdc_dumpkeysize);
|
||||
free(dumper.kdcrypto, M_EKCD);
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
kerneldumpcomp_destroy(&dumper);
|
||||
|
||||
if (dumper.blockbuf != NULL) {
|
||||
explicit_bzero(dumper.blockbuf, dumper.blocksize);
|
||||
free(dumper.blockbuf, M_DUMPER);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
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));
|
||||
if (nd_conf.kda_af != AF_INET) {
|
||||
error = EOPNOTSUPP;
|
||||
break;
|
||||
case NETDUMPSCONF:
|
||||
conf = (struct netdump_conf *)addr;
|
||||
encryptedkey = NULL;
|
||||
kda = &conf->ndc_kda;
|
||||
}
|
||||
|
||||
conf->ndc_iface[sizeof(conf->ndc_iface) - 1] = '\0';
|
||||
if (kda->kda_enable == 0) {
|
||||
if (nd_enabled) {
|
||||
error = clear_dumper(td);
|
||||
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->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;
|
||||
|
||||
#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 (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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue