chiark / gitweb /
Extract Subversion ignore data.
[unet] / unetcfg.c
CommitLineData
4e3819cf 1/* -*-c-*-
2 *
7e365031 3 * $Id: unetcfg.c,v 1.3 2001/02/19 19:10:28 mdw Exp $
4e3819cf 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 $
7e365031 32 * Revision 1.3 2001/02/19 19:10:28 mdw
33 * New option to allow changing interface flags.
34 *
88613db4 35 * Revision 1.2 2001/02/03 18:39:59 mdw
36 * Setting the maximum interface count now does the right thing.
37 *
4e3819cf 38 * Revision 1.1 2001/01/25 22:03:39 mdw
39 * Initial check-in (somewhat belated).
40 *
41 */
42
43/*----- Include files -----------------------------------------------------*/
44
45#include <errno.h>
46#include <getopt.h>
47#include <stdarg.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51
4e3819cf 52#include <sys/types.h>
53#include <sys/time.h>
54#include <sys/ioctl.h>
55
56#include <fcntl.h>
57#include <unistd.h>
58
7e365031 59#include <sys/socket.h>
60#include <netinet/in.h>
61
62#include <linux/if.h>
63#include <linux/if_ether.h>
64
4e3819cf 65#include "unet.h"
66
67/*----- Static variables --------------------------------------------------*/
68
69#define VERSION "1.00"
70#define ETH_P_HELP 0xffffu
71
72static const char *quis = "unetcfg"; /* My program's name */
73static int flags = 0; /* Various useful status flags */
74static const char *current = "<stdin>"; /* Current Usernet device */
75static int fd = 0; /* Default to @stdin@ */
76
77#define f_verbose 1u
78#define f_duff 2u
79#define f_check 4u
80#define f_close 8u
81
82/*----- Common routines ---------------------------------------------------*/
83
84/* --- @die@ --- *
85 *
86 * Arguments: @const char *format@ = format string to print
87 * @...@ = values for the placeholders
88 *
89 * Returns: Doesn't
90 *
91 * Use: Reports a fatal error message.
92 */
93
94static void die(const char *format, ...)
95{
96 va_list ap;
97 va_start(ap, format);
98 fputs(quis, stderr);
99 fputs(": ", stderr);
100 vfprintf(stderr, format, ap);
101 fputc('\n', stderr);
102 va_end(ap);
103 exit(EXIT_FAILURE);
104}
105
106/* --- @whiiter@ --- *
107 *
108 * Arguments: @const char *format@ = format string to print
109 * @...@ = values for the placeholders
110 *
111 * Returns: Doesn't
112 *
113 * Use: Reports a non-fatal error message.
114 */
115
116static void whitter(const char *format, ...)
117{
118 va_list ap;
119
120 if ((flags & f_verbose) == 0)
121 return;
122
123 va_start(ap, format);
124 fputs(quis, stderr);
125 fputs(": ", stderr);
126 vfprintf(stderr, format, ap);
127 fputc('\n', stderr);
128 va_end(ap);
129}
130
131/* --- @lookup@ --- *
132 *
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
136 *
137 * Returns: Pointer to the matching block.
138 *
139 * Use: Finds an item in a table.
140 */
141
142static void *lookup(const void *p, size_t sz, const char *s)
143{
144 const char **q = (const char **)p;
145 const void *e = 0;
146
147 while (*q) {
148 const char *x = *q, *y = s;
149
150 for (;;) {
151 if (!*y) {
152 if (!*x)
153 return ((void *)q);
154 if (e)
155 die("ambiguous name `%s'", s);
156 e = q;
157 break;
158 } else if (*x == *y) {
159 x++;
160 y++;
161 continue;
162 } else
163 break;
164 }
165 q = (const char **)((const char *)q + sz);
166 }
167 return ((void *)e);
168}
169
170#define LOOKUP(tbl, key) lookup((tbl), sizeof((tbl)[0]), (key))
171
172/* --- @check@ --- *
173 *
174 * Arguments: ---
175 *
176 * Returns: ---
177 *
178 * Use: Ensures that the current device really is a Usernet device.
179 */
180
181static void check(void)
182{
183 if (flags & f_check)
184 return;
185 if (ioctl(fd, UNIOCGINFO, 0) < 0)
186 die("file `%s' is not a Usernet device", current);
187 flags |= f_check;
188}
189
190/*----- Command implementations -------------------------------------------*/
191
192/* --- Forward declarations --- */
193
194struct command {
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 */
199};
200
201static struct command cmdtab[];
202
203struct eth_proto {
204 const char *name; /* User-level protocol name */
205 unsigned short proto; /* Protocol number */
206 const char *desc; /* Readable description */
207};
208
209static struct eth_proto eth_prototab[];
210
211#define YNQ_YES 1
212#define YNQ_NO 0
213#define YNQ_QUERY (-1)
214
215static struct ynq {
216 const char *s;
217 int val;
218} ynqtab[] = {
219 { "query", YNQ_QUERY },
220 { "show", YNQ_QUERY },
221 { "?", YNQ_QUERY },
222 { "yes", YNQ_YES },
223 { "on", YNQ_YES },
224 { "no", YNQ_NO },
225 { "off", YNQ_NO },
226 { 0, 0 }
227};
228
229static int run(char **av);
230
231/* --- @ynq@ --- *
232 *
233 * Arguments: @const char *p@ = pointer to string to decode
234 *
235 * Returns: One of the @YNQ@ codes.
236 *
237 * Use: Decodes a `yes/no/query' response.
238 */
239
240static int ynq(const char *p)
241{
242 struct ynq *ynq = LOOKUP(ynqtab, p);
243 if (!ynq)
244 die("unknown setting `%s': I understand `on', `off' and `query'", p);
245 return (ynq->val);
246}
247
248/* --- @cmd_help@ --- */
249
250static int cmd_help(char **av)
251{
252 struct command *c;
253
254 if (!*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);
260 else {
261 fputs(c->syn, stdout);
262 fputs("\n\n", stdout);
263 fputs(c->vhelp, stdout);
264 av++;
265 }
266 return (run(av));
267}
268
269/* --- @cmd_select@ --- */
270
271static int cmd_select(char **av)
272{
273 const char *fn = *av++;
274 if (flags & f_close) {
275 close(fd);
276 whitter("closed previous device `%s'", current);
277 }
278 fd = open(fn, O_RDWR);
279 if (fd < 0)
280 die("couldn't open Usernet device `%s': %s", fn, strerror(errno));
281 current = fn;
282 flags &= ~f_check;
283 check();
284 whitter("opened new device: `%s'", fn);
285 flags |= f_close;
286 return (run(av));
287}
288
289/* --- @cmd_fd@ --- */
290
291static int cmd_fd(char **av)
292{
293 const char *p;
294 static char namebuf[20]; /* XXX Big enough? */
295
296 static struct {
297 const char *name;
298 int fd;
299 const char *desc;
300 } *fdp, fdtab[] = {
301 { "stdin", 0, "<stdin>" },
302 { "stdout", 1, "<stdout>" },
303 { "stderr", 2, "<stderr>" },
304 { 0, 0, 0 }
305 };
306
307 if (flags & f_close) {
308 close(fd);
309 whitter("closed previous device `%s'", current);
310 }
311
312 p = *av++;
313 if ((fdp = LOOKUP(fdtab, p)) != 0)
314 fd = fdp->fd;
315 else if (sscanf(p, "%i", &fd) < 1 || fd < 0)
316 die("bad file descriptor name: `%s'", p);
317
318 flags &= ~f_check | f_close;
319 if (fd < 3)
320 current = fdtab[fd].desc;
321 else
322 current = namebuf, sprintf(namebuf, "<fd %i>", fd);
323
324 check();
325 whitter("opened device on `%s'", current);
326
327 return (run(av));
328}
329
330/* --- @cmd_show@ --- */
331
332static int cmd_show(char **av)
333{
334 struct unet_info uni;
335 const char *af, *proto;
336
337 /* --- Name table for address families --- */
338
339 static const char *aftab[] = {
340 "Unspecified",
341 "Unix file domain (!)",
342 "Internet (IPv4)",
343 "Amateur radio AX.25",
344 "Novell IPX",
345 "Appletalk",
346 "Amateur radio NetROM",
347 "Multiprotocol bridge (!)",
348 "Reserved (for ATM)",
349 "Reserved (for X.25)",
350 "Internet (IPv6)",
351 };
352
353 /* --- Name table for Usernet flags --- */
354
355 static struct {
356 const char *name;
357 unsigned int f;
358 } *flp, fltab[] = {
359 { "trans", UNIF_TRANS },
360 /* @{ "open", UNIF_OPEN }@ -- ignore: this flag always appears on */
361 { "debug", UNIF_DEBUG },
362 { 0, 0 }
363 };
364
365 /* --- Read the attachment information --- */
366
367 check();
368 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
369 die("couldn't read information about attachment: %s", strerror(errno));
370
371 /* --- Look the address family up --- */
372
373 if (uni.uni_family < sizeof(aftab) / sizeof(aftab[0]))
374 af = aftab[uni.uni_family];
375 else
376 af = "Unknown family";
377
378 /* --- Look the protocol up --- */
379
380 {
381 struct eth_proto *ep;
382
383 proto = "unknown";
384 for (ep = eth_prototab; ep->name; ep++) {
385 if (ep->proto == uni.uni_proto) {
386 proto = ep->desc;
387 break;
388 }
389 }
390 }
391
392 /* --- Display appropriate information --- */
393
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);
398 printf("Flags:");
399
400 for (flp = fltab; flp->name; flp++) {
401 if (uni.uni_flags & flp->f) {
402 putchar(' ');
403 fputs(flp->name, stdout);
404 }
405 }
406 putchar('\n');
407
408 /* --- Done --- */
409
410 return (run(av));
411}
412
413/* --- @cmd_ifname@ --- */
414
415static int cmd_ifname(char **av)
416{
417 struct unet_info uni;
418
419 check();
420 if (ioctl(fd, UNIOCGINFO, &uni) < 0)
421 die("couldn't read attachment information: %s", strerror(errno));
422 puts(uni.uni_ifname);
423 return (run(av));
424}
425
426/* --- @cmd_protocol@ --- */
427
428static int cmd_protocol(char **av)
429{
430 const char *pn = *av++;
431 const struct eth_proto *ep;
432
433 if ((ep = LOOKUP(eth_prototab, pn)) == 0)
434 die("unknown protocol name `%s'", pn);
435
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);
440 }
441 } else {
442 check();
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);
446 }
447
448 return (run(av));
449}
450
451/* --- @cmd_debug@ --- */
452
453static int cmd_debug(char **av)
454{
455 check();
456 switch (ynq(*av++)) {
457
458 case YNQ_QUERY: {
459 struct unet_info uni;
460
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");
465 } break;
466
467 case YNQ_YES:
468 if (ioctl(fd, UNIOCSDEBUG, 1) < 0)
469 die("error setting debug state: %s", strerror(errno));
470 whitter("set debugging for `%s'", current);
471 break;
472
473 case YNQ_NO:
474 if (ioctl(fd, UNIOCSDEBUG, 0) < 0)
475 die("error clearing debug state: %s", strerror(errno));
476 whitter("cleared debugging for `%s'", current);
477 break;
478 }
479
480 return (run(av));
481}
482
483/* --- @cmd_gdebug@ --- */
484
485static int cmd_gdebug(char **av)
486{
487 check();
488 switch (ynq(*av++)) {
489
490 case YNQ_QUERY: {
491 int i = ioctl(fd, UNIOCGGDEBUG);
492 if (i < 0)
493 die("error reading global debug state: %s", strerror(errno));
494 else
495 printf("global debugging is %s\n", i ? "enabled" : "disabled");
496 } break;
497
498 case YNQ_YES:
499 if (ioctl(fd, UNIOCSGDEBUG, 1) < 0)
500 die("error setting global debug state: %s", strerror(errno));
501 whitter("set global debugging");
502 break;
503
504 case YNQ_NO:
505 if (ioctl(fd, UNIOCSGDEBUG, 0) < 0)
506 die("error clearing global debug state: %s", strerror(errno));
507 whitter("cleared global debugging");
508 break;
509 }
510
511 return (run(av));
512}
513
514/* --- @cmd_maxif@ --- */
515
516static int cmd_maxif(char **av)
517{
518 check();
519 if (!*av) {
520 int i = ioctl(fd, UNIOCGMAXIF);
521 if (i < 0)
522 die("error reading maxif: %s", strerror(errno));
523 else
524 printf("interface maximum is %d\n", i);
525 } else {
526 char *p = *av++, *q;
527 long i = strtol(p, &q, 0);
528 if (*q)
529 die("malformed integer: %s", p);
530 if (ioctl(fd, UNIOCSMAXIF, i))
531 die("error setting maxif: %s", strerror(errno));
532 }
533
534 return (run(av));
535}
536
7e365031 537/* --- @cmd_ifflags@ --- */
538
539static int cmd_ifflags(char **av)
540{
541 static const struct iftab {
542 const char *name;
543 unsigned f;
544 } iftab[] = {
545 { "broadcast", IFF_BROADCAST },
546 { "loopback", IFF_LOOPBACK },
547 { "pointopoint", IFF_POINTOPOINT },
548 { 0, 0 }
549 };
550
551 char *p;
552 int sense;
553 struct iftab *ift;
554 int f;
555
556 check();
557 if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0)
558 die("error reading interface flags: %s", strerror(errno));
559
560 for (p = strtok(*av++, ","); p; p = strtok(0, ",")) {
561 sense = 1;
562 switch (*p) {
563 case '-': sense = 0;
564 case '+': p++;
565 break;
566 }
567 ift = LOOKUP(iftab, p);
568 if (!ift)
569 die("unknown interface flag `%s'", p);
570 if (sense)
571 f |= ift->f;
572 else
573 f &= ~ift->f;
574 }
575 if (ioctl(fd, UNIOCSIFFLAGS, f))
576 die("error setting interface flags: %s", strerror(errno));
577 return (run(av));
578}
579
4e3819cf 580/* --- @run@ --- *
581 *
582 * Arguments: @char **av@ = array of command line arguments
583 *
584 * Returns: Zero for success, nonzero for failure
585 *
586 * Use: Handles a sequence of commands.
587 */
588
589static int run(char **av)
590{
591 struct command *c;
592 int i;
593
594 if (!*av)
595 return (EXIT_SUCCESS);
596 if ((c = LOOKUP(cmdtab, *av)) == 0)
597 c = &cmdtab[0];
598 else
599 av++;
600 if (!c->func)
601 die("command `%s' not implemented", c->cmd);
602 for (i = 0; i < c->minarg; i++) {
603 if (!av[i])
604 die("Usage: %s", c->syn);
605 }
606 return (c->func(av));
607}
608
609/* --- The command definition block --- */
610
611static struct command cmdtab[] = {
612
613 { "select", 1, cmd_select,
614 "[select] FILE", "select a Usernet device", "\
615Selects FILE as the current device. Each command acts only on the current\n\
616device. The word `select' may be omitted if desired.\n\
617" },
618
619 {
620 "fd", 1, cmd_fd,
621 "fd NUMBER", "select Usernet device from a file descriptor", "\
622Selects the file descriptor NUMBER as the current device. This allows\n\
623configuration of already existing transient attachments which would\n\
624otherwise be unnecessarily awkward.\n\
625" },
626
627 { "show", 0, cmd_show,
628 "show", "show status of device", "\
629Displays status information about the current device.\n\
630" },
631
632 { "ifname", 0, cmd_ifname,
633 "ifname", "print attached network interface name", "\
634Displays the name of the network interface attached to the current device.\n\
635This is useful in configuration scripts, for example.\n\
636" },
637
638 { "protocol", 1, cmd_protocol,
639 "protocol PROT", "selects PROT as the device's protocol", "\
640Sets PROT as the current device's protocol. All packets received by the\n\
641device are stamped with this protocol tag. To see a list of currently\n\
642known protocols, use the `help' protocol.\n\
643" },
644
645 { "debug", 1, cmd_debug,
646 "debug on|off|query", "set attachment debugging state", "\
647Set the current debugging state for the attachment. When debugging is\n\
648enabled, all packets flowing through the attachment are logged, along with\n\
649a large number of other informative messages. Note: the logs generated\n\
650tend to be very large, so don't flood the interface with data while\n\
651debugging is enabled!\n\
652" },
653
654 { "gdebug", 1, cmd_gdebug,
655 "gdebug on|off|query", "set global debugging state", "\
656Set the global debugging state for Usernet. This controls the emission\n\
657emission of various non-attachment-specific message. Also, new\n\
658attachments inherit their debug flags from the global flag.\n\
659" },
660
661 { "maxif", 0, cmd_maxif,
662 "maxif [MAX]", "set maximum number of interfaces allowed", "\
88613db4 663Configures the maximum number of interfaces allowed.\n\
7e365031 664" },
665
666 { "ifflags", 1, cmd_ifflags,
667 "ifflags [-]FLAG,[-]FLAG,...", "set interface flags", "\
668Configures the network interface flags, because `ifconfig' is prevented\n\
669from doing the job properly.\n\
4e3819cf 670" },
671
672 { "help", 0, cmd_help,
673 "help [COMMAND]", "display help about COMMAND", "\
674If COMMAND is given, display help about it. If no COMAMND is specified,\n\
675provide general help.\n\
676" },
677
678 { 0, 0, 0, 0 }
679};
680
681/* --- Protocol description table --- */
682
683static 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 "
699 "Architecture" },
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>" },
706 { 0, 0, 0 }
707};
708
709/*----- Informative messages ----------------------------------------------*/
710
711/* --- @usage@ --- *
712 *
713 * Arguments: @FILE *fp@ = stream to write on
714 *
715 * Returns: ---
716 *
717 * Use: Displays usage information for the program.
718 */
719
720static void usage(FILE *fp)
721{
722 fprintf(fp, "Usage: %s [-v] command ...\n", quis);
723}
724
725/* --- @version@ --- *
726 *
727 * Arguments: ---
728 *
729 * Returns: ---
730 *
731 * Use: Displays the system's version number.
732 */
733
734static void version(void)
735{
736 printf("%s version %s\n", quis, VERSION);
737}
738
739/* --- @help@ --- *
740 *
741 * Arguments: ---
742 *
743 * Returns: Doesn't
744 *
745 * Use: Displays some help and quits.
746 */
747
748static void help(void)
749{
750 struct command *c;
751 version();
752 putchar('\n');
753 usage(stdout);
754 putchar('\n');
755 puts("Commands provided:\n");
756 for (c = cmdtab; c->cmd; c++)
757 printf("%-30s%s\n", c->syn, c->shelp);
758 exit(0);
759}
760
761/* --- @main@ --- *
762 *
763 * Arguments: @int argc@ = number of command line arguments received
764 * @char *argv[]@ = pointers to the command line arguments
765 *
766 * Returns: Zero for success, nonzero for failure
767 *
768 * Use: Dumps and manipulates Usernet attachments.
769 */
770
771int main(int argc, char *argv[])
772{
773 /* --- Set the program name properly --- */
774
775 if (argc >= 1) {
776 if ((quis = strrchr(argv[0], '/')) == 0)
777 quis = argv[0];
778 else
779 quis++;
780 }
781
782 /* --- Now start parsing options --- */
783
784 for (;;) {
785 int i;
786
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' },
792 { 0, 0, 0, 0 }
793 };
794
7e365031 795 if ((i = getopt_long(argc, argv, "+hVv", opt, 0)) < 0)
4e3819cf 796 break;
797 switch (i) {
798 case 'h':
799 help();
800 break;
801 case 'U':
802 usage(stdout);
803 exit(0);
804 break;
805 case 'V':
806 version();
807 break;
808 case 'v':
809 flags |= f_verbose;
810 break;
811 default:
812 flags |= f_duff;
813 break;
814 }
815 }
816
817 if (flags & f_duff) {
818 usage(stderr);
819 printf("(Type `%s --help' for more information.)\n", quis);
820 exit(EXIT_FAILURE);
821 }
822
823 return (run(argv + optind));
824}
825
826/*----- That's all, folks -------------------------------------------------*/