(Logical change 1.3)
diff --git a/lib/Makefile b/lib/Makefile
index e69de29..bc270bf 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -0,0 +1,18 @@
+
+UTILOBJ=utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o inet_proto.o
+
+NLOBJ=ll_map.o libnetlink.o
+
+all: libnetlink.a libutil.a
+
+libnetlink.a: $(NLOBJ)
+	$(AR) rcs $@ $(NLOBJ)
+
+libutil.a: $(UTILOBJ) $(ADDLIB)
+	$(AR) rcs $@ $(UTILOBJ) $(ADDLIB)
+
+install:
+
+clean:
+	rm -f $(NLOBJ) $(UTILOBJ) $(ADDLIB) libnetlink.a libutil.a
+
diff --git a/lib/dnet_ntop.c b/lib/dnet_ntop.c
index e69de29..9500df8 100644
--- a/lib/dnet_ntop.c
+++ b/lib/dnet_ntop.c
@@ -0,0 +1,98 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static __inline__ u_int16_t dn_ntohs(u_int16_t addr)
+{
+	union {
+		u_int8_t byte[2];
+		u_int16_t word;
+	} u;
+
+	u.word = addr;
+	return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
+}
+
+static __inline__ int do_digit(char *str, u_int16_t *addr, u_int16_t scale, size_t *pos, size_t len, int *started)
+{
+	u_int16_t tmp = *addr / scale;
+
+	if (*pos == len)
+		return 1;
+
+	if (((tmp) > 0) || *started || (scale == 1)) {
+		*str = tmp + '0';
+		*started = 1;
+		(*pos)++;
+		*addr -= (tmp * scale);
+	}
+
+	return 0;
+}
+
+
+static const char *dnet_ntop1(const struct dn_naddr *dna, char *str, size_t len)
+{
+	u_int16_t addr = dn_ntohs(*(u_int16_t *)dna->a_addr);
+	u_int16_t area = addr >> 10;
+	size_t pos = 0;
+	int started = 0;
+
+	if (dna->a_len != 2)
+		return NULL;
+
+	addr &= 0x03ff;
+
+	if (len == 0)
+		return str;
+
+	if (do_digit(str + pos, &area, 10, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &area, 1, &pos, len, &started))
+		return str;
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = '.';
+	pos++;
+	started = 0;
+
+	if (do_digit(str + pos, &addr, 1000, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &addr, 100, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &addr, 10, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &addr, 1, &pos, len, &started))
+		return str;
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = 0;
+
+	return str;
+}
+
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len)
+{
+	switch(af) {
+		case AF_DECnet:
+			errno = 0;
+			return dnet_ntop1((struct dn_naddr *)addr, str, len);
+		default:
+			errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
+
+
diff --git a/lib/dnet_pton.c b/lib/dnet_pton.c
index e69de29..bd7727a 100644
--- a/lib/dnet_pton.c
+++ b/lib/dnet_pton.c
@@ -0,0 +1,71 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static __inline__ u_int16_t dn_htons(u_int16_t addr)
+{
+        union {
+                u_int8_t byte[2];
+                u_int16_t word;
+        } u;
+
+        u.word = addr;
+        return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
+}
+
+
+static int dnet_num(const char *src, u_int16_t * dst)
+{
+	int rv = 0;
+	int tmp;
+	*dst = 0;
+
+	while ((tmp = *src++) != 0) {
+		tmp -= '0';
+		if ((tmp < 0) || (tmp > 9))
+			return rv;
+
+		rv++;
+		(*dst) *= 10;
+		(*dst) += tmp;
+	}
+
+	return rv;
+}
+
+static int dnet_pton1(const char *src, struct dn_naddr *dna)
+{
+	u_int16_t area = 0;
+	u_int16_t node = 0;
+	int pos;
+
+	pos = dnet_num(src, &area);
+	if ((pos == 0) || (area > 63) || (*(src + pos) != '.'))
+		return 0;
+	pos = dnet_num(src + pos + 1, &node);
+	if ((pos == 0) || (node > 1023))
+		return 0;
+	dna->a_len = 2;
+	*(u_int16_t *)dna->a_addr = dn_htons((area << 10) | node);
+
+	return 1;
+}
+
+int dnet_pton(int af, const char *src, void *addr)
+{
+	int err;
+
+	switch (af) {
+	case AF_DECnet:
+		errno = 0;
+		err = dnet_pton1(src, (struct dn_naddr *)addr);
+		break;
+	default:
+		errno = EAFNOSUPPORT;
+		err = -1;
+	}
+
+	return err;
+}
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
index e69de29..a3722d6 100644
--- a/lib/inet_ntop.c
+++ b/lib/inet_ntop.c
@@ -0,0 +1,199 @@
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$Id: inet_ntop.c,v 1.4 1996/09/27 03:24:13 drepper Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <linux/in6.h>
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ sizeof(struct in6_addr)
+#endif
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4 __P((const u_char *src, char *dst, size_t size));
+static const char *inet_ntop6 __P((const u_char *src, char *dst, size_t size));
+
+/* char *
+ * inet_ntop(af, src, dst, size)
+ *	convert a network format address to presentation format.
+ * return:
+ *	pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ *	Paul Vixie, 1996.
+ */
+const char *
+inet_ntop(af, src, dst, size)
+	int af;
+	const void *src;
+	char *dst;
+	size_t size;
+{
+	switch (af) {
+	case AF_INET:
+		return (inet_ntop4(src, dst, size));
+	case AF_INET6:
+		return (inet_ntop6(src, dst, size));
+	default:
+		errno = (EAFNOSUPPORT);
+		return (NULL);
+	}
+	/* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ *	format an IPv4 address, more or less like inet_ntoa()
+ * return:
+ *	`dst' (as a const)
+ * notes:
+ *	(1) uses no statics
+ *	(2) takes a u_char* not an in_addr as input
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(src, dst, size)
+	const u_char *src;
+	char *dst;
+	size_t size;
+{
+	static const char fmt[] = "%u.%u.%u.%u";
+	char tmp[sizeof "255.255.255.255"];
+
+	if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) > size) {
+		errno = (ENOSPC);
+		return (NULL);
+	}
+	strcpy(dst, tmp);
+	return (dst);
+}
+
+/* const char *
+ * inet_ntop6(src, dst, size)
+ *	convert IPv6 binary address into presentation (printable) format
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop6(src, dst, size)
+	const u_char *src;
+	char *dst;
+	size_t size;
+{
+	/*
+	 * Note that int32_t and int16_t need only be "at least" large enough
+	 * to contain a value of the specified size.  On some systems, like
+	 * Crays, there is no such thing as an integer variable with 16 bits.
+	 * Keep this in mind if you think this function should have been coded
+	 * to use pointer overlays.  All the world's not a VAX.
+	 */
+	char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+	struct { int base, len; } best, cur;
+	u_int words[sizeof(struct in6_addr) / INT16SZ];
+	int i;
+
+	/*
+	 * Preprocess:
+	 *	Copy the input (bytewise) array into a wordwise array.
+	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
+	 */
+	memset(words, '\0', sizeof words);
+	for (i = 0; i < IN6ADDRSZ; i++)
+		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+	best.base = -1;
+	cur.base = -1;
+	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+		if (words[i] == 0) {
+			if (cur.base == -1)
+				cur.base = i, cur.len = 1;
+			else
+				cur.len++;
+		} else {
+			if (cur.base != -1) {
+				if (best.base == -1 || cur.len > best.len)
+					best = cur;
+				cur.base = -1;
+			}
+		}
+	}
+	if (cur.base != -1) {
+		if (best.base == -1 || cur.len > best.len)
+			best = cur;
+	}
+	if (best.base != -1 && best.len < 2)
+		best.base = -1;
+
+	/*
+	 * Format the result.
+	 */
+	tp = tmp;
+	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+		/* Are we inside the best run of 0x00's? */
+		if (best.base != -1 && i >= best.base &&
+		    i < (best.base + best.len)) {
+			if (i == best.base)
+				*tp++ = ':';
+			continue;
+		}
+		/* Are we following an initial run of 0x00s or any real hex? */
+		if (i != 0)
+			*tp++ = ':';
+		/* Is this address an encapsulated IPv4? */
+		if (i == 6 && best.base == 0 &&
+		    (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+			if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
+				return (NULL);
+			tp += strlen(tp);
+			break;
+		}
+		tp += SPRINTF((tp, "%x", words[i]));
+	}
+	/* Was it a trailing run of 0x00's? */
+	if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+		*tp++ = ':';
+	*tp++ = '\0';
+
+	/*
+	 * Check for overflow, copy, and we're done.
+	 */
+	if ((size_t)(tp - tmp) > size) {
+		errno = (ENOSPC);
+		return (NULL);
+	}
+	strcpy(dst, tmp);
+	return (dst);
+}
diff --git a/lib/inet_proto.c b/lib/inet_proto.c
index e69de29..a55e0e7 100644
--- a/lib/inet_proto.c
+++ b/lib/inet_proto.c
@@ -0,0 +1,70 @@
+/*
+ * inet_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/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+#include "utils.h"
+
+char *inet_proto_n2a(int proto, char *buf, int len)
+{
+	static char ncache[16];
+	static int icache = -1;
+	struct protoent *pe;
+
+	if (proto == icache)
+		return ncache;
+
+	pe = getprotobynumber(proto);
+	if (pe) {
+		icache = proto;
+		strncpy(ncache, pe->p_name, 16);
+		strncpy(buf, pe->p_name, len);
+		return buf;
+	}
+	snprintf(buf, len, "ipproto-%d", proto);
+	return buf;
+}
+
+int inet_proto_a2n(char *buf)
+{
+	static char ncache[16];
+	static int icache = -1;
+	struct protoent *pe;
+
+	if (icache>=0 && strcmp(ncache, buf) == 0)
+		return icache;
+
+	if (buf[0] >= '0' && buf[0] <= '9') {
+		__u8 ret;
+		if (get_u8(&ret, buf, 10))
+			return -1;
+		return ret;
+	}
+
+	pe = getprotobyname(buf);
+	if (pe) {
+		icache = pe->p_proto;
+		strncpy(ncache, pe->p_name, 16);
+		return pe->p_proto;
+	}
+	return -1;
+}
+
+
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
index e69de29..9950834 100644
--- a/lib/inet_pton.c
+++ b/lib/inet_pton.c
@@ -0,0 +1,217 @@
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$Id: inet_pton.c,v 1.5 1996/09/27 03:24:16 drepper Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/in6.h>
+#define IN6ADDRSZ sizeof(struct in6_addr)
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int	inet_pton4 __P((const char *src, u_char *dst));
+static int	inet_pton6 __P((const char *src, u_char *dst));
+
+/* int
+ * inet_pton(af, src, dst)
+ *	convert from presentation format (which usually means ASCII printable)
+ *	to network format (which is usually some kind of binary format).
+ * return:
+ *	1 if the address was valid for the specified address family
+ *	0 if the address wasn't valid (`dst' is untouched in this case)
+ *	-1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ *	Paul Vixie, 1996.
+ */
+int
+inet_pton(af, src, dst)
+	int af;
+	const char *src;
+	void *dst;
+{
+	switch (af) {
+	case AF_INET:
+		return (inet_pton4(src, dst));
+	case AF_INET6:
+		return (inet_pton6(src, dst));
+	default:
+		errno = EAFNOSUPPORT;
+		return (-1);
+	}
+	/* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ *	like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ *	1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ *	does not touch `dst' unless it's returning 1.
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static int
+inet_pton4(src, dst)
+	const char *src;
+	u_char *dst;
+{
+	static const char digits[] = "0123456789";
+	int saw_digit, octets, ch;
+	u_char tmp[INADDRSZ], *tp;
+
+	saw_digit = 0;
+	octets = 0;
+	*(tp = tmp) = 0;
+	while ((ch = *src++) != '\0') {
+		const char *pch;
+
+		if ((pch = strchr(digits, ch)) != NULL) {
+			u_int new = *tp * 10 + (pch - digits);
+
+			if (new > 255)
+				return (0);
+			*tp = new;
+			if (! saw_digit) {
+				if (++octets > 4)
+					return (0);
+				saw_digit = 1;
+			}
+		} else if (ch == '.' && saw_digit) {
+			if (octets == 4)
+				return (0);
+			*++tp = 0;
+			saw_digit = 0;
+		} else
+			return (0);
+	}
+	if (octets < 4)
+		return (0);
+
+	memcpy(dst, tmp, INADDRSZ);
+	return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *	convert presentation level address to network order binary form.
+ * return:
+ *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *	(1) does not touch `dst' unless it's returning 1.
+ *	(2) :: in a full address is silently ignored.
+ * credit:
+ *	inspired by Mark Andrews.
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static int
+inet_pton6(src, dst)
+	const char *src;
+	u_char *dst;
+{
+	static const char xdigits_l[] = "0123456789abcdef",
+			  xdigits_u[] = "0123456789ABCDEF";
+	u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+	const char *xdigits, *curtok;
+	int ch, saw_xdigit;
+	u_int val;
+
+	memset((tp = tmp), '\0', IN6ADDRSZ);
+	endp = tp + IN6ADDRSZ;
+	colonp = NULL;
+	/* Leading :: requires some special handling. */
+	if (*src == ':')
+		if (*++src != ':')
+			return (0);
+	curtok = src;
+	saw_xdigit = 0;
+	val = 0;
+	while ((ch = *src++) != '\0') {
+		const char *pch;
+
+		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+			pch = strchr((xdigits = xdigits_u), ch);
+		if (pch != NULL) {
+			val <<= 4;
+			val |= (pch - xdigits);
+			if (val > 0xffff)
+				return (0);
+			saw_xdigit = 1;
+			continue;
+		}
+		if (ch == ':') {
+			curtok = src;
+			if (!saw_xdigit) {
+				if (colonp)
+					return (0);
+				colonp = tp;
+				continue;
+			}
+			if (tp + INT16SZ > endp)
+				return (0);
+			*tp++ = (u_char) (val >> 8) & 0xff;
+			*tp++ = (u_char) val & 0xff;
+			saw_xdigit = 0;
+			val = 0;
+			continue;
+		}
+		if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+		    inet_pton4(curtok, tp) > 0) {
+			tp += INADDRSZ;
+			saw_xdigit = 0;
+			break;	/* '\0' was seen by inet_pton4(). */
+		}
+		return (0);
+	}
+	if (saw_xdigit) {
+		if (tp + INT16SZ > endp)
+			return (0);
+		*tp++ = (u_char) (val >> 8) & 0xff;
+		*tp++ = (u_char) val & 0xff;
+	}
+	if (colonp != NULL) {
+		/*
+		 * Since some memmove()'s erroneously fail to handle
+		 * overlapping regions, we'll do the shift by hand.
+		 */
+		const int n = tp - colonp;
+		int i;
+
+		for (i = 1; i <= n; i++) {
+			endp[- i] = colonp[n - i];
+			colonp[n - i] = 0;
+		}
+		tp = endp;
+	}
+	if (tp != endp)
+		return (0);
+	memcpy(dst, tmp, IN6ADDRSZ);
+	return (1);
+}
diff --git a/lib/ipx_ntop.c b/lib/ipx_ntop.c
index e69de29..b2d6790 100644
--- a/lib/ipx_ntop.c
+++ b/lib/ipx_ntop.c
@@ -0,0 +1,71 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static __inline__ int do_digit(char *str, u_int32_t addr, u_int32_t scale, size_t *pos, size_t len)
+{
+	u_int32_t tmp = addr >> (scale * 4);
+
+	if (*pos == len)
+		return 1;
+
+	tmp &= 0x0f;
+	if (tmp > 9)
+		*str = tmp + 'A' - 10;
+	else
+		*str = tmp + '0';
+	(*pos)++;
+
+	return 0;
+}
+
+static const char *ipx_ntop1(const struct ipx_addr *addr, char *str, size_t len)
+{
+	int i;
+	size_t pos = 0;
+
+	if (len == 0)
+		return str;
+
+	for(i = 7; i >= 0; i--)
+		if (do_digit(str + pos, ntohl(addr->ipx_net), i, &pos, len))
+			return str;
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = '.';
+	pos++;
+	
+	for(i = 0; i < 6; i++) {
+		if (do_digit(str + pos, addr->ipx_node[i], 1, &pos, len))
+			return str;
+		if (do_digit(str + pos, addr->ipx_node[i], 0, &pos, len))
+			return str;
+	}
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = 0;
+
+	return str;
+}
+
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len)
+{
+	switch(af) {
+		case AF_IPX:
+			errno = 0;
+			return ipx_ntop1((struct ipx_addr *)addr, str, len);
+		default:
+			errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
+
+
diff --git a/lib/ipx_pton.c b/lib/ipx_pton.c
index e69de29..1a52b7f 100644
--- a/lib/ipx_pton.c
+++ b/lib/ipx_pton.c
@@ -0,0 +1,107 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static u_int32_t hexget(char c)
+{
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= '0' && c <= '9')
+		return c - '0';
+
+	return 0xf0;
+}
+
+static int ipx_getnet(u_int32_t *net, const char *str)
+{
+	int i;
+	u_int32_t tmp;
+
+	for(i = 0; *str && (i < 8); i++) {
+
+		if ((tmp = hexget(*str)) & 0xf0) {
+			if (*str == '.')
+				return 0;
+			else
+				return -1;
+		}
+
+		str++;
+		(*net) <<= 4;
+		(*net) |= tmp;
+	}
+
+	if (*str == 0)
+		return 0;
+
+	return -1;
+}
+
+static int ipx_getnode(u_int8_t *node, const char *str)
+{
+	int i;
+	u_int32_t tmp;
+
+	for(i = 0; i < 6; i++) {
+		if ((tmp = hexget(*str++)) & 0xf0)
+			return -1;
+		node[i] = (u_int8_t)tmp;
+		node[i] <<= 4;
+		if ((tmp = hexget(*str++)) & 0xf0)
+			return -1;
+		node[i] |= (u_int8_t)tmp;
+		if (*str == ':')
+			str++;
+	}
+
+	return 0;
+}
+
+static int ipx_pton1(const char *src, struct ipx_addr *addr)
+{
+	char *sep = (char *)src;
+	int no_node = 0;
+
+	memset(addr, 0, sizeof(struct ipx_addr));
+
+	while(*sep && (*sep != '.'))
+		sep++;
+
+	if (*sep != '.')
+		no_node = 1;
+
+	if (ipx_getnet(&addr->ipx_net, src))
+		return 0;
+
+	addr->ipx_net = htonl(addr->ipx_net);
+
+	if (no_node)
+		return 1;
+
+	if (ipx_getnode(addr->ipx_node, sep + 1))
+		return 0;
+
+	return 1;
+}
+
+int ipx_pton(int af, const char *src, void *addr)
+{
+	int err;
+
+	switch (af) {
+	case AF_IPX:
+		errno = 0;
+		err = ipx_pton1(src, (struct ipx_addr *)addr);
+		break;
+	default:
+		errno = EAFNOSUPPORT;
+		err = -1;
+	}
+
+	return err;
+}
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index e69de29..a1f39d4 100644
--- a/lib/libnetlink.c
+++ b/lib/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/lib/ll_addr.c b/lib/ll_addr.c
index e69de29..082cb3c 100644
--- a/lib/ll_addr.c
+++ b/lib/ll_addr.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.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/lib/ll_map.c b/lib/ll_map.c
index e69de29..e5a95e6 100644
--- a/lib/ll_map.c
+++ b/lib/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/lib/ll_proto.c b/lib/ll_proto.c
index e69de29..71f149d 100644
--- a/lib/ll_proto.c
+++ b/lib/ll_proto.c
@@ -0,0 +1,127 @@
+/*
+ * 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 <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.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)      
+#ifdef ETH_P_PPP_DISC
+__PF(PPP_DISC,ppp_disc)
+#endif      
+#ifdef ETH_P_PPP_SES
+__PF(PPP_SES,ppp_ses)
+#endif      
+#ifdef ETH_P_ATMMPOA
+__PF(ATMMPOA,atmmpoa)
+#endif
+#ifdef ETH_P_ATMFATE
+__PF(ATMFATE,atmfate)
+#endif      
+
+__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)      
+#ifdef ETH_P_ECONET
+__PF(ECONET,econet)
+#endif      
+
+{ 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/lib/ll_types.c b/lib/ll_types.c
index e69de29..165ecfa 100644
--- a/lib/ll_types.c
+++ b/lib/ll_types.c
@@ -0,0 +1,128 @@
+/*
+ * 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 <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.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)
+#ifdef ARPHRD_ATM
+__PF(ATM,atm)
+#endif
+__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)
+#ifdef ARPHRD_HWX25
+__PF(HWX25,hwx25)
+#endif
+__PF(PPP,ppp)
+__PF(HDLC,hdlc)
+__PF(LAPB,lapb)
+#ifdef ARPHRD_DDCMP
+__PF(DDCMP,ddcmp)
+#endif
+#ifdef ARPHRD_RAWHDLC
+__PF(RAWHDLC,rawhdlc)
+#endif
+
+__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/lib/rt_names.c b/lib/rt_names.c
index e69de29..429f73e 100644
--- a/lib/rt_names.c
+++ b/lib/rt_names.c
@@ -0,0 +1,388 @@
+/*
+ * 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>
+
+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(__u32 *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(__u32 *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(__u32 *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(__u32 *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(__u32 *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/lib/utils.c b/lib/utils.c
index e69de29..6763be2 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -0,0 +1,528 @@
+/*
+ * 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) {
+		if (family == AF_DECnet)
+			return -1;
+		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;
+	}
+
+	if (family == AF_DECnet) {
+		struct dn_naddr dna;
+		addr->family = AF_DECnet;
+		if (dnet_pton(AF_DECnet, name, &dna) <= 0)
+			return -1;
+		memcpy(addr->data, dna.a_addr, 2);
+		addr->bytelen = 2;
+		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 ||
+	    strcmp(arg, "all") == 0) {
+		if (family == AF_DECnet)
+			return -1;
+		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;
+			case AF_DECnet:
+				dst->bitlen = 16;
+				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)
+{
+	char name[1024];
+	int hz = 0;
+	FILE *fp;
+
+	if (getenv("HZ"))
+		return atoi(getenv("HZ")) ? : HZ;
+
+	if (getenv("PROC_NET_PSCHED")) {
+		snprintf(name, sizeof(name)-1, "%s", getenv("PROC_NET_PSCHED"));
+	} else if (getenv("PROC_ROOT")) { 
+		snprintf(name, sizeof(name)-1, "%s/net/psched", getenv("PROC_ROOT"));
+	} else {
+		strcpy(name, "/proc/net/psched");
+	}
+	fp = fopen(name, "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);
+	case AF_IPX:
+		return ipx_ntop(af, addr, buf, buflen);
+	case AF_DECnet:
+	{
+		struct dn_naddr dna = { 2, { 0, 0, }};
+		memcpy(dna.a_addr, addr, 2);
+		return dnet_ntop(af, &dna, buf, buflen);
+	}
+	default:
+		return "???";
+	}
+}
+
+#ifdef RESOLVE_HOSTNAMES
+struct namerec
+{
+	struct namerec *next;
+	inet_prefix addr;
+	char	    *name;
+};
+
+static struct namerec *nht[256];
+
+char *resolve_address(char *addr, int len, int af)
+{
+	struct namerec *n;
+	struct hostent *h_ent;
+	unsigned hash;
+	static int notfirst;
+
+
+	if (af == AF_INET6 && ((__u32*)addr)[0] == 0 &&
+	    ((__u32*)addr)[1] == 0 && ((__u32*)addr)[2] == htonl(0xffff)) {
+		af = AF_INET;
+		addr += 12;
+		len = 4;
+	}
+
+	hash = addr[len-1] ^ addr[len-2] ^ addr[len-3] ^ addr[len-4];
+
+	for (n = nht[hash]; n; n = n->next) {
+		if (n->addr.family == af &&
+		    n->addr.bytelen == len &&
+		    memcmp(n->addr.data, addr, len) == 0)
+			return n->name;
+	}
+	if ((n = malloc(sizeof(*n))) == NULL)
+		return NULL;
+	n->addr.family = af;
+	n->addr.bytelen = len;
+	n->name = NULL;
+	memcpy(n->addr.data, addr, len);
+	n->next = nht[hash];
+	nht[hash] = n;
+	if (++notfirst == 1)
+		sethostent(1);
+	fflush(stdout);
+
+	if ((h_ent = gethostbyaddr(addr, len, af)) != NULL)
+		n->name = strdup(h_ent->h_name);
+
+	/* Even if we fail, "negative" entry is remembered. */
+	return n->name;
+}
+#endif
+
+
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+#ifdef RESOLVE_HOSTNAMES
+	if (resolve_hosts) {
+		char *n;
+		if (len <= 0) {
+			switch (af) {
+			case AF_INET:
+				len = 4;
+				break;
+			case AF_INET6:
+				len = 16;
+				break;
+			case AF_IPX:
+				len = 10;
+				break;
+#ifdef AF_DECnet
+			/* I see no reasons why gethostbyname
+			   may not work for DECnet */
+			case AF_DECnet:
+				len = 2;
+				break;
+#endif
+			default: ;
+			}
+		}
+		if (len > 0 &&
+		    (n = resolve_address(addr, len, af)) != NULL)
+			return n;
+	}
+#endif
+	return rt_addr_n2a(af, len, addr, buf, buflen);
+}
+
+
+__u8* hexstring_n2a(const __u8 *str, int len, __u8 *buf, int blen)
+{
+	__u8 *ptr = buf;
+	int i;
+
+	for (i=0; i<len; i++) {
+		if (blen < 3)
+			break;
+		sprintf(ptr, "%02x", str[i]);
+		ptr += 2;
+		blen -= 2;
+		if (i != len-1 && blen > 1) {
+			*ptr++ = ':';
+			blen--;
+		}
+	}
+	return buf;
+}
+
+__u8* hexstring_a2n(const __u8 *str, __u8 *buf, int blen)
+{
+	int cnt = 0;
+
+	for (;;) {
+		unsigned acc;
+		char ch;
+
+		acc = 0;
+
+		while ((ch = *str) != ':' && ch != 0) {
+			if (ch >= '0' && ch <= '9')
+				ch -= '0';
+			else if (ch >= 'a' && ch <= 'f')
+				ch -= 'a'-10;
+			else if (ch >= 'A' && ch <= 'F')
+				ch -= 'A'-10;
+			else
+				return NULL;
+			acc = (acc<<4) + ch;
+			str++;
+		}
+
+		if (acc > 255)
+			return NULL;
+		if (cnt < blen) {
+			buf[cnt] = acc;
+			cnt++;
+		}
+		if (ch == 0)
+			break;
+		++str;
+	}
+	if (cnt < blen)
+		memset(buf+cnt, 0, blen-cnt);
+	return buf;
+}