[PATCH iproute2 1/3] ip: add support of netconf messages

Subsystems: networking [general], the rest

STALE4992d

10 messages, 3 authors, 2012-12-12 · open the first message on its own page

[PATCH iproute2 1/3] ip: add support of netconf messages

From: Nicolas Dichtel <hidden>
Date: 2012-12-12 09:54:22

Example of the output:
$ ip monitor netconf&
[1] 24901
$ echo 0 > /proc/sys/net/ipv6/conf/all/forwarding
ipv6 dev lo forwarding off
ipv6 dev eth0 forwarding off
ipv6 all forwarding off
$ echo 1 > /proc/sys/net/ipv4/conf/eth0/forwarding
ipv4 dev eth0 forwarding on

$ ip -6 netconf
ipv6 all forwarding on mc_forwarding 0
$ ip netconf show dev eth0
ipv4 dev eth0 forwarding on rp_filter off mc_forwarding 1

Signed-off-by: Nicolas Dichtel <redacted>
---
 include/libnetlink.h      |   1 +
 include/linux/netconf.h   |  24 ++++++
 include/linux/rtnetlink.h |   9 +++
 ip/Makefile               |   2 +-
 ip/ip.c                   |   1 +
 ip/ip_common.h            |   3 +
 ip/ipmonitor.c            |  16 ++++
 ip/ipnetconf.c            | 183 ++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 238 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/netconf.h
 create mode 100644 ip/ipnetconf.c
diff --git a/include/libnetlink.h b/include/libnetlink.h
index 81649af..4a6b878 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -8,6 +8,7 @@
 #include <linux/if_link.h>
 #include <linux/if_addr.h>
 #include <linux/neighbour.h>
