3 * $Id: unetcfg.c,v 1.2 2001/02/03 18:39:59 mdw Exp $
5 * User-space network device support.
7 * (c) 1998 Mark Wooding
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Usernet.
14 * Usernet is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * Usernet is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with Usernet; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.2 2001/02/03 18:39:59 mdw
33 * Setting the maximum interface count now does the right thing.
35 * Revision 1.1 2001/01/25 22:03:39 mdw
36 * Initial check-in (somewhat belated).
40 /*----- Include files -----------------------------------------------------*/
49 #include <linux/if_ether.h>
51 #include <sys/types.h>
53 #include <sys/ioctl.h>
60 /*----- Static variables --------------------------------------------------*/
62 #define VERSION "1.00"
63 #define ETH_P_HELP 0xffffu
65 static const char *quis = "unetcfg"; /* My program's name */
66 static int flags = 0; /* Various useful status flags */
67 static const char *current = "<stdin>"; /* Current Usernet device */
68 static int fd = 0; /* Default to @stdin@ */
75 /*----- Common routines ---------------------------------------------------*/
79 * Arguments: @const char *format@ = format string to print
80 * @...@ = values for the placeholders
84 * Use: Reports a fatal error message.
87 static void die(const char *format, ...)
93 vfprintf(stderr, format, ap);
99 /* --- @whiiter@ --- *
101 * Arguments: @const char *format@ = format string to print
102 * @...@ = values for the placeholders
106 * Use: Reports a non-fatal error message.
109 static void whitter(const char *format, ...)
113 if ((flags & f_verbose) == 0)
116 va_start(ap, format);
119 vfprintf(stderr, format, ap);
124 /* --- @lookup@ --- *
126 * Arguments: @void *p@ = pointer to a table
127 * @size_t sz@ = size of the table items
128 * @const char *s@ = pointer to string to match
130 * Returns: Pointer to the matching block.
132 * Use: Finds an item in a table.
135 static void *lookup(const void *p, size_t sz, const char *s)
137 const char **q = (const char **)p;
141 const char *x = *q, *y = s;
148 die("ambiguous name `%s'", s);
151 } else if (*x == *y) {
158 q = (const char **)((const char *)q + sz);
163 #define LOOKUP(tbl, key) lookup((tbl), sizeof((tbl)[0]), (key))
171 * Use: Ensures that the current device really is a Usernet device.
174 static void check(void)
178 if (ioctl(fd, UNIOCGINFO, 0) < 0)
179 die("file `%s' is not a Usernet device", current);
183 /*----- Command implementations -------------------------------------------*/
185 /* --- Forward declarations --- */
188 const char *cmd; /* Command name */
189 int minarg; /* Minimum number of args */
190 int (*func)(char **av); /* Command handler function */
191 const char *syn, *shelp, *vhelp; /* Short and verbose help */
194 static struct command cmdtab[];
197 const char *name; /* User-level protocol name */
198 unsigned short proto; /* Protocol number */
199 const char *desc; /* Readable description */
202 static struct eth_proto eth_prototab[];
206 #define YNQ_QUERY (-1)
212 { "query", YNQ_QUERY },
213 { "show", YNQ_QUERY },
222 static int run(char **av);
226 * Arguments: @const char *p@ = pointer to string to decode
228 * Returns: One of the @YNQ@ codes.
230 * Use: Decodes a `yes/no/query' response.
233 static int ynq(const char *p)
235 struct ynq *ynq = LOOKUP(ynqtab, p);
237 die("unknown setting `%s': I understand `on', `off' and `query'", p);
241 /* --- @cmd_help@ --- */
243 static int cmd_help(char **av)
248 puts("Commands provided:\n");
249 for (c = cmdtab; c->cmd; c++)
250 printf("%-30s%s\n", c->syn, c->shelp);
251 } else if ((c = LOOKUP(cmdtab, *av)) == 0)
252 die("unknown command: `%s'", *av);
254 fputs(c->syn, stdout);
255 fputs("\n\n", stdout);
256 fputs(c->vhelp, stdout);
262 /* --- @cmd_select@ --- */
264 static int cmd_select(char **av)
266 const char *fn = *av++;
267 if (flags & f_close) {
269 whitter("closed previous device `%s'", current);
271 fd = open(fn, O_RDWR);
273 die("couldn't open Usernet device `%s': %s", fn, strerror(errno));
277 whitter("opened new device: `%s'", fn);
282 /* --- @cmd_fd@ --- */
284 static int cmd_fd(char **av)
287 static char namebuf[20]; /* XXX Big enough? */
294 { "stdin", 0, "<stdin>" },
295 { "stdout", 1, "<stdout>" },
296 { "stderr", 2, "<stderr>" },
300 if (flags & f_close) {
302 whitter("closed previous device `%s'", current);
306 if ((fdp = LOOKUP(fdtab, p)) != 0)
308 else if (sscanf(p, "%i", &fd) < 1 || fd < 0)
309 die("bad file descriptor name: `%s'", p);
311 flags &= ~f_check | f_close;
313 current = fdtab[fd].desc;
315 current = namebuf, sprintf(namebuf, "<fd %i>", fd);
318 whitter("opened device on `%s'", current);
323 /* --- @cmd_show@ --- */
325 static int cmd_show(char **av)
327 struct unet_info uni;
328 const char *af, *proto;
330 /* --- Name table for address families --- */
332 static const char *aftab[] = {
334 "Unix file domain (!)",
336 "Amateur radio AX.25",
339 "Amateur radio NetROM",
340 "Multiprotocol bridge (!)",
341 "Reserved (for ATM)",
342 "Reserved (for X.25)",
346 /* --- Name table for Usernet flags --- */
352 { "trans", UNIF_TRANS },
353 /* @{ "open", UNIF_OPEN }@ -- ignore: this flag always appears on */
354 { "debug", UNIF_DEBUG },
358 /* --- Read the attachment information --- */
361 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
362 die("couldn't read information about attachment: %s", strerror(errno));
364 /* --- Look the address family up --- */
366 if (uni.uni_family < sizeof(aftab) / sizeof(aftab[0]))
367 af = aftab[uni.uni_family];
369 af = "Unknown family";
371 /* --- Look the protocol up --- */
374 struct eth_proto *ep;
377 for (ep = eth_prototab; ep->name; ep++) {
378 if (ep->proto == uni.uni_proto) {
385 /* --- Display appropriate information --- */
387 printf("Interface name: %s\n", uni.uni_ifname);
388 printf("MTU: %u\n", uni.uni_mtu);
389 printf("Address family: %s\n", af);
390 printf("Protocol: %s\n", proto);
393 for (flp = fltab; flp->name; flp++) {
394 if (uni.uni_flags & flp->f) {
396 fputs(flp->name, stdout);
406 /* --- @cmd_ifname@ --- */
408 static int cmd_ifname(char **av)
410 struct unet_info uni;
413 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
414 die("couldn't read attachment information: %s", strerror(errno));
415 puts(uni.uni_ifname);
419 /* --- @cmd_protocol@ --- */
421 static int cmd_protocol(char **av)
423 const char *pn = *av++;
424 const struct eth_proto *ep;
426 if ((ep = LOOKUP(eth_prototab, pn)) == 0)
427 die("unknown protocol name `%s'", pn);
429 if (ep->proto == ETH_P_HELP) {
430 for (ep = eth_prototab; ep->name; ep++) {
431 if (ep->proto != ETH_P_HELP)
432 printf("%s -- %s\n", ep->name, ep->desc);
436 if (ioctl(fd, UNIOCSPROTO, ep->proto) < 0)
437 die("couldn't set protocol `%s': %s", ep->name, strerror(errno));
438 whitter("set protocol `%s' for `%s'", ep->name, current);
444 /* --- @cmd_debug@ --- */
446 static int cmd_debug(char **av)
449 switch (ynq(*av++)) {
452 struct unet_info uni;
454 if (ioctl(fd, UNIOCGINFO, &uni))
455 die("error reading debug state: %s", strerror(errno));
456 printf("debugging for `%s' is %s\n", current,
457 uni.uni_flags & UNIF_DEBUG ? "enabled" : "disabled");
461 if (ioctl(fd, UNIOCSDEBUG, 1) < 0)
462 die("error setting debug state: %s", strerror(errno));
463 whitter("set debugging for `%s'", current);
467 if (ioctl(fd, UNIOCSDEBUG, 0) < 0)
468 die("error clearing debug state: %s", strerror(errno));
469 whitter("cleared debugging for `%s'", current);
476 /* --- @cmd_gdebug@ --- */
478 static int cmd_gdebug(char **av)
481 switch (ynq(*av++)) {
484 int i = ioctl(fd, UNIOCGGDEBUG);
486 die("error reading global debug state: %s", strerror(errno));
488 printf("global debugging is %s\n", i ? "enabled" : "disabled");
492 if (ioctl(fd, UNIOCSGDEBUG, 1) < 0)
493 die("error setting global debug state: %s", strerror(errno));
494 whitter("set global debugging");
498 if (ioctl(fd, UNIOCSGDEBUG, 0) < 0)
499 die("error clearing global debug state: %s", strerror(errno));
500 whitter("cleared global debugging");
507 /* --- @cmd_maxif@ --- */
509 static int cmd_maxif(char **av)
513 int i = ioctl(fd, UNIOCGMAXIF);
515 die("error reading maxif: %s", strerror(errno));
517 printf("interface maximum is %d\n", i);
520 long i = strtol(p, &q, 0);
522 die("malformed integer: %s", p);
523 if (ioctl(fd, UNIOCSMAXIF, i))
524 die("error setting maxif: %s", strerror(errno));
532 * Arguments: @char **av@ = array of command line arguments
534 * Returns: Zero for success, nonzero for failure
536 * Use: Handles a sequence of commands.
539 static int run(char **av)
545 return (EXIT_SUCCESS);
546 if ((c = LOOKUP(cmdtab, *av)) == 0)
551 die("command `%s' not implemented", c->cmd);
552 for (i = 0; i < c->minarg; i++) {
554 die("Usage: %s", c->syn);
556 return (c->func(av));
559 /* --- The command definition block --- */
561 static struct command cmdtab[] = {
563 { "select", 1, cmd_select,
564 "[select] FILE", "select a Usernet device", "\
565 Selects FILE as the current device. Each command acts only on the current\n\
566 device. The word `select' may be omitted if desired.\n\
571 "fd NUMBER", "select Usernet device from a file descriptor", "\
572 Selects the file descriptor NUMBER as the current device. This allows\n\
573 configuration of already existing transient attachments which would\n\
574 otherwise be unnecessarily awkward.\n\
577 { "show", 0, cmd_show,
578 "show", "show status of device", "\
579 Displays status information about the current device.\n\
582 { "ifname", 0, cmd_ifname,
583 "ifname", "print attached network interface name", "\
584 Displays the name of the network interface attached to the current device.\n\
585 This is useful in configuration scripts, for example.\n\
588 { "protocol", 1, cmd_protocol,
589 "protocol PROT", "selects PROT as the device's protocol", "\
590 Sets PROT as the current device's protocol. All packets received by the\n\
591 device are stamped with this protocol tag. To see a list of currently\n\
592 known protocols, use the `help' protocol.\n\
595 { "debug", 1, cmd_debug,
596 "debug on|off|query", "set attachment debugging state", "\
597 Set the current debugging state for the attachment. When debugging is\n\
598 enabled, all packets flowing through the attachment are logged, along with\n\
599 a large number of other informative messages. Note: the logs generated\n\
600 tend to be very large, so don't flood the interface with data while\n\
601 debugging is enabled!\n\
604 { "gdebug", 1, cmd_gdebug,
605 "gdebug on|off|query", "set global debugging state", "\
606 Set the global debugging state for Usernet. This controls the emission\n\
607 emission of various non-attachment-specific message. Also, new\n\
608 attachments inherit their debug flags from the global flag.\n\
611 { "maxif", 0, cmd_maxif,
612 "maxif [MAX]", "set maximum number of interfaces allowed", "\
613 Configures the maximum number of interfaces allowed.\n\
616 { "help", 0, cmd_help,
617 "help [COMMAND]", "display help about COMMAND", "\
618 If COMMAND is given, display help about it. If no COMAMND is specified,\n\
619 provide general help.\n\
625 /* --- Protocol description table --- */
627 static struct eth_proto eth_prototab[] = {
628 { "loop", ETH_P_LOOP, "Ethernet loopback" },
629 { "echo", ETH_P_ECHO, "Ethernet echo" },
630 { "pup", ETH_P_PUP, "Xerox PUP" },
631 { "ip", ETH_P_IP, "Internet IP" },
632 { "x25", ETH_P_X25, "CCITT X.25" },
633 { "arp", ETH_P_ARP, "Address resolution protocol" },
634 { "bpq", ETH_P_BPQ, "G8BPQ AX.25" },
635 { "dec", ETH_P_DEC, "DEC assigned" },
636 { "dna-dl", ETH_P_DNA_DL, "DEC DNA dump/load" },
637 { "dna-rcon", ETH_P_DNA_RC, "DEC DNA remote console" },
638 { "dna-route", ETH_P_DNA_RT, "DEC DNA routing" },
639 { "lat", ETH_P_LAT, "DEC LAT" },
640 { "diag", ETH_P_DIAG, "DEC diagnostics" },
641 { "cust", ETH_P_CUST, "DEC customer user" },
642 { "sca", ETH_P_SCA, "DEC Systems Comminications "
644 { "rarp", ETH_P_RARP, "Reverse address resolution" },
645 { "atalk", ETH_P_ATALK, "Appletalk DDP" },
646 { "aarp", ETH_P_AARP, "Appletalk AARP" },
647 { "ipx", ETH_P_IPX, "Novell IPX" },
648 { "ipv6", ETH_P_IPV6, "Internet IPv6" },
649 { "help", ETH_P_HELP, "<magical help token>" },
653 /*----- Informative messages ----------------------------------------------*/
657 * Arguments: @FILE *fp@ = stream to write on
661 * Use: Displays usage information for the program.
664 static void usage(FILE *fp)
666 fprintf(fp, "Usage: %s [-v] command ...\n", quis);
669 /* --- @version@ --- *
675 * Use: Displays the system's version number.
678 static void version(void)
680 printf("%s version %s\n", quis, VERSION);
689 * Use: Displays some help and quits.
692 static void help(void)
699 puts("Commands provided:\n");
700 for (c = cmdtab; c->cmd; c++)
701 printf("%-30s%s\n", c->syn, c->shelp);
707 * Arguments: @int argc@ = number of command line arguments received
708 * @char *argv[]@ = pointers to the command line arguments
710 * Returns: Zero for success, nonzero for failure
712 * Use: Dumps and manipulates Usernet attachments.
715 int main(int argc, char *argv[])
717 /* --- Set the program name properly --- */
720 if ((quis = strrchr(argv[0], '/')) == 0)
726 /* --- Now start parsing options --- */
731 static struct option opt[] = {
732 { "help", 0, 0, 'h' },
733 { "usage", 0, 0, 'U' },
734 { "version", 0, 0, 'V' },
735 { "verbose", 0, 0, 'v' },
739 if ((i = getopt_long(argc, argv, "hVv", opt, 0)) < 0)
761 if (flags & f_duff) {
763 printf("(Type `%s --help' for more information.)\n", quis);
767 return (run(argv + optind));
770 /*----- That's all, folks -------------------------------------------------*/