Several new additions to net-tools. From Alexey's iproute2,
authorPhil Blundell <philb@gnu.org>
Sat, 9 Jan 1999 14:35:59 +0000 (14:35 +0000)
committerPhil Blundell <philb@gnu.org>
Sat, 9 Jan 1999 14:35:59 +0000 (14:35 +0000)
the `maddr' and `tunnel' routines are now included here.  Also
slattach and plipconfig, taken from the Debian netbase package.

17 files changed:
Makefile
README
include/interface.h [new file with mode: 0644]
include/sockets.h [new file with mode: 0644]
include/utils.h [new file with mode: 0644]
ipmaddr.c [new file with mode: 0644]
iptunnel.c [new file with mode: 0644]
lib/Makefile
lib/interface.c [moved from interface.c with 99% similarity]
lib/sockets.c [moved from sockets.c with 100% similarity]
lib/utils.c [new file with mode: 0644]
man/en_US/plipconfig.8 [new file with mode: 0644]
man/en_US/slattach.8 [new file with mode: 0644]
man/fr_FR/plipconfig.8 [new file with mode: 0644]
man/fr_FR/slattach.8 [new file with mode: 0644]
plipconfig.c [new file with mode: 0644]
slattach.c [new file with mode: 0644]

index 077170e..44dc31c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -76,7 +76,7 @@
 NET_LIB_PATH = lib
 NET_LIB_NAME = net-tools
 
-PROGS  = ifconfig hostname arp netstat route rarp 
+PROGS  = ifconfig hostname arp netstat route rarp slattach plipconfig iptunnel ipmaddr
 
 # Compiler and Linker Options
 # You may need to uncomment and edit these if you are using libc5 and IPv6.
@@ -164,8 +164,8 @@ i18ndir:
 subdirs:
                @for i in $(SUBDIRS); do $(MAKE) -C $$i $(MDEFINES) ; done
 
-ifconfig:      $(NET-LIB) ifconfig.o interface.o sockets.o
-               $(CC) $(LDFLAGS) -o ifconfig ifconfig.o interface.o sockets.o $(NLIB) $(RESLIB)
+ifconfig:      $(NET-LIB) ifconfig.o
+               $(CC) $(LDFLAGS) -o ifconfig ifconfig.o $(NLIB) $(RESLIB)
 
 hostname:      hostname.o
                $(CC) $(LDFLAGS) -o hostname hostname.o
@@ -179,8 +179,20 @@ arp:               $(NET-LIB) arp.o
 rarp:          $(NET-LIB) rarp.o
                $(CC) $(LDFLAGS) -o rarp rarp.o $(NLIB)
 
-netstat:       $(NET-LIB) netstat.o statistics.o interface.o sockets.o
-               $(CC) $(LDFLAGS) -o netstat netstat.o statistics.o interface.o sockets.o $(NLIB) $(RESLIB)
+slattach:      $(NET-LIB) slattach.o
+               $(CC) $(LDFLAGS) -o slattach slattach.o $(NLIB)
+
+plipconfig:    $(NET-LIB) plipconfig.o
+               $(CC) $(LDFLAGS) -o plipconfig plipconfig.o $(NLIB)
+
+netstat:       $(NET-LIB) netstat.o statistics.o
+               $(CC) $(LDFLAGS) -o netstat netstat.o statistics.o $(NLIB) $(RESLIB)
+
+iptunnel:      $(NET-LIB) iptunnel.o
+               $(CC) $(LDFLAGS) -o iptunnel iptunnel.o $(NLIB) $(RESLIB)
+
+ipmaddr:       $(NET-LIB) ipmaddr.o
+               $(CC) $(LDFLAGS) -o ipmaddr ipmaddr.o $(NLIB) $(RESLIB)
 
 installbin:
        install -m 0755 -d ${BASEDIR}/sbin
diff --git a/README b/README
index 525d9fa..2ffd1c3 100644 (file)
--- a/README
+++ b/README
@@ -34,6 +34,7 @@ This is net-tools 1.50.  Notable changes since 1.49 include:
    sockets.
  - netstat -s is improved.
  - route/netstat -r display the routing cache correctly.
+ - plipconfig and slattach are included in the distribution.
 
 Notable changes since 1.48 include:
 
@@ -94,8 +95,6 @@ patches if you have problems.
 
 Phil Blundell
 philb@gnu.org
-11th December 1998
 
 Bernd Eckenfels
 net-tools@lina.inka.de
