diff --git a/include/utils.h b/include/utils.h index f81928ab..8b8ee2e5 100644 --- a/include/utils.h +++ b/include/utils.h @@ -284,6 +284,9 @@ int make_path(const char *path, mode_t mode); char *find_cgroup2_mount(void); int get_command_name(const char *pid, char *comm, size_t len); +int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64, + struct rtattr *tb[]); + #ifdef NEED_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); size_t strlcat(char *dst, const char *src, size_t size); diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c index 783e28a6..c7fa0822 100644 --- a/ip/ip6tunnel.c +++ b/ip/ip6tunnel.c @@ -67,8 +67,9 @@ static void usage(void) exit(-1); } -static void print_tunnel(struct ip6_tnl_parm2 *p) +static void print_tunnel(const void *t) { + const struct ip6_tnl_parm2 *p = t; char s1[1024]; char s2[1024]; @@ -313,13 +314,24 @@ static void ip6_tnl_parm_init(struct ip6_tnl_parm2 *p, int apply_default) } } -/* - * @p1: user specified parameter - * @p2: database entry - */ -static int ip6_tnl_parm_match(const struct ip6_tnl_parm2 *p1, - const struct ip6_tnl_parm2 *p2) +static void ip6_tnl_parm_initialize(const struct tnl_print_nlmsg_info *info) { + const struct ifinfomsg *ifi = info->ifi; + const struct ip6_tnl_parm2 *p1 = info->p1; + struct ip6_tnl_parm2 *p2 = info->p2; + + ip6_tnl_parm_init(p2, 0); + if (ifi->ifi_type == ARPHRD_IP6GRE) + p2->proto = IPPROTO_GRE; + p2->link = ifi->ifi_index; + strcpy(p2->name, p1->name); +} + +static bool ip6_tnl_parm_match(const struct tnl_print_nlmsg_info *info) +{ + const struct ip6_tnl_parm2 *p1 = info->p1; + const struct ip6_tnl_parm2 *p2 = info->p2; + return ((!p1->link || p1->link == p2->link) && (!p1->name[0] || strcmp(p1->name, p2->name) == 0) && (IN6_IS_ADDR_UNSPECIFIED(&p1->laddr) || @@ -336,90 +348,33 @@ static int ip6_tnl_parm_match(const struct ip6_tnl_parm2 *p1, (!p1->flags || (p1->flags & p2->flags))); } -static int do_tunnels_list(struct ip6_tnl_parm2 *p) -{ - char buf[512]; - int err = -1; - FILE *fp = fopen("/proc/net/dev", "r"); - - if (fp == NULL) { - perror("fopen"); - return -1; - } - - /* skip two lines at the begenning of the file */ - if (!fgets(buf, sizeof(buf), fp) || - !fgets(buf, sizeof(buf), fp)) { - fprintf(stderr, "/proc/net/dev read error\n"); - goto end; - } - - while (fgets(buf, sizeof(buf), fp) != NULL) { - char name[IFNAMSIZ]; - int index, type; - struct ip6_tnl_parm2 p1 = {}; - char *ptr; - - buf[sizeof(buf) - 1] = '\0'; - if ((ptr = strchr(buf, ':')) == NULL || - (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { - fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n"); - goto end; - } - if (p->name[0] && strcmp(p->name, name)) - continue; - index = ll_name_to_index(name); - if (index == 0) - continue; - type = ll_index_to_type(index); - if (type == -1) { - fprintf(stderr, "Failed to get type of \"%s\"\n", name); - continue; - } - if (type != ARPHRD_TUNNEL6 && type != ARPHRD_IP6GRE) - continue; - ip6_tnl_parm_init(&p1, 0); - if (type == ARPHRD_IP6GRE) - p1.proto = IPPROTO_GRE; - strcpy(p1.name, name); - p1.link = ll_name_to_index(p1.name); - if (p1.link == 0) - continue; - if (tnl_get_ioctl(p1.name, &p1)) - continue; - if (!ip6_tnl_parm_match(p, &p1)) - continue; - print_tunnel(&p1); - if (show_stats) - tnl_print_stats(ptr); - printf("\n"); - } - err = 0; - end: - fclose(fp); - return err; -} - static int do_show(int argc, char **argv) { - struct ip6_tnl_parm2 p; + struct ip6_tnl_parm2 p, p1; - ll_init_map(&rth); ip6_tnl_parm_init(&p, 0); p.proto = 0; /* default to any */ if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) return -1; - if (!p.name[0] || show_stats) - do_tunnels_list(&p); - else { - if (tnl_get_ioctl(p.name, &p)) - return -1; - print_tunnel(&p); - printf("\n"); + if (!p.name[0] || show_stats) { + struct tnl_print_nlmsg_info info = { + .p1 = &p, + .p2 = &p1, + .init = ip6_tnl_parm_initialize, + .match = ip6_tnl_parm_match, + .print = print_tunnel, + }; + + return do_tunnels_list(&info); } + if (tnl_get_ioctl(p.name, &p)) + return -1; + + print_tunnel(&p); + fputc('\n', stdout); return 0; } diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 4707c2b7..6990b815 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -593,11 +593,18 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) } } -static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s, - const struct rtattr *carrier_changes) +static void __print_link_stats(FILE *fp, struct rtattr *tb[]) { + const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES]; + struct rtnl_link_stats64 _s, *s = &_s; + int ret; + + ret = get_rtnl_link_stats_rta(s, tb); + if (ret < 0) + return; + if (is_json_context()) { - open_json_object("stats64"); + open_json_object((ret == sizeof(*s)) ? "stats64" : "stats"); /* RX stats */ open_json_object("rx"); @@ -609,8 +616,7 @@ static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s, print_uint(PRINT_JSON, "multicast", NULL, s->multicast); if (s->rx_compressed) print_uint(PRINT_JSON, - "compressed", - NULL, s->rx_compressed); + "compressed", NULL, s->rx_compressed); /* RX error stats */ if (show_stats > 1) { @@ -647,8 +653,7 @@ static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s, print_uint(PRINT_JSON, "collisions", NULL, s->collisions); if (s->tx_compressed) print_uint(PRINT_JSON, - "compressed", - NULL, s->tx_compressed); + "compressed", NULL, s->tx_compressed); /* TX error stats */ if (show_stats > 1) { @@ -668,154 +673,6 @@ static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s, print_uint(PRINT_JSON, "carrier_changes", NULL, rta_getattr_u32(carrier_changes)); } - close_json_object(); - close_json_object(); - - } else { - /* RX stats */ - fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", - s->rx_compressed ? "compressed" : "", _SL_); - - fprintf(fp, " "); - print_num(fp, 10, s->rx_bytes); - print_num(fp, 8, s->rx_packets); - print_num(fp, 7, s->rx_errors); - print_num(fp, 7, s->rx_dropped); - print_num(fp, 7, s->rx_over_errors); - print_num(fp, 7, s->multicast); - if (s->rx_compressed) - print_num(fp, 7, s->rx_compressed); - - /* RX error stats */ - if (show_stats > 1) { - fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors: length crc frame fifo missed%s%s", - s->rx_nohandler ? " nohandler" : "", _SL_); - - fprintf(fp, " "); - print_num(fp, 8, s->rx_length_errors); - print_num(fp, 7, s->rx_crc_errors); - print_num(fp, 7, s->rx_frame_errors); - print_num(fp, 7, s->rx_fifo_errors); - print_num(fp, 7, s->rx_missed_errors); - if (s->rx_nohandler) - print_num(fp, 7, s->rx_nohandler); - - } - fprintf(fp, "%s", _SL_); - - /* TX stats */ - fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", - s->tx_compressed ? "compressed" : "", _SL_); - - fprintf(fp, " "); - print_num(fp, 10, s->tx_bytes); - print_num(fp, 8, s->tx_packets); - print_num(fp, 7, s->tx_errors); - print_num(fp, 7, s->tx_dropped); - print_num(fp, 7, s->tx_carrier_errors); - print_num(fp, 7, s->collisions); - if (s->tx_compressed) - print_num(fp, 7, s->tx_compressed); - - /* TX error stats */ - if (show_stats > 1) { - fprintf(fp, "%s", _SL_); - fprintf(fp, " TX errors: aborted fifo window heartbeat"); - if (carrier_changes) - fprintf(fp, " transns"); - fprintf(fp, "%s", _SL_); - - fprintf(fp, " "); - print_num(fp, 8, s->tx_aborted_errors); - print_num(fp, 7, s->tx_fifo_errors); - print_num(fp, 7, s->tx_window_errors); - print_num(fp, 7, s->tx_heartbeat_errors); - if (carrier_changes) - print_num(fp, 7, - rta_getattr_u32(carrier_changes)); - } - } -} - -static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s, - const struct rtattr *carrier_changes) -{ - if (is_json_context()) { - open_json_object("stats"); - - /* RX stats */ - open_json_object("rx"); - print_uint(PRINT_JSON, "bytes", NULL, s->rx_bytes); - print_uint(PRINT_JSON, "packets", NULL, s->rx_packets); - print_uint(PRINT_JSON, "errors", NULL, s->rx_errors); - print_uint(PRINT_JSON, "dropped", NULL, s->rx_dropped); - print_uint(PRINT_JSON, "over_errors", NULL, s->rx_over_errors); - print_uint(PRINT_JSON, "multicast", NULL, s->multicast); - if (s->rx_compressed) - print_int(PRINT_JSON, - "compressed", - NULL, s->rx_compressed); - - /* RX error stats */ - if (show_stats > 1) { - print_uint(PRINT_JSON, - "length_errors", - NULL, s->rx_length_errors); - print_uint(PRINT_JSON, - "crc_errors", - NULL, s->rx_crc_errors); - print_uint(PRINT_JSON, - "frame_errors", - NULL, s->rx_frame_errors); - print_uint(PRINT_JSON, - "fifo_errors", - NULL, s->rx_fifo_errors); - print_uint(PRINT_JSON, - "missed_errors", - NULL, s->rx_missed_errors); - if (s->rx_nohandler) - print_int(PRINT_JSON, - "nohandler", - NULL, s->rx_nohandler); - } - close_json_object(); - - /* TX stats */ - open_json_object("tx"); - print_uint(PRINT_JSON, "bytes", NULL, s->tx_bytes); - print_uint(PRINT_JSON, "packets", NULL, s->tx_packets); - print_uint(PRINT_JSON, "errors", NULL, s->tx_errors); - print_uint(PRINT_JSON, "dropped", NULL, s->tx_dropped); - print_uint(PRINT_JSON, - "carrier_errors", - NULL, s->tx_carrier_errors); - print_uint(PRINT_JSON, "collisions", NULL, s->collisions); - if (s->tx_compressed) - print_int(PRINT_JSON, - "compressed", - NULL, s->tx_compressed); - - /* TX error stats */ - if (show_stats > 1) { - print_uint(PRINT_JSON, - "aborted_errors", - NULL, s->tx_aborted_errors); - print_uint(PRINT_JSON, - "fifo_errors", - NULL, s->tx_fifo_errors); - print_uint(PRINT_JSON, - "window_errors", - NULL, s->tx_window_errors); - print_uint(PRINT_JSON, - "heartbeat_errors", - NULL, s->tx_heartbeat_errors); - if (carrier_changes) - print_uint(PRINT_JSON, - "carrier_changes", - NULL, - rta_getattr_u32(carrier_changes)); - } close_json_object(); close_json_object(); @@ -824,7 +681,6 @@ static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s, fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", s->rx_compressed ? "compressed" : "", _SL_); - fprintf(fp, " "); print_num(fp, 10, s->rx_bytes); print_num(fp, 8, s->rx_packets); @@ -885,27 +741,6 @@ static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s, } } -static void __print_link_stats(FILE *fp, struct rtattr **tb) -{ - const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES]; - - if (tb[IFLA_STATS64]) { - struct rtnl_link_stats64 stats = { 0 }; - - memcpy(&stats, RTA_DATA(tb[IFLA_STATS64]), - MIN(RTA_PAYLOAD(tb[IFLA_STATS64]), sizeof(stats))); - - print_link_stats64(fp, &stats, carrier_changes); - } else if (tb[IFLA_STATS]) { - struct rtnl_link_stats stats = { 0 }; - - memcpy(&stats, RTA_DATA(tb[IFLA_STATS]), - MIN(RTA_PAYLOAD(tb[IFLA_STATS]), sizeof(stats))); - - print_link_stats32(fp, &stats, carrier_changes); - } -} - static void print_link_stats(FILE *fp, struct nlmsghdr *n) { struct ifinfomsg *ifi = NLMSG_DATA(n); diff --git a/ip/iptunnel.c b/ip/iptunnel.c index 0aa3b332..1f04f95a 100644 --- a/ip/iptunnel.c +++ b/ip/iptunnel.c @@ -286,8 +286,9 @@ static int do_del(int argc, char **argv) return tnl_del_ioctl(tnl_defname(&p) ? : p.name, p.name, &p); } -static void print_tunnel(struct ip_tunnel_parm *p) +static void print_tunnel(const void *t) { + const struct ip_tunnel_parm *p = t; struct ip_tunnel_6rd ip6rd = {}; char s1[1024]; char s2[1024]; @@ -373,86 +374,52 @@ static void print_tunnel(struct ip_tunnel_parm *p) printf("%s Checksum output packets.", _SL_); } -static int do_tunnels_list(struct ip_tunnel_parm *p) + +static void ip_tunnel_parm_initialize(const struct tnl_print_nlmsg_info *info) { - char buf[512]; - int err = -1; - FILE *fp = fopen("/proc/net/dev", "r"); + struct ip_tunnel_parm *p2 = info->p2; - if (fp == NULL) { - perror("fopen"); - return -1; - } + memset(p2, 0, sizeof(*p2)); +} - /* skip header lines */ - if (!fgets(buf, sizeof(buf), fp) || - !fgets(buf, sizeof(buf), fp)) { - fprintf(stderr, "/proc/net/dev read error\n"); - goto end; - } +static bool ip_tunnel_parm_match(const struct tnl_print_nlmsg_info *info) +{ + const struct ip_tunnel_parm *p1 = info->p1; + const struct ip_tunnel_parm *p2 = info->p2; - while (fgets(buf, sizeof(buf), fp) != NULL) { - char name[IFNAMSIZ]; - int index, type; - struct ip_tunnel_parm p1 = {}; - char *ptr; - - buf[sizeof(buf) - 1] = 0; - ptr = strchr(buf, ':'); - if (ptr == NULL || - (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { - fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n"); - goto end; - } - if (p->name[0] && strcmp(p->name, name)) - continue; - index = ll_name_to_index(name); - if (index == 0) - continue; - type = ll_index_to_type(index); - if (type == -1) { - fprintf(stderr, "Failed to get type of \"%s\"\n", name); - continue; - } - if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) - continue; - if (tnl_get_ioctl(name, &p1)) - continue; - if ((p->link && p1.link != p->link) || - (p->name[0] && strcmp(p1.name, p->name)) || - (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || - (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || - (p->i_key && p1.i_key != p->i_key)) - continue; - print_tunnel(&p1); - if (show_stats) - tnl_print_stats(ptr); - printf("\n"); - } - err = 0; - end: - fclose(fp); - return err; + return ((!p1->link || p1->link == p2->link) && + (!p1->name[0] || strcmp(p1->name, p2->name) == 0) && + (!p1->iph.daddr || p1->iph.daddr == p2->iph.daddr) && + (!p1->iph.saddr || p1->iph.saddr == p2->iph.saddr) && + (!p1->i_key || p1->i_key == p2->i_key)); } static int do_show(int argc, char **argv) { - struct ip_tunnel_parm p; + struct ip_tunnel_parm p, p1; const char *basedev; - ll_init_map(&rth); if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) return -1; basedev = tnl_defname(&p); - if (!basedev) - return do_tunnels_list(&p); + if (!basedev) { + struct tnl_print_nlmsg_info info = { + .p1 = &p, + .p2 = &p1, + .init = ip_tunnel_parm_initialize, + .match = ip_tunnel_parm_match, + .print = print_tunnel, + }; + + return do_tunnels_list(&info); + } if (tnl_get_ioctl(p.name[0] ? p.name : basedev, &p)) return -1; print_tunnel(&p); - printf("\n"); + fputc('\n', stdout); return 0; } diff --git a/ip/iptuntap.c b/ip/iptuntap.c index 09f2be24..4628db28 100644 --- a/ip/iptuntap.c +++ b/ip/iptuntap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,8 @@ #include "utils.h" #include "ip_common.h" +static const char drv_name[] = "tun"; + #define TUNDEV "/dev/net/tun" static void usage(void) __attribute__((noreturn)); @@ -348,43 +351,101 @@ next: globfree(&globbuf); } +static int tuntap_filter_req(struct nlmsghdr *nlh, int reqlen) +{ + struct rtattr *linkinfo; + int err; + + linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO); + + err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, + drv_name, sizeof(drv_name) - 1); + if (err) + return err; + + addattr_nest_end(nlh, linkinfo); + + return 0; +} + +static int print_tuntap(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + const char *name, *kind; + long flags, owner = -1, group = -1; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) + return -1; + + switch (ifi->ifi_type) { + case ARPHRD_NONE: + case ARPHRD_ETHER: + break; + default: + return 0; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + + if (!tb[IFLA_IFNAME]) + return 0; + + if (!tb[IFLA_LINKINFO]) + return 0; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_KIND]) + return 0; + + kind = rta_getattr_str(linkinfo[IFLA_INFO_KIND]); + if (strcmp(kind, drv_name)) + return 0; + + name = rta_getattr_str(tb[IFLA_IFNAME]); + + if (read_prop(name, "tun_flags", &flags)) + return 0; + if (read_prop(name, "owner", &owner)) + return 0; + if (read_prop(name, "group", &group)) + return 0; + + printf("%s:", name); + print_flags(flags); + if (owner != -1) + printf(" user %ld", owner); + if (group != -1) + printf(" group %ld", group); + fputc('\n', stdout); + if (show_details) { + printf("\tAttached to processes:"); + show_processes(name); + fputc('\n', stdout); + } + + return 0; +} static int do_show(int argc, char **argv) { - DIR *dir; - struct dirent *d; - long flags, owner = -1, group = -1; - - dir = opendir("/sys/class/net"); - if (!dir) { - perror("opendir"); + if (rtnl_wilddump_req_filter_fn(&rth, AF_UNSPEC, RTM_GETLINK, + tuntap_filter_req) < 0) { + perror("Cannot send dump request\n"); return -1; } - while ((d = readdir(dir))) { - if (d->d_name[0] == '.' && - (d->d_name[1] == 0 || d->d_name[1] == '.')) - continue; - if (read_prop(d->d_name, "tun_flags", &flags)) - continue; - - read_prop(d->d_name, "owner", &owner); - read_prop(d->d_name, "group", &group); - - printf("%s:", d->d_name); - print_flags(flags); - if (owner != -1) - printf(" user %ld", owner); - if (group != -1) - printf(" group %ld", group); - printf("\n"); - if (show_details) { - printf("\tAttached to processes:"); - show_processes(d->d_name); - printf("\n"); - } + if (rtnl_dump_filter(&rth, print_tuntap, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; } - closedir(dir); + return 0; } diff --git a/ip/tunnel.c b/ip/tunnel.c index 948d5f7c..7030995c 100644 --- a/ip/tunnel.c +++ b/ip/tunnel.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "utils.h" #include "tunnel.h" @@ -307,30 +308,99 @@ void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family) } } -/* tnl_print_stats - print tunnel statistics - * - * @buf - tunnel interface's line in /proc/net/dev, - * starting past the interface name and following colon - */ -void tnl_print_stats(const char *buf) +static void tnl_print_stats(const struct rtnl_link_stats64 *s) { - unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, - rx_fifo, rx_frame, - tx_bytes, tx_packets, tx_errs, tx_drops, - tx_fifo, tx_colls, tx_carrier, rx_multi; - - if (sscanf(buf, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu", - &rx_bytes, &rx_packets, &rx_errs, &rx_drops, - &rx_fifo, &rx_frame, &rx_multi, - &tx_bytes, &tx_packets, &tx_errs, &tx_drops, - &tx_fifo, &tx_colls, &tx_carrier) != 14) - return; - printf("%s", _SL_); printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); - printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", - rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); + printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-8lld%s", + s->rx_packets, s->rx_bytes, s->rx_errors, s->rx_frame_errors, + s->rx_fifo_errors, s->multicast, _SL_); printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); - printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", - tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); + printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-6lld", + s->tx_packets, s->tx_bytes, s->tx_errors, s->collisions, + s->tx_carrier_errors, s->tx_dropped); +} + +static int print_nlmsg_tunnel(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + struct tnl_print_nlmsg_info *info = arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + const char *name, *n1; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) + return -1; + + if (preferred_family == AF_INET) { + switch (ifi->ifi_type) { + case ARPHRD_TUNNEL: + case ARPHRD_IPGRE: + case ARPHRD_SIT: + break; + default: + return 0; + } + } else { + switch (ifi->ifi_type) { + case ARPHRD_TUNNEL6: + case ARPHRD_IP6GRE: + break; + default: + return 0; + } + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + + if (!tb[IFLA_IFNAME]) + return 0; + + name = rta_getattr_str(tb[IFLA_IFNAME]); + + /* Assume p1->name[IFNAMSIZ] is first field of structure */ + n1 = info->p1; + if (n1[0] && strcmp(n1, name)) + return 0; + + info->ifi = ifi; + info->init(info); + + /* TODO: parse netlink attributes */ + if (tnl_get_ioctl(name, info->p2)) + return 0; + + if (!info->match(info)) + return 0; + + info->print(info->p2); + if (show_stats) { + struct rtnl_link_stats64 s; + + if (get_rtnl_link_stats_rta(&s, tb) <= 0) + return -1; + + tnl_print_stats(&s); + } + fputc('\n', stdout); + + return 0; +} + +int do_tunnels_list(struct tnl_print_nlmsg_info *info) +{ + if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { + perror("Cannot send dump request\n"); + return -1; + } + + if (rtnl_dump_filter(&rth, print_nlmsg_tunnel, info) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + + return 0; } diff --git a/ip/tunnel.h b/ip/tunnel.h index 5bd27c32..e530d07c 100644 --- a/ip/tunnel.h +++ b/ip/tunnel.h @@ -21,9 +21,25 @@ #ifndef __TUNNEL_H__ #define __TUNNEL_H__ 1 +#include #include struct rtattr; +struct ifinfomsg; + +extern struct rtnl_handle rth; + +struct tnl_print_nlmsg_info { + const struct ifinfomsg *ifi; + const void *p1; + void *p2; + + void (*init)(const struct tnl_print_nlmsg_info *info); + bool (*match)(const struct tnl_print_nlmsg_info *info); + void (*print)(const void *t); +}; + +int do_tunnels_list(struct tnl_print_nlmsg_info *info); const char *tnl_strproto(__u8 proto); @@ -39,6 +55,5 @@ void tnl_print_encap(struct rtattr *tb[], int encap_sport, int encap_dport); void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family); -void tnl_print_stats(const char *buf); #endif diff --git a/lib/utils.c b/lib/utils.c index 8e15625e..d86c2eec 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1431,6 +1431,51 @@ 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++; +} + +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 { + 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) {