2 * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
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.
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.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <sys/sockio.h>
40 #include <net/ethernet.h>
42 #include <net/if_var.h>
43 #include <net/if_vxlan.h>
44 #include <net/route.h>
45 #include <netinet/in.h>
57 static struct ifvxlanparam params = {
58 .vxlp_vni = VXLAN_VNI_MAX,
62 get_val(const char *cp, u_long *valp)
68 val = strtoul(cp, &endptr, 0);
69 if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
77 do_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
81 bzero(&ifd, sizeof(ifd));
83 strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name));
85 ifd.ifd_len = argsize;
88 return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
92 vxlan_exists(int sock)
94 struct ifvxlancfg cfg;
96 bzero(&cfg, sizeof(cfg));
98 return (do_cmd(sock, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1);
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;
110 bzero(&cfg, sizeof(cfg));
112 if (do_cmd(s, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0)
116 lsa = &cfg.vxlc_local_sa.sa;
117 rsa = &cfg.vxlc_remote_sa.sa;
118 ipv6 = rsa->sa_family == AF_INET6;
120 /* Just report nothing if the network identity isn't set yet. */
121 if (vni >= VXLAN_VNI_MAX)
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';
132 struct sockaddr_in *sin = (struct sockaddr_in *)rsa;
133 mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
135 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rsa;
136 mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
139 printf("\tvxlan vni %d", vni);
140 printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
142 printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "",
143 dst, ipv6 ? "]" : "", dstport);
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);
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)
165 vxlan_check_params(void)
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");
180 #undef _REMOTE_ADDR46
183 vxlan_cb(int s, void *arg)
189 vxlan_create(int s, struct ifreq *ifr)
192 vxlan_check_params();
194 ifr->ifr_data = (caddr_t) ¶ms;
195 if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
196 err(1, "SIOCIFCREATE2");
200 DECL_CMD_FUNC(setvxlan_vni, arg, d)
202 struct ifvxlancmd cmd;
205 if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX)
206 errx(1, "invalid network identifier: %s", arg);
208 if (!vxlan_exists(s)) {
209 params.vxlp_with |= VXLAN_PARAM_WITH_VNI;
210 params.vxlp_vni = val;
214 bzero(&cmd, sizeof(cmd));
215 cmd.vxlcmd_vni = val;
217 if (do_cmd(s, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0)
218 err(1, "VXLAN_CMD_SET_VNI");
222 DECL_CMD_FUNC(setvxlan_local, addr, d)
224 struct ifvxlancmd cmd;
229 bzero(&cmd, sizeof(cmd));
231 if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
232 errx(1, "error in parsing local address string: %s",
233 gai_strerror(error));
237 switch (ai->ai_family) {
240 struct in_addr addr = ((struct sockaddr_in *) sa)->sin_addr;
242 if (IN_MULTICAST(ntohl(addr.s_addr)))
243 errx(1, "local address cannot be multicast");
245 cmd.vxlcmd_sa.in4.sin_family = AF_INET;
246 cmd.vxlcmd_sa.in4.sin_addr = addr;
252 struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
254 if (IN6_IS_ADDR_MULTICAST(addr))
255 errx(1, "local address cannot be multicast");
257 cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
258 cmd.vxlcmd_sa.in6.sin6_addr = *addr;
263 errx(1, "local address %s not supported", addr);
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;
273 params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6;
274 params.vxlp_local_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
279 if (do_cmd(s, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0)
280 err(1, "VXLAN_CMD_SET_LOCAL_ADDR");
284 DECL_CMD_FUNC(setvxlan_remote, addr, d)
286 struct ifvxlancmd cmd;
291 bzero(&cmd, sizeof(cmd));
293 if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
294 errx(1, "error in parsing remote address string: %s",
295 gai_strerror(error));
299 switch (ai->ai_family) {
302 struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
304 if (IN_MULTICAST(ntohl(addr.s_addr)))
305 errx(1, "remote address cannot be multicast");
307 cmd.vxlcmd_sa.in4.sin_family = AF_INET;
308 cmd.vxlcmd_sa.in4.sin_addr = addr;
314 struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
316 if (IN6_IS_ADDR_MULTICAST(addr))
317 errx(1, "remote address cannot be multicast");
319 cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
320 cmd.vxlcmd_sa.in6.sin6_addr = *addr;
325 errx(1, "remote address %s not supported", addr);
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;
335 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
336 params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
341 if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
342 err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
346 DECL_CMD_FUNC(setvxlan_group, addr, d)
348 struct ifvxlancmd cmd;
353 bzero(&cmd, sizeof(cmd));
355 if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
356 errx(1, "error in parsing group address string: %s",
357 gai_strerror(error));
361 switch (ai->ai_family) {
364 struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr;
366 if (!IN_MULTICAST(ntohl(addr.s_addr)))
367 errx(1, "group address must be multicast");
369 cmd.vxlcmd_sa.in4.sin_family = AF_INET;
370 cmd.vxlcmd_sa.in4.sin_addr = addr;
376 struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
378 if (!IN6_IS_ADDR_MULTICAST(addr))
379 errx(1, "group address must be multicast");
381 cmd.vxlcmd_sa.in6.sin6_family = AF_INET6;
382 cmd.vxlcmd_sa.in6.sin6_addr = *addr;
387 errx(1, "group address %s not supported", addr);
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;
397 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
398 params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr;
403 if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
404 err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
408 DECL_CMD_FUNC(setvxlan_local_port, arg, d)
410 struct ifvxlancmd cmd;
413 if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
414 errx(1, "invalid local port: %s", arg);
416 if (!vxlan_exists(s)) {
417 params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT;
418 params.vxlp_local_port = val;
422 bzero(&cmd, sizeof(cmd));
423 cmd.vxlcmd_port = val;
425 if (do_cmd(s, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0)
426 err(1, "VXLAN_CMD_SET_LOCAL_PORT");
430 DECL_CMD_FUNC(setvxlan_remote_port, arg, d)
432 struct ifvxlancmd cmd;
435 if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
436 errx(1, "invalid remote port: %s", arg);
438 if (!vxlan_exists(s)) {
439 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT;
440 params.vxlp_remote_port = val;
444 bzero(&cmd, sizeof(cmd));
445 cmd.vxlcmd_port = val;
447 if (do_cmd(s, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0)
448 err(1, "VXLAN_CMD_SET_REMOTE_PORT");
452 DECL_CMD_FUNC2(setvxlan_port_range, arg1, arg2)
454 struct ifvxlancmd cmd;
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);
462 errx(1, "invalid port range");
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;
471 bzero(&cmd, sizeof(cmd));
472 cmd.vxlcmd_port_min = min;
473 cmd.vxlcmd_port_max = max;
475 if (do_cmd(s, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0)
476 err(1, "VXLAN_CMD_SET_PORT_RANGE");
480 DECL_CMD_FUNC(setvxlan_timeout, arg, d)
482 struct ifvxlancmd cmd;
485 if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
486 errx(1, "invalid timeout value: %s", arg);
488 if (!vxlan_exists(s)) {
489 params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT;
490 params.vxlp_ftable_timeout = val & 0xFFFFFFFF;
494 bzero(&cmd, sizeof(cmd));
495 cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF;
497 if (do_cmd(s, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0)
498 err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT");
502 DECL_CMD_FUNC(setvxlan_maxaddr, arg, d)
504 struct ifvxlancmd cmd;
507 if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
508 errx(1, "invalid maxaddr value: %s", arg);
510 if (!vxlan_exists(s)) {
511 params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX;
512 params.vxlp_ftable_max = val & 0xFFFFFFFF;
516 bzero(&cmd, sizeof(cmd));
517 cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF;
519 if (do_cmd(s, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0)
520 err(1, "VXLAN_CMD_SET_FTABLE_MAX");
524 DECL_CMD_FUNC(setvxlan_dev, arg, d)
526 struct ifvxlancmd cmd;
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));
535 bzero(&cmd, sizeof(cmd));
536 strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname));
538 if (do_cmd(s, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0)
539 err(1, "VXLAN_CMD_SET_MULTICAST_IF");
543 DECL_CMD_FUNC(setvxlan_ttl, arg, d)
545 struct ifvxlancmd cmd;
548 if (get_val(arg, &val) < 0 || val > 256)
549 errx(1, "invalid TTL value: %s", arg);
551 if (!vxlan_exists(s)) {
552 params.vxlp_with |= VXLAN_PARAM_WITH_TTL;
553 params.vxlp_ttl = val;
557 bzero(&cmd, sizeof(cmd));
558 cmd.vxlcmd_ttl = val;
560 if (do_cmd(s, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0)
561 err(1, "VXLAN_CMD_SET_TTL");
565 DECL_CMD_FUNC(setvxlan_learn, arg, d)
567 struct ifvxlancmd cmd;
569 if (!vxlan_exists(s)) {
570 params.vxlp_with |= VXLAN_PARAM_WITH_LEARN;
571 params.vxlp_learn = d;
575 bzero(&cmd, sizeof(cmd));
577 cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN;
579 if (do_cmd(s, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0)
580 err(1, "VXLAN_CMD_SET_LEARN");
584 setvxlan_flush(const char *val, int d, int s, const struct afswtch *afp)
586 struct ifvxlancmd cmd;
588 bzero(&cmd, sizeof(cmd));
590 cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL;
592 if (do_cmd(s, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0)
593 err(1, "VXLAN_CMD_FLUSH");
596 static struct cmd vxlan_cmds[] = {
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),
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),
626 DEF_CMD("vxlanflush", 0, setvxlan_flush),
627 DEF_CMD("vxlanflushall", 1, setvxlan_flush),
630 static struct afswtch af_vxlan = {
631 .af_name = "af_vxlan",
633 .af_other_status = vxlan_status,
636 static __constructor void
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);