-1999-01-02
diff --git a/include/interface.h b/include/interface.h
new file mode 100644 (file)
index 0000000..67076c6
--- /dev/null
@@ -0,0 +1,92 @@
+struct user_net_device_stats {
+    unsigned long rx_packets;  /* total packets received       */
+    unsigned long tx_packets;  /* total packets transmitted    */
+    unsigned long rx_bytes;    /* total bytes received         */
+    unsigned long tx_bytes;    /* total bytes transmitted      */
+    unsigned long rx_errors;   /* bad packets received         */
+    unsigned long tx_errors;   /* packet transmit problems     */
+    unsigned long rx_dropped;  /* no space in linux buffers    */
+    unsigned long tx_dropped;  /* no space available in linux  */
+    unsigned long rx_multicast;        /* multicast packets received   */
+    unsigned long rx_compressed;
+    unsigned long tx_compressed;
+    unsigned long collisions;
+
+    /* detailed rx_errors: */
+    unsigned long rx_length_errors;
+    unsigned long rx_over_errors;      /* receiver ring buff overflow  */
+    unsigned long rx_crc_errors;       /* recved pkt with crc error    */
+    unsigned long rx_frame_errors;     /* recv'd frame alignment error */
+    unsigned long rx_fifo_errors;      /* recv'r fifo overrun          */
+    unsigned long rx_missed_errors;    /* receiver missed packet     */
+    /* detailed tx_errors */
+    unsigned long tx_aborted_errors;
+    unsigned long tx_carrier_errors;
+    unsigned long tx_fifo_errors;
+    unsigned long tx_heartbeat_errors;
+    unsigned long tx_window_errors;
+};
+
+struct interface {
+    struct interface *next;
+
+    char name[IFNAMSIZ];       /* interface name        */
+    short type;                        /* if type               */
+    short flags;               /* various flags         */
+    int metric;                        /* routing metric        */
+    int mtu;                   /* MTU value             */
+    int tx_queue_len;          /* transmit queue length */
+    struct ifmap map;          /* hardware setup        */
+    struct sockaddr addr;      /* IP address            */
+    struct sockaddr dstaddr;   /* P-P IP address        */
+    struct sockaddr broadaddr; /* IP broadcast address  */
+    struct sockaddr netmask;   /* IP network mask       */
+    struct sockaddr ipxaddr_bb;        /* IPX network address   */
+    struct sockaddr ipxaddr_sn;        /* IPX network address   */
+    struct sockaddr ipxaddr_e3;        /* IPX network address   */
+    struct sockaddr ipxaddr_e2;        /* IPX network address   */
+    struct sockaddr ddpaddr;   /* Appletalk DDP address */
+    struct sockaddr ecaddr;    /* Econet address        */
+    int has_ip;
+    int has_ipx_bb;
+    int has_ipx_sn;
+    int has_ipx_e3;
+    int has_ipx_e2;
+    int has_ax25;
+    int has_ddp;
+    int has_econet;
+    char hwaddr[32];           /* HW address            */
+    int statistics_valid;
+    struct user_net_device_stats stats;                /* statistics            */
+    int keepalive;             /* keepalive value for SLIP */
+    int outfill;               /* outfill value for SLIP */
+};
+
+extern int if_fetch(struct interface *ife);
+
+extern int for_all_interfaces(int (*)(struct interface *, void *), void *);
+extern struct interface *lookup_interface(char *name);
+extern int if_readlist(void);
+
+extern int do_if_fetch(struct interface *ife);
+extern int do_if_print(struct interface *ife, void *cookie);
+
+extern void ife_print(struct interface *ptr);
+
+/* Defines for poor glibc2.0 users, the feature check is done at runtime */
+#if !defined(SIOCSIFTXQLEN)
+#define SIOCSIFTXQLEN      0x8943
+#define SIOCGIFTXQLEN      0x8942
+#endif
+
+#if !defined(ifr_qlen)
+/* Actually it is ifru_ivalue, but that is not present in 2.0 kernel headers */   
+#define ifr_qlen        ifr_ifru.ifru_mtu
+#endif
+
+#define HAVE_TXQUEUELEN
+
+#define HAVE_DYNAMIC
+#ifndef IFF_DYNAMIC
+#define IFF_DYNAMIC    0x8000  /* dialup device with changing addresses */
+#endif
diff --git a/include/sockets.h b/include/sockets.h
new file mode 100644 (file)
index 0000000..74baacb
--- /dev/null
@@ -0,0 +1,4 @@
+extern int skfd, ipx_sock, ax25_sock, rose_sock, inet_sock, inet6_sock,
+ ddp_sock, ec_sock;
+
+extern int sockets_open(int family);
diff --git a/include/utils.h b/include/utils.h
new file mode 100644 (file)
index 0000000..73f6bd1
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#if 0
+#include <asm/types.h>
+#include <asm/bitops.h>
+#include <linux/inetdevice.h>
+#include <resolv.h>
+#endif
+
+extern int preferred_family;
+extern int show_stats;
+extern int show_details;
+extern int show_raw;
+extern int resolve_hosts;
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP    50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH     51
+#endif
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x)  char x[SPRINT_BSIZE]
+
+
+#define NEXT_ARG() \
+argv++; \
+if (--argc <= 0) \
+       usage();
+
+typedef struct
+{
+       __u8 family;
+       __u8 bytelen;
+       __s16 bitlen;
+       __u32 data[4];
+} inet_prefix;
+
+extern __u32 get_addr32(char *name);
+extern int get_addr_1(inet_prefix *dst, char *arg, int family);
+extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
+extern int get_addr(inet_prefix *dst, char *arg, int family);
+extern int get_prefix(inet_prefix *dst, char *arg, int family);
+
+extern int scan_number(char *arg, unsigned *val);
+
+extern int get_integer(int *val, char *arg, int base);
+extern int get_unsigned(unsigned *val, char *arg, int base);
+#define get_byte get_u8
+#define get_ushort get_u16
+#define get_short get_s16
+extern int get_u32(__u32 *val, char *arg, int base);
+extern int get_u16(__u16 *val, char *arg, int base);
+extern int get_s16(__s16 *val, char *arg, int base);
+extern int get_u8(__u8 *val, char *arg, int base);
+extern int get_s8(__s8 *val, char *arg, int base);
+
+extern int get_tc_classid(__u32 *h, char *str);
+extern int print_tc_classid(char *buf, int len, __u32 h);
+extern char * sprint_tc_classid(__u32 h, char *buf);
+
+/* static void usage(void) __attribute__((noreturn)); */
+void invarg(char *) __attribute__((noreturn));
+int matches(char *arg, char *pattern);
+extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+
+extern int ipaddr_list(int argc, char **argv);
+extern int iproute_monitor(int argc, char **argv);
+extern int do_ipaddr(int argc, char **argv);
+extern int do_iproute(int argc, char **argv);
+extern int do_iprule(int argc, char **argv);
+extern int do_ipneigh(int argc, char **argv);
+extern int do_iptunnel(int argc, char **argv);
+extern int do_iplink(int argc, char **argv);
+extern int do_ipmonitor(int argc, char **argv);
+extern int do_multiaddr(int argc, char **argv);
+extern int do_qdisc(int argc, char **argv);
+extern int do_class(int argc, char **argv);
+extern int do_filter(int argc, char **argv);
+
+extern const char *format_host(int af, void *addr, __u8 *abuf, int alen);
+
+#endif /* __UTILS_H__ */
diff --git a/ipmaddr.c b/ipmaddr.c
new file mode 100644 (file)
index 0000000..5edf1ac
--- /dev/null
+++ b/ipmaddr.c
@@ -0,0 +1,403 @@
+/*
+ * ipmaddr.c           "ip maddress".
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+
+char filter_dev[16];
+int  filter_family;
+
+/* These have nothing to do with rtnetlink. :-) */
+#define NEWADDR                1
+#define DELADDR                2
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+       fprintf(stderr, "Usage: ipmaddr [ add | del ] MULTIADDR dev STRING\n");
+       fprintf(stderr, "       ipmaddr show [ dev STRING ] [ ipv4 | ipv6 | link | all ]\n");
+       exit(-1);
+}
+
+static void print_lla(FILE *fp, int len, unsigned char *addr)
+{
+       int i;
+       for (i=0; i<len; i++) {
+               if (i==0)
+                       fprintf(fp, "%02x", addr[i]);
+               else
+                       fprintf(fp, ":%02x", addr[i]);
+       }
+}
+
+static int parse_lla(char *str, unsigned char *addr)
+{
+       int len=0;
+
+       while (*str) {
+               int tmp;
+               if (str[0] == ':' || str[0] == '.') {
+                       str++;
+                       continue;
+               }
+               if (str[1] == 0)
+                       return -1;
+               if (sscanf(str, "%02x", &tmp) != 1)
+                       return -1;
+               addr[len] = tmp;
+               len++;
+               str += 2;
+       }
+       return len;
+}
+
+static int parse_hex(char *str, unsigned char *addr)
+{
+       int len=0;
+
+       while (*str) {
+               int tmp;
+               if (str[1] == 0)
+                       return -1;
+               if (sscanf(str, "%02x", &tmp) != 1)
+                       return -1;
+               addr[len] = tmp;
+               len++;
+               str += 2;
+       }
+       return len;
+}
+
+struct ma_info
+{
+       struct ma_info *next;
+       int             index;
+       int             users;
+       char            *features;
+       char            name[IFNAMSIZ];
+       inet_prefix     addr;
+};
+
+void maddr_ins(struct ma_info **lst, struct ma_info *m)
+{
+       struct ma_info *mp;
+
+       for (; (mp=*lst) != NULL; lst = &mp->next) {
+               if (mp->index > m->index)
+                       break;
+       }
+       m->next = *lst;
+       *lst = m;
+}
+
+void read_dev_mcast(struct ma_info **result_p)
+{
+       char buf[256];
+       FILE *fp = fopen("/proc/net/dev_mcast", "r");
+
+       if (!fp)
+               return;
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               char hexa[256];
+               struct ma_info m;
+               int len;
+               int st;
+
+               memset(&m, 0, sizeof(m));
+               sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st,
+                      hexa);
+               if (filter_dev[0] && strcmp(filter_dev, m.name))
+                       continue;
+
+               m.addr.family = AF_PACKET;
+
+               len = parse_hex(hexa, (unsigned char*)&m.addr.data);
+               if (len >= 0) {
+                       struct ma_info *ma = malloc(sizeof(m));
+
+                       memcpy(ma, &m, sizeof(m));
+                       ma->addr.bytelen = len;
+                       ma->addr.bitlen = len<<3;
+                       if (st)
+                               ma->features = "static";
+                       maddr_ins(result_p, ma);
+               }
+       }
+       fclose(fp);
+}
+
+void read_igmp(struct ma_info **result_p)
+{
+       struct ma_info m;
+       char buf[256];
+       FILE *fp = fopen("/proc/net/igmp", "r");
+
+       if (!fp)
+               return;
+       memset(&m, 0, sizeof(m));
+       fgets(buf, sizeof(buf), fp);
+
+       m.addr.family = AF_INET;
+       m.addr.bitlen = 32;
+       m.addr.bytelen = 4;
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               struct ma_info *ma = malloc(sizeof(m));
+
+               if (buf[0] != '\t') {
+                       sscanf(buf, "%d%s", &m.index, m.name);
+                       continue;
+               }
+
+               if (filter_dev[0] && strcmp(filter_dev, m.name))
+                       continue;
+
+               sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users);
+
+               ma = malloc(sizeof(m));
+               memcpy(ma, &m, sizeof(m));
+               maddr_ins(result_p, ma);
+       }
+       fclose(fp);
+}
+
+
+void read_igmp6(struct ma_info **result_p)
+{
+       char buf[256];
+       FILE *fp = fopen("/proc/net/igmp6", "r");
+
+       if (!fp)
+               return;
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               char hexa[256];
+               struct ma_info m;
+               int len;
+
+               memset(&m, 0, sizeof(m));
+               sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users);
+
+               if (filter_dev[0] && strcmp(filter_dev, m.name))
+                       continue;
+
+               m.addr.family = AF_INET6;
+
+               len = parse_hex(hexa, (unsigned char*)&m.addr.data);
+               if (len >= 0) {
+                       struct ma_info *ma = malloc(sizeof(m));
+
+                       memcpy(ma, &m, sizeof(m));
+
+                       ma->addr.bytelen = len;
+                       ma->addr.bitlen = len<<3;
+                       maddr_ins(result_p, ma);
+               }
+       }
+       fclose(fp);
+}
+
+static void print_maddr(FILE *fp, struct ma_info *list)
+{
+       fprintf(fp, "\t");
+
+       if (list->addr.family == AF_PACKET) {
+               fprintf(fp, "link  ");
+               print_lla(fp, list->addr.bytelen, (unsigned char*)list->addr.data);
+       } else {
+               char abuf[256];
+               switch(list->addr.family) {
+               case AF_INET:
+                       fprintf(fp, "inet  ");
+                       break;
+               case AF_INET6:
+                       fprintf(fp, "inet6 ");
+                       break;
+               default:
+                       fprintf(fp, "family %d ", list->addr.family);
+                       break;
+               }
+               if (format_host(list->addr.family, list->addr.data, abuf, sizeof(abuf)))
+                       fprintf(fp, "%s", abuf);
+               else
+                       fprintf(fp, "?");
+       }
+       if (list->users != 1)
+               fprintf(fp, " users %d", list->users);
+       if (list->features)
+               fprintf(fp, " %s", list->features);
+       fprintf(fp, "\n");
+}
+
+static void print_mlist(FILE *fp, struct ma_info *list)
+{
+       int cur_index = 0;
+
+       for (; list; list = list->next) {
+               if (cur_index != list->index) {
+                       cur_index = list->index;
+                       fprintf(fp, "%d:\t%s\n", cur_index, list->name);
+               }
+               print_maddr(fp, list);
+       }
+}
+
+static int multiaddr_list(int argc, char **argv)
+{
+       struct ma_info *list = NULL;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       if (filter_dev[0])
+                               usage();
+                       strcpy(filter_dev, *argv);
+               } else if (strcmp(*argv, "all") == 0) {
+                       filter_family = AF_UNSPEC;
+               } else if (strcmp(*argv, "ipv4") == 0) {
+                       filter_family = AF_INET;
+               } else if (strcmp(*argv, "ipv6") == 0) {
+                       filter_family = AF_INET6;
+               } else if (strcmp(*argv, "link") == 0) {
+                       filter_family = AF_PACKET;
+               } else {
+                       if (filter_dev[0])
+                               usage();
+                       strcpy(filter_dev, *argv);
+               }
+               argv++; argc--;
+       }
+
+       if (!filter_family || filter_family == AF_PACKET)
+               read_dev_mcast(&list);
+       if (!filter_family || filter_family == AF_INET)
+               read_igmp(&list);
+       if (!filter_family || filter_family == AF_INET6)
+               read_igmp6(&list);
+       print_mlist(stdout, list);
+       return 0;
+}
+
+int multiaddr_modify(int cmd, int argc, char **argv)
+{
+       struct ifreq ifr;
+       int fd;
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       if (cmd == NEWADDR)
+               cmd = SIOCADDMULTI;
+       else
+               cmd = SIOCDELMULTI;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       if (ifr.ifr_name[0])
+                               usage();
+                       strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+               } else {
+                       if (ifr.ifr_hwaddr.sa_data[0])
+                               usage();
+                       if (parse_lla(*argv, ifr.ifr_hwaddr.sa_data) < 0)
+                               usage();
+               }
+               argc--; argv++;
+       }
+       if (ifr.ifr_name[0] == 0)
+               usage();
+
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               perror("Cannot create socket");
+               exit(1);
+       }
+       if (ioctl(fd, cmd, (char*)&ifr) != 0) {
+               perror("ioctl");
+               exit(1);
+       }
+       close(fd);
+
+       exit(0);
+}
+
+
+int do_multiaddr(int argc, char **argv)
+{
+       if (argc < 1)
+               return multiaddr_list(0, NULL);
+       if (matches(*argv, "add") == 0)
+               return multiaddr_modify(NEWADDR, argc-1, argv+1);
+       if (matches(*argv, "delete") == 0)
+               return multiaddr_modify(DELADDR, argc-1, argv+1);
+       if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+           || matches(*argv, "lst") == 0)
+               return multiaddr_list(argc-1, argv+1);
+       usage();
+}
+
+int preferred_family = AF_UNSPEC;
+int show_stats = 0;
+int resolve_hosts = 0;
+
+int main(int argc, char **argv)
+{
+       char *basename;
+
+       basename = strrchr(argv[0], '/');
+       if (basename == NULL)
+               basename = argv[0];
+       else
+               basename++;
+       
+       while (argc > 1) {
+               if (argv[1][0] != '-')
+                       break;
+               if (matches(argv[1], "-family") == 0) {
+                       argc--;
+                       argv++;
+                       if (argc <= 1)
+                               usage();
+                       if (strcmp(argv[1], "inet") == 0)
+                               preferred_family = AF_INET;
+                       else if (strcmp(argv[1], "inet6") == 0)
+                               preferred_family = AF_INET6;
+                       else
+                               usage();
+               } else if (matches(argv[1], "-stats") == 0 ||
+                          matches(argv[1], "-statistics") == 0) {
+                       ++show_stats;
+               } else if (matches(argv[1], "-resolve") == 0) {
+                       ++resolve_hosts;
+               } else
+                       usage();
+               argc--; argv++;
+       }
+
+       return do_multiaddr(argc-1, argv+1);
+}
diff --git a/iptunnel.c b/iptunnel.c
new file mode 100644 (file)
index 0000000..254e873
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * iptunnel.c         "ip tunnel"
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980930:        do not allow key for ipip/sit
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <linux/if_tunnel.h>
+
+#undef GRE_CSUM
+#define GRE_CSUM       htons(0x8000)
+#undef GRE_ROUTING
+#define GRE_ROUTING    htons(0x4000)
+#undef GRE_KEY
+#define GRE_KEY                htons(0x2000)
+#undef GRE_SEQ
+#define GRE_SEQ                htons(0x1000)
+#undef GRE_STRICT
+#define GRE_STRICT     htons(0x0800)
+#undef GRE_REC
+#define GRE_REC                htons(0x0700)
+#undef GRE_FLAGS
+#define GRE_FLAGS      htons(0x00F8)
+#undef GRE_VERSION
+#define GRE_VERSION    htons(0x0007)
+
+#include "utils.h"
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+       fprintf(stderr, "Usage: iptunnel { add | change | del | show } [ NAME ]\n");
+       fprintf(stderr, "          [ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n");
+       fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+       fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ nopmtudisc ] [ dev PHYS_DEV ]\n");
+       fprintf(stderr, "\n");
+       fprintf(stderr, "Where: NAME := STRING\n");
+       fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+       fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+       fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+       fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
+       exit(-1);
+}
+
+static int do_ioctl_get_ifindex(char *dev)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, dev);
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCGIFINDEX, &ifr);
+       if (err) {
+               perror("ioctl");
+               return 0;
+       }
+       close(fd);
+       return ifr.ifr_ifindex;
+}
+
+static int do_ioctl_get_iftype(char *dev)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, dev);
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCGIFHWADDR, &ifr);
+       if (err) {
+               perror("ioctl");
+               return -1;
+       }
+       close(fd);
+       return ifr.ifr_addr.sa_family;
+}
+
+
+static char * do_ioctl_get_ifname(int idx)
+{
+       static struct ifreq ifr;
+       int fd;
+       int err;
+
+       ifr.ifr_ifindex = idx;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCGIFNAME, &ifr);
+       if (err) {
+               perror("ioctl");
+               return NULL;
+       }
+       close(fd);
+       return ifr.ifr_name;
+}
+
+
+
+static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, basedev);
+       ifr.ifr_ifru.ifru_data = (void*)p;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCGETTUNNEL, &ifr);
+       if (err)
+               perror("ioctl");
+       close(fd);
+       return err;
+}
+
+static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, basedev);
+       ifr.ifr_ifru.ifru_data = (void*)p;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, cmd, &ifr);
+       if (err)
+               perror("ioctl");
+       close(fd);
+       return err;
+}
+
+static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, basedev);
+       ifr.ifr_ifru.ifru_data = (void*)p;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCDELTUNNEL, &ifr);
+       if (err)
+               perror("ioctl");
+       close(fd);
+       return err;
+}
+
+static int parse_args(int argc, char **argv, struct ip_tunnel_parm *p)
+{
+       char medium[IFNAMSIZ];
+
+       memset(p, 0, sizeof(*p));
+       memset(&medium, 0, sizeof(medium));
+
+       p->iph.version = 4;
+       p->iph.ihl = 5;
+#ifndef IP_DF
+#define IP_DF          0x4000          /* Flag: "Don't Fragment"       */
+#endif
+       p->iph.frag_off = htons(IP_DF);
+
+       while (argc > 0) {
+               if (strcmp(*argv, "mode") == 0) {
+                       NEXT_ARG();
+                       if (strcmp(*argv, "ipip") == 0) {
+                               if (p->iph.protocol)
+                                       usage();
+                               p->iph.protocol = IPPROTO_IPIP;
+                       } else if (strcmp(*argv, "gre") == 0) {
+                               if (p->iph.protocol)
+                                       usage();
+                               p->iph.protocol = IPPROTO_GRE;
+                       } else if (strcmp(*argv, "sit") == 0) {
+                               if (p->iph.protocol)
+                                       usage();
+                               p->iph.protocol = IPPROTO_IPV6;
+                       } else
+                               usage();
+               } else if (strcmp(*argv, "key") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       p->i_flags |= GRE_KEY;
+                       p->o_flags |= GRE_KEY;
+                       if (strchr(*argv, '.'))
+                               p->i_key = p->o_key = get_addr32(*argv);
+                       else {
+                               if (scan_number(*argv, &uval)<0)
+                                       usage();
+                               p->i_key = p->o_key = htonl(uval);
+                       }
+               } else if (strcmp(*argv, "ikey") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       p->i_flags |= GRE_KEY;
+                       if (strchr(*argv, '.'))
+                               p->o_key = get_addr32(*argv);
+                       else {
+                               if (scan_number(*argv, &uval)<0)
+                                       usage();
+                               p->i_key = htonl(uval);
+                       }
+               } else if (strcmp(*argv, "okey") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       p->o_flags |= GRE_KEY;
+                       if (strchr(*argv, '.'))
+                               p->o_key = get_addr32(*argv);
+                       else {
+                               if (scan_number(*argv, &uval)<0)
+                                       usage();
+                               p->o_key = htonl(uval);
+                       }
+               } else if (strcmp(*argv, "seq") == 0) {
+                       p->i_flags |= GRE_SEQ;
+                       p->o_flags |= GRE_SEQ;
+               } else if (strcmp(*argv, "iseq") == 0) {
+                       p->i_flags |= GRE_SEQ;
+               } else if (strcmp(*argv, "oseq") == 0) {
+                       p->o_flags |= GRE_SEQ;
+               } else if (strcmp(*argv, "csum") == 0) {
+                       p->i_flags |= GRE_CSUM;
+                       p->o_flags |= GRE_CSUM;
+               } else if (strcmp(*argv, "icsum") == 0) {
+                       p->i_flags |= GRE_CSUM;
+               } else if (strcmp(*argv, "ocsum") == 0) {
+                       p->o_flags |= GRE_CSUM;
+               } else if (strcmp(*argv, "nopmtudisc") == 0) {
+                       p->iph.frag_off = 0;
+               } else if (strcmp(*argv, "remote") == 0) {
+                       NEXT_ARG();
+                       if (strcmp(*argv, "any"))
+                               p->iph.daddr = get_addr32(*argv);
+               } else if (strcmp(*argv, "local") == 0) {
+                       NEXT_ARG();
+                       if (strcmp(*argv, "any"))
+                               p->iph.saddr = get_addr32(*argv);
+               } else if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       strncpy(medium, *argv, IFNAMSIZ-1);
+               } else if (strcmp(*argv, "ttl") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "inherit") != 0) {
+                               if (scan_number(*argv, &uval)<0)
+                                       usage();
+                               if (uval > 255)
+                                       usage();
+                               p->iph.ttl = uval;
+                       }
+               } else if (strcmp(*argv, "tos") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "inherit") != 0) {
+                               if (scan_number(*argv, &uval)<0)
+                                       usage();
+                               if (uval > 255)
+                                       usage();
+                               p->iph.tos = uval;
+                       } else
+                               p->iph.tos = 1;
+               } else {
+                       if (p->name[0])
+                               usage();
+                       strncpy(p->name, *argv, IFNAMSIZ);
+               }
+               argc--; argv++;
+       }
+
+       if (p->iph.protocol == 0) {
+               if (memcmp(p->name, "gre", 3) == 0)
+                       p->iph.protocol = IPPROTO_GRE;
+               else if (memcmp(p->name, "ipip", 4) == 0)
+                       p->iph.protocol = IPPROTO_IPIP;
+               else if (memcmp(p->name, "sit", 3) == 0)
+                       p->iph.protocol = IPPROTO_IPV6;
+       }
+
+       if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
+               if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
+                       fprintf(stderr, "Keys are not allowed with ipip and sit.\n");
+                       return -1;
+               }
+       }
+
+       if (medium[0]) {
+               p->link = do_ioctl_get_ifindex(medium);
+               if (p->link == 0)
+                       return -1;
+       }
+
+       if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+               p->i_key = p->iph.daddr;
+               p->i_flags |= GRE_KEY;
+       }
+       if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+               p->o_key = p->iph.daddr;
+               p->o_flags |= GRE_KEY;
+       }
+       if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
+               fprintf(stderr, "Broadcast tunnel requires a source address.\n");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int do_add(int cmd, int argc, char **argv)
+{
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, &p) < 0)
+               return -1;
+
+       if (p.iph.ttl && p.iph.frag_off == 0) {
+               fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
+               return -1;
+       }
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:
+               return do_add_ioctl(cmd, "tunl0", &p);
+       case IPPROTO_GRE:
+               return do_add_ioctl(cmd, "gre0", &p);
+       case IPPROTO_IPV6:
+               return do_add_ioctl(cmd, "sit0", &p);
+       default:        
+               fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n");
+               return -1;
+       }
+       return -1;
+}
+
+int do_del(int argc, char **argv)
+{
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, &p) < 0)
+               return -1;
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:      
+               return do_del_ioctl(p.name[0] ? p.name : "tunl0", &p);
+       case IPPROTO_GRE:       
+               return do_del_ioctl(p.name[0] ? p.name : "gre0", &p);
+       case IPPROTO_IPV6:      
+               return do_del_ioctl(p.name[0] ? p.name : "sit0", &p);
+       default:        
+               return do_del_ioctl(p.name, &p);
+       }
+       return -1;
+}
+
+void print_tunnel(struct ip_tunnel_parm *p)
+{
+       char s1[256];
+       char s2[256];
+       char s3[64];
+       char s4[64];
+
+       format_host(AF_INET, &p->iph.daddr, s1, sizeof(s1));
+       format_host(AF_INET, &p->iph.saddr, s2, sizeof(s2));
+       inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+       inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+
+       printf("%s: %s/ip  remote %s  local %s ",
+              p->name,
+              p->iph.protocol == IPPROTO_IPIP ? "ip" :
+              (p->iph.protocol == IPPROTO_GRE ? "gre" :
+               (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
+              p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+       if (p->link) {
+               char *n = do_ioctl_get_ifname(p->link);
+               if (n)
+                       printf(" dev %s ", n);
+       }
+       if (p->iph.ttl)
+               printf(" ttl %d ", p->iph.ttl);
+       else
+               printf(" ttl inherit ");
+       if (p->iph.tos) {
+               printf(" tos");
+               if (p->iph.tos&1)
+                       printf(" inherit");
+               if (p->iph.tos&~1)
+                       printf("%c%02x ", p->iph.tos&1 ? '/' : ' ', p->iph.tos&~1);
+       }
+       if (!(p->iph.frag_off&htons(IP_DF)))
+               printf(" nopmtudisc");
+
+       if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
+               printf(" key %s", s3);
+       else if ((p->i_flags|p->o_flags)&GRE_KEY) {
+               if (p->i_flags&GRE_KEY)
+                       printf(" ikey %s ", s3);
+               if (p->o_flags&GRE_KEY)
+                       printf(" okey %s ", s4);
+       }
+       printf("\n");
+
+       if (p->i_flags&GRE_SEQ)
+               printf("  Drop packets out of sequence.\n");
+       if (p->i_flags&GRE_CSUM)
+               printf("  Checksum in received packet is required.\n");
+       if (p->o_flags&GRE_SEQ)
+               printf("  Sequence packets on output.\n");
+       if (p->o_flags&GRE_CSUM)
+               printf("  Checksum output packets.\n");
+}
+
+static int do_tunnels_list(struct ip_tunnel_parm *p)
+{
+       char name[IFNAMSIZ];
+       unsigned long  rx_bytes, rx_packets, rx_errs, rx_drops,
+       rx_fifo, rx_frame,
+       tx_bytes, tx_packets, tx_errs, tx_drops,
+       tx_fifo, tx_colls, tx_carrier, rx_multi;
+       int type;
+       struct ip_tunnel_parm p1;
+
+       char buf[512];
+       FILE *fp = fopen("/proc/net/dev", "r");
+       if (fp == NULL) {
+               perror("fopen");
+               return -1;
+       }
+
+       fgets(buf, sizeof(buf), fp);
+       fgets(buf, sizeof(buf), fp);
+
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               char *ptr;
+               buf[sizeof(buf) - 1] = 0;
+               if ((ptr = strchr(buf, ':')) == NULL ||
+                   (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+                       fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n");
+                       return -1;
+               }
+               if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
+                          &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+                          &rx_fifo, &rx_frame, &rx_multi,
+                          &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+                          &tx_fifo, &tx_colls, &tx_carrier) != 14)
+                       continue;
+               if (p->name[0] && strcmp(p->name, name))
+                       continue;
+               type = do_ioctl_get_iftype(name);
+               if (type == -1) {
+                       fprintf(stderr, "Failed to get type of [%s]\n", name);
+                       continue;
+               }
+               if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+                       continue;
+               memset(&p1, 0, sizeof(p1));
+               if (do_get_ioctl(name, &p1))
+                       continue;
+               if ((p->link && p1.link != p->link) ||
+                   (p->name[0] && strcmp(p1.name, p->name)) ||
+                   (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+                   (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+                   (p->i_key && p1.i_key != p->i_key))
+                       continue;
+               print_tunnel(&p1);
+               if (show_stats) {
+                       printf("RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts\n");
+                       printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld\n",
+                              rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi);
+                       printf("TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs\n");
+                       printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld\n\n",
+                              tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
+               }
+       }
+       return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+       int err;
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, &p) < 0)
+               return -1;
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:      
+               err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+               break;
+       case IPPROTO_GRE:
+               err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+               break;
+       case IPPROTO_IPV6:
+               err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
+               break;
+       default:
+               do_tunnels_list(&p);
+               return 0;
+       }
+       if (err)
+               return -1;
+
+       print_tunnel(&p);
+       return 0;
+}
+
+int do_iptunnel(int argc, char **argv)
+{
+       if (argc > 0) {
+               if (matches(*argv, "add") == 0)
+                       return do_add(SIOCADDTUNNEL, argc-1, argv+1);
+               if (matches(*argv, "change") == 0)
+                       return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
+               if (matches(*argv, "del") == 0)
+                       return do_del(argc-1, argv+1);
+               if (matches(*argv, "show") == 0 ||
+                   matches(*argv, "lst") == 0 ||
+                   matches(*argv, "list") == 0)
+                       return do_show(argc-1, argv+1);
+       } else
+               return do_show(0, NULL);
+
+       usage();
+}
+
+
+int preferred_family = AF_UNSPEC;
+int show_stats = 0;
+int resolve_hosts = 0;
+
+int main(int argc, char **argv)
+{
+       char *basename;
+
+       basename = strrchr(argv[0], '/');
+       if (basename == NULL)
+               basename = argv[0];
+       else
+               basename++;
+       
+       while (argc > 1) {
+               if (argv[1][0] != '-')
+                       break;
+               if (matches(argv[1], "-family") == 0) {
+                       argc--;
+                       argv++;
+                       if (argc <= 1)
+                               usage();
+                       if (strcmp(argv[1], "inet") == 0)
+                               preferred_family = AF_INET;
+                       else if (strcmp(argv[1], "inet6") == 0)
+                               preferred_family = AF_INET6;
+                       else
+                               usage();
+               } else if (matches(argv[1], "-stats") == 0 ||
+                          matches(argv[1], "-statistics") == 0) {
+                       ++show_stats;
+               } else if (matches(argv[1], "-resolve") == 0) {
+                       ++resolve_hosts;
+               } else
+                       usage();
+               argc--; argv++;
+       }
+
+       return do_iptunnel(argc-1, argv+1);
+}
index 7afb61c..607e0b5 100644 (file)
@@ -21,7 +21,7 @@ AFOBJS         = unix.o inet.o inet6.o ax25.o ipx.o ddp.o ipx.o netrom.o af.o rose.o ec
 AFGROBJS = inet_gr.o inet6_gr.o ipx_gr.o ddp_gr.o netrom_gr.o ax25_gr.o rose_gr.o getroute.o
 AFSROBJS = inet_sr.o inet6_sr.o netrom_sr.o ipx_sr.o setroute.o
 ACTOBJS  = slip_ac.o ppp_ac.o activate.o
