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