Changeset View
Changeset View
Standalone View
Standalone View
sbin/dumpon/dumpon.c
Show First 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
static int verbose; | static int verbose; | ||||
static void _Noreturn | static void _Noreturn | ||||
usage(void) | usage(void) | ||||
{ | { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"usage: dumpon [-v] [-k <pubkey>] [-Zz] <device>\n" | "usage: dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz] <device>\n" | ||||
" dumpon [-v] [-k <pubkey>] [-Zz]\n" | " dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz]\n" | ||||
" [-g <gateway>] -s <server> -c <client> <iface>\n" | " [-g <gateway>] -s <server> -c <client> <iface>\n" | ||||
" dumpon [-v] off\n" | " dumpon [-v] off\n" | ||||
" dumpon [-v] -l\n"); | " dumpon [-v] -l\n"); | ||||
exit(EX_USAGE); | exit(EX_USAGE); | ||||
} | } | ||||
/* | /* | ||||
* Look for a default route on the specified interface. | * Look for a default route on the specified interface. | ||||
▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | #endif | ||||
RSA_free(pubkey); | RSA_free(pubkey); | ||||
} | } | ||||
#endif | #endif | ||||
static void | static void | ||||
listdumpdev(void) | listdumpdev(void) | ||||
{ | { | ||||
char dumpdev[PATH_MAX]; | char dumpdev[PATH_MAX]; | ||||
struct netdump_conf ndconf; | struct diocskerneldump_arg ndconf; | ||||
size_t len; | size_t len; | ||||
const char *sysctlname = "kern.shutdown.dumpdevname"; | const char *sysctlname = "kern.shutdown.dumpdevname"; | ||||
int fd; | int fd; | ||||
len = sizeof(dumpdev); | len = sizeof(dumpdev); | ||||
if (sysctlbyname(sysctlname, &dumpdev, &len, NULL, 0) != 0) { | if (sysctlbyname(sysctlname, &dumpdev, &len, NULL, 0) != 0) { | ||||
if (errno == ENOMEM) { | if (errno == ENOMEM) { | ||||
err(EX_OSERR, "Kernel returned too large of a buffer for '%s'\n", | err(EX_OSERR, "Kernel returned too large of a buffer for '%s'\n", | ||||
sysctlname); | sysctlname); | ||||
} else { | } else { | ||||
err(EX_OSERR, "Sysctl get '%s'\n", sysctlname); | err(EX_OSERR, "Sysctl get '%s'\n", sysctlname); | ||||
} | } | ||||
} | } | ||||
if (strlen(dumpdev) == 0) | if (strlen(dumpdev) == 0) | ||||
(void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev)); | (void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev)); | ||||
if (verbose) | if (verbose) { | ||||
printf("kernel dumps on "); | 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); | printf("%s\n", dumpdev); | ||||
/* If netdump is enabled, print the configuration parameters. */ | /* If netdump is enabled, print the configuration parameters. */ | ||||
scottl: Consider using strsep() here, it'll make the code more resilient to input errors and eliminate… | |||||
Done Inline ActionsSure, I can convert it to that API. In this case, the input comes from a trusted source (kernel) so I'm not especially worried about handling invalid input. I do like that the interface seems more ergonomic. cem: Sure, I can convert it to that API. In this case, the input comes from a trusted source… | |||||
if (verbose) { | if (verbose) { | ||||
fd = open(_PATH_NETDUMP, O_RDONLY); | fd = open(_PATH_NETDUMP, O_RDONLY); | ||||
if (fd < 0) { | if (fd < 0) { | ||||
if (errno != ENOENT) | if (errno != ENOENT) | ||||
err(EX_OSERR, "opening %s", _PATH_NETDUMP); | err(EX_OSERR, "opening %s", _PATH_NETDUMP); | ||||
return; | return; | ||||
} | } | ||||
if (ioctl(fd, NETDUMPGCONF, &ndconf) != 0) { | if (ioctl(fd, DIOCGKERNELDUMP, &ndconf) != 0) { | ||||
if (errno != ENXIO) | if (errno != ENXIO) | ||||
err(EX_OSERR, "ioctl(NETDUMPGCONF)"); | err(EX_OSERR, "ioctl(DIOCGKERNELDUMP)"); | ||||
(void)close(fd); | (void)close(fd); | ||||
return; | return; | ||||
} | } | ||||
printf("server address: %s\n", inet_ntoa(ndconf.ndc_server)); | printf("server address: %s\n", inet_ntoa(ndconf.kda_server)); | ||||
printf("client address: %s\n", inet_ntoa(ndconf.ndc_client)); | printf("client address: %s\n", inet_ntoa(ndconf.kda_client)); | ||||
printf("gateway address: %s\n", inet_ntoa(ndconf.ndc_gateway)); | printf("gateway address: %s\n", inet_ntoa(ndconf.kda_gateway)); | ||||
(void)close(fd); | (void)close(fd); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
opendumpdev(const char *arg, char *dumpdev) | opendumpdev(const char *arg, char *dumpdev) | ||||
{ | { | ||||
int fd, i; | int fd, i; | ||||
Show All 13 Lines | if (fd < 0) | ||||
err(EX_OSFILE, "%s", dumpdev); | err(EX_OSFILE, "%s", dumpdev); | ||||
return (fd); | return (fd); | ||||
} | } | ||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
char dumpdev[PATH_MAX]; | char dumpdev[PATH_MAX]; | ||||
struct diocskerneldump_arg _kda, *kdap; | struct diocskerneldump_arg ndconf, *kdap; | ||||
struct netdump_conf ndconf; | |||||
struct addrinfo hints, *res; | struct addrinfo hints, *res; | ||||
const char *dev, *pubkeyfile, *server, *client, *gateway; | const char *dev, *pubkeyfile, *server, *client, *gateway; | ||||
int ch, error, fd; | int ch, error, fd; | ||||
bool enable, gzip, list, netdump, zstd; | bool gzip, list, netdump, zstd, insert, remove; | ||||
uint8_t ins_idx; | |||||
gzip = list = netdump = zstd = false; | gzip = list = netdump = zstd = insert = remove = false; | ||||
kdap = NULL; | kdap = NULL; | ||||
pubkeyfile = NULL; | pubkeyfile = NULL; | ||||
server = client = gateway = 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) { | switch ((char)ch) { | ||||
case 'c': | case 'c': | ||||
client = optarg; | client = optarg; | ||||
break; | break; | ||||
case 'g': | case 'g': | ||||
gateway = optarg; | gateway = optarg; | ||||
break; | 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': | case 'k': | ||||
pubkeyfile = optarg; | pubkeyfile = optarg; | ||||
break; | break; | ||||
case 'l': | case 'l': | ||||
list = true; | list = true; | ||||
break; | break; | ||||
case 'r': | |||||
remove = true; | |||||
break; | |||||
case 's': | case 's': | ||||
server = optarg; | server = optarg; | ||||
break; | break; | ||||
case 'v': | case 'v': | ||||
verbose = 1; | verbose = 1; | ||||
break; | break; | ||||
case 'Z': | case 'Z': | ||||
zstd = true; | zstd = true; | ||||
break; | break; | ||||
case 'z': | case 'z': | ||||
gzip = true; | gzip = true; | ||||
break; | break; | ||||
default: | default: | ||||
usage(); | usage(); | ||||
} | } | ||||
if (gzip && zstd) | if (gzip && zstd) | ||||
errx(EX_USAGE, "The -z and -Z options are mutually exclusive."); | errx(EX_USAGE, "The -z and -Z options are mutually exclusive."); | ||||
if (insert && remove) | |||||
errx(EX_USAGE, "The -i and -r options are mutually exclusive."); | |||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
if (list) { | if (list) { | ||||
listdumpdev(); | listdumpdev(); | ||||
exit(EX_OK); | exit(EX_OK); | ||||
} | } | ||||
if (argc != 1) | if (argc != 1) | ||||
usage(); | usage(); | ||||
#ifndef HAVE_CRYPTO | #ifndef HAVE_CRYPTO | ||||
if (pubkeyfile != NULL) | if (pubkeyfile != NULL) | ||||
errx(EX_UNAVAILABLE,"Unable to use the public key." | errx(EX_UNAVAILABLE,"Unable to use the public key." | ||||
" Recompile dumpon with OpenSSL support."); | " Recompile dumpon with OpenSSL support."); | ||||
#endif | #endif | ||||
if (server != NULL && client != NULL) { | if (server != NULL && client != NULL) { | ||||
enable = true; | |||||
dev = _PATH_NETDUMP; | dev = _PATH_NETDUMP; | ||||
netdump = true; | netdump = true; | ||||
kdap = &ndconf.ndc_kda; | |||||
} else if (server == NULL && client == NULL && argc > 0) { | } else if (server == NULL && client == NULL && argc > 0) { | ||||
enable = strcmp(argv[0], "off") != 0; | if (strcmp(argv[0], "off") == 0) { | ||||
dev = enable ? argv[0] : _PATH_DEVNULL; | remove = true; | ||||
dev = _PATH_DEVNULL; | |||||
} else | |||||
dev = argv[0]; | |||||
netdump = false; | netdump = false; | ||||
kdap = &_kda; | |||||
} else | } else | ||||
usage(); | usage(); | ||||
fd = opendumpdev(dev, dumpdev); | fd = opendumpdev(dev, dumpdev); | ||||
if (!netdump && !gzip) | if (!netdump && !gzip && !remove) | ||||
check_size(fd, dumpdev); | check_size(fd, dumpdev); | ||||
kdap = &ndconf; | |||||
bzero(kdap, sizeof(*kdap)); | 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)); | if (remove) | ||||
kdap->kda_enable = 1; | kdap->kda_index = KDA_REMOVE; | ||||
else | |||||
kdap->kda_index = ins_idx; | |||||
kdap->kda_compression = KERNELDUMP_COMP_NONE; | kdap->kda_compression = KERNELDUMP_COMP_NONE; | ||||
if (zstd) | if (zstd) | ||||
kdap->kda_compression = KERNELDUMP_COMP_ZSTD; | kdap->kda_compression = KERNELDUMP_COMP_ZSTD; | ||||
else if (gzip) | else if (gzip) | ||||
kdap->kda_compression = KERNELDUMP_COMP_GZIP; | kdap->kda_compression = KERNELDUMP_COMP_GZIP; | ||||
if (netdump) { | if (netdump) { | ||||
memset(&hints, 0, sizeof(hints)); | memset(&hints, 0, sizeof(hints)); | ||||
hints.ai_family = AF_INET; | hints.ai_family = AF_INET; | ||||
hints.ai_protocol = IPPROTO_UDP; | hints.ai_protocol = IPPROTO_UDP; | ||||
res = NULL; | res = NULL; | ||||
error = getaddrinfo(server, NULL, &hints, &res); | error = getaddrinfo(server, NULL, &hints, &res); | ||||
if (error != 0) | if (error != 0) | ||||
err(1, "%s", gai_strerror(error)); | err(1, "%s", gai_strerror(error)); | ||||
if (res == NULL) | if (res == NULL) | ||||
errx(1, "failed to resolve '%s'", server); | errx(1, "failed to resolve '%s'", server); | ||||
server = inet_ntoa( | server = inet_ntoa( | ||||
((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr); | ((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr); | ||||
freeaddrinfo(res); | freeaddrinfo(res); | ||||
if (strlcpy(ndconf.ndc_iface, argv[0], | if (strlcpy(ndconf.kda_iface, argv[0], | ||||
sizeof(ndconf.ndc_iface)) >= sizeof(ndconf.ndc_iface)) | sizeof(ndconf.kda_iface)) >= sizeof(ndconf.kda_iface)) | ||||
errx(EX_USAGE, "invalid interface name '%s'", argv[0]); | errx(EX_USAGE, "invalid interface name '%s'", argv[0]); | ||||
if (inet_aton(server, &ndconf.ndc_server) == 0) | if (inet_aton(server, &ndconf.kda_server) == 0) | ||||
errx(EX_USAGE, "invalid server address '%s'", server); | errx(EX_USAGE, "invalid server address '%s'", server); | ||||
if (inet_aton(client, &ndconf.ndc_client) == 0) | if (inet_aton(client, &ndconf.kda_client) == 0) | ||||
errx(EX_USAGE, "invalid client address '%s'", client); | errx(EX_USAGE, "invalid client address '%s'", client); | ||||
if (gateway == NULL) { | if (gateway == NULL) { | ||||
gateway = find_gateway(argv[0]); | gateway = find_gateway(argv[0]); | ||||
if (gateway == NULL) { | if (gateway == NULL) { | ||||
if (verbose) | if (verbose) | ||||
printf( | printf( | ||||
"failed to look up gateway for %s\n", | "failed to look up gateway for %s\n", | ||||
server); | server); | ||||
gateway = server; | gateway = server; | ||||
} | } | ||||
} | } | ||||
if (inet_aton(gateway, &ndconf.ndc_gateway) == 0) | if (inet_aton(gateway, &ndconf.kda_gateway) == 0) | ||||
errx(EX_USAGE, "invalid gateway address '%s'", gateway); | errx(EX_USAGE, "invalid gateway address '%s'", gateway); | ||||
} | |||||
#ifdef HAVE_CRYPTO | #ifdef HAVE_CRYPTO | ||||
if (pubkeyfile != NULL) | if (pubkeyfile != NULL) | ||||
genkey(pubkeyfile, kdap); | genkey(pubkeyfile, kdap); | ||||
#endif | #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); | error = ioctl(fd, DIOCSKERNELDUMP, kdap); | ||||
if (error != 0) | if (error != 0) | ||||
error = errno; | error = errno; | ||||
explicit_bzero(kdap->kda_encryptedkey, | explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize); | ||||
kdap->kda_encryptedkeysize); | |||||
free(kdap->kda_encryptedkey); | free(kdap->kda_encryptedkey); | ||||
explicit_bzero(kdap, sizeof(*kdap)); | 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)"); | errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)"); | ||||
} | } | ||||
if (verbose) | if (verbose) | ||||
printf("kernel dumps on %s\n", dumpdev); | listdumpdev(); | ||||
exit(EX_OK); | exit(EX_OK); | ||||
} | } |
Consider using strsep() here, it'll make the code more resilient to input errors and eliminate one of the two calls.