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_vxlan.h>
43 #include <net/route.h>
44 #include <netinet/in.h>
56 static struct ifvxlanparam params = {
57 .vxlp_vni = VXLAN_VNI_MAX,
61 get_val(const char *cp, u_long *valp)
67 val = strtoul(cp, &endptr, 0);
68 if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
76 do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set)
78 struct ifdrv ifd = {};
80 strlcpy(ifd.ifd_name, ctx->ifname, sizeof(ifd.ifd_name));
82 ifd.ifd_len = argsize;
85 return (ioctl_ctx(ctx, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
89 vxlan_exists(if_ctx *ctx)
91 struct ifvxlancfg cfg;
93 bzero(&cfg, sizeof(cfg));
95 return (do_cmd(ctx, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1);
99 vxlan_status(if_ctx *ctx)
101 struct ifvxlancfg cfg;
102 char src[NI_MAXHOST], dst[NI_MAXHOST];
103 char srcport[NI_MAXSERV], dstport[NI_MAXSERV];
104 struct sockaddr *lsa, *rsa;
107 bzero(&cfg, sizeof(cfg));
109 if (do_cmd(ctx, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0)
113 lsa = &cfg.vxlc_local_sa.sa;
114 rsa = &cfg.vxlc_remote_sa.sa;
115 ipv6 = rsa->sa_family == AF_INET6;
117 /* Just report nothing if the network identity isn't set yet. */
118 if (vni >= VXLAN_VNI_MAX)
121 if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src),
122 srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
123 src[0] = srcport[0] = '\0';
124 if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst),
125 dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0)
126 dst[0] = dstport[0] = '\0';
129 struct sockaddr_in *sin = satosin(rsa);
130 mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
132 struct sockaddr_in6 *sin6 = satosin6(rsa);
133 mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
136 printf("\tvxlan vni %d", vni);
137 printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
139 printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "",
140 dst, ipv6 ? "]" : "", dstport);
142 if (ctx->args->verbose) {
143 printf("\n\t\tconfig: ");
144 printf("%slearning portrange %d-%d ttl %d",
145 cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min,
146 cfg.vxlc_port_max, cfg.vxlc_ttl);
147 printf("\n\t\tftable: ");
148 printf("cnt %d max %d timeout %d",
149 cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max,
150 cfg.vxlc_ftable_timeout);
156 #define _LOCAL_ADDR46 \
157 (VXLAN_PARAM_WITH_LOCAL_ADDR4 | VXLAN_PARAM_WITH_LOCAL_ADDR6)
158 #define _REMOTE_ADDR46 \
159 (VXLAN_PARAM_WITH_REMOTE_ADDR4 | VXLAN_PARAM_WITH_REMOTE_ADDR6)
162 vxlan_check_params(void)
165 if ((params.vxlp_with & _LOCAL_ADDR46) == _LOCAL_ADDR46)
166 errx(1, "cannot specify both local IPv4 and IPv6 addresses");
167 if ((params.vxlp_with & _REMOTE_ADDR46) == _REMOTE_ADDR46)
168 errx(1, "cannot specify both remote IPv4 and IPv6 addresses");
169 if ((params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4 &&
170 params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) ||
171 (params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6 &&
172 params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4))
173 errx(1, "cannot mix IPv4 and IPv6 addresses");
177 #undef _REMOTE_ADDR46
180 vxlan_create(if_ctx *ctx, struct ifreq *ifr)
183 vxlan_check_params();
185 ifr->ifr_data = (caddr_t) ¶ms;
186 ifcreate_ioctl(ctx, ifr);
190 setvxlan_vni(if_ctx *ctx, const char *arg, int dummy __unused)
192 struct ifvxlancmd cmd;
195 if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX)
196 errx(1, "invalid network identifier: %s", arg);
198 if (!vxlan_exists(ctx)) {
199 params.vxlp_with |= VXLAN_PARAM_WITH_VNI;
200 params.vxlp_vni = val;
204 bzero(&cmd, sizeof(cmd));
205 cmd.vxlcmd_vni = val;
207 if (do_cmd(ctx, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0)
208 err(1, "VXLAN_CMD_SET_VNI");
212 setvxlan_local(if_ctx *ctx, const char *addr, int dummy __unused)
214 struct ifvxlancmd cmd;
216 #if (defined INET || defined INET6)
221 bzero(&cmd, sizeof(cmd));
223 if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
224 errx(1, "error in parsing local address string: %s",
225 gai_strerror(error));
227 #if (defined INET || defined INET6)
231 switch (ai->ai_family) {
234 struct sockaddr_in *sin = satosin(sa);
236 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
237 errx(1, "local address cannot be multicast");
239 cmd.vxlcmd_sa.in4 = *sin;
245 struct sockaddr_in6 *sin6 = satosin6(sa);
247 if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
248 errx(1, "local address cannot be multicast");
250 cmd.vxlcmd_sa.in6 = *sin6;
255 errx(1, "local address %s not supported", addr);
260 if (!vxlan_exists(ctx)) {
261 if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
262 params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR4;
263 params.vxlp_local_sa.in4 = cmd.vxlcmd_sa.in4;
265 params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6;
266 params.vxlp_local_sa.in6 = cmd.vxlcmd_sa.in6;
271 if (do_cmd(ctx, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0)
272 err(1, "VXLAN_CMD_SET_LOCAL_ADDR");
276 setvxlan_remote(if_ctx *ctx, const char *addr, int dummy __unused)
278 struct ifvxlancmd cmd;
280 #if (defined INET || defined INET6)
285 bzero(&cmd, sizeof(cmd));
287 if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
288 errx(1, "error in parsing remote address string: %s",
289 gai_strerror(error));
291 #if (defined INET || defined INET6)
295 switch (ai->ai_family) {
298 struct sockaddr_in *sin = satosin(sa);
300 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
301 errx(1, "remote address cannot be multicast");
303 cmd.vxlcmd_sa.in4 = *sin;
309 struct sockaddr_in6 *sin6 = satosin6(sa);
311 if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
312 errx(1, "remote address cannot be multicast");
314 cmd.vxlcmd_sa.in6 = *sin6;
319 errx(1, "remote address %s not supported", addr);
324 if (!vxlan_exists(ctx)) {
325 if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
326 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
327 params.vxlp_remote_sa.in4 = cmd.vxlcmd_sa.in4;
329 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
330 params.vxlp_remote_sa.in6 = cmd.vxlcmd_sa.in6;
335 if (do_cmd(ctx, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
336 err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
340 setvxlan_group(if_ctx *ctx, const char *addr, int dummy __unused)
342 struct ifvxlancmd cmd;
344 #if (defined INET || defined INET6)
349 bzero(&cmd, sizeof(cmd));
351 if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
352 errx(1, "error in parsing group address string: %s",
353 gai_strerror(error));
355 #if (defined INET || defined INET6)
359 switch (ai->ai_family) {
362 struct sockaddr_in *sin = satosin(sa);
364 if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
365 errx(1, "group address must be multicast");
367 cmd.vxlcmd_sa.in4 = *sin;
373 struct sockaddr_in6 *sin6 = satosin6(sa);
375 if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
376 errx(1, "group address must be multicast");
378 cmd.vxlcmd_sa.in6 = *sin6;
383 errx(1, "group address %s not supported", addr);
388 if (!vxlan_exists(ctx)) {
389 if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) {
390 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4;
391 params.vxlp_remote_sa.in4 = cmd.vxlcmd_sa.in4;
393 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6;
394 params.vxlp_remote_sa.in6 = cmd.vxlcmd_sa.in6;
399 if (do_cmd(ctx, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0)
400 err(1, "VXLAN_CMD_SET_REMOTE_ADDR");
404 setvxlan_local_port(if_ctx *ctx, const char *arg, int dummy __unused)
406 struct ifvxlancmd cmd;
409 if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
410 errx(1, "invalid local port: %s", arg);
412 if (!vxlan_exists(ctx)) {
413 params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT;
414 params.vxlp_local_port = val;
418 bzero(&cmd, sizeof(cmd));
419 cmd.vxlcmd_port = val;
421 if (do_cmd(ctx, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0)
422 err(1, "VXLAN_CMD_SET_LOCAL_PORT");
426 setvxlan_remote_port(if_ctx *ctx, const char *arg, int dummy __unused)
428 struct ifvxlancmd cmd;
431 if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
432 errx(1, "invalid remote port: %s", arg);
434 if (!vxlan_exists(ctx)) {
435 params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT;
436 params.vxlp_remote_port = val;
440 bzero(&cmd, sizeof(cmd));
441 cmd.vxlcmd_port = val;
443 if (do_cmd(ctx, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0)
444 err(1, "VXLAN_CMD_SET_REMOTE_PORT");
448 setvxlan_port_range(if_ctx *ctx, const char *arg1, const char *arg2)
450 struct ifvxlancmd cmd;
453 if (get_val(arg1, &min) < 0 || min >= UINT16_MAX)
454 errx(1, "invalid port range minimum: %s", arg1);
455 if (get_val(arg2, &max) < 0 || max >= UINT16_MAX)
456 errx(1, "invalid port range maximum: %s", arg2);
458 errx(1, "invalid port range");
460 if (!vxlan_exists(ctx)) {
461 params.vxlp_with |= VXLAN_PARAM_WITH_PORT_RANGE;
462 params.vxlp_min_port = min;
463 params.vxlp_max_port = max;
467 bzero(&cmd, sizeof(cmd));
468 cmd.vxlcmd_port_min = min;
469 cmd.vxlcmd_port_max = max;
471 if (do_cmd(ctx, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0)
472 err(1, "VXLAN_CMD_SET_PORT_RANGE");
476 setvxlan_timeout(if_ctx *ctx, const char *arg, int dummy __unused)
478 struct ifvxlancmd cmd;
481 if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
482 errx(1, "invalid timeout value: %s", arg);
484 if (!vxlan_exists(ctx)) {
485 params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT;
486 params.vxlp_ftable_timeout = val & 0xFFFFFFFF;
490 bzero(&cmd, sizeof(cmd));
491 cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF;
493 if (do_cmd(ctx, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0)
494 err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT");
498 setvxlan_maxaddr(if_ctx *ctx, const char *arg, int dummy __unused)
500 struct ifvxlancmd cmd;
503 if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
504 errx(1, "invalid maxaddr value: %s", arg);
506 if (!vxlan_exists(ctx)) {
507 params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX;
508 params.vxlp_ftable_max = val & 0xFFFFFFFF;
512 bzero(&cmd, sizeof(cmd));
513 cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF;
515 if (do_cmd(ctx, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0)
516 err(1, "VXLAN_CMD_SET_FTABLE_MAX");
520 setvxlan_dev(if_ctx *ctx, const char *arg, int dummy __unused)
522 struct ifvxlancmd cmd;
524 if (!vxlan_exists(ctx)) {
525 params.vxlp_with |= VXLAN_PARAM_WITH_MULTICAST_IF;
526 strlcpy(params.vxlp_mc_ifname, arg,
527 sizeof(params.vxlp_mc_ifname));
531 bzero(&cmd, sizeof(cmd));
532 strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname));
534 if (do_cmd(ctx, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0)
535 err(1, "VXLAN_CMD_SET_MULTICAST_IF");
539 setvxlan_ttl(if_ctx *ctx, const char *arg, int dummy __unused)
541 struct ifvxlancmd cmd;
544 if (get_val(arg, &val) < 0 || val > 256)
545 errx(1, "invalid TTL value: %s", arg);
547 if (!vxlan_exists(ctx)) {
548 params.vxlp_with |= VXLAN_PARAM_WITH_TTL;
549 params.vxlp_ttl = val;
553 bzero(&cmd, sizeof(cmd));
554 cmd.vxlcmd_ttl = val;
556 if (do_cmd(ctx, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0)
557 err(1, "VXLAN_CMD_SET_TTL");
561 setvxlan_learn(if_ctx *ctx, const char *arg __unused, int d)
563 struct ifvxlancmd cmd;
565 if (!vxlan_exists(ctx)) {
566 params.vxlp_with |= VXLAN_PARAM_WITH_LEARN;
567 params.vxlp_learn = d;
571 bzero(&cmd, sizeof(cmd));
573 cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN;
575 if (do_cmd(ctx, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0)
576 err(1, "VXLAN_CMD_SET_LEARN");
580 setvxlan_flush(if_ctx *ctx, const char *val __unused, int d)
582 struct ifvxlancmd cmd;
584 bzero(&cmd, sizeof(cmd));
586 cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL;
588 if (do_cmd(ctx, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0)
589 err(1, "VXLAN_CMD_FLUSH");
592 static struct cmd vxlan_cmds[] = {
594 DEF_CLONE_CMD_ARG("vni", setvxlan_vni),
595 DEF_CLONE_CMD_ARG("vxlanid", setvxlan_vni),
596 DEF_CLONE_CMD_ARG("vxlanlocal", setvxlan_local),
597 DEF_CLONE_CMD_ARG("vxlanremote", setvxlan_remote),
598 DEF_CLONE_CMD_ARG("vxlangroup", setvxlan_group),
599 DEF_CLONE_CMD_ARG("vxlanlocalport", setvxlan_local_port),
600 DEF_CLONE_CMD_ARG("vxlanremoteport", setvxlan_remote_port),
601 DEF_CLONE_CMD_ARG2("vxlanportrange", setvxlan_port_range),
602 DEF_CLONE_CMD_ARG("vxlantimeout", setvxlan_timeout),
603 DEF_CLONE_CMD_ARG("vxlanmaxaddr", setvxlan_maxaddr),
604 DEF_CLONE_CMD_ARG("vxlandev", setvxlan_dev),
605 DEF_CLONE_CMD_ARG("vxlanttl", setvxlan_ttl),
606 DEF_CLONE_CMD("vxlanlearn", 1, setvxlan_learn),
607 DEF_CLONE_CMD("-vxlanlearn", 0, setvxlan_learn),
609 DEF_CMD_ARG("vni", setvxlan_vni),
610 DEF_CMD_ARG("vxlanid", setvxlan_vni),
611 DEF_CMD_ARG("vxlanlocal", setvxlan_local),
612 DEF_CMD_ARG("vxlanremote", setvxlan_remote),
613 DEF_CMD_ARG("vxlangroup", setvxlan_group),
614 DEF_CMD_ARG("vxlanlocalport", setvxlan_local_port),
615 DEF_CMD_ARG("vxlanremoteport", setvxlan_remote_port),
616 DEF_CMD_ARG2("vxlanportrange", setvxlan_port_range),
617 DEF_CMD_ARG("vxlantimeout", setvxlan_timeout),
618 DEF_CMD_ARG("vxlanmaxaddr", setvxlan_maxaddr),
619 DEF_CMD_ARG("vxlandev", setvxlan_dev),
620 DEF_CMD_ARG("vxlanttl", setvxlan_ttl),
621 DEF_CMD("vxlanlearn", 1, setvxlan_learn),
622 DEF_CMD("-vxlanlearn", 0, setvxlan_learn),
624 DEF_CMD("vxlanflush", 0, setvxlan_flush),
625 DEF_CMD("vxlanflushall", 1, setvxlan_flush),
627 DEF_CMD("vxlanhwcsum", IFCAP_VXLAN_HWCSUM, setifcap),
628 DEF_CMD("-vxlanhwcsum", IFCAP_VXLAN_HWCSUM, clearifcap),
629 DEF_CMD("vxlanhwtso", IFCAP_VXLAN_HWTSO, setifcap),
630 DEF_CMD("-vxlanhwtso", IFCAP_VXLAN_HWTSO, clearifcap),
633 static struct afswtch af_vxlan = {
634 .af_name = "af_vxlan",
636 .af_other_status = vxlan_status,
639 static __constructor void
644 for (i = 0; i < nitems(vxlan_cmds); i++)
645 cmd_register(&vxlan_cmds[i]);
646 af_register(&af_vxlan);
647 clone_setdefcallback_prefix("vxlan", vxlan_create);