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 "opt_inet.h" 28273331Sbryanv#include "opt_inet6.h" 29273331Sbryanv 30273331Sbryanv#include <sys/cdefs.h> 31273331Sbryanv__FBSDID("$FreeBSD: stable/10/sys/net/if_vxlan.c 327142 2017-12-24 02:06:16Z ae $"); 32273331Sbryanv 33273331Sbryanv#include <sys/param.h> 34273331Sbryanv#include <sys/eventhandler.h> 35273331Sbryanv#include <sys/kernel.h> 36273331Sbryanv#include <sys/lock.h> 37273331Sbryanv#include <sys/hash.h> 38273331Sbryanv#include <sys/malloc.h> 39273331Sbryanv#include <sys/mbuf.h> 40273331Sbryanv#include <sys/module.h> 41273331Sbryanv#include <sys/refcount.h> 42273331Sbryanv#include <sys/rmlock.h> 43273331Sbryanv#include <sys/priv.h> 44273331Sbryanv#include <sys/proc.h> 45273331Sbryanv#include <sys/queue.h> 46273331Sbryanv#include <sys/sbuf.h> 47273331Sbryanv#include <sys/socket.h> 48273331Sbryanv#include <sys/socketvar.h> 49273331Sbryanv#include <sys/sockio.h> 50273331Sbryanv#include <sys/sysctl.h> 51273331Sbryanv#include <sys/systm.h> 52273331Sbryanv 53273331Sbryanv#include <net/bpf.h> 54273331Sbryanv#include <net/ethernet.h> 55273331Sbryanv#include <net/if.h> 56273331Sbryanv#include <net/if_var.h> 57273331Sbryanv#include <net/if_clone.h> 58273331Sbryanv#include <net/if_dl.h> 59273331Sbryanv#include <net/if_types.h> 60273331Sbryanv#include <net/if_vxlan.h> 61273331Sbryanv#include <net/netisr.h> 62273331Sbryanv 63273331Sbryanv#include <netinet/in.h> 64273331Sbryanv#include <netinet/in_systm.h> 65273331Sbryanv#include <netinet/in_var.h> 66273331Sbryanv#include <netinet/in_pcb.h> 67273331Sbryanv#include <netinet/ip.h> 68273331Sbryanv#include <netinet/ip6.h> 69273331Sbryanv#include <netinet/ip_var.h> 70273331Sbryanv#include <netinet6/ip6_var.h> 71273331Sbryanv#include <netinet/udp.h> 72273331Sbryanv#include <netinet/udp_var.h> 73273331Sbryanv 74273331Sbryanvstruct vxlan_softc; 75273331SbryanvLIST_HEAD(vxlan_softc_head, vxlan_softc); 76273331Sbryanv 77273331Sbryanvstruct vxlan_socket_mc_info { 78273331Sbryanv union vxlan_sockaddr vxlsomc_saddr; 79273331Sbryanv union vxlan_sockaddr vxlsomc_gaddr; 80273331Sbryanv int vxlsomc_ifidx; 81273331Sbryanv int vxlsomc_users; 82273331Sbryanv}; 83273331Sbryanv 84273331Sbryanv#define VXLAN_SO_MC_MAX_GROUPS 32 85273331Sbryanv 86273331Sbryanv#define VXLAN_SO_VNI_HASH_SHIFT 6 87273331Sbryanv#define VXLAN_SO_VNI_HASH_SIZE (1 << VXLAN_SO_VNI_HASH_SHIFT) 88273331Sbryanv#define VXLAN_SO_VNI_HASH(_vni) ((_vni) % VXLAN_SO_VNI_HASH_SIZE) 89273331Sbryanv 90273331Sbryanvstruct vxlan_socket { 91273331Sbryanv struct socket *vxlso_sock; 92273331Sbryanv struct rmlock vxlso_lock; 93273331Sbryanv u_int vxlso_refcnt; 94273331Sbryanv union vxlan_sockaddr vxlso_laddr; 95273331Sbryanv LIST_ENTRY(vxlan_socket) vxlso_entry; 96273331Sbryanv struct vxlan_softc_head vxlso_vni_hash[VXLAN_SO_VNI_HASH_SIZE]; 97273331Sbryanv struct vxlan_socket_mc_info vxlso_mc[VXLAN_SO_MC_MAX_GROUPS]; 98273331Sbryanv}; 99273331Sbryanv 100273331Sbryanv#define VXLAN_SO_RLOCK(_vso, _p) rm_rlock(&(_vso)->vxlso_lock, (_p)) 101273331Sbryanv#define VXLAN_SO_RUNLOCK(_vso, _p) rm_runlock(&(_vso)->vxlso_lock, (_p)) 102273331Sbryanv#define VXLAN_SO_WLOCK(_vso) rm_wlock(&(_vso)->vxlso_lock) 103273331Sbryanv#define VXLAN_SO_WUNLOCK(_vso) rm_wunlock(&(_vso)->vxlso_lock) 104273331Sbryanv#define VXLAN_SO_LOCK_ASSERT(_vso) \ 105273331Sbryanv rm_assert(&(_vso)->vxlso_lock, RA_LOCKED) 106273331Sbryanv#define VXLAN_SO_LOCK_WASSERT(_vso) \ 107273331Sbryanv rm_assert(&(_vso)->vxlso_lock, RA_WLOCKED) 108273331Sbryanv 109273331Sbryanv#define VXLAN_SO_ACQUIRE(_vso) refcount_acquire(&(_vso)->vxlso_refcnt) 110273331Sbryanv#define VXLAN_SO_RELEASE(_vso) refcount_release(&(_vso)->vxlso_refcnt) 111273331Sbryanv 112273331Sbryanvstruct vxlan_ftable_entry { 113273331Sbryanv LIST_ENTRY(vxlan_ftable_entry) vxlfe_hash; 114273331Sbryanv uint16_t vxlfe_flags; 115273331Sbryanv uint8_t vxlfe_mac[ETHER_ADDR_LEN]; 116273331Sbryanv union vxlan_sockaddr vxlfe_raddr; 117273331Sbryanv time_t vxlfe_expire; 118273331Sbryanv}; 119273331Sbryanv 120273331Sbryanv#define VXLAN_FE_FLAG_DYNAMIC 0x01 121273331Sbryanv#define VXLAN_FE_FLAG_STATIC 0x02 122273331Sbryanv 123273331Sbryanv#define VXLAN_FE_IS_DYNAMIC(_fe) \ 124273331Sbryanv ((_fe)->vxlfe_flags & VXLAN_FE_FLAG_DYNAMIC) 125273331Sbryanv 126273331Sbryanv#define VXLAN_SC_FTABLE_SHIFT 9 127273331Sbryanv#define VXLAN_SC_FTABLE_SIZE (1 << VXLAN_SC_FTABLE_SHIFT) 128273331Sbryanv#define VXLAN_SC_FTABLE_MASK (VXLAN_SC_FTABLE_SIZE - 1) 129273331Sbryanv#define VXLAN_SC_FTABLE_HASH(_sc, _mac) \ 130273331Sbryanv (vxlan_mac_hash(_sc, _mac) % VXLAN_SC_FTABLE_SIZE) 131273331Sbryanv 132273331SbryanvLIST_HEAD(vxlan_ftable_head, vxlan_ftable_entry); 133273331Sbryanv 134273331Sbryanvstruct vxlan_statistics { 135273331Sbryanv uint32_t ftable_nospace; 136273331Sbryanv uint32_t ftable_lock_upgrade_failed; 137273331Sbryanv}; 138273331Sbryanv 139273331Sbryanvstruct vxlan_softc { 140273331Sbryanv struct ifnet *vxl_ifp; 141273331Sbryanv struct vxlan_socket *vxl_sock; 142273331Sbryanv uint32_t vxl_vni; 143273331Sbryanv union vxlan_sockaddr vxl_src_addr; 144273331Sbryanv union vxlan_sockaddr vxl_dst_addr; 145273331Sbryanv uint32_t vxl_flags; 146273331Sbryanv#define VXLAN_FLAG_INIT 0x0001 147273331Sbryanv#define VXLAN_FLAG_TEARDOWN 0x0002 148273331Sbryanv#define VXLAN_FLAG_LEARN 0x0004 149273331Sbryanv 150273331Sbryanv uint32_t vxl_port_hash_key; 151273331Sbryanv uint16_t vxl_min_port; 152273331Sbryanv uint16_t vxl_max_port; 153273331Sbryanv uint8_t vxl_ttl; 154273331Sbryanv 155273331Sbryanv /* Lookup table from MAC address to forwarding entry. */ 156273331Sbryanv uint32_t vxl_ftable_cnt; 157273331Sbryanv uint32_t vxl_ftable_max; 158273331Sbryanv uint32_t vxl_ftable_timeout; 159273331Sbryanv uint32_t vxl_ftable_hash_key; 160273331Sbryanv struct vxlan_ftable_head *vxl_ftable; 161273331Sbryanv 162273331Sbryanv /* Derived from vxl_dst_addr. */ 163273331Sbryanv struct vxlan_ftable_entry vxl_default_fe; 164273331Sbryanv 165273331Sbryanv struct ip_moptions *vxl_im4o; 166273331Sbryanv struct ip6_moptions *vxl_im6o; 167273331Sbryanv 168273331Sbryanv struct rmlock vxl_lock; 169273331Sbryanv volatile u_int vxl_refcnt; 170273331Sbryanv 171273331Sbryanv int vxl_unit; 172273331Sbryanv int vxl_vso_mc_index; 173273331Sbryanv struct vxlan_statistics vxl_stats; 174273331Sbryanv struct sysctl_oid *vxl_sysctl_node; 175273331Sbryanv struct sysctl_ctx_list vxl_sysctl_ctx; 176273331Sbryanv struct callout vxl_callout; 177273331Sbryanv uint8_t vxl_hwaddr[ETHER_ADDR_LEN]; 178273331Sbryanv int vxl_mc_ifindex; 179273331Sbryanv struct ifnet *vxl_mc_ifp; 180273331Sbryanv char vxl_mc_ifname[IFNAMSIZ]; 181273331Sbryanv LIST_ENTRY(vxlan_softc) vxl_entry; 182273331Sbryanv LIST_ENTRY(vxlan_softc) vxl_ifdetach_list; 183273331Sbryanv}; 184273331Sbryanv 185273331Sbryanv#define VXLAN_RLOCK(_sc, _p) rm_rlock(&(_sc)->vxl_lock, (_p)) 186273331Sbryanv#define VXLAN_RUNLOCK(_sc, _p) rm_runlock(&(_sc)->vxl_lock, (_p)) 187273331Sbryanv#define VXLAN_WLOCK(_sc) rm_wlock(&(_sc)->vxl_lock) 188273331Sbryanv#define VXLAN_WUNLOCK(_sc) rm_wunlock(&(_sc)->vxl_lock) 189273331Sbryanv#define VXLAN_LOCK_WOWNED(_sc) rm_wowned(&(_sc)->vxl_lock) 190273331Sbryanv#define VXLAN_LOCK_ASSERT(_sc) rm_assert(&(_sc)->vxl_lock, RA_LOCKED) 191273331Sbryanv#define VXLAN_LOCK_WASSERT(_sc) rm_assert(&(_sc)->vxl_lock, RA_WLOCKED) 192273331Sbryanv#define VXLAN_UNLOCK(_sc, _p) do { \ 193273331Sbryanv if (VXLAN_LOCK_WOWNED(_sc)) \ 194273331Sbryanv VXLAN_WUNLOCK(_sc); \ 195273331Sbryanv else \ 196273331Sbryanv VXLAN_RUNLOCK(_sc, _p); \ 197273331Sbryanv} while (0) 198273331Sbryanv 199273331Sbryanv#define VXLAN_ACQUIRE(_sc) refcount_acquire(&(_sc)->vxl_refcnt) 200273331Sbryanv#define VXLAN_RELEASE(_sc) refcount_release(&(_sc)->vxl_refcnt) 201273331Sbryanv 202273331Sbryanv#define satoconstsin(sa) ((const struct sockaddr_in *)(sa)) 203273331Sbryanv#define satoconstsin6(sa) ((const struct sockaddr_in6 *)(sa)) 204273331Sbryanv 205273331Sbryanvstruct vxlanudphdr { 206273331Sbryanv struct udphdr vxlh_udp; 207273331Sbryanv struct vxlan_header vxlh_hdr; 208273331Sbryanv} __packed; 209273331Sbryanv 210273331Sbryanvstatic int vxlan_ftable_addr_cmp(const uint8_t *, const uint8_t *); 211273331Sbryanvstatic void vxlan_ftable_init(struct vxlan_softc *); 212273331Sbryanvstatic void vxlan_ftable_fini(struct vxlan_softc *); 213273331Sbryanvstatic void vxlan_ftable_flush(struct vxlan_softc *, int); 214273331Sbryanvstatic void vxlan_ftable_expire(struct vxlan_softc *); 215273331Sbryanvstatic int vxlan_ftable_update_locked(struct vxlan_softc *, 216273331Sbryanv const struct sockaddr *, const uint8_t *, 217273331Sbryanv struct rm_priotracker *); 218273331Sbryanvstatic int vxlan_ftable_update(struct vxlan_softc *, 219273331Sbryanv const struct sockaddr *, const uint8_t *); 220273331Sbryanvstatic int vxlan_ftable_sysctl_dump(SYSCTL_HANDLER_ARGS); 221273331Sbryanv 222273331Sbryanvstatic struct vxlan_ftable_entry * 223273331Sbryanv vxlan_ftable_entry_alloc(void); 224273331Sbryanvstatic void vxlan_ftable_entry_free(struct vxlan_ftable_entry *); 225273331Sbryanvstatic void vxlan_ftable_entry_init(struct vxlan_softc *, 226273331Sbryanv struct vxlan_ftable_entry *, const uint8_t *, 227273331Sbryanv const struct sockaddr *, uint32_t); 228273331Sbryanvstatic void vxlan_ftable_entry_destroy(struct vxlan_softc *, 229273331Sbryanv struct vxlan_ftable_entry *); 230273331Sbryanvstatic int vxlan_ftable_entry_insert(struct vxlan_softc *, 231273331Sbryanv struct vxlan_ftable_entry *); 232273331Sbryanvstatic struct vxlan_ftable_entry * 233273331Sbryanv vxlan_ftable_entry_lookup(struct vxlan_softc *, 234273331Sbryanv const uint8_t *); 235273331Sbryanvstatic void vxlan_ftable_entry_dump(struct vxlan_ftable_entry *, 236273331Sbryanv struct sbuf *); 237273331Sbryanv 238273331Sbryanvstatic struct vxlan_socket * 239273331Sbryanv vxlan_socket_alloc(const union vxlan_sockaddr *); 240273331Sbryanvstatic void vxlan_socket_destroy(struct vxlan_socket *); 241273331Sbryanvstatic void vxlan_socket_release(struct vxlan_socket *); 242273331Sbryanvstatic struct vxlan_socket * 243273331Sbryanv vxlan_socket_lookup(union vxlan_sockaddr *vxlsa); 244273331Sbryanvstatic void vxlan_socket_insert(struct vxlan_socket *); 245273331Sbryanvstatic int vxlan_socket_init(struct vxlan_socket *, struct ifnet *); 246273331Sbryanvstatic int vxlan_socket_bind(struct vxlan_socket *, struct ifnet *); 247273331Sbryanvstatic int vxlan_socket_create(struct ifnet *, int, 248273331Sbryanv const union vxlan_sockaddr *, struct vxlan_socket **); 249273331Sbryanvstatic void vxlan_socket_ifdetach(struct vxlan_socket *, 250273331Sbryanv struct ifnet *, struct vxlan_softc_head *); 251273331Sbryanv 252273331Sbryanvstatic struct vxlan_socket * 253273331Sbryanv vxlan_socket_mc_lookup(const union vxlan_sockaddr *); 254273331Sbryanvstatic int vxlan_sockaddr_mc_info_match( 255273331Sbryanv const struct vxlan_socket_mc_info *, 256273331Sbryanv const union vxlan_sockaddr *, 257273331Sbryanv const union vxlan_sockaddr *, int); 258273331Sbryanvstatic int vxlan_socket_mc_join_group(struct vxlan_socket *, 259273331Sbryanv const union vxlan_sockaddr *, const union vxlan_sockaddr *, 260273331Sbryanv int *, union vxlan_sockaddr *); 261273331Sbryanvstatic int vxlan_socket_mc_leave_group(struct vxlan_socket *, 262273331Sbryanv const union vxlan_sockaddr *, 263273331Sbryanv const union vxlan_sockaddr *, int); 264273331Sbryanvstatic int vxlan_socket_mc_add_group(struct vxlan_socket *, 265273331Sbryanv const union vxlan_sockaddr *, const union vxlan_sockaddr *, 266273331Sbryanv int, int *); 267273331Sbryanvstatic void vxlan_socket_mc_release_group_by_idx(struct vxlan_socket *, 268273331Sbryanv int); 269273331Sbryanv 270273331Sbryanvstatic struct vxlan_softc * 271273331Sbryanv vxlan_socket_lookup_softc_locked(struct vxlan_socket *, 272273331Sbryanv uint32_t); 273273331Sbryanvstatic struct vxlan_softc * 274273331Sbryanv vxlan_socket_lookup_softc(struct vxlan_socket *, uint32_t); 275273331Sbryanvstatic int vxlan_socket_insert_softc(struct vxlan_socket *, 276273331Sbryanv struct vxlan_softc *); 277273331Sbryanvstatic void vxlan_socket_remove_softc(struct vxlan_socket *, 278273331Sbryanv struct vxlan_softc *); 279273331Sbryanv 280273331Sbryanvstatic struct ifnet * 281273331Sbryanv vxlan_multicast_if_ref(struct vxlan_softc *, int); 282273331Sbryanvstatic void vxlan_free_multicast(struct vxlan_softc *); 283273331Sbryanvstatic int vxlan_setup_multicast_interface(struct vxlan_softc *); 284273331Sbryanv 285273331Sbryanvstatic int vxlan_setup_multicast(struct vxlan_softc *); 286273331Sbryanvstatic int vxlan_setup_socket(struct vxlan_softc *); 287273331Sbryanvstatic void vxlan_setup_interface(struct vxlan_softc *); 288273331Sbryanvstatic int vxlan_valid_init_config(struct vxlan_softc *); 289273331Sbryanvstatic void vxlan_init_wait(struct vxlan_softc *); 290273331Sbryanvstatic void vxlan_init_complete(struct vxlan_softc *); 291273331Sbryanvstatic void vxlan_init(void *); 292273331Sbryanvstatic void vxlan_release(struct vxlan_softc *); 293273331Sbryanvstatic void vxlan_teardown_wait(struct vxlan_softc *); 294273331Sbryanvstatic void vxlan_teardown_complete(struct vxlan_softc *); 295273331Sbryanvstatic void vxlan_teardown_locked(struct vxlan_softc *); 296273331Sbryanvstatic void vxlan_teardown(struct vxlan_softc *); 297273331Sbryanvstatic void vxlan_ifdetach(struct vxlan_softc *, struct ifnet *, 298273331Sbryanv struct vxlan_softc_head *); 299273331Sbryanvstatic void vxlan_timer(void *); 300273331Sbryanv 301273331Sbryanvstatic int vxlan_ctrl_get_config(struct vxlan_softc *, void *); 302273331Sbryanvstatic int vxlan_ctrl_set_vni(struct vxlan_softc *, void *); 303273331Sbryanvstatic int vxlan_ctrl_set_local_addr(struct vxlan_softc *, void *); 304273331Sbryanvstatic int vxlan_ctrl_set_remote_addr(struct vxlan_softc *, void *); 305273331Sbryanvstatic int vxlan_ctrl_set_local_port(struct vxlan_softc *, void *); 306273331Sbryanvstatic int vxlan_ctrl_set_remote_port(struct vxlan_softc *, void *); 307273331Sbryanvstatic int vxlan_ctrl_set_port_range(struct vxlan_softc *, void *); 308273331Sbryanvstatic int vxlan_ctrl_set_ftable_timeout(struct vxlan_softc *, void *); 309273331Sbryanvstatic int vxlan_ctrl_set_ftable_max(struct vxlan_softc *, void *); 310273331Sbryanvstatic int vxlan_ctrl_set_multicast_if(struct vxlan_softc * , void *); 311273331Sbryanvstatic int vxlan_ctrl_set_ttl(struct vxlan_softc *, void *); 312273331Sbryanvstatic int vxlan_ctrl_set_learn(struct vxlan_softc *, void *); 313273331Sbryanvstatic int vxlan_ctrl_ftable_entry_add(struct vxlan_softc *, void *); 314273331Sbryanvstatic int vxlan_ctrl_ftable_entry_rem(struct vxlan_softc *, void *); 315273331Sbryanvstatic int vxlan_ctrl_flush(struct vxlan_softc *, void *); 316273331Sbryanvstatic int vxlan_ioctl_drvspec(struct vxlan_softc *, 317273331Sbryanv struct ifdrv *, int); 318273331Sbryanvstatic int vxlan_ioctl_ifflags(struct vxlan_softc *); 319273331Sbryanvstatic int vxlan_ioctl(struct ifnet *, u_long, caddr_t); 320273331Sbryanv 321273331Sbryanv#if defined(INET) || defined(INET6) 322273331Sbryanvstatic uint16_t vxlan_pick_source_port(struct vxlan_softc *, struct mbuf *); 323273331Sbryanvstatic void vxlan_encap_header(struct vxlan_softc *, struct mbuf *, 324273331Sbryanv int, uint16_t, uint16_t); 325273331Sbryanv#endif 326273331Sbryanvstatic int vxlan_encap4(struct vxlan_softc *, 327273331Sbryanv const union vxlan_sockaddr *, struct mbuf *); 328273331Sbryanvstatic int vxlan_encap6(struct vxlan_softc *, 329273331Sbryanv const union vxlan_sockaddr *, struct mbuf *); 330273331Sbryanvstatic int vxlan_transmit(struct ifnet *, struct mbuf *); 331273331Sbryanvstatic void vxlan_qflush(struct ifnet *); 332273331Sbryanvstatic void vxlan_rcv_udp_packet(struct mbuf *, int, struct inpcb *, 333273331Sbryanv const struct sockaddr *, void *); 334273331Sbryanvstatic int vxlan_input(struct vxlan_socket *, uint32_t, struct mbuf **, 335273331Sbryanv const struct sockaddr *); 336273331Sbryanv 337273331Sbryanvstatic void vxlan_set_default_config(struct vxlan_softc *); 338273331Sbryanvstatic int vxlan_set_user_config(struct vxlan_softc *, 339273331Sbryanv struct ifvxlanparam *); 340273331Sbryanvstatic int vxlan_clone_create(struct if_clone *, int, caddr_t); 341273331Sbryanvstatic void vxlan_clone_destroy(struct ifnet *); 342273331Sbryanv 343273331Sbryanvstatic uint32_t vxlan_mac_hash(struct vxlan_softc *, const uint8_t *); 344273331Sbryanvstatic void vxlan_fakeaddr(struct vxlan_softc *); 345273331Sbryanv 346273331Sbryanvstatic int vxlan_sockaddr_cmp(const union vxlan_sockaddr *, 347273331Sbryanv const struct sockaddr *); 348273331Sbryanvstatic void vxlan_sockaddr_copy(union vxlan_sockaddr *, 349273331Sbryanv const struct sockaddr *); 350273331Sbryanvstatic int vxlan_sockaddr_in_equal(const union vxlan_sockaddr *, 351273331Sbryanv const struct sockaddr *); 352273331Sbryanvstatic void vxlan_sockaddr_in_copy(union vxlan_sockaddr *, 353273331Sbryanv const struct sockaddr *); 354273331Sbryanvstatic int vxlan_sockaddr_supported(const union vxlan_sockaddr *, int); 355273331Sbryanvstatic int vxlan_sockaddr_in_any(const union vxlan_sockaddr *); 356273331Sbryanvstatic int vxlan_sockaddr_in_multicast(const union vxlan_sockaddr *); 357273331Sbryanv 358273331Sbryanvstatic int vxlan_can_change_config(struct vxlan_softc *); 359273331Sbryanvstatic int vxlan_check_vni(uint32_t); 360273331Sbryanvstatic int vxlan_check_ttl(int); 361273331Sbryanvstatic int vxlan_check_ftable_timeout(uint32_t); 362273331Sbryanvstatic int vxlan_check_ftable_max(uint32_t); 363273331Sbryanv 364273331Sbryanvstatic void vxlan_sysctl_setup(struct vxlan_softc *); 365273331Sbryanvstatic void vxlan_sysctl_destroy(struct vxlan_softc *); 366273331Sbryanvstatic int vxlan_tunable_int(struct vxlan_softc *, const char *, int); 367273331Sbryanv 368273331Sbryanvstatic void vxlan_ifdetach_event(void *, struct ifnet *); 369273331Sbryanvstatic void vxlan_load(void); 370273331Sbryanvstatic void vxlan_unload(void); 371273331Sbryanvstatic int vxlan_modevent(module_t, int, void *); 372273331Sbryanv 373273331Sbryanvstatic const char vxlan_name[] = "vxlan"; 374273331Sbryanvstatic MALLOC_DEFINE(M_VXLAN, vxlan_name, 375273331Sbryanv "Virtual eXtensible LAN Interface"); 376273331Sbryanvstatic struct if_clone *vxlan_cloner; 377273331Sbryanvstatic struct mtx vxlan_list_mtx; 378273331Sbryanvstatic LIST_HEAD(, vxlan_socket) vxlan_socket_list; 379273331Sbryanv 380273331Sbryanvstatic eventhandler_tag vxlan_ifdetach_event_tag; 381273331Sbryanv 382273331SbryanvSYSCTL_DECL(_net_link); 383273331SbryanvSYSCTL_NODE(_net_link, OID_AUTO, vxlan, CTLFLAG_RW, 0, 384273331Sbryanv "Virtual eXtensible Local Area Network"); 385273331Sbryanv 386273331Sbryanvstatic int vxlan_legacy_port = 0; 387273331SbryanvTUNABLE_INT("net.link.vxlan.legacy_port", &vxlan_legacy_port); 388273331Sbryanvstatic int vxlan_reuse_port = 0; 389273331SbryanvTUNABLE_INT("net.link.vxlan.reuse_port", &vxlan_reuse_port); 390273331Sbryanv 391273331Sbryanv/* Default maximum number of addresses in the forwarding table. */ 392273331Sbryanv#ifndef VXLAN_FTABLE_MAX 393273331Sbryanv#define VXLAN_FTABLE_MAX 2000 394273331Sbryanv#endif 395273331Sbryanv 396273331Sbryanv/* Timeout (in seconds) of addresses learned in the forwarding table. */ 397273331Sbryanv#ifndef VXLAN_FTABLE_TIMEOUT 398273331Sbryanv#define VXLAN_FTABLE_TIMEOUT (20 * 60) 399273331Sbryanv#endif 400273331Sbryanv 401273331Sbryanv/* 402273331Sbryanv * Maximum timeout (in seconds) of addresses learned in the forwarding 403273331Sbryanv * table. 404273331Sbryanv */ 405273331Sbryanv#ifndef VXLAN_FTABLE_MAX_TIMEOUT 406273331Sbryanv#define VXLAN_FTABLE_MAX_TIMEOUT (60 * 60 * 24) 407273331Sbryanv#endif 408273331Sbryanv 409273331Sbryanv/* Number of seconds between pruning attempts of the forwarding table. */ 410273331Sbryanv#ifndef VXLAN_FTABLE_PRUNE 411273331Sbryanv#define VXLAN_FTABLE_PRUNE (5 * 60) 412273331Sbryanv#endif 413273331Sbryanv 414273331Sbryanvstatic int vxlan_ftable_prune_period = VXLAN_FTABLE_PRUNE; 415273331Sbryanv 416273331Sbryanvstruct vxlan_control { 417273331Sbryanv int (*vxlc_func)(struct vxlan_softc *, void *); 418273331Sbryanv int vxlc_argsize; 419273331Sbryanv int vxlc_flags; 420273331Sbryanv#define VXLAN_CTRL_FLAG_COPYIN 0x01 421273331Sbryanv#define VXLAN_CTRL_FLAG_COPYOUT 0x02 422273331Sbryanv#define VXLAN_CTRL_FLAG_SUSER 0x04 423273331Sbryanv}; 424273331Sbryanv 425273331Sbryanvstatic const struct vxlan_control vxlan_control_table[] = { 426273331Sbryanv [VXLAN_CMD_GET_CONFIG] = 427273331Sbryanv { vxlan_ctrl_get_config, sizeof(struct ifvxlancfg), 428273331Sbryanv VXLAN_CTRL_FLAG_COPYOUT 429273331Sbryanv }, 430273331Sbryanv 431273331Sbryanv [VXLAN_CMD_SET_VNI] = 432273331Sbryanv { vxlan_ctrl_set_vni, sizeof(struct ifvxlancmd), 433273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 434273331Sbryanv }, 435273331Sbryanv 436273331Sbryanv [VXLAN_CMD_SET_LOCAL_ADDR] = 437273331Sbryanv { vxlan_ctrl_set_local_addr, sizeof(struct ifvxlancmd), 438273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 439273331Sbryanv }, 440273331Sbryanv 441273331Sbryanv [VXLAN_CMD_SET_REMOTE_ADDR] = 442273331Sbryanv { vxlan_ctrl_set_remote_addr, sizeof(struct ifvxlancmd), 443273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 444273331Sbryanv }, 445273331Sbryanv 446273331Sbryanv [VXLAN_CMD_SET_LOCAL_PORT] = 447273331Sbryanv { vxlan_ctrl_set_local_port, sizeof(struct ifvxlancmd), 448273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 449273331Sbryanv }, 450273331Sbryanv 451273331Sbryanv [VXLAN_CMD_SET_REMOTE_PORT] = 452273331Sbryanv { vxlan_ctrl_set_remote_port, sizeof(struct ifvxlancmd), 453273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 454273331Sbryanv }, 455273331Sbryanv 456273331Sbryanv [VXLAN_CMD_SET_PORT_RANGE] = 457273331Sbryanv { vxlan_ctrl_set_port_range, sizeof(struct ifvxlancmd), 458273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 459273331Sbryanv }, 460273331Sbryanv 461273331Sbryanv [VXLAN_CMD_SET_FTABLE_TIMEOUT] = 462273331Sbryanv { vxlan_ctrl_set_ftable_timeout, sizeof(struct ifvxlancmd), 463273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 464273331Sbryanv }, 465273331Sbryanv 466273331Sbryanv [VXLAN_CMD_SET_FTABLE_MAX] = 467273331Sbryanv { vxlan_ctrl_set_ftable_max, sizeof(struct ifvxlancmd), 468273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 469273331Sbryanv }, 470273331Sbryanv 471273331Sbryanv [VXLAN_CMD_SET_MULTICAST_IF] = 472273331Sbryanv { vxlan_ctrl_set_multicast_if, sizeof(struct ifvxlancmd), 473273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 474273331Sbryanv }, 475273331Sbryanv 476273331Sbryanv [VXLAN_CMD_SET_TTL] = 477273331Sbryanv { vxlan_ctrl_set_ttl, sizeof(struct ifvxlancmd), 478273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 479273331Sbryanv }, 480273331Sbryanv 481273331Sbryanv [VXLAN_CMD_SET_LEARN] = 482273331Sbryanv { vxlan_ctrl_set_learn, sizeof(struct ifvxlancmd), 483273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 484273331Sbryanv }, 485273331Sbryanv 486273331Sbryanv [VXLAN_CMD_FTABLE_ENTRY_ADD] = 487273331Sbryanv { vxlan_ctrl_ftable_entry_add, sizeof(struct ifvxlancmd), 488273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 489273331Sbryanv }, 490273331Sbryanv 491273331Sbryanv [VXLAN_CMD_FTABLE_ENTRY_REM] = 492273331Sbryanv { vxlan_ctrl_ftable_entry_rem, sizeof(struct ifvxlancmd), 493273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 494273331Sbryanv }, 495273331Sbryanv 496273331Sbryanv [VXLAN_CMD_FLUSH] = 497273331Sbryanv { vxlan_ctrl_flush, sizeof(struct ifvxlancmd), 498273331Sbryanv VXLAN_CTRL_FLAG_COPYIN | VXLAN_CTRL_FLAG_SUSER, 499273331Sbryanv }, 500273331Sbryanv}; 501273331Sbryanv 502273331Sbryanvstatic const int vxlan_control_table_size = nitems(vxlan_control_table); 503273331Sbryanv 504273331Sbryanvstatic int 505273331Sbryanvvxlan_ftable_addr_cmp(const uint8_t *a, const uint8_t *b) 506273331Sbryanv{ 507273331Sbryanv int i, d; 508273331Sbryanv 509273331Sbryanv for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) 510273331Sbryanv d = ((int)a[i]) - ((int)b[i]); 511273331Sbryanv 512273331Sbryanv return (d); 513273331Sbryanv} 514273331Sbryanv 515273331Sbryanvstatic void 516273331Sbryanvvxlan_ftable_init(struct vxlan_softc *sc) 517273331Sbryanv{ 518273331Sbryanv int i; 519273331Sbryanv 520273331Sbryanv sc->vxl_ftable = malloc(sizeof(struct vxlan_ftable_head) * 521273331Sbryanv VXLAN_SC_FTABLE_SIZE, M_VXLAN, M_ZERO | M_WAITOK); 522273331Sbryanv 523273331Sbryanv for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) 524273331Sbryanv LIST_INIT(&sc->vxl_ftable[i]); 525273331Sbryanv sc->vxl_ftable_hash_key = arc4random(); 526273331Sbryanv} 527273331Sbryanv 528273331Sbryanvstatic void 529273331Sbryanvvxlan_ftable_fini(struct vxlan_softc *sc) 530273331Sbryanv{ 531273331Sbryanv int i; 532273331Sbryanv 533273331Sbryanv for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) { 534273331Sbryanv KASSERT(LIST_EMPTY(&sc->vxl_ftable[i]), 535273331Sbryanv ("%s: vxlan %p ftable[%d] not empty", __func__, sc, i)); 536273331Sbryanv } 537273331Sbryanv MPASS(sc->vxl_ftable_cnt == 0); 538273331Sbryanv 539273331Sbryanv free(sc->vxl_ftable, M_VXLAN); 540273331Sbryanv sc->vxl_ftable = NULL; 541273331Sbryanv} 542273331Sbryanv 543273331Sbryanvstatic void 544273331Sbryanvvxlan_ftable_flush(struct vxlan_softc *sc, int all) 545273331Sbryanv{ 546273331Sbryanv struct vxlan_ftable_entry *fe, *tfe; 547273331Sbryanv int i; 548273331Sbryanv 549273331Sbryanv for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) { 550273331Sbryanv LIST_FOREACH_SAFE(fe, &sc->vxl_ftable[i], vxlfe_hash, tfe) { 551273331Sbryanv if (all || VXLAN_FE_IS_DYNAMIC(fe)) 552273331Sbryanv vxlan_ftable_entry_destroy(sc, fe); 553273331Sbryanv } 554273331Sbryanv } 555273331Sbryanv} 556273331Sbryanv 557273331Sbryanvstatic void 558273331Sbryanvvxlan_ftable_expire(struct vxlan_softc *sc) 559273331Sbryanv{ 560273331Sbryanv struct vxlan_ftable_entry *fe, *tfe; 561273331Sbryanv int i; 562273331Sbryanv 563273331Sbryanv VXLAN_LOCK_WASSERT(sc); 564273331Sbryanv 565273331Sbryanv for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) { 566273331Sbryanv LIST_FOREACH_SAFE(fe, &sc->vxl_ftable[i], vxlfe_hash, tfe) { 567273331Sbryanv if (VXLAN_FE_IS_DYNAMIC(fe) && 568273331Sbryanv time_uptime >= fe->vxlfe_expire) 569273331Sbryanv vxlan_ftable_entry_destroy(sc, fe); 570273331Sbryanv } 571273331Sbryanv } 572273331Sbryanv} 573273331Sbryanv 574273331Sbryanvstatic int 575273331Sbryanvvxlan_ftable_update_locked(struct vxlan_softc *sc, const struct sockaddr *sa, 576273331Sbryanv const uint8_t *mac, struct rm_priotracker *tracker) 577273331Sbryanv{ 578273331Sbryanv union vxlan_sockaddr vxlsa; 579273331Sbryanv struct vxlan_ftable_entry *fe; 580273331Sbryanv int error; 581273331Sbryanv 582273331Sbryanv VXLAN_LOCK_ASSERT(sc); 583273331Sbryanv 584273331Sbryanvagain: 585273331Sbryanv /* 586273331Sbryanv * A forwarding entry for this MAC address might already exist. If 587273331Sbryanv * so, update it, otherwise create a new one. We may have to upgrade 588273331Sbryanv * the lock if we have to change or create an entry. 589273331Sbryanv */ 590273331Sbryanv fe = vxlan_ftable_entry_lookup(sc, mac); 591273331Sbryanv if (fe != NULL) { 592273331Sbryanv fe->vxlfe_expire = time_uptime + sc->vxl_ftable_timeout; 593273331Sbryanv 594273331Sbryanv if (!VXLAN_FE_IS_DYNAMIC(fe) || 595273331Sbryanv vxlan_sockaddr_in_equal(&fe->vxlfe_raddr, sa)) 596273331Sbryanv return (0); 597273331Sbryanv if (!VXLAN_LOCK_WOWNED(sc)) { 598273331Sbryanv VXLAN_RUNLOCK(sc, tracker); 599273331Sbryanv VXLAN_WLOCK(sc); 600273331Sbryanv sc->vxl_stats.ftable_lock_upgrade_failed++; 601273331Sbryanv goto again; 602273331Sbryanv } 603273331Sbryanv vxlan_sockaddr_in_copy(&fe->vxlfe_raddr, sa); 604273331Sbryanv return (0); 605273331Sbryanv } 606273331Sbryanv 607273331Sbryanv if (!VXLAN_LOCK_WOWNED(sc)) { 608273331Sbryanv VXLAN_RUNLOCK(sc, tracker); 609273331Sbryanv VXLAN_WLOCK(sc); 610273331Sbryanv sc->vxl_stats.ftable_lock_upgrade_failed++; 611273331Sbryanv goto again; 612273331Sbryanv } 613273331Sbryanv 614273331Sbryanv if (sc->vxl_ftable_cnt >= sc->vxl_ftable_max) { 615273331Sbryanv sc->vxl_stats.ftable_nospace++; 616273331Sbryanv return (ENOSPC); 617273331Sbryanv } 618273331Sbryanv 619273331Sbryanv fe = vxlan_ftable_entry_alloc(); 620273331Sbryanv if (fe == NULL) 621273331Sbryanv return (ENOMEM); 622273331Sbryanv 623273331Sbryanv /* 624273331Sbryanv * The source port may be randomly select by the remove host, so 625273331Sbryanv * use the port of the default destination address. 626273331Sbryanv */ 627273331Sbryanv vxlan_sockaddr_copy(&vxlsa, sa); 628273331Sbryanv vxlsa.in4.sin_port = sc->vxl_dst_addr.in4.sin_port; 629273331Sbryanv 630273331Sbryanv vxlan_ftable_entry_init(sc, fe, mac, &vxlsa.sa, 631273331Sbryanv VXLAN_FE_FLAG_DYNAMIC); 632273331Sbryanv 633273331Sbryanv /* The prior lookup failed, so the insert should not. */ 634273331Sbryanv error = vxlan_ftable_entry_insert(sc, fe); 635273331Sbryanv MPASS(error == 0); 636273331Sbryanv 637273331Sbryanv return (0); 638273331Sbryanv} 639273331Sbryanv 640273331Sbryanvstatic int 641273331Sbryanvvxlan_ftable_update(struct vxlan_softc *sc, const struct sockaddr *sa, 642273331Sbryanv const uint8_t *mac) 643273331Sbryanv{ 644273331Sbryanv struct rm_priotracker tracker; 645273331Sbryanv int error; 646273331Sbryanv 647273331Sbryanv VXLAN_RLOCK(sc, &tracker); 648273331Sbryanv error = vxlan_ftable_update_locked(sc, sa, mac, &tracker); 649273331Sbryanv VXLAN_UNLOCK(sc, &tracker); 650273331Sbryanv 651273331Sbryanv return (error); 652273331Sbryanv} 653273331Sbryanv 654273331Sbryanvstatic int 655273331Sbryanvvxlan_ftable_sysctl_dump(SYSCTL_HANDLER_ARGS) 656273331Sbryanv{ 657273331Sbryanv struct rm_priotracker tracker; 658273331Sbryanv struct sbuf sb; 659273331Sbryanv struct vxlan_softc *sc; 660273331Sbryanv struct vxlan_ftable_entry *fe; 661273331Sbryanv size_t size; 662273331Sbryanv int i, error; 663273331Sbryanv 664273331Sbryanv /* 665273331Sbryanv * This is mostly intended for debugging during development. It is 666273331Sbryanv * not practical to dump an entire large table this way. 667273331Sbryanv */ 668273331Sbryanv 669273331Sbryanv sc = arg1; 670273331Sbryanv size = PAGE_SIZE; /* Calculate later. */ 671273331Sbryanv 672273331Sbryanv sbuf_new(&sb, NULL, size, SBUF_FIXEDLEN); 673273331Sbryanv sbuf_putc(&sb, '\n'); 674273331Sbryanv 675273331Sbryanv VXLAN_RLOCK(sc, &tracker); 676273331Sbryanv for (i = 0; i < VXLAN_SC_FTABLE_SIZE; i++) { 677273331Sbryanv LIST_FOREACH(fe, &sc->vxl_ftable[i], vxlfe_hash) { 678273331Sbryanv if (sbuf_error(&sb) != 0) 679273331Sbryanv break; 680273331Sbryanv vxlan_ftable_entry_dump(fe, &sb); 681273331Sbryanv } 682273331Sbryanv } 683273331Sbryanv VXLAN_RUNLOCK(sc, &tracker); 684273331Sbryanv 685273331Sbryanv if (sbuf_len(&sb) == 1) 686273331Sbryanv sbuf_setpos(&sb, 0); 687273331Sbryanv 688273331Sbryanv sbuf_finish(&sb); 689273331Sbryanv error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 690273331Sbryanv sbuf_delete(&sb); 691273331Sbryanv 692273331Sbryanv return (error); 693273331Sbryanv} 694273331Sbryanv 695273331Sbryanvstatic struct vxlan_ftable_entry * 696273331Sbryanvvxlan_ftable_entry_alloc(void) 697273331Sbryanv{ 698273331Sbryanv struct vxlan_ftable_entry *fe; 699273331Sbryanv 700273331Sbryanv fe = malloc(sizeof(*fe), M_VXLAN, M_ZERO | M_NOWAIT); 701273331Sbryanv 702273331Sbryanv return (fe); 703273331Sbryanv} 704273331Sbryanv 705273331Sbryanvstatic void 706273331Sbryanvvxlan_ftable_entry_free(struct vxlan_ftable_entry *fe) 707273331Sbryanv{ 708273331Sbryanv 709273331Sbryanv free(fe, M_VXLAN); 710273331Sbryanv} 711273331Sbryanv 712273331Sbryanvstatic void 713273331Sbryanvvxlan_ftable_entry_init(struct vxlan_softc *sc, struct vxlan_ftable_entry *fe, 714273331Sbryanv const uint8_t *mac, const struct sockaddr *sa, uint32_t flags) 715273331Sbryanv{ 716273331Sbryanv 717273331Sbryanv fe->vxlfe_flags = flags; 718273331Sbryanv fe->vxlfe_expire = time_uptime + sc->vxl_ftable_timeout; 719284365Sbryanv memcpy(fe->vxlfe_mac, mac, ETHER_ADDR_LEN); 720273331Sbryanv vxlan_sockaddr_copy(&fe->vxlfe_raddr, sa); 721273331Sbryanv} 722273331Sbryanv 723273331Sbryanvstatic void 724273331Sbryanvvxlan_ftable_entry_destroy(struct vxlan_softc *sc, 725273331Sbryanv struct vxlan_ftable_entry *fe) 726273331Sbryanv{ 727273331Sbryanv 728273331Sbryanv sc->vxl_ftable_cnt--; 729273331Sbryanv LIST_REMOVE(fe, vxlfe_hash); 730273331Sbryanv vxlan_ftable_entry_free(fe); 731273331Sbryanv} 732273331Sbryanv 733273331Sbryanvstatic int 734273331Sbryanvvxlan_ftable_entry_insert(struct vxlan_softc *sc, 735273331Sbryanv struct vxlan_ftable_entry *fe) 736273331Sbryanv{ 737273331Sbryanv struct vxlan_ftable_entry *lfe; 738273331Sbryanv uint32_t hash; 739273331Sbryanv int dir; 740273331Sbryanv 741273331Sbryanv VXLAN_LOCK_WASSERT(sc); 742273331Sbryanv hash = VXLAN_SC_FTABLE_HASH(sc, fe->vxlfe_mac); 743273331Sbryanv 744273331Sbryanv lfe = LIST_FIRST(&sc->vxl_ftable[hash]); 745273331Sbryanv if (lfe == NULL) { 746273331Sbryanv LIST_INSERT_HEAD(&sc->vxl_ftable[hash], fe, vxlfe_hash); 747273331Sbryanv goto out; 748273331Sbryanv } 749273331Sbryanv 750273331Sbryanv do { 751273331Sbryanv dir = vxlan_ftable_addr_cmp(fe->vxlfe_mac, lfe->vxlfe_mac); 752273331Sbryanv if (dir == 0) 753273331Sbryanv return (EEXIST); 754273331Sbryanv if (dir > 0) { 755273331Sbryanv LIST_INSERT_BEFORE(lfe, fe, vxlfe_hash); 756273331Sbryanv goto out; 757273331Sbryanv } else if (LIST_NEXT(lfe, vxlfe_hash) == NULL) { 758273331Sbryanv LIST_INSERT_AFTER(lfe, fe, vxlfe_hash); 759273331Sbryanv goto out; 760273331Sbryanv } else 761273331Sbryanv lfe = LIST_NEXT(lfe, vxlfe_hash); 762273331Sbryanv } while (lfe != NULL); 763273331Sbryanv 764273331Sbryanvout: 765273331Sbryanv sc->vxl_ftable_cnt++; 766273331Sbryanv 767273331Sbryanv return (0); 768273331Sbryanv} 769273331Sbryanv 770273331Sbryanvstatic struct vxlan_ftable_entry * 771273331Sbryanvvxlan_ftable_entry_lookup(struct vxlan_softc *sc, const uint8_t *mac) 772273331Sbryanv{ 773273331Sbryanv struct vxlan_ftable_entry *fe; 774273331Sbryanv uint32_t hash; 775273331Sbryanv int dir; 776273331Sbryanv 777273331Sbryanv VXLAN_LOCK_ASSERT(sc); 778273331Sbryanv hash = VXLAN_SC_FTABLE_HASH(sc, mac); 779273331Sbryanv 780273331Sbryanv LIST_FOREACH(fe, &sc->vxl_ftable[hash], vxlfe_hash) { 781327142Sae dir = vxlan_ftable_addr_cmp(mac, fe->vxlfe_mac); 782273331Sbryanv if (dir == 0) 783273331Sbryanv return (fe); 784273331Sbryanv if (dir > 0) 785273331Sbryanv break; 786273331Sbryanv } 787273331Sbryanv 788273331Sbryanv return (NULL); 789273331Sbryanv} 790273331Sbryanv 791273331Sbryanvstatic void 792273331Sbryanvvxlan_ftable_entry_dump(struct vxlan_ftable_entry *fe, struct sbuf *sb) 793273331Sbryanv{ 794273331Sbryanv char buf[64]; 795273331Sbryanv const union vxlan_sockaddr *sa; 796273331Sbryanv const void *addr; 797273331Sbryanv int i, len, af, width; 798273331Sbryanv 799273331Sbryanv sa = &fe->vxlfe_raddr; 800273331Sbryanv af = sa->sa.sa_family; 801273331Sbryanv len = sbuf_len(sb); 802273331Sbryanv 803273331Sbryanv sbuf_printf(sb, "%c 0x%02X ", VXLAN_FE_IS_DYNAMIC(fe) ? 'D' : 'S', 804273331Sbryanv fe->vxlfe_flags); 805273331Sbryanv 806273331Sbryanv for (i = 0; i < ETHER_ADDR_LEN - 1; i++) 807273331Sbryanv sbuf_printf(sb, "%02X:", fe->vxlfe_mac[i]); 808273331Sbryanv sbuf_printf(sb, "%02X ", fe->vxlfe_mac[i]); 809273331Sbryanv 810273331Sbryanv if (af == AF_INET) { 811273331Sbryanv addr = &sa->in4.sin_addr; 812273331Sbryanv width = INET_ADDRSTRLEN - 1; 813273331Sbryanv } else { 814273331Sbryanv addr = &sa->in6.sin6_addr; 815273331Sbryanv width = INET6_ADDRSTRLEN - 1; 816273331Sbryanv } 817273331Sbryanv inet_ntop(af, addr, buf, sizeof(buf)); 818273331Sbryanv sbuf_printf(sb, "%*s ", width, buf); 819273331Sbryanv 820273331Sbryanv sbuf_printf(sb, "%08jd", (intmax_t)fe->vxlfe_expire); 821273331Sbryanv 822273331Sbryanv sbuf_putc(sb, '\n'); 823273331Sbryanv 824273331Sbryanv /* Truncate a partial line. */ 825273331Sbryanv if (sbuf_error(sb) != 0) 826273331Sbryanv sbuf_setpos(sb, len); 827273331Sbryanv} 828273331Sbryanv 829273331Sbryanvstatic struct vxlan_socket * 830273331Sbryanvvxlan_socket_alloc(const union vxlan_sockaddr *sa) 831273331Sbryanv{ 832273331Sbryanv struct vxlan_socket *vso; 833273331Sbryanv int i; 834273331Sbryanv 835273331Sbryanv vso = malloc(sizeof(*vso), M_VXLAN, M_WAITOK | M_ZERO); 836273331Sbryanv rm_init(&vso->vxlso_lock, "vxlansorm"); 837273331Sbryanv refcount_init(&vso->vxlso_refcnt, 0); 838273331Sbryanv for (i = 0; i < VXLAN_SO_VNI_HASH_SIZE; i++) 839273331Sbryanv LIST_INIT(&vso->vxlso_vni_hash[i]); 840273331Sbryanv vso->vxlso_laddr = *sa; 841273331Sbryanv 842273331Sbryanv return (vso); 843273331Sbryanv} 844273331Sbryanv 845273331Sbryanvstatic void 846273331Sbryanvvxlan_socket_destroy(struct vxlan_socket *vso) 847273331Sbryanv{ 848273331Sbryanv struct socket *so; 849273331Sbryanv struct vxlan_socket_mc_info *mc; 850273331Sbryanv int i; 851273331Sbryanv 852273331Sbryanv for (i = 0; i < VXLAN_SO_MC_MAX_GROUPS; i++) { 853273331Sbryanv mc = &vso->vxlso_mc[i]; 854273331Sbryanv KASSERT(mc->vxlsomc_gaddr.sa.sa_family == AF_UNSPEC, 855273331Sbryanv ("%s: socket %p mc[%d] still has address", 856273331Sbryanv __func__, vso, i)); 857273331Sbryanv } 858273331Sbryanv 859273331Sbryanv for (i = 0; i < VXLAN_SO_VNI_HASH_SIZE; i++) { 860273331Sbryanv KASSERT(LIST_EMPTY(&vso->vxlso_vni_hash[i]), 861273331Sbryanv ("%s: socket %p vni_hash[%d] not empty", 862273331Sbryanv __func__, vso, i)); 863273331Sbryanv } 864273331Sbryanv 865273331Sbryanv so = vso->vxlso_sock; 866273331Sbryanv if (so != NULL) { 867273331Sbryanv vso->vxlso_sock = NULL; 868273331Sbryanv soclose(so); 869273331Sbryanv } 870273331Sbryanv 871273331Sbryanv rm_destroy(&vso->vxlso_lock); 872273331Sbryanv free(vso, M_VXLAN); 873273331Sbryanv} 874273331Sbryanv 875273331Sbryanvstatic void 876273331Sbryanvvxlan_socket_release(struct vxlan_socket *vso) 877273331Sbryanv{ 878273331Sbryanv int destroy; 879273331Sbryanv 880273331Sbryanv mtx_lock(&vxlan_list_mtx); 881273331Sbryanv destroy = VXLAN_SO_RELEASE(vso); 882273331Sbryanv if (destroy != 0) 883273331Sbryanv LIST_REMOVE(vso, vxlso_entry); 884273331Sbryanv mtx_unlock(&vxlan_list_mtx); 885273331Sbryanv 886273331Sbryanv if (destroy != 0) 887273331Sbryanv vxlan_socket_destroy(vso); 888273331Sbryanv} 889273331Sbryanv 890273331Sbryanvstatic struct vxlan_socket * 891273331Sbryanvvxlan_socket_lookup(union vxlan_sockaddr *vxlsa) 892273331Sbryanv{ 893273331Sbryanv struct vxlan_socket *vso; 894273331Sbryanv 895273331Sbryanv mtx_lock(&vxlan_list_mtx); 896273331Sbryanv LIST_FOREACH(vso, &vxlan_socket_list, vxlso_entry) { 897273331Sbryanv if (vxlan_sockaddr_cmp(&vso->vxlso_laddr, &vxlsa->sa) == 0) { 898273331Sbryanv VXLAN_SO_ACQUIRE(vso); 899273331Sbryanv break; 900273331Sbryanv } 901273331Sbryanv } 902273331Sbryanv mtx_unlock(&vxlan_list_mtx); 903273331Sbryanv 904273331Sbryanv return (vso); 905273331Sbryanv} 906273331Sbryanv 907273331Sbryanvstatic void 908273331Sbryanvvxlan_socket_insert(struct vxlan_socket *vso) 909273331Sbryanv{ 910273331Sbryanv 911273331Sbryanv mtx_lock(&vxlan_list_mtx); 912273331Sbryanv VXLAN_SO_ACQUIRE(vso); 913273331Sbryanv LIST_INSERT_HEAD(&vxlan_socket_list, vso, vxlso_entry); 914273331Sbryanv mtx_unlock(&vxlan_list_mtx); 915273331Sbryanv} 916273331Sbryanv 917273331Sbryanvstatic int 918273331Sbryanvvxlan_socket_init(struct vxlan_socket *vso, struct ifnet *ifp) 919273331Sbryanv{ 920273331Sbryanv struct thread *td; 921273331Sbryanv int error; 922273331Sbryanv 923273331Sbryanv td = curthread; 924273331Sbryanv 925273331Sbryanv error = socreate(vso->vxlso_laddr.sa.sa_family, &vso->vxlso_sock, 926273331Sbryanv SOCK_DGRAM, IPPROTO_UDP, td->td_ucred, td); 927273331Sbryanv if (error) { 928273331Sbryanv if_printf(ifp, "cannot create socket: %d\n", error); 929273331Sbryanv return (error); 930273331Sbryanv } 931273331Sbryanv 932273331Sbryanv error = udp_set_kernel_tunneling(vso->vxlso_sock, 933273331Sbryanv vxlan_rcv_udp_packet, vso); 934273331Sbryanv if (error) { 935273331Sbryanv if_printf(ifp, "cannot set tunneling function: %d\n", error); 936273331Sbryanv return (error); 937273331Sbryanv } 938273331Sbryanv 939273331Sbryanv if (vxlan_reuse_port != 0) { 940273331Sbryanv struct sockopt sopt; 941273331Sbryanv int val = 1; 942273331Sbryanv 943273331Sbryanv bzero(&sopt, sizeof(sopt)); 944273331Sbryanv sopt.sopt_dir = SOPT_SET; 945273331Sbryanv sopt.sopt_level = IPPROTO_IP; 946273331Sbryanv sopt.sopt_name = SO_REUSEPORT; 947273331Sbryanv sopt.sopt_val = &val; 948273331Sbryanv sopt.sopt_valsize = sizeof(val); 949273331Sbryanv error = sosetopt(vso->vxlso_sock, &sopt); 950273331Sbryanv if (error) { 951273331Sbryanv if_printf(ifp, 952273331Sbryanv "cannot set REUSEADDR socket opt: %d\n", error); 953273331Sbryanv return (error); 954273331Sbryanv } 955273331Sbryanv } 956273331Sbryanv 957273331Sbryanv return (0); 958273331Sbryanv} 959273331Sbryanv 960273331Sbryanvstatic int 961273331Sbryanvvxlan_socket_bind(struct vxlan_socket *vso, struct ifnet *ifp) 962273331Sbryanv{ 963273331Sbryanv union vxlan_sockaddr laddr; 964273331Sbryanv struct thread *td; 965273331Sbryanv int error; 966273331Sbryanv 967273331Sbryanv td = curthread; 968273331Sbryanv laddr = vso->vxlso_laddr; 969273331Sbryanv 970273331Sbryanv error = sobind(vso->vxlso_sock, &laddr.sa, td); 971273331Sbryanv if (error) { 972273331Sbryanv if (error != EADDRINUSE) 973273331Sbryanv if_printf(ifp, "cannot bind socket: %d\n", error); 974273331Sbryanv return (error); 975273331Sbryanv } 976273331Sbryanv 977273331Sbryanv return (0); 978273331Sbryanv} 979273331Sbryanv 980273331Sbryanvstatic int 981273331Sbryanvvxlan_socket_create(struct ifnet *ifp, int multicast, 982273331Sbryanv const union vxlan_sockaddr *saddr, struct vxlan_socket **vsop) 983273331Sbryanv{ 984273331Sbryanv union vxlan_sockaddr laddr; 985273331Sbryanv struct vxlan_socket *vso; 986273331Sbryanv int error; 987273331Sbryanv 988273331Sbryanv laddr = *saddr; 989273331Sbryanv 990273331Sbryanv /* 991273331Sbryanv * If this socket will be multicast, then only the local port 992273331Sbryanv * must be specified when binding. 993273331Sbryanv */ 994273331Sbryanv if (multicast != 0) { 995273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(&laddr)) 996273331Sbryanv laddr.in4.sin_addr.s_addr = INADDR_ANY; 997273331Sbryanv#ifdef INET6 998273331Sbryanv else 999273331Sbryanv laddr.in6.sin6_addr = in6addr_any; 1000273331Sbryanv#endif 1001273331Sbryanv } 1002273331Sbryanv 1003273331Sbryanv vso = vxlan_socket_alloc(&laddr); 1004273331Sbryanv if (vso == NULL) 1005273331Sbryanv return (ENOMEM); 1006273331Sbryanv 1007273331Sbryanv error = vxlan_socket_init(vso, ifp); 1008273331Sbryanv if (error) 1009273331Sbryanv goto fail; 1010273331Sbryanv 1011273331Sbryanv error = vxlan_socket_bind(vso, ifp); 1012273331Sbryanv if (error) 1013273331Sbryanv goto fail; 1014273331Sbryanv 1015273331Sbryanv /* 1016273331Sbryanv * There is a small window between the bind completing and 1017273331Sbryanv * inserting the socket, so that a concurrent create may fail. 1018273331Sbryanv * Let's not worry about that for now. 1019273331Sbryanv */ 1020273331Sbryanv vxlan_socket_insert(vso); 1021273331Sbryanv *vsop = vso; 1022273331Sbryanv 1023273331Sbryanv return (0); 1024273331Sbryanv 1025273331Sbryanvfail: 1026273331Sbryanv vxlan_socket_destroy(vso); 1027273331Sbryanv 1028273331Sbryanv return (error); 1029273331Sbryanv} 1030273331Sbryanv 1031273331Sbryanvstatic void 1032273331Sbryanvvxlan_socket_ifdetach(struct vxlan_socket *vso, struct ifnet *ifp, 1033273331Sbryanv struct vxlan_softc_head *list) 1034273331Sbryanv{ 1035273331Sbryanv struct rm_priotracker tracker; 1036273331Sbryanv struct vxlan_softc *sc; 1037273331Sbryanv int i; 1038273331Sbryanv 1039273331Sbryanv VXLAN_SO_RLOCK(vso, &tracker); 1040273331Sbryanv for (i = 0; i < VXLAN_SO_VNI_HASH_SIZE; i++) { 1041273331Sbryanv LIST_FOREACH(sc, &vso->vxlso_vni_hash[i], vxl_entry) 1042273331Sbryanv vxlan_ifdetach(sc, ifp, list); 1043273331Sbryanv } 1044273331Sbryanv VXLAN_SO_RUNLOCK(vso, &tracker); 1045273331Sbryanv} 1046273331Sbryanv 1047273331Sbryanvstatic struct vxlan_socket * 1048273331Sbryanvvxlan_socket_mc_lookup(const union vxlan_sockaddr *vxlsa) 1049273331Sbryanv{ 1050273331Sbryanv struct vxlan_socket *vso; 1051273331Sbryanv union vxlan_sockaddr laddr; 1052273331Sbryanv 1053273331Sbryanv laddr = *vxlsa; 1054273331Sbryanv 1055273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(&laddr)) 1056273331Sbryanv laddr.in4.sin_addr.s_addr = INADDR_ANY; 1057273331Sbryanv#ifdef INET6 1058273331Sbryanv else 1059273331Sbryanv laddr.in6.sin6_addr = in6addr_any; 1060273331Sbryanv#endif 1061273331Sbryanv 1062273331Sbryanv vso = vxlan_socket_lookup(&laddr); 1063273331Sbryanv 1064273331Sbryanv return (vso); 1065273331Sbryanv} 1066273331Sbryanv 1067273331Sbryanvstatic int 1068273331Sbryanvvxlan_sockaddr_mc_info_match(const struct vxlan_socket_mc_info *mc, 1069273331Sbryanv const union vxlan_sockaddr *group, const union vxlan_sockaddr *local, 1070273331Sbryanv int ifidx) 1071273331Sbryanv{ 1072273331Sbryanv 1073273331Sbryanv if (!vxlan_sockaddr_in_any(local) && 1074273331Sbryanv !vxlan_sockaddr_in_equal(&mc->vxlsomc_saddr, &local->sa)) 1075273331Sbryanv return (0); 1076273331Sbryanv if (!vxlan_sockaddr_in_equal(&mc->vxlsomc_gaddr, &group->sa)) 1077273331Sbryanv return (0); 1078273331Sbryanv if (ifidx != 0 && ifidx != mc->vxlsomc_ifidx) 1079273331Sbryanv return (0); 1080273331Sbryanv 1081273331Sbryanv return (1); 1082273331Sbryanv} 1083273331Sbryanv 1084273331Sbryanvstatic int 1085273331Sbryanvvxlan_socket_mc_join_group(struct vxlan_socket *vso, 1086273331Sbryanv const union vxlan_sockaddr *group, const union vxlan_sockaddr *local, 1087273331Sbryanv int *ifidx, union vxlan_sockaddr *source) 1088273331Sbryanv{ 1089273331Sbryanv struct sockopt sopt; 1090273331Sbryanv int error; 1091273331Sbryanv 1092273331Sbryanv *source = *local; 1093273331Sbryanv 1094273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(group)) { 1095273331Sbryanv struct ip_mreq mreq; 1096273331Sbryanv 1097273331Sbryanv mreq.imr_multiaddr = group->in4.sin_addr; 1098273331Sbryanv mreq.imr_interface = local->in4.sin_addr; 1099273331Sbryanv 1100273331Sbryanv bzero(&sopt, sizeof(sopt)); 1101273331Sbryanv sopt.sopt_dir = SOPT_SET; 1102273331Sbryanv sopt.sopt_level = IPPROTO_IP; 1103273331Sbryanv sopt.sopt_name = IP_ADD_MEMBERSHIP; 1104273331Sbryanv sopt.sopt_val = &mreq; 1105273331Sbryanv sopt.sopt_valsize = sizeof(mreq); 1106273331Sbryanv error = sosetopt(vso->vxlso_sock, &sopt); 1107273331Sbryanv if (error) 1108273331Sbryanv return (error); 1109273331Sbryanv 1110273331Sbryanv /* 1111273331Sbryanv * BMV: Ideally, there would be a formal way for us to get 1112273331Sbryanv * the local interface that was selected based on the 1113273331Sbryanv * imr_interface address. We could then update *ifidx so 1114273331Sbryanv * vxlan_sockaddr_mc_info_match() would return a match for 1115273331Sbryanv * later creates that explicitly set the multicast interface. 1116273331Sbryanv * 1117273331Sbryanv * If we really need to, we can of course look in the INP's 1118273331Sbryanv * membership list: 1119273331Sbryanv * sotoinpcb(vso->vxlso_sock)->inp_moptions-> 1120273331Sbryanv * imo_membership[]->inm_ifp 1121273331Sbryanv * similarly to imo_match_group(). 1122273331Sbryanv */ 1123273331Sbryanv source->in4.sin_addr = local->in4.sin_addr; 1124273331Sbryanv 1125273331Sbryanv } else if (VXLAN_SOCKADDR_IS_IPV6(group)) { 1126273331Sbryanv struct ipv6_mreq mreq; 1127273331Sbryanv 1128273331Sbryanv mreq.ipv6mr_multiaddr = group->in6.sin6_addr; 1129273331Sbryanv mreq.ipv6mr_interface = *ifidx; 1130273331Sbryanv 1131273331Sbryanv bzero(&sopt, sizeof(sopt)); 1132273331Sbryanv sopt.sopt_dir = SOPT_SET; 1133273331Sbryanv sopt.sopt_level = IPPROTO_IPV6; 1134273331Sbryanv sopt.sopt_name = IPV6_JOIN_GROUP; 1135273331Sbryanv sopt.sopt_val = &mreq; 1136273331Sbryanv sopt.sopt_valsize = sizeof(mreq); 1137273331Sbryanv error = sosetopt(vso->vxlso_sock, &sopt); 1138273331Sbryanv if (error) 1139273331Sbryanv return (error); 1140273331Sbryanv 1141273331Sbryanv /* 1142273331Sbryanv * BMV: As with IPv4, we would really like to know what 1143273331Sbryanv * interface in6p_lookup_mcast_ifp() selected. 1144273331Sbryanv */ 1145273331Sbryanv } else 1146273331Sbryanv error = EAFNOSUPPORT; 1147273331Sbryanv 1148273331Sbryanv return (error); 1149273331Sbryanv} 1150273331Sbryanv 1151273331Sbryanvstatic int 1152273331Sbryanvvxlan_socket_mc_leave_group(struct vxlan_socket *vso, 1153273331Sbryanv const union vxlan_sockaddr *group, const union vxlan_sockaddr *source, 1154273331Sbryanv int ifidx) 1155273331Sbryanv{ 1156273331Sbryanv struct sockopt sopt; 1157273331Sbryanv int error; 1158273331Sbryanv 1159273331Sbryanv bzero(&sopt, sizeof(sopt)); 1160273331Sbryanv sopt.sopt_dir = SOPT_SET; 1161273331Sbryanv 1162273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(group)) { 1163273331Sbryanv struct ip_mreq mreq; 1164273331Sbryanv 1165273331Sbryanv mreq.imr_multiaddr = group->in4.sin_addr; 1166273331Sbryanv mreq.imr_interface = source->in4.sin_addr; 1167273331Sbryanv 1168273331Sbryanv sopt.sopt_level = IPPROTO_IP; 1169273331Sbryanv sopt.sopt_name = IP_DROP_MEMBERSHIP; 1170273331Sbryanv sopt.sopt_val = &mreq; 1171273331Sbryanv sopt.sopt_valsize = sizeof(mreq); 1172273331Sbryanv error = sosetopt(vso->vxlso_sock, &sopt); 1173273331Sbryanv 1174273331Sbryanv } else if (VXLAN_SOCKADDR_IS_IPV6(group)) { 1175273331Sbryanv struct ipv6_mreq mreq; 1176273331Sbryanv 1177273331Sbryanv mreq.ipv6mr_multiaddr = group->in6.sin6_addr; 1178273331Sbryanv mreq.ipv6mr_interface = ifidx; 1179273331Sbryanv 1180273331Sbryanv sopt.sopt_level = IPPROTO_IPV6; 1181273331Sbryanv sopt.sopt_name = IPV6_LEAVE_GROUP; 1182273331Sbryanv sopt.sopt_val = &mreq; 1183273331Sbryanv sopt.sopt_valsize = sizeof(mreq); 1184273331Sbryanv error = sosetopt(vso->vxlso_sock, &sopt); 1185273331Sbryanv 1186273331Sbryanv } else 1187273331Sbryanv error = EAFNOSUPPORT; 1188273331Sbryanv 1189273331Sbryanv return (error); 1190273331Sbryanv} 1191273331Sbryanv 1192273331Sbryanvstatic int 1193273331Sbryanvvxlan_socket_mc_add_group(struct vxlan_socket *vso, 1194273331Sbryanv const union vxlan_sockaddr *group, const union vxlan_sockaddr *local, 1195273331Sbryanv int ifidx, int *idx) 1196273331Sbryanv{ 1197273331Sbryanv union vxlan_sockaddr source; 1198273331Sbryanv struct vxlan_socket_mc_info *mc; 1199273331Sbryanv int i, empty, error; 1200273331Sbryanv 1201273331Sbryanv /* 1202273331Sbryanv * Within a socket, the same multicast group may be used by multiple 1203273331Sbryanv * interfaces, each with a different network identifier. But a socket 1204273331Sbryanv * may only join a multicast group once, so keep track of the users 1205273331Sbryanv * here. 1206273331Sbryanv */ 1207273331Sbryanv 1208273331Sbryanv VXLAN_SO_WLOCK(vso); 1209273331Sbryanv for (empty = 0, i = 0; i < VXLAN_SO_MC_MAX_GROUPS; i++) { 1210273331Sbryanv mc = &vso->vxlso_mc[i]; 1211273331Sbryanv 1212273331Sbryanv if (mc->vxlsomc_gaddr.sa.sa_family == AF_UNSPEC) { 1213273331Sbryanv empty++; 1214273331Sbryanv continue; 1215273331Sbryanv } 1216273331Sbryanv 1217273331Sbryanv if (vxlan_sockaddr_mc_info_match(mc, group, local, ifidx)) 1218273331Sbryanv goto out; 1219273331Sbryanv } 1220273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1221273331Sbryanv 1222273331Sbryanv if (empty == 0) 1223273331Sbryanv return (ENOSPC); 1224273331Sbryanv 1225273331Sbryanv error = vxlan_socket_mc_join_group(vso, group, local, &ifidx, &source); 1226273331Sbryanv if (error) 1227273331Sbryanv return (error); 1228273331Sbryanv 1229273331Sbryanv VXLAN_SO_WLOCK(vso); 1230273331Sbryanv for (i = 0; i < VXLAN_SO_MC_MAX_GROUPS; i++) { 1231273331Sbryanv mc = &vso->vxlso_mc[i]; 1232273331Sbryanv 1233273331Sbryanv if (mc->vxlsomc_gaddr.sa.sa_family == AF_UNSPEC) { 1234273331Sbryanv vxlan_sockaddr_copy(&mc->vxlsomc_gaddr, &group->sa); 1235273331Sbryanv vxlan_sockaddr_copy(&mc->vxlsomc_saddr, &source.sa); 1236273331Sbryanv mc->vxlsomc_ifidx = ifidx; 1237273331Sbryanv goto out; 1238273331Sbryanv } 1239273331Sbryanv } 1240273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1241273331Sbryanv 1242273331Sbryanv error = vxlan_socket_mc_leave_group(vso, group, &source, ifidx); 1243273331Sbryanv MPASS(error == 0); 1244273331Sbryanv 1245273331Sbryanv return (ENOSPC); 1246273331Sbryanv 1247273331Sbryanvout: 1248273331Sbryanv mc->vxlsomc_users++; 1249273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1250273331Sbryanv 1251273331Sbryanv *idx = i; 1252273331Sbryanv 1253273331Sbryanv return (0); 1254273331Sbryanv} 1255273331Sbryanv 1256273331Sbryanvstatic void 1257273331Sbryanvvxlan_socket_mc_release_group_by_idx(struct vxlan_socket *vso, int idx) 1258273331Sbryanv{ 1259273331Sbryanv union vxlan_sockaddr group, source; 1260273331Sbryanv struct vxlan_socket_mc_info *mc; 1261273331Sbryanv int ifidx, leave; 1262273331Sbryanv 1263273331Sbryanv KASSERT(idx >= 0 && idx < VXLAN_SO_MC_MAX_GROUPS, 1264273331Sbryanv ("%s: vso %p idx %d out of bounds", __func__, vso, idx)); 1265273331Sbryanv 1266273331Sbryanv leave = 0; 1267273331Sbryanv mc = &vso->vxlso_mc[idx]; 1268273331Sbryanv 1269273331Sbryanv VXLAN_SO_WLOCK(vso); 1270273331Sbryanv mc->vxlsomc_users--; 1271273331Sbryanv if (mc->vxlsomc_users == 0) { 1272273331Sbryanv group = mc->vxlsomc_gaddr; 1273273331Sbryanv source = mc->vxlsomc_saddr; 1274273331Sbryanv ifidx = mc->vxlsomc_ifidx; 1275273331Sbryanv bzero(mc, sizeof(*mc)); 1276273331Sbryanv leave = 1; 1277273331Sbryanv } 1278273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1279273331Sbryanv 1280273331Sbryanv if (leave != 0) { 1281273331Sbryanv /* 1282273331Sbryanv * Our socket's membership in this group may have already 1283273331Sbryanv * been removed if we joined through an interface that's 1284273331Sbryanv * been detached. 1285273331Sbryanv */ 1286273331Sbryanv vxlan_socket_mc_leave_group(vso, &group, &source, ifidx); 1287273331Sbryanv } 1288273331Sbryanv} 1289273331Sbryanv 1290273331Sbryanvstatic struct vxlan_softc * 1291273331Sbryanvvxlan_socket_lookup_softc_locked(struct vxlan_socket *vso, uint32_t vni) 1292273331Sbryanv{ 1293273331Sbryanv struct vxlan_softc *sc; 1294273331Sbryanv uint32_t hash; 1295273331Sbryanv 1296273331Sbryanv VXLAN_SO_LOCK_ASSERT(vso); 1297273331Sbryanv hash = VXLAN_SO_VNI_HASH(vni); 1298273331Sbryanv 1299273331Sbryanv LIST_FOREACH(sc, &vso->vxlso_vni_hash[hash], vxl_entry) { 1300273331Sbryanv if (sc->vxl_vni == vni) { 1301273331Sbryanv VXLAN_ACQUIRE(sc); 1302273331Sbryanv break; 1303273331Sbryanv } 1304273331Sbryanv } 1305273331Sbryanv 1306273331Sbryanv return (sc); 1307273331Sbryanv} 1308273331Sbryanv 1309273331Sbryanvstatic struct vxlan_softc * 1310273331Sbryanvvxlan_socket_lookup_softc(struct vxlan_socket *vso, uint32_t vni) 1311273331Sbryanv{ 1312273331Sbryanv struct rm_priotracker tracker; 1313273331Sbryanv struct vxlan_softc *sc; 1314273331Sbryanv 1315273331Sbryanv VXLAN_SO_RLOCK(vso, &tracker); 1316273331Sbryanv sc = vxlan_socket_lookup_softc_locked(vso, vni); 1317273331Sbryanv VXLAN_SO_RUNLOCK(vso, &tracker); 1318273331Sbryanv 1319273331Sbryanv return (sc); 1320273331Sbryanv} 1321273331Sbryanv 1322273331Sbryanvstatic int 1323273331Sbryanvvxlan_socket_insert_softc(struct vxlan_socket *vso, struct vxlan_softc *sc) 1324273331Sbryanv{ 1325273331Sbryanv struct vxlan_softc *tsc; 1326273331Sbryanv uint32_t vni, hash; 1327273331Sbryanv 1328273331Sbryanv vni = sc->vxl_vni; 1329273331Sbryanv hash = VXLAN_SO_VNI_HASH(vni); 1330273331Sbryanv 1331273331Sbryanv VXLAN_SO_WLOCK(vso); 1332273331Sbryanv tsc = vxlan_socket_lookup_softc_locked(vso, vni); 1333273331Sbryanv if (tsc != NULL) { 1334273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1335273331Sbryanv vxlan_release(tsc); 1336273331Sbryanv return (EEXIST); 1337273331Sbryanv } 1338273331Sbryanv 1339273331Sbryanv VXLAN_ACQUIRE(sc); 1340273331Sbryanv LIST_INSERT_HEAD(&vso->vxlso_vni_hash[hash], sc, vxl_entry); 1341273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1342273331Sbryanv 1343273331Sbryanv return (0); 1344273331Sbryanv} 1345273331Sbryanv 1346273331Sbryanvstatic void 1347273331Sbryanvvxlan_socket_remove_softc(struct vxlan_socket *vso, struct vxlan_softc *sc) 1348273331Sbryanv{ 1349273331Sbryanv 1350273331Sbryanv VXLAN_SO_WLOCK(vso); 1351273331Sbryanv LIST_REMOVE(sc, vxl_entry); 1352273331Sbryanv VXLAN_SO_WUNLOCK(vso); 1353273331Sbryanv 1354273331Sbryanv vxlan_release(sc); 1355273331Sbryanv} 1356273331Sbryanv 1357273331Sbryanvstatic struct ifnet * 1358273331Sbryanvvxlan_multicast_if_ref(struct vxlan_softc *sc, int ipv4) 1359273331Sbryanv{ 1360273331Sbryanv struct ifnet *ifp; 1361273331Sbryanv 1362273331Sbryanv VXLAN_LOCK_ASSERT(sc); 1363273331Sbryanv 1364273331Sbryanv if (ipv4 && sc->vxl_im4o != NULL) 1365273331Sbryanv ifp = sc->vxl_im4o->imo_multicast_ifp; 1366273331Sbryanv else if (!ipv4 && sc->vxl_im6o != NULL) 1367273331Sbryanv ifp = sc->vxl_im6o->im6o_multicast_ifp; 1368273331Sbryanv else 1369273331Sbryanv ifp = NULL; 1370273331Sbryanv 1371273331Sbryanv if (ifp != NULL) 1372273331Sbryanv if_ref(ifp); 1373273331Sbryanv 1374273331Sbryanv return (ifp); 1375273331Sbryanv} 1376273331Sbryanv 1377273331Sbryanvstatic void 1378273331Sbryanvvxlan_free_multicast(struct vxlan_softc *sc) 1379273331Sbryanv{ 1380273331Sbryanv 1381273331Sbryanv if (sc->vxl_mc_ifp != NULL) { 1382273331Sbryanv if_rele(sc->vxl_mc_ifp); 1383273331Sbryanv sc->vxl_mc_ifp = NULL; 1384273331Sbryanv sc->vxl_mc_ifindex = 0; 1385273331Sbryanv } 1386273331Sbryanv 1387273331Sbryanv if (sc->vxl_im4o != NULL) { 1388273331Sbryanv free(sc->vxl_im4o, M_VXLAN); 1389273331Sbryanv sc->vxl_im4o = NULL; 1390273331Sbryanv } 1391273331Sbryanv 1392273331Sbryanv if (sc->vxl_im6o != NULL) { 1393273331Sbryanv free(sc->vxl_im6o, M_VXLAN); 1394273331Sbryanv sc->vxl_im6o = NULL; 1395273331Sbryanv } 1396273331Sbryanv} 1397273331Sbryanv 1398273331Sbryanvstatic int 1399273331Sbryanvvxlan_setup_multicast_interface(struct vxlan_softc *sc) 1400273331Sbryanv{ 1401273331Sbryanv struct ifnet *ifp; 1402273331Sbryanv 1403273331Sbryanv ifp = ifunit_ref(sc->vxl_mc_ifname); 1404273331Sbryanv if (ifp == NULL) { 1405273331Sbryanv if_printf(sc->vxl_ifp, "multicast interfaces %s does " 1406273331Sbryanv "not exist\n", sc->vxl_mc_ifname); 1407273331Sbryanv return (ENOENT); 1408273331Sbryanv } 1409273331Sbryanv 1410273331Sbryanv if ((ifp->if_flags & IFF_MULTICAST) == 0) { 1411273331Sbryanv if_printf(sc->vxl_ifp, "interface %s does not support " 1412273331Sbryanv "multicast\n", sc->vxl_mc_ifname); 1413273331Sbryanv if_rele(ifp); 1414273331Sbryanv return (ENOTSUP); 1415273331Sbryanv } 1416273331Sbryanv 1417273331Sbryanv sc->vxl_mc_ifp = ifp; 1418273331Sbryanv sc->vxl_mc_ifindex = ifp->if_index; 1419273331Sbryanv 1420273331Sbryanv return (0); 1421273331Sbryanv} 1422273331Sbryanv 1423273331Sbryanvstatic int 1424273331Sbryanvvxlan_setup_multicast(struct vxlan_softc *sc) 1425273331Sbryanv{ 1426273331Sbryanv const union vxlan_sockaddr *group; 1427273331Sbryanv int error; 1428273331Sbryanv 1429273331Sbryanv group = &sc->vxl_dst_addr; 1430273331Sbryanv error = 0; 1431273331Sbryanv 1432273331Sbryanv if (sc->vxl_mc_ifname[0] != '\0') { 1433273331Sbryanv error = vxlan_setup_multicast_interface(sc); 1434273331Sbryanv if (error) 1435273331Sbryanv return (error); 1436273331Sbryanv } 1437273331Sbryanv 1438273331Sbryanv /* 1439273331Sbryanv * Initialize an multicast options structure that is sufficiently 1440273331Sbryanv * populated for use in the respective IP output routine. This 1441273331Sbryanv * structure is typically stored in the socket, but our sockets 1442273331Sbryanv * may be shared among multiple interfaces. 1443273331Sbryanv */ 1444273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(group)) { 1445273331Sbryanv sc->vxl_im4o = malloc(sizeof(struct ip_moptions), M_VXLAN, 1446273331Sbryanv M_ZERO | M_WAITOK); 1447273331Sbryanv sc->vxl_im4o->imo_multicast_ifp = sc->vxl_mc_ifp; 1448273331Sbryanv sc->vxl_im4o->imo_multicast_ttl = sc->vxl_ttl; 1449273331Sbryanv sc->vxl_im4o->imo_multicast_vif = -1; 1450273331Sbryanv } else if (VXLAN_SOCKADDR_IS_IPV6(group)) { 1451273331Sbryanv sc->vxl_im6o = malloc(sizeof(struct ip6_moptions), M_VXLAN, 1452273331Sbryanv M_ZERO | M_WAITOK); 1453273331Sbryanv sc->vxl_im6o->im6o_multicast_ifp = sc->vxl_mc_ifp; 1454273331Sbryanv sc->vxl_im6o->im6o_multicast_hlim = sc->vxl_ttl; 1455273331Sbryanv } 1456273331Sbryanv 1457273331Sbryanv return (error); 1458273331Sbryanv} 1459273331Sbryanv 1460273331Sbryanvstatic int 1461273331Sbryanvvxlan_setup_socket(struct vxlan_softc *sc) 1462273331Sbryanv{ 1463273331Sbryanv struct vxlan_socket *vso; 1464273331Sbryanv struct ifnet *ifp; 1465273331Sbryanv union vxlan_sockaddr *saddr, *daddr; 1466273331Sbryanv int multicast, error; 1467273331Sbryanv 1468273331Sbryanv vso = NULL; 1469273331Sbryanv ifp = sc->vxl_ifp; 1470273331Sbryanv saddr = &sc->vxl_src_addr; 1471273331Sbryanv daddr = &sc->vxl_dst_addr; 1472273331Sbryanv 1473273331Sbryanv multicast = vxlan_sockaddr_in_multicast(daddr); 1474273331Sbryanv MPASS(multicast != -1); 1475273331Sbryanv sc->vxl_vso_mc_index = -1; 1476273331Sbryanv 1477273331Sbryanv /* 1478273331Sbryanv * Try to create the socket. If that fails, attempt to use an 1479273331Sbryanv * existing socket. 1480273331Sbryanv */ 1481273331Sbryanv error = vxlan_socket_create(ifp, multicast, saddr, &vso); 1482273331Sbryanv if (error) { 1483273331Sbryanv if (multicast != 0) 1484273331Sbryanv vso = vxlan_socket_mc_lookup(saddr); 1485273331Sbryanv else 1486273331Sbryanv vso = vxlan_socket_lookup(saddr); 1487273331Sbryanv 1488273331Sbryanv if (vso == NULL) { 1489273331Sbryanv if_printf(ifp, "cannot create socket (error: %d), " 1490273331Sbryanv "and no existing socket found\n", error); 1491273331Sbryanv goto out; 1492273331Sbryanv } 1493273331Sbryanv } 1494273331Sbryanv 1495273331Sbryanv if (multicast != 0) { 1496273331Sbryanv error = vxlan_setup_multicast(sc); 1497273331Sbryanv if (error) 1498273331Sbryanv goto out; 1499273331Sbryanv 1500273331Sbryanv error = vxlan_socket_mc_add_group(vso, daddr, saddr, 1501273331Sbryanv sc->vxl_mc_ifindex, &sc->vxl_vso_mc_index); 1502273331Sbryanv if (error) 1503273331Sbryanv goto out; 1504273331Sbryanv } 1505273331Sbryanv 1506273331Sbryanv sc->vxl_sock = vso; 1507273331Sbryanv error = vxlan_socket_insert_softc(vso, sc); 1508273331Sbryanv if (error) { 1509273331Sbryanv sc->vxl_sock = NULL; 1510273331Sbryanv if_printf(ifp, "network identifier %d already exists in " 1511273331Sbryanv "this socket\n", sc->vxl_vni); 1512273331Sbryanv goto out; 1513273331Sbryanv } 1514273331Sbryanv 1515273331Sbryanv return (0); 1516273331Sbryanv 1517273331Sbryanvout: 1518273331Sbryanv if (vso != NULL) { 1519273331Sbryanv if (sc->vxl_vso_mc_index != -1) { 1520273331Sbryanv vxlan_socket_mc_release_group_by_idx(vso, 1521273331Sbryanv sc->vxl_vso_mc_index); 1522273331Sbryanv sc->vxl_vso_mc_index = -1; 1523273331Sbryanv } 1524273331Sbryanv if (multicast != 0) 1525273331Sbryanv vxlan_free_multicast(sc); 1526273331Sbryanv vxlan_socket_release(vso); 1527273331Sbryanv } 1528273331Sbryanv 1529273331Sbryanv return (error); 1530273331Sbryanv} 1531273331Sbryanv 1532273331Sbryanvstatic void 1533273331Sbryanvvxlan_setup_interface(struct vxlan_softc *sc) 1534273331Sbryanv{ 1535273331Sbryanv struct ifnet *ifp; 1536273331Sbryanv 1537273331Sbryanv ifp = sc->vxl_ifp; 1538273331Sbryanv ifp->if_hdrlen = ETHER_HDR_LEN + sizeof(struct vxlanudphdr); 1539273331Sbryanv 1540273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_dst_addr) != 0) 1541273331Sbryanv ifp->if_hdrlen += sizeof(struct ip); 1542273331Sbryanv else if (VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_dst_addr) != 0) 1543273331Sbryanv ifp->if_hdrlen += sizeof(struct ip6_hdr); 1544273331Sbryanv} 1545273331Sbryanv 1546273331Sbryanvstatic int 1547273331Sbryanvvxlan_valid_init_config(struct vxlan_softc *sc) 1548273331Sbryanv{ 1549273331Sbryanv const char *reason; 1550273331Sbryanv 1551273331Sbryanv if (vxlan_check_vni(sc->vxl_vni) != 0) { 1552273331Sbryanv reason = "invalid virtual network identifier specified"; 1553273331Sbryanv goto fail; 1554273331Sbryanv } 1555273331Sbryanv 1556273331Sbryanv if (vxlan_sockaddr_supported(&sc->vxl_src_addr, 1) == 0) { 1557273331Sbryanv reason = "source address type is not supported"; 1558273331Sbryanv goto fail; 1559273331Sbryanv } 1560273331Sbryanv 1561273331Sbryanv if (vxlan_sockaddr_supported(&sc->vxl_dst_addr, 0) == 0) { 1562273331Sbryanv reason = "destination address type is not supported"; 1563273331Sbryanv goto fail; 1564273331Sbryanv } 1565273331Sbryanv 1566273331Sbryanv if (vxlan_sockaddr_in_any(&sc->vxl_dst_addr) != 0) { 1567273331Sbryanv reason = "no valid destination address specified"; 1568273331Sbryanv goto fail; 1569273331Sbryanv } 1570273331Sbryanv 1571273331Sbryanv if (vxlan_sockaddr_in_multicast(&sc->vxl_dst_addr) == 0 && 1572273331Sbryanv sc->vxl_mc_ifname[0] != '\0') { 1573273331Sbryanv reason = "can only specify interface with a group address"; 1574273331Sbryanv goto fail; 1575273331Sbryanv } 1576273331Sbryanv 1577273331Sbryanv if (vxlan_sockaddr_in_any(&sc->vxl_src_addr) == 0) { 1578273331Sbryanv if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_src_addr) ^ 1579273331Sbryanv VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_dst_addr)) { 1580273331Sbryanv reason = "source and destination address must both " 1581273331Sbryanv "be either IPv4 or IPv6"; 1582273331Sbryanv goto fail; 1583273331Sbryanv } 1584273331Sbryanv } 1585273331Sbryanv 1586273331Sbryanv if (sc->vxl_src_addr.in4.sin_port == 0) { 1587273331Sbryanv reason = "local port not specified"; 1588273331Sbryanv goto fail; 1589273331Sbryanv } 1590273331Sbryanv 1591273331Sbryanv if (sc->vxl_dst_addr.in4.sin_port == 0) { 1592273331Sbryanv reason = "remote port not specified"; 1593273331Sbryanv goto fail; 1594273331Sbryanv } 1595273331Sbryanv 1596273331Sbryanv return (0); 1597273331Sbryanv 1598273331Sbryanvfail: 1599273331Sbryanv if_printf(sc->vxl_ifp, "cannot initialize interface: %s\n", reason); 1600273331Sbryanv return (EINVAL); 1601273331Sbryanv} 1602273331Sbryanv 1603273331Sbryanvstatic void 1604273331Sbryanvvxlan_init_wait(struct vxlan_softc *sc) 1605273331Sbryanv{ 1606273331Sbryanv 1607273331Sbryanv VXLAN_LOCK_WASSERT(sc); 1608273331Sbryanv while (sc->vxl_flags & VXLAN_FLAG_INIT) 1609273331Sbryanv rm_sleep(sc, &sc->vxl_lock, 0, "vxlint", hz); 1610273331Sbryanv} 1611273331Sbryanv 1612273331Sbryanvstatic void 1613273331Sbryanvvxlan_init_complete(struct vxlan_softc *sc) 1614273331Sbryanv{ 1615273331Sbryanv 1616273331Sbryanv VXLAN_WLOCK(sc); 1617273331Sbryanv sc->vxl_flags &= ~VXLAN_FLAG_INIT; 1618273331Sbryanv wakeup(sc); 1619273331Sbryanv VXLAN_WUNLOCK(sc); 1620273331Sbryanv} 1621273331Sbryanv 1622273331Sbryanvstatic void 1623273331Sbryanvvxlan_init(void *xsc) 1624273331Sbryanv{ 1625273331Sbryanv static const uint8_t empty_mac[ETHER_ADDR_LEN]; 1626273331Sbryanv struct vxlan_softc *sc; 1627273331Sbryanv struct ifnet *ifp; 1628273331Sbryanv 1629273331Sbryanv sc = xsc; 1630273331Sbryanv ifp = sc->vxl_ifp; 1631273331Sbryanv 1632273331Sbryanv VXLAN_WLOCK(sc); 1633273331Sbryanv if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1634273331Sbryanv VXLAN_WUNLOCK(sc); 1635273331Sbryanv return; 1636273331Sbryanv } 1637273331Sbryanv sc->vxl_flags |= VXLAN_FLAG_INIT; 1638273331Sbryanv VXLAN_WUNLOCK(sc); 1639273331Sbryanv 1640273331Sbryanv if (vxlan_valid_init_config(sc) != 0) 1641273331Sbryanv goto out; 1642273331Sbryanv 1643273331Sbryanv vxlan_setup_interface(sc); 1644273331Sbryanv 1645273331Sbryanv if (vxlan_setup_socket(sc) != 0) 1646273331Sbryanv goto out; 1647273331Sbryanv 1648273331Sbryanv /* Initialize the default forwarding entry. */ 1649273331Sbryanv vxlan_ftable_entry_init(sc, &sc->vxl_default_fe, empty_mac, 1650273331Sbryanv &sc->vxl_dst_addr.sa, VXLAN_FE_FLAG_STATIC); 1651273331Sbryanv 1652273331Sbryanv VXLAN_WLOCK(sc); 1653273331Sbryanv ifp->if_drv_flags |= IFF_DRV_RUNNING; 1654273331Sbryanv callout_reset(&sc->vxl_callout, vxlan_ftable_prune_period * hz, 1655273331Sbryanv vxlan_timer, sc); 1656273331Sbryanv VXLAN_WUNLOCK(sc); 1657273331Sbryanv 1658273331Sbryanvout: 1659273331Sbryanv vxlan_init_complete(sc); 1660273331Sbryanv} 1661273331Sbryanv 1662273331Sbryanvstatic void 1663273331Sbryanvvxlan_release(struct vxlan_softc *sc) 1664273331Sbryanv{ 1665273331Sbryanv 1666273331Sbryanv /* 1667273331Sbryanv * The softc may be destroyed as soon as we release our reference, 1668273331Sbryanv * so we cannot serialize the wakeup with the softc lock. We use a 1669273331Sbryanv * timeout in our sleeps so a missed wakeup is unfortunate but not 1670273331Sbryanv * fatal. 1671273331Sbryanv */ 1672273331Sbryanv if (VXLAN_RELEASE(sc) != 0) 1673273331Sbryanv wakeup(sc); 1674273331Sbryanv} 1675273331Sbryanv 1676273331Sbryanvstatic void 1677273331Sbryanvvxlan_teardown_wait(struct vxlan_softc *sc) 1678273331Sbryanv{ 1679273331Sbryanv 1680273331Sbryanv VXLAN_LOCK_WASSERT(sc); 1681273331Sbryanv while (sc->vxl_flags & VXLAN_FLAG_TEARDOWN) 1682273331Sbryanv rm_sleep(sc, &sc->vxl_lock, 0, "vxltrn", hz); 1683273331Sbryanv} 1684273331Sbryanv 1685273331Sbryanvstatic void 1686273331Sbryanvvxlan_teardown_complete(struct vxlan_softc *sc) 1687273331Sbryanv{ 1688273331Sbryanv 1689273331Sbryanv VXLAN_WLOCK(sc); 1690273331Sbryanv sc->vxl_flags &= ~VXLAN_FLAG_TEARDOWN; 1691273331Sbryanv wakeup(sc); 1692273331Sbryanv VXLAN_WUNLOCK(sc); 1693273331Sbryanv} 1694273331Sbryanv 1695273331Sbryanvstatic void 1696273331Sbryanvvxlan_teardown_locked(struct vxlan_softc *sc) 1697273331Sbryanv{ 1698273331Sbryanv struct ifnet *ifp; 1699273331Sbryanv struct vxlan_socket *vso; 1700273331Sbryanv 1701273331Sbryanv ifp = sc->vxl_ifp; 1702273331Sbryanv 1703273331Sbryanv VXLAN_LOCK_WASSERT(sc); 1704273331Sbryanv MPASS(sc->vxl_flags & VXLAN_FLAG_TEARDOWN); 1705273331Sbryanv 1706273331Sbryanv ifp->if_flags &= ~IFF_UP; 1707273331Sbryanv ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1708273331Sbryanv callout_stop(&sc->vxl_callout); 1709273331Sbryanv vso = sc->vxl_sock; 1710273331Sbryanv sc->vxl_sock = NULL; 1711273331Sbryanv 1712273331Sbryanv VXLAN_WUNLOCK(sc); 1713273331Sbryanv 1714273331Sbryanv if (vso != NULL) { 1715273331Sbryanv vxlan_socket_remove_softc(vso, sc); 1716273331Sbryanv 1717273331Sbryanv if (sc->vxl_vso_mc_index != -1) { 1718273331Sbryanv vxlan_socket_mc_release_group_by_idx(vso, 1719273331Sbryanv sc->vxl_vso_mc_index); 1720273331Sbryanv sc->vxl_vso_mc_index = -1; 1721273331Sbryanv } 1722273331Sbryanv } 1723273331Sbryanv 1724273331Sbryanv VXLAN_WLOCK(sc); 1725273331Sbryanv while (sc->vxl_refcnt != 0) 1726273331Sbryanv rm_sleep(sc, &sc->vxl_lock, 0, "vxldrn", hz); 1727273331Sbryanv VXLAN_WUNLOCK(sc); 1728273331Sbryanv 1729273331Sbryanv callout_drain(&sc->vxl_callout); 1730273331Sbryanv 1731273331Sbryanv vxlan_free_multicast(sc); 1732273331Sbryanv if (vso != NULL) 1733273331Sbryanv vxlan_socket_release(vso); 1734273331Sbryanv 1735273331Sbryanv vxlan_teardown_complete(sc); 1736273331Sbryanv} 1737273331Sbryanv 1738273331Sbryanvstatic void 1739273331Sbryanvvxlan_teardown(struct vxlan_softc *sc) 1740273331Sbryanv{ 1741273331Sbryanv 1742273331Sbryanv VXLAN_WLOCK(sc); 1743273331Sbryanv if (sc->vxl_flags & VXLAN_FLAG_TEARDOWN) { 1744273331Sbryanv vxlan_teardown_wait(sc); 1745273331Sbryanv VXLAN_WUNLOCK(sc); 1746273331Sbryanv return; 1747273331Sbryanv } 1748273331Sbryanv 1749273331Sbryanv sc->vxl_flags |= VXLAN_FLAG_TEARDOWN; 1750273331Sbryanv vxlan_teardown_locked(sc); 1751273331Sbryanv} 1752273331Sbryanv 1753273331Sbryanvstatic void 1754273331Sbryanvvxlan_ifdetach(struct vxlan_softc *sc, struct ifnet *ifp, 1755273331Sbryanv struct vxlan_softc_head *list) 1756273331Sbryanv{ 1757273331Sbryanv 1758273331Sbryanv VXLAN_WLOCK(sc); 1759273331Sbryanv 1760273331Sbryanv if (sc->vxl_mc_ifp != ifp) 1761273331Sbryanv goto out; 1762273331Sbryanv if (sc->vxl_flags & VXLAN_FLAG_TEARDOWN) 1763273331Sbryanv goto out; 1764273331Sbryanv 1765273331Sbryanv sc->vxl_flags |= VXLAN_FLAG_TEARDOWN; 1766273331Sbryanv LIST_INSERT_HEAD(list, sc, vxl_ifdetach_list); 1767273331Sbryanv 1768273331Sbryanvout: 1769273331Sbryanv VXLAN_WUNLOCK(sc); 1770273331Sbryanv} 1771273331Sbryanv 1772273331Sbryanvstatic void 1773273331Sbryanvvxlan_timer(void *xsc) 1774273331Sbryanv{ 1775273331Sbryanv struct vxlan_softc *sc; 1776273331Sbryanv 1777273331Sbryanv sc = xsc; 1778273331Sbryanv VXLAN_LOCK_WASSERT(sc); 1779273331Sbryanv 1780273331Sbryanv vxlan_ftable_expire(sc); 1781273331Sbryanv callout_schedule(&sc->vxl_callout, vxlan_ftable_prune_period * hz); 1782273331Sbryanv} 1783273331Sbryanv 1784273331Sbryanvstatic int 1785273331Sbryanvvxlan_ioctl_ifflags(struct vxlan_softc *sc) 1786273331Sbryanv{ 1787273331Sbryanv struct ifnet *ifp; 1788273331Sbryanv 1789273331Sbryanv ifp = sc->vxl_ifp; 1790273331Sbryanv 1791273331Sbryanv if (ifp->if_flags & IFF_UP) { 1792273331Sbryanv if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 1793273331Sbryanv vxlan_init(sc); 1794273331Sbryanv } else { 1795273331Sbryanv if (ifp->if_drv_flags & IFF_DRV_RUNNING) 1796273331Sbryanv vxlan_teardown(sc); 1797273331Sbryanv } 1798273331Sbryanv 1799273331Sbryanv return (0); 1800273331Sbryanv} 1801273331Sbryanv 1802273331Sbryanvstatic int 1803273331Sbryanvvxlan_ctrl_get_config(struct vxlan_softc *sc, void *arg) 1804273331Sbryanv{ 1805273331Sbryanv struct rm_priotracker tracker; 1806273331Sbryanv struct ifvxlancfg *cfg; 1807273331Sbryanv 1808273331Sbryanv cfg = arg; 1809273331Sbryanv bzero(cfg, sizeof(*cfg)); 1810273331Sbryanv 1811273331Sbryanv VXLAN_RLOCK(sc, &tracker); 1812273331Sbryanv cfg->vxlc_vni = sc->vxl_vni; 1813273331Sbryanv memcpy(&cfg->vxlc_local_sa, &sc->vxl_src_addr, 1814273331Sbryanv sizeof(union vxlan_sockaddr)); 1815273331Sbryanv memcpy(&cfg->vxlc_remote_sa, &sc->vxl_dst_addr, 1816273331Sbryanv sizeof(union vxlan_sockaddr)); 1817273331Sbryanv cfg->vxlc_mc_ifindex = sc->vxl_mc_ifindex; 1818273331Sbryanv cfg->vxlc_ftable_cnt = sc->vxl_ftable_cnt; 1819273331Sbryanv cfg->vxlc_ftable_max = sc->vxl_ftable_max; 1820273331Sbryanv cfg->vxlc_ftable_timeout = sc->vxl_ftable_timeout; 1821273331Sbryanv cfg->vxlc_port_min = sc->vxl_min_port; 1822273331Sbryanv cfg->vxlc_port_max = sc->vxl_max_port; 1823273331Sbryanv cfg->vxlc_learn = (sc->vxl_flags & VXLAN_FLAG_LEARN) != 0; 1824273331Sbryanv cfg->vxlc_ttl = sc->vxl_ttl; 1825273331Sbryanv VXLAN_RUNLOCK(sc, &tracker); 1826273331Sbryanv 1827273331Sbryanv return (0); 1828273331Sbryanv} 1829273331Sbryanv 1830273331Sbryanvstatic int 1831273331Sbryanvvxlan_ctrl_set_vni(struct vxlan_softc *sc, void *arg) 1832273331Sbryanv{ 1833273331Sbryanv struct ifvxlancmd *cmd; 1834273331Sbryanv int error; 1835273331Sbryanv 1836273331Sbryanv cmd = arg; 1837273331Sbryanv 1838273331Sbryanv if (vxlan_check_vni(cmd->vxlcmd_vni) != 0) 1839273331Sbryanv return (EINVAL); 1840273331Sbryanv 1841273331Sbryanv VXLAN_WLOCK(sc); 1842273331Sbryanv if (vxlan_can_change_config(sc)) { 1843273331Sbryanv sc->vxl_vni = cmd->vxlcmd_vni; 1844273331Sbryanv error = 0; 1845273331Sbryanv } else 1846273331Sbryanv error = EBUSY; 1847273331Sbryanv VXLAN_WUNLOCK(sc); 1848273331Sbryanv 1849273331Sbryanv return (error); 1850273331Sbryanv} 1851273331Sbryanv 1852273331Sbryanvstatic int 1853273331Sbryanvvxlan_ctrl_set_local_addr(struct vxlan_softc *sc, void *arg) 1854273331Sbryanv{ 1855273331Sbryanv struct ifvxlancmd *cmd; 1856273331Sbryanv union vxlan_sockaddr *vxlsa; 1857273331Sbryanv int error; 1858273331Sbryanv 1859273331Sbryanv cmd = arg; 1860273331Sbryanv vxlsa = &cmd->vxlcmd_sa; 1861273331Sbryanv 1862273331Sbryanv if (!VXLAN_SOCKADDR_IS_IPV46(vxlsa)) 1863273331Sbryanv return (EINVAL); 1864273331Sbryanv if (vxlan_sockaddr_in_multicast(vxlsa) != 0) 1865273331Sbryanv return (EINVAL); 1866273331Sbryanv 1867273331Sbryanv VXLAN_WLOCK(sc); 1868273331Sbryanv if (vxlan_can_change_config(sc)) { 1869273331Sbryanv vxlan_sockaddr_in_copy(&sc->vxl_src_addr, &vxlsa->sa); 1870273331Sbryanv error = 0; 1871273331Sbryanv } else 1872273331Sbryanv error = EBUSY; 1873273331Sbryanv VXLAN_WUNLOCK(sc); 1874273331Sbryanv 1875273331Sbryanv return (error); 1876273331Sbryanv} 1877273331Sbryanv 1878273331Sbryanvstatic int 1879273331Sbryanvvxlan_ctrl_set_remote_addr(struct vxlan_softc *sc, void *arg) 1880273331Sbryanv{ 1881273331Sbryanv struct ifvxlancmd *cmd; 1882273331Sbryanv union vxlan_sockaddr *vxlsa; 1883273331Sbryanv int error; 1884273331Sbryanv 1885273331Sbryanv cmd = arg; 1886273331Sbryanv vxlsa = &cmd->vxlcmd_sa; 1887273331Sbryanv 1888273331Sbryanv if (!VXLAN_SOCKADDR_IS_IPV46(vxlsa)) 1889273331Sbryanv return (EINVAL); 1890273331Sbryanv 1891273331Sbryanv VXLAN_WLOCK(sc); 1892273331Sbryanv if (vxlan_can_change_config(sc)) { 1893273331Sbryanv vxlan_sockaddr_in_copy(&sc->vxl_dst_addr, &vxlsa->sa); 1894273331Sbryanv error = 0; 1895273331Sbryanv } else 1896273331Sbryanv error = EBUSY; 1897273331Sbryanv VXLAN_WUNLOCK(sc); 1898273331Sbryanv 1899273331Sbryanv return (error); 1900273331Sbryanv} 1901273331Sbryanv 1902273331Sbryanvstatic int 1903273331Sbryanvvxlan_ctrl_set_local_port(struct vxlan_softc *sc, void *arg) 1904273331Sbryanv{ 1905273331Sbryanv struct ifvxlancmd *cmd; 1906273331Sbryanv int error; 1907273331Sbryanv 1908273331Sbryanv cmd = arg; 1909273331Sbryanv 1910273331Sbryanv if (cmd->vxlcmd_port == 0) 1911273331Sbryanv return (EINVAL); 1912273331Sbryanv 1913273331Sbryanv VXLAN_WLOCK(sc); 1914273331Sbryanv if (vxlan_can_change_config(sc)) { 1915273331Sbryanv sc->vxl_src_addr.in4.sin_port = htons(cmd->vxlcmd_port); 1916273331Sbryanv error = 0; 1917273331Sbryanv } else 1918273331Sbryanv error = EBUSY; 1919273331Sbryanv VXLAN_WUNLOCK(sc); 1920273331Sbryanv 1921273331Sbryanv return (error); 1922273331Sbryanv} 1923273331Sbryanv 1924273331Sbryanvstatic int 1925273331Sbryanvvxlan_ctrl_set_remote_port(struct vxlan_softc *sc, void *arg) 1926273331Sbryanv{ 1927273331Sbryanv struct ifvxlancmd *cmd; 1928273331Sbryanv int error; 1929273331Sbryanv 1930273331Sbryanv cmd = arg; 1931273331Sbryanv 1932273331Sbryanv if (cmd->vxlcmd_port == 0) 1933273331Sbryanv return (EINVAL); 1934273331Sbryanv 1935273331Sbryanv VXLAN_WLOCK(sc); 1936273331Sbryanv if (vxlan_can_change_config(sc)) { 1937273331Sbryanv sc->vxl_dst_addr.in4.sin_port = htons(cmd->vxlcmd_port); 1938273331Sbryanv error = 0; 1939273331Sbryanv } else 1940273331Sbryanv error = EBUSY; 1941273331Sbryanv VXLAN_WUNLOCK(sc); 1942273331Sbryanv 1943273331Sbryanv return (error); 1944273331Sbryanv} 1945273331Sbryanv 1946273331Sbryanvstatic int 1947273331Sbryanvvxlan_ctrl_set_port_range(struct vxlan_softc *sc, void *arg) 1948273331Sbryanv{ 1949273331Sbryanv struct ifvxlancmd *cmd; 1950273331Sbryanv uint16_t min, max; 1951273331Sbryanv int error; 1952273331Sbryanv 1953273331Sbryanv cmd = arg; 1954273331Sbryanv min = cmd->vxlcmd_port_min; 1955273331Sbryanv max = cmd->vxlcmd_port_max; 1956273331Sbryanv 1957273331Sbryanv if (max < min) 1958273331Sbryanv return (EINVAL); 1959273331Sbryanv 1960273331Sbryanv VXLAN_WLOCK(sc); 1961273331Sbryanv if (vxlan_can_change_config(sc)) { 1962273331Sbryanv sc->vxl_min_port = min; 1963273331Sbryanv sc->vxl_max_port = max; 1964273331Sbryanv error = 0; 1965273331Sbryanv } else 1966273331Sbryanv error = EBUSY; 1967273331Sbryanv VXLAN_WUNLOCK(sc); 1968273331Sbryanv 1969273331Sbryanv return (error); 1970273331Sbryanv} 1971273331Sbryanv 1972273331Sbryanvstatic int 1973273331Sbryanvvxlan_ctrl_set_ftable_timeout(struct vxlan_softc *sc, void *arg) 1974273331Sbryanv{ 1975273331Sbryanv struct ifvxlancmd *cmd; 1976273331Sbryanv int error; 1977273331Sbryanv 1978273331Sbryanv cmd = arg; 1979273331Sbryanv 1980273331Sbryanv VXLAN_WLOCK(sc); 1981273331Sbryanv if (vxlan_check_ftable_timeout(cmd->vxlcmd_ftable_timeout) == 0) { 1982273331Sbryanv sc->vxl_ftable_timeout = cmd->vxlcmd_ftable_timeout; 1983273331Sbryanv error = 0; 1984273331Sbryanv } else 1985273331Sbryanv error = EINVAL; 1986273331Sbryanv VXLAN_WUNLOCK(sc); 1987273331Sbryanv 1988273331Sbryanv return (error); 1989273331Sbryanv} 1990273331Sbryanv 1991273331Sbryanvstatic int 1992273331Sbryanvvxlan_ctrl_set_ftable_max(struct vxlan_softc *sc, void *arg) 1993273331Sbryanv{ 1994273331Sbryanv struct ifvxlancmd *cmd; 1995273331Sbryanv int error; 1996273331Sbryanv 1997273331Sbryanv cmd = arg; 1998273331Sbryanv 1999273331Sbryanv VXLAN_WLOCK(sc); 2000273331Sbryanv if (vxlan_check_ftable_max(cmd->vxlcmd_ftable_max) == 0) { 2001273331Sbryanv sc->vxl_ftable_max = cmd->vxlcmd_ftable_max; 2002273331Sbryanv error = 0; 2003273331Sbryanv } else 2004273331Sbryanv error = EINVAL; 2005273331Sbryanv VXLAN_WUNLOCK(sc); 2006273331Sbryanv 2007273331Sbryanv return (error); 2008273331Sbryanv} 2009273331Sbryanv 2010273331Sbryanvstatic int 2011273331Sbryanvvxlan_ctrl_set_multicast_if(struct vxlan_softc * sc, void *arg) 2012273331Sbryanv{ 2013273331Sbryanv struct ifvxlancmd *cmd; 2014273331Sbryanv int error; 2015273331Sbryanv 2016273331Sbryanv cmd = arg; 2017273331Sbryanv 2018273331Sbryanv VXLAN_WLOCK(sc); 2019273331Sbryanv if (vxlan_can_change_config(sc)) { 2020273331Sbryanv strlcpy(sc->vxl_mc_ifname, cmd->vxlcmd_ifname, IFNAMSIZ); 2021273331Sbryanv error = 0; 2022273331Sbryanv } else 2023273331Sbryanv error = EBUSY; 2024273331Sbryanv VXLAN_WUNLOCK(sc); 2025273331Sbryanv 2026273331Sbryanv return (error); 2027273331Sbryanv} 2028273331Sbryanv 2029273331Sbryanvstatic int 2030273331Sbryanvvxlan_ctrl_set_ttl(struct vxlan_softc *sc, void *arg) 2031273331Sbryanv{ 2032273331Sbryanv struct ifvxlancmd *cmd; 2033273331Sbryanv int error; 2034273331Sbryanv 2035273331Sbryanv cmd = arg; 2036273331Sbryanv 2037273331Sbryanv VXLAN_WLOCK(sc); 2038273331Sbryanv if (vxlan_check_ttl(cmd->vxlcmd_ttl) == 0) { 2039273331Sbryanv sc->vxl_ttl = cmd->vxlcmd_ttl; 2040273331Sbryanv if (sc->vxl_im4o != NULL) 2041273331Sbryanv sc->vxl_im4o->imo_multicast_ttl = sc->vxl_ttl; 2042273331Sbryanv if (sc->vxl_im6o != NULL) 2043273331Sbryanv sc->vxl_im6o->im6o_multicast_hlim = sc->vxl_ttl; 2044273331Sbryanv error = 0; 2045273331Sbryanv } else 2046273331Sbryanv error = EINVAL; 2047273331Sbryanv VXLAN_WUNLOCK(sc); 2048273331Sbryanv 2049273331Sbryanv return (error); 2050273331Sbryanv} 2051273331Sbryanv 2052273331Sbryanvstatic int 2053273331Sbryanvvxlan_ctrl_set_learn(struct vxlan_softc *sc, void *arg) 2054273331Sbryanv{ 2055273331Sbryanv struct ifvxlancmd *cmd; 2056273331Sbryanv 2057273331Sbryanv cmd = arg; 2058273331Sbryanv 2059273331Sbryanv VXLAN_WLOCK(sc); 2060273331Sbryanv if (cmd->vxlcmd_flags & VXLAN_CMD_FLAG_LEARN) 2061273331Sbryanv sc->vxl_flags |= VXLAN_FLAG_LEARN; 2062273331Sbryanv else 2063273331Sbryanv sc->vxl_flags &= ~VXLAN_FLAG_LEARN; 2064273331Sbryanv VXLAN_WUNLOCK(sc); 2065273331Sbryanv 2066273331Sbryanv return (0); 2067273331Sbryanv} 2068273331Sbryanv 2069273331Sbryanvstatic int 2070273331Sbryanvvxlan_ctrl_ftable_entry_add(struct vxlan_softc *sc, void *arg) 2071273331Sbryanv{ 2072273331Sbryanv union vxlan_sockaddr vxlsa; 2073273331Sbryanv struct ifvxlancmd *cmd; 2074273331Sbryanv struct vxlan_ftable_entry *fe; 2075273331Sbryanv int error; 2076273331Sbryanv 2077273331Sbryanv cmd = arg; 2078273331Sbryanv vxlsa = cmd->vxlcmd_sa; 2079273331Sbryanv 2080273331Sbryanv if (!VXLAN_SOCKADDR_IS_IPV46(&vxlsa)) 2081273331Sbryanv return (EINVAL); 2082273331Sbryanv if (vxlan_sockaddr_in_any(&vxlsa) != 0) 2083273331Sbryanv return (EINVAL); 2084273331Sbryanv if (vxlan_sockaddr_in_multicast(&vxlsa) != 0) 2085273331Sbryanv return (EINVAL); 2086273331Sbryanv /* BMV: We could support both IPv4 and IPv6 later. */ 2087273331Sbryanv if (vxlsa.sa.sa_family != sc->vxl_dst_addr.sa.sa_family) 2088273331Sbryanv return (EAFNOSUPPORT); 2089273331Sbryanv 2090273331Sbryanv fe = vxlan_ftable_entry_alloc(); 2091273331Sbryanv if (fe == NULL) 2092273331Sbryanv return (ENOMEM); 2093273331Sbryanv 2094273331Sbryanv if (vxlsa.in4.sin_port == 0) 2095273331Sbryanv vxlsa.in4.sin_port = sc->vxl_dst_addr.in4.sin_port; 2096273331Sbryanv 2097273331Sbryanv vxlan_ftable_entry_init(sc, fe, cmd->vxlcmd_mac, &vxlsa.sa, 2098273331Sbryanv VXLAN_FE_FLAG_STATIC); 2099273331Sbryanv 2100273331Sbryanv VXLAN_WLOCK(sc); 2101273331Sbryanv error = vxlan_ftable_entry_insert(sc, fe); 2102273331Sbryanv VXLAN_WUNLOCK(sc); 2103273331Sbryanv 2104273331Sbryanv if (error) 2105273331Sbryanv vxlan_ftable_entry_free(fe); 2106273331Sbryanv 2107273331Sbryanv return (error); 2108273331Sbryanv} 2109273331Sbryanv 2110273331Sbryanvstatic int 2111273331Sbryanvvxlan_ctrl_ftable_entry_rem(struct vxlan_softc *sc, void *arg) 2112273331Sbryanv{ 2113273331Sbryanv struct ifvxlancmd *cmd; 2114273331Sbryanv struct vxlan_ftable_entry *fe; 2115273331Sbryanv int error; 2116273331Sbryanv 2117273331Sbryanv cmd = arg; 2118273331Sbryanv 2119273331Sbryanv VXLAN_WLOCK(sc); 2120273331Sbryanv fe = vxlan_ftable_entry_lookup(sc, cmd->vxlcmd_mac); 2121273331Sbryanv if (fe != NULL) { 2122273331Sbryanv vxlan_ftable_entry_destroy(sc, fe); 2123273331Sbryanv error = 0; 2124273331Sbryanv } else 2125273331Sbryanv error = ENOENT; 2126273331Sbryanv VXLAN_WUNLOCK(sc); 2127273331Sbryanv 2128273331Sbryanv return (error); 2129273331Sbryanv} 2130273331Sbryanv 2131273331Sbryanvstatic int 2132273331Sbryanvvxlan_ctrl_flush(struct vxlan_softc *sc, void *arg) 2133273331Sbryanv{ 2134273331Sbryanv struct ifvxlancmd *cmd; 2135273331Sbryanv int all; 2136273331Sbryanv 2137273331Sbryanv cmd = arg; 2138273331Sbryanv all = cmd->vxlcmd_flags & VXLAN_CMD_FLAG_FLUSH_ALL; 2139273331Sbryanv 2140273331Sbryanv VXLAN_WLOCK(sc); 2141273331Sbryanv vxlan_ftable_flush(sc, all); 2142273331Sbryanv VXLAN_WUNLOCK(sc); 2143273331Sbryanv 2144273331Sbryanv return (0); 2145273331Sbryanv} 2146273331Sbryanv 2147273331Sbryanvstatic int 2148273331Sbryanvvxlan_ioctl_drvspec(struct vxlan_softc *sc, struct ifdrv *ifd, int get) 2149273331Sbryanv{ 2150273331Sbryanv const struct vxlan_control *vc; 2151273331Sbryanv union { 2152273331Sbryanv struct ifvxlancfg cfg; 2153273331Sbryanv struct ifvxlancmd cmd; 2154273331Sbryanv } args; 2155273331Sbryanv int out, error; 2156273331Sbryanv 2157273331Sbryanv if (ifd->ifd_cmd >= vxlan_control_table_size) 2158273331Sbryanv return (EINVAL); 2159273331Sbryanv 2160273331Sbryanv bzero(&args, sizeof(args)); 2161273331Sbryanv vc = &vxlan_control_table[ifd->ifd_cmd]; 2162273331Sbryanv out = (vc->vxlc_flags & VXLAN_CTRL_FLAG_COPYOUT) != 0; 2163273331Sbryanv 2164273331Sbryanv if ((get != 0 && out == 0) || (get == 0 && out != 0)) 2165273331Sbryanv return (EINVAL); 2166273331Sbryanv 2167273331Sbryanv if (vc->vxlc_flags & VXLAN_CTRL_FLAG_SUSER) { 2168273331Sbryanv error = priv_check(curthread, PRIV_NET_VXLAN); 2169273331Sbryanv if (error) 2170273331Sbryanv return (error); 2171273331Sbryanv } 2172273331Sbryanv 2173273331Sbryanv if (ifd->ifd_len != vc->vxlc_argsize || 2174273331Sbryanv ifd->ifd_len > sizeof(args)) 2175273331Sbryanv return (EINVAL); 2176273331Sbryanv 2177273331Sbryanv if (vc->vxlc_flags & VXLAN_CTRL_FLAG_COPYIN) { 2178273331Sbryanv error = copyin(ifd->ifd_data, &args, ifd->ifd_len); 2179273331Sbryanv if (error) 2180273331Sbryanv return (error); 2181273331Sbryanv } 2182273331Sbryanv 2183273331Sbryanv error = vc->vxlc_func(sc, &args); 2184273331Sbryanv if (error) 2185273331Sbryanv return (error); 2186273331Sbryanv 2187273331Sbryanv if (vc->vxlc_flags & VXLAN_CTRL_FLAG_COPYOUT) { 2188273331Sbryanv error = copyout(&args, ifd->ifd_data, ifd->ifd_len); 2189273331Sbryanv if (error) 2190273331Sbryanv return (error); 2191273331Sbryanv } 2192273331Sbryanv 2193273331Sbryanv return (0); 2194273331Sbryanv} 2195273331Sbryanv 2196273331Sbryanvstatic int 2197273331Sbryanvvxlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2198273331Sbryanv{ 2199273331Sbryanv struct vxlan_softc *sc; 2200273331Sbryanv struct ifreq *ifr; 2201273331Sbryanv struct ifdrv *ifd; 2202273331Sbryanv int error; 2203273331Sbryanv 2204273331Sbryanv sc = ifp->if_softc; 2205273331Sbryanv ifr = (struct ifreq *) data; 2206273331Sbryanv ifd = (struct ifdrv *) data; 2207273331Sbryanv 2208273331Sbryanv switch (cmd) { 2209273331Sbryanv case SIOCADDMULTI: 2210273331Sbryanv case SIOCDELMULTI: 2211273331Sbryanv error = 0; 2212273331Sbryanv break; 2213273331Sbryanv 2214273331Sbryanv case SIOCGDRVSPEC: 2215273331Sbryanv case SIOCSDRVSPEC: 2216273331Sbryanv error = vxlan_ioctl_drvspec(sc, ifd, cmd == SIOCGDRVSPEC); 2217273331Sbryanv break; 2218273331Sbryanv 2219273331Sbryanv case SIOCSIFFLAGS: 2220273331Sbryanv error = vxlan_ioctl_ifflags(sc); 2221273331Sbryanv break; 2222273331Sbryanv default: 2223273331Sbryanv error = ether_ioctl(ifp, cmd, data); 2224273331Sbryanv break; 2225273331Sbryanv } 2226273331Sbryanv 2227273331Sbryanv return (error); 2228273331Sbryanv} 2229273331Sbryanv 2230273331Sbryanv#if defined(INET) || defined(INET6) 2231273331Sbryanvstatic uint16_t 2232273331Sbryanvvxlan_pick_source_port(struct vxlan_softc *sc, struct mbuf *m) 2233273331Sbryanv{ 2234273331Sbryanv int range; 2235273331Sbryanv uint32_t hash; 2236273331Sbryanv 2237273331Sbryanv range = sc->vxl_max_port - sc->vxl_min_port + 1; 2238273331Sbryanv 2239273331Sbryanv if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE && 2240273331Sbryanv M_HASHTYPE_GET(m) != M_HASHTYPE_OPAQUE) 2241273331Sbryanv hash = m->m_pkthdr.flowid; 2242273331Sbryanv else 2243273331Sbryanv hash = jenkins_hash(m->m_data, ETHER_HDR_LEN, 2244273331Sbryanv sc->vxl_port_hash_key); 2245273331Sbryanv 2246273331Sbryanv return (sc->vxl_min_port + (hash % range)); 2247273331Sbryanv} 2248273331Sbryanv 2249273331Sbryanvstatic void 2250273331Sbryanvvxlan_encap_header(struct vxlan_softc *sc, struct mbuf *m, int ipoff, 2251273331Sbryanv uint16_t srcport, uint16_t dstport) 2252273331Sbryanv{ 2253273331Sbryanv struct vxlanudphdr *hdr; 2254273331Sbryanv struct udphdr *udph; 2255273331Sbryanv struct vxlan_header *vxh; 2256273331Sbryanv int len; 2257273331Sbryanv 2258273331Sbryanv len = m->m_pkthdr.len - ipoff; 2259273331Sbryanv MPASS(len >= sizeof(struct vxlanudphdr)); 2260273331Sbryanv hdr = mtodo(m, ipoff); 2261273331Sbryanv 2262273331Sbryanv udph = &hdr->vxlh_udp; 2263273331Sbryanv udph->uh_sport = srcport; 2264273331Sbryanv udph->uh_dport = dstport; 2265273331Sbryanv udph->uh_ulen = htons(len); 2266273331Sbryanv udph->uh_sum = 0; 2267273331Sbryanv 2268273331Sbryanv vxh = &hdr->vxlh_hdr; 2269273331Sbryanv vxh->vxlh_flags = htonl(VXLAN_HDR_FLAGS_VALID_VNI); 2270273331Sbryanv vxh->vxlh_vni = htonl(sc->vxl_vni << VXLAN_HDR_VNI_SHIFT); 2271273331Sbryanv} 2272273331Sbryanv#endif 2273273331Sbryanv 2274273331Sbryanvstatic int 2275273331Sbryanvvxlan_encap4(struct vxlan_softc *sc, const union vxlan_sockaddr *fvxlsa, 2276273331Sbryanv struct mbuf *m) 2277273331Sbryanv{ 2278273331Sbryanv#ifdef INET 2279273331Sbryanv struct ifnet *ifp; 2280273331Sbryanv struct ip *ip; 2281273331Sbryanv struct in_addr srcaddr, dstaddr; 2282273331Sbryanv uint16_t srcport, dstport; 2283273331Sbryanv int len, mcast, error; 2284273331Sbryanv 2285273331Sbryanv ifp = sc->vxl_ifp; 2286273331Sbryanv srcaddr = sc->vxl_src_addr.in4.sin_addr; 2287273331Sbryanv srcport = vxlan_pick_source_port(sc, m); 2288273331Sbryanv dstaddr = fvxlsa->in4.sin_addr; 2289273331Sbryanv dstport = fvxlsa->in4.sin_port; 2290273331Sbryanv 2291273331Sbryanv M_PREPEND(m, sizeof(struct ip) + sizeof(struct vxlanudphdr), 2292273331Sbryanv M_NOWAIT); 2293273331Sbryanv if (m == NULL) { 2294273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2295273331Sbryanv return (ENOBUFS); 2296273331Sbryanv } 2297273331Sbryanv 2298273331Sbryanv len = m->m_pkthdr.len; 2299273331Sbryanv 2300273331Sbryanv ip = mtod(m, struct ip *); 2301273331Sbryanv ip->ip_tos = 0; 2302273331Sbryanv ip->ip_len = htons(len); 2303273331Sbryanv ip->ip_off = 0; 2304273331Sbryanv ip->ip_ttl = sc->vxl_ttl; 2305273331Sbryanv ip->ip_p = IPPROTO_UDP; 2306273331Sbryanv ip->ip_sum = 0; 2307273331Sbryanv ip->ip_src = srcaddr; 2308273331Sbryanv ip->ip_dst = dstaddr; 2309273331Sbryanv 2310273331Sbryanv vxlan_encap_header(sc, m, sizeof(struct ip), srcport, dstport); 2311273331Sbryanv 2312273331Sbryanv mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; 2313273331Sbryanv m->m_flags &= ~(M_MCAST | M_BCAST); 2314273331Sbryanv 2315273331Sbryanv error = ip_output(m, NULL, NULL, 0, sc->vxl_im4o, NULL); 2316273331Sbryanv if (error == 0) { 2317273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 2318273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OBYTES, len); 2319273331Sbryanv if (mcast != 0) 2320273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); 2321273331Sbryanv } else 2322273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2323273331Sbryanv 2324273331Sbryanv return (error); 2325273331Sbryanv#else 2326273331Sbryanv m_freem(m); 2327273331Sbryanv return (ENOTSUP); 2328273331Sbryanv#endif 2329273331Sbryanv} 2330273331Sbryanv 2331273331Sbryanvstatic int 2332273331Sbryanvvxlan_encap6(struct vxlan_softc *sc, const union vxlan_sockaddr *fvxlsa, 2333273331Sbryanv struct mbuf *m) 2334273331Sbryanv{ 2335273331Sbryanv#ifdef INET6 2336273331Sbryanv struct ifnet *ifp; 2337273331Sbryanv struct ip6_hdr *ip6; 2338273331Sbryanv const struct in6_addr *srcaddr, *dstaddr; 2339273331Sbryanv uint16_t srcport, dstport; 2340273331Sbryanv int len, mcast, error; 2341273331Sbryanv 2342273331Sbryanv ifp = sc->vxl_ifp; 2343273331Sbryanv srcaddr = &sc->vxl_src_addr.in6.sin6_addr; 2344273331Sbryanv srcport = vxlan_pick_source_port(sc, m); 2345273331Sbryanv dstaddr = &fvxlsa->in6.sin6_addr; 2346273331Sbryanv dstport = fvxlsa->in6.sin6_port; 2347273331Sbryanv 2348273331Sbryanv M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct vxlanudphdr), 2349273331Sbryanv M_NOWAIT); 2350273331Sbryanv if (m == NULL) { 2351273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2352273331Sbryanv return (ENOBUFS); 2353273331Sbryanv } 2354273331Sbryanv 2355273331Sbryanv len = m->m_pkthdr.len; 2356273331Sbryanv 2357273331Sbryanv ip6 = mtod(m, struct ip6_hdr *); 2358273331Sbryanv ip6->ip6_flow = 0; /* BMV: Keep in forwarding entry? */ 2359273331Sbryanv ip6->ip6_vfc = IPV6_VERSION; 2360273331Sbryanv ip6->ip6_plen = 0; 2361273331Sbryanv ip6->ip6_nxt = IPPROTO_UDP; 2362273331Sbryanv ip6->ip6_hlim = sc->vxl_ttl; 2363273331Sbryanv ip6->ip6_src = *srcaddr; 2364273331Sbryanv ip6->ip6_dst = *dstaddr; 2365273331Sbryanv 2366273331Sbryanv vxlan_encap_header(sc, m, sizeof(struct ip6_hdr), srcport, dstport); 2367273331Sbryanv 2368273331Sbryanv /* 2369273331Sbryanv * XXX BMV We need support for RFC6935 before we can send and 2370273331Sbryanv * receive IPv6 UDP packets with a zero checksum. 2371273331Sbryanv */ 2372273331Sbryanv { 2373273331Sbryanv struct udphdr *hdr = mtodo(m, sizeof(struct ip6_hdr)); 2374273331Sbryanv hdr->uh_sum = in6_cksum_pseudo(ip6, 2375273331Sbryanv m->m_pkthdr.len - sizeof(struct ip6_hdr), IPPROTO_UDP, 0); 2376273331Sbryanv m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; 2377273331Sbryanv m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 2378273331Sbryanv } 2379273331Sbryanv 2380273331Sbryanv mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; 2381273331Sbryanv m->m_flags &= ~(M_MCAST | M_BCAST); 2382273331Sbryanv 2383273331Sbryanv error = ip6_output(m, NULL, NULL, 0, sc->vxl_im6o, NULL, NULL); 2384273331Sbryanv if (error == 0) { 2385273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 2386273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OBYTES, len); 2387273331Sbryanv if (mcast != 0) 2388273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); 2389273331Sbryanv } else 2390273331Sbryanv if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2391273331Sbryanv 2392273331Sbryanv return (error); 2393273331Sbryanv#else 2394273331Sbryanv m_freem(m); 2395273331Sbryanv return (ENOTSUP); 2396273331Sbryanv#endif 2397273331Sbryanv} 2398273331Sbryanv 2399273331Sbryanvstatic int 2400273331Sbryanvvxlan_transmit(struct ifnet *ifp, struct mbuf *m) 2401273331Sbryanv{ 2402273331Sbryanv struct rm_priotracker tracker; 2403273331Sbryanv union vxlan_sockaddr vxlsa; 2404273331Sbryanv struct vxlan_softc *sc; 2405273331Sbryanv struct vxlan_ftable_entry *fe; 2406273331Sbryanv struct ifnet *mcifp; 2407273331Sbryanv struct ether_header *eh; 2408273331Sbryanv int ipv4, error; 2409273331Sbryanv 2410273331Sbryanv sc = ifp->if_softc; 2411273331Sbryanv eh = mtod(m, struct ether_header *); 2412273331Sbryanv fe = NULL; 2413273331Sbryanv mcifp = NULL; 2414273331Sbryanv 2415273331Sbryanv ETHER_BPF_MTAP(ifp, m); 2416273331Sbryanv 2417273331Sbryanv VXLAN_RLOCK(sc, &tracker); 2418273331Sbryanv if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 2419273331Sbryanv VXLAN_RUNLOCK(sc, &tracker); 2420273331Sbryanv m_freem(m); 2421273331Sbryanv return (ENETDOWN); 2422273331Sbryanv } 2423273331Sbryanv 2424273331Sbryanv if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) 2425273331Sbryanv fe = vxlan_ftable_entry_lookup(sc, eh->ether_dhost); 2426273331Sbryanv if (fe == NULL) 2427273331Sbryanv fe = &sc->vxl_default_fe; 2428273331Sbryanv vxlan_sockaddr_copy(&vxlsa, &fe->vxlfe_raddr.sa); 2429273331Sbryanv 2430273331Sbryanv ipv4 = VXLAN_SOCKADDR_IS_IPV4(&vxlsa) != 0; 2431273331Sbryanv if (vxlan_sockaddr_in_multicast(&vxlsa) != 0) 2432273331Sbryanv mcifp = vxlan_multicast_if_ref(sc, ipv4); 2433273331Sbryanv 2434273331Sbryanv VXLAN_ACQUIRE(sc); 2435273331Sbryanv VXLAN_RUNLOCK(sc, &tracker); 2436273331Sbryanv 2437273331Sbryanv if (ipv4 != 0) 2438273331Sbryanv error = vxlan_encap4(sc, &vxlsa, m); 2439273331Sbryanv else 2440273331Sbryanv error = vxlan_encap6(sc, &vxlsa, m); 2441273331Sbryanv 2442273331Sbryanv vxlan_release(sc); 2443273331Sbryanv if (mcifp != NULL) 2444273331Sbryanv if_rele(mcifp); 2445273331Sbryanv 2446273331Sbryanv return (error); 2447273331Sbryanv} 2448273331Sbryanv 2449273331Sbryanvstatic void 2450273331Sbryanvvxlan_qflush(struct ifnet *ifp __unused) 2451273331Sbryanv{ 2452273331Sbryanv} 2453273331Sbryanv 2454273331Sbryanvstatic void 2455273331Sbryanvvxlan_rcv_udp_packet(struct mbuf *m, int offset, struct inpcb *inpcb, 2456273331Sbryanv const struct sockaddr *srcsa, void *xvso) 2457273331Sbryanv{ 2458273331Sbryanv struct vxlan_socket *vso; 2459273331Sbryanv struct vxlan_header *vxh, vxlanhdr; 2460273331Sbryanv uint32_t vni; 2461273331Sbryanv int error; 2462273331Sbryanv 2463273331Sbryanv M_ASSERTPKTHDR(m); 2464273331Sbryanv vso = xvso; 2465273331Sbryanv offset += sizeof(struct udphdr); 2466273331Sbryanv 2467273331Sbryanv if (m->m_pkthdr.len < offset + sizeof(struct vxlan_header)) 2468273331Sbryanv goto out; 2469273331Sbryanv 2470273331Sbryanv if (__predict_false(m->m_len < offset + sizeof(struct vxlan_header))) { 2471273331Sbryanv m_copydata(m, offset, sizeof(struct vxlan_header), 2472273331Sbryanv (caddr_t) &vxlanhdr); 2473273331Sbryanv vxh = &vxlanhdr; 2474273331Sbryanv } else 2475273331Sbryanv vxh = mtodo(m, offset); 2476273331Sbryanv 2477273331Sbryanv /* 2478273331Sbryanv * Drop if there is a reserved bit set in either the flags or VNI 2479273331Sbryanv * fields of the header. This goes against the specification, but 2480273331Sbryanv * a bit set may indicate an unsupported new feature. This matches 2481273331Sbryanv * the behavior of the Linux implementation. 2482273331Sbryanv */ 2483273331Sbryanv if (vxh->vxlh_flags != htonl(VXLAN_HDR_FLAGS_VALID_VNI) || 2484273331Sbryanv vxh->vxlh_vni & ~htonl(VXLAN_VNI_MASK)) 2485273331Sbryanv goto out; 2486273331Sbryanv 2487273331Sbryanv vni = ntohl(vxh->vxlh_vni) >> VXLAN_HDR_VNI_SHIFT; 2488273331Sbryanv /* Adjust to the start of the inner Ethernet frame. */ 2489273331Sbryanv m_adj(m, offset + sizeof(struct vxlan_header)); 2490273331Sbryanv 2491273331Sbryanv error = vxlan_input(vso, vni, &m, srcsa); 2492273331Sbryanv MPASS(error != 0 || m == NULL); 2493273331Sbryanv 2494273331Sbryanvout: 2495273331Sbryanv if (m != NULL) 2496273331Sbryanv m_freem(m); 2497273331Sbryanv} 2498273331Sbryanv 2499273331Sbryanvstatic int 2500273331Sbryanvvxlan_input(struct vxlan_socket *vso, uint32_t vni, struct mbuf **m0, 2501273331Sbryanv const struct sockaddr *sa) 2502273331Sbryanv{ 2503273331Sbryanv struct vxlan_softc *sc; 2504273331Sbryanv struct ifnet *ifp; 2505273331Sbryanv struct mbuf *m; 2506273331Sbryanv struct ether_header *eh; 2507273331Sbryanv int error; 2508273331Sbryanv 2509273331Sbryanv sc = vxlan_socket_lookup_softc(vso, vni); 2510273331Sbryanv if (sc == NULL) 2511273331Sbryanv return (ENOENT); 2512273331Sbryanv 2513273331Sbryanv ifp = sc->vxl_ifp; 2514273331Sbryanv m = *m0; 2515273331Sbryanv eh = mtod(m, struct ether_header *); 2516273331Sbryanv 2517273331Sbryanv if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 2518273331Sbryanv error = ENETDOWN; 2519273331Sbryanv goto out; 2520273331Sbryanv } else if (ifp == m->m_pkthdr.rcvif) { 2521273331Sbryanv /* XXX Does not catch more complex loops. */ 2522273331Sbryanv error = EDEADLK; 2523273331Sbryanv goto out; 2524273331Sbryanv } 2525273331Sbryanv 2526273331Sbryanv if (sc->vxl_flags & VXLAN_FLAG_LEARN) 2527273331Sbryanv vxlan_ftable_update(sc, sa, eh->ether_shost); 2528273331Sbryanv 2529273331Sbryanv m_clrprotoflags(m); 2530273331Sbryanv m->m_pkthdr.rcvif = ifp; 2531273331Sbryanv M_SETFIB(m, ifp->if_fib); 2532273331Sbryanv 2533273331Sbryanv error = netisr_queue_src(NETISR_ETHER, 0, m); 2534273331Sbryanv *m0 = NULL; 2535273331Sbryanv 2536273331Sbryanvout: 2537273331Sbryanv vxlan_release(sc); 2538273331Sbryanv return (error); 2539273331Sbryanv} 2540273331Sbryanv 2541273331Sbryanvstatic void 2542273331Sbryanvvxlan_set_default_config(struct vxlan_softc *sc) 2543273331Sbryanv{ 2544273331Sbryanv 2545273331Sbryanv sc->vxl_flags |= VXLAN_FLAG_LEARN; 2546273331Sbryanv 2547273331Sbryanv sc->vxl_vni = VXLAN_VNI_MAX; 2548273331Sbryanv sc->vxl_ttl = IPDEFTTL; 2549273331Sbryanv 2550273331Sbryanv if (!vxlan_tunable_int(sc, "legacy_port", vxlan_legacy_port)) { 2551273331Sbryanv sc->vxl_src_addr.in4.sin_port = htons(VXLAN_PORT); 2552273331Sbryanv sc->vxl_dst_addr.in4.sin_port = htons(VXLAN_PORT); 2553273331Sbryanv } else { 2554273331Sbryanv sc->vxl_src_addr.in4.sin_port = htons(VXLAN_LEGACY_PORT); 2555273331Sbryanv sc->vxl_dst_addr.in4.sin_port = htons(VXLAN_LEGACY_PORT); 2556273331Sbryanv } 2557273331Sbryanv 2558273331Sbryanv sc->vxl_min_port = V_ipport_firstauto; 2559273331Sbryanv sc->vxl_max_port = V_ipport_lastauto; 2560273331Sbryanv 2561273331Sbryanv sc->vxl_ftable_max = VXLAN_FTABLE_MAX; 2562273331Sbryanv sc->vxl_ftable_timeout = VXLAN_FTABLE_TIMEOUT; 2563273331Sbryanv} 2564273331Sbryanv 2565273331Sbryanvstatic int 2566273331Sbryanvvxlan_set_user_config(struct vxlan_softc *sc, struct ifvxlanparam *vxlp) 2567273331Sbryanv{ 2568273331Sbryanv 2569273331Sbryanv#ifndef INET 2570273331Sbryanv if (vxlp->vxlp_with & (VXLAN_PARAM_WITH_LOCAL_ADDR4 | 2571273331Sbryanv VXLAN_PARAM_WITH_REMOTE_ADDR4)) 2572273331Sbryanv return (EAFNOSUPPORT); 2573273331Sbryanv#endif 2574273331Sbryanv 2575273331Sbryanv#ifndef INET6 2576273331Sbryanv if (vxlp->vxlp_with & (VXLAN_PARAM_WITH_LOCAL_ADDR6 | 2577273331Sbryanv VXLAN_PARAM_WITH_REMOTE_ADDR6)) 2578273331Sbryanv return (EAFNOSUPPORT); 2579273331Sbryanv#endif 2580273331Sbryanv 2581273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_VNI) { 2582273331Sbryanv if (vxlan_check_vni(vxlp->vxlp_vni) == 0) 2583273331Sbryanv sc->vxl_vni = vxlp->vxlp_vni; 2584273331Sbryanv } 2585273331Sbryanv 2586273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4) { 2587273331Sbryanv sc->vxl_src_addr.in4.sin_len = sizeof(struct sockaddr_in); 2588273331Sbryanv sc->vxl_src_addr.in4.sin_family = AF_INET; 2589273331Sbryanv sc->vxl_src_addr.in4.sin_addr = vxlp->vxlp_local_in4; 2590273331Sbryanv } else if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6) { 2591273331Sbryanv sc->vxl_src_addr.in6.sin6_len = sizeof(struct sockaddr_in6); 2592273331Sbryanv sc->vxl_src_addr.in6.sin6_family = AF_INET6; 2593273331Sbryanv sc->vxl_src_addr.in6.sin6_addr = vxlp->vxlp_local_in6; 2594273331Sbryanv } 2595273331Sbryanv 2596273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4) { 2597273331Sbryanv sc->vxl_dst_addr.in4.sin_len = sizeof(struct sockaddr_in); 2598273331Sbryanv sc->vxl_dst_addr.in4.sin_family = AF_INET; 2599273331Sbryanv sc->vxl_dst_addr.in4.sin_addr = vxlp->vxlp_remote_in4; 2600273331Sbryanv } else if (vxlp->vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) { 2601273331Sbryanv sc->vxl_dst_addr.in6.sin6_len = sizeof(struct sockaddr_in6); 2602273331Sbryanv sc->vxl_dst_addr.in6.sin6_family = AF_INET6; 2603273331Sbryanv sc->vxl_dst_addr.in6.sin6_addr = vxlp->vxlp_remote_in6; 2604273331Sbryanv } 2605273331Sbryanv 2606273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LOCAL_PORT) 2607273331Sbryanv sc->vxl_src_addr.in4.sin_port = htons(vxlp->vxlp_local_port); 2608273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_REMOTE_PORT) 2609273331Sbryanv sc->vxl_dst_addr.in4.sin_port = htons(vxlp->vxlp_remote_port); 2610273331Sbryanv 2611273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_PORT_RANGE) { 2612273331Sbryanv if (vxlp->vxlp_min_port <= vxlp->vxlp_max_port) { 2613273331Sbryanv sc->vxl_min_port = vxlp->vxlp_min_port; 2614273331Sbryanv sc->vxl_max_port = vxlp->vxlp_max_port; 2615273331Sbryanv } 2616273331Sbryanv } 2617273331Sbryanv 2618273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_MULTICAST_IF) 2619273331Sbryanv strlcpy(sc->vxl_mc_ifname, vxlp->vxlp_mc_ifname, IFNAMSIZ); 2620273331Sbryanv 2621273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_FTABLE_TIMEOUT) { 2622273331Sbryanv if (vxlan_check_ftable_timeout(vxlp->vxlp_ftable_timeout) == 0) 2623273331Sbryanv sc->vxl_ftable_timeout = vxlp->vxlp_ftable_timeout; 2624273331Sbryanv } 2625273331Sbryanv 2626273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_FTABLE_MAX) { 2627273331Sbryanv if (vxlan_check_ftable_max(vxlp->vxlp_ftable_max) == 0) 2628273331Sbryanv sc->vxl_ftable_max = vxlp->vxlp_ftable_max; 2629273331Sbryanv } 2630273331Sbryanv 2631273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_TTL) { 2632273331Sbryanv if (vxlan_check_ttl(vxlp->vxlp_ttl) == 0) 2633273331Sbryanv sc->vxl_ttl = vxlp->vxlp_ttl; 2634273331Sbryanv } 2635273331Sbryanv 2636273331Sbryanv if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LEARN) { 2637273331Sbryanv if (vxlp->vxlp_learn == 0) 2638273331Sbryanv sc->vxl_flags &= ~VXLAN_FLAG_LEARN; 2639273331Sbryanv } 2640273331Sbryanv 2641273331Sbryanv return (0); 2642273331Sbryanv} 2643273331Sbryanv 2644273331Sbryanvstatic int 2645273331Sbryanvvxlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) 2646273331Sbryanv{ 2647273331Sbryanv struct vxlan_softc *sc; 2648273331Sbryanv struct ifnet *ifp; 2649273331Sbryanv struct ifvxlanparam vxlp; 2650273331Sbryanv int error; 2651273331Sbryanv 2652273331Sbryanv sc = malloc(sizeof(struct vxlan_softc), M_VXLAN, M_WAITOK | M_ZERO); 2653273331Sbryanv sc->vxl_unit = unit; 2654273331Sbryanv vxlan_set_default_config(sc); 2655273331Sbryanv 2656273331Sbryanv if (params != 0) { 2657273331Sbryanv error = copyin(params, &vxlp, sizeof(vxlp)); 2658273331Sbryanv if (error) 2659273331Sbryanv goto fail; 2660273331Sbryanv 2661273331Sbryanv error = vxlan_set_user_config(sc, &vxlp); 2662273331Sbryanv if (error) 2663273331Sbryanv goto fail; 2664273331Sbryanv } 2665273331Sbryanv 2666273331Sbryanv ifp = if_alloc(IFT_ETHER); 2667273331Sbryanv if (ifp == NULL) { 2668273331Sbryanv error = ENOSPC; 2669273331Sbryanv goto fail; 2670273331Sbryanv } 2671273331Sbryanv 2672273331Sbryanv sc->vxl_ifp = ifp; 2673273331Sbryanv rm_init(&sc->vxl_lock, "vxlanrm"); 2674273331Sbryanv callout_init_rw(&sc->vxl_callout, &sc->vxl_lock, 0); 2675273331Sbryanv sc->vxl_port_hash_key = arc4random(); 2676273331Sbryanv vxlan_ftable_init(sc); 2677273331Sbryanv 2678273331Sbryanv vxlan_sysctl_setup(sc); 2679273331Sbryanv 2680273331Sbryanv ifp->if_softc = sc; 2681273331Sbryanv if_initname(ifp, vxlan_name, unit); 2682273331Sbryanv ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 2683273331Sbryanv ifp->if_init = vxlan_init; 2684273331Sbryanv ifp->if_ioctl = vxlan_ioctl; 2685273331Sbryanv ifp->if_transmit = vxlan_transmit; 2686273331Sbryanv ifp->if_qflush = vxlan_qflush; 2687273331Sbryanv 2688273331Sbryanv vxlan_fakeaddr(sc); 2689273331Sbryanv ether_ifattach(ifp, sc->vxl_hwaddr); 2690273331Sbryanv 2691273331Sbryanv ifp->if_baudrate = 0; 2692273331Sbryanv ifp->if_hdrlen = 0; 2693273331Sbryanv 2694273331Sbryanv return (0); 2695273331Sbryanv 2696273331Sbryanvfail: 2697273331Sbryanv free(sc, M_VXLAN); 2698273331Sbryanv return (error); 2699273331Sbryanv} 2700273331Sbryanv 2701273331Sbryanvstatic void 2702273331Sbryanvvxlan_clone_destroy(struct ifnet *ifp) 2703273331Sbryanv{ 2704273331Sbryanv struct vxlan_softc *sc; 2705273331Sbryanv 2706273331Sbryanv sc = ifp->if_softc; 2707273331Sbryanv 2708273331Sbryanv vxlan_teardown(sc); 2709273331Sbryanv 2710273331Sbryanv vxlan_ftable_flush(sc, 1); 2711273331Sbryanv 2712273331Sbryanv ether_ifdetach(ifp); 2713273331Sbryanv if_free(ifp); 2714273331Sbryanv 2715273331Sbryanv vxlan_ftable_fini(sc); 2716273331Sbryanv 2717273331Sbryanv vxlan_sysctl_destroy(sc); 2718273331Sbryanv rm_destroy(&sc->vxl_lock); 2719273331Sbryanv free(sc, M_VXLAN); 2720273331Sbryanv} 2721273331Sbryanv 2722273331Sbryanv/* BMV: Taken from if_bridge. */ 2723273331Sbryanvstatic uint32_t 2724273331Sbryanvvxlan_mac_hash(struct vxlan_softc *sc, const uint8_t *addr) 2725273331Sbryanv{ 2726273331Sbryanv uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->vxl_ftable_hash_key; 2727273331Sbryanv 2728273331Sbryanv b += addr[5] << 8; 2729273331Sbryanv b += addr[4]; 2730273331Sbryanv a += addr[3] << 24; 2731273331Sbryanv a += addr[2] << 16; 2732273331Sbryanv a += addr[1] << 8; 2733273331Sbryanv a += addr[0]; 2734273331Sbryanv 2735273331Sbryanv/* 2736273331Sbryanv * The following hash function is adapted from "Hash Functions" by Bob Jenkins 2737273331Sbryanv * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). 2738273331Sbryanv */ 2739273331Sbryanv#define mix(a, b, c) \ 2740273331Sbryanvdo { \ 2741273331Sbryanv a -= b; a -= c; a ^= (c >> 13); \ 2742273331Sbryanv b -= c; b -= a; b ^= (a << 8); \ 2743273331Sbryanv c -= a; c -= b; c ^= (b >> 13); \ 2744273331Sbryanv a -= b; a -= c; a ^= (c >> 12); \ 2745273331Sbryanv b -= c; b -= a; b ^= (a << 16); \ 2746273331Sbryanv c -= a; c -= b; c ^= (b >> 5); \ 2747273331Sbryanv a -= b; a -= c; a ^= (c >> 3); \ 2748273331Sbryanv b -= c; b -= a; b ^= (a << 10); \ 2749273331Sbryanv c -= a; c -= b; c ^= (b >> 15); \ 2750273331Sbryanv} while (0) 2751273331Sbryanv 2752273331Sbryanv mix(a, b, c); 2753273331Sbryanv 2754273331Sbryanv#undef mix 2755273331Sbryanv 2756273331Sbryanv return (c); 2757273331Sbryanv} 2758273331Sbryanv 2759273331Sbryanvstatic void 2760273331Sbryanvvxlan_fakeaddr(struct vxlan_softc *sc) 2761273331Sbryanv{ 2762273331Sbryanv 2763273331Sbryanv /* 2764273331Sbryanv * Generate a non-multicast, locally administered address. 2765273331Sbryanv * 2766273331Sbryanv * BMV: Should we use the FreeBSD OUI range instead? 2767273331Sbryanv */ 2768273331Sbryanv arc4rand(sc->vxl_hwaddr, ETHER_ADDR_LEN, 1); 2769273331Sbryanv sc->vxl_hwaddr[0] &= ~1; 2770273331Sbryanv sc->vxl_hwaddr[0] |= 2; 2771273331Sbryanv} 2772273331Sbryanv 2773273331Sbryanvstatic int 2774273331Sbryanvvxlan_sockaddr_cmp(const union vxlan_sockaddr *vxladdr, 2775273331Sbryanv const struct sockaddr *sa) 2776273331Sbryanv{ 2777273331Sbryanv 2778273331Sbryanv return (bcmp(&vxladdr->sa, sa, vxladdr->sa.sa_len)); 2779273331Sbryanv} 2780273331Sbryanv 2781273331Sbryanvstatic void 2782273331Sbryanvvxlan_sockaddr_copy(union vxlan_sockaddr *vxladdr, 2783273331Sbryanv const struct sockaddr *sa) 2784273331Sbryanv{ 2785273331Sbryanv 2786273331Sbryanv MPASS(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); 2787273331Sbryanv bzero(vxladdr, sizeof(*vxladdr)); 2788273331Sbryanv 2789273331Sbryanv if (sa->sa_family == AF_INET) { 2790273331Sbryanv vxladdr->in4 = *satoconstsin(sa); 2791273331Sbryanv vxladdr->in4.sin_len = sizeof(struct sockaddr_in); 2792273331Sbryanv } else if (sa->sa_family == AF_INET6) { 2793273331Sbryanv vxladdr->in6 = *satoconstsin6(sa); 2794273331Sbryanv vxladdr->in6.sin6_len = sizeof(struct sockaddr_in6); 2795273331Sbryanv } 2796273331Sbryanv} 2797273331Sbryanv 2798273331Sbryanvstatic int 2799273331Sbryanvvxlan_sockaddr_in_equal(const union vxlan_sockaddr *vxladdr, 2800273331Sbryanv const struct sockaddr *sa) 2801273331Sbryanv{ 2802273331Sbryanv int equal; 2803273331Sbryanv 2804273331Sbryanv if (sa->sa_family == AF_INET) { 2805273331Sbryanv const struct in_addr *in4 = &satoconstsin(sa)->sin_addr; 2806273331Sbryanv equal = in4->s_addr == vxladdr->in4.sin_addr.s_addr; 2807273331Sbryanv } else if (sa->sa_family == AF_INET6) { 2808273331Sbryanv const struct in6_addr *in6 = &satoconstsin6(sa)->sin6_addr; 2809273331Sbryanv equal = IN6_ARE_ADDR_EQUAL(in6, &vxladdr->in6.sin6_addr); 2810273331Sbryanv } else 2811273331Sbryanv equal = 0; 2812273331Sbryanv 2813273331Sbryanv return (equal); 2814273331Sbryanv} 2815273331Sbryanv 2816273331Sbryanvstatic void 2817273331Sbryanvvxlan_sockaddr_in_copy(union vxlan_sockaddr *vxladdr, 2818273331Sbryanv const struct sockaddr *sa) 2819273331Sbryanv{ 2820273331Sbryanv 2821273331Sbryanv MPASS(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); 2822273331Sbryanv 2823273331Sbryanv if (sa->sa_family == AF_INET) { 2824273331Sbryanv const struct in_addr *in4 = &satoconstsin(sa)->sin_addr; 2825273331Sbryanv vxladdr->in4.sin_family = AF_INET; 2826273331Sbryanv vxladdr->in4.sin_len = sizeof(struct sockaddr_in); 2827273331Sbryanv vxladdr->in4.sin_addr = *in4; 2828273331Sbryanv } else if (sa->sa_family == AF_INET6) { 2829273331Sbryanv const struct in6_addr *in6 = &satoconstsin6(sa)->sin6_addr; 2830273331Sbryanv vxladdr->in6.sin6_family = AF_INET6; 2831273331Sbryanv vxladdr->in6.sin6_len = sizeof(struct sockaddr_in6); 2832273331Sbryanv vxladdr->in6.sin6_addr = *in6; 2833273331Sbryanv } 2834273331Sbryanv} 2835273331Sbryanv 2836273331Sbryanvstatic int 2837273331Sbryanvvxlan_sockaddr_supported(const union vxlan_sockaddr *vxladdr, int unspec) 2838273331Sbryanv{ 2839273331Sbryanv const struct sockaddr *sa; 2840273331Sbryanv int supported; 2841273331Sbryanv 2842273331Sbryanv sa = &vxladdr->sa; 2843273331Sbryanv supported = 0; 2844273331Sbryanv 2845273331Sbryanv if (sa->sa_family == AF_UNSPEC && unspec != 0) { 2846273331Sbryanv supported = 1; 2847273331Sbryanv } else if (sa->sa_family == AF_INET) { 2848273331Sbryanv#ifdef INET 2849273331Sbryanv supported = 1; 2850273331Sbryanv#endif 2851273331Sbryanv } else if (sa->sa_family == AF_INET6) { 2852273331Sbryanv#ifdef INET6 2853273331Sbryanv supported = 1; 2854273331Sbryanv#endif 2855273331Sbryanv } 2856273331Sbryanv 2857273331Sbryanv return (supported); 2858273331Sbryanv} 2859273331Sbryanv 2860273331Sbryanvstatic int 2861273331Sbryanvvxlan_sockaddr_in_any(const union vxlan_sockaddr *vxladdr) 2862273331Sbryanv{ 2863273331Sbryanv const struct sockaddr *sa; 2864273331Sbryanv int any; 2865273331Sbryanv 2866273331Sbryanv sa = &vxladdr->sa; 2867273331Sbryanv 2868273331Sbryanv if (sa->sa_family == AF_INET) { 2869273331Sbryanv const struct in_addr *in4 = &satoconstsin(sa)->sin_addr; 2870273331Sbryanv any = in4->s_addr == INADDR_ANY; 2871273331Sbryanv } else if (sa->sa_family == AF_INET6) { 2872273331Sbryanv const struct in6_addr *in6 = &satoconstsin6(sa)->sin6_addr; 2873273331Sbryanv any = IN6_IS_ADDR_UNSPECIFIED(in6); 2874273331Sbryanv } else 2875273331Sbryanv any = -1; 2876273331Sbryanv 2877273331Sbryanv return (any); 2878273331Sbryanv} 2879273331Sbryanv 2880273331Sbryanvstatic int 2881273331Sbryanvvxlan_sockaddr_in_multicast(const union vxlan_sockaddr *vxladdr) 2882273331Sbryanv{ 2883273331Sbryanv const struct sockaddr *sa; 2884273331Sbryanv int mc; 2885273331Sbryanv 2886273331Sbryanv sa = &vxladdr->sa; 2887273331Sbryanv 2888273331Sbryanv if (sa->sa_family == AF_INET) { 2889273331Sbryanv const struct in_addr *in4 = &satoconstsin(sa)->sin_addr; 2890273331Sbryanv mc = IN_MULTICAST(ntohl(in4->s_addr)); 2891273331Sbryanv } else if (sa->sa_family == AF_INET6) { 2892273331Sbryanv const struct in6_addr *in6 = &satoconstsin6(sa)->sin6_addr; 2893273331Sbryanv mc = IN6_IS_ADDR_MULTICAST(in6); 2894273331Sbryanv } else 2895273331Sbryanv mc = -1; 2896273331Sbryanv 2897273331Sbryanv return (mc); 2898273331Sbryanv} 2899273331Sbryanv 2900273331Sbryanvstatic int 2901273331Sbryanvvxlan_can_change_config(struct vxlan_softc *sc) 2902273331Sbryanv{ 2903273331Sbryanv struct ifnet *ifp; 2904273331Sbryanv 2905273331Sbryanv ifp = sc->vxl_ifp; 2906273331Sbryanv VXLAN_LOCK_ASSERT(sc); 2907273331Sbryanv 2908273331Sbryanv if (ifp->if_drv_flags & IFF_DRV_RUNNING) 2909273331Sbryanv return (0); 2910273331Sbryanv if (sc->vxl_flags & (VXLAN_FLAG_INIT | VXLAN_FLAG_TEARDOWN)) 2911273331Sbryanv return (0); 2912273331Sbryanv 2913273331Sbryanv return (1); 2914273331Sbryanv} 2915273331Sbryanv 2916273331Sbryanvstatic int 2917273331Sbryanvvxlan_check_vni(uint32_t vni) 2918273331Sbryanv{ 2919273331Sbryanv 2920273331Sbryanv return (vni >= VXLAN_VNI_MAX); 2921273331Sbryanv} 2922273331Sbryanv 2923273331Sbryanvstatic int 2924273331Sbryanvvxlan_check_ttl(int ttl) 2925273331Sbryanv{ 2926273331Sbryanv 2927273331Sbryanv return (ttl > MAXTTL); 2928273331Sbryanv} 2929273331Sbryanv 2930273331Sbryanvstatic int 2931273331Sbryanvvxlan_check_ftable_timeout(uint32_t timeout) 2932273331Sbryanv{ 2933273331Sbryanv 2934273331Sbryanv return (timeout > VXLAN_FTABLE_MAX_TIMEOUT); 2935273331Sbryanv} 2936273331Sbryanv 2937273331Sbryanvstatic int 2938273331Sbryanvvxlan_check_ftable_max(uint32_t max) 2939273331Sbryanv{ 2940273331Sbryanv 2941273331Sbryanv return (max > VXLAN_FTABLE_MAX); 2942273331Sbryanv} 2943273331Sbryanv 2944273331Sbryanvstatic void 2945273331Sbryanvvxlan_sysctl_setup(struct vxlan_softc *sc) 2946273331Sbryanv{ 2947273331Sbryanv struct sysctl_ctx_list *ctx; 2948273331Sbryanv struct sysctl_oid *node; 2949273331Sbryanv struct vxlan_statistics *stats; 2950273331Sbryanv char namebuf[8]; 2951273331Sbryanv 2952273331Sbryanv ctx = &sc->vxl_sysctl_ctx; 2953273331Sbryanv stats = &sc->vxl_stats; 2954273331Sbryanv snprintf(namebuf, sizeof(namebuf), "%d", sc->vxl_unit); 2955273331Sbryanv 2956273331Sbryanv sysctl_ctx_init(ctx); 2957273331Sbryanv sc->vxl_sysctl_node = SYSCTL_ADD_NODE(ctx, 2958273331Sbryanv SYSCTL_STATIC_CHILDREN(_net_link_vxlan), OID_AUTO, namebuf, 2959273331Sbryanv CTLFLAG_RD, NULL, ""); 2960273331Sbryanv 2961273331Sbryanv node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sc->vxl_sysctl_node), 2962273331Sbryanv OID_AUTO, "ftable", CTLFLAG_RD, NULL, ""); 2963273331Sbryanv SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "count", 2964273331Sbryanv CTLFLAG_RD, &sc->vxl_ftable_cnt, 0, 2965273331Sbryanv "Number of entries in fowarding table"); 2966273331Sbryanv SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "max", 2967273331Sbryanv CTLFLAG_RD, &sc->vxl_ftable_max, 0, 2968273331Sbryanv "Maximum number of entries allowed in fowarding table"); 2969273331Sbryanv SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "timeout", 2970273331Sbryanv CTLFLAG_RD, &sc->vxl_ftable_timeout, 0, 2971273331Sbryanv "Number of seconds between prunes of the forwarding table"); 2972273331Sbryanv SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "dump", 2973273331Sbryanv CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_SKIP, 2974273331Sbryanv sc, 0, vxlan_ftable_sysctl_dump, "A", 2975273331Sbryanv "Dump the forwarding table entries"); 2976273331Sbryanv 2977273331Sbryanv node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sc->vxl_sysctl_node), 2978273331Sbryanv OID_AUTO, "stats", CTLFLAG_RD, NULL, ""); 2979273331Sbryanv SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(node), OID_AUTO, 2980273331Sbryanv "ftable_nospace", CTLFLAG_RD, &stats->ftable_nospace, 0, 2981273331Sbryanv "Fowarding table reached maximum entries"); 2982273331Sbryanv SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(node), OID_AUTO, 2983273331Sbryanv "ftable_lock_upgrade_failed", CTLFLAG_RD, 2984273331Sbryanv &stats->ftable_lock_upgrade_failed, 0, 2985273331Sbryanv "Forwarding table update required lock upgrade"); 2986273331Sbryanv} 2987273331Sbryanv 2988273331Sbryanvstatic void 2989273331Sbryanvvxlan_sysctl_destroy(struct vxlan_softc *sc) 2990273331Sbryanv{ 2991273331Sbryanv 2992273331Sbryanv sysctl_ctx_free(&sc->vxl_sysctl_ctx); 2993273331Sbryanv sc->vxl_sysctl_node = NULL; 2994273331Sbryanv} 2995273331Sbryanv 2996273331Sbryanvstatic int 2997273331Sbryanvvxlan_tunable_int(struct vxlan_softc *sc, const char *knob, int def) 2998273331Sbryanv{ 2999273331Sbryanv char path[64]; 3000273331Sbryanv 3001273331Sbryanv snprintf(path, sizeof(path), "net.link.vxlan.%d.%s", 3002273331Sbryanv sc->vxl_unit, knob); 3003273331Sbryanv TUNABLE_INT_FETCH(path, &def); 3004273331Sbryanv 3005273331Sbryanv return (def); 3006273331Sbryanv} 3007273331Sbryanv 3008273331Sbryanvstatic void 3009273331Sbryanvvxlan_ifdetach_event(void *arg __unused, struct ifnet *ifp) 3010273331Sbryanv{ 3011273331Sbryanv struct vxlan_softc_head list; 3012273331Sbryanv struct vxlan_socket *vso; 3013273331Sbryanv struct vxlan_softc *sc, *tsc; 3014273331Sbryanv 3015273331Sbryanv LIST_INIT(&list); 3016273331Sbryanv 3017273331Sbryanv if (ifp->if_flags & IFF_RENAMING) 3018273331Sbryanv return; 3019273331Sbryanv if ((ifp->if_flags & IFF_MULTICAST) == 0) 3020273331Sbryanv return; 3021273331Sbryanv 3022273331Sbryanv mtx_lock(&vxlan_list_mtx); 3023273331Sbryanv LIST_FOREACH(vso, &vxlan_socket_list, vxlso_entry) 3024273331Sbryanv vxlan_socket_ifdetach(vso, ifp, &list); 3025273331Sbryanv mtx_unlock(&vxlan_list_mtx); 3026273331Sbryanv 3027273331Sbryanv LIST_FOREACH_SAFE(sc, &list, vxl_ifdetach_list, tsc) { 3028273331Sbryanv LIST_REMOVE(sc, vxl_ifdetach_list); 3029273331Sbryanv 3030273331Sbryanv VXLAN_WLOCK(sc); 3031273331Sbryanv if (sc->vxl_flags & VXLAN_FLAG_INIT) 3032273331Sbryanv vxlan_init_wait(sc); 3033273331Sbryanv vxlan_teardown_locked(sc); 3034273331Sbryanv } 3035273331Sbryanv} 3036273331Sbryanv 3037273331Sbryanvstatic void 3038273331Sbryanvvxlan_load(void) 3039273331Sbryanv{ 3040273331Sbryanv 3041273331Sbryanv mtx_init(&vxlan_list_mtx, "vxlan list", NULL, MTX_DEF); 3042273331Sbryanv LIST_INIT(&vxlan_socket_list); 3043273331Sbryanv vxlan_ifdetach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 3044273331Sbryanv vxlan_ifdetach_event, NULL, EVENTHANDLER_PRI_ANY); 3045273331Sbryanv vxlan_cloner = if_clone_simple(vxlan_name, vxlan_clone_create, 3046273331Sbryanv vxlan_clone_destroy, 0); 3047273331Sbryanv} 3048273331Sbryanv 3049273331Sbryanvstatic void 3050273331Sbryanvvxlan_unload(void) 3051273331Sbryanv{ 3052273331Sbryanv 3053273331Sbryanv EVENTHANDLER_DEREGISTER(ifnet_departure_event, 3054273331Sbryanv vxlan_ifdetach_event_tag); 3055273331Sbryanv if_clone_detach(vxlan_cloner); 3056273331Sbryanv mtx_destroy(&vxlan_list_mtx); 3057273331Sbryanv MPASS(LIST_EMPTY(&vxlan_socket_list)); 3058273331Sbryanv} 3059273331Sbryanv 3060273331Sbryanvstatic int 3061273331Sbryanvvxlan_modevent(module_t mod, int type, void *unused) 3062273331Sbryanv{ 3063273331Sbryanv int error; 3064273331Sbryanv 3065273331Sbryanv error = 0; 3066273331Sbryanv 3067273331Sbryanv switch (type) { 3068273331Sbryanv case MOD_LOAD: 3069273331Sbryanv vxlan_load(); 3070273331Sbryanv break; 3071273331Sbryanv case MOD_UNLOAD: 3072273331Sbryanv vxlan_unload(); 3073273331Sbryanv break; 3074273331Sbryanv default: 3075273331Sbryanv error = ENOTSUP; 3076273331Sbryanv break; 3077273331Sbryanv } 3078273331Sbryanv 3079273331Sbryanv return (error); 3080273331Sbryanv} 3081273331Sbryanv 3082273331Sbryanvstatic moduledata_t vxlan_mod = { 3083273331Sbryanv "if_vxlan", 3084273331Sbryanv vxlan_modevent, 3085273331Sbryanv 0 3086273331Sbryanv}; 3087273331Sbryanv 3088273331SbryanvDECLARE_MODULE(if_vxlan, vxlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 3089273331SbryanvMODULE_VERSION(if_vxlan, 1); 3090