IP applet by Bastian Blank <waldi@debian.org>
authorGlenn L McGrath <bug1@ihug.co.nz>
Sun, 10 Nov 2002 01:33:55 +0000 (01:33 -0000)
committerGlenn L McGrath <bug1@ihug.co.nz>
Sun, 10 Nov 2002 01:33:55 +0000 (01:33 -0000)
27 files changed:
Makefile
include/applets.h
include/usage.h
networking/Makefile.in
networking/config.in
networking/ip.c [new file with mode: 0644]
networking/libiproute/Makefile [new file with mode: 0644]
networking/libiproute/Makefile.in [new file with mode: 0644]
networking/libiproute/ip_common.h [new file with mode: 0644]
networking/libiproute/ipaddress.c [new file with mode: 0644]
networking/libiproute/iplink.c [new file with mode: 0644]
networking/libiproute/iproute.c [new file with mode: 0644]
networking/libiproute/iptunnel.c [new file with mode: 0644]
networking/libiproute/libnetlink.c [new file with mode: 0644]
networking/libiproute/libnetlink.h [new file with mode: 0644]
networking/libiproute/linux/pkt_sched.h [new file with mode: 0644]
networking/libiproute/ll_addr.c [new file with mode: 0644]
networking/libiproute/ll_map.c [new file with mode: 0644]
networking/libiproute/ll_map.h [new file with mode: 0644]
networking/libiproute/ll_proto.c [new file with mode: 0644]
networking/libiproute/ll_types.c [new file with mode: 0644]
networking/libiproute/rt_names.c [new file with mode: 0644]
networking/libiproute/rt_names.h [new file with mode: 0644]
networking/libiproute/rtm_map.c [new file with mode: 0644]
networking/libiproute/rtm_map.h [new file with mode: 0644]
networking/libiproute/utils.c [new file with mode: 0644]
networking/libiproute/utils.h [new file with mode: 0644]

index 693d47b7c3293c2e06fbee23742869d758136583..9a9780ec9672dacade4490e1acda8446dd6593d5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,8 +22,8 @@ include $(TOPDIR).config
 include $(TOPDIR)Rules.mak
 SUBDIRS:=applets archival archival/libunarchive console-tools \
        editors fileutils findutils init miscutils modutils networking \
-       networking/udhcp procps loginutils shell shellutils sysklogd \
-       textutils util-linux libbb libpwdgrp
+       networking/libiproute networking/udhcp procps loginutils shell \
+       shellutils sysklogd textutils util-linux libbb libpwdgrp
 
 all:    do-it-all
 
