Index: sbin/ifconfig/ifconfig.8 =================================================================== --- sbin/ifconfig/ifconfig.8 +++ sbin/ifconfig/ifconfig.8 @@ -2734,13 +2734,18 @@ When the interface is configured in unicast mode, the listening socket is bound to this address. .It Cm vxlanremote Ar address -The interface can be configured in a unicast, or point-to-point, mode -to create a tunnel between two hosts. -This is the IP address of the remote end of the tunnel. +The interface can be configured in a unicast mode, where frames without a +learned address are flooded to specific remote hosts. This adds the specified +address to the list of destination hosts. +.It Cm -vxlanremote Ar address +Remove the specified address from the list of destination unicast hosts. .It Cm vxlangroup Ar address The interface can be configured in a multicast mode to create a virtual network of hosts. This is the IP multicast group address the interface will join. +The multicast destination will not be used if any unicast destinations are +specified by +.Cm vxlanremote . .It Cm vxlanlocalport Ar port The port number the interface will listen on. The default port number is 4789. Index: sbin/ifconfig/ifvxlan.c =================================================================== --- sbin/ifconfig/ifvxlan.c +++ sbin/ifconfig/ifvxlan.c @@ -98,13 +98,52 @@ } static void +vxlan_status_remotes(int s) +{ + struct ifvxlanreqremotes rmts; + struct ifvxlanreqremote *rmt; + struct sockaddr *rsa; + char dst[NI_MAXHOST], dstport[NI_MAXSERV]; + char *inbuf = NULL, *ninbuf; + int i, len = 2048, ipv6; + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate remotes buffer"); + rmts.vxlr_len = len; + rmts.vxlr_buf = inbuf = ninbuf; + if (do_cmd(s, VXLAN_CMD_GET_REMOTES, &rmts, sizeof(rmts), 0) < 0) + err(1, "unable to get list of remotes"); + if ((rmts.vxlr_len + sizeof(*rmt)) < len) + break; + len *= 2; + } + + for (i = 0; i < (rmts.vxlr_len / sizeof(*rmt)); i++) { + rmt = rmts.vxlr_rmt + i; + rsa = &rmt->vxlr_sa.sa; + ipv6 = rsa->sa_family == AF_INET6; + + if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst), + dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) + dst[0] = dstport[0] = '\0'; + + printf("\n\t\t\t%s%s%s", + ipv6 ? "[" : "", dst, ipv6 ? "]" : ""); + } + + free(inbuf); +} + +static void vxlan_status(int s) { struct ifvxlancfg cfg; - char src[NI_MAXHOST], dst[NI_MAXHOST]; + char src[NI_MAXHOST], group[NI_MAXHOST]; char srcport[NI_MAXSERV], dstport[NI_MAXSERV]; - struct sockaddr *lsa, *rsa; - int vni, mc, ipv6; + struct sockaddr *lsa, *msa; + int vni, ipv6; bzero(&cfg, sizeof(cfg)); @@ -113,8 +152,8 @@ vni = cfg.vxlc_vni; lsa = &cfg.vxlc_local_sa.sa; - rsa = &cfg.vxlc_remote_sa.sa; - ipv6 = rsa->sa_family == AF_INET6; + msa = &cfg.vxlc_mcast_sa.sa; + ipv6 = lsa->sa_family == AF_INET6; /* Just report nothing if the network identity isn't set yet. */ if (vni >= VXLAN_VNI_MAX) @@ -123,33 +162,30 @@ if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src), srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) src[0] = srcport[0] = '\0'; - if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst), + if (getnameinfo(msa, msa->sa_len, group, sizeof(group), dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) - dst[0] = dstport[0] = '\0'; + group[0] = dstport[0] = '\0'; - if (!ipv6) { - struct sockaddr_in *sin = (struct sockaddr_in *)rsa; - mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr)); - } else { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rsa; - mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr); - } - printf("\tvxlan vni %d", vni); printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "", srcport); - printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "", - dst, ipv6 ? "]" : "", dstport); + if (cfg.vxlc_remote_cnt == 0) + printf(" group %s%s%s", + ipv6 ? "[" : "", + group, ipv6 ? "]" : ""); + if (verbose) { printf("\n\t\tconfig: "); - printf("%slearning portrange %d-%d ttl %d", + printf("%slearning portrange %d-%d ttl %d rport %d", cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min, - cfg.vxlc_port_max, cfg.vxlc_ttl); + cfg.vxlc_port_max, cfg.vxlc_ttl, cfg.vxlc_remote_port); printf("\n\t\tftable: "); printf("cnt %d max %d timeout %d", cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max, cfg.vxlc_ftable_timeout); + printf("\n\t\tflooding: %d hosts", cfg.vxlc_remote_cnt); + vxlan_status_remotes(s); } putchar('\n'); @@ -338,6 +374,55 @@ } static +DECL_CMD_FUNC(unsetvxlan_remote, addr, d) +{ + struct ifvxlancmd cmd; + struct addrinfo *ai; + struct sockaddr *sa; + int error; + + bzero(&cmd, sizeof(cmd)); + + if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) + errx(1, "error in parsing remote address string: %s", + gai_strerror(error)); + + sa = ai->ai_addr; + + switch (ai->ai_family) { +#ifdef INET + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) + errx(1, "remote address cannot be multicast"); + + cmd.vxlcmd_sa.in4 = *sin; + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + errx(1, "remote address cannot be multicast"); + + cmd.vxlcmd_sa.in6 = *sin6; + break; + } +#endif + default: + errx(1, "remote address %s not supported", addr); + } + + freeaddrinfo(ai); + + if (do_cmd(s, VXLAN_CMD_UNSET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_UNSET_REMOTE_ADDR"); +} + +static DECL_CMD_FUNC(setvxlan_group, addr, d) { struct ifvxlancmd cmd; @@ -384,17 +469,17 @@ if (!vxlan_exists(s)) { if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { - params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4; - params.vxlp_remote_sa.in4 = cmd.vxlcmd_sa.in4; + params.vxlp_with |= VXLAN_PARAM_WITH_MCAST_GROUP4; + params.vxlp_mcast_group4 = cmd.vxlcmd_sa.in4.sin_addr; } else { - params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6; - params.vxlp_remote_sa.in6 = cmd.vxlcmd_sa.in6; + params.vxlp_with |= VXLAN_PARAM_WITH_MCAST_GROUP6; + params.vxlp_mcast_group6 = cmd.vxlcmd_sa.in6.sin6_addr; } return; } - if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0) - err(1, "VXLAN_CMD_SET_REMOTE_ADDR"); + if (do_cmd(s, VXLAN_CMD_SET_MULTICAST_GROUP, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_MULTICAST_GROUP"); } static @@ -607,6 +692,7 @@ DEF_CMD_ARG("vxlanid", setvxlan_vni), DEF_CMD_ARG("vxlanlocal", setvxlan_local), DEF_CMD_ARG("vxlanremote", setvxlan_remote), + DEF_CMD_ARG("-vxlanremote", unsetvxlan_remote), DEF_CMD_ARG("vxlangroup", setvxlan_group), DEF_CMD_ARG("vxlanlocalport", setvxlan_local_port), DEF_CMD_ARG("vxlanremoteport", setvxlan_remote_port), Index: sys/net/if_vxlan.h =================================================================== --- sys/net/if_vxlan.h +++ sys/net/if_vxlan.h @@ -73,9 +73,11 @@ #define VXLAN_PARAM_WITH_PORT_RANGE 0x0080 #define VXLAN_PARAM_WITH_FTABLE_TIMEOUT 0x0100 #define VXLAN_PARAM_WITH_FTABLE_MAX 0x0200 -#define VXLAN_PARAM_WITH_MULTICAST_IF 0x0400 -#define VXLAN_PARAM_WITH_TTL 0x0800 -#define VXLAN_PARAM_WITH_LEARN 0x1000 +#define VXLAN_PARAM_WITH_MCAST_GROUP4 0x0400 +#define VXLAN_PARAM_WITH_MCAST_GROUP6 0x0800 +#define VXLAN_PARAM_WITH_MULTICAST_IF 0x1000 +#define VXLAN_PARAM_WITH_TTL 0x2000 +#define VXLAN_PARAM_WITH_LEARN 0x4000 uint32_t vxlp_vni; union vxlan_sockaddr vxlp_local_sa; @@ -85,6 +87,8 @@ uint16_t vxlp_min_port; uint16_t vxlp_max_port; char vxlp_mc_ifname[IFNAMSIZ]; + struct in_addr vxlp_mcast_group4; + struct in6_addr vxlp_mcast_group6; uint32_t vxlp_ftable_timeout; uint32_t vxlp_ftable_max; uint8_t vxlp_ttl; @@ -99,31 +103,50 @@ #define VXLAN_CMD_GET_CONFIG 0 #define VXLAN_CMD_SET_VNI 1 #define VXLAN_CMD_SET_LOCAL_ADDR 2 -#define VXLAN_CMD_SET_REMOTE_ADDR 4 -#define VXLAN_CMD_SET_LOCAL_PORT 5 -#define VXLAN_CMD_SET_REMOTE_PORT 6 -#define VXLAN_CMD_SET_PORT_RANGE 7 -#define VXLAN_CMD_SET_FTABLE_TIMEOUT 8 -#define VXLAN_CMD_SET_FTABLE_MAX 9 -#define VXLAN_CMD_SET_MULTICAST_IF 10 -#define VXLAN_CMD_SET_TTL 11 -#define VXLAN_CMD_SET_LEARN 12 -#define VXLAN_CMD_FTABLE_ENTRY_ADD 13 -#define VXLAN_CMD_FTABLE_ENTRY_REM 14 -#define VXLAN_CMD_FLUSH 15 +#define VXLAN_CMD_SET_REMOTE_ADDR 3 +#define VXLAN_CMD_UNSET_REMOTE_ADDR 4 +#define VXLAN_CMD_GET_REMOTES 5 +#define VXLAN_CMD_SET_LOCAL_PORT 6 +#define VXLAN_CMD_SET_REMOTE_PORT 7 +#define VXLAN_CMD_SET_PORT_RANGE 8 +#define VXLAN_CMD_SET_FTABLE_TIMEOUT 9 +#define VXLAN_CMD_SET_FTABLE_MAX 10 +#define VXLAN_CMD_SET_MULTICAST_GROUP 11 +#define VXLAN_CMD_SET_MULTICAST_IF 12 +#define VXLAN_CMD_SET_TTL 13 +#define VXLAN_CMD_SET_LEARN 14 +#define VXLAN_CMD_FTABLE_ENTRY_ADD 15 +#define VXLAN_CMD_FTABLE_ENTRY_REM 16 +#define VXLAN_CMD_FLUSH 17 struct ifvxlancfg { uint32_t vxlc_vni; union vxlan_sockaddr vxlc_local_sa; - union vxlan_sockaddr vxlc_remote_sa; + union vxlan_sockaddr vxlc_mcast_sa; uint32_t vxlc_mc_ifindex; uint32_t vxlc_ftable_cnt; uint32_t vxlc_ftable_max; uint32_t vxlc_ftable_timeout; + uint32_t vxlc_remote_cnt; + uint16_t vxlc_remote_port; uint16_t vxlc_port_min; uint16_t vxlc_port_max; uint8_t vxlc_learn; uint8_t vxlc_ttl; +}; + +struct ifvxlanreqremote { + union vxlan_sockaddr vxlr_sa; +}; + +struct ifvxlanreqremotes { + uint32_t vxlr_len; + union { + caddr_t vxlru_buf; + struct ifvxlanreqremote *vxlru_rmt; + } vxlr_u; +#define vxlr_buf vxlr_u.vxlru_buf +#define vxlr_rmt vxlr_u.vxlru_rmt }; struct ifvxlancmd { Index: sys/net/if_vxlan.c =================================================================== --- sys/net/if_vxlan.c +++ sys/net/if_vxlan.c @@ -139,18 +139,27 @@ uint32_t ftable_lock_upgrade_failed; }; +struct vxlan_rmtlist_entry { + LIST_ENTRY(vxlan_rmtlist_entry) vxlre_next; + union vxlan_sockaddr vxlre_raddr; +}; + +#define VXLAN_REMOTES_MAX 64 + struct vxlan_softc { struct ifnet *vxl_ifp; struct vxlan_socket *vxl_sock; uint32_t vxl_vni; union vxlan_sockaddr vxl_src_addr; - union vxlan_sockaddr vxl_dst_addr; + LIST_HEAD(, vxlan_rmtlist_entry) vxl_rmtlist; + uint32_t vxl_rmtlist_cnt; uint32_t vxl_flags; #define VXLAN_FLAG_INIT 0x0001 #define VXLAN_FLAG_TEARDOWN 0x0002 #define VXLAN_FLAG_LEARN 0x0004 uint32_t vxl_port_hash_key; + uint16_t vxl_dst_port; uint16_t vxl_min_port; uint16_t vxl_max_port; uint8_t vxl_ttl; @@ -162,9 +171,7 @@ uint32_t vxl_ftable_hash_key; struct vxlan_ftable_head *vxl_ftable; - /* Derived from vxl_dst_addr. */ - struct vxlan_ftable_entry vxl_default_fe; - + union vxlan_sockaddr vxl_mcast_addr; struct ip_moptions *vxl_im4o; struct ip6_moptions *vxl_im6o; @@ -239,6 +246,12 @@ static void vxlan_ftable_entry_dump(struct vxlan_ftable_entry *, struct sbuf *); +static int vxlan_rmtlist_entry_add(struct vxlan_softc *, + struct vxlan_rmtlist_entry *); +static int vxlan_rmtlist_entry_remove(struct vxlan_softc *, + union vxlan_sockaddr *); +static void vxlan_rmtlist_flush(struct vxlan_softc *); + static struct vxlan_socket * vxlan_socket_alloc(const union vxlan_sockaddr *); static void vxlan_socket_destroy(struct vxlan_socket *); @@ -306,11 +319,14 @@ static int vxlan_ctrl_set_vni(struct vxlan_softc *, void *); static int vxlan_ctrl_set_local_addr(struct vxlan_softc *, void *); static int vxlan_ctrl_set_remote_addr(struct vxlan_softc *, void *); +static int vxlan_ctrl_unset_remote_addr(struct vxlan_softc *, void *); +static int vxlan_ctrl_get_remotes(struct vxlan_softc *, void *); static int vxlan_ctrl_set_local_port(struct vxlan_softc *, void *); static int vxlan_ctrl_set_remote_port(struct vxlan_softc *, void *); static int vxlan_ctrl_set_port_range(struct vxlan_softc *, void *); static int vxlan_ctrl_set_ftable_timeout(struct vxlan_softc *, void *); static int vxlan_ctrl_set_ftable_max(struct vxlan_softc *, void *); +static int vxlan_ctrl_set_multicast_group(struct vxlan_softc * , void *); static int vxlan_ctrl_set_multicast_if(struct vxlan_softc * , void *); static int vxlan_ctrl_set_ttl(struct vxlan_softc *, void *); static int vxlan_ctrl_set_learn(struct vxlan_softc *, void *); @@ -454,6 +470,16 @@ VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, }, + [VXLAN_CMD_UNSET_REMOTE_ADDR] = + { vxlan_ctrl_unset_remote_addr, sizeof(struct ifvxlancmd), + VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, + }, + + [VXLAN_CMD_GET_REMOTES] = + { vxlan_ctrl_get_remotes, sizeof(struct ifvxlanreqremotes), + VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_COPYOUT, + }, + [VXLAN_CMD_SET_LOCAL_PORT] = { vxlan_ctrl_set_local_port, sizeof(struct ifvxlancmd), VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, @@ -479,6 +505,11 @@ VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, }, + [VXLAN_CMD_SET_MULTICAST_GROUP] = + { vxlan_ctrl_set_multicast_group, sizeof(struct ifvxlancmd), + VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, + }, + [VXLAN_CMD_SET_MULTICAST_IF] = { vxlan_ctrl_set_multicast_if, sizeof(struct ifvxlancmd), VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, @@ -648,12 +679,8 @@ union vxlan_sockaddr vxlsa; int error; - /* - * The source port may be randomly selected by the remote host, so - * use the port of the default destination address. - */ vxlan_sockaddr_copy(&vxlsa, sa); - vxlsa.in4.sin_port = sc->vxl_dst_addr.in4.sin_port; + vxlsa.in4.sin_port = sc->vxl_dst_port; if (VXLAN_SOCKADDR_IS_IPV6(&vxlsa)) { error = vxlan_sockaddr_in6_embedscope(&vxlsa); @@ -843,6 +870,80 @@ sbuf_setpos(sb, len); } +static int +vxlan_rmtlist_entry_add(struct vxlan_softc *sc, + struct vxlan_rmtlist_entry *re) +{ + struct vxlan_rmtlist_entry *lre; + int found; + + VXLAN_LOCK_WASSERT(sc); + + if (sc->vxl_rmtlist_cnt == VXLAN_REMOTES_MAX) + return (ENOSPC); + + found = 0; + LIST_FOREACH(lre, &sc->vxl_rmtlist, vxlre_next) { + if (bcmp(&re->vxlre_raddr.sa, &lre->vxlre_raddr.sa, + re->vxlre_raddr.sa.sa_len) == 0) { + found++; + break; + } + } + + if (found) + return (EEXIST); + + LIST_INSERT_HEAD(&sc->vxl_rmtlist, re, vxlre_next); + sc->vxl_rmtlist_cnt++; + + return (0); +} + +static int +vxlan_rmtlist_entry_remove(struct vxlan_softc *sc, + union vxlan_sockaddr *vxlsa) +{ + struct vxlan_rmtlist_entry *lre, *tre; + int found; + + VXLAN_LOCK_WASSERT(sc); + + if (LIST_EMPTY(&sc->vxl_rmtlist)) + return (ENOENT); + + found = 0; + LIST_FOREACH_SAFE(lre, &sc->vxl_rmtlist, vxlre_next, tre) { + if (bcmp(&vxlsa->sa, &lre->vxlre_raddr.sa, + vxlsa->sa.sa_len) == 0) { + LIST_REMOVE(lre, vxlre_next); + free(lre, M_VXLAN); + sc->vxl_rmtlist_cnt--; + found++; + break; + } + } + + if (!found) + return (ENOENT); + + return (0); +} + +static void +vxlan_rmtlist_flush(struct vxlan_softc *sc) +{ + struct vxlan_rmtlist_entry *lre, *tre; + + VXLAN_LOCK_WASSERT(sc); + + LIST_FOREACH_SAFE(lre, &sc->vxl_rmtlist, vxlre_next, tre) { + LIST_REMOVE(lre, vxlre_next); + free(lre, M_VXLAN); + sc->vxl_rmtlist_cnt--; + } +} + static struct vxlan_socket * vxlan_socket_alloc(const union vxlan_sockaddr *sa) { @@ -1443,7 +1544,7 @@ const union vxlan_sockaddr *group; int error; - group = &sc->vxl_dst_addr; + group = &sc->vxl_mcast_addr; error = 0; if (sc->vxl_mc_ifname[0] != '\0') { @@ -1479,16 +1580,16 @@ { struct vxlan_socket *vso; struct ifnet *ifp; - union vxlan_sockaddr *saddr, *daddr; - int multicast, error; + union vxlan_sockaddr *saddr, *gaddr; + int multicast = 0, error; vso = NULL; ifp = sc->vxl_ifp; saddr = &sc->vxl_src_addr; - daddr = &sc->vxl_dst_addr; + gaddr = &sc->vxl_mcast_addr; - multicast = vxlan_sockaddr_in_multicast(daddr); - MPASS(multicast != -1); + if (gaddr->sa.sa_len != 0) + multicast = 1; sc->vxl_vso_mc_index = -1; /* @@ -1514,7 +1615,7 @@ if (error) goto out; - error = vxlan_socket_mc_add_group(vso, daddr, saddr, + error = vxlan_socket_mc_add_group(vso, gaddr, saddr, sc->vxl_mc_ifindex, &sc->vxl_vso_mc_index); if (error) goto out; @@ -1554,9 +1655,9 @@ ifp = sc->vxl_ifp; ifp->if_hdrlen = ETHER_HDR_LEN + sizeof(struct vxlanudphdr); - if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_dst_addr) != 0) + if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_src_addr) != 0) ifp->if_hdrlen += sizeof(struct ip); - else if (VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_dst_addr) != 0) + else if (VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_src_addr) != 0) ifp->if_hdrlen += sizeof(struct ip6_hdr); } @@ -1575,37 +1676,18 @@ goto fail; } - if (vxlan_sockaddr_supported(&sc->vxl_dst_addr, 0) == 0) { - reason = "destination address type is not supported"; - goto fail; - } - - if (vxlan_sockaddr_in_any(&sc->vxl_dst_addr) != 0) { - reason = "no valid destination address specified"; - goto fail; - } - - if (vxlan_sockaddr_in_multicast(&sc->vxl_dst_addr) == 0 && + if (vxlan_sockaddr_in_any(&sc->vxl_mcast_addr) != 0 && sc->vxl_mc_ifname[0] != '\0') { reason = "can only specify interface with a group address"; goto fail; } - if (vxlan_sockaddr_in_any(&sc->vxl_src_addr) == 0) { - if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_src_addr) ^ - VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_dst_addr)) { - reason = "source and destination address must both " - "be either IPv4 or IPv6"; - goto fail; - } - } - if (sc->vxl_src_addr.in4.sin_port == 0) { reason = "local port not specified"; goto fail; } - if (sc->vxl_dst_addr.in4.sin_port == 0) { + if (sc->vxl_dst_port == 0) { reason = "remote port not specified"; goto fail; } @@ -1639,7 +1721,6 @@ static void vxlan_init(void *xsc) { - static const uint8_t empty_mac[ETHER_ADDR_LEN]; struct vxlan_softc *sc; struct ifnet *ifp; @@ -1662,10 +1743,6 @@ if (vxlan_setup_socket(sc) != 0) goto out; - /* Initialize the default forwarding entry. */ - vxlan_ftable_entry_init(sc, &sc->vxl_default_fe, empty_mac, - &sc->vxl_dst_addr.sa, VXLAN_FE_FLAG_STATIC); - VXLAN_WLOCK(sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; callout_reset(&sc->vxl_callout, vxlan_ftable_prune_period * hz, @@ -1831,12 +1908,14 @@ cfg->vxlc_vni = sc->vxl_vni; memcpy(&cfg->vxlc_local_sa, &sc->vxl_src_addr, sizeof(union vxlan_sockaddr)); - memcpy(&cfg->vxlc_remote_sa, &sc->vxl_dst_addr, + memcpy(&cfg->vxlc_mcast_sa, &sc->vxl_mcast_addr, sizeof(union vxlan_sockaddr)); cfg->vxlc_mc_ifindex = sc->vxl_mc_ifindex; cfg->vxlc_ftable_cnt = sc->vxl_ftable_cnt; cfg->vxlc_ftable_max = sc->vxl_ftable_max; cfg->vxlc_ftable_timeout = sc->vxl_ftable_timeout; + cfg->vxlc_remote_cnt = sc->vxl_rmtlist_cnt; + cfg->vxlc_remote_port = sc->vxl_dst_port; cfg->vxlc_port_min = sc->vxl_min_port; cfg->vxlc_port_max = sc->vxl_max_port; cfg->vxlc_learn = (sc->vxl_flags & VXLAN_FLAG_LEARN) != 0; @@ -1846,8 +1925,6 @@ #ifdef INET6 if (VXLAN_SOCKADDR_IS_IPV6(&cfg->vxlc_local_sa)) sa6_recoverscope(&cfg->vxlc_local_sa.in6); - if (VXLAN_SOCKADDR_IS_IPV6(&cfg->vxlc_remote_sa)) - sa6_recoverscope(&cfg->vxlc_remote_sa.in6); #endif return (0); @@ -1895,6 +1972,12 @@ return (error); } + if (sc->vxl_rmtlist_cnt != 0 && + vxlsa->sa.sa_family != sc->vxl_src_addr.sa.sa_family) { + if_printf(sc->vxl_ifp, "removed all remote hosts because source address family changed\n"); + vxlan_rmtlist_flush(sc); + } + VXLAN_WLOCK(sc); if (vxlan_can_change_config(sc)) { vxlan_sockaddr_in_copy(&sc->vxl_src_addr, &vxlsa->sa); @@ -1911,11 +1994,56 @@ { struct ifvxlancmd *cmd; union vxlan_sockaddr *vxlsa; + struct vxlan_rmtlist_entry *re; int error; + const char *reason; cmd = arg; vxlsa = &cmd->vxlcmd_sa; + if (vxlan_sockaddr_supported(vxlsa, 0) == 0) { + reason = "destination address type is not supported"; + goto fail; + } + + if (vxlan_sockaddr_in_any(vxlsa) != 0) { + reason = "no valid destination address specified"; + goto fail; + } + + if (vxlan_sockaddr_in_any(&sc->vxl_src_addr) == 0) { + if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_src_addr) ^ + VXLAN_SOCKADDR_IS_IPV4(vxlsa)) { + reason = "source and destination address must both " + "be either IPv4 or IPv6"; + goto fail; + } + } + + re = malloc(sizeof(struct vxlan_rmtlist_entry), M_VXLAN, M_WAITOK | M_ZERO); + + VXLAN_WLOCK(sc); + vxlan_sockaddr_in_copy(&re->vxlre_raddr, &vxlsa->sa); + error = vxlan_rmtlist_entry_add(sc, re); + VXLAN_WUNLOCK(sc); + + return (error); + +fail: + if_printf(sc->vxl_ifp, "cannot add remote: %s\n", reason); + return (EINVAL); +} + +static int +vxlan_ctrl_unset_remote_addr(struct vxlan_softc *sc, void *arg) +{ + struct ifvxlancmd *cmd; + union vxlan_sockaddr *vxlsa; + int error; + + cmd = arg; + vxlsa = &cmd->vxlcmd_sa; + if (!VXLAN_SOCKADDR_IS_IPV46(vxlsa)) return (EINVAL); if (VXLAN_SOCKADDR_IS_IPV6(vxlsa)) { @@ -1925,17 +2053,70 @@ } VXLAN_WLOCK(sc); - if (vxlan_can_change_config(sc)) { - vxlan_sockaddr_in_copy(&sc->vxl_dst_addr, &vxlsa->sa); - error = 0; - } else - error = EBUSY; + error = vxlan_rmtlist_entry_remove(sc, vxlsa); VXLAN_WUNLOCK(sc); return (error); } static int +vxlan_ctrl_get_remotes(struct vxlan_softc *sc, void *arg) +{ + struct rm_priotracker tracker; + struct vxlan_rmtlist_entry *re; + struct ifvxlanreqremotes *rmts; + struct ifvxlanreqremote rmt; + int count, buflen, len, error; + char *buf, *outbuf; + + rmts = arg; + + VXLAN_RLOCK(sc, &tracker); + count = 0; + LIST_FOREACH(re, &sc->vxl_rmtlist, vxlre_next) + count++; + VXLAN_RUNLOCK(sc, &tracker); + + buflen = sizeof(rmt) * count; + if (rmts->vxlr_len == 0) { + rmts->vxlr_len = buflen; + return (0); + } + + outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); + + count = 0; + buf = outbuf; + len = min(rmts->vxlr_len, buflen); + + VXLAN_RLOCK(sc, &tracker); + LIST_FOREACH(re, &sc->vxl_rmtlist, vxlre_next) { + bzero(&rmt, sizeof(rmt)); + if (len < sizeof(rmt)) + break; + + memcpy(&rmt.vxlr_sa, &re->vxlre_raddr.sa, + sizeof(union vxlan_sockaddr)); + +#ifdef INET6 + if (VXLAN_SOCKADDR_IS_IPV6(&rmt.vxlr_sa)) + sa6_recoverscope(&rmt.vxlr_sa.in6); +#endif + + memcpy(buf, &rmt, sizeof(rmt)); + count++; + buf += sizeof(rmt); + len -= sizeof(rmt); + } + VXLAN_RUNLOCK(sc, &tracker); + + rmts->vxlr_len = sizeof(rmt) * count; + error = copyout(outbuf, rmts->vxlr_buf, rmts->vxlr_len); + free(outbuf, M_TEMP); + return (error); +} + +static int vxlan_ctrl_set_local_port(struct vxlan_softc *sc, void *arg) { struct ifvxlancmd *cmd; @@ -1970,7 +2151,7 @@ VXLAN_WLOCK(sc); if (vxlan_can_change_config(sc)) { - sc->vxl_dst_addr.in4.sin_port = htons(cmd->vxlcmd_port); + sc->vxl_dst_port = cmd->vxlcmd_port; error = 0; } else error = EBUSY; @@ -2044,6 +2225,34 @@ } static int +vxlan_ctrl_set_multicast_group(struct vxlan_softc *sc, void *arg) +{ + struct ifvxlancmd *cmd; + union vxlan_sockaddr *vxlsa; + int error; + + cmd = arg; + vxlsa = &cmd->vxlcmd_sa; + + if (!VXLAN_SOCKADDR_IS_IPV46(vxlsa)) + return (EINVAL); + if (vxlan_sockaddr_in_any(vxlsa) != 0) + return (EINVAL); + if (vxlan_sockaddr_in_multicast(vxlsa) == 0) + return (EINVAL); + + VXLAN_WLOCK(sc); + if (vxlan_can_change_config(sc)) { + vxlan_sockaddr_in_copy(&sc->vxl_mcast_addr, &vxlsa->sa); + error = 0; + } else + error = EBUSY; + VXLAN_WUNLOCK(sc); + + return (error); +} + +static int vxlan_ctrl_set_multicast_if(struct vxlan_softc * sc, void *arg) { struct ifvxlancmd *cmd; @@ -2120,7 +2329,7 @@ if (vxlan_sockaddr_in_multicast(&vxlsa) != 0) return (EINVAL); /* BMV: We could support both IPv4 and IPv6 later. */ - if (vxlsa.sa.sa_family != sc->vxl_dst_addr.sa.sa_family) + if (vxlsa.sa.sa_family != sc->vxl_src_addr.sa.sa_family) return (EAFNOSUPPORT); if (VXLAN_SOCKADDR_IS_IPV6(&vxlsa)) { @@ -2133,9 +2342,6 @@ if (fe == NULL) return (ENOMEM); - if (vxlsa.in4.sin_port == 0) - vxlsa.in4.sin_port = sc->vxl_dst_addr.in4.sin_port; - vxlan_ftable_entry_init(sc, fe, cmd->vxlcmd_mac, &vxlsa.sa, VXLAN_FE_FLAG_STATIC); @@ -2333,7 +2539,7 @@ srcaddr = sc->vxl_src_addr.in4.sin_addr; srcport = vxlan_pick_source_port(sc, m); dstaddr = fvxlsa->in4.sin_addr; - dstport = fvxlsa->in4.sin_port; + dstport = htons(sc->vxl_dst_port); M_PREPEND(m, sizeof(struct ip) + sizeof(struct vxlanudphdr), M_NOWAIT); @@ -2390,7 +2596,7 @@ srcaddr = &sc->vxl_src_addr.in6.sin6_addr; srcport = vxlan_pick_source_port(sc, m); dstaddr = &fvxlsa->in6.sin6_addr; - dstport = fvxlsa->in6.sin6_port; + dstport = htons(sc->vxl_dst_port); M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct vxlanudphdr), M_NOWAIT); @@ -2450,9 +2656,11 @@ union vxlan_sockaddr vxlsa; struct vxlan_softc *sc; struct vxlan_ftable_entry *fe; + struct vxlan_rmtlist_entry *re; struct ifnet *mcifp; struct ether_header *eh; int ipv4, error; + struct mbuf *nm; sc = ifp->if_softc; eh = mtod(m, struct ether_header *); @@ -2470,22 +2678,52 @@ if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) fe = vxlan_ftable_entry_lookup(sc, eh->ether_dhost); - if (fe == NULL) - fe = &sc->vxl_default_fe; - vxlan_sockaddr_copy(&vxlsa, &fe->vxlfe_raddr.sa); - ipv4 = VXLAN_SOCKADDR_IS_IPV4(&vxlsa) != 0; - if (vxlan_sockaddr_in_multicast(&vxlsa) != 0) - mcifp = vxlan_multicast_if_ref(sc, ipv4); - VXLAN_ACQUIRE(sc); VXLAN_RUNLOCK(sc, &tracker); - if (ipv4 != 0) - error = vxlan_encap4(sc, &vxlsa, m); - else - error = vxlan_encap6(sc, &vxlsa, m); + if (fe != NULL) { + vxlan_sockaddr_copy(&vxlsa, &fe->vxlfe_raddr.sa); + ipv4 = VXLAN_SOCKADDR_IS_IPV4(&vxlsa) != 0; + + if (ipv4 != 0) + error = vxlan_encap4(sc, &vxlsa, m); + else + error = vxlan_encap6(sc, &vxlsa, m); + } else if (sc->vxl_rmtlist_cnt != 0) { + LIST_FOREACH(re, &sc->vxl_rmtlist, vxlre_next) { + nm = m_dup(m, M_NOWAIT); + if (nm == NULL) { + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + continue; + } + + vxlan_sockaddr_copy(&vxlsa, &re->vxlre_raddr.sa); + + ipv4 = VXLAN_SOCKADDR_IS_IPV4(&vxlsa) != 0; + + if (ipv4 != 0) + error = vxlan_encap4(sc, &vxlsa, nm); + else + error = vxlan_encap6(sc, &vxlsa, nm); + } + + m_freem(m); + } else if (vxlan_sockaddr_in_multicast(&sc->vxl_mcast_addr) != 0) { + vxlan_sockaddr_copy(&vxlsa, &sc->vxl_mcast_addr.sa); + + ipv4 = VXLAN_SOCKADDR_IS_IPV4(&vxlsa) != 0; + mcifp = vxlan_multicast_if_ref(sc, ipv4); + + if (ipv4 != 0) + error = vxlan_encap4(sc, &vxlsa, m); + else + error = vxlan_encap6(sc, &vxlsa, m); + } else { + error = ENETDOWN; + } + vxlan_release(sc); if (mcifp != NULL) if_rele(mcifp); @@ -2596,10 +2834,10 @@ if (!vxlan_tunable_int(sc, "legacy_port", vxlan_legacy_port)) { sc->vxl_src_addr.in4.sin_port = htons(VXLAN_PORT); - sc->vxl_dst_addr.in4.sin_port = htons(VXLAN_PORT); + sc->vxl_dst_port = VXLAN_PORT; } else { sc->vxl_src_addr.in4.sin_port = htons(VXLAN_LEGACY_PORT); - sc->vxl_dst_addr.in4.sin_port = htons(VXLAN_LEGACY_PORT); + sc->vxl_dst_port = VXLAN_LEGACY_PORT; } sc->vxl_min_port = V_ipport_firstauto; @@ -2612,6 +2850,7 @@ static int vxlan_set_user_config(struct vxlan_softc *sc, struct ifvxlanparam *vxlp) { + struct vxlan_rmtlist_entry *re; #ifndef INET if (vxlp->vxlp_with & (VXLAN_PARAM_WITH_LOCAL_ADDR4 | @@ -2655,21 +2894,25 @@ } if (vxlp->vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4) { - sc->vxl_dst_addr.in4.sin_len = sizeof(struct sockaddr_in); - sc->vxl_dst_addr.in4.sin_family = AF_INET; - sc->vxl_dst_addr.in4.sin_addr = + re = malloc(sizeof(struct vxlan_rmtlist_entry), M_VXLAN, M_WAITOK | M_ZERO); + re->vxlre_raddr.in4.sin_len = sizeof(struct sockaddr_in); + re->vxlre_raddr.in4.sin_family = AF_INET; + re->vxlre_raddr.in4.sin_addr = vxlp->vxlp_remote_sa.in4.sin_addr; + vxlan_rmtlist_entry_add(sc, re); } else if (vxlp->vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) { - sc->vxl_dst_addr.in6.sin6_len = sizeof(struct sockaddr_in6); - sc->vxl_dst_addr.in6.sin6_family = AF_INET6; - sc->vxl_dst_addr.in6.sin6_addr = + re = malloc(sizeof(struct vxlan_rmtlist_entry), M_VXLAN, M_WAITOK | M_ZERO); + re->vxlre_raddr.in6.sin6_len = sizeof(struct sockaddr_in6); + re->vxlre_raddr.in6.sin6_family = AF_INET6; + re->vxlre_raddr.in6.sin6_addr = vxlp->vxlp_remote_sa.in6.sin6_addr; + vxlan_rmtlist_entry_add(sc, re); } if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LOCAL_PORT) sc->vxl_src_addr.in4.sin_port = htons(vxlp->vxlp_local_port); if (vxlp->vxlp_with & VXLAN_PARAM_WITH_REMOTE_PORT) - sc->vxl_dst_addr.in4.sin_port = htons(vxlp->vxlp_remote_port); + sc->vxl_dst_port = vxlp->vxlp_remote_port; if (vxlp->vxlp_with & VXLAN_PARAM_WITH_PORT_RANGE) { if (vxlp->vxlp_min_port <= vxlp->vxlp_max_port) { @@ -2678,6 +2921,16 @@ } } + if (vxlp->vxlp_with & VXLAN_PARAM_WITH_MCAST_GROUP4) { + sc->vxl_mcast_addr.in4.sin_len = sizeof(struct sockaddr_in); + sc->vxl_mcast_addr.in4.sin_family = AF_INET; + sc->vxl_mcast_addr.in4.sin_addr = vxlp->vxlp_mcast_group4; + } else if (vxlp->vxlp_with & VXLAN_PARAM_WITH_MCAST_GROUP6) { + sc->vxl_mcast_addr.in6.sin6_len = sizeof(struct sockaddr_in6); + sc->vxl_mcast_addr.in6.sin6_family = AF_INET6; + sc->vxl_mcast_addr.in6.sin6_addr = vxlp->vxlp_mcast_group6; + } + if (vxlp->vxlp_with & VXLAN_PARAM_WITH_MULTICAST_IF) strlcpy(sc->vxl_mc_ifname, vxlp->vxlp_mc_ifname, IFNAMSIZ); @@ -2715,6 +2968,7 @@ sc = malloc(sizeof(struct vxlan_softc), M_VXLAN, M_WAITOK | M_ZERO); sc->vxl_unit = unit; vxlan_set_default_config(sc); + LIST_INIT(&sc->vxl_rmtlist); if (params != 0) { error = copyin(params, &vxlp, sizeof(vxlp)); @@ -2777,6 +3031,8 @@ vxlan_teardown(sc); vxlan_ftable_flush(sc, 1); + + vxlan_rmtlist_flush(sc); ether_ifdetach(ifp); if_free(ifp);