3 * $Id: unetcfg.c,v 1.3 2001/02/19 19:10:28 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.3 2001/02/19 19:10:28 mdw
33 * New option to allow changing interface flags.
35 * Revision 1.2 2001/02/03 18:39:59 mdw
36 * Setting the maximum interface count now does the right thing.
38 * Revision 1.1 2001/01/25 22:03:39 mdw
39 * Initial check-in (somewhat belated).
43 /*----- Include files -----------------------------------------------------*/
52 #include <sys/types.h>
54 #include <sys/ioctl.h>
59 #include <sys/socket.h>
60 #include <netinet/in.h>
63 #include <linux/if_ether.h>
67 /*----- Static variables --------------------------------------------------*/
69 #define VERSION "1.00"
70 #define ETH_P_HELP 0xffffu
72 static const char *quis = "unetcfg"; /* My program's name */
73 static int flags = 0; /* Various useful status flags */
74 static const char *current = "<stdin>"; /* Current Usernet device */
75 static int fd = 0; /* Default to @stdin@ */
82 /*----- Common routines ---------------------------------------------------*/
86 * Arguments: @const char *format@ = format string to print
87 * @...@ = values for the placeholders
91 * Use: Reports a fatal error message.
94 static void die(const char *format, ...)
100 vfprintf(stderr, format, ap);
106 /* --- @whiiter@ --- *
108 * Arguments: @const char *format@ = format string to print
109 * @...@ = values for the placeholders
113 * Use: Reports a non-fatal error message.
116 static void whitter(const char *format, ...)
120 if ((flags & f_verbose) == 0)
123 va_start(ap, format);
126 vfprintf(stderr, format, ap);
131 /* --- @lookup@ --- *
133 * Arguments: @void *p@ = pointer to a table
134 * @size_t sz@ = size of the table items
135 * @const char *s@ = pointer to string to match
137 * Returns: Pointer to the matching block.
139 * Use: Finds an item in a table.
142 static void *lookup(const void *p, size_t sz, const char *s)
144 const char **q = (const char **)p;
148 const char *x = *q, *y = s;
155 die("ambiguous name `%s'", s);
158 } else if (*x == *y) {
165 q = (const char **)((const char *)q + sz);
170 #define LOOKUP(tbl, key) lookup((tbl), sizeof((tbl)[0]), (key))
178 * Use: Ensures that the current device really is a Usernet device.
181 static void check(void)
185 if (ioctl(fd, UNIOCGINFO, 0) < 0)
186 die("file `%s' is not a Usernet device", current);
190 /*----- Command implementations -------------------------------------------*/
192 /* --- Forward declarations --- */
195 const char *cmd; /* Command name */
196 int minarg; /* Minimum number of args */
197 int (*func)(char **av); /* Command handler function */
198 const char *syn, *shelp, *vhelp; /* Short and verbose help */
201 static struct command cmdtab[];
204 const char *name; /* User-level protocol name */
205 unsigned short proto; /* Protocol number */
206 const char *desc; /* Readable description */
209 static struct eth_proto eth_prototab[];
213 #define YNQ_QUERY (-1)
219 { "query", YNQ_QUERY },
220 { "show", YNQ_QUERY },
229 static int run(char **av);
233 * Arguments: @const char *p@ = pointer to string to decode
235 * Returns: One of the @YNQ@ codes.
237 * Use: Decodes a `yes/no/query' response.
240 static int ynq(const char *p)
242 struct ynq *ynq = LOOKUP(ynqtab, p);
244 die("unknown setting `%s': I understand `on', `off' and `query'", p);
248 /* --- @cmd_help@ --- */
250 static int cmd_help(char **av)
255 puts("Commands provided:\n");
256 for (c = cmdtab; c->cmd; c++)
257 printf("%-30s%s\n", c->syn, c->shelp);
258 } else if ((c = LOOKUP(cmdtab, *av)) == 0)
259 die("unknown command: `%s'", *av);
261 fputs(c->syn, stdout);
262 fputs("\n\n", stdout);
263 fputs(c->vhelp, stdout);
269 /* --- @cmd_select@ --- */
271 static int cmd_select(char **av)
273 const char *fn = *av++;
274 if (flags & f_close) {
276 whitter("closed previous device `%s'", current);
278 fd = open(fn, O_RDWR);
280 die("couldn't open Usernet device `%s': %s", fn, strerror(errno));
284 whitter("opened new device: `%s'", fn);
289 /* --- @cmd_fd@ --- */
291 static int cmd_fd(char **av)
294 static char namebuf[20]; /* XXX Big enough? */
301 { "stdin", 0, "<stdin>" },
302 { "stdout", 1, "<stdout>" },
303 { "stderr", 2, "<stderr>" },
307 if (flags & f_close) {
309 whitter("closed previous device `%s'", current);
313 if ((fdp = LOOKUP(fdtab, p)) != 0)
315 else if (sscanf(p, "%i", &fd) < 1 || fd < 0)
316 die("bad file descriptor name: `%s'", p);
318 flags &= ~f_check | f_close;
320 current = fdtab[fd].desc;
322 current = namebuf, sprintf(namebuf, "<fd %i>", fd);
325 whitter("opened device on `%s'", current);
330 /* --- @cmd_show@ --- */
332 static int cmd_show(char **av)
334 struct unet_info uni;
335 const char *af, *proto;
337 /* --- Name table for address families --- */
339 static const char *aftab[] = {
341 "Unix file domain (!)",
343 "Amateur radio AX.25",
346 "Amateur radio NetROM",
347 "Multiprotocol bridge (!)",
348 "Reserved (for ATM)",
349 "Reserved (for X.25)",
353 /* --- Name table for Usernet flags --- */
359 { "trans", UNIF_TRANS },
360 /* @{ "open", UNIF_OPEN }@ -- ignore: this flag always appears on */
361 { "debug", UNIF_DEBUG },
365 /* --- Read the attachment information --- */
368 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
369 die("couldn't read information about attachment: %s", strerror(errno));
371 /* --- Look the address family up --- */
373 if (uni.uni_family < sizeof(aftab) / sizeof(aftab[0]))
374 af = aftab[uni.uni_family];
376 af = "Unknown family";
378 /* --- Look the protocol up --- */
381 struct eth_proto *ep;
384 for (ep = eth_prototab; ep->name; ep++) {
385 if (ep->proto == uni.uni_proto) {
392 /* --- Display appropriate information --- */
394 printf("Interface name: %s\n", uni.uni_ifname);
395 printf("MTU: %u\n", uni.uni_mtu);
396 printf("Address family: %s\n", af);
397 printf("Protocol: %s\n", proto);
400 for (flp = fltab; flp->name; flp++) {
401 if (uni.uni_flags & flp->f) {
403 fputs(flp->name, stdout);
413 /* --- @cmd_ifname@ --- */
415 static int cmd_ifname(char **av)
417 struct unet_info uni;
420 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
421 die("couldn't read attachment information: %s", strerror(errno));
422 puts(uni.uni_ifname);
426 /* --- @cmd_protocol@ --- */
428 static int cmd_protocol(char **av)
430 const char *pn = *av++;
431 const struct eth_proto *ep;
433 if ((ep = LOOKUP(eth_prototab, pn)) == 0)
434 die("unknown protocol name `%s'", pn);
436 if (ep->proto == ETH_P_HELP) {
437 for (ep = eth_prototab; ep->name; ep++) {
438 if (ep->proto != ETH_P_HELP)
439 printf("%s -- %s\n", ep->name, ep->desc);
443 if (ioctl(fd, UNIOCSPROTO, ep->proto) < 0)
444 die("couldn't set protocol `%s': %s", ep->name, strerror(errno));
445 whitter("set protocol `%s' for `%s'", ep->name, current);
451 /* --- @cmd_debug@ --- */
453 static int cmd_debug(char **av)
456 switch (ynq(*av++)) {
459 struct unet_info uni;
461 if (ioctl(fd, UNIOCGINFO, &uni))
462 die("error reading debug state: %s", strerror(errno));
463 printf("debugging for `%s' is %s\n", current,
464 uni.uni_flags & UNIF_DEBUG ? "enabled" : "disabled");
468 if (ioctl(fd, UNIOCSDEBUG, 1) < 0)
469 die("error setting debug state: %s", strerror(errno));
470 whitter("set debugging for `%s'", current);
474 if (ioctl(fd, UNIOCSDEBUG, 0) < 0)
475 die("error clearing debug state: %s", strerror(errno));
476 whitter("cleared debugging for `%s'", current);
483 /* --- @cmd_gdebug@ --- */
485 static int cmd_gdebug(char **av)
488 switch (ynq(*av++)) {
491 int i = ioctl(fd, UNIOCGGDEBUG);
493 die("error reading global debug state: %s", strerror(errno));
495 printf("global debugging is %s\n", i ? "enabled" : "disabled");
499 if (ioctl(fd, UNIOCSGDEBUG, 1) < 0)
500 die("error setting global debug state: %s", strerror(errno));
501 whitter("set global debugging");
505 if (ioctl(fd, UNIOCSGDEBUG, 0) < 0)
506 die("error clearing global debug state: %s", strerror(errno));
507 whitter("cleared global debugging");
514 /* --- @cmd_maxif@ --- */
516 static int cmd_maxif(char **av)
520 int i = ioctl(fd, UNIOCGMAXIF);
522 die("error reading maxif: %s", strerror(errno));
524 printf("interface maximum is %d\n", i);
527 long i = strtol(p, &q, 0);
529 die("malformed integer: %s", p);
530 if (ioctl(fd, UNIOCSMAXIF, i))
531 die("error setting maxif: %s", strerror(errno));
537 /* --- @cmd_ifflags@ --- */
539 static int cmd_ifflags(char **av)
541 static const struct iftab {
545 { "broadcast", IFF_BROADCAST },
546 { "loopback", IFF_LOOPBACK },
547 { "pointopoint", IFF_POINTOPOINT },
557 if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0)
558 die("error reading interface flags: %s", strerror(errno));
560 for (p = strtok(*av++, ","); p; p = strtok(0, ",")) {
567 ift = LOOKUP(iftab, p);
569 die("unknown interface flag `%s'", p);
575 if (ioctl(fd, UNIOCSIFFLAGS, f))
576 die("error setting interface flags: %s", strerror(errno));
582 * Arguments: @char **av@ = array of command line arguments
584 * Returns: Zero for success, nonzero for failure
586 * Use: Handles a sequence of commands.
589 static int run(char **av)
595 return (EXIT_SUCCESS);
596 if ((c = LOOKUP(cmdtab, *av)) == 0)
601 die("command `%s' not implemented", c->cmd);
602 for (i = 0; i < c->minarg; i++) {
604 die("Usage: %s", c->syn);
606 return (c->func(av));
609 /* --- The command definition block --- */
611 static struct command cmdtab[] = {
613 { "select", 1, cmd_select,
614 "[select] FILE", "select a Usernet device", "\
615 Selects FILE as the current device. Each command acts only on the current\n\
616 device. The word `select' may be omitted if desired.\n\
621 "fd NUMBER", "select Usernet device from a file descriptor", "\
622 Selects the file descriptor NUMBER as the current device. This allows\n\
623 configuration of already existing transient attachments which would\n\
624 otherwise be unnecessarily awkward.\n\
627 { "show", 0, cmd_show,
628 "show", "show status of device", "\
629 Displays status information about the current device.\n\
632 { "ifname", 0, cmd_ifname,
633 "ifname", "print attached network interface name", "\
634 Displays the name of the network interface attached to the current device.\n\
635 This is useful in configuration scripts, for example.\n\
638 { "protocol", 1, cmd_protocol,
639 "protocol PROT", "selects PROT as the device's protocol", "\
640 Sets PROT as the current device's protocol. All packets received by the\n\
641 device are stamped with this protocol tag. To see a list of currently\n\
642 known protocols, use the `help' protocol.\n\
645 { "debug", 1, cmd_debug,
646 "debug on|off|query", "set attachment debugging state", "\
647 Set the current debugging state for the attachment. When debugging is\n\
648 enabled, all packets flowing through the attachment are logged, along with\n\
649 a large number of other informative messages. Note: the logs generated\n\
650 tend to be very large, so don't flood the interface with data while\n\
651 debugging is enabled!\n\
654 { "gdebug", 1, cmd_gdebug,
655 "gdebug on|off|query", "set global debugging state", "\
656 Set the global debugging state for Usernet. This controls the emission\n\
657 emission of various non-attachment-specific message. Also, new\n\
658 attachments inherit their debug flags from the global flag.\n\
661 { "maxif", 0, cmd_maxif,
662 "maxif [MAX]", "set maximum number of interfaces allowed", "\
663 Configures the maximum number of interfaces allowed.\n\
666 { "ifflags", 1, cmd_ifflags,
667 "ifflags [-]FLAG,[-]FLAG,...", "set interface flags", "\
668 Configures the network interface flags, because `ifconfig' is prevented\n\
669 from doing the job properly.\n\
672 { "help", 0, cmd_help,
673 "help [COMMAND]", "display help about COMMAND", "\
674 If COMMAND is given, display help about it. If no COMAMND is specified,\n\
675 provide general help.\n\
681 /* --- Protocol description table --- */
683 static struct eth_proto eth_prototab[] = {
684 { "loop", ETH_P_LOOP, "Ethernet loopback" },
685 { "echo", ETH_P_ECHO, "Ethernet echo" },
686 { "pup", ETH_P_PUP, "Xerox PUP" },
687 { "ip", ETH_P_IP, "Internet IP" },
688 { "x25", ETH_P_X25, "CCITT X.25" },
689 { "arp", ETH_P_ARP, "Address resolution protocol" },
690 { "bpq", ETH_P_BPQ, "G8BPQ AX.25" },
691 { "dec", ETH_P_DEC, "DEC assigned" },
692 { "dna-dl", ETH_P_DNA_DL, "DEC DNA dump/load" },
693 { "dna-rcon", ETH_P_DNA_RC, "DEC DNA remote console" },
694 { "dna-route", ETH_P_DNA_RT, "DEC DNA routing" },
695 { "lat", ETH_P_LAT, "DEC LAT" },
696 { "diag", ETH_P_DIAG, "DEC diagnostics" },
697 { "cust", ETH_P_CUST, "DEC customer user" },
698 { "sca", ETH_P_SCA, "DEC Systems Comminications "
700 { "rarp", ETH_P_RARP, "Reverse address resolution" },
701 { "atalk", ETH_P_ATALK, "Appletalk DDP" },
702 { "aarp", ETH_P_AARP, "Appletalk AARP" },
703 { "ipx", ETH_P_IPX, "Novell IPX" },
704 { "ipv6", ETH_P_IPV6, "Internet IPv6" },
705 { "help", ETH_P_HELP, "<magical help token>" },
709 /*----- Informative messages ----------------------------------------------*/
713 * Arguments: @FILE *fp@ = stream to write on
717 * Use: Displays usage information for the program.
720 static void usage(FILE *fp)
722 fprintf(fp, "Usage: %s [-v] command ...\n", quis);
725 /* --- @version@ --- *
731 * Use: Displays the system's version number.
734 static void version(void)
736 printf("%s version %s\n", quis, VERSION);
745 * Use: Displays some help and quits.
748 static void help(void)
755 puts("Commands provided:\n");
756 for (c = cmdtab; c->cmd; c++)
757 printf("%-30s%s\n", c->syn, c->shelp);
763 * Arguments: @int argc@ = number of command line arguments received
764 * @char *argv[]@ = pointers to the command line arguments
766 * Returns: Zero for success, nonzero for failure
768 * Use: Dumps and manipulates Usernet attachments.
771 int main(int argc, char *argv[])
773 /* --- Set the program name properly --- */
776 if ((quis = strrchr(argv[0], '/')) == 0)
782 /* --- Now start parsing options --- */
787 static struct option opt[] = {
788 { "help", 0, 0, 'h' },
789 { "usage", 0, 0, 'U' },
790 { "version", 0, 0, 'V' },
791 { "verbose", 0, 0, 'v' },
795 if ((i = getopt_long(argc, argv, "+hVv", opt, 0)) < 0)
817 if (flags & f_duff) {
819 printf("(Type `%s --help' for more information.)\n", quis);
823 return (run(argv + optind));
826 /*----- That's all, folks -------------------------------------------------*/