IP applet by Bastian Blank <waldi@debian.org>
diff --git a/networking/libiproute/Makefile b/networking/libiproute/Makefile
new file mode 100644
index 0000000..29419fd
--- /dev/null
+++ b/networking/libiproute/Makefile
@@ -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
index 0000000..9f782af
--- /dev/null
+++ b/networking/libiproute/Makefile.in
@@ -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
index 0000000..5ac4321
--- /dev/null
+++ b/networking/libiproute/ip_common.h
@@ -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
index 0000000..8f491f3
--- /dev/null
+++ b/networking/libiproute/ipaddress.c
@@ -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
index 0000000..90d60b4
--- /dev/null
+++ b/networking/libiproute/iplink.c
@@ -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
index 0000000..e38abcd
--- /dev/null
+++ b/networking/libiproute/iproute.c
@@ -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
index 0000000..beb8bb6
--- /dev/null
+++ b/networking/libiproute/iptunnel.c
@@ -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
index 0000000..a1f39d4
--- /dev/null
+++ b/networking/libiproute/libnetlink.c
@@ -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
index 0000000..45d3ad2
--- /dev/null
+++ b/networking/libiproute/libnetlink.h
@@ -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
index 0000000..e174588
--- /dev/null
+++ b/networking/libiproute/linux/pkt_sched.h
@@ -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
index 0000000..51ff13b
--- /dev/null
+++ b/networking/libiproute/ll_addr.c
@@ -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
index 0000000..e5a95e6
--- /dev/null
+++ b/networking/libiproute/ll_map.c
@@ -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
index 0000000..739f157
--- /dev/null
+++ b/networking/libiproute/ll_map.h
@@ -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
index 0000000..394338a
--- /dev/null
+++ b/networking/libiproute/ll_proto.c
@@ -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
index 0000000..f145836
--- /dev/null
+++ b/networking/libiproute/ll_types.c
@@ -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
index 0000000..2a7d85c
--- /dev/null
+++ b/networking/libiproute/rt_names.c
@@ -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
index 0000000..97bc616
--- /dev/null
+++ b/networking/libiproute/rt_names.h
@@ -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
index 0000000..21e818b
--- /dev/null
+++ b/networking/libiproute/rtm_map.c
@@ -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
index 0000000..70bda7d
--- /dev/null
+++ b/networking/libiproute/rtm_map.h
@@ -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
index 0000000..afc02d9
--- /dev/null
+++ b/networking/libiproute/utils.c
@@ -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
index 0000000..dc28c1b
--- /dev/null
+++ b/networking/libiproute/utils.h
@@ -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__ */