aboutsummaryrefslogtreecommitdiff
path: root/networking/libiproute
diff options
context:
space:
mode:
Diffstat (limited to 'networking/libiproute')
-rw-r--r--networking/libiproute/Kbuild64
-rw-r--r--networking/libiproute/ip_common.h41
-rw-r--r--networking/libiproute/ip_parse_common_args.c84
-rw-r--r--networking/libiproute/ipaddress.c784
-rw-r--r--networking/libiproute/iplink.c306
-rw-r--r--networking/libiproute/iproute.c907
-rw-r--r--networking/libiproute/iprule.c334
-rw-r--r--networking/libiproute/iptunnel.c580
-rw-r--r--networking/libiproute/libnetlink.c409
-rw-r--r--networking/libiproute/libnetlink.h55
-rw-r--r--networking/libiproute/ll_addr.c79
-rw-r--r--networking/libiproute/ll_map.c200
-rw-r--r--networking/libiproute/ll_map.h21
-rw-r--r--networking/libiproute/ll_proto.c127
-rw-r--r--networking/libiproute/ll_types.c205
-rw-r--r--networking/libiproute/rt_names.c349
-rw-r--r--networking/libiproute/rt_names.h35
-rw-r--r--networking/libiproute/rtm_map.c118
-rw-r--r--networking/libiproute/rtm_map.h18
-rw-r--r--networking/libiproute/utils.c324
-rw-r--r--networking/libiproute/utils.h96
21 files changed, 5136 insertions, 0 deletions
diff --git a/networking/libiproute/Kbuild b/networking/libiproute/Kbuild
new file mode 100644
index 0000000..5f9dd32
--- /dev/null
+++ b/networking/libiproute/Kbuild
@@ -0,0 +1,64 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+
+lib-$(CONFIG_SLATTACH) += \
+ utils.o
+
+lib-$(CONFIG_IP) += \
+ ip_parse_common_args.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_proto.o \
+ ll_types.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_ADDRESS) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_LINK) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_ROUTE) += \
+ ip_parse_common_args.o \
+ iproute.o \
+ libnetlink.o \
+ ll_map.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_TUNNEL) += \
+ ip_parse_common_args.o \
+ iptunnel.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_RULE) += \
+ ip_parse_common_args.o \
+ iprule.o \
+ rt_names.o \
+ utils.o
diff --git a/networking/libiproute/ip_common.h b/networking/libiproute/ip_common.h
new file mode 100644
index 0000000..305b491
--- /dev/null
+++ b/networking/libiproute/ip_common.h
@@ -0,0 +1,41 @@
+/* vi: set sw=4 ts=4: */
+#ifndef _IP_COMMON_H
+#define _IP_COMMON_H 1
+
+#include "libbb.h"
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if !defined IFA_RTA
+#include <linux/if_addr.h>
+#endif
+#if !defined IFLA_RTA
+#include <linux/if_link.h>
+#endif
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+extern char **ip_parse_common_args(char **argv);
+extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ipaddr_list_or_flush(char **argv, int flush);
+extern int iproute_monitor(char **argv);
+extern void iplink_usage(void) NORETURN;
+extern void ipneigh_reset_filter(void);
+
+extern int do_ipaddr(char **argv);
+extern int do_iproute(char **argv);
+extern int do_iprule(char **argv);
+extern int do_ipneigh(char **argv);
+extern int do_iptunnel(char **argv);
+extern int do_iplink(char **argv);
+extern int do_ipmonitor(char **argv);
+extern int do_multiaddr(char **argv);
+extern int do_multiroute(char **argv);
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif /* ip_common.h */
diff --git a/networking/libiproute/ip_parse_common_args.c b/networking/libiproute/ip_parse_common_args.c
new file mode 100644
index 0000000..5e4012b
--- /dev/null
+++ b/networking/libiproute/ip_parse_common_args.c
@@ -0,0 +1,84 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "utils.h"
+
+family_t preferred_family = AF_UNSPEC;
+smallint oneline;
+char _SL_;
+
+char **ip_parse_common_args(char **argv)
+{
+ static const char ip_common_commands[] ALIGN1 =
+ "oneline" "\0"
+ "family" "\0"
+ "4" "\0"
+ "6" "\0"
+ "0" "\0"
+ ;
+ enum {
+ ARG_oneline,
+ ARG_family,
+ ARG_IPv4,
+ ARG_IPv6,
+ ARG_packet,
+ };
+ static const family_t af_numbers[] = { AF_INET, AF_INET6, AF_PACKET };
+ int arg;
+
+ while (*argv) {
+ char *opt = *argv;
+
+ if (opt[0] != '-')
+ break;
+ opt++;
+ if (opt[0] == '-') {
+ opt++;
+ if (!opt[0]) { /* "--" */
+ argv++;
+ break;
+ }
+ }
+ arg = index_in_substrings(ip_common_commands, opt);
+ if (arg < 0)
+ bb_show_usage();
+ if (arg == ARG_oneline) {
+ oneline = 1;
+ argv++;
+ continue;
+ }
+ if (arg == ARG_family) {
+ static const char families[] ALIGN1 =
+ "inet" "\0" "inet6" "\0" "link" "\0";
+ argv++;
+ if (!*argv)
+ bb_show_usage();
+ arg = index_in_strings(families, *argv);
+ if (arg < 0)
+ invarg(*argv, "protocol family");
+ /* now arg == 0, 1 or 2 */
+ } else {
+ arg -= ARG_IPv4;
+ /* now arg == 0, 1 or 2 */
+ }
+ preferred_family = af_numbers[arg];
+ argv++;
+ }
+ _SL_ = oneline ? '\\' : '\n';
+ return argv;
+}
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c
new file mode 100644
index 0000000..288dcca
--- /dev/null
+++ b/networking/libiproute/ipaddress.c
@@ -0,0 +1,784 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipaddress.c "ip address".
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include <fnmatch.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+#ifndef IFF_LOWER_UP
+/* from linux/if.h */
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up*/
+#endif
+
+typedef struct filter_t {
+ char *label;
+ char *flushb;
+ struct rtnl_handle *rth;
+ int scope, scopemask;
+ int flags, flagmask;
+ int flushp;
+ int flushe;
+ int ifindex;
+ family_t family;
+ smallint showqueue;
+ smallint oneline;
+ smallint up;
+ smallint flushed;
+ inet_prefix pfx;
+} filter_t;
+
+#define filter (*(filter_t*)&bb_common_bufsiz1)
+
+
+static void print_link_flags(unsigned flags, unsigned mdown)
+{
+ static const int flag_masks[] = {
+ IFF_LOOPBACK, IFF_BROADCAST, IFF_POINTOPOINT,
+ IFF_MULTICAST, IFF_NOARP, IFF_UP, IFF_LOWER_UP };
+ static const char flag_labels[] ALIGN1 =
+ "LOOPBACK\0""BROADCAST\0""POINTOPOINT\0"
+ "MULTICAST\0""NOARP\0""UP\0""LOWER_UP\0";
+
+ bb_putchar('<');
+ flags &= ~IFF_RUNNING;
+#if 0
+ _PF(ALLMULTI);
+ _PF(PROMISC);
+ _PF(MASTER);
+ _PF(SLAVE);
+ _PF(DEBUG);
+ _PF(DYNAMIC);
+ _PF(AUTOMEDIA);
+ _PF(PORTSEL);
+ _PF(NOTRAILERS);
+#endif
+ flags = print_flags_separated(flag_masks, flag_labels, flags, ",");
+ if (flags)
+ printf("%x", flags);
+ if (mdown)
+ printf(",M-DOWN");
+ printf("> ");
+}
+
+static void print_queuelen(char *name)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl_or_warn(s, SIOCGIFTXQLEN, &ifr) < 0) {
+ close(s);
+ return;
+ }
+ close(s);
+
+ if (ifr.ifr_qlen)
+ printf("qlen %d", ifr.ifr_qlen);
+}
+
+static int print_linkinfo(const struct nlmsghdr *n)
+{
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr * tb[IFLA_MAX+1];
+ int len = n->nlmsg_len;
+ unsigned m_flag = 0;
+
+ if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ return -1;
+
+ if (filter.ifindex && ifi->ifi_index != filter.ifindex)
+ return 0;
+ if (filter.up && !(ifi->ifi_flags & IFF_UP))
+ return 0;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+ if (tb[IFLA_IFNAME] == NULL) {
+ bb_error_msg("nil ifname");
+ return -1;
+ }
+ if (filter.label
+ && (!filter.family || filter.family == AF_PACKET)
+ && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)
+ ) {
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELLINK)
+ printf("Deleted ");
+
+ printf("%d: %s", ifi->ifi_index,
+ tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+
+ if (tb[IFLA_LINK]) {
+ SPRINT_BUF(b1);
+ int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+ if (iflink == 0)
+ printf("@NONE: ");
+ else {
+ printf("@%s: ", ll_idx_n2a(iflink, b1));
+ m_flag = ll_index_to_flags(iflink);
+ m_flag = !(m_flag & IFF_UP);
+ }
+ } else {
+ printf(": ");
+ }
+ print_link_flags(ifi->ifi_flags, m_flag);
+
+ if (tb[IFLA_MTU])
+ printf("mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+ if (tb[IFLA_QDISC])
+ printf("qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+#ifdef IFLA_MASTER
+ if (tb[IFLA_MASTER]) {
+ SPRINT_BUF(b1);
+ printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+ }
+#endif
+ if (filter.showqueue)
+ print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
+
+ if (!filter.family || filter.family == AF_PACKET) {
+ SPRINT_BUF(b1);
+ printf("%c link/%s ", _SL_, ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+
+ if (tb[IFLA_ADDRESS]) {
+ fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+ RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+ ifi->ifi_type,
+ b1, sizeof(b1)), stdout);
+ }
+ if (tb[IFLA_BROADCAST]) {
+ if (ifi->ifi_flags & IFF_POINTOPOINT)
+ printf(" peer ");
+ else
+ printf(" brd ");
+ fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+ RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+ ifi->ifi_type,
+ b1, sizeof(b1)), stdout);
+ }
+ }
+ bb_putchar('\n');
+ /*fflush(stdout);*/
+ return 0;
+}
+
+static int flush_update(void)
+{
+ if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+ bb_perror_msg("failed to send flush request");
+ return -1;
+ }
+ filter.flushp = 0;
+ return 0;
+}
+
+static int print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * rta_tb[IFA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
+ return 0;
+ len -= NLMSG_LENGTH(sizeof(*ifa));
+ if (len < 0) {
+ bb_error_msg("wrong nlmsg len %d", len);
+ return -1;
+ }
+
+ if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
+ return 0;
+
+ memset(rta_tb, 0, sizeof(rta_tb));
+ parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+
+ if (!rta_tb[IFA_LOCAL])
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ if (!rta_tb[IFA_ADDRESS])
+ rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+ if (filter.ifindex && filter.ifindex != ifa->ifa_index)
+ return 0;
+ if ((filter.scope ^ ifa->ifa_scope) & filter.scopemask)
+ return 0;
+ if ((filter.flags ^ ifa->ifa_flags) & filter.flagmask)
+ return 0;
+ if (filter.label) {
+ 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.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 = ++filter.rth->seq;
+ filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+ filter.flushed = 1;
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELADDR)
+ printf("Deleted ");
+
+ if (filter.oneline)
+ printf("%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+ if (ifa->ifa_family == AF_INET)
+ printf(" inet ");
+ else if (ifa->ifa_family == AF_INET6)
+ printf(" inet6 ");
+ else
+ printf(" family %d ", ifa->ifa_family);
+
+ if (rta_tb[IFA_LOCAL]) {
+ fputs(rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+ RTA_DATA(rta_tb[IFA_LOCAL]),
+ abuf, sizeof(abuf)), stdout);
+
+ if (rta_tb[IFA_ADDRESS] == NULL ||
+ memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
+ printf("/%d ", ifa->ifa_prefixlen);
+ } else {
+ printf(" 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]) {
+ printf("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]) {
+ printf("any %s ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
+ RTA_DATA(rta_tb[IFA_ANYCAST]),
+ abuf, sizeof(abuf)));
+ }
+ printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+ if (ifa->ifa_flags & IFA_F_SECONDARY) {
+ ifa->ifa_flags &= ~IFA_F_SECONDARY;
+ printf("secondary ");
+ }
+ if (ifa->ifa_flags & IFA_F_TENTATIVE) {
+ ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+ printf("tentative ");
+ }
+ if (ifa->ifa_flags & IFA_F_DEPRECATED) {
+ ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+ printf("deprecated ");
+ }
+ if (!(ifa->ifa_flags & IFA_F_PERMANENT)) {
+ printf("dynamic ");
+ } else
+ ifa->ifa_flags &= ~IFA_F_PERMANENT;
+ if (ifa->ifa_flags)
+ printf("flags %02x ", ifa->ifa_flags);
+ if (rta_tb[IFA_LABEL])
+ fputs((char*)RTA_DATA(rta_tb[IFA_LABEL]), stdout);
+ if (rta_tb[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+ char buf[128];
+ bb_putchar(_SL_);
+ if (ci->ifa_valid == 0xFFFFFFFFU)
+ sprintf(buf, "valid_lft forever");
+ else
+ sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
+ if (ci->ifa_prefered == 0xFFFFFFFFU)
+ sprintf(buf+strlen(buf), " preferred_lft forever");
+ else
+ sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
+ printf(" %s", buf);
+ }
+ bb_putchar('\n');
+ /*fflush(stdout);*/
+ return 0;
+}
+
+
+struct nlmsg_list
+{
+ struct nlmsg_list *next;
+ struct nlmsghdr h;
+};
+
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo)
+{
+ 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, NULL);
+ }
+ 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)
+ continue;
+ *lp = h;
+
+ ll_remember_index(who, n, NULL);
+ return 0;
+}
+
+static void ipaddr_reset_filter(int _oneline)
+{
+ memset(&filter, 0, sizeof(filter));
+ filter.oneline = _oneline;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int ipaddr_list_or_flush(char **argv, int flush)
+{
+ static const char option[] ALIGN1 = "to\0""scope\0""up\0""label\0""dev\0";
+
+ struct nlmsg_list *linfo = NULL;
+ struct nlmsg_list *ainfo = NULL;
+ struct nlmsg_list *l;
+ struct rtnl_handle rth;
+ char *filter_dev = NULL;
+ int no_link = 0;
+
+ ipaddr_reset_filter(oneline);
+ filter.showqueue = 1;
+
+ if (filter.family == AF_UNSPEC)
+ filter.family = preferred_family;
+
+ if (flush) {
+ if (!*argv) {
+ bb_error_msg_and_die(bb_msg_requires_arg, "flush");
+ }
+ if (filter.family == AF_PACKET) {
+ bb_error_msg_and_die("cannot flush link addresses");
+ }
+ }
+
+ while (*argv) {
+ const int option_num = index_in_strings(option, *argv);
+ switch (option_num) {
+ case 0: /* to */
+ NEXT_ARG();
+ get_prefix(&filter.pfx, *argv, filter.family);
+ if (filter.family == AF_UNSPEC) {
+ filter.family = filter.pfx.family;
+ }
+ break;
+ case 1: /* scope */
+ {
+ uint32_t scope = 0;
+ NEXT_ARG();
+ filter.scopemask = -1;
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg(*argv, "scope");
+ }
+ scope = RT_SCOPE_NOWHERE;
+ filter.scopemask = 0;
+ }
+ filter.scope = scope;
+ break;
+ }
+ case 2: /* up */
+ filter.up = 1;
+ break;
+ case 3: /* label */
+ NEXT_ARG();
+ filter.label = *argv;
+ break;
+ case 4: /* dev */
+ NEXT_ARG();
+ default:
+ if (filter_dev) {
+ duparg2("dev", *argv);
+ }
+ filter_dev = *argv;
+ }
+ argv++;
+ }
+
+ xrtnl_open(&rth);
+
+ xrtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK);
+ xrtnl_dump_filter(&rth, store_nlmsg, &linfo);
+
+ if (filter_dev) {
+ filter.ifindex = xll_name_to_index(filter_dev);
+ }
+
+ if (flush) {
+ char flushb[4096-512];
+
+ filter.flushb = flushb;
+ filter.flushp = 0;
+ filter.flushe = sizeof(flushb);
+ filter.rth = &rth;
+
+ for (;;) {
+ xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
+ filter.flushed = 0;
+ xrtnl_dump_filter(&rth, print_addrinfo, NULL);
+ if (filter.flushed == 0) {
+ return 0;
+ }
+ if (flush_update() < 0)
+ return 1;
+ }
+ }
+
+ if (filter.family != AF_PACKET) {
+ xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
+ xrtnl_dump_filter(&rth, store_nlmsg, &ainfo);
+ }
+
+
+ if (filter.family && filter.family != AF_PACKET) {
+ struct nlmsg_list **lp;
+ lp = &linfo;
+
+ if (filter.oneline)
+ no_link = 1;
+
+ while ((l = *lp) != NULL) {
+ int ok = 0;
+ struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+ struct nlmsg_list *a;
+
+ for (a = ainfo; a; a = a->next) {
+ struct nlmsghdr *n = &a->h;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+ if (ifa->ifa_index != ifi->ifi_index ||
+ (filter.family && filter.family != ifa->ifa_family))
+ continue;
+ if ((filter.scope ^ ifa->ifa_scope) & filter.scopemask)
+ continue;
+ if ((filter.flags ^ ifa->ifa_flags) & filter.flagmask)
+ continue;
+ if (filter.pfx.family || filter.label) {
+ struct rtattr *tb[IFA_MAX+1];
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
+ if (!tb[IFA_LOCAL])
+ tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+
+ if (filter.pfx.family && tb[IFA_LOCAL]) {
+ inet_prefix dst;
+ memset(&dst, 0, sizeof(dst));
+ dst.family = ifa->ifa_family;
+ memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+ if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+ continue;
+ }
+ if (filter.label) {
+ SPRINT_BUF(b1);
+ const char *label;
+ if (tb[IFA_LABEL])
+ label = RTA_DATA(tb[IFA_LABEL]);
+ else
+ label = ll_idx_n2a(ifa->ifa_index, b1);
+ if (fnmatch(filter.label, label, 0) != 0)
+ continue;
+ }
+ }
+
+ ok = 1;
+ break;
+ }
+ if (!ok)
+ *lp = l->next;
+ else
+ lp = &l->next;
+ }
+ }
+
+ for (l = linfo; l; l = l->next) {
+ if (no_link || print_linkinfo(&l->h) == 0) {
+ struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+ if (filter.family != AF_PACKET)
+ print_selected_addrinfo(ifi->ifi_index, ainfo);
+ }
+ }
+
+ return 0;
+}
+
+static int default_scope(inet_prefix *lcl)
+{
+ if (lcl->family == AF_INET) {
+ if (lcl->bytelen >= 1 && *(uint8_t*)&lcl->data == 127)
+ return RT_SCOPE_HOST;
+ }
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int ipaddr_modify(int cmd, char **argv)
+{
+ static const char option[] ALIGN1 =
+ "peer\0""remote\0""broadcast\0""brd\0"
+ "anycast\0""scope\0""dev\0""label\0""local\0";
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg ifa;
+ char buf[256];
+ } req;
+ char *d = NULL;
+ char *l = NULL;
+ inet_prefix lcl;
+ inet_prefix peer;
+ int local_len = 0;
+ int peer_len = 0;
+ int brd_len = 0;
+ int any_len = 0;
+ bool scoped = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = cmd;
+ req.ifa.ifa_family = preferred_family;
+
+ while (*argv) {
+ const int option_num = index_in_strings(option, *argv);
+ switch (option_num) {
+ case 0: /* peer */
+ case 1: /* remote */
+ 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;
+ break;
+ case 2: /* broadcast */
+ case 3: /* brd */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (brd_len) {
+ duparg("broadcast", *argv);
+ }
+ if (LONE_CHAR(*argv, '+')) {
+ brd_len = -1;
+ } else if (LONE_DASH(*argv)) {
+ 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;
+ }
+ break;
+ }
+ case 4: /* anycast */
+ {
+ 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;
+ break;
+ }
+ case 5: /* scope */
+ {
+ uint32_t scope = 0;
+ NEXT_ARG();
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ invarg(*argv, "scope");
+ }
+ req.ifa.ifa_scope = scope;
+ scoped = 1;
+ break;
+ }
+ case 6: /* dev */
+ NEXT_ARG();
+ d = *argv;
+ break;
+ case 7: /* label */
+ NEXT_ARG();
+ l = *argv;
+ addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
+ break;
+ case 8: /* local */
+ NEXT_ARG();
+ default:
+ if (local_len) {
+ duparg2("local", *argv);
+ }
+ get_prefix(&lcl, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = lcl.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
+ local_len = lcl.bytelen;
+ }
+ argv++;
+ }
+
+ if (d == NULL) {
+ bb_error_msg(bb_msg_requires_arg, "\"dev\"");
+ return -1;
+ }
+ if (l && strncmp(d, l, strlen(d)) != 0) {
+ bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
+ }
+
+ if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
+ peer = lcl;
+ addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+ }
+ if (req.ifa.ifa_prefixlen == 0)
+ req.ifa.ifa_prefixlen = lcl.bitlen;
+
+ if (brd_len < 0 && cmd != RTM_DELADDR) {
+ inet_prefix brd;
+ int i;
+ if (req.ifa.ifa_family != AF_INET) {
+ bb_error_msg_and_die("broadcast can be set only for IPv4 addresses");
+ }
+ 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);
+
+ xrtnl_open(&rth);
+
+ ll_init_map(&rth);
+
+ req.ifa.ifa_index = xll_name_to_index(d);
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_ipaddr(char **argv)
+{
+ static const char commands[] ALIGN1 =
+ "add\0""delete\0""list\0""show\0""lst\0""flush\0";
+
+ int command_num = 2; /* default command is list */
+
+ if (*argv) {
+ command_num = index_in_substrings(commands, *argv);
+ if (command_num < 0 || command_num > 5)
+ bb_error_msg_and_die("unknown command %s", *argv);
+ argv++;
+ }
+ if (command_num == 0) /* add */
+ return ipaddr_modify(RTM_NEWADDR, argv);
+ if (command_num == 1) /* delete */
+ return ipaddr_modify(RTM_DELADDR, argv);
+ if (command_num == 5) /* flush */
+ return ipaddr_list_or_flush(argv, 1);
+ /* 2 == list, 3 == show, 4 == lst */
+ return ipaddr_list_or_flush(argv, 0);
+}
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
new file mode 100644
index 0000000..8de17bf
--- /dev/null
+++ b/networking/libiproute/iplink.c
@@ -0,0 +1,306 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iplink.c "ip link".
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+//#include <sys/ioctl.h>
+//#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_packet.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+/* taken from linux/sockios.h */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+
+/* Exits on error */
+static int get_ctl_fd(void)
+{
+ int fd;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ return xsocket(PF_INET6, SOCK_DGRAM, 0);
+}
+
+/* Exits on error */
+static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = get_ctl_fd();
+ xioctl(fd, SIOCGIFFLAGS, &ifr);
+ if ((ifr.ifr_flags ^ flags) & mask) {
+ ifr.ifr_flags &= ~mask;
+ ifr.ifr_flags |= mask & flags;
+ xioctl(fd, SIOCSIFFLAGS, &ifr);
+ }
+ close(fd);
+}
+
+/* Exits on error */
+static void do_changename(char *dev, char *newdev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname));
+ fd = get_ctl_fd();
+ xioctl(fd, SIOCSIFNAME, &ifr);
+ close(fd);
+}
+
+/* Exits on error */
+static void set_qlen(char *dev, int qlen)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = get_ctl_fd();
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ ifr.ifr_qlen = qlen;
+ xioctl(s, SIOCSIFTXQLEN, &ifr);
+ close(s);
+}
+
+/* Exits on error */
+static void set_mtu(char *dev, int mtu)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = get_ctl_fd();
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+ xioctl(s, SIOCSIFMTU, &ifr);
+ close(s);
+}
+
+/* Exits on error */
+static int get_address(char *dev, int *htype)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll me;
+ socklen_t alen;
+ int s;
+
+ s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ xioctl(s, SIOCGIFINDEX, &ifr);
+
+ memset(&me, 0, sizeof(me));
+ me.sll_family = AF_PACKET;
+ me.sll_ifindex = ifr.ifr_ifindex;
+ me.sll_protocol = htons(ETH_P_LOOP);
+ xbind(s, (struct sockaddr*)&me, sizeof(me));
+
+ alen = sizeof(me);
+ if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
+ bb_perror_msg_and_die("getsockname");
+ }
+ close(s);
+ *htype = me.sll_hatype;
+ return me.sll_halen;
+}
+
+/* Exits on error */
+static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+{
+ int alen;
+
+ memset(ifr, 0, sizeof(*ifr));
+ strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name));
+ ifr->ifr_hwaddr.sa_family = hatype;
+
+ alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
+ alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla);
+ if (alen < 0)
+ exit(EXIT_FAILURE);
+ if (alen != halen) {
+ bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
+ }
+}
+
+/* Exits on error */
+static void set_address(struct ifreq *ifr, int brd)
+{
+ int s;
+
+ s = get_ctl_fd();
+ if (brd)
+ xioctl(s, SIOCSIFHWBROADCAST, ifr);
+ else
+ xioctl(s, SIOCSIFHWADDR, ifr);
+ close(s);
+}
+
+
+static void die_must_be_on_off(const char *msg) NORETURN;
+static void die_must_be_on_off(const char *msg)
+{
+ bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg);
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_set(char **argv)
+{
+ char *dev = NULL;
+ uint32_t mask = 0;
+ uint32_t flags = 0;
+ int qlen = -1;
+ int mtu = -1;
+ char *newaddr = NULL;
+ char *newbrd = NULL;
+ struct ifreq ifr0, ifr1;
+ char *newname = NULL;
+ int htype, halen;
+ static const char keywords[] ALIGN1 =
+ "up\0""down\0""name\0""mtu\0""multicast\0"
+ "arp\0""address\0""dev\0";
+ enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_multicast,
+ ARG_arp, ARG_addr, ARG_dev };
+ static const char str_on_off[] ALIGN1 = "on\0""off\0";
+ enum { PARM_on = 0, PARM_off };
+ smalluint key;
+
+ while (*argv) {
+ /* substring search ensures that e.g. "addr" and "address"
+ * are both accepted */
+ key = index_in_substrings(keywords, *argv);
+ if (key == ARG_up) {
+ mask |= IFF_UP;
+ flags |= IFF_UP;
+ }
+ if (key == ARG_down) {
+ mask |= IFF_UP;
+ flags &= ~IFF_UP;
+ }
+ if (key == ARG_name) {
+ NEXT_ARG();
+ newname = *argv;
+ }
+ if (key == ARG_mtu) {
+ NEXT_ARG();
+ if (mtu != -1)
+ duparg("mtu", *argv);
+ if (get_integer(&mtu, *argv, 0))
+ invarg(*argv, "mtu");
+ }
+ if (key == ARG_multicast) {
+ int param;
+ NEXT_ARG();
+ mask |= IFF_MULTICAST;
+ param = index_in_strings(str_on_off, *argv);
+ if (param < 0)
+ die_must_be_on_off("multicast");
+ if (param == PARM_on)
+ flags |= IFF_MULTICAST;
+ else
+ flags &= ~IFF_MULTICAST;
+ }
+ if (key == ARG_arp) {
+ int param;
+ NEXT_ARG();
+ mask |= IFF_NOARP;
+ param = index_in_strings(str_on_off, *argv);
+ if (param < 0)
+ die_must_be_on_off("arp");
+ if (param == PARM_on)
+ flags &= ~IFF_NOARP;
+ else
+ flags |= IFF_NOARP;
+ }
+ if (key == ARG_addr) {
+ NEXT_ARG();
+ newaddr = *argv;
+ }
+ if (key >= ARG_dev) {
+ if (key == ARG_dev) {
+ NEXT_ARG();
+ }
+ if (dev)
+ duparg2("dev", *argv);
+ dev = *argv;
+ }
+ argv++;
+ }
+
+ if (!dev) {
+ bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
+ }
+
+ if (newaddr || newbrd) {
+ halen = get_address(dev, &htype);
+ if (newaddr) {
+ parse_address(dev, htype, halen, newaddr, &ifr0);
+ }
+ if (newbrd) {
+ parse_address(dev, htype, halen, newbrd, &ifr1);
+ }
+ }
+
+ if (newname && strcmp(dev, newname)) {
+ do_changename(dev, newname);
+ dev = newname;
+ }
+ if (qlen != -1) {
+ set_qlen(dev, qlen);
+ }
+ if (mtu != -1) {
+ set_mtu(dev, mtu);
+ }
+ if (newaddr || newbrd) {
+ if (newbrd) {
+ set_address(&ifr1, 1);
+ }
+ if (newaddr) {
+ set_address(&ifr0, 0);
+ }
+ }
+ if (mask)
+ do_chflags(dev, flags, mask);
+ return 0;
+}
+
+static int ipaddr_list_link(char **argv)
+{
+ preferred_family = AF_PACKET;
+ return ipaddr_list_or_flush(argv, 0);
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iplink(char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ "set\0""show\0""lst\0""list\0";
+ int key;
+ if (!*argv)
+ return ipaddr_list_link(argv);
+ key = index_in_substrings(keywords, *argv);
+ if (key < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ argv++;
+ if (key == 0) /* set */
+ return do_set(argv);
+ /* show, lst, list */
+ return ipaddr_list_link(argv);
+}
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
new file mode 100644
index 0000000..a7ec66c
--- /dev/null
+++ b/networking/libiproute/iproute.c
@@ -0,0 +1,907 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iproute.c "ip route".
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
+ */
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+typedef struct filter_t {
+ int tb;
+ smallint flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+ int protocol, protocolmask;
+ int scope, scopemask;
+ int type, typemask;
+ int tos, tosmask;
+ int iif, iifmask;
+ int oif, oifmask;
+ int realm, realmmask;
+ inet_prefix rprefsrc;
+ inet_prefix rvia;
+ inet_prefix rdst;
+ inet_prefix mdst;
+ inet_prefix rsrc;
+ inet_prefix msrc;
+} filter_t;
+
+#define filter (*(filter_t*)&bb_common_bufsiz1)
+
+static int flush_update(void)
+{
+ if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+ bb_perror_msg("failed to send flush request");
+ return -1;
+ }
+ filter.flushp = 0;
+ return 0;
+}
+
+static unsigned get_hz(void)
+{
+ static unsigned hz_internal;
+ FILE *fp;
+
+ if (hz_internal)
+ return hz_internal;
+
+ fp = fopen_for_read("/proc/net/psched");
+ if (fp) {
+ unsigned nom, denom;
+
+ if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+ if (nom == 1000000)
+ hz_internal = denom;
+ fclose(fp);
+ }
+ if (!hz_internal)
+ hz_internal = sysconf(_SC_CLK_TCK);
+ return hz_internal;
+}
+
+static int print_route(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ inet_prefix dst;
+ inet_prefix src;
+ int host_len = -1;
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
+ fprintf(stderr, "Not a route: %08x %08x %08x\n",
+ n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+ return 0;
+ }
+ if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
+ return 0;
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0)
+ bb_error_msg_and_die("wrong nlmsg len %d", len);
+
+ if (r->rtm_family == AF_INET6)
+ host_len = 128;
+ else if (r->rtm_family == AF_INET)
+ host_len = 32;
+
+ if (r->rtm_family == AF_INET6) {
+ if (filter.tb) {
+ if (filter.tb < 0) {
+ if (!(r->rtm_flags & RTM_F_CLONED)) {
+ return 0;
+ }
+ } else {
+ if (r->rtm_flags & RTM_F_CLONED) {
+ return 0;
+ }
+ if (filter.tb == RT_TABLE_LOCAL) {
+ if (r->rtm_type != RTN_LOCAL) {
+ return 0;
+ }
+ } else if (filter.tb == RT_TABLE_MAIN) {
+ if (r->rtm_type == RTN_LOCAL) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+ } else {
+ if (filter.tb > 0 && filter.tb != r->rtm_table) {
+ return 0;
+ }
+ }
+ if (filter.rdst.family &&
+ (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
+ return 0;
+ }
+ if (filter.mdst.family &&
+ (r->rtm_family != filter.mdst.family ||
+ (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
+ return 0;
+ }
+ if (filter.rsrc.family &&
+ (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
+ return 0;
+ }
+ if (filter.msrc.family &&
+ (r->rtm_family != filter.msrc.family ||
+ (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
+ return 0;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (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.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;
+
+ if (filter.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+ if (flush_update())
+ bb_error_msg_and_die("flush");
+ }
+ 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 = ++filter.rth->seq;
+ filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+ filter.flushed = 1;
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELROUTE) {
+ printf("Deleted ");
+ }
+ if (r->rtm_type != RTN_UNICAST && !filter.type) {
+ printf("%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+ }
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ printf("%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 {
+ printf("%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) {
+ printf("0/%d ", r->rtm_dst_len);
+ } else {
+ printf("default ");
+ }
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ printf("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 {
+ printf("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) {
+ printf("from 0/%u ", r->rtm_src_len);
+ }
+ if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
+ printf("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) {
+ printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
+ }
+
+ if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
+ /* Do not use format_host(). It is our local addr
+ and symbolic name will not be useful.
+ */
+ printf(" 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]) {
+ printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
+ }
+ if (r->rtm_family == AF_INET6) {
+ struct rta_cacheinfo *ci = NULL;
+ if (tb[RTA_CACHEINFO]) {
+ ci = RTA_DATA(tb[RTA_CACHEINFO]);
+ }
+ if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+ if (r->rtm_flags & RTM_F_CLONED) {
+ printf("%c cache ", _SL_);
+ }
+ if (ci->rta_expires) {
+ printf(" expires %dsec", ci->rta_expires / get_hz());
+ }
+ if (ci->rta_error != 0) {
+ printf(" error %d", ci->rta_error);
+ }
+ } else if (ci) {
+ if (ci->rta_error != 0)
+ printf(" error %d", ci->rta_error);
+ }
+ }
+ if (tb[RTA_IIF] && filter.iifmask != -1) {
+ printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+ }
+ bb_putchar('\n');
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_modify(int cmd, unsigned flags, char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ "src\0""via\0""mtu\0""lock\0""protocol\0"USE_FEATURE_IP_RULE("table\0")
+ "dev\0""oif\0""to\0""metric\0";
+ enum {
+ ARG_src,
+ ARG_via,
+ ARG_mtu, PARM_lock,
+ ARG_protocol,
+USE_FEATURE_IP_RULE(ARG_table,)
+ ARG_dev,
+ ARG_oif,
+ ARG_to,
+ ARG_metric,
+ };
+ enum {
+ gw_ok = 1 << 0,
+ dst_ok = 1 << 1,
+ proto_ok = 1 << 2,
+ type_ok = 1 << 3
+ };
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ char mxbuf[256];
+ struct rtattr * mxrta = (void*)mxbuf;
+ unsigned mxlock = 0;
+ char *d = NULL;
+ smalluint ok = 0;
+ int arg;
+
+ 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;
+ if (RT_TABLE_MAIN) /* if it is zero, memset already did it */
+ req.r.rtm_table = RT_TABLE_MAIN;
+ if (RT_SCOPE_NOWHERE)
+ 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 (*argv) {
+ arg = index_in_substrings(keywords, *argv);
+ if (arg == ARG_src) {
+ 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 (arg == ARG_via) {
+ inet_prefix addr;
+ ok |= gw_ok;
+ 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 (arg == ARG_mtu) {
+ unsigned mtu;
+ NEXT_ARG();
+ if (index_in_strings(keywords, *argv) == PARM_lock) {
+ mxlock |= (1 << RTAX_MTU);
+ NEXT_ARG();
+ }
+ if (get_unsigned(&mtu, *argv, 0))
+ invarg(*argv, "mtu");
+ rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+ } else if (arg == ARG_protocol) {
+ uint32_t prot;
+ NEXT_ARG();
+ if (rtnl_rtprot_a2n(&prot, *argv))
+ invarg(*argv, "protocol");
+ req.r.rtm_protocol = prot;
+ ok |= proto_ok;
+#if ENABLE_FEATURE_IP_RULE
+ } else if (arg == ARG_table) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table");
+ req.r.rtm_table = tid;
+#endif
+ } else if (arg == ARG_dev || arg == ARG_oif) {
+ NEXT_ARG();
+ d = *argv;
+ } else if (arg == ARG_metric) {
+ uint32_t metric;
+ NEXT_ARG();
+ if (get_u32(&metric, *argv, 0))
+ invarg(*argv, "metric");
+ addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
+ } else {
+ int type;
+ inet_prefix dst;
+
+ if (arg == ARG_to) {
+ NEXT_ARG();
+ }
+ if ((**argv < '0' || **argv > '9')
+ && rtnl_rtntype_a2n(&type, *argv) == 0) {
+ NEXT_ARG();
+ req.r.rtm_type = type;
+ ok |= type_ok;
+ }
+
+ if (ok & 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;
+ ok |= dst_ok;
+ if (dst.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+ }
+ }
+ argv++;
+ }
+
+ xrtnl_open(&rth);
+
+ if (d) {
+ int idx;
+
+ ll_init_map(&rth);
+
+ if (d) {
+ idx = xll_name_to_index(d);
+ 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 (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 (!(ok & gw_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, NULL, NULL) < 0) {
+ return 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);
+ if (RTM_GETROUTE)
+ req.nlh.nlmsg_type = RTM_GETROUTE;
+ if (NLM_F_ROOT | NLM_F_REQUEST)
+ req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+ /*req.nlh.nlmsg_pid = 0; - memset did it already */
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.rtm.rtm_family = family;
+ if (RTM_F_CLONED)
+ req.rtm.rtm_flags = RTM_F_CLONED;
+
+ return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+static void iproute_flush_cache(void)
+{
+ static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
+ int flush_fd = open_or_warn(fn, O_WRONLY);
+
+ if (flush_fd < 0) {
+ return;
+ }
+
+ if (write(flush_fd, "-1", 2) < 2) {
+ bb_perror_msg("cannot flush routing cache");
+ return;
+ }
+ close(flush_fd);
+}
+
+static void iproute_reset_filter(void)
+{
+ memset(&filter, 0, sizeof(filter));
+ filter.mdst.bitlen = -1;
+ filter.msrc.bitlen = -1;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_list_or_flush(char **argv, int flush)
+{
+ int do_ipv6 = preferred_family;
+ struct rtnl_handle rth;
+ char *id = NULL;
+ char *od = NULL;
+ static const char keywords[] ALIGN1 =
+ /* "ip route list/flush" parameters: */
+ "protocol\0" "dev\0" "oif\0" "iif\0"
+ "via\0" "table\0" "cache\0"
+ "from\0" "to\0"
+ /* and possible further keywords */
+ "all\0"
+ "root\0"
+ "match\0"
+ "exact\0"
+ "main\0"
+ ;
+ enum {
+ KW_proto, KW_dev, KW_oif, KW_iif,
+ KW_via, KW_table, KW_cache,
+ KW_from, KW_to,
+ /* */
+ KW_all,
+ KW_root,
+ KW_match,
+ KW_exact,
+ KW_main,
+ };
+ int arg, parm;
+
+ iproute_reset_filter();
+ filter.tb = RT_TABLE_MAIN;
+
+ if (flush && !*argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
+
+ while (*argv) {
+ arg = index_in_substrings(keywords, *argv);
+ if (arg == KW_proto) {
+ uint32_t prot = 0;
+ NEXT_ARG();
+ filter.protocolmask = -1;
+ if (rtnl_rtprot_a2n(&prot, *argv)) {
+ if (index_in_strings(keywords, *argv) != KW_all)
+ invarg(*argv, "protocol");
+ prot = 0;
+ filter.protocolmask = 0;
+ }
+ filter.protocol = prot;
+ } else if (arg == KW_dev || arg == KW_oif) {
+ NEXT_ARG();
+ od = *argv;
+ } else if (arg == KW_iif) {
+ NEXT_ARG();
+ id = *argv;
+ } else if (arg == KW_via) {
+ NEXT_ARG();
+ get_prefix(&filter.rvia, *argv, do_ipv6);
+ } else if (arg == KW_table) { /* table all/cache/main */
+ NEXT_ARG();
+ parm = index_in_substrings(keywords, *argv);
+ if (parm == KW_cache)
+ filter.tb = -1;
+ else if (parm == KW_all)
+ filter.tb = 0;
+ else if (parm != KW_main) {
+#if ENABLE_FEATURE_IP_RULE
+ uint32_t tid;
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table");
+ filter.tb = tid;
+#else
+ invarg(*argv, "table");
+#endif
+ }
+ } else if (arg == KW_cache) {
+ /* The command 'ip route flush cache' is used by OpenSWAN.
+ * Assuming it's a synonym for 'ip route flush table cache' */
+ filter.tb = -1;
+ } else if (arg == KW_from) {
+ NEXT_ARG();
+ parm = index_in_substrings(keywords, *argv);
+ if (parm == KW_root) {
+ NEXT_ARG();
+ get_prefix(&filter.rsrc, *argv, do_ipv6);
+ } else if (parm == KW_match) {
+ NEXT_ARG();
+ get_prefix(&filter.msrc, *argv, do_ipv6);
+ } else {
+ if (parm == KW_exact)
+ NEXT_ARG();
+ get_prefix(&filter.msrc, *argv, do_ipv6);
+ filter.rsrc = filter.msrc;
+ }
+ } else { /* "to" is the default parameter */
+ if (arg == KW_to) {
+ NEXT_ARG();
+ arg = index_in_substrings(keywords, *argv);
+ }
+ /* parm = arg; - would be more plausible, but we reuse 'arg' here */
+ if (arg == KW_root) {
+ NEXT_ARG();
+ get_prefix(&filter.rdst, *argv, do_ipv6);
+ } else if (arg == KW_match) {
+ NEXT_ARG();
+ get_prefix(&filter.mdst, *argv, do_ipv6);
+ } else { /* "to exact" is the default */
+ if (arg == KW_exact)
+ NEXT_ARG();
+ get_prefix(&filter.mdst, *argv, do_ipv6);
+ filter.rdst = filter.mdst;
+ }
+ }
+ argv++;
+ }
+
+ if (do_ipv6 == AF_UNSPEC && filter.tb) {
+ do_ipv6 = AF_INET;
+ }
+
+ xrtnl_open(&rth);
+ ll_init_map(&rth);
+
+ if (id || od) {
+ int idx;
+
+ if (id) {
+ idx = xll_name_to_index(id);
+ filter.iif = idx;
+ filter.iifmask = -1;
+ }
+ if (od) {
+ idx = xll_name_to_index(od);
+ filter.oif = idx;
+ filter.oifmask = -1;
+ }
+ }
+
+ if (flush) {
+ char flushb[4096-512];
+
+ if (filter.tb == -1) { /* "flush table cache" */
+ if (do_ipv6 != AF_INET6)
+ iproute_flush_cache();
+ if (do_ipv6 == AF_INET)
+ return 0;
+ }
+
+ filter.flushb = flushb;
+ filter.flushp = 0;
+ filter.flushe = sizeof(flushb);
+ filter.rth = &rth;
+
+ for (;;) {
+ xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
+ filter.flushed = 0;
+ xrtnl_dump_filter(&rth, print_route, NULL);
+ if (filter.flushed == 0)
+ return 0;
+ if (flush_update())
+ return 1;
+ }
+ }
+
+ if (filter.tb != -1) {
+ xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
+ } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+ xrtnl_dump_filter(&rth, print_route, NULL);
+
+ return 0;
+}
+
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_get(char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ char *idev = NULL;
+ char *odev = NULL;
+ bool connected = 0;
+ bool from_ok = 0;
+ static const char options[] ALIGN1 =
+ "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
+
+ memset(&req, 0, sizeof(req));
+
+ iproute_reset_filter();
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ if (NLM_F_REQUEST)
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ if (RTM_GETROUTE)
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.r.rtm_family = preferred_family;
+ /*req.r.rtm_table = 0; - memset did this already */
+ /*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 (*argv) {
+ switch (index_in_strings(options, *argv)) {
+ case 0: /* from */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ from_ok = 1;
+ get_prefix(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ if (addr.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
+ }
+ req.r.rtm_src_len = addr.bitlen;
+ break;
+ }
+ case 1: /* iif */
+ NEXT_ARG();
+ idev = *argv;
+ break;
+ case 2: /* oif */
+ case 3: /* dev */
+ NEXT_ARG();
+ odev = *argv;
+ break;
+ case 4: /* notify */
+ req.r.rtm_flags |= RTM_F_NOTIFY;
+ break;
+ case 5: /* connected */
+ connected = 1;
+ break;
+ case 6: /* to */
+ NEXT_ARG();
+ default:
+ {
+ inet_prefix addr;
+ 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;
+ }
+ argv++;
+ }
+ }
+
+ if (req.r.rtm_dst_len == 0) {
+ bb_error_msg_and_die("need at least destination address");
+ }
+
+ xrtnl_open(&rth);
+
+ ll_init_map(&rth);
+
+ if (idev || odev) {
+ int idx;
+
+ if (idev) {
+ idx = xll_name_to_index(idev);
+ addattr32(&req.n, sizeof(req), RTA_IIF, idx);
+ }
+ if (odev) {
+ idx = xll_name_to_index(odev);
+ addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+ }
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = AF_INET;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ return 2;
+ }
+
+ if (connected && !from_ok) {
+ struct rtmsg *r = NLMSG_DATA(&req.n);
+ int len = req.n.nlmsg_len;
+ struct rtattr * tb[RTA_MAX+1];
+
+ print_route(NULL, &req.n, NULL);
+
+ if (req.n.nlmsg_type != RTM_NEWROUTE) {
+ bb_error_msg_and_die("not a route?");
+ }
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ bb_error_msg_and_die("wrong len %d", len);
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (tb[RTA_PREFSRC]) {
+ tb[RTA_PREFSRC]->rta_type = RTA_SRC;
+ r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
+ } else if (!tb[RTA_SRC]) {
+ bb_error_msg_and_die("failed to connect the route");
+ }
+ if (!odev && tb[RTA_OIF]) {
+ tb[RTA_OIF]->rta_type = 0;
+ }
+ if (tb[RTA_GATEWAY]) {
+ tb[RTA_GATEWAY]->rta_type = 0;
+ }
+ if (!idev && tb[RTA_IIF]) {
+ tb[RTA_IIF]->rta_type = 0;
+ }
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ return 2;
+ }
+ }
+ print_route(NULL, &req.n, NULL);
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iproute(char **argv)
+{
+ static const char ip_route_commands[] ALIGN1 =
+ /*0-3*/ "add\0""append\0""change\0""chg\0"
+ /*4-7*/ "delete\0""get\0""list\0""show\0"
+ /*8..*/ "prepend\0""replace\0""test\0""flush\0";
+ int command_num;
+ unsigned flags = 0;
+ int cmd = RTM_NEWROUTE;
+
+ if (!*argv)
+ return iproute_list_or_flush(argv, 0);
+
+ /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
+ /* It probably means that it is using "first match" rule */
+ command_num = index_in_substrings(ip_route_commands, *argv);
+
+ switch (command_num) {
+ case 0: /* add */
+ flags = NLM_F_CREATE|NLM_F_EXCL;
+ break;
+ case 1: /* append */
+ flags = NLM_F_CREATE|NLM_F_APPEND;
+ break;
+ case 2: /* change */
+ case 3: /* chg */
+ flags = NLM_F_REPLACE;
+ break;
+ case 4: /* delete */
+ cmd = RTM_DELROUTE;
+ break;
+ case 5: /* get */
+ return iproute_get(argv+1);
+ case 6: /* list */
+ case 7: /* show */
+ return iproute_list_or_flush(argv+1, 0);
+ case 8: /* prepend */
+ flags = NLM_F_CREATE;
+ break;
+ case 9: /* replace */
+ flags = NLM_F_CREATE|NLM_F_REPLACE;
+ break;
+ case 10: /* test */
+ flags = NLM_F_EXCL;
+ break;
+ case 11: /* flush */
+ return iproute_list_or_flush(argv+1, 1);
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+
+ return iproute_modify(cmd, flags, argv+1);
+}
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c
new file mode 100644
index 0000000..ca22546
--- /dev/null
+++ b/networking/libiproute/iprule.c
@@ -0,0 +1,334 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * initially integrated into busybox by Bernhard Reutner-Fischer
+ */
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+/*
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
+ fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
+ fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n");
+ fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
+ fprintf(stderr, " [ prohibit | reject | unreachable ]\n");
+ fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
+ fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
+ exit(-1);
+}
+*/
+
+static int print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ int host_len = -1;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWRULE)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0)
+ return -1;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_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 (tb[RTA_PRIORITY])
+ printf("%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY]));
+ else
+ printf("0:\t");
+
+ printf("from ");
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ printf("%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 {
+ fputs(format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)), stdout);
+ }
+ } else if (r->rtm_src_len) {
+ printf("0/%d", r->rtm_src_len);
+ } else {
+ printf("all");
+ }
+ bb_putchar(' ');
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ printf("to %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 {
+ printf("to %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) {
+ printf("to 0/%d ", r->rtm_dst_len);
+ }
+
+ if (r->rtm_tos) {
+ printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+ }
+ if (tb[RTA_PROTOINFO]) {
+ printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
+ }
+
+ if (tb[RTA_IIF]) {
+ printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
+ }
+
+ if (r->rtm_table)
+ printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1)));
+
+ if (tb[RTA_FLOW]) {
+ uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
+ uint32_t from = to>>16;
+ to &= 0xFFFF;
+ if (from) {
+ printf("realms %s/",
+ rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+ }
+ printf("%s ",
+ rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+ }
+
+ if (r->rtm_type == RTN_NAT) {
+ if (tb[RTA_GATEWAY]) {
+ printf("map-to %s ",
+ format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
+ } else
+ printf("masquerade");
+ } else if (r->rtm_type != RTN_UNICAST)
+ fputs(rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)), stdout);
+
+ bb_putchar('\n');
+ /*fflush(stdout);*/
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_list(char **argv)
+{
+ struct rtnl_handle rth;
+ int af = preferred_family;
+
+ if (af == AF_UNSPEC)
+ af = AF_INET;
+
+ if (*argv) {
+ //bb_error_msg("\"rule show\" needs no arguments");
+ bb_warn_ignoring_args(1);
+ return -1;
+ }
+
+ xrtnl_open(&rth);
+
+ xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
+ xrtnl_dump_filter(&rth, print_rule, NULL);
+
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_modify(int cmd, char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ "from\0""to\0""preference\0""order\0""priority\0"
+ "tos\0""fwmark\0""realms\0""table\0""lookup\0""dev\0"
+ "iif\0""nat\0""map-to\0""type\0""help\0";
+ enum {
+ ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
+ ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup, ARG_dev,
+ ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help
+ };
+ bool table_ok = 0;
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ smalluint key;
+
+ 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;
+
+ if (cmd == RTM_NEWRULE) {
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+ req.r.rtm_type = RTN_UNICAST;
+ }
+
+ while (*argv) {
+ key = index_in_substrings(keywords, *argv) + 1;
+ if (key == 0) /* no match found in keywords array, bail out. */
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ if (key == ARG_from) {
+ 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), RTA_SRC, &dst.data, dst.bytelen);
+ } else if (key == ARG_to) {
+ 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), RTA_DST, &dst.data, dst.bytelen);
+ } else if (key == ARG_preference ||
+ key == ARG_order ||
+ key == ARG_priority) {
+ uint32_t pref;
+ NEXT_ARG();
+ if (get_u32(&pref, *argv, 0))
+ invarg(*argv, "preference");
+ addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
+ } else if (key == ARG_tos) {
+ uint32_t tos;
+ NEXT_ARG();
+ if (rtnl_dsfield_a2n(&tos, *argv))
+ invarg(*argv, "TOS");
+ req.r.rtm_tos = tos;
+ } else if (key == ARG_fwmark) {
+ uint32_t fwmark;
+ NEXT_ARG();
+ if (get_u32(&fwmark, *argv, 0))
+ invarg(*argv, "fwmark");
+ addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
+ } else if (key == ARG_realms) {
+ uint32_t realm;
+ NEXT_ARG();
+ if (get_rt_realms(&realm, *argv))
+ invarg(*argv, "realms");
+ addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
+ } else if (key == ARG_table ||
+ key == ARG_lookup) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table ID");
+ req.r.rtm_table = tid;
+ table_ok = 1;
+ } else if (key == ARG_dev ||
+ key == ARG_iif) {
+ NEXT_ARG();
+ addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
+ } else if (key == ARG_nat ||
+ key == ARG_map_to) {
+ NEXT_ARG();
+ addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+ req.r.rtm_type = RTN_NAT;
+ } else {
+ int type;
+
+ if (key == ARG_type) {
+ NEXT_ARG();
+ }
+ if (key == ARG_help)
+ bb_show_usage();
+ if (rtnl_rtntype_a2n(&type, *argv))
+ invarg(*argv, "type");
+ req.r.rtm_type = type;
+ }
+ 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;
+
+ xrtnl_open(&rth);
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iprule(char **argv)
+{
+ static const char ip_rule_commands[] ALIGN1 =
+ "add\0""delete\0""list\0""show\0";
+ int cmd = 2; /* list */
+
+ if (!*argv)
+ return iprule_list(argv);
+
+ cmd = index_in_substrings(ip_rule_commands, *argv);
+ switch (cmd) {
+ case 0: /* add */
+ cmd = RTM_NEWRULE;
+ break;
+ case 1: /* delete */
+ cmd = RTM_DELRULE;
+ break;
+ case 2: /* list */
+ case 3: /* show */
+ return iprule_list(argv+1);
+ break;
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+ return iprule_modify(cmd, argv+1);
+}
diff --git a/networking/libiproute/iptunnel.c b/networking/libiproute/iptunnel.c
new file mode 100644
index 0000000..14fc6bb
--- /dev/null
+++ b/networking/libiproute/iptunnel.c
@@ -0,0 +1,580 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iptunnel.c "ip tunnel"
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
+ * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
+ */
+
+#include <netinet/ip.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <asm/types.h>
+
+#ifndef __constant_htons
+#define __constant_htons htons
+#endif
+
+// FYI: #define SIOCDEVPRIVATE 0x89F0
+
+/* From linux/if_tunnel.h. #including it proved troublesome
+ * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
+#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
+#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
+#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
+#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
+//#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
+//#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
+//#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
+//#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
+#define GRE_CSUM __constant_htons(0x8000)
+//#define GRE_ROUTING __constant_htons(0x4000)
+#define GRE_KEY __constant_htons(0x2000)
+#define GRE_SEQ __constant_htons(0x1000)
+//#define GRE_STRICT __constant_htons(0x0800)
+//#define GRE_REC __constant_htons(0x0700)
+//#define GRE_FLAGS __constant_htons(0x00F8)
+//#define GRE_VERSION __constant_htons(0x0007)
+struct ip_tunnel_parm {
+ char name[IFNAMSIZ];
+ int link;
+ uint16_t i_flags;
+ uint16_t o_flags;
+ uint32_t i_key;
+ uint32_t o_key;
+ struct iphdr iph;
+};
+/* SIT-mode i_flags */
+//#define SIT_ISATAP 0x0001
+//struct ip_tunnel_prl {
+// uint32_t addr;
+// uint16_t flags;
+// uint16_t __reserved;
+// uint32_t datalen;
+// uint32_t __reserved2;
+// /* data follows */
+//};
+///* PRL flags */
+//#define PRL_DEFAULT 0x0001
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+
+/* Dies on error */
+static int do_ioctl_get_ifindex(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ xioctl(fd, SIOCGIFINDEX, &ifr);
+ close(fd);
+ return ifr.ifr_ifindex;
+}
+
+static int do_ioctl_get_iftype(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr);
+ close(fd);
+ return err ? -1 : ifr.ifr_addr.sa_family;
+}
+
+static char *do_ioctl_get_ifname(int idx)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ ifr.ifr_ifindex = idx;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr);
+ close(fd);
+ return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name));
+}
+
+static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr);
+ close(fd);
+ return err;
+}
+
+/* Dies on error, otherwise returns 0 */
+static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+
+ if (cmd == SIOCCHGTUNNEL && p->name[0]) {
+ strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
+ } else {
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+#if ENABLE_IOCTL_HEX2STR_ERROR
+ /* #define magic will turn ioctl# into string */
+ if (cmd == SIOCCHGTUNNEL)
+ xioctl(fd, SIOCCHGTUNNEL, &ifr);
+ else
+ xioctl(fd, SIOCADDTUNNEL, &ifr);
+#else
+ xioctl(fd, cmd, &ifr);
+#endif
+ close(fd);
+ return 0;
+}
+
+/* Dies on error, otherwise returns 0 */
+static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+
+ if (p->name[0]) {
+ strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
+ } else {
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ xioctl(fd, SIOCDELTUNNEL, &ifr);
+ close(fd);
+ return 0;
+}
+
+/* Dies on error */
+static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
+{
+ static const char keywords[] ALIGN1 =
+ "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
+ "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
+ "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
+ "remote\0""any\0""local\0""dev\0"
+ "ttl\0""inherit\0""tos\0""dsfield\0"
+ "name\0";
+ enum {
+ ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip,
+ ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq,
+ ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc,
+ ARG_remote, ARG_any, ARG_local, ARG_dev,
+ ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield,
+ ARG_name
+ };
+ int count = 0;
+ char medium[IFNAMSIZ];
+ int key;
+
+ 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 (*argv) {
+ key = index_in_strings(keywords, *argv);
+ if (key == ARG_mode) {
+ NEXT_ARG();
+ key = index_in_strings(keywords, *argv);
+ if (key == ARG_ipip ||
+ key == ARG_ip_ip) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
+ bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
+ }
+ p->iph.protocol = IPPROTO_IPIP;
+ } else if (key == ARG_gre ||
+ key == ARG_gre_ip) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+ bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
+ }
+ p->iph.protocol = IPPROTO_GRE;
+ } else if (key == ARG_sit ||
+ key == ARG_ip6_ip) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+ bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
+ }
+ p->iph.protocol = IPPROTO_IPV6;
+ } else {
+ bb_error_msg_and_die("%s tunnel mode", "cannot guess");
+ }
+ } else if (key == ARG_key) {
+ 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) {
+ invarg(*argv, "key");
+ }
+ p->i_key = p->o_key = htonl(uval);
+ }
+ } else if (key == ARG_ikey) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0) < 0) {
+ invarg(*argv, "ikey");
+ }
+ p->i_key = htonl(uval);
+ }
+ } else if (key == ARG_okey) {
+ 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) {
+ invarg(*argv, "okey");
+ }
+ p->o_key = htonl(uval);
+ }
+ } else if (key == ARG_seq) {
+ p->i_flags |= GRE_SEQ;
+ p->o_flags |= GRE_SEQ;
+ } else if (key == ARG_iseq) {
+ p->i_flags |= GRE_SEQ;
+ } else if (key == ARG_oseq) {
+ p->o_flags |= GRE_SEQ;
+ } else if (key == ARG_csum) {
+ p->i_flags |= GRE_CSUM;
+ p->o_flags |= GRE_CSUM;
+ } else if (key == ARG_icsum) {
+ p->i_flags |= GRE_CSUM;
+ } else if (key == ARG_ocsum) {
+ p->o_flags |= GRE_CSUM;
+ } else if (key == ARG_nopmtudisc) {
+ p->iph.frag_off = 0;
+ } else if (key == ARG_pmtudisc) {
+ p->iph.frag_off = htons(IP_DF);
+ } else if (key == ARG_remote) {
+ NEXT_ARG();
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_any)
+ p->iph.daddr = get_addr32(*argv);
+ } else if (key == ARG_local) {
+ NEXT_ARG();
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_any)
+ p->iph.saddr = get_addr32(*argv);
+ } else if (key == ARG_dev) {
+ NEXT_ARG();
+ strncpy(medium, *argv, IFNAMSIZ-1);
+ } else if (key == ARG_ttl) {
+ unsigned uval;
+ NEXT_ARG();
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_inherit) {
+ if (get_unsigned(&uval, *argv, 0))
+ invarg(*argv, "TTL");
+ if (uval > 255)
+ invarg(*argv, "TTL must be <=255");
+ p->iph.ttl = uval;
+ }
+ } else if (key == ARG_tos ||
+ key == ARG_dsfield) {
+ uint32_t uval;
+ NEXT_ARG();
+ key = index_in_strings(keywords, *argv);
+ if (key != ARG_inherit) {
+ if (rtnl_dsfield_a2n(&uval, *argv))
+ invarg(*argv, "TOS");
+ p->iph.tos = uval;
+ } else
+ p->iph.tos = 1;
+ } else {
+ if (key == ARG_name) {
+ NEXT_ARG();
+ }
+ if (p->name[0])
+ duparg2("name", *argv);
+ strncpy(p->name, *argv, IFNAMSIZ);
+ if (cmd == SIOCCHGTUNNEL && count == 0) {
+ struct ip_tunnel_parm old_p;
+ memset(&old_p, 0, sizeof(old_p));
+ if (do_get_ioctl(*argv, &old_p))
+ exit(EXIT_FAILURE);
+ *p = old_p;
+ }
+ }
+ count++;
+ argv++;
+ }
+
+ if (p->iph.protocol == 0) {
+ if (memcmp(p->name, "gre", 3) == 0)
+ p->iph.protocol = IPPROTO_GRE;
+ else if (memcmp(p->name, "ipip", 4) == 0)
+ p->iph.protocol = IPPROTO_IPIP;
+ else if (memcmp(p->name, "sit", 3) == 0)
+ p->iph.protocol = IPPROTO_IPV6;
+ }
+
+ if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
+ if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
+ bb_error_msg_and_die("keys are not allowed with ipip and sit");
+ }
+ }
+
+ if (medium[0]) {
+ p->link = do_ioctl_get_ifindex(medium);
+ }
+
+ 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) {
+ bb_error_msg_and_die("broadcast tunnel requires a source address");
+ }
+}
+
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_add(int cmd, char **argv)
+{
+ struct ip_tunnel_parm p;
+
+ parse_args(argv, cmd, &p);
+
+ if (p.iph.ttl && p.iph.frag_off == 0) {
+ bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
+ }
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ return do_add_ioctl(cmd, "tunl0", &p);
+ case IPPROTO_GRE:
+ return do_add_ioctl(cmd, "gre0", &p);
+ case IPPROTO_IPV6:
+ return do_add_ioctl(cmd, "sit0", &p);
+ default:
+ bb_error_msg_and_die("cannot determine tunnel mode (ipip, gre or sit)");
+ }
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_del(char **argv)
+{
+ struct ip_tunnel_parm p;
+
+ parse_args(argv, SIOCDELTUNNEL, &p);
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ return do_del_ioctl("tunl0", &p);
+ case IPPROTO_GRE:
+ return do_del_ioctl("gre0", &p);
+ case IPPROTO_IPV6:
+ return do_del_ioctl("sit0", &p);
+ default:
+ return do_del_ioctl(p.name, &p);
+ }
+}
+
+static void print_tunnel(struct ip_tunnel_parm *p)
+{
+ char s1[256];
+ char s2[256];
+ char s3[64];
+ char s4[64];
+
+ format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
+ format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
+ inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+ inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+
+ printf("%s: %s/ip remote %s local %s ",
+ p->name,
+ p->iph.protocol == IPPROTO_IPIP ? "ip" :
+ (p->iph.protocol == IPPROTO_GRE ? "gre" :
+ (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
+ p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+ if (p->link) {
+ char *n = do_ioctl_get_ifname(p->link);
+ if (n) {
+ printf(" dev %s ", n);
+ free(n);
+ }
+ }
+ if (p->iph.ttl)
+ printf(" ttl %d ", p->iph.ttl);
+ else
+ printf(" ttl inherit ");
+ if (p->iph.tos) {
+ SPRINT_BUF(b1);
+ printf(" tos");
+ if (p->iph.tos & 1)
+ printf(" inherit");
+ if (p->iph.tos & ~1)
+ printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
+ rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
+ }
+ if (!(p->iph.frag_off & htons(IP_DF)))
+ printf(" nopmtudisc");
+
+ if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
+ printf(" key %s", s3);
+ else if ((p->i_flags | p->o_flags) & GRE_KEY) {
+ if (p->i_flags & GRE_KEY)
+ printf(" ikey %s ", s3);
+ if (p->o_flags & GRE_KEY)
+ printf(" okey %s ", s4);
+ }
+
+ if (p->i_flags & GRE_SEQ)
+ printf("%c Drop packets out of sequence.\n", _SL_);
+ if (p->i_flags & GRE_CSUM)
+ printf("%c Checksum in received packet is required.", _SL_);
+ if (p->o_flags & GRE_SEQ)
+ printf("%c Sequence packets on output.", _SL_);
+ if (p->o_flags & GRE_CSUM)
+ printf("%c Checksum output packets.", _SL_);
+}
+
+static void do_tunnels_list(struct ip_tunnel_parm *p)
+{
+ char name[IFNAMSIZ];
+ unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
+ rx_fifo, rx_frame,
+ tx_bytes, tx_packets, tx_errs, tx_drops,
+ tx_fifo, tx_colls, tx_carrier, rx_multi;
+ int type;
+ struct ip_tunnel_parm p1;
+ char buf[512];
+ FILE *fp = fopen_or_warn("/proc/net/dev", "r");
+
+ if (fp == NULL) {
+ return;
+ }
+ /* skip headers */
+ fgets(buf, sizeof(buf), fp);
+ fgets(buf, sizeof(buf), fp);
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ char *ptr;
+
+ /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
+ ptr = strchr(buf, ':');
+ if (ptr == NULL ||
+ (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+ bb_error_msg("wrong format of /proc/net/dev");
+ return;
+ }
+ if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
+ &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+ &rx_fifo, &rx_frame, &rx_multi,
+ &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+ &tx_fifo, &tx_colls, &tx_carrier) != 14)
+ continue;
+ if (p->name[0] && strcmp(p->name, name))
+ continue;
+ type = do_ioctl_get_iftype(name);
+ if (type == -1) {
+ bb_error_msg("cannot get type of [%s]", name);
+ continue;
+ }
+ if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+ continue;
+ memset(&p1, 0, sizeof(p1));
+ if (do_get_ioctl(name, &p1))
+ continue;
+ if ((p->link && p1.link != p->link) ||
+ (p->name[0] && strcmp(p1.name, p->name)) ||
+ (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+ (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+ (p->i_key && p1.i_key != p->i_key))
+ continue;
+ print_tunnel(&p1);
+ bb_putchar('\n');
+ }
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_show(char **argv)
+{
+ int err;
+ struct ip_tunnel_parm p;
+
+ parse_args(argv, SIOCGETTUNNEL, &p);
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+ break;
+ case IPPROTO_GRE:
+ err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+ break;
+ case IPPROTO_IPV6:
+ err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
+ break;
+ default:
+ do_tunnels_list(&p);
+ return 0;
+ }
+ if (err)
+ return -1;
+
+ print_tunnel(&p);
+ bb_putchar('\n');
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int do_iptunnel(char **argv)
+{
+ static const char keywords[] ALIGN1 =
+ "add\0""change\0""delete\0""show\0""list\0""lst\0";
+ enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst };
+ int key;
+
+ if (*argv) {
+ key = index_in_substrings(keywords, *argv);
+ if (key < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ argv++;
+ if (key == ARG_add)
+ return do_add(SIOCADDTUNNEL, argv);
+ if (key == ARG_change)
+ return do_add(SIOCCHGTUNNEL, argv);
+ if (key == ARG_del)
+ return do_del(argv);
+ }
+ return do_show(argv);
+}
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
new file mode 100644
index 0000000..01454fb
--- /dev/null
+++ b/networking/libiproute/libnetlink.c
@@ -0,0 +1,409 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include "libbb.h"
+#include "libnetlink.h"
+
+void FAST_FUNC rtnl_close(struct rtnl_handle *rth)
+{
+ close(rth->fd);
+}
+
+int FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
+{
+ socklen_t addr_len;
+
+ memset(rth, 0, sizeof(rth));
+
+ rth->fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ /*rth->local.nl_groups = subscriptions;*/
+
+ xbind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local));
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0)
+ bb_perror_msg_and_die("getsockname");
+ if (addr_len != sizeof(rth->local))
+ bb_error_msg_and_die("wrong address length %d", addr_len);
+ if (rth->local.nl_family != AF_NETLINK)
+ bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return xsendto(rth->fd, (void*)&req, sizeof(req),
+ (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
+{
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return xsendto(rth->fd, buf, len, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ iov, 2,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+static int rtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg1/*,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg2*/)
+{
+ int retval = -1;
+ char *buf = xmalloc(8*1024); /* avoid big stack buffer */
+ struct sockaddr_nl nladdr;
+ struct iovec iov = { buf, 8*1024 };
+
+ while (1) {
+ int status;
+ struct nlmsghdr *h;
+
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ goto ret;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+ }
+
+ h = (struct nlmsghdr*)buf;
+ while (NLMSG_OK(h, status)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+// if (junk) {
+// err = junk(&nladdr, h, arg2);
+// if (err < 0) {
+// retval = err;
+// goto ret;
+// }
+// }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ goto ret_0;
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ bb_error_msg("ERROR truncated");
+ } else {
+ errno = -l_err->error;
+ bb_perror_msg("RTNETLINK answers");
+ }
+ goto ret;
+ }
+ err = filter(&nladdr, h, arg1);
+ if (err < 0) {
+ retval = err;
+ goto ret;
+ }
+
+ skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("remnant of size %d!", status);
+ }
+ } /* while (1) */
+ ret_0:
+ retval++; /* = 0 */
+ ret:
+ free(buf);
+ return retval;
+}
+
+int FAST_FUNC xrtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *, void *),
+ void *arg1)
+{
+ int ret = rtnl_dump_filter(rth, filter, arg1/*, NULL, NULL*/);
+ if (ret < 0)
+ bb_error_msg_and_die("dump terminated");
+ return ret;
+}
+
+int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+ pid_t peer, unsigned groups,
+ struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *, void *),
+ void *jarg)
+{
+/* bbox doesn't use parameters no. 3, 4, 6, 7, they are stubbed out */
+#define peer 0
+#define groups 0
+#define junk NULL
+#define jarg NULL
+ int retval = -1;
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = { (void*)n, n->nlmsg_len };
+ char *buf = xmalloc(8*1024); /* avoid big stack buffer */
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+// nladdr.nl_pid = peer;
+// nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+ if (answer == NULL) {
+ n->nlmsg_flags |= NLM_F_ACK;
+ }
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ bb_perror_msg("cannot talk to rtnetlink");
+ goto ret;
+ }
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = 8*1024;
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ goto ret;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+// int l_err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len > status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("truncated message");
+ goto ret;
+ }
+ bb_error_msg_and_die("malformed message: len=%d!", len);
+ }
+
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+// if (junk) {
+// l_err = junk(&nladdr, h, jarg);
+// if (l_err < 0) {
+// retval = l_err;
+// goto ret;
+// }
+// }
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < (int)sizeof(struct nlmsgerr)) {
+ bb_error_msg("ERROR truncated");
+ } else {
+ errno = - err->error;
+ if (errno == 0) {
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ }
+ goto ret_0;
+ }
+ bb_perror_msg("RTNETLINK answers");
+ }
+ goto ret;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ goto ret_0;
+ }
+
+ bb_error_msg("unexpected reply!");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("remnant of size %d!", status);
+ }
+ } /* while (1) */
+ ret_0:
+ retval++; /* = 0 */
+ ret:
+ free(buf);
+ return retval;
+}
+
+int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
+ return -1;
+ rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
+ return -1;
+ rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+
+int FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max) {
+ tb[rta->rta_type] = rta;
+ }
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len) {
+ bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
+ }
+ return 0;
+}
diff --git a/networking/libiproute/libnetlink.h b/networking/libiproute/libnetlink.h
new file mode 100644
index 0000000..079153b
--- /dev/null
+++ b/networking/libiproute/libnetlink.h
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <linux/types.h>
+/* We need linux/types.h because older kernels use __u32 etc
+ * in linux/[rt]netlink.h. 2.6.19 seems to be ok, though */
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ uint32_t seq;
+ uint32_t dump;
+};
+
+extern int xrtnl_open(struct rtnl_handle *rth) FAST_FUNC;
+extern void rtnl_close(struct rtnl_handle *rth) FAST_FUNC;
+extern int xrtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type) FAST_FUNC;
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) FAST_FUNC;
+extern int xrtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(const struct sockaddr_nl*, struct nlmsghdr *n, void*),
+ void *arg1) FAST_FUNC;
+
+/* bbox doesn't use parameters no. 3, 4, 6, 7, stub them out */
+#define rtnl_talk(rtnl, n, peer, groups, answer, junk, jarg) \
+ rtnl_talk(rtnl, n, answer)
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg) FAST_FUNC;
+
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int) FAST_FUNC;
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) FAST_FUNC;
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) FAST_FUNC;
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data) FAST_FUNC;
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) FAST_FUNC;
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) FAST_FUNC;
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif /* __LIBNETLINK_H__ */
diff --git a/networking/libiproute/ll_addr.c b/networking/libiproute/ll_addr.c
new file mode 100644
index 0000000..e732efd
--- /dev/null
+++ b/networking/libiproute/ll_addr.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_addr.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <net/if_arp.h>
+
+#include "libbb.h"
+#include "rt_names.h"
+#include "utils.h"
+
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
+{
+ int i;
+ int l;
+
+ if (alen == 4 &&
+ (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
+ return inet_ntop(AF_INET, addr, buf, blen);
+ }
+ l = 0;
+ for (i=0; i<alen; i++) {
+ if (i==0) {
+ snprintf(buf+l, blen, ":%02x"+1, addr[i]);
+ blen -= 2;
+ l += 2;
+ } else {
+ snprintf(buf+l, blen, ":%02x", addr[i]);
+ blen -= 3;
+ l += 3;
+ }
+ }
+ return buf;
+}
+
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
+{
+ if (strchr(arg, '.')) {
+ inet_prefix pfx;
+ if (get_addr_1(&pfx, arg, AF_INET)) {
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
+ return -1;
+ }
+ if (len < 4) {
+ return -1;
+ }
+ memcpy(lladdr, pfx.data, 4);
+ return 4;
+ } else {
+ int i;
+
+ for (i=0; i<len; i++) {
+ int temp;
+ char *cp = strchr(arg, ':');
+ if (cp) {
+ *cp = 0;
+ cp++;
+ }
+ if (sscanf(arg, "%x", &temp) != 1 || (temp < 0 || temp > 255)) {
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
+ return -1;
+ }
+ lladdr[i] = temp;
+ if (!cp) {
+ break;
+ }
+ arg = cp;
+ }
+ return i+1;
+ }
+}
diff --git a/networking/libiproute/ll_map.c b/networking/libiproute/ll_map.c
new file mode 100644
index 0000000..eeae4e2
--- /dev/null
+++ b/networking/libiproute/ll_map.c
@@ -0,0 +1,200 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_map.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <net/if.h> /* struct ifreq and co. */
+
+#include "libbb.h"
+#include "libnetlink.h"
+#include "ll_map.h"
+
+struct idxmap {
+ struct idxmap *next;
+ int index;
+ int type;
+ int alen;
+ unsigned flags;
+ unsigned char addr[8];
+ char name[16];
+};
+
+static struct idxmap *idxmap[16];
+
+static struct idxmap *find_by_index(int idx)
+{
+ struct idxmap *im;
+
+ for (im = idxmap[idx & 0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im;
+ return NULL;
+}
+
+int ll_remember_index(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n,
+ void *arg UNUSED_PARAM)
+{
+ int h;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct idxmap *im, **imp;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (n->nlmsg_type != RTM_NEWLINK)
+ return 0;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+ return -1;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ if (tb[IFLA_IFNAME] == NULL)
+ return 0;
+
+ h = ifi->ifi_index & 0xF;
+
+ for (imp = &idxmap[h]; (im = *imp) != NULL; imp = &im->next)
+ if (im->index == ifi->ifi_index)
+ goto found;
+
+ im = xmalloc(sizeof(*im));
+ im->next = *imp;
+ im->index = ifi->ifi_index;
+ *imp = im;
+ found:
+ im->type = ifi->ifi_type;
+ im->flags = ifi->ifi_flags;
+ if (tb[IFLA_ADDRESS]) {
+ int alen;
+ im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+ if (alen > (int)sizeof(im->addr))
+ alen = sizeof(im->addr);
+ memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+ } else {
+ im->alen = 0;
+ memset(im->addr, 0, sizeof(im->addr));
+ }
+ strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
+ return 0;
+}
+
+const char *ll_idx_n2a(int idx, char *buf)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return "*";
+ im = find_by_index(idx);
+ if (im)
+ return im->name;
+ snprintf(buf, 16, "if%d", idx);
+ return buf;
+}
+
+
+const char *ll_index_to_name(int idx)
+{
+ static char nbuf[16];
+
+ return ll_idx_n2a(idx, nbuf);
+}
+
+#ifdef UNUSED
+int ll_index_to_type(int idx)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return -1;
+ im = find_by_index(idx);
+ if (im)
+ return im->type;
+ return -1;
+}
+#endif
+
+unsigned ll_index_to_flags(int idx)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return 0;
+ im = find_by_index(idx);
+ if (im)
+ return im->flags;
+ return 0;
+}
+
+int xll_name_to_index(const char *const name)
+{
+ int ret = 0;
+ int sock_fd;
+
+/* caching is not warranted - no users which repeatedly call it */
+#ifdef UNUSED
+ static char ncache[16];
+ static int icache;
+
+ struct idxmap *im;
+ int i;
+
+ if (name == NULL)
+ goto out;
+ if (icache && strcmp(name, ncache) == 0) {
+ ret = icache;
+ goto out;
+ }
+ for (i = 0; i < 16; i++) {
+ for (im = idxmap[i]; im; im = im->next) {
+ if (strcmp(im->name, name) == 0) {
+ icache = im->index;
+ strcpy(ncache, name);
+ ret = im->index;
+ goto out;
+ }
+ }
+ }
+ /* We have not found the interface in our cache, but the kernel
+ * may still know about it. One reason is that we may be using
+ * module on-demand loading, which means that the kernel will
+ * load the module and make the interface exist only when
+ * we explicitely request it (check for dev_load() in net/core/dev.c).
+ * I can think of other similar scenario, but they are less common...
+ * Jean II */
+#endif
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd) {
+ struct ifreq ifr;
+ int tmp;
+
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_ifindex = -1;
+ tmp = ioctl(sock_fd, SIOCGIFINDEX, &ifr);
+ close(sock_fd);
+ if (tmp >= 0)
+ /* In theory, we should redump the interface list
+ * to update our cache, this is left as an exercise
+ * to the reader... Jean II */
+ ret = ifr.ifr_ifindex;
+ }
+/* out:*/
+ if (ret <= 0)
+ bb_error_msg_and_die("cannot find device \"%s\"", name);
+ return ret;
+}
+
+int ll_init_map(struct rtnl_handle *rth)
+{
+ xrtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK);
+ xrtnl_dump_filter(rth, ll_remember_index, &idxmap);
+ return 0;
+}
diff --git a/networking/libiproute/ll_map.h b/networking/libiproute/ll_map.h
new file mode 100644
index 0000000..6d64ac1
--- /dev/null
+++ b/networking/libiproute/ll_map.h
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+int ll_remember_index(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+int ll_init_map(struct rtnl_handle *rth);
+int xll_name_to_index(const char *const name);
+const char *ll_index_to_name(int idx);
+const char *ll_idx_n2a(int idx, char *buf);
+/* int ll_index_to_type(int idx); */
+unsigned ll_index_to_flags(int idx);
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif /* __LL_MAP_H__ */
diff --git a/networking/libiproute/ll_proto.c b/networking/libiproute/ll_proto.c
new file mode 100644
index 0000000..b826873
--- /dev/null
+++ b/networking/libiproute/ll_proto.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_proto.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include "libbb.h"
+#include "rt_names.h"
+#include "utils.h"
+
+#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#if !ENABLE_WERROR
+#warning de-bloat
+#endif
+/* Before re-enabling this, please (1) conditionalize exotic protocols
+ * on CONFIG_something, and (2) decouple strings and numbers
+ * (use llproto_ids[] = n,n,n..; and llproto_names[] = "loop\0" "pup\0" ...;)
+ */
+
+#define __PF(f,n) { ETH_P_##f, #n },
+static struct {
+ int id;
+ const char *name;
+} llproto_names[] = {
+__PF(LOOP,loop)
+__PF(PUP,pup)
+#ifdef ETH_P_PUPAT
+__PF(PUPAT,pupat)
+#endif
+__PF(IP,ip)
+__PF(X25,x25)
+__PF(ARP,arp)
+__PF(BPQ,bpq)
+#ifdef ETH_P_IEEEPUP
+__PF(IEEEPUP,ieeepup)
+#endif
+#ifdef ETH_P_IEEEPUPAT
+__PF(IEEEPUPAT,ieeepupat)
+#endif
+__PF(DEC,dec)
+__PF(DNA_DL,dna_dl)
+__PF(DNA_RC,dna_rc)
+__PF(DNA_RT,dna_rt)
+__PF(LAT,lat)
+__PF(DIAG,diag)
+__PF(CUST,cust)
+__PF(SCA,sca)
+__PF(RARP,rarp)
+__PF(ATALK,atalk)
+__PF(AARP,aarp)
+__PF(IPX,ipx)
+__PF(IPV6,ipv6)
+#ifdef ETH_P_PPP_DISC
+__PF(PPP_DISC,ppp_disc)
+#endif
+#ifdef ETH_P_PPP_SES
+__PF(PPP_SES,ppp_ses)
+#endif
+#ifdef ETH_P_ATMMPOA
+__PF(ATMMPOA,atmmpoa)
+#endif
+#ifdef ETH_P_ATMFATE
+__PF(ATMFATE,atmfate)
+#endif
+
+__PF(802_3,802_3)
+__PF(AX25,ax25)
+__PF(ALL,all)
+__PF(802_2,802_2)
+__PF(SNAP,snap)
+__PF(DDCMP,ddcmp)
+__PF(WAN_PPP,wan_ppp)
+__PF(PPP_MP,ppp_mp)
+__PF(LOCALTALK,localtalk)
+__PF(PPPTALK,ppptalk)
+__PF(TR_802_2,tr_802_2)
+__PF(MOBITEX,mobitex)
+__PF(CONTROL,control)
+__PF(IRDA,irda)
+#ifdef ETH_P_ECONET
+__PF(ECONET,econet)
+#endif
+
+{ 0x8100, "802.1Q" },
+{ ETH_P_IP, "ipv4" },
+};
+#undef __PF
+
+
+const char *ll_proto_n2a(unsigned short id, char *buf, int len)
+{
+ unsigned i;
+ id = ntohs(id);
+ for (i = 0; i < ARRAY_SIZE(llproto_names); i++) {
+ if (llproto_names[i].id == id)
+ return llproto_names[i].name;
+ }
+ snprintf(buf, len, "[%d]", id);
+ return buf;
+}
+
+int ll_proto_a2n(unsigned short *id, char *buf)
+{
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE(llproto_names); i++) {
+ if (strcasecmp(llproto_names[i].name, buf) == 0) {
+ *id = htons(llproto_names[i].id);
+ return 0;
+ }
+ }
+ if (get_u16(id, buf, 0))
+ return -1;
+ *id = htons(*id);
+ return 0;
+}
+
diff --git a/networking/libiproute/ll_types.c b/networking/libiproute/ll_types.c
new file mode 100644
index 0000000..d5d2a1f
--- /dev/null
+++ b/networking/libiproute/ll_types.c
@@ -0,0 +1,205 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_types.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+#include <arpa/inet.h>
+#include <linux/if_arp.h>
+
+#include "libbb.h"
+#include "rt_names.h"
+
+const char *ll_type_n2a(int type, char *buf, int len)
+{
+ static const char arphrd_name[] =
+ /* 0, */ "generic" "\0"
+ /* ARPHRD_LOOPBACK, */ "loopback" "\0"
+ /* ARPHRD_ETHER, */ "ether" "\0"
+#ifdef ARPHRD_INFINIBAND
+ /* ARPHRD_INFINIBAND, */ "infiniband" "\0"
+#endif
+#ifdef ARPHRD_IEEE802_TR
+ /* ARPHRD_IEEE802, */ "ieee802" "\0"
+ /* ARPHRD_IEEE802_TR, */ "tr" "\0"
+#else
+ /* ARPHRD_IEEE802, */ "tr" "\0"
+#endif
+#ifdef ARPHRD_IEEE80211
+ /* ARPHRD_IEEE80211, */ "ieee802.11" "\0"
+#endif
+#ifdef ARPHRD_IEEE1394
+ /* ARPHRD_IEEE1394, */ "ieee1394" "\0"
+#endif
+ /* ARPHRD_IRDA, */ "irda" "\0"
+ /* ARPHRD_SLIP, */ "slip" "\0"
+ /* ARPHRD_CSLIP, */ "cslip" "\0"
+ /* ARPHRD_SLIP6, */ "slip6" "\0"
+ /* ARPHRD_CSLIP6, */ "cslip6" "\0"
+ /* ARPHRD_PPP, */ "ppp" "\0"
+ /* ARPHRD_TUNNEL, */ "ipip" "\0"
+ /* ARPHRD_TUNNEL6, */ "tunnel6" "\0"
+ /* ARPHRD_SIT, */ "sit" "\0"
+ /* ARPHRD_IPGRE, */ "gre" "\0"
+#ifdef ARPHRD_VOID
+ /* ARPHRD_VOID, */ "void" "\0"
+#endif
+
+#if ENABLE_FEATURE_IP_RARE_PROTOCOLS
+ /* ARPHRD_EETHER, */ "eether" "\0"
+ /* ARPHRD_AX25, */ "ax25" "\0"
+ /* ARPHRD_PRONET, */ "pronet" "\0"
+ /* ARPHRD_CHAOS, */ "chaos" "\0"
+ /* ARPHRD_ARCNET, */ "arcnet" "\0"
+ /* ARPHRD_APPLETLK, */ "atalk" "\0"
+ /* ARPHRD_DLCI, */ "dlci" "\0"
+#ifdef ARPHRD_ATM
+ /* ARPHRD_ATM, */ "atm" "\0"
+#endif
+ /* ARPHRD_METRICOM, */ "metricom" "\0"
+ /* ARPHRD_RSRVD, */ "rsrvd" "\0"
+ /* ARPHRD_ADAPT, */ "adapt" "\0"
+ /* ARPHRD_ROSE, */ "rose" "\0"
+ /* ARPHRD_X25, */ "x25" "\0"
+#ifdef ARPHRD_HWX25
+ /* ARPHRD_HWX25, */ "hwx25" "\0"
+#endif
+ /* ARPHRD_HDLC, */ "hdlc" "\0"
+ /* ARPHRD_LAPB, */ "lapb" "\0"
+#ifdef ARPHRD_DDCMP
+ /* ARPHRD_DDCMP, */ "ddcmp" "\0"
+ /* ARPHRD_RAWHDLC, */ "rawhdlc" "\0"
+#endif
+ /* ARPHRD_FRAD, */ "frad" "\0"
+ /* ARPHRD_SKIP, */ "skip" "\0"
+ /* ARPHRD_LOCALTLK, */ "ltalk" "\0"
+ /* ARPHRD_FDDI, */ "fddi" "\0"
+ /* ARPHRD_BIF, */ "bif" "\0"
+ /* ARPHRD_IPDDP, */ "ip/ddp" "\0"
+ /* ARPHRD_PIMREG, */ "pimreg" "\0"
+ /* ARPHRD_HIPPI, */ "hippi" "\0"
+ /* ARPHRD_ASH, */ "ash" "\0"
+ /* ARPHRD_ECONET, */ "econet" "\0"
+ /* ARPHRD_FCPP, */ "fcpp" "\0"
+ /* ARPHRD_FCAL, */ "fcal" "\0"
+ /* ARPHRD_FCPL, */ "fcpl" "\0"
+ /* ARPHRD_FCFABRIC, */ "fcfb0" "\0"
+ /* ARPHRD_FCFABRIC+1, */ "fcfb1" "\0"
+ /* ARPHRD_FCFABRIC+2, */ "fcfb2" "\0"
+ /* ARPHRD_FCFABRIC+3, */ "fcfb3" "\0"
+ /* ARPHRD_FCFABRIC+4, */ "fcfb4" "\0"
+ /* ARPHRD_FCFABRIC+5, */ "fcfb5" "\0"
+ /* ARPHRD_FCFABRIC+6, */ "fcfb6" "\0"
+ /* ARPHRD_FCFABRIC+7, */ "fcfb7" "\0"
+ /* ARPHRD_FCFABRIC+8, */ "fcfb8" "\0"
+ /* ARPHRD_FCFABRIC+9, */ "fcfb9" "\0"
+ /* ARPHRD_FCFABRIC+10, */ "fcfb10" "\0"
+ /* ARPHRD_FCFABRIC+11, */ "fcfb11" "\0"
+ /* ARPHRD_FCFABRIC+12, */ "fcfb12" "\0"
+#endif /* FEATURE_IP_RARE_PROTOCOLS */
+ ;
+
+ /* Keep these arrays in sync! */
+
+ static const uint16_t arphrd_type[] = {
+ 0, /* "generic" "\0" */
+ ARPHRD_LOOPBACK, /* "loopback" "\0" */
+ ARPHRD_ETHER, /* "ether" "\0" */
+#ifdef ARPHRD_INFINIBAND
+ ARPHRD_INFINIBAND, /* "infiniband" "\0" */
+#endif
+#ifdef ARPHRD_IEEE802_TR
+ ARPHRD_IEEE802, /* "ieee802" "\0" */
+ ARPHRD_IEEE802_TR, /* "tr" "\0" */
+#else
+ ARPHRD_IEEE802, /* "tr" "\0" */
+#endif
+#ifdef ARPHRD_IEEE80211
+ ARPHRD_IEEE80211, /* "ieee802.11" "\0" */
+#endif
+#ifdef ARPHRD_IEEE1394
+ ARPHRD_IEEE1394, /* "ieee1394" "\0" */
+#endif
+ ARPHRD_IRDA, /* "irda" "\0" */
+ ARPHRD_SLIP, /* "slip" "\0" */
+ ARPHRD_CSLIP, /* "cslip" "\0" */
+ ARPHRD_SLIP6, /* "slip6" "\0" */
+ ARPHRD_CSLIP6, /* "cslip6" "\0" */
+ ARPHRD_PPP, /* "ppp" "\0" */
+ ARPHRD_TUNNEL, /* "ipip" "\0" */
+ ARPHRD_TUNNEL6, /* "tunnel6" "\0" */
+ ARPHRD_SIT, /* "sit" "\0" */
+ ARPHRD_IPGRE, /* "gre" "\0" */
+#ifdef ARPHRD_VOID
+ ARPHRD_VOID, /* "void" "\0" */
+#endif
+
+#if ENABLE_FEATURE_IP_RARE_PROTOCOLS
+ ARPHRD_EETHER, /* "eether" "\0" */
+ ARPHRD_AX25, /* "ax25" "\0" */
+ ARPHRD_PRONET, /* "pronet" "\0" */
+ ARPHRD_CHAOS, /* "chaos" "\0" */
+ ARPHRD_ARCNET, /* "arcnet" "\0" */
+ ARPHRD_APPLETLK, /* "atalk" "\0" */
+ ARPHRD_DLCI, /* "dlci" "\0" */
+#ifdef ARPHRD_ATM
+ ARPHRD_ATM, /* "atm" "\0" */
+#endif
+ ARPHRD_METRICOM, /* "metricom" "\0" */
+ ARPHRD_RSRVD, /* "rsrvd" "\0" */
+ ARPHRD_ADAPT, /* "adapt" "\0" */
+ ARPHRD_ROSE, /* "rose" "\0" */
+ ARPHRD_X25, /* "x25" "\0" */
+#ifdef ARPHRD_HWX25
+ ARPHRD_HWX25, /* "hwx25" "\0" */
+#endif
+ ARPHRD_HDLC, /* "hdlc" "\0" */
+ ARPHRD_LAPB, /* "lapb" "\0" */
+#ifdef ARPHRD_DDCMP
+ ARPHRD_DDCMP, /* "ddcmp" "\0" */
+ ARPHRD_RAWHDLC, /* "rawhdlc" "\0" */
+#endif
+ ARPHRD_FRAD, /* "frad" "\0" */
+ ARPHRD_SKIP, /* "skip" "\0" */
+ ARPHRD_LOCALTLK, /* "ltalk" "\0" */
+ ARPHRD_FDDI, /* "fddi" "\0" */
+ ARPHRD_BIF, /* "bif" "\0" */
+ ARPHRD_IPDDP, /* "ip/ddp" "\0" */
+ ARPHRD_PIMREG, /* "pimreg" "\0" */
+ ARPHRD_HIPPI, /* "hippi" "\0" */
+ ARPHRD_ASH, /* "ash" "\0" */
+ ARPHRD_ECONET, /* "econet" "\0" */
+ ARPHRD_FCPP, /* "fcpp" "\0" */
+ ARPHRD_FCAL, /* "fcal" "\0" */
+ ARPHRD_FCPL, /* "fcpl" "\0" */
+ ARPHRD_FCFABRIC, /* "fcfb0" "\0" */
+ ARPHRD_FCFABRIC+1, /* "fcfb1" "\0" */
+ ARPHRD_FCFABRIC+2, /* "fcfb2" "\0" */
+ ARPHRD_FCFABRIC+3, /* "fcfb3" "\0" */
+ ARPHRD_FCFABRIC+4, /* "fcfb4" "\0" */
+ ARPHRD_FCFABRIC+5, /* "fcfb5" "\0" */
+ ARPHRD_FCFABRIC+6, /* "fcfb6" "\0" */
+ ARPHRD_FCFABRIC+7, /* "fcfb7" "\0" */
+ ARPHRD_FCFABRIC+8, /* "fcfb8" "\0" */
+ ARPHRD_FCFABRIC+9, /* "fcfb9" "\0" */
+ ARPHRD_FCFABRIC+10, /* "fcfb10" "\0" */
+ ARPHRD_FCFABRIC+11, /* "fcfb11" "\0" */
+ ARPHRD_FCFABRIC+12, /* "fcfb12" "\0" */
+#endif /* FEATURE_IP_RARE_PROTOCOLS */
+ };
+
+ unsigned i;
+ const char *aname = arphrd_name;
+ for (i = 0; i < ARRAY_SIZE(arphrd_type); i++) {
+ if (arphrd_type[i] == type)
+ return aname;
+ aname += strlen(aname) + 1;
+ }
+ snprintf(buf, len, "[%d]", type);
+ return buf;
+}
diff --git a/networking/libiproute/rt_names.c b/networking/libiproute/rt_names.c
new file mode 100644
index 0000000..e4d1061
--- /dev/null
+++ b/networking/libiproute/rt_names.c
@@ -0,0 +1,349 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rt_names.c rtnetlink names DB.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include "libbb.h"
+#include "rt_names.h"
+
+/* so far all callers have size == 256 */
+#define rtnl_tab_initialize(file, tab, size) rtnl_tab_initialize(file, tab)
+#define size 256
+static void rtnl_tab_initialize(const char *file, const char **tab, int size)
+{
+ char *token[2];
+ parser_t *parser = config_open2(file, fopen_for_read);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+ int id = bb_strtou(token[0], NULL, 0);
+ if (id < 0 || id > size) {
+ bb_error_msg("database %s is corrupted at line %d",
+ file, parser->lineno);
+ break;
+ }
+ tab[id] = xstrdup(token[1]);
+ }
+ config_close(parser);
+}
+#undef size
+
+static const char **rtnl_rtprot_tab; /* [256] */
+
+static void rtnl_rtprot_initialize(void)
+{
+ static const char *const init_tab[] = {
+ "none",
+ "redirect",
+ "kernel",
+ "boot",
+ "static",
+ NULL,
+ NULL,
+ NULL,
+ "gated",
+ "ra",
+ "mrt",
+ "zebra",
+ "bird",
+ };
+ if (rtnl_rtprot_tab) return;
+ rtnl_rtprot_tab = xzalloc(256 * sizeof(rtnl_rtprot_tab[0]));
+ memcpy(rtnl_rtprot_tab, init_tab, sizeof(init_tab));
+ rtnl_tab_initialize("/etc/iproute2/rt_protos",
+ rtnl_rtprot_tab, 256);
+}
+
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtprot_initialize();
+
+ if (rtnl_rtprot_tab[id])
+ return rtnl_rtprot_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rtprot_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtprot_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtprot_tab[i] &&
+ strcmp(rtnl_rtprot_tab[i], arg) == 0) {
+ cache = rtnl_rtprot_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+static const char **rtnl_rtscope_tab; /* [256] */
+
+static void rtnl_rtscope_initialize(void)
+{
+ if (rtnl_rtscope_tab) return;
+ rtnl_rtscope_tab = xzalloc(256 * sizeof(rtnl_rtscope_tab[0]));
+ rtnl_rtscope_tab[0] = "global";
+ rtnl_rtscope_tab[255] = "nowhere";
+ rtnl_rtscope_tab[254] = "host";
+ rtnl_rtscope_tab[253] = "link";
+ rtnl_rtscope_tab[200] = "site";
+ rtnl_tab_initialize("/etc/iproute2/rt_scopes",
+ rtnl_rtscope_tab, 256);
+}
+
+
+const char* rtnl_rtscope_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtscope_initialize();
+
+ if (rtnl_rtscope_tab[id])
+ return rtnl_rtscope_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rtscope_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtscope_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtscope_tab[i] &&
+ strcmp(rtnl_rtscope_tab[i], arg) == 0) {
+ cache = rtnl_rtscope_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+static const char **rtnl_rtrealm_tab; /* [256] */
+
+static void rtnl_rtrealm_initialize(void)
+{
+ if (rtnl_rtrealm_tab) return;
+ rtnl_rtrealm_tab = xzalloc(256 * sizeof(rtnl_rtrealm_tab[0]));
+ rtnl_rtrealm_tab[0] = "unknown";
+ rtnl_tab_initialize("/etc/iproute2/rt_realms",
+ rtnl_rtrealm_tab, 256);
+}
+
+
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtrealm_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtrealm_tab[i] &&
+ strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
+ cache = rtnl_rtrealm_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+#if ENABLE_FEATURE_IP_RULE
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtrealm_initialize();
+
+ if (rtnl_rtrealm_tab[id])
+ return rtnl_rtrealm_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+#endif
+
+
+static const char **rtnl_rtdsfield_tab; /* [256] */
+
+static void rtnl_rtdsfield_initialize(void)
+{
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rtdsfield_tab = xzalloc(256 * sizeof(rtnl_rtdsfield_tab[0]));
+ rtnl_rtdsfield_tab[0] = "0";
+ rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
+}
+
+
+const char * rtnl_dsfield_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtdsfield_initialize();
+
+ if (rtnl_rtdsfield_tab[id])
+ return rtnl_rtdsfield_tab[id];
+ snprintf(buf, len, "0x%02x", id);
+ return buf;
+}
+
+
+int rtnl_dsfield_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtdsfield_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtdsfield_tab[i] &&
+ strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
+ cache = rtnl_rtdsfield_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 16);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+#if ENABLE_FEATURE_IP_RULE
+static const char **rtnl_rttable_tab; /* [256] */
+
+static void rtnl_rttable_initialize(void)
+{
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rttable_tab = xzalloc(256 * sizeof(rtnl_rttable_tab[0]));
+ rtnl_rttable_tab[0] = "unspec";
+ rtnl_rttable_tab[255] = "local";
+ rtnl_rttable_tab[254] = "main";
+ rtnl_rttable_tab[253] = "default";
+ rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab, 256);
+}
+
+
+const char *rtnl_rttable_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rttable_initialize();
+
+ if (rtnl_rttable_tab[id])
+ return rtnl_rttable_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rttable_a2n(uint32_t * id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rttable_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rttable_tab[i] && strcmp(rtnl_rttable_tab[i], arg) == 0) {
+ cache = (char*)rtnl_rttable_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ i = bb_strtoul(arg, NULL, 0);
+ if (errno || i > 255)
+ return -1;
+ *id = i;
+ return 0;
+}
+
+#endif
diff --git a/networking/libiproute/rt_names.h b/networking/libiproute/rt_names.h
new file mode 100644
index 0000000..3d68b67
--- /dev/null
+++ b/networking/libiproute/rt_names.h
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+#ifndef RT_NAMES_H_
+#define RT_NAMES_H_ 1
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+extern const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+extern const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+extern const char* rtnl_rttable_n2a(int id, char *buf, int len);
+extern int rtnl_rtprot_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtscope_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+extern int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+extern int rtnl_rttable_a2n(uint32_t *id, char *arg);
+
+
+extern const char* ll_type_n2a(int type, char *buf, int len);
+
+extern const char* ll_addr_n2a(unsigned char *addr, int alen, int type,
+ char *buf, int blen);
+extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
+
+
+extern const char* ll_proto_n2a(unsigned short id, char *buf, int len);
+extern int ll_proto_a2n(unsigned short *id, char *buf);
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif
diff --git a/networking/libiproute/rtm_map.c b/networking/libiproute/rtm_map.c
new file mode 100644
index 0000000..ca2f443
--- /dev/null
+++ b/networking/libiproute/rtm_map.c
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+#include "rt_names.h"
+#include "utils.h"
+
+const 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)
+{
+ static const char keywords[] ALIGN1 =
+ "local\0""nat\0""broadcast\0""brd\0""anycast\0"
+ "multicast\0""prohibit\0""unreachable\0""blackhole\0"
+ "xresolve\0""unicast\0""throw\0";
+ enum {
+ ARG_local = 1, ARG_nat, ARG_broadcast, ARG_brd, ARG_anycast,
+ ARG_multicast, ARG_prohibit, ARG_unreachable, ARG_blackhole,
+ ARG_xresolve, ARG_unicast, ARG_throw
+ };
+ const smalluint key = index_in_substrings(keywords, arg) + 1;
+ char *end;
+ unsigned long res;
+
+ if (key == ARG_local)
+ res = RTN_LOCAL;
+ else if (key == ARG_nat)
+ res = RTN_NAT;
+ else if (key == ARG_broadcast || key == ARG_brd)
+ res = RTN_BROADCAST;
+ else if (key == ARG_anycast)
+ res = RTN_ANYCAST;
+ else if (key == ARG_multicast)
+ res = RTN_MULTICAST;
+ else if (key == ARG_prohibit)
+ res = RTN_PROHIBIT;
+ else if (key == ARG_unreachable)
+ res = RTN_UNREACHABLE;
+ else if (key == ARG_blackhole)
+ res = RTN_BLACKHOLE;
+ else if (key == ARG_xresolve)
+ res = RTN_XRESOLVE;
+ else if (key == ARG_unicast)
+ res = RTN_UNICAST;
+ else if (key == ARG_throw)
+ 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(uint32_t *realms, char *arg)
+{
+ uint32_t realm = 0;
+ char *p = strchr(arg, '/');
+
+ *realms = 0;
+ if (p) {
+ *p = 0;
+ if (rtnl_rtrealm_a2n(realms, arg)) {
+ *p = '/';
+ return -1;
+ }
+ *realms <<= 16;
+ *p = '/';
+ arg = p+1;
+ }
+ if (*arg && rtnl_rtrealm_a2n(&realm, arg))
+ return -1;
+ *realms |= realm;
+ return 0;
+}
diff --git a/networking/libiproute/rtm_map.h b/networking/libiproute/rtm_map.h
new file mode 100644
index 0000000..02fa77e
--- /dev/null
+++ b/networking/libiproute/rtm_map.h
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __RTM_MAP_H__
+#define __RTM_MAP_H__ 1
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+const char *rtnl_rtntype_n2a(int id, char *buf, int len);
+int rtnl_rtntype_a2n(int *id, char *arg);
+
+int get_rt_realms(uint32_t *realms, char *arg);
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif /* __RTM_MAP_H__ */
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c
new file mode 100644
index 0000000..cd101f1
--- /dev/null
+++ b/networking/libiproute/utils.c
@@ -0,0 +1,324 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * utils.c
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libbb.h"
+#include "utils.h"
+#include "inet_common.h"
+
+int get_integer(int *val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
+ return -1;
+ *val = res;
+ return 0;
+}
+//XXX: FIXME: use some libbb function instead
+int get_unsigned(unsigned *val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u32(uint32_t * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u16(uint16_t * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u8(uint8_t * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFF)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_s16(int16_t * val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_s8(int8_t * val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_addr_1(inet_prefix * addr, char *name, int family)
+{
+ memset(addr, 0, sizeof(*addr));
+
+ if (strcmp(name, bb_str_default) == 0 ||
+ strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
+ addr->family = family;
+ addr->bytelen = (family == AF_INET6 ? 16 : 4);
+ addr->bitlen = -1;
+ return 0;
+ }
+
+ if (strchr(name, ':')) {
+ addr->family = AF_INET6;
+ if (family != AF_UNSPEC && family != AF_INET6)
+ return -1;
+ if (inet_pton(AF_INET6, name, addr->data) <= 0)
+ return -1;
+ addr->bytelen = 16;
+ addr->bitlen = -1;
+ return 0;
+ }
+
+ addr->family = AF_INET;
+ if (family != AF_UNSPEC && family != AF_INET)
+ return -1;
+ if (inet_pton(AF_INET, name, addr->data) <= 0)
+ return -1;
+ addr->bytelen = 4;
+ addr->bitlen = -1;
+ return 0;
+}
+
+int get_prefix_1(inet_prefix * dst, char *arg, int family)
+{
+ int err;
+ unsigned plen;
+ char *slash;
+
+ memset(dst, 0, sizeof(*dst));
+
+ if (strcmp(arg, bb_str_default) == 0 || strcmp(arg, "any") == 0) {
+ dst->family = family;
+ dst->bytelen = 0;
+ dst->bitlen = 0;
+ return 0;
+ }
+
+ slash = strchr(arg, '/');
+ if (slash)
+ *slash = '\0';
+ err = get_addr_1(dst, arg, family);
+ if (err == 0) {
+ dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
+ if (slash) {
+ inet_prefix netmask_pfx;
+
+ netmask_pfx.family = AF_UNSPEC;
+ if ((get_unsigned(&plen, slash + 1, 0) || plen > dst->bitlen)
+ && (get_addr_1(&netmask_pfx, slash + 1, family)))
+ err = -1;
+ else if (netmask_pfx.family == AF_INET) {
+ /* fill in prefix length of dotted quad */
+ uint32_t mask = ntohl(netmask_pfx.data[0]);
+ uint32_t host = ~mask;
+
+ /* a valid netmask must be 2^n - 1 */
+ if (!(host & (host + 1))) {
+ for (plen = 0; mask; mask <<= 1)
+ ++plen;
+ if (plen >= 0 && plen <= dst->bitlen) {
+ dst->bitlen = plen;
+ /* dst->flags |= PREFIXLEN_SPECIFIED; */
+ } else
+ err = -1;
+ } else
+ err = -1;
+ } else {
+ /* plain prefix */
+ dst->bitlen = plen;
+ }
+ }
+ }
+ if (slash)
+ *slash = '/';
+ return err;
+}
+
+int get_addr(inet_prefix * dst, char *arg, int family)
+{
+ if (family == AF_PACKET) {
+ bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "address");
+ }
+ if (get_addr_1(dst, arg, family)) {
+ bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "address", arg);
+ }
+ return 0;
+}
+
+int get_prefix(inet_prefix * dst, char *arg, int family)
+{
+ if (family == AF_PACKET) {
+ bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix");
+ }
+ if (get_prefix_1(dst, arg, family)) {
+ bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
+ }
+ return 0;
+}
+
+uint32_t get_addr32(char *name)
+{
+ inet_prefix addr;
+
+ if (get_addr_1(&addr, name, AF_INET)) {
+ bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "IP", "address", name);
+ }
+ return addr.data[0];
+}
+
+void incomplete_command(void)
+{
+ bb_error_msg_and_die("command line is not complete, try option \"help\"");
+}
+
+void invarg(const char *arg, const char *opt)
+{
+ bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
+}
+
+void duparg(const char *key, const char *arg)
+{
+ bb_error_msg_and_die("duplicate \"%s\": \"%s\" is the second value", key, arg);
+}
+
+void duparg2(const char *key, const char *arg)
+{
+ bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
+}
+
+int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
+{
+ uint32_t *a1 = a->data;
+ uint32_t *a2 = b->data;
+ int words = bits >> 0x05;
+
+ bits &= 0x1f;
+
+ if (words)
+ if (memcmp(a1, a2, words << 2))
+ return -1;
+
+ if (bits) {
+ uint32_t w1, w2;
+ uint32_t mask;
+
+ w1 = a1[words];
+ w2 = a2[words];
+
+ mask = htonl((0xffffffff) << (0x20 - bits));
+
+ if ((w1 ^ w2) & mask)
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *rt_addr_n2a(int af, int UNUSED_PARAM len,
+ void *addr, char *buf, int buflen)
+{
+ switch (af) {
+ case AF_INET:
+ case AF_INET6:
+ return inet_ntop(af, addr, buf, buflen);
+ default:
+ return "???";
+ }
+}
+
+
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+#ifdef RESOLVE_HOSTNAMES
+ if (resolve_hosts) {
+ struct hostent *h_ent;
+
+ if (len <= 0) {
+ switch (af) {
+ case AF_INET:
+ len = 4;
+ break;
+ case AF_INET6:
+ len = 16;
+ break;
+ default:;
+ }
+ }
+ if (len > 0) {
+ h_ent = gethostbyaddr(addr, len, af);
+ if (h_ent != NULL) {
+ safe_strncpy(buf, h_ent->h_name, buflen);
+ return buf;
+ }
+ }
+ }
+#endif
+ return rt_addr_n2a(af, len, addr, buf, buflen);
+}
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h
new file mode 100644
index 0000000..1af39ff
--- /dev/null
+++ b/networking/libiproute/utils.h
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "rtm_map.h"
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+extern family_t preferred_family;
+extern smallint show_stats; /* UNUSED */
+extern smallint show_details; /* UNUSED */
+extern smallint show_raw; /* UNUSED */
+extern smallint resolve_hosts; /* UNUSED */
+extern smallint oneline;
+extern char _SL_;
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
+
+extern void incomplete_command(void) NORETURN;
+
+#define NEXT_ARG() do { if (!*++argv) incomplete_command(); } while (0)
+
+typedef struct {
+ uint8_t family;
+ uint8_t bytelen;
+ int16_t bitlen;
+ uint32_t data[4];
+} inet_prefix;
+
+#define PREFIXLEN_SPECIFIED 1
+
+#define DN_MAXADDL 20
+#ifndef AF_DECnet
+#define AF_DECnet 12
+#endif
+
+struct dn_naddr {
+ unsigned short a_len;
+ unsigned char a_addr[DN_MAXADDL];
+};
+
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+ uint32_t ipx_net;
+ uint8_t ipx_node[IPX_NODE_LEN];
+};
+
+extern uint32_t get_addr32(char *name);
+extern int get_addr_1(inet_prefix *dst, char *arg, int family);
+extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
+extern int get_addr(inet_prefix *dst, char *arg, int family);
+extern int get_prefix(inet_prefix *dst, char *arg, int family);
+
+extern int get_integer(int *val, char *arg, int base);
+extern int get_unsigned(unsigned *val, char *arg, int base);
+#define get_byte get_u8
+#define get_ushort get_u16
+#define get_short get_s16
+extern int get_u32(uint32_t *val, char *arg, int base);
+extern int get_u16(uint16_t *val, char *arg, int base);
+extern int get_s16(int16_t *val, char *arg, int base);
+extern int get_u8(uint8_t *val, char *arg, int base);
+extern int get_s8(int8_t *val, char *arg, int base);
+
+extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
+
+void invarg(const char *, const char *) NORETURN;
+void duparg(const char *, const char *) NORETURN;
+void duparg2(const char *, const char *) NORETURN;
+int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
+int dnet_pton(int af, const char *src, void *addr);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif /* __UTILS_H__ */