diff mbox series

[iproute2-next,5/6] iptunnel/ip6tunnel: Use netlink to walk through tunnels list

Message ID 1517577055-23788-6-git-send-email-serhe.popovych@gmail.com
State Changes Requested, archived
Delegated to: David Ahern
Headers show
Series ip: Use netlink to walk through network device list | expand

Commit Message

Serhey Popovych Feb. 2, 2018, 1:10 p.m. UTC
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 <serhe.popovych@gmail.com>
---
 ip/ip6tunnel.c |  113 +++++++++++++++---------------------------------------
 ip/iptunnel.c  |  106 +++++++++++++-------------------------------------
 ip/tunnel.c    |  117 +++++++++++++++++++++++++++++++++++++++++---------------
 ip/tunnel.h    |   20 ++++++++--
 4 files changed, 159 insertions(+), 197 deletions(-)
diff mbox series

Patch

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 <linux/if.h>
 #include <linux/ip.h>
 #include <linux/if_tunnel.h>
+#include <linux/if_arp.h>
 
 #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 <stdbool.h>
 #include <linux/types.h>
 
 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