Index: net/if_rport.c =================================================================== RCS file: net/if_rport.c diff -N net/if_rport.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ net/if_rport.c 4 Jan 2023 06:41:37 -0000 @@ -0,0 +1,475 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#include +#include +#endif /* INET6 */ + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include +#endif + +#ifdef MPLS +#include +#endif + +#include "pf.h" +#if NPF > 0 +#include +#endif + +#define RPORT_MTU_MIN 1280 +#define RPORT_MTU_MAX 32768 /* LOMTU, but could be higher */ +#define RPORT_MTU_DEFAULT RPORT_MTU_MAX + +struct rport_softc { + struct ifnet sc_if; + + unsigned int sc_peer_idx; + struct task sc_peer_ltask; + struct task sc_peer_dtask; +}; + +static int rport_clone_create(struct if_clone *, int); +static int rport_clone_destroy(struct ifnet *); + +static int rport_ioctl(struct ifnet *, u_long, caddr_t); +static int rport_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +static int rport_enqueue(struct ifnet *, struct mbuf *); +static void rport_start(struct ifqueue *); +static void rport_input(struct ifnet *, struct mbuf *); + +static int rport_up(struct rport_softc *); +static int rport_down(struct rport_softc *); + +static struct if_clone rport_cloner = + IF_CLONE_INITIALIZER("rport", rport_clone_create, rport_clone_destroy); + +static struct rwlock rport_interfaces_lock = + RWLOCK_INITIALIZER("rports"); + +void +rportattach(int count) +{ + if_clone_attach(&rport_cloner); +} + +static int +rport_clone_create(struct if_clone *ifc, int unit) +{ + struct rport_softc *sc; + struct ifnet *ifp; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); + ifp = &sc->sc_if; + + snprintf(ifp->if_xname, sizeof(ifp->if_xname), + "%s%d", ifc->ifc_name, unit); + + ifp->if_mtu = RPORT_MTU_DEFAULT; + ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; + ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; + ifp->if_ioctl = rport_ioctl; + ifp->if_bpf_mtap = p2p_bpf_mtap; + ifp->if_output = rport_output; + ifp->if_enqueue = rport_enqueue; + ifp->if_qstart = rport_start; + ifp->if_input = rport_input; + ifp->if_rtrequest = p2p_rtrequest; + ifp->if_type = IFT_TUNNEL; + ifp->if_softc = sc; + + if_attach(ifp); + if_alloc_sadl(ifp); + if_counters_alloc(ifp); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t)); +#endif + + return (0); +} + +int +rport_clone_destroy(struct ifnet *ifp) +{ + struct rport_softc *sc = ifp->if_softc; + + NET_LOCK(); + if (ISSET(ifp->if_flags, IFF_RUNNING)) + rport_down(sc); + NET_UNLOCK(); + + if_detach(ifp); + + free(sc, M_DEVBUF, sizeof(*sc)); + + return (0); +} + +static int +rport_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + struct m_tag *mtag; + int error = 0; + + if (!ISSET(ifp->if_flags, IFF_RUNNING)) { + error = ENETDOWN; + goto drop; + } + + switch (dst->sa_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif +#ifdef MPLS + case AF_MPLS: +#endif + break; + default: + error = EAFNOSUPPORT; + goto drop; + } + + /* Try to limit infinite recursion through misconfiguration. */ + mtag = NULL; + while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { + if (*(int *)(mtag + 1) == ifp->if_index) { + error = EIO; + goto drop; + } + } + + mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); + if (mtag == NULL) { + error = ENOBUFS; + goto drop; + } + *(int *)(mtag + 1) = ifp->if_index; + m_tag_prepend(m, mtag); + + m->m_flags &= ~(M_BCAST|M_MCAST); + m->m_pkthdr.ph_family = dst->sa_family; +#if NPF > 0 + pf_pkt_addr_changed(m); +#endif + + error = if_enqueue(ifp, m); + if (error) + counters_inc(ifp->if_counters, ifc_oerrors); + + return (error); + +drop: + m_freem(m); + return (error); +} + +static int +rport_enqueue(struct ifnet *ifp, struct mbuf *m) +{ + struct rport_softc *sc; + struct ifnet *ifp0; +#if NBPFILTER > 0 + caddr_t if_bpf; +#endif + int error = 0; + + if (!ifq_is_priq(&ifp->if_snd)) + return (if_enqueue_ifq(ifp, m)); + + sc = ifp->if_softc; + ifp0 = if_get(sc->sc_peer_idx); + if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) { + m_freem(m); + error = ENETDOWN; + } else { + counters_pkt(ifp->if_counters, + ifc_opackets, ifc_obytes, m->m_pkthdr.len); + +#if NBPFILTER > 0 + if_bpf = READ_ONCE(ifp->if_bpf); + if (if_bpf && bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, + m, BPF_DIRECTION_OUT)) + m_freem(m); + else +#endif + ifiq_enqueue(&ifp0->if_rcv, m); + } + if_put(ifp0); + + return (error); +} + +static void +rport_start(struct ifqueue *ifq) +{ + struct ifnet *ifp = ifq->ifq_if; + struct rport_softc *sc = ifp->if_softc; + struct ifnet *ifp0; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct mbuf *m; +#if NBPFILTER > 0 + caddr_t if_bpf; +#endif + + ifp0 = if_get(sc->sc_peer_idx); + if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) { + ifq_purge(ifq); + if_put(ifp0); + return; + } + + while ((m = ifq_dequeue(ifq)) != NULL) { +#if NBPFILTER > 0 + if_bpf = READ_ONCE(ifp->if_bpf); + if (if_bpf && bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, + m, BPF_DIRECTION_OUT)) { + m_freem(m); + continue; + } +#endif + + ml_enqueue(&ml, m); + } + + ifiq_input(&ifp0->if_rcv, &ml); + + if_put(ifp0); +} + +static void +rport_input(struct ifnet *ifp, struct mbuf *m) +{ + switch (m->m_pkthdr.ph_family) { + case AF_INET: + ipv4_input(ifp, m); + break; +#ifdef INET6 + case AF_INET6: + ipv6_input(ifp, m); + break; +#endif +#ifdef MPLS + case AF_MPLS: + mpls_input(ifp, m); + break; +#endif + default: + counters_inc(ifp->if_counters, ifc_noproto); + m_freem(m); + break; + } +} + +static int +rport_up(struct rport_softc *sc) +{ + NET_ASSERT_LOCKED(); + + SET(sc->sc_if.if_flags, IFF_RUNNING); + + return (0); +} + +static int +rport_down(struct rport_softc *sc) +{ + NET_ASSERT_LOCKED(); + + CLR(sc->sc_if.if_flags, IFF_RUNNING); + + /* barrier? */ + + return (0); +} + +static int +rport_set_parent(struct rport_softc *sc, const struct if_parent *p) +{ + struct ifnet *ifp = &sc->sc_if; + struct ifnet *ifp0; + struct rport_softc *sc0; + int error; + + error = rw_enter(&rport_interfaces_lock, RW_WRITE | RW_INTR); + if (error != 0) + return (error); + + ifp0 = if_unit(p->ifp_parent); + if (ifp0 == NULL) { + error = EINVAL; + goto leave; + } + + if (ifp0->if_input != rport_input) { + error = EPROTONOSUPPORT; + goto put; + } + + if (ifp0 == ifp) { + error = EINVAL; + goto leave; + } + + sc0 = ifp0->if_softc; + + if (sc->sc_peer_idx == ifp0->if_index) { + /* nop */ + KASSERT(sc0->sc_peer_idx == ifp->if_index); + goto put; + } + + if (sc0->sc_peer_idx != 0) { + error = EBUSY; + goto put; + } + + /* commit */ + sc->sc_peer_idx = ifp0->if_index; + sc0->sc_peer_idx = ifp->if_index; + +put: + if_put(ifp0); +leave: + rw_exit(&rport_interfaces_lock); + + return (error); +} + +static int +rport_get_parent(struct rport_softc *sc, struct if_parent *p) +{ + struct ifnet *ifp0; + int error = 0; + + ifp0 = if_get(sc->sc_peer_idx); + if (ifp0 == NULL) + error = EADDRNOTAVAIL; + else { + if (strlcpy(p->ifp_parent, ifp0->if_xname, + sizeof(p->ifp_parent)) >= sizeof(p->ifp_parent)) + panic("%s strlcpy", __func__); + } + if_put(ifp0); + + return (error); +} + +static int +rport_del_parent(struct rport_softc *sc) +{ + struct rport_softc *sc0; + struct ifnet *ifp0; + int error; + + error = rw_enter(&rport_interfaces_lock, RW_WRITE | RW_INTR); + if (error != 0) + return (error); + + ifp0 = if_get(sc->sc_peer_idx); + sc->sc_peer_idx = 0; + + if (ifp0 != NULL) { + sc0 = ifp0->if_softc; + sc0->sc_peer_idx = 0; + } + if_put(ifp0); + + rw_exit(&rport_interfaces_lock); + + return (0); +} + +static int +rport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct rport_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)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 = rport_up(sc); + } else { + if (ISSET(ifp->if_flags, IFF_RUNNING)) + error = rport_down(sc); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu < RPORT_MTU_MIN || + ifr->ifr_mtu > RPORT_MTU_MAX) { + error = EINVAL; + break; + } + + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFPARENT: + error = rport_set_parent(sc, (struct if_parent *)data); + break; + case SIOCGIFPARENT: + error = rport_get_parent(sc, (struct if_parent *)data); + break; + case SIOCDIFPARENT: + error = rport_del_parent(sc); + break; + + default: + error = ENOTTY; + break; + } + + return (error); +} Index: conf/GENERIC =================================================================== RCS file: /cvs/src/sys/conf/GENERIC,v retrieving revision 1.286 diff -u -p -r1.286 GENERIC --- conf/GENERIC 30 Sep 2022 02:56:23 -0000 1.286 +++ conf/GENERIC 4 Jan 2023 06:41:37 -0000 @@ -96,6 +96,7 @@ pseudo-device mpe # MPLS PE interface pseudo-device mpw # MPLS pseudowire support pseudo-device mpip # MPLS IP Layer2 pseudowire support pseudo-device bpe # Provider Backbone Bridge edge interface +pseudo-device rport # rdomain port interface pseudo-device pair # Virtual Ethernet interface pair pseudo-device ppp # PPP pseudo-device pppoe # PPP over Ethernet (RFC 2516) Index: conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.720 diff -u -p -r1.720 files --- conf/files 22 Dec 2022 05:59:26 -0000 1.720 +++ conf/files 4 Jan 2023 06:41:37 -0000 @@ -547,6 +547,7 @@ pseudo-device msts: tty pseudo-device endrun: tty pseudo-device loop: ifnet +pseudo-device rport: ifnet pseudo-device pair: ifnet, ether pseudo-device ppp: ifnet pseudo-device tun: ifnet @@ -833,6 +834,7 @@ file net/if_mpw.c mpw file net/if_mpip.c mpip file net/if_bpe.c bpe needs-count file net/if_vether.c vether +file net/if_rport.c rport file net/if_pair.c pair file net/if_pppx.c pppx needs-count file net/if_vxlan.c vxlan needs-count