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: releng/11.0/sbin/ifconfig/ifvxlan.c 288305 2015-09-27 07:51:18Z ngie $");
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_vxlan.h>
43273331Sbryanv#include <net/route.h>
44273331Sbryanv#include <netinet/in.h>
45273331Sbryanv
46273331Sbryanv#include <ctype.h>
47273331Sbryanv#include <stdio.h>
48273331Sbryanv#include <string.h>
49273331Sbryanv#include <stdlib.h>
50273331Sbryanv#include <unistd.h>
51273331Sbryanv#include <err.h>
52273331Sbryanv#include <errno.h>
53273331Sbryanv
54273331Sbryanv#include "ifconfig.h"
55273331Sbryanv
56273331Sbryanvstatic struct ifvxlanparam params = {
57273331Sbryanv	.vxlp_vni	= VXLAN_VNI_MAX,
58273331Sbryanv};
59273331Sbryanv
60273331Sbryanvstatic int
61273331Sbryanvget_val(const char *cp, u_long *valp)
62273331Sbryanv{
63273331Sbryanv	char *endptr;
64273331Sbryanv	u_long val;
65273331Sbryanv
66273331Sbryanv	errno = 0;
67273331Sbryanv	val = strtoul(cp, &endptr, 0);
68273331Sbryanv	if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
69273331Sbryanv		return (-1);
70273331Sbryanv
71273331Sbryanv	*valp = val;
72273331Sbryanv	return (0);
73273331Sbryanv}
74273331Sbryanv
75273331Sbryanvstatic int
76273331Sbryanvdo_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
77273331Sbryanv{
78273331Sbryanv	struct ifdrv ifd;
79273331Sbryanv
80273331Sbryanv	bzero(&ifd, sizeof(ifd));
81273331Sbryanv
82273331Sbryanv	strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name));
83273331Sbryanv	ifd.ifd_cmd = op;
84273331Sbryanv	ifd.ifd_len = argsize;
85273331Sbryanv	ifd.ifd_data = arg;
86273331Sbryanv
87273331Sbryanv	return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
88273331Sbryanv}
89273331Sbryanv
90273331Sbryanvstatic int
91273331Sbryanvvxlan_exists(int sock)
92273331Sbryanv{
93273331Sbryanv	struct ifvxlancfg cfg;
94273331Sbryanv
95273331Sbryanv	bzero(&cfg, sizeof(cfg));
96273331Sbryanv
97273331Sbryanv	return (do_cmd(sock, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1);
98273331Sbryanv}
99273331Sbryanv
100273331Sbryanvstatic void
101273331Sbryanvvxlan_status(int s)
102273331Sbryanv{
103273331Sbryanv	struct ifvxlancfg cfg;
104273331Sbryanv	char src[NI_MAXHOST], dst[NI_MAXHOST];
105273331Sbryanv	char srcport[NI_MAXSERV], dstport[NI_MAXSERV];
106273331Sbryanv	struct sockaddr *lsa, *rsa;
107273331Sbryanv	int vni, mc, ipv6;
108273331Sbryanv
109273331Sbryanv	bzero(&cfg, sizeof(cfg));
110273331Sbryanv
111273331Sbryanv	if (do_cmd(s, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0)
112273331Sbryanv		return;
113273331Sbryanv
114273331Sbryanv	vni = cfg.vxlc_vni;
115273331Sbryanv	lsa = &cfg.vxlc_local_sa.sa;
116273331Sbryanv	rsa = &cfg.vxlc_remote_sa.sa;
117273331Sbryanv	ipv6 = rsa->sa_family == AF_INET6;
118273331Sbryanv
119273331Sbryanv	/* Just report nothing if the network identity isn't set yet. */
120273331Sbryanv	if (vni >= VXLAN_VNI_MAX)
121273331Sbryanv		return;
122273331Sbryanv
123273331Sbryanv	if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src),
124273331Sbryanv	    srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
125273331Sbryanv		src[0] = srcport[0] = '\0';
126273331Sbryanv	if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst),
127273331Sbryanv	    dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
128273331Sbryanv		dst[0] = dstport[0] = '\0';
129273331Sbryanv
130273331Sbryanv	if (!ipv6) {
131273331Sbryanv		struct sockaddr_in *sin = (struct sockaddr_in *)rsa;
132273331Sbryanv		mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
133273331Sbryanv	} else {
134273331Sbryanv		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rsa;
135273331Sbryanv		mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
136273331Sbryanv	}
137273331Sbryanv
138273331Sbryanv	printf("\tvxlan vni %d", vni);
139273331Sbryanv	printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
140273331Sbryanv	    srcport);
141273331Sbryanv	printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "",
142273331Sbryanv	    dst, ipv6 ? "]" : "", dstport);
143273331Sbryanv
144273331Sbryanv	if (verbose) {
145273331Sbryanv		printf("\n\t\tconfig: ");
146273331Sbryanv		printf("%slearning portrange %d-%d ttl %d",
147273331Sbryanv		    cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min,
148273331Sbryanv		    cfg.vxlc_port_max, cfg.vxlc_ttl);
149273331Sbryanv		printf("\n\t\tftable: ");
150273331Sbryanv		printf("cnt %d max %d timeout %d",
151273331Sbryanv		    cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max,
152273331Sbryanv		    cfg.vxlc_ftable_timeout);
153273331Sbryanv	}
154273331Sbryanv
155273331Sbryanv	putchar('\n');
156273331Sbryanv}
157273331Sbryanv
158273331Sbryanv#define _LOCAL_ADDR46 \
159273331Sbryanv    (VXLAN_PARAM_WITH_LOCAL_ADDR4 | VXLAN_PARAM_WITH_LOCAL_ADDR6)
160273331Sbryanv#define _REMOTE_ADDR46 \
161273331Sbryanv    (VXLAN_PARAM_WITH_REMOTE_ADDR4 | VXLAN_PARAM_WITH_REMOTE_ADDR6)
162273331Sbryanv
163273331Sbryanvstatic void
164273331Sbryanvvxlan_check_params(void)
165273331Sbryanv{
166273331Sbryanv
167273331Sbryanv	if ((params.vxlp_with & _LOCAL_ADDR46) == _LOCAL_ADDR46)
168273331Sbryanv		errx(1, "cannot specify both local IPv4 and IPv6 addresses");
169273331Sbryanv	if ((params.vxlp_with & _REMOTE_ADDR46) == _REMOTE_ADDR46)
170273331Sbryanv		errx(1, "cannot specify both remote IPv4 and IPv6 addresses");
171273331Sbryanv	if ((params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4 &&
172273331Sbryanv	     params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) ||
173273331Sbryanv	    (params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6 &&
174273331Sbryanv	     params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4))
175273331Sbryanv		errx(1, "cannot mix IPv4 and IPv6 addresses");
176273331Sbryanv}
177273331Sbryanv
178273331Sbryanv#undef _LOCAL_ADDR46
179273331Sbryanv#undef _REMOTE_ADDR46
180273331Sbryanv
181273331Sbryanvstatic void
182273331Sbryanvvxlan_cb(int s, void *arg)
183273331Sbryanv{
184273331Sbryanv
185273331Sbryanv}
186273331Sbryanv
187273331Sbryanvstatic void
188273331Sbryanvvxlan_create(int s, struct ifreq *ifr)
189273331Sbryanv{
190273331Sbryanv
191273331Sbryanv	vxlan_check_params();
192273331Sbryanv
193273331Sbryanv	ifr->ifr_data = (caddr_t) &params;
194273331Sbryanv	if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
195273331Sbryanv		err(1, "SIOCIFCREATE2");
196273331Sbryanv}
197273331Sbryanv
198273331Sbryanvstatic
199273331SbryanvDECL_CMD_FUNC(setvxlan_vni, arg, d)
200273331Sbryanv{
201273331Sbryanv	struct ifvxlancmd cmd;
202273331Sbryanv	u_long val;
203273331Sbryanv
204273331Sbryanv	if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX)
205273331Sbryanv		errx(1, "invalid network identifier: %s", arg);
206273331Sbryanv
207273331Sbryanv	if (!vxlan_exists(s)) {
208273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_VNI;
209273331Sbryanv		params.vxlp_vni = val;
210273331Sbryanv		return;
211273331Sbryanv	}
212273331Sbryanv
213273331Sbryanv	bzero(&cmd, sizeof(cmd));
214273331Sbryanv	cmd.vxlcmd_vni = val;
215273331Sbryanv
216273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0)
217273331Sbryanv		err(1, "VXLAN_CMD_SET_VNI");
218273331Sbryanv}
219273331Sbryanv
220273331Sbryanvstatic
221273331SbryanvDECL_CMD_FUNC(setvxlan_local, addr, d)
222273331Sbryanv{
223273331Sbryanv	struct ifvxlancmd cmd;
224273331Sbryanv	struct addrinfo *ai;
225273331Sbryanv	struct sockaddr *sa;
226273331Sbryanv	int error;
227273331Sbryanv
228273331Sbryanv	bzero(&cmd, sizeof(cmd));
229273331Sbryanv
230273331Sbryanv	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
231273331Sbryanv		errx(1, "error in parsing local address string: %s",
232273331Sbryanv		    gai_strerror(error));
233273331Sbryanv
234273331Sbryanv	sa = ai->ai_addr;
235273331Sbryanv
236273331Sbryanv	switch (ai->ai_family) {
237273331Sbryanv#ifdef INET
238273331Sbryanv	case AF_INET: {
239273331Sbryanv		struct in_addr addr = ((struct sockaddr_in *) sa)->sin_addr;
240273331Sbryanv
241273331Sbryanv		if (IN_MULTICAST(ntohl(addr.s_addr)))
242273331Sbryanv			errx(1, "local address cannot be multicast");
243273331Sbryanv
244273331Sbryanv		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
245273331Sbryanv		cmd.vxlcmd_sa.in4.sin_addr = addr;
246273331Sbryanv		break;
247273331Sbryanv	}
248273331Sbryanv#endif
249273331Sbryanv#ifdef INET6
250273331Sbryanv	case AF_INET6: {
251273331Sbryanv		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
252273331Sbryanv
253273331Sbryanv		if (IN6_IS_ADDR_MULTICAST(addr))
254273331Sbryanv			errx(1, "local address cannot be multicast");
255273331Sbryanv
256273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
257273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
258273331Sbryanv		break;
259273331Sbryanv	}
260273331Sbryanv#endif
261273331Sbryanv	default:
262273331Sbryanv		errx(1, "local address %s not supported", addr);
263273331Sbryanv	}
264273331Sbryanv
265273331Sbryanv	freeaddrinfo(ai);
266273331Sbryanv
267273331Sbryanv	if (!vxlan_exists(s)) {
268273331Sbryanv		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
269273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR4;
270273331Sbryanv			params.vxlp_local_in4 = cmd.vxlcmd_sa.in4.sin_addr;
271273331Sbryanv		} else {
272273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6;
273273331Sbryanv			params.vxlp_local_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
274273331Sbryanv		}
275273331Sbryanv		return;
276273331Sbryanv	}
277273331Sbryanv
278273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0)
279273331Sbryanv		err(1, "VXLAN_CMD_SET_LOCAL_ADDR");
280273331Sbryanv}
281273331Sbryanv
282273331Sbryanvstatic
283273331SbryanvDECL_CMD_FUNC(setvxlan_remote, addr, d)
284273331Sbryanv{
285273331Sbryanv	struct ifvxlancmd cmd;
286273331Sbryanv	struct addrinfo *ai;
287273331Sbryanv	struct sockaddr *sa;
288273331Sbryanv	int error;
289273331Sbryanv
290273331Sbryanv	bzero(&cmd, sizeof(cmd));
291273331Sbryanv
292273331Sbryanv	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
293273331Sbryanv		errx(1, "error in parsing remote address string: %s",
294273331Sbryanv		    gai_strerror(error));
295273331Sbryanv
296273331Sbryanv	sa = ai->ai_addr;
297273331Sbryanv
298273331Sbryanv	switch (ai->ai_family) {
299273331Sbryanv#ifdef INET
300273331Sbryanv	case AF_INET: {
301273331Sbryanv		struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
302273331Sbryanv
303273331Sbryanv		if (IN_MULTICAST(ntohl(addr.s_addr)))
304273331Sbryanv			errx(1, "remote address cannot be multicast");
305273331Sbryanv
306273331Sbryanv		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
307273331Sbryanv		cmd.vxlcmd_sa.in4.sin_addr = addr;
308273331Sbryanv		break;
309273331Sbryanv	}
310273331Sbryanv#endif
311273331Sbryanv#ifdef INET6
312273331Sbryanv	case AF_INET6: {
313273331Sbryanv		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
314273331Sbryanv
315273331Sbryanv		if (IN6_IS_ADDR_MULTICAST(addr))
316273331Sbryanv			errx(1, "remote address cannot be multicast");
317273331Sbryanv
318273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
319273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
320273331Sbryanv		break;
321273331Sbryanv	}
322273331Sbryanv#endif
323273331Sbryanv	default:
324273331Sbryanv		errx(1, "remote address %s not supported", addr);
325273331Sbryanv	}
326273331Sbryanv
327273331Sbryanv	freeaddrinfo(ai);
328273331Sbryanv
329273331Sbryanv	if (!vxlan_exists(s)) {
330273331Sbryanv		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
331273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
332273331Sbryanv			params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr;
333273331Sbryanv		} else {
334273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
335273331Sbryanv			params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
336273331Sbryanv		}
337273331Sbryanv		return;
338273331Sbryanv	}
339273331Sbryanv
340273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
341273331Sbryanv		err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
342273331Sbryanv}
343273331Sbryanv
344273331Sbryanvstatic
345273331SbryanvDECL_CMD_FUNC(setvxlan_group, addr, d)
346273331Sbryanv{
347273331Sbryanv	struct ifvxlancmd cmd;
348273331Sbryanv	struct addrinfo *ai;
349273331Sbryanv	struct sockaddr *sa;
350273331Sbryanv	int error;
351273331Sbryanv
352273331Sbryanv	bzero(&cmd, sizeof(cmd));
353273331Sbryanv
354273331Sbryanv	if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
355273331Sbryanv		errx(1, "error in parsing group address string: %s",
356273331Sbryanv		    gai_strerror(error));
357273331Sbryanv
358273331Sbryanv	sa = ai->ai_addr;
359273331Sbryanv
360273331Sbryanv	switch (ai->ai_family) {
361273331Sbryanv#ifdef INET
362273331Sbryanv	case AF_INET: {
363273331Sbryanv		struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
364273331Sbryanv
365273331Sbryanv		if (!IN_MULTICAST(ntohl(addr.s_addr)))
366273331Sbryanv			errx(1, "group address must be multicast");
367273331Sbryanv
368273331Sbryanv		cmd.vxlcmd_sa.in4.sin_family = AF_INET;
369273331Sbryanv		cmd.vxlcmd_sa.in4.sin_addr = addr;
370273331Sbryanv		break;
371273331Sbryanv	}
372273331Sbryanv#endif
373273331Sbryanv#ifdef INET6
374273331Sbryanv	case AF_INET6: {
375273331Sbryanv		struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
376273331Sbryanv
377273331Sbryanv		if (!IN6_IS_ADDR_MULTICAST(addr))
378273331Sbryanv			errx(1, "group address must be multicast");
379273331Sbryanv
380273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
381273331Sbryanv		cmd.vxlcmd_sa.in6.sin6_addr = *addr;
382273331Sbryanv		break;
383273331Sbryanv	}
384273331Sbryanv#endif
385273331Sbryanv	default:
386273331Sbryanv		errx(1, "group address %s not supported", addr);
387273331Sbryanv	}
388273331Sbryanv
389273331Sbryanv	freeaddrinfo(ai);
390273331Sbryanv
391273331Sbryanv	if (!vxlan_exists(s)) {
392273331Sbryanv		if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
393273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
394273331Sbryanv			params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr;
395273331Sbryanv		} else {
396273331Sbryanv			params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
397273331Sbryanv			params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
398273331Sbryanv		}
399273331Sbryanv		return;
400273331Sbryanv	}
401273331Sbryanv
402273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
403273331Sbryanv		err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
404273331Sbryanv}
405273331Sbryanv
406273331Sbryanvstatic
407273331SbryanvDECL_CMD_FUNC(setvxlan_local_port, arg, d)
408273331Sbryanv{
409273331Sbryanv	struct ifvxlancmd cmd;
410273331Sbryanv	u_long val;
411273331Sbryanv
412273331Sbryanv	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
413273331Sbryanv		errx(1, "invalid local port: %s", arg);
414273331Sbryanv
415273331Sbryanv	if (!vxlan_exists(s)) {
416273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT;
417273331Sbryanv		params.vxlp_local_port = val;
418273331Sbryanv		return;
419273331Sbryanv	}
420273331Sbryanv
421273331Sbryanv	bzero(&cmd, sizeof(cmd));
422273331Sbryanv	cmd.vxlcmd_port = val;
423273331Sbryanv
424273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0)
425273331Sbryanv		err(1, "VXLAN_CMD_SET_LOCAL_PORT");
426273331Sbryanv}
427273331Sbryanv
428273331Sbryanvstatic
429273331SbryanvDECL_CMD_FUNC(setvxlan_remote_port, arg, d)
430273331Sbryanv{
431273331Sbryanv	struct ifvxlancmd cmd;
432273331Sbryanv	u_long val;
433273331Sbryanv
434273331Sbryanv	if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
435273331Sbryanv		errx(1, "invalid remote port: %s", arg);
436273331Sbryanv
437273331Sbryanv	if (!vxlan_exists(s)) {
438273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT;
439273331Sbryanv		params.vxlp_remote_port = val;
440273331Sbryanv		return;
441273331Sbryanv	}
442273331Sbryanv
443273331Sbryanv	bzero(&cmd, sizeof(cmd));
444273331Sbryanv	cmd.vxlcmd_port = val;
445273331Sbryanv
446273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0)
447273331Sbryanv		err(1, "VXLAN_CMD_SET_REMOTE_PORT");
448273331Sbryanv}
449273331Sbryanv
450273331Sbryanvstatic
451273331SbryanvDECL_CMD_FUNC2(setvxlan_port_range, arg1, arg2)
452273331Sbryanv{
453273331Sbryanv	struct ifvxlancmd cmd;
454273331Sbryanv	u_long min, max;
455273331Sbryanv
456273331Sbryanv	if (get_val(arg1, &min) < 0 || min >= UINT16_MAX)
457273331Sbryanv		errx(1, "invalid port range minimum: %s", arg1);
458273331Sbryanv	if (get_val(arg2, &max) < 0 || max >= UINT16_MAX)
459273331Sbryanv		errx(1, "invalid port range maximum: %s", arg2);
460273331Sbryanv	if (max < min)
461273331Sbryanv		errx(1, "invalid port range");
462273331Sbryanv
463273331Sbryanv	if (!vxlan_exists(s)) {
464273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_PORT_RANGE;
465273331Sbryanv		params.vxlp_min_port = min;
466273331Sbryanv		params.vxlp_max_port = max;
467273331Sbryanv		return;
468273331Sbryanv	}
469273331Sbryanv
470273331Sbryanv	bzero(&cmd, sizeof(cmd));
471273331Sbryanv	cmd.vxlcmd_port_min = min;
472273331Sbryanv	cmd.vxlcmd_port_max = max;
473273331Sbryanv
474273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0)
475273331Sbryanv		err(1, "VXLAN_CMD_SET_PORT_RANGE");
476273331Sbryanv}
477273331Sbryanv
478273331Sbryanvstatic
479273331SbryanvDECL_CMD_FUNC(setvxlan_timeout, arg, d)
480273331Sbryanv{
481273331Sbryanv	struct ifvxlancmd cmd;
482273331Sbryanv	u_long val;
483273331Sbryanv
484273331Sbryanv	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
485273331Sbryanv		errx(1, "invalid timeout value: %s", arg);
486273331Sbryanv
487273331Sbryanv	if (!vxlan_exists(s)) {
488273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT;
489273331Sbryanv		params.vxlp_ftable_timeout = val & 0xFFFFFFFF;
490273331Sbryanv		return;
491273331Sbryanv	}
492273331Sbryanv
493273331Sbryanv	bzero(&cmd, sizeof(cmd));
494273331Sbryanv	cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF;
495273331Sbryanv
496273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0)
497273331Sbryanv		err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT");
498273331Sbryanv}
499273331Sbryanv
500273331Sbryanvstatic
501273331SbryanvDECL_CMD_FUNC(setvxlan_maxaddr, arg, d)
502273331Sbryanv{
503273331Sbryanv	struct ifvxlancmd cmd;
504273331Sbryanv	u_long val;
505273331Sbryanv
506273331Sbryanv	if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
507273331Sbryanv		errx(1, "invalid maxaddr value: %s",  arg);
508273331Sbryanv
509273331Sbryanv	if (!vxlan_exists(s)) {
510273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX;
511273331Sbryanv		params.vxlp_ftable_max = val & 0xFFFFFFFF;
512273331Sbryanv		return;
513273331Sbryanv	}
514273331Sbryanv
515273331Sbryanv	bzero(&cmd, sizeof(cmd));
516273331Sbryanv	cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF;
517273331Sbryanv
518273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0)
519273331Sbryanv		err(1, "VXLAN_CMD_SET_FTABLE_MAX");
520273331Sbryanv}
521273331Sbryanv
522273331Sbryanvstatic
523273331SbryanvDECL_CMD_FUNC(setvxlan_dev, arg, d)
524273331Sbryanv{
525273331Sbryanv	struct ifvxlancmd cmd;
526273331Sbryanv
527273331Sbryanv	if (!vxlan_exists(s)) {
528273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_MULTICAST_IF;
529273331Sbryanv		strlcpy(params.vxlp_mc_ifname, arg,
530273331Sbryanv		    sizeof(params.vxlp_mc_ifname));
531273331Sbryanv		return;
532273331Sbryanv	}
533273331Sbryanv
534273331Sbryanv	bzero(&cmd, sizeof(cmd));
535273331Sbryanv	strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname));
536273331Sbryanv
537273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0)
538273331Sbryanv		err(1, "VXLAN_CMD_SET_MULTICAST_IF");
539273331Sbryanv}
540273331Sbryanv
541273331Sbryanvstatic
542273331SbryanvDECL_CMD_FUNC(setvxlan_ttl, arg, d)
543273331Sbryanv{
544273331Sbryanv	struct ifvxlancmd cmd;
545273331Sbryanv	u_long val;
546273331Sbryanv
547273331Sbryanv	if (get_val(arg, &val) < 0 || val > 256)
548273331Sbryanv		errx(1, "invalid TTL value: %s", arg);
549273331Sbryanv
550273331Sbryanv	if (!vxlan_exists(s)) {
551273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_TTL;
552273331Sbryanv		params.vxlp_ttl = val;
553273331Sbryanv		return;
554273331Sbryanv	}
555273331Sbryanv
556273331Sbryanv	bzero(&cmd, sizeof(cmd));
557273331Sbryanv	cmd.vxlcmd_ttl = val;
558273331Sbryanv
559273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0)
560273331Sbryanv		err(1, "VXLAN_CMD_SET_TTL");
561273331Sbryanv}
562273331Sbryanv
563273331Sbryanvstatic
564273331SbryanvDECL_CMD_FUNC(setvxlan_learn, arg, d)
565273331Sbryanv{
566273331Sbryanv	struct ifvxlancmd cmd;
567273331Sbryanv
568273331Sbryanv	if (!vxlan_exists(s)) {
569273331Sbryanv		params.vxlp_with |= VXLAN_PARAM_WITH_LEARN;
570273331Sbryanv		params.vxlp_learn = d;
571273331Sbryanv		return;
572273331Sbryanv	}
573273331Sbryanv
574273331Sbryanv	bzero(&cmd, sizeof(cmd));
575273331Sbryanv	if (d != 0)
576273331Sbryanv		cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN;
577273331Sbryanv
578273331Sbryanv	if (do_cmd(s, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0)
579273331Sbryanv		err(1, "VXLAN_CMD_SET_LEARN");
580273331Sbryanv}
581273331Sbryanv
582273331Sbryanvstatic void
583273331Sbryanvsetvxlan_flush(const char *val, int d, int s, const struct afswtch *afp)
584273331Sbryanv{
585273331Sbryanv	struct ifvxlancmd cmd;
586273331Sbryanv
587273331Sbryanv	bzero(&cmd, sizeof(cmd));
588273331Sbryanv	if (d != 0)
589273331Sbryanv		cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL;
590273331Sbryanv
591273331Sbryanv	if (do_cmd(s, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0)
592273331Sbryanv		err(1, "VXLAN_CMD_FLUSH");
593273331Sbryanv}
594273331Sbryanv
595273331Sbryanvstatic struct cmd vxlan_cmds[] = {
596273331Sbryanv
597275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanid",		setvxlan_vni),
598275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanlocal",		setvxlan_local),
599275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanremote",	setvxlan_remote),
600275851Sbryanv	DEF_CLONE_CMD_ARG("vxlangroup",		setvxlan_group),
601275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanlocalport",	setvxlan_local_port),
602275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanremoteport",	setvxlan_remote_port),
603275851Sbryanv	DEF_CLONE_CMD_ARG2("vxlanportrange",	setvxlan_port_range),
604275851Sbryanv	DEF_CLONE_CMD_ARG("vxlantimeout",	setvxlan_timeout),
605275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanmaxaddr",	setvxlan_maxaddr),
606273331Sbryanv	DEF_CLONE_CMD_ARG("vxlandev",		setvxlan_dev),
607275851Sbryanv	DEF_CLONE_CMD_ARG("vxlanttl",		setvxlan_ttl),
608275851Sbryanv	DEF_CLONE_CMD("vxlanlearn", 1,		setvxlan_learn),
609275851Sbryanv	DEF_CLONE_CMD("-vxlanlearn", 0,		setvxlan_learn),
610273331Sbryanv
611275851Sbryanv	DEF_CMD_ARG("vxlanvni",			setvxlan_vni),
612275851Sbryanv	DEF_CMD_ARG("vxlanlocal",		setvxlan_local),
613275851Sbryanv	DEF_CMD_ARG("vxlanremote",		setvxlan_remote),
614275851Sbryanv	DEF_CMD_ARG("vxlangroup",		setvxlan_group),
615275851Sbryanv	DEF_CMD_ARG("vxlanlocalport",		setvxlan_local_port),
616275851Sbryanv	DEF_CMD_ARG("vxlanremoteport",		setvxlan_remote_port),
617275851Sbryanv	DEF_CMD_ARG2("vxlanportrange",		setvxlan_port_range),
618275851Sbryanv	DEF_CMD_ARG("vxlantimeout",		setvxlan_timeout),
619275851Sbryanv	DEF_CMD_ARG("vxlanmaxaddr",		setvxlan_maxaddr),
620273331Sbryanv	DEF_CMD_ARG("vxlandev",			setvxlan_dev),
621275851Sbryanv	DEF_CMD_ARG("vxlanttl",			setvxlan_ttl),
622275851Sbryanv	DEF_CMD("vxlanlearn", 1,		setvxlan_learn),
623275851Sbryanv	DEF_CMD("-vxlanlearn", 0,		setvxlan_learn),
624273331Sbryanv
625275851Sbryanv	DEF_CMD("vxlanflush", 0,		setvxlan_flush),
626275851Sbryanv	DEF_CMD("vxlanflushall", 1,		setvxlan_flush),
627273331Sbryanv};
628273331Sbryanv
629273331Sbryanvstatic struct afswtch af_vxlan = {
630273331Sbryanv	.af_name		= "af_vxlan",
631273331Sbryanv	.af_af			= AF_UNSPEC,
632273331Sbryanv	.af_other_status	= vxlan_status,
633273331Sbryanv};
634273331Sbryanv
635273331Sbryanvstatic __constructor void
636273331Sbryanvvxlan_ctor(void)
637273331Sbryanv{
638273331Sbryanv	size_t i;
639273331Sbryanv
640288305Sngie	for (i = 0; i < nitems(vxlan_cmds); i++)
641273331Sbryanv		cmd_register(&vxlan_cmds[i]);
642273331Sbryanv	af_register(&af_vxlan);
643273331Sbryanv	callback_register(vxlan_cb, NULL);
644273331Sbryanv	clone_setdefcallback("vxlan", vxlan_create);
645273331Sbryanv}
646