+#include <linux/netconf.h>
 
 struct rtnl_handle
 {
diff --git a/include/linux/netconf.h b/include/linux/netconf.h
new file mode 100644
index 0000000..64804a7
--- /dev/null
+++ b/include/linux/netconf.h
@@ -0,0 +1,24 @@
+#ifndef _UAPI_LINUX_NETCONF_H_
+#define _UAPI_LINUX_NETCONF_H_
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+struct netconfmsg {
+	__u8	ncm_family;
+};
+
+enum {
+	NETCONFA_UNSPEC,
+	NETCONFA_IFINDEX,
+	NETCONFA_FORWARDING,
+	NETCONFA_RP_FILTER,
+	NETCONFA_MC_FORWARDING,
+	__NETCONFA_MAX
+};
+#define NETCONFA_MAX	(__NETCONFA_MAX - 1)
+
+#define NETCONFA_IFINDEX_ALL		-1
+#define NETCONFA_IFINDEX_DEFAULT	-2
+
+#endif /* _UAPI_LINUX_NETCONF_H_ */
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 0e3e0c1..a30530e 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -120,6 +120,11 @@ enum {
 	RTM_SETDCB,
 #define RTM_SETDCB RTM_SETDCB
 
+	RTM_NEWNETCONF = 80,
+#define RTM_NEWNETCONF RTM_NEWNETCONF
+	RTM_GETNETCONF = 82,
+#define RTM_GETNETCONF RTM_GETNETCONF
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -585,6 +590,10 @@ enum rtnetlink_groups {
 #define RTNLGRP_PHONET_ROUTE	RTNLGRP_PHONET_ROUTE
 	RTNLGRP_DCB,
 #define RTNLGRP_DCB		RTNLGRP_DCB
+	RTNLGRP_IPV4_NETCONF,
+#define RTNLGRP_IPV4_NETCONF	RTNLGRP_IPV4_NETCONF
+	RTNLGRP_IPV6_NETCONF,
+#define RTNLGRP_IPV6_NETCONF	RTNLGRP_IPV6_NETCONF
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
diff --git a/ip/Makefile b/ip/Makefile
index 1676f0f..4bc33d7 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -4,7 +4,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
     iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
-    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o
+    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip.c b/ip/ip.c
index e0f7e60..632d271 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -85,6 +85,7 @@ static const struct cmd {
 	{ "mroute",	do_multiroute },
 	{ "mrule",	do_multirule },
 	{ "netns",	do_netns },
+	{ "netconf",	do_ipnetconf },
 	{ "help",	do_help },
 	{ 0 }
 };
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 2fd66b7..a394669 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -25,6 +25,8 @@ extern int print_prefix(const struct sockaddr_nl *who,
 			struct nlmsghdr *n, void *arg);
 extern int print_rule(const struct sockaddr_nl *who,
 		      struct nlmsghdr *n, void *arg);
+extern int print_netconf(const struct sockaddr_nl *who,
+			 struct nlmsghdr *n, void *arg);
 extern int do_ipaddr(int argc, char **argv);
 extern int do_ipaddrlabel(int argc, char **argv);
 extern int do_iproute(int argc, char **argv);
@@ -43,6 +45,7 @@ extern int do_netns(int argc, char **argv);
 extern int do_xfrm(int argc, char **argv);
 extern int do_ipl2tp(int argc, char **argv);
 extern int do_tcp_metrics(int argc, char **argv);
+extern int do_ipnetconf(int argc, char **argv);
 
 static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
 {
diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c
index 4b1d469..d87e58f 100644
--- a/ip/ipmonitor.c
+++ b/ip/ipmonitor.c
@@ -85,6 +85,12 @@ int accept_msg(const struct sockaddr_nl *who,
 		print_rule(who, n, arg);
 		return 0;
 	}
+	if (n->nlmsg_type == RTM_NEWNETCONF) {
+		if (prefix_banner)
+			fprintf(fp, "[NETCONF]");
+		print_netconf(who, n, arg);
+		return 0;
+	}
 	if (n->nlmsg_type == 15) {
 		char *tstr;
 		time_t secs = ((__u32*)NLMSG_DATA(n))[0];
@@ -118,6 +124,7 @@ int do_ipmonitor(int argc, char **argv)
 	int lroute=0;
 	int lprefix=0;
 	int lneigh=0;
+	int lnetconf=0;
 
 	rtnl_close(&rth);
 	ipaddr_reset_filter(1);
@@ -143,6 +150,9 @@ int do_ipmonitor(int argc, char **argv)
 		} else if (matches(*argv, "neigh") == 0) {
 			lneigh = 1;
 			groups = 0;
+		} else if (matches(*argv, "netconf") == 0) {
+			lnetconf = 1;
+			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			groups = ~RTMGRP_TC;
 			prefix_banner=1;
@@ -176,6 +186,12 @@ int do_ipmonitor(int argc, char **argv)
 	if (lneigh) {
 		groups |= nl_mgrp(RTNLGRP_NEIGH);
 	}
+	if (lnetconf) {
+		if (!preferred_family || preferred_family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_NETCONF);
+		if (!preferred_family || preferred_family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_NETCONF);
+	}
 	if (file) {
 		FILE *fp;
 		fp = fopen(file, "r");
diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c
new file mode 100644
index 0000000..66d667b
--- /dev/null
+++ b/ip/ipnetconf.c
@@ -0,0 +1,183 @@
+/*
+ * ipnetconf.c		"ip netconf".
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel, <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static struct
+{
+	int family;
+	int ifindex;
+} filter;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip netconf show [ dev STRING ]\n");
+	exit(-1);
+}
+
+#define NETCONF_RTA(r)	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct netconfmsg))))
+
+int print_netconf(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct netconfmsg *ncm = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[NETCONFA_MAX+1];
+
+	if (n->nlmsg_type == NLMSG_ERROR)
+		return -1;
+	if (n->nlmsg_type != RTM_NEWNETCONF) {
+		fprintf(stderr, "Not RTM_NEWNETCONF: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+
+		return -1;
+	}
+	len -= NLMSG_SPACE(sizeof(*ncm));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (filter.family && filter.family != ncm->ncm_family)
+		return 0;
+
+	parse_rtattr(tb, NETCONFA_MAX, NETCONF_RTA(ncm),
+		     NLMSG_PAYLOAD(n, sizeof(*ncm)));
+
+	switch (ncm->ncm_family) {
+	case AF_INET:
+		fprintf(fp, "ipv4 ");
+		break;
+	case AF_INET6:
+		fprintf(fp, "ipv6 ");
+		break;
+	default:
+		fprintf(fp, "unknown ");
+		break;
+	}
+
+	if (tb[NETCONFA_IFINDEX]) {
+		int *ifindex = (int *)RTA_DATA(tb[NETCONFA_IFINDEX]);
+
+		switch (*ifindex) {
+		case NETCONFA_IFINDEX_ALL:
+			fprintf(fp, "all ");
+			break;
+		case NETCONFA_IFINDEX_DEFAULT:
+			fprintf(fp, "default ");
+			break;
+		default:
+			fprintf(fp, "dev %s ", ll_index_to_name(*ifindex));
+			break;
+		}
+	}
+
+	if (tb[NETCONFA_FORWARDING])
+		fprintf(fp, "forwarding %s ",
+			*(int *)RTA_DATA(tb[NETCONFA_FORWARDING])?"on":"off");
+	if (tb[NETCONFA_RP_FILTER]) {
+		int rp_filter = *(int *)RTA_DATA(tb[NETCONFA_RP_FILTER]);
+
+		if (rp_filter == 0)
+			fprintf(fp, "rp_filter off ");
+		else if (rp_filter == 1)
+			fprintf(fp, "rp_filter strict ");
+		else if (rp_filter == 2)
+			fprintf(fp, "rp_filter loose ");
+		else
+			fprintf(fp, "rp_filter unknown mode ");
+	}
+	if (tb[NETCONFA_MC_FORWARDING])
+		fprintf(fp, "mc_forwarding %d ",
+			*(int *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]));
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+void ipnetconf_reset_filter(void)
+{
+	memset(&filter, 0, sizeof(filter));
+}
+
+int do_show(int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr		n;
+		struct netconfmsg	ncm;
+		char			buf[1024];
+	} req;
+
+	ipnetconf_reset_filter();
+	filter.family = preferred_family;
+	if (filter.family == AF_UNSPEC)
+		filter.family = AF_INET;
+	filter.ifindex = NETCONFA_IFINDEX_ALL;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			filter.ifindex = ll_name_to_index(*argv);
+			if (filter.ifindex <= 0) {
+				fprintf(stderr, "Device \"%s\" does not exist.\n",
+					*argv);
+				return -1;
+			}
+		}
+		argv++; argc--;
+	}
+
+	ll_init_map(&rth);
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+	req.n.nlmsg_type = RTM_GETNETCONF;
+	req.ncm.ncm_family = filter.family;
+	addattr_l(&req.n, sizeof(req), NETCONFA_IFINDEX, &filter.ifindex,
+		  sizeof(filter.ifindex));
+
+	rtnl_send(&rth, &req.n, req.n.nlmsg_len);
+	rtnl_listen(&rth, print_netconf, stdout);
+
+	return 0;
+}
+
+int do_ipnetconf(int argc, char **argv)
+{
+	if (argc > 0) {
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return do_show(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return do_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netconf help\".\n", *argv);
+	exit(-1);
+}
-- 
1.8.0.1

[PATCH iproute2 3/3] ip: add support of 'ip link type [ipip|sit]'

From: Nicolas Dichtel <hidden>
Date: 2012-12-12 09:54:21

This patch allows to manage ip tunnels via the interface ip link.
The syntax for parameters is the same that 'ip tunnel'.

It also allows to display tunnels parameters with 'ip -details link' or
'ip -details monitor link'.

Signed-off-by: Nicolas Dichtel <redacted>
---
 ip/Makefile     |   3 +-
 ip/iplink.c     |   2 +-
 ip/link_iptnl.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 343 insertions(+), 2 deletions(-)
 create mode 100644 ip/link_iptnl.c
diff --git a/ip/Makefile b/ip/Makefile
index abf54bf..2b606d4 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -4,7 +4,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
     iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
-    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o
+    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
+    link_iptnl.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/iplink.c b/ip/iplink.c
index 8aac9fc..d73c705 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -84,7 +84,7 @@ void iplink_usage(void)
 	if (iplink_have_newlink()) {
 		fprintf(stderr, "\n");
 		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |\n");
-		fprintf(stderr, "          bridge | ipoib | ip6tnl }\n");
+		fprintf(stderr, "          bridge | ipoib | ip6tnl | ipip | sit }\n");
 	}
 	exit(-1);
 }
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
new file mode 100644
index 0000000..238722d
--- /dev/null
+++ b/ip/link_iptnl.c
@@ -0,0 +1,340 @@
+/*
+ * link_iptnl.c	ipip and sit driver module
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+static void usage(int sit) __attribute__((noreturn));
+static void usage(int sit)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type { ipip | sit } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	if (sit)
+		fprintf(stderr, "          [ isatap ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	exit(-1);
+}
+
+static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
+	int len;
+	__u32 link = 0;
+	__u32 laddr = 0;
+	__u32 raddr = 0;
+	__u8 ttl = 0;
+	__u8 tos = 0;
+	__u8 pmtudisc = 1;
+	__u16 iflags = 0;
+	struct in6_addr ip6rdprefix;
+	__u16 ip6rdprefixlen = 0;
+	__u32 ip6rdrelayprefix = 0;
+	__u16 ip6rdrelayprefixlen = 0;
+
+	memset(&ip6rdprefix, 0, sizeof(ip6rdprefix));
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			laddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LOCAL]);
+
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			raddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_REMOTE]);
+
+		if (iptuninfo[IFLA_IPTUN_TTL])
+			ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
+
+		if (iptuninfo[IFLA_IPTUN_TOS])
+			tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]);
+
+		if (iptuninfo[IFLA_IPTUN_PMTUDISC])
+			pmtudisc =
+				rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]);
+
+		if (iptuninfo[IFLA_IPTUN_FLAGS])
+			iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]);
+
+		if (iptuninfo[IFLA_IPTUN_LINK])
+			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIX])
+			memcpy(&ip6rdprefix,
+			       RTA_DATA(iptuninfo[IFLA_IPTUN_6RD_PREFIX]),
+			       sizeof(laddr));
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN])
+			ip6rdprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX])
+			ip6rdrelayprefix =
+				rta_getattr_u32(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN])
+			ip6rdrelayprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+	}
+
+	while (argc > 0) {
+		if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				raddr = get_addr32(*argv);
+			else
+				raddr = 0;
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				laddr = get_addr32(*argv);
+			else
+				laddr = 0;
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hoplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (get_u8(&ttl, *argv, 0))
+					invarg("invalid TTL\n", *argv);
+			} else
+				ttl = 0;
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   strcmp(*argv, "tclass") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (rtnl_dsfield_a2n(&uval, *argv))
+					invarg("bad TOS value", *argv);
+				tos = uval;
+			} else
+				tos = 1;
+		} else if (strcmp(*argv, "nopmtudisc") == 0) {
+			pmtudisc = 0;
+		} else if (strcmp(*argv, "pmtudisc") == 0) {
+			pmtudisc = 1;
+		} else if (strcmp(lu->id, "sit") == 0 &&
+			   strcmp(*argv, "isatap") == 0) {
+			iflags |= SIT_ISATAP;
+		} else if (strcmp(*argv, "6rd-prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET6))
+				invarg("invalid 6rd_prefix\n", *argv);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET))
+				invarg("invalid 6rd-relay_prefix\n", *argv);
+			memcpy(&ip6rdrelayprefix, prefix.data, 4);
+			ip6rdrelayprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-reset") == 0) {
+			inet_prefix prefix;
+			get_prefix(&prefix, "2002::", AF_INET6);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = 16;
+			ip6rdrelayprefix = 0;
+			ip6rdrelayprefixlen = 0;
+		} else
+			usage(strcmp(lu->id, "sit") == 0);
+		argc--, argv++;
+	}
+
+	if (ttl && pmtudisc == 0) {
+		fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
+		exit(-1);
+	}
+
+	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
+	addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr);
+	addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr);
+	addattr8(n, 1024, IFLA_IPTUN_TTL, ttl);
+	addattr8(n, 1024, IFLA_IPTUN_TOS, tos);
+	addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc);
+	if (strcmp(lu->id, "sit") == 0) {
+		addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags);
+		if (ip6rdprefixlen) {
+			addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX,
+				  &ip6rdprefix, sizeof(ip6rdprefix));
+			addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN,
+				  ip6rdprefixlen);
+			addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX,
+				  ip6rdrelayprefix);
+			addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+				  ip6rdrelayprefixlen);
+		}
+	}
+
+	return 0;
+}
+
+static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[1024];
+	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]);
+
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+	else
+		fprintf(f, "ttl inherit ");
+
+	if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
+
+		fputs("tos ", f);
+		if (tos == 1)
+			fputs("inherit ", f);
+		else
+			fprintf(f, "0x%x ", tos);
+	}
+
+	if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC]))
+		fprintf(f, "pmtudisc ");
+	else
+		fprintf(f, "nopmtudisc ");
+
+	if (tb[IFLA_IPTUN_FLAGS]) {
+	       __u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]);
+
+	      if (iflags & SIT_ISATAP)
+		      fprintf(f, "isatap ");
+	}
+
+	if (tb[IFLA_IPTUN_6RD_PREFIXLEN] &&
+	    *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIXLEN])) {
+		__u16 prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]);
+		__u16 relayprefixlen =
+			rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+		__u32 relayprefix =
+			rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		printf("6rd-prefix %s/%u ",
+		       inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
+			         s1, sizeof(s1)),
+		       prefixlen);
+		if (relayprefix) {
+			printf("6rd-relay_prefix %s/%u ",
+			       format_host(AF_INET, 4, &relayprefix, s1,
+				           sizeof(s1)),
+			       relayprefixlen);
+		}
+	}
+}
+
+struct link_util ipip_link_util = {
+	.id = "ipip",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
+
+struct link_util sit_link_util = {
+	.id = "sit",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
-- 
1.8.0.1

[PATCH iproute2 2/3] ip: add support of 'ip link type ip6tnl'

From: Nicolas Dichtel <hidden>
Date: 2012-12-12 09:54:21

This patch allows to manage ip6 tunnels via the interface ip link.
The syntax for parameters is the same that 'ip -6 tunnel'.

It also allows to display tunnels parameters with 'ip -details link' or
'ip -details monitor link'.

Signed-off-by: Nicolas Dichtel <redacted>
---
 include/linux/if_tunnel.h |  20 +++
 ip/Makefile               |   2 +-
 ip/iplink.c               |   3 +-
 ip/link_ip6tnl.c          | 344 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 367 insertions(+), 2 deletions(-)
 create mode 100644 ip/link_ip6tnl.c
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 5db5942..aee73d0 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -37,6 +37,26 @@ struct ip_tunnel_parm {
 	struct iphdr		iph;
 };
 
+enum {
+	IFLA_IPTUN_UNSPEC,
+	IFLA_IPTUN_LINK,
+	IFLA_IPTUN_LOCAL,
+	IFLA_IPTUN_REMOTE,
+	IFLA_IPTUN_TTL,
+	IFLA_IPTUN_TOS,
+	IFLA_IPTUN_ENCAP_LIMIT,
+	IFLA_IPTUN_FLOWINFO,
+	IFLA_IPTUN_FLAGS,
+	IFLA_IPTUN_PROTO,
+	IFLA_IPTUN_PMTUDISC,
+	IFLA_IPTUN_6RD_PREFIX,
+	IFLA_IPTUN_6RD_RELAY_PREFIX,
+	IFLA_IPTUN_6RD_PREFIXLEN,
+	IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+	__IFLA_IPTUN_MAX,
+};
+#define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
+
 /* SIT-mode i_flags */
 #define	SIT_ISATAP	0x0001
 
diff --git a/ip/Makefile b/ip/Makefile
index 4bc33d7..abf54bf 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -4,7 +4,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
     iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
-    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o
+    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/iplink.c b/ip/iplink.c
index 7451aa0..8aac9fc 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -83,7 +83,8 @@ void iplink_usage(void)
 
 	if (iplink_have_newlink()) {
 		fprintf(stderr, "\n");
-		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can | bridge | ipoib }\n");
+		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |\n");
+		fprintf(stderr, "          bridge | ipoib | ip6tnl }\n");
 	}
 	exit(-1);
 }
diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c
new file mode 100644
index 0000000..2947364
--- /dev/null
+++ b/ip/link_ip6tnl.c
@@ -0,0 +1,344 @@
+/*
+ * link_ip6tnl.c	ip6tnl driver module
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+#define IP6_FLOWINFO_TCLASS	htonl(0x0FF00000)
+#define IP6_FLOWINFO_FLOWLABEL	htonl(0x000FFFFF)
+
+#define DEFAULT_TNL_HOP_LIMIT	(64)
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type ip6tnl [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ dev PHYS_DEV ] [ encaplimit ELIM ]\n");
+	fprintf(stderr ,"          [ hoplimit HLIM ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+	fprintf(stderr, "          [ dscp inherit ] [ fwmark inherit ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME      := STRING\n");
+	fprintf(stderr, "       ADDR      := IPV6_ADDRESS\n");
+	fprintf(stderr, "       ELIM      := { none | 0..255 }(default=%d)\n",
+		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(stderr, "       HLIM      := 0..255 (default=%d)\n",
+		DEFAULT_TNL_HOP_LIMIT);
+	fprintf(stderr, "       TCLASS    := { 0x0..0xff | inherit }\n");
+	fprintf(stderr, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+	exit(-1);
+}
+
+static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv,
+			       struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
+	int len;
+	struct in6_addr laddr;
+	struct in6_addr raddr;
+	__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
+	__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
+	__u32 flowinfo = 0;
+	__u32 flags = 0;
+	__u32 link = 0;
+	__u8 proto = 0;
+
+	memset(&laddr, 0, sizeof(laddr));
+	memset(&raddr, 0, sizeof(raddr));
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			memcpy(&laddr, RTA_DATA(iptuninfo[IFLA_IPTUN_LOCAL]),
+			       sizeof(laddr));
+
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			memcpy(&raddr, RTA_DATA(iptuninfo[IFLA_IPTUN_REMOTE]),
+			       sizeof(raddr));
+
+		if (iptuninfo[IFLA_IPTUN_TTL])
+			hop_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
+
+		if (iptuninfo[IFLA_IPTUN_ENCAP_LIMIT])
+			encap_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_ENCAP_LIMIT]);
+
+		if (iptuninfo[IFLA_IPTUN_FLOWINFO])
+			flowinfo = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLOWINFO]);
+
+		if (iptuninfo[IFLA_IPTUN_FLAGS])
+			flags = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLAGS]);
+
+		if (iptuninfo[IFLA_IPTUN_LINK])
+			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
+
+		if (iptuninfo[IFLA_IPTUN_PROTO])
+			proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]);
+	}
+
+	while (argc > 0) {
+		if (matches(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ipv6/ipv6") == 0 ||
+			    strcmp(*argv, "ip6ip6") == 0)
+				proto = IPPROTO_IPV6;
+			else if (strcmp(*argv, "ip/ipv6") == 0 ||
+				 strcmp(*argv, "ipv4/ipv6") == 0 ||
+				 strcmp(*argv, "ipip6") == 0 ||
+				 strcmp(*argv, "ip4ip6") == 0)
+				proto = IPPROTO_IPIP;
+			else if (strcmp(*argv, "any/ipv6") == 0 ||
+				 strcmp(*argv, "any") == 0)
+				proto = 0;
+			else
+				invarg("Cannot guess tunnel mode.", *argv);
+		} else if (strcmp(*argv, "remote") == 0) {
+			inet_prefix addr;
+			NEXT_ARG();
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"remote\" address family is AF_UNSPEC", *argv);
+			memcpy(&raddr, addr.data, addr.bytelen);
+		} else if (strcmp(*argv, "local") == 0) {
+			inet_prefix addr;
+			NEXT_ARG();
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"local\" address family is AF_UNSPEC", *argv);
+			memcpy(&laddr, addr.data, addr.bytelen);
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "hoplimit") == 0 ||
+			   strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hlim") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid HLIM", *argv);
+			hop_limit = uval;
+		} else if (matches(*argv, "encaplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "none") == 0) {
+				flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
+			} else {
+				__u8 uval;
+				if (get_u8(&uval, *argv, 0) < -1)
+					invarg("invalid ELIM", *argv);
+				encap_limit = uval;
+				flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
+			}
+		} else if (strcmp(*argv, "tclass") == 0 ||
+			   strcmp(*argv, "tc") == 0 ||
+			   strcmp(*argv, "tos") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			flowinfo &= ~IP6_FLOWINFO_TCLASS;
+			if (strcmp(*argv, "inherit") == 0)
+				flags |= IP6_TNL_F_USE_ORIG_TCLASS;
+			else {
+				if (get_u8(&uval, *argv, 16))
+					invarg("invalid TClass", *argv);
+				flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
+				flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
+			}
+		} else if (strcmp(*argv, "flowlabel") == 0 ||
+			   strcmp(*argv, "fl") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
+			if (strcmp(*argv, "inherit") == 0)
+				flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
+			else {
+				if (get_u32(&uval, *argv, 16))
+					invarg("invalid Flowlabel", *argv);
+				if (uval > 0xFFFFF)
+					invarg("invalid Flowlabel", *argv);
+				flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
+				flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
+			}
+		} else if (strcmp(*argv, "dscp") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0)
+				invarg("not inherit", *argv);
+			flags |= IP6_TNL_F_RCV_DSCP_COPY;
+		} else if (strcmp(*argv, "fwmark") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0)
+				invarg("not inherit", *argv);
+			flags |= IP6_TNL_F_USE_ORIG_FWMARK;
+		} else
+			usage();
+		argc--, argv++;
+	}
+
+	addattr8(n, 1024, IFLA_IPTUN_PROTO, proto);
+	addattr_l(n, 1024, IFLA_IPTUN_LOCAL, &laddr, sizeof(laddr));
+	addattr_l(n, 1024, IFLA_IPTUN_REMOTE, &raddr, sizeof(raddr));
+	addattr8(n, 1024, IFLA_IPTUN_TTL, hop_limit);
+	addattr8(n, 1024, IFLA_IPTUN_ENCAP_LIMIT, encap_limit);
+	addattr32(n, 1024, IFLA_IPTUN_FLOWINFO, flowinfo);
+	addattr32(n, 1024, IFLA_IPTUN_FLAGS, flags);
+	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
+
+	return 0;
+}
+
+static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[256];
+	char s2[64];
+	int flags = 0;
+	__u32 flowinfo = 0;
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPTUN_FLAGS])
+		flags = rta_getattr_u32(tb[IFLA_IPTUN_FLAGS]);
+
+	if (tb[IFLA_IPTUN_FLOWINFO])
+		flowinfo = rta_getattr_u32(tb[IFLA_IPTUN_FLOWINFO]);
+
+	if (tb[IFLA_IPTUN_PROTO]) {
+		switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) {
+		case IPPROTO_IPIP:
+			fprintf(f, "ipip6 ");
+			break;
+		case IPPROTO_IPV6:
+			fprintf(f, "ip6ip6 ");
+			break;
+		case 0:
+			fprintf(f, "any ");
+			break;
+		}
+	}
+
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		fprintf(f, "remote %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[IFLA_IPTUN_REMOTE]),
+				    RTA_DATA(tb[IFLA_IPTUN_REMOTE]),
+				    s1, sizeof(s1)));
+	}
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		fprintf(f, "local %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[IFLA_IPTUN_LOCAL]),
+				    RTA_DATA(tb[IFLA_IPTUN_LOCAL]),
+				    s1, sizeof(s1)));
+	}
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+		printf("encaplimit none ");
+	else if (tb[IFLA_IPTUN_ENCAP_LIMIT])
+		fprintf(f, "encaplimit %u ",
+			rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]));
+
+	if (tb[IFLA_IPTUN_TTL])
+		fprintf(f, "hoplimit %u ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+
+	if (flags & IP6_TNL_F_USE_ORIG_TCLASS)
+		printf("tclass inherit ");
+	else if (tb[IFLA_IPTUN_FLOWINFO]) {
+		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS);
+
+		printf("tclass 0x%02x ", (__u8)(val >> 20));
+	}
+
+	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+		printf("flowlabel inherit ");
+	else
+		printf("flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+
+	printf("(flowinfo 0x%08x) ", ntohl(flowinfo));
+
+	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
+		printf("dscp inherit ");
+
+	if (flags & IP6_TNL_F_MIP6_DEV)
+		fprintf(f, "mip6 ");
+
+	if (flags & IP6_TNL_F_USE_ORIG_FWMARK)
+		fprintf(f, "fwmark inherit ");
+}
+
+struct link_util ip6tnl_link_util = {
+	.id = "ip6tnl",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = ip6tunnel_parse_opt,
+	.print_opt = ip6tunnel_print_opt,
+};
-- 
1.8.0.1

[PATCH net-next] uapi: add missing netconf.h to export list

From: Stephen Hemminger <hidden>
Date: 2012-12-12 17:00:07

Add netconf.h for use by iproute2.

Signed-off-by: Stephen Hemminger <redacted>
--- a/include/uapi/linux/Kbuild	2012-10-25 09:11:15.499273810 -0700
+++ b/include/uapi/linux/Kbuild	2012-12-12 08:56:36.130263710 -0800
@@ -258,6 +258,7 @@ header-y += neighbour.h
 header-y += net.h
 header-y += net_dropmon.h
 header-y += net_tstamp.h
+header-y += netconf.h
 header-y += netdevice.h
 header-y += netfilter.h
 header-y += netfilter_arp.h

Re: [PATCH iproute2 1/3] ip: add support of netconf messages

From: Stephen Hemminger <hidden>
Date: 2012-12-12 17:01:14

Ok, but the headers for all of iproute2 are supposed to come from
sanitized kernel headers from "make headers_install"

You missed that piece in the original patch.

Re: [PATCH iproute2 1/3] ip: add support of netconf messages

From: Nicolas Dichtel <hidden>
Date: 2012-12-12 17:03:40

Le 12/12/2012 17:59, Stephen Hemminger a écrit :
Ok, but the headers for all of iproute2 are supposed to come from
sanitized kernel headers from "make headers_install"

You missed that piece in the original patch.
Right! I will update the patch.

Re: [PATCH net-next] uapi: add missing netconf.h to export list

From: Nicolas Dichtel <hidden>
Date: 2012-12-12 17:04:30

Le 12/12/2012 17:58, Stephen Hemminger a écrit :
Add netconf.h for use by iproute2.

Signed-off-by: Stephen Hemminger <redacted>
Acked-by: Nicolas Dichtel <redacted>
quoted hunk
--- a/include/uapi/linux/Kbuild	2012-10-25 09:11:15.499273810 -0700
+++ b/include/uapi/linux/Kbuild	2012-12-12 08:56:36.130263710 -0800
@@ -258,6 +258,7 @@ header-y += neighbour.h
  header-y += net.h
  header-y += net_dropmon.h
  header-y += net_tstamp.h
+header-y += netconf.h
  header-y += netdevice.h
  header-y += netfilter.h
  header-y += netfilter_arp.h

Re: [PATCH iproute2 3/3] ip: add support of 'ip link type [ipip|sit]'

From: Stephen Hemminger <hidden>
Date: 2012-12-12 17:12:35

On Wed, 12 Dec 2012 10:51:47 +0100
Nicolas Dichtel [off-list ref] wrote:
quoted hunk
This patch allows to manage ip tunnels via the interface ip link.
The syntax for parameters is the same that 'ip tunnel'.

It also allows to display tunnels parameters with 'ip -details link' or
'ip -details monitor link'.

Signed-off-by: Nicolas Dichtel <redacted>
---
 ip/Makefile     |   3 +-
 ip/iplink.c     |   2 +-
 ip/link_iptnl.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 343 insertions(+), 2 deletions(-)
 create mode 100644 ip/link_iptnl.c
diff --git a/ip/Makefile b/ip/Makefile
index abf54bf..2b606d4 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -4,7 +4,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
     iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
-    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o
+    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
+    link_iptnl.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/iplink.c b/ip/iplink.c
index 8aac9fc..d73c705 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -84,7 +84,7 @@ void iplink_usage(void)
 	if (iplink_have_newlink()) {
 		fprintf(stderr, "\n");
 		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |\n");
-		fprintf(stderr, "          bridge | ipoib | ip6tnl }\n");
+		fprintf(stderr, "          bridge | ipoib | ip6tnl | ipip | sit }\n");
 	}
 	exit(-1);
 }
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
new file mode 100644
index 0000000..238722d
--- /dev/null
+++ b/ip/link_iptnl.c
@@ -0,0 +1,340 @@
+/*
+ * link_iptnl.c	ipip and sit driver module
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+static void usage(int sit) __attribute__((noreturn));
+static void usage(int sit)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type { ipip | sit } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	if (sit)
+		fprintf(stderr, "          [ isatap ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	exit(-1);
+}
+
+static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
+	int len;
+	__u32 link = 0;
+	__u32 laddr = 0;
+	__u32 raddr = 0;
+	__u8 ttl = 0;
+	__u8 tos = 0;
+	__u8 pmtudisc = 1;
+	__u16 iflags = 0;
+	struct in6_addr ip6rdprefix;
+	__u16 ip6rdprefixlen = 0;
+	__u32 ip6rdrelayprefix = 0;
+	__u16 ip6rdrelayprefixlen = 0;
+
+	memset(&ip6rdprefix, 0, sizeof(ip6rdprefix));
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			laddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LOCAL]);
+
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			raddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_REMOTE]);
+
+		if (iptuninfo[IFLA_IPTUN_TTL])
+			ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
+
+		if (iptuninfo[IFLA_IPTUN_TOS])
+			tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]);
+
+		if (iptuninfo[IFLA_IPTUN_PMTUDISC])
+			pmtudisc =
+				rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]);
+
+		if (iptuninfo[IFLA_IPTUN_FLAGS])
+			iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]);
+
+		if (iptuninfo[IFLA_IPTUN_LINK])
+			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIX])
+			memcpy(&ip6rdprefix,
+			       RTA_DATA(iptuninfo[IFLA_IPTUN_6RD_PREFIX]),
+			       sizeof(laddr));
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN])
+			ip6rdprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX])
+			ip6rdrelayprefix =
+				rta_getattr_u32(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN])
+			ip6rdrelayprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+	}
+
+	while (argc > 0) {
+		if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				raddr = get_addr32(*argv);
+			else
+				raddr = 0;
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				laddr = get_addr32(*argv);
+			else
+				laddr = 0;
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hoplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (get_u8(&ttl, *argv, 0))
+					invarg("invalid TTL\n", *argv);
+			} else
+				ttl = 0;
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   strcmp(*argv, "tclass") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (rtnl_dsfield_a2n(&uval, *argv))
+					invarg("bad TOS value", *argv);
+				tos = uval;
+			} else
+				tos = 1;
+		} else if (strcmp(*argv, "nopmtudisc") == 0) {
+			pmtudisc = 0;
+		} else if (strcmp(*argv, "pmtudisc") == 0) {
+			pmtudisc = 1;
+		} else if (strcmp(lu->id, "sit") == 0 &&
+			   strcmp(*argv, "isatap") == 0) {
+			iflags |= SIT_ISATAP;
+		} else if (strcmp(*argv, "6rd-prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET6))
+				invarg("invalid 6rd_prefix\n", *argv);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET))
+				invarg("invalid 6rd-relay_prefix\n", *argv);
+			memcpy(&ip6rdrelayprefix, prefix.data, 4);
+			ip6rdrelayprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-reset") == 0) {
+			inet_prefix prefix;
+			get_prefix(&prefix, "2002::", AF_INET6);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = 16;
+			ip6rdrelayprefix = 0;
+			ip6rdrelayprefixlen = 0;
+		} else
+			usage(strcmp(lu->id, "sit") == 0);
+		argc--, argv++;
+	}
+
+	if (ttl && pmtudisc == 0) {
+		fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
+		exit(-1);
+	}
+
+	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
+	addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr);
+	addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr);
+	addattr8(n, 1024, IFLA_IPTUN_TTL, ttl);
+	addattr8(n, 1024, IFLA_IPTUN_TOS, tos);
+	addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc);
+	if (strcmp(lu->id, "sit") == 0) {
+		addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags);
+		if (ip6rdprefixlen) {
+			addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX,
+				  &ip6rdprefix, sizeof(ip6rdprefix));
+			addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN,
+				  ip6rdprefixlen);
+			addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX,
+				  ip6rdrelayprefix);
+			addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+				  ip6rdrelayprefixlen);
+		}
+	}
+
+	return 0;
+}
+
+static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[1024];
+	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]);
+
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+	else
+		fprintf(f, "ttl inherit ");
+
+	if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
+
+		fputs("tos ", f);
+		if (tos == 1)
+			fputs("inherit ", f);
+		else
+			fprintf(f, "0x%x ", tos);
+	}
+
+	if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC]))
+		fprintf(f, "pmtudisc ");
+	else
+		fprintf(f, "nopmtudisc ");
+
+	if (tb[IFLA_IPTUN_FLAGS]) {
+	       __u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]);
+
+	      if (iflags & SIT_ISATAP)
+		      fprintf(f, "isatap ");
+	}
+
+	if (tb[IFLA_IPTUN_6RD_PREFIXLEN] &&
+	    *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIXLEN])) {
+		__u16 prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]);
+		__u16 relayprefixlen =
+			rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+		__u32 relayprefix =
+			rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		printf("6rd-prefix %s/%u ",
+		       inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
+			         s1, sizeof(s1)),
+		       prefixlen);
+		if (relayprefix) {
+			printf("6rd-relay_prefix %s/%u ",
+			       format_host(AF_INET, 4, &relayprefix, s1,
+				           sizeof(s1)),
+			       relayprefixlen);
+		}
+	}
+}
+
+struct link_util ipip_link_util = {
+	.id = "ipip",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
+
+struct link_util sit_link_util = {
+	.id = "sit",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
All applied with minor corrections to header files.

Could you please add man pages for this new functionality?

Re: [PATCH iproute2 3/3] ip: add support of 'ip link type [ipip|sit]'

From: Nicolas Dichtel <hidden>
Date: 2012-12-12 17:20:34

Le 12/12/2012 18:11, Stephen Hemminger a écrit :
On Wed, 12 Dec 2012 10:51:47 +0100
Nicolas Dichtel [off-list ref] wrote:
quoted
This patch allows to manage ip tunnels via the interface ip link.
The syntax for parameters is the same that 'ip tunnel'.

It also allows to display tunnels parameters with 'ip -details link' or
'ip -details monitor link'.

Signed-off-by: Nicolas Dichtel <redacted>
---
  ip/Makefile     |   3 +-
  ip/iplink.c     |   2 +-
  ip/link_iptnl.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3 files changed, 343 insertions(+), 2 deletions(-)
  create mode 100644 ip/link_iptnl.c
diff --git a/ip/Makefile b/ip/Makefile
index abf54bf..2b606d4 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -4,7 +4,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
      ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
      iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
      iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
-    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o
+    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
+    link_iptnl.o

  RTMONOBJ=rtmon.o
diff --git a/ip/iplink.c b/ip/iplink.c
index 8aac9fc..d73c705 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -84,7 +84,7 @@ void iplink_usage(void)
  	if (iplink_have_newlink()) {
  		fprintf(stderr, "\n");
  		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |\n");
-		fprintf(stderr, "          bridge | ipoib | ip6tnl }\n");
+		fprintf(stderr, "          bridge | ipoib | ip6tnl | ipip | sit }\n");
  	}
  	exit(-1);
  }
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
new file mode 100644
index 0000000..238722d
--- /dev/null
+++ b/ip/link_iptnl.c
@@ -0,0 +1,340 @@
+/*
+ * link_iptnl.c	ipip and sit driver module
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+static void usage(int sit) __attribute__((noreturn));
+static void usage(int sit)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type { ipip | sit } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	if (sit)
+		fprintf(stderr, "          [ isatap ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	exit(-1);
+}
+
+static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
+	int len;
+	__u32 link = 0;
+	__u32 laddr = 0;
+	__u32 raddr = 0;
+	__u8 ttl = 0;
+	__u8 tos = 0;
+	__u8 pmtudisc = 1;
+	__u16 iflags = 0;
+	struct in6_addr ip6rdprefix;
+	__u16 ip6rdprefixlen = 0;
+	__u32 ip6rdrelayprefix = 0;
+	__u16 ip6rdrelayprefixlen = 0;
+
+	memset(&ip6rdprefix, 0, sizeof(ip6rdprefix));
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			laddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LOCAL]);
+
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			raddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_REMOTE]);
+
+		if (iptuninfo[IFLA_IPTUN_TTL])
+			ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
+
+		if (iptuninfo[IFLA_IPTUN_TOS])
+			tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]);
+
+		if (iptuninfo[IFLA_IPTUN_PMTUDISC])
+			pmtudisc =
+				rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]);
+
+		if (iptuninfo[IFLA_IPTUN_FLAGS])
+			iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]);
+
+		if (iptuninfo[IFLA_IPTUN_LINK])
+			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIX])
+			memcpy(&ip6rdprefix,
+			       RTA_DATA(iptuninfo[IFLA_IPTUN_6RD_PREFIX]),
+			       sizeof(laddr));
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN])
+			ip6rdprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX])
+			ip6rdrelayprefix =
+				rta_getattr_u32(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN])
+			ip6rdrelayprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+	}
+
+	while (argc > 0) {
+		if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				raddr = get_addr32(*argv);
+			else
+				raddr = 0;
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				laddr = get_addr32(*argv);
+			else
+				laddr = 0;
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hoplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (get_u8(&ttl, *argv, 0))
+					invarg("invalid TTL\n", *argv);
+			} else
+				ttl = 0;
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   strcmp(*argv, "tclass") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (rtnl_dsfield_a2n(&uval, *argv))
+					invarg("bad TOS value", *argv);
+				tos = uval;
+			} else
+				tos = 1;
+		} else if (strcmp(*argv, "nopmtudisc") == 0) {
+			pmtudisc = 0;
+		} else if (strcmp(*argv, "pmtudisc") == 0) {
+			pmtudisc = 1;
+		} else if (strcmp(lu->id, "sit") == 0 &&
+			   strcmp(*argv, "isatap") == 0) {
+			iflags |= SIT_ISATAP;
+		} else if (strcmp(*argv, "6rd-prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET6))
+				invarg("invalid 6rd_prefix\n", *argv);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET))
+				invarg("invalid 6rd-relay_prefix\n", *argv);
+			memcpy(&ip6rdrelayprefix, prefix.data, 4);
+			ip6rdrelayprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-reset") == 0) {
+			inet_prefix prefix;
+			get_prefix(&prefix, "2002::", AF_INET6);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = 16;
+			ip6rdrelayprefix = 0;
+			ip6rdrelayprefixlen = 0;
+		} else
+			usage(strcmp(lu->id, "sit") == 0);
+		argc--, argv++;
+	}
+
+	if (ttl && pmtudisc == 0) {
+		fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
+		exit(-1);
+	}
+
+	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
+	addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr);
+	addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr);
+	addattr8(n, 1024, IFLA_IPTUN_TTL, ttl);
+	addattr8(n, 1024, IFLA_IPTUN_TOS, tos);
+	addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc);
+	if (strcmp(lu->id, "sit") == 0) {
+		addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags);
+		if (ip6rdprefixlen) {
+			addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX,
+				  &ip6rdprefix, sizeof(ip6rdprefix));
+			addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN,
+				  ip6rdprefixlen);
+			addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX,
+				  ip6rdrelayprefix);
+			addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+				  ip6rdrelayprefixlen);
+		}
+	}
+
+	return 0;
+}
+
+static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[1024];
+	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]);
+
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+	else
+		fprintf(f, "ttl inherit ");
+
+	if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
+
+		fputs("tos ", f);
+		if (tos == 1)
+			fputs("inherit ", f);
+		else
+			fprintf(f, "0x%x ", tos);
+	}
+
+	if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC]))
+		fprintf(f, "pmtudisc ");
+	else
+		fprintf(f, "nopmtudisc ");
+
+	if (tb[IFLA_IPTUN_FLAGS]) {
+	       __u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]);
+
+	      if (iflags & SIT_ISATAP)
+		      fprintf(f, "isatap ");
+	}
+
+	if (tb[IFLA_IPTUN_6RD_PREFIXLEN] &&
+	    *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIXLEN])) {
+		__u16 prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]);
+		__u16 relayprefixlen =
+			rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+		__u32 relayprefix =
+			rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		printf("6rd-prefix %s/%u ",
+		       inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
+			         s1, sizeof(s1)),
+		       prefixlen);
+		if (relayprefix) {
+			printf("6rd-relay_prefix %s/%u ",
+			       format_host(AF_INET, 4, &relayprefix, s1,
+				           sizeof(s1)),
+			       relayprefixlen);
+		}
+	}
+}
+
+struct link_util ipip_link_util = {
+	.id = "ipip",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
+
+struct link_util sit_link_util = {
+	.id = "sit",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
All applied with minor corrections to header files.

Could you please add man pages for this new functionality?
Ok.

Re: [PATCH net-next] uapi: add missing netconf.h to export list

From: David Miller <davem@davemloft.net>
Date: 2012-12-12 17:40:42

From: Nicolas Dichtel <redacted>
Date: Wed, 12 Dec 2012 18:04:26 +0100
Le 12/12/2012 17:58, Stephen Hemminger a écrit :
quoted
Add netconf.h for use by iproute2.

Signed-off-by: Stephen Hemminger <redacted>
Acked-by: Nicolas Dichtel <redacted>
Applied.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help