chiark / gitweb /
Use correct directory for installation.
[unet] / unetcfg.c
CommitLineData
4e3819cf 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
62static const char *quis = "unetcfg"; /* My program's name */
63static int flags = 0; /* Various useful status flags */
64static const char *current = "<stdin>"; /* Current Usernet device */
65static 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
84static 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
106static 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
132static 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
171static 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
184struct 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
191static struct command cmdtab[];
192
193struct eth_proto {
194 const char *name; /* User-level protocol name */
195 unsigned short proto; /* Protocol number */
196 const char *desc; /* Readable description */
197};
198
199static struct eth_proto eth_prototab[];
200
201#define YNQ_YES 1
202#define YNQ_NO 0
203#define YNQ_QUERY (-1)
204
205static 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
219static 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
230static 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
240static 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
261static 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
281static 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
322static 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
405static 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
418static 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
443static 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
475static 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
506static 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
536static 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
558static struct command cmdtab[] = {
559
560 { "select", 1, cmd_select,
561 "[select] FILE", "select a Usernet device", "\
562Selects FILE as the current device. Each command acts only on the current\n\
563device. 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", "\
569Selects the file descriptor NUMBER as the current device. This allows\n\
570configuration of already existing transient attachments which would\n\
571otherwise be unnecessarily awkward.\n\
572" },
573
574 { "show", 0, cmd_show,
575 "show", "show status of device", "\
576Displays status information about the current device.\n\
577" },
578
579 { "ifname", 0, cmd_ifname,
580 "ifname", "print attached network interface name", "\
581Displays the name of the network interface attached to the current device.\n\
582This 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", "\
587Sets PROT as the current device's protocol. All packets received by the\n\
588device are stamped with this protocol tag. To see a list of currently\n\
589known protocols, use the `help' protocol.\n\
590" },
591
592 { "debug", 1, cmd_debug,
593 "debug on|off|query", "set attachment debugging state", "\
594Set the current debugging state for the attachment. When debugging is\n\
595enabled, all packets flowing through the attachment are logged, along with\n\
596a large number of other informative messages. Note: the logs generated\n\
597tend to be very large, so don't flood the interface with data while\n\
598debugging is enabled!\n\
599" },
600
601 { "gdebug", 1, cmd_gdebug,
602 "gdebug on|off|query", "set global debugging state", "\
603Set the global debugging state for Usernet. This controls the emission\n\
604emission of various non-attachment-specific message. Also, new\n\
605attachments 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", "\
610Configures the maximum number of interfaces allowed (actually, the highest\n\
611number of any interface).\n\
612" },
613
614 { "help", 0, cmd_help,
615 "help [COMMAND]", "display help about COMMAND", "\
616If COMMAND is given, display help about it. If no COMAMND is specified,\n\
617provide general help.\n\
618" },
619
620 { 0, 0, 0, 0 }
621};
622
623/* --- Protocol description table --- */
624
625static 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
662static 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
676static 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
690static 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
713int 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 -------------------------------------------------*/