1273331Sbryanv/*-
2273331Sbryanv * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
3273331Sbryanv * All rights reserved.
4273331Sbryanv *
5273331Sbryanv * Redistribution and use in source and binary forms, with or without
6273331Sbryanv * modification, are permitted provided that the following conditions
7273331Sbryanv * are met:
8273331Sbryanv * 1. Redistributions of source code must retain the above copyright
9273331Sbryanv *    notice unmodified, this list of conditions, and the following
10273331Sbryanv *    disclaimer.
11273331Sbryanv * 2. Redistributions in binary form must reproduce the above copyright
12273331Sbryanv *    notice, this list of conditions and the following disclaimer in the
13273331Sbryanv *    documentation and/or other materials provided with the distribution.
14273331Sbryanv *
15273331Sbryanv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16273331Sbryanv * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17273331Sbryanv * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18273331Sbryanv * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19273331Sbryanv * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20273331Sbryanv * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21273331Sbryanv * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22273331Sbryanv * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23273331Sbryanv * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24273331Sbryanv * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25273331Sbryanv */
26273331Sbryanv
27273331Sbryanv#include <sys/cdefs.h>
28273331Sbryanv__FBSDID("$FreeBSD$");
29273331Sbryanv
30273331Sbryanv#include <sys/param.h>
31273331Sbryanv#include <sys/ioctl.h>
32273331Sbryanv#include <sys/socket.h>
33273331Sbryanv#include <sys/sockio.h>
34273331Sbryanv
35273331Sbryanv#include <stdlib.h>
36273331Sbryanv#include <stdint.h>
37273331Sbryanv#include <unistd.h>
38273331Sbryanv#include <netdb.h>
39273331Sbryanv
40273331Sbryanv#include <net/ethernet.h>
41273331Sbryanv#include <net/if.h>
42273331Sbryanv#include <net/if_var.h>
43273331Sbryanv#include <net/if_vxlan.h>
44273331Sbryanv#include <net/route.h>
45273331Sbryanv#include <netinet/in.h>
46273331Sbryanv
47273331Sbryanv#include <ctype.h>
48273331Sbryanv#include <stdio.h>
49273331Sbryanv#include <string.h>
50273331Sbryanv#include <stdlib.h>
51273331Sbryanv#include <unistd.h>
52273331Sbryanv#include <err.h>
53273331Sbryanv#include <errno.h>
54273331Sbryanv
55273331Sbryanv#include "ifconfig.h"
56273331Sbryanv
57273331Sbryanvstatic struct ifvxlanparam params = {
58273331Sbryanv	.vxlp_vni	= VXLAN_VNI_MAX,
59273331Sbryanv};
60273331Sbryanv
61273331Sbryanvstatic int
62273331Sbryanvget_val(const char *cp, u_long *valp)
63273331Sbryanv{
64273331Sbryanv	char *endptr;
65273331Sbryanv	u_long val;
66273331Sbryanv
67273331Sbryanv	errno = 0;
68273331Sbryanv	val = strtoul(cp, &endptr, 0);
69273331Sbryanv	if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
70273331Sbryanv		return (-1);
71273331Sbryanv
72273331Sbryanv	*valp = val;
73273331Sbryanv	return (0);
74273331Sbryanv}
75273331Sbryanv
76273331Sbryanvstatic int
77273331Sbryanvdo_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
78273331Sbryanv{
79273331Sbryanv	struct ifdrv ifd;
80273331Sbryanv
81273331Sbryanv	bzero(&ifd, sizeof(ifd));
82273331Sbryanv
83273331Sbryanv	strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name));
84273331Sbryanv	ifd.ifd_cmd = op;
85273331Sbryanv	ifd.ifd_len = argsize;
86273331Sbryanv	ifd.ifd_data = arg;
87273331Sbryanv
88273331Sbryanv	return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
89273331Sbryanv}
90273331Sbryanv
91273331Sbryanvstatic int
92273331Sbryanvvxlan_exists(int sock)
93273331Sbryanv{
94273331Sbryanv	struct ifvxlancfg cfg;
95273331Sbryanv
96273331Sbryanv	bzero(&cfg, sizeof(cfg));
97273331Sbryanv
98273331Sbryanv	return (do_cmd(sock, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1);
99273331Sbryanv}
100273331Sbryanv
101273331Sbryanvstatic void
102273331Sbryanvvxlan_status(int s)
103273331Sbryanv{
104273331Sbryanv	struct ifvxlancfg cfg;
105273331Sbryanv	char src[NI_MAXHOST], dst[NI_MAXHOST];
106273331Sbryanv	char srcport[NI_MAXSERV], dstport[NI_MAXSERV];
107273331Sbryanv	struct sockaddr *lsa, *rsa;
108273331Sbryanv	int vni, mc, ipv6;
109273331Sbryanv
110273331Sbryanv	bzero(&cfg, sizeof(cfg));
111273331Sbryanv
112273331Sbryanv	if (do_cmd(s, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0)
113273331Sbryanv		return;
114273331Sbryanv
115273331Sbryanv	vni = cfg.vxlc_vni;
116273331Sbryanv	lsa = &cfg.vxlc_local_sa.sa;
117273331Sbryanv	rsa = &cfg.vxlc_remote_sa.sa;
118273331Sbryanv	ipv6 = rsa->sa_family == AF_INET6;
119273331Sbryanv
120273331Sbryanv	/* Just report nothing if the network identity isn't set yet. */
121273331Sbryanv	if (vni >= VXLAN_VNI_MAX)
122273331Sbryanv		return;
123273331Sbryanv
124273331Sbryanv	if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src),
125273331Sbryanv	    srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
126273331Sbryanv		src[0] = srcport[0] = '\0';
127273331Sbryanv	if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst),
128273331Sbryanv	    dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
129273331Sbryanv		dst[0] = dstport[0] = '\0';
130273331Sbryanv
131273331Sbryanv	if (!ipv6) {
132273331Sbryanv		struct sockaddr_in *sin = (struct sockaddr_in *)rsa;
133273331Sbryanv		mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
134273331Sbryanv	} else {
135273331Sbryanv		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rsa;
136273331Sbryanv		mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
137273331Sbryanv	}
138273331Sbryanv
139273331Sbryanv	printf("\tvxlan vni %d", vni);
140273331Sbryanv	printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
141273331Sbryanv	    srcport);
142273331Sbryanv	printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "",
143273331Sbryanv	    dst, ipv6 ? "]" : "", dstport);
144273331Sbryanv
145273331Sbryanv	if (verbose) {
146273331Sbryanv		printf("\n\t\tconfig: ");
147273331Sbryanv		printf("%slearning portrange %d-%d ttl %d",
148273331Sbryanv		    cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min,
149273331Sbryanv		    cfg.vxlc_port_max, cfg.vxlc_ttl);
150273331Sbryanv		printf("\n\t\tftable: ");
151273331Sbryanv		printf("cnt %d max %d timeout %d",
152273331Sbryanv		    cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max,
153273331Sbryanv		    cfg.vxlc_ftable_timeout);
154273331Sbryanv	}
155273331Sbryanv
156273331Sbryanv	putchar('\n');
157273331Sbryanv}
158273331Sbryanv
159273331Sbryanv#define _LOCAL_ADDR46 \
160273331Sbryanv    (VXLAN_PARAM_WITH_LOCAL_ADDR4 | VXLAN_PARAM_WITH_LOCAL_ADDR6)
161273331Sbryanv#define _REMOTE_ADDR46 \
162273331Sbryanv    (VXLAN_PARAM_WITH_REMOTE_ADDR4 | VXLAN_PARAM_WITH_REMOTE_ADDR6)
163273331Sbryanv
164273331Sbryanvstatic void
165273331Sbryanvvxlan_check_params(void)
166273331Sbryanv{
167273331Sbryanv
168273331Sbryanv	if ((params.vxlp_with & _LOCAL_ADDR46) == _LOCAL_ADDR46)
169273331Sbryanv		errx(1, "cannot specify both local IPv4 and IPv6 addresses");
170273331Sbryanv	if ((params.vxlp_with & _REMOTE_ADDR46) == _REMOTE_ADDR46)
171273331Sbryanv		errx(1, "cannot specify both remote IPv4 and IPv6 addresses");
172273331Sbryanv	if ((params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4 &&
173273331Sbryanv	     params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) ||
174273331Sbryanv	    (params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6 &&
175273331Sbryanv	     params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4))
176273331Sbryanv		errx(1, "cannot mix IPv4 and IPv6 addresses");
177273331Sbryanv}
178273331Sbryanv
179273331Sbryanv#undef _LOCAL_ADDR46
180273331Sbryanv#undef _REMOTE_ADDR46
181273331Sbryanv
182273331Sbryanvstatic void
183273331Sbryanvvxlan_cb(int s, void *arg)
184273331Sbryanv{
185273331Sbryanv
186273331Sbryanv}
187273331Sbryanv
188273331Sbryanvstatic void
189273331Sbryanvvxlan_create(int s, struct ifreq *ifr)
190273331Sbryanv{
191273331Sbryanv
192273331Sbryanv	vxlan_check_params();
193273331Sbryanv
194273331Sbryanv	ifr->ifr_data = (caddr_t) &params;
195273331Sbryanv	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
196273331Sbryanv		err(1, "SIOCIFCREATE2");
197273331Sbryanv}
198273331Sbryanv
199273331Sbryanvstatic
200273331SbryanvDECL_CMD_FUNC(setvxlan_vni, arg, d)
201273331Sbryanv{
202273331Sbryanv	struct ifvxlancmd cmd;
203273331Sbryanv	u_long val;
204273331Sbryanv
205273331Sbryanv	if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX)
206273331Sbryanv		errx(1, "invalid network identifier: %s", arg);
207273331Sbryanv
208273331Sbryanv	if (!vxlan_exists(s)) {
209273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_VNI;
210273331Sbryanv		params.vxlp_vni = val;
211273331Sbryanv		return;
212273331Sbryanv	}
213273331Sbryanv
214273331Sbryanv	bzero(&cmd, sizeof(cmd));
215273331Sbryanv	cmd.vxlcmd_vni = val;
216273331Sbryanv
217273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0)
218273331Sbryanv		err(1, "VXLAN_CMD_SET_VNI");
219273331Sbryanv}
220273331Sbryanv
221273331Sbryanvstatic
222273331SbryanvDECL_CMD_FUNC(setvxlan_local, addr, d)
223273331Sbryanv{
224273331Sbryanv	struct ifvxlancmd cmd;
225273331Sbryanv	struct addrinfo *ai;
226273331Sbryanv	struct sockaddr *sa;
227273331Sbryanv	int error;
228273331Sbryanv
229273331Sbryanv	bzero(&cmd, sizeof(cmd));
230273331Sbryanv
231273331Sbryanv	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
232273331Sbryanv		errx(1, "error in parsing local address string: %s",
233273331Sbryanv		    gai_strerror(error));
234273331Sbryanv
235273331Sbryanv	sa = ai->ai_addr;
236273331Sbryanv
237273331Sbryanv	switch (ai->ai_family) {
238273331Sbryanv#ifdef INET
239273331Sbryanv	case AF_INET: {
240273331Sbryanv		struct in_addr addr = ((struct sockaddr_in *) sa)->sin_addr;
241273331Sbryanv
242273331Sbryanv		if (IN_MULTICAST(ntohl(addr.s_addr)))
243273331Sbryanv			errx(1, "local address cannot be multicast");
244273331Sbryanv
245273331Sbryanv		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
246273331Sbryanv		cmd.vxlcmd_sa.in4.sin_addr = addr;
247273331Sbryanv		break;
248273331Sbryanv	}
249273331Sbryanv#endif
250273331Sbryanv#ifdef INET6
251273331Sbryanv	case AF_INET6: {
252273331Sbryanv		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
253273331Sbryanv
254273331Sbryanv		if (IN6_IS_ADDR_MULTICAST(addr))
255273331Sbryanv			errx(1, "local address cannot be multicast");
256273331Sbryanv
257273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
258273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
259273331Sbryanv		break;
260273331Sbryanv	}
261273331Sbryanv#endif
262273331Sbryanv	default:
263273331Sbryanv		errx(1, "local address %s not supported", addr);
264273331Sbryanv	}
265273331Sbryanv
266273331Sbryanv	freeaddrinfo(ai);
267273331Sbryanv
268273331Sbryanv	if (!vxlan_exists(s)) {
269273331Sbryanv		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
270273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR4;
271273331Sbryanv			params.vxlp_local_in4 = cmd.vxlcmd_sa.in4.sin_addr;
272273331Sbryanv		} else {
273273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6;
274273331Sbryanv			params.vxlp_local_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
275273331Sbryanv		}
276273331Sbryanv		return;
277273331Sbryanv	}
278273331Sbryanv
279273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0)
280273331Sbryanv		err(1, "VXLAN_CMD_SET_LOCAL_ADDR");
281273331Sbryanv}
282273331Sbryanv
283273331Sbryanvstatic
284273331SbryanvDECL_CMD_FUNC(setvxlan_remote, addr, d)
285273331Sbryanv{
286273331Sbryanv	struct ifvxlancmd cmd;
287273331Sbryanv	struct addrinfo *ai;
288273331Sbryanv	struct sockaddr *sa;
289273331Sbryanv	int error;
290273331Sbryanv
291273331Sbryanv	bzero(&cmd, sizeof(cmd));
292273331Sbryanv
293273331Sbryanv	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
294273331Sbryanv		errx(1, "error in parsing remote address string: %s",
295273331Sbryanv		    gai_strerror(error));
296273331Sbryanv
297273331Sbryanv	sa = ai->ai_addr;
298273331Sbryanv
299273331Sbryanv	switch (ai->ai_family) {
300273331Sbryanv#ifdef INET
301273331Sbryanv	case AF_INET: {
302273331Sbryanv		struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
303273331Sbryanv
304273331Sbryanv		if (IN_MULTICAST(ntohl(addr.s_addr)))
305273331Sbryanv			errx(1, "remote address cannot be multicast");
306273331Sbryanv
307273331Sbryanv		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
308273331Sbryanv		cmd.vxlcmd_sa.in4.sin_addr = addr;
309273331Sbryanv		break;
310273331Sbryanv	}
311273331Sbryanv#endif
312273331Sbryanv#ifdef INET6
313273331Sbryanv	case AF_INET6: {
314273331Sbryanv		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
315273331Sbryanv
316273331Sbryanv		if (IN6_IS_ADDR_MULTICAST(addr))
317273331Sbryanv			errx(1, "remote address cannot be multicast");
318273331Sbryanv
319273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
320273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
321273331Sbryanv		break;
322273331Sbryanv	}
323273331Sbryanv#endif
324273331Sbryanv	default:
325273331Sbryanv		errx(1, "remote address %s not supported", addr);
326273331Sbryanv	}
327273331Sbryanv
328273331Sbryanv	freeaddrinfo(ai);
329273331Sbryanv
330273331Sbryanv	if (!vxlan_exists(s)) {
331273331Sbryanv		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
332273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
333273331Sbryanv			params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr;
334273331Sbryanv		} else {
335273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
336273331Sbryanv			params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
337273331Sbryanv		}
338273331Sbryanv		return;
339273331Sbryanv	}
340273331Sbryanv
341273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
342273331Sbryanv		err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
343273331Sbryanv}
344273331Sbryanv
345273331Sbryanvstatic
346273331SbryanvDECL_CMD_FUNC(setvxlan_group, addr, d)
347273331Sbryanv{
348273331Sbryanv	struct ifvxlancmd cmd;
349273331Sbryanv	struct addrinfo *ai;
350273331Sbryanv	struct sockaddr *sa;
351273331Sbryanv	int error;
352273331Sbryanv
353273331Sbryanv	bzero(&cmd, sizeof(cmd));
354273331Sbryanv
355273331Sbryanv	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
356273331Sbryanv		errx(1, "error in parsing group address string: %s",
357273331Sbryanv		    gai_strerror(error));
358273331Sbryanv
359273331Sbryanv	sa = ai->ai_addr;
360273331Sbryanv
361273331Sbryanv	switch (ai->ai_family) {
362273331Sbryanv#ifdef INET
363273331Sbryanv	case AF_INET: {
364273331Sbryanv		struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
365273331Sbryanv
366273331Sbryanv		if (!IN_MULTICAST(ntohl(addr.s_addr)))
367273331Sbryanv			errx(1, "group address must be multicast");
368273331Sbryanv
369273331Sbryanv		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
370273331Sbryanv		cmd.vxlcmd_sa.in4.sin_addr = addr;
371273331Sbryanv		break;
372273331Sbryanv	}
373273331Sbryanv#endif
374273331Sbryanv#ifdef INET6
375273331Sbryanv	case AF_INET6: {
376273331Sbryanv		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
377273331Sbryanv
378273331Sbryanv		if (!IN6_IS_ADDR_MULTICAST(addr))
379273331Sbryanv			errx(1, "group address must be multicast");
380273331Sbryanv
381273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
382273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
383273331Sbryanv		break;
384273331Sbryanv	}
385273331Sbryanv#endif
386273331Sbryanv	default:
387273331Sbryanv		errx(1, "group address %s not supported", addr);
388273331Sbryanv	}
389273331Sbryanv
390273331Sbryanv	freeaddrinfo(ai);
391273331Sbryanv
392273331Sbryanv	if (!vxlan_exists(s)) {
393273331Sbryanv		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
394273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
395273331Sbryanv			params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr;
396273331Sbryanv		} else {
397273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
398273331Sbryanv			params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
399273331Sbryanv		}
400273331Sbryanv		return;
401273331Sbryanv	}
402273331Sbryanv
403273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
404273331Sbryanv		err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
405273331Sbryanv}
406273331Sbryanv
407273331Sbryanvstatic
408273331SbryanvDECL_CMD_FUNC(setvxlan_local_port, arg, d)
409273331Sbryanv{
410273331Sbryanv	struct ifvxlancmd cmd;
411273331Sbryanv	u_long val;
412273331Sbryanv
413273331Sbryanv	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
414273331Sbryanv		errx(1, "invalid local port: %s", arg);
415273331Sbryanv
416273331Sbryanv	if (!vxlan_exists(s)) {
417273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT;
418273331Sbryanv		params.vxlp_local_port = val;
419273331Sbryanv		return;
420273331Sbryanv	}
421273331Sbryanv
422273331Sbryanv	bzero(&cmd, sizeof(cmd));
423273331Sbryanv	cmd.vxlcmd_port = val;
424273331Sbryanv
425273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0)
426273331Sbryanv		err(1, "VXLAN_CMD_SET_LOCAL_PORT");
427273331Sbryanv}
428273331Sbryanv
429273331Sbryanvstatic
430273331SbryanvDECL_CMD_FUNC(setvxlan_remote_port, arg, d)
431273331Sbryanv{
432273331Sbryanv	struct ifvxlancmd cmd;
433273331Sbryanv	u_long val;
434273331Sbryanv
435273331Sbryanv	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
436273331Sbryanv		errx(1, "invalid remote port: %s", arg);
437273331Sbryanv
438273331Sbryanv	if (!vxlan_exists(s)) {
439273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT;
440273331Sbryanv		params.vxlp_remote_port = val;
441273331Sbryanv		return;
442273331Sbryanv	}
443273331Sbryanv
444273331Sbryanv	bzero(&cmd, sizeof(cmd));
445273331Sbryanv	cmd.vxlcmd_port = val;
446273331Sbryanv
447273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0)
448273331Sbryanv		err(1, "VXLAN_CMD_SET_REMOTE_PORT");
449273331Sbryanv}
450273331Sbryanv
451273331Sbryanvstatic
452273331SbryanvDECL_CMD_FUNC2(setvxlan_port_range, arg1, arg2)
453273331Sbryanv{
454273331Sbryanv	struct ifvxlancmd cmd;
455273331Sbryanv	u_long min, max;
456273331Sbryanv
457273331Sbryanv	if (get_val(arg1, &min) < 0 || min >= UINT16_MAX)
458273331Sbryanv		errx(1, "invalid port range minimum: %s", arg1);
459273331Sbryanv	if (get_val(arg2, &max) < 0 || max >= UINT16_MAX)
460273331Sbryanv		errx(1, "invalid port range maximum: %s", arg2);
461273331Sbryanv	if (max < min)
462273331Sbryanv		errx(1, "invalid port range");
463273331Sbryanv
464273331Sbryanv	if (!vxlan_exists(s)) {
465273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_PORT_RANGE;
466273331Sbryanv		params.vxlp_min_port = min;
467273331Sbryanv		params.vxlp_max_port = max;
468273331Sbryanv		return;
469273331Sbryanv	}
470273331Sbryanv
471273331Sbryanv	bzero(&cmd, sizeof(cmd));
472273331Sbryanv	cmd.vxlcmd_port_min = min;
473273331Sbryanv	cmd.vxlcmd_port_max = max;
474273331Sbryanv
475273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0)
476273331Sbryanv		err(1, "VXLAN_CMD_SET_PORT_RANGE");
477273331Sbryanv}
478273331Sbryanv
479273331Sbryanvstatic
480273331SbryanvDECL_CMD_FUNC(setvxlan_timeout, arg, d)
481273331Sbryanv{
482273331Sbryanv	struct ifvxlancmd cmd;
483273331Sbryanv	u_long val;
484273331Sbryanv
485273331Sbryanv	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
486273331Sbryanv		errx(1, "invalid timeout value: %s", arg);
487273331Sbryanv
488273331Sbryanv	if (!vxlan_exists(s)) {
489273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT;
490273331Sbryanv		params.vxlp_ftable_timeout = val & 0xFFFFFFFF;
491273331Sbryanv		return;
492273331Sbryanv	}
493273331Sbryanv
494273331Sbryanv	bzero(&cmd, sizeof(cmd));
495273331Sbryanv	cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF;
496273331Sbryanv
497273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0)
498273331Sbryanv		err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT");
499273331Sbryanv}
500273331Sbryanv
501273331Sbryanvstatic
502273331SbryanvDECL_CMD_FUNC(setvxlan_maxaddr, arg, d)
503273331Sbryanv{
504273331Sbryanv	struct ifvxlancmd cmd;
505273331Sbryanv	u_long val;
506273331Sbryanv
507273331Sbryanv	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
508273331Sbryanv		errx(1, "invalid maxaddr value: %s",  arg);
509273331Sbryanv
510273331Sbryanv	if (!vxlan_exists(s)) {
511273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX;
512273331Sbryanv		params.vxlp_ftable_max = val & 0xFFFFFFFF;
513273331Sbryanv		return;
514273331Sbryanv	}
515273331Sbryanv
516273331Sbryanv	bzero(&cmd, sizeof(cmd));
517273331Sbryanv	cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF;
518273331Sbryanv
519273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0)
520273331Sbryanv		err(1, "VXLAN_CMD_SET_FTABLE_MAX");
521273331Sbryanv}
522273331Sbryanv
523273331Sbryanvstatic
524273331SbryanvDECL_CMD_FUNC(setvxlan_dev, arg, d)
525273331Sbryanv{
526273331Sbryanv	struct ifvxlancmd cmd;
527273331Sbryanv
528273331Sbryanv	if (!vxlan_exists(s)) {
529273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_MULTICAST_IF;
530273331Sbryanv		strlcpy(params.vxlp_mc_ifname, arg,
531273331Sbryanv		    sizeof(params.vxlp_mc_ifname));
532273331Sbryanv		return;
533273331Sbryanv	}
534273331Sbryanv
535273331Sbryanv	bzero(&cmd, sizeof(cmd));
536273331Sbryanv	strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname));
537273331Sbryanv
538273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0)
539273331Sbryanv		err(1, "VXLAN_CMD_SET_MULTICAST_IF");
540273331Sbryanv}
541273331Sbryanv
542273331Sbryanvstatic
543273331SbryanvDECL_CMD_FUNC(setvxlan_ttl, arg, d)
544273331Sbryanv{
545273331Sbryanv	struct ifvxlancmd cmd;
546273331Sbryanv	u_long val;
547273331Sbryanv
548273331Sbryanv	if (get_val(arg, &val) < 0 || val > 256)
549273331Sbryanv		errx(1, "invalid TTL value: %s", arg);
550273331Sbryanv
551273331Sbryanv	if (!vxlan_exists(s)) {
552273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_TTL;
553273331Sbryanv		params.vxlp_ttl = val;
554273331Sbryanv		return;
555273331Sbryanv	}
556273331Sbryanv
557273331Sbryanv	bzero(&cmd, sizeof(cmd));
558273331Sbryanv	cmd.vxlcmd_ttl = val;
559273331Sbryanv
560273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0)
561273331Sbryanv		err(1, "VXLAN_CMD_SET_TTL");
562273331Sbryanv}
563273331Sbryanv
564273331Sbryanvstatic
565273331SbryanvDECL_CMD_FUNC(setvxlan_learn, arg, d)
566273331Sbryanv{
567273331Sbryanv	struct ifvxlancmd cmd;
568273331Sbryanv
569273331Sbryanv	if (!vxlan_exists(s)) {
570273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_LEARN;
571273331Sbryanv		params.vxlp_learn = d;
572273331Sbryanv		return;
573273331Sbryanv	}
574273331Sbryanv
575273331Sbryanv	bzero(&cmd, sizeof(cmd));
576273331Sbryanv	if (d != 0)
577273331Sbryanv		cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN;
578273331Sbryanv
579273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0)
580273331Sbryanv		err(1, "VXLAN_CMD_SET_LEARN");
581273331Sbryanv}
582273331Sbryanv
583273331Sbryanvstatic void
584273331Sbryanvsetvxlan_flush(const char *val, int d, int s, const struct afswtch *afp)
585273331Sbryanv{
586273331Sbryanv	struct ifvxlancmd cmd;
587273331Sbryanv
588273331Sbryanv	bzero(&cmd, sizeof(cmd));
589273331Sbryanv	if (d != 0)
590273331Sbryanv		cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL;
591273331Sbryanv
592273331Sbryanv	if (do_cmd(s, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0)
593273331Sbryanv		err(1, "VXLAN_CMD_FLUSH");
594273331Sbryanv}
595273331Sbryanv
596273331Sbryanvstatic struct cmd vxlan_cmds[] = {
597273331Sbryanv
598284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanid",		setvxlan_vni),
599284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanlocal",		setvxlan_local),
600284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanremote",	setvxlan_remote),
601284365Sbryanv	DEF_CLONE_CMD_ARG("vxlangroup",		setvxlan_group),
602284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanlocalport",	setvxlan_local_port),
603284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanremoteport",	setvxlan_remote_port),
604284365Sbryanv	DEF_CLONE_CMD_ARG2("vxlanportrange",	setvxlan_port_range),
605284365Sbryanv	DEF_CLONE_CMD_ARG("vxlantimeout",	setvxlan_timeout),
606284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanmaxaddr",	setvxlan_maxaddr),
607273331Sbryanv	DEF_CLONE_CMD_ARG("vxlandev",		setvxlan_dev),
608284365Sbryanv	DEF_CLONE_CMD_ARG("vxlanttl",		setvxlan_ttl),
609284365Sbryanv	DEF_CLONE_CMD("vxlanlearn", 1,		setvxlan_learn),
610284365Sbryanv	DEF_CLONE_CMD("-vxlanlearn", 0,		setvxlan_learn),
611273331Sbryanv
612284365Sbryanv	DEF_CMD_ARG("vxlanvni",			setvxlan_vni),
613284365Sbryanv	DEF_CMD_ARG("vxlanlocal",		setvxlan_local),
614284365Sbryanv	DEF_CMD_ARG("vxlanremote",		setvxlan_remote),
615284365Sbryanv	DEF_CMD_ARG("vxlangroup",		setvxlan_group),
616284365Sbryanv	DEF_CMD_ARG("vxlanlocalport",		setvxlan_local_port),
617284365Sbryanv	DEF_CMD_ARG("vxlanremoteport",		setvxlan_remote_port),
618284365Sbryanv	DEF_CMD_ARG2("vxlanportrange",		setvxlan_port_range),
619284365Sbryanv	DEF_CMD_ARG("vxlantimeout",		setvxlan_timeout),
620284365Sbryanv	DEF_CMD_ARG("vxlanmaxaddr",		setvxlan_maxaddr),
621273331Sbryanv	DEF_CMD_ARG("vxlandev",			setvxlan_dev),
622284365Sbryanv	DEF_CMD_ARG("vxlanttl",			setvxlan_ttl),
623284365Sbryanv	DEF_CMD("vxlanlearn", 1,		setvxlan_learn),
624284365Sbryanv	DEF_CMD("-vxlanlearn", 0,		setvxlan_learn),
625273331Sbryanv
626284365Sbryanv	DEF_CMD("vxlanflush", 0,		setvxlan_flush),
627284365Sbryanv	DEF_CMD("vxlanflushall", 1,		setvxlan_flush),
628273331Sbryanv};
629273331Sbryanv
630273331Sbryanvstatic struct afswtch af_vxlan = {
631273331Sbryanv	.af_name		= "af_vxlan",
632273331Sbryanv	.af_af			= AF_UNSPEC,
633273331Sbryanv	.af_other_status	= vxlan_status,
634273331Sbryanv};
635273331Sbryanv
636273331Sbryanvstatic __constructor void
637273331Sbryanvvxlan_ctor(void)
638273331Sbryanv{
639273331Sbryanv	size_t i;
640273331Sbryanv
641289986Sngie	for (i = 0; i < nitems(vxlan_cmds); i++)
642273331Sbryanv		cmd_register(&vxlan_cmds[i]);
643273331Sbryanv	af_register(&af_vxlan);
644273331Sbryanv	callback_register(vxlan_cb, NULL);
645273331Sbryanv	clone_setdefcallback("vxlan", vxlan_create);
646273331Sbryanv}
647