#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;
{
memset(addr, 0, sizeof(*addr));
- if (strcmp(name, "default") == 0 ||
- strcmp(name, "all") == 0 ||
- strcmp(name, "any") == 0) {
+ if (strcmp(name, "default") == 0) {
if ((family == AF_DECnet) || (family == AF_MPLS))
return -1;
- addr->family = (family != AF_UNSPEC) ? family : AF_INET;
+ 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 = 0;
+ addr->bitlen = -2;
return 0;
}
bitlen = af_bit_len(dst->family);
- flags = PREFIXLEN_SPECIFIED;
+ flags = 0;
if (slash) {
unsigned int plen;
if (plen > bitlen)
return -1;
+ flags |= PREFIXLEN_SPECIFIED;
bitlen = plen;
} else {
if (dst->bitlen == -2)
bitlen = 0;
- else
- flags = 0;
}
dst->flags |= flags;
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);
}
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;
*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[])
{
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;
}
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;
+}