]> 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 61af123e9922273527b2cf797d374984f0d1d51a..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"
@@ -39,6 +43,7 @@
 int resolve_hosts;
 int timestamp_short;
 int pretty;
+const char *_SL_ = "\n";
 
 int read_prop(const char *dev, char *prop, long *value)
 {
@@ -102,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;
@@ -379,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;
@@ -562,14 +588,23 @@ 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;
        }
 
@@ -697,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;
 
@@ -708,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;
@@ -845,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 */
@@ -1004,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 "???";
        }
@@ -1262,7 +1321,7 @@ int print_timestamp(FILE *fp)
        return 0;
 }
 
-unsigned int print_name_and_link(const char *fmt, enum color_attr color,
+unsigned int print_name_and_link(const char *fmt,
                                 const char *name, struct rtattr *tb[])
 {
        const char *link = NULL;
@@ -1305,7 +1364,7 @@ unsigned int print_name_and_link(const char *fmt, enum color_attr color,
                }
        }
 
-       print_color_string(PRINT_ANY, color, "ifname", fmt, name);
+       print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", fmt, name);
 
        return m_flag;
 }
@@ -1398,7 +1457,7 @@ int makeargs(char *line, char *argv[], int maxargs)
                                break;
                }
 
-               /* seperate words */
+               /* separate words */
                *cp++ = 0;
        }
        argv[argc] = NULL;
@@ -1512,6 +1571,24 @@ static void copy_rtnl_link_stats64(struct rtnl_link_stats64 *stats64,
                *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[])
 {
@@ -1528,6 +1605,14 @@ int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
                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;
        }
@@ -1569,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;
+}