diff options
Diffstat (limited to 'networking/libiproute')
-rw-r--r-- | networking/libiproute/Kbuild | 64 | ||||
-rw-r--r-- | networking/libiproute/ip_common.h | 41 | ||||
-rw-r--r-- | networking/libiproute/ip_parse_common_args.c | 84 | ||||
-rw-r--r-- | networking/libiproute/ipaddress.c | 784 | ||||
-rw-r--r-- | networking/libiproute/iplink.c | 306 | ||||
-rw-r--r-- | networking/libiproute/iproute.c | 907 | ||||
-rw-r--r-- | networking/libiproute/iprule.c | 334 | ||||
-rw-r--r-- | networking/libiproute/iptunnel.c | 580 | ||||
-rw-r--r-- | networking/libiproute/libnetlink.c | 409 | ||||
-rw-r--r-- | networking/libiproute/libnetlink.h | 55 | ||||
-rw-r--r-- | networking/libiproute/ll_addr.c | 79 | ||||
-rw-r--r-- | networking/libiproute/ll_map.c | 200 | ||||
-rw-r--r-- | networking/libiproute/ll_map.h | 21 | ||||
-rw-r--r-- | networking/libiproute/ll_proto.c | 127 | ||||
-rw-r--r-- | networking/libiproute/ll_types.c | 205 | ||||
-rw-r--r-- | networking/libiproute/rt_names.c | 349 | ||||
-rw-r--r-- | networking/libiproute/rt_names.h | 35 | ||||
-rw-r--r-- | networking/libiproute/rtm_map.c | 118 | ||||
-rw-r--r-- | networking/libiproute/rtm_map.h | 18 | ||||
-rw-r--r-- | networking/libiproute/utils.c | 324 | ||||
-rw-r--r-- | networking/libiproute/utils.h | 96 |
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__ */ |