#include <linux/param.h>
#include <linux/if_arp.h>
#include <linux/mpls.h>
+#include <linux/snmp.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
+#ifdef HAVE_LIBCAP
+#include <sys/capability.h>
+#endif
#include "rt_names.h"
#include "utils.h"
+#include "ll_map.h"
#include "namespace.h"
int resolve_hosts;
int timestamp_short;
+int pretty;
+const char *_SL_ = "\n";
int read_prop(const char *dev, char *prop, long *value)
{
*val = strtod(str, &p) / 100.;
if (*val == HUGE_VALF || *val == HUGE_VALL)
return 1;
- if (*val == 0.0 || (*p && strcmp(p, "%")))
+ if (*p && strcmp(p, "%"))
return -1;
return 0;
return 0;
}
+int get_s64(__s64 *val, const char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ errno = 0;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoll(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr)
+ return -1;
+ if ((res == LLONG_MIN || res == LLONG_MAX) && errno == ERANGE)
+ return -1;
+ if (res > INT64_MAX || res < INT64_MIN)
+ return -1;
+
+ *val = res;
+ return 0;
+}
+
int get_s32(__s32 *val, const char *arg, int base)
{
long res;
return 1;
}
-int get_addr_1(inet_prefix *addr, const char *name, int family)
+static void set_address_type(inet_prefix *addr)
+{
+ switch (addr->family) {
+ case AF_INET:
+ if (!addr->data[0])
+ addr->flags |= ADDRTYPE_INET_UNSPEC;
+ else if (IN_MULTICAST(ntohl(addr->data[0])))
+ addr->flags |= ADDRTYPE_INET_MULTI;
+ else
+ addr->flags |= ADDRTYPE_INET;
+ break;
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(addr->data))
+ addr->flags |= ADDRTYPE_INET_UNSPEC;
+ else if (IN6_IS_ADDR_MULTICAST(addr->data))
+ addr->flags |= ADDRTYPE_INET_MULTI;
+ else
+ addr->flags |= ADDRTYPE_INET;
+ break;
+ }
+}
+
+static int __get_addr_1(inet_prefix *addr, const char *name, int family)
{
memset(addr, 0, sizeof(*addr));
- if (strcmp(name, "default") == 0 ||
- strcmp(name, "all") == 0 ||
+ if (strcmp(name, "default") == 0) {
+ if ((family == AF_DECnet) || (family == AF_MPLS))
+ return -1;
+ addr->family = family;
+ addr->bytelen = af_byte_len(addr->family);
+ addr->bitlen = -2;
+ addr->flags |= PREFIXLEN_SPECIFIED;
+ return 0;
+ }
+
+ if (strcmp(name, "all") == 0 ||
strcmp(name, "any") == 0) {
if ((family == AF_DECnet) || (family == AF_MPLS))
return -1;
addr->family = family;
- addr->bytelen = (family == AF_INET6 ? 16 : 4);
- addr->bitlen = -1;
+ addr->bytelen = 0;
+ addr->bitlen = -2;
return 0;
}
return 0;
}
+int get_addr_1(inet_prefix *addr, const char *name, int family)
+{
+ int ret;
+
+ ret = __get_addr_1(addr, name, family);
+ if (ret)
+ return ret;
+
+ set_address_type(addr);
+ return 0;
+}
+
int af_bit_len(int af)
{
switch (af) {
int get_prefix_1(inet_prefix *dst, char *arg, int family)
{
- int err;
- unsigned int plen;
char *slash;
-
- memset(dst, 0, sizeof(*dst));
-
- if (strcmp(arg, "default") == 0 ||
- strcmp(arg, "any") == 0 ||
- strcmp(arg, "all") == 0) {
- if ((family == AF_DECnet) || (family == AF_MPLS))
- return -1;
- dst->family = family;
- dst->bytelen = 0;
- dst->bitlen = 0;
- dst->flags |= PREFIXLEN_SPECIFIED;
- return 0;
- }
+ int err, bitlen, flags;
slash = strchr(arg, '/');
if (slash)
*slash = 0;
err = get_addr_1(dst, arg, family);
- if (err == 0) {
- dst->bitlen = af_bit_len(dst->family);
-
- if (slash) {
- if (get_netmask(&plen, slash+1, 0)
- || plen > dst->bitlen) {
- err = -1;
- goto done;
- }
- dst->flags |= PREFIXLEN_SPECIFIED;
- dst->bitlen = plen;
- }
- }
-done:
+
if (slash)
*slash = '/';
- return err;
+
+ if (err)
+ return err;
+
+ bitlen = af_bit_len(dst->family);
+
+ flags = 0;
+ if (slash) {
+ unsigned int plen;
+
+ if (dst->bitlen == -2)
+ return -1;
+ if (get_netmask(&plen, slash + 1, 0))
+ return -1;
+ if (plen > bitlen)
+ return -1;
+
+ flags |= PREFIXLEN_SPECIFIED;
+ bitlen = plen;
+ } else {
+ if (dst->bitlen == -2)
+ bitlen = 0;
+ }
+
+ dst->flags |= flags;
+ dst->bitlen = bitlen;
+
+ return 0;
}
static const char *family_name_verbose(int family)
return 0;
}
+int get_addr_rta(inet_prefix *dst, const struct rtattr *rta, int family)
+{
+ const int len = RTA_PAYLOAD(rta);
+ const void *data = RTA_DATA(rta);
+
+ switch (len) {
+ case 4:
+ dst->family = AF_INET;
+ dst->bytelen = 4;
+ memcpy(dst->data, data, 4);
+ break;
+ case 16:
+ dst->family = AF_INET6;
+ dst->bytelen = 16;
+ memcpy(dst->data, data, 16);
+ break;
+ case 2:
+ dst->family = AF_DECnet;
+ dst->bytelen = 2;
+ memcpy(dst->data, data, 2);
+ break;
+ case 10:
+ dst->family = AF_IPX;
+ dst->bytelen = 10;
+ memcpy(dst->data, data, 10);
+ break;
+ default:
+ return -1;
+ }
+
+ if (family != AF_UNSPEC && family != dst->family)
+ return -2;
+
+ dst->bitlen = -1;
+ dst->flags = 0;
+
+ set_address_type(dst);
+ return 0;
+}
+
int get_prefix(inet_prefix *dst, char *arg, int family)
{
if (family == AF_PACKET) {
exit(-1);
}
+int nodev(const char *dev)
+{
+ fprintf(stderr, "Cannot find device \"%s\"\n", dev);
+ return -1;
+}
+
int check_ifname(const char *name)
{
/* These checks mimic kernel checks in dev_valid_name */
return ret;
}
+const char *get_ifname_rta(int ifindex, const struct rtattr *rta)
+{
+ const char *name;
+
+ if (rta) {
+ name = rta_getattr_str(rta);
+ } else {
+ fprintf(stderr,
+ "BUG: device with ifindex %d has nil ifname\n",
+ ifindex);
+ name = ll_idx_n2a(ifindex);
+ }
+
+ if (check_ifname(name))
+ return NULL;
+
+ return name;
+}
+
int matches(const char *cmd, const char *pattern)
{
int len = strlen(cmd);
return 0;
}
+int inet_addr_match_rta(const inet_prefix *m, const struct rtattr *rta)
+{
+ inet_prefix dst;
+
+ if (!rta || m->family == AF_UNSPEC || m->bitlen <= 0)
+ return 0;
+
+ if (get_addr_rta(&dst, rta, m->family))
+ return -1;
+
+ return inet_addr_match(&dst, m, m->bitlen);
+}
+
int __iproute2_hz_internal;
int __get_hz(void)
}
case AF_PACKET:
return ll_addr_n2a(addr, len, ARPHRD_VOID, buf, buflen);
+ case AF_BRIDGE:
+ {
+ const union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } *sa = addr;
+
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ return inet_ntop(AF_INET, &sa->sin.sin_addr,
+ buf, buflen);
+ case AF_INET6:
+ return inet_ntop(AF_INET6, &sa->sin6.sin6_addr,
+ buf, buflen);
+ }
+
+ /* fallthrough */
+ }
default:
return "???";
}
return 0;
}
+unsigned int print_name_and_link(const char *fmt,
+ const char *name, struct rtattr *tb[])
+{
+ const char *link = NULL;
+ unsigned int m_flag = 0;
+ SPRINT_BUF(b1);
+
+ if (tb[IFLA_LINK]) {
+ int iflink = rta_getattr_u32(tb[IFLA_LINK]);
+
+ if (iflink) {
+ if (tb[IFLA_LINK_NETNSID]) {
+ if (is_json_context()) {
+ print_int(PRINT_JSON,
+ "link_index", NULL, iflink);
+ } else {
+ link = ll_idx_n2a(iflink);
+ }
+ } else {
+ link = ll_index_to_name(iflink);
+
+ if (is_json_context()) {
+ print_string(PRINT_JSON,
+ "link", NULL, link);
+ link = NULL;
+ }
+
+ m_flag = ll_index_to_flags(iflink);
+ m_flag = !(m_flag & IFF_UP);
+ }
+ } else {
+ if (is_json_context())
+ print_null(PRINT_JSON, "link", NULL, NULL);
+ else
+ link = "NONE";
+ }
+
+ if (link) {
+ snprintf(b1, sizeof(b1), "%s@%s", name, link);
+ name = b1;
+ }
+ }
+
+ print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", fmt, name);
+
+ return m_flag;
+}
+
int cmdlineno;
/* Like glibc getline but handle continuation lines and comments */
break;
}
- /* seperate words */
+ /* separate words */
*cp++ = 0;
}
argv[argc] = NULL;
return argc;
}
-int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6)
-{
- if (strchr(src, ':'))
- return inet_pton(AF_INET6, src, dst6);
- else
- return inet_pton(AF_INET, src, dst);
-}
-
void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n)
{
char *tstr;
return rtm_family;
}
+/* Based on copy_rtnl_link_stats() from kernel at net/core/rtnetlink.c */
+static void copy_rtnl_link_stats64(struct rtnl_link_stats64 *stats64,
+ const struct rtnl_link_stats *stats)
+{
+ __u64 *a = (__u64 *)stats64;
+ const __u32 *b = (const __u32 *)stats;
+ const __u32 *e = b + sizeof(*stats) / sizeof(*b);
+
+ while (b < e)
+ *a++ = *b++;
+}
+
+#define IPSTATS_MIB_MAX_LEN (__IPSTATS_MIB_MAX * sizeof(__u64))
+static void get_snmp_counters(struct rtnl_link_stats64 *stats64,
+ struct rtattr *s)
+{
+ __u64 *mib = (__u64 *)RTA_DATA(s);
+
+ memset(stats64, 0, sizeof(*stats64));
+
+ stats64->rx_packets = mib[IPSTATS_MIB_INPKTS];
+ stats64->rx_bytes = mib[IPSTATS_MIB_INOCTETS];
+ stats64->tx_packets = mib[IPSTATS_MIB_OUTPKTS];
+ stats64->tx_bytes = mib[IPSTATS_MIB_OUTOCTETS];
+ stats64->rx_errors = mib[IPSTATS_MIB_INDISCARDS];
+ stats64->tx_errors = mib[IPSTATS_MIB_OUTDISCARDS];
+ stats64->multicast = mib[IPSTATS_MIB_INMCASTPKTS];
+ stats64->rx_frame_errors = mib[IPSTATS_MIB_CSUMERRORS];
+}
+
+int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
+ struct rtattr *tb[])
+{
+ struct rtnl_link_stats stats;
+ void *s;
+ struct rtattr *rta;
+ int size, len;
+
+ if (tb[IFLA_STATS64]) {
+ rta = tb[IFLA_STATS64];
+ size = sizeof(struct rtnl_link_stats64);
+ s = stats64;
+ } else if (tb[IFLA_STATS]) {
+ rta = tb[IFLA_STATS];
+ size = sizeof(struct rtnl_link_stats);
+ s = &stats;
+ } else if (tb[IFLA_PROTINFO]) {
+ struct rtattr *ptb[IPSTATS_MIB_MAX_LEN + 1];
+
+ parse_rtattr_nested(ptb, IPSTATS_MIB_MAX_LEN,
+ tb[IFLA_PROTINFO]);
+ if (ptb[IFLA_INET6_STATS])
+ get_snmp_counters(stats64, ptb[IFLA_INET6_STATS]);
+ return sizeof(*stats64);
+ } else {
+ return -1;
+ }
+
+ len = RTA_PAYLOAD(rta);
+ if (len < size)
+ memset(s + len, 0, size - len);
+ else
+ len = size;
+
+ memcpy(s, RTA_DATA(rta), len);
+
+ if (s != stats64)
+ copy_rtnl_link_stats64(stats64, s);
+ return size;
+}
+
#ifdef NEED_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t size)
{
return dlen + strlcpy(dst + dlen, src, size - dlen);
}
#endif
+
+void drop_cap(void)
+{
+#ifdef HAVE_LIBCAP
+ /* don't harmstring root/sudo */
+ if (getuid() != 0 && geteuid() != 0) {
+ cap_t capabilities;
+ cap_value_t net_admin = CAP_NET_ADMIN;
+ cap_flag_t inheritable = CAP_INHERITABLE;
+ cap_flag_value_t is_set;
+
+ capabilities = cap_get_proc();
+ if (!capabilities)
+ exit(EXIT_FAILURE);
+ if (cap_get_flag(capabilities, net_admin, inheritable,
+ &is_set) != 0)
+ exit(EXIT_FAILURE);
+ /* apps with ambient caps can fork and call ip */
+ if (is_set == CAP_CLEAR) {
+ if (cap_clear(capabilities) != 0)
+ exit(EXIT_FAILURE);
+ if (cap_set_proc(capabilities) != 0)
+ exit(EXIT_FAILURE);
+ }
+ cap_free(capabilities);
+ }
+#endif
+}
+
+int get_time(unsigned int *time, const char *str)
+{
+ double t;
+ char *p;
+
+ t = strtod(str, &p);
+ if (p == str)
+ return -1;
+
+ if (*p) {
+ if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 ||
+ strcasecmp(p, "secs") == 0)
+ t *= TIME_UNITS_PER_SEC;
+ else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 ||
+ strcasecmp(p, "msecs") == 0)
+ t *= TIME_UNITS_PER_SEC/1000;
+ else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 ||
+ strcasecmp(p, "usecs") == 0)
+ t *= TIME_UNITS_PER_SEC/1000000;
+ else
+ return -1;
+ }
+
+ *time = t;
+ return 0;
+}
+
+
+void print_time(char *buf, int len, __u32 time)
+{
+ double tmp = time;
+
+ if (tmp >= TIME_UNITS_PER_SEC)
+ snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC);
+ else if (tmp >= TIME_UNITS_PER_SEC/1000)
+ snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000));
+ else
+ snprintf(buf, len, "%uus", time);
+}
+
+char *sprint_time(__u32 time, char *buf)
+{
+ print_time(buf, SPRINT_BSIZE-1, time);
+ return buf;
+}
+
+/* 64 bit times are represented internally in nanoseconds */
+int get_time64(__s64 *time, const char *str)
+{
+ double nsec;
+ char *p;
+
+ nsec = strtod(str, &p);
+ if (p == str)
+ return -1;
+
+ if (*p) {
+ if (strcasecmp(p, "s") == 0 ||
+ strcasecmp(p, "sec") == 0 ||
+ strcasecmp(p, "secs") == 0)
+ nsec *= NSEC_PER_SEC;
+ else if (strcasecmp(p, "ms") == 0 ||
+ strcasecmp(p, "msec") == 0 ||
+ strcasecmp(p, "msecs") == 0)
+ nsec *= NSEC_PER_MSEC;
+ else if (strcasecmp(p, "us") == 0 ||
+ strcasecmp(p, "usec") == 0 ||
+ strcasecmp(p, "usecs") == 0)
+ nsec *= NSEC_PER_USEC;
+ else if (strcasecmp(p, "ns") == 0 ||
+ strcasecmp(p, "nsec") == 0 ||
+ strcasecmp(p, "nsecs") == 0)
+ nsec *= 1;
+ else
+ return -1;
+ }
+
+ *time = nsec;
+ return 0;
+}
+
+void print_time64(char *buf, int len, __s64 time)
+{
+ double nsec = time;
+
+ if (time >= NSEC_PER_SEC)
+ snprintf(buf, len, "%.3fs", nsec/NSEC_PER_SEC);
+ else if (time >= NSEC_PER_MSEC)
+ snprintf(buf, len, "%.3fms", nsec/NSEC_PER_MSEC);
+ else if (time >= NSEC_PER_USEC)
+ snprintf(buf, len, "%.3fus", nsec/NSEC_PER_USEC);
+ else
+ snprintf(buf, len, "%lldns", time);
+}
+
+char *sprint_time64(__s64 time, char *buf)
+{
+ print_time64(buf, SPRINT_BSIZE-1, time);
+ return buf;
+}