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