index 2de9c990966d3e35bc1f06dac0e6c606e095566c..31734451168504acef7784e2d7a519deb36d4617 100644 (file)
 #ifdef CONFIG_INSMOD
        APPLET(insmod, insmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
 #endif
+#ifdef CONFIG_IP
+       APPLET(ip, ip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
 #ifdef CONFIG_KILL
        APPLET(kill, kill_main, _BB_DIR_BIN, _BB_SUID_NEVER)
 #endif
index 2d20722a9a7add47bc58640f571103c73a8c5668..d0aa3243f5202140159251b29a067d5883b91f40 100644 (file)
        "\t-L\tLock to prevent simultaneous loads of a module\n" \
        "\t-x\tdo not export externs"
 
+#define ip_trivial_usage \
+       "not written yet"
+#define ip_full_usage \
+       "not written yet"
+
 #define kill_trivial_usage \
        "[-signal] process-id [process-id ...]"
 #define kill_full_usage \
index 743990bb6489080bbf7be48ca9533256eb04e9ee..d249c042301e517d2ad03a8b52d4448ae7497d03 100644 (file)
@@ -26,7 +26,8 @@ NETWORKING-y:=
 NETWORKING-$(CONFIG_HOSTNAME)          += hostname.o
 NETWORKING-$(CONFIG_IFCONFIG)          += ifconfig.o
 NETWORKING-$(CONFIG_IFUPDOWN)          += ifupdown.o
-NETWORKING-$(CONFIG_NC)                        += nc.o
+NETWORKING-$(CONFIG_IP)                    += ip.o
+NETWORKING-$(CONFIG_NC)                    += nc.o
 NETWORKING-$(CONFIG_NETSTAT)           += netstat.o
 NETWORKING-$(CONFIG_NSLOOKUP)          += nslookup.o
 NETWORKING-$(CONFIG_PING)              += ping.o
index 09b2b5c6f37068a1a38c375e70e828065a970380..fe714fe0ce12ff879a6fba8024f1e7c05b3a477f 100644 (file)
@@ -22,6 +22,13 @@ if [ "$CONFIG_IFUPDOWN" = "y" ]; then
     bool '  Enable support for IPv6 (requires ip command)' CONFIG_FEATURE_IFUPDOWN_IPV6
     bool '  Enable support for IPX (requires ipx_interface command)' CONFIG_FEATURE_IFUPDOWN_IPX
 fi
+bool 'ip'          CONFIG_IP
+if [ "$CONFIG_IP" = "y" ]; then
+    bool '  address'           CONFIG_FEATURE_IP_ADDRESS
+    bool '  link'              CONFIG_FEATURE_IP_LINK
+    bool '  route'             CONFIG_FEATURE_IP_ROUTE
+    bool '  tunnel'            CONFIG_FEATURE_IP_TUNNEL
+fi
 bool 'nc'          CONFIG_NC
 bool 'netstat'     CONFIG_NETSTAT
 bool 'nslookup'            CONFIG_NSLOOKUP
diff --git a/networking/ip.c b/networking/ip.c
new file mode 100644 (file)
index 0000000..f045f5f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             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 "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+char * _SL_ = NULL;
+
+int ip_main(int argc, char **argv)
+{
+       char *basename;
+
+       basename = strrchr(argv[0], '/');
+       if (basename == NULL)
+               basename = argv[0];
+       else
+               basename++;
+       
+       while (argc > 1) {
+               char *opt = argv[1];
+               if (strcmp(opt,"--") == 0) {
+                       argc--; argv++;
+                       break;
+               }
+               if (opt[0] != '-')
+                       break;
+               if (opt[1] == '-')
+                       opt++;
+               if (matches(opt, "-family") == 0) {
+                       argc--;
+                       argv++;
+                       if (strcmp(argv[1], "inet") == 0)
+                               preferred_family = AF_INET;
+                       else if (strcmp(argv[1], "inet6") == 0)
+                               preferred_family = AF_INET6;
+                       else if (strcmp(argv[1], "link") == 0)
+                               preferred_family = AF_PACKET;
+                       else
+                               invarg(argv[1], "invalid protocol family");
+               } else if (strcmp(opt, "-4") == 0) {
+                       preferred_family = AF_INET;
+               } else if (strcmp(opt, "-6") == 0) {
+                       preferred_family = AF_INET6;
+               } else if (strcmp(opt, "-0") == 0) {
+                       preferred_family = AF_PACKET;
+               } else if (matches(opt, "-oneline") == 0) {
+                       ++oneline;
+               } else {
+                       fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt);
+                       exit(-1);
+               }
+               argc--; argv++;
+       }
+
+       _SL_ = oneline ? "\\" : "\n" ;
+
+       if (argc > 1) {
+#ifdef CONFIG_FEATURE_IP_ADDRESS
+               if (matches(argv[1], "address") == 0)
+                       return do_ipaddr(argc-2, argv+2);
+#endif
+#ifdef CONFIG_FEATURE_IP_ROUTE
+               if (matches(argv[1], "route") == 0)
+                       return do_iproute(argc-2, argv+2);
+#endif
+#ifdef CONFIG_FEATURE_IP_LINK
+               if (matches(argv[1], "link") == 0)
+                       return do_iplink(argc-2, argv+2);
+#endif
+#ifdef CONFIG_FEATURE_IP_TUNNEL
+               if (matches(argv[1], "tunnel") == 0 ||
+                   strcmp(argv[1], "tunl") == 0)
+                       return do_iptunnel(argc-2, argv+2);
+#endif
+               fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv[1]);
+               exit(-1);
+       }
+}
diff --git a/networking/libiproute/Makefile b/networking/libiproute/Makefile
new file mode 100644 (file)
index 0000000..29419fd
--- /dev/null
@@ -0,0 +1,30 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2002 Erik Andersen <andersee@debian.org>
+#
+# 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.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+TOPDIR:= ../../
+LIBIPROUTE_DIR:=./
+include $(TOPDIR).config
+include $(TOPDIR)Rules.mak
+include Makefile.in
+all: $(libraries-y)
+-include $(TOPDIR).depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/networking/libiproute/Makefile.in b/networking/libiproute/Makefile.in
new file mode 100644 (file)
index 0000000..9f782af
--- /dev/null
@@ -0,0 +1,43 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2002 by Erik Andersen <andersee@debian.org>
+#
+# 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.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LIBIPROUTE_AR:=libiproute.a
+ifndef $(LIBIPROUTE_DIR)
+LIBIPROUTE_DIR:=$(TOPDIR)networking/libiproute/
+endif
+
+LIBIPROUTE-y:= \
+       ipaddress.o \
+       iplink.o \
+       iproute.o \
+       iptunnel.o \
+       libnetlink.o \
+       ll_addr.o \
+       ll_map.o \
+       ll_proto.o \
+       ll_types.o \
+       rt_names.o \
+       rtm_map.o \
+       utils.o
+
+libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR)
+
+$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
+       $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
+
diff --git a/networking/libiproute/ip_common.h b/networking/libiproute/ip_common.h
new file mode 100644 (file)
index 0000000..5ac4321
--- /dev/null
@@ -0,0 +1,20 @@
+extern int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ipaddr_list(int argc, char **argv);
+extern int ipaddr_list_link(int argc, char **argv);
+extern int iproute_monitor(int argc, char **argv);
+extern void iplink_usage(void) __attribute__((noreturn));
+extern void iproute_reset_filter(void);
+extern void ipaddr_reset_filter(int);
+extern void ipneigh_reset_filter(void);
+extern int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+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_multiroute(int argc, char **argv);
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c
new file mode 100644 (file)
index 0000000..8f491f3
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+ * ipaddress.c         "ip address".
+ *
+ *             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:
+ *     Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ll_map.h"
+#include "ip_common.h"
+
+#include "busybox.h"
+
+static struct
+{
+       int ifindex;
+       int family;
+       int oneline;
+       int showqueue;
+       inet_prefix pfx;
+       int scope, scopemask;
+       int flags, flagmask;
+       int up;
+       char *label;
+       struct rtnl_handle *rth;
+} filter;
+
+static int do_link;
+
+void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+{
+       fprintf(fp, "<");
+       flags &= ~IFF_RUNNING;
+#define _PF(f) if (flags&IFF_##f) { \
+                  flags &= ~IFF_##f ; \
+                  fprintf(fp, #f "%s", flags ? "," : ""); }
+       _PF(LOOPBACK);
+       _PF(BROADCAST);
+       _PF(POINTOPOINT);
+       _PF(MULTICAST);
+       _PF(NOARP);
+#if 0
+       _PF(ALLMULTI);
+       _PF(PROMISC);
+       _PF(MASTER);
+       _PF(SLAVE);
+       _PF(DEBUG);
+       _PF(DYNAMIC);
+       _PF(AUTOMEDIA);
+       _PF(PORTSEL);
+       _PF(NOTRAILERS);
+#endif
+       _PF(UP);
+#undef _PF
+        if (flags)
+               fprintf(fp, "%x", flags);
+       if (mdown)
+               fprintf(fp, ",M-DOWN");
+       fprintf(fp, "> ");
+}
+
+void print_queuelen(char *name)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s < 0)
+               return;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, name);
+       if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { 
+               perror("SIOCGIFXQLEN");
+               close(s);
+               return;
+       }
+       close(s);
+
+       if (ifr.ifr_qlen)
+               printf("qlen %d", ifr.ifr_qlen);
+}
+
+int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = (FILE*)arg;
+       struct ifinfomsg *ifi = NLMSG_DATA(n);
+       struct rtattr * tb[IFLA_MAX+1];
+       int len = n->nlmsg_len;
+       unsigned m_flag = 0;
+
+       if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+               return 0;
+
+       len -= NLMSG_LENGTH(sizeof(*ifi));
+       if (len < 0)
+               return -1;
+
+       if (filter.ifindex && ifi->ifi_index != filter.ifindex)
+               return 0;
+       if (filter.up && !(ifi->ifi_flags&IFF_UP))
+               return 0;
+
+       memset(tb, 0, sizeof(tb));
+       parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+       if (tb[IFLA_IFNAME] == NULL) {
+               fprintf(stderr, "BUG: nil ifname\n");
+               return -1;
+       }
+       if (filter.label &&
+           (!filter.family || filter.family == AF_PACKET) &&
+           fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+               return 0;
+
+       if (n->nlmsg_type == RTM_DELLINK)
+               fprintf(fp, "Deleted ");
+
+       fprintf(fp, "%d: %s", ifi->ifi_index,
+               tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+
+       if (tb[IFLA_LINK]) {
+               SPRINT_BUF(b1);
+               int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+               if (iflink == 0)
+                       fprintf(fp, "@NONE: ");
+               else {
+                       fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+                       m_flag = ll_index_to_flags(iflink);
+                       m_flag = !(m_flag & IFF_UP);
+               }
+       } else {
+               fprintf(fp, ": ");
+       }
+       print_link_flags(fp, ifi->ifi_flags, m_flag);
+
+       if (tb[IFLA_MTU])
+               fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+       if (tb[IFLA_QDISC])
+               fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+#ifdef IFLA_MASTER
+       if (tb[IFLA_MASTER]) {
+               SPRINT_BUF(b1);
+               fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+       }
+#endif
+       if (filter.showqueue)
+               print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
+       
+       if (!filter.family || filter.family == AF_PACKET) {
+               SPRINT_BUF(b1);
+               fprintf(fp, "%s", _SL_);
+               fprintf(fp, "    link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+
+               if (tb[IFLA_ADDRESS]) {
+                       fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+                                                     RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+                                                     ifi->ifi_type,
+                                                     b1, sizeof(b1)));
+               }
+               if (tb[IFLA_BROADCAST]) {
+                       if (ifi->ifi_flags&IFF_POINTOPOINT)
+                               fprintf(fp, " peer ");
+                       else
+                               fprintf(fp, " brd ");
+                       fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+                                                     RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+                                                     ifi->ifi_type,
+                                                     b1, sizeof(b1)));
+               }
+       }
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
+
+int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = (FILE*)arg;
+       struct ifaddrmsg *ifa = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       struct rtattr * rta_tb[IFA_MAX+1];
+       char abuf[256];
+       SPRINT_BUF(b1);
+
+       if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
+               return 0;
+       len -= NLMSG_LENGTH(sizeof(*ifa));
+       if (len < 0) {
+               fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+               return -1;
+       }
+
+       memset(rta_tb, 0, sizeof(rta_tb));
+       parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+
+       if (!rta_tb[IFA_LOCAL])
+               rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+       if (!rta_tb[IFA_ADDRESS])
+               rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+       if (filter.ifindex && filter.ifindex != ifa->ifa_index)
+               return 0;
+       if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+               return 0;
+       if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+               return 0;
+       if (filter.label) {
+               SPRINT_BUF(b1);
+               const char *label;
+               if (rta_tb[IFA_LABEL])
+                       label = RTA_DATA(rta_tb[IFA_LABEL]);
+               else
+                       label = ll_idx_n2a(ifa->ifa_index, b1);
+               if (fnmatch(filter.label, label, 0) != 0)
+                       return 0;
+       }
+       if (filter.pfx.family) {
+               if (rta_tb[IFA_LOCAL]) {
+                       inet_prefix dst;
+                       memset(&dst, 0, sizeof(dst));
+                       dst.family = ifa->ifa_family;
+                       memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
+                       if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+                               return 0;
+               }
+       }
+
+       if (n->nlmsg_type == RTM_DELADDR)
+               fprintf(fp, "Deleted ");
+
+       if (filter.oneline)
+               fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+       if (ifa->ifa_family == AF_INET)
+               fprintf(fp, "    inet ");
+       else if (ifa->ifa_family == AF_INET6)
+               fprintf(fp, "    inet6 ");
+       else
+               fprintf(fp, "    family %d ", ifa->ifa_family);
+
+       if (rta_tb[IFA_LOCAL]) {
+               fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+                                             RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+                                             RTA_DATA(rta_tb[IFA_LOCAL]),
+                                             abuf, sizeof(abuf)));
+
+               if (rta_tb[IFA_ADDRESS] == NULL ||
+                   memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
+                       fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+               } else {
+                       fprintf(fp, " peer %s/%d ",
+                               rt_addr_n2a(ifa->ifa_family,
+                                           RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+                                           RTA_DATA(rta_tb[IFA_ADDRESS]),
+                                           abuf, sizeof(abuf)),
+                               ifa->ifa_prefixlen);
+               }
+       }
+
+       if (rta_tb[IFA_BROADCAST]) {
+               fprintf(fp, "brd %s ",
+                       rt_addr_n2a(ifa->ifa_family,
+                                   RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
+                                   RTA_DATA(rta_tb[IFA_BROADCAST]),
+                                   abuf, sizeof(abuf)));
+       }
+       if (rta_tb[IFA_ANYCAST]) {
+               fprintf(fp, "any %s ",
+                       rt_addr_n2a(ifa->ifa_family,
+                                   RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
+                                   RTA_DATA(rta_tb[IFA_ANYCAST]),
+                                   abuf, sizeof(abuf)));
+       }
+       fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+       if (ifa->ifa_flags&IFA_F_SECONDARY) {
+               ifa->ifa_flags &= ~IFA_F_SECONDARY;
+               fprintf(fp, "secondary ");
+       }
+       if (ifa->ifa_flags&IFA_F_TENTATIVE) {
+               ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+               fprintf(fp, "tentative ");
+       }
+       if (ifa->ifa_flags&IFA_F_DEPRECATED) {
+               ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+               fprintf(fp, "deprecated ");
+       }
+       if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
+               fprintf(fp, "dynamic ");
+       } else
+               ifa->ifa_flags &= ~IFA_F_PERMANENT;
+       if (ifa->ifa_flags)
+               fprintf(fp, "flags %02x ", ifa->ifa_flags);
+       if (rta_tb[IFA_LABEL])
+               fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+       if (rta_tb[IFA_CACHEINFO]) {
+               struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+               char buf[128];
+               fprintf(fp, "%s", _SL_);
+               if (ci->ifa_valid == 0xFFFFFFFFU)
+                       sprintf(buf, "valid_lft forever");
+               else
+                       sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
+               if (ci->ifa_prefered == 0xFFFFFFFFU)
+                       sprintf(buf+strlen(buf), " preferred_lft forever");
+               else
+                       sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
+               fprintf(fp, "       %s", buf);
+       }
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
+
+
+struct nlmsg_list
+{
+       struct nlmsg_list *next;
+       struct nlmsghdr   h;
+};
+
+int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
+{
+       for ( ;ainfo ;  ainfo = ainfo->next) {
+               struct nlmsghdr *n = &ainfo->h;
+               struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+               if (n->nlmsg_type != RTM_NEWADDR)
+                       continue;
+
+               if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
+                       return -1;
+
+               if (ifa->ifa_index != ifindex || 
+                   (filter.family && filter.family != ifa->ifa_family))
+                       continue;
+
+               print_addrinfo(NULL, n, fp);
+       }
+       return 0;
+}
+
+
+int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
+       struct nlmsg_list *h;
+       struct nlmsg_list **lp;
+
+       h = malloc(n->nlmsg_len+sizeof(void*));
+       if (h == NULL)
+               return -1;
+
+       memcpy(&h->h, n, n->nlmsg_len);
+       h->next = NULL;
+
+       for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
+       *lp = h;
+
+       ll_remember_index(who, n, NULL);
+       return 0;
+}
+
+int ipaddr_list(int argc, char **argv)
+{
+       struct nlmsg_list *linfo = NULL;
+       struct nlmsg_list *ainfo = NULL;
+       struct nlmsg_list *l;
+       struct rtnl_handle rth;
+       char *filter_dev = NULL;
+       int no_link = 0;
+
+       ipaddr_reset_filter(oneline);
+       filter.showqueue = 1;
+
+       if (filter.family == AF_UNSPEC)
+               filter.family = preferred_family;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "to") == 0) {
+                       NEXT_ARG();
+                       get_prefix(&filter.pfx, *argv, filter.family);
+                       if (filter.family == AF_UNSPEC)
+                               filter.family = filter.pfx.family;
+               } else if (strcmp(*argv, "scope") == 0) {
+                       int scope = 0;
+                       NEXT_ARG();
+                       filter.scopemask = -1;
+                       if (rtnl_rtscope_a2n(&scope, *argv)) {
+                               if (strcmp(*argv, "all") != 0)
+                                       invarg("invalid \"scope\"\n", *argv);
+                               scope = RT_SCOPE_NOWHERE;
+                               filter.scopemask = 0;
+                       }
+                       filter.scope = scope;
+               } else if (strcmp(*argv, "up") == 0) {
+                       filter.up = 1;
+               } else if (strcmp(*argv, "label") == 0) {
+                       NEXT_ARG();
+                       filter.label = *argv;
+               } else {
+                       if (strcmp(*argv, "dev") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (filter_dev)
+                               duparg2("dev", *argv);
+                       filter_dev = *argv;
+               }
+               argv++; argc--;
+       }
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
+               perror("Cannot send dump request");
+               exit(1);
+       }
+
+       if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               exit(1);
+       }
+
+       if (filter_dev) {
+               filter.ifindex = ll_name_to_index(filter_dev);
+               if (filter.ifindex <= 0) {
+                       fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev);
+                       return -1;
+               }
+       }
+
+       if (filter.family != AF_PACKET) {
+               if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+                       perror("Cannot send dump request");
+                       exit(1);
+               }
+
+               if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
+                       fprintf(stderr, "Dump terminated\n");
+                       exit(1);
+               }
+       }
+
+
+       if (filter.family && filter.family != AF_PACKET) {
+               struct nlmsg_list **lp;
+               lp=&linfo;
+
+               if (filter.oneline)
+                       no_link = 1;
+
+               while ((l=*lp)!=NULL) {
+                       int ok = 0;
+                       struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+                       struct nlmsg_list *a;
+
+                       for (a=ainfo; a; a=a->next) {
+                               struct nlmsghdr *n = &a->h;
+                               struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+                               if (ifa->ifa_index != ifi->ifi_index || 
+                                   (filter.family && filter.family != ifa->ifa_family))
+                                       continue;
+                               if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+                                       continue;
+                               if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+                                       continue;
+                               if (filter.pfx.family || filter.label) {
+                                       struct rtattr *tb[IFA_MAX+1];
+                                       memset(tb, 0, sizeof(tb));
+                                       parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
+                                       if (!tb[IFA_LOCAL])
+                                               tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+
+                                       if (filter.pfx.family && tb[IFA_LOCAL]) {
+                                               inet_prefix dst;
+                                               memset(&dst, 0, sizeof(dst));
+                                               dst.family = ifa->ifa_family;
+                                               memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+                                               if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+                                                       continue;
+                                       }
+                                       if (filter.label) {
+                                               SPRINT_BUF(b1);
+                                               const char *label;
+                                               if (tb[IFA_LABEL])
+                                                       label = RTA_DATA(tb[IFA_LABEL]);
+                                               else
+                                                       label = ll_idx_n2a(ifa->ifa_index, b1);
+                                               if (fnmatch(filter.label, label, 0) != 0)
+                                                       continue;
+                                       }
+                               }
+
+                               ok = 1;
+                               break;
+                       }
+                       if (!ok)
+                               *lp = l->next;
+                       else
+                               lp = &l->next;
+               }
+       }
+
+       for (l=linfo; l; l = l->next) {
+               if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
+                       struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+                       if (filter.family != AF_PACKET)
+                               print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
+               }
+               fflush(stdout);
+       }
+
+       exit(0);
+}
+
+int ipaddr_list_link(int argc, char **argv)
+{
+       preferred_family = AF_PACKET;
+       do_link = 1;
+       return ipaddr_list(argc, argv);
+}
+
+void ipaddr_reset_filter(int oneline)
+{
+       memset(&filter, 0, sizeof(filter));
+       filter.oneline = oneline;
+}
+
+int default_scope(inet_prefix *lcl)
+{
+       if (lcl->family == AF_INET) {
+               if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
+                       return RT_SCOPE_HOST;
+       }
+       return 0;
+}
+
+int ipaddr_modify(int cmd, int argc, char **argv)
+{
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct ifaddrmsg        ifa;
+               char                    buf[256];
+       } req;
+       char  *d = NULL;
+       char  *l = NULL;
+       inet_prefix lcl;
+       inet_prefix peer;
+       int local_len = 0;
+       int peer_len = 0;
+       int brd_len = 0;
+       int any_len = 0;
+       int scoped = 0;
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = cmd;
+       req.ifa.ifa_family = preferred_family;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "peer") == 0 ||
+                   strcmp(*argv, "remote") == 0) {
+                       NEXT_ARG();
+
+                       if (peer_len)
+                               duparg("peer", *argv);
+                       get_prefix(&peer, *argv, req.ifa.ifa_family);
+                       peer_len = peer.bytelen;
+                       if (req.ifa.ifa_family == AF_UNSPEC)
+                               req.ifa.ifa_family = peer.family;
+                       addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
+                       req.ifa.ifa_prefixlen = peer.bitlen;
+               } else if (matches(*argv, "broadcast") == 0 ||
+                          strcmp(*argv, "brd") == 0) {
+                       inet_prefix addr;
+                       NEXT_ARG();
+                       if (brd_len)
+                               duparg("broadcast", *argv);
+                       if (strcmp(*argv, "+") == 0)
+                               brd_len = -1;
+                       else if (strcmp(*argv, "-") == 0)
+                               brd_len = -2;
+                       else {
+                               get_addr(&addr, *argv, req.ifa.ifa_family);
+                               if (req.ifa.ifa_family == AF_UNSPEC)
+                                       req.ifa.ifa_family = addr.family;
+                               addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
+                               brd_len = addr.bytelen;
+                       }
+               } else if (strcmp(*argv, "anycast") == 0) {
+                       inet_prefix addr;
+                       NEXT_ARG();
+                       if (any_len)
+                               duparg("anycast", *argv);
+                       get_addr(&addr, *argv, req.ifa.ifa_family);
+                       if (req.ifa.ifa_family == AF_UNSPEC)
+                               req.ifa.ifa_family = addr.family;
+                       addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
+                       any_len = addr.bytelen;
+               } else if (strcmp(*argv, "scope") == 0) {
+                       int scope = 0;
+                       NEXT_ARG();
+                       if (rtnl_rtscope_a2n(&scope, *argv))
+                               invarg(*argv, "invalid scope value.");
+                       req.ifa.ifa_scope = scope;
+                       scoped = 1;
+               } else if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       d = *argv;
+               } else if (strcmp(*argv, "label") == 0) {
+                       NEXT_ARG();
+                       l = *argv;
+                       addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
+               } else {
+                       if (strcmp(*argv, "local") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (local_len)
+                               duparg2("local", *argv);
+                       get_prefix(&lcl, *argv, req.ifa.ifa_family);
+                       if (req.ifa.ifa_family == AF_UNSPEC)
+                               req.ifa.ifa_family = lcl.family;
+                       addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
+                       local_len = lcl.bytelen;
+               }
+               argc--; argv++;
+       }
+       if (d == NULL) {
+               fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
+               return -1;
+       }
+       if (l && matches(d, l) != 0) {
+               fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
+               exit(1);
+       }
+
+       if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
+               peer = lcl;
+               addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+       }
+       if (req.ifa.ifa_prefixlen == 0)
+               req.ifa.ifa_prefixlen = lcl.bitlen;
+
+       if (brd_len < 0 && cmd != RTM_DELADDR) {
+               inet_prefix brd;
+               int i;
+               if (req.ifa.ifa_family != AF_INET) {
+                       fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n");
+                       return -1;
+               }
+               brd = peer;
+               if (brd.bitlen <= 30) {
+                       for (i=31; i>=brd.bitlen; i--) {
+                               if (brd_len == -1)
+                                       brd.data[0] |= htonl(1<<(31-i));
+                               else
+                                       brd.data[0] &= ~htonl(1<<(31-i));
+                       }
+                       addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
+                       brd_len = brd.bytelen;
+               }
+       }
+       if (!scoped && cmd != RTM_DELADDR)
+               req.ifa.ifa_scope = default_scope(&lcl);
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       ll_init_map(&rth);
+
+       if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
+               fprintf(stderr, "Cannot find device \"%s\"\n", d);
+               return -1;
+       }
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+               exit(2);
+
+       exit(0);
+}
+
+int do_ipaddr(int argc, char **argv)
+{
+       if (argc < 1)
+               return ipaddr_list(0, NULL);
+       if (matches(*argv, "add") == 0)
+               return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
+       if (matches(*argv, "delete") == 0)
+               return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
+       if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+           || matches(*argv, "lst") == 0)
+               return ipaddr_list(argc-1, argv+1);
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv);
+       exit(-1);
+}
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
new file mode 100644 (file)
index 0000000..90d60b4
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * iplink.c            "ip link".
+ *
+ *             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 <errno.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#include "busybox.h"
+
+static int on_off(char *msg)
+{
+       fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
+       return -1;
+}
+
+static int get_ctl_fd(void)
+{
+       int s_errno;
+       int fd;
+
+       fd = socket(PF_INET, SOCK_DGRAM, 0);
+       if (fd >= 0)
+               return fd;
+       s_errno = errno;
+       fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (fd >= 0)
+               return fd;
+       fd = socket(PF_INET6, SOCK_DGRAM, 0);
+       if (fd >= 0)
+               return fd;
+       errno = s_errno;
+       perror("Cannot create control socket");
+       return -1;
+}
+
+static int do_chflags(char *dev, __u32 flags, __u32 mask)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, dev);
+       fd = get_ctl_fd();
+       if (fd < 0)
+               return -1;
+       err = ioctl(fd, SIOCGIFFLAGS, &ifr);
+       if (err) {
+               perror("SIOCGIFFLAGS");
+               close(fd);
+               return -1;
+       }
+       if ((ifr.ifr_flags^flags)&mask) {
+               ifr.ifr_flags &= ~mask;
+               ifr.ifr_flags |= mask&flags;
+               err = ioctl(fd, SIOCSIFFLAGS, &ifr);
+               if (err)
+                       perror("SIOCSIFFLAGS");
+       }
+       close(fd);
+       return err;
+}
+
+static int do_changename(char *dev, char *newdev)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, dev);
+       strcpy(ifr.ifr_newname, newdev);
+       fd = get_ctl_fd();
+       if (fd < 0)
+               return -1;
+       err = ioctl(fd, SIOCSIFNAME, &ifr);
+       if (err) {
+               perror("SIOCSIFNAME");
+               close(fd);
+               return -1;
+       }
+       close(fd);
+       return err;
+}
+
+static int set_qlen(char *dev, int qlen)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = get_ctl_fd();
+       if (s < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev); 
+       ifr.ifr_qlen = qlen; 
+       if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
+               perror("SIOCSIFXQLEN");
+               close(s);
+               return -1;
+       }
+       close(s);
+
+       return 0; 
+}
+
+static int set_mtu(char *dev, int mtu)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = get_ctl_fd();
+       if (s < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev); 
+       ifr.ifr_mtu = mtu; 
+       if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
+               perror("SIOCSIFMTU");
+               close(s);
+               return -1;
+       }
+       close(s);
+
+       return 0; 
+}
+
+static int get_address(char *dev, int *htype)
+{
+       struct ifreq ifr;
+       struct sockaddr_ll me;
+       int alen;
+       int s;
+
+       s = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (s < 0) { 
+               perror("socket(PF_PACKET)");
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev);
+       if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+               perror("SIOCGIFINDEX");
+               close(s);
+               return -1;
+       }
+
+       memset(&me, 0, sizeof(me));
+       me.sll_family = AF_PACKET;
+       me.sll_ifindex = ifr.ifr_ifindex;
+       me.sll_protocol = htons(ETH_P_LOOP);
+       if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
+               perror("bind");
+               close(s);
+               return -1;
+       }
+
+       alen = sizeof(me);
+       if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
+               perror("getsockname");
+               close(s);
+               return -1;
+       }
+       close(s);
+       *htype = me.sll_hatype;
+       return me.sll_halen;
+}
+
+static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+{
+       int alen;
+
+       memset(ifr, 0, sizeof(*ifr));
+       strcpy(ifr->ifr_name, dev);
+       ifr->ifr_hwaddr.sa_family = hatype;
+       alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
+       if (alen < 0)
+               return -1;
+       if (alen != halen) {
+               fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
+               return -1;
+       }
+       return 0; 
+}
+
+static int set_address(struct ifreq *ifr, int brd)
+{
+       int s;
+
+       s = get_ctl_fd();
+       if (s < 0)
+               return -1;
+       if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
+               perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
+               close(s);
+               return -1;
+       }
+       close(s);
+       return 0; 
+}
+
+
+static int do_set(int argc, char **argv)
+{
+       char *dev = NULL;
+       __u32 mask = 0;
+       __u32 flags = 0;
+       int qlen = -1;
+       int mtu = -1;
+       char *newaddr = NULL;
+       char *newbrd = NULL;
+       struct ifreq ifr0, ifr1;
+       char *newname = NULL;
+       int htype, halen;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "up") == 0) {
+                       mask |= IFF_UP;
+                       flags |= IFF_UP;
+               } else if (strcmp(*argv, "down") == 0) {
+                       mask |= IFF_UP;
+                       flags &= ~IFF_UP;
+               } else if (strcmp(*argv, "name") == 0) {
+                       NEXT_ARG();
+                       newname = *argv;
+               } else if (strcmp(*argv, "mtu") == 0) {
+                       NEXT_ARG();
+                       if (mtu != -1)
+                               duparg("mtu", *argv);
+                       if (get_integer(&mtu, *argv, 0))
+                               invarg("Invalid \"mtu\" value\n", *argv);
+               } else if (strcmp(*argv, "multicast") == 0) {
+                       NEXT_ARG();
+                       mask |= IFF_MULTICAST;
+                       if (strcmp(*argv, "on") == 0) {
+                               flags |= IFF_MULTICAST;
+                       } else if (strcmp(*argv, "off") == 0) {
+                               flags &= ~IFF_MULTICAST;
+                       } else
+                               return on_off("multicast");
+               } else if (strcmp(*argv, "arp") == 0) {
+                       NEXT_ARG();
+                       mask |= IFF_NOARP;
+                       if (strcmp(*argv, "on") == 0) {
+                               flags &= ~IFF_NOARP;
+                       } else if (strcmp(*argv, "off") == 0) {
+                               flags |= IFF_NOARP;
+                       } else
+                               return on_off("noarp");
+               } else {
+                        if (strcmp(*argv, "dev") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (dev)
+                               duparg2("dev", *argv);
+                       dev = *argv;
+               }
+               argc--; argv++;
+       }
+
+       if (!dev) {
+               fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
+               exit(-1);
+       }
+
+       if (newaddr || newbrd) {
+               halen = get_address(dev, &htype);
+               if (halen < 0)
+                       return -1;
+               if (newaddr) {
+                       if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
+                               return -1;
+               }
+               if (newbrd) {
+                       if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
+                               return -1; 
+               }
+       }
+
+       if (newname && strcmp(dev, newname)) {
+               if (do_changename(dev, newname) < 0)
+                       return -1;
+               dev = newname;
+       }
+       if (qlen != -1) { 
+               if (set_qlen(dev, qlen) < 0)
+                       return -1; 
+       }
+       if (mtu != -1) { 
+               if (set_mtu(dev, mtu) < 0)
+                       return -1; 
+       }
+       if (newaddr || newbrd) {
+               if (newbrd) {
+                       if (set_address(&ifr1, 1) < 0)
+                               return -1; 
+               }
+               if (newaddr) {
+                       if (set_address(&ifr0, 0) < 0)
+                               return -1;
+               }
+       }
+       if (mask)
+               return do_chflags(dev, flags, mask);
+       return 0;
+}
+
+int do_iplink(int argc, char **argv)
+{
+       if (argc > 0) {
+               if (matches(*argv, "set") == 0)
+                       return do_set(argc-1, argv+1);
+               if (matches(*argv, "show") == 0 ||
+                   matches(*argv, "lst") == 0 ||
+                   matches(*argv, "list") == 0)
+                       return ipaddr_list_link(argc-1, argv+1);
+       } else
+               return ipaddr_list_link(0, NULL);
+
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
+       exit(-1);
+}
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
new file mode 100644 (file)
index 0000000..e38abcd
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * iproute.c           "ip route".
+ *
+ *             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
+ * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <linux/in_route.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#include "busybox.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+static struct
+{
+       int tb;
+       int flushp;
+       int flushe;
+       struct rtnl_handle *rth;
+       int protocol, protocolmask;
+       int scope, scopemask;
+       int type, typemask;
+       int tos, tosmask;
+       int iif, iifmask;
+       int oif, oifmask;
+       int realm, realmmask;
+       inet_prefix rprefsrc;
+       inet_prefix rvia;
+       inet_prefix rdst;
+       inet_prefix mdst;
+       inet_prefix rsrc;
+       inet_prefix msrc;
+} filter;
+
+int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = (FILE*)arg;
+       struct rtmsg *r = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       struct rtattr * tb[RTA_MAX+1];
+       char abuf[256];
+       int host_len = -1;
+       SPRINT_BUF(b1);
+       
+
+       if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
+               fprintf(stderr, "Not a route: %08x %08x %08x\n",
+                       n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+               return 0;
+       }
+       len -= NLMSG_LENGTH(sizeof(*r));
+       if (len < 0) {
+               fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+               return -1;
+       }
+
+       if (r->rtm_family == AF_INET6)
+               host_len = 128;
+       else if (r->rtm_family == AF_INET)
+               host_len = 32;
+
+       if (r->rtm_family == AF_INET6) {
+               if (filter.tb) {
+                       if (filter.tb < 0) {
+                               if (!(r->rtm_flags&RTM_F_CLONED))
+                                       return 0;
+                       } else {
+                               if (r->rtm_flags&RTM_F_CLONED)
+                                       return 0;
+                               if (filter.tb == RT_TABLE_LOCAL) {
+                                       if (r->rtm_type != RTN_LOCAL)
+                                               return 0;
+                               } else if (filter.tb == RT_TABLE_MAIN) {
+                                       if (r->rtm_type == RTN_LOCAL)
+                                               return 0;
+                               } else {
+                                       return 0;
+                               }
+                       }
+               }
+       } else {
+               if (filter.tb > 0 && filter.tb != r->rtm_table)
+                       return 0;
+       }
+       if (filter.rdst.family &&
+           (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len))
+               return 0;
+       if (filter.mdst.family &&
+           (r->rtm_family != filter.mdst.family ||
+            (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len)))
+               return 0;
+       if (filter.rsrc.family &&
+           (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len))
+               return 0;
+       if (filter.msrc.family &&
+           (r->rtm_family != filter.msrc.family ||
+            (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len)))
+               return 0;
+
+       memset(tb, 0, sizeof(tb));
+       parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+       if (n->nlmsg_type == RTM_DELROUTE)
+               fprintf(fp, "Deleted ");
+       if (r->rtm_type != RTN_UNICAST && !filter.type)
+               fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+       if (tb[RTA_DST]) {
+               if (r->rtm_dst_len != host_len) {
+                       fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
+                                                        RTA_PAYLOAD(tb[RTA_DST]),
+                                                        RTA_DATA(tb[RTA_DST]),
+                                                        abuf, sizeof(abuf)),
+                               r->rtm_dst_len
+                               );
+               } else {
+                       fprintf(fp, "%s ", format_host(r->rtm_family,
+                                                      RTA_PAYLOAD(tb[RTA_DST]),
+                                                      RTA_DATA(tb[RTA_DST]),
+                                                      abuf, sizeof(abuf))
+                               );
+               }
+       } else if (r->rtm_dst_len) {
+               fprintf(fp, "0/%d ", r->rtm_dst_len);
+       } else {
+               fprintf(fp, "default ");
+       }
+       if (tb[RTA_SRC]) {
+               if (r->rtm_src_len != host_len) {
+                       fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+                                                        RTA_PAYLOAD(tb[RTA_SRC]),
+                                                        RTA_DATA(tb[RTA_SRC]),
+                                                        abuf, sizeof(abuf)),
+                               r->rtm_src_len
+                               );
+               } else {
+                       fprintf(fp, "from %s ", format_host(r->rtm_family,
+                                                      RTA_PAYLOAD(tb[RTA_SRC]),
+                                                      RTA_DATA(tb[RTA_SRC]),
+                                                      abuf, sizeof(abuf))
+                               );
+               }
+       } else if (r->rtm_src_len) {
+               fprintf(fp, "from 0/%u ", r->rtm_src_len);
+       }
+       if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
+               fprintf(fp, "via %s ", 
+                       format_host(r->rtm_family,
+                                   RTA_PAYLOAD(tb[RTA_GATEWAY]),
+                                   RTA_DATA(tb[RTA_GATEWAY]),
+                                   abuf, sizeof(abuf)));
+       }
+       if (tb[RTA_OIF] && filter.oifmask != -1)
+               fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
+
+       if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
+               /* Do not use format_host(). It is our local addr
+                  and symbolic name will not be useful.
+                */
+               fprintf(fp, " src %s ", 
+                       rt_addr_n2a(r->rtm_family,
+                                   RTA_PAYLOAD(tb[RTA_PREFSRC]),
+                                   RTA_DATA(tb[RTA_PREFSRC]),
+                                   abuf, sizeof(abuf)));
+       }
+       if (tb[RTA_PRIORITY])
+               fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
+       if (r->rtm_family == AF_INET6) {
+               struct rta_cacheinfo *ci = NULL;
+               if (tb[RTA_CACHEINFO])
+                       ci = RTA_DATA(tb[RTA_CACHEINFO]);
+               if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+                       static int hz;
+                       if (!hz)
+                               hz = get_hz();
+                       if (r->rtm_flags & RTM_F_CLONED)
+                               fprintf(fp, "%s    cache ", _SL_);
+                       if (ci->rta_expires)
+                               fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+                       if (ci->rta_error != 0)
+                               fprintf(fp, " error %d", ci->rta_error);
+               } else if (ci) {
+                       if (ci->rta_error != 0)
+                               fprintf(fp, " error %d", ci->rta_error);
+               }
+       }
+       if (tb[RTA_IIF] && filter.iifmask != -1) {
+               fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+       }
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
+
+int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct rtmsg            r;
+               char                    buf[1024];
+       } req;
+       char  mxbuf[256];
+       struct rtattr * mxrta = (void*)mxbuf;
+       unsigned mxlock = 0;
+       char  *d = NULL;
+       int gw_ok = 0;
+       int dst_ok = 0;
+       //int nhs_ok = 0;
+       //int scope_ok = 0;
+       //int table_ok = 0;
+       int proto_ok = 0;
+       int type_ok = 0;
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+       req.n.nlmsg_type = cmd;
+       req.r.rtm_family = preferred_family;
+       req.r.rtm_table = RT_TABLE_MAIN;
+       req.r.rtm_scope = RT_SCOPE_NOWHERE;
+
+       if (cmd != RTM_DELROUTE) {
+               req.r.rtm_protocol = RTPROT_BOOT;
+               req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+               req.r.rtm_type = RTN_UNICAST;
+       }
+
+       mxrta->rta_type = RTA_METRICS;
+       mxrta->rta_len = RTA_LENGTH(0);
+
+       while (argc > 0) {
+               if (strcmp(*argv, "src") == 0) {
+                       inet_prefix addr;
+                       NEXT_ARG();
+                       get_addr(&addr, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC)
+                               req.r.rtm_family = addr.family;
+                       addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
+               } else if (strcmp(*argv, "via") == 0) {
+                       inet_prefix addr;
+                       gw_ok = 1;
+                       NEXT_ARG();
+                       get_addr(&addr, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC)
+                               req.r.rtm_family = addr.family;
+                       addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
+               } else if (strcmp(*argv, "mtu") == 0) {
+                       unsigned mtu;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "lock") == 0) {
+                               mxlock |= (1<<RTAX_MTU);
+                               NEXT_ARG();
+                       }
+                       if (get_unsigned(&mtu, *argv, 0))
+                               invarg("\"mtu\" value is invalid\n", *argv);
+                       rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+               } else if (matches(*argv, "protocol") == 0) {
+                       int prot;
+                       NEXT_ARG();
+                       if (rtnl_rtprot_a2n(&prot, *argv))
+                               invarg("\"protocol\" value is invalid\n", *argv);
+                       req.r.rtm_protocol = prot;
+                       proto_ok =1;
+               } else if (strcmp(*argv, "dev") == 0 ||
+                          strcmp(*argv, "oif") == 0) {
+                       NEXT_ARG();
+                       d = *argv;
+               } else {
+                       int type;
+                       inet_prefix dst;
+
+                       if (strcmp(*argv, "to") == 0) {
+                               NEXT_ARG();
+                       }
+                       if ((**argv < '0' || **argv > '9') &&
+                           rtnl_rtntype_a2n(&type, *argv) == 0) {
+                               NEXT_ARG();
+                               req.r.rtm_type = type;
+                               type_ok = 1;
+                       }
+
+                       if (dst_ok)
+                               duparg2("to", *argv);
+                       get_prefix(&dst, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC)
+                               req.r.rtm_family = dst.family;
+                       req.r.rtm_dst_len = dst.bitlen;
+                       dst_ok = 1;
+                       if (dst.bytelen)
+                               addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+               }
+               argc--; argv++;
+       }
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       if (mxrta->rta_len > RTA_LENGTH(0)) {
+               if (mxlock)
+                       rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
+               addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
+       }
+
+       if (req.r.rtm_family == AF_UNSPEC)
+               req.r.rtm_family = AF_INET;
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+               exit(2);
+
+       return 0;
+}
+
+static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct rtmsg rtm;
+       } req;
+       struct sockaddr_nl nladdr;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       memset(&req, 0, sizeof(req));
+       nladdr.nl_family = AF_NETLINK;
+
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = RTM_GETROUTE;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+       req.rtm.rtm_family = family;
+       req.rtm.rtm_flags |= RTM_F_CLONED;
+
+       return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+static int iproute_list(int argc, char **argv)
+{
+       int do_ipv6 = preferred_family;
+       struct rtnl_handle rth;
+       char *id = NULL;
+       char *od = NULL;
+
+       iproute_reset_filter();
+       filter.tb = RT_TABLE_MAIN;
+
+       while (argc > 0) {
+               if (matches(*argv, "protocol") == 0) {
+                       int prot = 0;
+                       NEXT_ARG();
+                       filter.protocolmask = -1;
+                       if (rtnl_rtprot_a2n(&prot, *argv)) {
+                               if (strcmp(*argv, "all") != 0)
+                                       invarg("invalid \"protocol\"\n", *argv);
+                               prot = 0;
+                               filter.protocolmask = 0;
+                       }
+                       filter.protocol = prot;
+               } else if (strcmp(*argv, "dev") == 0 ||
+                          strcmp(*argv, "oif") == 0) {
+                       NEXT_ARG();
+                       od = *argv;
+               } else if (strcmp(*argv, "iif") == 0) {
+                       NEXT_ARG();
+                       id = *argv;
+               } else if (matches(*argv, "from") == 0) {
+                       NEXT_ARG();
+                       if (matches(*argv, "root") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.rsrc, *argv, do_ipv6);
+                       } else if (matches(*argv, "match") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.msrc, *argv, do_ipv6);
+                       } else {
+                               if (matches(*argv, "exact") == 0) {
+                                       NEXT_ARG();
+                               }
+                               get_prefix(&filter.msrc, *argv, do_ipv6);
+                               filter.rsrc = filter.msrc;
+                       }
+               } else {
+                       if (matches(*argv, "to") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (matches(*argv, "root") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.rdst, *argv, do_ipv6);
+                       } else if (matches(*argv, "match") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.mdst, *argv, do_ipv6);
+                       } else {
+                               if (matches(*argv, "exact") == 0) {
+                                       NEXT_ARG();
+                               }
+                               get_prefix(&filter.mdst, *argv, do_ipv6);
+                               filter.rdst = filter.mdst;
+                       }
+               }
+               argc--; argv++;
+       }
+
+       if (do_ipv6 == AF_UNSPEC && filter.tb)
+               do_ipv6 = AF_INET;
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       ll_init_map(&rth);
+
+       if (id || od)  {
+               int idx;
+
+               if (id) {
+                       if ((idx = ll_name_to_index(id)) == 0) {
+                               fprintf(stderr, "Cannot find device \"%s\"\n", id);
+                               return -1;
+                       }
+                       filter.iif = idx;
+                       filter.iifmask = -1;
+               }
+               if (od) {
+                       if ((idx = ll_name_to_index(od)) == 0) {
+                               fprintf(stderr, "Cannot find device \"%s\"\n", od);
+                               return -1;
+                       }
+                       filter.oif = idx;
+                       filter.oifmask = -1;
+               }
+       }
+
+       if (filter.tb != -1) {
+               if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+                       perror("Cannot send dump request");
+                       exit(1);
+               }
+       } else {
+               if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+                       perror("Cannot send dump request");
+                       exit(1);
+               }
+       }
+
+       if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               exit(1);
+       }
+
+       exit(0);
+}
+
+
+int iproute_get(int argc, char **argv)
+{
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct rtmsg            r;
+               char                    buf[1024];
+       } req;
+       char  *idev = NULL;
+       char  *odev = NULL;
+       int connected = 0;
+       int from_ok = 0;
+
+       memset(&req, 0, sizeof(req));
+
+       iproute_reset_filter();
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = RTM_GETROUTE;
+       req.r.rtm_family = preferred_family;
+       req.r.rtm_table = 0;
+       req.r.rtm_protocol = 0;
+       req.r.rtm_scope = 0;
+       req.r.rtm_type = 0;
+       req.r.rtm_src_len = 0;
+       req.r.rtm_dst_len = 0;
+       req.r.rtm_tos = 0;
+       
+       while (argc > 0) {
+               if (matches(*argv, "from") == 0) {
+                       inet_prefix addr;
+                       NEXT_ARG();
+                       from_ok = 1;
+                       get_prefix(&addr, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC)
+                               req.r.rtm_family = addr.family;
+                       if (addr.bytelen)
+                               addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
+                       req.r.rtm_src_len = addr.bitlen;
+               } else if (matches(*argv, "iif") == 0) {
+                       NEXT_ARG();
+                       idev = *argv;
+               } else if (matches(*argv, "oif") == 0 ||
+                          strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       odev = *argv;
+               } else if (matches(*argv, "notify") == 0) {
+                       req.r.rtm_flags |= RTM_F_NOTIFY;
+               } else if (matches(*argv, "connected") == 0) {
+                       connected = 1;
+               } else {
+                       inet_prefix addr;
+                       if (strcmp(*argv, "to") == 0) {
+                               NEXT_ARG();
+                       }
+                       get_prefix(&addr, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC)
+                               req.r.rtm_family = addr.family;
+                       if (addr.bytelen)
+                               addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
+                       req.r.rtm_dst_len = addr.bitlen;
+               }
+               argc--; argv++;
+       }
+
+       if (req.r.rtm_dst_len == 0) {
+               fprintf(stderr, "need at least destination address\n");
+               exit(1);
+       }
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       ll_init_map(&rth);
+
+       if (idev || odev)  {
+               int idx;
+
+               if (idev) {
+                       if ((idx = ll_name_to_index(idev)) == 0) {
+                               fprintf(stderr, "Cannot find device \"%s\"\n", idev);
+                               return -1;
+                       }
+                       addattr32(&req.n, sizeof(req), RTA_IIF, idx);
+               }
+               if (odev) {
+                       if ((idx = ll_name_to_index(odev)) == 0) {
+                               fprintf(stderr, "Cannot find device \"%s\"\n", odev);
+                               return -1;
+                       }
+                       addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+               }
+       }
+
+       if (req.r.rtm_family == AF_UNSPEC)
+               req.r.rtm_family = AF_INET;
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0)
+               exit(2);
+
+       if (connected && !from_ok) {
+               struct rtmsg *r = NLMSG_DATA(&req.n);
+               int len = req.n.nlmsg_len;
+               struct rtattr * tb[RTA_MAX+1];
+
+               if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+                       fprintf(stderr, "An error :-)\n");
+                       exit(1);
+               }
+
+               if (req.n.nlmsg_type != RTM_NEWROUTE) {
+                       fprintf(stderr, "Not a route?\n");
+                       return -1;
+               }
+               len -= NLMSG_LENGTH(sizeof(*r));
+               if (len < 0) {
+                       fprintf(stderr, "Wrong len %d\n", len);
+                       return -1;
+               }
+
+               memset(tb, 0, sizeof(tb));
+               parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+               if (tb[RTA_PREFSRC]) {
+                       tb[RTA_PREFSRC]->rta_type = RTA_SRC;
+                       r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
+               } else if (!tb[RTA_SRC]) {
+                       fprintf(stderr, "Failed to connect the route\n");
+                       return -1;
+               }
+               if (!odev && tb[RTA_OIF])
+                       tb[RTA_OIF]->rta_type = 0;
+               if (tb[RTA_GATEWAY])
+                       tb[RTA_GATEWAY]->rta_type = 0;
+               if (!idev && tb[RTA_IIF])
+                       tb[RTA_IIF]->rta_type = 0;
+               req.n.nlmsg_flags = NLM_F_REQUEST;
+               req.n.nlmsg_type = RTM_GETROUTE;
+
+               if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0)
+                       exit(2);
+       }
+
+       if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+               fprintf(stderr, "An error :-)\n");
+               exit(1);
+       }
+
+       exit(0);
+}
+
+void iproute_reset_filter()
+{
+       memset(&filter, 0, sizeof(filter));
+       filter.mdst.bitlen = -1;
+       filter.msrc.bitlen = -1;
+}
+
+int do_iproute(int argc, char **argv)
+{
+       if (argc < 1)
+               return iproute_list(0, NULL);
+       
+       if (matches(*argv, "add") == 0)
+               return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL,
+                                     argc-1, argv+1);
+       if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0)
+               return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE,
+                                     argc-1, argv+1);
+       if (matches(*argv, "replace") == 0)
+               return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE,
+                                     argc-1, argv+1);
+       if (matches(*argv, "prepend") == 0)
+               return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE,
+                                     argc-1, argv+1);
+       if (matches(*argv, "append") == 0)
+               return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND,
+                                     argc-1, argv+1);
+       if (matches(*argv, "test") == 0)
+               return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL,
+                                     argc-1, argv+1);
+       if (matches(*argv, "delete") == 0)
+               return iproute_modify(RTM_DELROUTE, 0,
+                                     argc-1, argv+1);
+       if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+           || matches(*argv, "lst") == 0)
+               return iproute_list(argc-1, argv+1);
+       if (matches(*argv, "get") == 0)
+               return iproute_get(argc-1, argv+1);
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
+       exit(-1);
+}
+
diff --git a/networking/libiproute/iptunnel.c b/networking/libiproute/iptunnel.c
new file mode 100644 (file)
index 0000000..beb8bb6
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * 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
+ * Phil Karn <karn@ka9q.ampr.org>      990408: "pmtudisc" flag
+ */
+
+#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 <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "busybox.h"
+
+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;
+
+       if (cmd == SIOCCHGTUNNEL && p->name[0])
+               strcpy(ifr.ifr_name, p->name);
+       else
+               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;
+
+       if (p->name[0])
+               strcpy(ifr.ifr_name, p->name);
+       else
+               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, int cmd, struct ip_tunnel_parm *p)
+{
+       int count = 0;
+       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 ||
+                           strcmp(*argv, "ip/ip") == 0) {
+                               if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
+                                       fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+                                       exit(-1);
+                               }
+                               p->iph.protocol = IPPROTO_IPIP;
+                       } else if (strcmp(*argv, "gre") == 0 ||
+                                  strcmp(*argv, "gre/ip") == 0) {
+                               if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+                                       fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+                                       exit(-1);
+                               }
+                               p->iph.protocol = IPPROTO_GRE;
+                       } else if (strcmp(*argv, "sit") == 0 ||
+                                  strcmp(*argv, "ipv6/ip") == 0) {
+                               if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+                                       fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+                                       exit(-1);
+                               }
+                               p->iph.protocol = IPPROTO_IPV6;
+                       } else {
+                               fprintf(stderr,"Cannot guess tunnel mode.\n");
+                               exit(-1);
+                       }
+               } 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 (get_unsigned(&uval, *argv, 0)<0) {
+                                       fprintf(stderr, "invalid value of \"key\"\n");
+                                       exit(-1);
+                               }
+                               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 (get_unsigned(&uval, *argv, 0)<0) {
+                                       fprintf(stderr, "invalid value of \"ikey\"\n");
+                                       exit(-1);
+                               }
+                               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 (get_unsigned(&uval, *argv, 0)<0) {
+                                       fprintf(stderr, "invalid value of \"okey\"\n");
+                                       exit(-1);
+                               }
+                               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, "pmtudisc") == 0) {
+                       p->iph.frag_off = htons(IP_DF);
+               } 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 (get_unsigned(&uval, *argv, 0))
+                                       invarg("invalid TTL\n", *argv);
+                               if (uval > 255)
+                                       invarg("TTL must be <=255\n", *argv);
+                               p->iph.ttl = uval;
+                       }
+               } else if (strcmp(*argv, "tos") == 0 ||
+                          matches(*argv, "dsfield") == 0) {
+                       __u32 uval;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "inherit") != 0) {
+                               if (rtnl_dsfield_a2n(&uval, *argv))
+                                       invarg("bad TOS value", *argv);
+                               p->iph.tos = uval;
+                       } else
+                               p->iph.tos = 1;
+               } else {
+                       if (strcmp(*argv, "name") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (p->name[0])
+                               duparg2("name", *argv);
+                       strncpy(p->name, *argv, IFNAMSIZ);
+                       if (cmd == SIOCCHGTUNNEL && count == 0) {
+                               struct ip_tunnel_parm old_p;
+                               memset(&old_p, 0, sizeof(old_p));
+                               if (do_get_ioctl(*argv, &old_p))
+                                       return -1;
+                               *p = old_p;
+                       }
+               }
+               count++;
+               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, cmd, &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, SIOCDELTUNNEL, &p) < 0)
+               return -1;
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:
+               return do_del_ioctl("tunl0", &p);
+       case IPPROTO_GRE:
+               return do_del_ioctl("gre0", &p);
+       case IPPROTO_IPV6:
+               return do_del_ioctl("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, 4, &p->iph.daddr, s1, sizeof(s1));
+       format_host(AF_INET, 4, &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) {
+               SPRINT_BUF(b1);
+               printf(" tos");
+               if (p->iph.tos&1)
+                       printf(" inherit");
+               if (p->iph.tos&~1)
+                       printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
+                              rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
+       }
+       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);
+       }
+
+       if (p->i_flags&GRE_SEQ)
+               printf("%s  Drop packets out of sequence.\n", _SL_);
+       if (p->i_flags&GRE_CSUM)
+               printf("%s  Checksum in received packet is required.", _SL_);
+       if (p->o_flags&GRE_SEQ)
+               printf("%s  Sequence packets on output.", _SL_);
+       if (p->o_flags&GRE_CSUM)
+               printf("%s  Checksum output packets.", _SL_);
+}
+
+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);
+               printf("\n");
+       }
+       return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+       int err;
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, SIOCGETTUNNEL, &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);
+       printf("\n");
+       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);
+
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv);
+       exit(-1);
+}
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
new file mode 100644 (file)
index 0000000..a1f39d4
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * libnetlink.c        RTnetlink service routines.
+ *
+ *             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 <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+       close(rth->fd);
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+       int addr_len;
+
+       memset(rth, 0, sizeof(rth));
+
+       rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       if (rth->fd < 0) {
+               perror("Cannot open netlink socket");
+               return -1;
+       }
+
+       memset(&rth->local, 0, sizeof(rth->local));
+       rth->local.nl_family = AF_NETLINK;
+       rth->local.nl_groups = subscriptions;
+
+       if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+               perror("Cannot bind netlink socket");
+               return -1;
+       }
+       addr_len = sizeof(rth->local);
+       if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+               perror("Cannot getsockname");
+               return -1;
+       }
+       if (addr_len != sizeof(rth->local)) {
+               fprintf(stderr, "Wrong address length %d\n", addr_len);
+               return -1;
+       }
+       if (rth->local.nl_family != AF_NETLINK) {
+               fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+               return -1;
+       }
+       rth->seq = time(NULL);
+       return 0;
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct rtgenmsg g;
+       } req;
+       struct sockaddr_nl nladdr;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = type;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+       req.g.rtgen_family = family;
+
+       return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
+{
+       struct sockaddr_nl nladdr;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+       struct nlmsghdr nlh;
+       struct sockaddr_nl nladdr;
+       struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
+       struct msghdr msg = {
+               (void*)&nladdr, sizeof(nladdr),
+               iov,    2,
+               NULL,   0,
+               0
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       nlh.nlmsg_len = NLMSG_LENGTH(len);
+       nlh.nlmsg_type = type;
+       nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       nlh.nlmsg_pid = 0;
+       nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+       return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+                    int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+                    void *arg1,
+                    int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                    void *arg2)
+{
+       char    buf[8192];
+       struct sockaddr_nl nladdr;
+       struct iovec iov = { buf, sizeof(buf) };
+
+       while (1) {
+               int status;
+               struct nlmsghdr *h;
+
+               struct msghdr msg = {
+                       (void*)&nladdr, sizeof(nladdr),
+                       &iov,   1,
+                       NULL,   0,
+                       0
+               };
+
+               status = recvmsg(rth->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+                       exit(1);
+               }
+
+               h = (struct nlmsghdr*)buf;
+               while (NLMSG_OK(h, status)) {
+                       int err;
+
+                       if (h->nlmsg_pid != rth->local.nl_pid ||
+                           h->nlmsg_seq != rth->dump) {
+                               if (junk) {
+                                       err = junk(&nladdr, h, arg2);
+                                       if (err < 0)
+                                               return err;
+                               }
+                               goto skip_it;
+                       }
+
+                       if (h->nlmsg_type == NLMSG_DONE)
+                               return 0;
+                       if (h->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                               if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                                       fprintf(stderr, "ERROR truncated\n");
+                               } else {
+                                       errno = -err->error;
+                                       perror("RTNETLINK answers");
+                               }
+                               return -1;
+                       }
+                       err = filter(&nladdr, h, arg1);
+                       if (err < 0)
+                               return err;
+
+skip_it:
+                       h = NLMSG_NEXT(h, status);
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       fprintf(stderr, "Message truncated\n");
+                       continue;
+               }
+               if (status) {
+                       fprintf(stderr, "!!!Remnant of size %d\n", status);
+                       exit(1);
+               }
+       }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+             unsigned groups, struct nlmsghdr *answer,
+             int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+             void *jarg)
+{
+       int status;
+       unsigned seq;
+       struct nlmsghdr *h;
+       struct sockaddr_nl nladdr;
+       struct iovec iov = { (void*)n, n->nlmsg_len };
+       char   buf[8192];
+       struct msghdr msg = {
+               (void*)&nladdr, sizeof(nladdr),
+               &iov,   1,
+               NULL,   0,
+               0
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = peer;
+       nladdr.nl_groups = groups;
+
+       n->nlmsg_seq = seq = ++rtnl->seq;
+       if (answer == NULL)
+               n->nlmsg_flags |= NLM_F_ACK;
+
+       status = sendmsg(rtnl->fd, &msg, 0);
+
+       if (status < 0) {
+               perror("Cannot talk to rtnetlink");
+               return -1;
+       }
+
+       iov.iov_base = buf;
+
+       while (1) {
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rtnl->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+                       exit(1);
+               }
+               for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+                       int err;
+                       int len = h->nlmsg_len;
+                       int l = len - sizeof(*h);
+
+                       if (l<0 || len>status) {
+                               if (msg.msg_flags & MSG_TRUNC) {
+                                       fprintf(stderr, "Truncated message\n");
+                                       return -1;
+                               }
+                               fprintf(stderr, "!!!malformed message: len=%d\n", len);
+                               exit(1);
+                       }
+
+                       if (h->nlmsg_pid != rtnl->local.nl_pid ||
+                           h->nlmsg_seq != seq) {
+                               if (junk) {
+                                       err = junk(&nladdr, h, jarg);
+                                       if (err < 0)
+                                               return err;
+                               }
+                               continue;
+                       }
+
+                       if (h->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                               if (l < sizeof(struct nlmsgerr)) {
+                                       fprintf(stderr, "ERROR truncated\n");
+                               } else {
+                                       errno = -err->error;
+                                       if (errno == 0) {
+                                               if (answer)
+                                                       memcpy(answer, h, h->nlmsg_len);
+                                               return 0;
+                                       }
+                                       perror("RTNETLINK answers");
+                               }
+                               return -1;
+                       }
+                       if (answer) {
+                               memcpy(answer, h, h->nlmsg_len);
+                               return 0;
+                       }
+
+                       fprintf(stderr, "Unexpected reply!!!\n");
+
+                       status -= NLMSG_ALIGN(len);
+                       h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       fprintf(stderr, "Message truncated\n");
+                       continue;
+               }
+               if (status) {
+                       fprintf(stderr, "!!!Remnant of size %d\n", status);
+                       exit(1);
+               }
+       }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl, 
+             int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+             void *jarg)
+{
+       int status;
+       struct nlmsghdr *h;
+       struct sockaddr_nl nladdr;
+       struct iovec iov;
+       char   buf[8192];
+       struct msghdr msg = {
+               (void*)&nladdr, sizeof(nladdr),
+               &iov,   1,
+               NULL,   0,
+               0
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = 0;
+       nladdr.nl_groups = 0;
+
+
+       iov.iov_base = buf;
+
+       while (1) {
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rtnl->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       fprintf(stderr, "EOF on netlink\n");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+                       exit(1);
+               }
+               for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+                       int err;
+                       int len = h->nlmsg_len;
+                       int l = len - sizeof(*h);
+
+                       if (l<0 || len>status) {
+                               if (msg.msg_flags & MSG_TRUNC) {
+                                       fprintf(stderr, "Truncated message\n");
+                                       return -1;
+                               }
+                               fprintf(stderr, "!!!malformed message: len=%d\n", len);
+                               exit(1);
+                       }
+
+                       err = handler(&nladdr, h, jarg);
+                       if (err < 0)
+                               return err;
+
+                       status -= NLMSG_ALIGN(len);
+                       h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       fprintf(stderr, "Message truncated\n");
+                       continue;
+               }
+               if (status) {
+                       fprintf(stderr, "!!!Remnant of size %d\n", status);
+                       exit(1);
+               }
+       }
+}
+
+int rtnl_from_file(FILE *rtnl, 
+             int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+             void *jarg)
+{
+       int status;
+       struct sockaddr_nl nladdr;
+       char   buf[8192];
+       struct nlmsghdr *h = (void*)buf;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = 0;
+       nladdr.nl_groups = 0;
+
+       while (1) {
+               int err, len, type;
+               int l;
+
+               status = fread(&buf, 1, sizeof(*h), rtnl);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("rtnl_from_file: fread");
+                       return -1;
+               }
+               if (status == 0)
+                       return 0;
+
+               len = h->nlmsg_len;
+               type= h->nlmsg_type;
+               l = len - sizeof(*h);
+
+               if (l<0 || len>sizeof(buf)) {
+                       fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+                               len, ftell(rtnl));
+                       return -1;
+               }
+
+               status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+               if (status < 0) {
+                       perror("rtnl_from_file: fread");
+                       return -1;
+               }
+               if (status < l) {
+                       fprintf(stderr, "rtnl-from_file: truncated message\n");
+                       return -1;
+               }
+
+               err = handler(&nladdr, h, jarg);
+               if (err < 0)
+                       return err;
+       }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *rta;
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+               return -1;
+       rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), &data, 4);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+       return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+{
+       int len = RTA_LENGTH(alen);
+       struct rtattr *rta;
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+               return -1;
+       rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), data, alen);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+       return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *subrta;
+
+       if (RTA_ALIGN(rta->rta_len) + len > maxlen)
+               return -1;
+       subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+       subrta->rta_type = type;
+       subrta->rta_len = len;
+       memcpy(RTA_DATA(subrta), &data, 4);
+       rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+       return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+{
+       struct rtattr *subrta;
+       int len = RTA_LENGTH(alen);
+
+       if (RTA_ALIGN(rta->rta_len) + len > maxlen)
+               return -1;
+       subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+       subrta->rta_type = type;
+       subrta->rta_len = len;
+       memcpy(RTA_DATA(subrta), data, alen);
+       rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+       return 0;
+}
+
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+       while (RTA_OK(rta, len)) {
+               if (rta->rta_type <= max)
+                       tb[rta->rta_type] = rta;
+               rta = RTA_NEXT(rta,len);
+       }
+       if (len)
+               fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+       return 0;
+}
diff --git a/networking/libiproute/libnetlink.h b/networking/libiproute/libnetlink.h
new file mode 100644 (file)
index 0000000..45d3ad2
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+       int                     fd;
+       struct sockaddr_nl      local;
+       struct sockaddr_nl      peer;
+       __u32                   seq;
+       __u32                   dump;
+};
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+extern int rtnl_dump_filter(struct rtnl_handle *rth,
+                           int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+                           void *arg1,
+                           int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                           void *arg2);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+                    unsigned groups, struct nlmsghdr *answer,
+                    int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                    void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                      void *jarg);
+extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                      void *jarg);
+
+#endif /* __LIBNETLINK_H__ */
+
diff --git a/networking/libiproute/linux/pkt_sched.h b/networking/libiproute/linux/pkt_sched.h
new file mode 100644 (file)
index 0000000..e174588
--- /dev/null
@@ -0,0 +1,413 @@
+#ifndef __LINUX_PKT_SCHED_H
+#define __LINUX_PKT_SCHED_H
+
+/* Logical priority bands not depending on specific packet scheduler.
+   Every scheduler will map them to real traffic classes, if it has
+   no more precise mechanism to classify packets.
+
+   These numbers have no special meaning, though their coincidence
+   with obsolete IPv6 values is not occasional :-). New IPv6 drafts
+   preferred full anarchy inspired by diffserv group.
+
+   Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
+   class, actually, as rule it will be handled with more care than
+   filler or even bulk.
+ */
+
+#include <asm/types.h>
+
+#define TC_PRIO_BESTEFFORT             0
+#define TC_PRIO_FILLER                 1
+#define TC_PRIO_BULK                   2
+#define TC_PRIO_INTERACTIVE_BULK       4
+#define TC_PRIO_INTERACTIVE            6
+#define TC_PRIO_CONTROL                        7
+
+#define TC_PRIO_MAX                    15
+
+/* Generic queue statistics, available for all the elements.
+   Particular schedulers may have also their private records.
+ */
+
+struct tc_stats
+{
+       __u64   bytes;                  /* NUmber of enqueues bytes */
+       __u32   packets;                /* Number of enqueued packets   */
+       __u32   drops;                  /* Packets dropped because of lack of resources */
+       __u32   overlimits;             /* Number of throttle events when this
+                                        * flow goes out of allocated bandwidth */
+       __u32   bps;                    /* Current flow byte rate */
+       __u32   pps;                    /* Current flow packet rate */
+       __u32   qlen;
+       __u32   backlog;
+#ifdef __KERNEL__
+       spinlock_t *lock;
+#endif
+};
+
+struct tc_estimator
+{
+       char            interval;
+       unsigned char   ewma_log;
+};
+
+/* "Handles"
+   ---------
+
+    All the traffic control objects have 32bit identifiers, or "handles".
+
+    They can be considered as opaque numbers from user API viewpoint,
+    but actually they always consist of two fields: major and
+    minor numbers, which are interpreted by kernel specially,
+    that may be used by applications, though not recommended.
+
+    F.e. qdisc handles always have minor number equal to zero,
+    classes (or flows) have major equal to parent qdisc major, and
+    minor uniquely identifying class inside qdisc.
+
+    Macros to manipulate handles:
+ */
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+#define TC_H_UNSPEC    (0U)
+#define TC_H_ROOT      (0xFFFFFFFFU)
+#define TC_H_INGRESS    (0xFFFFFFF1U)
+
+struct tc_ratespec
+{
+       unsigned char   cell_log;
+       unsigned char   __reserved;
+       unsigned short  feature;
+       short           addend;
+       unsigned short  mpu;
+       __u32           rate;
+};
+
+/* FIFO section */
+
+struct tc_fifo_qopt
+{
+       __u32   limit;  /* Queue length: bytes for bfifo, packets for pfifo */
+};
+
+/* PRIO section */
+
+#define TCQ_PRIO_BANDS 16
+
+struct tc_prio_qopt
+{
+       int     bands;                  /* Number of bands */
+       __u8    priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
+};
+
+/* CSZ section */
+
+struct tc_csz_qopt
+{
+       int             flows;          /* Maximal number of guaranteed flows */
+       unsigned char   R_log;          /* Fixed point position for round number */
+       unsigned char   delta_log;      /* Log of maximal managed time interval */
+       __u8            priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
+};
+
+struct tc_csz_copt
+{
+       struct tc_ratespec slice;
+       struct tc_ratespec rate;
+       struct tc_ratespec peakrate;
+       __u32           limit;
+       __u32           buffer;
+       __u32           mtu;
+};
+
+enum
+{
+       TCA_CSZ_UNSPEC,
+       TCA_CSZ_PARMS,
+       TCA_CSZ_RTAB,
+       TCA_CSZ_PTAB,
+};
+
+/* TBF section */
+
+struct tc_tbf_qopt
+{
+       struct tc_ratespec rate;
+       struct tc_ratespec peakrate;
+       __u32           limit;
+       __u32           buffer;
+       __u32           mtu;
+};
+
+enum
+{
+       TCA_TBF_UNSPEC,
+       TCA_TBF_PARMS,
+       TCA_TBF_RTAB,
+       TCA_TBF_PTAB,
+};
+
+
+/* TEQL section */
+
+/* TEQL does not require any parameters */
+
+/* SFQ section */
+
+struct tc_sfq_qopt
+{
+       unsigned        quantum;        /* Bytes per round allocated to flow */
+       int             perturb_period; /* Period of hash perturbation */
+       __u32           limit;          /* Maximal packets in queue */
+       unsigned        divisor;        /* Hash divisor  */
+       unsigned        flows;          /* Maximal number of flows  */
+};
+
+/*
+ *  NOTE: limit, divisor and flows are hardwired to code at the moment.
+ *
+ *     limit=flows=128, divisor=1024;
+ *
+ *     The only reason for this is efficiency, it is possible
+ *     to change these parameters in compile time.
+ */
+
+/* RED section */
+
+enum
+{
+       TCA_RED_UNSPEC,
+       TCA_RED_PARMS,
+       TCA_RED_STAB,
+};
+
+struct tc_red_qopt
+{
+       __u32           limit;          /* HARD maximal queue length (bytes)    */
+       __u32           qth_min;        /* Min average length threshold (bytes) */
+       __u32           qth_max;        /* Max average length threshold (bytes) */
+       unsigned char   Wlog;           /* log(W)               */
+       unsigned char   Plog;           /* log(P_max/(qth_max-qth_min)) */
+       unsigned char   Scell_log;      /* cell size for idle damping */
+       unsigned char   flags;
+#define TC_RED_ECN     1
+};
+
+struct tc_red_xstats
+{
+       __u32           early;          /* Early drops */
+       __u32           pdrop;          /* Drops due to queue limits */
+       __u32           other;          /* Drops due to drop() calls */
+       __u32           marked;         /* Marked packets */
+};
+
+/* GRED section */
+
+#define MAX_DPs 16
+
+enum
+{
+       TCA_GRED_UNSPEC,
+       TCA_GRED_PARMS,
+       TCA_GRED_STAB,
+       TCA_GRED_DPS,
+};
+
+#define TCA_SET_OFF TCA_GRED_PARMS
+struct tc_gred_qopt
+{
+       __u32           limit;          /* HARD maximal queue length (bytes)    
+*/
+       __u32           qth_min;        /* Min average length threshold (bytes) 
+*/
+       __u32           qth_max;        /* Max average length threshold (bytes) 
+*/
+       __u32           DP;             /* upto 2^32 DPs */
+       __u32           backlog;        
+       __u32           qave;   
+       __u32           forced; 
+       __u32           early;  
+       __u32           other;  
+       __u32           pdrop;  
+
+       unsigned char   Wlog;           /* log(W)               */
+       unsigned char   Plog;           /* log(P_max/(qth_max-qth_min)) */
+       unsigned char   Scell_log;      /* cell size for idle damping */
+       __u8            prio;           /* prio of this VQ */
+       __u32   packets;
+       __u32   bytesin;
+};
+/* gred setup */
+struct tc_gred_sopt
+{
+       __u32           DPs;
+       __u32           def_DP;
+       __u8            grio;
+};
+
+/* HTB section */
+#define TC_HTB_NUMPRIO         4
+#define TC_HTB_MAXDEPTH                4
+
+struct tc_htb_opt
+{
+       struct tc_ratespec      rate;
+       struct tc_ratespec      ceil;
+       __u32   buffer;
+       __u32   cbuffer;
+       __u32   quantum;        /* out only */
+       __u32   level;          /* out only */
+       __u8    prio;
+       __u8    injectd;        /* inject class distance */
+       __u8    pad[2];
+};
+struct tc_htb_glob
+{
+       __u32 rate2quantum;     /* bps->quantum divisor */
+       __u32 defcls;           /* default class number */
+       __u32 use_dcache;       /* use dequeue cache ? */
+       __u32 debug;            /* debug flags */
+
+
+       /* stats */
+       __u32 deq_rate; /* dequeue rate */
+       __u32 utilz;    /* dequeue utilization */
+       __u32 trials;   /* deq_prio trials per dequeue */
+       __u32 dcache_hits;
+       __u32 direct_pkts; /* count of non shapped packets */
+};
+enum
+{
+       TCA_HTB_UNSPEC,
+       TCA_HTB_PARMS,
+       TCA_HTB_INIT,
+       TCA_HTB_CTAB,
+       TCA_HTB_RTAB,
+};
+struct tc_htb_xstats
+{
+       __u32 lends;
+       __u32 borrows;
+       __u32 giants;   /* too big packets (rate will not be accurate) */
+       __u32 injects;  /* how many times leaf used injected bw */      
+       __u32 tokens;
+       __u32 ctokens;
+};
+
+/* CBQ section */
+
+#define TC_CBQ_MAXPRIO         8
+#define TC_CBQ_MAXLEVEL                8
+#define TC_CBQ_DEF_EWMA                5
+
+struct tc_cbq_lssopt
+{
+       unsigned char   change;
+       unsigned char   flags;
+#define TCF_CBQ_LSS_BOUNDED    1
+#define TCF_CBQ_LSS_ISOLATED   2
+       unsigned char   ewma_log;
+       unsigned char   level;
+#define TCF_CBQ_LSS_FLAGS      1
+#define TCF_CBQ_LSS_EWMA       2
+#define TCF_CBQ_LSS_MAXIDLE    4
+#define TCF_CBQ_LSS_MINIDLE    8
+#define TCF_CBQ_LSS_OFFTIME    0x10
+#define TCF_CBQ_LSS_AVPKT      0x20
+       __u32           maxidle;
+       __u32           minidle;
+       __u32           offtime;
+       __u32           avpkt;
+};
+
+struct tc_cbq_wrropt
+{
+       unsigned char   flags;
+       unsigned char   priority;
+       unsigned char   cpriority;
+       unsigned char   __reserved;
+       __u32           allot;
+       __u32           weight;
+};
+
+struct tc_cbq_ovl
+{
+       unsigned char   strategy;
+#define        TC_CBQ_OVL_CLASSIC      0
+#define        TC_CBQ_OVL_DELAY        1
+#define        TC_CBQ_OVL_LOWPRIO      2
+#define        TC_CBQ_OVL_DROP         3
+#define        TC_CBQ_OVL_RCLASSIC     4
+       unsigned char   priority2;
+       __u32           penalty;
+};
+
+struct tc_cbq_police
+{
+       unsigned char   police;
+       unsigned char   __res1;
+       unsigned short  __res2;
+};
+
+struct tc_cbq_fopt
+{
+       __u32           split;
+       __u32           defmap;
+       __u32           defchange;
+};
+
+struct tc_cbq_xstats
+{
+       __u32           borrows;
+       __u32           overactions;
+       __s32           avgidle;
+       __s32           undertime;
+};
+
+enum
+{
+       TCA_CBQ_UNSPEC,
+       TCA_CBQ_LSSOPT,
+       TCA_CBQ_WRROPT,
+       TCA_CBQ_FOPT,
+       TCA_CBQ_OVL_STRATEGY,
+       TCA_CBQ_RATE,
+       TCA_CBQ_RTAB,
+       TCA_CBQ_POLICE,
+};
+
+#define TCA_CBQ_MAX    TCA_CBQ_POLICE
+
+/* dsmark section */
+
+enum {
+       TCA_DSMARK_UNSPEC,
+       TCA_DSMARK_INDICES,
+       TCA_DSMARK_DEFAULT_INDEX,
+       TCA_DSMARK_SET_TC_INDEX,
+       TCA_DSMARK_MASK,
+       TCA_DSMARK_VALUE
+};
+
+#define TCA_DSMARK_MAX TCA_DSMARK_VALUE
+
+/* ATM  section */
+
+enum {
+       TCA_ATM_UNSPEC,
+       TCA_ATM_FD,             /* file/socket descriptor */
+       TCA_ATM_PTR,            /* pointer to descriptor - later */
+       TCA_ATM_HDR,            /* LL header */
+       TCA_ATM_EXCESS,         /* excess traffic class (0 for CLP)  */
+       TCA_ATM_ADDR,           /* PVC address (for output only) */
+       TCA_ATM_STATE           /* VC state (ATM_VS_*; for output only) */
+};
+
+#define TCA_ATM_MAX    TCA_ATM_STATE
+
+#endif
diff --git a/networking/libiproute/ll_addr.c b/networking/libiproute/ll_addr.c
new file mode 100644 (file)
index 0000000..51ff13b
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * ll_addr.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>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+
+#include "utils.h"
+
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
+{
+       int i;
+       int l;
+
+       if (alen == 4 &&
+           (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
+               return inet_ntop(AF_INET, addr, buf, blen);
+       }
+       l = 0;
+       for (i=0; i<alen; i++) {
+               if (i==0) {
+                       snprintf(buf+l, blen, "%02x", addr[i]);
+                       blen -= 2;
+                       l += 2;
+               } else {
+                       snprintf(buf+l, blen, ":%02x", addr[i]);
+                       blen -= 3;
+                       l += 3;
+               }
+       }
+       return buf;
+}
+
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
+{
+       if (strchr(arg, '.')) {
+               inet_prefix pfx;
+               if (get_addr_1(&pfx, arg, AF_INET)) {
+                       fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg);
+                       return -1;
+               }
+               if (len < 4)
+                       return -1;
+               memcpy(lladdr, pfx.data, 4);
+               return 4;
+       } else {
+               int i;
+
+               for (i=0; i<len; i++) {
+                       int temp;
+                       char *cp = strchr(arg, ':');
+                       if (cp) {
+                               *cp = 0;
+                               cp++;
+                       }
+                       if (sscanf(arg, "%x", &temp) != 1) {
+                               fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg);
+                               return -1;
+                       }
+                       if (temp < 0 || temp > 255) {
+                               fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg);
+                               return -1;
+                       }
+                       lladdr[i] = temp;
+                       if (!cp)
+                               break;
+                       arg = cp;
+               }
+               return i+1;
+       }
+}
diff --git a/networking/libiproute/ll_map.c b/networking/libiproute/ll_map.c
new file mode 100644 (file)
index 0000000..e5a95e6
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * ll_map.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>
+ *
+ */
+
+#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 "libnetlink.h"
+#include "ll_map.h"
+
+struct idxmap
+{
+       struct idxmap * next;
+       int             index;
+       int             type;
+       int             alen;
+       unsigned        flags;
+       unsigned char   addr[8];
+       char            name[16];
+};
+
+static struct idxmap *idxmap[16];
+
+int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       int h;
+       struct ifinfomsg *ifi = NLMSG_DATA(n);
+       struct idxmap *im, **imp;
+       struct rtattr *tb[IFLA_MAX+1];
+
+       if (n->nlmsg_type != RTM_NEWLINK)
+               return 0;
+
+       if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+               return -1;
+
+
+       memset(tb, 0, sizeof(tb));
+       parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+       if (tb[IFLA_IFNAME] == NULL)
+               return 0;
+
+       h = ifi->ifi_index&0xF;
+
+       for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
+               if (im->index == ifi->ifi_index)
+                       break;
+
+       if (im == NULL) {
+               im = malloc(sizeof(*im));
+               if (im == NULL)
+                       return 0;
+               im->next = *imp;
+               im->index = ifi->ifi_index;
+               *imp = im;
+       }
+
+       im->type = ifi->ifi_type;
+       im->flags = ifi->ifi_flags;
+       if (tb[IFLA_ADDRESS]) {
+               int alen;
+               im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+               if (alen > sizeof(im->addr))
+                       alen = sizeof(im->addr);
+               memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+       } else {
+               im->alen = 0;
+               memset(im->addr, 0, sizeof(im->addr));
+       }
+       strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
+       return 0;
+}
+
+const char *ll_idx_n2a(int idx, char *buf)
+{
+       struct idxmap *im;
+
+       if (idx == 0)
+               return "*";
+       for (im = idxmap[idx&0xF]; im; im = im->next)
+               if (im->index == idx)
+                       return im->name;
+       snprintf(buf, 16, "if%d", idx);
+       return buf;
+}
+
+
+const char *ll_index_to_name(int idx)
+{
+       static char nbuf[16];
+
+       return ll_idx_n2a(idx, nbuf);
+}
+
+int ll_index_to_type(int idx)
+{
+       struct idxmap *im;
+
+       if (idx == 0)
+               return -1;
+       for (im = idxmap[idx&0xF]; im; im = im->next)
+               if (im->index == idx)
+                       return im->type;
+       return -1;
+}
+
+unsigned ll_index_to_flags(int idx)
+{
+       struct idxmap *im;
+
+       if (idx == 0)
+               return 0;
+
+       for (im = idxmap[idx&0xF]; im; im = im->next)
+               if (im->index == idx)
+                       return im->flags;
+       return 0;
+}
+
+int ll_name_to_index(char *name)
+{
+       static char ncache[16];
+       static int icache;
+       struct idxmap *im;
+       int i;
+
+       if (name == NULL)
+               return 0;
+       if (icache && strcmp(name, ncache) == 0)
+               return icache;
+       for (i=0; i<16; i++) {
+               for (im = idxmap[i]; im; im = im->next) {
+                       if (strcmp(im->name, name) == 0) {
+                               icache = im->index;
+                               strcpy(ncache, name);
+                               return im->index;
+                       }
+               }
+       }
+       return 0;
+}
+
+int ll_init_map(struct rtnl_handle *rth)
+{
+       if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+               perror("Cannot send dump request");
+               exit(1);
+       }
+
+       if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               exit(1);
+       }
+       return 0;
+}
diff --git a/networking/libiproute/ll_map.h b/networking/libiproute/ll_map.h
new file mode 100644 (file)
index 0000000..739f157
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ll_init_map(struct rtnl_handle *rth);
+extern int ll_name_to_index(char *name);
+extern const char *ll_index_to_name(int idx);
+extern const char *ll_idx_n2a(int idx, char *buf);
+extern int ll_index_to_type(int idx);
+extern unsigned ll_index_to_flags(int idx);
+
+#endif /* __LL_MAP_H__ */
diff --git a/networking/libiproute/ll_proto.c b/networking/libiproute/ll_proto.c
new file mode 100644 (file)
index 0000000..394338a
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * ll_proto.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>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+
+#include "utils.h"
+
+
+#define __PF(f,n) { ETH_P_##f, #n },
+static struct {
+       int id;
+       char *name;
+} llproto_names[] = {
+__PF(LOOP,loop)
+__PF(PUP,pup)  
+#ifdef ETH_P_PUPAT
+__PF(PUPAT,pupat)     
+#endif
+__PF(IP,ip)
+__PF(X25,x25)
+__PF(ARP,arp)
+__PF(BPQ,bpq)
+#ifdef ETH_P_IEEEPUP
+__PF(IEEEPUP,ieeepup)  
+#endif
+#ifdef ETH_P_IEEEPUPAT
+__PF(IEEEPUPAT,ieeepupat)  
+#endif
+__PF(DEC,dec)       
+__PF(DNA_DL,dna_dl)    
+__PF(DNA_RC,dna_rc)    
+__PF(DNA_RT,dna_rt)    
+__PF(LAT,lat)       
+__PF(DIAG,diag)      
+__PF(CUST,cust)      
+__PF(SCA,sca)       
+__PF(RARP,rarp)      
+__PF(ATALK,atalk)     
+__PF(AARP,aarp)      
+__PF(IPX,ipx)       
+__PF(IPV6,ipv6)      
+__PF(PPP_DISC,ppp_disc)      
+__PF(PPP_SES,ppp_ses)      
+__PF(ATMMPOA,atmmpoa)      
+__PF(ATMFATE,atmfate)      
+
+__PF(802_3,802_3)     
+__PF(AX25,ax25)      
+__PF(ALL,all)       
+__PF(802_2,802_2)     
+__PF(SNAP,snap)      
+__PF(DDCMP,ddcmp)     
+__PF(WAN_PPP,wan_ppp)   
+__PF(PPP_MP,ppp_mp)    
+__PF(LOCALTALK,localtalk) 
+__PF(PPPTALK,ppptalk)   
+__PF(TR_802_2,tr_802_2)  
+__PF(MOBITEX,mobitex)   
+__PF(CONTROL,control)   
+__PF(IRDA,irda)      
+__PF(ECONET,econet)      
+
+{ 0x8100, "802.1Q" },
+{ ETH_P_IP, "ipv4" },
+};
+#undef __PF
+
+
+char * ll_proto_n2a(unsigned short id, char *buf, int len)
+{
+        int i;
+
+       id = ntohs(id);
+
+        for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+                 if (llproto_names[i].id == id)
+                       return llproto_names[i].name;
+       }
+        snprintf(buf, len, "[%d]", id);
+        return buf;
+}
+
+int ll_proto_a2n(unsigned short *id, char *buf)
+{
+        int i;
+        for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+                 if (strcasecmp(llproto_names[i].name, buf) == 0) {
+                        *id = htons(llproto_names[i].id);
+                        return 0;
+                }
+       }
+       if (get_u16(id, buf, 0))
+               return -1;
+       *id = htons(*id);
+       return 0;
+}
diff --git a/networking/libiproute/ll_types.c b/networking/libiproute/ll_types.c
new file mode 100644 (file)
index 0000000..f145836
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * ll_types.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>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+
+char * ll_type_n2a(int type, char *buf, int len)
+{
+#define __PF(f,n) { ARPHRD_##f, #n },
+static struct {
+       int type;
+       char *name;
+} arphrd_names[] = {
+{ 0, "generic" },
+__PF(ETHER,ether)
+__PF(EETHER,eether)
+__PF(AX25,ax25)
+__PF(PRONET,pronet)
+__PF(CHAOS,chaos)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802,ieee802)
+#else
+__PF(IEEE802,tr)
+#endif
+__PF(ARCNET,arcnet)
+__PF(APPLETLK,atalk)
+__PF(DLCI,dlci)
+__PF(ATM,atm)
+__PF(METRICOM,metricom)
+#ifdef ARPHRD_IEEE1394
+__PF(IEEE1394,ieee1394)
+#endif
+
+__PF(SLIP,slip)
+__PF(CSLIP,cslip)
+__PF(SLIP6,slip6)
+__PF(CSLIP6,cslip6)
+__PF(RSRVD,rsrvd)
+__PF(ADAPT,adapt)
+__PF(ROSE,rose)
+__PF(X25,x25)
+__PF(HWX25,hwx25)
+__PF(PPP,ppp)
+__PF(HDLC,hdlc)
+__PF(LAPB,lapb)
+__PF(DDCMP,ddcmp)
+__PF(RAWHDLC,rawhdlc)
+
+__PF(TUNNEL,ipip)
+__PF(TUNNEL6,tunnel6)
+__PF(FRAD,frad)
+__PF(SKIP,skip)
+__PF(LOOPBACK,loopback)
+__PF(LOCALTLK,ltalk)
+__PF(FDDI,fddi)
+__PF(BIF,bif)
+__PF(SIT,sit)
+__PF(IPDDP,ip/ddp)
+__PF(IPGRE,gre)
+__PF(PIMREG,pimreg)
+__PF(HIPPI,hippi)
+__PF(ASH,ash)
+__PF(ECONET,econet)
+__PF(IRDA,irda)
+__PF(FCPP,fcpp)
+__PF(FCAL,fcal)
+__PF(FCPL,fcpl)
+__PF(FCFABRIC,fcfb0)
+__PF(FCFABRIC+1,fcfb1)
+__PF(FCFABRIC+2,fcfb2)
+__PF(FCFABRIC+3,fcfb3)
+__PF(FCFABRIC+4,fcfb4)
+__PF(FCFABRIC+5,fcfb5)
+__PF(FCFABRIC+6,fcfb6)
+__PF(FCFABRIC+7,fcfb7)
+__PF(FCFABRIC+8,fcfb8)
+__PF(FCFABRIC+9,fcfb9)
+__PF(FCFABRIC+10,fcfb10)
+__PF(FCFABRIC+11,fcfb11)
+__PF(FCFABRIC+12,fcfb12)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802_TR,tr)
+#endif
+#ifdef ARPHRD_IEEE80211
+__PF(IEEE80211,ieee802.11)
+#endif
+#ifdef ARPHRD_VOID
+__PF(VOID,void)
+#endif
+};
+#undef __PF
+
+        int i;
+        for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
+                 if (arphrd_names[i].type == type)
+                       return arphrd_names[i].name;
+       }
+        snprintf(buf, len, "[%d]", type);
+        return buf;
+}
diff --git a/networking/libiproute/rt_names.c b/networking/libiproute/rt_names.c
new file mode 100644 (file)
index 0000000..2a7d85c
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * rt_names.c          rtnetlink names DB.
+ *
+ *             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 <string.h>
+#include <sys/time.h>
+#include <stdint.h>
+
+static void rtnl_tab_initialize(char *file, char **tab, int size)
+{
+       char buf[512];
+       FILE *fp;
+
+       fp = fopen(file, "r");
+       if (!fp)
+               return;
+       while (fgets(buf, sizeof(buf), fp)) {
+               char *p = buf;
+               int id;
+               char namebuf[512];
+
+               while (*p == ' ' || *p == '\t')
+                       p++;
+               if (*p == '#' || *p == '\n' || *p == 0)
+                       continue;
+               if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 &&
+                   sscanf(p, "0x%x %s #", &id, namebuf) != 2 &&
+                   sscanf(p, "%d %s\n", &id, namebuf) != 2 &&
+                   sscanf(p, "%d %s #", &id, namebuf) != 2) {
+                       fprintf(stderr, "Database %s is corrupted at %s\n",
+                               file, p);
+                       return;
+               }
+
+               if (id<0 || id>size)
+                       continue;
+
+               tab[id] = strdup(namebuf);
+       }
+       fclose(fp);
+}
+
+
+static char * rtnl_rtprot_tab[256] = {
+       "none",
+       "redirect",
+       "kernel",
+       "boot",
+       "static",
+       NULL,
+       NULL,
+       NULL,
+       "gated",
+       "ra",
+       "mrt",
+       "zebra",
+       "bird",
+};
+
+
+
+static int rtnl_rtprot_init;
+
+static void rtnl_rtprot_initialize(void)
+{
+       rtnl_rtprot_init = 1;
+       rtnl_tab_initialize("/etc/iproute2/rt_protos",
+                           rtnl_rtprot_tab, 256);
+}
+
+char * rtnl_rtprot_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtprot_tab[id]) {
+               if (!rtnl_rtprot_init)
+                       rtnl_rtprot_initialize();
+       }
+       if (rtnl_rtprot_tab[id])
+               return rtnl_rtprot_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+int rtnl_rtprot_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtprot_init)
+               rtnl_rtprot_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtprot_tab[i] &&
+                   strcmp(rtnl_rtprot_tab[i], arg) == 0) {
+                       cache = rtnl_rtprot_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
+
+
+static char * rtnl_rtscope_tab[256] = {
+       "global",
+};
+
+static int rtnl_rtscope_init;
+
+static void rtnl_rtscope_initialize(void)
+{
+       rtnl_rtscope_init = 1;
+       rtnl_rtscope_tab[255] = "nowhere";
+       rtnl_rtscope_tab[254] = "host";
+       rtnl_rtscope_tab[253] = "link";
+       rtnl_rtscope_tab[200] = "site";
+       rtnl_tab_initialize("/etc/iproute2/rt_scopes",
+                           rtnl_rtscope_tab, 256);
+}
+
+char * rtnl_rtscope_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtscope_tab[id]) {
+               if (!rtnl_rtscope_init)
+                       rtnl_rtscope_initialize();
+       }
+       if (rtnl_rtscope_tab[id])
+               return rtnl_rtscope_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+int rtnl_rtscope_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtscope_init)
+               rtnl_rtscope_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtscope_tab[i] &&
+                   strcmp(rtnl_rtscope_tab[i], arg) == 0) {
+                       cache = rtnl_rtscope_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
+
+
+static char * rtnl_rtrealm_tab[256] = {
+       "unknown",
+};
+
+static int rtnl_rtrealm_init;
+
+static void rtnl_rtrealm_initialize(void)
+{
+       rtnl_rtrealm_init = 1;
+       rtnl_tab_initialize("/etc/iproute2/rt_realms",
+                           rtnl_rtrealm_tab, 256);
+}
+
+char * rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtrealm_tab[id]) {
+               if (!rtnl_rtrealm_init)
+                       rtnl_rtrealm_initialize();
+       }
+       if (rtnl_rtrealm_tab[id])
+               return rtnl_rtrealm_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtrealm_init)
+               rtnl_rtrealm_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtrealm_tab[i] &&
+                   strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
+                       cache = rtnl_rtrealm_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
+
+
+static char * rtnl_rttable_tab[256] = {
+       "unspec",
+};
+
+static int rtnl_rttable_init;
+
+static void rtnl_rttable_initialize(void)
+{
+       rtnl_rttable_init = 1;
+       rtnl_rttable_tab[255] = "local";
+       rtnl_rttable_tab[254] = "main";
+       rtnl_tab_initialize("/etc/iproute2/rt_tables",
+                           rtnl_rttable_tab, 256);
+}
+
+char * rtnl_rttable_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rttable_tab[id]) {
+               if (!rtnl_rttable_init)
+                       rtnl_rttable_initialize();
+       }
+       if (rtnl_rttable_tab[id])
+               return rtnl_rttable_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+int rtnl_rttable_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rttable_init)
+               rtnl_rttable_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rttable_tab[i] &&
+                   strcmp(rtnl_rttable_tab[i], arg) == 0) {
+                       cache = rtnl_rttable_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       i = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || i > 255)
+               return -1;
+       *id = i;
+       return 0;
+}
+
+
+static char * rtnl_rtdsfield_tab[256] = {
+       "0",
+};
+
+static int rtnl_rtdsfield_init;
+
+static void rtnl_rtdsfield_initialize(void)
+{
+       rtnl_rtdsfield_init = 1;
+       rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+                           rtnl_rtdsfield_tab, 256);
+}
+
+char * rtnl_dsfield_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtdsfield_tab[id]) {
+               if (!rtnl_rtdsfield_init)
+                       rtnl_rtdsfield_initialize();
+       }
+       if (rtnl_rtdsfield_tab[id])
+               return rtnl_rtdsfield_tab[id];
+       snprintf(buf, len, "0x%02x", id);
+       return buf;
+}
+
+
+int rtnl_dsfield_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtdsfield_init)
+               rtnl_rtdsfield_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtdsfield_tab[i] &&
+                   strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
+                       cache = rtnl_rtdsfield_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 16);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
diff --git a/networking/libiproute/rt_names.h b/networking/libiproute/rt_names.h
new file mode 100644 (file)
index 0000000..97bc616
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef RT_NAMES_H_
+#define RT_NAMES_H_ 1
+
+#include <stdint.h>
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+const char* rtnl_rttable_n2a(int id, char *buf, int len);
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+int rtnl_rtprot_a2n(int *id, char *arg);
+int rtnl_rtscope_a2n(int *id, char *arg);
+int rtnl_rttable_a2n(int *id, char *arg);
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+
+const char *inet_proto_n2a(int proto, char *buf, int len);
+int inet_proto_a2n(char *buf);
+
+
+const char * ll_type_n2a(int type, char *buf, int len);
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
+
+const char * ll_proto_n2a(unsigned short id, char *buf, int len);
+int ll_proto_a2n(unsigned short *id, char *buf);
+
+
+#endif
diff --git a/networking/libiproute/rtm_map.c b/networking/libiproute/rtm_map.c
new file mode 100644 (file)
index 0000000..21e818b
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * rtm_map.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>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+char *rtnl_rtntype_n2a(int id, char *buf, int len)
+{
+       switch (id) {
+       case RTN_UNSPEC:
+               return "none";
+       case RTN_UNICAST:
+               return "unicast";
+       case RTN_LOCAL:
+               return "local";
+       case RTN_BROADCAST:
+               return "broadcast";
+       case RTN_ANYCAST:
+               return "anycast";
+       case RTN_MULTICAST:
+               return "multicast";
+       case RTN_BLACKHOLE:
+               return "blackhole";
+       case RTN_UNREACHABLE:
+               return "unreachable";
+       case RTN_PROHIBIT:
+               return "prohibit";
+       case RTN_THROW:
+               return "throw";
+       case RTN_NAT:
+               return "nat";
+       case RTN_XRESOLVE:
+               return "xresolve";
+       default:
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+}
+
+
+int rtnl_rtntype_a2n(int *id, char *arg)
+{
+       char *end;
+       unsigned long res;
+
+       if (strcmp(arg, "local") == 0)
+               res = RTN_LOCAL;
+       else if (strcmp(arg, "nat") == 0)
+               res = RTN_NAT;
+       else if (matches(arg, "broadcast") == 0 ||
+                strcmp(arg, "brd") == 0)
+               res = RTN_BROADCAST;
+       else if (matches(arg, "anycast") == 0)
+               res = RTN_ANYCAST;
+       else if (matches(arg, "multicast") == 0)
+               res = RTN_MULTICAST;
+       else if (matches(arg, "prohibit") == 0)
+               res = RTN_PROHIBIT;
+       else if (matches(arg, "unreachable") == 0)
+               res = RTN_UNREACHABLE;
+       else if (matches(arg, "blackhole") == 0)
+               res = RTN_BLACKHOLE;
+       else if (matches(arg, "xresolve") == 0)
+               res = RTN_XRESOLVE;
+       else if (matches(arg, "unicast") == 0)
+               res = RTN_UNICAST;
+       else if (strcmp(arg, "throw") == 0)
+               res = RTN_THROW;
+       else {
+               res = strtoul(arg, &end, 0);
+               if (!end || end == arg || *end || res > 255)
+                       return -1;
+       }
+       *id = res;
+       return 0;
+}
+
+int get_rt_realms(__u32 *realms, char *arg)
+{
+       __u32 realm = 0;
+       char *p = strchr(arg, '/');
+
+       *realms = 0;
+       if (p) {
+               *p = 0;
+               if (rtnl_rtrealm_a2n(realms, arg)) {
+                       *p = '/';
+                       return -1;
+               }
+               *realms <<= 16;
+               *p = '/';
+               arg = p+1;
+       }
+       if (*arg && rtnl_rtrealm_a2n(&realm, arg))
+               return -1;
+       *realms |= realm;
+       return 0;
+}
diff --git a/networking/libiproute/rtm_map.h b/networking/libiproute/rtm_map.h
new file mode 100644 (file)
index 0000000..70bda7d
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __RTM_MAP_H__
+#define __RTM_MAP_H__ 1
+
+char *rtnl_rtntype_n2a(int id, char *buf, int len);
+int rtnl_rtntype_a2n(int *id, char *arg);
+
+int get_rt_realms(__u32 *realms, char *arg);
+
+
+#endif /* __RTM_MAP_H__ */
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c
new file mode 100644 (file)
index 0000000..afc02d9
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * 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 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_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, "all") == 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) {
+               switch(dst->family) {
+                       case AF_INET6:
+                               dst->bitlen = 128;
+                               break;
+                       default:
+                       case AF_INET:
+                               dst->bitlen = 32;
+               }
+               if (slash) {
+                       if (get_integer(&plen, slash+1, 0) || 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 (family == AF_PACKET) {
+               fprintf(stderr, "Error: \"%s\" may be inet address, but it is not allowed in this context.\n", arg);
+               exit(1);
+       }
+       if (get_addr_1(dst, arg, family)) {
+               fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", arg);
+               exit(1);
+       }
+       return 0;
+}
+
+int get_prefix(inet_prefix *dst, char *arg, int family)
+{
+       if (family == AF_PACKET) {
+               fprintf(stderr, "Error: \"%s\" may be inet prefix, but it is not allowed in this context.\n", arg);
+               exit(1);
+       }
+       if (get_prefix_1(dst, arg, family)) {
+               fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", arg);
+               exit(1);
+       }
+       return 0;
+}
+
+__u32 get_addr32(char *name)
+{
+       inet_prefix addr;
+       if (get_addr_1(&addr, name, AF_INET)) {
+               fprintf(stderr, "Error: an IP address is expected rather than \"%s\"\n", name);
+               exit(1);
+       }
+       return addr.data[0];
+}
+
+void incomplete_command()
+{
+       fprintf(stderr, "Command line is not complete. Try option \"help\"\n");
+       exit(-1);
+}
+
+void invarg(char *msg, char *arg)
+{
+       fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg);
+       exit(-1);
+}
+
+void duparg(char *key, char *arg)
+{
+       fprintf(stderr, "Error: duplicate \"%s\": \"%s\" is the second value.\n", key, arg);
+       exit(-1);
+}
+
+void duparg2(char *key, char *arg)
+{
+       fprintf(stderr, "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n", key, arg);
+       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;
+}
+
+int __iproute2_hz_internal;
+
+int __get_hz(void)
+{
+       int hz = 0;
+       FILE *fp = fopen("/proc/net/psched", "r");
+
+       if (fp) {
+               unsigned nom, denom;
+               if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+                       if (nom == 1000000)
+                               hz = denom;
+               fclose(fp);
+       }
+       if (hz)
+               return hz;
+       return HZ;
+}
+
+const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
+{
+       switch (af) {
+       case AF_INET:
+       case AF_INET6:
+               return inet_ntop(af, addr, buf, buflen);
+       default:
+               return "???";
+       }
+}
+
+
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+#ifdef RESOLVE_HOSTNAMES
+       if (resolve_hosts) {
+               struct hostent *h_ent;
+               if (len <= 0) {
+                       switch (af) {
+                       case AF_INET:
+                               len = 4;
+                               break;
+                       case AF_INET6:
+                               len = 16;
+                               break;
+                       default: ;
+                       }
+               }
+               if (len > 0 &&
+                   (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
+                       snprintf(buf, buflen-1, "%s", h_ent->h_name);
+                       return buf;
+               }
+       }
+#endif
+       return rt_addr_n2a(af, len, addr, buf, buflen);
+}
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h
new file mode 100644 (file)
index 0000000..dc28c1b
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#include <asm/types.h>
+#include <resolv.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "rtm_map.h"
+
+extern int preferred_family;
+extern int show_stats;
+extern int show_details;
+extern int show_raw;
+extern int resolve_hosts;
+extern int oneline;
+extern char * _SL_;
+
+#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]
+
+extern void incomplete_command(void) __attribute__((noreturn));
+
+#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
+
+typedef struct
+{
+       __u8 family;
+       __u8 bytelen;
+       __s16 bitlen;
+       __u32 data[4];
+} inet_prefix;
+
+#define DN_MAXADDL 20
+#ifndef AF_DECnet
+#define AF_DECnet 12
+#endif
+
+struct dn_naddr 
+{
+        unsigned short          a_len;
+        unsigned char a_addr[DN_MAXADDL];
+};
+
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+       u_int32_t ipx_net;
+       u_int8_t  ipx_node[IPX_NODE_LEN];
+};
+
+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 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 const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
+
+void invarg(char *, char *) __attribute__((noreturn));
+void duparg(char *, char *) __attribute__((noreturn));
+void duparg2(char *, char *) __attribute__((noreturn));
+int matches(char *arg, char *pattern);
+extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
+int dnet_pton(int af, const char *src, void *addr);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
+
+extern int __iproute2_hz_internal;
+extern int __get_hz(void);
+
+static __inline__ int get_hz(void)
+{
+       if (__iproute2_hz_internal == 0)
+               __iproute2_hz_internal = __get_hz();
+       return __iproute2_hz_internal;
+}
+
+#endif /* __UTILS_H__ */