Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 28 May 2014 03:10:17 +0000 (UTC)
From:      Bryan Venteicher <bryanv@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r266781 - in projects/vxlan: sbin/ifconfig sys/net
Message-ID:  <201405280310.s4S3AHRl088243@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: bryanv
Date: Wed May 28 03:10:16 2014
New Revision: 266781
URL: http://svnweb.freebsd.org/changeset/base/266781

Log:
  Better IPv4 multicast support and more cleanup

Modified:
  projects/vxlan/sbin/ifconfig/ifvxlan.c
  projects/vxlan/sys/net/if_vxlan.c
  projects/vxlan/sys/net/if_vxlan.h

Modified: projects/vxlan/sbin/ifconfig/ifvxlan.c
==============================================================================
--- projects/vxlan/sbin/ifconfig/ifvxlan.c	Wed May 28 00:45:35 2014	(r266780)
+++ projects/vxlan/sbin/ifconfig/ifvxlan.c	Wed May 28 03:10:16 2014	(r266781)
@@ -499,6 +499,25 @@ DECL_CMD_FUNC(setvxlan_maxaddr, arg, d)
 }
 
 static
+DECL_CMD_FUNC(setvxlan_dev, arg, d)
+{
+	struct ifvxlancmd cmd;
+
+	if (!vxlan_exists(s)) {
+		params.vxlp_with |= VXLAN_PARAM_WITH_MC_INTERFACE;
+		strlcpy(params.vxlp_mc_ifname, arg,
+		    sizeof(params.vxlp_mc_ifname));
+		return;
+	}
+
+	bzero(&cmd, sizeof(cmd));
+	strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname));
+
+	if (do_cmd(s, VXLAN_CMD_SET_MC_INTERFACE, &cmd, sizeof(cmd), 1) < 0)
+		err(1, "VXLAN_CMD_SET_MC_INTERFACE");
+}
+
+static
 DECL_CMD_FUNC(setvxlan_ttl, arg, d)
 {
 	struct ifvxlancmd cmd;
@@ -563,6 +582,7 @@ static struct cmd vxlan_cmds[] = {
 	DEF_CLONE_CMD_ARG2("portrange",		setvxlan_port_range),
 	DEF_CLONE_CMD_ARG("timeout",		setvxlan_timeout),
 	DEF_CLONE_CMD_ARG("maxaddr",		setvxlan_maxaddr),
+	DEF_CLONE_CMD_ARG("vxlandev",		setvxlan_dev),
 	DEF_CLONE_CMD_ARG("ttl",		setvxlan_ttl),
 	DEF_CLONE_CMD("learn", 1,		setvxlan_learn),
 	DEF_CLONE_CMD("-learn", 0,		setvxlan_learn),
@@ -576,6 +596,7 @@ static struct cmd vxlan_cmds[] = {
 	DEF_CMD_ARG2("portrange",		setvxlan_port_range),
 	DEF_CMD_ARG("timeout",			setvxlan_timeout),
 	DEF_CMD_ARG("maxaddr",			setvxlan_maxaddr),
+	DEF_CMD_ARG("vxlandev",			setvxlan_dev),
 	DEF_CMD_ARG("ttl",			setvxlan_ttl),
 	DEF_CMD("learn", 1,			setvxlan_learn),
 	DEF_CMD("-learn", 0,			setvxlan_learn),

Modified: projects/vxlan/sys/net/if_vxlan.c
==============================================================================
--- projects/vxlan/sys/net/if_vxlan.c	Wed May 28 00:45:35 2014	(r266780)
+++ projects/vxlan/sys/net/if_vxlan.c	Wed May 28 03:10:16 2014	(r266781)
@@ -31,6 +31,7 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/eventhandler.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/hash.h>
@@ -42,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/queue.h>
+#include <sys/sbuf.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/sockio.h>
@@ -70,13 +72,8 @@ __FBSDID("$FreeBSD$");
 #include <netinet/udp_var.h>
 
 struct vxlan_softc;
-struct vxlan_ftable_entry;
-
-LIST_HEAD(vxlan_ftable_head, vxlan_ftable_entry);
 LIST_HEAD(vxlan_softc_head, vxlan_softc);
 
-#define VXLAN_SO_MC_MAX_GROUPS		32
-
 struct vxlan_socket_mc_info {
 	union vxlan_sockaddr		 vxlsomc_saddr;
 	union vxlan_sockaddr		 vxlsomc_gaddr;
@@ -84,7 +81,9 @@ struct vxlan_socket_mc_info {
 	int				 vxlsomc_users;
 };
 
-#define VXLAN_SO_VNI_HASH_SHIFT		5
+#define VXLAN_SO_MC_MAX_GROUPS		32
+
+#define VXLAN_SO_VNI_HASH_SHIFT		6
 #define VXLAN_SO_VNI_HASH_SIZE		(1 << VXLAN_SO_VNI_HASH_SHIFT)
 #define VXLAN_SO_VNI_HASH(_vni)		((_vni) % VXLAN_SO_VNI_HASH_SIZE)
 
@@ -125,15 +124,17 @@ struct vxlan_ftable_entry {
 #define VXLAN_FE_IS_DYNAMIC(_fe) \
     ((_fe)->vxlfe_flags & VXLAN_FE_FLAG_DYNAMIC)
 
-#define VXLAN_SC_FTABLE_SHIFT		8
+#define VXLAN_SC_FTABLE_SHIFT		9
 #define VXLAN_SC_FTABLE_SIZE		(1 << VXLAN_SC_FTABLE_SHIFT)
 #define VXLAN_SC_FTABLE_MASK		(VXLAN_SC_FTABLE_SIZE - 1)
 #define VXLAN_SC_FTABLE_HASH(_sc, _mac)	\
     (vxlan_mac_hash(_sc, _mac) % VXLAN_SC_FTABLE_SIZE)
 
+LIST_HEAD(vxlan_ftable_head, vxlan_ftable_entry);
+
 struct vxlan_statistics {
 	uint32_t	ftable_nospace;
-	uint32_t	ftable_lock_upgrade;
+	uint32_t	ftable_lock_upgrade_failed;
 };
 
 struct vxlan_softc {
@@ -164,22 +165,21 @@ struct vxlan_softc {
 	/* Derived from vxl_dst_addr. */
 	struct vxlan_ftable_entry	 vxl_default_fe;
 
-#ifdef INET
 	struct ip_moptions		*vxl_im4o;
-#endif
-#ifdef INET6
 	struct ip6_moptions		*vxl_im6o;
-#endif
 
 	int				 vxl_unit;
-	int				 vxl_ifindex;
 	int				 vxl_vso_mc_index;
 	struct vxlan_statistics		 vxl_stats;
 	struct sysctl_oid		*vxl_sysctl_node;
 	struct sysctl_ctx_list		 vxl_sysctl_ctx;
 	struct callout			 vxl_callout;
 	uint8_t				 vxl_hwaddr[ETHER_ADDR_LEN];
+	int				 vxl_mc_ifindex;
+	struct ifnet			*vxl_mc_ifp;
+	char				 vxl_mc_ifname[IFNAMSIZ];
 	LIST_ENTRY(vxlan_softc)		 vxl_entry;
+	LIST_ENTRY(vxlan_softc)		 vxl_ifdetach_list;
 };
 
 #define VXLAN_RLOCK(_sc)	rw_rlock(&(_sc)->vxl_lock)
@@ -204,38 +204,49 @@ static int	vxlan_ftable_addr_cmp(const u
 static void	vxlan_ftable_init(struct vxlan_softc *);
 static void	vxlan_ftable_fini(struct vxlan_softc *);
 static void	vxlan_ftable_flush(struct vxlan_softc *, int);
+static void	vxlan_ftable_expire(struct vxlan_softc *);
+static int	vxlan_ftable_update_locked(struct vxlan_softc *,
+		    const struct sockaddr *, const uint8_t *);
+static int	vxlan_ftable_update(struct vxlan_softc *,
+		    const struct sockaddr *, const uint8_t *);
+static int	vxlan_ftable_sysctl_dump(SYSCTL_HANDLER_ARGS);
+
 static struct vxlan_ftable_entry *
 		vxlan_ftable_entry_alloc(void);
+static void	vxlan_ftable_entry_free(struct vxlan_ftable_entry *);
 static void	vxlan_ftable_entry_init(struct vxlan_softc *,
 		    struct vxlan_ftable_entry *, const uint8_t *,
 		    const struct sockaddr *, uint32_t);
-static void	vxlan_ftable_entry_free(struct vxlan_ftable_entry *);
+static void	vxlan_ftable_entry_destroy(struct vxlan_softc *,
+		    struct vxlan_ftable_entry *);
+static int	vxlan_ftable_entry_insert(struct vxlan_softc *,
+		    struct vxlan_ftable_entry *);
 static struct vxlan_ftable_entry *
 		vxlan_ftable_entry_lookup(struct vxlan_softc *,
 		    const uint8_t *);
-static int	vxlan_ftable_entry_insert(struct vxlan_softc *,
-		    struct vxlan_ftable_entry *);
-static void	vxlan_ftable_entry_destroy(struct vxlan_softc *,
-		    struct vxlan_ftable_entry *);
-static int	vxlan_ftable_update(struct vxlan_softc *,
-		    const struct sockaddr *, const uint8_t *);
-static void	vxlan_ftable_expire(struct vxlan_softc *);
+static void	vxlan_ftable_entry_dump(struct vxlan_ftable_entry *,
+		    struct sbuf *);
 
 static struct vxlan_socket *
 		vxlan_socket_alloc(const union vxlan_sockaddr *);
 static void	vxlan_socket_destroy(struct vxlan_socket *);
+static void	vxlan_socket_release(struct vxlan_socket *);
+static struct vxlan_socket *
+		vxlan_socket_lookup(union vxlan_sockaddr *vxlsa);
+static void	vxlan_socket_insert(struct vxlan_socket *);
+static int	vxlan_socket_init(struct vxlan_socket *, struct ifnet *);
+static int	vxlan_socket_bind(struct vxlan_socket *, struct ifnet *);
 static int	vxlan_socket_create(struct ifnet *, int,
 		    const union vxlan_sockaddr *, struct vxlan_socket **);
-static int	vxlan_socket_insert_softc(struct vxlan_socket *,
-		    struct vxlan_softc *);
-static struct vxlan_softc *
-		vxlan_socket_lookup_softc_locked(struct vxlan_socket *,
-		    uint32_t);
-static struct vxlan_softc *
-		vxlan_socket_lookup_softc(struct vxlan_socket *, uint32_t);
+static void	vxlan_socket_ifdetach(struct vxlan_socket *,
+		    struct ifnet *, struct vxlan_softc_head *);
 
 static struct vxlan_socket *
 		vxlan_socket_mc_lookup(const union vxlan_sockaddr *);
+static int	vxlan_sockaddr_mc_info_match(
+		    const struct vxlan_socket_mc_info *,
+		    const union vxlan_sockaddr *,
+		    const union vxlan_sockaddr *, int);
 static int	vxlan_socket_mc_join_group(struct vxlan_socket *,
 		    const union vxlan_sockaddr *, const union vxlan_sockaddr *,
 		    int *, union vxlan_sockaddr *);
@@ -248,29 +259,35 @@ static int	vxlan_socket_mc_add_group(str
 static void	vxlan_socket_mc_release_group_by_idx(struct vxlan_socket *,
 		    int);
 
-static struct vxlan_socket *
-		vxlan_socket_lookup(union vxlan_sockaddr *vxlsa);
+static struct vxlan_softc *
+		vxlan_socket_lookup_softc_locked(struct vxlan_socket *,
+		    uint32_t);
+static struct vxlan_softc *
+		vxlan_socket_lookup_softc(struct vxlan_socket *, uint32_t);
+static int	vxlan_socket_insert_softc(struct vxlan_socket *,
+		    struct vxlan_softc *);
+static void	vxlan_socket_remove_softc(struct vxlan_socket *,
+		    struct vxlan_softc *);
+
+static struct ifnet *
+		vxlan_multicast_if_ref(struct vxlan_softc *, int);
+static void	vxlan_free_multicast(struct vxlan_softc *);
+static int	vxlan_setup_multicast_interface(struct vxlan_softc *);
 
+static int	vxlan_setup_multicast(struct vxlan_softc *);
+static int	vxlan_setup_socket(struct vxlan_softc *);
 static int	vxlan_valid_init_config(struct vxlan_softc *);
+static void	vxlan_init_wait(struct vxlan_softc *);
+static void	vxlan_init_complete(struct vxlan_softc *);
 static void	vxlan_init(void *);
 static void	vxlan_release(struct vxlan_softc *);
+static void	vxlan_teardown_wait(struct vxlan_softc *);
+static void	vxlan_teardown_complete(struct vxlan_softc *);
+static void	vxlan_teardown_locked(struct vxlan_softc *);
 static void	vxlan_teardown(struct vxlan_softc *);
+static void	vxlan_ifdetach(struct vxlan_softc *, struct ifnet *,
+		    struct vxlan_softc_head *);
 static void	vxlan_timer(void *);
-static uint16_t vxlan_pick_source_port(struct vxlan_softc *,
-		    const struct ether_header *);
-static void	vxlan_encap_header(struct vxlan_softc *, struct mbuf *,
-		    int, uint16_t, uint16_t);
-static int	vxlan_encap4(struct vxlan_softc *,
-		    const union vxlan_sockaddr *, struct mbuf *);
-static int	vxlan_encap6(struct vxlan_softc *,
-		    const union vxlan_sockaddr *, struct mbuf *);
-static int	vxlan_transmit(struct ifnet *, struct mbuf *);
-static void	vxlan_qflush(struct ifnet *);
-
-static void	vxlan_rcv_udp_packet(struct mbuf *, int, struct inpcb *,
-		    const struct sockaddr *);
-static int	vxlan_input(struct vxlan_socket *, uint32_t, struct mbuf **,
-		    const struct sockaddr *);
 
 static int	vxlan_ctrl_get_config(struct vxlan_softc *, void *);
 static int	vxlan_ctrl_set_vni(struct vxlan_softc *, void *);
@@ -281,6 +298,7 @@ static int	vxlan_ctrl_set_remote_port(st
 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_mc_interface(struct vxlan_softc * , void *);
 static int	vxlan_ctrl_set_ttl(struct vxlan_softc *, void *);
 static int	vxlan_ctrl_set_learn(struct vxlan_softc *, void *);
 static int	vxlan_ctrl_ftable_entry_add(struct vxlan_softc *, void *);
@@ -291,6 +309,21 @@ static int	vxlan_ioctl_drvspec(struct vx
 static int	vxlan_ioctl_ifflags(struct vxlan_softc *);
 static int	vxlan_ioctl(struct ifnet *, u_long, caddr_t);
 
+static uint16_t vxlan_pick_source_port(struct vxlan_softc *,
+		    const struct ether_header *);
+static void	vxlan_encap_header(struct vxlan_softc *, struct mbuf *,
+		    int, uint16_t, uint16_t);
+static int	vxlan_encap4(struct vxlan_softc *,
+		    const union vxlan_sockaddr *, struct mbuf *);
+static int	vxlan_encap6(struct vxlan_softc *,
+		    const union vxlan_sockaddr *, struct mbuf *);
+static int	vxlan_transmit(struct ifnet *, struct mbuf *);
+static void	vxlan_qflush(struct ifnet *);
+static void	vxlan_rcv_udp_packet(struct mbuf *, int, struct inpcb *,
+		    const struct sockaddr *);
+static int	vxlan_input(struct vxlan_socket *, uint32_t, struct mbuf **,
+		    const struct sockaddr *);
+
 static void	vxlan_set_default_config(struct vxlan_softc *);
 static int	vxlan_set_user_config(struct vxlan_softc *,
 		     struct ifvxlanparam *);
@@ -299,6 +332,7 @@ static void	vxlan_clone_destroy(struct i
 
 static uint32_t vxlan_mac_hash(struct vxlan_softc *, const uint8_t *);
 static void	vxlan_fakeaddr(struct vxlan_softc *);
+
 static int	vxlan_sockaddr_cmp(const union vxlan_sockaddr *,
 		    const struct sockaddr *);
 static void	vxlan_sockaddr_copy(union vxlan_sockaddr *,
@@ -307,6 +341,7 @@ static int	vxlan_sockaddr_in_equal(const
 		    const struct sockaddr *);
 static void	vxlan_sockaddr_in_copy(union vxlan_sockaddr *,
 		    const struct sockaddr *);
+static int	vxlan_sockaddr_supported(const union vxlan_sockaddr *, int);
 static int	vxlan_sockaddr_in_any(const union vxlan_sockaddr *);
 static int	vxlan_sockaddr_in_multicast(const union vxlan_sockaddr *);
 
@@ -320,6 +355,11 @@ static void	vxlan_sysctl_setup(struct vx
 static void	vxlan_sysctl_destroy(struct vxlan_softc *);
 static int	vxlan_tunable_int(struct vxlan_softc *, const char *, int);
 
+static void	vxlan_ifdetach_event(void *, struct ifnet *);
+static void	vxlan_load(void);
+static void	vxlan_unload(void);
+static int	vxlan_modevent(module_t, int, void *);
+
 static const char vxlan_name[] = "vxlan";
 static MALLOC_DEFINE(M_VXLAN, vxlan_name,
     "Virtual eXtensible LAN Interface");
@@ -327,6 +367,8 @@ static struct if_clone *vxlan_cloner;
 static struct mtx vxlan_list_mtx;
 static LIST_HEAD(, vxlan_socket) vxlan_socket_list;
 
+static eventhandler_tag vxlan_ifdetach_event_tag;
+
 SYSCTL_DECL(_net_link);
 SYSCTL_NODE(_net_link, OID_AUTO, vxlan, CTLFLAG_RW, 0,
     "Virtual eXtensible Local Area Network");
@@ -372,86 +414,77 @@ struct vxlan_control {
 
 static const struct vxlan_control vxlan_control_table[] = {
 	[VXLAN_CMD_GET_CONFIG] =
-	    {	vxlan_ctrl_get_config,
-		sizeof(struct ifvxlancfg),
+	    {	vxlan_ctrl_get_config, sizeof(struct ifvxlancfg),
 		VXLAN_CTRL_FLAG_COPYOUT
 	    },
 
 	[VXLAN_CMD_SET_VNI] =
-	    {   vxlan_ctrl_set_vni,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_set_vni, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_LOCAL_ADDR] =
-	    {   vxlan_ctrl_set_local_addr,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_set_local_addr, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_REMOTE_ADDR] =
-	    {   vxlan_ctrl_set_remote_addr,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_set_remote_addr, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_LOCAL_PORT] =
-	    {   vxlan_ctrl_set_local_port,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_set_local_port, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_REMOTE_PORT] =
-	    {   vxlan_ctrl_set_remote_port,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_set_remote_port, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_PORT_RANGE] =
-	    {   vxlan_ctrl_set_port_range,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_set_port_range, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_FTABLE_TIMEOUT] =
-	    {	vxlan_ctrl_set_ftable_timeout,
-		sizeof(struct ifvxlancmd),
+	    {	vxlan_ctrl_set_ftable_timeout, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_FTABLE_MAX] =
-	    {	vxlan_ctrl_set_ftable_max,
-		sizeof(struct ifvxlancmd),
+	    {	vxlan_ctrl_set_ftable_max, sizeof(struct ifvxlancmd),
+		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
+	    },
+
+	[VXLAN_CMD_SET_MC_INTERFACE] =
+	    {	vxlan_ctrl_set_mc_interface, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_TTL] =
-	    {	vxlan_ctrl_set_ttl,
-		sizeof(struct ifvxlancmd),
+	    {	vxlan_ctrl_set_ttl, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_SET_LEARN] =
-	    {	vxlan_ctrl_set_learn,
-		sizeof(struct ifvxlancmd),
+	    {	vxlan_ctrl_set_learn, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_FTABLE_ENTRY_ADD] =
-	    {	vxlan_ctrl_ftable_entry_add,
-		sizeof(struct ifvxlancmd),
+	    {	vxlan_ctrl_ftable_entry_add, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_FTABLE_ENTRY_REM] =
-	    {	vxlan_ctrl_ftable_entry_rem,
-		sizeof(struct ifvxlancmd),
+	    {	vxlan_ctrl_ftable_entry_rem, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 
 	[VXLAN_CMD_FLUSH] =
-	    {   vxlan_ctrl_flush,
-		sizeof(struct ifvxlancmd),
+	    {   vxlan_ctrl_flush, sizeof(struct ifvxlancmd),
 		VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER,
 	    },
 };
@@ -463,7 +496,6 @@ vxlan_ftable_addr_cmp(const uint8_t *a, 
 {
 	int i, d;
 
-	/* Same MAC comparison as done in if_bridge. */
 	for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++)
 		d = ((int)a[i]) - ((int)b[i]);
 
@@ -488,9 +520,10 @@ vxlan_ftable_fini(struct vxlan_softc *sc
 {
 	int i;
 
-	for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++)
+	for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) {
 		KASSERT(LIST_EMPTY(&sc->vxl_ftable[i]),
 		    ("%s: vxlan %p ftable[%d] not empty", __func__, sc, i));
+	}
 	MPASS(sc->vxl_ftable_cnt == 0);
 
 	free(sc->vxl_ftable, M_VXLAN);
@@ -546,7 +579,6 @@ again:
 	 */
 	fe = vxlan_ftable_entry_lookup(sc, mac);
 	if (fe != NULL) {
-		/* Accept the race if we only hold the read lock. */
 		fe->vxlfe_expire = time_uptime + sc->vxl_ftable_timeout;
 
 		if (!VXLAN_FE_IS_DYNAMIC(fe))
@@ -556,7 +588,7 @@ again:
 		if (!VXLAN_LOCK_WOWNED(sc) && VXLAN_LOCK_UPGRADE(sc) == 0) {
 			VXLAN_RUNLOCK(sc);
 			VXLAN_WLOCK(sc);
-			sc->vxl_stats.ftable_lock_upgrade++;
+			sc->vxl_stats.ftable_lock_upgrade_failed++;
 			goto again;
 		}
 		vxlan_sockaddr_in_copy(&fe->vxlfe_raddr, sa);
@@ -566,7 +598,7 @@ again:
 	if (!VXLAN_LOCK_WOWNED(sc) && VXLAN_LOCK_UPGRADE(sc) == 0) {
 		VXLAN_RUNLOCK(sc);
 		VXLAN_WLOCK(sc);
-		sc->vxl_stats.ftable_lock_upgrade++;
+		sc->vxl_stats.ftable_lock_upgrade_failed++;
 		goto again;
 	}
 
@@ -609,6 +641,46 @@ vxlan_ftable_update(struct vxlan_softc *
 	return (error);
 }
 
+static int
+vxlan_ftable_sysctl_dump(SYSCTL_HANDLER_ARGS)
+{
+	struct sbuf sb;
+	struct vxlan_softc *sc;
+	struct vxlan_ftable_entry *fe;
+	size_t size;
+	int i, error;
+
+	/*
+	 * This is mostly intended for debugging during development. It is
+	 * not practical to dump an entire large table this way.
+	 */
+
+	sc = arg1;
+	size = PAGE_SIZE;	/* Calculate later. */
+
+	sbuf_new(&sb, NULL, size, SBUF_FIXEDLEN);
+	sbuf_putc(&sb, '\n');
+
+	VXLAN_RLOCK(sc);
+	for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) {
+		LIST_FOREACH(fe, &sc->vxl_ftable[i], vxlfe_hash) {
+			if (sbuf_error(&sb) != 0)
+				break;
+			vxlan_ftable_entry_dump(fe, &sb);
+		}
+	}
+	VXLAN_RUNLOCK(sc);
+
+	if (sbuf_len(&sb) == 1)
+		sbuf_setpos(&sb, 0);
+
+	sbuf_finish(&sb);
+	error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+	sbuf_delete(&sb);
+
+	return (error);
+}
+
 static struct vxlan_ftable_entry *
 vxlan_ftable_entry_alloc(void)
 {
@@ -647,27 +719,6 @@ vxlan_ftable_entry_destroy(struct vxlan_
 	vxlan_ftable_entry_free(fe);
 }
 
-static struct vxlan_ftable_entry *
-vxlan_ftable_entry_lookup(struct vxlan_softc *sc, const uint8_t *mac)
-{
-	struct vxlan_ftable_entry *fe;
-	uint32_t hash;
-	int dir;
-
-	VXLAN_LOCK_ASSERT(sc);
-	hash = VXLAN_SC_FTABLE_HASH(sc, mac);
-
-	LIST_FOREACH(fe, &sc->vxl_ftable[hash], vxlfe_hash) {
-		dir = vxlan_ftable_addr_cmp(fe->vxlfe_mac, mac);
-		if (dir == 0)
-			return (fe);
-		if (dir > 0)
-			break;
-	}
-
-	return (NULL);
-}
-
 static int
 vxlan_ftable_entry_insert(struct vxlan_softc *sc,
     struct vxlan_ftable_entry *fe)
@@ -704,6 +755,65 @@ out:
 	return (0);
 }
 
+static struct vxlan_ftable_entry *
+vxlan_ftable_entry_lookup(struct vxlan_softc *sc, const uint8_t *mac)
+{
+	struct vxlan_ftable_entry *fe;
+	uint32_t hash;
+	int dir;
+
+	VXLAN_LOCK_ASSERT(sc);
+	hash = VXLAN_SC_FTABLE_HASH(sc, mac);
+
+	LIST_FOREACH(fe, &sc->vxl_ftable[hash], vxlfe_hash) {
+		dir = vxlan_ftable_addr_cmp(fe->vxlfe_mac, mac);
+		if (dir == 0)
+			return (fe);
+		if (dir > 0)
+			break;
+	}
+
+	return (NULL);
+}
+
+static void
+vxlan_ftable_entry_dump(struct vxlan_ftable_entry *fe, struct sbuf *sb)
+{
+	char buf[64];
+	const union vxlan_sockaddr *sa;
+	const void *addr;
+	int i, len, af, width;
+
+	sa = &fe->vxlfe_raddr;
+	af = sa->sa.sa_family;
+	len = sbuf_len(sb);
+
+	sbuf_printf(sb, "%c 0x%02X ", VXLAN_FE_IS_DYNAMIC(fe) ? 'D' : 'S',
+	    fe->vxlfe_flags);
+
+	for (i = 0; i < ETHER_ADDR_LEN - 1; i++)
+		sbuf_printf(sb, "%02X:", fe->vxlfe_mac[i]);
+	sbuf_printf(sb, "%02X ", fe->vxlfe_mac[i]);
+
+	if (af == AF_INET) {
+		addr = &sa->in4.sin_addr;
+		width = INET_ADDRSTRLEN - 1;
+	} else {
+		addr = &sa->in6.sin6_addr;
+		width = INET6_ADDRSTRLEN - 1;
+	}
+	inet_ntop(af, addr, buf, sizeof(buf));
+	sbuf_printf(sb, "%*s ", width, buf);
+
+	sbuf_printf(sb, "%08ld", fe->vxlfe_expire);
+
+	sbuf_putc(sb, '\n');
+
+	/* Truncate a partial line. */
+	if (sbuf_error(sb) != 0)
+		sbuf_setpos(sb, len);
+}
+
 static struct vxlan_socket *
 vxlan_socket_alloc(const union vxlan_sockaddr *sa)
 {
@@ -902,8 +1012,9 @@ vxlan_socket_create(struct ifnet *ifp, i
 		goto fail;
 
 	/*
-	 * There is a small window between the bind and the insert, but
-	 * let's not worry about that for now.
+	 * There is a small window between the bind completing and
+	 * inserting the socket, so that a concurrent create may fail.
+	 * Let's not worry about that for now.
 	 */
 	vxlan_socket_insert(vso);
 	*vsop = vso;
@@ -916,6 +1027,21 @@ fail:
 	return (error);
 }
 
+static void
+vxlan_socket_ifdetach(struct vxlan_socket *vso, struct ifnet *ifp,
+    struct vxlan_softc_head *list)
+{
+	struct vxlan_softc *sc;
+	int i;
+
+	VXLAN_SO_RLOCK(vso);
+	for (i = 0; i < VXLAN_SO_VNI_HASH_SIZE; i++) {
+		LIST_FOREACH(sc, &vso->vxlso_vni_hash[i], vxl_entry)
+			vxlan_ifdetach(sc, ifp, list);
+	}
+	VXLAN_SO_RUNLOCK(vso);
+}
+
 static struct vxlan_socket *
 vxlan_socket_mc_lookup(const union vxlan_sockaddr *vxlsa)
 {
@@ -999,7 +1125,6 @@ vxlan_socket_mc_join_group(struct vxlan_
 		source->in4.sin_addr = local->in4.sin_addr;
 		*ifidx = 0;
 #endif
-
 	} else if (VXLAN_SOCKADDR_IS_IPV6(group)) {
 		struct ipv6_mreq mreq;
 
@@ -1033,7 +1158,6 @@ vxlan_socket_mc_join_group(struct vxlan_
 #else
 		*ifidx = 0;
 #endif
-
 	} else
 		error = EAFNOSUPPORT;
 
@@ -1092,8 +1216,8 @@ vxlan_socket_mc_add_group(struct vxlan_s
 
 	/*
 	 * Within a socket, the same multicast group may be used by multiple
-	 * interfaces, each with a different network identifier. A socket may
-	 * only join each multicast group once, so keep track of the users
+	 * interfaces, each with a different network identifier. But a socket
+	 * may only join a multicast group once, so keep track of the users
 	 * here.
 	 */
 
@@ -1242,42 +1366,105 @@ vxlan_socket_remove_softc(struct vxlan_s
 	vxlan_release(sc);
 }
 
-/*
- * Initialize an multicast options structure that is sufficiently populated
- * for use in the respective IP output routine. This structure is typically
- * stored in the socket, but our sockets may be shared among many interfaces.
- *
- * BMV: TODO We should convert our interface index into its pointer.
- */
+static struct ifnet *
+vxlan_multicast_if_ref(struct vxlan_softc *sc, int ipv4)
+{
+	struct ifnet *ifp;
+
+	VXLAN_LOCK_ASSERT(sc);
+
+	if (ipv4 && sc->vxl_im4o != NULL)
+		ifp = sc->vxl_im4o->imo_multicast_ifp;
+	else if (!ipv4 && sc->vxl_im6o != NULL)
+		ifp = sc->vxl_im6o->im6o_multicast_ifp;
+	else
+		ifp = NULL;
+
+	if (ifp != NULL)
+		if_ref(ifp);
+
+	return (ifp);
+}
+
+static void
+vxlan_free_multicast(struct vxlan_softc *sc)
+{
+
+	if (sc->vxl_mc_ifp != NULL) {
+		if_rele(sc->vxl_mc_ifp);
+		sc->vxl_mc_ifp = NULL;
+		sc->vxl_mc_ifindex = 0;
+	}
+
+	if (sc->vxl_im4o != NULL) {
+		free(sc->vxl_im4o, M_VXLAN);
+		sc->vxl_im4o = NULL;
+	}
+
+	if (sc->vxl_im6o != NULL) {
+		free(sc->vxl_im6o, M_VXLAN);
+		sc->vxl_im6o = NULL;
+	}
+}
+
 static int
-vxlan_setup_multicast_options(struct vxlan_softc *sc)
+vxlan_setup_multicast_interface(struct vxlan_softc *sc)
+{
+	struct ifnet *ifp;
+
+	ifp = ifunit_ref(sc->vxl_mc_ifname);
+	if (ifp == NULL) {
+		if_printf(sc->vxl_ifp, "multicast interfaces %s does "
+		    "not exist\n", sc->vxl_mc_ifname);
+		return (ENOENT);
+	}
+
+	if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+		if_printf(sc->vxl_ifp, "interface %s does not support "
+		     "multicast\n", sc->vxl_mc_ifname);
+		if_rele(ifp);
+		return (ENOTSUP);
+	}
+
+	sc->vxl_mc_ifp = ifp;
+	sc->vxl_mc_ifindex = ifp->if_index;
+
+	return (0);
+}
+
+static int
+vxlan_setup_multicast(struct vxlan_softc *sc)
 {
 	const union vxlan_sockaddr *group;
 	int error;
 
 	group = &sc->vxl_dst_addr;
+	error = 0;
 
+	if (sc->vxl_mc_ifname[0] != '\0') {
+		error = vxlan_setup_multicast_interface(sc);
+		if (error)
+			return (error);
+	}
+
+	/*
+	 * Initialize an multicast options structure that is sufficiently
+	 * populated for use in the respective IP output routine. This
+	 * structure is typically stored in the socket, but our sockets
+	 * may be shared among multiple interfaces.
+	 */
 	if (VXLAN_SOCKADDR_IS_IPV4(group)) {
 		sc->vxl_im4o = malloc(sizeof(struct ip_moptions), M_VXLAN,
-		    M_ZERO | M_NOWAIT);
-		if (sc->vxl_im4o != NULL) {
-			sc->vxl_im4o->imo_multicast_ttl = sc->vxl_ttl;
-			sc->vxl_im4o->imo_multicast_vif = -1;
-			error = 0;
-		} else
-			error = ENOMEM;
-
+		    M_ZERO | M_WAITOK);
+		sc->vxl_im4o->imo_multicast_ifp = sc->vxl_mc_ifp;
+		sc->vxl_im4o->imo_multicast_ttl = sc->vxl_ttl;
+		sc->vxl_im4o->imo_multicast_vif = -1;
 	} else if (VXLAN_SOCKADDR_IS_IPV6(group)) {
 		sc->vxl_im6o = malloc(sizeof(struct ip6_moptions), M_VXLAN,
-		    M_ZERO | M_NOWAIT);
-		if (sc->vxl_im6o != NULL) {
-			sc->vxl_im6o->im6o_multicast_hlim = sc->vxl_ttl;
-			error = 0;
-		} else
-			error = ENOMEM;
-
-	} else
-		error = EAFNOSUPPORT;
+		    M_ZERO | M_WAITOK);
+		sc->vxl_im6o->im6o_multicast_ifp = sc->vxl_mc_ifp;
+		sc->vxl_im6o->im6o_multicast_hlim = sc->vxl_ttl;
+	}
 
 	return (error);
 }
@@ -1288,33 +1475,41 @@ vxlan_setup_socket(struct vxlan_softc *s
 	struct vxlan_socket *vso;
 	struct ifnet *ifp;
 	union vxlan_sockaddr *saddr, *daddr;
-	int ifidx, multicast, error;
+	int multicast, error;
 
 	ifp = sc->vxl_ifp;
 	saddr = &sc->vxl_src_addr;
 	daddr = &sc->vxl_dst_addr;
-	ifidx = sc->vxl_ifindex;
-	multicast = vxlan_sockaddr_in_multicast(daddr);
 
+	multicast = vxlan_sockaddr_in_multicast(daddr);
+	MPASS(multicast != -1);
 	sc->vxl_vso_mc_index = -1;
 
 	/*
 	 * Try to create the socket. If that fails, attempt to use an
-	 * existing socket that is already bound.
+	 * existing socket.
 	 */
 	error = vxlan_socket_create(ifp, multicast, saddr, &vso);
-	if (error != 0) {
+	if (error) {
 		if (multicast != 0)
 			vso = vxlan_socket_mc_lookup(saddr);
 		else
 			vso = vxlan_socket_lookup(saddr);
-		if (vso == NULL)
+
+		if (vso == NULL) {
+			if_printf(ifp, "cannot create socket (error: %d), "
+			    "and no existing socket found\n", error);
 			goto out;
+		}
 	}
 
 	if (multicast != 0) {
-		error = vxlan_socket_mc_add_group(vso, daddr, saddr, ifidx,
-		    &sc->vxl_vso_mc_index);
+		error = vxlan_setup_multicast(sc);
+		if (error)
+			goto out;
+
+		error = vxlan_socket_mc_add_group(vso, daddr, saddr,
+		    sc->vxl_mc_ifindex, &sc->vxl_vso_mc_index);
 		if (error)
 			goto out;
 	}
@@ -1323,34 +1518,25 @@ vxlan_setup_socket(struct vxlan_softc *s
 	error = vxlan_socket_insert_softc(vso, sc);
 	if (error) {
 		sc->vxl_sock = NULL;
+		if_printf(ifp, "network identifier %d already exists in "
+		    "this socket\n", sc->vxl_vni);
 		goto out;
 	}
 
-	if (multicast != 0) {
-		error = vxlan_setup_multicast_options(sc);
-		if (error)
-			goto out;
-	}
-
 	return (0);
 
 out:
 	if (vso != NULL) {
-		if (sc->vxl_sock != NULL) {
-			vxlan_socket_remove_softc(vso, sc);
-			sc->vxl_sock = NULL;
-		}
 		if (sc->vxl_vso_mc_index != -1) {
 			vxlan_socket_mc_release_group_by_idx(vso,
 			    sc->vxl_vso_mc_index);
 			sc->vxl_vso_mc_index = -1;
 		}
+		if (multicast != 0)
+			vxlan_free_multicast(sc);
 		vxlan_socket_release(vso);
 	}
 
-	if_printf(ifp, "%s: cannot create socket (%d), and no existing "
-	    "socket found\n", __func__, error);
-
 	return (error);
 }
 
@@ -1360,7 +1546,17 @@ vxlan_valid_init_config(struct vxlan_sof
 	const char *reason;
 
 	if (vxlan_check_vni(sc->vxl_vni) != 0) {
-		reason = "no valid virtual network identifier specified";
+		reason = "invalid virtual network identifier specified";
+		goto fail;
+	}
+
+	if (vxlan_sockaddr_supported(&sc->vxl_src_addr, 1) == 0) {
+		reason = "source address type is not supported";
+		goto fail;
+	}
+
+	if (vxlan_sockaddr_supported(&sc->vxl_dst_addr, 0) == 0) {
+		reason = "destination address type is not supported";
 		goto fail;
 	}
 
@@ -1370,16 +1566,16 @@ vxlan_valid_init_config(struct vxlan_sof
 	}
 
 	if (vxlan_sockaddr_in_multicast(&sc->vxl_dst_addr) == 0 &&
-	    sc->vxl_ifindex != 0) {
-		reason = "can only specify the interface with a group address";
+	    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 be "
-			    "both IPv4 or IPv6";
+			reason = "source and destination address must both "
+			    "be either IPv4 or IPv6";
 			goto fail;
 		}
 	}
@@ -1397,15 +1593,34 @@ vxlan_valid_init_config(struct vxlan_sof
 	return (0);
 
 fail:
-	if_printf(sc->vxl_ifp, "cannot init interface: %s\n", reason);
+	if_printf(sc->vxl_ifp, "cannot initialize interface: %s\n", reason);
 
 	return (EINVAL);
 }
 
 static void
+vxlan_init_wait(struct vxlan_softc *sc)
+{
+
+	VXLAN_LOCK_ASSERT(sc);
+	while (sc->vxl_flags & VXLAN_FLAG_INIT)
+		rw_sleep(sc, &sc->vxl_lock, 0, "vxlint", hz);
+}
+
+static void
+vxlan_init_complete(struct vxlan_softc *sc)
+{
+
+	VXLAN_WLOCK(sc);
+	sc->vxl_flags &= ~VXLAN_FLAG_INIT;
+	wakeup(sc);
+	VXLAN_WUNLOCK(sc);
+}
+
+static void
 vxlan_init(void *xsc)
 {
-	static const uint8_t mac[ETHER_ADDR_LEN];
+	static const uint8_t empty_mac[ETHER_ADDR_LEN];
 	struct vxlan_softc *sc;
 	struct ifnet *ifp;
 
@@ -1426,37 +1641,55 @@ vxlan_init(void *xsc)
 	if (vxlan_setup_socket(sc) != 0)
 		goto out;
 
-	vxlan_ftable_entry_init(sc, &sc->vxl_default_fe, mac,
+	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,
 	    vxlan_timer, sc);
-	sc->vxl_flags &= ~VXLAN_FLAG_INIT;
 	VXLAN_WUNLOCK(sc);
 
-	return;
-
 out:
-	VXLAN_WLOCK(sc);
-	sc->vxl_flags &= ~VXLAN_FLAG_INIT;
-	VXLAN_WUNLOCK(sc);
+	vxlan_init_complete(sc);
+
 }
 
 static void
 vxlan_release(struct vxlan_softc *sc)
 {
 
-	if (VXLAN_RELEASE(sc) != 0) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405280310.s4S3AHRl088243>