chiark / gitweb /
Setting the maximum interface count now does the right thing.
[unet] / unetcfg.c
... / ...
CommitLineData
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
65static const char *quis = "unetcfg"; /* My program's name */
66static int flags = 0; /* Various useful status flags */
67static const char *current = "<stdin>"; /* Current Usernet device */
68static 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
87static 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
109static 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
135static 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
174static 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
187struct 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
194static struct command cmdtab[];
195
196struct eth_proto {
197 const char *name; /* User-level protocol name */
198 unsigned short proto; /* Protocol number */
199 const char *desc; /* Readable description */
200};
201
202static struct eth_proto eth_prototab[];
203
204#define YNQ_YES 1
205#define YNQ_NO 0
206#define YNQ_QUERY (-1)
207
208static 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
222static 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
233static 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
243static 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
264static 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
284static 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
325static 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
408static 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
421static 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
446static 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
478static 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
509static 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
539static 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
561static struct command cmdtab[] = {
562
563 { "select", 1, cmd_select,
564 "[select] FILE", "select a Usernet device", "\
565Selects FILE as the current device. Each command acts only on the current\n\
566device. 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", "\
572Selects the file descriptor NUMBER as the current device. This allows\n\
573configuration of already existing transient attachments which would\n\
574otherwise be unnecessarily awkward.\n\
575" },
576
577 { "show", 0, cmd_show,
578 "show", "show status of device", "\
579Displays status information about the current device.\n\
580" },
581
582 { "ifname", 0, cmd_ifname,
583 "ifname", "print attached network interface name", "\
584Displays the name of the network interface attached to the current device.\n\
585This 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", "\
590Sets PROT as the current device's protocol. All packets received by the\n\
591device are stamped with this protocol tag. To see a list of currently\n\
592known protocols, use the `help' protocol.\n\
593" },
594
595 { "debug", 1, cmd_debug,
596 "debug on|off|query", "set attachment debugging state", "\
597Set the current debugging state for the attachment. When debugging is\n\
598enabled, all packets flowing through the attachment are logged, along with\n\
599a large number of other informative messages. Note: the logs generated\n\
600tend to be very large, so don't flood the interface with data while\n\
601debugging is enabled!\n\
602" },
603
604 { "gdebug", 1, cmd_gdebug,
605 "gdebug on|off|query", "set global debugging state", "\
606Set the global debugging state for Usernet. This controls the emission\n\
607emission of various non-attachment-specific message. Also, new\n\
608attachments 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", "\
613Configures the maximum number of interfaces allowed.\n\
614" },
615
616 { "help", 0, cmd_help,
617 "help [COMMAND]", "display help about COMMAND", "\
618If COMMAND is given, display help about it. If no COMAMND is specified,\n\
619provide general help.\n\
620" },
621
622 { 0, 0, 0, 0 }
623};
624
625/* --- Protocol description table --- */
626
627static 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
664static 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
678static 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
692static 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
715int 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 -------------------------------------------------*/