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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to