1/*-
2 * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/ioctl.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_var.h>
43#include <net/if_vxlan.h>
44#include <net/route.h>
45#include <netinet/in.h>
46
47#include <ctype.h>
48#include <stdio.h>
49#include <string.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include <err.h>
53#include <errno.h>
54
55#include "ifconfig.h"
56
57static struct ifvxlanparam params = {
58	.vxlp_vni	= VXLAN_VNI_MAX,
59};
60
61static int
62get_val(const char *cp, u_long *valp)
63{
64	char *endptr;
65	u_long val;
66
67	errno = 0;
68	val = strtoul(cp, &endptr, 0);
69	if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
70		return (-1);
71
72	*valp = val;
73	return (0);
74}
75
76static int
77do_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
78{
79	struct ifdrv ifd;
80
81	bzero(&ifd, sizeof(ifd));
82
83	strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name));
84	ifd.ifd_cmd = op;
85	ifd.ifd_len = argsize;
86	ifd.ifd_data = arg;
87
88	return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
89}
90
91static int
92vxlan_exists(int sock)
93{
94	struct ifvxlancfg cfg;
95
96	bzero(&cfg, sizeof(cfg));
97
98	return (do_cmd(sock, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1);
99}
100
101static void
102vxlan_status(int s)
103{
104	struct ifvxlancfg cfg;
105	char src[NI_MAXHOST], dst[NI_MAXHOST];
106	char srcport[NI_MAXSERV], dstport[NI_MAXSERV];
107	struct sockaddr *lsa, *rsa;
108	int vni, mc, ipv6;
109
110	bzero(&cfg, sizeof(cfg));
111
112	if (do_cmd(s, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0)
113		return;
114
115	vni = cfg.vxlc_vni;
116	lsa = &cfg.vxlc_local_sa.sa;
117	rsa = &cfg.vxlc_remote_sa.sa;
118	ipv6 = rsa->sa_family == AF_INET6;
119
120	/* Just report nothing if the network identity isn't set yet. */
121	if (vni >= VXLAN_VNI_MAX)
122		return;
123
124	if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src),
125	    srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
126		src[0] = srcport[0] = '\0';
127	if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst),
128	    dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
129		dst[0] = dstport[0] = '\0';
130
131	if (!ipv6) {
132		struct sockaddr_in *sin = (struct sockaddr_in *)rsa;
133		mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
134	} else {
135		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rsa;
136		mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
137	}
138
139	printf("\tvxlan vni %d", vni);
140	printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
141	    srcport);
142	printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "",
143	    dst, ipv6 ? "]" : "", dstport);
144
145	if (verbose) {
146		printf("\n\t\tconfig: ");
147		printf("%slearning portrange %d-%d ttl %d",
148		    cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min,
149		    cfg.vxlc_port_max, cfg.vxlc_ttl);
150		printf("\n\t\tftable: ");
151		printf("cnt %d max %d timeout %d",
152		    cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max,
153		    cfg.vxlc_ftable_timeout);
154	}
155
156	putchar('\n');
157}
158
159#define _LOCAL_ADDR46 \
160    (VXLAN_PARAM_WITH_LOCAL_ADDR4 | VXLAN_PARAM_WITH_LOCAL_ADDR6)
161#define _REMOTE_ADDR46 \
162    (VXLAN_PARAM_WITH_REMOTE_ADDR4 | VXLAN_PARAM_WITH_REMOTE_ADDR6)
163
164static void
165vxlan_check_params(void)
166{
167
168	if ((params.vxlp_with & _LOCAL_ADDR46) == _LOCAL_ADDR46)
169		errx(1, "cannot specify both local IPv4 and IPv6 addresses");
170	if ((params.vxlp_with & _REMOTE_ADDR46) == _REMOTE_ADDR46)
171		errx(1, "cannot specify both remote IPv4 and IPv6 addresses");
172	if ((params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4 &&
173	     params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) ||
174	    (params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6 &&
175	     params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4))
176		errx(1, "cannot mix IPv4 and IPv6 addresses");
177}
178
179#undef _LOCAL_ADDR46
180#undef _REMOTE_ADDR46
181
182static void
183vxlan_cb(int s, void *arg)
184{
185
186}
187
188static void
189vxlan_create(int s, struct ifreq *ifr)
190{
191
192	vxlan_check_params();
193
194	ifr->ifr_data = (caddr_t) &params;
195	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
196		err(1, "SIOCIFCREATE2");
197}
198
199static
200DECL_CMD_FUNC(setvxlan_vni, arg, d)
201{
202	struct ifvxlancmd cmd;
203	u_long val;
204
205	if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX)
206		errx(1, "invalid network identifier: %s", arg);
207
208	if (!vxlan_exists(s)) {
209		params.vxlp_with |= VXLAN_PARAM_WITH_VNI;
210		params.vxlp_vni = val;
211		return;
212	}
213
214	bzero(&cmd, sizeof(cmd));
215	cmd.vxlcmd_vni = val;
216
217	if (do_cmd(s, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0)
218		err(1, "VXLAN_CMD_SET_VNI");
219}
220
221static
222DECL_CMD_FUNC(setvxlan_local, addr, d)
223{
224	struct ifvxlancmd cmd;
225	struct addrinfo *ai;
226	struct sockaddr *sa;
227	int error;
228
229	bzero(&cmd, sizeof(cmd));
230
231	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
232		errx(1, "error in parsing local address string: %s",
233		    gai_strerror(error));
234
235	sa = ai->ai_addr;
236
237	switch (ai->ai_family) {
238#ifdef INET
239	case AF_INET: {
240		struct in_addr addr = ((struct sockaddr_in *) sa)->sin_addr;
241
242		if (IN_MULTICAST(ntohl(addr.s_addr)))
243			errx(1, "local address cannot be multicast");
244
245		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
246		cmd.vxlcmd_sa.in4.sin_addr = addr;
247		break;
248	}
249#endif
250#ifdef INET6
251	case AF_INET6: {
252		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
253
254		if (IN6_IS_ADDR_MULTICAST(addr))
255			errx(1, "local address cannot be multicast");
256
257		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
258		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
259		break;
260	}
261#endif
262	default:
263		errx(1, "local address %s not supported", addr);
264	}
265
266	freeaddrinfo(ai);
267
268	if (!vxlan_exists(s)) {
269		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
270			params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR4;
271			params.vxlp_local_in4 = cmd.vxlcmd_sa.in4.sin_addr;
272		} else {
273			params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6;
274			params.vxlp_local_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
275		}
276		return;
277	}
278
279	if (do_cmd(s, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0)
280		err(1, "VXLAN_CMD_SET_LOCAL_ADDR");
281}
282
283static
284DECL_CMD_FUNC(setvxlan_remote, addr, d)
285{
286	struct ifvxlancmd cmd;
287	struct addrinfo *ai;
288	struct sockaddr *sa;
289	int error;
290
291	bzero(&cmd, sizeof(cmd));
292
293	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
294		errx(1, "error in parsing remote address string: %s",
295		    gai_strerror(error));
296
297	sa = ai->ai_addr;
298
299	switch (ai->ai_family) {
300#ifdef INET
301	case AF_INET: {
302		struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
303
304		if (IN_MULTICAST(ntohl(addr.s_addr)))
305			errx(1, "remote address cannot be multicast");
306
307		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
308		cmd.vxlcmd_sa.in4.sin_addr = addr;
309		break;
310	}
311#endif
312#ifdef INET6
313	case AF_INET6: {
314		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
315
316		if (IN6_IS_ADDR_MULTICAST(addr))
317			errx(1, "remote address cannot be multicast");
318
319		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
320		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
321		break;
322	}
323#endif
324	default:
325		errx(1, "remote address %s not supported", addr);
326	}
327
328	freeaddrinfo(ai);
329
330	if (!vxlan_exists(s)) {
331		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
332			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
333			params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr;
334		} else {
335			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
336			params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
337		}
338		return;
339	}
340
341	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
342		err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
343}
344
345static
346DECL_CMD_FUNC(setvxlan_group, addr, d)
347{
348	struct ifvxlancmd cmd;
349	struct addrinfo *ai;
350	struct sockaddr *sa;
351	int error;
352
353	bzero(&cmd, sizeof(cmd));
354
355	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
356		errx(1, "error in parsing group address string: %s",
357		    gai_strerror(error));
358
359	sa = ai->ai_addr;
360
361	switch (ai->ai_family) {
362#ifdef INET
363	case AF_INET: {
364		struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
365
366		if (!IN_MULTICAST(ntohl(addr.s_addr)))
367			errx(1, "group address must be multicast");
368
369		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
370		cmd.vxlcmd_sa.in4.sin_addr = addr;
371		break;
372	}
373#endif
374#ifdef INET6
375	case AF_INET6: {
376		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
377
378		if (!IN6_IS_ADDR_MULTICAST(addr))
379			errx(1, "group address must be multicast");
380
381		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
382		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
383		break;
384	}
385#endif
386	default:
387		errx(1, "group address %s not supported", addr);
388	}
389
390	freeaddrinfo(ai);
391
392	if (!vxlan_exists(s)) {
393		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
394			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
395			params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr;
396		} else {
397			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
398			params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
399		}
400		return;
401	}
402
403	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
404		err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
405}
406
407static
408DECL_CMD_FUNC(setvxlan_local_port, arg, d)
409{
410	struct ifvxlancmd cmd;
411	u_long val;
412
413	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
414		errx(1, "invalid local port: %s", arg);
415
416	if (!vxlan_exists(s)) {
417		params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT;
418		params.vxlp_local_port = val;
419		return;
420	}
421
422	bzero(&cmd, sizeof(cmd));
423	cmd.vxlcmd_port = val;
424
425	if (do_cmd(s, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0)
426		err(1, "VXLAN_CMD_SET_LOCAL_PORT");
427}
428
429static
430DECL_CMD_FUNC(setvxlan_remote_port, arg, d)
431{
432	struct ifvxlancmd cmd;
433	u_long val;
434
435	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
436		errx(1, "invalid remote port: %s", arg);
437
438	if (!vxlan_exists(s)) {
439		params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT;
440		params.vxlp_remote_port = val;
441		return;
442	}
443
444	bzero(&cmd, sizeof(cmd));
445	cmd.vxlcmd_port = val;
446
447	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0)
448		err(1, "VXLAN_CMD_SET_REMOTE_PORT");
449}
450
451static
452DECL_CMD_FUNC2(setvxlan_port_range, arg1, arg2)
453{
454	struct ifvxlancmd cmd;
455	u_long min, max;
456
457	if (get_val(arg1, &min) < 0 || min >= UINT16_MAX)
458		errx(1, "invalid port range minimum: %s", arg1);
459	if (get_val(arg2, &max) < 0 || max >= UINT16_MAX)
460		errx(1, "invalid port range maximum: %s", arg2);
461	if (max < min)
462		errx(1, "invalid port range");
463
464	if (!vxlan_exists(s)) {
465		params.vxlp_with |= VXLAN_PARAM_WITH_PORT_RANGE;
466		params.vxlp_min_port = min;
467		params.vxlp_max_port = max;
468		return;
469	}
470
471	bzero(&cmd, sizeof(cmd));
472	cmd.vxlcmd_port_min = min;
473	cmd.vxlcmd_port_max = max;
474
475	if (do_cmd(s, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0)
476		err(1, "VXLAN_CMD_SET_PORT_RANGE");
477}
478
479static
480DECL_CMD_FUNC(setvxlan_timeout, arg, d)
481{
482	struct ifvxlancmd cmd;
483	u_long val;
484
485	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
486		errx(1, "invalid timeout value: %s", arg);
487
488	if (!vxlan_exists(s)) {
489		params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT;
490		params.vxlp_ftable_timeout = val & 0xFFFFFFFF;
491		return;
492	}
493
494	bzero(&cmd, sizeof(cmd));
495	cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF;
496
497	if (do_cmd(s, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0)
498		err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT");
499}
500
501static
502DECL_CMD_FUNC(setvxlan_maxaddr, arg, d)
503{
504	struct ifvxlancmd cmd;
505	u_long val;
506
507	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
508		errx(1, "invalid maxaddr value: %s",  arg);
509
510	if (!vxlan_exists(s)) {
511		params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX;
512		params.vxlp_ftable_max = val & 0xFFFFFFFF;
513		return;
514	}
515
516	bzero(&cmd, sizeof(cmd));
517	cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF;
518
519	if (do_cmd(s, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0)
520		err(1, "VXLAN_CMD_SET_FTABLE_MAX");
521}
522
523static
524DECL_CMD_FUNC(setvxlan_dev, arg, d)
525{
526	struct ifvxlancmd cmd;
527
528	if (!vxlan_exists(s)) {
529		params.vxlp_with |= VXLAN_PARAM_WITH_MULTICAST_IF;
530		strlcpy(params.vxlp_mc_ifname, arg,
531		    sizeof(params.vxlp_mc_ifname));
532		return;
533	}
534
535	bzero(&cmd, sizeof(cmd));
536	strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname));
537
538	if (do_cmd(s, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0)
539		err(1, "VXLAN_CMD_SET_MULTICAST_IF");
540}
541
542static
543DECL_CMD_FUNC(setvxlan_ttl, arg, d)
544{
545	struct ifvxlancmd cmd;
546	u_long val;
547
548	if (get_val(arg, &val) < 0 || val > 256)
549		errx(1, "invalid TTL value: %s", arg);
550
551	if (!vxlan_exists(s)) {
552		params.vxlp_with |= VXLAN_PARAM_WITH_TTL;
553		params.vxlp_ttl = val;
554		return;
555	}
556
557	bzero(&cmd, sizeof(cmd));
558	cmd.vxlcmd_ttl = val;
559
560	if (do_cmd(s, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0)
561		err(1, "VXLAN_CMD_SET_TTL");
562}
563
564static
565DECL_CMD_FUNC(setvxlan_learn, arg, d)
566{
567	struct ifvxlancmd cmd;
568
569	if (!vxlan_exists(s)) {
570		params.vxlp_with |= VXLAN_PARAM_WITH_LEARN;
571		params.vxlp_learn = d;
572		return;
573	}
574
575	bzero(&cmd, sizeof(cmd));
576	if (d != 0)
577		cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN;
578
579	if (do_cmd(s, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0)
580		err(1, "VXLAN_CMD_SET_LEARN");
581}
582
583static void
584setvxlan_flush(const char *val, int d, int s, const struct afswtch *afp)
585{
586	struct ifvxlancmd cmd;
587
588	bzero(&cmd, sizeof(cmd));
589	if (d != 0)
590		cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL;
591
592	if (do_cmd(s, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0)
593		err(1, "VXLAN_CMD_FLUSH");
594}
595
596static struct cmd vxlan_cmds[] = {
597
598	DEF_CLONE_CMD_ARG("vxlanid",		setvxlan_vni),
599	DEF_CLONE_CMD_ARG("vxlanlocal",		setvxlan_local),
600	DEF_CLONE_CMD_ARG("vxlanremote",	setvxlan_remote),
601	DEF_CLONE_CMD_ARG("vxlangroup",		setvxlan_group),
602	DEF_CLONE_CMD_ARG("vxlanlocalport",	setvxlan_local_port),
603	DEF_CLONE_CMD_ARG("vxlanremoteport",	setvxlan_remote_port),
604	DEF_CLONE_CMD_ARG2("vxlanportrange",	setvxlan_port_range),
605	DEF_CLONE_CMD_ARG("vxlantimeout",	setvxlan_timeout),
606	DEF_CLONE_CMD_ARG("vxlanmaxaddr",	setvxlan_maxaddr),
607	DEF_CLONE_CMD_ARG("vxlandev",		setvxlan_dev),
608	DEF_CLONE_CMD_ARG("vxlanttl",		setvxlan_ttl),
609	DEF_CLONE_CMD("vxlanlearn", 1,		setvxlan_learn),
610	DEF_CLONE_CMD("-vxlanlearn", 0,		setvxlan_learn),
611
612	DEF_CMD_ARG("vxlanvni",			setvxlan_vni),
613	DEF_CMD_ARG("vxlanlocal",		setvxlan_local),
614	DEF_CMD_ARG("vxlanremote",		setvxlan_remote),
615	DEF_CMD_ARG("vxlangroup",		setvxlan_group),
616	DEF_CMD_ARG("vxlanlocalport",		setvxlan_local_port),
617	DEF_CMD_ARG("vxlanremoteport",		setvxlan_remote_port),
618	DEF_CMD_ARG2("vxlanportrange",		setvxlan_port_range),
619	DEF_CMD_ARG("vxlantimeout",		setvxlan_timeout),
620	DEF_CMD_ARG("vxlanmaxaddr",		setvxlan_maxaddr),
621	DEF_CMD_ARG("vxlandev",			setvxlan_dev),
622	DEF_CMD_ARG("vxlanttl",			setvxlan_ttl),
623	DEF_CMD("vxlanlearn", 1,		setvxlan_learn),
624	DEF_CMD("-vxlanlearn", 0,		setvxlan_learn),
625
626	DEF_CMD("vxlanflush", 0,		setvxlan_flush),
627	DEF_CMD("vxlanflushall", 1,		setvxlan_flush),
628};
629
630static struct afswtch af_vxlan = {
631	.af_name		= "af_vxlan",
632	.af_af			= AF_UNSPEC,
633	.af_other_status	= vxlan_status,
634};
635
636static __constructor void
637vxlan_ctor(void)
638{
639	size_t i;
640
641	for (i = 0; i < nitems(vxlan_cmds); i++)
642		cmd_register(&vxlan_cmds[i]);
643	af_register(&af_vxlan);
644	callback_register(vxlan_cb, NULL);
645	clone_setdefcallback("vxlan", vxlan_create);
646}
647