Home | History | Annotate | Line # | Download | only in rndctl
rndctl.c revision 1.33
      1 /*	$NetBSD: rndctl.c,v 1.33 2020/04/30 03:27:15 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1997 Michael Graff.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the author nor the names of other contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __RCSID("$NetBSD: rndctl.c,v 1.33 2020/04/30 03:27:15 riastradh Exp $");
     35 #endif
     36 
     37 #include <sys/param.h>
     38 #include <sys/types.h>
     39 #include <sys/endian.h>
     40 #include <sys/ioctl.h>
     41 #include <sys/rndio.h>
     42 #include <sys/sha3.h>
     43 
     44 #include <err.h>
     45 #include <errno.h>
     46 #include <fcntl.h>
     47 #include <paths.h>
     48 #include <sha1.h>
     49 #include <stdio.h>
     50 #include <stdlib.h>
     51 #include <string.h>
     52 #include <unistd.h>
     53 
     54 typedef struct {
     55 	const char *a_name;
     56 	u_int32_t a_type;
     57 } arg_t;
     58 
     59 static const arg_t source_types[] = {
     60 	{ "???",     RND_TYPE_UNKNOWN },
     61 	{ "disk",    RND_TYPE_DISK },
     62 	{ "net",     RND_TYPE_NET },
     63 	{ "tape",    RND_TYPE_TAPE },
     64 	{ "tty",     RND_TYPE_TTY },
     65 	{ "rng",     RND_TYPE_RNG },
     66 	{ "skew",    RND_TYPE_SKEW },
     67 	{ "env",     RND_TYPE_ENV },
     68 	{ "vm",      RND_TYPE_VM },
     69 	{ "power",   RND_TYPE_POWER },
     70 	{ NULL,      0 }
     71 };
     72 
     73 __dead static void usage(void);
     74 static u_int32_t find_type(const char *name);
     75 static const char *find_name(u_int32_t);
     76 static void do_ioctl(rndctl_t *);
     77 static char * strflags(u_int32_t);
     78 static void do_list(int, u_int32_t, char *);
     79 static void do_stats(void);
     80 
     81 static int vflag;
     82 
     83 static void
     84 usage(void)
     85 {
     86 
     87 	fprintf(stderr, "usage: %s [-CEce] [-d devname | -t devtype]\n",
     88 	    getprogname());
     89 	fprintf(stderr, "       %s [-lsv] [-d devname | -t devtype]\n",
     90 	    getprogname());
     91 	fprintf(stderr, "	%s -[L|S] save-file\n", getprogname());
     92 	exit(1);
     93 }
     94 
     95 static u_int32_t
     96 find_type(const char *name)
     97 {
     98 	const arg_t *a;
     99 
    100 	a = source_types;
    101 
    102 	while (a->a_name != NULL) {
    103 		if (strcmp(a->a_name, name) == 0)
    104 			return (a->a_type);
    105 		a++;
    106 	}
    107 
    108 	errx(1, "device name %s unknown", name);
    109 	return (0);
    110 }
    111 
    112 static const char *
    113 find_name(u_int32_t type)
    114 {
    115 	const arg_t *a;
    116 
    117 	a = source_types;
    118 
    119 	while (a->a_name != NULL) {
    120 		if (type == a->a_type)
    121 			return (a->a_name);
    122 		a++;
    123 	}
    124 
    125 	warnx("device type %u unknown", type);
    126 	return ("???");
    127 }
    128 
    129 static void
    130 do_save(const char *filename, const void *extra, size_t nextra,
    131     uint32_t extraentropy)
    132 {
    133 	char tmp[PATH_MAX];
    134 	uint32_t systementropy;
    135 	uint8_t buf[32];
    136 	SHAKE128_CTX shake128;
    137 	rndsave_t rs;
    138 	SHA1_CTX s;
    139 	ssize_t nread, nwrit;
    140 	int fd;
    141 
    142 	/* Paranoia: Avoid stack memory disclosure.  */
    143 	memset(&rs, 0, sizeof rs);
    144 
    145 	/* Format the temporary file name.  */
    146 	if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
    147 		errx(1, "path too long");
    148 
    149 	/* Open /dev/urandom.  */
    150 	if ((fd = open(_PATH_URANDOM, O_RDONLY)) == -1)
    151 		err(1, "device open");
    152 
    153 	/* Find how much entropy is in the pool.  */
    154 	if (ioctl(fd, RNDGETENTCNT, &systementropy) == -1)
    155 		err(1, "ioctl(RNDGETENTCNT)");
    156 
    157 	/* Read some data from /dev/urandom.  */
    158 	if ((size_t)(nread = read(fd, buf, sizeof buf)) != sizeof buf) {
    159 		if (nread == -1)
    160 			err(1, "read");
    161 		else
    162 			errx(1, "truncated read");
    163 	}
    164 
    165 	/* Close /dev/urandom; we're done with it.  */
    166 	if (close(fd) == -1)
    167 		warn("close");
    168 	fd = -1;		/* paranoia */
    169 
    170 	/*
    171 	 * Hash what we read together with the extra input to generate
    172 	 * the seed data.
    173 	 */
    174 	SHAKE128_Init(&shake128);
    175 	SHAKE128_Update(&shake128, buf, sizeof buf);
    176 	SHAKE128_Update(&shake128, extra, nextra);
    177 	SHAKE128_Final(rs.data, sizeof(rs.data), &shake128);
    178 	explicit_memset(&shake128, 0, sizeof shake128); /* paranoia */
    179 
    180 	/*
    181 	 * Report an upper bound on the min-entropy of the seed data.
    182 	 * We take the larger of the system entropy and the extra
    183 	 * entropy -- the system state and the extra input may or may
    184 	 * not be independent, so we can't add them -- and clamp to the
    185 	 * size of the data.
    186 	 */
    187 	systementropy = MIN(systementropy,
    188 	    MIN(sizeof(buf), UINT32_MAX/NBBY)*NBBY);
    189 	extraentropy = MIN(extraentropy, MIN(nextra, UINT32_MAX/NBBY)*NBBY);
    190 	rs.entropy = MIN(MAX(systementropy, extraentropy),
    191 	    MIN(sizeof(rs.data), UINT32_MAX/NBBY)*NBBY);
    192 
    193 	/*
    194 	 * Compute the checksum on the 32-bit entropy count, followed
    195 	 * by the seed data.
    196 	 */
    197 	SHA1Init(&s);
    198 	SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
    199 	SHA1Update(&s, rs.data, sizeof(rs.data));
    200 	SHA1Final(rs.digest, &s);
    201 	explicit_memset(&s, 0, sizeof s); /* paranoia */
    202 
    203 	/*
    204 	 * Write it to a temporary file and sync it before we commit.
    205 	 * This way either the old seed or the new seed is completely
    206 	 * written in the expected location on disk even if the system
    207 	 * crashes as long as the file system doesn't get corrupted too
    208 	 * badly.
    209 	 *
    210 	 * If interrupted after this point and the temporary file is
    211 	 * disclosed, no big deal -- either the pool was predictable to
    212 	 * begin with in which case we're hosed either way, or we've
    213 	 * just revealed some output which is not a problem.
    214 	 */
    215 	if ((fd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1)
    216 		err(1, "open seed file to save");
    217 	if ((size_t)(nwrit = write(fd, &rs, sizeof rs)) != sizeof rs) {
    218 		int error = errno;
    219 		if (unlink(tmp) == -1)
    220 			warn("unlink");
    221 		if (nwrit == -1)
    222 			errc(1, error, "write");
    223 		else
    224 			errx(1, "truncated write");
    225 	}
    226 	explicit_memset(&rs, 0, sizeof rs); /* paranoia */
    227 	if (fsync_range(fd, FDATASYNC|FDISKSYNC, 0, 0) == -1) {
    228 		int error = errno;
    229 		if (unlink(tmp) == -1)
    230 			warn("unlink");
    231 		errc(1, error, "fsync_range");
    232 	}
    233 	if (close(fd) == -1)
    234 		warn("close");
    235 
    236 	/* Rename it over the original file to commit.  */
    237 	if (rename(tmp, filename) == -1)
    238 		err(1, "rename");
    239 }
    240 
    241 static void
    242 do_load(const char *filename)
    243 {
    244 	char tmp[PATH_MAX];
    245 	int fd_seed, fd_random;
    246 	rndsave_t rs;
    247 	rnddata_t rd;
    248 	ssize_t nread, nwrit;
    249 	SHA1_CTX s;
    250 	uint8_t digest[SHA1_DIGEST_LENGTH];
    251 
    252 	/*
    253 	 * The order of operations is important here:
    254 	 *
    255 	 * 1. Load the old seed.
    256 	 * 2. Feed the old seed into the kernel.
    257 	 * 3. Generate and write a new seed.
    258 	 * 4. Erase the old seed.
    259 	 *
    260 	 * This follows the procedure in
    261 	 *
    262 	 *	Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno,
    263 	 *	_Cryptography Engineering_, Wiley, 2010, Sec. 9.6.2
    264 	 *	`Update Seed File'.
    265 	 *
    266 	 * There is a race condition: If another process generates a
    267 	 * key from /dev/urandom after step (2) but before step (3),
    268 	 * and if the machine crashes before step (3), an adversary who
    269 	 * can read the disk after the crash can probably guess the
    270 	 * complete state of the entropy pool and thereby predict the
    271 	 * key.
    272 	 *
    273 	 * There's not much we can do here without some kind of
    274 	 * systemwide lock on /dev/urandom and without introducing an
    275 	 * opportunity for a crash to wipe out the entropy altogether.
    276 	 * To avoid this race, you should ensure that any key
    277 	 * generation happens _after_ `rndctl -L' has completed.
    278 	 */
    279 
    280 	/* Format the temporary file name.  */
    281 	if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
    282 		errx(1, "path too long");
    283 
    284 	/* 1. Load the old seed.  */
    285 	if ((fd_seed = open(filename, O_RDWR)) == -1)
    286 		err(1, "open seed file to load");
    287 	if ((size_t)(nread = read(fd_seed, &rs, sizeof rs)) != sizeof rs) {
    288 		if (nread == -1)
    289 			err(1, "read seed");
    290 		else
    291 			errx(1, "seed too short");
    292 	}
    293 
    294 	/* Verify its checksum.  */
    295 	SHA1Init(&s);
    296 	SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
    297 	SHA1Update(&s, rs.data, sizeof(rs.data));
    298 	SHA1Final(digest, &s);
    299 	if (!consttime_memequal(digest, rs.digest, sizeof(digest))) {
    300 		/*
    301 		 * If the checksum doesn't match, doesn't hurt to feed
    302 		 * the seed in anyway, but act as though it has zero
    303 		 * entropy in case it was corrupted with predictable
    304 		 * garbage.
    305 		 */
    306 		warnx("bad checksum");
    307 		rs.entropy = 0;
    308 	}
    309 
    310 	/*
    311 	 * If the entropy is insensibly large, try byte-swapping.
    312 	 * Otherwise assume the file is corrupted and act as though it
    313 	 * has zero entropy.
    314 	 */
    315 	if (howmany(rs.entropy, NBBY) > sizeof(rs.data)) {
    316 		rs.entropy = bswap32(rs.entropy);
    317 		if (howmany(rs.entropy, NBBY) > sizeof(rs.data))
    318 			rs.entropy = 0;
    319 	}
    320 
    321 	/* Format the ioctl request.  */
    322 	rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
    323 	rd.entropy = rs.entropy;
    324 	memcpy(rd.data, rs.data, rd.len);
    325 	explicit_memset(&rs, 0, sizeof rs); /* paranoia */
    326 
    327 	/* 2. Feed the old seed into the kernel.  */
    328 	if ((fd_random = open(_PATH_URANDOM, O_WRONLY)) == -1)
    329 		err(1, "open /dev/urandom");
    330 	if (ioctl(fd_random, RNDADDDATA, &rd) == -1)
    331 		err(1, "RNDADDDATA");
    332 	if (close(fd_random) == -1)
    333 		warn("close /dev/urandom");
    334 	fd_random = -1;		/* paranoia */
    335 
    336 	/*
    337 	 * 3. Generate and write a new seed.  Note that we hash the old
    338 	 * seed together with whatever /dev/urandom returns in do_save.
    339 	 * Why?  After RNDADDDATA, the input may not be distributed
    340 	 * immediately to /dev/urandom.
    341 	 */
    342 	do_save(filename, rd.data, rd.len, rd.entropy);
    343 	explicit_memset(&rd, 0, sizeof rd); /* paranoia */
    344 
    345 	/*
    346 	 * 4. Erase the old seed.  Only effective if we're on a
    347 	 * fixed-address file system like ffs -- doesn't help to erase
    348 	 * the data on lfs, but doesn't hurt either.  No need to unlink
    349 	 * because do_save will have already overwritten it.
    350 	 */
    351 	memset(&rs, 0, sizeof rs);
    352 	if ((size_t)(nwrit = pwrite(fd_seed, &rs, sizeof rs, 0)) !=
    353 	    sizeof rs) {
    354 		if (nwrit == -1)
    355 			err(1, "overwrite old seed");
    356 		else
    357 			errx(1, "truncated overwrite");
    358 	}
    359 	if (fsync_range(fd_seed, FDATASYNC|FDISKSYNC, 0, 0) == -1)
    360 		err(1, "fsync_range");
    361 }
    362 
    363 static void
    364 do_ioctl(rndctl_t *rctl)
    365 {
    366 	int fd;
    367 	int res;
    368 
    369 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    370 	if (fd < 0)
    371 		err(1, "open");
    372 
    373 	res = ioctl(fd, RNDCTL, rctl);
    374 	if (res < 0)
    375 		err(1, "ioctl(RNDCTL)");
    376 
    377 	close(fd);
    378 }
    379 
    380 static char *
    381 strflags(u_int32_t fl)
    382 {
    383 	static char str[512];
    384 
    385 	str[0] = '\0';
    386 	if (fl & RND_FLAG_NO_ESTIMATE)
    387 		;
    388 	else
    389 		strlcat(str, "estimate, ", sizeof(str));
    390 
    391 	if (fl & RND_FLAG_NO_COLLECT)
    392 		;
    393 	else
    394 		strlcat(str, "collect, ", sizeof(str));
    395 
    396 	if (fl & RND_FLAG_COLLECT_VALUE)
    397 		strlcat(str, "v, ", sizeof(str));
    398 	if (fl & RND_FLAG_COLLECT_TIME)
    399 		strlcat(str, "t, ", sizeof(str));
    400 	if (fl & RND_FLAG_ESTIMATE_VALUE)
    401 		strlcat(str, "dv, ", sizeof(str));
    402 	if (fl & RND_FLAG_ESTIMATE_TIME)
    403 		strlcat(str, "dt, ", sizeof(str));
    404 
    405 	if (str[strlen(str) - 2] == ',')
    406 		str[strlen(str) - 2] = '\0';
    407 
    408 	return (str);
    409 }
    410 
    411 #define HEADER "Source                 Bits Type      Flags\n"
    412 
    413 static void
    414 do_list(int all, u_int32_t type, char *name)
    415 {
    416 	rndstat_est_t rstat;
    417 	rndstat_est_name_t rstat_name;
    418 	int fd;
    419 	int res;
    420 	uint32_t i;
    421 	u_int32_t start;
    422 
    423 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    424 	if (fd < 0)
    425 		err(1, "open");
    426 
    427 	if (all == 0 && type == 0xff) {
    428 		strncpy(rstat_name.name, name, sizeof(rstat_name.name));
    429 		res = ioctl(fd, RNDGETESTNAME, &rstat_name);
    430 		if (res < 0)
    431 			err(1, "ioctl(RNDGETESTNAME)");
    432 		printf(HEADER);
    433 		printf("%-16s %10u %-4s %s\n",
    434 		    rstat_name.source.rt.name,
    435 		    rstat_name.source.rt.total,
    436 		    find_name(rstat_name.source.rt.type),
    437 		    strflags(rstat_name.source.rt.flags));
    438 		if (vflag) {
    439 			printf("\tDt samples = %d\n",
    440 			       rstat_name.source.dt_samples);
    441 			printf("\tDt bits = %d\n",
    442 			       rstat_name.source.dt_total);
    443 			printf("\tDv samples = %d\n",
    444 				rstat_name.source.dv_samples);
    445 			printf("\tDv bits = %d\n",
    446 			       rstat_name.source.dv_total);
    447 		}
    448 		close(fd);
    449 		return;
    450 	}
    451 
    452 	/*
    453 	 * Run through all the devices present in the system, and either
    454 	 * print out ones that match, or print out all of them.
    455 	 */
    456 	printf(HEADER);
    457 	start = 0;
    458 	for (;;) {
    459 		rstat.count = RND_MAXSTATCOUNT;
    460 		rstat.start = start;
    461 		res = ioctl(fd, RNDGETESTNUM, &rstat);
    462 		if (res < 0)
    463 			err(1, "ioctl(RNDGETESTNUM)");
    464 
    465 		if (rstat.count == 0)
    466 			break;
    467 
    468 		for (i = 0; i < rstat.count; i++) {
    469 			if (all != 0 ||
    470 			    type == rstat.source[i].rt.type)
    471 				printf("%-16s %10u %-4s %s\n",
    472 				    rstat.source[i].rt.name,
    473 				    rstat.source[i].rt.total,
    474 				    find_name(rstat.source[i].rt.type),
    475 				    strflags(rstat.source[i].rt.flags));
    476 			if (vflag) {
    477 				printf("\tDt samples = %d\n",
    478 				       rstat.source[i].dt_samples);
    479 				printf("\tDt bits = %d\n",
    480 				       rstat.source[i].dt_total);
    481 				printf("\tDv samples = %d\n",
    482 				       rstat.source[i].dv_samples);
    483 				printf("\tDv bits = %d\n",
    484 				       rstat.source[i].dv_total);
    485 			}
    486                 }
    487 		start += rstat.count;
    488 	}
    489 
    490 	close(fd);
    491 }
    492 
    493 static void
    494 do_stats(void)
    495 {
    496 	rndpoolstat_t rs;
    497 	int fd;
    498 
    499 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    500 	if (fd < 0)
    501 		err(1, "open");
    502 
    503 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
    504 		err(1, "ioctl(RNDGETPOOLSTAT)");
    505 
    506 	printf("\t%9u bits mixed into pool\n", rs.added);
    507 	printf("\t%9u bits currently stored in pool (max %u)\n",
    508 	    rs.curentropy, rs.maxentropy);
    509 	printf("\t%9u bits of entropy discarded due to full pool\n",
    510 	    rs.discarded);
    511 	printf("\t%9u hard-random bits generated\n", rs.removed);
    512 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
    513 
    514 	close(fd);
    515 }
    516 
    517 int
    518 main(int argc, char **argv)
    519 {
    520 	rndctl_t rctl;
    521 	int ch, cmd, lflag, mflag, sflag;
    522 	u_int32_t type;
    523 	char name[16];
    524 	const char *filename = NULL;
    525 
    526 	if (SHA3_Selftest() != 0)
    527 		errx(1, "SHA-3 self-test failed");
    528 
    529 	rctl.mask = 0;
    530 	rctl.flags = 0;
    531 
    532 	cmd = 0;
    533 	lflag = 0;
    534 	mflag = 0;
    535 	sflag = 0;
    536 	type = 0xff;
    537 
    538 	while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) {
    539 		switch (ch) {
    540 		case 'C':
    541 			rctl.flags |= RND_FLAG_NO_COLLECT;
    542 			rctl.mask |= RND_FLAG_NO_COLLECT;
    543 			mflag++;
    544 			break;
    545 		case 'E':
    546 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
    547 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    548 			mflag++;
    549 			break;
    550 		case 'L':
    551 			if (cmd != 0)
    552 				usage();
    553 			cmd = 'L';
    554 			filename = optarg;
    555 			break;
    556 		case 'S':
    557 			if (cmd != 0)
    558 				usage();
    559 			cmd = 'S';
    560 			filename = optarg;
    561 			break;
    562 		case 'c':
    563 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
    564 			rctl.mask |= RND_FLAG_NO_COLLECT;
    565 			mflag++;
    566 			break;
    567 		case 'e':
    568 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
    569 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    570 			mflag++;
    571 			break;
    572 		case 'l':
    573 			lflag++;
    574 			break;
    575 		case 't':
    576 			if (cmd != 0)
    577 				usage();
    578 			cmd = 't';
    579 
    580 			type = find_type(optarg);
    581 			break;
    582 		case 'd':
    583 			if (cmd != 0)
    584 				usage();
    585 			cmd = 'd';
    586 
    587 			type = 0xff;
    588 			strlcpy(name, optarg, sizeof(name));
    589 			break;
    590 		case 's':
    591 			sflag++;
    592 			break;
    593 		case 'v':
    594 			vflag++;
    595 			break;
    596 		case '?':
    597 		default:
    598 			usage();
    599 		}
    600 	}
    601 	argc -= optind;
    602 	argv += optind;
    603 
    604 	/*
    605 	 * No leftover non-option arguments.
    606 	 */
    607 	if (argc > 0)
    608 		usage();
    609 
    610 	/*
    611 	 * Save.
    612 	 */
    613 	if (cmd == 'S') {
    614 		do_save(filename, NULL, 0, 0);
    615 		exit(0);
    616 	}
    617 
    618 	/*
    619 	 * Load.
    620 	 */
    621 	if (cmd == 'L') {
    622 		do_load(filename);
    623 		exit(0);
    624 	}
    625 
    626 	/*
    627 	 * Cannot list and modify at the same time.
    628 	 */
    629 	if ((lflag != 0 || sflag != 0) && mflag != 0)
    630 		usage();
    631 
    632 	/*
    633 	 * Bomb out on no-ops.
    634 	 */
    635 	if (lflag == 0 && mflag == 0 && sflag == 0)
    636 		usage();
    637 
    638 	/*
    639 	 * If not listing, we need a device name or a type.
    640 	 */
    641 	if (lflag == 0 && cmd == 0 && sflag == 0)
    642 		usage();
    643 
    644 	/*
    645 	 * Modify request.
    646 	 */
    647 	if (mflag != 0) {
    648 		rctl.type = type;
    649 		strncpy(rctl.name, name, sizeof(rctl.name));
    650 		do_ioctl(&rctl);
    651 
    652 		exit(0);
    653 	}
    654 
    655 	/*
    656 	 * List sources.
    657 	 */
    658 	if (lflag != 0)
    659 		do_list(cmd == 0, type, name);
    660 
    661 	if (sflag != 0)
    662 		do_stats();
    663 
    664 	exit(0);
    665 }
    666