chiark / gitweb /
Setting the maximum interface count now does the right thing.
[unet] / unetcfg.c
1 /* -*-c-*-
2  *
3  * $Id: unetcfg.c,v 1.2 2001/02/03 18:39:59 mdw Exp $
4  *
5  * User-space network device support.
6  *
7  * (c) 1998 Mark Wooding
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of Usernet.
13  *
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.
18  *
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.
23  *
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.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: unetcfg.c,v $
32  * Revision 1.2  2001/02/03 18:39:59  mdw
33  * Setting the maximum interface count now does the right thing.
34  *
35  * Revision 1.1  2001/01/25 22:03:39  mdw
36  * Initial check-in (somewhat belated).
37  *
38  */
39
40 /*----- Include files -----------------------------------------------------*/
41
42 #include <errno.h>
43 #include <getopt.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include <linux/if_ether.h>
50
51 #include <sys/types.h>
52 #include <sys/time.h>
53 #include <sys/ioctl.h>
54
55 #include <fcntl.h>
56 #include <unistd.h>
57
58 #include "unet.h"
59
60 /*----- Static variables --------------------------------------------------*/
61
62 #define VERSION "1.00"
63 #define ETH_P_HELP 0xffffu
64
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@ */
69
70 #define f_verbose 1u
71 #define f_duff 2u
72 #define f_check 4u
73 #define f_close 8u
74
75 /*----- Common routines ---------------------------------------------------*/
76
77 /* --- @die@ --- *
78  *
79  * Arguments:   @const char *format@ = format string to print
80  *              @...@ = values for the placeholders
81  *
82  * Returns:     Doesn't
83  *
84  * Use:         Reports a fatal error message.
85  */
86
87 static void die(const char *format, ...)
88 {
89   va_list ap;
90   va_start(ap, format);
91   fputs(quis, stderr);
92   fputs(": ", stderr);
93   vfprintf(stderr, format, ap);
94   fputc('\n', stderr);
95   va_end(ap);
96   exit(EXIT_FAILURE);
97 }
98
99 /* --- @whiiter@ --- *
100  *
101  * Arguments:   @const char *format@ = format string to print
102  *              @...@ = values for the placeholders
103  *
104  * Returns:     Doesn't
105  *
106  * Use:         Reports a non-fatal error message.
107  */
108
109 static void whitter(const char *format, ...)
110 {
111   va_list ap;
112
113   if ((flags & f_verbose) == 0)
114     return;
115
116   va_start(ap, format);
117   fputs(quis, stderr);
118   fputs(": ", stderr);
119   vfprintf(stderr, format, ap);
120   fputc('\n', stderr);
121   va_end(ap);
122 }
123
124 /* --- @lookup@ --- *
125  *
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
129  *
130  * Returns:     Pointer to the matching block.
131  *
132  * Use:         Finds an item in a table.
133  */
134
135 static void *lookup(const void *p, size_t sz, const char *s)
136 {
137   const char **q = (const char **)p;
138   const void *e = 0;
139
140   while (*q) {
141     const char *x = *q, *y = s;
142
143     for (;;) {
144       if (!*y) {
145         if (!*x)
146           return ((void *)q);
147         if (e)
148           die("ambiguous name `%s'", s);
149         e = q;
150         break;
151       } else if (*x == *y) {
152         x++;
153         y++;
154         continue;
155       } else
156         break;
157     }
158     q = (const char **)((const char *)q + sz);
159   }
160   return ((void *)e);
161 }
162
163 #define LOOKUP(tbl, key) lookup((tbl), sizeof((tbl)[0]), (key))
164
165 /* --- @check@ --- *
166  *
167  * Arguments:   ---
168  *
169  * Returns:     ---
170  *
171  * Use:         Ensures that the current device really is a Usernet device.
172  */
173
174 static void check(void)
175 {
176   if (flags & f_check)
177     return;
178   if (ioctl(fd, UNIOCGINFO, 0) < 0)
179     die("file `%s' is not a Usernet device", current);
180   flags |= f_check;
181 }
182
183 /*----- Command implementations -------------------------------------------*/
184
185 /* --- Forward declarations --- */
186
187 struct command {
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 */
192 };
193
194 static struct command cmdtab[];
195
196 struct eth_proto {
197   const char *name;                     /* User-level protocol name */
198   unsigned short proto;                 /* Protocol number */
199   const char *desc;                     /* Readable description */
200 };
201
202 static struct eth_proto eth_prototab[];
203
204 #define YNQ_YES 1
205 #define YNQ_NO 0
206 #define YNQ_QUERY (-1)
207
208 static struct ynq {
209   const char *s;
210   int val;
211 } ynqtab[] = {
212   { "query",    YNQ_QUERY },
213   { "show",     YNQ_QUERY },
214   { "?",        YNQ_QUERY },
215   { "yes",      YNQ_YES },
216   { "on",       YNQ_YES },
217   { "no",       YNQ_NO },
218   { "off",      YNQ_NO },
219   { 0,          0 }
220 };
221
222 static int run(char **av);
223
224 /* --- @ynq@ --- *
225  *
226  * Arguments:   @const char *p@ = pointer to string to decode
227  *
228  * Returns:     One of the @YNQ@ codes.
229  *
230  * Use:         Decodes a `yes/no/query' response.
231  */
232
233 static int ynq(const char *p)
234 {
235   struct ynq *ynq = LOOKUP(ynqtab, p);
236   if (!ynq)
237     die("unknown setting `%s': I understand `on', `off' and `query'", p);
238   return (ynq->val);
239 }
240
241 /* --- @cmd_help@ --- */
242
243 static int cmd_help(char **av)
244 {
245   struct command *c;
246
247   if (!*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);
253   else {
254     fputs(c->syn, stdout);
255     fputs("\n\n", stdout);
256     fputs(c->vhelp, stdout);
257     av++;
258   }
259   return (run(av));
260 }
261
262 /* --- @cmd_select@ --- */
263
264 static int cmd_select(char **av)
265 {
266   const char *fn = *av++;
267   if (flags & f_close) {
268     close(fd);
269     whitter("closed previous device `%s'", current);
270   }
271   fd = open(fn, O_RDWR);
272   if (fd < 0)
273     die("couldn't open Usernet device `%s': %s", fn, strerror(errno));
274   current = fn;
275   flags &= ~f_check;
276   check();
277   whitter("opened new device: `%s'", fn);
278   flags |= f_close;
279   return (run(av));
280 }
281
282 /* --- @cmd_fd@ --- */
283
284 static int cmd_fd(char **av)
285 {
286   const char *p;
287   static char namebuf[20]; /* XXX Big enough? */
288
289   static struct {
290     const char *name;
291     int fd;
292     const char *desc;
293   } *fdp, fdtab[] = {
294     { "stdin",  0, "<stdin>" },
295     { "stdout", 1, "<stdout>" },
296     { "stderr", 2, "<stderr>" },
297     { 0,        0, 0 }
298   };
299
300   if (flags & f_close) {
301     close(fd);
302     whitter("closed previous device `%s'", current);
303   }
304
305   p = *av++;
306   if ((fdp = LOOKUP(fdtab, p)) != 0)
307     fd = fdp->fd;
308   else if (sscanf(p, "%i", &fd) < 1 || fd < 0)
309     die("bad file descriptor name: `%s'", p);
310
311   flags &= ~f_check | f_close;
312   if (fd < 3)
313     current = fdtab[fd].desc;
314   else
315     current = namebuf, sprintf(namebuf, "<fd %i>", fd);
316
317   check();
318   whitter("opened device on `%s'", current);
319
320   return (run(av));
321 }
322
323 /* --- @cmd_show@ --- */
324
325 static int cmd_show(char **av)
326 {
327   struct unet_info uni;
328   const char *af, *proto;
329
330   /* --- Name table for address families --- */
331
332   static const char *aftab[] = {
333     "Unspecified",
334     "Unix file domain (!)",
335     "Internet (IPv4)",
336     "Amateur radio AX.25",
337     "Novell IPX",
338     "Appletalk",
339     "Amateur radio NetROM",
340     "Multiprotocol bridge (!)",
341     "Reserved (for ATM)",
342     "Reserved (for X.25)",
343     "Internet (IPv6)",
344   };
345
346   /* --- Name table for Usernet flags --- */
347
348   static struct {
349     const char *name;
350     unsigned int f;
351   } *flp, fltab[] = {
352     { "trans", UNIF_TRANS },
353     /* @{ "open", UNIF_OPEN }@ -- ignore: this flag always appears on */
354     { "debug", UNIF_DEBUG },
355     { 0, 0 }
356   };
357
358   /* --- Read the attachment information --- */
359
360   check();
361   if (ioctl(fd, UNIOCGINFO, &uni) < 0)
362     die("couldn't read information about attachment: %s", strerror(errno));
363
364   /* --- Look the address family up --- */
365
366   if (uni.uni_family < sizeof(aftab) / sizeof(aftab[0]))
367     af = aftab[uni.uni_family];
368   else
369     af = "Unknown family";
370
371   /* --- Look the protocol up --- */
372
373   {
374     struct eth_proto *ep;
375
376     proto = "unknown";
377     for (ep = eth_prototab; ep->name; ep++) {
378       if (ep->proto == uni.uni_proto) {
379         proto = ep->desc;
380         break;
381       }
382     }
383   }
384
385   /* --- Display appropriate information --- */
386
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);
391   printf("Flags:");
392
393   for (flp = fltab; flp->name; flp++) {
394     if (uni.uni_flags & flp->f) {
395       putchar(' ');
396       fputs(flp->name, stdout);
397     }
398   }
399   putchar('\n');
400
401   /* --- Done --- */
402
403   return (run(av));
404 }
405
406 /* --- @cmd_ifname@ --- */
407
408 static int cmd_ifname(char **av)
409 {
410   struct unet_info uni;
411
412   check();
413   if (ioctl(fd, UNIOCGINFO, &uni) < 0)
414     die("couldn't read attachment information: %s", strerror(errno));
415   puts(uni.uni_ifname);
416   return (run(av));
417 }
418
419 /* --- @cmd_protocol@ --- */
420
421 static int cmd_protocol(char **av)
422 {
423   const char *pn = *av++;
424   const struct eth_proto *ep;
425
426   if ((ep = LOOKUP(eth_prototab, pn)) == 0)
427     die("unknown protocol name `%s'", pn);
428
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);
433     }
434   } else {
435     check();
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);
439   }
440
441   return (run(av));
442 }
443
444 /* --- @cmd_debug@ --- */
445
446 static int cmd_debug(char **av)
447 {
448   check();
449   switch (ynq(*av++)) {
450
451     case YNQ_QUERY: {
452       struct unet_info uni;
453
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");
458     } break;
459
460     case YNQ_YES:
461       if (ioctl(fd, UNIOCSDEBUG, 1) < 0)
462         die("error setting debug state: %s", strerror(errno));
463       whitter("set debugging for `%s'", current);
464       break;
465
466     case YNQ_NO:
467       if (ioctl(fd, UNIOCSDEBUG, 0) < 0)
468         die("error clearing debug state: %s", strerror(errno));
469       whitter("cleared debugging for `%s'", current);
470       break;
471   }
472
473   return (run(av));
474 }
475       
476 /* --- @cmd_gdebug@ --- */
477
478 static int cmd_gdebug(char **av)
479 {
480   check();
481   switch (ynq(*av++)) {
482
483     case YNQ_QUERY: {
484       int i = ioctl(fd, UNIOCGGDEBUG);
485       if (i < 0)
486         die("error reading global debug state: %s", strerror(errno));
487       else
488         printf("global debugging is %s\n", i ? "enabled" : "disabled");
489     } break;
490
491     case YNQ_YES:
492       if (ioctl(fd, UNIOCSGDEBUG, 1) < 0)
493         die("error setting global debug state: %s", strerror(errno));
494       whitter("set global debugging");
495       break;
496
497     case YNQ_NO:
498       if (ioctl(fd, UNIOCSGDEBUG, 0) < 0)
499         die("error clearing global debug state: %s", strerror(errno));
500       whitter("cleared global debugging");
501       break;
502   }
503
504   return (run(av));
505 }
506
507 /* --- @cmd_maxif@ --- */
508
509 static int cmd_maxif(char **av)
510 {
511   check();
512   if (!*av) {
513     int i = ioctl(fd, UNIOCGMAXIF);
514     if (i < 0)
515       die("error reading maxif: %s", strerror(errno));
516     else
517       printf("interface maximum is %d\n", i);
518   } else {
519     char *p = *av++, *q;
520     long i = strtol(p, &q, 0);
521     if (*q)
522       die("malformed integer: %s", p);
523     if (ioctl(fd, UNIOCSMAXIF, i))
524       die("error setting maxif: %s", strerror(errno));
525   }
526
527   return (run(av));
528 }
529
530 /* --- @run@ --- *
531  *
532  * Arguments:   @char **av@ = array of command line arguments
533  *
534  * Returns:     Zero for success, nonzero for failure
535  *
536  * Use:         Handles a sequence of commands.
537  */
538
539 static int run(char **av)
540 {
541   struct command *c;
542   int i;
543
544   if (!*av)
545     return (EXIT_SUCCESS);
546   if ((c = LOOKUP(cmdtab, *av)) == 0)
547     c = &cmdtab[0];
548   else
549     av++;
550   if (!c->func)
551     die("command `%s' not implemented", c->cmd);
552   for (i = 0; i < c->minarg; i++) {
553     if (!av[i])
554       die("Usage: %s", c->syn);
555   }
556   return (c->func(av));
557 }
558
559 /* --- The command definition block --- */
560
561 static struct command cmdtab[] = {
562
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\
567 " },
568
569   {
570     "fd", 1, cmd_fd,
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\
575 " },
576
577   { "show", 0, cmd_show,
578     "show", "show status of device", "\
579 Displays status information about the current device.\n\
580 " },
581
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\
586 " },
587
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\
593 " },
594
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\
602 " },
603
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\
609 " },
610
611   { "maxif", 0, cmd_maxif,
612     "maxif [MAX]", "set maximum number of interfaces allowed", "\
613 Configures the maximum number of interfaces allowed.\n\
614 " },
615
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\
620 " },
621
622   { 0, 0, 0, 0 }
623 };
624
625 /* --- Protocol description table --- */
626
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 "
643                                            "Architecture" },
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>" },
650   { 0,                  0,              0 }
651 };
652
653 /*----- Informative messages ----------------------------------------------*/
654
655 /* --- @usage@ --- *
656  *
657  * Arguments:   @FILE *fp@ = stream to write on
658  *
659  * Returns:     ---
660  *
661  * Use:         Displays usage information for the program.
662  */
663
664 static void usage(FILE *fp)
665 {
666   fprintf(fp, "Usage: %s [-v] command ...\n", quis);
667 }
668
669 /* --- @version@ --- *
670  *
671  * Arguments:   ---
672  *
673  * Returns:     ---
674  *
675  * Use:         Displays the system's version number.
676  */
677
678 static void version(void)
679 {
680   printf("%s version %s\n", quis, VERSION);
681 }
682
683 /* --- @help@ --- *
684  *
685  * Arguments:   ---
686  *
687  * Returns:     Doesn't
688  *
689  * Use:         Displays some help and quits.
690  */
691
692 static void help(void)
693 {
694   struct command *c;
695   version();
696   putchar('\n');
697   usage(stdout);
698   putchar('\n');
699   puts("Commands provided:\n");
700   for (c = cmdtab; c->cmd; c++)
701     printf("%-30s%s\n", c->syn, c->shelp);
702   exit(0);
703 }
704
705 /* --- @main@ --- *
706  *
707  * Arguments:   @int argc@ = number of command line arguments received
708  *              @char *argv[]@ = pointers to the command line arguments
709  *
710  * Returns:     Zero for success, nonzero for failure
711  *
712  * Use:         Dumps and manipulates Usernet attachments.
713  */
714
715 int main(int argc, char *argv[])
716 {
717   /* --- Set the program name properly --- */
718
719   if (argc >= 1) {
720     if ((quis = strrchr(argv[0], '/')) == 0)
721       quis = argv[0];
722     else
723       quis++;
724   }
725
726   /* --- Now start parsing options --- */
727
728   for (;;) {
729     int i;
730
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' },
736       { 0,                      0,      0,      0 }
737     };
738
739     if ((i = getopt_long(argc, argv, "hVv", opt, 0)) < 0)
740       break;
741     switch (i) {
742       case 'h':
743         help();
744         break;
745       case 'U':
746         usage(stdout);
747         exit(0);
748         break;
749       case 'V':
750         version();
751         break;
752       case 'v':
753         flags |= f_verbose;
754         break;
755       default:
756         flags |= f_duff;
757         break;
758     }
759   }
760
761   if (flags & f_duff) {
762     usage(stderr);
763     printf("(Type `%s --help' for more information.)\n", quis);
764     exit(EXIT_FAILURE);
765   }
766
767   return (run(argv + optind));
768 }
769
770 /*----- That's all, folks -------------------------------------------------*/