busybox/networking/libiproute/ipaddress.c

849 lines
21 KiB
C
Raw Normal View History

/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
* 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>
2007-06-01 04:12:12 +05:30
#include "ip_common.h" /* #include "libbb.h" is inside */
#include "common_bufsiz.h"
#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
#ifndef IFA_F_NOPREFIXROUTE
# define IFA_FLAGS 8
/* ifa_flags */
# define IFA_F_NOPREFIXROUTE 0x200
#endif
struct filter_t {
char *label;
/* Flush cmd buf. If !NULL, print_addrinfo() constructs flush commands in it */
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;
/* Misnomer. Does not mean "flushed something" */
/* More like "flush commands were constructed by print_addrinfo()" */
smallint flushed;
inet_prefix pfx;
} FIX_ALIASING;
typedef struct filter_t filter_t;
#define G_filter (*(filter_t*)bb_common_bufsiz1)
#define INIT_G() do { setup_common_bufsiz(); } while (0)
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('<');
if (flags & IFF_UP && !(flags & IFF_RUNNING))
printf("NO-CARRIER,");
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, ",");
2006-01-25 05:38:53 +05:30
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_IFNAMSIZ(ifr.ifr_name, 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 NOINLINE int print_linkinfo(const struct nlmsghdr *n)
{
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct rtattr *tb[IFLA_MAX+1];
int len = n->nlmsg_len;
if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
return 0;
len -= NLMSG_LENGTH(sizeof(*ifi));
if (len < 0)
return -1;
if (G_filter.ifindex && ifi->ifi_index != G_filter.ifindex)
return 0;
if (G_filter.up && !(ifi->ifi_flags & IFF_UP))
return 0;
//memset(tb, 0, sizeof(tb)); - parse_rtattr does this
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
if (tb[IFLA_IFNAME] == NULL) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg("nil ifname");
return -1;
}
if (G_filter.label
&& (!G_filter.family || G_filter.family == AF_PACKET)
&& fnmatch(G_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>" - we checked tb[IFLA_IFNAME] above*/
(char*)RTA_DATA(tb[IFLA_IFNAME])
);
{
unsigned m_flag = 0;
if (tb[IFLA_LINK]) {
int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
if (iflink == 0)
printf("@NONE: ");
else {
printf("@%s: ", ll_index_to_name(iflink));
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]) {
printf("master %s ", ll_index_to_name(*(int*)RTA_DATA(tb[IFLA_MASTER])));
}
#endif
/* IFLA_OPERSTATE was added to kernel with the same commit as IFF_DORMANT */
#ifdef IFF_DORMANT
if (tb[IFLA_OPERSTATE]) {
static const char operstate_labels[] ALIGN1 =
"UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0"
"TESTING\0""DORMANT\0""UP\0";
printf("state %s ", nth_string(operstate_labels,
*(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE])));
}
#endif
if (G_filter.showqueue)
print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
if (!G_filter.family || G_filter.family == AF_PACKET) {
SPRINT_BUF(b1);
printf("%c link/%s ", _SL_, ll_type_n2a(ifi->ifi_type, 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_all();*/
return 0;
}
static int flush_update(void)
{
if (rtnl_send_check(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_perror_msg("can't send flush request");
return -1;
}
G_filter.flushp = 0;
return 0;
}
static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
2008-07-05 14:48:54 +05:30
struct nlmsghdr *n, void *arg UNUSED_PARAM)
{
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
unsigned int ifa_flags;
struct rtattr *rta_tb[IFA_MAX+1];
if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
return 0;
len -= NLMSG_LENGTH(sizeof(*ifa));
if (len < 0) {
2003-03-19 14:43:01 +05:30
bb_error_msg("wrong nlmsg len %d", len);
return -1;
}
if (G_filter.flushb && n->nlmsg_type != RTM_NEWADDR)
return 0;
//memset(rta_tb, 0, sizeof(rta_tb)); - parse_rtattr does this
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
ifa_flags = rta_tb[IFA_FLAGS] ? *(__u32*)RTA_DATA(rta_tb[IFA_FLAGS]) : ifa->ifa_flags;
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 (G_filter.ifindex && G_filter.ifindex != ifa->ifa_index)
return 0;
if ((G_filter.scope ^ ifa->ifa_scope) & G_filter.scopemask)
return 0;
if ((G_filter.flags ^ ifa_flags) & G_filter.flagmask)
return 0;
if (G_filter.label) {
const char *label;
if (rta_tb[IFA_LABEL])
label = RTA_DATA(rta_tb[IFA_LABEL]);
else
label = ll_index_to_name(ifa->ifa_index);
if (fnmatch(G_filter.label, label, 0) != 0)
return 0;
}
if (G_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, &G_filter.pfx, G_filter.pfx.bitlen))
return 0;
}
}
if (G_filter.flushb) {
struct nlmsghdr *fn;
if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
if (flush_update())
return -1;
}
fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
memcpy(fn, n, n->nlmsg_len);
fn->nlmsg_type = RTM_DELADDR;
fn->nlmsg_flags = NLM_F_REQUEST;
fn->nlmsg_seq = ++G_filter.rth->seq;
G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
G_filter.flushed = 1;
return 0;
}
if (n->nlmsg_type == RTM_DELADDR)
printf("Deleted ");
if (G_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_DATA(rta_tb[IFA_LOCAL])),
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_DATA(rta_tb[IFA_ADDRESS])),
ifa->ifa_prefixlen
);
}
}
if (rta_tb[IFA_BROADCAST]) {
printf("brd %s ",
rt_addr_n2a(ifa->ifa_family,
RTA_DATA(rta_tb[IFA_BROADCAST]))
);
}
if (rta_tb[IFA_ANYCAST]) {
printf("any %s ",
rt_addr_n2a(ifa->ifa_family,
RTA_DATA(rta_tb[IFA_ANYCAST]))
);
}
printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope));
if (ifa_flags & IFA_F_SECONDARY) {
ifa_flags &= ~IFA_F_SECONDARY;
printf("secondary ");
}
if (ifa_flags & IFA_F_TENTATIVE) {
ifa_flags &= ~IFA_F_TENTATIVE;
printf("tentative ");
}
if (ifa_flags & IFA_F_DADFAILED) {
ifa_flags &= ~IFA_F_DADFAILED;
printf("dadfailed ");
}
if (ifa_flags & IFA_F_DEPRECATED) {
ifa_flags &= ~IFA_F_DEPRECATED;
printf("deprecated ");
}
if (!(ifa_flags & IFA_F_PERMANENT)) {
printf("dynamic ");
} else
ifa_flags &= ~IFA_F_PERMANENT;
if (ifa_flags & IFA_F_NOPREFIXROUTE) {
ifa_flags &= ~IFA_F_NOPREFIXROUTE;
printf("noprefixroute ");
}
if (ifa_flags)
printf("flags %02x ", 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_all();*/
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
|| (G_filter.family && G_filter.family != ifa->ifa_family)
) {
continue;
}
print_addrinfo(NULL, n, NULL);
}
return 0;
}
static int FAST_FUNC 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 = xzalloc(n->nlmsg_len + sizeof(void*));
memcpy(&h->h, n, n->nlmsg_len);
/*h->next = NULL; - xzalloc did it */
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(&G_filter, 0, sizeof(G_filter));
G_filter.oneline = _oneline;
}
/* Return value becomes exitcode. It's okay to not return at all */
int FAST_FUNC 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;
ipaddr_reset_filter(oneline);
G_filter.showqueue = 1;
if (G_filter.family == AF_UNSPEC)
G_filter.family = preferred_family;
if (flush) {
if (!*argv) {
bb_error_msg_and_die(bb_msg_requires_arg, "flush");
}
if (G_filter.family == AF_PACKET) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg_and_die("can't flush link addresses");
}
}
while (*argv) {
const smalluint key = index_in_strings(option, *argv);
if (key == 0) { /* to */
NEXT_ARG();
get_prefix(&G_filter.pfx, *argv, G_filter.family);
if (G_filter.family == AF_UNSPEC) {
G_filter.family = G_filter.pfx.family;
}
} else if (key == 1) { /* scope */
uint32_t scope = 0;
NEXT_ARG();
G_filter.scopemask = -1;
if (rtnl_rtscope_a2n(&scope, *argv)) {
if (strcmp(*argv, "all") != 0) {
invarg_1_to_2(*argv, "scope");
}
scope = RT_SCOPE_NOWHERE;
G_filter.scopemask = 0;
}
G_filter.scope = scope;
} else if (key == 2) { /* up */
G_filter.up = 1;
} else if (key == 3) { /* label */
NEXT_ARG();
G_filter.label = *argv;
} else {
if (key == 4) /* dev */
NEXT_ARG();
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) {
G_filter.ifindex = xll_name_to_index(filter_dev);
}
if (flush) {
char flushb[4096-512];
G_filter.flushb = flushb;
G_filter.flushp = 0;
G_filter.flushe = sizeof(flushb);
G_filter.rth = &rth;
for (;;) {
xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETADDR);
G_filter.flushed = 0;
xrtnl_dump_filter(&rth, print_addrinfo, NULL);
if (G_filter.flushed == 0) {
return 0;
}
if (flush_update() < 0) {
return 1;
}
}
}
if (G_filter.family != AF_PACKET) {
xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETADDR);
xrtnl_dump_filter(&rth, store_nlmsg, &ainfo);
}
if (G_filter.family && G_filter.family != AF_PACKET) {
struct nlmsg_list **lp;
lp = &linfo;
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
|| (G_filter.family && G_filter.family != ifa->ifa_family)
) {
continue;
}
if ((G_filter.scope ^ ifa->ifa_scope) & G_filter.scopemask)
continue;
if ((G_filter.flags ^ ifa->ifa_flags) & G_filter.flagmask)
continue;
if (G_filter.pfx.family || G_filter.label) {
struct rtattr *tb[IFA_MAX+1];
//memset(tb, 0, sizeof(tb)); - parse_rtattr does this
parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
if (!tb[IFA_LOCAL])
tb[IFA_LOCAL] = tb[IFA_ADDRESS];
if (G_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, &G_filter.pfx, G_filter.pfx.bitlen))
continue;
}
if (G_filter.label) {
const char *label;
if (tb[IFA_LABEL])
label = RTA_DATA(tb[IFA_LABEL]);
else
label = ll_index_to_name(ifa->ifa_index);
if (fnmatch(G_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 ((oneline && G_filter.family != AF_PACKET)
/* ^^^^^^^^^ "ip -oneline a" does not print link info */
|| (print_linkinfo(&l->h) == 0)
) {
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
if (G_filter.family != AF_PACKET)
print_selected_addrinfo(ifi->ifi_index, ainfo);
}
}
return 0;
}
static void set_lifetime(unsigned int *lifetime, char *argv, const char *errmsg)
{
if (strcmp(argv, "forever") == 0)
*lifetime = INFINITY_LIFE_TIME;
else
*lifetime = get_u32(argv, errmsg);
}
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, int flags, char **argv)
{
/* If you add stuff here, update ipaddr_full_usage */
static const char option[] ALIGN1 =
"peer\0""remote\0""broadcast\0""brd\0"
"anycast\0""valid_lft\0""preferred_lft\0"
"scope\0""dev\0""label\0""noprefixroute\0""local\0";
#define option_peer option
#define option_broadcast (option + sizeof("peer") + sizeof("remote"))
#define option_anycast (option_broadcast + sizeof("broadcast") + sizeof("brd"))
#define option_valid_lft (option_anycast + sizeof("anycast"))
#define option_pref_lft (option_valid_lft + sizeof("valid_lft"))
struct rtnl_handle rth;
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[256];
} req;
char *d = NULL;
char *l = NULL;
char *valid_lftp = NULL;
char *preferred_lftp = NULL;
inet_prefix lcl;
inet_prefix peer;
int local_len = 0;
int peer_len = 0;
int brd_len = 0;
int any_len = 0;
bool scoped = 0;
__u32 valid_lft = INFINITY_LIFE_TIME;
__u32 preferred_lft = INFINITY_LIFE_TIME;
unsigned int ifa_flags = 0;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
req.n.nlmsg_type = cmd;
req.ifa.ifa_family = preferred_family;
while (*argv) {
unsigned arg = index_in_strings(option, *argv);
/* if search fails, "local" is assumed */
if (arg <= 1) { /* peer, remote */
NEXT_ARG();
if (peer_len) {
duparg(option_peer, *argv);
}
get_prefix(&peer, *argv, req.ifa.ifa_family);
peer_len = peer.bytelen;
if (req.ifa.ifa_family == AF_UNSPEC) {
req.ifa.ifa_family = peer.family;
}
addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
req.ifa.ifa_prefixlen = peer.bitlen;
} else if (arg <= 3) { /* broadcast, brd */
inet_prefix addr;
NEXT_ARG();
if (brd_len) {
duparg(option_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;
}
} else if (arg == 4) { /* anycast */
inet_prefix addr;
NEXT_ARG();
if (any_len) {
duparg(option_anycast, *argv);
}
get_addr(&addr, *argv, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC) {
req.ifa.ifa_family = addr.family;
}
addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
any_len = addr.bytelen;
} else if (arg == 5) { /* valid_lft */
if (valid_lftp)
duparg(option_valid_lft, *argv);
NEXT_ARG();
valid_lftp = *argv;
set_lifetime(&valid_lft, *argv, option_valid_lft);
} else if (arg == 6) { /* preferred_lft */
if (preferred_lftp)
duparg(option_pref_lft, *argv);
NEXT_ARG();
preferred_lftp = *argv;
set_lifetime(&preferred_lft, *argv, option_pref_lft);
} else if (arg == 7) { /* scope */
uint32_t scope = 0;
NEXT_ARG();
if (rtnl_rtscope_a2n(&scope, *argv)) {
invarg_1_to_2(*argv, "scope");
}
req.ifa.ifa_scope = scope;
scoped = 1;
} else if (arg == 8) { /* dev */
NEXT_ARG();
d = *argv;
} else if (arg == 9) { /* label */
NEXT_ARG();
l = *argv;
addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l) + 1);
} else if (arg == 10) { /* noprefixroute */
ifa_flags |= IFA_F_NOPREFIXROUTE;
} else {
/* local (specified or assumed) */
if ((int)arg >= 0)
NEXT_ARG();
if (local_len) {
duparg2("local", *argv);
}
get_prefix(&lcl, *argv, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC) {
req.ifa.ifa_family = lcl.family;
}
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
local_len = lcl.bytelen;
}
argv++;
}
if (ifa_flags <= 0xff)
req.ifa.ifa_flags = ifa_flags;
else
addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags);
if (!d) {
/* There was no "dev IFACE", but we need that */
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg_and_die("need \"dev IFACE\"");
}
libbb: introduce and use is_prefixed_with() function old new delta is_prefixed_with - 18 +18 complete_username 78 77 -1 man_main 737 735 -2 fsck_device 429 427 -2 unpack_ar_archive 80 76 -4 strip_unsafe_prefix 105 101 -4 singlemount 1054 1050 -4 rtc_adjtime_is_utc 90 86 -4 resolve_mount_spec 88 84 -4 parse_one_line 1029 1025 -4 parse_conf 1460 1456 -4 may_wakeup 83 79 -4 loadkmap_main 219 215 -4 get_irqs_from_stat 103 99 -4 get_header_cpio 913 909 -4 findfs_main 79 75 -4 fbsplash_main 1230 1226 -4 load_crontab 776 771 -5 expand_vars_to_list 1151 1146 -5 date_main 881 876 -5 skip_dev_pfx 30 24 -6 make_device 2199 2193 -6 complete_cmd_dir_file 773 767 -6 run_applet_and_exit 715 708 -7 uudecode_main 321 313 -8 pwdx_main 197 189 -8 execute 568 560 -8 i2cdetect_main 1186 1176 -10 procps_scan 1242 1230 -12 procps_read_smaps 1017 1005 -12 process_module 746 734 -12 patch_main 1903 1891 -12 nfsmount 3572 3560 -12 stack_machine 126 112 -14 process_timer_stats 449 435 -14 match_fstype 111 97 -14 do_ipaddr 1344 1330 -14 open_list_and_close 359 343 -16 get_header_tar 1795 1779 -16 prepend_new_eth_table 340 323 -17 fsck_main 1811 1794 -17 find_iface_state 56 38 -18 dnsd_main 1321 1303 -18 base_device 179 158 -21 find_keyword 104 82 -22 handle_incoming_and_exit 2785 2762 -23 parse_and_put_prompt 774 746 -28 modinfo 347 317 -30 find_action 204 171 -33 update_passwd 1470 1436 -34 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/49 up/down: 18/-540) Total: -522 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2015-03-12 22:18:34 +05:30
if (l && !is_prefixed_with(l, d)) {
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) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_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 (valid_lftp || preferred_lftp) {
struct ifa_cacheinfo cinfo = {};
if (!valid_lft) {
fprintf(stderr, "valid_lft is zero\n");
return 1;
}
if (valid_lft < preferred_lft) {
fprintf(stderr, "preferred_lft is greater than valid_lft\n");
return 1;
}
cinfo.ifa_prefered = preferred_lft;
cinfo.ifa_valid = valid_lft;
addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
sizeof(cinfo));
}
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
return 2;
return 0;
}
/* Return value becomes exitcode. It's okay to not return at all */
int FAST_FUNC do_ipaddr(char **argv)
{
static const char commands[] ALIGN1 =
/* 0 1 2 3 4 5 6 7 8 */
"add\0""change\0""chg\0""replace\0""delete\0""list\0""show\0""lst\0""flush\0";
int cmd = 2;
INIT_G();
if (*argv) {
cmd = index_in_substrings(commands, *argv);
if (cmd < 0)
invarg_1_to_2(*argv, applet_name);
argv++;
if (cmd <= 4) {
return ipaddr_modify(
/*cmd:*/ cmd == 4 ? RTM_DELADDR : RTM_NEWADDR,
/*flags:*/
cmd == 0 ? NLM_F_CREATE|NLM_F_EXCL : /* add */
cmd == 1 || cmd == 2 ? NLM_F_REPLACE : /* change */
cmd == 3 ? NLM_F_CREATE|NLM_F_REPLACE : /* replace */
0 /* delete */
, argv);
}
}
return ipaddr_list_or_flush(argv, cmd == 8);
}