1 /*
2  * link_vti6.c	VTI driver module
3  *
4  *		This program is free software; you can redistribute it and/or
5  *		modify it under the terms of the GNU General Public License
6  *		as published by the Free Software Foundation; either version
7  *		2 of the License, or (at your option) any later version.
8  *
9  * Authors:	Herbert Xu <herbert@gondor.apana.org.au>
10  *		Saurabh Mohan <saurabh.mohan@vyatta.com> Modified link_gre.c for VTI
11  *		Steffen Klassert <steffen.klassert@secunet.com> Modified link_vti.c for IPv6
12  */
13 
14 #include <string.h>
15 #include <net/if.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <arpa/inet.h>
19 
20 #include <linux/ip.h>
21 #include <linux/if_tunnel.h>
22 #include "rt_names.h"
23 #include "utils.h"
24 #include "ip_common.h"
25 #include "tunnel.h"
26 
27 
28 static void usage(void) __attribute__((noreturn));
usage(void)29 static void usage(void)
30 {
31 	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
32 	fprintf(stderr, "          type { vti6 } [ remote ADDR ] [ local ADDR ]\n");
33 	fprintf(stderr, "          [ [i|o]key KEY ]\n");
34 	fprintf(stderr, "          [ dev PHYS_DEV ]\n");
35 	fprintf(stderr, "          [ fwmark MARK ]\n");
36 	fprintf(stderr, "\n");
37 	fprintf(stderr, "Where: NAME := STRING\n");
38 	fprintf(stderr, "       ADDR := { IPV6_ADDRESS }\n");
39 	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
40 	fprintf(stderr, "       MARK := { 0x0..0xffffffff }\n");
41 	exit(-1);
42 }
43 
vti6_parse_opt(struct link_util * lu,int argc,char ** argv,struct nlmsghdr * n)44 static int vti6_parse_opt(struct link_util *lu, int argc, char **argv,
45 			  struct nlmsghdr *n)
46 {
47 	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
48 	struct {
49 		struct nlmsghdr n;
50 		struct ifinfomsg i;
51 		char buf[1024];
52 	} req = {
53 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
54 		.n.nlmsg_flags = NLM_F_REQUEST,
55 		.n.nlmsg_type = RTM_GETLINK,
56 		.i.ifi_family = preferred_family,
57 		.i.ifi_index = ifi->ifi_index,
58 	};
59 	struct rtattr *tb[IFLA_MAX + 1];
60 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
61 	struct rtattr *vtiinfo[IFLA_VTI_MAX + 1];
62 	struct in6_addr saddr = IN6ADDR_ANY_INIT;
63 	struct in6_addr daddr = IN6ADDR_ANY_INIT;
64 	unsigned int ikey = 0;
65 	unsigned int okey = 0;
66 	unsigned int link = 0;
67 	__u32 fwmark = 0;
68 	int len;
69 
70 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
71 		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
72 get_failed:
73 			fprintf(stderr,
74 				"Failed to get existing tunnel info.\n");
75 			return -1;
76 		}
77 
78 		len = req.n.nlmsg_len;
79 		len -= NLMSG_LENGTH(sizeof(*ifi));
80 		if (len < 0)
81 			goto get_failed;
82 
83 		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
84 
85 		if (!tb[IFLA_LINKINFO])
86 			goto get_failed;
87 
88 		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
89 
90 		if (!linkinfo[IFLA_INFO_DATA])
91 			goto get_failed;
92 
93 		parse_rtattr_nested(vtiinfo, IFLA_VTI_MAX,
94 				    linkinfo[IFLA_INFO_DATA]);
95 
96 		if (vtiinfo[IFLA_VTI_IKEY])
97 			ikey = rta_getattr_u32(vtiinfo[IFLA_VTI_IKEY]);
98 
99 		if (vtiinfo[IFLA_VTI_OKEY])
100 			okey = rta_getattr_u32(vtiinfo[IFLA_VTI_OKEY]);
101 
102 		if (vtiinfo[IFLA_VTI_LOCAL])
103 			memcpy(&saddr, RTA_DATA(vtiinfo[IFLA_VTI_LOCAL]), sizeof(saddr));
104 
105 		if (vtiinfo[IFLA_VTI_REMOTE])
106 			memcpy(&daddr, RTA_DATA(vtiinfo[IFLA_VTI_REMOTE]), sizeof(daddr));
107 
108 		if (vtiinfo[IFLA_VTI_LINK])
109 			link = rta_getattr_u8(vtiinfo[IFLA_VTI_LINK]);
110 
111 		if (vtiinfo[IFLA_VTI_FWMARK])
112 			fwmark = rta_getattr_u32(vtiinfo[IFLA_VTI_FWMARK]);
113 	}
114 
115 	while (argc > 0) {
116 		if (!matches(*argv, "key")) {
117 			unsigned int uval;
118 
119 			NEXT_ARG();
120 			if (strchr(*argv, '.'))
121 				uval = get_addr32(*argv);
122 			else {
123 				if (get_unsigned(&uval, *argv, 0) < 0) {
124 					fprintf(stderr,
125 						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
126 					exit(-1);
127 				}
128 				uval = htonl(uval);
129 			}
130 
131 			ikey = okey = uval;
132 		} else if (!matches(*argv, "ikey")) {
133 			unsigned int uval;
134 
135 			NEXT_ARG();
136 			if (strchr(*argv, '.'))
137 				uval = get_addr32(*argv);
138 			else {
139 				if (get_unsigned(&uval, *argv, 0) < 0) {
140 					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
141 					exit(-1);
142 				}
143 				uval = htonl(uval);
144 			}
145 			ikey = uval;
146 		} else if (!matches(*argv, "okey")) {
147 			unsigned int uval;
148 
149 			NEXT_ARG();
150 			if (strchr(*argv, '.'))
151 				uval = get_addr32(*argv);
152 			else {
153 				if (get_unsigned(&uval, *argv, 0) < 0) {
154 					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
155 					exit(-1);
156 				}
157 				uval = htonl(uval);
158 			}
159 			okey = uval;
160 		} else if (!matches(*argv, "remote")) {
161 			NEXT_ARG();
162 			if (!strcmp(*argv, "any")) {
163 				fprintf(stderr, "invalid value for \"remote\": \"%s\"\n", *argv);
164 				exit(-1);
165 			} else {
166 				inet_prefix addr;
167 
168 				get_prefix(&addr, *argv, AF_INET6);
169 				memcpy(&daddr, addr.data, addr.bytelen);
170 			}
171 		} else if (!matches(*argv, "local")) {
172 			NEXT_ARG();
173 			if (!strcmp(*argv, "any")) {
174 				fprintf(stderr, "invalid value for \"local\": \"%s\"\n", *argv);
175 				exit(-1);
176 			} else {
177 				inet_prefix addr;
178 
179 				get_prefix(&addr, *argv, AF_INET6);
180 				memcpy(&saddr, addr.data, addr.bytelen);
181 			}
182 		} else if (!matches(*argv, "dev")) {
183 			NEXT_ARG();
184 			link = if_nametoindex(*argv);
185 			if (link == 0)
186 				exit(-1);
187 		} else if (strcmp(*argv, "fwmark") == 0) {
188 			NEXT_ARG();
189 			if (get_u32(&fwmark, *argv, 0))
190 				invarg("invalid fwmark\n", *argv);
191 		} else
192 			usage();
193 		argc--; argv++;
194 	}
195 
196 	addattr32(n, 1024, IFLA_VTI_IKEY, ikey);
197 	addattr32(n, 1024, IFLA_VTI_OKEY, okey);
198 
199 	if (memcmp(&saddr, &in6addr_any, sizeof(in6addr_any)))
200 	    addattr_l(n, 1024, IFLA_VTI_LOCAL, &saddr, sizeof(saddr));
201 	if (memcmp(&daddr, &in6addr_any, sizeof(in6addr_any)))
202 	    addattr_l(n, 1024, IFLA_VTI_REMOTE, &daddr, sizeof(daddr));
203 	addattr32(n, 1024, IFLA_VTI_FWMARK, fwmark);
204 	if (link)
205 		addattr32(n, 1024, IFLA_VTI_LINK, link);
206 
207 	return 0;
208 }
209 
vti6_print_opt(struct link_util * lu,FILE * f,struct rtattr * tb[])210 static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
211 {
212 	const char *local = "any";
213 	const char *remote = "any";
214 	struct in6_addr saddr;
215 	struct in6_addr daddr;
216 	unsigned int link;
217 	char s2[64];
218 
219 	if (!tb)
220 		return;
221 
222 	if (tb[IFLA_VTI_REMOTE]) {
223 		memcpy(&daddr, RTA_DATA(tb[IFLA_VTI_REMOTE]), sizeof(daddr));
224 
225 		remote = format_host(AF_INET6, 16, &daddr);
226 	}
227 
228 	print_string(PRINT_ANY, "remote", "remote %s ", remote);
229 
230 	if (tb[IFLA_VTI_LOCAL]) {
231 		memcpy(&saddr, RTA_DATA(tb[IFLA_VTI_LOCAL]), sizeof(saddr));
232 
233 		local = format_host(AF_INET6, 16, &saddr);
234 	}
235 
236 	print_string(PRINT_ANY, "local", "local %s ", local);
237 
238 	if (tb[IFLA_VTI_LINK] && (link = rta_getattr_u32(tb[IFLA_VTI_LINK]))) {
239 		const char *n = if_indextoname(link, s2);
240 
241 		if (n)
242 			print_string(PRINT_ANY, "link", "dev %s ", n);
243 		else
244 			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
245 	}
246 
247 	if (tb[IFLA_VTI_IKEY]) {
248 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_IKEY]), s2, sizeof(s2));
249 		print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
250 	}
251 
252 	if (tb[IFLA_VTI_OKEY]) {
253 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_OKEY]), s2, sizeof(s2));
254 		print_string(PRINT_ANY, "okey", "okey %s ", s2);
255 	}
256 
257 	if (tb[IFLA_VTI_FWMARK]) {
258 		__u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]);
259 
260 		if (fwmark) {
261 			snprintf(s2, sizeof(s2), "0x%x", fwmark);
262 
263 			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
264 		}
265 	}
266 }
267 
268 struct link_util vti6_link_util = {
269 	.id = "vti6",
270 	.maxattr = IFLA_VTI_MAX,
271 	.parse_opt = vti6_parse_opt,
272 	.print_opt = vti6_print_opt,
273 };
274