xref: /src/sbin/ifconfig/ifgeneve.c (revision bc793ad78734acc4833f8f38bfb505e810c52963)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2025-2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/ioctl.h>
31 #include <sys/nv.h>
32 #include <sys/socket.h>
33 #include <sys/sockio.h>
34 
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <unistd.h>
38 #include <netdb.h>
39 
40 #include <net/ethernet.h>
41 #include <net/if.h>
42 #include <net/if_strings.h>
43 #include <netinet/in.h>
44 #include <net/if_geneve.h>
45 
46 #include <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <err.h>
51 #include <errno.h>
52 
53 #include "ifconfig.h"
54 #include "ifconfig_netlink.h"
55 
56 struct nl_parsed_geneve {
57 	/* essential */
58 	uint32_t			ifla_vni;
59 	uint16_t			ifla_proto;
60 	struct sockaddr			*ifla_local;
61 	struct sockaddr			*ifla_remote;
62 	uint16_t			ifla_local_port;
63 	uint16_t			ifla_remote_port;
64 
65 	/* optional */
66 	struct ifla_geneve_port_range	*ifla_port_range;
67 	enum ifla_geneve_df		ifla_df;
68 	uint8_t				ifla_ttl;
69 	bool				ifla_ttl_inherit;
70 	bool				ifla_dscp_inherit;
71 	bool				ifla_external;
72 
73 	/* l2 specific */
74 	bool				ifla_ftable_learn;
75 	bool				ifla_ftable_flush;
76 	uint32_t			ifla_ftable_max;
77 	uint32_t			ifla_ftable_timeout;
78 	uint32_t			ifla_ftable_count;
79 	uint32_t			ifla_ftable_nospace;
80 	uint32_t			ifla_ftable_lock_upgrade_failed;
81 
82 	/* multicast specific */
83 	char				*ifla_mc_ifname;
84 	uint32_t			ifla_mc_ifindex;
85 
86 	/* csum info */
87 	uint64_t			ifla_stats_txcsum;
88 	uint64_t			ifla_stats_tso;
89 	uint64_t			ifla_stats_rxcsum;
90 };
91 
92 static struct geneve_params gnvp = {
93 	.ifla_proto		=	GENEVE_PROTO_ETHER,
94 };
95 
96 static int
get_proto(const char * cp,uint16_t * valp)97 get_proto(const char *cp, uint16_t *valp)
98 {
99 	uint16_t val;
100 
101 	if (!strcmp(cp, "l2"))
102 		val = GENEVE_PROTO_ETHER;
103 	else if (!strcmp(cp, "l3"))
104 		val = GENEVE_PROTO_INHERIT;
105 	else
106 		return (-1);
107 
108 	*valp = val;
109 	return (0);
110 }
111 
112 static int
get_val(const char * cp,u_long * valp)113 get_val(const char *cp, u_long *valp)
114 {
115 	char *endptr;
116 	u_long val;
117 
118 	errno = 0;
119 	val = strtoul(cp, &endptr, 0);
120 	if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
121 		return (-1);
122 
123 	*valp = val;
124 	return (0);
125 }
126 
127 static int
get_df(const char * cp,enum ifla_geneve_df * valp)128 get_df(const char *cp, enum ifla_geneve_df *valp)
129 {
130 	enum ifla_geneve_df df;
131 
132 	if (!strcmp(cp, "set"))
133 		df = IFLA_GENEVE_DF_SET;
134 	else if (!strcmp(cp, "inherit"))
135 		df = IFLA_GENEVE_DF_INHERIT;
136 	else if (!strcmp(cp, "unset"))
137 		df = IFLA_GENEVE_DF_UNSET;
138 	else
139 		return (-1);
140 
141 	*valp = df;
142 	return (0);
143 }
144 
145 static bool
is_multicast(struct addrinfo * ai)146 is_multicast(struct addrinfo *ai)
147 {
148 #if (defined INET || defined INET6)
149 	struct sockaddr *sa;
150 	sa = ai->ai_addr;
151 #endif
152 
153 	switch (ai->ai_family) {
154 #ifdef INET
155 	case AF_INET: {
156 		struct sockaddr_in *sin = satosin(sa);
157 
158 		return (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)));
159 	}
160 #endif
161 #ifdef INET6
162 	case AF_INET6: {
163 		struct sockaddr_in6 *sin6 = satosin6(sa);
164 
165 		return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr));
166 	}
167 #endif
168 	default:
169 		errx(1, "address family not supported");
170 	}
171 }
172 
173 /*
174  * geneve mode is read-only after creation,
175  * therefore there is no need for separate netlink implementation
176  */
177 static void
setgeneve_mode_clone(if_ctx * ctx __unused,const char * arg,int dummy __unused)178 setgeneve_mode_clone(if_ctx *ctx __unused, const char *arg, int dummy __unused)
179 {
180 	uint16_t val;
181 
182 	if (get_proto(arg, &val) < 0)
183 		errx(1, "invalid inner protocol: %s", arg);
184 
185 	gnvp.ifla_proto = val;
186 }
187 
188 struct nla_geneve_info {
189 	const char		*kind;
190 	struct nl_parsed_geneve	data;
191 };
192 
193 struct nla_geneve_link {
194 	uint32_t		ifi_index;
195 	struct nla_geneve_info	linkinfo;
196 };
197 
198 static inline void
geneve_nl_init(if_ctx * ctx,struct snl_writer * nw,uint32_t flags)199 geneve_nl_init(if_ctx *ctx, struct snl_writer *nw, uint32_t flags)
200 {
201 	struct nlmsghdr *hdr;
202 
203 	snl_init_writer(ctx->io_ss, nw);
204 	hdr = snl_create_msg_request(nw, NL_RTM_NEWLINK);
205 	hdr->nlmsg_flags |= flags;
206 	snl_reserve_msg_object(nw, struct ifinfomsg);
207         snl_add_msg_attr_string(nw, IFLA_IFNAME, ctx->ifname);
208 }
209 
210 static inline void
geneve_nl_fini(if_ctx * ctx,struct snl_writer * nw)211 geneve_nl_fini(if_ctx *ctx, struct snl_writer *nw)
212 {
213 	struct nlmsghdr *hdr;
214 	struct snl_errmsg_data errmsg = {};
215 
216 	hdr = snl_finalize_msg(nw);
217 	if (hdr == NULL || !snl_send_message(ctx->io_ss, hdr))
218 		err(1, "unable to send netlink message");
219 
220 	if (!snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &errmsg))
221 		errx(errmsg.error, "%s", errmsg.error_str);
222 }
223 
224 #define _OUT(_field)	offsetof(struct nl_parsed_geneve, _field)
225 static const struct snl_attr_parser nla_geneve_linkinfo_data[] = {
226 	{ .type = IFLA_GENEVE_ID, .off = _OUT(ifla_vni), .cb = snl_attr_get_uint32 },
227 	{ .type = IFLA_GENEVE_PROTOCOL, .off = _OUT(ifla_proto), .cb = snl_attr_get_uint16 },
228 	{ .type = IFLA_GENEVE_LOCAL, .off = _OUT(ifla_local), .cb = snl_attr_get_ip },
229 	{ .type = IFLA_GENEVE_REMOTE, .off = _OUT(ifla_remote), .cb = snl_attr_get_ip },
230 	{ .type = IFLA_GENEVE_LOCAL_PORT, .off = _OUT(ifla_local_port), .cb = snl_attr_get_uint16 },
231 	{ .type = IFLA_GENEVE_PORT, .off = _OUT(ifla_remote_port), .cb = snl_attr_get_uint16 },
232 	{ .type = IFLA_GENEVE_PORT_RANGE, .off = _OUT(ifla_port_range), .cb = snl_attr_dup_struct },
233 	{ .type = IFLA_GENEVE_DF, .off = _OUT(ifla_df), .cb = snl_attr_get_uint8 },
234 	{ .type = IFLA_GENEVE_TTL, .off = _OUT(ifla_ttl), .cb = snl_attr_get_uint8 },
235 	{ .type = IFLA_GENEVE_TTL_INHERIT, .off = _OUT(ifla_ttl_inherit), .cb = snl_attr_get_bool },
236 	{ .type = IFLA_GENEVE_DSCP_INHERIT, .off = _OUT(ifla_dscp_inherit), .cb = snl_attr_get_bool },
237 	{ .type = IFLA_GENEVE_COLLECT_METADATA, .off = _OUT(ifla_external), .cb = snl_attr_get_bool },
238 	{ .type = IFLA_GENEVE_FTABLE_LEARN, .off = _OUT(ifla_ftable_learn), .cb = snl_attr_get_bool },
239 	{ .type = IFLA_GENEVE_FTABLE_FLUSH, .off = _OUT(ifla_ftable_flush), .cb = snl_attr_get_bool },
240 	{ .type = IFLA_GENEVE_FTABLE_MAX, .off = _OUT(ifla_ftable_max), .cb = snl_attr_get_uint32 },
241 	{ .type = IFLA_GENEVE_FTABLE_TIMEOUT, .off = _OUT(ifla_ftable_timeout), .cb = snl_attr_get_uint32 },
242 	{ .type = IFLA_GENEVE_FTABLE_COUNT, .off = _OUT(ifla_ftable_count), .cb = snl_attr_get_uint32 },
243 	{ .type = IFLA_GENEVE_FTABLE_NOSPACE_CNT, .off = _OUT(ifla_ftable_nospace), .cb = snl_attr_get_uint32 },
244 	{ .type = IFLA_GENEVE_FTABLE_LOCK_UP_FAIL_CNT, .off = _OUT(ifla_ftable_lock_upgrade_failed), .cb = snl_attr_get_uint32 },
245 	{ .type = IFLA_GENEVE_MC_IFNAME, .off = _OUT(ifla_mc_ifname), .cb = snl_attr_get_string },
246 	{ .type = IFLA_GENEVE_MC_IFINDEX, .off = _OUT(ifla_mc_ifindex), .cb = snl_attr_get_uint32 },
247 	{ .type = IFLA_GENEVE_TXCSUM_CNT, .off = _OUT(ifla_stats_txcsum), .cb = snl_attr_get_uint64 },
248 	{ .type = IFLA_GENEVE_TSO_CNT, .off = _OUT(ifla_stats_tso), .cb = snl_attr_get_uint64 },
249 	{ .type = IFLA_GENEVE_RXCSUM_CNT, .off = _OUT(ifla_stats_rxcsum), .cb = snl_attr_get_uint64 },
250 };
251 #undef _OUT
252 SNL_DECLARE_ATTR_PARSER(geneve_linkinfo_data_parser, nla_geneve_linkinfo_data);
253 
254 #define _OUT(_field)	offsetof(struct nla_geneve_info, _field)
255 static const struct snl_attr_parser ap_geneve_linkinfo[] = {
256 	{ .type = IFLA_INFO_KIND, .off = _OUT(kind), .cb = snl_attr_get_string },
257 	{ .type = IFLA_INFO_DATA, .off = _OUT(data),
258 		.arg = &geneve_linkinfo_data_parser, .cb = snl_attr_get_nested },
259 };
260 #undef _OUT
261 SNL_DECLARE_ATTR_PARSER(geneve_linkinfo_parser, ap_geneve_linkinfo);
262 
263 #define _IN(_field)	offsetof(struct ifinfomsg, _field)
264 #define _OUT(_field)	offsetof(struct nla_geneve_link, _field)
265 static const struct snl_attr_parser ap_geneve_link[] = {
266 	{ .type = IFLA_LINKINFO, .off = _OUT(linkinfo),
267 		.arg = &geneve_linkinfo_parser, .cb = snl_attr_get_nested },
268 };
269 
270 static const struct snl_field_parser fp_geneve_link[] = {
271 	{ .off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
272 };
273 #undef _IN
274 #undef _OUT
275 SNL_DECLARE_PARSER(geneve_parser, struct ifinfomsg, fp_geneve_link, ap_geneve_link);
276 
277 static const struct snl_hdr_parser *all_parsers[] = {
278 	&geneve_linkinfo_data_parser,
279 	&geneve_linkinfo_parser,
280 	&geneve_parser,
281 };
282 
283 static void
geneve_status_nl(if_ctx * ctx)284 geneve_status_nl(if_ctx *ctx)
285 {
286 	struct snl_writer nw;
287 	struct nlmsghdr *hdr;
288 	struct snl_errmsg_data errmsg;
289 	struct nla_geneve_link geneve_link;
290 	char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN];
291 	struct sockaddr *lsa, *rsa;
292 	int mc;
293 	bool ipv6 = false;
294 
295 	if (strncmp(ctx->ifname, "geneve", sizeof("geneve") - 1) != 0)
296 		return;
297 
298 	snl_init_writer(ctx->io_ss, &nw);
299 	hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
300 	hdr->nlmsg_flags |= NLM_F_DUMP;
301 	snl_reserve_msg_object(&nw, struct ifinfomsg);
302         snl_add_msg_attr_string(&nw, IFLA_IFNAME, ctx->ifname);
303 
304 	if (!(hdr = snl_finalize_msg(&nw)) || (!snl_send_message(ctx->io_ss, hdr)))
305 		return;
306 
307 	hdr = snl_read_reply(ctx->io_ss, hdr->nlmsg_seq);
308 	if (hdr->nlmsg_type != NL_RTM_NEWLINK) {
309 		if (!snl_parse_errmsg(ctx->io_ss, hdr, &errmsg))
310 			errx(EINVAL, "(NETLINK)");
311 		if (errmsg.error_str != NULL)
312 			errx(errmsg.error, "(NETLINK) %s", errmsg.error_str);
313 	}
314 
315 	if (!snl_parse_nlmsg(ctx->io_ss, hdr, &geneve_parser, &geneve_link))
316 		return;
317 
318 	struct nla_geneve_info geneve_info = geneve_link.linkinfo;
319 	struct nl_parsed_geneve geneve_data = geneve_info.data;
320 
321 	printf("\tgeneve mode: ");
322 	switch (geneve_data.ifla_proto) {
323 	case GENEVE_PROTO_INHERIT:
324 		printf("l3");
325 		break;
326 	case GENEVE_PROTO_ETHER:
327 	default:
328 		printf("l2");
329 		break;
330 	}
331 
332 	printf("\n\tgeneve config:\n");
333 	/* Just report nothing if the network identity isn't set yet. */
334 	if (geneve_data.ifla_vni >= GENEVE_VNI_MAX) {
335 		printf("\t\tvirtual network identifier (vni): not configured\n");
336 		return;
337 	}
338 
339 	lsa = geneve_data.ifla_local;
340 	rsa = geneve_data.ifla_remote;
341 
342 	if ((lsa == NULL) ||
343 	    (getnameinfo(lsa, lsa->sa_len, src, sizeof(src),
344 	    NULL, 0, NI_NUMERICHOST) != 0))
345 		src[0] = '\0';
346 	if ((rsa == NULL) ||
347 	    (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst),
348 	    NULL, 0, NI_NUMERICHOST) != 0))
349 		dst[0] = '\0';
350 	else {
351 		ipv6 = rsa->sa_family == AF_INET6;
352 		if (!ipv6) {
353 			struct sockaddr_in *sin = satosin(rsa);
354 			mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
355 		} else {
356 			struct sockaddr_in6 *sin6 = satosin6(rsa);
357 			mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
358 		}
359 	}
360 
361 	printf("\t\tvirtual network identifier (vni): %d", geneve_data.ifla_vni);
362 	if (src[0] != '\0')
363 		printf("\n\t\tlocal: %s%s%s:%u", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
364 		    geneve_data.ifla_local_port);
365 	if (dst[0] != '\0') {
366 		printf("\n\t\t%s: %s%s%s:%u", mc ? "group" : "remote", ipv6 ? "[" : "",
367 		    dst, ipv6 ? "]" : "", geneve_data.ifla_local_port);
368 		if (mc)
369 			printf(", dev: %s", geneve_data.ifla_mc_ifname);
370 	}
371 
372 	if (ctx->args->verbose) {
373 		printf("\n\t\tportrange: %u-%u",
374 		    geneve_data.ifla_port_range->low,
375 		    geneve_data.ifla_port_range->high);
376 
377 		if (geneve_data.ifla_ttl_inherit)
378 			printf(", ttl: inherit");
379 		else
380 			printf(", ttl: %d", geneve_data.ifla_ttl);
381 
382 		if (geneve_data.ifla_dscp_inherit)
383 			printf(", dscp: inherit");
384 
385 		if (geneve_data.ifla_df == IFLA_GENEVE_DF_INHERIT)
386 			printf(", df: inherit");
387 		else if (geneve_data.ifla_df == IFLA_GENEVE_DF_SET)
388 			printf(", df: set");
389 		else if (geneve_data.ifla_df == IFLA_GENEVE_DF_UNSET)
390 			printf(", df: unset");
391 
392 		if (geneve_data.ifla_external)
393 			printf(", externally controlled");
394 
395 		if (geneve_data.ifla_proto == GENEVE_PROTO_ETHER) {
396 			printf("\n\t\tftable mode: %slearning",
397 			    geneve_data.ifla_ftable_learn ? "" : "no");
398 			printf(", count: %d, max: %d, timeout: %d",
399 			    geneve_data.ifla_ftable_count,
400 			    geneve_data.ifla_ftable_max,
401 			    geneve_data.ifla_ftable_timeout);
402 			printf(", nospace: %u",
403 			    geneve_data.ifla_ftable_nospace);
404 		}
405 
406 		printf("\n\t\tstats: tso %ju, txcsum %ju, rxcsum %ju",
407 		    (uintmax_t)geneve_data.ifla_stats_tso,
408 		    (uintmax_t)geneve_data.ifla_stats_txcsum,
409 		    (uintmax_t)geneve_data.ifla_stats_rxcsum);
410 	}
411 
412 	putchar('\n');
413 }
414 
415 
416 static void
geneve_create_nl(if_ctx * ctx,struct ifreq * ifr)417 geneve_create_nl(if_ctx *ctx, struct ifreq *ifr)
418 {
419 	struct snl_writer nw = {};
420 	struct nlmsghdr *hdr;
421 	int off, off2;
422 
423 	snl_init_writer(ctx->io_ss, &nw);
424 	hdr = snl_create_msg_request(&nw, RTM_NEWLINK);
425 	hdr->nlmsg_flags |= (NLM_F_CREATE | NLM_F_EXCL);
426 	snl_reserve_msg_object(&nw, struct ifinfomsg);
427         snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifr->ifr_name);
428 
429 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
430         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
431 
432 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
433         snl_add_msg_attr_u16(&nw, IFLA_GENEVE_PROTOCOL, gnvp.ifla_proto);
434 
435 	snl_end_attr_nested(&nw, off2);
436 	snl_end_attr_nested(&nw, off);
437 
438 	geneve_nl_fini(ctx, &nw);
439 }
440 
441 static void
setgeneve_vni_nl(if_ctx * ctx,const char * arg,int dummy __unused)442 setgeneve_vni_nl(if_ctx *ctx, const char *arg, int dummy __unused)
443 {
444 	struct snl_writer nw = {};
445 	int off, off2;
446 	u_long val;
447 
448 	if (get_val(arg, &val) < 0 || val >= GENEVE_VNI_MAX)
449 		errx(1, "invalid network identifier: %s", arg);
450 
451 	geneve_nl_init(ctx, &nw, 0);
452 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
453         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
454 
455 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
456         snl_add_msg_attr_u32(&nw, IFLA_GENEVE_ID, val);
457 
458 	snl_end_attr_nested(&nw, off2);
459 	snl_end_attr_nested(&nw, off);
460 
461 	geneve_nl_fini(ctx, &nw);
462 }
463 
464 static void
setgeneve_local_nl(if_ctx * ctx,const char * addr,int dummy __unused)465 setgeneve_local_nl(if_ctx *ctx, const char *addr, int dummy __unused)
466 {
467 	struct snl_writer nw = {};
468 	int off, off2;
469 	struct addrinfo *ai;
470 	const struct sockaddr *sa;
471 	int error;
472 
473 	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
474 		errx(1, "error in parsing local address string: %s",
475 		    gai_strerror(error));
476 
477 	if (is_multicast(ai))
478 		errx(1, "local address cannot be multicast");
479 
480 	geneve_nl_init(ctx, &nw, 0);
481 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
482         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
483 
484 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
485 
486 	sa = ai->ai_addr;
487         snl_add_msg_attr_ip(&nw, IFLA_GENEVE_LOCAL, sa);
488 
489 	snl_end_attr_nested(&nw, off2);
490 	snl_end_attr_nested(&nw, off);
491 
492 	geneve_nl_fini(ctx, &nw);
493 }
494 
495 static void
setgeneve_remote_nl(if_ctx * ctx,const char * addr,int dummy __unused)496 setgeneve_remote_nl(if_ctx *ctx, const char *addr, int dummy __unused)
497 {
498 	struct snl_writer nw = {};
499 	int off, off2;
500 	struct addrinfo *ai;
501 	const struct sockaddr *sa;
502 	int error;
503 
504 	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
505 		errx(1, "error in parsing remote address string: %s",
506 		    gai_strerror(error));
507 
508 	if (is_multicast(ai))
509 		errx(1, "remote address cannot be multicast");
510 
511 	geneve_nl_init(ctx, &nw, 0);
512 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
513         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
514 
515 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
516 
517 	sa = ai->ai_addr;
518         snl_add_msg_attr_ip(&nw, IFLA_GENEVE_REMOTE, sa);
519 
520 	snl_end_attr_nested(&nw, off2);
521 	snl_end_attr_nested(&nw, off);
522 
523 	geneve_nl_fini(ctx, &nw);
524 }
525 
526 static void
setgeneve_group_nl(if_ctx * ctx,const char * addr,int dummy __unused)527 setgeneve_group_nl(if_ctx *ctx, const char *addr, int dummy __unused)
528 {
529 	struct snl_writer nw = {};
530 	int off, off2;
531 	struct addrinfo *ai;
532 	struct sockaddr *sa;
533 	int error;
534 
535 	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
536 		errx(1, "error in parsing local address string: %s",
537 		    gai_strerror(error));
538 
539 	if (!is_multicast(ai))
540 		errx(1, "group address must be multicast");
541 
542 	geneve_nl_init(ctx, &nw, 0);
543 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
544         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
545 
546 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
547 
548 	sa = ai->ai_addr;
549         snl_add_msg_attr_ip(&nw, IFLA_GENEVE_REMOTE, sa);
550 
551 	snl_end_attr_nested(&nw, off2);
552 	snl_end_attr_nested(&nw, off);
553 
554 	geneve_nl_fini(ctx, &nw);
555 }
556 
557 
558 static void
setgeneve_local_port_nl(if_ctx * ctx,const char * arg,int dummy __unused)559 setgeneve_local_port_nl(if_ctx *ctx, const char *arg, int dummy __unused)
560 {
561 	struct snl_writer nw = {};
562 	int off, off2;
563 	u_long val;
564 
565 	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
566 		errx(1, "invalid local port: %s", arg);
567 
568 	geneve_nl_init(ctx, &nw, 0);
569 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
570         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
571 
572 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
573 
574         snl_add_msg_attr_u16(&nw, IFLA_GENEVE_LOCAL_PORT, val);
575 
576 	snl_end_attr_nested(&nw, off2);
577 	snl_end_attr_nested(&nw, off);
578 
579 	geneve_nl_fini(ctx, &nw);
580 }
581 
582 static void
setgeneve_remote_port_nl(if_ctx * ctx,const char * arg,int dummy __unused)583 setgeneve_remote_port_nl(if_ctx *ctx, const char *arg, int dummy __unused)
584 {
585 	struct snl_writer nw = {};
586 	int off, off2;
587 	u_long val;
588 
589 	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
590 		errx(1, "invalid remote port: %s", arg);
591 
592 	geneve_nl_init(ctx, &nw, 0);
593 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
594         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
595 
596 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
597 
598         snl_add_msg_attr_u16(&nw, IFLA_GENEVE_PORT, val);
599 
600 	snl_end_attr_nested(&nw, off2);
601 	snl_end_attr_nested(&nw, off);
602 
603 	geneve_nl_fini(ctx, &nw);
604 }
605 
606 static void
setgeneve_port_range_nl(if_ctx * ctx,const char * arg1,const char * arg2)607 setgeneve_port_range_nl(if_ctx *ctx, const char *arg1, const char *arg2)
608 {
609 	struct snl_writer nw = {};
610 	int off, off2;
611 	u_long min, max;
612 
613 	if (get_val(arg1, &min) < 0 || min >= UINT16_MAX)
614 		errx(1, "invalid port range minimum: %s", arg1);
615 	if (get_val(arg2, &max) < 0 || max >= UINT16_MAX)
616 		errx(1, "invalid port range maximum: %s", arg2);
617 	if (max < min)
618 		errx(1, "invalid port range");
619 
620 	const struct ifla_geneve_port_range port_range = {
621 		.low = min,
622 		.high = max
623 	};
624 
625 	geneve_nl_init(ctx, &nw, 0);
626 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
627         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
628 
629 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
630 
631         snl_add_msg_attr(&nw, IFLA_GENEVE_PORT_RANGE,
632 			sizeof(port_range), (const void *)&port_range);
633 
634 	snl_end_attr_nested(&nw, off2);
635 	snl_end_attr_nested(&nw, off);
636 
637 	geneve_nl_fini(ctx, &nw);
638 }
639 
640 static void
setgeneve_timeout_nl(if_ctx * ctx,const char * arg,int dummy __unused)641 setgeneve_timeout_nl(if_ctx *ctx, const char *arg, int dummy __unused)
642 {
643 	struct snl_writer nw = {};
644 	int off, off2;
645 	u_long val;
646 
647 	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
648 		errx(1, "invalid timeout value: %s", arg);
649 
650 	geneve_nl_init(ctx, &nw, 0);
651 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
652         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
653 
654 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
655 
656         snl_add_msg_attr_u32(&nw, IFLA_GENEVE_FTABLE_TIMEOUT, val);
657 
658 	snl_end_attr_nested(&nw, off2);
659 	snl_end_attr_nested(&nw, off);
660 
661 	geneve_nl_fini(ctx, &nw);
662 }
663 
664 static void
setgeneve_maxaddr_nl(if_ctx * ctx,const char * arg,int dummy __unused)665 setgeneve_maxaddr_nl(if_ctx *ctx, const char *arg, int dummy __unused)
666 {
667 	struct snl_writer nw = {};
668 	int off, off2;
669 	u_long val;
670 
671 	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
672 		errx(1, "invalid maxaddr value: %s",  arg);
673 
674 	geneve_nl_init(ctx, &nw, 0);
675 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
676         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
677 
678 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
679 
680         snl_add_msg_attr_u32(&nw, IFLA_GENEVE_FTABLE_MAX, val);
681 
682 	snl_end_attr_nested(&nw, off2);
683 	snl_end_attr_nested(&nw, off);
684 
685 	geneve_nl_fini(ctx, &nw);
686 }
687 
688 static void
setgeneve_dev_nl(if_ctx * ctx,const char * arg,int dummy __unused)689 setgeneve_dev_nl(if_ctx *ctx, const char *arg, int dummy __unused)
690 {
691 	struct snl_writer nw = {};
692 	int off, off2;
693 
694 	geneve_nl_init(ctx, &nw, 0);
695 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
696         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
697 
698 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
699 
700         snl_add_msg_attr_string(&nw, IFLA_GENEVE_MC_IFNAME, arg);
701 
702 	snl_end_attr_nested(&nw, off2);
703 	snl_end_attr_nested(&nw, off);
704 
705 	geneve_nl_fini(ctx, &nw);
706 }
707 
708 static void
setgeneve_ttl_nl(if_ctx * ctx,const char * arg,int dummy __unused)709 setgeneve_ttl_nl(if_ctx *ctx, const char *arg, int dummy __unused)
710 {
711 	struct snl_writer nw = {};
712 	int off, off2;
713 	u_long val;
714 
715 	geneve_nl_init(ctx, &nw, 0);
716 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
717         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
718 
719 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
720 	if ((get_val(arg, &val) < 0 || val > 256) == 0) {
721 		snl_add_msg_attr_u8(&nw, IFLA_GENEVE_TTL, val);
722 		snl_add_msg_attr_bool(&nw, IFLA_GENEVE_TTL_INHERIT, false);
723 	} else if (!strcmp(arg, "inherit")) {
724 		snl_add_msg_attr_bool(&nw, IFLA_GENEVE_TTL_INHERIT, true);
725 	} else
726 		errx(1, "invalid TTL value: %s", arg);
727 
728 	snl_end_attr_nested(&nw, off2);
729 	snl_end_attr_nested(&nw, off);
730 
731 	geneve_nl_fini(ctx, &nw);
732 }
733 
734 static void
setgeneve_df_nl(if_ctx * ctx,const char * arg,int dummy __unused)735 setgeneve_df_nl(if_ctx *ctx, const char *arg, int dummy __unused)
736 {
737 	struct snl_writer nw = {};
738 	int off, off2;
739 	enum ifla_geneve_df df;
740 
741 	if (get_df(arg, &df) < 0)
742 		errx(1, "invalid df value: %s", arg);
743 
744 	geneve_nl_init(ctx, &nw, 0);
745 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
746         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
747 
748 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
749 
750         snl_add_msg_attr_u8(&nw, IFLA_GENEVE_DF, df);
751 
752 	snl_end_attr_nested(&nw, off2);
753 	snl_end_attr_nested(&nw, off);
754 
755 	geneve_nl_fini(ctx, &nw);
756 }
757 
758 static void
setgeneve_inherit_dscp_nl(if_ctx * ctx,const char * arg __unused,int d)759 setgeneve_inherit_dscp_nl(if_ctx *ctx, const char *arg __unused, int d)
760 {
761 	struct snl_writer nw = {};
762 	int off, off2;
763 
764 	geneve_nl_init(ctx, &nw, 0);
765 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
766         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
767 
768 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
769 
770         snl_add_msg_attr_bool(&nw, IFLA_GENEVE_DSCP_INHERIT, d != 0);
771 
772 	snl_end_attr_nested(&nw, off2);
773 	snl_end_attr_nested(&nw, off);
774 
775 	geneve_nl_fini(ctx, &nw);
776 }
777 
778 static void
setgeneve_learn_nl(if_ctx * ctx,const char * arg __unused,int d)779 setgeneve_learn_nl(if_ctx *ctx, const char *arg __unused, int d)
780 {
781 	struct snl_writer nw = {};
782 	int off, off2;
783 
784 	geneve_nl_init(ctx, &nw, 0);
785 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
786         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
787 
788 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
789 
790         snl_add_msg_attr_bool(&nw, IFLA_GENEVE_FTABLE_LEARN, d != 0);
791 
792 	snl_end_attr_nested(&nw, off2);
793 	snl_end_attr_nested(&nw, off);
794 
795 	geneve_nl_fini(ctx, &nw);
796 }
797 
798 static void
setgeneve_flush_nl(if_ctx * ctx,const char * val __unused,int d)799 setgeneve_flush_nl(if_ctx *ctx, const char *val __unused, int d)
800 {
801 	struct snl_writer nw = {};
802 	int off, off2;
803 
804 	geneve_nl_init(ctx, &nw, 0);
805 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
806         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
807 
808 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
809 
810         snl_add_msg_attr_bool(&nw, IFLA_GENEVE_FTABLE_FLUSH, d != 0);
811 
812 	snl_end_attr_nested(&nw, off2);
813 	snl_end_attr_nested(&nw, off);
814 
815 	geneve_nl_fini(ctx, &nw);
816 }
817 
818 static void
setgeneve_external_nl(if_ctx * ctx,const char * val __unused,int d)819 setgeneve_external_nl(if_ctx *ctx, const char *val __unused, int d)
820 {
821 	struct snl_writer nw = {};
822 	int off, off2;
823 
824 	geneve_nl_init(ctx, &nw, 0);
825 	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
826         snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
827 
828 	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
829 
830         snl_add_msg_attr_bool(&nw, IFLA_GENEVE_COLLECT_METADATA, d != 0);
831 
832 	snl_end_attr_nested(&nw, off2);
833 	snl_end_attr_nested(&nw, off);
834 
835 	geneve_nl_fini(ctx, &nw);
836 }
837 
838 static struct cmd geneve_cmds[] = {
839 
840 	DEF_CLONE_CMD_ARG("genevemode",		setgeneve_mode_clone),
841 
842 	DEF_CMD_ARG("geneveid",			setgeneve_vni_nl),
843 	DEF_CMD_ARG("genevelocal",		setgeneve_local_nl),
844 	DEF_CMD_ARG("geneveremote",		setgeneve_remote_nl),
845 	DEF_CMD_ARG("genevegroup",		setgeneve_group_nl),
846 	DEF_CMD_ARG("genevelocalport",		setgeneve_local_port_nl),
847 	DEF_CMD_ARG("geneveremoteport",		setgeneve_remote_port_nl),
848 	DEF_CMD_ARG2("geneveportrange",		setgeneve_port_range_nl),
849 	DEF_CMD_ARG("genevetimeout",		setgeneve_timeout_nl),
850 	DEF_CMD_ARG("genevemaxaddr",		setgeneve_maxaddr_nl),
851 	DEF_CMD_ARG("genevedev",		setgeneve_dev_nl),
852 	DEF_CMD_ARG("genevettl",		setgeneve_ttl_nl),
853 	DEF_CMD_ARG("genevedf",			setgeneve_df_nl),
854 	DEF_CMD("genevedscpinherit", 1,		setgeneve_inherit_dscp_nl),
855 	DEF_CMD("-genevedscpinherit", 0,	setgeneve_inherit_dscp_nl),
856 	DEF_CMD("genevelearn", 1,		setgeneve_learn_nl),
857 	DEF_CMD("-genevelearn", 0,		setgeneve_learn_nl),
858 	DEF_CMD("geneveflushall", 0,		setgeneve_flush_nl),
859 	DEF_CMD("geneveflush", 1,		setgeneve_flush_nl),
860 	DEF_CMD("geneveexternal", 1,		setgeneve_external_nl),
861 	DEF_CMD("-geneveexternal", 0,		setgeneve_external_nl),
862 
863 	DEF_CMD_SARG("genevehwcsum",	IFCAP2_GENEVE_HWCSUM_NAME,
864 	    setifcapnv),
865 	DEF_CMD_SARG("-genevehwcsum",	"-"IFCAP2_GENEVE_HWCSUM_NAME,
866 	    setifcapnv),
867 	DEF_CMD_SARG("genevehwtso",	IFCAP2_GENEVE_HWTSO_NAME,
868 	    setifcapnv),
869 	DEF_CMD_SARG("-genevehwtso",	"-"IFCAP2_GENEVE_HWTSO_NAME,
870 	    setifcapnv),
871 };
872 
873 static struct afswtch af_geneve = {
874 	.af_name		= "af_geneve",
875 	.af_af			= AF_UNSPEC,
876 	.af_other_status	= geneve_status_nl,
877 };
878 
879 static __constructor void
geneve_ctor(void)880 geneve_ctor(void)
881 {
882 	size_t i;
883 
884 	for (i = 0; i < nitems(geneve_cmds); i++)
885 		cmd_register(&geneve_cmds[i]);
886 	af_register(&af_geneve);
887 	clone_setdefcallback_prefix("geneve", geneve_create_nl);
888 	SNL_VERIFY_PARSERS(all_parsers);
889 }
890