]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - lib/utils.c
bpf: check map symbol type properly with newer llvm compiler
[mirror_iproute2.git] / lib / utils.c
index e20b60e4bcbf870cdae576d1afe8f20b11e4c909..345630d04929dbe82a82fb19721b9ea36695993a 100644 (file)
 #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)
 {
@@ -100,7 +107,7 @@ int parse_percent(double *val, const char *str)
        *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;
@@ -377,6 +384,27 @@ int get_u8(__u8 *val, const char *arg, int base)
        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;
@@ -534,18 +562,49 @@ int get_addr64(__u64 *ap, const char *cp)
        return 1;
 }
 
+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 ||
-           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;
        }
 
@@ -627,25 +686,7 @@ int get_addr_1(inet_prefix *addr, const char *name, int family)
        if (ret)
                return ret;
 
-       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;
-       }
-
+       set_address_type(addr);
        return 0;
 }
 
@@ -691,7 +732,7 @@ int get_prefix_1(inet_prefix *dst, char *arg, int family)
 
        bitlen = af_bit_len(dst->family);
 
-       flags = PREFIXLEN_SPECIFIED;
+       flags = 0;
        if (slash) {
                unsigned int plen;
 
@@ -702,12 +743,11 @@ int get_prefix_1(inet_prefix *dst, char *arg, int family)
                if (plen > bitlen)
                        return -1;
 
+               flags |= PREFIXLEN_SPECIFIED;
                bitlen = plen;
        } else {
                if (dst->bitlen == -2)
                        bitlen = 0;
-               else
-                       flags = 0;
        }
 
        dst->flags |= flags;
@@ -734,6 +774,46 @@ int get_addr(inet_prefix *dst, const char *arg, 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) {
@@ -799,6 +879,12 @@ void duparg2(const char *key, const char *arg)
        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 */
@@ -827,6 +913,25 @@ int get_ifname(char *buf, const char *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);
@@ -864,6 +969,19 @@ int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits)
        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)
@@ -926,6 +1044,25 @@ const char *rt_addr_n2a_r(int af, int len,
        }
        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 "???";
        }
@@ -1184,6 +1321,54 @@ int print_timestamp(FILE *fp)
        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 */
@@ -1272,7 +1457,7 @@ int makeargs(char *line, char *argv[], int maxargs)
                                break;
                }
 
-               /* seperate words */
+               /* separate words */
                *cp++ = 0;
        }
        argv[argc] = NULL;
@@ -1374,6 +1559,77 @@ int get_real_family(int rtm_type, int rtm_family)
        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)
 {
@@ -1398,3 +1654,132 @@ size_t strlcat(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;
+}