Index: conf/GENERIC =================================================================== RCS file: /cvs/src/sys/conf/GENERIC,v retrieving revision 1.274 diff -u -p -r1.274 GENERIC --- conf/GENERIC 25 Feb 2021 01:19:35 -0000 1.274 +++ conf/GENERIC 22 Mar 2021 01:53:59 -0000 @@ -108,6 +109,7 @@ pseudo-device tpmr # 802.1Q Two-Port MA pseudo-device tun # network tunneling over tty (tun & tap) pseudo-device vether # Virtual ethernet pseudo-device vxlan # Virtual extensible LAN +pseudo-device geneve # Generic Network Virtualisation Encapsulation pseudo-device vlan # IEEE 802.1Q VLAN pseudo-device switch # Switch pseudo-device wg # WireGuard Index: conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.699 diff -u -p -r1.699 files --- conf/files 12 Mar 2021 16:26:27 -0000 1.699 +++ conf/files 22 Mar 2021 01:53:59 -0000 @@ -574,6 +574,7 @@ pseudo-device bpe: ifnet, ether, ifmedia pseudo-device vether: ifnet, ether pseudo-device pppx: ifnet pseudo-device vxlan: ifnet, ether, ifmedia +pseudo-device geneve: ifnet, ether, etherbridge pseudo-device switch: ifnet, ether pseudo-device wg: ifnet @@ -841,6 +842,7 @@ file net/if_vether.c vether file net/if_pair.c pair file net/if_pppx.c pppx needs-count file net/if_vxlan.c vxlan needs-count +file net/if_geneve.c geneve file net/if_wg.c wg file net/wg_noise.c wg file net/wg_cookie.c wg Index: net/if_geneve.c =================================================================== RCS file: net/if_geneve.c diff -N net/if_geneve.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ net/if_geneve.c 22 Mar 2021 01:54:31 -0000 @@ -0,0 +1,1817 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bpfilter.h" +#include "pf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#include +#include +#endif + +/* for bridge stuff */ +#include +#include + +#if NBPFILTER > 0 +#include +#endif + +/* + * The protocol. + */ + +#define GENEVE_PORT 6081 + +struct geneve_header { + uint8_t gnv_ver_optlen; +#define GENEVE_VER_SHIFT 6 +#define GENEVE_VER_MASK (0x3U << GENEVE_VER_SHIFT) +#define GENEVE_VER_0 (0x0U << GENEVE_VER_SHIFT) +#define GENEVE_OPTLEN_SHIFT 0 +#define GENEVE_OPTLEN_MASK (0x3fU << GENEVE_OPTLEN_SHIFT) +#define GENEVE_OPTLEN_UNIT 4U + uint8_t gnv_flags; +#define GENEVE_F_OAM (1U << 7) +#define GENEVE_F_CRITICAL (1U << 6) + uint16_t gnv_proto; + uint32_t gnv_vni; +#define GENEVE_VNI_SHIFT 8 +#define GENEVE_VNI_MASK (0xffffffU << GENEVE_VNI_SHIFT) +} __packed __aligned(4); + +#define GENEVE_VNI_MAX 0x00ffffffU +#define GENEVE_VNI_MIN 0x00000000U + +struct geneve_option { + uint16_t gnv_o_class; + uint8_t gnv_o_type; +#define GENEVE_OPTION_C (1U << 7) +#define GENEVE_OPTION_TYPE_MASK 0x7fU + uint8_t gnv_o_flags; +#define GENEVE_OPTION_LEN_SHIFT 0 +#define GENEVE_OPTION_LEN_MASK (0x1fU << GENEVE_OPTION_LEN_SHIFT) +#define GENEVE_OPTION_LEN_UNIT 4U +}; + +/* + * The driver. + */ + +union geneve_addr { + struct in_addr in4; + struct in6_addr in6; +}; + +struct geneve_softc; + +struct geneve_peer { + RBT_ENTRY(geneve_peer) p_entry; + + unsigned int p_mask; /* do we use addr in the comparison */ + union geneve_addr p_addr; + struct geneve_header p_header __aligned(sizeof(unsigned long)); + + unsigned int p_if_index; +}; + +RBT_HEAD(geneve_peers, geneve_peer); + +struct geneve_tep { + TAILQ_ENTRY(geneve_tep) gtep_entry; + + sa_family_t gtep_af; + unsigned int gtep_rdomain; + union geneve_addr gtep_addr; +#define gtep_addr4 gtep_addr.in4 +#define gtep_addr6 gtep_addr.in6 + in_port_t gtep_port; + + struct socket *gtep_so; + + struct mutex gtep_mtx; + struct geneve_peers gtep_peers; +}; + +TAILQ_HEAD(geneve_teps, geneve_tep); + +enum geneve_tunnel_mode { + GENEVE_TMODE_UNSET, + GENEVE_TMODE_P2P, /* unicast destination, no learning */ + GENEVE_TMODE_LEARNING, /* multicast destination, learning */ + GENEVE_TMODE_ENDPOINT, /* unset destination, no learning */ +}; + +struct geneve_softc { + struct arpcom sc_ac; + struct etherbridge sc_eb; + + unsigned int sc_rdomain; + sa_family_t sc_af; + union geneve_addr sc_src; + union geneve_addr sc_dst; + in_port_t sc_port; + struct geneve_header sc_header; + unsigned int sc_if_index0; + + struct task sc_dtask; + void *sc_inmulti; + + enum geneve_tunnel_mode sc_mode; + struct geneve_peer *sc_ucast_peer; + struct geneve_peer *sc_mcast_peer; + + uint16_t sc_df; + int sc_ttl; + int sc_txhprio; + int sc_rxhprio; + + struct task sc_send_task; +}; + +void geneveattach(int); + +static int geneve_clone_create(struct if_clone *, int); +static int geneve_clone_destroy(struct ifnet *); + +static int geneve_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +static int geneve_enqueue(struct ifnet *, struct mbuf *); +static void geneve_start(struct ifqueue *); +static void geneve_send(void *); + +static int geneve_ioctl(struct ifnet *, u_long, caddr_t); +static int geneve_up(struct geneve_softc *); +static int geneve_down(struct geneve_softc *); +static int geneve_addmulti(struct geneve_softc *, struct ifnet *); +static void geneve_delmulti(struct geneve_softc *); + +static struct mbuf * + geneve_input(void *, struct mbuf *, + struct ip *, struct ip6_hdr *, void *, int); + +static int geneve_set_rdomain(struct geneve_softc *, const struct ifreq *); +static int geneve_get_rdomain(struct geneve_softc *, struct ifreq *); +static int geneve_set_tunnel(struct geneve_softc *, + const struct if_laddrreq *); +static int geneve_get_tunnel(struct geneve_softc *, struct if_laddrreq *); +static int geneve_del_tunnel(struct geneve_softc *); +static int geneve_set_vnetid(struct geneve_softc *, const struct ifreq *); +static int geneve_get_vnetid(struct geneve_softc *, struct ifreq *); +static int geneve_set_parent(struct geneve_softc *, + const struct if_parent *); +static int geneve_get_parent(struct geneve_softc *, struct if_parent *); +static int geneve_del_parent(struct geneve_softc *); + +static int geneve_add_addr(struct geneve_softc *, const struct ifbareq *); +static int geneve_del_addr(struct geneve_softc *, const struct ifbareq *); + +static void geneve_detach_hook(void *); + +static struct if_clone geneve_cloner = + IF_CLONE_INITIALIZER("geneve", geneve_clone_create, geneve_clone_destroy); + +static int geneve_eb_port_eq(void *, void *, void *); +static void *geneve_eb_port_take(void *, void *); +static void geneve_eb_port_rele(void *, void *); +static size_t geneve_eb_port_ifname(void *, char *, size_t, void *); +static void geneve_eb_port_sa(void *, struct sockaddr_storage *, void *); + +static const struct etherbridge_ops geneve_etherbridge_ops = { + geneve_eb_port_eq, + geneve_eb_port_take, + geneve_eb_port_rele, + geneve_eb_port_ifname, + geneve_eb_port_sa, +}; + +static struct rwlock geneve_lock = RWLOCK_INITIALIZER("gnvteps"); +static struct geneve_teps geneve_teps = TAILQ_HEAD_INITIALIZER(geneve_teps); +static struct pool geneve_endpoint_pool; + +static inline int geneve_peer_cmp(const struct geneve_peer *, + const struct geneve_peer *); + +RBT_PROTOTYPE(geneve_peers, geneve_peer, p_entry, geneve_peer_cmp); + +void +geneveattach(int count) +{ + if_clone_attach(&geneve_cloner); +} + +static int +geneve_clone_create(struct if_clone *ifc, int unit) +{ + struct geneve_softc *sc; + struct ifnet *ifp; + int error; + + if (geneve_endpoint_pool.pr_size == 0) { + pool_init(&geneve_endpoint_pool, sizeof(union geneve_addr), + 0, IPL_SOFTNET, 0, "geneveep", NULL); + } + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO|M_CANFAIL); + if (sc == NULL) + return (ENOMEM); + + ifp = &sc->sc_ac.ac_if; + + snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", + ifc->ifc_name, unit); + + error = etherbridge_init(&sc->sc_eb, ifp->if_xname, + &geneve_etherbridge_ops, sc); + if (error == -1) { + free(sc, M_DEVBUF, sizeof(*sc)); + return (error); + } + + sc->sc_af = AF_UNSPEC; + sc->sc_txhprio = 0; + sc->sc_rxhprio = IF_HDRPRIO_OUTER; + sc->sc_df = 0; + sc->sc_ttl = IP_DEFAULT_MULTICAST_TTL; + + sc->sc_header.gnv_ver_optlen = GENEVE_VER_0 << GENEVE_VER_SHIFT; + sc->sc_header.gnv_flags = 0; + sc->sc_header.gnv_proto = htons(ETHERTYPE_TRANSETHER); + sc->sc_header.gnv_vni = htonl(0 << GENEVE_VNI_SHIFT); + + task_set(&sc->sc_dtask, geneve_detach_hook, sc); + task_set(&sc->sc_send_task, geneve_send, sc); + + ifp->if_softc = sc; + ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; + ifp->if_ioctl = geneve_ioctl; + ifp->if_output = geneve_output; + ifp->if_enqueue = geneve_enqueue; + ifp->if_qstart = geneve_start; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; + ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; + ether_fakeaddr(ifp); + + if_counters_alloc(ifp); + if_attach(ifp); + ether_ifattach(ifp); + + return (0); +} + +static int +geneve_clone_destroy(struct ifnet *ifp) +{ + struct geneve_softc *sc = ifp->if_softc; + + NET_LOCK(); + if (ISSET(ifp->if_flags, IFF_RUNNING)) + geneve_down(sc); + NET_UNLOCK(); + + ether_ifdetach(ifp); + if_detach(ifp); + + etherbridge_destroy(&sc->sc_eb); + + free(sc, M_DEVBUF, sizeof(*sc)); + + return (0); +} + +static struct mbuf * +geneve_encap(struct geneve_softc *sc, struct mbuf *m, + struct mbuf *(ip_encap)(struct geneve_softc *sc, struct mbuf *, + const union geneve_addr *, uint8_t)) +{ + struct mbuf *m0; + union geneve_addr gateway; + const union geneve_addr *endpoint; + struct geneve_header *vh; + struct udphdr *uh; + int prio; + uint8_t tos; + + if (sc->sc_mode == GENEVE_TMODE_UNSET) + goto drop; + + if (sc->sc_mode == GENEVE_TMODE_P2P) + endpoint = &sc->sc_dst; + else { /* GENEVE_TMODE_LEARNING || GENEVE_TMODE_ENDPOINT */ + struct ether_header *eh = mtod(m, struct ether_header *); + + smr_read_enter(); + endpoint = etherbridge_resolve_ea(&sc->sc_eb, + (struct ether_addr *)eh->ether_dhost); + if (endpoint != NULL) { + gateway = *endpoint; + endpoint = &gateway; + } + smr_read_leave(); + + if (endpoint == NULL) { + if (sc->sc_mode == GENEVE_TMODE_ENDPOINT) + goto drop; + + /* "flood" to unknown destinations */ + endpoint = &sc->sc_dst; + } + } + + /* force prepend mbuf because of payload alignment */ + m0 = m_get(M_DONTWAIT, m->m_type); + if (m0 == NULL) + goto drop; + + m_align(m0, 0); + m0->m_len = 0; + + M_MOVE_PKTHDR(m0, m); + m0->m_next = m; + + m = m_prepend(m0, sizeof(*vh), M_DONTWAIT); + if (m == NULL) + return (NULL); + + vh = mtod(m, struct geneve_header *); + *vh = sc->sc_header; + + m = m_prepend(m, sizeof(*uh), M_DONTWAIT); + if (m == NULL) + return (NULL); + + uh = mtod(m, struct udphdr *); + uh->uh_sport = sc->sc_port; /* XXX */ + uh->uh_dport = sc->sc_port; + htobem16(&uh->uh_ulen, m->m_pkthdr.len); + uh->uh_sum = htons(0); + + SET(m->m_pkthdr.csum_flags, M_UDP_CSUM_OUT); + + prio = sc->sc_txhprio; + if (prio == IF_HDRPRIO_PACKET) + prio = m->m_pkthdr.pf.prio; + tos = IFQ_PRIO2TOS(prio); + + CLR(m->m_flags, M_BCAST|M_MCAST); + m->m_pkthdr.ph_rtableid = sc->sc_rdomain; + +#if NPF > 0 + pf_pkt_addr_changed(m); +#endif + + return ((*ip_encap)(sc, m, endpoint, tos)); +drop: + m_freem(m); + return (NULL); +} + +static struct mbuf * +geneve_encap_ipv4(struct geneve_softc *sc, struct mbuf *m, + const union geneve_addr *endpoint, uint8_t tos) +{ + struct ip *ip; + + m = m_prepend(m, sizeof(*ip), M_DONTWAIT); + if (m == NULL) + return (NULL); + + ip = mtod(m, struct ip *); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_off = sc->sc_df; + ip->ip_tos = tos; + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_ttl = sc->sc_ttl; + ip->ip_p = IPPROTO_UDP; + ip->ip_src = sc->sc_src.in4; + ip->ip_dst = endpoint->in4; + + return (m); +} + +#ifdef INET6 +static struct mbuf * +geneve_encap_ipv6(struct geneve_softc *sc, struct mbuf *m, + const union geneve_addr *endpoint, uint8_t tos) +{ + struct ip6_hdr *ip6; + int len = m->m_pkthdr.len; + + m = m_prepend(m, sizeof(*ip6), M_DONTWAIT); + if (m == NULL) + return (NULL); + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = ISSET(m->m_pkthdr.csum_flags, M_FLOWID) ? + htonl(m->m_pkthdr.ph_flowid) : 0; + ip6->ip6_vfc |= IPV6_VERSION; + ip6->ip6_flow |= htonl((uint32_t)tos << 20); + ip6->ip6_plen = htons(len); + ip6->ip6_nxt = IPPROTO_UDP; + ip6->ip6_hlim = sc->sc_ttl; + ip6->ip6_src = sc->sc_src.in6; + ip6->ip6_dst = endpoint->in6; + + if (sc->sc_df) + SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT); + + return (m); +} +#endif /* INET6 */ + +static int +geneve_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + struct m_tag *mtag; + int error = 0; + + mtag = NULL; + while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { + if (memcmp((caddr_t)(mtag + 1), &ifp->if_index, + sizeof(ifp->if_index)) == 0) { + error = EIO; + goto drop; + } + } + + mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); + if (mtag == NULL) { + error = ENOBUFS; + goto drop; + } + memcpy((caddr_t)(mtag + 1), &ifp->if_index, sizeof(ifp->if_index)); + m_tag_prepend(m, mtag); + + return (ether_output(ifp, m, dst, rt)); + +drop: + m_freem(m); + return (error); +} + +static int +geneve_enqueue(struct ifnet *ifp, struct mbuf *m) +{ + struct geneve_softc *sc = ifp->if_softc; + struct ifqueue *ifq = &ifp->if_snd; + + if (ifq_enqueue(ifq, m) != 0) + return (ENOBUFS); + + task_add(ifq->ifq_softnet, &sc->sc_send_task); + + return (0); +} + +static void +geneve_start(struct ifqueue *ifq) +{ + struct ifnet *ifp = ifq->ifq_if; + struct geneve_softc *sc = ifp->if_softc; + + task_add(ifq->ifq_softnet, &sc->sc_send_task); +} + +static uint64_t +geneve_send_ipv4(struct geneve_softc *sc, struct mbuf_list *ml) +{ + struct ip_moptions imo; + struct mbuf *m; + uint64_t oerrors = 0; + + imo.imo_ifidx = sc->sc_if_index0; + imo.imo_ttl = sc->sc_ttl; + imo.imo_loop = 0; + + NET_LOCK(); + while ((m = ml_dequeue(ml)) != NULL) { + if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &imo, NULL, 0) != 0) + oerrors++; + } + NET_UNLOCK(); + + return (oerrors); +} + +#ifdef INET6 +static uint64_t +geneve_send_ipv6(struct geneve_softc *sc, struct mbuf_list *ml) +{ + struct ip6_moptions im6o; + struct mbuf *m; + uint64_t oerrors = 0; + + im6o.im6o_ifidx = sc->sc_if_index0; + im6o.im6o_hlim = sc->sc_ttl; + im6o.im6o_loop = 0; + + NET_LOCK(); + while ((m = ml_dequeue(ml)) != NULL) { + if (ip6_output(m, NULL, NULL, 0, &im6o, NULL) != 0) + oerrors++; + } + NET_UNLOCK(); + + return (oerrors); +} +#endif /* INET6 */ + +static void +geneve_send(void *arg) +{ + struct geneve_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mbuf *(*ip_encap)(struct geneve_softc *, struct mbuf *, + const union geneve_addr *, uint8_t); + uint64_t (*ip_send)(struct geneve_softc *, struct mbuf_list *); + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct mbuf *m; + uint64_t oerrors; + + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + return; + + switch (sc->sc_af) { + case AF_INET: + ip_encap = geneve_encap_ipv4; + ip_send = geneve_send_ipv4; + break; +#ifdef INET6 + case AF_INET6: + ip_encap = geneve_encap_ipv6; + ip_send = geneve_send_ipv6; + break; +#endif + default: + unhandled_af(sc->sc_af); + /* NOTREACHED */ + } + + while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) { +#if NBPFILTER > 0 + caddr_t if_bpf = READ_ONCE(ifp->if_bpf); + if (if_bpf != NULL) + bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT); +#endif + m = geneve_encap(sc, m, ip_encap); + if (m == NULL) + continue; + + ml_enqueue(&ml, m); + } + + oerrors = (*ip_send)(sc, &ml); + + counters_add(ifp->if_counters, ifc_oerrors, oerrors); +} + +static struct mbuf * +geneve_input(void *arg, struct mbuf *m, struct ip *ip, struct ip6_hdr *ip6, + void *uhp, int hlen) +{ + struct geneve_tep *gtep = arg; + struct geneve_peer key, *p; + struct udphdr *uh; + struct geneve_header *gnvh; + struct ether_header *eh; + int gnvhlen = hlen + sizeof(*gnvh); + int optlen; + struct mbuf *n; + int off; + in_port_t port; + struct ifnet *ifp; + struct geneve_softc *sc; + + if (m->m_pkthdr.len < gnvhlen) + goto drop; + + uh = uhp; + port = uh->uh_sport; + + memset(&key, 0, sizeof(key)); + key.p_mask = 0; + + if (ip != NULL) + key.p_addr.in4 = ip->ip_src; +#ifdef INET6 + else + key.p_addr.in6 = ip6->ip6_src; +#endif + + if (m->m_len < gnvhlen) { + m = m_pullup(m, gnvhlen); + if (m == NULL) + return (NULL); + } + + gnvh = (struct geneve_header *)(mtod(m, caddr_t) + hlen); + if ((gnvh->gnv_ver_optlen & GENEVE_VER_MASK) != GENEVE_VER_0) { + /* bad geneve version++ */ + goto drop; + } + + switch (gnvh->gnv_proto) { + case htons(ETHERTYPE_TRANSETHER): + key.p_header.gnv_proto = gnvh->gnv_proto; + break; + + /* layer 3 protocols are identified here */ + + default: + /* unsupported geneve protocol */ + goto drop; + } + key.p_header.gnv_vni = gnvh->gnv_vni & htonl(GENEVE_VNI_MASK); + + mtx_enter(>ep->gtep_mtx); + p = RBT_FIND(geneve_peers, >ep->gtep_peers, &key); + if (p != NULL) + ifp = if_get(p->p_if_index); + mtx_leave(>ep->gtep_mtx); + + if (ifp == NULL) { + /* no interface found */ + goto drop; + } + + if (ISSET(gnvh->gnv_flags, GENEVE_F_OAM)) { + /* we dont understand oam */ + goto rele_drop; + } + + if (ISSET(gnvh->gnv_flags, GENEVE_F_CRITICAL)) { + /* we dont understand critical options */ + goto rele_drop; + } + + if (ISSET(ifp->if_flags, IFF_LINK0) && port != sc->sc_port) + goto rele_drop; + + optlen = gnvh->gnv_ver_optlen & GENEVE_OPTLEN_MASK; + optlen *= GENEVE_OPTLEN_UNIT; + + m_adj(m, gnvhlen + optlen); + + if (m->m_pkthdr.len < sizeof(*eh)) { + /* short packet */ + goto rele_drop; + } + + if (m->m_len < sizeof(*eh)) { + m = m_pullup(m, sizeof(*eh)); + if (m == NULL) + goto rele; + } + + n = m_getptr(m, sizeof(*eh), &off); + if (n == NULL) + goto rele_drop; + + if (!ALIGNED_POINTER(mtod(n, caddr_t) + off, uint32_t)) { + n = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT); + m_freem(m); + if (n == NULL) + goto rele; + m = n; + } + + sc = ifp->if_softc; + if (sc->sc_mode == GENEVE_TMODE_LEARNING) { + eh = mtod(m, struct ether_header *); + etherbridge_map_ea(&sc->sc_eb, &key.p_addr, + (struct ether_addr *)eh->ether_shost); + } + + /* XXX prio */ + + if_vinput(ifp, m); +rele: + if_put(ifp); + return (NULL); + +rele_drop: + if_put(ifp); +drop: + m_freem(m); + return (NULL); +} + +static int +geneve_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct geneve_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifbrparam *bparam = (struct ifbrparam *)data; + int error = 0; + + switch (cmd) { + case SIOCSIFADDR: + break; + case SIOCSIFFLAGS: + if (ISSET(ifp->if_flags, IFF_UP)) { + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + error = geneve_up(sc); + else + error = 0; + } else { + if (ISSET(ifp->if_flags, IFF_RUNNING)) + error = geneve_down(sc); + } + break; + + case SIOCSLIFPHYRTABLE: + error = geneve_set_rdomain(sc, ifr); + break; + case SIOCGLIFPHYRTABLE: + error = geneve_get_rdomain(sc, ifr); + break; + + case SIOCSLIFPHYADDR: + error = geneve_set_tunnel(sc, (const struct if_laddrreq *)data); + break; + case SIOCGLIFPHYADDR: + error = geneve_get_tunnel(sc, (struct if_laddrreq *)data); + break; + case SIOCDIFPHYADDR: + error = geneve_del_tunnel(sc); + break; + + case SIOCSVNETID: + error = geneve_set_vnetid(sc, ifr); + break; + case SIOCGVNETID: + error = geneve_get_vnetid(sc, ifr); + break; + + case SIOCSIFPARENT: + error = geneve_set_parent(sc, (struct if_parent *)data); + break; + case SIOCGIFPARENT: + error = geneve_get_parent(sc, (struct if_parent *)data); + break; + case SIOCDIFPARENT: + error = geneve_del_parent(sc); + break; + + case SIOCSTXHPRIO: + error = if_txhprio_l2_check(ifr->ifr_hdrprio); + if (error != 0) + break; + + sc->sc_txhprio = ifr->ifr_hdrprio; + break; + case SIOCGTXHPRIO: + ifr->ifr_hdrprio = sc->sc_txhprio; + break; + + case SIOCSRXHPRIO: + error = if_rxhprio_l2_check(ifr->ifr_hdrprio); + if (error != 0) + break; + + sc->sc_rxhprio = ifr->ifr_hdrprio; + break; + case SIOCGRXHPRIO: + ifr->ifr_hdrprio = sc->sc_rxhprio; + break; + + case SIOCSLIFPHYDF: + /* commit */ + sc->sc_df = ifr->ifr_df ? htons(IP_DF) : htons(0); + break; + case SIOCGLIFPHYDF: + ifr->ifr_df = sc->sc_df ? 1 : 0; + break; + + case SIOCSLIFPHYTTL: + if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) { + error = EINVAL; + break; + } + + /* commit */ + sc->sc_ttl = (uint8_t)ifr->ifr_ttl; + break; + case SIOCGLIFPHYTTL: + ifr->ifr_ttl = (int)sc->sc_ttl; + break; + + case SIOCBRDGSCACHE: + error = etherbridge_set_max(&sc->sc_eb, bparam); + break; + case SIOCBRDGGCACHE: + error = etherbridge_get_max(&sc->sc_eb, bparam); + break; + case SIOCBRDGSTO: + error = etherbridge_set_tmo(&sc->sc_eb, bparam); + break; + case SIOCBRDGGTO: + error = etherbridge_get_tmo(&sc->sc_eb, bparam); + break; + + case SIOCBRDGRTS: + error = etherbridge_rtfind(&sc->sc_eb, + (struct ifbaconf *)data); + break; + case SIOCBRDGFLUSH: + etherbridge_flush(&sc->sc_eb, + ((struct ifbreq *)data)->ifbr_ifsflags); + break; + case SIOCBRDGSADDR: + error = geneve_add_addr(sc, (struct ifbareq *)data); + break; + case SIOCBRDGDADDR: + error = geneve_del_addr(sc, (struct ifbareq *)data); + break; + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + break; + } + + return (error); +} + +static struct geneve_tep * +geneve_tep_get(struct geneve_softc *sc, const union geneve_addr *addr) +{ + struct geneve_tep *gtep; + + TAILQ_FOREACH(gtep, &geneve_teps, gtep_entry) { + if (sc->sc_af == gtep->gtep_af && + sc->sc_rdomain == gtep->gtep_rdomain && + memcmp(addr, >ep->gtep_addr, sizeof(*addr)) == 0 && + sc->sc_port == gtep->gtep_port) + return (gtep); + } + + return (NULL); +} + +static int +geneve_tep_add_addr(struct geneve_softc *sc, const union geneve_addr *addr, + struct geneve_peer *p) +{ + struct mbuf m; + struct geneve_tep *gtep; + struct socket *so; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + int error; + int s; + + gtep = geneve_tep_get(sc, addr); + if (gtep != NULL) { + struct geneve_peer *op; + + mtx_enter(>ep->gtep_mtx); + op = RBT_INSERT(geneve_peers, >ep->gtep_peers, p); + mtx_leave(>ep->gtep_mtx); + + if (op != NULL) + return (EADDRINUSE); + + return (0); + } + + gtep = malloc(sizeof(*gtep), M_DEVBUF, M_NOWAIT|M_ZERO); + if (gtep == NULL) + return (ENOMEM); + + gtep->gtep_af = sc->sc_af; + gtep->gtep_rdomain = sc->sc_rdomain; + gtep->gtep_addr = *addr; + gtep->gtep_port = sc->sc_port; + + mtx_init(>ep->gtep_mtx, IPL_SOFTNET); + RBT_INIT(geneve_peers, >ep->gtep_peers); + RBT_INSERT(geneve_peers, >ep->gtep_peers, p); + + error = socreate(gtep->gtep_af, &so, SOCK_DGRAM, IPPROTO_UDP); + if (error != 0) + goto free; + + s = solock(so); + + sotoinpcb(so)->inp_upcall = geneve_input; + sotoinpcb(so)->inp_upcall_arg = gtep; + + m_inithdr(&m); + m.m_len = sizeof(gtep->gtep_rdomain); + *mtod(&m, unsigned int *) = gtep->gtep_rdomain; + error = sosetopt(so, SOL_SOCKET, SO_RTABLE, &m); + if (error != 0) + goto close; + + m_inithdr(&m); + switch (gtep->gtep_af) { + case AF_INET: + sin = mtod(&m, struct sockaddr_in *); + memset(sin, 0, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = addr->in4; + sin->sin_port = gtep->gtep_port; + + m.m_len = sizeof(*sin); + break; + +#ifdef INET6 + case AF_INET6: + sin6 = mtod(&m, struct sockaddr_in6 *); + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + in6_recoverscope(sin6, &addr->in6); + sin6->sin6_port = sc->sc_port; + + m.m_len = sizeof(*sin6); + break; +#endif + default: + unhandled_af(gtep->gtep_af); + } + + error = sobind(so, &m, curproc); + if (error != 0) + goto close; + + sounlock(so, s); + + rw_assert_wrlock(&geneve_lock); + TAILQ_INSERT_TAIL(&geneve_teps, gtep, gtep_entry); + + gtep->gtep_so = so; + + return (0); + +close: + sounlock(so, s); + soclose(so, MSG_DONTWAIT); +free: + free(gtep, M_DEVBUF, sizeof(*gtep)); + return (error); +} + +static void +geneve_tep_del_addr(struct geneve_softc *sc, const union geneve_addr *addr, + struct geneve_peer *p) +{ + struct geneve_tep *gtep; + int empty; + + gtep = geneve_tep_get(sc, addr); + if (gtep == NULL) + panic("unable to find geneve_tep for peer %p (sc %p)", p, sc); + + mtx_enter(>ep->gtep_mtx); + RBT_REMOVE(geneve_peers, >ep->gtep_peers, p); + empty = RBT_EMPTY(geneve_peers, >ep->gtep_peers); + mtx_leave(>ep->gtep_mtx); + + if (!empty) + return; + + rw_assert_wrlock(&geneve_lock); + TAILQ_REMOVE(&geneve_teps, gtep, gtep_entry); + + soclose(gtep->gtep_so, MSG_DONTWAIT); + free(gtep, M_DEVBUF, sizeof(*gtep)); +} + +static int +geneve_tep_up(struct geneve_softc *sc) +{ + struct geneve_peer *up, *mp; + int error; + + up = malloc(sizeof(*up), M_DEVBUF, M_NOWAIT|M_ZERO); + if (up == NULL) + return (ENOMEM); + + up->p_mask = (sc->sc_mode != GENEVE_TMODE_P2P); + up->p_addr = sc->sc_dst; + up->p_header = sc->sc_header; + up->p_if_index = sc->sc_ac.ac_if.if_index; + + error = geneve_tep_add_addr(sc, &sc->sc_src, up); + if (error != 0) + goto freeup; + + sc->sc_ucast_peer = up; + + if (sc->sc_mode != GENEVE_TMODE_LEARNING) + return (0); + + mp = malloc(sizeof(*mp), M_DEVBUF, M_NOWAIT|M_ZERO); + if (mp == NULL) { + error = ENOMEM; + goto delup; + } + + mp->p_mask = 1; + /* addr is masked, leave it as 0s */ + mp->p_header = sc->sc_header; + mp->p_if_index = sc->sc_ac.ac_if.if_index; + + /* destination address is a multicast group we want to join */ + error = geneve_tep_add_addr(sc, &sc->sc_dst, up); + if (error != 0) + goto freemp; + + sc->sc_mcast_peer = mp; + + return (0); + +freemp: + free(mp, M_DEVBUF, sizeof(*mp)); +delup: + geneve_tep_del_addr(sc, &sc->sc_src, up); +freeup: + free(up, M_DEVBUF, sizeof(*up)); + return (error); +} + +static void +geneve_tep_down(struct geneve_softc *sc) +{ + struct geneve_peer *up = sc->sc_ucast_peer; + + if (sc->sc_mode == GENEVE_TMODE_LEARNING) { + struct geneve_peer *mp = sc->sc_mcast_peer; + geneve_tep_del_addr(sc, &sc->sc_dst, mp); + free(mp, M_DEVBUF, sizeof(*mp)); + } + + geneve_tep_del_addr(sc, &sc->sc_src, up); + free(up, M_DEVBUF, sizeof(*up)); +} + +static int +geneve_up(struct geneve_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ifnet *ifp0 = NULL; + int error; + + KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING)); + NET_ASSERT_LOCKED(); + + if (sc->sc_af == AF_UNSPEC) + return (EDESTADDRREQ); + KASSERT(sc->sc_mode != GENEVE_TMODE_UNSET); + + NET_UNLOCK(); + + error = rw_enter(&geneve_lock, RW_WRITE|RW_INTR); + if (error != 0) + goto netlock; + + NET_LOCK(); + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + /* something else beat us */ + rw_exit(&geneve_lock); + return (0); + } + NET_UNLOCK(); + + if (sc->sc_mode != GENEVE_TMODE_P2P) { + error = etherbridge_up(&sc->sc_eb); + if (error != 0) + goto unlock; + } + + if (sc->sc_mode == GENEVE_TMODE_LEARNING) { + ifp0 = if_get(sc->sc_if_index0); + if (ifp0 == NULL) { + error = ENXIO; + goto down; + } + + /* check again if multicast will work on top of the parent */ + if (!ISSET(ifp0->if_flags, IFF_MULTICAST)) { + error = EPROTONOSUPPORT; + goto put; + } + + error = geneve_addmulti(sc, ifp0); + if (error != 0) + goto put; + + /* Register callback if parent wants to unregister */ + if_detachhook_add(ifp0, &sc->sc_dtask); + } else { + if (sc->sc_if_index0 != 0) { + error = EPROTONOSUPPORT; + goto down; + } + } + + error = geneve_tep_up(sc); + if (error != 0) + goto del; + + if_put(ifp0); + + NET_LOCK(); + SET(ifp->if_flags, IFF_RUNNING); + rw_exit(&geneve_lock); + + return (0); + +del: + if (ifp0 != NULL) + if_detachhook_del(ifp0, &sc->sc_dtask); + geneve_delmulti(sc); +put: + if_put(ifp0); +down: + if (sc->sc_mode != GENEVE_TMODE_P2P) + etherbridge_down(&sc->sc_eb); +unlock: + rw_exit(&geneve_lock); +netlock: + NET_LOCK(); + + return (error); +} + +static int +geneve_down(struct geneve_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ifnet *ifp0; + int error; + + KASSERT(ISSET(ifp->if_flags, IFF_RUNNING)); + NET_UNLOCK(); + + error = rw_enter(&geneve_lock, RW_WRITE|RW_INTR); + if (error != 0) { + NET_LOCK(); + return (error); + } + + NET_LOCK(); + if (!ISSET(ifp->if_flags, IFF_RUNNING)) { + /* something else beat us */ + rw_exit(&geneve_lock); + return (0); + } + NET_UNLOCK(); + + geneve_tep_down(sc); + + if (sc->sc_mode == GENEVE_TMODE_LEARNING) { + geneve_delmulti(sc); + ifp0 = if_get(sc->sc_if_index0); + if (ifp0 != NULL) { + if_detachhook_del(ifp0, &sc->sc_dtask); + } + if_put(ifp0); + } + + if (sc->sc_mode != GENEVE_TMODE_P2P) + etherbridge_down(&sc->sc_eb); + + taskq_del_barrier(ifp->if_snd.ifq_softnet, &sc->sc_send_task); + NET_LOCK(); + CLR(ifp->if_flags, IFF_RUNNING); + rw_exit(&geneve_lock); + + return (0); +} + +static int +geneve_addmulti(struct geneve_softc *sc, struct ifnet *ifp0) +{ + int error = 0; + + NET_LOCK(); + + switch (sc->sc_af) { + case AF_INET: + sc->sc_inmulti = in_addmulti(&sc->sc_dst.in4, ifp0); + if (sc->sc_inmulti == NULL) + error = EADDRNOTAVAIL; + break; +#ifdef INET6 + case AF_INET6: + sc->sc_inmulti = in6_addmulti(&sc->sc_dst.in6, ifp0, &error); + break; +#endif + default: + unhandled_af(sc->sc_af); + } + + NET_UNLOCK(); + + return (error); +} + +static void +geneve_delmulti(struct geneve_softc *sc) +{ + NET_LOCK(); + + switch (sc->sc_af) { + case AF_INET: + in_delmulti(sc->sc_inmulti); + break; +#ifdef INET6 + case AF_INET6: + in6_delmulti(sc->sc_inmulti); + break; +#endif + default: + unhandled_af(sc->sc_af); + } + + sc->sc_inmulti = NULL; /* keep it tidy */ + + NET_UNLOCK(); +} + +static int +geneve_set_rdomain(struct geneve_softc *sc, const struct ifreq *ifr) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + if (ifr->ifr_rdomainid < 0 || + ifr->ifr_rdomainid > RT_TABLEID_MAX) + return (EINVAL); + if (!rtable_exists(ifr->ifr_rdomainid)) + return (EADDRNOTAVAIL); + + if (sc->sc_rdomain == ifr->ifr_rdomainid) + return (0); + + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* commit */ + sc->sc_rdomain = ifr->ifr_rdomainid; + etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL); + + return (0); +} + +static int +geneve_get_rdomain(struct geneve_softc *sc, struct ifreq *ifr) +{ + ifr->ifr_rdomainid = sc->sc_rdomain; + + return (0); +} + +static int +geneve_set_tunnel(struct geneve_softc *sc, const struct if_laddrreq *req) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct sockaddr *src = (struct sockaddr *)&req->addr; + struct sockaddr *dst = (struct sockaddr *)&req->dstaddr; + struct sockaddr_in *src4, *dst4; +#ifdef INET6 + struct sockaddr_in6 *src6, *dst6; + int error; +#endif + union geneve_addr saddr, daddr; + unsigned int mode = GENEVE_TMODE_ENDPOINT; + in_port_t port = htons(GENEVE_PORT); + + memset(&saddr, 0, sizeof(saddr)); + memset(&daddr, 0, sizeof(daddr)); + + /* validate */ + switch (src->sa_family) { + case AF_INET: + src4 = (struct sockaddr_in *)src; + if (in_nullhost(src4->sin_addr) || + IN_MULTICAST(src4->sin_addr.s_addr)) + return (EINVAL); + + if (src4->sin_port != htons(0)) + port = src4->sin_port; + + if (dst->sa_family != AF_UNSPEC) { + if (dst->sa_family != AF_INET) + return (EINVAL); + + dst4 = (struct sockaddr_in *)dst; + if (in_nullhost(dst4->sin_addr)) + return (EINVAL); + + /* all good */ + mode = IN_MULTICAST(dst4->sin_addr.s_addr) ? + GENEVE_TMODE_LEARNING : GENEVE_TMODE_P2P; + daddr.in4 = dst4->sin_addr; + } + + saddr.in4 = src4->sin_addr; + break; + +#ifdef INET6 + case AF_INET6: + src6 = (struct sockaddr_in6 *)src; + if (IN6_IS_ADDR_UNSPECIFIED(&src6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&src6->sin6_addr)) + return (EINVAL); + + if (src6->sin6_port != htons(0)) + port = src6->sin6_port; + + if (dst->sa_family != AF_UNSPEC) { + if (dst->sa_family != AF_INET6) + return (EINVAL); + + dst6 = (struct sockaddr_in6 *)dst; + if (IN6_IS_ADDR_UNSPECIFIED(&dst6->sin6_addr)) + return (EINVAL); + + if (src6->sin6_scope_id != dst6->sin6_scope_id) + return (EINVAL); + + /* all good */ + mode = IN6_IS_ADDR_MULTICAST(&dst6->sin6_addr) ? + GENEVE_TMODE_LEARNING : GENEVE_TMODE_P2P; + error = in6_embedscope(&daddr.in6, dst6, NULL); + if (error != 0) + return (error); + } + + error = in6_embedscope(&saddr.in6, src6, NULL); + if (error != 0) + return (error); + + break; +#endif + default: + return (EAFNOSUPPORT); + } + + if (memcmp(&sc->sc_src, &saddr, sizeof(sc->sc_src)) == 0 && + memcmp(&sc->sc_dst, &daddr, sizeof(sc->sc_dst)) == 0 && + sc->sc_port == port) + return (0); + + if (ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* commit */ + sc->sc_af = src->sa_family; + sc->sc_src = saddr; + sc->sc_dst = daddr; + sc->sc_port = port; + sc->sc_mode = mode; + etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL); + + return (0); +} + +static int +geneve_get_tunnel(struct geneve_softc *sc, struct if_laddrreq *req) +{ + struct sockaddr *dstaddr = (struct sockaddr *)&req->dstaddr; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + if (sc->sc_af == AF_UNSPEC) + return (EADDRNOTAVAIL); + KASSERT(sc->sc_mode != GENEVE_TMODE_UNSET); + + memset(&req->addr, 0, sizeof(req->addr)); + memset(&req->dstaddr, 0, sizeof(req->dstaddr)); + + /* default to endpoint */ + dstaddr->sa_len = 2; + dstaddr->sa_family = AF_UNSPEC; + + switch (sc->sc_af) { + case AF_INET: + sin = (struct sockaddr_in *)&req->addr; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = sc->sc_src.in4; + sin->sin_port = sc->sc_port; + + if (sc->sc_mode == GENEVE_TMODE_ENDPOINT) + break; + + sin = (struct sockaddr_in *)&req->dstaddr; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = sc->sc_dst.in4; + break; + +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&req->addr; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + in6_recoverscope(sin6, &sc->sc_src.in6); + sin6->sin6_port = sc->sc_port; + + if (sc->sc_mode == GENEVE_TMODE_ENDPOINT) + break; + + sin6 = (struct sockaddr_in6 *)&req->dstaddr; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + in6_recoverscope(sin6, &sc->sc_dst.in6); + break; +#endif + default: + unhandled_af(sc->sc_af); + } + + return (0); +} + +static int +geneve_del_tunnel(struct geneve_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + if (sc->sc_af == AF_UNSPEC) + return (0); + + if (ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* commit */ + sc->sc_af = AF_UNSPEC; + memset(&sc->sc_src, 0, sizeof(sc->sc_src)); + memset(&sc->sc_dst, 0, sizeof(sc->sc_dst)); + sc->sc_port = htons(0); + sc->sc_mode = GENEVE_TMODE_UNSET; + etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL); + + return (0); +} + +static int +geneve_set_vnetid(struct geneve_softc *sc, const struct ifreq *ifr) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t vni; + + if (ifr->ifr_vnetid < GENEVE_VNI_MIN || + ifr->ifr_vnetid > GENEVE_VNI_MAX) + return (EINVAL); + + vni = htonl(ifr->ifr_vnetid << GENEVE_VNI_SHIFT); + if (sc->sc_header.gnv_vni == vni) + return (0); + + if (ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* commit */ + sc->sc_header.gnv_vni = vni; + etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL); + + return (0); +} + +static int +geneve_get_vnetid(struct geneve_softc *sc, struct ifreq *ifr) +{ + uint32_t vni; + + vni = ntohl(sc->sc_header.gnv_vni); + vni &= GENEVE_VNI_MASK; + vni >>= GENEVE_VNI_SHIFT; + + ifr->ifr_vnetid = vni; + + return (0); +} + +static int +geneve_set_parent(struct geneve_softc *sc, const struct if_parent *p) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ifnet *ifp0; + int error = 0; + + ifp0 = if_unit(p->ifp_parent); + if (ifp0 == NULL) + return (ENXIO); + + if (!ISSET(ifp0->if_flags, IFF_MULTICAST)) { + error = ENXIO; + goto put; + } + + if (sc->sc_if_index0 == ifp0->if_index) + goto put; + + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + error = EBUSY; + goto put; + } + + /* commit */ + sc->sc_if_index0 = ifp0->if_index; + etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL); + +put: + if_put(ifp0); + return (error); +} + +static int +geneve_get_parent(struct geneve_softc *sc, struct if_parent *p) +{ + struct ifnet *ifp0; + int error = 0; + + ifp0 = if_get(sc->sc_if_index0); + if (ifp0 == NULL) + error = EADDRNOTAVAIL; + else + strlcpy(p->ifp_parent, ifp0->if_xname, sizeof(p->ifp_parent)); + if_put(ifp0); + + return (error); +} + +static int +geneve_del_parent(struct geneve_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + if (sc->sc_if_index0 == 0) + return (0); + + if (ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* commit */ + sc->sc_if_index0 = 0; + etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL); + + return (0); +} + +static int +geneve_add_addr(struct geneve_softc *sc, const struct ifbareq *ifba) +{ + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; + struct sockaddr_in6 src6 = { + .sin6_len = sizeof(src6), + .sin6_family = AF_UNSPEC, + }; + int error; +#endif + union geneve_addr endpoint; + unsigned int type; + + switch (sc->sc_mode) { + case GENEVE_TMODE_UNSET: + return (ENOPROTOOPT); + case GENEVE_TMODE_P2P: + return (EPROTONOSUPPORT); + default: + break; + } + + /* ignore ifba_ifsname */ + + if (ISSET(ifba->ifba_flags, ~IFBAF_TYPEMASK)) + return (EINVAL); + switch (ifba->ifba_flags & IFBAF_TYPEMASK) { + case IFBAF_DYNAMIC: + type = EBE_DYNAMIC; + break; + case IFBAF_STATIC: + type = EBE_STATIC; + break; + default: + return (EINVAL); + } + + memset(&endpoint, 0, sizeof(endpoint)); + + if (ifba->ifba_dstsa.ss_family != sc->sc_af) + return (EAFNOSUPPORT); + switch (ifba->ifba_dstsa.ss_family) { + case AF_INET: + sin = (struct sockaddr_in *)&ifba->ifba_dstsa; + if (in_nullhost(sin->sin_addr) || + IN_MULTICAST(sin->sin_addr.s_addr)) + return (EADDRNOTAVAIL); + + if (sin->sin_port != htons(0)) + return (EADDRNOTAVAIL); + + endpoint.in4 = sin->sin_addr; + break; + +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&ifba->ifba_dstsa; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + return (EADDRNOTAVAIL); + + in6_recoverscope(&src6, &sc->sc_src.in6); + if (src6.sin6_scope_id != sin6->sin6_scope_id) + return (EADDRNOTAVAIL); + + if (sin6->sin6_port != htons(0)) + return (EADDRNOTAVAIL); + + error = in6_embedscope(&endpoint.in6, sin6, NULL); + if (error != 0) + return (error); + + break; +#endif + default: /* AF_UNSPEC */ + return (EADDRNOTAVAIL); + } + + return (etherbridge_add_addr(&sc->sc_eb, &endpoint, + &ifba->ifba_dst, type)); +} + +static int +geneve_del_addr(struct geneve_softc *sc, const struct ifbareq *ifba) +{ + return (etherbridge_del_addr(&sc->sc_eb, &ifba->ifba_dst)); +} + +void +geneve_detach_hook(void *arg) +{ + struct geneve_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + geneve_down(sc); + CLR(ifp->if_flags, IFF_UP); + } + + sc->sc_if_index0 = 0; +} + +static int +geneve_eb_port_eq(void *arg, void *a, void *b) +{ + const union geneve_addr *va = a, *vb = b; + size_t i; + + for (i = 0; i < nitems(va->in6.s6_addr32); i++) { + if (va->in6.s6_addr32[i] != vb->in6.s6_addr32[i]) + return (0); + } + + return (1); +} + +static void * +geneve_eb_port_take(void *arg, void *port) +{ + union geneve_addr *endpoint; + + endpoint = pool_get(&geneve_endpoint_pool, PR_NOWAIT); + if (endpoint == NULL) + return (NULL); + + *endpoint = *(union geneve_addr *)port; + + return (endpoint); +} + +static void +geneve_eb_port_rele(void *arg, void *port) +{ + union geneve_addr *endpoint = port; + + pool_put(&geneve_endpoint_pool, endpoint); +} + +static size_t +geneve_eb_port_ifname(void *arg, char *dst, size_t len, void *port) +{ + struct geneve_softc *sc = arg; + + return (strlcpy(dst, sc->sc_ac.ac_if.if_xname, len)); +} + +static void +geneve_eb_port_sa(void *arg, struct sockaddr_storage *ss, void *port) +{ + struct geneve_softc *sc = arg; + union geneve_addr *endpoint = port; + + switch (sc->sc_af) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)ss; + + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = endpoint->in4; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; + + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + in6_recoverscope(sin6, &endpoint->in6); + break; + } +#endif /* INET6 */ + default: + unhandled_af(sc->sc_af); + } +} + +static inline int +geneve_peer_cmp(const struct geneve_peer *ap, const struct geneve_peer *bp) +{ + const unsigned long *la, *lb; + size_t i; + + la = (const unsigned long *)&ap->p_header; + lb = (const unsigned long *)&bp->p_header; + for (i = 0; i < sizeof(ap->p_header) / sizeof(*la); i++) { + if (la[i] > lb[i]) + return (1); + if (la[i] < lb[i]) + return (-1); + } + + if (ap->p_mask || bp->p_mask) + return (0); + + for (i = 0; i < nitems(ap->p_addr.in6.s6_addr32); i++) { + if (ap->p_addr.in6.s6_addr32[i] > + bp->p_addr.in6.s6_addr32[i]) + return (1); + if (ap->p_addr.in6.s6_addr32[i] < + bp->p_addr.in6.s6_addr32[i]) + return (-1); + } + + return (0); +} + +RBT_GENERATE(geneve_peers, geneve_peer, p_entry, geneve_peer_cmp);