• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * iplink_geneve.c	GENEVE device support
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:     John W. Linville <linville@tuxdriver.com>
10  */
11 
12 #include <stdio.h>
13 
14 #include "rt_names.h"
15 #include "utils.h"
16 #include "ip_common.h"
17 
18 #define GENEVE_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0)
19 
print_explain(FILE * f)20 static void print_explain(FILE *f)
21 {
22 	fprintf(f,
23 		"Usage: ... geneve id VNI\n"
24 		"                  remote ADDR\n"
25 		"                  [ ttl TTL ]\n"
26 		"                  [ tos TOS ]\n"
27 		"                  [ flowlabel LABEL ]\n"
28 		"                  [ dstport PORT ]\n"
29 		"                  [ [no]external ]\n"
30 		"                  [ [no]udpcsum ]\n"
31 		"                  [ [no]udp6zerocsumtx ]\n"
32 		"                  [ [no]udp6zerocsumrx ]\n"
33 		"\n"
34 		"Where: VNI   := 0-16777215\n"
35 		"       ADDR  := IP_ADDRESS\n"
36 		"       TOS   := { NUMBER | inherit }\n"
37 		"       TTL   := { 1..255 | inherit }\n"
38 		"       LABEL := 0-1048575\n"
39 	);
40 }
41 
explain(void)42 static void explain(void)
43 {
44 	print_explain(stderr);
45 }
46 
check_duparg(__u64 * attrs,int type,const char * key,const char * argv)47 static void check_duparg(__u64 *attrs, int type, const char *key,
48 			 const char *argv)
49 {
50 	if (!GENEVE_ATTRSET(*attrs, type)) {
51 		*attrs |= (1L << type);
52 		return;
53 	}
54 	duparg2(key, argv);
55 }
56 
geneve_parse_opt(struct link_util * lu,int argc,char ** argv,struct nlmsghdr * n)57 static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
58 			  struct nlmsghdr *n)
59 {
60 	__u32 vni = 0;
61 	__u32 daddr = 0;
62 	struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
63 	__u32 label = 0;
64 	__u8 ttl = 0;
65 	__u8 tos = 0;
66 	__u16 dstport = 0;
67 	bool metadata = 0;
68 	__u8 udpcsum = 0;
69 	__u8 udp6zerocsumtx = 0;
70 	__u8 udp6zerocsumrx = 0;
71 	__u64 attrs = 0;
72 	bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
73 		       !(n->nlmsg_flags & NLM_F_CREATE));
74 
75 	while (argc > 0) {
76 		if (!matches(*argv, "id") ||
77 		    !matches(*argv, "vni")) {
78 			NEXT_ARG();
79 			check_duparg(&attrs, IFLA_GENEVE_ID, "id", *argv);
80 			if (get_u32(&vni, *argv, 0) ||
81 			    vni >= 1u << 24)
82 				invarg("invalid id", *argv);
83 		} else if (!matches(*argv, "remote")) {
84 			NEXT_ARG();
85 			check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote",
86 				     *argv);
87 			if (!inet_get_addr(*argv, &daddr, &daddr6)) {
88 				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
89 				return -1;
90 			}
91 			if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
92 				invarg("invalid remote address", *argv);
93 		} else if (!matches(*argv, "ttl") ||
94 			   !matches(*argv, "hoplimit")) {
95 			unsigned int uval;
96 
97 			NEXT_ARG();
98 			check_duparg(&attrs, IFLA_GENEVE_TTL, "ttl", *argv);
99 			if (strcmp(*argv, "inherit") != 0) {
100 				if (get_unsigned(&uval, *argv, 0))
101 					invarg("invalid TTL", *argv);
102 				if (uval > 255)
103 					invarg("TTL must be <= 255", *argv);
104 				ttl = uval;
105 			}
106 		} else if (!matches(*argv, "tos") ||
107 			   !matches(*argv, "dsfield")) {
108 			__u32 uval;
109 
110 			NEXT_ARG();
111 			check_duparg(&attrs, IFLA_GENEVE_TOS, "tos", *argv);
112 			if (strcmp(*argv, "inherit") != 0) {
113 				if (rtnl_dsfield_a2n(&uval, *argv))
114 					invarg("bad TOS value", *argv);
115 				tos = uval;
116 			} else
117 				tos = 1;
118 		} else if (!matches(*argv, "label") ||
119 			   !matches(*argv, "flowlabel")) {
120 			__u32 uval;
121 
122 			NEXT_ARG();
123 			check_duparg(&attrs, IFLA_GENEVE_LABEL, "flowlabel",
124 				     *argv);
125 			if (get_u32(&uval, *argv, 0) ||
126 			    (uval & ~LABEL_MAX_MASK))
127 				invarg("invalid flowlabel", *argv);
128 			label = htonl(uval);
129 		} else if (!matches(*argv, "dstport")) {
130 			NEXT_ARG();
131 			check_duparg(&attrs, IFLA_GENEVE_PORT, "dstport",
132 				     *argv);
133 			if (get_u16(&dstport, *argv, 0))
134 				invarg("dstport", *argv);
135 		} else if (!matches(*argv, "external")) {
136 			check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA,
137 				     *argv, *argv);
138 			metadata = true;
139 		} else if (!matches(*argv, "noexternal")) {
140 			check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA,
141 				     *argv, *argv);
142 			metadata = false;
143 		} else if (!matches(*argv, "udpcsum")) {
144 			check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv,
145 				     *argv);
146 			udpcsum = 1;
147 		} else if (!matches(*argv, "noudpcsum")) {
148 			check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv,
149 				     *argv);
150 			udpcsum = 0;
151 		} else if (!matches(*argv, "udp6zerocsumtx")) {
152 			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
153 				     *argv, *argv);
154 			udp6zerocsumtx = 1;
155 		} else if (!matches(*argv, "noudp6zerocsumtx")) {
156 			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
157 				     *argv, *argv);
158 			udp6zerocsumtx = 0;
159 		} else if (!matches(*argv, "udp6zerocsumrx")) {
160 			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
161 				     *argv, *argv);
162 			udp6zerocsumrx = 1;
163 		} else if (!matches(*argv, "noudp6zerocsumrx")) {
164 			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
165 				     *argv, *argv);
166 			udp6zerocsumrx = 0;
167 		} else if (matches(*argv, "help") == 0) {
168 			explain();
169 			return -1;
170 		} else {
171 			fprintf(stderr, "geneve: unknown command \"%s\"?\n", *argv);
172 			explain();
173 			return -1;
174 		}
175 		argc--, argv++;
176 	}
177 
178 	if (metadata && GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) {
179 		fprintf(stderr, "geneve: both 'external' and vni cannot be specified\n");
180 		return -1;
181 	}
182 
183 	if (!metadata) {
184 		/* parameter checking make sense only for full geneve tunnels */
185 		if (!GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) {
186 			fprintf(stderr, "geneve: missing virtual network identifier\n");
187 			return -1;
188 		}
189 
190 		/* If we are modifying the geneve device, then we only need the
191 		 * ID (VNI) to identify the geneve device, and we do not need
192 		 * the remote IP.
193 		 */
194 		if (!set_op && !daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
195 			fprintf(stderr, "geneve: remote link partner not specified\n");
196 			return -1;
197 		}
198 	}
199 
200 	addattr32(n, 1024, IFLA_GENEVE_ID, vni);
201 	if (daddr)
202 		addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4);
203 	if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
204 		addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6,
205 			  sizeof(struct in6_addr));
206 	}
207 	if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL))
208 		addattr32(n, 1024, IFLA_GENEVE_LABEL, label);
209 	if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TTL))
210 		addattr8(n, 1024, IFLA_GENEVE_TTL, ttl);
211 	if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TOS))
212 		addattr8(n, 1024, IFLA_GENEVE_TOS, tos);
213 	if (dstport)
214 		addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport));
215 	if (metadata)
216 		addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA);
217 	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM))
218 		addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum);
219 	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX))
220 		addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx);
221 	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX))
222 		addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx);
223 
224 	return 0;
225 }
226 
geneve_print_opt(struct link_util * lu,FILE * f,struct rtattr * tb[])227 static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
228 {
229 	__u32 vni;
230 	__u8 tos;
231 
232 	if (!tb)
233 		return;
234 
235 	if (!tb[IFLA_GENEVE_ID] ||
236 	    RTA_PAYLOAD(tb[IFLA_GENEVE_ID]) < sizeof(__u32))
237 		return;
238 
239 	vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]);
240 	print_uint(PRINT_ANY, "id", "id %u ", vni);
241 
242 	if (tb[IFLA_GENEVE_REMOTE]) {
243 		__be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]);
244 
245 		if (addr)
246 			print_string(PRINT_ANY,
247 				     "remote",
248 				     "remote %s ",
249 				     format_host(AF_INET, 4, &addr));
250 	} else if (tb[IFLA_GENEVE_REMOTE6]) {
251 		struct in6_addr addr;
252 
253 		memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr));
254 		if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
255 			if (!IN6_IS_ADDR_MULTICAST(&addr))
256 				print_string(PRINT_ANY,
257 					     "remote6",
258 					     "remote %s ",
259 					     format_host(AF_INET6,
260 							 sizeof(struct in6_addr),
261 							 &addr));
262 		}
263 	}
264 
265 	if (tb[IFLA_GENEVE_TTL]) {
266 		__u8 ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]);
267 
268 		if (ttl)
269 			print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
270 	}
271 
272 	if (tb[IFLA_GENEVE_TOS] &&
273 	    (tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]))) {
274 		if (is_json_context()) {
275 			print_0xhex(PRINT_JSON, "tos", "%#x", tos);
276 		} else {
277 			if (tos == 1) {
278 				print_string(PRINT_FP,
279 					     "tos",
280 					     "tos %s ",
281 					     "inherit");
282 			} else {
283 				fprintf(f, "tos %#x ", tos);
284 			}
285 		}
286 	}
287 
288 	if (tb[IFLA_GENEVE_LABEL]) {
289 		__u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]);
290 
291 		if (label)
292 			print_0xhex(PRINT_ANY,
293 				    "label",
294 				    "flowlabel %#x ",
295 				    ntohl(label));
296 	}
297 
298 	if (tb[IFLA_GENEVE_PORT])
299 		print_uint(PRINT_ANY,
300 			   "port",
301 			   "dstport %u ",
302 			   rta_getattr_be16(tb[IFLA_GENEVE_PORT]));
303 
304 	if (tb[IFLA_GENEVE_COLLECT_METADATA])
305 		print_bool(PRINT_ANY, "collect_metadata", "external ", true);
306 
307 	if (tb[IFLA_GENEVE_UDP_CSUM]) {
308 		if (is_json_context()) {
309 			print_bool(PRINT_JSON,
310 				   "udp_csum",
311 				   NULL,
312 				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]));
313 		} else {
314 			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]))
315 				fputs("no", f);
316 			fputs("udpcsum ", f);
317 		}
318 	}
319 
320 	if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) {
321 		if (is_json_context()) {
322 			print_bool(PRINT_JSON,
323 				   "udp_zero_csum6_tx",
324 				   NULL,
325 				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]));
326 		} else {
327 			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
328 				fputs("no", f);
329 			fputs("udp6zerocsumtx ", f);
330 		}
331 	}
332 
333 	if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) {
334 		if (is_json_context()) {
335 			print_bool(PRINT_JSON,
336 				   "udp_zero_csum6_rx",
337 				   NULL,
338 				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]));
339 		} else {
340 			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
341 				fputs("no", f);
342 			fputs("udp6zerocsumrx ", f);
343 		}
344 	}
345 }
346 
geneve_print_help(struct link_util * lu,int argc,char ** argv,FILE * f)347 static void geneve_print_help(struct link_util *lu, int argc, char **argv,
348 	FILE *f)
349 {
350 	print_explain(f);
351 }
352 
353 struct link_util geneve_link_util = {
354 	.id		= "geneve",
355 	.maxattr	= IFLA_GENEVE_MAX,
356 	.parse_opt	= geneve_parse_opt,
357 	.print_opt	= geneve_print_opt,
358 	.print_help	= geneve_print_help,
359 };
360