-VARIA   = getargs.o masq_info.o proc.o util.o nstrcmp.o
+VARIA   = getargs.o masq_info.o proc.o util.o nstrcmp.o interface.o sockets.o utils.o
 
 OBJS   = $(sort $(VARIA) $(AFOBJS) $(HWOBJS) \
                $(AFGROBJS) $(AFSROBJS) $(ACTOBJS))
similarity index 99%
rename from interface.c
rename to lib/interface.c
index 18733a3..ad0037f 100644 (file)
@@ -4,7 +4,7 @@
    10/1998 partly rewriten by Andi Kleen to support an interface list.   
    I don't claim that the list operations are efficient @).  
 
-   $Id: interface.c,v 1.15 1998/12/01 09:30:06 philip Exp $
+   $Id: interface.c,v 1.1 1999/01/09 14:36:36 philip Exp $
  */
 
 #include "config.h"
similarity index 100%
rename from sockets.c
rename to lib/sockets.c
diff --git a/lib/utils.c b/lib/utils.c
new file mode 100644 (file)
index 0000000..c0be22d
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * utils.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <linux/pkt_sched.h>
+
+#include "utils.h"
+
+int scan_number(char *arg, unsigned *val)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, 0);
+       if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_integer(int *val, char *arg, int base)
+{
+       long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtol(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_unsigned(unsigned *val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_u32(__u32 *val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_u16(__u16 *val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_u8(__u8 *val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0xFF)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_s16(__s16 *val, char *arg, int base)
+{
+       long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtol(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_s8(__s8 *val, char *arg, int base)
+{
+       long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtol(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_tc_classid(__u32 *h, char *str)
+{
+       __u32 maj, min;
+       char *p;
+
+       maj = TC_H_ROOT;
+       if (strcmp(str, "root") == 0)
+               goto ok;
+       maj = TC_H_UNSPEC;
+       if (strcmp(str, "none") == 0)
+               goto ok;
+       maj = strtoul(str, &p, 16);
+       if (p == str) {
+               maj = 0;
+               if (*p != ':')
+                       return -1;
+       }
+       if (*p == ':') {
+               maj <<= 16;
+               str = p+1;
+               min = strtoul(str, &p, 16);
+               if (*p != 0)
+                       return -1;
+               maj |= min;
+       } else if (*p != 0)
+               return -1;
+
+ok:
+       *h = maj;
+       return 0;
+}
+
+int print_tc_classid(char *buf, int len, __u32 h)
+{
+       if (h == TC_H_ROOT)
+               sprintf(buf, "root");
+       else if (h == TC_H_UNSPEC)
+               snprintf(buf, len, "none");
+       else if (TC_H_MAJ(h) == 0)
+               snprintf(buf, len, ":%x", TC_H_MIN(h));
+       else if (TC_H_MIN(h) == 0)
+               snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16);
+       else
+               snprintf(buf, len, "%x:%x", TC_H_MAJ(h)>>16, TC_H_MIN(h));
+       return 0;
+}
+
+char * sprint_tc_classid(__u32 h, char *buf)
+{
+       if (print_tc_classid(buf, SPRINT_BSIZE-1, h))
+               strcpy(buf, "???");
+       return buf;
+}
+
+
+int get_addr_1(inet_prefix *addr, char *name, int family)
+{
+       char *cp;
+       unsigned char *ap = (unsigned char*)addr->data;
+       int i;
+
+       memset(addr, 0, sizeof(*addr));
+
+       if (strcmp(name, "default") == 0 || strcmp(name, "any") == 0) {
+               addr->family = family;
+               addr->bytelen = (family == AF_INET6 ? 16 : 4);
+               addr->bitlen = -1;
+               return 0;
+       }
+
+       if (strchr(name, ':')) {
+               addr->family = AF_INET6;
+               if (family != AF_UNSPEC && family != AF_INET6)
+                       return -1;
+               if (inet_pton(AF_INET6, name, addr->data) <= 0)
+                       return -1;
+               addr->bytelen = 16;
+               addr->bitlen = -1;
+               return 0;
+       }
+
+       addr->family = AF_INET;
+       if (family != AF_UNSPEC && family != AF_INET)
+               return -1;
+       addr->bytelen = 4;
+       addr->bitlen = -1;
+       for (cp=name, i=0; *cp; cp++) {
+               if (*cp <= '9' && *cp >= '0') {
+                       ap[i] = 10*ap[i] + (*cp-'0');
+                       continue;
+               }
+               if (*cp == '.' && ++i <= 3)
+                       continue;
+               return -1;
+       }
+       return 0;
+}
+
+int get_prefix_1(inet_prefix *dst, char *arg, int family)
+{
+       int err;
+       unsigned plen;
+       char *slash;
+
+       memset(dst, 0, sizeof(*dst));
+
+       if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) {
+               dst->family = family;
+               dst->bytelen = 0;
+               dst->bitlen = 0;
+               return 0;
+       }
+
+       slash = strchr(arg, '/');
+       if (slash)
+               *slash = 0;
+       err = get_addr_1(dst, arg, family);
+       if (err == 0) {
+               dst->bitlen = (dst->family == AF_INET6 ? 128 : 32);
+               if (slash) {
+                       if (scan_number(slash+1, &plen) || plen > dst->bitlen) {
+                               err = -1;
+                               goto done;
+                       }
+                       dst->bitlen = plen;
+               }
+       }
+done:
+       if (slash)
+               *slash = '/';
+       return err;
+}
+
+int get_addr(inet_prefix *dst, char *arg, int family)
+{
+       if (get_addr_1(dst, arg, family)) {
+               fprintf(stderr, "ip: %s is invalid inet address\n", arg);
+               exit(1);
+       }
+       return 0;
+}
+
+int get_prefix(inet_prefix *dst, char *arg, int family)
+{
+       if (get_prefix_1(dst, arg, family)) {
+               fprintf(stderr, "ip: %s is invalid inet prefix\n", arg);
+               exit(1);
+       }
+       return 0;
+}
+
+__u32 get_addr32(char *name)
+{
+       inet_prefix addr;
+       if (get_addr_1(&addr, name, AF_INET)) {
+               fprintf(stderr, "ip: %s is invalid IPv4 address\n", name);
+               exit(1);
+       }
+       return addr.data[0];
+}
+
+void invarg(char *msg)
+{
+       fprintf(stderr, "ip: argument is wrong: %s\n", msg);
+       exit(1);
+}
+
+int matches(char *cmd, char *pattern)
+{
+       int len = strlen(cmd);
+       if (len > strlen(pattern))
+               return -1;
+       return memcmp(pattern, cmd, len);
+}
+
+int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits)
+{
+       __u32 *a1 = a->data;
+       __u32 *a2 = b->data;
+       int words = bits >> 0x05;
+
+       bits &= 0x1f;
+
+       if (words)
+               if (memcmp(a1, a2, words << 2))
+                       return -1;
+
+       if (bits) {
+               __u32 w1, w2;
+               __u32 mask;
+
+               w1 = a1[words];
+               w2 = a2[words];
+
+               mask = htonl((0xffffffff) << (0x20 - bits));
+
+               if ((w1 ^ w2) & mask)
+                       return 1;
+       }
+
+       return 0;
+}
+
+const char *format_host(int af, void *addr, __u8 *abuf, int alen)
+{
+#ifdef RESOLVE_HOSTNAMES
+       if (resolve_hosts) {
+               int addrlen = 0;
+               struct hostent *h_ent;
+               switch (af) {
+               case AF_INET:
+                       addrlen = 4;
+                       break;
+               case AF_INET6:
+                       addrlen = 16;
+                       break;
+               }
+               if (addrlen &&
+                   (h_ent = gethostbyaddr(addr, addrlen, af)) != NULL) {
+                       snprintf(abuf, alen-1, "%s", h_ent->h_name);
+                       return abuf;
+               }
+       }
+#endif
+       return inet_ntop(af, addr, abuf, alen);
+}
diff --git a/man/en_US/plipconfig.8 b/man/en_US/plipconfig.8
new file mode 100644 (file)
index 0000000..c6d2567
--- /dev/null
@@ -0,0 +1,45 @@
+.TH PLIPCONFIG 8 "17 February 1995" "" ""
+.SH NAME
+plipconfig \- fine tune PLIP device parameters 
+.SH SYNOPSIS
+.B "plipconfig interface"
+.br
+.B "plipconfig interface [nibble NN] [trigger NN] [unit NN]"
+.SH DESCRIPTION
+.B Plipconfig
+is used to (hopefully) improve PLIP performance by changing the default
+timing parameters used by the PLIP protocol. Results are dependent on
+the parallel port hardware, cable, and the CPU speed of each machine
+on each end of the PLIP link.
+.LP
+If the single 
+.B interface
+argument is given,
+.B plipconfig
+displays the status of the given interface
+only.  Otherwise, it will try to set the options.
+.SH OPTIONS
+.TP
+.B "nibble NN"
+Sets the nibble wait value in microseconds. Default is 3000.
+.TP
+.B "trigger NN"
+Sets the trigger wait value in microseconds. Default is 500.
+.LP
+PLIP speed can in some cases be improved by lowering the default values.
+Values which are too low may cause excess use of CPU, poor interrupt 
+response time resulting in serial ports dropping characters, or in dropping
+of PLIP packets. Changing the plip MTU can also affect PLIP speed.
+.SH NOTE
+If you get no response it is far more likely the irq is wrong and needs
+setting with ifconfig. The few cases where the default parameters will
+be too fast are those using very long cables. Something you should
+never do as the parallel port is not specified or designed for driving
+long cable runs.
+.SH SEE ALSO
+.I ifconfig(8)
+.SH BUGS
+Non.
+.SH AUTHOR
+John Paul Morrison, <jmorriso@bogomips.ee.ubc.ca>, <ve7jpm@ve7jpm.ampr.org>
+
diff --git a/man/en_US/slattach.8 b/man/en_US/slattach.8
new file mode 100644 (file)
index 0000000..0c4c863
--- /dev/null
@@ -0,0 +1,95 @@
+.TH SLATTACH 8 "12 Feb 1994" "" ""
+.SH NAME
+slattach \- attach a network interface to a serial line
+.SH SYNOPSIS
+.B "slattach [-dehlLmnqv] [-c command] [-p proto] [-s speed] [tty]"
+.br
+.SH DESCRIPTION
+.B Slattach
+is a tiny little program that can be used to put a normal terminal
+("serial") line into one of several "network" modes, thus allowing
+you to use it for point-to-point links to other computers.
+.SH OPTIONS
+.TP
+.B "[-c command]"
+Execute
+.B command
+when the line is hung up. This can be used to run scripts or re-establish
+connections when a link goes down.
+.TP
+.B "[-d]"
+Enable debugging output.  Useful when determining why a given
+setup doesn't work.
+.TP
+.B "[-h]"
+Exit when the carrier is lost. This works on both /dev/tty and /dev/cua
+devices by directly monitoring the carrier status every 15 seconds.
+.B "[-v]"
+Enable verbose output.  Useful in shell scripts.
+.TP
+.B "[-q]"
+Operate in quiet mode - no messages at all.
+.TP
+.B "[-l]"
+Create an UUCP-style lockfile for the device in /var/lock.
+.TP
+.B "[-n]"
+Equivalent to the "mesg n" command.
+.TP
+.B "[-m]"
+Do \fBnot\fP initialize the line into 8 bits raw mode.
+.TP
+.B "[-e]"
+Exit right after initializing device, instead of waiting for the
+line to hangup.
+.TP
+.B "[-L]"
+Enable 3 wire operation. The terminal is moved into CLOCAL mode, 
+carrier watching is disabled.
+.TP
+.B "[-p proto]"
+Set a specific kind of protocol to use on the line.  The default
+is set to
+.B "cslip"
+, i.e. compressed SLIP.  Other possible values are
+.B "slip"
+(normal SLIP), 
+.B "adaptive"
+(adaptive CSLIP/SLIP),
+.B "ppp"
+(Point-to-Point Protocol)
+and
+.B "kiss"
+(a protocol used for communicating with AX.25 packet radio terminal node controllers).
+The special argument
+.B "tty"
+can be used to put the device back into normal serial operation.
+Using 'ppp' mode is not normally useful as ppp requires an additional ppp daemon
+.B pppd
+to be active on the line. For kiss connections the 
+.B axattach
+program should be used.
+.TP
+.B "[-s speed]"
+Set a specific line speed, other than the default.
+.PP
+If no arguments are given, the current terminal line (usually: the
+login device) is used.  Otherwise, an attempt is made to claim the
+indicated terminal port, lock it, and open it.
+.SH FILES
+.I /dev/cua* /var/lock/LCK.*
+.SH BUGS
+None known.
+.SH SEE ALSO
+axattach(8), dip(8) pppd(8), sliplogin(8).
+.SH AUTHORS
+Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+.br
+Alan Cox, <Alan.Cox@linux.org>
+.br
+Miquel van Smoorenburg, <miquels@drinkel.ow.org>
+.br
+George Shearer, <gshearer@one.net>
+.br
+Yossi Gottlieb, <yogo@math.tau.ac.il>
+.br
diff --git a/man/fr_FR/plipconfig.8 b/man/fr_FR/plipconfig.8
new file mode 100644 (file)
index 0000000..c0e7e6e
--- /dev/null
@@ -0,0 +1,48 @@
+.TH PLIPCONFIG 8 "17 February 1995" "" ""
+.SH NOM
+plipconfig \- réglage fin des paramètres du périphérique PLIP
+.SH SYNOPSIS
+.B "plipconfig interface"
+.br
+.B "plipconfig interface [nibble NN] [trigger NN] [unité NN]"
+.SH DESCRIPTION
+.B Plipconfig
+est utilisé pour améliorer (espérons-le) les performances PLIP
+en changeant les parmètres par défaut liés au temps et utilisés
+par le protocole PLIP. Les résultat dépendent du port parallèle
+utilisé, du cable, et de la vitesse CPU de chaque machine
+à chaque bout du lien PLIP.
+.LP
+Si le seul argument est l'
+.B interface,
+.B plipconfig
+affiche seulement l'état de cette interface. Autrement, il essaiera de
+positionner les options données.
+.SH OPTIONS
+.TP
+.B "nibble NN"
+Définit la valeur d'attente des digits en microsecondes. Par défault : 3000.
+.TP
+.B "trigger NN"
+Définit le temps avant déclenchement en microsecondes. Par défault : 500.
+.LP
+La vitesse de PLIP peut, dans certains cas, Ãªtre améliorée en
+diminuant les valeurs par défaut.
+Les valeurs trop petites peuvent engendrer une utilisation excessive de CPU,
+et un temps de réponse aux interruptions mauvais, dont le résultat est
+la perte de caractères lus sur le port, ou de paquets PLIP.
+La modification du MTU PLIP peut aussi affecter la vitesse PLIP.
+.SH NOTE
+Si vous n'obtenez aucune réponse, il est fort probable que l'IRQ configurée
+soit mauvaise, et qu'elle nécessite une configuration avec ifconfig.
+Les quelques cas pour lesquels les paramètres par défaut sont trop rapides,
+arrivent lorque l'on utilise de longs cables. Quelque chose Ã  ne pas faire,
+puisque le port parallèle n'est pas fait pour fonctionner avec de longs cables.
+.SH VOIR AUSSI
+.I ifconfig(8)
+.SH BUGS
+Non.
+.SH AUTEURS
+John Paul Morrison, <jmorriso@bogomips.ee.ubc.ca>, <ve7jpm@ve7jpm.ampr.org>
+.SH TRADUCTION
+Jean-Michel VANSTEENE (J.M.Vansteene@frcl.bull.fr)
diff --git a/man/fr_FR/slattach.8 b/man/fr_FR/slattach.8
new file mode 100644 (file)
index 0000000..250988d
--- /dev/null
@@ -0,0 +1,103 @@
+.TH SLATTACH 8 "12 Feb 1994" "" ""
+.SH NOM
+slattach \- attache une interface réseau Ã  une ligne série
+.SH SYNOPSIS
+.B "slattach [-dehlLmnqv] [-c commande] [-p proto] [-s vitesse] [tty]"
+.br
+.SH DESCRIPTION
+.B Slattach
+est un minuscule programme qui peut Ãªtre utilisé pour attacher
+un terminal normal ("série") dans un des différents modes "réseau",
+ceci vous permettant de l'utiliser pour des liaisons point-à-point
+vers d'autres systèmes.
+.SH OPTIONS
+.TP
+.B "[-c commande]"
+Exécute
+.B `commande'
+lorsque la ligne est suspendue. Ceci peut Ãªtre utilisé pour lancer
+des scripts ou réétablir des connexions quand un lien tombe.
+.TP
+.B "[-d]"
+Valide le débogage.  Utile pour déterminer pourquoi une configuration
+ne fonctionne pas.
+.TP
+.B "[-h]"
+Termine lorsque la porteuse est perdue. Ceci fonctionne Ã  la fois sur
+les périphériques /dev/tty et /dev/cua en contrôlant directement
+l'état de la porteuse toutes les 15 secondes.
+.TP
+.B "[-v]"
+Valide le mode verbeux.  Utile pour les shell scripts.
+.TP
+.B "[-q]"
+Opère en mode silencieux - pas de messages du tout.
+.TP
+.B "[-l]"
+Crée un fichier de vérouillage pour le périphérique comme
+pour UUCP dans /var/spool/uucp.
+.TP
+.B "[-n]"
+Equivalent Ã  la commande "mesg n".
+.TP
+.B "[-m]"
+\fBn'\fPinitialise \fBpas\fP la ligne en mode raw 8 bits.
+.TP
+.B "[-e]"
+Termine correctement après l'initialisation du périphérique,
+au lieu d'attendre que la ligne soit suspendue.
+.TP
+.B "[-L]"
+Valide les opérations 3 lignes. Le terminal est mis en mode CLOCAL, 
+la surveillance de porteuse est invalidée.
+.TP
+.B "[-p proto]"
+Définit le protocole spécifique Ã  utiliser sur la ligne.
+La valeur par défaut est positionnée Ã 
+.B "cslip"
+, i.e. SLIP compressé.  Les autres valeurs possibles sont :
+.B "slip"
+(SLIP normal), 
+.B "adaptive"
+(CSLIP/SLIP adaptatifs),
+.B "ppp"
+(Protocole Point-à-Point)
+et
+.B "kiss"
+(protocole AX.25 TNC).
+L'argument spécifique
+.B "tty"
+peut Ãªtre utilisé pour refaire passer le périphérique en
+fonctionnement série normal. L'utilisation du mode 'ppp' n'est en
+principe pas utilise puisque ppp nécessite un démon additionnel
+.B pppd
+pour Ãªtre actif sur la ligne. Pour les connexions `kiss', le programme 
+.B axattach
+doit Ãªtre utilisé.
+.TP
+.B "[-s vitesse]"
+Définit la vitesse de la ligne, différente de la valeur par défaut.
+.PP
+Si aucun argument n'est donné, la ligne courante du terminal
+(habituellement liée au login) est utilisée. Autrement, une tentative
+est effectuée pour obtenir le port du terminal indiqué, puis il est
+vérouillé et ouvert.
+.SH FICHIERS
+.I /dev/cua* /var/spool/uucp/LCK.*
+.SH BUGS
+Aucun connu.
+.SH VOIR AUSSI
+axattach(8), dip(8) pppd(8), sliplogin(8).
+.SH AUTEURS
+Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+.br
+Alan Cox, <Alan.Cox@linux.org>
+.br
+Miquel van Smoorenburg, <miquels@drinkel.ow.org>
+.br
+George Shearer, <gshearer@one.net>
+.br
+Yossi Gottlieb, <yogo@math.tau.ac.il>
+.br
+.SH TRADUCTION
+Jean-Michel VANSTEENE (J.M.Vansteene@frcl.bull.fr)
diff --git a/plipconfig.c b/plipconfig.c
new file mode 100644 (file)
index 0000000..2ab5252
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+
+   plipconfig.c: plip-ifconfig program for the Linux PLIP device driver
+   Copyright (c) 1994 John Paul Morrison (VE7JPM).
+
+   version 0.2
+   
+   Changed by Alan Cox, to reflect the way SIOCDEVPRIVATE is meant to work
+   and for the extra parameter added by Niibe.
+
+   plipconfig is a quick hack to set PLIP parameters by using driver
+   ioctls.  plipconfig will no doubt be revised many times as the Linux
+   PLIP driver and Linux 1.1 mutates.
+
+*/
+
+/*
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2, as
+   published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 675 Mass Ave, Cambridge MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_plip.h>
+#include "config.h"
+#include "intl.h"
+
+int opt_a = 0;
+int opt_i = 0;
+int opt_v = 0;
+int skfd = -1;
+
+struct ifreq ifr;
+struct plipconf *plip;
+
+void usage(void)
+{
+    fprintf(stderr, _("Usage: plipconfig [-a] [-i] [-v] interface\n"));
+    fprintf(stderr, _("                  [nibble NN] [trigger NN]\n"));
+    exit(-1);
+}
+
+void print_plip(void)
+{
+    printf(_("%s\tnibble %lu  trigger %lu\n"), ifr.ifr_name, plip->nibble, plip->trigger);
+}
+
+int main(int argc, char **argv)
+{
+    int ret = 0;
+    char **spp;
+
+    if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+       perror("socket");
+       exit(-1);
+    }
+    /* Find any options. */
+    argc--;
+    argv++;
+    while (argv[0] && *argv[0] == '-') {
+       if (!strcmp(*argv, "-a"))
+           opt_a = 1;
+       if (!strcmp(*argv, "-v"))
+           opt_v = 1;
+       argv++;
+       argc--;
+    }
+
+    if (argc == 0)
+       usage();
+
+    spp = argv;
+    strncpy(ifr.ifr_name, *spp++, IFNAMSIZ);
+    plip=(struct plipconf *)&ifr.ifr_data;
+
+    plip->pcmd = PLIP_GET_TIMEOUT;     /* get current settings for device */
+    if (ioctl(skfd, SIOCDEVPLIP, &ifr) < 0) {
+       perror("ioctl");
+       exit(-1);
+    }
+    if (*spp == (char *) NULL) {
+       print_plip();
+       (void) close(skfd);
+       exit(0);
+    }
+    while (*spp != (char *) NULL) {
+       if (!strcmp(*spp, "nibble")) {
+           if (*++spp == NULL)
+               usage();
+           plip->nibble = atoi(*spp);
+           spp++;
+           continue;
+       }
+       if (!strcmp(*spp, "trigger")) {
+           if (*++spp == NULL)
+               usage();
+           plip->trigger = atoi(*spp);
+           spp++;
+           continue;
+       }
+       usage();
+    }
+
+    plip->pcmd = PLIP_SET_TIMEOUT;
+    if (ioctl(skfd, SIOCDEVPLIP, &ifr) < 0)
+       perror("ioctl");
+
+    print_plip();
+
+    /* Close the socket. */
+    (void) close(skfd);
+
+    return (ret);
+}
diff --git a/slattach.c b/slattach.c
new file mode 100644 (file)
index 0000000..581dfc0
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ * slattach    A program for handling dialup IP connecions.
+ *             This program forces a TTY line to go into a special
+ *             terminal line discipline, so that it can be used for
+ *             network traffic instead of the regular terminal I/O.
+ *
+ * Usage:      slattach [-ehlmnqv] [ -k keepalive ] [ -o outfill ]
+ *                     [-c cmd] [-s speed] [-p protocol] tty | -
+ *
+ * Version:    @(#)slattach.c  1.1.30  03/01/95
+ *
+ * Author:      Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *             Copyright 1988-1993 MicroWalt Corporation
+ *
+ * Modified:
+ *             Alan Cox, <A.Cox@swansea.ac.uk> , July 16 1994
+ *             Miquel van Smoorenburg, <miquels@drinkel.ow.org>, October 1994
+ *             George Shearer, <gshearer@one.net>, January 3, 1995
+ *             Yossi Gottlieb, <yogo@math.tau.ac.il>, February 11, 1995
+ *             Peter Tobias, <tobias@et-inf.fho-emden.de>, July 30 1995
+ *
+ *             This program is free software; you can redistribute it
+ *             and/or  modify it under  the terms of  the GNU General
+ *             Public  License as  published  by  the  Free  Software
+ *             Foundation;  either  version 2 of the License, or  (at
+ *             your option) any later version.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>          
+#include <string.h>
+#include <unistd.h>
+#include <linux/if_slip.h>
+
+#if defined(__GLIBC__)
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
+# include <termbits.h>
+#else
+# include <termios.h>
+#endif
+#endif
+
+#include "pathnames.h"
+#include "net-support.h"
+#include "version.h"
+#include "config.h"
+#include "intl.h"
+
+#ifndef _PATH_LOCKD
+#define _PATH_LOCKD            "/var/lock"             /* lock files   */
+#endif
+#ifndef _UID_UUCP
+#define _UID_UUCP              "uucp"                  /* owns locks   */
+#endif
+
+
+#define DEF_PROTO      "cslip"
+
+
+char *Release = RELEASE,
+     *Version = "@(#) slattach 1.1.91 (12-Feb-95)";
+
+
+struct {
+  char *speed;
+  int  code;
+} tty_speeds[] = {                     /* table of usable baud rates   */
+  { "50",      B50     }, { "75",      B75     },      
+  { "110",     B110    }, { "300",     B300    },
+  { "600",     B600    }, { "1200",    B1200   },
+  { "2400",    B2400   }, { "4800",    B4800   },
+  { "9600",    B9600   },
+#ifdef B14400
+  { "14400",   B14400  },
+#endif
+#ifdef B19200
+  { "19200",   B19200  },
+#endif
+#ifdef B38400
+  { "38400",   B38400  },
+#endif
+#ifdef B57600
+  { "57600",   B57600  },
+#endif
+#ifdef B115200
+  { "115200",  B115200 },
+#endif
+  { NULL,      0       }
+};
+struct termios tty_saved,              /* saved TTY device state       */
+               tty_current;            /* current TTY device state     */
+int            tty_sdisc,              /* saved TTY line discipline    */
+               tty_ldisc,              /* current TTY line discipline  */
+               tty_fd = -1;            /* TTY file descriptor          */
+int            opt_c = 0;              /* "command" to run at exit     */
+int            opt_e = 0;              /* "activate only" flag         */
+int            opt_h = 0;              /* "hangup" on carrier loss     */
+#ifdef SIOCSKEEPALIVE
+int            opt_k = 0;              /* "keepalive" value            */
+#endif
+int            opt_l = 0;              /* "lock it" flag               */
+int            opt_L = 0;              /* clocal flag                  */
+int            opt_m = 0;              /* "set RAW mode" flag          */
+int            opt_n = 0;              /* "set No Mesg" flag           */
+#ifdef SIOCSOUTFILL
+int            opt_o = 0;              /* "outfill" value              */
+#endif
+int            opt_q = 0;              /* "quiet" flag                 */
+int            opt_d = 0;              /* debug flag                   */
+int            opt_v = 0;              /* Verbose flag                 */
+
+/* Disable any messages to the input channel of this process. */
+static int
+tty_nomesg(int fd)
+{
+  if (opt_n == 0) return(0);
+  return(fchmod(fd, 0600));
+}
+
+/* Check for an existing lock file on our device */
+static int
+tty_already_locked(char *nam)
+{
+  int  i = 0, pid = 0;
+  FILE *fd = (FILE *)0;
+
+  /* Does the lock file on our device exist? */
+  if ((fd = fopen(nam, "r")) == (FILE *)0)
+    return(0); /* No, return perm to continue */
+
+  /* Yes, the lock is there.  Now let's make sure */
+  /* at least there's no active process that owns */
+  /* that lock.                                   */
+  i = fscanf(fd, "%d", &pid);
+  (void) fclose(fd);
+  if (i != 1) /* Lock file format's wrong! Kill't */
+    return(0);
+
+  /* We got the pid, check if the process's alive */
+  if (kill(pid, 0) == 0)      /* it found process */
+      return(1);          /* Yup, it's running... */
+
+  /* Dead, we can proceed locking this device...  */
+  return(0);
+}
+
+/* Lock or unlock a terminal line. */
+static int
+tty_lock(char *path, int mode)
+{
+  static char saved_path[PATH_MAX];
+  static int saved_lock = 0;
+  struct passwd *pw;
+  int fd;
+  char apid[16];
+
+  /* We do not lock standard input. */
+  if ((opt_l == 0) || ((path == NULL) && (saved_lock == 0))) return(0);
+
+  if (mode == 1) {     /* lock */
+       sprintf(saved_path, "%s/LCK..%s", _PATH_LOCKD, path);
+       if (tty_already_locked(saved_path)) {
+               fprintf(stderr, _("slattach: /dev/%s already locked!\n"), path);
+               return(-1);
+       }
+       if ((fd = creat(saved_path, 0644)) < 0) {
+               if (errno != EEXIST)
+                       if (opt_q == 0) fprintf(stderr,
+                               _("slattach: tty_lock: (%s): %s\n"),
+                                       saved_path, strerror(errno));
+               return(-1);
+       }
+       sprintf(apid, "%10d\n", getpid());
+       if (write(fd, apid, strlen(apid)) != strlen(apid)) {
+               fprintf(stderr, _("slattach: cannot write PID file\n"));
+               close(fd);
+               unlink(saved_path);
+               return(-1);
+       }
+
+       (void) close(fd);
+
+       /* Make sure UUCP owns the lockfile.  Required by some packages. */
+       if ((pw = getpwnam(_UID_UUCP)) == NULL) {
+               if (opt_q == 0) fprintf(stderr, _("slattach: tty_lock: UUCP user %s unknown!\n"),
+                                       _UID_UUCP);
+               return(0);      /* keep the lock anyway */
+       }
+       (void) chown(saved_path, pw->pw_uid, pw->pw_gid);
+       saved_lock = 1;
+  } else {     /* unlock */
+       if (saved_lock != 1) return(0);
+       if (unlink(saved_path) < 0) {
+               if (opt_q == 0) fprintf(stderr,
+                       "slattach: tty_unlock: (%s): %s\n", saved_path,
+                                                       strerror(errno));
+               return(-1);
+       }
+       saved_lock = 0;
+  }
+
+  return(0);
+}
+
+
+/* Find a serial speed code in the table. */
+static int
+tty_find_speed(char *speed)
+{
+  int i;
+
+  i = 0;
+  while (tty_speeds[i].speed != NULL) {
+       if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
+       i++;
+  }
+  return(-EINVAL);
+}
+
+
+/* Set the number of stop bits. */
+static int
+tty_set_stopbits(struct termios *tty, char *stopbits)
+{
+  if (opt_d) printf("slattach: tty_set_stopbits: %c\n", *stopbits);
+  switch(*stopbits) {
+       case '1':
+               tty->c_cflag &= ~CSTOPB;
+               break;
+
+       case '2':
+               tty->c_cflag |= CSTOPB;
+               break;
+
+       default:
+               return(-EINVAL);
+  }
+  return(0);
+}
+
+
+/* Set the number of data bits. */
+static int
+tty_set_databits(struct termios *tty, char *databits)
+{
+  if (opt_d) printf("slattach: tty_set_databits: %c\n", *databits);
+  tty->c_cflag &= ~CSIZE;
+  switch(*databits) {
+       case '5':
+               tty->c_cflag |= CS5;
+               break;
+
+       case '6':
+               tty->c_cflag |= CS6;
+               break;
+
+       case '7':
+               tty->c_cflag |= CS7;
+               break;
+
+       case '8':
+               tty->c_cflag |= CS8;
+               break;
+
+       default:
+               return(-EINVAL);
+  }
+  return(0);
+}
+
+
+/* Set the type of parity encoding. */
+static int
+tty_set_parity(struct termios *tty, char *parity)
+{
+  if (opt_d) printf("slattach: tty_set_parity: %c\n", *parity);
+  switch(toupper(*parity)) {
+       case 'N':
+               tty->c_cflag &= ~(PARENB | PARODD);
+               break;  
+
+       case 'O':
+               tty->c_cflag &= ~(PARENB | PARODD);
+               tty->c_cflag |= (PARENB | PARODD);
+               break;
+
+       case 'E':
+               tty->c_cflag &= ~(PARENB | PARODD);
+               tty->c_cflag |= (PARENB);
+               break;
+
+       default:
+               return(-EINVAL);
+  }
+  return(0);
+}
+
+
+/* Set the line speed of a terminal line. */
+static int
+tty_set_speed(struct termios *tty, char *speed)
+{
+  int code;
+
+  if (opt_d) printf("slattach: tty_set_speed: %s\n", speed);
+  if ((code = tty_find_speed(speed)) < 0) return(code);
+  tty->c_cflag &= ~CBAUD;
+  tty->c_cflag |= code;
+  return(0);
+}
+
+
+/* Put a terminal line in a transparent state. */
+static int
+tty_set_raw(struct termios *tty)
+{
+  int i;
+  int speed;
+
+  for(i = 0; i < NCCS; i++)
+               tty->c_cc[i] = '\0';            /* no spec chr          */
+  tty->c_cc[VMIN] = 1;
+  tty->c_cc[VTIME] = 0;
+  tty->c_iflag = (IGNBRK | IGNPAR);            /* input flags          */
+  tty->c_oflag = (0);                          /* output flags         */
+  tty->c_lflag = (0);                          /* local flags          */
+  speed = (tty->c_cflag & CBAUD);              /* save current speed   */
+  tty->c_cflag = (CRTSCTS | HUPCL | CREAD);    /* UART flags           */
+  if (opt_L) 
+       tty->c_cflag |= CLOCAL;
+  tty->c_cflag |= speed;                       /* restore speed        */
+  return(0);
+}
+
+
+/* Fetch the state of a terminal. */
+static int
+tty_get_state(struct termios *tty)
+{
+  if (ioctl(tty_fd, TCGETS, tty) < 0) {
+       if (opt_q == 0) fprintf(stderr,
+               "slattach: tty_get_state: %s\n", strerror(errno));
+       return(-errno);
+  }
+  return(0);
+}
+
+
+/* Set the state of a terminal. */
+static int
+tty_set_state(struct termios *tty)
+{
+  if (ioctl(tty_fd, TCSETS, tty) < 0) {
+       if (opt_q == 0) fprintf(stderr,
+               "slattach: tty_set_state: %s\n", strerror(errno));
+       return(-errno);
+  }
+  return(0);
+}
+
+
+/* Get the line discipline of a terminal line. */
+static int
+tty_get_disc(int *disc)
+{
+  if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
+       if (opt_q == 0) fprintf(stderr,
+               "slattach: tty_get_disc: %s\n", strerror(errno));
+       return(-errno);
+  }
+  return(0);
+}
+
+
+/* Set the line discipline of a terminal line. */
+static int
+tty_set_disc(int disc)
+{
+  if (disc == -1) disc = tty_sdisc;
+
+  if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
+       if (opt_q == 0) fprintf(stderr,
+               "slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
+                       disc, strerror(errno));
+       return(-errno);
+  }
+  return(0);
+}
+
+
+/* Fetch the name of the network interface attached to this terminal. */
+static int
+tty_get_name(char *name)
+{
+  if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
+       if (opt_q == 0) fprintf(stderr,
+               "slattach: tty_get_name: %s\n", strerror(errno));
+       return(-errno);
+  }
+  return(0);
+}
+
+
+/* Hangup the line. */
+static int
+tty_hangup(void)
+{
+  struct termios tty;
+
+  tty = tty_current;
+  (void) tty_set_speed(&tty, "0");
+  if (tty_set_state(&tty) < 0) {
+       if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
+       return(-errno);
+  }
+
+  (void) sleep(1);
+
+  if (tty_set_state(&tty_current) < 0) {
+       if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
+       return(-errno);
+  }
+  return(0);
+}
+
+
+/* Close down a terminal line. */
+static int
+tty_close(void)
+{
+  (void) tty_set_disc(tty_sdisc);
+  (void) tty_hangup();
+  (void) tty_lock(NULL, 0);
+  return(0);
+}
+
+
+/* Open and initialize a terminal line. */
+static int
+tty_open(char *name, char *speed)
+{
+  char path[PATH_MAX];
+  register char *sp;
+  int fd;
+
+  /* Try opening the TTY device. */
+  if (name != NULL) {
+       if ((sp = strrchr(name, '/')) != (char *)NULL) *sp++ = '\0';
+         else sp = name;
+       sprintf(path, "/dev/%s", sp);
+       if (tty_lock(sp, 1)) return(-1); /* can we lock the device? */
+       if ((fd = open(path, O_RDWR)) < 0) {
+               if (opt_q == 0) fprintf(stderr,
+                       "slattach: tty_open(%s, RW): %s\n",
+                                       path, strerror(errno));
+               return(-errno);
+       }
+       tty_fd = fd;
+       if (opt_d) printf("slattach: tty_open: %s (%d) ", path, fd);
+  } else {
+       tty_fd = 0;
+       sp = (char *)NULL;
+  }
+
+  /* Fetch the current state of the terminal. */
+  if (tty_get_state(&tty_saved) < 0) {
+       if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
+       return(-errno);
+  }
+  tty_current = tty_saved;
+
+  /* Fetch the current line discipline of this terminal. */
+  if (tty_get_disc(&tty_sdisc) < 0) {
+       if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
+       return(-errno);
+  } 
+  tty_ldisc = tty_sdisc;
+
+  /* Put this terminal line in a 8-bit transparent mode. */
+  if (opt_m == 0) {
+       if (tty_set_raw(&tty_current) < 0) {
+               if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
+               return(-errno);
+       }
+
+       /* Set the default speed if we need to. */
+       if (speed != NULL) {
+               if (tty_set_speed(&tty_current, speed) != 0) {
+                       if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
+                                               speed);
+                       return(-errno);
+               }
+       }
+
+       /* Set up a completely 8-bit clean line. */
+       if (tty_set_databits(&tty_current, "8") ||
+           tty_set_stopbits(&tty_current, "1") ||
+           tty_set_parity(&tty_current, "N")) {
+               if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
+               return(-errno);
+       }
+
+       /* Set the new line mode. */
+       if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
+  }
+
+  /* OK, line is open.  Do we need to "silence" it? */
+  (void) tty_nomesg(tty_fd);
+
+  return(0);
+}
+
+
+/* Catch any signals. */
+static void
+sig_catch(int sig)
+{
+/*  (void) signal(sig, sig_catch); */
+  tty_close();
+  exit(0);
+}
+
+
+static void
+usage(void)
+{
+  char *usage_msg = "Usage: slattach [-ehlLmnqv] "
+#ifdef SIOCSKEEPALIVE
+         "[-k keepalive] "
+#endif
+#ifdef SIOCSOUTFILL
+         "[-o outfill] "
+#endif
+         "[-c cmd] [-s speed] [-p protocol] tty | -\n";
+
+  fprintf(stderr, usage_msg);
+  exit(1);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+  char path[128];
+  char buff[128];
+  char *speed = NULL;
+  char *proto = DEF_PROTO;
+  char *extcmd = (char *)0;
+  struct hwtype *ht;
+  char *sp;
+  int s;
+
+  strcpy(path, "");
+
+  /* Scan command line for any arguments. */
+  opterr = 0;
+  while ((s = getopt(argc, argv, "c:ehlLmnp:qs:vd"
+#ifdef SIOCSKEEPALIVE
+                    "k:"
+#endif
+#ifdef SIOCSOUTFILL
+                    "o:"
+#endif
+                    )) != EOF) switch(s) {
+       case 'c':
+               extcmd = optarg;
+               break;
+
+       case 'e':
+               opt_e = 1 - opt_e;
+               break;
+
+       case 'h':
+               opt_h = 1 - opt_h;
+               break;
+
+#ifdef SIOCSKEEPALIVE
+       case 'k':
+               opt_k = atoi(optarg);
+               break;
+#endif
+
+       case 'L':
+               opt_L = 1 - opt_L;
+               break;
+
+       case 'l':
+               opt_l = 1 - opt_l;
+               break;
+
+       case 'm':
+               opt_m = 1 - opt_m;
+               break;
+
+       case 'n':
+               opt_n = 1 - opt_n;
+               break;
+
+#ifdef SIOCSOUTFILL
+       case 'o':
+               opt_o = atoi(optarg);
+               break;
+#endif
+
+       case 'p':
+               proto = optarg;
+               break;
+
+       case 'q':
+               opt_q = 1 - opt_q;
+               break;
+
+       case 's':
+               speed = optarg;
+               break;
+
+       case 'd':
+               opt_d = 1 - opt_d;
+               break;
+
+       case 'v':
+               opt_v = 1 - opt_v;
+               break;
+
+       default:
+               usage();
+  }
+  
+  activate_init();
+
+  /* Check the protocol. */
+  if ((ht = get_hwtype(proto)) == NULL && strcmp(proto, "tty")) {
+       if (opt_q == 0) fprintf(stderr, _("slattach: unsupported protocol %s\n"), proto);
+       return(2);
+  }
+  if (ht == NULL) opt_m++;
+
+  /* Is a terminal given? */
+  if (optind != (argc - 1)) usage();
+  strncpy(path, argv[optind], 128);
+  if (!strcmp(path, "-")) {
+       opt_e = 1;
+       sp = NULL;
+       if (tty_open(NULL, speed) < 0) { return(3); }
+  } else {
+       if ((sp = strrchr(path, '/')) != NULL) *sp++ = '\0';
+         else sp = path;
+       if (tty_open(sp, speed) < 0) { return(3); }
+  }
+
+  /* Start the correct protocol. */
+  if (ht == NULL) {
+       tty_sdisc = N_TTY;
+       tty_close();
+       return(0);
+  }
+  (*ht->activate)(tty_fd);
+  if (opt_v == 1) {
+       tty_get_name(buff);
+       printf("%s started", proto);
+       if (sp != NULL) printf(" on %s", sp);
+       printf(" interface %s\n", buff);
+  }
+
+  /* Configure keepalive and outfill. */
+#ifdef SIOCSKEEPALIVE
+  if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
+         fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
+#endif
+#ifdef SIOCSOUTFILL
+  if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
+         fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
+#endif
+
+  (void) signal(SIGHUP, sig_catch);
+  (void) signal(SIGINT, sig_catch);
+  (void) signal(SIGQUIT, sig_catch);
+  (void) signal(SIGTERM, sig_catch);
+
+  /* Wait until we get killed if hanging on a terminal. */
+  if (opt_e == 0) {
+       while(1) {
+               if(opt_h == 1) { /* hangup on carrier loss */
+                       int n = 0;
+
+                       ioctl(tty_fd, TIOCMGET, &n);
+                       if(!(n & TIOCM_CAR))
+                               break;
+                       sleep(15);
+               }
+               else
+                       sleep(60);
+       };
+
+       tty_close();
+       if(extcmd!=(char *)0) /* external command on exit */
+               system(extcmd);
+  }
+  exit(0);
+}