Date: Mon, 6 May 2019 18:24:07 +0000 (UTC) From: Conrad Meyer <cem@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r347192 - in head: sbin/dumpon sys/dev/null sys/geom sys/geom/raid sys/kern sys/netinet/netdump sys/sys Message-ID: <201905061824.x46IO7uQ077004@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: cem Date: Mon May 6 18:24:07 2019 New Revision: 347192 URL: https://svnweb.freebsd.org/changeset/base/347192 Log: 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 Modified: head/sbin/dumpon/dumpon.8 head/sbin/dumpon/dumpon.c head/sys/dev/null/null.c head/sys/geom/geom_dev.c head/sys/geom/raid/g_raid.h head/sys/kern/kern_shutdown.c head/sys/netinet/netdump/netdump.h head/sys/netinet/netdump/netdump_client.c head/sys/sys/conf.h head/sys/sys/disk.h head/sys/sys/param.h Modified: head/sbin/dumpon/dumpon.8 ============================================================================== --- head/sbin/dumpon/dumpon.8 Mon May 6 16:54:35 2019 (r347191) +++ head/sbin/dumpon/dumpon.8 Mon May 6 18:24:07 2019 (r347192) @@ -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. Modified: head/sbin/dumpon/dumpon.c ============================================================================== --- head/sbin/dumpon/dumpon.c Mon May 6 16:54:35 2019 (r347191) +++ head/sbin/dumpon/dumpon.c Mon May 6 18:24:07 2019 (r347192) @@ -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_ 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,10 +310,18 @@ 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) { fd = open(_PATH_NETDUMP, O_RDONLY); @@ -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); + 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); -#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); } Modified: head/sys/dev/null/null.c ============================================================================== --- head/sys/dev/null/null.c Mon May 6 16:54:35 2019 (r347191) +++ head/sys/dev/null/null.c Mon May 6 18:24:07 2019 (r347192) @@ -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; Modified: head/sys/geom/geom_dev.c ============================================================================== --- head/sys/geom/geom_dev.c Mon May 6 16:54:35 2019 (r347191) +++ head/sys/geom/geom_dev.c Mon May 6 18:24:07 2019 (r347192) @@ -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 diocskerneld 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 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 { 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 } 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); Modified: head/sys/geom/raid/g_raid.h ============================================================================== --- head/sys/geom/raid/g_raid.h Mon May 6 16:54:35 2019 (r347191) +++ head/sys/geom/raid/g_raid.h Mon May 6 18:24:07 2019 (r347192) @@ -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. */ Modified: head/sys/kern/kern_shutdown.c ============================================================================== --- head/sys/kern/kern_shutdown.c Mon May 6 16:54:35 2019 (r347191) +++ head/sys/kern/kern_shutdown.c Mon May 6 18:24:07 2019 (r347192) @@ -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,8 +212,17 @@ 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. */ lwpid_t dumptid; /* Thread ID. */ @@ -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,12 +387,19 @@ 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,10 +971,36 @@ 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, 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); +} - if (dumper.blockbuf != NULL) { - explicit_bzero(dumper.blockbuf, dumper.blocksize); - free(dumper.blockbuf, M_DUMPER); +int +dumper_remove(const char *devname, const struct diocskerneldump_arg *kda) +{ + struct dumperinfo *di, *sdi; + bool found; + int error; + + 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); } Modified: head/sys/netinet/netdump/netdump.h ============================================================================== --- head/sys/netinet/netdump/netdump.h Mon May 6 16:54:35 2019 (r347191) +++ head/sys/netinet/netdump/netdump.h Mon May 6 18:24:07 2019 (r347192) @@ -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 Modified: head/sys/netinet/netdump/netdump_client.c ============================================================================== --- head/sys/netinet/netdump/netdump_client.c Mon May 6 16:54:35 2019 (r347191) +++ head/sys/netinet/netdump/netdump_client.c Mon May 6 18:24:07 2019 (r347192) @@ -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 th } 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 th 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 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201905061824.x46IO7uQ077004>