3 * $Id: unetcfg.c,v 1.1 2001/01/25 22:03:39 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.1 2001/01/25 22:03:39 mdw
33 * Initial check-in (somewhat belated).
37 /*----- Include files -----------------------------------------------------*/
46 #include <linux/if_ether.h>
48 #include <sys/types.h>
50 #include <sys/ioctl.h>
57 /*----- Static variables --------------------------------------------------*/
59 #define VERSION "1.00"
60 #define ETH_P_HELP 0xffffu
62 static const char *quis = "unetcfg"; /* My program's name */
63 static int flags = 0; /* Various useful status flags */
64 static const char *current = "<stdin>"; /* Current Usernet device */
65 static int fd = 0; /* Default to @stdin@ */
72 /*----- Common routines ---------------------------------------------------*/
76 * Arguments: @const char *format@ = format string to print
77 * @...@ = values for the placeholders
81 * Use: Reports a fatal error message.
84 static void die(const char *format, ...)
90 vfprintf(stderr, format, ap);
96 /* --- @whiiter@ --- *
98 * Arguments: @const char *format@ = format string to print
99 * @...@ = values for the placeholders
103 * Use: Reports a non-fatal error message.
106 static void whitter(const char *format, ...)
110 if ((flags & f_verbose) == 0)
113 va_start(ap, format);
116 vfprintf(stderr, format, ap);
121 /* --- @lookup@ --- *
123 * Arguments: @void *p@ = pointer to a table
124 * @size_t sz@ = size of the table items
125 * @const char *s@ = pointer to string to match
127 * Returns: Pointer to the matching block.
129 * Use: Finds an item in a table.
132 static void *lookup(const void *p, size_t sz, const char *s)
134 const char **q = (const char **)p;
138 const char *x = *q, *y = s;
145 die("ambiguous name `%s'", s);
148 } else if (*x == *y) {
155 q = (const char **)((const char *)q + sz);
160 #define LOOKUP(tbl, key) lookup((tbl), sizeof((tbl)[0]), (key))
168 * Use: Ensures that the current device really is a Usernet device.
171 static void check(void)
175 if (ioctl(fd, UNIOCGINFO, 0) < 0)
176 die("file `%s' is not a Usernet device", current);
180 /*----- Command implementations -------------------------------------------*/
182 /* --- Forward declarations --- */
185 const char *cmd; /* Command name */
186 int minarg; /* Minimum number of args */
187 int (*func)(char **av); /* Command handler function */
188 const char *syn, *shelp, *vhelp; /* Short and verbose help */
191 static struct command cmdtab[];
194 const char *name; /* User-level protocol name */
195 unsigned short proto; /* Protocol number */
196 const char *desc; /* Readable description */
199 static struct eth_proto eth_prototab[];
203 #define YNQ_QUERY (-1)
209 { "query", YNQ_QUERY },
210 { "show", YNQ_QUERY },
219 static int run(char **av);
223 * Arguments: @const char *p@ = pointer to string to decode
225 * Returns: One of the @YNQ@ codes.
227 * Use: Decodes a `yes/no/query' response.
230 static int ynq(const char *p)
232 struct ynq *ynq = LOOKUP(ynqtab, p);
234 die("unknown setting `%s': I understand `on', `off' and `query'", p);
238 /* --- @cmd_help@ --- */
240 static int cmd_help(char **av)
245 puts("Commands provided:\n");
246 for (c = cmdtab; c->cmd; c++)
247 printf("%-30s%s\n", c->syn, c->shelp);
248 } else if ((c = LOOKUP(cmdtab, *av)) == 0)
249 die("unknown command: `%s'", *av);
251 fputs(c->syn, stdout);
252 fputs("\n\n", stdout);
253 fputs(c->vhelp, stdout);
259 /* --- @cmd_select@ --- */
261 static int cmd_select(char **av)
263 const char *fn = *av++;
264 if (flags & f_close) {
266 whitter("closed previous device `%s'", current);
268 fd = open(fn, O_RDWR);
270 die("couldn't open Usernet device `%s': %s", fn, strerror(errno));
274 whitter("opened new device: `%s'", fn);
279 /* --- @cmd_fd@ --- */
281 static int cmd_fd(char **av)
284 static char namebuf[20]; /* XXX Big enough? */
291 { "stdin", 0, "<stdin>" },
292 { "stdout", 1, "<stdout>" },
293 { "stderr", 2, "<stderr>" },
297 if (flags & f_close) {
299 whitter("closed previous device `%s'", current);
303 if ((fdp = LOOKUP(fdtab, p)) != 0)
305 else if (sscanf(p, "%i", &fd) < 1 || fd < 0)
306 die("bad file descriptor name: `%s'", p);
308 flags &= ~f_check | f_close;
310 current = fdtab[fd].desc;
312 current = namebuf, sprintf(namebuf, "<fd %i>", fd);
315 whitter("opened device on `%s'", current);
320 /* --- @cmd_show@ --- */
322 static int cmd_show(char **av)
324 struct unet_info uni;
325 const char *af, *proto;
327 /* --- Name table for address families --- */
329 static const char *aftab[] = {
331 "Unix file domain (!)",
333 "Amateur radio AX.25",
336 "Amateur radio NetROM",
337 "Multiprotocol bridge (!)",
338 "Reserved (for ATM)",
339 "Reserved (for X.25)",
343 /* --- Name table for Usernet flags --- */
349 { "trans", UNIF_TRANS },
350 /* @{ "open", UNIF_OPEN }@ -- ignore: this flag always appears on */
351 { "debug", UNIF_DEBUG },
355 /* --- Read the attachment information --- */
358 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
359 die("couldn't read information about attachment: %s", strerror(errno));
361 /* --- Look the address family up --- */
363 if (uni.uni_family < sizeof(aftab) / sizeof(aftab[0]))
364 af = aftab[uni.uni_family];
366 af = "Unknown family";
368 /* --- Look the protocol up --- */
371 struct eth_proto *ep;
374 for (ep = eth_prototab; ep->name; ep++) {
375 if (ep->proto == uni.uni_proto) {
382 /* --- Display appropriate information --- */
384 printf("Interface name: %s\n", uni.uni_ifname);
385 printf("MTU: %u\n", uni.uni_mtu);
386 printf("Address family: %s\n", af);
387 printf("Protocol: %s\n", proto);
390 for (flp = fltab; flp->name; flp++) {
391 if (uni.uni_flags & flp->f) {
393 fputs(flp->name, stdout);
403 /* --- @cmd_ifname@ --- */
405 static int cmd_ifname(char **av)
407 struct unet_info uni;
410 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
411 die("couldn't read attachment information: %s", strerror(errno));
412 puts(uni.uni_ifname);
416 /* --- @cmd_protocol@ --- */
418 static int cmd_protocol(char **av)
420 const char *pn = *av++;
421 const struct eth_proto *ep;
423 if ((ep = LOOKUP(eth_prototab, pn)) == 0)
424 die("unknown protocol name `%s'", pn);
426 if (ep->proto == ETH_P_HELP) {
427 for (ep = eth_prototab; ep->name; ep++) {
428 if (ep->proto != ETH_P_HELP)
429 printf("%s -- %s\n", ep->name, ep->desc);
433 if (ioctl(fd, UNIOCSPROTO, ep->proto) < 0)
434 die("couldn't set protocol `%s': %s", ep->name, strerror(errno));
435 whitter("set protocol `%s' for `%s'", ep->name, current);
441 /* --- @cmd_debug@ --- */
443 static int cmd_debug(char **av)
446 switch (ynq(*av++)) {
449 struct unet_info uni;
451 if (ioctl(fd, UNIOCGINFO, &uni))
452 die("error reading debug state: %s", strerror(errno));
453 printf("debugging for `%s' is %s\n", current,
454 uni.uni_flags & UNIF_DEBUG ? "enabled" : "disabled");
458 if (ioctl(fd, UNIOCSDEBUG, 1) < 0)
459 die("error setting debug state: %s", strerror(errno));
460 whitter("set debugging for `%s'", current);
464 if (ioctl(fd, UNIOCSDEBUG, 0) < 0)
465 die("error clearing debug state: %s", strerror(errno));
466 whitter("cleared debugging for `%s'", current);
473 /* --- @cmd_gdebug@ --- */
475 static int cmd_gdebug(char **av)
478 switch (ynq(*av++)) {
481 int i = ioctl(fd, UNIOCGGDEBUG);
483 die("error reading global debug state: %s", strerror(errno));
485 printf("global debugging is %s\n", i ? "enabled" : "disabled");
489 if (ioctl(fd, UNIOCSGDEBUG, 1) < 0)
490 die("error setting global debug state: %s", strerror(errno));
491 whitter("set global debugging");
495 if (ioctl(fd, UNIOCSGDEBUG, 0) < 0)
496 die("error clearing global debug state: %s", strerror(errno));
497 whitter("cleared global debugging");
504 /* --- @cmd_maxif@ --- */
506 static int cmd_maxif(char **av)
510 int i = ioctl(fd, UNIOCGMAXIF);
512 die("error reading maxif: %s", strerror(errno));
514 printf("interface maximum is %d\n", i);
517 long i = strtol(p, &q, 0);
519 die("malformed integer: %s", p);
520 if (ioctl(fd, UNIOCSMAXIF, i))
521 die("error setting maxif: %s", strerror(errno));
529 * Arguments: @char **av@ = array of command line arguments
531 * Returns: Zero for success, nonzero for failure
533 * Use: Handles a sequence of commands.
536 static int run(char **av)
542 return (EXIT_SUCCESS);
543 if ((c = LOOKUP(cmdtab, *av)) == 0)
548 die("command `%s' not implemented", c->cmd);
549 for (i = 0; i < c->minarg; i++) {
551 die("Usage: %s", c->syn);
553 return (c->func(av));
556 /* --- The command definition block --- */
558 static struct command cmdtab[] = {
560 { "select", 1, cmd_select,
561 "[select] FILE", "select a Usernet device", "\
562 Selects FILE as the current device. Each command acts only on the current\n\
563 device. The word `select' may be omitted if desired.\n\
568 "fd NUMBER", "select Usernet device from a file descriptor", "\
569 Selects the file descriptor NUMBER as the current device. This allows\n\
570 configuration of already existing transient attachments which would\n\
571 otherwise be unnecessarily awkward.\n\
574 { "show", 0, cmd_show,
575 "show", "show status of device", "\
576 Displays status information about the current device.\n\
579 { "ifname", 0, cmd_ifname,
580 "ifname", "print attached network interface name", "\
581 Displays the name of the network interface attached to the current device.\n\
582 This is useful in configuration scripts, for example.\n\
585 { "protocol", 1, cmd_protocol,
586 "protocol PROT", "selects PROT as the device's protocol", "\
587 Sets PROT as the current device's protocol. All packets received by the\n\
588 device are stamped with this protocol tag. To see a list of currently\n\
589 known protocols, use the `help' protocol.\n\
592 { "debug", 1, cmd_debug,
593 "debug on|off|query", "set attachment debugging state", "\
594 Set the current debugging state for the attachment. When debugging is\n\
595 enabled, all packets flowing through the attachment are logged, along with\n\
596 a large number of other informative messages. Note: the logs generated\n\
597 tend to be very large, so don't flood the interface with data while\n\
598 debugging is enabled!\n\
601 { "gdebug", 1, cmd_gdebug,
602 "gdebug on|off|query", "set global debugging state", "\
603 Set the global debugging state for Usernet. This controls the emission\n\
604 emission of various non-attachment-specific message. Also, new\n\
605 attachments inherit their debug flags from the global flag.\n\
608 { "maxif", 0, cmd_maxif,
609 "maxif [MAX]", "set maximum number of interfaces allowed", "\
610 Configures the maximum number of interfaces allowed (actually, the highest\n\
611 number of any interface).\n\
614 { "help", 0, cmd_help,
615 "help [COMMAND]", "display help about COMMAND", "\
616 If COMMAND is given, display help about it. If no COMAMND is specified,\n\
617 provide general help.\n\
623 /* --- Protocol description table --- */
625 static struct eth_proto eth_prototab[] = {
626 { "loop", ETH_P_LOOP, "Ethernet loopback" },
627 { "echo", ETH_P_ECHO, "Ethernet echo" },
628 { "pup", ETH_P_PUP, "Xerox PUP" },
629 { "ip", ETH_P_IP, "Internet IP" },
630 { "x25", ETH_P_X25, "CCITT X.25" },
631 { "arp", ETH_P_ARP, "Address resolution protocol" },
632 { "bpq", ETH_P_BPQ, "G8BPQ AX.25" },
633 { "dec", ETH_P_DEC, "DEC assigned" },
634 { "dna-dl", ETH_P_DNA_DL, "DEC DNA dump/load" },
635 { "dna-rcon", ETH_P_DNA_RC, "DEC DNA remote console" },
636 { "dna-route", ETH_P_DNA_RT, "DEC DNA routing" },
637 { "lat", ETH_P_LAT, "DEC LAT" },
638 { "diag", ETH_P_DIAG, "DEC diagnostics" },
639 { "cust", ETH_P_CUST, "DEC customer user" },
640 { "sca", ETH_P_SCA, "DEC Systems Comminications "
642 { "rarp", ETH_P_RARP, "Reverse address resolution" },
643 { "atalk", ETH_P_ATALK, "Appletalk DDP" },
644 { "aarp", ETH_P_AARP, "Appletalk AARP" },
645 { "ipx", ETH_P_IPX, "Novell IPX" },
646 { "ipv6", ETH_P_IPV6, "Internet IPv6" },
647 { "help", ETH_P_HELP, "<magical help token>" },
651 /*----- Informative messages ----------------------------------------------*/
655 * Arguments: @FILE *fp@ = stream to write on
659 * Use: Displays usage information for the program.
662 static void usage(FILE *fp)
664 fprintf(fp, "Usage: %s [-v] command ...\n", quis);
667 /* --- @version@ --- *
673 * Use: Displays the system's version number.
676 static void version(void)
678 printf("%s version %s\n", quis, VERSION);
687 * Use: Displays some help and quits.
690 static void help(void)
697 puts("Commands provided:\n");
698 for (c = cmdtab; c->cmd; c++)
699 printf("%-30s%s\n", c->syn, c->shelp);
705 * Arguments: @int argc@ = number of command line arguments received
706 * @char *argv[]@ = pointers to the command line arguments
708 * Returns: Zero for success, nonzero for failure
710 * Use: Dumps and manipulates Usernet attachments.
713 int main(int argc, char *argv[])
715 /* --- Set the program name properly --- */
718 if ((quis = strrchr(argv[0], '/')) == 0)
724 /* --- Now start parsing options --- */
729 static struct option opt[] = {
730 { "help", 0, 0, 'h' },
731 { "usage", 0, 0, 'U' },
732 { "version", 0, 0, 'V' },
733 { "verbose", 0, 0, 'v' },
737 if ((i = getopt_long(argc, argv, "hVv", opt, 0)) < 0)
759 if (flags & f_duff) {
761 printf("(Type `%s --help' for more information.)\n", quis);
765 return (run(argv + optind));
768 /*----- That's all, folks -------------------------------------------------*/