From patchwork Fri Feb 2 13:10:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhey Popovych X-Patchwork-Id: 868574 X-Patchwork-Delegate: dsahern@gmail.com Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="RajyBmrH"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zXy7R1Gpwz9sBZ for ; Sat, 3 Feb 2018 00:12:02 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752139AbeBBNL6 (ORCPT ); Fri, 2 Feb 2018 08:11:58 -0500 Received: from mail-lf0-f66.google.com ([209.85.215.66]:37847 "EHLO mail-lf0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752009AbeBBNLP (ORCPT ); Fri, 2 Feb 2018 08:11:15 -0500 Received: by mail-lf0-f66.google.com with SMTP id 63so31385267lfv.4 for ; Fri, 02 Feb 2018 05:11:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=669InVf6QeLopJepFQkVS2gmFdy5308LvExXZdHZ5kI=; b=RajyBmrHijTmBwzd57Y95taNIYmhOjX73yr864svQDb1LAj1Qq9VQQ6/56QLYzbqYI EMImenPEyXpeJhHCb2xAuPBDfdqUZ48zMRCf/eBl+KFBV3RJFV4/Q68bFS2Q/Jjt6fji b6Ln0VEKaQw9Bz2m5C/1TFYjZiL4jplzITX+H6tBrmnefe21hvkq8+13gLxa0RR3378N sVJRKgSBVCVVtcAiKc/889vEPdtUiHI+0MVcQZhsMpX0+9Ndxj0HmHrttNunYAUt+QKm bMDsalfYCG4n26YxMitV6JULNnPCHN7R3eW16Fa15SAAbw09S44ysWW+6VJZCTFTPwkc JhVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=669InVf6QeLopJepFQkVS2gmFdy5308LvExXZdHZ5kI=; b=QAa8iZwXWpO79SeZrxX+5gKQsjVhKy+eM8S9ijuOiBFPuZbpktRROmG7tFojh5O6eQ WQywI4wYRnTru+o/Gw1Z2+Lr/eYvNJ9JlHCR6DptD24zCtiCHngmTL3oHTsXEowb1oms W4NvMd5+IeCUj4p7QG5lRu0VOqSKN9RIpg5AIv0O0mhl3TXcCiCzX2JLuMO6+uPclJkK TXJvVoBW+LP4c+9wKmmADU+H2YOpu/Ua7U3JP4Zwbo6tnF8AbM4a3Rnb3DZfMnQWFUNi VIGy3pW7b3Zns0C27jwKGcqcKBU9BpcPA/+Qmx6953BcY5QiwxXOqeUveVA6j1N173x2 Ke/Q== X-Gm-Message-State: AKwxytcyW72W2WEYQdwZTKC3phce3yBGdnmMnm3Fk9IwiYB5U9LAOZ4U +eOuvlT4l2LDBcSqdG/9Ln0qvw== X-Google-Smtp-Source: AH8x225/Ofr0WpTUU6VDm9aPHFDC6P8bn5RXdbM7B94w14B7wEp4MofNuqjN9ap2RXZzvWCDeNQkuA== X-Received: by 10.46.31.2 with SMTP id f2mr5773049ljf.48.1517577072896; Fri, 02 Feb 2018 05:11:12 -0800 (PST) Received: from tuxracer.localdomain ([2a01:6d80::195:20:96:53]) by smtp.gmail.com with ESMTPSA id o188sm379121lff.5.2018.02.02.05.11.11 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 02 Feb 2018 05:11:11 -0800 (PST) From: Serhey Popovych To: netdev@vger.kernel.org Subject: [PATCH iproute2-next 5/6] iptunnel/ip6tunnel: Use netlink to walk through tunnels list Date: Fri, 2 Feb 2018 15:10:54 +0200 Message-Id: <1517577055-23788-6-git-send-email-serhe.popovych@gmail.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1517577055-23788-1-git-send-email-serhe.popovych@gmail.com> References: <1517577055-23788-1-git-send-email-serhe.popovych@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Both tunnels use legacy /proc/net/dev interface to get tunnel device and it's statistics. This may cause problems for cases when procfs either not mounted or not unshare(2)d for given network namespace. Use netlink to walk through list of tunnel devices which is network namespace aware and provides additional information such as statistics in the dump message. Since both address family specific variants of do_tunnels_list() nearly the same, except for tunnel parameters structure initialization, matching and printing we can introduce common one in tunnel.c. To implement address family specific parts introduce new data structure @struct tnl_print_nlmsg_info what contains all necessary information as well as pointers to ->init(), ->match() and ->print() callbacks. Annotate data structures by const where appropriate. Signed-off-by: Serhey Popovych --- ip/ip6tunnel.c | 113 +++++++++++++++--------------------------------------- ip/iptunnel.c | 106 +++++++++++++------------------------------------- ip/tunnel.c | 117 +++++++++++++++++++++++++++++++++++++++++--------------- ip/tunnel.h | 20 ++++++++-- 4 files changed, 159 insertions(+), 197 deletions(-) diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c index 0e53c23..c7fa082 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,27 @@ 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; - } - switch (type) { - case ARPHRD_TUNNEL6: - case ARPHRD_IP6GRE: - break; - default: - continue; - } - ip6_tnl_parm_init(&p1, 0); - if (type == ARPHRD_IP6GRE) - p1.proto = IPPROTO_GRE; - p1.link = index; - strcpy(p1.name, name); - if (tnl_get_ioctl(name, &p1)) - continue; - if (!ip6_tnl_parm_match(p, &p1)) - continue; - print_tunnel(&p1); - if (show_stats) { - struct rtnl_link_stats64 s; - - if (!tnl_get_stats(ptr, &s)) - tnl_print_stats(&s); - } - fputc('\n', stdout); - } - 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) - return do_tunnels_list(&p); + 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; diff --git a/ip/iptunnel.c b/ip/iptunnel.c index dba5942..1f04f95 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,13 +374,19 @@ static void print_tunnel(struct ip_tunnel_parm *p) printf("%s Checksum output packets.", _SL_); } -/* - * @p1: user specified parameter - * @p2: database entry - */ -static int ip_tunnel_parm_match(const struct ip_tunnel_parm *p1, - const struct ip_tunnel_parm *p2) + +static void ip_tunnel_parm_initialize(const struct tnl_print_nlmsg_info *info) +{ + struct ip_tunnel_parm *p2 = info->p2; + + memset(p2, 0, sizeof(*p2)); +} + +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; + 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) && @@ -387,87 +394,26 @@ static int ip_tunnel_parm_match(const struct ip_tunnel_parm *p1, (!p1->i_key || p1->i_key == p2->i_key)); } -static int do_tunnels_list(struct ip_tunnel_parm *p) -{ - char buf[512]; - int err = -1; - FILE *fp = fopen("/proc/net/dev", "r"); - - if (fp == NULL) { - perror("fopen"); - return -1; - } - - /* skip header lines */ - 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 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; - } - switch (type) { - case ARPHRD_TUNNEL: - case ARPHRD_IPGRE: - case ARPHRD_SIT: - break; - default: - continue; - } - memset(p1, 0, sizeof(p1)); - if (tnl_get_ioctl(name, &p1)) - continue; - if (!ip_tunnel_parm_match(p, &p1)) - continue; - print_tunnel(&p1); - if (show_stats) { - struct rtnl_link_stats64 s; - - if (!tnl_get_stats(ptr, &s)) - tnl_print_stats(&s); - } - fputc('\n', stdout); - } - err = 0; - end: - fclose(fp); - return err; -} - 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; diff --git a/ip/tunnel.c b/ip/tunnel.c index 06533cf..7030995 100644 --- a/ip/tunnel.c +++ b/ip/tunnel.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "utils.h" #include "tunnel.h" @@ -307,37 +308,7 @@ void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family) } } -int tnl_get_stats(const char *buf, struct rtnl_link_stats64 *s) -{ - /* rx */ - __u64 *rx_bytes = &s->rx_bytes; - __u64 *rx_packets = &s->rx_packets; - __u64 *rx_errs = &s->rx_errors; - __u64 *rx_drops = &s->rx_dropped; - __u64 *rx_fifo = &s->rx_fifo_errors; - __u64 *rx_frame = &s->rx_frame_errors; - __u64 *rx_multi = &s->multicast; - /* tx */ - __u64 *tx_bytes = &s->tx_bytes; - __u64 *tx_packets = &s->tx_packets; - __u64 *tx_errs = &s->tx_errors; - __u64 *tx_drops = &s->tx_dropped; - __u64 *tx_fifo = &s->tx_fifo_errors; - __u64 *tx_carrier = &s->tx_carrier_errors; - __u64 *tx_colls = &s->collisions; - - if (sscanf(buf, - "%llu%llu%llu%llu%llu%llu%llu%*d%llu%llu%llu%llu%llu%llu%llu", - 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 -1; - - return 0; -} - -void tnl_print_stats(const struct rtnl_link_stats64 *s) +static void tnl_print_stats(const struct rtnl_link_stats64 *s) { printf("%s", _SL_); printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); @@ -349,3 +320,87 @@ void tnl_print_stats(const struct rtnl_link_stats64 *s) 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 5fe488b..e530d07 100644 --- a/ip/tunnel.h +++ b/ip/tunnel.h @@ -21,10 +21,25 @@ #ifndef __TUNNEL_H__ #define __TUNNEL_H__ 1 +#include #include struct rtattr; -struct rtnl_link_stats64; +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); @@ -40,8 +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 struct rtnl_link_stats64 *s); - -int tnl_get_stats(const char *buf, struct rtnl_link_stats64 *s); #endif