[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/Makefile b/ap/app/iproute2/iproute2-3.4.0/ip/Makefile
new file mode 100755
index 0000000..ed4b926
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/Makefile
@@ -0,0 +1,50 @@
+IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
+    rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \
+    ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
+    ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
+    iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
+    iplink_macvlan.o iplink_macvtap.o ipl2tp.o
+
+RTMONOBJ=rtmon.o
+
+include ../Config
+
+ifeq ($(IP_CONFIG_SETNS),y)
+	CFLAGS += -DHAVE_SETNS
+endif
+
+ALLOBJ=$(IPOBJ) $(RTMONOBJ)
+SCRIPTS=ifcfg rtpr routel routef
+TARGETS=ip rtmon
+
+all: $(TARGETS) $(SCRIPTS)
+
+ip: $(IPOBJ) $(LIBNETLINK)
+
+
+rtmon: $(RTMONOBJ)
+
+install: all
+	install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR)
+	install -m 0755 $(SCRIPTS) $(DESTDIR)$(SBINDIR)
+
+clean:
+	rm -f $(ALLOBJ) $(TARGETS) *.elf
+
+SHARED_LIBS ?= y
+ifeq ($(SHARED_LIBS),y)
+
+LDLIBS += -ldl
+LDFLAGS += -Wl,-export-dynamic
+
+else
+
+ip: static-syms.o
+static-syms.o: static-syms.h
+static-syms.h: $(wildcard *.c)
+	files="$^" ; \
+	for s in `grep -B 3 '\<dlsym' $$files | sed -n '/snprintf/{s:.*"\([^"]*\)".*:\1:;s:%s::;p}'` ; do \
+		sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \
+	done > $@
+
+endif
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ifcfg b/ap/app/iproute2/iproute2-3.4.0/ip/ifcfg
new file mode 100755
index 0000000..083d9df
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ifcfg
@@ -0,0 +1,149 @@
+#! /bin/bash
+
+CheckForwarding () {
+  local sbase fwd
+  sbase=/proc/sys/net/ipv4/conf
+  fwd=0
+  if [ -d $sbase ]; then
+    for dir in $sbase/*/forwarding; do
+      fwd=$[$fwd + `cat $dir`]
+    done
+  else
+    fwd=2
+  fi
+  return $fwd
+}
+
+RestartRDISC () {
+  killall -HUP rdisc || rdisc -fs
+}
+
+ABCMaskLen () {
+  local class;
+
+  class=${1%%.*}
+  if [ "$1" = "" -o $class -eq 0 -o $class -ge 224 ]; then return 0
+  elif [ $class -ge 224 ]; then return 0
+  elif [ $class -ge 192 ]; then return 24
+  elif [ $class -ge 128 ]; then return 16
+  else return 8; fi
+}
+
+label="label $1"
+ldev="$1"
+dev=${1%:*}
+if [ "$dev" = "" -o "$1" = "help" ]; then
+  echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2
+  echo "       add - add new address" 1>&2
+  echo "       del - delete address" 1>&2
+  echo "       stop - completely disable IP" 1>&2
+  exit 1
+fi
+shift
+
+CheckForwarding
+fwd=$?
+if [ $fwd -ne 0 ]; then
+  echo "Forwarding is ON or its state is unknown ($fwd). OK, No RDISC." 1>&2
+fi
+
+
+deleting=0
+case "$1" in
+add) shift ;;
+stop)
+  if [ "$ldev" != "$dev" ]; then
+    echo "Cannot stop alias $ldev" 1>&2
+    exit 1;
+  fi
+  ip -4 addr flush dev $dev $label || exit 1
+  if [ $fwd -eq 0 ]; then RestartRDISC; fi
+  exit 0 ;;
+del*)
+  deleting=1; shift ;;
+*)
+esac
+
+ipaddr=
+pfxlen=
+if [ "$1" != "" ]; then
+  ipaddr=${1%/*}
+  if [ "$1" != "$ipaddr" ]; then
+    pfxlen=${1#*/}
+  fi
+  if [ "$ipaddr" = "" ]; then
+    echo "$1 is bad IP address." 1>&2
+    exit 1
+  fi
+fi
+shift
+
+peer=$1
+if [ "$peer" != "" ]; then
+  if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then
+    echo "Peer address with non-trivial netmask." 1>&2
+    exit 1
+  fi
+  pfx="$ipaddr peer $peer"
+else
+  if [ "$ipaddr" = "" ]; then
+    echo "Missing IP address argument." 1>&2
+    exit 1
+  fi
+  if [ "$pfxlen" = "" ]; then
+    ABCMaskLen $ipaddr
+    pfxlen=$?
+  fi
+  pfx="$ipaddr/$pfxlen"
+fi
+
+if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then
+  label=
+fi
+
+if [ $deleting -ne 0 ]; then
+  ip addr del $pfx dev $dev $label || exit 1
+  if [ $fwd -eq 0 ]; then RestartRDISC; fi
+  exit 0
+fi
+
+
+if ! ip link set up dev $dev ; then
+  echo "Error: cannot enable interface $dev." 1>&2
+  exit 1
+fi
+if [ "$ipaddr" = "" ]; then exit 0; fi
+
+if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then
+  echo "Error: some host already uses address $ipaddr on $dev." 1>&2
+  exit 1
+fi
+
+if ! ip address add $pfx brd + dev $dev $label; then
+  echo "Error: failed to add $pfx on $dev." 1>&2
+  exit 1
+fi
+
+arping -q -A -c 1 -I $dev $ipaddr
+noarp=$?
+( sleep 2 ;
+  arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null &
+
+ip route add unreachable 224.0.0.0/24 >& /dev/null
+ip route add unreachable 255.255.255.255 >& /dev/null
+if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then
+  ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null
+fi
+
+if [ $fwd -eq 0 ]; then
+  if [ $noarp -eq 0 ]; then
+    ip ro append default dev $dev metric 30000 scope global
+  elif [ "$peer" != "" ]; then
+    if ping -q -c 2 -w 4 $peer ; then
+      ip ro append default via $peer dev $dev metric 30001
+    fi
+  fi
+  RestartRDISC
+fi
+
+exit 0
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ip.c b/ap/app/iproute2/iproute2-3.4.0/ip/ip.c
new file mode 100755
index 0000000..20dc3b5
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ip.c
@@ -0,0 +1,266 @@
+/*
+ * ip.c		"ip" utility frontend.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#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 <errno.h>
+
+#include "SNAPSHOT.h"
+#include "utils.h"
+#include "ip_common.h"
+
+int preferred_family = AF_UNSPEC;
+int show_stats = 0;
+int show_details = 0;
+int resolve_hosts = 0;
+int oneline = 0;
+int timestamp = 0;
+char * _SL_ = NULL;
+char *batch_file = NULL;
+int force = 0;
+int max_flush_loops = 10;
+
+struct rtnl_handle rth = { .fd = -1 };
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr,
+"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
+"       ip [ -force ] -batch filename\n"
+"where  OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n"
+"                   tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm |\n"
+"                   netns | l2tp }\n"
+"       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
+"                    -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
+"                    -l[oops] { maximum-addr-flush-attempts } |\n"
+"                    -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
+"                    -rc[vbuf] [size]}\n");
+	exit(-1);
+}
+
+static int do_help(int argc, char **argv)
+{
+	usage();
+}
+
+static const struct cmd {
+	const char *cmd;
+	int (*func)(int argc, char **argv);
+} cmds[] = {
+	{ "address", 	do_ipaddr },
+	{ "addrlabel",	do_ipaddrlabel },
+	{ "maddress",	do_multiaddr },
+	{ "route",	do_iproute },
+	{ "rule",	do_iprule },
+	{ "neighbor",	do_ipneigh },
+	{ "neighbour",	do_ipneigh },
+	{ "ntable",	do_ipntable },
+	{ "ntbl",	do_ipntable },
+	{ "link",	do_iplink },
+	{ "l2tp",	do_ipl2tp },
+	{ "tunnel",	do_iptunnel },
+	{ "tunl",	do_iptunnel },
+	{ "tuntap",	do_iptuntap },
+	{ "tap",	do_iptuntap },
+	{ "monitor",	do_ipmonitor },
+	{ "xfrm",	do_xfrm },
+	{ "mroute",	do_multiroute },
+	{ "mrule",	do_multirule },
+	{ "netns",	do_netns },
+	{ "help",	do_help },
+	{ 0 }
+};
+
+static int do_cmd(const char *argv0, int argc, char **argv)
+{
+	const struct cmd *c;
+
+	for (c = cmds; c->cmd; ++c) {
+		if (matches(argv0, c->cmd) == 0) {
+			return -(c->func(argc-1, argv+1));
+		}
+	}
+
+	fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv0);
+	return EXIT_FAILURE;
+}
+
+static int batch(const char *name)
+{
+	char *line = NULL;
+	size_t len = 0;
+	int ret = EXIT_SUCCESS;
+
+	if (name && strcmp(name, "-") != 0) {
+		if (freopen(name, "r", stdin) == NULL) {
+			fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
+				name, strerror(errno));
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (rtnl_open(&rth, 0) < 0) {
+		fprintf(stderr, "Cannot open rtnetlink\n");
+		return EXIT_FAILURE;
+	}
+
+	cmdlineno = 0;
+	while (getcmdline(&line, &len, stdin) != -1) {
+		char *largv[100];
+		int largc;
+
+		largc = makeargs(line, largv, 100);
+		if (largc == 0)
+			continue;	/* blank line */
+
+		if (do_cmd(largv[0], largc, largv)) {
+			fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
+			ret = EXIT_FAILURE;
+			if (!force)
+				break;
+		}
+	}
+	if (line)
+		free(line);
+
+	rtnl_close(&rth);
+	return ret;
+}
+
+
+int main(int argc, char **argv)
+{
+	char *basename;
+
+	basename = strrchr(argv[0], '/');
+	if (basename == NULL)
+		basename = argv[0];
+	else
+		basename++;
+
+	while (argc > 1) {
+		char *opt = argv[1];
+		if (strcmp(opt,"--") == 0) {
+			argc--; argv++;
+			break;
+		}
+		if (opt[0] != '-')
+			break;
+		if (opt[1] == '-')
+			opt++;
+		if (matches(opt, "-loops") == 0) {
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+                        max_flush_loops = atoi(argv[1]);
+                } else if (matches(opt, "-family") == 0) {
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+			if (strcmp(argv[1], "inet") == 0)
+				preferred_family = AF_INET;
+			else if (strcmp(argv[1], "inet6") == 0)
+				preferred_family = AF_INET6;
+			else if (strcmp(argv[1], "dnet") == 0)
+				preferred_family = AF_DECnet;
+			else if (strcmp(argv[1], "link") == 0)
+				preferred_family = AF_PACKET;
+			else if (strcmp(argv[1], "ipx") == 0)
+				preferred_family = AF_IPX;
+			else if (strcmp(argv[1], "help") == 0)
+				usage();
+			else
+				invarg(argv[1], "invalid protocol family");
+		} else if (strcmp(opt, "-4") == 0) {
+			preferred_family = AF_INET;
+		} else if (strcmp(opt, "-6") == 0) {
+			preferred_family = AF_INET6;
+		} else if (strcmp(opt, "-0") == 0) {
+			preferred_family = AF_PACKET;
+		} else if (strcmp(opt, "-I") == 0) {
+			preferred_family = AF_IPX;
+		} else if (strcmp(opt, "-D") == 0) {
+			preferred_family = AF_DECnet;
+		} else if (matches(opt, "-stats") == 0 ||
+			   matches(opt, "-statistics") == 0) {
+			++show_stats;
+		} else if (matches(opt, "-details") == 0) {
+			++show_details;
+		} else if (matches(opt, "-resolve") == 0) {
+			++resolve_hosts;
+		} else if (matches(opt, "-oneline") == 0) {
+			++oneline;
+		} else if (matches(opt, "-timestamp") == 0) {
+			++timestamp;
+#if 0
+		} else if (matches(opt, "-numeric") == 0) {
+			rtnl_names_numeric++;
+#endif
+		} else if (matches(opt, "-Version") == 0) {
+			printf("ip utility, iproute2-ss%s\n", SNAPSHOT);
+			exit(0);
+		} else if (matches(opt, "-force") == 0) {
+			++force;
+		} else if (matches(opt, "-batch") == 0) {
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+			batch_file = argv[1];
+		} else if (matches(opt, "-rcvbuf") == 0) {
+			unsigned int size;
+
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+			if (get_unsigned(&size, argv[1], 0)) {
+				fprintf(stderr, "Invalid rcvbuf size '%s'\n",
+					argv[1]);
+				exit(-1);
+			}
+			rcvbuf = size;
+		} else if (matches(opt, "-help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt);
+			exit(-1);
+		}
+		argc--;	argv++;
+	}
+
+	_SL_ = oneline ? "\\" : "\n" ;
+
+	if (batch_file)
+		return batch(batch_file);
+
+	if (rtnl_open(&rth, 0) < 0)
+		exit(1);
+
+	if (strlen(basename) > 2)
+		return do_cmd(basename+2, argc, argv);
+
+	if (argc > 1)
+		return do_cmd(argv[1], argc-1, argv+1);
+
+	rtnl_close(&rth);
+	usage();
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ip6tunnel.c b/ap/app/iproute2/iproute2-3.4.0/ip/ip6tunnel.c
new file mode 100755
index 0000000..d5bee36
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ip6tunnel.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * Author:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <linux/ip.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+
+#include "utils.h"
+#include "tunnel.h"
+#include "ip_common.h"
+
+#define IP6_FLOWINFO_TCLASS	htonl(0x0FF00000)
+#define IP6_FLOWINFO_FLOWLABEL	htonl(0x000FFFFF)
+
+#define DEFAULT_TNL_HOP_LIMIT	(64)
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n");
+	fprintf(stderr, "          [ mode { ip6ip6 | ipip6 | any } ]\n");
+	fprintf(stderr, "          [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "          [ encaplimit ELIM ]\n");
+	fprintf(stderr ,"          [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+	fprintf(stderr, "          [ dscp inherit ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME      := STRING\n");
+	fprintf(stderr, "       ADDR      := IPV6_ADDRESS\n");
+	fprintf(stderr, "       ELIM      := { none | 0..255 }(default=%d)\n",
+		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(stderr, "       TTL       := 0..255 (default=%d)\n",
+		DEFAULT_TNL_HOP_LIMIT);
+	fprintf(stderr, "       TOS       := { 0x0..0xff | inherit }\n");
+	fprintf(stderr, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+	exit(-1);
+}
+
+static void print_tunnel(struct ip6_tnl_parm *p)
+{
+	char remote[64];
+	char local[64];
+
+	inet_ntop(AF_INET6, &p->raddr, remote, sizeof(remote));
+	inet_ntop(AF_INET6, &p->laddr, local, sizeof(local));
+
+	printf("%s: %s/ipv6 remote %s local %s",
+	       p->name, tnl_strproto(p->proto), remote, local);
+	if (p->link) {
+		const char *n = ll_index_to_name(p->link);
+		if (n)
+			printf(" dev %s", n);
+	}
+
+	if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+		printf(" encaplimit none");
+	else
+		printf(" encaplimit %u", p->encap_limit);
+
+	printf(" hoplimit %u", p->hop_limit);
+
+	if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS)
+		printf(" tclass inherit");
+	else {
+		__u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS);
+		printf(" tclass 0x%02x", (__u8)(val >> 20));
+	}
+
+	if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+		printf(" flowlabel inherit");
+	else
+		printf(" flowlabel 0x%05x", ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL));
+
+	printf(" (flowinfo 0x%08x)", ntohl(p->flowinfo));
+
+	if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
+		printf(" dscp inherit");
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
+{
+	int count = 0;
+	char medium[IFNAMSIZ];
+
+	memset(medium, 0, sizeof(medium));
+
+	while (argc > 0) {
+		if (strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ipv6/ipv6") == 0 ||
+			    strcmp(*argv, "ip6ip6") == 0)
+				p->proto = IPPROTO_IPV6;
+			else if (strcmp(*argv, "ip/ipv6") == 0 ||
+				 strcmp(*argv, "ipv4/ipv6") == 0 ||
+				 strcmp(*argv, "ipip6") == 0 ||
+				 strcmp(*argv, "ip4ip6") == 0)
+				p->proto = IPPROTO_IPIP;
+			else if (strcmp(*argv, "any/ipv6") == 0 ||
+				 strcmp(*argv, "any") == 0)
+				p->proto = 0;
+			else {
+                                fprintf(stderr,"Cannot guess tunnel mode.\n");
+                                exit(-1);
+                        }
+                } else if (strcmp(*argv, "remote") == 0) {
+			inet_prefix raddr;
+			NEXT_ARG();
+			get_prefix(&raddr, *argv, preferred_family);
+			if (raddr.family == AF_UNSPEC)
+				invarg("\"remote\" address family is AF_UNSPEC", *argv);
+			memcpy(&p->raddr, &raddr.data, sizeof(p->raddr));
+		} else if (strcmp(*argv, "local") == 0) {
+			inet_prefix laddr;
+			NEXT_ARG();
+			get_prefix(&laddr, *argv, preferred_family);
+			if (laddr.family == AF_UNSPEC)
+				invarg("\"local\" address family is AF_UNSPEC", *argv);
+			memcpy(&p->laddr, &laddr.data, sizeof(p->laddr));
+		} else if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			strncpy(medium, *argv, IFNAMSIZ - 1);
+		} else if (strcmp(*argv, "encaplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "none") == 0) {
+				p->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
+			} else {
+				__u8 uval;
+				if (get_u8(&uval, *argv, 0) < -1)
+					invarg("invalid ELIM", *argv);
+				p->encap_limit = uval;
+			}
+		} else if (strcmp(*argv, "hoplimit") == 0 ||
+			   strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hlim") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid TTL", *argv);
+			p->hop_limit = uval;
+		} else if (strcmp(*argv, "tclass") == 0 ||
+			   strcmp(*argv, "tc") == 0 ||
+			   strcmp(*argv, "tos") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") == 0)
+				p->flags |= IP6_TNL_F_USE_ORIG_TCLASS;
+			else {
+				if (get_u8(&uval, *argv, 16))
+					invarg("invalid TClass", *argv);
+				p->flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
+				p->flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
+			}
+		} else if (strcmp(*argv, "flowlabel") == 0 ||
+			   strcmp(*argv, "fl") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") == 0)
+				p->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
+			else {
+				if (get_u32(&uval, *argv, 16))
+					invarg("invalid Flowlabel", *argv);
+				if (uval > 0xFFFFF)
+					invarg("invalid Flowlabel", *argv);
+				p->flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
+				p->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
+			}
+		} else if (strcmp(*argv, "dscp") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0)
+				invarg("not inherit", *argv);
+			p->flags |= IP6_TNL_F_RCV_DSCP_COPY;
+		} else {
+			if (strcmp(*argv, "name") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (p->name[0])
+				duparg2("name", *argv);
+			strncpy(p->name, *argv, IFNAMSIZ - 1);
+			if (cmd == SIOCCHGTUNNEL && count == 0) {
+				struct ip6_tnl_parm old_p;
+				memset(&old_p, 0, sizeof(old_p));
+				if (tnl_get_ioctl(*argv, &old_p))
+					return -1;
+				*p = old_p;
+			}
+		}
+		count++;
+		argc--; argv++;
+	}
+	if (medium[0]) {
+		p->link = ll_name_to_index(medium);
+		if (p->link == 0)
+			return -1;
+	}
+	return 0;
+}
+
+static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default)
+{
+	memset(p, 0, sizeof(*p));
+	p->proto = IPPROTO_IPV6;
+	if (apply_default) {
+		p->hop_limit = DEFAULT_TNL_HOP_LIMIT;
+		p->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
+	}
+}
+
+/*
+ * @p1: user specified parameter
+ * @p2: database entry
+ */
+static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
+			      const struct ip6_tnl_parm *p2)
+{
+	return ((!p1->link || p1->link == p2->link) &&
+		(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
+		(memcmp(&p1->laddr, &in6addr_any, sizeof(p1->laddr)) == 0 ||
+		 memcmp(&p1->laddr, &p2->laddr, sizeof(p1->laddr)) == 0) &&
+		(memcmp(&p1->raddr, &in6addr_any, sizeof(p1->raddr)) == 0 ||
+		 memcmp(&p1->raddr, &p2->raddr, sizeof(p1->raddr)) == 0) &&
+		(!p1->proto || !p2->proto || p1->proto == p2->proto) &&
+		(!p1->encap_limit || p1->encap_limit == p2->encap_limit) &&
+		(!p1->hop_limit || p1->hop_limit == p2->hop_limit) &&
+		(!(p1->flowinfo & IP6_FLOWINFO_TCLASS) ||
+		 !((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_TCLASS)) &&
+		(!(p1->flowinfo & IP6_FLOWINFO_FLOWLABEL) ||
+		 !((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_FLOWLABEL)) &&
+		(!p1->flags || (p1->flags & p2->flags)));
+}
+
+static int do_tunnels_list(struct ip6_tnl_parm *p)
+{
+	char buf[512];
+	int err = -1;
+	FILE *fp = fopen("/proc/net/dev", "r");
+	if (fp == NULL) {
+		perror("fopen");
+		goto end;
+	}
+
+	/* skip two lines at the begenning of the file */
+	if (!fgets(buf, sizeof(buf), fp) ||
+	    !fgets(buf, sizeof(buf), fp)) {
+		fprintf(stderr, "/proc/net/dev read error\n");
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		char name[IFNAMSIZ];
+		int index, type;
+		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;
+		struct ip6_tnl_parm p1;
+		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");
+			goto end;
+		}
+		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;
+		index = ll_name_to_index(name);
+		if (index == 0)
+			continue;
+		type = ll_index_to_type(index);
+		if (type == -1) {
+			fprintf(stderr, "Failed to get type of [%s]\n", name);
+			continue;
+		}
+		if (type != ARPHRD_TUNNEL6)
+			continue;
+		memset(&p1, 0, sizeof(p1));
+		ip6_tnl_parm_init(&p1, 0);
+		strcpy(p1.name, name);
+		p1.link = ll_name_to_index(p1.name);
+		if (p1.link == 0)
+			continue;
+		if (tnl_get_ioctl(p1.name, &p1))
+			continue;
+		if (!ip6_tnl_parm_match(p, &p1))
+			continue;
+		print_tunnel(&p1);
+		if (show_stats) {
+			printf("%s", _SL_);
+			printf("RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
+			printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s",
+			       rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_);
+			printf("TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs%s", _SL_);
+			printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld",
+			       tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
+		}
+		printf("\n");
+	}
+	err = 0;
+
+ end:
+	if (fp)
+		fclose(fp);
+	return err;
+}
+
+static int do_show(int argc, char **argv)
+{
+        struct ip6_tnl_parm p;
+
+	ll_init_map(&rth);
+	ip6_tnl_parm_init(&p, 0);
+	p.proto = 0;  /* default to any */
+
+        if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
+                return -1;
+
+	if (!p.name[0] || show_stats)
+		do_tunnels_list(&p);
+	else {
+		if (tnl_get_ioctl(p.name, &p))
+			return -1;
+		print_tunnel(&p);
+		printf("\n");
+	}
+
+        return 0;
+}
+
+static int do_add(int cmd, int argc, char **argv)
+{
+	struct ip6_tnl_parm p;
+
+	ip6_tnl_parm_init(&p, 1);
+
+	if (parse_args(argc, argv, cmd, &p) < 0)
+		return -1;
+
+	return tnl_add_ioctl(cmd,
+			     cmd == SIOCCHGTUNNEL && p.name[0] ?
+			     p.name : "ip6tnl0", p.name, &p);
+}
+
+static int do_del(int argc, char **argv)
+{
+	struct ip6_tnl_parm p;
+
+	ip6_tnl_parm_init(&p, 1);
+
+	if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
+		return -1;
+
+	return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name, &p);
+}
+
+int do_ip6tunnel(int argc, char **argv)
+{
+	switch (preferred_family) {
+	case AF_UNSPEC:
+		preferred_family = AF_INET6;
+		break;
+	case AF_INET6:
+		break;
+	default:
+		fprintf(stderr, "Unsupported family:%d\n", preferred_family);
+		exit(-1);
+	}
+
+	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);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return do_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip -f inet6 tunnel help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ip_common.h b/ap/app/iproute2/iproute2-3.4.0/ip/ip_common.h
new file mode 100755
index 0000000..5fa2cc0
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ip_common.h
@@ -0,0 +1,74 @@
+extern int get_operstate(const char *name);
+extern int print_linkinfo(const struct sockaddr_nl *who,
+			  struct nlmsghdr *n,
+			  void *arg);
+extern int print_addrinfo(const struct sockaddr_nl *who,
+			  struct nlmsghdr *n,
+			  void *arg);
+extern int print_addrlabel(const struct sockaddr_nl *who,
+			   struct nlmsghdr *n, void *arg);
+extern int print_neigh(const struct sockaddr_nl *who,
+		       struct nlmsghdr *n, void *arg);
+extern int print_ntable(const 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 void ipntable_reset_filter(void);
+extern int print_route(const struct sockaddr_nl *who,
+		       struct nlmsghdr *n, void *arg);
+extern int print_prefix(const struct sockaddr_nl *who,
+			struct nlmsghdr *n, void *arg);
+extern int print_rule(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n, void *arg);
+extern int do_ipaddr(int argc, char **argv);
+extern int do_ipaddrlabel(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_ipntable(int argc, char **argv);
+extern int do_iptunnel(int argc, char **argv);
+extern int do_ip6tunnel(int argc, char **argv);
+extern int do_iptuntap(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);
+extern int do_multirule(int argc, char **argv);
+extern int do_netns(int argc, char **argv);
+extern int do_xfrm(int argc, char **argv);
+extern int do_ipl2tp(int argc, char **argv);
+
+static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
+{
+	__u32 table = r->rtm_table;
+	if (tb[RTA_TABLE])
+		table = rta_getattr_u32(tb[RTA_TABLE]);
+	return table;
+}
+
+extern struct rtnl_handle rth;
+
+struct link_util
+{
+	struct link_util	*next;
+	const char		*id;
+	int			maxattr;
+	int			(*parse_opt)(struct link_util *, int, char **,
+					     struct nlmsghdr *);
+	void			(*print_opt)(struct link_util *, FILE *,
+					     struct rtattr *[]);
+	void			(*print_xstats)(struct link_util *, FILE *,
+					     struct rtattr *);
+};
+
+struct link_util *get_link_kind(const char *kind);
+int get_netns_fd(const char *name);
+
+#ifndef	INFINITY_LIFE_TIME
+#define     INFINITY_LIFE_TIME      0xFFFFFFFFU
+#endif
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipaddress.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipaddress.c
new file mode 100755
index 0000000..9ab65ec
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipaddress.c
@@ -0,0 +1,1267 @@
+/*
+ * 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>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.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"
+
+
+static struct
+{
+	int ifindex;
+	int family;
+	int oneline;
+	int showqueue;
+	inet_prefix pfx;
+	int scope, scopemask;
+	int flags, flagmask;
+	int up;
+	char *label;
+	int flushed;
+	char *flushb;
+	int flushp;
+	int flushe;
+	int group;
+} filter;
+
+static int do_link;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	if (do_link) {
+		iplink_usage();
+	}
+	fprintf(stderr, "Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]\n");
+	fprintf(stderr, "                                                      [ CONFFLAG-LIST ]\n");
+	fprintf(stderr, "       ip addr del IFADDR dev STRING\n");
+	fprintf(stderr, "       ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
+	fprintf(stderr, "                            [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n");
+	fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
+	fprintf(stderr, "          [ broadcast ADDR ] [ anycast ADDR ]\n");
+	fprintf(stderr, "          [ label STRING ] [ scope SCOPE-ID ]\n");
+	fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
+	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
+	fprintf(stderr, "FLAG  := [ permanent | dynamic | secondary | primary |\n");
+	fprintf(stderr, "           tentative | deprecated | dadfailed | temporary |\n");
+	fprintf(stderr, "           CONFFLAG-LIST ]\n");
+	fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n");
+	fprintf(stderr, "CONFFLAG  := [ home | nodad ]\n");
+	fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
+	fprintf(stderr, "LFT := forever | SECONDS\n");
+
+	exit(-1);
+}
+
+void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+{
+	fprintf(fp, "<");
+	if (flags & IFF_UP && !(flags & IFF_RUNNING))
+		fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
+	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);
+	_PF(ALLMULTI);
+	_PF(PROMISC);
+	_PF(MASTER);
+	_PF(SLAVE);
+	_PF(DEBUG);
+	_PF(DYNAMIC);
+	_PF(AUTOMEDIA);
+	_PF(PORTSEL);
+	_PF(NOTRAILERS);
+	_PF(UP);
+	_PF(LOWER_UP);
+	_PF(DORMANT);
+	_PF(ECHO);
+#undef _PF
+        if (flags)
+		fprintf(fp, "%x", flags);
+	if (mdown)
+		fprintf(fp, ",M-DOWN");
+	fprintf(fp, "> ");
+}
+
+static const char *oper_states[] = {
+	"UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN", 
+	"TESTING", "DORMANT",	 "UP"
+};
+
+static void print_operstate(FILE *f, __u8 state)
+{
+	if (state >= sizeof(oper_states)/sizeof(oper_states[0]))
+		fprintf(f, "state %#x ", state);
+	else
+		fprintf(f, "state %s ", oper_states[state]);
+}
+
+int get_operstate(const char *name)
+{
+	int i;
+
+	for (i = 0; i < sizeof(oper_states)/sizeof(oper_states[0]); i++)
+		if (strcasecmp(name, oper_states[i]) == 0)
+			return i;
+	return -1;
+}
+
+static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1])
+{
+	int qlen;
+
+	if (tb[IFLA_TXQLEN])
+		qlen = *(int *)RTA_DATA(tb[IFLA_TXQLEN]);
+	else {
+		struct ifreq ifr;
+		int s = socket(AF_INET, SOCK_STREAM, 0);
+
+		if (s < 0)
+			return;
+
+		memset(&ifr, 0, sizeof(ifr));
+		strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME]));
+		if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
+			fprintf(f, "ioctl(SIOCGIFXQLEN) failed: %s\n", strerror(errno));
+			close(s);
+			return;
+		}
+		close(s);
+		qlen = ifr.ifr_qlen;
+	}
+	if (qlen)
+		fprintf(f, "qlen %d", qlen);
+}
+
+static const char *link_modes[] = {
+	"DEFAULT", "DORMANT"
+};
+
+static void print_linkmode(FILE *f, struct rtattr *tb)
+{
+	unsigned int mode = rta_getattr_u8(tb);
+
+	if (mode >= sizeof(link_modes) / sizeof(link_modes[0]))
+		fprintf(f, "mode %d ", mode);
+	else
+		fprintf(f, "mode %s ", link_modes[mode]);
+}
+
+static void print_linktype(FILE *fp, struct rtattr *tb)
+{
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct link_util *lu;
+	char *kind;
+
+	parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
+
+	if (!linkinfo[IFLA_INFO_KIND])
+		return;
+	kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]);
+
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    %s ", kind);
+
+	lu = get_link_kind(kind);
+	if (!lu || !lu->print_opt)
+		return;
+
+	if (1) {
+		struct rtattr *attr[lu->maxattr+1], **data = NULL;
+
+		if (linkinfo[IFLA_INFO_DATA]) {
+			parse_rtattr_nested(attr, lu->maxattr,
+					    linkinfo[IFLA_INFO_DATA]);
+			data = attr;
+		}
+		lu->print_opt(lu, fp, data);
+
+		if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
+		    lu->print_xstats)
+			lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
+	}
+}
+
+static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
+{
+	struct ifla_vf_mac *vf_mac;
+	struct ifla_vf_vlan *vf_vlan;
+	struct ifla_vf_tx_rate *vf_tx_rate;
+	struct ifla_vf_spoofchk *vf_spoofchk;
+	struct rtattr *vf[IFLA_VF_MAX+1];
+	struct rtattr *tmp;
+	SPRINT_BUF(b1);
+
+	if (vfinfo->rta_type != IFLA_VF_INFO) {
+		fprintf(stderr, "BUG: rta type is %d\n", vfinfo->rta_type);
+		return;
+	}
+
+	parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
+
+	vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
+	vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
+	vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
+
+	/* Check if the spoof checking vf info type is supported by
+	 * this kernel.
+	 */
+	tmp = (struct rtattr *)((char *)vf[IFLA_VF_TX_RATE] +
+			vf[IFLA_VF_TX_RATE]->rta_len);
+
+	if (tmp->rta_type != IFLA_VF_SPOOFCHK)
+		vf_spoofchk = NULL;
+	else
+		vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
+
+	fprintf(fp, "\n    vf %d MAC %s", vf_mac->vf,
+		ll_addr_n2a((unsigned char *)&vf_mac->mac,
+		ETH_ALEN, 0, b1, sizeof(b1)));
+	if (vf_vlan->vlan)
+		fprintf(fp, ", vlan %d", vf_vlan->vlan);
+	if (vf_vlan->qos)
+		fprintf(fp, ", qos %d", vf_vlan->qos);
+	if (vf_tx_rate->rate)
+		fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
+	if (vf_spoofchk && vf_spoofchk->setting != -1) {
+		if (vf_spoofchk->setting)
+			fprintf(fp, ", spoof checking on");
+		else
+			fprintf(fp, ", spoof checking off");
+	}
+}
+
+static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s) {
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+		s->rx_compressed ? "compressed" : "", _SL_);
+	fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+		(uint64_t)s->rx_bytes,
+		(uint64_t)s->rx_packets,
+		(uint64_t)s->rx_errors,
+		(uint64_t)s->rx_dropped,
+		(uint64_t)s->rx_over_errors,
+		(uint64_t)s->multicast);
+	if (s->rx_compressed)
+		fprintf(fp, " %-7"PRIu64"",
+			(uint64_t)s->rx_compressed);
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
+		fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+			(uint64_t)s->rx_length_errors,
+			(uint64_t)s->rx_crc_errors,
+			(uint64_t)s->rx_frame_errors,
+			(uint64_t)s->rx_fifo_errors,
+			(uint64_t)s->rx_missed_errors);
+	}
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+		(uint64_t)s->tx_compressed ? "compressed" : "", _SL_);
+	fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+		(uint64_t)s->tx_bytes,
+		(uint64_t)s->tx_packets,
+		(uint64_t)s->tx_errors,
+		(uint64_t)s->tx_dropped,
+		(uint64_t)s->tx_carrier_errors,
+		(uint64_t)s->collisions);
+	if (s->tx_compressed)
+		fprintf(fp, " %-7"PRIu64"",
+			(uint64_t)s->tx_compressed);
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
+		fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
+			(uint64_t)s->tx_aborted_errors,
+			(uint64_t)s->tx_fifo_errors,
+			(uint64_t)s->tx_window_errors,
+			(uint64_t)s->tx_heartbeat_errors);
+	}
+}
+
+static void print_link_stats(FILE *fp, const struct rtnl_link_stats *s)
+{
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+		s->rx_compressed ? "compressed" : "", _SL_);
+	fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
+		s->rx_bytes, s->rx_packets, s->rx_errors,
+		s->rx_dropped, s->rx_over_errors,
+		s->multicast
+		);
+	if (s->rx_compressed)
+		fprintf(fp, " %-7u", s->rx_compressed);
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
+		fprintf(fp, "               %-7u  %-7u %-7u %-7u %-7u",
+			s->rx_length_errors,
+			s->rx_crc_errors,
+			s->rx_frame_errors,
+			s->rx_fifo_errors,
+			s->rx_missed_errors
+			);
+	}
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+		s->tx_compressed ? "compressed" : "", _SL_);
+	fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
+		s->tx_bytes, s->tx_packets, s->tx_errors,
+		s->tx_dropped, s->tx_carrier_errors, s->collisions);
+	if (s->tx_compressed)
+		fprintf(fp, " %-7u", s->tx_compressed);
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
+		fprintf(fp, "               %-7u  %-7u %-7u %-7u",
+			s->tx_aborted_errors,
+			s->tx_fifo_errors,
+			s->tx_window_errors,
+			s->tx_heartbeat_errors
+			);
+	}
+}
+
+int print_linkinfo(const 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;
+
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+	if (tb[IFLA_IFNAME] == NULL) {
+		fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index);
+	}
+	if (filter.label &&
+	    (!filter.family || filter.family == AF_PACKET) &&
+	    fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+		return 0;
+
+	if (tb[IFLA_GROUP]) {
+		int group = *(int*)RTA_DATA(tb[IFLA_GROUP]);
+		if (group != filter.group)
+			return -1;
+	}
+
+	if (n->nlmsg_type == RTM_DELLINK)
+		fprintf(fp, "Deleted ");
+
+	fprintf(fp, "%d: %s", ifi->ifi_index,
+		tb[IFLA_IFNAME] ? rta_getattr_str(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 ", rta_getattr_str(tb[IFLA_QDISC]));
+	if (tb[IFLA_MASTER]) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+	}
+
+	if (tb[IFLA_OPERSTATE])
+		print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
+
+	if (do_link && tb[IFLA_LINKMODE])
+		print_linkmode(fp, tb[IFLA_LINKMODE]);
+
+	if (filter.showqueue)
+		print_queuelen(fp, tb);
+
+	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)));
+		}
+	}
+
+	if (do_link && tb[IFLA_LINKINFO] && show_details)
+		print_linktype(fp, tb[IFLA_LINKINFO]);
+
+	if (do_link && tb[IFLA_IFALIAS])
+		fprintf(fp,"\n    alias %s", 
+			rta_getattr_str(tb[IFLA_IFALIAS]));
+
+	if (do_link && show_stats) {
+		if (tb[IFLA_STATS64])
+			print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]));
+		else if (tb[IFLA_STATS])
+			print_link_stats(fp, RTA_DATA(tb[IFLA_STATS]));
+	}
+
+	if (do_link && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
+		struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
+		int rem = RTA_PAYLOAD(vflist);
+		for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
+			print_vfinfo(fp, i);
+	}
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+static int flush_update(void)
+{
+	if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
+		perror("Failed to send flush request");
+		return -1;
+	}
+	filter.flushp = 0;
+	return 0;
+}
+
+static int set_lifetime(unsigned int *lifetime, char *argv)
+{
+	if (strcmp(argv, "forever") == 0)
+		*lifetime = INFINITY_LIFE_TIME;
+	else if (get_u32(lifetime, argv, 0))
+		return -1;
+
+	return 0;
+}
+
+int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		   void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	int deprecated = 0;
+	/* Use local copy of ifa_flags to not interfere with filtering code */
+	unsigned int ifa_flags;
+	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;
+	}
+
+	if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
+		return 0;
+
+	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 (filter.family && filter.family != ifa->ifa_family)
+		return 0;
+
+	if (filter.flushb) {
+		struct nlmsghdr *fn;
+		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+			if (flush_update())
+				return -1;
+		}
+		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+		memcpy(fn, n, n->nlmsg_len);
+		fn->nlmsg_type = RTM_DELADDR;
+		fn->nlmsg_flags = NLM_F_REQUEST;
+		fn->nlmsg_seq = ++rth.seq;
+		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+		filter.flushed++;
+		if (show_stats < 2)
+			return 0;
+	}
+
+	if (n->nlmsg_type == RTM_DELADDR)
+		fprintf(fp, "Deleted ");
+
+	if (filter.oneline || filter.flushb)
+		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 if (ifa->ifa_family == AF_DECnet)
+		fprintf(fp, "    dnet ");
+	else if (ifa->ifa_family == AF_IPX)
+		fprintf(fp, "     ipx ");
+	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)));
+	ifa_flags = ifa->ifa_flags;
+	if (ifa->ifa_flags&IFA_F_SECONDARY) {
+		ifa_flags &= ~IFA_F_SECONDARY;
+		if (ifa->ifa_family == AF_INET6)
+			fprintf(fp, "temporary ");
+		else
+			fprintf(fp, "secondary ");
+	}
+	if (ifa->ifa_flags&IFA_F_TENTATIVE) {
+		ifa_flags &= ~IFA_F_TENTATIVE;
+		fprintf(fp, "tentative ");
+	}
+	if (ifa->ifa_flags&IFA_F_DEPRECATED) {
+		ifa_flags &= ~IFA_F_DEPRECATED;
+		deprecated = 1;
+		fprintf(fp, "deprecated ");
+	}
+	if (ifa->ifa_flags&IFA_F_HOMEADDRESS) {
+		ifa_flags &= ~IFA_F_HOMEADDRESS;
+		fprintf(fp, "home ");
+	}
+	if (ifa->ifa_flags&IFA_F_NODAD) {
+		ifa_flags &= ~IFA_F_NODAD;
+		fprintf(fp, "nodad ");
+	}
+	if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
+		fprintf(fp, "dynamic ");
+	} else
+		ifa_flags &= ~IFA_F_PERMANENT;
+	if (ifa->ifa_flags&IFA_F_DADFAILED) {
+		ifa_flags &= ~IFA_F_DADFAILED;
+		fprintf(fp, "dadfailed ");
+	}
+	if (ifa_flags)
+		fprintf(fp, "flags %02x ", ifa_flags);
+	if (rta_tb[IFA_LABEL])
+		fprintf(fp, "%s", rta_getattr_str(rta_tb[IFA_LABEL]));
+	if (rta_tb[IFA_CACHEINFO]) {
+		struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "       valid_lft ");
+		if (ci->ifa_valid == INFINITY_LIFE_TIME)
+			fprintf(fp, "forever");
+		else
+			fprintf(fp, "%usec", ci->ifa_valid);
+		fprintf(fp, " preferred_lft ");
+		if (ci->ifa_prefered == INFINITY_LIFE_TIME)
+			fprintf(fp, "forever");
+		else {
+			if (deprecated)
+				fprintf(fp, "%dsec", ci->ifa_prefered);
+			else
+				fprintf(fp, "%usec", ci->ifa_prefered);
+		}
+	}
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+int print_addrinfo_primary(const struct sockaddr_nl *who, struct nlmsghdr *n,
+			   void *arg)
+{
+	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+	if (ifa->ifa_flags & IFA_F_SECONDARY)
+		return 0;
+
+	return print_addrinfo(who, n, arg);
+}
+
+int print_addrinfo_secondary(const struct sockaddr_nl *who, struct nlmsghdr *n,
+			     void *arg)
+{
+	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
+		return 0;
+
+	return print_addrinfo(who, n, arg);
+}
+
+struct nlmsg_list
+{
+	struct nlmsg_list *next;
+	struct nlmsghdr	  h;
+};
+
+static 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;
+}
+
+
+static int store_nlmsg(const 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;
+}
+
+static int ipaddr_list_or_flush(int argc, char **argv, int flush)
+{
+	struct nlmsg_list *linfo = NULL;
+	struct nlmsg_list *ainfo = NULL;
+	struct nlmsg_list *l, *n;
+	char *filter_dev = NULL;
+	int no_link = 0;
+
+	ipaddr_reset_filter(oneline);
+	filter.showqueue = 1;
+
+	if (filter.family == AF_UNSPEC)
+		filter.family = preferred_family;
+
+	filter.group = INIT_NETDEV_GROUP;
+
+	if (flush) {
+		if (argc <= 0) {
+			fprintf(stderr, "Flush requires arguments.\n");
+
+			return -1;
+		}
+		if (filter.family == AF_PACKET) {
+			fprintf(stderr, "Cannot flush link addresses.\n");
+			return -1;
+		}
+	}
+
+	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) {
+			unsigned 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, "dynamic") == 0) {
+			filter.flags &= ~IFA_F_PERMANENT;
+			filter.flagmask |= IFA_F_PERMANENT;
+		} else if (strcmp(*argv, "permanent") == 0) {
+			filter.flags |= IFA_F_PERMANENT;
+			filter.flagmask |= IFA_F_PERMANENT;
+		} else if (strcmp(*argv, "secondary") == 0 ||
+			   strcmp(*argv, "temporary") == 0) {
+			filter.flags |= IFA_F_SECONDARY;
+			filter.flagmask |= IFA_F_SECONDARY;
+		} else if (strcmp(*argv, "primary") == 0) {
+			filter.flags &= ~IFA_F_SECONDARY;
+			filter.flagmask |= IFA_F_SECONDARY;
+		} else if (strcmp(*argv, "tentative") == 0) {
+			filter.flags |= IFA_F_TENTATIVE;
+			filter.flagmask |= IFA_F_TENTATIVE;
+		} else if (strcmp(*argv, "deprecated") == 0) {
+			filter.flags |= IFA_F_DEPRECATED;
+			filter.flagmask |= IFA_F_DEPRECATED;
+		} else if (strcmp(*argv, "home") == 0) {
+			filter.flags |= IFA_F_HOMEADDRESS;
+			filter.flagmask |= IFA_F_HOMEADDRESS;
+		} else if (strcmp(*argv, "nodad") == 0) {
+			filter.flags |= IFA_F_NODAD;
+			filter.flagmask |= IFA_F_NODAD;
+		} else if (strcmp(*argv, "dadfailed") == 0) {
+			filter.flags |= IFA_F_DADFAILED;
+			filter.flagmask |= IFA_F_DADFAILED;
+		} else if (strcmp(*argv, "label") == 0) {
+			NEXT_ARG();
+			filter.label = *argv;
+		} else if (strcmp(*argv, "group") == 0) {
+			NEXT_ARG();
+			if (rtnl_group_a2n(&filter.group, *argv))
+				invarg("Invalid \"group\" value\n", *argv);
+		} else {
+			if (strcmp(*argv, "dev") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (filter_dev)
+				duparg2("dev", *argv);
+			filter_dev = *argv;
+		}
+		argv++; argc--;
+	}
+
+	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) < 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 (flush) {
+		int round = 0;
+		char flushb[4096-512];
+
+		filter.flushb = flushb;
+		filter.flushp = 0;
+		filter.flushe = sizeof(flushb);
+
+		while ((max_flush_loops == 0) || (round < max_flush_loops)) {
+			const struct rtnl_dump_filter_arg a[3] = {
+				{
+					.filter = print_addrinfo_secondary,
+					.arg1 = stdout,
+				},
+				{
+					.filter = print_addrinfo_primary,
+					.arg1 = stdout,
+				},
+				{
+					.filter = NULL,
+					.arg1 = NULL,
+				},
+			};
+			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+				perror("Cannot send dump request");
+				exit(1);
+			}
+			filter.flushed = 0;
+			if (rtnl_dump_filter_l(&rth, a) < 0) {
+				fprintf(stderr, "Flush terminated\n");
+				exit(1);
+			}
+			if (filter.flushed == 0) {
+flush_done:
+				if (show_stats) {
+					if (round == 0)
+						printf("Nothing to flush.\n");
+					else 
+						printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+				}
+				fflush(stdout);
+				return 0;
+			}
+			round++;
+			if (flush_update() < 0)
+				return 1;
+
+			if (show_stats) {
+				printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed);
+				fflush(stdout);
+			}
+
+			/* If we are flushing, and specifying primary, then we
+			 * want to flush only a single round.  Otherwise, we'll
+			 * start flushing secondaries that were promoted to
+			 * primaries.
+			 */
+			if (!(filter.flags & IFA_F_SECONDARY) && (filter.flagmask & IFA_F_SECONDARY))
+				goto flush_done;
+		}
+		fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", max_flush_loops);
+		fflush(stderr);
+		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) < 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];
+					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 = n) {
+		n = 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);
+		free(l);
+	}
+
+	return 0;
+}
+
+int ipaddr_list_link(int argc, char **argv)
+{
+	preferred_family = AF_PACKET;
+	do_link = 1;
+	return ipaddr_list_or_flush(argc, argv, 0);
+}
+
+void ipaddr_reset_filter(int oneline)
+{
+	memset(&filter, 0, sizeof(filter));
+	filter.oneline = oneline;
+}
+
+static 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;
+}
+
+static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct ifaddrmsg 	ifa;
+		char   			buf[256];
+	} req;
+	char  *d = NULL;
+	char  *l = NULL;
+	char  *lcl_arg = NULL;
+	char  *valid_lftp = NULL;
+	char  *preferred_lftp = 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;
+	__u32 preferred_lft = INFINITY_LIFE_TIME;
+	__u32 valid_lft = INFINITY_LIFE_TIME;
+	struct ifa_cacheinfo cinfo;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST | flags;
+	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) {
+			unsigned 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 (matches(*argv, "valid_lft") == 0) {
+			if (valid_lftp)
+				duparg("valid_lft", *argv);
+			NEXT_ARG();
+			valid_lftp = *argv;
+			if (set_lifetime(&valid_lft, *argv))
+				invarg("valid_lft value", *argv);
+		} else if (matches(*argv, "preferred_lft") == 0) {
+			if (preferred_lftp)
+				duparg("preferred_lft", *argv);
+			NEXT_ARG();
+			preferred_lftp = *argv;
+			if (set_lifetime(&preferred_lft, *argv))
+				invarg("preferred_lft value", *argv);
+		} else if (strcmp(*argv, "home") == 0) {
+			req.ifa.ifa_flags |= IFA_F_HOMEADDRESS;
+		} else if (strcmp(*argv, "nodad") == 0) {
+			req.ifa.ifa_flags |= IFA_F_NODAD;
+		} else {
+			if (strcmp(*argv, "local") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (local_len)
+				duparg2("local", *argv);
+			lcl_arg = *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);
+		return -1;
+	}
+
+	if (peer_len == 0 && local_len) {
+		if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) {
+			fprintf(stderr,
+			    "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" \
+			    "         Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" \
+			    "         This special behaviour is likely to disappear in further releases,\n" \
+			    "         fix your scripts!\n", lcl_arg, local_len*8);
+		} else {
+			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);
+
+	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 (valid_lftp || preferred_lftp) {
+		if (!valid_lft) {
+			fprintf(stderr, "valid_lft is zero\n");
+			return -1;
+		}
+		if (valid_lft < preferred_lft) {
+			fprintf(stderr, "preferred_lft is greater than valid_lft\n");
+			return -1;
+		}
+
+		memset(&cinfo, 0, sizeof(cinfo));
+		cinfo.ifa_prefered = preferred_lft;
+		cinfo.ifa_valid = valid_lft;
+		addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
+			  sizeof(cinfo));
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+int do_ipaddr(int argc, char **argv)
+{
+	if (argc < 1)
+		return ipaddr_list_or_flush(0, NULL, 0);
+	if (matches(*argv, "add") == 0)
+		return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
+	if (matches(*argv, "change") == 0 ||
+		strcmp(*argv, "chg") == 0)
+		return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc-1, argv+1);
+	if (matches(*argv, "replace") == 0)
+		return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
+	if (matches(*argv, "delete") == 0)
+		return ipaddr_modify(RTM_DELADDR, 0, argc-1, argv+1);
+	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+	    || matches(*argv, "lst") == 0)
+		return ipaddr_list_or_flush(argc-1, argv+1, 0);
+	if (matches(*argv, "flush") == 0)
+		return ipaddr_list_or_flush(argc-1, argv+1, 1);
+	if (matches(*argv, "help") == 0)
+		usage();
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip addr help\".\n", *argv);
+	exit(-1);
+}
+
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipaddrlabel.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipaddrlabel.c
new file mode 100755
index 0000000..eb6a48c
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipaddrlabel.c
@@ -0,0 +1,268 @@
+/*
+ * ipaddrlabel.c	"ip addrlabel"
+ *
+ * Copyright (C)2007 USAGI/WIDE Project
+ *
+ * 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
+ *
+ *
+ * Based on iprule.c.
+ *
+ * Authors:	YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <linux/types.h>
+#include <linux/if_addrlabel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#define IFAL_RTA(r)	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))))
+#define IFAL_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ifaddrlblmsg))
+
+extern struct rtnl_handle rth;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip addrlabel [ list | add | del | flush ] prefix PREFIX [ dev DEV ] [ label LABEL ]\n");
+	exit(-1);
+}
+
+int print_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct ifaddrlblmsg *ifal = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[IFAL_MAX+1];
+	char abuf[256];
+
+	if (n->nlmsg_type != RTM_NEWADDRLABEL && n->nlmsg_type != RTM_DELADDRLABEL)
+		return 0;
+
+	len -= NLMSG_LENGTH(sizeof(*ifal));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(tb, IFAL_MAX, IFAL_RTA(ifal), len);
+
+	if (n->nlmsg_type == RTM_DELADDRLABEL)
+		fprintf(fp, "Deleted ");
+
+	if (tb[IFAL_ADDRESS]) {
+		fprintf(fp, "prefix %s/%u ",
+			format_host(ifal->ifal_family,
+				    RTA_PAYLOAD(tb[IFAL_ADDRESS]),
+				    RTA_DATA(tb[IFAL_ADDRESS]),
+				    abuf, sizeof(abuf)),
+			ifal->ifal_prefixlen);
+	}
+
+	if (ifal->ifal_index)
+		fprintf(fp, "dev %s ", ll_index_to_name(ifal->ifal_index));
+
+	if (tb[IFAL_LABEL] && RTA_PAYLOAD(tb[IFAL_LABEL]) == sizeof(int32_t)) {
+		int32_t label;
+		memcpy(&label, RTA_DATA(tb[IFAL_LABEL]), sizeof(label));
+		fprintf(fp, "label %d ", label);
+	}
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+static int ipaddrlabel_list(int argc, char **argv)
+{
+	int af = preferred_family;
+
+	if (af == AF_UNSPEC)
+		af = AF_INET6;
+
+	if (argc > 0) {
+		fprintf(stderr, "\"ip addrlabel show\" does not take any arguments.\n");
+		return -1;
+	}
+
+	if (rtnl_wilddump_request(&rth, af, RTM_GETADDRLABEL) < 0) {
+		perror("Cannot send dump request");
+		return 1;
+	}
+
+	if (rtnl_dump_filter(&rth, print_addrlabel, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int ipaddrlabel_modify(int cmd, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct ifaddrlblmsg	ifal;
+		char   			buf[1024];
+	} req;
+
+	inet_prefix prefix;
+	uint32_t label = 0xffffffffUL;
+	char *p = NULL;
+	char *l = NULL;        
+
+	memset(&req, 0, sizeof(req));
+	memset(&prefix, 0, sizeof(prefix));
+
+	req.n.nlmsg_type = cmd;
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.ifal.ifal_family = preferred_family;
+	req.ifal.ifal_prefixlen = 0;
+	req.ifal.ifal_index = 0;
+
+	if (cmd == RTM_NEWADDRLABEL) {
+		req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+	}
+
+	while (argc > 0) {
+		if (strcmp(*argv, "prefix") == 0) {
+			NEXT_ARG();
+			p = *argv;
+			get_prefix(&prefix, *argv, preferred_family);
+		} else if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if ((req.ifal.ifal_index = ll_name_to_index(*argv)) == 0)
+				invarg("dev is invalid\n", *argv);
+		} else if (strcmp(*argv, "label") == 0) {
+			NEXT_ARG();
+			l = *argv;
+			if (get_u32(&label, *argv, 0) || label == 0xffffffffUL)
+				invarg("label is invalid\n", *argv);
+		}
+		argc--;
+		argv++;
+	}
+	if (p == NULL) {
+		fprintf(stderr, "Not enough information: \"prefix\" argument is required.\n");
+		return -1;
+	}
+	if (l == NULL) {
+		fprintf(stderr, "Not enough information: \"label\" argument is required.\n");
+		return -1;
+	}
+	addattr32(&req.n, sizeof(req), IFAL_LABEL, label);
+	addattr_l(&req.n, sizeof(req), IFAL_ADDRESS, &prefix.data, prefix.bytelen);
+	req.ifal.ifal_prefixlen = prefix.bitlen;
+
+	if (req.ifal.ifal_family == AF_UNSPEC)
+		req.ifal.ifal_family = AF_INET6;
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		return 2;
+
+	return 0;
+}
+
+
+static int flush_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	struct rtnl_handle rth2;
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[IFAL_MAX+1];
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(tb, IFAL_MAX, RTM_RTA(r), len);
+
+	if (tb[IFAL_ADDRESS]) {
+		n->nlmsg_type = RTM_DELADDRLABEL;
+		n->nlmsg_flags = NLM_F_REQUEST;
+
+		if (rtnl_open(&rth2, 0) < 0)
+			return -1;
+
+		if (rtnl_talk(&rth2, n, 0, 0, NULL) < 0)
+			return -2;
+
+		rtnl_close(&rth2);
+	}
+
+	return 0;
+}
+
+static int ipaddrlabel_flush(int argc, char **argv)
+{
+	int af = preferred_family;
+
+	if (af == AF_UNSPEC)
+		af = AF_INET6;
+
+	if (argc > 0) {
+		fprintf(stderr, "\"ip addrlabel flush\" does not allow extra arguments\n");
+		return -1;
+	}
+
+	if (rtnl_wilddump_request(&rth, af, RTM_GETADDRLABEL) < 0) {
+		perror("Cannot send dump request");
+		return 1;
+	}
+
+	if (rtnl_dump_filter(&rth, flush_addrlabel, NULL) < 0) {
+		fprintf(stderr, "Flush terminated\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+int do_ipaddrlabel(int argc, char **argv)
+{
+	ll_init_map(&rth);
+
+	if (argc < 1) {
+		return ipaddrlabel_list(0, NULL);
+	} else if (matches(argv[0], "list") == 0 ||
+		   matches(argv[0], "show") == 0) {
+		return ipaddrlabel_list(argc-1, argv+1);
+	} else if (matches(argv[0], "add") == 0) {
+		return ipaddrlabel_modify(RTM_NEWADDRLABEL, argc-1, argv+1);
+	} else if (matches(argv[0], "delete") == 0) {
+		return ipaddrlabel_modify(RTM_DELADDRLABEL, argc-1, argv+1);
+	} else if (matches(argv[0], "flush") == 0) {
+		return ipaddrlabel_flush(argc-1, argv+1);
+	} else if (matches(argv[0], "help") == 0)
+		usage();
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip addrlabel help\".\n", *argv);
+	exit(-1);
+}
+
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipl2tp.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipl2tp.c
new file mode 100755
index 0000000..c5683f5
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipl2tp.c
@@ -0,0 +1,809 @@
+/*
+ * ipl2tp.c	       "ip l2tp"
+ *
+ *		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.
+ *
+ * Original Author:	James Chapman <jchapman@katalix.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+
+#include <linux/genetlink.h>
+#include <linux/l2tp.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+enum {
+	L2TP_ADD,
+	L2TP_CHG,
+	L2TP_DEL,
+	L2TP_GET
+};
+
+struct l2tp_parm {
+	uint32_t tunnel_id;
+	uint32_t peer_tunnel_id;
+	uint32_t session_id;
+	uint32_t peer_session_id;
+	uint32_t offset;
+	uint32_t peer_offset;
+	enum l2tp_encap_type encap;
+	uint16_t local_udp_port;
+	uint16_t peer_udp_port;
+	int cookie_len;
+	uint8_t cookie[8];
+	int peer_cookie_len;
+	uint8_t peer_cookie[8];
+	struct in_addr local_ip;
+	struct in_addr peer_ip;
+
+	uint16_t pw_type;
+	uint16_t mtu;
+	int udp_csum:1;
+	int recv_seq:1;
+	int send_seq:1;
+	int lns_mode:1;
+	int data_seq:2;
+	int tunnel:1;
+	int session:1;
+	int reorder_timeout;
+	const char *ifname;
+};
+
+struct l2tp_stats {
+	uint64_t data_rx_packets;
+	uint64_t data_rx_bytes;
+	uint64_t data_rx_errors;
+	uint64_t data_rx_oos_packets;
+	uint64_t data_rx_oos_discards;
+	uint64_t data_tx_packets;
+	uint64_t data_tx_bytes;
+	uint64_t data_tx_errors;
+};
+
+struct l2tp_data {
+	struct l2tp_parm config;
+	struct l2tp_stats stats;
+};
+
+/* netlink socket */
+static struct rtnl_handle genl_rth;
+static int genl_family = -1;
+
+/*****************************************************************************
+ * Netlink actions
+ *****************************************************************************/
+
+static int create_tunnel(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_TUNNEL_CREATE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
+	addattr8(&req.n, 1024, L2TP_ATTR_PROTO_VERSION, 3);
+	addattr16(&req.n, 1024, L2TP_ATTR_ENCAP_TYPE, p->encap);
+
+	addattr32(&req.n, 1024, L2TP_ATTR_IP_SADDR, p->local_ip.s_addr);
+	addattr32(&req.n, 1024, L2TP_ATTR_IP_DADDR, p->peer_ip.s_addr);
+	if (p->encap == L2TP_ENCAPTYPE_UDP) {
+		addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port);
+		addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port);
+	}
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static int delete_tunnel(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_TUNNEL_DELETE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static int create_session(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_SESSION_CREATE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id);
+	addattr16(&req.n, 1024, L2TP_ATTR_PW_TYPE, p->pw_type);
+
+	if (p->mtu)		addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu);
+	if (p->recv_seq)	addattr(&req.n, 1024, L2TP_ATTR_RECV_SEQ);
+	if (p->send_seq)	addattr(&req.n, 1024, L2TP_ATTR_SEND_SEQ);
+	if (p->lns_mode)	addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE);
+	if (p->data_seq)	addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq);
+	if (p->reorder_timeout) addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
+					  p->reorder_timeout);
+	if (p->offset)		addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset);
+	if (p->cookie_len)	addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE,
+					  p->cookie, p->cookie_len);
+	if (p->peer_cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
+					  p->peer_cookie,  p->peer_cookie_len);
+	if (p->ifname && p->ifname[0])
+		addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static int delete_session(struct l2tp_parm *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char   			buf[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.g.cmd = L2TP_CMD_SESSION_DELETE;
+	req.g.version = L2TP_GENL_VERSION;
+
+	addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, NULL) < 0)
+		return -2;
+
+	return 0;
+}
+
+static void print_cookie(char *name, const uint8_t *cookie, int len)
+{
+	printf("  %s %02x%02x%02x%02x", name,
+	       cookie[0], cookie[1],
+	       cookie[2], cookie[3]);
+	if (len == 8)
+		printf("%02x%02x%02x%02x",
+		       cookie[4], cookie[5],
+		       cookie[6], cookie[7]);
+}
+
+static void print_tunnel(const struct l2tp_data *data)
+{
+	const struct l2tp_parm *p = &data->config;
+
+	printf("Tunnel %u, encap %s\n",
+	       p->tunnel_id,
+	       p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
+	       p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
+	printf("  From %s ", inet_ntoa(p->local_ip));
+	printf("to %s\n", inet_ntoa(p->peer_ip));
+	printf("  Peer tunnel %u\n",
+	       p->peer_tunnel_id);
+
+	if (p->encap == L2TP_ENCAPTYPE_UDP)
+		printf("  UDP source / dest ports: %hu/%hu\n",
+		       p->local_udp_port, p->peer_udp_port);
+}
+
+static void print_session(struct l2tp_data *data)
+{
+	struct l2tp_parm *p = &data->config;
+
+	printf("Session %u in tunnel %u\n",
+	       p->session_id, p->tunnel_id);
+	printf("  Peer session %u, tunnel %u\n",
+	       p->peer_session_id, p->peer_tunnel_id);
+
+	if (p->ifname != NULL) {
+		printf("  interface name: %s\n", p->ifname);
+	}
+	printf("  offset %u, peer offset %u\n",
+	       p->offset, p->peer_offset);
+	if (p->cookie_len > 0)
+		print_cookie("cookie", p->cookie, p->cookie_len);
+	if (p->peer_cookie_len > 0)
+		print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
+
+	if (p->reorder_timeout != 0) {
+		printf("  reorder timeout: %u\n", p->reorder_timeout);
+	}
+}
+
+static int get_response(struct nlmsghdr *n, void *arg)
+{
+	struct genlmsghdr *ghdr;
+	struct l2tp_data *data = arg;
+	struct l2tp_parm *p = &data->config;
+	struct rtattr *attrs[L2TP_ATTR_MAX + 1];
+	struct rtattr *nla_stats;
+	int len;
+
+	/* Validate message and parse attributes */
+	if (n->nlmsg_type == NLMSG_ERROR)
+		return -EBADMSG;
+
+	ghdr = NLMSG_DATA(n);
+	len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ghdr));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(attrs, L2TP_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
+
+	if (attrs[L2TP_ATTR_PW_TYPE])
+		p->pw_type = rta_getattr_u16(attrs[L2TP_ATTR_PW_TYPE]);
+	if (attrs[L2TP_ATTR_ENCAP_TYPE])
+		p->encap = rta_getattr_u16(attrs[L2TP_ATTR_ENCAP_TYPE]);
+	if (attrs[L2TP_ATTR_OFFSET])
+		p->offset = rta_getattr_u16(attrs[L2TP_ATTR_OFFSET]);
+	if (attrs[L2TP_ATTR_DATA_SEQ])
+		p->data_seq = rta_getattr_u16(attrs[L2TP_ATTR_DATA_SEQ]);
+	if (attrs[L2TP_ATTR_CONN_ID])
+		p->tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_CONN_ID]);
+	if (attrs[L2TP_ATTR_PEER_CONN_ID])
+		p->peer_tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_CONN_ID]);
+	if (attrs[L2TP_ATTR_SESSION_ID])
+		p->session_id = rta_getattr_u32(attrs[L2TP_ATTR_SESSION_ID]);
+	if (attrs[L2TP_ATTR_PEER_SESSION_ID])
+		p->peer_session_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]);
+
+	p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM];
+	if (attrs[L2TP_ATTR_COOKIE])
+		memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]),
+		       p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE]));
+
+	if (attrs[L2TP_ATTR_PEER_COOKIE])
+		memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
+		       p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
+
+	p->recv_seq = !!attrs[L2TP_ATTR_RECV_SEQ];
+	p->send_seq = !!attrs[L2TP_ATTR_SEND_SEQ];
+
+	if (attrs[L2TP_ATTR_RECV_TIMEOUT])
+		p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]);
+	if (attrs[L2TP_ATTR_IP_SADDR])
+		p->local_ip.s_addr = rta_getattr_u32(attrs[L2TP_ATTR_IP_SADDR]);
+	if (attrs[L2TP_ATTR_IP_DADDR])
+		p->peer_ip.s_addr = rta_getattr_u32(attrs[L2TP_ATTR_IP_DADDR]);
+	if (attrs[L2TP_ATTR_UDP_SPORT])
+		p->local_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_SPORT]);
+	if (attrs[L2TP_ATTR_UDP_DPORT])
+		p->peer_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_DPORT]);
+	if (attrs[L2TP_ATTR_MTU])
+		p->mtu = rta_getattr_u16(attrs[L2TP_ATTR_MTU]);
+	if (attrs[L2TP_ATTR_IFNAME])
+		p->ifname = rta_getattr_str(attrs[L2TP_ATTR_IFNAME]);
+
+	nla_stats = attrs[L2TP_ATTR_STATS];
+	if (nla_stats) {
+		struct rtattr *tb[L2TP_ATTR_STATS_MAX + 1];
+
+		parse_rtattr_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats);
+
+		if (tb[L2TP_ATTR_TX_PACKETS])
+			data->stats.data_tx_packets = rta_getattr_u64(tb[L2TP_ATTR_TX_PACKETS]);
+		if (tb[L2TP_ATTR_TX_BYTES])
+			data->stats.data_tx_bytes = rta_getattr_u64(tb[L2TP_ATTR_TX_BYTES]);
+		if (tb[L2TP_ATTR_TX_ERRORS])
+			data->stats.data_tx_errors = rta_getattr_u64(tb[L2TP_ATTR_TX_ERRORS]);
+		if (tb[L2TP_ATTR_RX_PACKETS])
+			data->stats.data_rx_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_PACKETS]);
+		if (tb[L2TP_ATTR_RX_BYTES])
+			data->stats.data_rx_bytes = rta_getattr_u64(tb[L2TP_ATTR_RX_BYTES]);
+		if (tb[L2TP_ATTR_RX_ERRORS])
+			data->stats.data_rx_errors = rta_getattr_u64(tb[L2TP_ATTR_RX_ERRORS]);
+		if (tb[L2TP_ATTR_RX_SEQ_DISCARDS])
+			data->stats.data_rx_oos_discards = rta_getattr_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]);
+		if (tb[L2TP_ATTR_RX_OOS_PACKETS])
+			data->stats.data_rx_oos_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]);
+	}
+
+	return 0;
+}
+
+static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	int ret = get_response(n, arg);
+
+	if (ret == 0)
+		print_session(arg);
+
+	return ret;
+}
+
+static int get_session(struct l2tp_data *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char buf[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
+
+	req.g.cmd = L2TP_CMD_SESSION_GET;
+	req.g.version = L2TP_GENL_VERSION;
+
+	if (p->config.tunnel_id && p->config.session_id) {
+		addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
+		addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, p->config.session_id);
+	}
+
+	if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
+		return -2;
+
+	if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	int ret = get_response(n, arg);
+
+	if (ret == 0)
+		print_tunnel(arg);
+
+	return ret;
+}
+
+static int get_tunnel(struct l2tp_data *p)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct genlmsghdr	g;
+		char buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.n.nlmsg_type = genl_family;
+	req.n.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
+
+	req.g.cmd = L2TP_CMD_TUNNEL_GET;
+	req.g.version = L2TP_GENL_VERSION;
+
+	if (p->config.tunnel_id)
+		addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
+
+	if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
+		return -2;
+
+	if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+ * Command parser
+ *****************************************************************************/
+
+static int hex(char ch)
+{
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	if ((ch >= 'A') && (ch <= 'F'))
+		return ch - 'A' + 10;
+	return -1;
+}
+
+static int hex2mem(const char *buf, uint8_t *mem, int count)
+{
+	int i, j;
+	int c;
+
+	for (i = 0, j = 0; i < count; i++, j += 2) {
+		c = hex(buf[j]);
+		if (c < 0)
+			goto err;
+
+		mem[i] = c << 4;
+
+		c = hex(buf[j + 1]);
+		if (c < 0)
+			goto err;
+
+		mem[i] |= c;
+	}
+
+	return 0;
+
+err:
+	return -1;
+}
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip l2tp add tunnel\n");
+	fprintf(stderr, "          remote ADDR local ADDR\n");
+	fprintf(stderr, "          tunnel_id ID peer_tunnel_id ID\n");
+	fprintf(stderr, "          [ encap { ip | udp } ]\n");
+	fprintf(stderr, "          [ udp_sport PORT ] [ udp_dport PORT ]\n");
+	fprintf(stderr, "Usage: ip l2tp add session [ name NAME ]\n");
+	fprintf(stderr, "          tunnel_id ID\n");
+	fprintf(stderr, "          session_id ID peer_session_id ID\n");
+	fprintf(stderr, "          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n");
+	fprintf(stderr, "          [ offset OFFSET ] [ peer_offset OFFSET ]\n");
+	fprintf(stderr, "       ip l2tp del tunnel tunnel_id ID\n");
+	fprintf(stderr, "       ip l2tp del session tunnel_id ID session_id ID\n");
+	fprintf(stderr, "       ip l2tp show tunnel [ tunnel_id ID ]\n");
+	fprintf(stderr, "       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME   := STRING\n");
+	fprintf(stderr, "       ADDR   := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       PORT   := { 0..65535 }\n");
+	fprintf(stderr, "       ID     := { 1..4294967295 }\n");
+	fprintf(stderr, "       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n");
+	exit(-1);
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
+{
+	memset(p, 0, sizeof(*p));
+
+	if (argc == 0)
+		usage();
+
+	while (argc > 0) {
+		if (strcmp(*argv, "encap") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ip") == 0) {
+				p->encap = L2TP_ENCAPTYPE_IP;
+			} else if (strcmp(*argv, "udp") == 0) {
+				p->encap = L2TP_ENCAPTYPE_UDP;
+			} else {
+				fprintf(stderr, "Unknown tunnel encapsulation.\n");
+				exit(-1);
+			}
+		} else if (strcmp(*argv, "name") == 0) {
+			NEXT_ARG();
+			p->ifname = *argv;
+		} else if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			p->peer_ip.s_addr = get_addr32(*argv);
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			p->local_ip.s_addr = get_addr32(*argv);
+		} else if ((strcmp(*argv, "tunnel_id") == 0) ||
+			   (strcmp(*argv, "tid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->tunnel_id = uval;
+		} else if ((strcmp(*argv, "peer_tunnel_id") == 0) ||
+			   (strcmp(*argv, "ptid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->peer_tunnel_id = uval;
+		} else if ((strcmp(*argv, "session_id") == 0) ||
+			   (strcmp(*argv, "sid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->session_id = uval;
+		} else if ((strcmp(*argv, "peer_session_id") == 0) ||
+			   (strcmp(*argv, "psid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->peer_session_id = uval;
+		} else if (strcmp(*argv, "udp_sport") == 0) {
+			__u16 uval;
+			NEXT_ARG();
+			if (get_u16(&uval, *argv, 0))
+				invarg("invalid port\n", *argv);
+			p->local_udp_port = uval;
+		} else if (strcmp(*argv, "udp_dport") == 0) {
+			__u16 uval;
+			NEXT_ARG();
+			if (get_u16(&uval, *argv, 0))
+				invarg("invalid port\n", *argv);
+			p->peer_udp_port = uval;
+		} else if (strcmp(*argv, "offset") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid offset\n", *argv);
+			p->offset = uval;
+		} else if (strcmp(*argv, "peer_offset") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid offset\n", *argv);
+			p->peer_offset = uval;
+		} else if (strcmp(*argv, "cookie") == 0) {
+			int slen;
+			NEXT_ARG();
+			slen = strlen(*argv);
+			if ((slen != 8) && (slen != 16))
+				invarg("cookie must be either 8 or 16 hex digits\n", *argv);
+
+			p->cookie_len = slen / 2;
+			if (hex2mem(*argv, p->cookie, p->cookie_len) < 0)
+				invarg("cookie must be a hex string\n", *argv);
+		} else if (strcmp(*argv, "peer_cookie") == 0) {
+			int slen;
+			NEXT_ARG();
+			slen = strlen(*argv);
+			if ((slen != 8) && (slen != 16))
+				invarg("cookie must be either 8 or 16 hex digits\n", *argv);
+
+			p->peer_cookie_len = slen / 2;
+			if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
+				invarg("cookie must be a hex string\n", *argv);
+		} else if (strcmp(*argv, "tunnel") == 0) {
+			p->tunnel = 1;
+		} else if (strcmp(*argv, "session") == 0) {
+			p->session = 1;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Unknown command: %s\n", *argv);
+			usage();
+		}
+
+		argc--; argv++;
+	}
+
+	return 0;
+}
+
+
+static int do_add(int argc, char **argv)
+{
+	struct l2tp_parm p;
+	int ret = 0;
+
+	if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
+		return -1;
+
+	if (!p.tunnel && !p.session)
+		missarg("tunnel or session");
+
+	if (p.tunnel_id == 0)
+		missarg("tunnel_id");
+
+	/* session_id and peer_session_id must be provided for sessions */
+	if ((p.session) && (p.peer_session_id == 0))
+		missarg("peer_session_id");
+	if ((p.session) && (p.session_id == 0))
+		missarg("session_id");
+
+	/* peer_tunnel_id is needed for tunnels */
+	if ((p.tunnel) && (p.peer_tunnel_id == 0))
+		missarg("peer_tunnel_id");
+
+	if (p.tunnel) {
+		if (p.local_ip.s_addr == 0)
+			missarg("local");
+
+		if (p.peer_ip.s_addr == 0)
+			missarg("remote");
+
+		if (p.encap == L2TP_ENCAPTYPE_UDP) {
+			if (p.local_udp_port == 0)
+				missarg("udp_sport");
+			if (p.peer_udp_port == 0)
+				missarg("udp_dport");
+		}
+
+		ret = create_tunnel(&p);
+	}
+
+	if (p.session) {
+		/* Only ethernet pseudowires supported */
+		p.pw_type = L2TP_PWTYPE_ETH;
+
+		ret = create_session(&p);
+	}
+
+	return ret;
+}
+
+static int do_del(int argc, char **argv)
+{
+	struct l2tp_parm p;
+
+	if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
+		return -1;
+
+	if (!p.tunnel && !p.session)
+		missarg("tunnel or session");
+
+	if ((p.tunnel) && (p.tunnel_id == 0))
+		missarg("tunnel_id");
+	if ((p.session) && (p.session_id == 0))
+		missarg("session_id");
+
+	if (p.session_id)
+		return delete_session(&p);
+	else
+		return delete_tunnel(&p);
+
+	return -1;
+}
+
+static int do_show(int argc, char **argv)
+{
+	struct l2tp_data data;
+	struct l2tp_parm *p = &data.config;
+
+	if (parse_args(argc, argv, L2TP_GET, p) < 0)
+		return -1;
+
+	if (!p->tunnel && !p->session)
+		missarg("tunnel or session");
+
+	if (p->session)
+		get_session(&data);
+	else
+		get_tunnel(&data);
+
+	return 0;
+}
+
+static int genl_parse_getfamily(struct nlmsghdr *nlh)
+{
+	struct rtattr *tb[CTRL_ATTR_MAX + 1];
+	struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
+	int len = nlh->nlmsg_len;
+	struct rtattr *attrs;
+
+	if (nlh->nlmsg_type != GENL_ID_CTRL) {
+		fprintf(stderr, "Not a controller message, nlmsg_len=%d "
+			"nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type);
+		return -1;
+	}
+
+	if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
+		fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
+		return -1;
+	}
+
+	len -= NLMSG_LENGTH(GENL_HDRLEN);
+
+	if (len < 0) {
+		fprintf(stderr, "wrong controller message len %d\n", len);
+		return -1;
+	}
+
+	attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+	parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+	if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
+		fprintf(stderr, "Missing family id TLV\n");
+		return -1;
+	}
+
+	return rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
+}
+
+int genl_ctrl_resolve_family(const char *family)
+{
+	struct {
+		struct nlmsghdr         n;
+		struct genlmsghdr	g;
+		char                    buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = GENL_ID_CTRL;
+	req.g.cmd = CTRL_CMD_GETFAMILY;
+
+	addattr_l(&req.n, 1024, CTRL_ATTR_FAMILY_NAME,
+		  family, strlen(family) + 1);
+
+	if (rtnl_talk(&genl_rth, &req.n, 0, 0, &req.n) < 0) {
+		fprintf(stderr, "Error talking to the kernel\n");
+		return -2;
+	}
+
+	return genl_parse_getfamily(&req.n);
+}
+
+int do_ipl2tp(int argc, char **argv)
+{
+	if (genl_family < 0) {
+		if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) {
+			fprintf(stderr, "Cannot open generic netlink socket\n");
+			exit(1);
+		}
+
+		genl_family = genl_ctrl_resolve_family(L2TP_GENL_NAME);
+		if (genl_family < 0)
+			exit(1);
+	}
+
+	if (argc < 1)
+		usage();
+
+	if (matches(*argv, "add") == 0)
+		return do_add(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);
+	if (matches(*argv, "help") == 0)
+		usage();
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iplink.c b/ap/app/iproute2/iproute2-3.4.0/ip/iplink.c
new file mode 100755
index 0000000..679091e
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iplink.c
@@ -0,0 +1,979 @@
+/*
+ * 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 <dlfcn.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"
+
+#define IPLINK_IOCTL_COMPAT	1
+#ifndef LIBDIR
+#define LIBDIR "/usr/lib"
+#endif
+
+static void usage(void) __attribute__((noreturn));
+static int iplink_have_newlink(void);
+
+void iplink_usage(void)
+{
+	if (iplink_have_newlink()) {
+		fprintf(stderr, "Usage: ip link add [link DEV] [ name ] NAME\n");
+		fprintf(stderr, "                   [ txqueuelen PACKETS ]\n");
+		fprintf(stderr, "                   [ address LLADDR ]\n");
+		fprintf(stderr, "                   [ broadcast LLADDR ]\n");
+		fprintf(stderr, "                   [ mtu MTU ]\n");
+		fprintf(stderr, "                   type TYPE [ ARGS ]\n");
+		fprintf(stderr, "       ip link delete DEV type TYPE [ ARGS ]\n");
+		fprintf(stderr, "\n");
+		fprintf(stderr, "       ip link set { dev DEVICE | group DEVGROUP } [ { up | down } ]\n");
+	} else
+		fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n");
+
+	fprintf(stderr, "	                  [ arp { on | off } ]\n");
+	fprintf(stderr, "	                  [ dynamic { on | off } ]\n");
+	fprintf(stderr, "	                  [ multicast { on | off } ]\n");
+	fprintf(stderr, "	                  [ allmulticast { on | off } ]\n");
+	fprintf(stderr, "	                  [ promisc { on | off } ]\n");
+	fprintf(stderr, "	                  [ trailers { on | off } ]\n");
+	fprintf(stderr, "	                  [ txqueuelen PACKETS ]\n");
+	fprintf(stderr, "	                  [ name NEWNAME ]\n");
+	fprintf(stderr, "	                  [ address LLADDR ]\n");
+	fprintf(stderr, "	                  [ broadcast LLADDR ]\n");
+	fprintf(stderr, "	                  [ mtu MTU ]\n");
+	fprintf(stderr, "	                  [ netns PID ]\n");
+	fprintf(stderr, "	                  [ netns NAME ]\n");
+	fprintf(stderr, "			  [ alias NAME ]\n");
+	fprintf(stderr, "	                  [ vf NUM [ mac LLADDR ]\n");
+	fprintf(stderr, "				   [ vlan VLANID [ qos VLAN-QOS ] ]\n");
+
+	fprintf(stderr, "				   [ rate TXRATE ] ] \n");
+
+	fprintf(stderr, "				   [ spoofchk { on | off} ] ] \n");
+	fprintf(stderr, "			  [ master DEVICE ]\n");
+	fprintf(stderr, "			  [ nomaster ]\n");
+	fprintf(stderr, "       ip link show [ DEVICE | group GROUP ]\n");
+
+	if (iplink_have_newlink()) {
+		fprintf(stderr, "\n");
+		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can | bridge }\n");
+	}
+	exit(-1);
+}
+
+static void usage(void)
+{
+	iplink_usage();
+}
+
+static int on_off(char *msg)
+{
+	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
+	return -1;
+}
+
+static void *BODY;		/* cached dlopen(NULL) handle */
+static struct link_util *linkutil_list;
+
+struct link_util *get_link_kind(const char *id)
+{
+	void *dlh;
+	char buf[256];
+	struct link_util *l;
+
+	for (l = linkutil_list; l; l = l->next)
+		if (strcmp(l->id, id) == 0)
+			return l;
+
+	snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id);
+	dlh = dlopen(buf, RTLD_LAZY);
+	if (dlh == NULL) {
+		/* look in current binary, only open once */
+		dlh = BODY;
+		if (dlh == NULL) {
+			dlh = BODY = dlopen(NULL, RTLD_LAZY);
+			if (dlh == NULL)
+				return NULL;
+		}
+	}
+
+	snprintf(buf, sizeof(buf), "%s_link_util", id);
+	l = dlsym(dlh, buf);
+	if (l == NULL)
+		return NULL;
+
+	l->next = linkutil_list;
+	linkutil_list = l;
+	return l;
+}
+
+int get_link_mode(const char *mode)
+{
+	if (strcasecmp(mode, "default") == 0)
+		return IF_LINK_MODE_DEFAULT;
+	if (strcasecmp(mode, "dormant") == 0)
+		return IF_LINK_MODE_DORMANT;
+	return -1;
+}
+
+#if IPLINK_IOCTL_COMPAT
+static int have_rtnl_newlink = -1;
+
+static int accept_msg(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n, void *arg)
+{
+	struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
+
+	if (n->nlmsg_type == NLMSG_ERROR &&
+	    (err->error == -EOPNOTSUPP || err->error == -EINVAL))
+		have_rtnl_newlink = 0;
+	else
+		have_rtnl_newlink = 1;
+	return -1;
+}
+
+static int iplink_have_newlink(void)
+{
+	struct {
+		struct nlmsghdr		n;
+		struct ifinfomsg	i;
+		char			buf[1024];
+	} req;
+
+	if (have_rtnl_newlink < 0) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+		req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+		req.n.nlmsg_type = RTM_NEWLINK;
+		req.i.ifi_family = AF_UNSPEC;
+
+		rtnl_send(&rth, &req.n, req.n.nlmsg_len);
+		rtnl_listen(&rth, accept_msg, NULL);
+	}
+	return have_rtnl_newlink;
+}
+#else /* IPLINK_IOCTL_COMPAT */
+static int iplink_have_newlink(void)
+{
+	return 1;
+}
+#endif /* ! IPLINK_IOCTL_COMPAT */
+
+struct iplink_req {
+	struct nlmsghdr		n;
+	struct ifinfomsg	i;
+	char			buf[1024];
+};
+
+int iplink_parse_vf(int vf, int *argcp, char ***argvp,
+			   struct iplink_req *req)
+{
+	int len, argc = *argcp;
+	char **argv = *argvp;
+	struct rtattr *vfinfo;
+
+	vfinfo = addattr_nest(&req->n, sizeof(*req), IFLA_VF_INFO);
+
+	while (NEXT_ARG_OK()) {
+		NEXT_ARG();
+		if (matches(*argv, "mac") == 0) {
+			struct ifla_vf_mac ivm;
+			NEXT_ARG();
+			ivm.vf = vf;
+			len = ll_addr_a2n((char *)ivm.mac, 32, *argv);
+			if (len < 0)
+				return -1;
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm));
+		} else if (matches(*argv, "vlan") == 0) {
+			struct ifla_vf_vlan ivv;
+			NEXT_ARG();
+			if (get_unsigned(&ivv.vlan, *argv, 0)) {
+				invarg("Invalid \"vlan\" value\n", *argv);
+			}
+			ivv.vf = vf;
+			ivv.qos = 0;
+			if (NEXT_ARG_OK()) {
+				NEXT_ARG();
+				if (matches(*argv, "qos") == 0) {
+					NEXT_ARG();
+					if (get_unsigned(&ivv.qos, *argv, 0)) {
+						invarg("Invalid \"qos\" value\n", *argv);
+					}
+				} else {
+					/* rewind arg */
+					PREV_ARG();
+				}
+			}
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv));
+		} else if (matches(*argv, "rate") == 0) {
+			struct ifla_vf_tx_rate ivt;
+			NEXT_ARG();
+			if (get_unsigned(&ivt.rate, *argv, 0)) {
+				invarg("Invalid \"rate\" value\n", *argv);
+			}
+			ivt.vf = vf;
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_TX_RATE, &ivt, sizeof(ivt));
+		
+		} else if (matches(*argv, "spoofchk") == 0) {
+			struct ifla_vf_spoofchk ivs;
+			NEXT_ARG();
+			if (matches(*argv, "on") == 0)
+				ivs.setting = 1;
+			else if (matches(*argv, "off") == 0)
+				ivs.setting = 0;
+			else
+				invarg("Invalid \"spoofchk\" value\n", *argv);
+			ivs.vf = vf;
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs));
+
+		} else {
+			/* rewind arg */
+			PREV_ARG();
+			break;
+		}
+	}
+
+	if (argc == *argcp)
+		incomplete_command();
+
+	addattr_nest_end(&req->n, vfinfo);
+
+	*argcp = argc;
+	*argvp = argv;
+	return 0;
+}
+
+
+int iplink_parse(int argc, char **argv, struct iplink_req *req,
+		char **name, char **type, char **link, char **dev, int *group)
+{
+	int ret, len;
+	char abuf[32];
+	int qlen = -1;
+	int mtu = -1;
+	int netns = -1;
+	int vf = -1;
+
+	*group = -1;
+	ret = argc;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "up") == 0) {
+			req->i.ifi_change |= IFF_UP;
+			req->i.ifi_flags |= IFF_UP;
+		} else if (strcmp(*argv, "down") == 0) {
+			req->i.ifi_change |= IFF_UP;
+			req->i.ifi_flags &= ~IFF_UP;
+		} else if (strcmp(*argv, "name") == 0) {
+			NEXT_ARG();
+			*name = *argv;
+		} else if (matches(*argv, "link") == 0) {
+			NEXT_ARG();
+			*link = *argv;
+		} else if (matches(*argv, "address") == 0) {
+			NEXT_ARG();
+			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+			if (len < 0)
+				return -1;
+			addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
+		} else if (matches(*argv, "broadcast") == 0 ||
+				strcmp(*argv, "brd") == 0) {
+			NEXT_ARG();
+			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+			if (len < 0)
+				return -1;
+			addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
+		} else if (matches(*argv, "txqueuelen") == 0 ||
+				strcmp(*argv, "qlen") == 0 ||
+				matches(*argv, "txqlen") == 0) {
+			NEXT_ARG();
+			if (qlen != -1)
+				duparg("txqueuelen", *argv);
+			if (get_integer(&qlen,  *argv, 0))
+				invarg("Invalid \"txqueuelen\" value\n", *argv);
+			addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
+		} 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);
+			addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
+                } else if (strcmp(*argv, "netns") == 0) {
+                        NEXT_ARG();
+                        if (netns != -1)
+                                duparg("netns", *argv);
+			if ((netns = get_netns_fd(*argv)) >= 0)
+				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
+			else if (get_integer(&netns, *argv, 0) == 0)
+				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
+			else
+                                invarg("Invalid \"netns\" value\n", *argv);
+		} else if (strcmp(*argv, "multicast") == 0) {
+			NEXT_ARG();
+			req->i.ifi_change |= IFF_MULTICAST;
+			if (strcmp(*argv, "on") == 0) {
+				req->i.ifi_flags |= IFF_MULTICAST;
+			} else if (strcmp(*argv, "off") == 0) {
+				req->i.ifi_flags &= ~IFF_MULTICAST;
+			} else
+				return on_off("multicast");
+		} else if (strcmp(*argv, "allmulticast") == 0) {
+			NEXT_ARG();
+			req->i.ifi_change |= IFF_ALLMULTI;
+			if (strcmp(*argv, "on") == 0) {
+				req->i.ifi_flags |= IFF_ALLMULTI;
+			} else if (strcmp(*argv, "off") == 0) {
+				req->i.ifi_flags &= ~IFF_ALLMULTI;
+			} else
+				return on_off("allmulticast");
+		} else if (strcmp(*argv, "promisc") == 0) {
+			NEXT_ARG();
+			req->i.ifi_change |= IFF_PROMISC;
+			if (strcmp(*argv, "on") == 0) {
+				req->i.ifi_flags |= IFF_PROMISC;
+			} else if (strcmp(*argv, "off") == 0) {
+				req->i.ifi_flags &= ~IFF_PROMISC;
+			} else
+				return on_off("promisc");
+		} else if (strcmp(*argv, "trailers") == 0) {
+			NEXT_ARG();
+			req->i.ifi_change |= IFF_NOTRAILERS;
+			if (strcmp(*argv, "off") == 0) {
+				req->i.ifi_flags |= IFF_NOTRAILERS;
+			} else if (strcmp(*argv, "on") == 0) {
+				req->i.ifi_flags &= ~IFF_NOTRAILERS;
+			} else
+				return on_off("trailers");
+		} else if (strcmp(*argv, "arp") == 0) {
+			NEXT_ARG();
+			req->i.ifi_change |= IFF_NOARP;
+			if (strcmp(*argv, "on") == 0) {
+				req->i.ifi_flags &= ~IFF_NOARP;
+			} else if (strcmp(*argv, "off") == 0) {
+				req->i.ifi_flags |= IFF_NOARP;
+			} else
+				return on_off("noarp");
+		} else if (strcmp(*argv, "vf") == 0) {
+			struct rtattr *vflist;
+			NEXT_ARG();
+			if (get_integer(&vf,  *argv, 0)) {
+				invarg("Invalid \"vf\" value\n", *argv);
+			}
+			vflist = addattr_nest(&req->n, sizeof(*req),
+					      IFLA_VFINFO_LIST);
+			len = iplink_parse_vf(vf, &argc, &argv, req);
+			if (len < 0)
+				return -1;
+			addattr_nest_end(&req->n, vflist);
+		} else if (matches(*argv, "master") == 0) {
+			int ifindex;
+			NEXT_ARG();
+			ifindex = ll_name_to_index(*argv);
+			if (!ifindex)
+				invarg("Device does not exist\n", *argv);
+			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+				  &ifindex, 4);
+		} else if (matches(*argv, "nomaster") == 0) {
+			int ifindex = 0;
+			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+				  &ifindex, 4);
+		} else if (matches(*argv, "dynamic") == 0) {
+			NEXT_ARG();
+			req->i.ifi_change |= IFF_DYNAMIC;
+			if (strcmp(*argv, "on") == 0) {
+				req->i.ifi_flags |= IFF_DYNAMIC;
+			} else if (strcmp(*argv, "off") == 0) {
+				req->i.ifi_flags &= ~IFF_DYNAMIC;
+			} else
+				return on_off("dynamic");
+		} else if (matches(*argv, "type") == 0) {
+			NEXT_ARG();
+			*type = *argv;
+			argc--; argv++;
+			break;
+		} else if (matches(*argv, "alias") == 0) {
+			NEXT_ARG();
+			addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS,
+				  *argv, strlen(*argv));
+			argc--; argv++;
+			break;
+		} else if (strcmp(*argv, "group") == 0) {
+			NEXT_ARG();
+			if (*group != -1)
+				duparg("group", *argv);
+			if (rtnl_group_a2n(group, *argv))
+				invarg("Invalid \"group\" value\n", *argv);
+		} else if (strcmp(*argv, "mode") == 0) {
+			int mode;
+			NEXT_ARG();
+			mode = get_link_mode(*argv);
+			if (mode < 0)
+				invarg("Invalid link mode\n", *argv);
+			addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode);
+		} else if (strcmp(*argv, "state") == 0) {
+			int state;
+			NEXT_ARG();
+			state = get_operstate(*argv);
+			if (state < 0)
+				invarg("Invalid operstate\n", *argv);
+
+			addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state);
+		} else {
+			if (strcmp(*argv, "dev") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (*dev)
+				duparg2("dev", *argv);
+			*dev = *argv;
+		}
+		argc--; argv++;
+	}
+
+	return ret - argc;
+}
+
+static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
+{
+	int len;
+	char *dev = NULL;
+	char *name = NULL;
+	char *link = NULL;
+	char *type = NULL;
+	int group;
+	struct link_util *lu = NULL;
+	struct iplink_req req;
+	int ret;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.i.ifi_family = preferred_family;
+
+	ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group);
+	if (ret < 0)
+		return ret;
+
+	argc -= ret;
+	argv += ret;
+
+	if (group != -1) {
+		if (dev)
+			addattr_l(&req.n, sizeof(req), IFLA_GROUP,
+					&group, sizeof(group));
+		else {
+			if (argc) {
+				fprintf(stderr, "Garbage instead of arguments "
+						"\"%s ...\". Try \"ip link "
+						"help\".\n", *argv);
+				return -1;
+			}
+			if (flags & NLM_F_CREATE) {
+				fprintf(stderr, "group cannot be used when "
+						"creating devices.\n");
+				return -1;
+			}
+
+			req.i.ifi_index = 0;
+			addattr32(&req.n, sizeof(req), IFLA_GROUP, group);
+			if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+				exit(2);
+			return 0;
+		}
+	}
+
+	ll_init_map(&rth);
+
+	if (!(flags & NLM_F_CREATE)) {
+		if (!dev) {
+			fprintf(stderr, "Not enough information: \"dev\" "
+					"argument is required.\n");
+			exit(-1);
+		}
+
+		req.i.ifi_index = ll_name_to_index(dev);
+		if (req.i.ifi_index == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", dev);
+			return -1;
+		}
+	} else {
+		/* Allow "ip link add dev" and "ip link add name" */
+		if (!name)
+			name = dev;
+
+		if (link) {
+			int ifindex;
+
+			ifindex = ll_name_to_index(link);
+			if (ifindex == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n",
+					link);
+				return -1;
+			}
+			addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4);
+		}
+	}
+
+	if (name) {
+		len = strlen(name) + 1;
+		if (len == 1)
+			invarg("\"\" is not a valid device identifier\n", "name");
+		if (len > IFNAMSIZ)
+			invarg("\"name\" too long\n", name);
+		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
+	}
+
+	if (type) {
+		struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
+		addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
+		addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
+			 strlen(type));
+
+		lu = get_link_kind(type);
+		if (lu && argc) {
+			struct rtattr * data = NLMSG_TAIL(&req.n);
+			addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
+
+			if (lu->parse_opt &&
+			    lu->parse_opt(lu, argc, argv, &req.n))
+				return -1;
+
+			data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
+		} else if (argc) {
+			if (matches(*argv, "help") == 0)
+				usage();
+			fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
+					"Try \"ip link help\".\n", *argv);
+			return -1;
+		}
+		linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
+	} else if (flags & NLM_F_CREATE) {
+		fprintf(stderr, "Not enough information: \"type\" argument "
+				"is required\n");
+		return -1;
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	return 0;
+}
+
+#if IPLINK_IOCTL_COMPAT
+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(const char *dev, __u32 flags, __u32 mask)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	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(const char *dev, const char *newdev)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	strncpy(ifr.ifr_newname, newdev, IFNAMSIZ);
+	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(const char *dev, int qlen)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = get_ctl_fd();
+	if (s < 0)
+		return -1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	ifr.ifr_qlen = qlen;
+	if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
+		perror("SIOCSIFXQLEN");
+		close(s);
+		return -1;
+	}
+	close(s);
+
+	return 0;
+}
+
+static int set_mtu(const char *dev, int mtu)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = get_ctl_fd();
+	if (s < 0)
+		return -1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	ifr.ifr_mtu = mtu;
+	if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
+		perror("SIOCSIFMTU");
+		close(s);
+		return -1;
+	}
+	close(s);
+
+	return 0;
+}
+
+static int get_address(const char *dev, int *htype)
+{
+	struct ifreq ifr;
+	struct sockaddr_ll me;
+	socklen_t alen;
+	int s;
+
+	s = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket(PF_PACKET)");
+		return -1;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	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(const char *dev, int hatype, int halen,
+		char *lla, struct ifreq *ifr)
+{
+	int alen;
+
+	memset(ifr, 0, sizeof(*ifr));
+	strncpy(ifr->ifr_name, dev, IFNAMSIZ);
+	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 (matches(*argv, "address") == 0) {
+			NEXT_ARG();
+			newaddr = *argv;
+		} else if (matches(*argv, "broadcast") == 0 ||
+			   strcmp(*argv, "brd") == 0) {
+			NEXT_ARG();
+			newbrd = *argv;
+		} else if (matches(*argv, "txqueuelen") == 0 ||
+			   strcmp(*argv, "qlen") == 0 ||
+			   matches(*argv, "txqlen") == 0) {
+			NEXT_ARG();
+			if (qlen != -1)
+				duparg("txqueuelen", *argv);
+			if (get_integer(&qlen,  *argv, 0))
+				invarg("Invalid \"txqueuelen\" value\n", *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, "allmulticast") == 0) {
+			NEXT_ARG();
+			mask |= IFF_ALLMULTI;
+			if (strcmp(*argv, "on") == 0) {
+				flags |= IFF_ALLMULTI;
+			} else if (strcmp(*argv, "off") == 0) {
+				flags &= ~IFF_ALLMULTI;
+			} else
+				return on_off("allmulticast");
+		} else if (strcmp(*argv, "promisc") == 0) {
+			NEXT_ARG();
+			mask |= IFF_PROMISC;
+			if (strcmp(*argv, "on") == 0) {
+				flags |= IFF_PROMISC;
+			} else if (strcmp(*argv, "off") == 0) {
+				flags &= ~IFF_PROMISC;
+			} else
+				return on_off("promisc");
+		} else if (strcmp(*argv, "trailers") == 0) {
+			NEXT_ARG();
+			mask |= IFF_NOTRAILERS;
+			if (strcmp(*argv, "off") == 0) {
+				flags |= IFF_NOTRAILERS;
+			} else if (strcmp(*argv, "on") == 0) {
+				flags &= ~IFF_NOTRAILERS;
+			} else
+				return on_off("trailers");
+		} 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 (matches(*argv, "dynamic") == 0) {
+			NEXT_ARG();
+			mask |= IFF_DYNAMIC;
+			if (strcmp(*argv, "on") == 0) {
+				flags |= IFF_DYNAMIC;
+			} else if (strcmp(*argv, "off") == 0) {
+				flags &= ~IFF_DYNAMIC;
+			} else
+				return on_off("dynamic");
+		} else {
+                        if (strcmp(*argv, "dev") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			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 (strlen(newname) == 0)
+			invarg("\"\" is not a valid device identifier\n", "name");
+		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;
+}
+#endif /* IPLINK_IOCTL_COMPAT */
+
+int do_iplink(int argc, char **argv)
+{
+	if (argc > 0) {
+		if (iplink_have_newlink()) {
+			if (matches(*argv, "add") == 0)
+				return iplink_modify(RTM_NEWLINK,
+						     NLM_F_CREATE|NLM_F_EXCL,
+						     argc-1, argv+1);
+			if (matches(*argv, "set") == 0 ||
+			    matches(*argv, "change") == 0)
+				return iplink_modify(RTM_NEWLINK, 0,
+						     argc-1, argv+1);
+			if (matches(*argv, "replace") == 0)
+				return iplink_modify(RTM_NEWLINK,
+						     NLM_F_CREATE|NLM_F_REPLACE,
+						     argc-1, argv+1);
+			if (matches(*argv, "delete") == 0)
+				return iplink_modify(RTM_DELLINK, 0,
+						     argc-1, argv+1);
+		} else {
+#if IPLINK_IOCTL_COMPAT
+			if (matches(*argv, "set") == 0)
+				return do_set(argc-1, argv+1);
+#endif
+		}
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return ipaddr_list_link(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return ipaddr_list_link(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iplink_can.c b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_can.c
new file mode 100755
index 0000000..c8af4bc
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_can.c
@@ -0,0 +1,283 @@
+/*
+ * iplink_can.c	CAN device support
+ *
+ *              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:     Wolfgang Grandegger <wg@grandegger.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/can/netlink.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"Usage: ip link set DEVICE type can\n"
+	        "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | \n"
+	        "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n "
+		"\t  phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n"
+		"\n"
+	        "\t[ loopback { on | off } ]\n"
+	        "\t[ listen-only { on | off } ]\n"
+	        "\t[ triple-sampling { on | off } ]\n"
+	        "\t[ one-shot { on | off } ]\n"
+	        "\t[ berr-reporting { on | off } ]\n"
+		"\n"
+	        "\t[ restart-ms TIME-MS ]\n"
+	        "\t[ restart ]\n"
+		"\n"
+		"\tWhere: BITRATE       := { 1..1000000 }\n"
+		"\t       SAMPLE-POINT  := { 0.000..0.999 }\n"
+		"\t       TQ            := { NUMBER }\n"
+		"\t       PROP-SEG      := { 1..8 }\n"
+		"\t       PHASE-SEG1    := { 1..8 }\n"
+		"\t       PHASE-SEG2    := { 1..8 }\n"
+		"\t       SJW           := { 1..4 }\n"
+		"\t       RESTART-MS    := { 0 | NUMBER }\n"
+		);
+}
+
+static int get_float(float *val, const char *arg)
+{
+	float res;
+	char *ptr;
+
+	if (!arg || !*arg)
+		return -1;
+	res = strtof(arg, &ptr);
+	if (!ptr || ptr == arg || *ptr)
+		return -1;
+	*val = res;
+	return 0;
+}
+
+static void set_ctrlmode(char* name, char *arg,
+			 struct can_ctrlmode *cm, __u32 flags)
+{
+	if (strcmp(arg, "on") == 0) {
+		cm->flags |= flags;
+	} else if (strcmp(arg, "off") != 0) {
+		fprintf(stderr,
+			"Error: argument of \"%s\" must be \"on\" or \"off\"\n",
+			name);
+		exit(-1);
+	}
+	cm->mask |= flags;
+}
+
+static void print_ctrlmode(FILE *f, __u32 cm)
+{
+	fprintf(f, "<");
+#define _PF(cmflag, cmname)					\
+	if (cm & cmflag) {					\
+		cm &= ~cmflag;					\
+		fprintf(f, "%s%s", cmname, cm ? "," : "");	\
+	}
+	_PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK");
+	_PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY");
+	_PF(CAN_CTRLMODE_3_SAMPLES, "TRIPLE-SAMPLING");
+	_PF(CAN_CTRLMODE_ONE_SHOT, "ONE-SHOT");
+	_PF(CAN_CTRLMODE_BERR_REPORTING, "BERR-REPORTING");
+#undef _PF
+	if (cm)
+		fprintf(f, "%x", cm);
+	fprintf(f, "> ");
+}
+
+static int can_parse_opt(struct link_util *lu, int argc, char **argv,
+			 struct nlmsghdr *n)
+{
+	struct can_bittiming bt;
+	struct can_ctrlmode cm = {0, 0};
+
+	memset(&bt, 0, sizeof(bt));
+	while (argc > 0) {
+		if (matches(*argv, "bitrate") == 0) {
+			NEXT_ARG();
+			if (get_u32(&bt.bitrate, *argv, 0))
+				invarg("invalid \"bitrate\" value\n", *argv);
+		} else if (matches(*argv, "sample-point") == 0) {
+			float sp;
+
+			NEXT_ARG();
+			if (get_float(&sp, *argv))
+				invarg("invalid \"sample-point\" value\n",
+				       *argv);
+			bt.sample_point = (__u32)(sp * 1000);
+		} else if (matches(*argv, "tq") == 0) {
+			NEXT_ARG();
+			if (get_u32(&bt.tq, *argv, 0))
+				invarg("invalid \"tq\" value\n", *argv);
+		} else if (matches(*argv, "prop-seg") == 0) {
+			NEXT_ARG();
+			if (get_u32(&bt.prop_seg, *argv, 0))
+				invarg("invalid \"prop-seg\" value\n", *argv);
+		} else if (matches(*argv, "phase-seg1") == 0) {
+			NEXT_ARG();
+			if (get_u32(&bt.phase_seg1, *argv, 0))
+				invarg("invalid \"phase-seg1\" value\n", *argv);
+		} else if (matches(*argv, "phase-seg2") == 0) {
+			NEXT_ARG();
+			if (get_u32(&bt.phase_seg2, *argv, 0))
+				invarg("invalid \"phase-seg2\" value\n", *argv);
+		} else if (matches(*argv, "sjw") == 0) {
+			NEXT_ARG();
+			if (get_u32(&bt.sjw, *argv, 0))
+				invarg("invalid \"sjw\" value\n", *argv);
+		} else if (matches(*argv, "loopback") == 0) {
+			NEXT_ARG();
+			set_ctrlmode("loopback", *argv, &cm,
+				     CAN_CTRLMODE_LOOPBACK);
+		} else if (matches(*argv, "listen-only") == 0) {
+			NEXT_ARG();
+			set_ctrlmode("listen-only", *argv, &cm,
+				     CAN_CTRLMODE_LISTENONLY);
+		} else if (matches(*argv, "triple-sampling") == 0) {
+			NEXT_ARG();
+			set_ctrlmode("triple-sampling", *argv, &cm,
+				     CAN_CTRLMODE_3_SAMPLES);
+		} else if (matches(*argv, "one-shot") == 0) {
+			NEXT_ARG();
+			set_ctrlmode("one-shot", *argv, &cm,
+				     CAN_CTRLMODE_ONE_SHOT);
+		} else if (matches(*argv, "berr-reporting") == 0) {
+			NEXT_ARG();
+			set_ctrlmode("berr-reporting", *argv, &cm,
+				     CAN_CTRLMODE_BERR_REPORTING);
+		} else if (matches(*argv, "restart") == 0) {
+			__u32 val = 1;
+
+			addattr32(n, 1024, IFLA_CAN_RESTART, val);
+		} else if (matches(*argv, "restart-ms") == 0) {
+			__u32 val;
+
+			NEXT_ARG();
+			if (get_u32(&val, *argv, 0))
+				invarg("invalid \"restart-ms\" value\n", *argv);
+			addattr32(n, 1024, IFLA_CAN_RESTART_MS, val);
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+			return -1;
+		} else {
+			fprintf(stderr, "can: what is \"%s\"?\n", *argv);
+			usage();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	if (bt.bitrate || bt.tq)
+		addattr_l(n, 1024, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
+	if (cm.mask)
+		addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
+
+	return 0;
+}
+
+static const char *can_state_names[] = {
+		[CAN_STATE_ERROR_ACTIVE] = "ERROR-ACTIVE",
+		[CAN_STATE_ERROR_WARNING] = "ERROR-WARNING",
+		[CAN_STATE_ERROR_PASSIVE] = "ERROR-PASSIVE",
+		[CAN_STATE_BUS_OFF] = "BUS-OFF",
+		[CAN_STATE_STOPPED] = "STOPPED",
+		[CAN_STATE_SLEEPING] = "SLEEPING"
+};
+
+static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	if (!tb)
+		return;
+
+	if (tb[IFLA_CAN_CTRLMODE]) {
+		struct can_ctrlmode *cm = RTA_DATA(tb[IFLA_CAN_CTRLMODE]);
+
+		if (cm->flags)
+			print_ctrlmode(f, cm->flags);
+	}
+
+	if (tb[IFLA_CAN_STATE]) {
+		int *state = RTA_DATA(tb[IFLA_CAN_STATE]);
+
+		fprintf(f, "state %s ", *state <= CAN_STATE_MAX ?
+			can_state_names[*state] : "UNKNOWN");
+	}
+
+	if (tb[IFLA_CAN_BERR_COUNTER]) {
+		struct can_berr_counter *bc =
+			RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]);
+
+		fprintf(f, "(berr-counter tx %d rx %d) ", bc->txerr, bc->rxerr);
+	}
+
+	if (tb[IFLA_CAN_RESTART_MS]) {
+		__u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]);
+
+		fprintf(f, "restart-ms %d ", *restart_ms);
+	}
+
+	if (tb[IFLA_CAN_BITTIMING]) {
+		struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]);
+
+		fprintf(f, "\n    "
+			"bitrate %d sample-point %.3f ",
+		        bt->bitrate, (float)bt->sample_point / 1000.);
+		fprintf(f, "\n    "
+			"tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
+			bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+			bt->sjw);
+	}
+
+	if (tb[IFLA_CAN_BITTIMING_CONST]) {
+		struct can_bittiming_const *btc =
+			RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]);
+
+		fprintf(f, "\n    "
+			"%s: tseg1 %d..%d tseg2 %d..%d "
+			"sjw 1..%d brp %d..%d brp-inc %d",
+		        btc->name, btc->tseg1_min, btc->tseg1_max,
+			btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
+			btc->brp_min, btc->brp_max, btc->brp_inc);
+	}
+
+	if (tb[IFLA_CAN_CLOCK]) {
+		struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]);
+
+		fprintf(f, "\n    clock %d", clock->freq);
+	}
+
+}
+
+static void can_print_xstats(struct link_util *lu,
+			     FILE *f, struct rtattr *xstats)
+{
+	struct can_device_stats *stats;
+
+	if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) {
+		stats = RTA_DATA(xstats);
+		fprintf(f, "\n    "
+			"re-started bus-errors arbit-lost "
+			"error-warn error-pass bus-off");
+		fprintf(f, "\n    %-10d %-10d %-10d %-10d %-10d %-10d",
+			stats->restarts, stats->bus_error,
+			stats->arbitration_lost, stats->error_warning,
+			stats->error_passive, stats->bus_off);
+	}
+}
+
+struct link_util can_link_util = {
+	.id		= "can",
+	.maxattr	= IFLA_CAN_MAX,
+	.parse_opt	= can_parse_opt,
+	.print_opt	= can_print_opt,
+	.print_xstats 	= can_print_xstats,
+};
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iplink_macvlan.c b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_macvlan.c
new file mode 100755
index 0000000..ed0e34b
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_macvlan.c
@@ -0,0 +1,96 @@
+/*
+ * iplink_vlan.c	VLAN device support
+ *
+ *              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:     Patrick McHardy <kaber@trash.net>
+ *		Arnd Bergmann <arnd@arndb.de>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if_link.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+	fprintf(stderr,
+		"Usage: ... macvlan mode { private | vepa | bridge | passthru }\n"
+	);
+}
+
+static int mode_arg(void)
+{
+        fprintf(stderr, "Error: argument of \"mode\" must be \"private\", "
+		"\"vepa\", \"bridge\" or \"passthru\" \n");
+        return -1;
+}
+
+static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
+			  struct nlmsghdr *n)
+{
+	while (argc > 0) {
+		if (matches(*argv, "mode") == 0) {
+			__u32 mode = 0;
+			NEXT_ARG();
+
+			if (strcmp(*argv, "private") == 0)
+				mode = MACVLAN_MODE_PRIVATE;
+			else if (strcmp(*argv, "vepa") == 0)
+				mode = MACVLAN_MODE_VEPA;
+			else if (strcmp(*argv, "bridge") == 0)
+				mode = MACVLAN_MODE_BRIDGE;
+			else if (strcmp(*argv, "passthru") == 0)
+				mode = MACVLAN_MODE_PASSTHRU;
+			else
+				return mode_arg();
+
+			addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "macvlan: what is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	return 0;
+}
+
+static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	__u32 mode;
+
+	if (!tb)
+		return;
+
+	if (!tb[IFLA_MACVLAN_MODE] ||
+	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
+		return;
+
+	mode = rta_getattr_u32(tb[IFLA_VLAN_ID]);
+	fprintf(f, " mode %s ",
+		  mode == MACVLAN_MODE_PRIVATE ? "private"
+		: mode == MACVLAN_MODE_VEPA    ? "vepa"
+		: mode == MACVLAN_MODE_BRIDGE  ? "bridge"
+		: mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
+		:				 "unknown");
+}
+
+struct link_util macvlan_link_util = {
+	.id		= "macvlan",
+	.maxattr	= IFLA_MACVLAN_MAX,
+	.parse_opt	= macvlan_parse_opt,
+	.print_opt	= macvlan_print_opt,
+};
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iplink_macvtap.c b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_macvtap.c
new file mode 100755
index 0000000..6c7fe1f
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_macvtap.c
@@ -0,0 +1,93 @@
+/*
+ * iplink_macvtap.c	macvtap device support
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              as published by the Free Software Foundation; either version
+ *              2 of the License, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if_link.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+	fprintf(stderr,
+		"Usage: ... macvtap mode { private | vepa | bridge | passthru }\n"
+	);
+}
+
+static int mode_arg(void)
+{
+        fprintf(stderr, "Error: argument of \"mode\" must be \"private\", "
+		"\"vepa\", \"bridge\" or \"passthru\" \n");
+        return -1;
+}
+
+static int macvtap_parse_opt(struct link_util *lu, int argc, char **argv,
+			  struct nlmsghdr *n)
+{
+	while (argc > 0) {
+		if (matches(*argv, "mode") == 0) {
+			__u32 mode = 0;
+			NEXT_ARG();
+
+			if (strcmp(*argv, "private") == 0)
+				mode = MACVLAN_MODE_PRIVATE;
+			else if (strcmp(*argv, "vepa") == 0)
+				mode = MACVLAN_MODE_VEPA;
+			else if (strcmp(*argv, "bridge") == 0)
+				mode = MACVLAN_MODE_BRIDGE;
+			else if (strcmp(*argv, "passthru") == 0)
+				mode = MACVLAN_MODE_PASSTHRU;
+			else
+				return mode_arg();
+
+			addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "macvtap: what is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	return 0;
+}
+
+static void macvtap_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	__u32 mode;
+
+	if (!tb)
+		return;
+
+	if (!tb[IFLA_MACVLAN_MODE] ||
+	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32))
+		return;
+
+	mode = rta_getattr_u32(tb[IFLA_VLAN_ID]);
+	fprintf(f, " mode %s ",
+		  mode == MACVLAN_MODE_PRIVATE ? "private"
+		: mode == MACVLAN_MODE_VEPA    ? "vepa"
+		: mode == MACVLAN_MODE_BRIDGE  ? "bridge"
+		: mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
+		:				 "unknown");
+}
+
+struct link_util macvtap_link_util = {
+	.id		= "macvtap",
+	.maxattr	= IFLA_MACVLAN_MAX,
+	.parse_opt	= macvtap_parse_opt,
+	.print_opt	= macvtap_print_opt,
+};
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iplink_vlan.c b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_vlan.c
new file mode 100755
index 0000000..97af8d6
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iplink_vlan.c
@@ -0,0 +1,205 @@
+/*
+ * iplink_vlan.c	VLAN device support
+ *
+ *              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:     Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/if_vlan.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+	fprintf(stderr,
+		"Usage: ... vlan id VLANID [ FLAG-LIST ]\n"
+		"                          [ ingress-qos-map QOS-MAP ] [ egress-qos-map QOS-MAP ]\n"
+		"\n"
+		"VLANID := 0-4095\n"
+		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
+		"FLAG := [ reorder_hdr { on | off } ] [ gvrp { on | off } ]\n"
+		"        [ loose_binding { on | off } ]\n"
+		"QOS-MAP := [ QOS-MAP ] QOS-MAPPING\n"
+		"QOS-MAPPING := FROM:TO\n"
+	);
+}
+
+static int on_off(char *msg)
+{
+	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
+	return -1;
+}
+
+static int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n,
+			      int attrtype)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	struct ifla_vlan_qos_mapping m;
+	struct rtattr *tail;
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, attrtype, NULL, 0);
+
+	while (argc > 0) {
+		char *colon = strchr(*argv, ':');
+
+		if (!colon)
+			break;
+		*colon = '\0';
+
+		if (get_u32(&m.from, *argv, 0))
+			return 1;
+		if (get_u32(&m.to, colon + 1, 0))
+			return 1;
+		argc--, argv++;
+
+		addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m));
+	}
+
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *)tail;
+
+	*argcp = argc;
+	*argvp = argv;
+	return 0;
+}
+
+static int vlan_parse_opt(struct link_util *lu, int argc, char **argv,
+			  struct nlmsghdr *n)
+{
+	struct ifla_vlan_flags flags = { 0 };
+	__u16 id;
+
+	while (argc > 0) {
+		if (matches(*argv, "id") == 0) {
+			NEXT_ARG();
+			if (get_u16(&id, *argv, 0))
+				invarg("id is invalid", *argv);
+			addattr_l(n, 1024, IFLA_VLAN_ID, &id, 2);
+		} else if (matches(*argv, "reorder_hdr") == 0) {
+			NEXT_ARG();
+			flags.mask |= VLAN_FLAG_REORDER_HDR;
+			if (strcmp(*argv, "on") == 0)
+				flags.flags |= VLAN_FLAG_REORDER_HDR;
+			else if (strcmp(*argv, "off") == 0)
+				flags.flags &= ~VLAN_FLAG_REORDER_HDR;
+			else
+				return on_off("reorder_hdr");
+		} else if (matches(*argv, "gvrp") == 0) {
+			NEXT_ARG();
+			flags.mask |= VLAN_FLAG_GVRP;
+			if (strcmp(*argv, "on") == 0)
+				flags.flags |= VLAN_FLAG_GVRP;
+			else if (strcmp(*argv, "off") == 0)
+				flags.flags &= ~VLAN_FLAG_GVRP;
+			else
+				return on_off("gvrp");
+		} else if (matches(*argv, "loose_binding") == 0) {
+			NEXT_ARG();
+			flags.mask |= VLAN_FLAG_LOOSE_BINDING;
+			if (strcmp(*argv, "on") == 0)
+				flags.flags |= VLAN_FLAG_LOOSE_BINDING;
+			else if (strcmp(*argv, "off") == 0)
+				flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
+			else
+				return on_off("loose_binding");
+		} else if (matches(*argv, "ingress-qos-map") == 0) {
+			NEXT_ARG();
+			if (vlan_parse_qos_map(&argc, &argv, n,
+					       IFLA_VLAN_INGRESS_QOS))
+				invarg("invalid ingress-qos-map", *argv);
+			continue;
+		} else if (matches(*argv, "egress-qos-map") == 0) {
+			NEXT_ARG();
+			if (vlan_parse_qos_map(&argc, &argv, n,
+					       IFLA_VLAN_EGRESS_QOS))
+				invarg("invalid egress-qos-map", *argv);
+			continue;
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "vlan: what is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	if (flags.mask)
+		addattr_l(n, 1024, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
+
+	return 0;
+}
+
+static void vlan_print_map(FILE *f, char *name, struct rtattr *attr)
+{
+	struct ifla_vlan_qos_mapping *m;
+	struct rtattr *i;
+	int rem;
+
+	fprintf(f, "\n      %s { ", name);
+
+	rem = RTA_PAYLOAD(attr);
+	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+		m = RTA_DATA(i);
+		fprintf(f, "%u:%u ", m->from, m->to);
+	}
+	fprintf(f, "} ");
+}
+
+static void vlan_print_flags(FILE *fp, __u32 flags)
+{
+	fprintf(fp, "<");
+#define _PF(f)	if (flags & VLAN_FLAG_##f) { \
+			flags &= ~ VLAN_FLAG_##f; \
+			fprintf(fp, #f "%s", flags ? "," : ""); \
+		}
+	_PF(REORDER_HDR);
+	_PF(GVRP);
+	_PF(LOOSE_BINDING);
+#undef _PF
+	if (flags)
+		fprintf(fp, "%x", flags);
+	fprintf(fp, "> ");
+}
+
+static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	struct ifla_vlan_flags *flags;
+	if (!tb)
+		return;
+
+	if (!tb[IFLA_VLAN_ID] ||
+	    RTA_PAYLOAD(tb[IFLA_VLAN_ID]) < sizeof(__u16))
+		return;
+
+	fprintf(f, "id %u ", rta_getattr_u16(tb[IFLA_VLAN_ID]));
+
+	if (tb[IFLA_VLAN_FLAGS]) {
+		if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags))
+			return;
+		flags = RTA_DATA(tb[IFLA_VLAN_FLAGS]);
+		vlan_print_flags(f, flags->flags);
+	}
+	if (tb[IFLA_VLAN_INGRESS_QOS])
+		vlan_print_map(f, "ingress-qos-map", tb[IFLA_VLAN_INGRESS_QOS]);
+	if (tb[IFLA_VLAN_EGRESS_QOS])
+		vlan_print_map(f, "egress-qos-map", tb[IFLA_VLAN_EGRESS_QOS]);
+}
+
+struct link_util vlan_link_util = {
+	.id		= "vlan",
+	.maxattr	= IFLA_VLAN_MAX,
+	.parse_opt	= vlan_parse_opt,
+	.print_opt	= vlan_print_opt,
+};
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipmaddr.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipmaddr.c
new file mode 100755
index 0000000..3ae9478
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipmaddr.c
@@ -0,0 +1,347 @@
+/*
+ * ipmaddr.c		"ip maddress".
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+static struct {
+	char *dev;
+	int  family;
+} filter;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n");
+	fprintf(stderr, "       ip maddr show [ dev STRING ]\n");
+	exit(-1);
+}
+
+static int parse_hex(char *str, unsigned char *addr, size_t size)
+{
+	int len=0;
+
+	while (*str && (len < 2 * size)) {
+		int tmp;
+		if (str[1] == 0)
+			return -1;
+		if (sscanf(str, "%02x", &tmp) != 1)
+			return -1;
+		addr[len] = tmp;
+		len++;
+		str += 2;
+	}
+	return len;
+}
+
+struct ma_info
+{
+	struct ma_info *next;
+	int		index;
+	int		users;
+	char		*features;
+	char		name[IFNAMSIZ];
+	inet_prefix	addr;
+};
+
+void maddr_ins(struct ma_info **lst, struct ma_info *m)
+{
+	struct ma_info *mp;
+
+	for (; (mp=*lst) != NULL; lst = &mp->next) {
+		if (mp->index > m->index)
+			break;
+	}
+	m->next = *lst;
+	*lst = m;
+}
+
+void read_dev_mcast(struct ma_info **result_p)
+{
+	char buf[256];
+	FILE *fp = fopen("/proc/net/dev_mcast", "r");
+
+	if (!fp)
+		return;
+
+	while (fgets(buf, sizeof(buf), fp)) {
+		char hexa[256];
+		struct ma_info m;
+		int len;
+		int st;
+
+		memset(&m, 0, sizeof(m));
+		sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st,
+		       hexa);
+		if (filter.dev && strcmp(filter.dev, m.name))
+			continue;
+
+		m.addr.family = AF_PACKET;
+
+		len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data));
+		if (len >= 0) {
+			struct ma_info *ma = malloc(sizeof(m));
+
+			memcpy(ma, &m, sizeof(m));
+			ma->addr.bytelen = len;
+			ma->addr.bitlen = len<<3;
+			if (st)
+				ma->features = "static";
+			maddr_ins(result_p, ma);
+		}
+	}
+	fclose(fp);
+}
+
+void read_igmp(struct ma_info **result_p)
+{
+	struct ma_info m;
+	char buf[256];
+	FILE *fp = fopen("/proc/net/igmp", "r");
+
+	if (!fp)
+		return;
+	memset(&m, 0, sizeof(m));
+	if (!fgets(buf, sizeof(buf), fp)) {
+		fclose(fp);
+		return;
+	}
+
+	m.addr.family = AF_INET;
+	m.addr.bitlen = 32;
+	m.addr.bytelen = 4;
+
+	while (fgets(buf, sizeof(buf), fp)) {
+		struct ma_info *ma;
+
+		if (buf[0] != '\t') {
+			sscanf(buf, "%d%s", &m.index, m.name);
+			continue;
+		}
+
+		if (filter.dev && strcmp(filter.dev, m.name))
+			continue;
+
+		sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users);
+
+		ma = malloc(sizeof(m));
+		memcpy(ma, &m, sizeof(m));
+		maddr_ins(result_p, ma);
+	}
+	fclose(fp);
+}
+
+
+void read_igmp6(struct ma_info **result_p)
+{
+	char buf[256];
+	FILE *fp = fopen("/proc/net/igmp6", "r");
+
+	if (!fp)
+		return;
+
+	while (fgets(buf, sizeof(buf), fp)) {
+		char hexa[256];
+		struct ma_info m;
+		int len;
+
+		memset(&m, 0, sizeof(m));
+		sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users);
+
+		if (filter.dev && strcmp(filter.dev, m.name))
+			continue;
+
+		m.addr.family = AF_INET6;
+
+		len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data));
+		if (len >= 0) {
+			struct ma_info *ma = malloc(sizeof(m));
+
+			memcpy(ma, &m, sizeof(m));
+
+			ma->addr.bytelen = len;
+			ma->addr.bitlen = len<<3;
+			maddr_ins(result_p, ma);
+		}
+	}
+	fclose(fp);
+}
+
+static void print_maddr(FILE *fp, struct ma_info *list)
+{
+	fprintf(fp, "\t");
+
+	if (list->addr.family == AF_PACKET) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "link  %s", ll_addr_n2a((unsigned char*)list->addr.data,
+						    list->addr.bytelen, 0,
+						    b1, sizeof(b1)));
+	} else {
+		char abuf[256];
+		switch(list->addr.family) {
+		case AF_INET:
+			fprintf(fp, "inet  ");
+			break;
+		case AF_INET6:
+			fprintf(fp, "inet6 ");
+			break;
+		default:
+			fprintf(fp, "family %d ", list->addr.family);
+			break;
+		}
+		fprintf(fp, "%s",
+			format_host(list->addr.family,
+				    -1,
+				    list->addr.data,
+				    abuf, sizeof(abuf)));
+	}
+	if (list->users != 1)
+		fprintf(fp, " users %d", list->users);
+	if (list->features)
+		fprintf(fp, " %s", list->features);
+	fprintf(fp, "\n");
+}
+
+static void print_mlist(FILE *fp, struct ma_info *list)
+{
+	int cur_index = 0;
+
+	for (; list; list = list->next) {
+		if (oneline) {
+			cur_index = list->index;
+			fprintf(fp, "%d:\t%s%s", cur_index, list->name, _SL_);
+		} else if (cur_index != list->index) {
+			cur_index = list->index;
+			fprintf(fp, "%d:\t%s\n", cur_index, list->name);
+		}
+		print_maddr(fp, list);
+	}
+}
+
+static int multiaddr_list(int argc, char **argv)
+{
+	struct ma_info *list = NULL;
+
+	if (!filter.family)
+		filter.family = preferred_family;
+
+	while (argc > 0) {
+		if (1) {
+			if (strcmp(*argv, "dev") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (filter.dev)
+				duparg2("dev", *argv);
+			filter.dev = *argv;
+		}
+		argv++; argc--;
+	}
+
+	if (!filter.family || filter.family == AF_PACKET)
+		read_dev_mcast(&list);
+	if (!filter.family || filter.family == AF_INET)
+		read_igmp(&list);
+	if (!filter.family || filter.family == AF_INET6)
+		read_igmp6(&list);
+	print_mlist(stdout, list);
+	return 0;
+}
+
+int multiaddr_modify(int cmd, int argc, char **argv)
+{
+	struct ifreq ifr;
+	int fd;
+
+	memset(&ifr, 0, sizeof(ifr));
+
+	if (cmd == RTM_NEWADDR)
+		cmd = SIOCADDMULTI;
+	else
+		cmd = SIOCDELMULTI;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (ifr.ifr_name[0])
+				duparg("dev", *argv);
+			strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+		} else {
+			if (matches(*argv, "address") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (ifr.ifr_hwaddr.sa_data[0])
+				duparg("address", *argv);
+			if (ll_addr_a2n(ifr.ifr_hwaddr.sa_data,
+					14, *argv) < 0) {
+				fprintf(stderr, "Error: \"%s\" is not a legal ll address.\n", *argv);
+				exit(1);
+			}
+		}
+		argc--; argv++;
+	}
+	if (ifr.ifr_name[0] == 0) {
+		fprintf(stderr, "Not enough information: \"dev\" is required.\n");
+		exit(-1);
+	}
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		perror("Cannot create socket");
+		exit(1);
+	}
+	if (ioctl(fd, cmd, (char*)&ifr) != 0) {
+		perror("ioctl");
+		exit(1);
+	}
+	close(fd);
+
+	exit(0);
+}
+
+
+int do_multiaddr(int argc, char **argv)
+{
+	if (argc < 1)
+		return multiaddr_list(0, NULL);
+	if (matches(*argv, "add") == 0)
+		return multiaddr_modify(RTM_NEWADDR, argc-1, argv+1);
+	if (matches(*argv, "delete") == 0)
+		return multiaddr_modify(RTM_DELADDR, argc-1, argv+1);
+	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+	    || matches(*argv, "lst") == 0)
+		return multiaddr_list(argc-1, argv+1);
+	if (matches(*argv, "help") == 0)
+		usage();
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip maddr help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipmonitor.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipmonitor.c
new file mode 100755
index 0000000..4b1d469
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipmonitor.c
@@ -0,0 +1,197 @@
+/*
+ * ipmonitor.c		"ip monitor".
+ *
+ *		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 <arpa/inet.h>
+#include <string.h>
+#include <time.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+static void usage(void) __attribute__((noreturn));
+int prefix_banner;
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ]\n");
+	exit(-1);
+}
+
+
+int accept_msg(const struct sockaddr_nl *who,
+	       struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+
+	if (timestamp)
+		print_timestamp(fp);
+
+	if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) {
+		if (prefix_banner)
+			fprintf(fp, "[ROUTE]");
+		print_route(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
+		ll_remember_index(who, n, NULL);
+		if (prefix_banner)
+			fprintf(fp, "[LINK]");
+		print_linkinfo(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
+		if (prefix_banner)
+			fprintf(fp, "[ADDR]");
+		print_addrinfo(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWADDRLABEL || n->nlmsg_type == RTM_DELADDRLABEL) {
+		if (prefix_banner)
+			fprintf(fp, "[ADDRLABEL]");
+		print_addrlabel(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH) {
+		if (prefix_banner)
+			fprintf(fp, "[NEIGH]");
+		print_neigh(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWPREFIX) {
+		if (prefix_banner)
+			fprintf(fp, "[PREFIX]");
+		print_prefix(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWRULE || n->nlmsg_type == RTM_DELRULE) {
+		if (prefix_banner)
+			fprintf(fp, "[RULE]");
+		print_rule(who, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == 15) {
+		char *tstr;
+		time_t secs = ((__u32*)NLMSG_DATA(n))[0];
+		long usecs = ((__u32*)NLMSG_DATA(n))[1];
+		tstr = asctime(localtime(&secs));
+		tstr[strlen(tstr)-1] = 0;
+		fprintf(fp, "Timestamp: %s %lu us\n", tstr, usecs);
+		return 0;
+	}
+	if (n->nlmsg_type == RTM_NEWQDISC ||
+	    n->nlmsg_type == RTM_DELQDISC ||
+	    n->nlmsg_type == RTM_NEWTCLASS ||
+	    n->nlmsg_type == RTM_DELTCLASS ||
+	    n->nlmsg_type == RTM_NEWTFILTER ||
+	    n->nlmsg_type == RTM_DELTFILTER)
+		return 0;
+	if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP &&
+	    n->nlmsg_type != NLMSG_DONE) {
+		fprintf(fp, "Unknown message: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+	}
+	return 0;
+}
+
+int do_ipmonitor(int argc, char **argv)
+{
+	char *file = NULL;
+	unsigned groups = ~RTMGRP_TC;
+	int llink=0;
+	int laddr=0;
+	int lroute=0;
+	int lprefix=0;
+	int lneigh=0;
+
+	rtnl_close(&rth);
+	ipaddr_reset_filter(1);
+	iproute_reset_filter();
+	ipneigh_reset_filter();
+
+	while (argc > 0) {
+		if (matches(*argv, "file") == 0) {
+			NEXT_ARG();
+			file = *argv;
+		} else if (matches(*argv, "link") == 0) {
+			llink=1;
+			groups = 0;
+		} else if (matches(*argv, "address") == 0) {
+			laddr=1;
+			groups = 0;
+		} else if (matches(*argv, "route") == 0) {
+			lroute=1;
+			groups = 0;
+		} else if (matches(*argv, "prefix") == 0) {
+			lprefix=1;
+			groups = 0;
+		} else if (matches(*argv, "neigh") == 0) {
+			lneigh = 1;
+			groups = 0;
+		} else if (strcmp(*argv, "all") == 0) {
+			groups = ~RTMGRP_TC;
+			prefix_banner=1;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Argument \"%s\" is unknown, try \"ip monitor help\".\n", *argv);
+			exit(-1);
+		}
+		argc--;	argv++;
+	}
+
+	if (llink)
+		groups |= nl_mgrp(RTNLGRP_LINK);
+	if (laddr) {
+		if (!preferred_family || preferred_family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR);
+		if (!preferred_family || preferred_family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR);
+	}
+	if (lroute) {
+		if (!preferred_family || preferred_family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE);
+		if (!preferred_family || preferred_family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE);
+	}
+	if (lprefix) {
+		if (!preferred_family || preferred_family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX);
+	}
+	if (lneigh) {
+		groups |= nl_mgrp(RTNLGRP_NEIGH);
+	}
+	if (file) {
+		FILE *fp;
+		fp = fopen(file, "r");
+		if (fp == NULL) {
+			perror("Cannot fopen");
+			exit(-1);
+		}
+		return rtnl_from_file(fp, accept_msg, stdout);
+	}
+
+	if (rtnl_open(&rth, groups) < 0)
+		exit(1);
+	ll_init_map(&rth);
+
+	if (rtnl_listen(&rth, accept_msg, stdout) < 0)
+		exit(2);
+
+	return 0;
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipmroute.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipmroute.c
new file mode 100755
index 0000000..c3b9098
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipmroute.c
@@ -0,0 +1,212 @@
+/*
+ * ipmroute.c		"ip mroute".
+ *
+ *		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 <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+
+#include "utils.h"
+
+char filter_dev[16];
+int  filter_family;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip mroute show [ PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
+#if 0
+	fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n");
+#endif
+	exit(-1);
+}
+
+static char *viftable[32];
+
+struct rtfilter
+{
+	inet_prefix mdst;
+	inet_prefix msrc;
+};
+static struct rtfilter filter;
+
+static void read_viftable(void)
+{
+	char buf[256];
+	FILE *fp = fopen("/proc/net/ip_mr_vif", "r");
+
+	if (!fp)
+		return;
+
+	if (!fgets(buf, sizeof(buf), fp)) {
+		fclose(fp);
+		return;
+	}
+	while (fgets(buf, sizeof(buf), fp)) {
+		int vifi;
+		char dev[256];
+
+		if (sscanf(buf, "%d%s", &vifi, dev) < 2)
+			continue;
+
+		if (vifi<0 || vifi>31)
+			continue;
+
+		viftable[vifi] = strdup(dev);
+	}
+	fclose(fp);
+}
+
+static void read_mroute_list(FILE *ofp)
+{
+	char buf[256];
+	FILE *fp = fopen("/proc/net/ip_mr_cache", "r");
+
+	if (!fp)
+		return;
+
+	if (!fgets(buf, sizeof(buf), fp)) {
+		fclose(fp);
+		return;
+	}
+
+	while (fgets(buf, sizeof(buf), fp)) {
+		inet_prefix maddr, msrc;
+		unsigned pkts, b, w;
+		int vifi;
+		char oiflist[256];
+		char sbuf[256];
+		char mbuf[256];
+		char obuf[256];
+
+		oiflist[0] = 0;
+		if (sscanf(buf, "%x%x%d%u%u%u %[^\n]",
+			   maddr.data, msrc.data, &vifi,
+			   &pkts, &b, &w, oiflist) < 6)
+			continue;
+
+		if (vifi!=-1 && (vifi < 0 || vifi>31))
+			continue;
+
+		if (filter_dev[0] && (vifi<0 || strcmp(filter_dev, viftable[vifi])))
+			continue;
+		if (filter.mdst.family && inet_addr_match(&maddr, &filter.mdst, filter.mdst.bitlen))
+			continue;
+		if (filter.msrc.family && inet_addr_match(&msrc, &filter.msrc, filter.msrc.bitlen))
+			continue;
+
+		snprintf(obuf, sizeof(obuf), "(%s, %s)",
+			 format_host(AF_INET, 4, &msrc.data[0], sbuf, sizeof(sbuf)),
+			 format_host(AF_INET, 4, &maddr.data[0], mbuf, sizeof(mbuf)));
+
+		fprintf(ofp, "%-32s Iif: ", obuf);
+
+		if (vifi == -1)
+			fprintf(ofp, "unresolved ");
+		else
+			fprintf(ofp, "%-10s ", viftable[vifi]);
+
+		if (oiflist[0]) {
+			char *next = NULL;
+			char *p = oiflist;
+			int ovifi, ottl;
+
+			fprintf(ofp, "Oifs: ");
+
+			while (p) {
+				next = strchr(p, ' ');
+				if (next) {
+					*next = 0;
+					next++;
+				}
+				if (sscanf(p, "%d:%d", &ovifi, &ottl)<2) {
+					p = next;
+					continue;
+				}
+				p = next;
+
+				fprintf(ofp, "%s", viftable[ovifi]);
+				if (ottl>1)
+					fprintf(ofp, "(ttl %d) ", ovifi);
+				else
+					fprintf(ofp, " ");
+			}
+		}
+
+		if (show_stats && b) {
+			fprintf(ofp, "%s  %u packets, %u bytes", _SL_, pkts, b);
+			if (w)
+				fprintf(ofp, ", %u arrived on wrong iif.", w);
+		}
+		fprintf(ofp, "\n");
+	}
+	fclose(fp);
+}
+
+
+static int mroute_list(int argc, char **argv)
+{
+	while (argc > 0) {
+		if (strcmp(*argv, "iif") == 0) {
+			NEXT_ARG();
+			strncpy(filter_dev, *argv, sizeof(filter_dev)-1);
+		} else if (matches(*argv, "from") == 0) {
+			NEXT_ARG();
+			get_prefix(&filter.msrc, *argv, AF_INET);
+		} else {
+			if (strcmp(*argv, "to") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			get_prefix(&filter.mdst, *argv, AF_INET);
+		}
+		argv++; argc--;
+	}
+
+	read_viftable();
+	read_mroute_list(stdout);
+	return 0;
+}
+
+int do_multiroute(int argc, char **argv)
+{
+	if (argc < 1)
+		return mroute_list(0, NULL);
+#if 0
+	if (matches(*argv, "add") == 0)
+		return mroute_modify(RTM_NEWADDR, argc-1, argv+1);
+	if (matches(*argv, "delete") == 0)
+		return mroute_modify(RTM_DELADDR, argc-1, argv+1);
+	if (matches(*argv, "get") == 0)
+		return mroute_get(argc-1, argv+1);
+#endif
+	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+	    || matches(*argv, "lst") == 0)
+		return mroute_list(argc-1, argv+1);
+	if (matches(*argv, "help") == 0)
+		usage();
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipneigh.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipneigh.c
new file mode 100755
index 0000000..93cfba2
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipneigh.c
@@ -0,0 +1,472 @@
+/*
+ * ipneigh.c		"ip neigh".
+ *
+ *		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 <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#define NUD_VALID	(NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
+#define MAX_ROUNDS	10
+
+static struct
+{
+	int family;
+        int index;
+	int state;
+	int unused_only;
+	inet_prefix pfx;
+	int flushed;
+	char *flushb;
+	int flushp;
+	int flushe;
+} filter;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n"
+		        "          [ nud { permanent | noarp | stale | reachable } ]\n"
+		        "          | proxy ADDR } [ dev DEV ]\n");
+	fprintf(stderr, "       ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
+	exit(-1);
+}
+
+int nud_state_a2n(unsigned *state, char *arg)
+{
+	if (matches(arg, "permanent") == 0)
+		*state = NUD_PERMANENT;
+	else if (matches(arg, "reachable") == 0)
+		*state = NUD_REACHABLE;
+	else if (strcmp(arg, "noarp") == 0)
+		*state = NUD_NOARP;
+	else if (strcmp(arg, "none") == 0)
+		*state = NUD_NONE;
+	else if (strcmp(arg, "stale") == 0)
+		*state = NUD_STALE;
+	else if (strcmp(arg, "incomplete") == 0)
+		*state = NUD_INCOMPLETE;
+	else if (strcmp(arg, "delay") == 0)
+		*state = NUD_DELAY;
+	else if (strcmp(arg, "probe") == 0)
+		*state = NUD_PROBE;
+	else if (matches(arg, "failed") == 0)
+		*state = NUD_FAILED;
+	else {
+		if (get_unsigned(state, arg, 0))
+			return -1;
+		if (*state>=0x100 || (*state&((*state)-1)))
+			return -1;
+	}
+	return 0;
+}
+
+static int flush_update(void)
+{
+	if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
+		perror("Failed to send flush request");
+		return -1;
+	}
+	filter.flushp = 0;
+	return 0;
+}
+
+
+static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct ndmsg 		ndm;
+		char   			buf[256];
+	} req;
+	char  *d = NULL;
+	int dst_ok = 0;
+	int lladdr_ok = 0;
+	char * lla = NULL;
+	inet_prefix dst;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.ndm.ndm_family = preferred_family;
+	req.ndm.ndm_state = NUD_PERMANENT;
+
+	while (argc > 0) {
+		if (matches(*argv, "lladdr") == 0) {
+			NEXT_ARG();
+			if (lladdr_ok)
+				duparg("lladdr", *argv);
+			lla = *argv;
+			lladdr_ok = 1;
+		} else if (strcmp(*argv, "nud") == 0) {
+			unsigned state;
+			NEXT_ARG();
+			if (nud_state_a2n(&state, *argv))
+				invarg("nud state is bad", *argv);
+			req.ndm.ndm_state = state;
+		} else if (matches(*argv, "proxy") == 0) {
+			NEXT_ARG();
+			if (matches(*argv, "help") == 0)
+				usage();
+			if (dst_ok)
+				duparg("address", *argv);
+			get_addr(&dst, *argv, preferred_family);
+			dst_ok = 1;
+			req.ndm.ndm_flags |= NTF_PROXY;
+		} else if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			d = *argv;
+		} else {
+			if (strcmp(*argv, "to") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0) {
+				NEXT_ARG();
+			}
+			if (dst_ok)
+				duparg2("to", *argv);
+			get_addr(&dst, *argv, preferred_family);
+			dst_ok = 1;
+		}
+		argc--; argv++;
+	}
+	if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) {
+		fprintf(stderr, "Device and destination are required arguments.\n");
+		exit(-1);
+	}
+	req.ndm.ndm_family = dst.family;
+	addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
+
+	if (lla && strcmp(lla, "null")) {
+		char llabuf[20];
+		int l;
+
+		l = ll_addr_a2n(llabuf, sizeof(llabuf), lla);
+		addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l);
+	}
+
+	ll_init_map(&rth);
+
+	if ((req.ndm.ndm_ifindex = 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) < 0)
+		exit(2);
+
+	return 0;
+}
+
+
+int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct ndmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[NDA_MAX+1];
+	char abuf[256];
+
+	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
+		fprintf(stderr, "Not RTM_NEWNEIGH: %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 (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH)
+		return 0;
+
+	if (filter.family && filter.family != r->ndm_family)
+		return 0;
+	if (filter.index && filter.index != r->ndm_ifindex)
+		return 0;
+	if (!(filter.state&r->ndm_state) &&
+	    !(r->ndm_flags & NTF_PROXY) &&
+	    (r->ndm_state || !(filter.state&0x100)) &&
+             (r->ndm_family != AF_DECnet))
+		return 0;
+
+	parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+	if (tb[NDA_DST]) {
+		if (filter.pfx.family) {
+			inet_prefix dst;
+			memset(&dst, 0, sizeof(dst));
+			dst.family = r->ndm_family;
+			memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
+			if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+				return 0;
+		}
+	}
+	if (filter.unused_only && tb[NDA_CACHEINFO]) {
+		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+		if (ci->ndm_refcnt)
+			return 0;
+	}
+
+	if (filter.flushb) {
+		struct nlmsghdr *fn;
+		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+			if (flush_update())
+				return -1;
+		}
+		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+		memcpy(fn, n, n->nlmsg_len);
+		fn->nlmsg_type = RTM_DELNEIGH;
+		fn->nlmsg_flags = NLM_F_REQUEST;
+		fn->nlmsg_seq = ++rth.seq;
+		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+		filter.flushed++;
+		if (show_stats < 2)
+			return 0;
+	}
+
+	if (tb[NDA_DST]) {
+		fprintf(fp, "%s ",
+			format_host(r->ndm_family,
+				    RTA_PAYLOAD(tb[NDA_DST]),
+				    RTA_DATA(tb[NDA_DST]),
+				    abuf, sizeof(abuf)));
+	}
+	if (!filter.index && r->ndm_ifindex)
+		fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
+	if (tb[NDA_LLADDR]) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
+					      RTA_PAYLOAD(tb[NDA_LLADDR]),
+					      ll_index_to_type(r->ndm_ifindex),
+					      b1, sizeof(b1)));
+	}
+	if (r->ndm_flags & NTF_ROUTER) {
+		fprintf(fp, " router");
+	}
+	if (r->ndm_flags & NTF_PROXY) {
+		fprintf(fp, " proxy");
+	}
+	if (tb[NDA_CACHEINFO] && show_stats) {
+		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+		int hz = get_user_hz();
+
+		if (ci->ndm_refcnt)
+			printf(" ref %d", ci->ndm_refcnt);
+		fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz,
+		       ci->ndm_confirmed/hz, ci->ndm_updated/hz);
+	}
+
+	if (tb[NDA_PROBES] && show_stats) {
+		__u32 p = rta_getattr_u32(tb[NDA_PROBES]);
+		fprintf(fp, " probes %u", p);
+	}
+
+	if (r->ndm_state) {
+		int nud = r->ndm_state;
+		fprintf(fp, " ");
+
+#define PRINT_FLAG(f) if (nud & NUD_##f) { \
+	nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); }
+		PRINT_FLAG(INCOMPLETE);
+		PRINT_FLAG(REACHABLE);
+		PRINT_FLAG(STALE);
+		PRINT_FLAG(DELAY);
+		PRINT_FLAG(PROBE);
+		PRINT_FLAG(FAILED);
+		PRINT_FLAG(NOARP);
+		PRINT_FLAG(PERMANENT);
+#undef PRINT_FLAG
+	}
+	fprintf(fp, "\n");
+
+	fflush(fp);
+	return 0;
+}
+
+void ipneigh_reset_filter()
+{
+	memset(&filter, 0, sizeof(filter));
+	filter.state = ~0;
+}
+
+int do_show_or_flush(int argc, char **argv, int flush)
+{
+	char *filter_dev = NULL;
+	int state_given = 0;
+	struct ndmsg ndm = { 0 };
+
+	ipneigh_reset_filter();
+
+	if (!filter.family)
+		filter.family = preferred_family;
+
+	if (flush) {
+		if (argc <= 0) {
+			fprintf(stderr, "Flush requires arguments.\n");
+			return -1;
+		}
+		filter.state = ~(NUD_PERMANENT|NUD_NOARP);
+	} else
+		filter.state = 0xFF & ~NUD_NOARP;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (filter_dev)
+				duparg("dev", *argv);
+			filter_dev = *argv;
+		} else if (strcmp(*argv, "unused") == 0) {
+			filter.unused_only = 1;
+		} else if (strcmp(*argv, "nud") == 0) {
+			unsigned state;
+			NEXT_ARG();
+			if (!state_given) {
+				state_given = 1;
+				filter.state = 0;
+			}
+			if (nud_state_a2n(&state, *argv)) {
+				if (strcmp(*argv, "all") != 0)
+					invarg("nud state is bad", *argv);
+				state = ~0;
+				if (flush)
+					state &= ~NUD_NOARP;
+			}
+			if (state == 0)
+				state = 0x100;
+			filter.state |= state;
+		} else if (strcmp(*argv, "proxy") == 0)
+			ndm.ndm_flags = NTF_PROXY;
+		else {
+			if (strcmp(*argv, "to") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			get_prefix(&filter.pfx, *argv, filter.family);
+			if (filter.family == AF_UNSPEC)
+				filter.family = filter.pfx.family;
+		}
+		argc--; argv++;
+	}
+
+	ll_init_map(&rth);
+
+	if (filter_dev) {
+		if ((filter.index = ll_name_to_index(filter_dev)) == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
+			return -1;
+		}
+	}
+
+	if (flush) {
+		int round = 0;
+		char flushb[4096-512];
+
+		filter.flushb = flushb;
+		filter.flushp = 0;
+		filter.flushe = sizeof(flushb);
+		filter.state &= ~NUD_FAILED;
+
+		while (round < MAX_ROUNDS) {
+			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
+				perror("Cannot send dump request");
+				exit(1);
+			}
+			filter.flushed = 0;
+			if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
+				fprintf(stderr, "Flush terminated\n");
+				exit(1);
+			}
+			if (filter.flushed == 0) {
+				if (show_stats) {
+					if (round == 0)
+						printf("Nothing to flush.\n");
+					else
+						printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+				}
+				fflush(stdout);
+				return 0;
+			}
+			round++;
+			if (flush_update() < 0)
+				exit(1);
+			if (show_stats) {
+				printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
+				fflush(stdout);
+			}
+		}
+		printf("*** Flush not complete bailing out after %d rounds\n",
+			MAX_ROUNDS);
+		return 1;
+	}
+
+	ndm.ndm_family = filter.family;
+
+	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+int do_ipneigh(int argc, char **argv)
+{
+	if (argc > 0) {
+		if (matches(*argv, "add") == 0)
+			return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
+		if (matches(*argv, "change") == 0 ||
+		    strcmp(*argv, "chg") == 0)
+			return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1);
+		if (matches(*argv, "replace") == 0)
+			return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
+		if (matches(*argv, "delete") == 0)
+			return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
+		if (matches(*argv, "get") == 0) {
+			fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n");
+			return -1;
+		}
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return do_show_or_flush(argc-1, argv+1, 0);
+		if (matches(*argv, "flush") == 0)
+			return do_show_or_flush(argc-1, argv+1, 1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return do_show_or_flush(0, NULL, 0);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipnetns.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipnetns.c
new file mode 100755
index 0000000..e41a598
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipnetns.c
@@ -0,0 +1,309 @@
+#define _ATFILE_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+#define NETNS_RUN_DIR "/var/run/netns"
+#define NETNS_ETC_DIR "/etc/netns"
+
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000	/* New network namespace (lo, device, names sockets, etc) */
+#endif
+
+#ifndef MNT_DETACH
+#define MNT_DETACH	0x00000002	/* Just detach from the tree */
+#endif /* MNT_DETACH */
+
+#ifndef HAVE_SETNS
+static int setns(int fd, int nstype)
+{
+#ifdef __NR_setns
+	return syscall(__NR_setns, fd, nstype);
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+#endif /* HAVE_SETNS */
+
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip netns list\n");
+	fprintf(stderr, "       ip netns add NAME\n");
+	fprintf(stderr, "       ip netns delete NAME\n");
+	fprintf(stderr, "       ip netns exec NAME cmd ...\n");
+	fprintf(stderr, "       ip netns monitor\n");
+	exit(-1);
+}
+
+int get_netns_fd(const char *name)
+{
+	char pathbuf[MAXPATHLEN];
+	const char *path, *ptr;
+
+	path = name;
+	ptr = strchr(name, '/');
+	if (!ptr) {
+		snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
+			NETNS_RUN_DIR, name );
+		path = pathbuf;
+	}
+	return open(path, O_RDONLY);
+}
+
+static int netns_list(int argc, char **argv)
+{
+	struct dirent *entry;
+	DIR *dir;
+
+	dir = opendir(NETNS_RUN_DIR);
+	if (!dir)
+		return 0;
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") == 0)
+			continue;
+		if (strcmp(entry->d_name, "..") == 0)
+			continue;
+		printf("%s\n", entry->d_name);
+	}
+	closedir(dir);
+	return 0;
+}
+
+static void bind_etc(const char *name)
+{
+	char etc_netns_path[MAXPATHLEN];
+	char netns_name[MAXPATHLEN];
+	char etc_name[MAXPATHLEN];
+	struct dirent *entry;
+	DIR *dir;
+
+	snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name);
+	dir = opendir(etc_netns_path);
+	if (!dir)
+		return;
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") == 0)
+			continue;
+		if (strcmp(entry->d_name, "..") == 0)
+			continue;
+		snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name);
+		snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name);
+		if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) {
+			fprintf(stderr, "Bind %s -> %s failed: %s\n",
+				netns_name, etc_name, strerror(errno));
+		}
+	}
+	closedir(dir);
+}
+
+static int netns_exec(int argc, char **argv)
+{
+	/* Setup the proper environment for apps that are not netns
+	 * aware, and execute a program in that environment.
+	 */
+	const char *name, *cmd;
+	char net_path[MAXPATHLEN];
+	int netns;
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
+	}
+	if (argc < 2) {
+		fprintf(stderr, "No cmd specified\n");
+		return -1;
+	}
+	name = argv[0];
+	cmd = argv[1];
+	snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
+	netns = open(net_path, O_RDONLY);
+	if (netns < 0) {
+		fprintf(stderr, "Cannot open network namespace: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	if (setns(netns, CLONE_NEWNET) < 0) {
+		fprintf(stderr, "seting the network namespace failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	if (unshare(CLONE_NEWNS) < 0) {
+		fprintf(stderr, "unshare failed: %s\n", strerror(errno));
+		return -1;
+	}
+	/* Mount a version of /sys that describes the network namespace */
+	if (umount2("/sys", MNT_DETACH) < 0) {
+		fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
+		return -1;
+	}
+	if (mount(name, "/sys", "sysfs", 0, NULL) < 0) {
+		fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
+		return -1;
+	}
+
+	/* Setup bind mounts for config files in /etc */
+	bind_etc(name);
+
+	if (execvp(cmd, argv + 1)  < 0)
+		fprintf(stderr, "exec of %s failed: %s\n",
+			cmd, strerror(errno));
+	exit(-1);
+}
+
+static int netns_delete(int argc, char **argv)
+{
+	const char *name;
+	char netns_path[MAXPATHLEN];
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
+	}
+
+	name = argv[0];
+	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
+	umount2(netns_path, MNT_DETACH);
+	if (unlink(netns_path) < 0) {
+		fprintf(stderr, "Cannot remove %s: %s\n",
+			netns_path, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int netns_add(int argc, char **argv)
+{
+	/* This function creates a new network namespace and
+	 * a new mount namespace and bind them into a well known
+	 * location in the filesystem based on the name provided.
+	 *
+	 * The mount namespace is created so that any necessary
+	 * userspace tweaks like remounting /sys, or bind mounting
+	 * a new /etc/resolv.conf can be shared between uers.
+	 */
+	char netns_path[MAXPATHLEN];
+	const char *name;
+	int fd;
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
+	}
+	name = argv[0];
+
+	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
+
+	/* Create the base netns directory if it doesn't exist */
+	mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+
+	/* Create the filesystem state */
+	fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Could not create %s: %s\n",
+			netns_path, strerror(errno));
+		return -1;
+	}
+	close(fd);
+	if (unshare(CLONE_NEWNET) < 0) {
+		fprintf(stderr, "Failed to create a new network namespace: %s\n",
+			strerror(errno));
+		goto out_delete;
+	}
+
+	/* Bind the netns last so I can watch for it */
+	if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) {
+		fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n",
+			netns_path, strerror(errno));
+		goto out_delete;
+	}
+	return 0;
+out_delete:
+	netns_delete(argc, argv);
+	exit(-1);
+	return -1;
+}
+
+
+static int netns_monitor(int argc, char **argv)
+{
+	char buf[4096];
+	struct inotify_event *event;
+	int fd;
+	fd = inotify_init();
+	if (fd < 0) {
+		fprintf(stderr, "inotify_init failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) {
+		fprintf(stderr, "inotify_add_watch failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	for(;;) {
+		ssize_t len = read(fd, buf, sizeof(buf));
+		if (len < 0) {
+			fprintf(stderr, "read failed: %s\n",
+				strerror(errno));
+			return -1;
+		}
+		for (event = (struct inotify_event *)buf;
+		     (char *)event < &buf[len];
+		     event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) {
+			if (event->mask & IN_CREATE)
+				printf("add %s\n", event->name);
+			if (event->mask & IN_DELETE)
+				printf("delete %s\n", event->name);
+		}
+	}
+	return 0;
+}
+
+int do_netns(int argc, char **argv)
+{
+	if (argc < 1)
+		return netns_list(0, NULL);
+
+	if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) ||
+	    (matches(*argv, "lst") == 0))
+		return netns_list(argc-1, argv+1);
+
+	if (matches(*argv, "help") == 0)
+		usage();
+
+	if (matches(*argv, "add") == 0)
+		return netns_add(argc-1, argv+1);
+
+	if (matches(*argv, "delete") == 0)
+		return netns_delete(argc-1, argv+1);
+
+	if (matches(*argv, "exec") == 0)
+		return netns_exec(argc-1, argv+1);
+
+	if (matches(*argv, "monitor") == 0)
+		return netns_monitor(argc-1, argv+1);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipntable.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipntable.c
new file mode 100755
index 0000000..639f512
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipntable.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * based on ipneigh.c
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+static struct
+{
+	int family;
+        int index;
+#define NONE_DEV	(-1)
+	char name[1024];
+} filter;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"Usage: ip ntable change name NAME [ dev DEV ]\n"
+		"          [ thresh1 VAL ] [ thresh2 VAL ] [ thresh3 VAL ] [ gc_int MSEC ]\n"
+		"          [ PARMS ]\n"
+		"Usage: ip ntable show [ dev DEV ] [ name NAME ]\n"
+
+		"PARMS := [ base_reachable MSEC ] [ retrans MSEC ] [ gc_stale MSEC ]\n"
+		"         [ delay_probe MSEC ] [ queue LEN ]\n"
+		"         [ app_probs VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n"
+		"         [ anycast_delay MSEC ] [ proxy_delay MSEC ] [ proxy_queue LEN ]\n"
+		"         [ locktime MSEC ]\n"
+		);
+
+	exit(-1);
+}
+
+static int ipntable_modify(int cmd, int flags, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct ndtmsg		ndtm;
+		char   			buf[1024];
+	} req;
+	char *namep = NULL;
+	char *threshsp = NULL;
+	char *gc_intp = NULL;
+	char parms_buf[1024];
+	struct rtattr *parms_rta = (struct rtattr *)parms_buf;
+	int parms_change = 0;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+
+	req.ndtm.ndtm_family = preferred_family;
+	req.ndtm.ndtm_pad1 = 0;
+	req.ndtm.ndtm_pad2 = 0;
+
+	memset(&parms_buf, 0, sizeof(parms_buf));
+
+	parms_rta->rta_type = NDTA_PARMS;
+	parms_rta->rta_len = RTA_LENGTH(0);
+
+	while (argc > 0) {
+		if (strcmp(*argv, "name") == 0) {
+			int len;
+
+			NEXT_ARG();
+			if (namep)
+				duparg("NAME", *argv);
+
+			namep = *argv;
+			len = strlen(namep) + 1;
+			addattr_l(&req.n, sizeof(req), NDTA_NAME, namep, len);
+		} else if (strcmp(*argv, "thresh1") == 0) {
+			__u32 thresh1;
+
+			NEXT_ARG();
+			threshsp = *argv;
+
+			if (get_u32(&thresh1, *argv, 0))
+				invarg("\"thresh1\" value is invalid", *argv);
+
+			addattr32(&req.n, sizeof(req), NDTA_THRESH1, thresh1);
+		} else if (strcmp(*argv, "thresh2") == 0) {
+			__u32 thresh2;
+
+			NEXT_ARG();
+			threshsp = *argv;
+
+			if (get_u32(&thresh2, *argv, 0))
+				invarg("\"thresh2\" value is invalid", *argv);
+
+			addattr32(&req.n, sizeof(req), NDTA_THRESH2, thresh2);
+		} else if (strcmp(*argv, "thresh3") == 0) {
+			__u32 thresh3;
+
+			NEXT_ARG();
+			threshsp = *argv;
+
+			if (get_u32(&thresh3, *argv, 0))
+				invarg("\"thresh3\" value is invalid", *argv);
+
+			addattr32(&req.n, sizeof(req), NDTA_THRESH3, thresh3);
+		} else if (strcmp(*argv, "gc_int") == 0) {
+			__u64 gc_int;
+
+			NEXT_ARG();
+			gc_intp = *argv;
+
+			if (get_u64(&gc_int, *argv, 0))
+				invarg("\"gc_int\" value is invalid", *argv);
+
+			addattr_l(&req.n, sizeof(req), NDTA_GC_INTERVAL,
+				  &gc_int, sizeof(gc_int));
+		} else if (strcmp(*argv, "dev") == 0) {
+			__u32 ifindex;
+
+			NEXT_ARG();
+			ifindex = ll_name_to_index(*argv);
+			if (ifindex == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", *argv);
+				return -1;
+			}
+
+			rta_addattr32(parms_rta, sizeof(parms_buf),
+				      NDTPA_IFINDEX, ifindex);
+		} else if (strcmp(*argv, "base_reachable") == 0) {
+			__u64 breachable;
+
+			NEXT_ARG();
+
+			if (get_u64(&breachable, *argv, 0))
+				invarg("\"base_reachable\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_BASE_REACHABLE_TIME,
+				      &breachable, sizeof(breachable));
+			parms_change = 1;
+		} else if (strcmp(*argv, "retrans") == 0) {
+			__u64 retrans;
+
+			NEXT_ARG();
+
+			if (get_u64(&retrans, *argv, 0))
+				invarg("\"retrans\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_RETRANS_TIME,
+				      &retrans, sizeof(retrans));
+			parms_change = 1;
+		} else if (strcmp(*argv, "gc_stale") == 0) {
+			__u64 gc_stale;
+
+			NEXT_ARG();
+
+			if (get_u64(&gc_stale, *argv, 0))
+				invarg("\"gc_stale\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_GC_STALETIME,
+				      &gc_stale, sizeof(gc_stale));
+			parms_change = 1;
+		} else if (strcmp(*argv, "delay_probe") == 0) {
+			__u64 delay_probe;
+
+			NEXT_ARG();
+
+			if (get_u64(&delay_probe, *argv, 0))
+				invarg("\"delay_probe\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_DELAY_PROBE_TIME,
+				      &delay_probe, sizeof(delay_probe));
+			parms_change = 1;
+		} else if (strcmp(*argv, "queue") == 0) {
+			__u32 queue;
+
+			NEXT_ARG();
+
+			if (get_u32(&queue, *argv, 0))
+				invarg("\"queue\" value is invalid", *argv);
+
+			if (!parms_rta)
+				parms_rta = (struct rtattr *)&parms_buf;
+			rta_addattr32(parms_rta, sizeof(parms_buf),
+				      NDTPA_QUEUE_LEN, queue);
+			parms_change = 1;
+		} else if (strcmp(*argv, "app_probes") == 0) {
+			__u32 aprobe;
+
+			NEXT_ARG();
+
+			if (get_u32(&aprobe, *argv, 0))
+				invarg("\"app_probes\" value is invalid", *argv);
+
+			rta_addattr32(parms_rta, sizeof(parms_buf),
+				      NDTPA_APP_PROBES, aprobe);
+			parms_change = 1;
+		} else if (strcmp(*argv, "ucast_probes") == 0) {
+			__u32 uprobe;
+
+			NEXT_ARG();
+
+			if (get_u32(&uprobe, *argv, 0))
+				invarg("\"ucast_probes\" value is invalid", *argv);
+
+			rta_addattr32(parms_rta, sizeof(parms_buf),
+				      NDTPA_UCAST_PROBES, uprobe);
+			parms_change = 1;
+		} else if (strcmp(*argv, "mcast_probes") == 0) {
+			__u32 mprobe;
+
+			NEXT_ARG();
+
+			if (get_u32(&mprobe, *argv, 0))
+				invarg("\"mcast_probes\" value is invalid", *argv);
+
+			rta_addattr32(parms_rta, sizeof(parms_buf),
+				      NDTPA_MCAST_PROBES, mprobe);
+			parms_change = 1;
+		} else if (strcmp(*argv, "anycast_delay") == 0) {
+			__u64 anycast_delay;
+
+			NEXT_ARG();
+
+			if (get_u64(&anycast_delay, *argv, 0))
+				invarg("\"anycast_delay\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_ANYCAST_DELAY,
+				      &anycast_delay, sizeof(anycast_delay));
+			parms_change = 1;
+		} else if (strcmp(*argv, "proxy_delay") == 0) {
+			__u64 proxy_delay;
+
+			NEXT_ARG();
+
+			if (get_u64(&proxy_delay, *argv, 0))
+				invarg("\"proxy_delay\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_PROXY_DELAY,
+				      &proxy_delay, sizeof(proxy_delay));
+			parms_change = 1;
+		} else if (strcmp(*argv, "proxy_queue") == 0) {
+			__u32 pqueue;
+
+			NEXT_ARG();
+
+			if (get_u32(&pqueue, *argv, 0))
+				invarg("\"proxy_queue\" value is invalid", *argv);
+
+			rta_addattr32(parms_rta, sizeof(parms_buf),
+				      NDTPA_PROXY_QLEN, pqueue);
+			parms_change = 1;
+		} else if (strcmp(*argv, "locktime") == 0) {
+			__u64 locktime;
+
+			NEXT_ARG();
+
+			if (get_u64(&locktime, *argv, 0))
+				invarg("\"locktime\" value is invalid", *argv);
+
+			rta_addattr_l(parms_rta, sizeof(parms_buf),
+				      NDTPA_LOCKTIME,
+				      &locktime, sizeof(locktime));
+			parms_change = 1;
+		} else {
+			invarg("unknown", *argv);
+		}
+
+		argc--; argv++;
+	}
+
+	if (!namep)
+		missarg("NAME");
+	if (!threshsp && !gc_intp && !parms_change) {
+		fprintf(stderr, "Not enough information: changable attributes required.\n");
+		exit(-1);
+	}
+
+	if (parms_rta->rta_len > RTA_LENGTH(0)) {
+		addattr_l(&req.n, sizeof(req), NDTA_PARMS, RTA_DATA(parms_rta),
+			  RTA_PAYLOAD(parms_rta));
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	return 0;
+}
+
+static const char *ntable_strtime_delta(__u32 msec)
+{
+	static char str[32];
+	struct timeval now;
+	time_t t;
+	struct tm *tp;
+
+	if (msec == 0)
+		goto error;
+
+	memset(&now, 0, sizeof(now));
+
+	if (gettimeofday(&now, NULL) < 0) {
+		perror("gettimeofday");
+		goto error;
+	}
+
+	t = now.tv_sec - (msec / 1000);
+	tp = localtime(&t);
+	if (!tp)
+		goto error;
+
+	strftime(str, sizeof(str), "%Y-%m-%d %T", tp);
+
+	return str;
+ error:
+	strcpy(str, "(error)");
+	return str;
+}
+
+int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct ndtmsg *ndtm = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[NDTA_MAX+1];
+	struct rtattr *tpb[NDTPA_MAX+1];
+	int ret;
+
+	if (n->nlmsg_type != RTM_NEWNEIGHTBL) {
+		fprintf(stderr, "Not NEIGHTBL: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+	len -= NLMSG_LENGTH(sizeof(*ndtm));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (preferred_family && preferred_family != ndtm->ndtm_family)
+		return 0;
+
+	parse_rtattr(tb, NDTA_MAX, NDTA_RTA(ndtm),
+		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*ndtm)));
+
+	if (tb[NDTA_NAME]) {
+		const char *name = rta_getattr_str(tb[NDTA_NAME]);
+
+		if (strlen(filter.name) > 0 && strcmp(filter.name, name))
+			return 0;
+	}
+	if (tb[NDTA_PARMS]) {
+		parse_rtattr(tpb, NDTPA_MAX, RTA_DATA(tb[NDTA_PARMS]),
+			     RTA_PAYLOAD(tb[NDTA_PARMS]));
+
+		if (tpb[NDTPA_IFINDEX]) {
+			__u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]);
+
+			if (filter.index && filter.index != ifindex)
+				return 0;
+		} else {
+			if (filter.index && filter.index != NONE_DEV)
+				return 0;
+		}
+	}
+
+	if (ndtm->ndtm_family == AF_INET)
+		fprintf(fp, "inet ");
+	else if (ndtm->ndtm_family == AF_INET6)
+		fprintf(fp, "inet6 ");
+	else if (ndtm->ndtm_family == AF_DECnet)
+		fprintf(fp, "dnet ");
+	else
+		fprintf(fp, "(%d) ", ndtm->ndtm_family);
+
+	if (tb[NDTA_NAME]) {
+		const char *name = rta_getattr_str(tb[NDTA_NAME]);
+		fprintf(fp, "%s ", name);
+	}
+
+	fprintf(fp, "%s", _SL_);
+
+	ret = (tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || tb[NDTA_THRESH3] ||
+	       tb[NDTA_GC_INTERVAL]);
+	if (ret)
+		fprintf(fp, "    ");
+
+	if (tb[NDTA_THRESH1]) {
+		__u32 thresh1 = rta_getattr_u32(tb[NDTA_THRESH1]);
+		fprintf(fp, "thresh1 %u ", thresh1);
+	}
+	if (tb[NDTA_THRESH2]) {
+		__u32 thresh2 = rta_getattr_u32(tb[NDTA_THRESH2]);
+		fprintf(fp, "thresh2 %u ", thresh2);
+	}
+	if (tb[NDTA_THRESH3]) {
+		__u32 thresh3 = rta_getattr_u32(tb[NDTA_THRESH3]);
+		fprintf(fp, "thresh3 %u ", thresh3);
+	}
+	if (tb[NDTA_GC_INTERVAL]) {
+		__u64 gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]);
+		fprintf(fp, "gc_int %llu ", gc_int);
+	}
+
+	if (ret)
+		fprintf(fp, "%s", _SL_);
+
+	if (tb[NDTA_CONFIG] && show_stats) {
+		struct ndt_config *ndtc = RTA_DATA(tb[NDTA_CONFIG]);
+
+		fprintf(fp, "    ");
+		fprintf(fp, "config ");
+
+		fprintf(fp, "key_len %u ", ndtc->ndtc_key_len);
+		fprintf(fp, "entry_size %u ", ndtc->ndtc_entry_size);
+		fprintf(fp, "entries %u ", ndtc->ndtc_entries);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "last_flush %s ",
+			ntable_strtime_delta(ndtc->ndtc_last_flush));
+		fprintf(fp, "last_rand %s ",
+			ntable_strtime_delta(ndtc->ndtc_last_rand));
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "hash_rnd %u ", ndtc->ndtc_hash_rnd);
+		fprintf(fp, "hash_mask %08x ", ndtc->ndtc_hash_mask);
+
+		fprintf(fp, "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc);
+		fprintf(fp, "proxy_qlen %u ", ndtc->ndtc_proxy_qlen);
+
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (tb[NDTA_PARMS]) {
+		if (tpb[NDTPA_IFINDEX]) {
+			__u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]);
+
+			fprintf(fp, "    ");
+			fprintf(fp, "dev %s ", ll_index_to_name(ifindex));
+			fprintf(fp, "%s", _SL_);
+		}
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_REFCNT]) {
+			__u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]);
+			fprintf(fp, "refcnt %u ", refcnt);
+		}
+		if (tpb[NDTPA_REACHABLE_TIME]) {
+			__u64 reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]);
+			fprintf(fp, "reachable %llu ", reachable);
+		}
+		if (tpb[NDTPA_BASE_REACHABLE_TIME]) {
+			__u64 breachable = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]);
+			fprintf(fp, "base_reachable %llu ", breachable);
+		}
+		if (tpb[NDTPA_RETRANS_TIME]) {
+			__u64 retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]);
+			fprintf(fp, "retrans %llu ", retrans);
+		}
+
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_GC_STALETIME]) {
+			__u64 gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]);
+			fprintf(fp, "gc_stale %llu ", gc_stale);
+		}
+		if (tpb[NDTPA_DELAY_PROBE_TIME]) {
+			__u64 delay_probe = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]);
+			fprintf(fp, "delay_probe %llu ", delay_probe);
+		}
+		if (tpb[NDTPA_QUEUE_LEN]) {
+			__u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]);
+			fprintf(fp, "queue %u ", queue);
+		}
+
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_APP_PROBES]) {
+			__u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]);
+			fprintf(fp, "app_probes %u ", aprobe);
+		}
+		if (tpb[NDTPA_UCAST_PROBES]) {
+			__u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]);
+			fprintf(fp, "ucast_probes %u ", uprobe);
+		}
+		if (tpb[NDTPA_MCAST_PROBES]) {
+			__u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]);
+			fprintf(fp, "mcast_probes %u ", mprobe);
+		}
+
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_ANYCAST_DELAY]) {
+			__u64 anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]);
+			fprintf(fp, "anycast_delay %llu ", anycast_delay);
+		}
+		if (tpb[NDTPA_PROXY_DELAY]) {
+			__u64 proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]);
+			fprintf(fp, "proxy_delay %llu ", proxy_delay);
+		}
+		if (tpb[NDTPA_PROXY_QLEN]) {
+			__u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]);
+			fprintf(fp, "proxy_queue %u ", pqueue);
+		}
+		if (tpb[NDTPA_LOCKTIME]) {
+			__u64 locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]);
+			fprintf(fp, "locktime %llu ", locktime);
+		}
+
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (tb[NDTA_STATS] && show_stats) {
+		struct ndt_stats *ndts = RTA_DATA(tb[NDTA_STATS]);
+
+		fprintf(fp, "    ");
+		fprintf(fp, "stats ");
+
+		fprintf(fp, "allocs %llu ", ndts->ndts_allocs);
+		fprintf(fp, "destroys %llu ", ndts->ndts_destroys);
+		fprintf(fp, "hash_grows %llu ", ndts->ndts_hash_grows);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "res_failed %llu ", ndts->ndts_res_failed);
+		fprintf(fp, "lookups %llu ", ndts->ndts_lookups);
+		fprintf(fp, "hits %llu ", ndts->ndts_hits);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "rcv_probes_mcast %llu ", ndts->ndts_rcv_probes_mcast);
+		fprintf(fp, "rcv_probes_ucast %llu ", ndts->ndts_rcv_probes_ucast);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "periodic_gc_runs %llu ", ndts->ndts_periodic_gc_runs);
+		fprintf(fp, "forced_gc_runs %llu ", ndts->ndts_forced_gc_runs);
+
+		fprintf(fp, "%s", _SL_);
+	}
+
+	fprintf(fp, "\n");
+
+	fflush(fp);
+	return 0;
+}
+
+void ipntable_reset_filter(void)
+{
+	memset(&filter, 0, sizeof(filter));
+}
+
+static int ipntable_show(int argc, char **argv)
+{
+	ipntable_reset_filter();
+
+	filter.family = preferred_family;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+
+			if (strcmp("none", *argv) == 0)
+				filter.index = NONE_DEV;
+			else if ((filter.index = ll_name_to_index(*argv)) == 0)
+				invarg("\"DEV\" is invalid", *argv);
+		} else if (strcmp(*argv, "name") == 0) {
+			NEXT_ARG();
+
+			strncpy(filter.name, *argv, sizeof(filter.name));
+		} else
+			invarg("unknown", *argv);
+
+		argc--; argv++;
+	}
+
+	if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETNEIGHTBL) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	if (rtnl_dump_filter(&rth, print_ntable, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+int do_ipntable(int argc, char **argv)
+{
+	ll_init_map(&rth);
+
+	if (argc > 0) {
+		if (matches(*argv, "change") == 0 ||
+		    matches(*argv, "chg") == 0)
+			return ipntable_modify(RTM_SETNEIGHTBL,
+					       NLM_F_REPLACE,
+					       argc-1, argv+1);
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return ipntable_show(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return ipntable_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip ntable help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipprefix.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipprefix.c
new file mode 100755
index 0000000..d8327be
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipprefix.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C)2005 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * based on ip.c, iproute.c
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/icmp6.h>
+#include "utils.h"
+
+/* prefix flags; see kernel's net/ipv6/addrconf.c and include/net/if_inet6.h */
+#define IF_PREFIX_ONLINK	0x01
+#define IF_PREFIX_AUTOCONF	0x02
+
+int print_prefix(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct prefixmsg *prefix = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[RTA_MAX+1];
+	int family = preferred_family;
+
+	if (n->nlmsg_type != RTM_NEWPREFIX) {
+		fprintf(stderr, "Not a prefix: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*prefix));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (family == AF_UNSPEC)
+		family = AF_INET6;
+	if (family != AF_INET6)
+		return 0;
+
+	if (prefix->prefix_family != AF_INET6) {
+		fprintf(stderr, "wrong family %d\n", prefix->prefix_family);
+		return 0;
+	}
+	if (prefix->prefix_type != ND_OPT_PREFIX_INFORMATION) {
+		fprintf(stderr, "wrong ND type %d\n", prefix->prefix_type);
+		return 0;
+	}
+
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(prefix), len);
+
+	fprintf(fp, "prefix ");
+
+	if (tb[PREFIX_ADDRESS]) {
+		struct in6_addr *pfx;
+		char abuf[256];
+
+		pfx = (struct in6_addr *)RTA_DATA(tb[PREFIX_ADDRESS]);
+
+		memset(abuf, '\0', sizeof(abuf));
+		fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*pfx), pfx,
+					      abuf, sizeof(abuf)));
+	}
+	fprintf(fp, "/%u ", prefix->prefix_len);
+
+	fprintf(fp, "dev %s ", ll_index_to_name(prefix->prefix_ifindex));
+
+	if (prefix->prefix_flags & IF_PREFIX_ONLINK)
+		fprintf(fp, "onlink ");
+	if (prefix->prefix_flags & IF_PREFIX_AUTOCONF)
+		fprintf(fp, "autoconf ");
+
+	if (tb[PREFIX_CACHEINFO]) {
+		struct prefix_cacheinfo *pc;
+		pc = (struct prefix_cacheinfo *)RTA_DATA(tb[PREFIX_CACHEINFO]);
+
+		fprintf(fp, "valid %u ", pc->valid_time);
+		fprintf(fp, "preferred %u ", pc->preferred_time);
+	}
+
+	fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iproute.c b/ap/app/iproute2/iproute2-3.4.0/ip/iproute.c
new file mode 100755
index 0000000..5cd313e
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iproute.c
@@ -0,0 +1,1578 @@
+/*
+ * 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>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.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 <errno.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+enum list_action {
+	IPROUTE_LIST,
+	IPROUTE_FLUSH,
+	IPROUTE_SAVE,
+};
+static const char *mx_names[RTAX_MAX+1] = {
+	[RTAX_MTU]	= "mtu",
+	[RTAX_WINDOW]	= "window",
+	[RTAX_RTT]	= "rtt",
+	[RTAX_RTTVAR]	= "rttvar",
+	[RTAX_SSTHRESH] = "ssthresh",
+	[RTAX_CWND]	= "cwnd",
+	[RTAX_ADVMSS]	= "advmss",
+	[RTAX_REORDERING]="reordering",
+	[RTAX_HOPLIMIT] = "hoplimit",
+	[RTAX_INITCWND] = "initcwnd",
+	[RTAX_FEATURES] = "features",
+	[RTAX_RTO_MIN]	= "rto_min",
+	[RTAX_INITRWND]	= "initrwnd",
+};
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n");
+	fprintf(stderr, "       ip route save SELECTOR\n");
+	fprintf(stderr, "       ip route restore\n");
+	fprintf(stderr, "       ip route get ADDRESS [ from ADDRESS iif STRING ]\n");
+	fprintf(stderr, "                            [ oif STRING ]  [ tos TOS ]\n");
+	fprintf(stderr, "                            [ mark NUMBER ]\n");
+	fprintf(stderr, "       ip route { add | del | change | append | replace } ROUTE\n");
+	fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n");
+	fprintf(stderr, "            [ table TABLE_ID ] [ proto RTPROTO ]\n");
+	fprintf(stderr, "            [ type TYPE ] [ scope SCOPE ]\n");
+	fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n");
+	fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n");
+	fprintf(stderr, "             [ table TABLE_ID ] [ proto RTPROTO ]\n");
+	fprintf(stderr, "             [ scope SCOPE ] [ metric METRIC ]\n");
+	fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n");
+	fprintf(stderr, "NH := [ via ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n");
+	fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ]\n");
+	fprintf(stderr, "           [ rtt TIME ] [ rttvar TIME ] [reordering NUMBER ]\n");
+	fprintf(stderr, "           [ window NUMBER] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n");
+	fprintf(stderr, "           [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n");
+	fprintf(stderr, "           [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n");
+	fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n");
+	fprintf(stderr, "          unreachable | prohibit | blackhole | nat ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
+	fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n");
+	fprintf(stderr, "MP_ALGO := { rr | drr | random | wrandom }\n");
+	fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n");
+	fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n");
+	fprintf(stderr, "TIME := NUMBER[s|ms]\n");
+	exit(-1);
+}
+
+
+static struct
+{
+	int tb;
+	int cloned;
+	int flushed;
+	char *flushb;
+	int flushp;
+	int flushe;
+	int protocol, protocolmask;
+	int scope, scopemask;
+	int type, typemask;
+	int tos, tosmask;
+	int iif, iifmask;
+	int oif, oifmask;
+	int mark, markmask;
+	int realm, realmmask;
+	inet_prefix rprefsrc;
+	inet_prefix rvia;
+	inet_prefix rdst;
+	inet_prefix mdst;
+	inet_prefix rsrc;
+	inet_prefix msrc;
+} filter;
+
+static int flush_update(void)
+{
+	if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
+		perror("Failed to send flush request");
+		return -1;
+	}
+	filter.flushp = 0;
+	return 0;
+}
+
+int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
+{
+	struct rtmsg *r = NLMSG_DATA(n);
+	inet_prefix dst;
+	inet_prefix src;
+	inet_prefix via;
+	inet_prefix prefsrc;
+	__u32 table;
+	static int ip6_multiple_tables;
+
+	table = rtm_get_table(r, tb);
+
+	if (r->rtm_family == AF_INET6 && table != RT_TABLE_MAIN)
+		ip6_multiple_tables = 1;
+
+	if (filter.cloned == !(r->rtm_flags&RTM_F_CLONED))
+		return 0;
+
+	if (r->rtm_family == AF_INET6 && !ip6_multiple_tables) {
+		if (filter.tb) {
+			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 != table)
+			return 0;
+	}
+	if ((filter.protocol^r->rtm_protocol)&filter.protocolmask)
+		return 0;
+	if ((filter.scope^r->rtm_scope)&filter.scopemask)
+		return 0;
+	if ((filter.type^r->rtm_type)&filter.typemask)
+		return 0;
+	if ((filter.tos^r->rtm_tos)&filter.tosmask)
+		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;
+	if (filter.rvia.family && r->rtm_family != filter.rvia.family)
+		return 0;
+	if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family)
+		return 0;
+
+	memset(&dst, 0, sizeof(dst));
+	dst.family = r->rtm_family;
+	if (tb[RTA_DST])
+		memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8);
+	if (filter.rsrc.family || filter.msrc.family) {
+		memset(&src, 0, sizeof(src));
+		src.family = r->rtm_family;
+		if (tb[RTA_SRC])
+			memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8);
+	}
+	if (filter.rvia.bitlen>0) {
+		memset(&via, 0, sizeof(via));
+		via.family = r->rtm_family;
+		if (tb[RTA_GATEWAY])
+			memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len/8);
+	}
+	if (filter.rprefsrc.bitlen>0) {
+		memset(&prefsrc, 0, sizeof(prefsrc));
+		prefsrc.family = r->rtm_family;
+		if (tb[RTA_PREFSRC])
+			memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8);
+	}
+
+	if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
+		return 0;
+	if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
+	    inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
+		return 0;
+
+	if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
+		return 0;
+	if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
+	    inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
+		return 0;
+
+	if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen))
+		return 0;
+	if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen))
+		return 0;
+	if (filter.realmmask) {
+		__u32 realms = 0;
+		if (tb[RTA_FLOW])
+			realms = rta_getattr_u32(tb[RTA_FLOW]);
+		if ((realms^filter.realm)&filter.realmmask)
+			return 0;
+	}
+	if (filter.iifmask) {
+		int iif = 0;
+		if (tb[RTA_IIF])
+			iif = *(int*)RTA_DATA(tb[RTA_IIF]);
+		if ((iif^filter.iif)&filter.iifmask)
+			return 0;
+	}
+	if (filter.oifmask) {
+		int oif = 0;
+		if (tb[RTA_OIF])
+			oif = *(int*)RTA_DATA(tb[RTA_OIF]);
+		if ((oif^filter.oif)&filter.oifmask)
+			return 0;
+	}
+	if (filter.markmask) {
+		int mark = 0;
+		if (tb[RTA_MARK])
+			mark = *(int *)RTA_DATA(tb[RTA_MARK]);
+		if ((mark ^ filter.mark) & filter.markmask)
+			return 0;
+	}
+	if (filter.flushb &&
+	    r->rtm_family == AF_INET6 &&
+	    r->rtm_dst_len == 0 &&
+	    r->rtm_type == RTN_UNREACHABLE &&
+	    tb[RTA_PRIORITY] &&
+	    *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
+		return 0;
+
+	return 1;
+}
+
+int calc_host_len(struct rtmsg *r)
+{
+	if (r->rtm_family == AF_INET6)
+		return 128;
+	else if (r->rtm_family == AF_INET)
+		return 32;
+	else if (r->rtm_family == AF_DECnet)
+		return 16;
+	else if (r->rtm_family == AF_IPX)
+		return 80;
+	else
+		return -1;
+}
+
+int print_route(const 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;
+	__u32 table;
+	SPRINT_BUF(b1);
+	static int hz;
+
+	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;
+	}
+	if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
+		return 0;
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	host_len = calc_host_len(r);
+
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+	table = rtm_get_table(r, tb);
+
+	if (!filter_nlmsg(n, tb, host_len))
+		return 0;
+
+	if (filter.flushb) {
+		struct nlmsghdr *fn;
+		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+			if (flush_update())
+				return -1;
+		}
+		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+		memcpy(fn, n, n->nlmsg_len);
+		fn->nlmsg_type = RTM_DELROUTE;
+		fn->nlmsg_flags = NLM_F_REQUEST;
+		fn->nlmsg_seq = ++rth.seq;
+		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+		filter.flushed++;
+		if (show_stats < 2)
+			return 0;
+	}
+
+	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 (r->rtm_tos && filter.tosmask != -1) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+	}
+
+	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 (!(r->rtm_flags&RTM_F_CLONED)) {
+		if (table != RT_TABLE_MAIN && !filter.tb)
+			fprintf(fp, " table %s ", rtnl_rttable_n2a(table, b1, sizeof(b1)));
+		if (r->rtm_protocol != RTPROT_BOOT && filter.protocolmask != -1)
+			fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1)));
+		if (r->rtm_scope != RT_SCOPE_UNIVERSE && filter.scopemask != -1)
+			fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1)));
+	}
+	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 %u ", rta_getattr_u32(tb[RTA_PRIORITY]));
+	if (r->rtm_flags & RTNH_F_DEAD)
+		fprintf(fp, "dead ");
+	if (r->rtm_flags & RTNH_F_ONLINK)
+		fprintf(fp, "onlink ");
+	if (r->rtm_flags & RTNH_F_PERVASIVE)
+		fprintf(fp, "pervasive ");
+	if (r->rtm_flags & RTM_F_NOTIFY)
+		fprintf(fp, "notify ");
+	if (tb[RTA_MARK]) {
+		unsigned int mark = *(unsigned int*)RTA_DATA(tb[RTA_MARK]);
+		if (mark) {
+			if (mark >= 16)
+				fprintf(fp, " mark 0x%x", mark);
+			else
+				fprintf(fp, " mark %u", mark);
+		}
+	}
+
+	if (tb[RTA_FLOW] && filter.realmmask != ~0U) {
+		__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
+		__u32 from = to>>16;
+		to &= 0xFFFF;
+		fprintf(fp, "realm%s ", from ? "s" : "");
+		if (from) {
+			fprintf(fp, "%s/",
+				rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+		}
+		fprintf(fp, "%s ",
+			rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+	}
+	if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) {
+		__u32 flags = r->rtm_flags&~0xFFFF;
+		int first = 1;
+
+		fprintf(fp, "%s    cache ", _SL_);
+
+#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \
+  flags &= ~RTCF_##fl; \
+  fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \
+  first = 0; }
+		PRTFL(LOCAL, "local");
+		PRTFL(REJECT, "reject");
+		PRTFL(MULTICAST, "mc");
+		PRTFL(BROADCAST, "brd");
+		PRTFL(DNAT, "dst-nat");
+		PRTFL(SNAT, "src-nat");
+		PRTFL(MASQ, "masq");
+		PRTFL(DIRECTDST, "dst-direct");
+		PRTFL(DIRECTSRC, "src-direct");
+		PRTFL(REDIRECTED, "redirected");
+		PRTFL(DOREDIRECT, "redirect");
+		PRTFL(FAST, "fastroute");
+		PRTFL(NOTIFY, "notify");
+		PRTFL(TPROXY, "proxy");
+
+		if (flags)
+			fprintf(fp, "%s%x> ", first ? "<" : "", flags);
+		if (tb[RTA_CACHEINFO]) {
+			struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]);
+			if (!hz)
+				hz = get_user_hz();
+			if (ci->rta_expires != 0)
+				fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+			if (ci->rta_error != 0)
+				fprintf(fp, " error %d", ci->rta_error);
+			if (show_stats) {
+				if (ci->rta_clntref)
+					fprintf(fp, " users %d", ci->rta_clntref);
+				if (ci->rta_used != 0)
+					fprintf(fp, " used %d", ci->rta_used);
+				if (ci->rta_lastuse != 0)
+					fprintf(fp, " age %dsec", ci->rta_lastuse/hz);
+			}
+			if (ci->rta_id)
+				fprintf(fp, " ipid 0x%04x", ci->rta_id);
+			if (ci->rta_ts || ci->rta_tsage)
+				fprintf(fp, " ts 0x%x tsage %dsec",
+					ci->rta_ts, ci->rta_tsage);
+		}
+	} else 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)) {
+			if (!hz)
+				hz = get_user_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);
+			if (show_stats) {
+				if (ci->rta_clntref)
+					fprintf(fp, " users %d", ci->rta_clntref);
+				if (ci->rta_used != 0)
+					fprintf(fp, " used %d", ci->rta_used);
+				if (ci->rta_lastuse != 0)
+					fprintf(fp, " age %dsec", ci->rta_lastuse/hz);
+			}
+		} else if (ci) {
+			if (ci->rta_error != 0)
+				fprintf(fp, " error %d", ci->rta_error);
+		}
+	}
+	if (tb[RTA_METRICS]) {
+		int i;
+		unsigned mxlock = 0;
+		struct rtattr *mxrta[RTAX_MAX+1];
+
+		parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]),
+			    RTA_PAYLOAD(tb[RTA_METRICS]));
+		if (mxrta[RTAX_LOCK])
+			mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]);
+
+		for (i=2; i<= RTAX_MAX; i++) {
+			unsigned val;
+
+			if (mxrta[i] == NULL)
+				continue;
+
+			if (i < sizeof(mx_names)/sizeof(char*) && mx_names[i])
+				fprintf(fp, " %s", mx_names[i]);
+			else
+				fprintf(fp, " metric %d", i);
+			if (mxlock & (1<<i))
+				fprintf(fp, " lock");
+
+			val = *(unsigned*)RTA_DATA(mxrta[i]);
+			switch (i) {
+			case RTAX_HOPLIMIT:
+				if ((int)val == -1)
+					val = 0;
+				/* fall through */
+			default:
+				fprintf(fp, " %u", val);
+				break;
+
+			case RTAX_RTT:
+			case RTAX_RTTVAR:
+			case RTAX_RTO_MIN:
+				if (i == RTAX_RTT)
+					val /= 8;
+				else if (i == RTAX_RTTVAR)
+					val /= 4;
+
+				if (val >= 1000)
+					fprintf(fp, " %gs", val/1e3);
+				else
+					fprintf(fp, " %ums", val);
+			}
+		}
+	}
+	if (tb[RTA_IIF] && filter.iifmask != -1) {
+		fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+	}
+	if (tb[RTA_MULTIPATH]) {
+		struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
+		int first = 0;
+
+		len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
+
+		for (;;) {
+			if (len < sizeof(*nh))
+				break;
+			if (nh->rtnh_len > len)
+				break;
+			if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
+				if (first)
+					fprintf(fp, " Oifs:");
+				else
+					fprintf(fp, " ");
+			} else
+				fprintf(fp, "%s\tnexthop", _SL_);
+			if (nh->rtnh_len > sizeof(*nh)) {
+				parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh));
+				if (tb[RTA_GATEWAY]) {
+					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_FLOW]) {
+					__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
+					__u32 from = to>>16;
+					to &= 0xFFFF;
+					fprintf(fp, " realm%s ", from ? "s" : "");
+					if (from) {
+						fprintf(fp, "%s/",
+							rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+					}
+					fprintf(fp, "%s",
+						rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+				}
+			}
+			if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
+				fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex));
+				if (nh->rtnh_hops != 1)
+					fprintf(fp, "(ttl>%d)", nh->rtnh_hops);
+			} else {
+				fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex));
+				fprintf(fp, " weight %d", nh->rtnh_hops+1);
+			}
+			if (nh->rtnh_flags & RTNH_F_DEAD)
+				fprintf(fp, " dead");
+			if (nh->rtnh_flags & RTNH_F_ONLINK)
+				fprintf(fp, " onlink");
+			if (nh->rtnh_flags & RTNH_F_PERVASIVE)
+				fprintf(fp, " pervasive");
+			len -= NLMSG_ALIGN(nh->rtnh_len);
+			nh = RTNH_NEXT(nh);
+		}
+	}
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+
+int parse_one_nh(struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	while (++argv, --argc > 0) {
+		if (strcmp(*argv, "via") == 0) {
+			NEXT_ARG();
+			rta_addattr32(rta, 4096, RTA_GATEWAY, get_addr32(*argv));
+			rtnh->rtnh_len += sizeof(struct rtattr) + 4;
+		} else if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", *argv);
+				exit(1);
+			}
+		} else if (strcmp(*argv, "weight") == 0) {
+			unsigned w;
+			NEXT_ARG();
+			if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256)
+				invarg("\"weight\" is invalid\n", *argv);
+			rtnh->rtnh_hops = w - 1;
+		} else if (strcmp(*argv, "onlink") == 0) {
+			rtnh->rtnh_flags |= RTNH_F_ONLINK;
+		} else if (matches(*argv, "realms") == 0) {
+			__u32 realm;
+			NEXT_ARG();
+			if (get_rt_realms(&realm, *argv))
+				invarg("\"realm\" value is invalid\n", *argv);
+			rta_addattr32(rta, 4096, RTA_FLOW, realm);
+			rtnh->rtnh_len += sizeof(struct rtattr) + 4;
+		} else
+			break;
+	}
+	*argcp = argc;
+	*argvp = argv;
+	return 0;
+}
+
+int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv)
+{
+	char buf[1024];
+	struct rtattr *rta = (void*)buf;
+	struct rtnexthop *rtnh;
+
+	rta->rta_type = RTA_MULTIPATH;
+	rta->rta_len = RTA_LENGTH(0);
+	rtnh = RTA_DATA(rta);
+
+	while (argc > 0) {
+		if (strcmp(*argv, "nexthop") != 0) {
+			fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", *argv);
+			exit(-1);
+		}
+		if (argc <= 1) {
+			fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n");
+			exit(-1);
+		}
+		memset(rtnh, 0, sizeof(*rtnh));
+		rtnh->rtnh_len = sizeof(*rtnh);
+		rta->rta_len += rtnh->rtnh_len;
+		parse_one_nh(rta, rtnh, &argc, &argv);
+		rtnh = RTNH_NEXT(rtnh);
+	}
+
+	if (rta->rta_len > RTA_LENGTH(0))
+		addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
+	return 0;
+}
+
+
+int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+	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 raw = 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, "from") == 0) {
+			inet_prefix addr;
+			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_SRC, &addr.data, addr.bytelen);
+			req.r.rtm_src_len = addr.bitlen;
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 tos;
+			NEXT_ARG();
+			if (rtnl_dsfield_a2n(&tos, *argv))
+				invarg("\"tos\" value is invalid\n", *argv);
+			req.r.rtm_tos = tos;
+		} else if (matches(*argv, "metric") == 0 ||
+			   matches(*argv, "priority") == 0 ||
+			   matches(*argv, "preference") == 0) {
+			__u32 metric;
+			NEXT_ARG();
+			if (get_u32(&metric, *argv, 0))
+				invarg("\"metric\" value is invalid\n", *argv);
+			addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
+		} else if (strcmp(*argv, "scope") == 0) {
+			__u32 scope = 0;
+			NEXT_ARG();
+			if (rtnl_rtscope_a2n(&scope, *argv))
+				invarg("invalid \"scope\" value\n", *argv);
+			req.r.rtm_scope = scope;
+			scope_ok = 1;
+		} 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 (strcmp(*argv, "hoplimit") == 0) {
+			unsigned hoplimit;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_HOPLIMIT);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&hoplimit, *argv, 0))
+				invarg("\"hoplimit\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
+		} else if (strcmp(*argv, "advmss") == 0) {
+			unsigned mss;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_ADVMSS);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&mss, *argv, 0))
+				invarg("\"mss\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
+		} else if (matches(*argv, "reordering") == 0) {
+			unsigned reord;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_REORDERING);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&reord, *argv, 0))
+				invarg("\"reordering\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
+		} else if (strcmp(*argv, "rtt") == 0) {
+			unsigned rtt;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_RTT);
+				NEXT_ARG();
+			}
+			if (get_time_rtt(&rtt, *argv, &raw))
+				invarg("\"rtt\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, 
+				(raw) ? rtt : rtt * 8);
+		} else if (strcmp(*argv, "rto_min") == 0) {
+			unsigned rto_min;
+			NEXT_ARG();
+			mxlock |= (1<<RTAX_RTO_MIN);
+			if (get_time_rtt(&rto_min, *argv, &raw))
+				invarg("\"rto_min\" value is invalid\n",
+				       *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
+				      rto_min);
+		} else if (matches(*argv, "window") == 0) {
+			unsigned win;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_WINDOW);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&win, *argv, 0))
+				invarg("\"window\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
+		} else if (matches(*argv, "cwnd") == 0) {
+			unsigned win;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_CWND);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&win, *argv, 0))
+				invarg("\"cwnd\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
+		} else if (matches(*argv, "initcwnd") == 0) {
+			unsigned win;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_INITCWND);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&win, *argv, 0))
+				invarg("\"initcwnd\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
+		} else if (matches(*argv, "initrwnd") == 0) {
+			unsigned win;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_INITRWND);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&win, *argv, 0))
+				invarg("\"initrwnd\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
+		} else if (matches(*argv, "rttvar") == 0) {
+			unsigned win;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_RTTVAR);
+				NEXT_ARG();
+			}
+			if (get_time_rtt(&win, *argv, &raw))
+				invarg("\"rttvar\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
+				(raw) ? win : win * 4);
+		} else if (matches(*argv, "ssthresh") == 0) {
+			unsigned win;
+			NEXT_ARG();
+			if (strcmp(*argv, "lock") == 0) {
+				mxlock |= (1<<RTAX_SSTHRESH);
+				NEXT_ARG();
+			}
+			if (get_unsigned(&win, *argv, 0))
+				invarg("\"ssthresh\" value is invalid\n", *argv);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
+		} else if (matches(*argv, "realms") == 0) {
+			__u32 realm;
+			NEXT_ARG();
+			if (get_rt_realms(&realm, *argv))
+				invarg("\"realm\" value is invalid\n", *argv);
+			addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
+		} else if (strcmp(*argv, "onlink") == 0) {
+			req.r.rtm_flags |= RTNH_F_ONLINK;
+		} else if (strcmp(*argv, "nexthop") == 0) {
+			nhs_ok = 1;
+			break;
+		} else if (matches(*argv, "protocol") == 0) {
+			__u32 prot;
+			NEXT_ARG();
+			if (rtnl_rtprot_a2n(&prot, *argv))
+				invarg("\"protocol\" value is invalid\n", *argv);
+			req.r.rtm_protocol = prot;
+		} else if (matches(*argv, "table") == 0) {
+			__u32 tid;
+			NEXT_ARG();
+			if (rtnl_rttable_a2n(&tid, *argv))
+				invarg("\"table\" value is invalid\n", *argv);
+			if (tid < 256)
+				req.r.rtm_table = tid;
+			else {
+				req.r.rtm_table = RT_TABLE_UNSPEC;
+				addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
+			}
+			table_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;
+			}
+
+			if (matches(*argv, "help") == 0)
+				usage();
+			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 (d || nhs_ok)  {
+		int idx;
+
+		ll_init_map(&rth);
+
+		if (d) {
+			if ((idx = ll_name_to_index(d)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", d);
+				return -1;
+			}
+			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+		}
+	}
+
+	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 (nhs_ok)
+		parse_nexthops(&req.n, &req.r, argc, argv);
+
+	if (!table_ok) {
+		if (req.r.rtm_type == RTN_LOCAL ||
+		    req.r.rtm_type == RTN_BROADCAST ||
+		    req.r.rtm_type == RTN_NAT ||
+		    req.r.rtm_type == RTN_ANYCAST)
+			req.r.rtm_table = RT_TABLE_LOCAL;
+	}
+	if (!scope_ok) {
+		if (req.r.rtm_type == RTN_LOCAL ||
+		    req.r.rtm_type == RTN_NAT)
+			req.r.rtm_scope = RT_SCOPE_HOST;
+		else if (req.r.rtm_type == RTN_BROADCAST ||
+			 req.r.rtm_type == RTN_MULTICAST ||
+			 req.r.rtm_type == RTN_ANYCAST)
+			req.r.rtm_scope = RT_SCOPE_LINK;
+		else if (req.r.rtm_type == RTN_UNICAST ||
+			 req.r.rtm_type == RTN_UNSPEC) {
+			if (cmd == RTM_DELROUTE)
+				req.r.rtm_scope = RT_SCOPE_NOWHERE;
+			else if (!gw_ok && !nhs_ok)
+				req.r.rtm_scope = RT_SCOPE_LINK;
+		}
+	}
+
+	if (req.r.rtm_family == AF_UNSPEC)
+		req.r.rtm_family = AF_INET;
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, 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_flush_cache(void)
+{
+#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
+
+	int len;
+	int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
+	char *buffer = "-1";
+
+	if (flush_fd < 0) {
+		fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
+		return -1;
+	}
+
+	len = strlen (buffer);
+
+	if ((write (flush_fd, (void *)buffer, len)) < len) {
+		fprintf (stderr, "Cannot flush routing cache\n");
+		close(flush_fd);
+		return -1;
+	}
+	close(flush_fd);
+	return 0;
+}
+
+int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	int ret;
+	int len = n->nlmsg_len;
+	struct rtmsg *r = NLMSG_DATA(n);
+	struct rtattr *tb[RTA_MAX+1];
+	int host_len = -1;
+
+	if (isatty(STDOUT_FILENO)) {
+		fprintf(stderr, "Not sending binary stream to stdout\n");
+		return -1;
+	}
+
+	host_len = calc_host_len(r);
+	len -= NLMSG_LENGTH(sizeof(*r));
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+	if (!filter_nlmsg(n, tb, host_len))
+		return 0;
+
+	ret = write(STDOUT_FILENO, n, n->nlmsg_len);
+	if ((ret > 0) && (ret != n->nlmsg_len)) {
+		fprintf(stderr, "Short write while saving nlmsg\n");
+		ret = -EIO;
+	}
+
+	return ret == n->nlmsg_len ? 0 : ret;
+}
+
+static int iproute_list_flush_or_save(int argc, char **argv, int action)
+{
+	int do_ipv6 = preferred_family;
+	char *id = NULL;
+	char *od = NULL;
+	unsigned int mark = 0;
+	rtnl_filter_t filter_fn;
+
+	if (action == IPROUTE_SAVE)
+		filter_fn = save_route;
+	else
+		filter_fn = print_route;
+
+	iproute_reset_filter();
+	filter.tb = RT_TABLE_MAIN;
+
+	if ((action == IPROUTE_FLUSH) && argc <= 0) {
+		fprintf(stderr, "\"ip route flush\" requires arguments.\n");
+		return -1;
+	}
+
+	while (argc > 0) {
+		if (matches(*argv, "table") == 0) {
+			__u32 tid;
+			NEXT_ARG();
+			if (rtnl_rttable_a2n(&tid, *argv)) {
+				if (strcmp(*argv, "all") == 0) {
+					filter.tb = 0;
+				} else if (strcmp(*argv, "cache") == 0) {
+					filter.cloned = 1;
+				} else if (strcmp(*argv, "help") == 0) {
+					usage();
+				} else {
+					invarg("table id value is invalid\n", *argv);
+				}
+			} else
+				filter.tb = tid;
+		} else if (matches(*argv, "cached") == 0 ||
+			   matches(*argv, "cloned") == 0) {
+			filter.cloned = 1;
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 tos;
+			NEXT_ARG();
+			if (rtnl_dsfield_a2n(&tos, *argv))
+				invarg("TOS value is invalid\n", *argv);
+			filter.tos = tos;
+			filter.tosmask = -1;
+		} else if (matches(*argv, "protocol") == 0) {
+			__u32 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 (matches(*argv, "scope") == 0) {
+			__u32 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 (matches(*argv, "type") == 0) {
+			int type;
+			NEXT_ARG();
+			filter.typemask = -1;
+			if (rtnl_rtntype_a2n(&type, *argv))
+				invarg("node type value is invalid\n", *argv);
+			filter.type = type;
+		} 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 (strcmp(*argv, "mark") == 0) {
+			NEXT_ARG();
+			get_unsigned(&mark, *argv, 0);
+			filter.markmask = -1;
+		} else if (strcmp(*argv, "via") == 0) {
+			NEXT_ARG();
+			get_prefix(&filter.rvia, *argv, do_ipv6);
+		} else if (strcmp(*argv, "src") == 0) {
+			NEXT_ARG();
+			get_prefix(&filter.rprefsrc, *argv, do_ipv6);
+		} else if (matches(*argv, "realms") == 0) {
+			__u32 realm;
+			NEXT_ARG();
+			if (get_rt_realms(&realm, *argv))
+				invarg("invalid realms\n", *argv);
+			filter.realm = realm;
+			filter.realmmask = ~0U;
+			if ((filter.realm&0xFFFF) == 0 &&
+			    (*argv)[strlen(*argv) - 1] == '/')
+				filter.realmmask &= ~0xFFFF;
+			if ((filter.realm&0xFFFF0000U) == 0 &&
+			    (strchr(*argv, '/') == NULL ||
+			     (*argv)[0] == '/'))
+				filter.realmmask &= ~0xFFFF0000U;
+		} 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;
+
+	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;
+		}
+	}
+	filter.mark = mark;
+
+	if (action == IPROUTE_FLUSH) {
+		int round = 0;
+		char flushb[4096-512];
+		time_t start = time(0);
+
+		if (filter.cloned) {
+			if (do_ipv6 != AF_INET6) {
+				iproute_flush_cache();
+				if (show_stats)
+					printf("*** IPv4 routing cache is flushed.\n");
+			}
+			if (do_ipv6 == AF_INET)
+				return 0;
+		}
+
+		filter.flushb = flushb;
+		filter.flushp = 0;
+		filter.flushe = sizeof(flushb);
+
+		for (;;) {
+			if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+				perror("Cannot send dump request");
+				exit(1);
+			}
+			filter.flushed = 0;
+			if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
+				fprintf(stderr, "Flush terminated\n");
+				exit(1);
+			}
+			if (filter.flushed == 0) {
+				if (show_stats) {
+					if (round == 0 && (!filter.cloned || do_ipv6 == AF_INET6))
+						printf("Nothing to flush.\n");
+					else
+						printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+				}
+				fflush(stdout);
+				return 0;
+			}
+			round++;
+			if (flush_update() < 0)
+				exit(1);
+
+			if (time(0) - start > 30) {
+				printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n",
+				       time(0) - start, filter.flushed);
+				exit(1);
+			}
+
+			if (show_stats) {
+				printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
+				fflush(stdout);
+			}
+		}
+	}
+
+	if (!filter.cloned) {
+		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, filter_fn, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	exit(0);
+}
+
+
+int iproute_get(int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct rtmsg 		r;
+		char   			buf[1024];
+	} req;
+	char  *idev = NULL;
+	char  *odev = NULL;
+	int connected = 0;
+	int from_ok = 0;
+	unsigned int mark = 0;
+
+	memset(&req, 0, sizeof(req));
+
+	iproute_reset_filter();
+	filter.cloned = 2;
+
+	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 (strcmp(*argv, "tos") == 0 ||
+		    matches(*argv, "dsfield") == 0) {
+			__u32 tos;
+			NEXT_ARG();
+			if (rtnl_dsfield_a2n(&tos, *argv))
+				invarg("TOS value is invalid\n", *argv);
+			req.r.rtm_tos = tos;
+		} else if (matches(*argv, "from") == 0) {
+			inet_prefix addr;
+			NEXT_ARG();
+			if (matches(*argv, "help") == 0)
+				usage();
+			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, "mark") == 0) {
+			NEXT_ARG();
+			get_unsigned(&mark, *argv, 0);
+		} 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();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			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);
+	}
+
+	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 (mark)
+		addattr32(&req.n, sizeof(req), RTA_MARK, mark);
+
+	if (req.r.rtm_family == AF_UNSPEC)
+		req.r.rtm_family = AF_INET;
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 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;
+		}
+
+		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) < 0)
+			exit(2);
+	}
+
+	if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+		fprintf(stderr, "An error :-)\n");
+		exit(1);
+	}
+
+	exit(0);
+}
+
+int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+{
+	int ret;
+
+	n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+
+	ll_init_map(&rth);
+
+	ret = rtnl_talk(&rth, n, 0, 0, n);
+	if ((ret < 0) && (errno == EEXIST))
+		ret = 0;
+
+	return ret;
+}
+
+int iproute_restore(void)
+{
+	exit(rtnl_from_file(stdin, &restore_handler, NULL));
+}
+
+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_flush_or_save(0, NULL, IPROUTE_LIST);
+
+	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_flush_or_save(argc-1, argv+1, IPROUTE_LIST);
+	if (matches(*argv, "get") == 0)
+		return iproute_get(argc-1, argv+1);
+	if (matches(*argv, "flush") == 0)
+		return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_FLUSH);
+	if (matches(*argv, "save") == 0)
+		return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_SAVE);
+	if (matches(*argv, "restore") == 0)
+		return iproute_restore();
+	if (matches(*argv, "help") == 0)
+		usage();
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
+	exit(-1);
+}
+
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iprule.c b/ap/app/iproute2/iproute2-3.4.0/ip/iprule.c
new file mode 100755
index 0000000..a5fcd43
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iprule.c
@@ -0,0 +1,460 @@
+/*
+ * iprule.c		"ip rule".
+ *
+ *		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 <netinet/ip.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <linux/fib_rules.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+extern struct rtnl_handle rth;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip rule [ list | add | del | flush ] SELECTOR ACTION\n");
+	fprintf(stderr, "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n");
+	fprintf(stderr, "            [ iif STRING ] [ oif STRING ] [ pref NUMBER ]\n");
+	fprintf(stderr, "ACTION := [ table TABLE_ID ]\n");
+	fprintf(stderr, "          [ prohibit | reject | unreachable ]\n");
+	fprintf(stderr, "          [ realms [SRCREALM/]DSTREALM ]\n");
+	fprintf(stderr, "          [ goto NUMBER ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
+	exit(-1);
+}
+
+int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	int host_len = -1;
+	__u32 table;
+	struct rtattr * tb[FRA_MAX+1];
+	char abuf[256];
+	SPRINT_BUF(b1);
+
+	if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE)
+		return 0;
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len);
+
+	if (r->rtm_family == AF_INET)
+		host_len = 32;
+	else if (r->rtm_family == AF_INET6)
+		host_len = 128;
+	else if (r->rtm_family == AF_DECnet)
+		host_len = 16;
+	else if (r->rtm_family == AF_IPX)
+		host_len = 80;
+
+	if (n->nlmsg_type == RTM_DELRULE)
+		fprintf(fp, "Deleted ");
+
+	if (tb[FRA_PRIORITY])
+		fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[FRA_PRIORITY]));
+	else
+		fprintf(fp, "0:\t");
+
+	if (r->rtm_flags & FIB_RULE_INVERT)
+		fprintf(fp, "not ");
+
+	if (tb[FRA_SRC]) {
+		if (r->rtm_src_len != host_len) {
+			fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+							 RTA_PAYLOAD(tb[FRA_SRC]),
+							 RTA_DATA(tb[FRA_SRC]),
+							 abuf, sizeof(abuf)),
+				r->rtm_src_len
+				);
+		} else {
+			fprintf(fp, "from %s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[FRA_SRC]),
+						       RTA_DATA(tb[FRA_SRC]),
+						       abuf, sizeof(abuf))
+				);
+		}
+	} else if (r->rtm_src_len) {
+		fprintf(fp, "from 0/%d ", r->rtm_src_len);
+	} else {
+		fprintf(fp, "from all ");
+	}
+
+	if (tb[FRA_DST]) {
+		if (r->rtm_dst_len != host_len) {
+			fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family,
+							 RTA_PAYLOAD(tb[FRA_DST]),
+							 RTA_DATA(tb[FRA_DST]),
+							 abuf, sizeof(abuf)),
+				r->rtm_dst_len
+				);
+		} else {
+			fprintf(fp, "to %s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[FRA_DST]),
+						       RTA_DATA(tb[FRA_DST]),
+						       abuf, sizeof(abuf)));
+		}
+	} else if (r->rtm_dst_len) {
+		fprintf(fp, "to 0/%d ", r->rtm_dst_len);
+	}
+
+	if (r->rtm_tos) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+	}
+
+ 	if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) {
+		__u32 mark = 0, mask = 0;
+
+		if (tb[FRA_FWMARK])
+			mark = rta_getattr_u32(tb[FRA_FWMARK]);
+
+		if (tb[FRA_FWMASK] &&
+		    (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF)
+			fprintf(fp, "fwmark 0x%x/0x%x ", mark, mask);
+		else
+			fprintf(fp, "fwmark 0x%x ", mark);
+	}
+
+	if (tb[FRA_IFNAME]) {
+		fprintf(fp, "iif %s ", rta_getattr_str(tb[FRA_IFNAME]));
+		if (r->rtm_flags & FIB_RULE_IIF_DETACHED)
+			fprintf(fp, "[detached] ");
+	}
+
+	if (tb[FRA_OIFNAME]) {
+		fprintf(fp, "oif %s ", rta_getattr_str(tb[FRA_OIFNAME]));
+		if (r->rtm_flags & FIB_RULE_OIF_DETACHED)
+			fprintf(fp, "[detached] ");
+	}
+
+	table = rtm_get_table(r, tb);
+	if (table)
+		fprintf(fp, "lookup %s ", rtnl_rttable_n2a(table, b1, sizeof(b1)));
+
+	if (tb[FRA_FLOW]) {
+		__u32 to = rta_getattr_u32(tb[FRA_FLOW]);
+		__u32 from = to>>16;
+		to &= 0xFFFF;
+		if (from) {
+			fprintf(fp, "realms %s/",
+				rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+		}
+		fprintf(fp, "%s ",
+			rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+	}
+
+	if (r->rtm_type == RTN_NAT) {
+		if (tb[RTA_GATEWAY]) {
+			fprintf(fp, "map-to %s ",
+				format_host(r->rtm_family,
+					    RTA_PAYLOAD(tb[RTA_GATEWAY]),
+					    RTA_DATA(tb[RTA_GATEWAY]),
+					    abuf, sizeof(abuf)));
+		} else
+			fprintf(fp, "masquerade");
+	} else if (r->rtm_type == FR_ACT_GOTO) {
+		fprintf(fp, "goto ");
+		if (tb[FRA_GOTO])
+			fprintf(fp, "%u", rta_getattr_u32(tb[FRA_GOTO]));
+		else
+			fprintf(fp, "none");
+		if (r->rtm_flags & FIB_RULE_UNRESOLVED)
+			fprintf(fp, " [unresolved]");
+	} else if (r->rtm_type == FR_ACT_NOP)
+		fprintf(fp, "nop");
+	else if (r->rtm_type != RTN_UNICAST)
+		fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+static int iprule_list(int argc, char **argv)
+{
+	int af = preferred_family;
+
+	if (af == AF_UNSPEC)
+		af = AF_INET;
+
+	if (argc > 0) {
+		fprintf(stderr, "\"ip rule show\" does not take any arguments.\n");
+		return -1;
+	}
+
+	if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
+		perror("Cannot send dump request");
+		return 1;
+	}
+
+	if (rtnl_dump_filter(&rth, print_rule, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int iprule_modify(int cmd, int argc, char **argv)
+{
+	int table_ok = 0;
+	struct {
+		struct nlmsghdr 	n;
+		struct rtmsg 		r;
+		char   			buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_type = cmd;
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.r.rtm_family = preferred_family;
+	req.r.rtm_protocol = RTPROT_BOOT;
+	req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+	req.r.rtm_table = 0;
+	req.r.rtm_type = RTN_UNSPEC;
+	req.r.rtm_flags = 0;
+
+	if (cmd == RTM_NEWRULE) {
+		req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+		req.r.rtm_type = RTN_UNICAST;
+	}
+
+	while (argc > 0) {
+		if (strcmp(*argv, "not") == 0) {
+			req.r.rtm_flags |= FIB_RULE_INVERT;
+		} else if (strcmp(*argv, "from") == 0) {
+			inet_prefix dst;
+			NEXT_ARG();
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			req.r.rtm_src_len = dst.bitlen;
+			addattr_l(&req.n, sizeof(req), FRA_SRC, &dst.data, dst.bytelen);
+		} else if (strcmp(*argv, "to") == 0) {
+			inet_prefix dst;
+			NEXT_ARG();
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			req.r.rtm_dst_len = dst.bitlen;
+			addattr_l(&req.n, sizeof(req), FRA_DST, &dst.data, dst.bytelen);
+		} else if (matches(*argv, "preference") == 0 ||
+			   matches(*argv, "order") == 0 ||
+			   matches(*argv, "priority") == 0) {
+			__u32 pref;
+			NEXT_ARG();
+			if (get_u32(&pref, *argv, 0))
+				invarg("preference value is invalid\n", *argv);
+			addattr32(&req.n, sizeof(req), FRA_PRIORITY, pref);
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 tos;
+			NEXT_ARG();
+			if (rtnl_dsfield_a2n(&tos, *argv))
+				invarg("TOS value is invalid\n", *argv);
+			req.r.rtm_tos = tos;
+		} else if (strcmp(*argv, "fwmark") == 0) {
+			char *slash;
+			__u32 fwmark, fwmask;
+			NEXT_ARG();
+			if ((slash = strchr(*argv, '/')) != NULL)
+				*slash = '\0';
+			if (get_u32(&fwmark, *argv, 0))
+				invarg("fwmark value is invalid\n", *argv);
+			addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark);
+			if (slash) {
+				if (get_u32(&fwmask, slash+1, 0))
+					invarg("fwmask value is invalid\n", slash+1);
+				addattr32(&req.n, sizeof(req), FRA_FWMASK, fwmask);
+			}
+		} else if (matches(*argv, "realms") == 0) {
+			__u32 realm;
+			NEXT_ARG();
+			if (get_rt_realms(&realm, *argv))
+				invarg("invalid realms\n", *argv);
+			addattr32(&req.n, sizeof(req), FRA_FLOW, realm);
+		} else if (matches(*argv, "table") == 0 ||
+			   strcmp(*argv, "lookup") == 0) {
+			__u32 tid;
+			NEXT_ARG();
+			if (rtnl_rttable_a2n(&tid, *argv))
+				invarg("invalid table ID\n", *argv);
+			if (tid < 256)
+				req.r.rtm_table = tid;
+			else {
+				req.r.rtm_table = RT_TABLE_UNSPEC;
+				addattr32(&req.n, sizeof(req), FRA_TABLE, tid);
+			}
+			table_ok = 1;
+		} else if (strcmp(*argv, "dev") == 0 ||
+			   strcmp(*argv, "iif") == 0) {
+			NEXT_ARG();
+			addattr_l(&req.n, sizeof(req), FRA_IFNAME, *argv, strlen(*argv)+1);
+		} else if (strcmp(*argv, "oif") == 0) {
+			NEXT_ARG();
+			addattr_l(&req.n, sizeof(req), FRA_OIFNAME, *argv, strlen(*argv)+1);
+		} else if (strcmp(*argv, "nat") == 0 ||
+			   matches(*argv, "map-to") == 0) {
+			NEXT_ARG();
+			fprintf(stderr, "Warning: route NAT is deprecated\n");
+			addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+			req.r.rtm_type = RTN_NAT;
+		} else {
+			int type;
+
+			if (strcmp(*argv, "type") == 0) {
+				NEXT_ARG();
+			}
+			if (matches(*argv, "help") == 0)
+				usage();
+			else if (matches(*argv, "goto") == 0) {
+				__u32 target;
+				type = FR_ACT_GOTO;
+				NEXT_ARG();
+				if (get_u32(&target, *argv, 0))
+					invarg("invalid target\n", *argv);
+				addattr32(&req.n, sizeof(req), FRA_GOTO, target);
+			} else if (matches(*argv, "nop") == 0)
+				type = FR_ACT_NOP;
+			else if (rtnl_rtntype_a2n(&type, *argv))
+				invarg("Failed to parse rule type", *argv);
+			req.r.rtm_type = type;
+			table_ok = 1;
+		}
+		argc--;
+		argv++;
+	}
+
+	if (req.r.rtm_family == AF_UNSPEC)
+		req.r.rtm_family = AF_INET;
+
+	if (!table_ok && cmd == RTM_NEWRULE)
+		req.r.rtm_table = RT_TABLE_MAIN;
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		return 2;
+
+	return 0;
+}
+
+
+static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	struct rtnl_handle rth2;
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[FRA_MAX+1];
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len);
+
+	if (tb[FRA_PRIORITY]) {
+		n->nlmsg_type = RTM_DELRULE;
+		n->nlmsg_flags = NLM_F_REQUEST;
+
+		if (rtnl_open(&rth2, 0) < 0)
+			return -1;
+
+		if (rtnl_talk(&rth2, n, 0, 0, NULL) < 0)
+			return -2;
+
+		rtnl_close(&rth2);
+	}
+
+	return 0;
+}
+
+static int iprule_flush(int argc, char **argv)
+{
+	int af = preferred_family;
+
+	if (af == AF_UNSPEC)
+		af = AF_INET;
+
+	if (argc > 0) {
+		fprintf(stderr, "\"ip rule flush\" does not allow arguments\n");
+		return -1;
+	}
+
+	if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
+		perror("Cannot send dump request");
+		return 1;
+	}
+
+	if (rtnl_dump_filter(&rth, flush_rule, NULL) < 0) {
+		fprintf(stderr, "Flush terminated\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+int do_iprule(int argc, char **argv)
+{
+	if (argc < 1) {
+		return iprule_list(0, NULL);
+	} else if (matches(argv[0], "list") == 0 ||
+		   matches(argv[0], "lst") == 0 ||
+		   matches(argv[0], "show") == 0) {
+		return iprule_list(argc-1, argv+1);
+	} else if (matches(argv[0], "add") == 0) {
+		return iprule_modify(RTM_NEWRULE, argc-1, argv+1);
+	} else if (matches(argv[0], "delete") == 0) {
+		return iprule_modify(RTM_DELRULE, argc-1, argv+1);
+	} else if (matches(argv[0], "flush") == 0) {
+		return iprule_flush(argc-1, argv+1);
+	} else if (matches(argv[0], "help") == 0)
+		usage();
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv);
+	exit(-1);
+}
+
+int do_multirule(int argc, char **argv)
+{
+	switch (preferred_family) {
+	case AF_UNSPEC:
+	case AF_INET:
+		preferred_family = RTNL_FAMILY_IPMR;
+		break;
+	case AF_INET6:
+		preferred_family = RTNL_FAMILY_IP6MR;
+		break;
+	case RTNL_FAMILY_IPMR:
+	case RTNL_FAMILY_IP6MR:
+		break;
+	default:
+		fprintf(stderr, "Multicast rules are only supported for IPv4/IPv6, was: %i\n",
+			preferred_family);
+		exit(-1);
+	}
+
+	return do_iprule(argc, argv);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iptunnel.c b/ap/app/iproute2/iproute2-3.4.0/ip/iptunnel.c
new file mode 100755
index 0000000..3d41a27
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iptunnel.c
@@ -0,0 +1,640 @@
+/*
+ * 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>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ]\n");
+	fprintf(stderr, "          [ mode { ipip | gre | sit | isatap } ] [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+	fprintf(stderr, "          [ prl-default ADDR ] [ prl-nodefault ADDR ] [ prl-delete ADDR ]\n");
+	fprintf(stderr, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
+	exit(-1);
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
+{
+	int count = 0;
+	char medium[IFNAMSIZ];
+	int isatap = 0;
+
+	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 if (strcmp(*argv, "isatap") == 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;
+				isatap++;
+			} 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->i_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 ||
+			   strcmp(*argv, "hoplimit") == 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 ||
+			   strcmp(*argv, "tclass") == 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();
+			} else if (matches(*argv, "help") == 0)
+				usage();
+			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 (tnl_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;
+		else if (memcmp(p->name, "isatap", 6) == 0) {
+			p->iph.protocol = IPPROTO_IPV6;
+			isatap++;
+		}
+	}
+
+	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 = if_nametoindex(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;
+	}
+	if (isatap)
+		p->i_flags |= SIT_ISATAP;
+
+	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 tnl_add_ioctl(cmd, "tunl0", p.name, &p);
+	case IPPROTO_GRE:
+		return tnl_add_ioctl(cmd, "gre0", p.name, &p);
+	case IPPROTO_IPV6:
+		return tnl_add_ioctl(cmd, "sit0", p.name, &p);
+	default:
+		fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n");
+		return -1;
+	}
+	return -1;
+}
+
+static 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 tnl_del_ioctl("tunl0", p.name, &p);
+	case IPPROTO_GRE:
+		return tnl_del_ioctl("gre0", p.name, &p);
+	case IPPROTO_IPV6:
+		return tnl_del_ioctl("sit0", p.name, &p);
+	default:
+		return tnl_del_ioctl(p.name, p.name, &p);
+	}
+	return -1;
+}
+
+static void print_tunnel(struct ip_tunnel_parm *p)
+{
+	struct ip_tunnel_6rd ip6rd;
+	char s1[1024];
+	char s2[1024];
+
+	memset(&ip6rd, 0, sizeof(ip6rd));
+
+	/* Do not use format_host() for local addr,
+	 * symbolic name will not be useful.
+	 */
+	printf("%s: %s/ip  remote %s  local %s ",
+	       p->name,
+	       tnl_strproto(p->iph.protocol),
+	       p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1))  : "any",
+	       p->iph.saddr ? rt_addr_n2a(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any");
+
+	if (p->i_flags & SIT_ISATAP) {
+		struct ip_tunnel_prl prl[16];
+		int i;
+		
+		memset(prl, 0, sizeof(prl));
+		prl[0].datalen = sizeof(prl) - sizeof(prl[0]);
+		prl[0].addr = htonl(INADDR_ANY);
+	
+		if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl))
+			for (i = 1; i < sizeof(prl) / sizeof(prl[0]); i++)
+		{
+			if (prl[i].addr != htonl(INADDR_ANY)) {
+				printf(" %s %s ", 
+					(prl[i].flags & PRL_DEFAULT) ? "pdr" : "pr",
+					format_host(AF_INET, 4, &prl[i].addr, s1, sizeof(s1)));
+			}
+		}
+	}
+
+	if (p->link) {
+		const char *n = ll_index_to_name(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->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) {
+		printf(" 6rd-prefix %s/%u ",
+		       inet_ntop(AF_INET6, &ip6rd.prefix, s1, sizeof(s1)),
+		       ip6rd.prefixlen);
+		if (ip6rd.relay_prefix) {
+			printf("6rd-relay_prefix %s/%u ",
+			       format_host(AF_INET, 4, &ip6rd.relay_prefix, s1, sizeof(s1)),
+			       ip6rd.relay_prefixlen);
+		}
+	}
+
+	if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
+		printf(" key %u", ntohl(p->i_key));
+	else if ((p->i_flags|p->o_flags)&GRE_KEY) {
+		if (p->i_flags&GRE_KEY)
+			printf(" ikey %u ", ntohl(p->i_key));
+		if (p->o_flags&GRE_KEY)
+			printf(" okey %u ", ntohl(p->o_key));
+	}
+
+	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;
+	struct ip_tunnel_parm p1;
+
+	char buf[512];
+	FILE *fp = fopen("/proc/net/dev", "r");
+	if (fp == NULL) {
+		perror("fopen");
+		return -1;
+	}
+
+	/* skip header lines */
+	if (!fgets(buf, sizeof(buf), fp) ||
+	    !fgets(buf, sizeof(buf), fp)) {
+		fprintf(stderr, "/proc/net/dev read error\n");
+		fclose(fp);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		int index, type;
+		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");
+			fclose(fp);
+			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;
+		index = ll_name_to_index(name);
+		if (index == 0)
+			continue;
+		type = ll_index_to_type(index);
+		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 (tnl_get_ioctl(name, &p1))
+			continue;
+		if ((p->link && p1.link != p->link) ||
+		    (p->name[0] && strcmp(p1.name, p->name)) ||
+		    (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+		    (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+		    (p->i_key && p1.i_key != p->i_key))
+			continue;
+		print_tunnel(&p1);
+		if (show_stats) {
+			printf("%s", _SL_);
+			printf("RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
+			printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s",
+			       rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_);
+			printf("TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs%s", _SL_);
+			printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld",
+			       tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
+		}
+		printf("\n");
+	}
+	fclose(fp);
+	return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+	int err;
+	struct ip_tunnel_parm p;
+
+	ll_init_map(&rth);
+	if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
+		return -1;
+
+	switch (p.iph.protocol) {
+	case IPPROTO_IPIP:
+		err = tnl_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+		break;
+	case IPPROTO_GRE:
+		err = tnl_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+		break;
+	case IPPROTO_IPV6:
+		err = tnl_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;
+}
+
+static int do_prl(int argc, char **argv)
+{
+	struct ip_tunnel_prl p;
+	int count = 0;
+	int devname = 0;
+	int cmd = 0;
+	char medium[IFNAMSIZ];
+
+	memset(&p, 0, sizeof(p));
+	memset(&medium, 0, sizeof(medium));
+
+	while (argc > 0) {
+		if (strcmp(*argv, "prl-default") == 0) {
+			NEXT_ARG();
+			cmd = SIOCADDPRL;
+			p.addr = get_addr32(*argv);
+			p.flags |= PRL_DEFAULT;
+			count++;
+		} else if (strcmp(*argv, "prl-nodefault") == 0) {
+			NEXT_ARG();
+			cmd = SIOCADDPRL;
+			p.addr = get_addr32(*argv);
+			count++;
+		} else if (strcmp(*argv, "prl-delete") == 0) {
+			NEXT_ARG();
+			cmd = SIOCDELPRL;
+			p.addr = get_addr32(*argv);
+			count++;
+		} else if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			strncpy(medium, *argv, IFNAMSIZ-1);
+			devname++;
+		} else {
+			fprintf(stderr,"%s: Invalid PRL parameter.\n", *argv);
+			exit(-1);
+		}
+		if (count > 1) {
+			fprintf(stderr,"One PRL entry at a time.\n");
+			exit(-1);
+		}
+		argc--; argv++;
+	}
+	if (devname == 0) {
+		fprintf(stderr, "Must specify dev.\n");
+		exit(-1);
+	}
+
+	return tnl_prl_ioctl(cmd, medium, &p);
+}
+
+static int do_6rd(int argc, char **argv)
+{
+	struct ip_tunnel_6rd ip6rd;
+	int devname = 0;
+	int cmd = 0;
+	char medium[IFNAMSIZ];
+	inet_prefix prefix;
+
+	memset(&ip6rd, 0, sizeof(ip6rd));
+	memset(&medium, 0, sizeof(medium));
+
+	while (argc > 0) {
+		if (strcmp(*argv, "6rd-prefix") == 0) {
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET6))
+				invarg("invalid 6rd_prefix\n", *argv);
+			cmd = SIOCADD6RD;
+			memcpy(&ip6rd.prefix, prefix.data, 16);
+			ip6rd.prefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET))
+				invarg("invalid 6rd-relay_prefix\n", *argv);
+			cmd = SIOCADD6RD;
+			memcpy(&ip6rd.relay_prefix, prefix.data, 4);
+			ip6rd.relay_prefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-reset") == 0) {
+			cmd = SIOCDEL6RD;
+		} else if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			strncpy(medium, *argv, IFNAMSIZ-1);
+			devname++;
+		} else {
+			fprintf(stderr,"%s: Invalid 6RD parameter.\n", *argv);
+			exit(-1);
+		}
+		argc--; argv++;
+	}
+	if (devname == 0) {
+		fprintf(stderr, "Must specify dev.\n");
+		exit(-1);
+	}
+
+	return tnl_6rd_ioctl(cmd, medium, &ip6rd);
+}
+
+int do_iptunnel(int argc, char **argv)
+{
+	switch (preferred_family) {
+	case AF_UNSPEC:
+		preferred_family = AF_INET;
+		break;
+	case AF_INET:
+		break;
+	/*
+	 * This is silly enough but we have no easy way to make it
+	 * protocol-independent because of unarranged structure between
+	 * IPv4 and IPv6.
+	 */
+	case AF_INET6:
+		return do_ip6tunnel(argc, argv);
+	default:
+		fprintf(stderr, "Unsupported family:%d\n", preferred_family);
+		exit(-1);
+	}
+
+	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);
+		if (matches(*argv, "prl") == 0)
+			return do_prl(argc-1, argv+1);
+		if (matches(*argv, "6rd") == 0)
+			return do_6rd(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return do_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/iptuntap.c b/ap/app/iproute2/iproute2-3.4.0/ip/iptuntap.c
new file mode 100755
index 0000000..29f2777
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/iptuntap.c
@@ -0,0 +1,324 @@
+/*
+ * iptunnel.c	       "ip tuntap"
+ *
+ *		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:	David Woodhouse <David.Woodhouse@intel.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#define TUNDEV "/dev/net/tun"
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip tuntap { add | del } [ dev PHYS_DEV ] \n");
+	fprintf(stderr, "          [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n");
+	fprintf(stderr, "          [ one_queue ] [ pi ] [ vnet_hdr ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: USER  := { STRING | NUMBER }\n");
+	fprintf(stderr, "       GROUP := { STRING | NUMBER }\n");
+	exit(-1);
+}
+
+static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid)
+{
+	int fd;
+	int ret = -1;
+
+#ifdef IFF_TUN_EXCL
+	ifr->ifr_flags |= IFF_TUN_EXCL;
+#endif
+
+	fd = open(TUNDEV, O_RDWR);
+	if (fd < 0) {
+		perror("open");
+		return -1;
+	}
+	if (ioctl(fd, TUNSETIFF, ifr)) {
+		perror("ioctl(TUNSETIFF)");
+		goto out;
+	}
+	if (uid != -1 && ioctl(fd, TUNSETOWNER, uid)) {
+		perror("ioctl(TUNSETOWNER)");
+		goto out;
+	}
+	if (gid != -1 && ioctl(fd, TUNSETGROUP, gid)) {
+		perror("ioctl(TUNSETGROUP)");
+		goto out;
+	}
+	if (ioctl(fd, TUNSETPERSIST, 1)) {
+		perror("ioctl(TUNSETPERSIST)");
+		goto out;
+	}
+	ret = 0;
+ out:
+	close(fd);
+	return ret;
+}
+
+static int tap_del_ioctl(struct ifreq *ifr)
+{
+	int fd = open(TUNDEV, O_RDWR);
+	int ret = -1;
+
+	if (fd < 0) {
+		perror("open");
+		return -1;
+	}
+	if (ioctl(fd, TUNSETIFF, ifr)) {
+		perror("ioctl(TUNSETIFF)");
+		goto out;
+	}
+	if (ioctl(fd, TUNSETPERSIST, 0)) {
+		perror("ioctl(TUNSETPERSIST)");
+		goto out;
+	}
+	ret = 0;
+ out:
+	close(fd);
+	return ret;
+
+}
+static int parse_args(int argc, char **argv, struct ifreq *ifr, uid_t *uid, gid_t *gid)
+{
+	int count = 0;
+
+	memset(ifr, 0, sizeof(*ifr));
+
+	ifr->ifr_flags |= IFF_NO_PI;
+
+	while (argc > 0) {
+		if (matches(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (matches(*argv, "tun") == 0) {
+				if (ifr->ifr_flags & IFF_TAP) {
+					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+					exit(-1);
+				}
+				ifr->ifr_flags |= IFF_TUN;
+			} else if (matches(*argv, "tap") == 0) {
+				if (ifr->ifr_flags & IFF_TUN) {
+					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+					exit(-1);
+				}
+				ifr->ifr_flags |= IFF_TAP;
+			} else {
+				fprintf(stderr,"Cannot guess tunnel mode.\n");
+				exit(-1);
+			}
+		} else if (uid && matches(*argv, "user") == 0) {
+			char *end;
+			unsigned long user;
+
+			NEXT_ARG();
+			if (**argv && ((user = strtol(*argv, &end, 10)), !*end))
+				*uid = user;
+			else {
+				struct passwd *pw = getpwnam(*argv);
+				if (!pw) {
+					fprintf(stderr, "invalid user \"%s\"\n", *argv);
+					exit(-1);
+				}
+				*uid = pw->pw_uid;
+			}
+		} else if (gid && matches(*argv, "group") == 0) {
+			char *end;
+			unsigned long group;
+
+			NEXT_ARG();
+
+			if (**argv && ((group = strtol(*argv, &end, 10)), !*end))
+				*gid = group;
+			else {
+				struct group *gr = getgrnam(*argv);
+				if (!gr) {
+					fprintf(stderr, "invalid group \"%s\"\n", *argv);
+					exit(-1);
+				}
+				*gid = gr->gr_gid;
+			}
+		} else if (matches(*argv, "pi") == 0) {
+			ifr->ifr_flags &= ~IFF_NO_PI;
+		} else if (matches(*argv, "one_queue") == 0) {
+			ifr->ifr_flags |= IFF_ONE_QUEUE;
+		} else if (matches(*argv, "vnet_hdr") == 0) {
+			ifr->ifr_flags |= IFF_VNET_HDR;
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			strncpy(ifr->ifr_name, *argv, IFNAMSIZ-1);
+		} else {
+			if (matches(*argv, "name") == 0) {
+				NEXT_ARG();
+			} else if (matches(*argv, "help") == 0)
+				usage();
+			if (ifr->ifr_name[0])
+				duparg2("name", *argv);
+			strncpy(ifr->ifr_name, *argv, IFNAMSIZ);
+		}
+		count++;
+		argc--; argv++;
+	}
+
+	return 0;
+}
+
+
+static int do_add(int argc, char **argv)
+{
+	struct ifreq ifr;
+	uid_t uid = -1;
+	gid_t gid = -1;
+
+	if (parse_args(argc, argv, &ifr, &uid, &gid) < 0)
+		return -1;
+
+	if (!(ifr.ifr_flags & TUN_TYPE_MASK)) {
+		fprintf(stderr, "You failed to specify a tunnel mode\n");
+		return -1;
+	}
+	return tap_add_ioctl(&ifr, uid, gid);
+}
+
+static int do_del(int argc, char **argv)
+{
+	struct ifreq ifr;
+
+	if (parse_args(argc, argv, &ifr, NULL, NULL) < 0)
+		return -1;
+
+	return tap_del_ioctl(&ifr);
+}
+
+static int read_prop(char *dev, char *prop, long *value)
+{
+	char fname[IFNAMSIZ+25], buf[80], *endp;
+	ssize_t len;
+	int fd;
+	long result;
+
+	sprintf(fname, "/sys/class/net/%s/%s", dev, prop);
+	fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		if (strcmp(prop, "tun_flags"))
+			fprintf(stderr, "open %s: %s\n", fname,
+				strerror(errno));
+		return -1;
+	}
+	len = read(fd, buf, sizeof(buf)-1);
+	close(fd);
+	if (len < 0) {
+		fprintf(stderr, "read %s: %s", fname, strerror(errno));
+		return -1;
+	}
+
+	buf[len] = 0;
+	result = strtol(buf, &endp, 0);
+	if (*endp != '\n') {
+		fprintf(stderr, "Failed to parse %s\n", fname);
+		return -1;
+	}
+	*value = result;
+	return 0;
+}
+
+static void print_flags(long flags)
+{
+	if (flags & IFF_TUN)
+		printf(" tun");
+
+	if (flags & IFF_TAP)
+		printf(" tap");
+
+	if (!(flags & IFF_NO_PI))
+		printf(" pi");
+
+	if (flags & IFF_ONE_QUEUE)
+		printf(" one_queue");
+
+	if (flags & IFF_VNET_HDR)
+		printf(" vnet_hdr");
+
+	flags &= ~(IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE|IFF_VNET_HDR);
+	if (flags)
+		printf(" UNKNOWN_FLAGS:%lx", flags);
+}
+
+static int do_show(int argc, char **argv)
+{
+	DIR *dir;
+	struct dirent *d;
+	long flags, owner = -1, group = -1;
+
+	dir = opendir("/sys/class/net");
+	if (!dir) {
+		perror("opendir");
+		return -1;
+	}
+	while ((d = readdir(dir))) {
+		if (d->d_name[0] == '.' &&
+		    (d->d_name[1] == 0 || d->d_name[1] == '.'))
+			continue;
+
+		if (read_prop(d->d_name, "tun_flags", &flags))
+			continue;
+
+		read_prop(d->d_name, "owner", &owner);
+		read_prop(d->d_name, "group", &group);
+
+		printf("%s:", d->d_name);
+		print_flags(flags);
+		if (owner != -1)
+			printf(" user %ld", owner);
+		if (group != -1)
+			printf(" group %ld", group);
+		printf("\n");
+	}
+	closedir(dir);
+	return 0;
+}
+
+int do_iptuntap(int argc, char **argv)
+{
+	if (argc > 0) {
+		if (matches(*argv, "add") == 0)
+			return do_add(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);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return do_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip tuntap help\".\n",
+		*argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/ipxfrm.c b/ap/app/iproute2/iproute2-3.4.0/ip/ipxfrm.c
new file mode 100755
index 0000000..c7b3420
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/ipxfrm.c
@@ -0,0 +1,1459 @@
+/* $USAGI: $ */
+
+/*
+ * Copyright (C)2004 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * based on ip.c, iproute.c
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <netdb.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/xfrm.h>
+
+#include "utils.h"
+#include "xfrm.h"
+
+#define STRBUF_SIZE	(128)
+#define STRBUF_CAT(buf, str) \
+	do { \
+		int rest = sizeof(buf) - 1 - strlen(buf); \
+		if (rest > 0) { \
+			int len = strlen(str); \
+			if (len > rest) \
+				len = rest; \
+			strncat(buf, str, len); \
+			buf[sizeof(buf) - 1] = '\0'; \
+		} \
+	} while(0);
+
+struct xfrm_filter filter;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"Usage: ip xfrm XFRM-OBJECT { COMMAND | help }\n"
+		"where  XFRM-OBJECT := state | policy | monitor\n");
+	exit(-1);
+}
+
+/* This is based on utils.c(inet_addr_match) */
+int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits)
+{
+	__u32 *a1 = (__u32 *)x1;
+	__u32 *a2 = (__u32 *)x2;
+	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 xfrm_xfrmproto_is_ipsec(__u8 proto)
+{
+	return (proto ==  IPPROTO_ESP ||
+		proto ==  IPPROTO_AH  ||
+		proto ==  IPPROTO_COMP);
+}
+
+int xfrm_xfrmproto_is_ro(__u8 proto)
+{
+	return (proto ==  IPPROTO_ROUTING ||
+		proto ==  IPPROTO_DSTOPTS);
+}
+
+struct typeent {
+	const char *t_name;
+	int t_type;
+};
+
+static const struct typeent xfrmproto_types[]= {
+	{ "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP },
+	{ "route2", IPPROTO_ROUTING }, { "hao", IPPROTO_DSTOPTS },
+	{ "ipsec-any", IPSEC_PROTO_ANY },
+	{ NULL, -1 }
+};
+
+int xfrm_xfrmproto_getbyname(char *name)
+{
+	int i;
+
+	for (i = 0; ; i++) {
+		const struct typeent *t = &xfrmproto_types[i];
+		if (!t->t_name || t->t_type == -1)
+			break;
+
+		if (strcmp(t->t_name, name) == 0)
+			return t->t_type;
+	}
+
+	return -1;
+}
+
+const char *strxf_xfrmproto(__u8 proto)
+{
+	static char str[16];
+	int i;
+
+	for (i = 0; ; i++) {
+		const struct typeent *t = &xfrmproto_types[i];
+		if (!t->t_name || t->t_type == -1)
+			break;
+
+		if (t->t_type == proto)
+			return t->t_name;
+	}
+
+	sprintf(str, "%u", proto);
+	return str;
+}
+
+static const struct typeent algo_types[]= {
+	{ "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH },
+	{ "comp", XFRMA_ALG_COMP }, { "aead", XFRMA_ALG_AEAD },
+	{ "auth-trunc", XFRMA_ALG_AUTH_TRUNC },
+	{ NULL, -1 }
+};
+
+int xfrm_algotype_getbyname(char *name)
+{
+	int i;
+
+	for (i = 0; ; i++) {
+		const struct typeent *t = &algo_types[i];
+		if (!t->t_name || t->t_type == -1)
+			break;
+
+		if (strcmp(t->t_name, name) == 0)
+			return t->t_type;
+	}
+
+	return -1;
+}
+
+const char *strxf_algotype(int type)
+{
+	static char str[32];
+	int i;
+
+	for (i = 0; ; i++) {
+		const struct typeent *t = &algo_types[i];
+		if (!t->t_name || t->t_type == -1)
+			break;
+
+		if (t->t_type == type)
+			return t->t_name;
+	}
+
+	sprintf(str, "%d", type);
+	return str;
+}
+
+const char *strxf_mask8(__u8 mask)
+{
+	static char str[16];
+	const int sn = sizeof(mask) * 8 - 1;
+	__u8 b;
+	int i = 0;
+
+	for (b = (1 << sn); b > 0; b >>= 1)
+		str[i++] = ((b & mask) ? '1' : '0');
+	str[i] = '\0';
+
+	return str;
+}
+
+const char *strxf_mask32(__u32 mask)
+{
+	static char str[16];
+
+	sprintf(str, "%.8x", mask);
+
+	return str;
+}
+
+const char *strxf_share(__u8 share)
+{
+	static char str[32];
+
+	switch (share) {
+	case XFRM_SHARE_ANY:
+		strcpy(str, "any");
+		break;
+	case XFRM_SHARE_SESSION:
+		strcpy(str, "session");
+		break;
+	case XFRM_SHARE_USER:
+		strcpy(str, "user");
+		break;
+	case XFRM_SHARE_UNIQUE:
+		strcpy(str, "unique");
+		break;
+	default:
+		sprintf(str, "%u", share);
+		break;
+	}
+
+	return str;
+}
+
+const char *strxf_proto(__u8 proto)
+{
+	static char buf[32];
+	struct protoent *pp;
+	const char *p;
+
+	pp = getprotobynumber(proto);
+	if (pp)
+		p = pp->p_name;
+	else {
+		sprintf(buf, "%u", proto);
+		p = buf;
+	}
+
+	return p;
+}
+
+const char *strxf_ptype(__u8 ptype)
+{
+	static char str[16];
+
+	switch (ptype) {
+	case XFRM_POLICY_TYPE_MAIN:
+		strcpy(str, "main");
+		break;
+	case XFRM_POLICY_TYPE_SUB:
+		strcpy(str, "sub");
+		break;
+	default:
+		sprintf(str, "%u", ptype);
+		break;
+	}
+
+	return str;
+}
+
+void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id,
+			__u8 mode, __u32 reqid, __u16 family, int force_spi,
+			FILE *fp, const char *prefix, const char *title)
+{
+	char abuf[256];
+
+	if (title)
+		fputs(title, fp);
+
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr),
+					   saddr, abuf, sizeof(abuf)));
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr),
+					  &id->daddr, abuf, sizeof(abuf)));
+	fprintf(fp, "%s", _SL_);
+
+	if (prefix)
+		fputs(prefix, fp);
+	fprintf(fp, "\t");
+
+	fprintf(fp, "proto %s ", strxf_xfrmproto(id->proto));
+
+	if (show_stats > 0 || force_spi || id->spi) {
+		__u32 spi = ntohl(id->spi);
+		fprintf(fp, "spi 0x%08x", spi);
+		if (show_stats > 0)
+			fprintf(fp, "(%u)", spi);
+		fprintf(fp, " ");
+	}
+
+	fprintf(fp, "reqid %u", reqid);
+	if (show_stats > 0)
+		fprintf(fp, "(0x%08x)", reqid);
+	fprintf(fp, " ");
+
+	fprintf(fp, "mode ");
+	switch (mode) {
+	case XFRM_MODE_TRANSPORT:
+		fprintf(fp, "transport");
+		break;
+	case XFRM_MODE_TUNNEL:
+		fprintf(fp, "tunnel");
+		break;
+	case XFRM_MODE_ROUTEOPTIMIZATION:
+		fprintf(fp, "ro");
+		break;
+	case XFRM_MODE_IN_TRIGGER:
+		fprintf(fp, "in_trigger");
+		break;
+	case XFRM_MODE_BEET:
+		fprintf(fp, "beet");
+		break;
+	default:
+		fprintf(fp, "%u", mode);
+		break;
+	}
+	fprintf(fp, "%s", _SL_);
+}
+
+static const char *strxf_limit(__u64 limit)
+{
+	static char str[32];
+	if (limit == XFRM_INF)
+		strcpy(str, "(INF)");
+	else
+		sprintf(str, "%llu", (unsigned long long) limit);
+
+	return str;
+}
+
+void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix)
+{
+	if (prefix)
+		fputs(prefix, fp);
+	fprintf(fp, "stats:%s", _SL_);
+
+	if (prefix)
+		fputs(prefix, fp);
+	fprintf(fp, "  replay-window %u replay %u failed %u%s", 
+		s->replay_window, s->replay, s->integrity_failed, _SL_);
+}
+
+static const char *strxf_time(__u64 time)
+{
+	static char str[32];
+
+	if (time == 0)
+		strcpy(str, "-");
+	else {
+		time_t t;
+		struct tm *tp;
+
+		/* XXX: treat time in the same manner of kernel's
+		 * net/xfrm/xfrm_{user,state}.c
+		 */
+		t = (long)time;
+		tp = localtime(&t);
+
+		strftime(str, sizeof(str), "%Y-%m-%d %T", tp);
+	}
+
+	return str;
+}
+
+void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg,
+			 struct xfrm_lifetime_cur *cur,
+			 FILE *fp, const char *prefix)
+{
+	if (cfg) {
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "lifetime config:%s",_SL_);
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "  limit: soft %s(bytes),",
+			strxf_limit(cfg->soft_byte_limit));
+		fprintf(fp, " hard %s(bytes)%s",
+			strxf_limit(cfg->hard_byte_limit), _SL_);
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "  limit: soft %s(packets),",
+			strxf_limit(cfg->soft_packet_limit));
+		fprintf(fp, " hard %s(packets)%s",
+			strxf_limit(cfg->hard_packet_limit), _SL_);
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "  expire add: soft %llu(sec), hard %llu(sec)%s", 
+			(unsigned long long) cfg->soft_add_expires_seconds,
+			(unsigned long long) cfg->hard_add_expires_seconds,
+			_SL_);
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "  expire use: soft %llu(sec), hard %llu(sec)%s",
+			(unsigned long long) cfg->soft_use_expires_seconds,
+			(unsigned long long) cfg->hard_use_expires_seconds,
+			_SL_);
+	}
+	if (cur) {
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "lifetime current:%s", _SL_);
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "  %llu(bytes), %llu(packets)%s",
+			(unsigned long long) cur->bytes,
+			(unsigned long long) cur->packets,
+			 _SL_);
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "  add %s ", strxf_time(cur->add_time));
+		fprintf(fp, "use %s%s", strxf_time(cur->use_time), _SL_);
+	}
+}
+
+void xfrm_selector_print(struct xfrm_selector *sel, __u16 family,
+			 FILE *fp, const char *prefix)
+{
+	char abuf[256];
+	__u16 f;
+
+	f = sel->family;
+	if (f == AF_UNSPEC)
+		f = family;
+	if (f == AF_UNSPEC)
+		f = preferred_family;
+
+	if (prefix)
+		fputs(prefix, fp);
+
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "src %s/%u ", rt_addr_n2a(f, sizeof(sel->saddr),
+					      &sel->saddr, abuf, sizeof(abuf)),
+		sel->prefixlen_s);
+
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "dst %s/%u ", rt_addr_n2a(f, sizeof(sel->daddr),
+					      &sel->daddr, abuf, sizeof(abuf)),
+		sel->prefixlen_d);
+
+	if (sel->proto)
+		fprintf(fp, "proto %s ", strxf_proto(sel->proto));
+	switch (sel->proto) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+	case IPPROTO_SCTP:
+	case IPPROTO_DCCP:
+	default: /* XXX */
+		if (sel->sport_mask)
+			fprintf(fp, "sport %u ", ntohs(sel->sport));
+		if (sel->dport_mask)
+			fprintf(fp, "dport %u ", ntohs(sel->dport));
+		break;
+	case IPPROTO_ICMP:
+	case IPPROTO_ICMPV6:
+		/* type/code is stored at sport/dport in selector */
+		if (sel->sport_mask)
+			fprintf(fp, "type %u ", ntohs(sel->sport));
+		if (sel->dport_mask)
+			fprintf(fp, "code %u ", ntohs(sel->dport));
+		break;
+	case IPPROTO_GRE:
+		if (sel->sport_mask || sel->dport_mask)
+			fprintf(fp, "key %u ",
+				(((__u32)ntohs(sel->sport)) << 16) +
+				ntohs(sel->dport));
+		break;
+	case IPPROTO_MH:
+		if (sel->sport_mask)
+			fprintf(fp, "type %u ", ntohs(sel->sport));
+		if (sel->dport_mask) {
+			if (show_stats > 0)
+				fprintf(fp, "(dport) 0x%.4x ", sel->dport);
+		}
+		break;
+	}
+
+	if (sel->ifindex > 0)
+		fprintf(fp, "dev %s ", ll_index_to_name(sel->ifindex));
+
+	if (show_stats > 0)
+		fprintf(fp, "uid %u", sel->user);
+
+	fprintf(fp, "%s", _SL_);
+}
+
+static void __xfrm_algo_print(struct xfrm_algo *algo, int type, int len,
+			      FILE *fp, const char *prefix, int newline)
+{
+	int keylen;
+	int i;
+
+	if (prefix)
+		fputs(prefix, fp);
+
+	fprintf(fp, "%s ", strxf_algotype(type));
+
+	if (len < sizeof(*algo)) {
+		fprintf(fp, "(ERROR truncated)");
+		goto fin;
+	}
+	len -= sizeof(*algo);
+
+	fprintf(fp, "%s ", algo->alg_name);
+
+	keylen = algo->alg_key_len / 8;
+	if (len < keylen) {
+		fprintf(fp, "(ERROR truncated)");
+		goto fin;
+	}
+
+	fprintf(fp, "0x");
+	for (i = 0; i < keylen; i ++)
+		fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]);
+
+	if (show_stats > 0)
+		fprintf(fp, " (%d bits)", algo->alg_key_len);
+
+ fin:
+	if (newline)
+		fprintf(fp, "%s", _SL_);
+}
+
+static inline void xfrm_algo_print(struct xfrm_algo *algo, int type, int len,
+				   FILE *fp, const char *prefix)
+{
+	return __xfrm_algo_print(algo, type, len, fp, prefix, 1);
+}
+
+static void xfrm_aead_print(struct xfrm_algo_aead *algo, int len,
+			    FILE *fp, const char *prefix)
+{
+	struct {
+		struct xfrm_algo algo;
+		char key[algo->alg_key_len / 8];
+	} base;
+
+	memcpy(base.algo.alg_name, algo->alg_name, sizeof(base.algo.alg_name));
+	base.algo.alg_key_len = algo->alg_key_len;
+	memcpy(base.algo.alg_key, algo->alg_key, algo->alg_key_len / 8);
+
+	__xfrm_algo_print(&base.algo, XFRMA_ALG_AEAD, len, fp, prefix, 0);
+
+	fprintf(fp, " %d", algo->alg_icv_len);
+
+	fprintf(fp, "%s", _SL_);
+}
+
+static void xfrm_auth_trunc_print(struct xfrm_algo_auth *algo, int len,
+				  FILE *fp, const char *prefix)
+{
+	struct {
+		struct xfrm_algo algo;
+		char key[algo->alg_key_len / 8];
+	} base;
+
+	memcpy(base.algo.alg_name, algo->alg_name, sizeof(base.algo.alg_name));
+	base.algo.alg_key_len = algo->alg_key_len;
+	memcpy(base.algo.alg_key, algo->alg_key, algo->alg_key_len / 8);
+
+	__xfrm_algo_print(&base.algo, XFRMA_ALG_AUTH_TRUNC, len, fp, prefix, 0);
+
+	fprintf(fp, " %d", algo->alg_trunc_len);
+
+	fprintf(fp, "%s", _SL_);
+}
+
+static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int len,
+			    __u16 family, FILE *fp, const char *prefix)
+{
+	int ntmpls = len / sizeof(struct xfrm_user_tmpl);
+	int i;
+
+	if (ntmpls <= 0) {
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "(ERROR \"tmpl\" truncated)");
+		fprintf(fp, "%s", _SL_);
+		return;
+	}
+
+	for (i = 0; i < ntmpls; i++) {
+		struct xfrm_user_tmpl *tmpl = &tmpls[i];
+
+		if (prefix)
+			fputs(prefix, fp);
+
+		xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode,
+				   tmpl->reqid, tmpl->family, 0, fp, prefix, "tmpl ");
+
+		if (show_stats > 0 || tmpl->optional) {
+			if (prefix)
+				fputs(prefix, fp);
+			fprintf(fp, "\t");
+			switch (tmpl->optional) {
+			case 0:
+				if (show_stats > 0)
+					fprintf(fp, "level required ");
+				break;
+			case 1:
+				fprintf(fp, "level use ");
+				break;
+			default:
+				fprintf(fp, "level %u ", tmpl->optional);
+				break;
+			}
+
+			if (show_stats > 0)
+				fprintf(fp, "share %s ", strxf_share(tmpl->share));
+
+			fprintf(fp, "%s", _SL_);
+		}
+
+		if (show_stats > 0) {
+			if (prefix)
+				fputs(prefix, fp);
+			fprintf(fp, "\t");
+			fprintf(fp, "%s-mask %s ",
+				strxf_algotype(XFRMA_ALG_CRYPT),
+				strxf_mask32(tmpl->ealgos));
+			fprintf(fp, "%s-mask %s ",
+				strxf_algotype(XFRMA_ALG_AUTH),
+				strxf_mask32(tmpl->aalgos));
+			fprintf(fp, "%s-mask %s",
+				strxf_algotype(XFRMA_ALG_COMP),
+				strxf_mask32(tmpl->calgos));
+
+			fprintf(fp, "%s", _SL_);
+		}
+	}
+}
+
+int xfrm_parse_mark(struct xfrm_mark *mark, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	NEXT_ARG();
+	if (get_u32(&mark->v, *argv, 0)) {
+		invarg("Illegal \"mark\" value\n", *argv);
+	}
+	if (argc > 1)
+		NEXT_ARG();
+	else { /* last entry on parse line */
+		mark->m = 0xffffffff;
+		goto done;
+	}
+
+	if (strcmp(*argv, "mask") == 0) {
+		NEXT_ARG();
+		if (get_u32(&mark->m, *argv, 0)) {
+			invarg("Illegal \"mark\" mask\n", *argv);
+		}
+	} else {
+		mark->m = 0xffffffff;
+		PREV_ARG();
+	}
+
+done:
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+void xfrm_xfrma_print(struct rtattr *tb[], __u16 family,
+		      FILE *fp, const char *prefix)
+{
+	if (tb[XFRMA_MARK]) {
+		struct rtattr *rta = tb[XFRMA_MARK];
+		struct xfrm_mark *m = (struct xfrm_mark *) RTA_DATA(rta);
+		fprintf(fp, "\tmark %d/0x%x\n", m->v, m->m);
+	}
+
+	if (tb[XFRMA_ALG_AUTH] && !tb[XFRMA_ALG_AUTH_TRUNC]) {
+		struct rtattr *rta = tb[XFRMA_ALG_AUTH];
+		xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta),
+				XFRMA_ALG_AUTH, RTA_PAYLOAD(rta), fp, prefix);
+	}
+
+	if (tb[XFRMA_ALG_AUTH_TRUNC]) {
+		struct rtattr *rta = tb[XFRMA_ALG_AUTH_TRUNC];
+		xfrm_auth_trunc_print((struct xfrm_algo_auth *) RTA_DATA(rta),
+				      RTA_PAYLOAD(rta), fp, prefix);
+	}
+
+	if (tb[XFRMA_ALG_AEAD]) {
+		struct rtattr *rta = tb[XFRMA_ALG_AEAD];
+		xfrm_aead_print((struct xfrm_algo_aead *)RTA_DATA(rta),
+				RTA_PAYLOAD(rta), fp, prefix);
+	}
+
+	if (tb[XFRMA_ALG_CRYPT]) {
+		struct rtattr *rta = tb[XFRMA_ALG_CRYPT];
+		xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta),
+				XFRMA_ALG_CRYPT, RTA_PAYLOAD(rta), fp, prefix);
+	}
+
+	if (tb[XFRMA_ALG_COMP]) {
+		struct rtattr *rta = tb[XFRMA_ALG_COMP];
+		xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta),
+				XFRMA_ALG_COMP, RTA_PAYLOAD(rta), fp, prefix);
+	}
+
+	if (tb[XFRMA_ENCAP]) {
+		struct xfrm_encap_tmpl *e;
+		char abuf[256];
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "encap ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_ENCAP]) < sizeof(*e)) {
+			fprintf(fp, "(ERROR truncated)");
+			fprintf(fp, "%s", _SL_);
+			return;
+		}
+		e = (struct xfrm_encap_tmpl *) RTA_DATA(tb[XFRMA_ENCAP]);
+
+		fprintf(fp, "type ");
+		switch (e->encap_type) {
+		case 1:
+			fprintf(fp, "espinudp-nonike ");
+			break;
+		case 2:
+			fprintf(fp, "espinudp ");
+			break;
+		default:
+			fprintf(fp, "%u ", e->encap_type);
+			break;
+		}
+		fprintf(fp, "sport %u ", ntohs(e->encap_sport));
+		fprintf(fp, "dport %u ", ntohs(e->encap_dport));
+
+		memset(abuf, '\0', sizeof(abuf));
+		fprintf(fp, "addr %s",
+			rt_addr_n2a(family, sizeof(e->encap_oa),
+				    &e->encap_oa, abuf, sizeof(abuf)));
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (tb[XFRMA_TMPL]) {
+		struct rtattr *rta = tb[XFRMA_TMPL];
+		xfrm_tmpl_print((struct xfrm_user_tmpl *) RTA_DATA(rta),
+				RTA_PAYLOAD(rta), family, fp, prefix);
+	}
+
+	if (tb[XFRMA_COADDR]) {
+		char abuf[256];
+		xfrm_address_t *coa;
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "coa ");
+
+		coa = (xfrm_address_t *)RTA_DATA(tb[XFRMA_COADDR]);
+
+		if (RTA_PAYLOAD(tb[XFRMA_COADDR]) < sizeof(*coa)) {
+			fprintf(fp, "(ERROR truncated)");
+			fprintf(fp, "%s", _SL_);
+			return;
+		}
+
+		memset(abuf, '\0', sizeof(abuf));
+		fprintf(fp, "%s",
+			rt_addr_n2a(family, sizeof(*coa), coa,
+				    abuf, sizeof(abuf)));
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (tb[XFRMA_LASTUSED]) {
+		__u64 lastused;
+
+		if (prefix)
+			fputs(prefix, fp);
+		fprintf(fp, "lastused ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_LASTUSED]) < sizeof(lastused)) {
+			fprintf(fp, "(ERROR truncated)");
+			fprintf(fp, "%s", _SL_);
+			return;
+		}
+
+		lastused = rta_getattr_u64(tb[XFRMA_LASTUSED]);
+
+		fprintf(fp, "%s", strxf_time(lastused));
+		fprintf(fp, "%s", _SL_);
+	}
+
+}
+
+static int xfrm_selector_iszero(struct xfrm_selector *s)
+{
+	struct xfrm_selector s0;
+
+	memset(&s0, 0, sizeof(s0));
+
+	return (memcmp(&s0, s, sizeof(s0)) == 0);
+}
+
+void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo,
+			    struct rtattr *tb[], FILE *fp, const char *prefix,
+			    const char *title)
+{
+	char buf[STRBUF_SIZE];
+	int force_spi = xfrm_xfrmproto_is_ipsec(xsinfo->id.proto);
+
+	memset(buf, '\0', sizeof(buf));
+
+	xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode,
+			   xsinfo->reqid, xsinfo->family, force_spi, fp,
+			   prefix, title);
+
+	if (prefix)
+		STRBUF_CAT(buf, prefix);
+	STRBUF_CAT(buf, "\t");
+
+	fputs(buf, fp);
+	fprintf(fp, "replay-window %u ", xsinfo->replay_window);
+	if (show_stats > 0)
+		fprintf(fp, "seq 0x%08u ", xsinfo->seq);
+	if (show_stats > 0 || xsinfo->flags) {
+		__u8 flags = xsinfo->flags;
+
+		fprintf(fp, "flag ");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOECN, "noecn");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_DECAP_DSCP, "decap-dscp");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOPMTUDISC, "nopmtudisc");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_WILDRECV, "wildrecv");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ICMP, "icmp");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_AF_UNSPEC, "af-unspec");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ALIGN4, "align4");
+		if (flags)
+			fprintf(fp, "%x", flags);
+	}
+	if (show_stats > 0)
+		fprintf(fp, " (0x%s)", strxf_mask8(xsinfo->flags));
+	fprintf(fp, "%s", _SL_);
+
+	xfrm_xfrma_print(tb, xsinfo->family, fp, buf);
+
+	if (!xfrm_selector_iszero(&xsinfo->sel)) {
+		char sbuf[STRBUF_SIZE];
+
+		memcpy(sbuf, buf, sizeof(sbuf));
+		STRBUF_CAT(sbuf, "sel ");
+
+		xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, sbuf);
+	}
+
+	if (show_stats > 0) {
+		xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, buf);
+		xfrm_stats_print(&xsinfo->stats, fp, buf);
+	}
+
+	if (tb[XFRMA_SEC_CTX]) {
+		struct xfrm_user_sec_ctx *sctx;
+
+		fprintf(fp, "\tsecurity context ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx))
+			fprintf(fp, "(ERROR truncated)");
+
+		sctx = (struct xfrm_user_sec_ctx *)RTA_DATA(tb[XFRMA_SEC_CTX]);
+
+		fprintf(fp, "%s %s", (char *)(sctx + 1), _SL_);
+	}
+
+}
+
+void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo,
+			    struct rtattr *tb[], FILE *fp, const char *prefix,
+			    const char *title)
+{
+	char buf[STRBUF_SIZE];
+
+	memset(buf, '\0', sizeof(buf));
+
+	xfrm_selector_print(&xpinfo->sel, preferred_family, fp, title);
+
+	if (tb[XFRMA_SEC_CTX]) {
+		struct xfrm_user_sec_ctx *sctx;
+
+		fprintf(fp, "\tsecurity context ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx))
+			fprintf(fp, "(ERROR truncated)");
+
+		sctx = (struct xfrm_user_sec_ctx *)RTA_DATA(tb[XFRMA_SEC_CTX]);
+
+		fprintf(fp, "%s ", (char *)(sctx + 1));
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (prefix)
+		STRBUF_CAT(buf, prefix);
+	STRBUF_CAT(buf, "\t");
+
+	fputs(buf, fp);
+	if (xpinfo->dir >= XFRM_POLICY_MAX) {
+		xpinfo->dir -= XFRM_POLICY_MAX;
+		fprintf(fp, "socket ");
+	} else
+		fprintf(fp, "dir ");
+
+	switch (xpinfo->dir) {
+	case XFRM_POLICY_IN:
+		fprintf(fp, "in");
+		break;
+	case XFRM_POLICY_OUT:
+		fprintf(fp, "out");
+		break;
+	case XFRM_POLICY_FWD:
+		fprintf(fp, "fwd");
+		break;
+	default:
+		fprintf(fp, "%u", xpinfo->dir);
+		break;
+	}
+	fprintf(fp, " ");
+
+	switch (xpinfo->action) {
+	case XFRM_POLICY_ALLOW:
+		if (show_stats > 0)
+			fprintf(fp, "action allow ");
+		break;
+	case XFRM_POLICY_BLOCK:
+		fprintf(fp, "action block ");
+		break;
+	default:
+		fprintf(fp, "action %u ", xpinfo->action);
+		break;
+	}
+
+	if (show_stats)
+		fprintf(fp, "index %u ", xpinfo->index);
+	fprintf(fp, "priority %u ", xpinfo->priority);
+
+	if (tb[XFRMA_POLICY_TYPE]) {
+		struct xfrm_userpolicy_type *upt;
+
+		fprintf(fp, "ptype ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt))
+			fprintf(fp, "(ERROR truncated)");
+
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		fprintf(fp, "%s ", strxf_ptype(upt->type));
+	}
+
+	if (show_stats > 0)
+		fprintf(fp, "share %s ", strxf_share(xpinfo->share));
+
+	if (show_stats > 0 || xpinfo->flags) {
+		__u8 flags = xpinfo->flags;
+
+		fprintf(fp, "flag ");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_LOCALOK, "localok");
+		XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_ICMP, "icmp");
+		if (flags)
+			fprintf(fp, "%x", flags);
+	}
+	if (show_stats > 0)
+		fprintf(fp, " (0x%s)", strxf_mask8(xpinfo->flags));
+	fprintf(fp, "%s", _SL_);
+
+	if (show_stats > 0)
+		xfrm_lifetime_print(&xpinfo->lft, &xpinfo->curlft, fp, buf);
+
+	xfrm_xfrma_print(tb, xpinfo->sel.family, fp, buf);
+}
+
+int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family,
+		  int loose, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	inet_prefix dst;
+	inet_prefix src;
+
+	memset(&dst, 0, sizeof(dst));
+	memset(&src, 0, sizeof(src));
+
+	while (1) {
+		if (strcmp(*argv, "src") == 0) {
+			NEXT_ARG();
+
+			get_prefix(&src, *argv, preferred_family);
+			if (src.family == AF_UNSPEC)
+				invarg("\"src\" address family is AF_UNSPEC", *argv);
+			if (family)
+				*family = src.family;
+
+			memcpy(saddr, &src.data, sizeof(*saddr));
+
+			filter.id_src_mask = src.bitlen;
+
+		} else if (strcmp(*argv, "dst") == 0) {
+			NEXT_ARG();
+
+			get_prefix(&dst, *argv, preferred_family);
+			if (dst.family == AF_UNSPEC)
+				invarg("\"dst\" address family is AF_UNSPEC", *argv);
+			if (family)
+				*family = dst.family;
+
+			memcpy(&id->daddr, &dst.data, sizeof(id->daddr));
+
+			filter.id_dst_mask = dst.bitlen;
+
+		} else if (strcmp(*argv, "proto") == 0) {
+			int ret;
+
+			NEXT_ARG();
+
+			ret = xfrm_xfrmproto_getbyname(*argv);
+			if (ret < 0)
+				invarg("\"XFRM-PROTO\" is invalid", *argv);
+
+			id->proto = (__u8)ret;
+
+			filter.id_proto_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "spi") == 0) {
+			__u32 spi;
+
+			NEXT_ARG();
+			if (get_u32(&spi, *argv, 0))
+				invarg("\"SPI\" is invalid", *argv);
+
+			spi = htonl(spi);
+			id->spi = spi;
+
+			filter.id_spi_mask = XFRM_FILTER_MASK_FULL;
+
+		} else {
+			PREV_ARG(); /* back track */
+			break;
+		}
+
+		if (!NEXT_ARG_OK())
+			break;
+		NEXT_ARG();
+	}
+
+	if (src.family && dst.family && (src.family != dst.family))
+		invarg("the same address family is required between \"src\" and \"dst\"", *argv);
+
+	if (loose == 0 && id->proto == 0)
+		missarg("XFRM-PROTO");
+	if (argc == *argcp)
+		missarg("ID");
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	if (matches(*argv, "transport") == 0)
+		*mode = XFRM_MODE_TRANSPORT;
+	else if (matches(*argv, "tunnel") == 0)
+		*mode = XFRM_MODE_TUNNEL;
+	else if (matches(*argv, "ro") == 0)
+		*mode = XFRM_MODE_ROUTEOPTIMIZATION;
+	else if (matches(*argv, "in_trigger") == 0)
+		*mode = XFRM_MODE_IN_TRIGGER;
+	else if (matches(*argv, "beet") == 0)
+		*mode = XFRM_MODE_BEET;
+	else
+		invarg("\"MODE\" is invalid", *argv);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	if (strcmp(*argv, "espinudp-nonike") == 0)
+		*type = 1;
+	else if (strcmp(*argv, "espinudp") == 0)
+		*type = 2;
+	else
+		invarg("\"ENCAP-TYPE\" is invalid", *argv);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+/* NOTE: reqid is used by host-byte order */
+int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	if (get_u32(reqid, *argv, 0))
+		invarg("\"REQID\" is invalid", *argv);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int xfrm_selector_upspec_parse(struct xfrm_selector *sel,
+				      int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	char *sportp = NULL;
+	char *dportp = NULL;
+	char *typep = NULL;
+	char *codep = NULL;
+	char *grekey = NULL;
+
+	while (1) {
+		if (strcmp(*argv, "proto") == 0) {
+			__u8 upspec;
+
+			NEXT_ARG();
+
+			if (strcmp(*argv, "any") == 0)
+				upspec = 0;
+			else {
+				struct protoent *pp;
+				pp = getprotobyname(*argv);
+				if (pp)
+					upspec = pp->p_proto;
+				else {
+					if (get_u8(&upspec, *argv, 0))
+						invarg("\"PROTO\" is invalid", *argv);
+				}
+			}
+			sel->proto = upspec;
+
+			filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "sport") == 0) {
+			sportp = *argv;
+
+			NEXT_ARG();
+
+			if (get_u16(&sel->sport, *argv, 0))
+				invarg("\"PORT\" is invalid", *argv);
+			sel->sport = htons(sel->sport);
+			if (sel->sport)
+				sel->sport_mask = ~((__u16)0);
+
+			filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "dport") == 0) {
+			dportp = *argv;
+
+			NEXT_ARG();
+
+			if (get_u16(&sel->dport, *argv, 0))
+				invarg("\"PORT\" is invalid", *argv);
+			sel->dport = htons(sel->dport);
+			if (sel->dport)
+				sel->dport_mask = ~((__u16)0);
+
+			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "type") == 0) {
+			typep = *argv;
+
+			NEXT_ARG();
+
+			if (get_u16(&sel->sport, *argv, 0) ||
+			    (sel->sport & ~((__u16)0xff)))
+				invarg("\"type\" value is invalid", *argv);
+			sel->sport = htons(sel->sport);
+			sel->sport_mask = ~((__u16)0);
+
+			filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL;
+
+
+		} else if (strcmp(*argv, "code") == 0) {
+			codep = *argv;
+
+			NEXT_ARG();
+
+			if (get_u16(&sel->dport, *argv, 0) ||
+			    (sel->dport & ~((__u16)0xff)))
+				invarg("\"code\" value is invalid", *argv);
+			sel->dport = htons(sel->dport);
+			sel->dport_mask = ~((__u16)0);
+
+			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "key") == 0) {
+			unsigned uval;
+
+			grekey = *argv;
+
+			NEXT_ARG();
+
+			if (strchr(*argv, '.'))
+				uval = htonl(get_addr32(*argv));
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value of \"key\"\n");
+					exit(-1);
+				}
+			}
+
+			sel->sport = htons(uval >> 16);
+			sel->dport = htons(uval & 0xffff);
+			sel->sport_mask = ~((__u16)0);
+			sel->dport_mask = ~((__u16)0);
+
+			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;
+
+		} else {
+			PREV_ARG(); /* back track */
+			break;
+		}
+
+		if (!NEXT_ARG_OK())
+			break;
+		NEXT_ARG();
+	}
+	if (argc == *argcp)
+		missarg("UPSPEC");
+	if (sportp || dportp) {
+		switch (sel->proto) {
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
+		case IPPROTO_SCTP:
+		case IPPROTO_DCCP:
+			break;
+		default:
+			fprintf(stderr, "\"sport\" and \"dport\" are invalid with proto=%s\n", strxf_proto(sel->proto));
+			exit(1);
+		}
+	}
+	if (typep || codep) {
+		switch (sel->proto) {
+		case IPPROTO_ICMP:
+		case IPPROTO_ICMPV6:
+		case IPPROTO_MH:
+			break;
+		default:
+			fprintf(stderr, "\"type\" and \"code\" are invalid with proto=%s\n", strxf_proto(sel->proto));
+			exit(1);
+		}
+	}
+	if (grekey) {
+		switch (sel->proto) {
+		case IPPROTO_GRE:
+			break;
+		default:
+			fprintf(stderr, "\"key\" is invalid with proto=%s\n", strxf_proto(sel->proto));
+			exit(1);
+		}
+	}
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	inet_prefix dst;
+	inet_prefix src;
+	char *upspecp = NULL;
+
+	memset(&dst, 0, sizeof(dst));
+	memset(&src, 0, sizeof(src));
+
+	while (1) {
+		if (strcmp(*argv, "src") == 0) {
+			NEXT_ARG();
+
+			get_prefix(&src, *argv, preferred_family);
+			if (src.family == AF_UNSPEC)
+				invarg("\"src\" address family is AF_UNSPEC", *argv);
+			sel->family = src.family;
+
+			memcpy(&sel->saddr, &src.data, sizeof(sel->saddr));
+			sel->prefixlen_s = src.bitlen;
+
+			filter.sel_src_mask = src.bitlen;
+
+		} else if (strcmp(*argv, "dst") == 0) {
+			NEXT_ARG();
+
+			get_prefix(&dst, *argv, preferred_family);
+			if (dst.family == AF_UNSPEC)
+				invarg("\"dst\" address family is AF_UNSPEC", *argv);
+			sel->family = dst.family;
+
+			memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr));
+			sel->prefixlen_d = dst.bitlen;
+
+			filter.sel_dst_mask = dst.bitlen;
+
+		} else if (strcmp(*argv, "dev") == 0) {
+			int ifindex;
+
+			NEXT_ARG();
+
+			if (strcmp(*argv, "none") == 0)
+				ifindex = 0;
+			else {
+				ifindex = ll_name_to_index(*argv);
+				if (ifindex <= 0)
+					invarg("\"DEV\" is invalid", *argv);
+			}
+			sel->ifindex = ifindex;
+
+			filter.sel_dev_mask = XFRM_FILTER_MASK_FULL;
+
+		} else {
+			if (upspecp) {
+				PREV_ARG(); /* back track */
+				break;
+			} else {
+				upspecp = *argv;
+				xfrm_selector_upspec_parse(sel, &argc, &argv);
+			}
+		}
+
+		if (!NEXT_ARG_OK())
+			break;
+
+		NEXT_ARG();
+	}
+
+	if (src.family && dst.family && (src.family != dst.family))
+		invarg("the same address family is required between \"src\" and \"dst\"", *argv);
+
+	if (argc == *argcp)
+		missarg("SELECTOR");
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft,
+			    int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	int ret;
+
+	if (strcmp(*argv, "time-soft") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->soft_add_expires_seconds, *argv, 0);
+		if (ret)
+			invarg("\"time-soft\" value is invalid", *argv);
+	} else if (strcmp(*argv, "time-hard") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->hard_add_expires_seconds, *argv, 0);
+		if (ret)
+			invarg("\"time-hard\" value is invalid", *argv);
+	} else if (strcmp(*argv, "time-use-soft") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->soft_use_expires_seconds, *argv, 0);
+		if (ret)
+			invarg("\"time-use-soft\" value is invalid", *argv);
+	} else if (strcmp(*argv, "time-use-hard") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->hard_use_expires_seconds, *argv, 0);
+		if (ret)
+			invarg("\"time-use-hard\" value is invalid", *argv);
+	} else if (strcmp(*argv, "byte-soft") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->soft_byte_limit, *argv, 0);
+		if (ret)
+			invarg("\"byte-soft\" value is invalid", *argv);
+	} else if (strcmp(*argv, "byte-hard") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->hard_byte_limit, *argv, 0);
+		if (ret)
+			invarg("\"byte-hard\" value is invalid", *argv);
+	} else if (strcmp(*argv, "packet-soft") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->soft_packet_limit, *argv, 0);
+		if (ret)
+			invarg("\"packet-soft\" value is invalid", *argv);
+	} else if (strcmp(*argv, "packet-hard") == 0) {
+		NEXT_ARG();
+		ret = get_u64(&lft->hard_packet_limit, *argv, 0);
+		if (ret)
+			invarg("\"packet-hard\" value is invalid", *argv);
+	} else
+		invarg("\"LIMIT\" is invalid", *argv);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+int do_xfrm(int argc, char **argv)
+{
+	memset(&filter, 0, sizeof(filter));
+
+	if (argc < 1)
+		usage();
+
+	if (matches(*argv, "state") == 0 ||
+	    matches(*argv, "sa") == 0)
+		return do_xfrm_state(argc-1, argv+1);
+	else if (matches(*argv, "policy") == 0)
+		return do_xfrm_policy(argc-1, argv+1);
+	else if (matches(*argv, "monitor") == 0)
+		return do_xfrm_monitor(argc-1, argv+1);
+	else if (matches(*argv, "help") == 0) {
+		usage();
+		fprintf(stderr, "xfrm Object \"%s\" is unknown.\n", *argv);
+		exit(-1);
+	}
+	usage();
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/link_gre.c b/ap/app/iproute2/iproute2-3.4.0/ip/link_gre.c
new file mode 100755
index 0000000..839fb29
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/link_gre.c
@@ -0,0 +1,366 @@
+/*
+ * link_gre.c	gre driver module
+ *
+ *		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:	Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type { gre | gretap } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
+	exit(-1);
+}
+
+static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
+			 struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[1024];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *greinfo[IFLA_GRE_MAX + 1];
+	__u16 iflags = 0;
+	__u16 oflags = 0;
+	unsigned ikey = 0;
+	unsigned okey = 0;
+	unsigned saddr = 0;
+	unsigned daddr = 0;
+	unsigned link = 0;
+	__u8 pmtudisc = 1;
+	__u8 ttl = 0;
+	__u8 tos = 0;
+	int len;
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (greinfo[IFLA_GRE_IKEY])
+			ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
+
+		if (greinfo[IFLA_GRE_OKEY])
+			okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
+
+		if (greinfo[IFLA_GRE_IFLAGS])
+			iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
+
+		if (greinfo[IFLA_GRE_OFLAGS])
+			oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
+
+		if (greinfo[IFLA_GRE_LOCAL])
+			saddr = rta_getattr_u32(greinfo[IFLA_GRE_LOCAL]);
+
+		if (greinfo[IFLA_GRE_REMOTE])
+			daddr = rta_getattr_u32(greinfo[IFLA_GRE_REMOTE]);
+
+		if (greinfo[IFLA_GRE_PMTUDISC])
+			pmtudisc = rta_getattr_u8(
+				greinfo[IFLA_GRE_PMTUDISC]);
+
+		if (greinfo[IFLA_GRE_TTL])
+			ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
+
+		if (greinfo[IFLA_GRE_TOS])
+			tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]);
+
+		if (greinfo[IFLA_GRE_LINK])
+			link = rta_getattr_u8(greinfo[IFLA_GRE_LINK]);
+	}
+
+	while (argc > 0) {
+		if (!matches(*argv, "key")) {
+			unsigned uval;
+
+			NEXT_ARG();
+			iflags |= GRE_KEY;
+			oflags |= GRE_KEY;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr,
+						"Invalid value for \"key\"\n");
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+
+			ikey = okey = uval;
+		} else if (!matches(*argv, "ikey")) {
+			unsigned uval;
+
+			NEXT_ARG();
+			iflags |= GRE_KEY;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value of \"ikey\"\n");
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			ikey = uval;
+		} else if (!matches(*argv, "okey")) {
+			unsigned uval;
+
+			NEXT_ARG();
+			oflags |= GRE_KEY;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value of \"okey\"\n");
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			okey = uval;
+		} else if (!matches(*argv, "seq")) {
+			iflags |= GRE_SEQ;
+			oflags |= GRE_SEQ;
+		} else if (!matches(*argv, "iseq")) {
+			iflags |= GRE_SEQ;
+		} else if (!matches(*argv, "oseq")) {
+			oflags |= GRE_SEQ;
+		} else if (!matches(*argv, "csum")) {
+			iflags |= GRE_CSUM;
+			oflags |= GRE_CSUM;
+		} else if (!matches(*argv, "icsum")) {
+			iflags |= GRE_CSUM;
+		} else if (!matches(*argv, "ocsum")) {
+			oflags |= GRE_CSUM;
+		} else if (!matches(*argv, "nopmtudisc")) {
+			pmtudisc = 0;
+		} else if (!matches(*argv, "pmtudisc")) {
+			pmtudisc = 1;
+		} else if (!matches(*argv, "remote")) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				daddr = get_addr32(*argv);
+		} else if (!matches(*argv, "local")) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				saddr = get_addr32(*argv);
+		} else if (!matches(*argv, "dev")) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				exit(-1);
+		} else if (!matches(*argv, "ttl") ||
+			   !matches(*argv, "hoplimit")) {
+			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);
+				ttl = uval;
+			}
+		} else if (!matches(*argv, "tos") ||
+			   !matches(*argv, "tclass") ||
+			   !matches(*argv, "dsfield")) {
+			__u32 uval;
+
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (rtnl_dsfield_a2n(&uval, *argv))
+					invarg("bad TOS value", *argv);
+				tos = uval;
+			} else
+				tos = 1;
+		} else 
+			usage();
+		argc--; argv++;
+	}
+
+	if (!ikey && IN_MULTICAST(ntohl(daddr))) {
+		ikey = daddr;
+		iflags |= GRE_KEY;
+	}
+	if (!okey && IN_MULTICAST(ntohl(daddr))) {
+		okey = daddr;
+		oflags |= GRE_KEY;
+	}
+	if (IN_MULTICAST(ntohl(daddr)) && !saddr) {
+		fprintf(stderr, "Broadcast tunnel requires a source address.\n");
+		return -1;
+	}
+
+	addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
+	addattr32(n, 1024, IFLA_GRE_OKEY, okey);
+	addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
+	addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
+	addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4);
+	addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4);
+	addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1);
+	if (link)
+		addattr32(n, 1024, IFLA_GRE_LINK, link);
+	addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
+	addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
+
+	return 0;
+}
+
+static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[1024];
+	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
+	unsigned iflags = 0;
+	unsigned oflags = 0;
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_GRE_REMOTE]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_GRE_REMOTE]);
+
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_GRE_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
+	else
+		fprintf(f, "ttl inherit ");
+
+	if (tb[IFLA_GRE_TOS] && rta_getattr_u8(tb[IFLA_GRE_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
+
+		fputs("tos ", f);
+		if (tos == 1)
+			fputs("inherit ", f);
+		else
+			fprintf(f, "0x%x ", tos);
+	}
+
+	if (tb[IFLA_GRE_PMTUDISC] &&
+	    !rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
+		fputs("nopmtudisc ", f);
+
+	if (tb[IFLA_GRE_IFLAGS])
+		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
+
+	if (tb[IFLA_GRE_OFLAGS])
+		oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
+
+	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
+		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
+		fprintf(f, "ikey %s ", s2);
+	}
+
+	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
+		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
+		fprintf(f, "okey %s ", s2);
+	}
+
+	if (iflags & GRE_SEQ)
+		fputs("iseq ", f);
+	if (oflags & GRE_SEQ)
+		fputs("oseq ", f);
+	if (iflags & GRE_CSUM)
+		fputs("icsum ", f);
+	if (oflags & GRE_CSUM)
+		fputs("ocsum ", f);
+}
+
+struct link_util gre_link_util = {
+	.id = "gre",
+	.maxattr = IFLA_GRE_MAX,
+	.parse_opt = gre_parse_opt,
+	.print_opt = gre_print_opt,
+};
+
+struct link_util gretap_link_util = {
+	.id = "gretap",
+	.maxattr = IFLA_GRE_MAX,
+	.parse_opt = gre_parse_opt,
+	.print_opt = gre_print_opt,
+};
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/link_veth.c b/ap/app/iproute2/iproute2-3.4.0/ip/link_veth.c
new file mode 100755
index 0000000..3d19b01
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/link_veth.c
@@ -0,0 +1,64 @@
+/*
+ * link_veth.c	veth driver module
+ *
+ *		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:	Pavel Emelianov <xemul@openvz.org>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <linux/veth.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+static void usage(void)
+{
+	printf("Usage: ip link <options> type veth "
+	       "[peer <options>]\nTo get <options> type "
+	       "'ip link add help'\n");
+}
+
+static int veth_parse_opt(struct link_util *lu, int argc, char **argv,
+			  struct nlmsghdr *hdr)
+{
+	char *name, *type, *link, *dev;
+	int err, len;
+	struct rtattr * data;
+	int group;
+
+	if (strcmp(argv[0], "peer") != 0) {
+		usage();
+		return -1;
+	}
+
+	data = NLMSG_TAIL(hdr);
+	addattr_l(hdr, 1024, VETH_INFO_PEER, NULL, 0);
+
+	hdr->nlmsg_len += sizeof(struct ifinfomsg);
+
+	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)hdr,
+			   &name, &type, &link, &dev, &group);
+	if (err < 0)
+		return err;
+
+	if (name) {
+		len = strlen(name) + 1;
+		if (len > IFNAMSIZ)
+			invarg("\"name\" too long\n", *argv);
+		addattr_l(hdr, 1024, IFLA_IFNAME, name, len);
+	}
+
+	data->rta_len = (void *)NLMSG_TAIL(hdr) - (void *)data;
+	return argc - 1 - err;
+}
+
+struct link_util veth_link_util = {
+	.id = "veth",
+	.parse_opt = veth_parse_opt,
+};
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/routef b/ap/app/iproute2/iproute2-3.4.0/ip/routef
new file mode 100755
index 0000000..d266e2d
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/routef
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+if [ -z "$*" ] ; then
+	exec ip -4 ro flush  scope global  type unicast
+else
+	echo "Usage: routef"
+	echo
+	echo "This script will flush the IPv4 routing table"
+fi
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/routel b/ap/app/iproute2/iproute2-3.4.0/ip/routel
new file mode 100755
index 0000000..8d1d352
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/routel
@@ -0,0 +1,60 @@
+#!/bin/sh
+#$Id$
+
+#
+# Script created by: Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18
+# Donated to the public domain.
+#
+# This script transforms the output of "ip" into more readable text.
+# "ip" is the Linux-advanced-routing configuration tool part of the
+# iproute package.
+#
+
+test "X-h" = "X$1" && echo "Usage: $0 [tablenr [raw ip args...]]" && exit 64
+
+test -z "$*" && set 0
+
+ip route list table "$@" |
+ while read network rest
+ do set xx $rest
+    shift
+    proto=""
+    via=""
+    dev=""
+    scope=""
+    src=""
+    table=""
+    case $network in
+       broadcast|local|unreachable) via=$network
+          network=$1
+          shift
+          ;;
+    esac
+    while test $# != 0
+    do
+       key=$1
+       val=$2
+       eval "$key=$val"
+       shift 2
+    done
+    echo "$network	$via	$src	$proto	$scope	$dev	$table"
+ done | awk -F '	' '
+BEGIN {
+   format="%15s%-3s %15s %15s %8s %8s%7s %s\n";
+   printf(format,"target","","gateway","source","proto","scope","dev","tbl");
+ }
+ { network=$1;
+   mask="";
+   if(match(network,"/"))
+    { mask=" "substr(network,RSTART+1);
+      network=substr(network,0,RSTART);
+    }
+   via=$2;
+   src=$3;
+   proto=$4;
+   scope=$5;
+   dev=$6;
+   table=$7;
+   printf(format,network,mask,via,src,proto,scope,dev,table);
+ }
+'
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/rtm_map.c b/ap/app/iproute2/iproute2-3.4.0/ip/rtm_map.c
new file mode 100755
index 0000000..21e818b
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/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/ap/app/iproute2/iproute2-3.4.0/ip/rtmon.c b/ap/app/iproute2/iproute2-3.4.0/ip/rtmon.c
new file mode 100755
index 0000000..c1416a0
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/rtmon.c
@@ -0,0 +1,177 @@
+/*
+ * rtmon.c		RTnetlink listener.
+ *
+ *		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 <sys/time.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "SNAPSHOT.h"
+
+#include "utils.h"
+#include "libnetlink.h"
+
+int resolve_hosts = 0;
+static int init_phase = 1;
+
+static void write_stamp(FILE *fp)
+{
+	char buf[128];
+	struct nlmsghdr *n1 = (void*)buf;
+	struct timeval tv;
+
+	n1->nlmsg_type = 15;
+	n1->nlmsg_flags = 0;
+	n1->nlmsg_seq = 0;
+	n1->nlmsg_pid = 0;
+	n1->nlmsg_len = NLMSG_LENGTH(4*2);
+	gettimeofday(&tv, NULL);
+	((__u32*)NLMSG_DATA(n1))[0] = tv.tv_sec;
+	((__u32*)NLMSG_DATA(n1))[1] = tv.tv_usec;
+	fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp);
+}
+
+static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		    void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	if (!init_phase)
+		write_stamp(fp);
+	fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp);
+	fflush(fp);
+	return 0;
+}
+
+void usage(void)
+{
+	fprintf(stderr, "Usage: rtmon file FILE [ all | LISTofOBJECTS]\n");
+	fprintf(stderr, "LISTofOBJECTS := [ link ] [ address ] [ route ]\n");
+	exit(-1);
+}
+
+int
+main(int argc, char **argv)
+{
+	FILE *fp;
+	struct rtnl_handle rth;
+	int family = AF_UNSPEC;
+	unsigned groups = ~0U;
+	int llink = 0;
+	int laddr = 0;
+	int lroute = 0;
+	char *file = NULL;
+
+	while (argc > 1) {
+		if (matches(argv[1], "-family") == 0) {
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+			if (strcmp(argv[1], "inet") == 0)
+				family = AF_INET;
+			else if (strcmp(argv[1], "inet6") == 0)
+				family = AF_INET6;
+			else if (strcmp(argv[1], "link") == 0)
+				family = AF_INET6;
+			else if (strcmp(argv[1], "help") == 0)
+				usage();
+			else {
+				fprintf(stderr, "Protocol ID \"%s\" is unknown, try \"rtmon help\".\n", argv[1]);
+				exit(-1);
+			}
+		} else if (strcmp(argv[1], "-4") == 0) {
+			family = AF_INET;
+		} else if (strcmp(argv[1], "-6") == 0) {
+			family = AF_INET6;
+		} else if (strcmp(argv[1], "-0") == 0) {
+			family = AF_PACKET;
+		} else if (matches(argv[1], "-Version") == 0) {
+			printf("rtmon utility, iproute2-ss%s\n", SNAPSHOT);
+			exit(0);
+		} else if (matches(argv[1], "file") == 0) {
+			argc--;
+			argv++;
+			if (argc <= 1)
+				usage();
+			file = argv[1];
+		} else if (matches(argv[1], "link") == 0) {
+			llink=1;
+			groups = 0;
+		} else if (matches(argv[1], "address") == 0) {
+			laddr=1;
+			groups = 0;
+		} else if (matches(argv[1], "route") == 0) {
+			lroute=1;
+			groups = 0;
+		} else if (strcmp(argv[1], "all") == 0) {
+			groups = ~0U;
+		} else if (matches(argv[1], "help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]);
+			exit(-1);
+		}
+		argc--;	argv++;
+	}
+
+	if (file == NULL) {
+		fprintf(stderr, "Not enough information: argument \"file\" is required\n");
+		exit(-1);
+	}
+	if (llink)
+		groups |= nl_mgrp(RTNLGRP_LINK);
+	if (laddr) {
+		if (!family || family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR);
+		if (!family || family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR);
+	}
+	if (lroute) {
+		if (!family || family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE);
+		if (!family || family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE);
+	}
+
+	fp = fopen(file, "w");
+	if (fp == NULL) {
+		perror("Cannot fopen");
+		exit(-1);
+	}
+
+	if (rtnl_open(&rth, groups) < 0)
+		exit(1);
+
+	if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	write_stamp(fp);
+
+	if (rtnl_dump_filter(&rth, dump_msg, fp) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		return 1;
+	}
+
+	init_phase = 0;
+
+	if (rtnl_listen(&rth, dump_msg, (void*)fp) < 0)
+		exit(2);
+
+	exit(0);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/rtpr b/ap/app/iproute2/iproute2-3.4.0/ip/rtpr
new file mode 100755
index 0000000..c3629fd
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/rtpr
@@ -0,0 +1,4 @@
+#! /bin/bash
+
+exec tr "[\\\\]" "[
+]"
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/static-syms.c b/ap/app/iproute2/iproute2-3.4.0/ip/static-syms.c
new file mode 100755
index 0000000..1ed3a8a
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/static-syms.c
@@ -0,0 +1,6 @@
+#include <string.h>
+void *_dlsym(const char *sym)
+{
+#include "static-syms.h"
+	return NULL;
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/tunnel.c b/ap/app/iproute2/iproute2-3.4.0/ip/tunnel.c
new file mode 100755
index 0000000..b176d3f
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/tunnel.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * split from ip_tunnel.c
+ */
+/*
+ * Author:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+
+#include "utils.h"
+#include "tunnel.h"
+
+const char *tnl_strproto(__u8 proto)
+{
+	static char buf[16];
+
+	switch (proto) {
+	case IPPROTO_IPIP:
+		strcpy(buf, "ip");
+		break;
+	case IPPROTO_GRE:
+		strcpy(buf, "gre");
+		break;
+	case IPPROTO_IPV6:
+		strcpy(buf, "ipv6");
+		break;
+	case 0:
+		strcpy(buf, "any");
+		break;
+	default:
+		strcpy(buf, "unknown");
+		break;
+	}
+
+	return buf;
+}
+
+int tnl_get_ioctl(const char *basedev, void *p)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+	ifr.ifr_ifru.ifru_data = (void*)p;
+	fd = socket(preferred_family, SOCK_DGRAM, 0);
+	err = ioctl(fd, SIOCGETTUNNEL, &ifr);
+	if (err)
+		fprintf(stderr, "get tunnel %s failed: %s\n", basedev, 
+			strerror(errno));
+
+	close(fd);
+	return err;
+}
+
+int tnl_add_ioctl(int cmd, const char *basedev, const char *name, void *p)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	if (cmd == SIOCCHGTUNNEL && name[0])
+		strncpy(ifr.ifr_name, name, IFNAMSIZ);
+	else
+		strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+	ifr.ifr_ifru.ifru_data = p;
+	fd = socket(preferred_family, SOCK_DGRAM, 0);
+	err = ioctl(fd, cmd, &ifr);
+	if (err)
+		fprintf(stderr, "add tunnel %s failed: %s\n", ifr.ifr_name,
+			strerror(errno));
+	close(fd);
+	return err;
+}
+
+int tnl_del_ioctl(const char *basedev, const char *name, void *p)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	if (name[0])
+		strncpy(ifr.ifr_name, name, IFNAMSIZ);
+	else
+		strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+
+	ifr.ifr_ifru.ifru_data = p;
+	fd = socket(preferred_family, SOCK_DGRAM, 0);
+	err = ioctl(fd, SIOCDELTUNNEL, &ifr);
+	if (err)
+		fprintf(stderr, "delete tunnel %s failed: %s\n",
+			ifr.ifr_name, strerror(errno));
+	close(fd);
+	return err;
+}
+
+static int tnl_gen_ioctl(int cmd, const char *name, 
+			 void *p, int skiperr)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	strncpy(ifr.ifr_name, name, IFNAMSIZ);
+	ifr.ifr_ifru.ifru_data = p;
+	fd = socket(preferred_family, SOCK_DGRAM, 0);
+	err = ioctl(fd, cmd, &ifr);
+	if (err && errno != skiperr)
+		fprintf(stderr, "%s: ioctl %x failed: %s\n", name,
+			cmd, strerror(errno));
+	close(fd);
+	return err;
+}
+
+int tnl_prl_ioctl(int cmd, const char *name, void *p)
+{
+	return tnl_gen_ioctl(cmd, name, p, -1);
+}
+
+int tnl_6rd_ioctl(int cmd, const char *name, void *p)
+{
+	return tnl_gen_ioctl(cmd, name, p, -1);
+}
+
+int tnl_ioctl_get_6rd(const char *name, void *p)
+{
+	return tnl_gen_ioctl(SIOCGET6RD, name, p, EINVAL);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/tunnel.h b/ap/app/iproute2/iproute2-3.4.0/ip/tunnel.h
new file mode 100755
index 0000000..7e7fe13
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/tunnel.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * Author:
+ *	Masahide NAKAMURA @USAGI
+ */
+#ifndef __TUNNEL_H__
+#define __TUNNEL_H__ 1
+
+#include <linux/types.h>
+
+const char *tnl_strproto(__u8 proto);
+
+int tnl_get_ioctl(const char *basedev, void *p);
+int tnl_add_ioctl(int cmd, const char *basedev, const char *name, void *p);
+int tnl_del_ioctl(const char *basedev, const char *name, void *p);
+int tnl_prl_ioctl(int cmd, const char *name, void *p);
+int tnl_6rd_ioctl(int cmd, const char *name, void *p);
+int tnl_ioctl_get_6rd(const char *name, void *p);
+
+#endif
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/xfrm.h b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm.h
new file mode 100755
index 0000000..784a201
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm.h
@@ -0,0 +1,159 @@
+/* $USAGI: $ */
+
+/*
+ * Copyright (C)2004 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#ifndef __XFRM_H__
+#define __XFRM_H__ 1
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/xfrm.h>
+
+#ifndef IPPROTO_SCTP
+# define IPPROTO_SCTP	132
+#endif
+#ifndef IPPROTO_DCCP
+# define IPPROTO_DCCP	33
+#endif
+#ifndef IPPROTO_MH
+# define IPPROTO_MH	135
+#endif
+
+#define XFRMS_RTA(x)  ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info))))
+#define XFRMS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_info))
+
+#define XFRMP_RTA(x)  ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_info))))
+#define XFRMP_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_info))
+
+#define XFRMSID_RTA(x)  ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_id))))
+#define XFRMSID_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_id))
+
+#define XFRMPID_RTA(x)  ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id))))
+#define XFRMPID_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_id))
+
+#define XFRMACQ_RTA(x)	((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))))
+#define XFRMEXP_RTA(x)	((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_expire))))
+#define XFRMPEXP_RTA(x)	((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))))
+
+#define XFRMREP_RTA(x)	((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_report))))
+
+#define XFRMSAPD_RTA(x)	((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(__u32))))
+#define XFRM_FLAG_PRINT(fp, flags, f, s) \
+	do { \
+		if (flags & f) { \
+			flags &= ~f; \
+			fprintf(fp, s "%s", (flags ? " " : "")); \
+		} \
+	} while(0)
+
+struct xfrm_buffer {
+	char *buf;
+	int size;
+	int offset;
+
+	int nlmsg_count;
+	struct rtnl_handle *rth;
+};
+
+struct xfrm_filter {
+	int use;
+
+	struct xfrm_usersa_info xsinfo;
+	__u8 id_src_mask;
+	__u8 id_dst_mask;
+	__u8 id_proto_mask;
+	__u32 id_spi_mask;
+	__u8 mode_mask;
+	__u32 reqid_mask;
+	__u8 state_flags_mask;
+
+	struct xfrm_userpolicy_info xpinfo;
+	__u8 dir_mask;
+	__u8 sel_src_mask;
+	__u8 sel_dst_mask;
+	__u32 sel_dev_mask;
+	__u8 upspec_proto_mask;
+	__u16 upspec_sport_mask;
+	__u16 upspec_dport_mask;
+	__u32 index_mask;
+	__u8 action_mask;
+	__u32 priority_mask;
+	__u8 policy_flags_mask;
+
+	__u8 ptype;
+	__u8 ptype_mask;
+
+};
+#define XFRM_FILTER_MASK_FULL (~0)
+
+extern struct xfrm_filter filter;
+
+int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		     void *arg);
+int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		      void *arg);
+int do_xfrm_state(int argc, char **argv);
+int do_xfrm_policy(int argc, char **argv);
+int do_xfrm_monitor(int argc, char **argv);
+
+int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits);
+int xfrm_xfrmproto_is_ipsec(__u8 proto);
+int xfrm_xfrmproto_is_ro(__u8 proto);
+int xfrm_xfrmproto_getbyname(char *name);
+int xfrm_algotype_getbyname(char *name);
+int xfrm_parse_mark(struct xfrm_mark *mark, int *argcp, char ***argvp);
+const char *strxf_xfrmproto(__u8 proto);
+const char *strxf_algotype(int type);
+const char *strxf_mask8(__u8 mask);
+const char *strxf_mask32(__u32 mask);
+const char *strxf_share(__u8 share);
+const char *strxf_proto(__u8 proto);
+const char *strxf_ptype(__u8 ptype);
+void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id,
+			__u8 mode, __u32 reqid, __u16 family, int force_spi,
+			FILE *fp, const char *prefix, const char *title);
+void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix);
+void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg,
+			 struct xfrm_lifetime_cur *cur,
+			 FILE *fp, const char *prefix);
+void xfrm_selector_print(struct xfrm_selector *sel, __u16 family,
+			 FILE *fp, const char *prefix);
+void xfrm_xfrma_print(struct rtattr *tb[], __u16 family,
+		      FILE *fp, const char *prefix);
+void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo,
+			    struct rtattr *tb[], FILE *fp, const char *prefix,
+			   const char *title);
+void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo,
+			    struct rtattr *tb[], FILE *fp, const char *prefix,
+			    const char *title);
+int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family,
+		  int loose, int *argcp, char ***argvp);
+int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp);
+int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp);
+int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp);
+int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp);
+int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft,
+			    int *argcp, char ***argvp);
+int xfrm_sctx_parse(char *ctxstr, char *context,
+		    struct xfrm_user_sec_ctx *sctx);
+#endif
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_monitor.c b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_monitor.c
new file mode 100755
index 0000000..ea17987
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_monitor.c
@@ -0,0 +1,420 @@
+/* $USAGI: $ */
+
+/*
+ * Copyright (C)2005 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * based on ipmonitor.c
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/xfrm.h>
+#include "utils.h"
+#include "xfrm.h"
+#include "ip_common.h"
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip xfrm monitor [ all | LISTofXFRM-OBJECTS ]\n");
+	exit(-1);
+}
+
+static int xfrm_acquire_print(const struct sockaddr_nl *who,
+			      struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct xfrm_user_acquire *xacq = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[XFRMA_MAX+1];
+	__u16 family;
+
+	len -= NLMSG_LENGTH(sizeof(*xacq));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	parse_rtattr(tb, XFRMA_MAX, XFRMACQ_RTA(xacq), len);
+
+	family = xacq->sel.family;
+	if (family == AF_UNSPEC)
+		family = xacq->policy.sel.family;
+	if (family == AF_UNSPEC)
+		family = preferred_family;
+
+	fprintf(fp, "acquire ");
+
+	fprintf(fp, "proto %s ", strxf_xfrmproto(xacq->id.proto));
+	if (show_stats > 0 || xacq->id.spi) {
+		__u32 spi = ntohl(xacq->id.spi);
+		fprintf(fp, "spi 0x%08x", spi);
+		if (show_stats > 0)
+			fprintf(fp, "(%u)", spi);
+		fprintf(fp, " ");
+	}
+	fprintf(fp, "%s", _SL_);
+
+	xfrm_selector_print(&xacq->sel, family, fp, "  sel ");
+
+	xfrm_policy_info_print(&xacq->policy, tb, fp, "    ", "  policy ");
+
+	if (show_stats > 0)
+		fprintf(fp, "  seq 0x%08u ", xacq->seq);
+	if (show_stats > 0) {
+		fprintf(fp, "%s-mask %s ",
+			strxf_algotype(XFRMA_ALG_CRYPT),
+			strxf_mask32(xacq->ealgos));
+		fprintf(fp, "%s-mask %s ",
+			strxf_algotype(XFRMA_ALG_AUTH),
+			strxf_mask32(xacq->aalgos));
+		fprintf(fp, "%s-mask %s",
+			strxf_algotype(XFRMA_ALG_COMP),
+			strxf_mask32(xacq->calgos));
+	}
+	fprintf(fp, "%s", _SL_);
+
+	if (oneline)
+		fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
+static int xfrm_state_flush_print(const struct sockaddr_nl *who,
+				  struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct xfrm_usersa_flush *xsf = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	const char *str;
+
+	len -= NLMSG_SPACE(sizeof(*xsf));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	fprintf(fp, "Flushed state ");
+
+	str = strxf_xfrmproto(xsf->proto);
+	if (str)
+		fprintf(fp, "proto %s", str);
+	else
+		fprintf(fp, "proto %u", xsf->proto);
+	fprintf(fp, "%s", _SL_);
+
+	if (oneline)
+		fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
+static int xfrm_policy_flush_print(const struct sockaddr_nl *who,
+				   struct nlmsghdr *n, void *arg)
+{
+	struct rtattr * tb[XFRMA_MAX+1];
+	FILE *fp = (FILE*)arg;
+	int len = n->nlmsg_len;
+
+	len -= NLMSG_SPACE(0);
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	fprintf(fp, "Flushed policy ");
+
+	parse_rtattr(tb, XFRMA_MAX, NLMSG_DATA(n), len);
+
+	if (tb[XFRMA_POLICY_TYPE]) {
+		struct xfrm_userpolicy_type *upt;
+
+		fprintf(fp, "ptype ");
+
+		if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt))
+			fprintf(fp, "(ERROR truncated)");
+
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		fprintf(fp, "%s ", strxf_ptype(upt->type));
+	}
+
+	fprintf(fp, "%s", _SL_);
+
+	if (oneline)
+		fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
+static int xfrm_report_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct xfrm_user_report *xrep = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[XFRMA_MAX+1];
+	__u16 family;
+
+	len -= NLMSG_LENGTH(sizeof(*xrep));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	family = xrep->sel.family;
+	if (family == AF_UNSPEC)
+		family = preferred_family;
+
+	fprintf(fp, "report ");
+
+	fprintf(fp, "proto %s ", strxf_xfrmproto(xrep->proto));
+	fprintf(fp, "%s", _SL_);
+
+	xfrm_selector_print(&xrep->sel, family, fp, "  sel ");
+
+	parse_rtattr(tb, XFRMA_MAX, XFRMREP_RTA(xrep), len);
+
+	xfrm_xfrma_print(tb, family, fp, "  ");
+
+	if (oneline)
+		fprintf(fp, "\n");
+
+	return 0;
+}
+
+void xfrm_ae_flags_print(__u32 flags, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	fprintf(fp, " (0x%x) ", flags);
+	if (!flags)
+		return;
+	if (flags & XFRM_AE_CR)
+		fprintf(fp, " replay update ");
+	if (flags & XFRM_AE_CE)
+		fprintf(fp, " timer expired ");
+	if (flags & XFRM_AE_CU)
+		fprintf(fp, " policy updated ");
+
+}
+
+static void xfrm_usersa_print(const struct xfrm_usersa_id *sa_id, __u32 reqid, FILE *fp)
+{
+	char buf[256];
+
+	buf[0] = 0;
+	fprintf(fp, "dst %s ", rt_addr_n2a(sa_id->family,
+		sizeof(sa_id->daddr), &sa_id->daddr, buf, sizeof(buf)));
+
+	fprintf(fp, " reqid 0x%x", reqid);
+
+	fprintf(fp, " protocol %s ", strxf_proto(sa_id->proto));
+	fprintf(fp, " SPI 0x%x", ntohl(sa_id->spi));
+}
+
+static int xfrm_ae_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct xfrm_aevent_id *id = NLMSG_DATA(n);
+	char abuf[256];
+
+	fprintf(fp, "Async event ");
+	xfrm_ae_flags_print(id->flags, arg);
+	fprintf(fp,"\n\t");
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "src %s ", rt_addr_n2a(id->sa_id.family,
+		sizeof(id->saddr), &id->saddr,
+		abuf, sizeof(abuf)));
+
+	xfrm_usersa_print(&id->sa_id, id->reqid, fp);
+
+	fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
+static void xfrm_print_addr(FILE *fp, int family, xfrm_address_t *a, size_t s)
+{
+	char buf[256];
+
+	buf[0] = 0;
+	fprintf(fp, "%s", rt_addr_n2a(family, s, a, buf, sizeof(buf)));
+}
+
+static int xfrm_mapping_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct xfrm_user_mapping *map = NLMSG_DATA(n);
+
+	fprintf(fp, "Mapping change ");
+	xfrm_print_addr(fp, map->id.family, &map->old_saddr,
+			sizeof(map->old_saddr));
+
+	fprintf(fp, ":%d -> ", ntohs(map->old_sport));
+	xfrm_print_addr(fp, map->id.family, &map->new_saddr,
+			sizeof(map->new_saddr));
+	fprintf(fp, ":%d\n\t", ntohs(map->new_sport));
+
+	xfrm_usersa_print(&map->id, map->reqid, fp);
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+static int xfrm_accept_msg(const struct sockaddr_nl *who,
+			   struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+
+	if (timestamp)
+		print_timestamp(fp);
+
+	switch (n->nlmsg_type) {
+	case XFRM_MSG_NEWSA:
+	case XFRM_MSG_DELSA:
+	case XFRM_MSG_UPDSA:
+	case XFRM_MSG_EXPIRE:
+		xfrm_state_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_NEWPOLICY:
+	case XFRM_MSG_DELPOLICY:
+	case XFRM_MSG_UPDPOLICY:
+	case XFRM_MSG_POLEXPIRE:
+		xfrm_policy_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_ACQUIRE:
+		xfrm_acquire_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_FLUSHSA:
+		xfrm_state_flush_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_FLUSHPOLICY:
+		xfrm_policy_flush_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_REPORT:
+		xfrm_report_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_NEWAE:
+		xfrm_ae_print(who, n, arg);
+		return 0;
+	case XFRM_MSG_MAPPING:
+		xfrm_mapping_print(who, n, arg);
+		return 0;
+	default:
+		break;
+	}
+
+	if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP &&
+	    n->nlmsg_type != NLMSG_DONE) {
+		fprintf(fp, "Unknown message: %08d 0x%08x 0x%08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+	}
+	return 0;
+}
+
+extern struct rtnl_handle rth;
+
+int do_xfrm_monitor(int argc, char **argv)
+{
+	char *file = NULL;
+	unsigned groups = ~((unsigned)0); /* XXX */
+	int lacquire=0;
+	int lexpire=0;
+	int laevent=0;
+	int lpolicy=0;
+	int lsa=0;
+	int lreport=0;
+
+	rtnl_close(&rth);
+
+	while (argc > 0) {
+		if (matches(*argv, "file") == 0) {
+			NEXT_ARG();
+			file = *argv;
+		} else if (matches(*argv, "acquire") == 0) {
+			lacquire=1;
+			groups = 0;
+		} else if (matches(*argv, "expire") == 0) {
+			lexpire=1;
+			groups = 0;
+		} else if (matches(*argv, "SA") == 0) {
+			lsa=1;
+			groups = 0;
+		} else if (matches(*argv, "aevent") == 0) {
+			laevent=1;
+			groups = 0;
+		} else if (matches(*argv, "policy") == 0) {
+			lpolicy=1;
+			groups = 0;
+		} else if (matches(*argv, "report") == 0) {
+			lreport=1;
+			groups = 0;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Argument \"%s\" is unknown, try \"ip xfrm monitor help\".\n", *argv);
+			exit(-1);
+		}
+		argc--;	argv++;
+	}
+
+	if (lacquire)
+		groups |= nl_mgrp(XFRMNLGRP_ACQUIRE);
+	if (lexpire)
+		groups |= nl_mgrp(XFRMNLGRP_EXPIRE);
+	if (lsa)
+		groups |= nl_mgrp(XFRMNLGRP_SA);
+	if (lpolicy)
+		groups |= nl_mgrp(XFRMNLGRP_POLICY);
+	if (laevent)
+		groups |= nl_mgrp(XFRMNLGRP_AEVENTS);
+	if (lreport)
+		groups |= nl_mgrp(XFRMNLGRP_REPORT);
+
+	if (file) {
+		FILE *fp;
+		fp = fopen(file, "r");
+		if (fp == NULL) {
+			perror("Cannot fopen");
+			exit(-1);
+		}
+		return rtnl_from_file(fp, xfrm_accept_msg, (void*)stdout);
+	}
+
+	//ll_init_map(&rth);
+
+	if (rtnl_open_byproto(&rth, groups, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (rtnl_listen(&rth, xfrm_accept_msg, (void*)stdout) < 0)
+		exit(2);
+
+	return 0;
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_policy.c b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_policy.c
new file mode 100755
index 0000000..8e3aec5
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_policy.c
@@ -0,0 +1,1066 @@
+/* $USAGI: $ */
+
+/*
+ * Copyright (C)2004 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * based on iproute.c
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <linux/netlink.h>
+#include <linux/xfrm.h>
+#include "utils.h"
+#include "xfrm.h"
+#include "ip_common.h"
+
+//#define NLMSG_DELETEALL_BUF_SIZE (4096-512)
+#define NLMSG_DELETEALL_BUF_SIZE 8192
+
+/*
+ * Receiving buffer defines:
+ * nlmsg
+ *   data = struct xfrm_userpolicy_info
+ *   rtattr
+ *     data = struct xfrm_user_tmpl[]
+ */
+#define NLMSG_BUF_SIZE 4096
+#define RTA_BUF_SIZE 2048
+#define XFRM_TMPLS_BUF_SIZE 1024
+#define CTX_BUF_SIZE 256
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip xfrm policy { add | update } SELECTOR dir DIR [ ctx CTX ]\n");
+	fprintf(stderr, "        [ mark MARK [ mask MASK ] ] [ index INDEX ] [ ptype PTYPE ]\n");
+	fprintf(stderr, "        [ action ACTION ] [ priority PRIORITY ] [ flag FLAG-LIST ]\n");
+	fprintf(stderr, "        [ LIMIT-LIST ] [ TMPL-LIST ]\n");
+	fprintf(stderr, "Usage: ip xfrm policy { delete | get } { SELECTOR | index INDEX } dir DIR\n");
+	fprintf(stderr, "        [ ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]\n");
+	fprintf(stderr, "Usage: ip xfrm policy { deleteall | list } [ SELECTOR ] [ dir DIR ]\n");
+	fprintf(stderr, "        [ index INDEX ] [ ptype PTYPE ] [ action ACTION ] [ priority PRIORITY ]\n");
+	fprintf(stderr, "        [ flag FLAG-LIST ]\n");
+	fprintf(stderr, "Usage: ip xfrm policy flush [ ptype PTYPE ]\n");
+	fprintf(stderr, "Usage: ip xfrm count\n");
+	fprintf(stderr, "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n");
+	fprintf(stderr, "UPSPEC := proto { { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_TCP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_UDP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_SCTP));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_DCCP));
+	fprintf(stderr, " } [ sport PORT ] [ dport PORT ] |\n");
+	fprintf(stderr, "                  { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMPV6));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_MH));
+	fprintf(stderr, " } [ type NUMBER ] [ code NUMBER ] |\n");
+	fprintf(stderr, "                  %s", strxf_proto(IPPROTO_GRE));
+	fprintf(stderr, " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n");
+	fprintf(stderr, "DIR := in | out | fwd\n");
+	fprintf(stderr, "PTYPE := main | sub\n");
+	fprintf(stderr, "ACTION := allow | block\n");
+	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
+	fprintf(stderr, "FLAG := localok | icmp\n");
+	fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n");
+	fprintf(stderr, "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n");
+	fprintf(stderr, "         { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n");
+	fprintf(stderr, "TMPL-LIST := [ TMPL-LIST ] tmpl TMPL\n");
+	fprintf(stderr, "TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]\n");
+	fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n");
+	fprintf(stderr, "XFRM-PROTO := ");
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP));
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH));
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_COMP));
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING));
+	fprintf(stderr, "%s\n", strxf_xfrmproto(IPPROTO_DSTOPTS));
+ 	fprintf(stderr, "MODE := transport | tunnel | ro | in_trigger | beet\n");
+	fprintf(stderr, "LEVEL := required | use\n");
+
+	exit(-1);
+}
+
+static int xfrm_policy_dir_parse(__u8 *dir, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	if (strcmp(*argv, "in") == 0)
+		*dir = XFRM_POLICY_IN;
+	else if (strcmp(*argv, "out") == 0)
+		*dir = XFRM_POLICY_OUT;
+	else if (strcmp(*argv, "fwd") == 0)
+		*dir = XFRM_POLICY_FWD;
+	else
+		invarg("\"DIR\" is invalid", *argv);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int xfrm_policy_ptype_parse(__u8 *ptype, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	if (strcmp(*argv, "main") == 0)
+		*ptype = XFRM_POLICY_TYPE_MAIN;
+	else if (strcmp(*argv, "sub") == 0)
+		*ptype = XFRM_POLICY_TYPE_SUB;
+	else
+		invarg("\"PTYPE\" is invalid", *argv);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int xfrm_policy_flag_parse(__u8 *flags, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	int len = strlen(*argv);
+
+	if (len > 2 && strncmp(*argv, "0x", 2) == 0) {
+		__u8 val = 0;
+
+		if (get_u8(&val, *argv, 16))
+			invarg("\"FLAG\" is invalid", *argv);
+		*flags = val;
+	} else {
+		while (1) {
+			if (strcmp(*argv, "localok") == 0)
+				*flags |= XFRM_POLICY_LOCALOK;
+			else if (strcmp(*argv, "icmp") == 0)
+				*flags |= XFRM_POLICY_ICMP;
+			else {
+				PREV_ARG(); /* back track */
+				break;
+			}
+
+			if (!NEXT_ARG_OK())
+				break;
+			NEXT_ARG();
+		}
+	}
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl,
+			   int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	char *idp = NULL;
+
+	while (1) {
+		if (strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			xfrm_mode_parse(&tmpl->mode,  &argc, &argv);
+		} else if (strcmp(*argv, "reqid") == 0) {
+			NEXT_ARG();
+			xfrm_reqid_parse(&tmpl->reqid, &argc, &argv);
+		} else if (strcmp(*argv, "level") == 0) {
+			NEXT_ARG();
+
+			if (strcmp(*argv, "required") == 0)
+				tmpl->optional = 0;
+			else if (strcmp(*argv, "use") == 0)
+				tmpl->optional = 1;
+			else
+				invarg("\"LEVEL\" is invalid\n", *argv);
+
+		} else {
+			if (idp) {
+				PREV_ARG(); /* back track */
+				break;
+			}
+			idp = *argv;
+			preferred_family = AF_UNSPEC;
+			xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family,
+				      0, &argc, &argv);
+			preferred_family = tmpl->family;
+		}
+
+		if (!NEXT_ARG_OK())
+			break;
+
+		NEXT_ARG();
+	}
+	if (argc == *argcp)
+		missarg("TMPL");
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+int xfrm_sctx_parse(char *ctxstr, char *s,
+			   struct xfrm_user_sec_ctx *sctx)
+{
+	int slen;
+
+	slen = strlen(s) + 1;
+
+	sctx->exttype = XFRMA_SEC_CTX;
+	sctx->ctx_doi = 1;
+	sctx->ctx_alg = 1;
+	sctx->ctx_len = slen;
+	sctx->len = sizeof(struct xfrm_user_sec_ctx) + slen;
+	memcpy(ctxstr, s, slen);
+
+	return 0;
+}
+
+static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		struct xfrm_userpolicy_info	xpinfo;
+		char				buf[RTA_BUF_SIZE];
+	} req;
+	char *dirp = NULL;
+	char *selp = NULL;
+	char *ptypep = NULL;
+	char *sctxp = NULL;
+	struct xfrm_userpolicy_type upt;
+	char tmpls_buf[XFRM_TMPLS_BUF_SIZE];
+	int tmpls_len = 0;
+	struct xfrm_mark mark = {0, 0};
+	struct {
+		struct xfrm_user_sec_ctx sctx;
+		char	str[CTX_BUF_SIZE];
+	} ctx;
+
+	memset(&req, 0, sizeof(req));
+	memset(&upt, 0, sizeof(upt));
+	memset(&tmpls_buf, 0, sizeof(tmpls_buf));
+	memset(&ctx, 0, sizeof(ctx));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.xpinfo.sel.family = preferred_family;
+
+	req.xpinfo.lft.soft_byte_limit = XFRM_INF;
+	req.xpinfo.lft.hard_byte_limit = XFRM_INF;
+	req.xpinfo.lft.soft_packet_limit = XFRM_INF;
+	req.xpinfo.lft.hard_packet_limit = XFRM_INF;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dir") == 0) {
+			if (dirp)
+				duparg("dir", *argv);
+			dirp = *argv;
+
+			NEXT_ARG();
+			xfrm_policy_dir_parse(&req.xpinfo.dir, &argc, &argv);
+		} else if (strcmp(*argv, "ctx") == 0) {
+			char *context;
+
+			if (sctxp)
+				duparg("ctx", *argv);
+			sctxp = *argv;
+			NEXT_ARG();
+			context = *argv;
+			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
+		} else if (strcmp(*argv, "mark") == 0) {
+			xfrm_parse_mark(&mark, &argc, &argv);
+		} else if (strcmp(*argv, "index") == 0) {
+			NEXT_ARG();
+			if (get_u32(&req.xpinfo.index, *argv, 0))
+				invarg("\"INDEX\" is invalid", *argv);
+		} else if (strcmp(*argv, "ptype") == 0) {
+			if (ptypep)
+				duparg("ptype", *argv);
+			ptypep = *argv;
+
+			NEXT_ARG();
+			xfrm_policy_ptype_parse(&upt.type, &argc, &argv);
+		} else if (strcmp(*argv, "action") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "allow") == 0)
+				req.xpinfo.action = XFRM_POLICY_ALLOW;
+			else if (strcmp(*argv, "block") == 0)
+				req.xpinfo.action = XFRM_POLICY_BLOCK;
+			else
+				invarg("\"action\" value is invalid\n", *argv);
+		} else if (strcmp(*argv, "priority") == 0) {
+			NEXT_ARG();
+			if (get_u32(&req.xpinfo.priority, *argv, 0))
+				invarg("\"PRIORITY\" is invalid", *argv);
+		} else if (strcmp(*argv, "flag") == 0) {
+			NEXT_ARG();
+			xfrm_policy_flag_parse(&req.xpinfo.flags, &argc,
+					       &argv);
+		} else if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			xfrm_lifetime_cfg_parse(&req.xpinfo.lft, &argc, &argv);
+		} else if (strcmp(*argv, "tmpl") == 0) {
+			struct xfrm_user_tmpl *tmpl;
+
+			if (tmpls_len + sizeof(*tmpl) > sizeof(tmpls_buf)) {
+				fprintf(stderr, "Too many tmpls: buffer overflow\n");
+				exit(1);
+			}
+			tmpl = (struct xfrm_user_tmpl *)((char *)tmpls_buf + tmpls_len);
+
+			tmpl->family = preferred_family;
+			tmpl->aalgos = (~(__u32)0);
+			tmpl->ealgos = (~(__u32)0);
+			tmpl->calgos = (~(__u32)0);
+
+			NEXT_ARG();
+			xfrm_tmpl_parse(tmpl, &argc, &argv);
+
+			tmpls_len += sizeof(*tmpl);
+		} else {
+			if (selp)
+				duparg("unknown", *argv);
+			selp = *argv;
+
+			xfrm_selector_parse(&req.xpinfo.sel, &argc, &argv);
+			if (preferred_family == AF_UNSPEC)
+				preferred_family = req.xpinfo.sel.family;
+		}
+
+		argc--; argv++;
+	}
+
+	if (!dirp) {
+		fprintf(stderr, "Not enough information: \"DIR\" is required.\n");
+		exit(1);
+	}
+
+	if (ptypep) {
+		addattr_l(&req.n, sizeof(req), XFRMA_POLICY_TYPE,
+			  (void *)&upt, sizeof(upt));
+	}
+
+	if (tmpls_len > 0) {
+		addattr_l(&req.n, sizeof(req), XFRMA_TMPL,
+			  (void *)tmpls_buf, tmpls_len);
+	}
+
+	if (mark.m & mark.v) {
+		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
+				  (void *)&mark, sizeof(mark));
+		if (r < 0) {
+			fprintf(stderr, "%s: XFRMA_MARK failed\n",__func__);
+			exit(1);
+		}
+	}
+
+	if (sctxp) {
+		addattr_l(&req.n, sizeof(req), XFRMA_SEC_CTX,
+			  (void *)&ctx, ctx.sctx.len);
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (req.xpinfo.sel.family == AF_UNSPEC)
+		req.xpinfo.sel.family = AF_INET;
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+static int xfrm_policy_filter_match(struct xfrm_userpolicy_info *xpinfo,
+				    __u8 ptype)
+{
+	if (!filter.use)
+		return 1;
+
+	if ((xpinfo->dir^filter.xpinfo.dir)&filter.dir_mask)
+		return 0;
+
+	if ((ptype^filter.ptype)&filter.ptype_mask)
+		return 0;
+
+	if (filter.sel_src_mask) {
+		if (xfrm_addr_match(&xpinfo->sel.saddr, &filter.xpinfo.sel.saddr,
+				    filter.sel_src_mask))
+			return 0;
+	}
+
+	if (filter.sel_dst_mask) {
+		if (xfrm_addr_match(&xpinfo->sel.daddr, &filter.xpinfo.sel.daddr,
+				    filter.sel_dst_mask))
+			return 0;
+	}
+
+	if ((xpinfo->sel.ifindex^filter.xpinfo.sel.ifindex)&filter.sel_dev_mask)
+		return 0;
+
+	if ((xpinfo->sel.proto^filter.xpinfo.sel.proto)&filter.upspec_proto_mask)
+		return 0;
+
+	if (filter.upspec_sport_mask) {
+		if ((xpinfo->sel.sport^filter.xpinfo.sel.sport)&filter.upspec_sport_mask)
+			return 0;
+	}
+
+	if (filter.upspec_dport_mask) {
+		if ((xpinfo->sel.dport^filter.xpinfo.sel.dport)&filter.upspec_dport_mask)
+			return 0;
+	}
+
+	if ((xpinfo->index^filter.xpinfo.index)&filter.index_mask)
+		return 0;
+
+	if ((xpinfo->action^filter.xpinfo.action)&filter.action_mask)
+		return 0;
+
+	if ((xpinfo->priority^filter.xpinfo.priority)&filter.priority_mask)
+		return 0;
+
+	if (filter.policy_flags_mask)
+		if ((xpinfo->flags & filter.xpinfo.flags) == 0)
+			return 0;
+
+	return 1;
+}
+
+int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		      void *arg)
+{
+	struct rtattr * tb[XFRMA_MAX+1];
+	struct rtattr * rta;
+	struct xfrm_userpolicy_info *xpinfo = NULL;
+	struct xfrm_user_polexpire *xpexp = NULL;
+	struct xfrm_userpolicy_id *xpid = NULL;
+	__u8 ptype = XFRM_POLICY_TYPE_MAIN;
+	FILE *fp = (FILE*)arg;
+	int len = n->nlmsg_len;
+
+	if (n->nlmsg_type != XFRM_MSG_NEWPOLICY &&
+	    n->nlmsg_type != XFRM_MSG_DELPOLICY &&
+	    n->nlmsg_type != XFRM_MSG_UPDPOLICY &&
+	    n->nlmsg_type != XFRM_MSG_POLEXPIRE) {
+		fprintf(stderr, "Not a policy: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	if (n->nlmsg_type == XFRM_MSG_DELPOLICY)  {
+		xpid = NLMSG_DATA(n);
+		len -= NLMSG_SPACE(sizeof(*xpid));
+	} else if (n->nlmsg_type == XFRM_MSG_POLEXPIRE) {
+		xpexp = NLMSG_DATA(n);
+		xpinfo = &xpexp->pol;
+		len -= NLMSG_SPACE(sizeof(*xpexp));
+	} else {
+		xpexp = NULL;
+		xpinfo = NLMSG_DATA(n);
+		len -= NLMSG_SPACE(sizeof(*xpinfo));
+	}
+
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (n->nlmsg_type == XFRM_MSG_DELPOLICY)
+		rta = XFRMPID_RTA(xpid);
+	else if (n->nlmsg_type == XFRM_MSG_POLEXPIRE)
+		rta = XFRMPEXP_RTA(xpexp);
+	else
+		rta = XFRMP_RTA(xpinfo);
+
+	parse_rtattr(tb, XFRMA_MAX, rta, len);
+
+	if (tb[XFRMA_POLICY_TYPE]) {
+		struct xfrm_userpolicy_type *upt;
+
+		if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt)) {
+			fprintf(stderr, "too short XFRMA_POLICY_TYPE len\n");
+			return -1;
+		}
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		ptype = upt->type;
+	}
+
+	if (xpinfo && !xfrm_policy_filter_match(xpinfo, ptype))
+		return 0;
+
+	if (n->nlmsg_type == XFRM_MSG_DELPOLICY)
+		fprintf(fp, "Deleted ");
+	else if (n->nlmsg_type == XFRM_MSG_UPDPOLICY)
+		fprintf(fp, "Updated ");
+	else if (n->nlmsg_type == XFRM_MSG_POLEXPIRE)
+		fprintf(fp, "Expired ");
+
+	if (n->nlmsg_type == XFRM_MSG_DELPOLICY) {
+		//xfrm_policy_id_print();
+		if (!tb[XFRMA_POLICY]) {
+			fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: no XFRMA_POLICY\n");
+			return -1;
+		}
+		if (RTA_PAYLOAD(tb[XFRMA_POLICY]) < sizeof(*xpinfo)) {
+			fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n");
+			return -1;
+		}
+		xpinfo = (struct xfrm_userpolicy_info *)RTA_DATA(tb[XFRMA_POLICY]);
+	}
+
+	xfrm_policy_info_print(xpinfo, tb, fp, NULL, NULL);
+
+	if (n->nlmsg_type == XFRM_MSG_POLEXPIRE) {
+		fprintf(fp, "\t");
+		fprintf(fp, "hard %u", xpexp->hard);
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (oneline)
+		fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
+static int xfrm_policy_get_or_delete(int argc, char **argv, int delete,
+				     void *res_nlbuf)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		struct xfrm_userpolicy_id	xpid;
+		char				buf[RTA_BUF_SIZE];
+	} req;
+	char *dirp = NULL;
+	char *selp = NULL;
+	char *indexp = NULL;
+	char *ptypep = NULL;
+	char *sctxp = NULL;
+	struct xfrm_userpolicy_type upt;
+	struct xfrm_mark mark = {0, 0};
+	struct {
+		struct xfrm_user_sec_ctx sctx;
+		char    str[CTX_BUF_SIZE];
+	} ctx;
+
+
+	memset(&req, 0, sizeof(req));
+	memset(&upt, 0, sizeof(upt));
+	memset(&ctx, 0, sizeof(ctx));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY : XFRM_MSG_GETPOLICY;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dir") == 0) {
+			if (dirp)
+				duparg("dir", *argv);
+			dirp = *argv;
+
+			NEXT_ARG();
+			xfrm_policy_dir_parse(&req.xpid.dir, &argc, &argv);
+
+		} else if (strcmp(*argv, "ctx") == 0) {
+			char *context;
+
+			if (sctxp)
+				duparg("ctx", *argv);
+			sctxp = *argv;
+			NEXT_ARG();
+			context = *argv;
+			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
+		} else if (strcmp(*argv, "mark") == 0) {
+			xfrm_parse_mark(&mark, &argc, &argv);
+		} else if (strcmp(*argv, "index") == 0) {
+			if (indexp)
+				duparg("index", *argv);
+			indexp = *argv;
+
+			NEXT_ARG();
+			if (get_u32(&req.xpid.index, *argv, 0))
+				invarg("\"INDEX\" is invalid", *argv);
+
+		} else if (strcmp(*argv, "ptype") == 0) {
+			if (ptypep)
+				duparg("ptype", *argv);
+			ptypep = *argv;
+
+			NEXT_ARG();
+			xfrm_policy_ptype_parse(&upt.type, &argc, &argv);
+
+		} else {
+			if (selp)
+				invarg("unknown", *argv);
+			selp = *argv;
+
+			xfrm_selector_parse(&req.xpid.sel, &argc, &argv);
+			if (preferred_family == AF_UNSPEC)
+				preferred_family = req.xpid.sel.family;
+
+		}
+
+		argc--; argv++;
+	}
+
+	if (!dirp) {
+		fprintf(stderr, "Not enough information: \"DIR\" is required.\n");
+		exit(1);
+	}
+	if (ptypep) {
+		addattr_l(&req.n, sizeof(req), XFRMA_POLICY_TYPE,
+			  (void *)&upt, sizeof(upt));
+	}
+	if (!selp && !indexp) {
+		fprintf(stderr, "Not enough information: either \"SELECTOR\" or \"INDEX\" is required.\n");
+		exit(1);
+	}
+	if (selp && indexp)
+		duparg2("SELECTOR", "INDEX");
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (req.xpid.sel.family == AF_UNSPEC)
+		req.xpid.sel.family = AF_INET;
+
+	if (mark.m & mark.v) {
+		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
+				  (void *)&mark, sizeof(mark));
+		if (r < 0) {
+			fprintf(stderr, "%s: XFRMA_MARK failed\n",__func__);
+			exit(1);
+		}
+	}
+
+	if (sctxp) {
+		addattr_l(&req.n, sizeof(req), XFRMA_SEC_CTX,
+			  (void *)&ctx, ctx.sctx.len);
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, res_nlbuf) < 0)
+		exit(2);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+static int xfrm_policy_delete(int argc, char **argv)
+{
+	return xfrm_policy_get_or_delete(argc, argv, 1, NULL);
+}
+
+static int xfrm_policy_get(int argc, char **argv)
+{
+	char buf[NLMSG_BUF_SIZE];
+	struct nlmsghdr *n = (struct nlmsghdr *)buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	xfrm_policy_get_or_delete(argc, argv, 0, n);
+
+	if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) {
+		fprintf(stderr, "An error :-)\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+/*
+ * With an existing policy of nlmsg, make new nlmsg for deleting the policy
+ * and store it to buffer.
+ */
+static int xfrm_policy_keep(const struct sockaddr_nl *who,
+			    struct nlmsghdr *n,
+			    void *arg)
+{
+	struct xfrm_buffer *xb = (struct xfrm_buffer *)arg;
+	struct rtnl_handle *rth = xb->rth;
+	struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[XFRMA_MAX+1];
+	__u8 ptype = XFRM_POLICY_TYPE_MAIN;
+	struct nlmsghdr *new_n;
+	struct xfrm_userpolicy_id *xpid;
+
+	if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) {
+		fprintf(stderr, "Not a policy: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*xpinfo));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	parse_rtattr(tb, XFRMA_MAX, XFRMP_RTA(xpinfo), len);
+
+	if (tb[XFRMA_POLICY_TYPE]) {
+		struct xfrm_userpolicy_type *upt;
+
+		if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt)) {
+			fprintf(stderr, "too short XFRMA_POLICY_TYPE len\n");
+			return -1;
+		}
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		ptype = upt->type;
+	}
+
+	if (!xfrm_policy_filter_match(xpinfo, ptype))
+		return 0;
+
+	if (xb->offset > xb->size) {
+		fprintf(stderr, "Policy buffer overflow\n");
+		return -1;
+	}
+
+	new_n = (struct nlmsghdr *)(xb->buf + xb->offset);
+	new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xpid));
+	new_n->nlmsg_flags = NLM_F_REQUEST;
+	new_n->nlmsg_type = XFRM_MSG_DELPOLICY;
+	new_n->nlmsg_seq = ++rth->seq;
+
+	xpid = NLMSG_DATA(new_n);
+	memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel));
+	xpid->dir = xpinfo->dir;
+	xpid->index = xpinfo->index;
+
+	xb->offset += new_n->nlmsg_len;
+	xb->nlmsg_count ++;
+
+	return 0;
+}
+
+static int xfrm_policy_list_or_deleteall(int argc, char **argv, int deleteall)
+{
+	char *selp = NULL;
+	struct rtnl_handle rth;
+
+	if (argc > 0)
+		filter.use = 1;
+	filter.xpinfo.sel.family = preferred_family;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dir") == 0) {
+			NEXT_ARG();
+			xfrm_policy_dir_parse(&filter.xpinfo.dir, &argc, &argv);
+
+			filter.dir_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "index") == 0) {
+			NEXT_ARG();
+			if (get_u32(&filter.xpinfo.index, *argv, 0))
+				invarg("\"INDEX\" is invalid", *argv);
+
+			filter.index_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "ptype") == 0) {
+			NEXT_ARG();
+			xfrm_policy_ptype_parse(&filter.ptype, &argc, &argv);
+
+			filter.ptype_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "action") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "allow") == 0)
+				filter.xpinfo.action = XFRM_POLICY_ALLOW;
+			else if (strcmp(*argv, "block") == 0)
+				filter.xpinfo.action = XFRM_POLICY_BLOCK;
+			else
+				invarg("\"ACTION\" is invalid\n", *argv);
+
+			filter.action_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "priority") == 0) {
+			NEXT_ARG();
+			if (get_u32(&filter.xpinfo.priority, *argv, 0))
+				invarg("\"PRIORITY\" is invalid", *argv);
+
+			filter.priority_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "flag") == 0) {
+			NEXT_ARG();
+			xfrm_policy_flag_parse(&filter.xpinfo.flags, &argc,
+					       &argv);
+
+			filter.policy_flags_mask = XFRM_FILTER_MASK_FULL;
+
+		} else {
+			if (selp)
+				invarg("unknown", *argv);
+			selp = *argv;
+
+			xfrm_selector_parse(&filter.xpinfo.sel, &argc, &argv);
+			if (preferred_family == AF_UNSPEC)
+				preferred_family = filter.xpinfo.sel.family;
+
+		}
+
+		argc--; argv++;
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (deleteall) {
+		struct xfrm_buffer xb;
+		char buf[NLMSG_DELETEALL_BUF_SIZE];
+		int i;
+
+		xb.buf = buf;
+		xb.size = sizeof(buf);
+		xb.rth = &rth;
+
+		for (i = 0; ; i++) {
+			xb.offset = 0;
+			xb.nlmsg_count = 0;
+
+			if (show_stats > 1)
+				fprintf(stderr, "Delete-all round = %d\n", i);
+
+			if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) {
+				perror("Cannot send dump request");
+				exit(1);
+			}
+
+			if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb) < 0) {
+				fprintf(stderr, "Delete-all terminated\n");
+				exit(1);
+			}
+			if (xb.nlmsg_count == 0) {
+				if (show_stats > 1)
+					fprintf(stderr, "Delete-all completed\n");
+				break;
+			}
+
+			if (rtnl_send_check(&rth, xb.buf, xb.offset) < 0) {
+				perror("Failed to send delete-all request");
+				exit(1);
+			}
+			if (show_stats > 1)
+				fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count);
+
+			xb.offset = 0;
+			xb.nlmsg_count = 0;
+		}
+	} else {
+		if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) {
+			perror("Cannot send dump request");
+			exit(1);
+		}
+
+		if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout) < 0) {
+			fprintf(stderr, "Dump terminated\n");
+			exit(1);
+		}
+	}
+
+	rtnl_close(&rth);
+
+	exit(0);
+}
+
+int print_spdinfo( struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	__u32 *f = NLMSG_DATA(n);
+	struct rtattr * tb[XFRMA_SPD_MAX+1];
+	struct rtattr * rta;
+
+	int len = n->nlmsg_len;
+
+	len -= NLMSG_LENGTH(sizeof(__u32));
+	if (len < 0) {
+		fprintf(stderr, "SPDinfo: Wrong len %d\n", len);
+		return -1;
+	}
+
+	rta = XFRMSAPD_RTA(f);
+	parse_rtattr(tb, XFRMA_SPD_MAX, rta, len);
+
+	fprintf(fp,"\t SPD");
+	if (tb[XFRMA_SPD_INFO]) {
+		struct xfrmu_spdinfo *si;
+
+		if (RTA_PAYLOAD(tb[XFRMA_SPD_INFO]) < sizeof(*si)) {
+			fprintf(stderr, "SPDinfo: Wrong len %d\n", len);
+			return -1;
+		}
+		si = RTA_DATA(tb[XFRMA_SPD_INFO]);
+		fprintf(fp," IN  %d", si->incnt);
+		fprintf(fp," OUT %d", si->outcnt);
+		fprintf(fp," FWD %d", si->fwdcnt);
+
+		if (show_stats) {
+			fprintf(fp," (Sock:");
+			fprintf(fp," IN %d", si->inscnt);
+			fprintf(fp," OUT %d", si->outscnt);
+			fprintf(fp," FWD %d", si->fwdscnt);
+			fprintf(fp,")");
+		}
+
+		fprintf(fp,"\n");
+	}
+	if (show_stats > 1) {
+		struct xfrmu_spdhinfo *sh;
+
+		if (tb[XFRMA_SPD_HINFO]) {
+			if (RTA_PAYLOAD(tb[XFRMA_SPD_HINFO]) < sizeof(*sh)) {
+				fprintf(stderr, "SPDinfo: Wrong len %d\n", len);
+				return -1;
+			}
+			sh = RTA_DATA(tb[XFRMA_SPD_HINFO]);
+			fprintf(fp,"\t SPD buckets:");
+			fprintf(fp," count %d", sh->spdhcnt);
+			fprintf(fp," Max %d", sh->spdhmcnt);
+		}
+	}
+	fprintf(fp,"\n");
+
+        return 0;
+}
+
+static int xfrm_spd_getinfo(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		__u32				flags;
+		char 				ans[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_GETSPDINFO;
+	req.flags = 0XFFFFFFFF;
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
+		exit(2);
+
+	print_spdinfo(&req.n, (void*)stdout);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+static int xfrm_policy_flush(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr	n;
+		char		buf[RTA_BUF_SIZE];
+	} req;
+	char *ptypep = NULL;
+	struct xfrm_userpolicy_type upt;
+
+	memset(&req, 0, sizeof(req));
+	memset(&upt, 0, sizeof(upt));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(0); /* nlmsg data is nothing */
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_FLUSHPOLICY;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "ptype") == 0) {
+			if (ptypep)
+				duparg("ptype", *argv);
+			ptypep = *argv;
+
+			NEXT_ARG();
+			xfrm_policy_ptype_parse(&upt.type, &argc, &argv);
+		} else
+			invarg("unknown", *argv);
+
+		argc--; argv++;
+	}
+
+	if (ptypep) {
+		addattr_l(&req.n, sizeof(req), XFRMA_POLICY_TYPE,
+			  (void *)&upt, sizeof(upt));
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (show_stats > 1)
+		fprintf(stderr, "Flush policy\n");
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+int do_xfrm_policy(int argc, char **argv)
+{
+	if (argc < 1)
+		return xfrm_policy_list_or_deleteall(0, NULL, 0);
+
+	if (matches(*argv, "add") == 0)
+		return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, 0,
+					  argc-1, argv+1);
+	if (matches(*argv, "update") == 0)
+		return xfrm_policy_modify(XFRM_MSG_UPDPOLICY, 0,
+					  argc-1, argv+1);
+	if (matches(*argv, "delete") == 0)
+		return xfrm_policy_delete(argc-1, argv+1);
+	if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0)
+		return xfrm_policy_list_or_deleteall(argc-1, argv+1, 1);
+	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+	    || matches(*argv, "lst") == 0)
+		return xfrm_policy_list_or_deleteall(argc-1, argv+1, 0);
+	if (matches(*argv, "get") == 0)
+		return xfrm_policy_get(argc-1, argv+1);
+	if (matches(*argv, "flush") == 0)
+		return xfrm_policy_flush(argc-1, argv+1);
+	if (matches(*argv, "count") == 0)
+		return xfrm_spd_getinfo(argc, argv);
+	if (matches(*argv, "help") == 0)
+		usage();
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm policy help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_state.c b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_state.c
new file mode 100755
index 0000000..0d98e78
--- /dev/null
+++ b/ap/app/iproute2/iproute2-3.4.0/ip/xfrm_state.c
@@ -0,0 +1,1216 @@
+/* $USAGI: $ */
+
+/*
+ * Copyright (C)2004 USAGI/WIDE Project
+ *
+ * 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
+ */
+/*
+ * based on iproute.c
+ */
+/*
+ * Authors:
+ *	Masahide NAKAMURA @USAGI
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <linux/xfrm.h>
+#include "utils.h"
+#include "xfrm.h"
+#include "ip_common.h"
+
+//#define NLMSG_DELETEALL_BUF_SIZE (4096-512)
+#define NLMSG_DELETEALL_BUF_SIZE 8192
+
+/*
+ * Receiving buffer defines:
+ * nlmsg
+ *   data = struct xfrm_usersa_info
+ *   rtattr
+ *   rtattr
+ *   ... (max count of rtattr is XFRM_MAX+1
+ *
+ *  each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t
+ */
+#define NLMSG_BUF_SIZE 4096
+#define RTA_BUF_SIZE 2048
+#define XFRM_ALGO_KEY_BUF_SIZE 512
+#define CTX_BUF_SIZE 256
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n");
+	fprintf(stderr, "        [ mark MARK [ mask MASK ] ] [ reqid REQID ] [ seq SEQ ]\n");
+	fprintf(stderr, "        [ replay-window SIZE ] [ replay-seq SEQ ] [ replay-oseq SEQ ]\n");
+	fprintf(stderr, "        [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n");
+	fprintf(stderr, "        [ coa ADDR[/PLEN] ] [ ctx CTX ]\n");
+	fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n");
+	fprintf(stderr, "        [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n");
+	fprintf(stderr, "Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n");
+	fprintf(stderr, "Usage: ip xfrm state { deleteall | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n");
+	fprintf(stderr, "        [ flag FLAG-LIST ]\n");
+	fprintf(stderr, "Usage: ip xfrm state flush [ proto XFRM-PROTO ]\n");
+	fprintf(stderr, "Usage: ip xfrm state count\n");
+	fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n");
+	fprintf(stderr, "XFRM-PROTO := ");
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP));
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH));
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_COMP));
+	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING));
+	fprintf(stderr, "%s\n", strxf_xfrmproto(IPPROTO_DSTOPTS));
+	fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] ALGO\n");
+	fprintf(stderr, "ALGO := { ");
+	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT));
+	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AUTH));
+	fprintf(stderr, "%s", strxf_algotype(XFRMA_ALG_COMP));
+	fprintf(stderr, " } ALGO-NAME ALGO-KEY |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AEAD));
+	fprintf(stderr, " ALGO-NAME ALGO-KEY ALGO-ICV-LEN |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC));
+	fprintf(stderr, " ALGO-NAME ALGO-KEY ALGO-TRUNC-LEN\n");
+ 	fprintf(stderr, "MODE := transport | tunnel | ro | in_trigger | beet\n");
+	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
+	fprintf(stderr, "FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4\n");
+	fprintf(stderr, "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n");
+	fprintf(stderr, "UPSPEC := proto { { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_TCP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_UDP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_SCTP));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_DCCP));
+	fprintf(stderr, " } [ sport PORT ] [ dport PORT ] |\n");
+	fprintf(stderr, "                  { ");
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMP));
+	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMPV6));
+	fprintf(stderr, "%s", strxf_proto(IPPROTO_MH));
+	fprintf(stderr, " } [ type NUMBER ] [ code NUMBER ] |\n");
+	fprintf(stderr, "                  %s", strxf_proto(IPPROTO_GRE));
+	fprintf(stderr, " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n");
+	fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n");
+	fprintf(stderr, "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n");
+	fprintf(stderr, "         { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n");
+        fprintf(stderr, "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n");
+
+	exit(-1);
+}
+
+static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type,
+			   char *name, char *key, char *buf, int max)
+{
+	int len;
+	int slen = strlen(key);
+
+#if 0
+	/* XXX: verifying both name and key is required! */
+	fprintf(stderr, "warning: ALGO-NAME/ALGO-KEY will send to kernel promiscuously! (verifying them isn't implemented yet)\n");
+#endif
+
+	strncpy(alg->alg_name, name, sizeof(alg->alg_name));
+
+	if (slen > 2 && strncmp(key, "0x", 2) == 0) {
+		/* split two chars "0x" from the top */
+		char *p = key + 2;
+		int plen = slen - 2;
+		int i;
+		int j;
+
+		/* Converting hexadecimal numbered string into real key;
+		 * Convert each two chars into one char(value). If number
+		 * of the length is odd, add zero on the top for rounding.
+		 */
+
+		/* calculate length of the converted values(real key) */
+		len = (plen + 1) / 2;
+		if (len > max)
+			invarg("\"ALGO-KEY\" makes buffer overflow\n", key);
+
+		for (i = - (plen % 2), j = 0; j < len; i += 2, j++) {
+			char vbuf[3];
+			__u8 val;
+
+			vbuf[0] = i >= 0 ? p[i] : '0';
+			vbuf[1] = p[i + 1];
+			vbuf[2] = '\0';
+
+			if (get_u8(&val, vbuf, 16))
+				invarg("\"ALGO-KEY\" is invalid", key);
+
+			buf[j] = val;
+		}
+	} else {
+		len = slen;
+		if (len > 0) {
+			if (len > max)
+				invarg("\"ALGO-KEY\" makes buffer overflow\n", key);
+
+			strncpy(buf, key, len);
+		}
+	}
+
+	alg->alg_key_len = len * 8;
+
+	return 0;
+}
+
+static int xfrm_seq_parse(__u32 *seq, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+
+	if (get_u32(seq, *argv, 0))
+		invarg("\"SEQ\" is invalid", *argv);
+
+	*seq = htonl(*seq);
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	int len = strlen(*argv);
+
+	if (len > 2 && strncmp(*argv, "0x", 2) == 0) {
+		__u8 val = 0;
+
+		if (get_u8(&val, *argv, 16))
+			invarg("\"FLAG\" is invalid", *argv);
+		*flags = val;
+	} else {
+		while (1) {
+			if (strcmp(*argv, "noecn") == 0)
+				*flags |= XFRM_STATE_NOECN;
+			else if (strcmp(*argv, "decap-dscp") == 0)
+				*flags |= XFRM_STATE_DECAP_DSCP;
+			else if (strcmp(*argv, "nopmtudisc") == 0)
+				*flags |= XFRM_STATE_NOPMTUDISC;
+			else if (strcmp(*argv, "wildrecv") == 0)
+				*flags |= XFRM_STATE_WILDRECV;
+			else if (strcmp(*argv, "icmp") == 0)
+				*flags |= XFRM_STATE_ICMP;
+			else if (strcmp(*argv, "af-unspec") == 0)
+				*flags |= XFRM_STATE_AF_UNSPEC;
+			else if (strcmp(*argv, "align4") == 0)
+				*flags |= XFRM_STATE_ALIGN4;
+			else {
+				PREV_ARG(); /* back track */
+				break;
+			}
+
+			if (!NEXT_ARG_OK())
+				break;
+			NEXT_ARG();
+		}
+	}
+
+	*argcp = argc;
+	*argvp = argv;
+
+	return 0;
+}
+
+static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr 	n;
+		struct xfrm_usersa_info xsinfo;
+		char   			buf[RTA_BUF_SIZE];
+	} req;
+	struct xfrm_replay_state replay;
+	char *idp = NULL;
+	char *aeadop = NULL;
+	char *ealgop = NULL;
+	char *aalgop = NULL;
+	char *calgop = NULL;
+	char *coap = NULL;
+	char *sctxp = NULL;
+	struct xfrm_mark mark = {0, 0};
+	struct {
+		struct xfrm_user_sec_ctx sctx;
+		char    str[CTX_BUF_SIZE];
+	} ctx;
+
+	memset(&req, 0, sizeof(req));
+	memset(&replay, 0, sizeof(replay));
+	memset(&ctx, 0, sizeof(ctx));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.xsinfo.family = preferred_family;
+
+	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
+	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
+	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
+	req.xsinfo.lft.hard_packet_limit = XFRM_INF;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv);
+		} else if (strcmp(*argv, "mark") == 0) {
+			xfrm_parse_mark(&mark, &argc, &argv);
+		} else if (strcmp(*argv, "reqid") == 0) {
+			NEXT_ARG();
+			xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv);
+		} else if (strcmp(*argv, "seq") == 0) {
+			NEXT_ARG();
+			xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv);
+		} else if (strcmp(*argv, "replay-window") == 0) {
+			NEXT_ARG();
+			if (get_u8(&req.xsinfo.replay_window, *argv, 0))
+				invarg("\"replay-window\" value is invalid", *argv);
+		} else if (strcmp(*argv, "replay-seq") == 0) {
+			NEXT_ARG();
+			if (get_u32(&replay.seq, *argv, 0))
+				invarg("\"replay-seq\" value is invalid", *argv);
+		} else if (strcmp(*argv, "replay-oseq") == 0) {
+			NEXT_ARG();
+			if (get_u32(&replay.oseq, *argv, 0))
+				invarg("\"replay-oseq\" value is invalid", *argv);
+		} else if (strcmp(*argv, "flag") == 0) {
+			NEXT_ARG();
+			xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv);
+		} else if (strcmp(*argv, "sel") == 0) {
+			NEXT_ARG();
+			xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv);
+		} else if (strcmp(*argv, "limit") == 0) {
+			NEXT_ARG();
+			xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv);
+		} else if (strcmp(*argv, "encap") == 0) {
+			struct xfrm_encap_tmpl encap;
+			inet_prefix oa;
+		        NEXT_ARG();
+			xfrm_encap_type_parse(&encap.encap_type, &argc, &argv);
+			NEXT_ARG();
+			if (get_u16(&encap.encap_sport, *argv, 0))
+				invarg("\"encap\" sport value is invalid", *argv);
+			encap.encap_sport = htons(encap.encap_sport);
+			NEXT_ARG();
+			if (get_u16(&encap.encap_dport, *argv, 0))
+				invarg("\"encap\" dport value is invalid", *argv);
+			encap.encap_dport = htons(encap.encap_dport);
+			NEXT_ARG();
+			get_addr(&oa, *argv, AF_UNSPEC);
+			memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa));
+			addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP,
+				  (void *)&encap, sizeof(encap));
+		} else if (strcmp(*argv, "coa") == 0) {
+			inet_prefix coa;
+			xfrm_address_t xcoa;
+
+			if (coap)
+				duparg("coa", *argv);
+			coap = *argv;
+
+			NEXT_ARG();
+
+			get_prefix(&coa, *argv, preferred_family);
+			if (coa.family == AF_UNSPEC)
+				invarg("\"coa\" address family is AF_UNSPEC", *argv);
+			if (coa.bytelen > sizeof(xcoa))
+				invarg("\"coa\" address length is too large", *argv);
+
+			memset(&xcoa, 0, sizeof(xcoa));
+			memcpy(&xcoa, &coa.data, coa.bytelen);
+
+			addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR,
+				  (void *)&xcoa, sizeof(xcoa));
+		} else if (strcmp(*argv, "ctx") == 0) {
+			char *context;
+
+			if (sctxp)
+				duparg("ctx", *argv);
+			sctxp = *argv;
+
+			NEXT_ARG();
+			context = *argv;
+
+			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
+			addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX,
+				  (void *)&ctx, ctx.sctx.len);
+		} else {
+			/* try to assume ALGO */
+			int type = xfrm_algotype_getbyname(*argv);
+			switch (type) {
+			case XFRMA_ALG_AEAD:
+			case XFRMA_ALG_CRYPT:
+			case XFRMA_ALG_AUTH:
+			case XFRMA_ALG_AUTH_TRUNC:
+			case XFRMA_ALG_COMP:
+			{
+				/* ALGO */
+				struct {
+					union {
+						struct xfrm_algo alg;
+						struct xfrm_algo_aead aead;
+						struct xfrm_algo_auth auth;
+					} u;
+					char buf[XFRM_ALGO_KEY_BUF_SIZE];
+				} alg = {};
+				int len;
+				__u32 icvlen, trunclen;
+				char *name;
+				char *key;
+				char *buf;
+
+				switch (type) {
+				case XFRMA_ALG_AEAD:
+					if (aeadop)
+						duparg("ALGO-TYPE", *argv);
+					aeadop = *argv;
+					break;
+				case XFRMA_ALG_CRYPT:
+					if (ealgop)
+						duparg("ALGO-TYPE", *argv);
+					ealgop = *argv;
+					break;
+				case XFRMA_ALG_AUTH:
+				case XFRMA_ALG_AUTH_TRUNC:
+					if (aalgop)
+						duparg("ALGO-TYPE", *argv);
+					aalgop = *argv;
+					break;
+				case XFRMA_ALG_COMP:
+					if (calgop)
+						duparg("ALGO-TYPE", *argv);
+					calgop = *argv;
+					break;
+				default:
+					/* not reached */
+					invarg("\"ALGO-TYPE\" is invalid\n", *argv);
+				}
+
+				if (!NEXT_ARG_OK())
+					missarg("ALGO-NAME");
+				NEXT_ARG();
+				name = *argv;
+
+				if (!NEXT_ARG_OK())
+					missarg("ALGO-KEY");
+				NEXT_ARG();
+				key = *argv;
+
+				buf = alg.u.alg.alg_key;
+				len = sizeof(alg.u.alg);
+
+				switch (type) {
+				case XFRMA_ALG_AEAD:
+					if (!NEXT_ARG_OK())
+						missarg("ALGO-ICV-LEN");
+					NEXT_ARG();
+					if (get_u32(&icvlen, *argv, 0))
+						invarg("\"aead\" ICV length is invalid",
+						       *argv);
+					alg.u.aead.alg_icv_len = icvlen;
+
+					buf = alg.u.aead.alg_key;
+					len = sizeof(alg.u.aead);
+					break;
+				case XFRMA_ALG_AUTH_TRUNC:
+					if (!NEXT_ARG_OK())
+						missarg("ALGO-TRUNC-LEN");
+					NEXT_ARG();
+					if (get_u32(&trunclen, *argv, 0))
+						invarg("\"auth\" trunc length is invalid",
+						       *argv);
+					alg.u.auth.alg_trunc_len = trunclen;
+
+					buf = alg.u.auth.alg_key;
+					len = sizeof(alg.u.auth);
+					break;
+				}
+
+				xfrm_algo_parse((void *)&alg, type, name, key,
+						buf, sizeof(alg.buf));
+				len += alg.u.alg.alg_key_len;
+
+				addattr_l(&req.n, sizeof(req.buf), type,
+					  (void *)&alg, len);
+				break;
+			}
+			default:
+				/* try to assume ID */
+				if (idp)
+					invarg("unknown", *argv);
+				idp = *argv;
+
+				/* ID */
+				xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id,
+					      &req.xsinfo.family, 0, &argc, &argv);
+				if (preferred_family == AF_UNSPEC)
+					preferred_family = req.xsinfo.family;
+			}
+		}
+		argc--; argv++;
+	}
+
+	if (replay.seq || replay.oseq)
+		addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL,
+			  (void *)&replay, sizeof(replay));
+
+	if (!idp) {
+		fprintf(stderr, "Not enough information: \"ID\" is required\n");
+		exit(1);
+	}
+
+	if (mark.m & mark.v) {
+		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
+				  (void *)&mark, sizeof(mark));
+		if (r < 0) {
+			fprintf(stderr, "XFRMA_MARK failed\n");
+			exit(1);
+		}
+	}
+
+	switch (req.xsinfo.mode) {
+	case XFRM_MODE_TRANSPORT:
+	case XFRM_MODE_TUNNEL:
+		if (!xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
+			fprintf(stderr, "\"mode\" is invalid with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit(1);
+		}
+		break;
+	case XFRM_MODE_ROUTEOPTIMIZATION:
+	case XFRM_MODE_IN_TRIGGER:
+		if (!xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
+			fprintf(stderr, "\"mode\" is invalid with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit(1);
+		}
+		if (req.xsinfo.id.spi != 0) {
+			fprintf(stderr, "\"spi\" must be 0 with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit(1);
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (aeadop || ealgop || aalgop || calgop) {
+		if (!xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
+			fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit(1);
+		}
+	} else {
+		if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
+			fprintf(stderr, "\"ALGO\" is required with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit (1);
+		}
+	}
+
+	if (coap) {
+		if (!xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
+			fprintf(stderr, "\"coa\" is invalid with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit(1);
+		}
+	} else {
+		if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
+			fprintf(stderr, "\"coa\" is required with proto=%s\n",
+				strxf_xfrmproto(req.xsinfo.id.proto));
+			exit (1);
+		}
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (req.xsinfo.family == AF_UNSPEC)
+		req.xsinfo.family = AF_INET;
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+static int xfrm_state_allocspi(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr 	n;
+		struct xfrm_userspi_info xspi;
+		char   			buf[RTA_BUF_SIZE];
+	} req;
+	char *idp = NULL;
+	char *minp = NULL;
+	char *maxp = NULL;
+	struct xfrm_mark mark = {0, 0};
+	char res_buf[NLMSG_BUF_SIZE];
+	struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf;
+
+	memset(res_buf, 0, sizeof(res_buf));
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_ALLOCSPI;
+	req.xspi.info.family = preferred_family;
+
+#if 0
+	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
+	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
+	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
+	req.xsinfo.lft.hard_packet_limit = XFRM_INF;
+#endif
+
+	while (argc > 0) {
+		if (strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			xfrm_mode_parse(&req.xspi.info.mode, &argc, &argv);
+		} else if (strcmp(*argv, "mark") == 0) {
+			xfrm_parse_mark(&mark, &argc, &argv);
+		} else if (strcmp(*argv, "reqid") == 0) {
+			NEXT_ARG();
+			xfrm_reqid_parse(&req.xspi.info.reqid, &argc, &argv);
+		} else if (strcmp(*argv, "seq") == 0) {
+			NEXT_ARG();
+			xfrm_seq_parse(&req.xspi.info.seq, &argc, &argv);
+		} else if (strcmp(*argv, "min") == 0) {
+			if (minp)
+				duparg("min", *argv);
+			minp = *argv;
+
+			NEXT_ARG();
+
+			if (get_u32(&req.xspi.min, *argv, 0))
+				invarg("\"min\" value is invalid", *argv);
+		} else if (strcmp(*argv, "max") == 0) {
+			if (maxp)
+				duparg("max", *argv);
+			maxp = *argv;
+
+			NEXT_ARG();
+
+			if (get_u32(&req.xspi.max, *argv, 0))
+				invarg("\"max\" value is invalid", *argv);
+		} else {
+			/* try to assume ID */
+			if (idp)
+				invarg("unknown", *argv);
+			idp = *argv;
+
+			/* ID */
+			xfrm_id_parse(&req.xspi.info.saddr, &req.xspi.info.id,
+				      &req.xspi.info.family, 0, &argc, &argv);
+			if (req.xspi.info.id.spi) {
+				fprintf(stderr, "\"SPI\" must be zero\n");
+				exit(1);
+			}
+			if (preferred_family == AF_UNSPEC)
+				preferred_family = req.xspi.info.family;
+		}
+		argc--; argv++;
+	}
+
+	if (!idp) {
+		fprintf(stderr, "Not enough information: \"ID\" is required\n");
+		exit(1);
+	}
+
+	if (minp) {
+		if (!maxp) {
+			fprintf(stderr, "\"max\" is missing\n");
+			exit(1);
+		}
+		if (req.xspi.min > req.xspi.max) {
+			fprintf(stderr, "\"min\" value is larger than \"max\" value\n");
+			exit(1);
+		}
+	} else {
+		if (maxp) {
+			fprintf(stderr, "\"min\" is missing\n");
+			exit(1);
+		}
+
+		/* XXX: Default value defined in PF_KEY;
+		 * See kernel's net/key/af_key.c(pfkey_getspi).
+		 */
+		req.xspi.min = 0x100;
+		req.xspi.max = 0x0fffffff;
+
+		/* XXX: IPCOMP spi is 16-bits;
+		 * See kernel's net/xfrm/xfrm_user(verify_userspi_info).
+		 */
+		if (req.xspi.info.id.proto == IPPROTO_COMP)
+			req.xspi.max = 0xffff;
+	}
+
+	if (mark.m & mark.v) {
+		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
+				  (void *)&mark, sizeof(mark));
+		if (r < 0) {
+			fprintf(stderr, "XFRMA_MARK failed\n");
+			exit(1);
+		}
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (req.xspi.info.family == AF_UNSPEC)
+		req.xspi.info.family = AF_INET;
+
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
+		exit(2);
+
+	if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
+		fprintf(stderr, "An error :-)\n");
+		exit(1);
+	}
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo)
+{
+	if (!filter.use)
+		return 1;
+
+	if (filter.id_src_mask)
+		if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr,
+				    filter.id_src_mask))
+			return 0;
+	if (filter.id_dst_mask)
+		if (xfrm_addr_match(&xsinfo->id.daddr, &filter.xsinfo.id.daddr,
+				    filter.id_dst_mask))
+			return 0;
+	if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask)
+		return 0;
+	if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask)
+		return 0;
+	if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask)
+		return 0;
+	if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask)
+		return 0;
+	if (filter.state_flags_mask)
+		if ((xsinfo->flags & filter.xsinfo.flags) == 0)
+			return 0;
+
+	return 1;
+}
+
+int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		     void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct rtattr * tb[XFRMA_MAX+1];
+	struct rtattr * rta;
+	struct xfrm_usersa_info *xsinfo = NULL;
+	struct xfrm_user_expire *xexp = NULL;
+	struct xfrm_usersa_id	*xsid = NULL;
+	int len = n->nlmsg_len;
+
+	if (n->nlmsg_type != XFRM_MSG_NEWSA &&
+	    n->nlmsg_type != XFRM_MSG_DELSA &&
+	    n->nlmsg_type != XFRM_MSG_UPDSA &&
+	    n->nlmsg_type != XFRM_MSG_EXPIRE) {
+		fprintf(stderr, "Not a state: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	if (n->nlmsg_type == XFRM_MSG_DELSA) {
+		/* Dont blame me for this .. Herbert made me do it */
+		xsid = NLMSG_DATA(n);
+		len -= NLMSG_SPACE(sizeof(*xsid));
+	} else if (n->nlmsg_type == XFRM_MSG_EXPIRE) {
+		xexp = NLMSG_DATA(n);
+		xsinfo = &xexp->state;
+		len -= NLMSG_SPACE(sizeof(*xexp));
+	} else {
+		xexp = NULL;
+		xsinfo = NLMSG_DATA(n);
+		len -= NLMSG_SPACE(sizeof(*xsinfo));
+	}
+
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (xsinfo && !xfrm_state_filter_match(xsinfo))
+		return 0;
+
+	if (n->nlmsg_type == XFRM_MSG_DELSA)
+		fprintf(fp, "Deleted ");
+	else if (n->nlmsg_type == XFRM_MSG_UPDSA)
+		fprintf(fp, "Updated ");
+	else if (n->nlmsg_type == XFRM_MSG_EXPIRE)
+		fprintf(fp, "Expired ");
+
+	if (n->nlmsg_type == XFRM_MSG_DELSA)
+		rta = XFRMSID_RTA(xsid);
+	else if (n->nlmsg_type == XFRM_MSG_EXPIRE)
+		rta = XFRMEXP_RTA(xexp);
+	else
+		rta = XFRMS_RTA(xsinfo);
+
+	parse_rtattr(tb, XFRMA_MAX, rta, len);
+
+	if (n->nlmsg_type == XFRM_MSG_DELSA) {
+		//xfrm_policy_id_print();
+
+		if (!tb[XFRMA_SA]) {
+			fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n");
+			return -1;
+		}
+		if (RTA_PAYLOAD(tb[XFRMA_SA]) < sizeof(*xsinfo)) {
+			fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n");
+			return -1;
+		}
+		xsinfo = RTA_DATA(tb[XFRMA_SA]);
+	}
+
+	xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL);
+
+	if (n->nlmsg_type == XFRM_MSG_EXPIRE) {
+		fprintf(fp, "\t");
+		fprintf(fp, "hard %u", xexp->hard);
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (oneline)
+		fprintf(fp, "\n");
+	fflush(fp);
+
+	return 0;
+}
+
+static int xfrm_state_get_or_delete(int argc, char **argv, int delete)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr 	n;
+		struct xfrm_usersa_id	xsid;
+		char   			buf[RTA_BUF_SIZE];
+	} req;
+	struct xfrm_id id;
+	char *idp = NULL;
+	struct xfrm_mark mark = {0, 0};
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA;
+	req.xsid.family = preferred_family;
+
+	while (argc > 0) {
+		xfrm_address_t saddr;
+
+		if (strcmp(*argv, "mark") == 0) {
+			xfrm_parse_mark(&mark, &argc, &argv);
+		} else {
+			if (idp)
+				invarg("unknown", *argv);
+			idp = *argv;
+
+			/* ID */
+			memset(&id, 0, sizeof(id));
+			memset(&saddr, 0, sizeof(saddr));
+			xfrm_id_parse(&saddr, &id, &req.xsid.family, 0,
+				      &argc, &argv);
+
+			memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr));
+			req.xsid.spi = id.spi;
+			req.xsid.proto = id.proto;
+
+			addattr_l(&req.n, sizeof(req.buf), XFRMA_SRCADDR,
+				  (void *)&saddr, sizeof(saddr));
+		}
+
+		argc--; argv++;
+	}
+
+	if (mark.m & mark.v) {
+		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
+				  (void *)&mark, sizeof(mark));
+		if (r < 0) {
+			fprintf(stderr, "XFRMA_MARK failed\n");
+			exit(1);
+		}
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (req.xsid.family == AF_UNSPEC)
+		req.xsid.family = AF_INET;
+
+	if (delete) {
+		if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+			exit(2);
+	} else {
+		char buf[NLMSG_BUF_SIZE];
+		struct nlmsghdr *res_n = (struct nlmsghdr *)buf;
+
+		memset(buf, 0, sizeof(buf));
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
+			exit(2);
+
+		if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
+			fprintf(stderr, "An error :-)\n");
+			exit(1);
+		}
+	}
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+/*
+ * With an existing state of nlmsg, make new nlmsg for deleting the state
+ * and store it to buffer.
+ */
+static int xfrm_state_keep(const struct sockaddr_nl *who,
+			   struct nlmsghdr *n,
+			   void *arg)
+{
+	struct xfrm_buffer *xb = (struct xfrm_buffer *)arg;
+	struct rtnl_handle *rth = xb->rth;
+	struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct nlmsghdr *new_n;
+	struct xfrm_usersa_id *xsid;
+
+	if (n->nlmsg_type != XFRM_MSG_NEWSA) {
+		fprintf(stderr, "Not a state: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*xsinfo));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (!xfrm_state_filter_match(xsinfo))
+		return 0;
+
+	if (xb->offset > xb->size) {
+		fprintf(stderr, "State buffer overflow\n");
+		return -1;
+	}
+
+	new_n = (struct nlmsghdr *)(xb->buf + xb->offset);
+	new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid));
+	new_n->nlmsg_flags = NLM_F_REQUEST;
+	new_n->nlmsg_type = XFRM_MSG_DELSA;
+	new_n->nlmsg_seq = ++rth->seq;
+
+	xsid = NLMSG_DATA(new_n);
+	xsid->family = xsinfo->family;
+	memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr));
+	xsid->spi = xsinfo->id.spi;
+	xsid->proto = xsinfo->id.proto;
+
+	addattr_l(new_n, xb->size, XFRMA_SRCADDR, &xsinfo->saddr,
+		  sizeof(xsid->daddr));
+
+	xb->offset += new_n->nlmsg_len;
+	xb->nlmsg_count ++;
+
+	return 0;
+}
+
+static int xfrm_state_list_or_deleteall(int argc, char **argv, int deleteall)
+{
+	char *idp = NULL;
+	struct rtnl_handle rth;
+
+	if(argc > 0)
+		filter.use = 1;
+	filter.xsinfo.family = preferred_family;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv);
+
+			filter.mode_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "reqid") == 0) {
+			NEXT_ARG();
+			xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv);
+
+			filter.reqid_mask = XFRM_FILTER_MASK_FULL;
+
+		} else if (strcmp(*argv, "flag") == 0) {
+			NEXT_ARG();
+			xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv);
+
+			filter.state_flags_mask = XFRM_FILTER_MASK_FULL;
+
+		} else {
+			if (idp)
+				invarg("unknown", *argv);
+			idp = *argv;
+
+			/* ID */
+			xfrm_id_parse(&filter.xsinfo.saddr, &filter.xsinfo.id,
+				      &filter.xsinfo.family, 1, &argc, &argv);
+			if (preferred_family == AF_UNSPEC)
+				preferred_family = filter.xsinfo.family;
+		}
+		argc--; argv++;
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (deleteall) {
+		struct xfrm_buffer xb;
+		char buf[NLMSG_DELETEALL_BUF_SIZE];
+		int i;
+
+		xb.buf = buf;
+		xb.size = sizeof(buf);
+		xb.rth = &rth;
+
+		for (i = 0; ; i++) {
+			xb.offset = 0;
+			xb.nlmsg_count = 0;
+
+			if (show_stats > 1)
+				fprintf(stderr, "Delete-all round = %d\n", i);
+
+			if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) {
+				perror("Cannot send dump request");
+				exit(1);
+			}
+
+			if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb) < 0) {
+				fprintf(stderr, "Delete-all terminated\n");
+				exit(1);
+			}
+			if (xb.nlmsg_count == 0) {
+				if (show_stats > 1)
+					fprintf(stderr, "Delete-all completed\n");
+				break;
+			}
+
+			if (rtnl_send_check(&rth, xb.buf, xb.offset) < 0) {
+				perror("Failed to send delete-all request\n");
+				exit(1);
+			}
+			if (show_stats > 1)
+				fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count);
+
+			xb.offset = 0;
+			xb.nlmsg_count = 0;
+		}
+
+	} else {
+		if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) {
+			perror("Cannot send dump request");
+			exit(1);
+		}
+
+		if (rtnl_dump_filter(&rth, xfrm_state_print, stdout) < 0) {
+			fprintf(stderr, "Dump terminated\n");
+			exit(1);
+		}
+	}
+
+	rtnl_close(&rth);
+
+	exit(0);
+}
+
+int print_sadinfo(struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	__u32 *f = NLMSG_DATA(n);
+	struct rtattr *tb[XFRMA_SAD_MAX+1];
+	struct rtattr *rta;
+	__u32 *cnt;
+
+	int len = n->nlmsg_len;
+
+	len -= NLMSG_LENGTH(sizeof(__u32));
+	if (len < 0) {
+		fprintf(stderr, "SADinfo: Wrong len %d\n", len);
+		return -1;
+	}
+
+	rta = XFRMSAPD_RTA(f);
+	parse_rtattr(tb, XFRMA_SAD_MAX, rta, len);
+
+	if (tb[XFRMA_SAD_CNT]) {
+		fprintf(fp,"\t SAD");
+		cnt = (__u32 *)RTA_DATA(tb[XFRMA_SAD_CNT]);
+		fprintf(fp," count %d", *cnt);
+	} else {
+		fprintf(fp,"BAD SAD info returned\n");
+		return -1;
+	}
+
+	if (show_stats) {
+		if (tb[XFRMA_SAD_HINFO]) {
+			struct xfrmu_sadhinfo *si;
+
+			if (RTA_PAYLOAD(tb[XFRMA_SAD_HINFO]) < sizeof(*si)) {
+				fprintf(fp,"BAD SAD length returned\n");
+				return -1;
+			}
+				
+			si = RTA_DATA(tb[XFRMA_SAD_HINFO]);
+			fprintf(fp," (buckets ");
+			fprintf(fp,"count %d", si->sadhcnt);
+			fprintf(fp," Max %d", si->sadhmcnt);
+			fprintf(fp,")");
+		}
+	}
+	fprintf(fp,"\n");
+
+        return 0;
+}
+
+static int xfrm_sad_getinfo(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		__u32				flags;
+		char				ans[64];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_GETSADINFO;
+	req.flags = 0XFFFFFFFF;
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
+		exit(2);
+
+	print_sadinfo(&req.n, (void*)stdout);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+static int xfrm_state_flush(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		struct xfrm_usersa_flush	xsf;
+	} req;
+	char *protop = NULL;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_FLUSHSA;
+	req.xsf.proto = 0;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "proto") == 0) {
+			int ret;
+
+			if (protop)
+				duparg("proto", *argv);
+			protop = *argv;
+
+			NEXT_ARG();
+
+			ret = xfrm_xfrmproto_getbyname(*argv);
+			if (ret < 0)
+				invarg("\"XFRM-PROTO\" is invalid", *argv);
+
+			req.xsf.proto = (__u8)ret;
+		} else
+			invarg("unknown", *argv);
+
+		argc--; argv++;
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (show_stats > 1)
+		fprintf(stderr, "Flush state proto=%s\n",
+			strxf_xfrmproto(req.xsf.proto));
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+int do_xfrm_state(int argc, char **argv)
+{
+	if (argc < 1)
+		return xfrm_state_list_or_deleteall(0, NULL, 0);
+
+	if (matches(*argv, "add") == 0)
+		return xfrm_state_modify(XFRM_MSG_NEWSA, 0,
+					 argc-1, argv+1);
+	if (matches(*argv, "update") == 0)
+		return xfrm_state_modify(XFRM_MSG_UPDSA, 0,
+					 argc-1, argv+1);
+	if (matches(*argv, "allocspi") == 0)
+		return xfrm_state_allocspi(argc-1, argv+1);
+	if (matches(*argv, "delete") == 0)
+		return xfrm_state_get_or_delete(argc-1, argv+1, 1);
+	if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0)
+		return xfrm_state_list_or_deleteall(argc-1, argv+1, 1);
+	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+	    || matches(*argv, "lst") == 0)
+		return xfrm_state_list_or_deleteall(argc-1, argv+1, 0);
+	if (matches(*argv, "get") == 0)
+		return xfrm_state_get_or_delete(argc-1, argv+1, 0);
+	if (matches(*argv, "flush") == 0)
+		return xfrm_state_flush(argc-1, argv+1);
+	if (matches(*argv, "count") == 0) {
+		return xfrm_sad_getinfo(argc, argv);
+	}
+	if (matches(*argv, "help") == 0)
+		usage();
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv);
+	exit(-1);
+}