Loving it!

A nit: s/Etherent/Ethernet

El mié., 19 dic. 2018 a las 5:25, David Gwynne (<da...@gwynne.id.au>) escribió:
>
> 802.1ah-2008 developed Provider Backbone Bridges, aka, mac-in-mac
> support. This was adopted as part of 802.1Q-2011.
>
> It basically provides Ethernet over Ethernet overlay networking. Unlike
> vlan and svlan, the entire ethernet packet is encapsulated in another.
> The motivation for this is to avoid the need for intermediate switches
> to learn all the "customer" mac addresses, they just need to know about
> the PBB endpoints.
>
> However, like vlan it does have a concept of a vnetid, and has the
> ability to store the packet priority. the vnetid is 24 bits and doesn't
> appear to have any reserved values.
>
> Thoughts?
>
> Index: share/man/man4/Makefile
> ===================================================================
> RCS file: /cvs/src/share/man/man4/Makefile,v
> retrieving revision 1.697
> diff -u -p -r1.697 Makefile
> --- share/man/man4/Makefile     23 Nov 2018 12:38:44 -0000      1.697
> +++ share/man/man4/Makefile     19 Dec 2018 04:16:07 -0000
> @@ -15,7 +15,7 @@ MAN=  aac.4 ac97.4 acphy.4 acrtc.4 \
>         auacer.4 audio.4 aue.4 auglx.4 auich.4 auixp.4 autri.4 auvia.4 \
>         axe.4 axen.4 axppmic.4 azalia.4 \
>         bce.4 bcmaux.4 bcmdog.4  bcmrng.4 bcmtemp.4 berkwdt.4 bge.4 \
> -       bgw.4 bio.4 bktr.4 bmtphy.4 bnx.4 bnxt.4 \
> +       bgw.4 bio.4 bpe.4 bktr.4 bmtphy.4 bnx.4 bnxt.4 \
>         boca.4 bpf.4 brgphy.4 bridge.4 brswphy.4 bwfm.4 bwi.4 bytgpio.4 \
>         cac.4 cas.4 cardbus.4 carp.4 ccp.4 ccpmic.4 cd.4 cdce.4 cfxga.4 \
>         ch.4 chvgpio.4 ciphy.4 ciss.4 clcs.4 clct.4 cmpci.4 \
> Index: share/man/man4/bpe.4
> ===================================================================
> RCS file: share/man/man4/bpe.4
> diff -N share/man/man4/bpe.4
> --- /dev/null   1 Jan 1970 00:00:00 -0000
> +++ share/man/man4/bpe.4        19 Dec 2018 04:16:07 -0000
> @@ -0,0 +1,159 @@
> +.\" $OpenBSD$
> +.\"
> +.\" Copyright (c) 2018 David Gwynne <d...@openbsd.org>
> +.\"
> +.\" 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.
> +.\"
> +.Dd $Mdocdate: November 16 2018 $
> +.Dt BPE 4
> +.Os
> +.Sh NAME
> +.Nm bpe
> +.Nd Backbone Provider Edge pseudo-device
> +.Sh SYNOPSIS
> +.Cd "pseudo-device bpe"
> +.Sh DESCRIPTION
> +The
> +.Nm bpe
> +driver allows construction of IEEE 802.1Q Provider Backbone Bridge
> +(PBB) networks by acting as a Backbone Edge Bridge (BEB).
> +PBB, also known as mac-in-mac, was originally specified in
> +IEEE 802.1ah-2008 and became part of IEEE 802.1Q-2011.
> +.Pp
> +A Provider Backbone Bridge Network (PBBN) consists of BEBs
> +interconnected by Backbone Core Bridges (BCBs) to form an Ethernet
> +network for the transport of encapsulated Ethernet packets.
> +Where VLAN and SVLAN protocols add a shim to differentiate Ethernet
> +packets for different networks but retain the Ethernet addresses
> +of encapsulated traffic, PBB completely encapsulates Ethernet packets
> +for transmission between BEBs on a PBBN.
> +This removes the need for intermediate BCB devices on the backbone
> +network  to learn the Ethernet addresses of devices on the encapsulated
> +network, but requires each BEB to maintain a mapping of addresses
> +on the encapsulated network to peer BEBs.
> +.Pp
> +A PBB packet consists of another Ethernet frame containing Ethernet
> +addresses for BEBs and the PBB Ethernet protocol type (0x88e7), a
> +32-bit Backbone Service Instance Tag (I-TAG), followed by the
> +encapsulated Ethernet frame.
> +The I-TAG contains a 24-bit Backbone Service Instance Identifiier
> +(I-SID) to differentiate different PBBNs on the same backbone network
> +.Pp
> +IEEE 802.1Q describes Customer VLANs being encapsulated by PBB,
> +which in turn uses an S-VLAN service.
> +This can be implemented with
> +.Xr vlan 4
> +using a
> +.Nm bpe
> +interface as the parent,
> +and with the
> +.Nm bpe
> +interface using
> +.Xr svlan 4
> +as the parent.
> +.Nm bpe
> +itself does not require this topology, therefore allowing flexible
> +deployment and network topologies.
> +.Pp
> +The
> +Nm. bpe
> +driver implements a learning bridge on each interface.
> +The driver will learn the mapping of BEPs to encapsulated Ethernet
> +address based on traffic received from other devices on the backbone
> +network.
> +Traffic sent to broadcast, multicast, or unknown unicast Etherent
> +addresses will be flooded to a multicast address on the backbone network.
> +The multicast address used for each PBB Service Instance
> +will begin with 01:1e:83 as the first three octets, with the I-SID
> +as the last three octets, e.g., a
> +.Nm bpe
> +interface with a vnetid of 1024 (0x400 in hex) will have a multicast
> +group address of 01:1e:83:00:04:00.
> +The address learning in
> +.Nm bpe
> +only uses the Ethernet addresses of encapsulated traffic for it's
> +forwarding decisions, it does not use VLAN or S-VLAN tags to
> +differentiate services.
> +.Pp
> +.Nm bpe
> +interfaces can be created at runtime using the
> +.Ic ifconfig bpe Ns Ar N Ic create
> +command or by setting up a
> +.Xr hostname.if 5
> +configuration file for
> +.Xr netstart 8 .
> +The interface itself can be configured with
> +.Xr ifconfig 8 ;
> +see its manual page for more information.
> +.Pp
> +.Nm bpe
> +interfaces must be configured with a parent Ethernet interface to
> +operate, and a virtual network identifier for use as the I-SID.
> +.Pp
> +The I-TAG includes a priority field.
> +By default, the value of the priority field in a transmitted packet
> +is based on the priority of packets sent over the interface, which
> +may be altered via
> +.Xr pf.conf 5 ;
> +see the
> +.Cm prio
> +option for more information.
> +Alternatively,
> +.Cm txprio
> +can set a specific priority for transmitted packets.
> +.Pp
> +.Nm bpe
> +interfaces support the following
> +.Xr ioctl 2 Ns s :
> +.Bl -tag -width indent -offset 3n
> +.It Dv SIOCSIFPARENT Fa "struct if_parent *"
> +Set the parent interface.
> +The parent may only be configured while the virtual interface is
> +administratively down.
> +.It Dv SIOCGIFPARENT Fa "struct if_parent *"
> +Get the currently configured parent interface.
> +.It Dv SIOCDIFPARENT Fa "struct ifreq *"
> +Delete the parent interface configuration.
> +The parent may only be removed while the virtual interface is
> +administratively down.
> +.It Dv SIOCSVNETID Fa "struct ifreq *"
> +Set the virtual network identifier.
> +Valid identifiers are 24-bit values in the range 0 to 16777215.
> +.It Dv SIOCGVNETID Fa "struct if_parent *"
> +Get the currently configured virtual network identifier.
> +.El
> +.Sh SEE ALSO
> +.Xr bridge 4 ,
> +.Xr inet 4 ,
> +.Xr ip 4 ,
> +.Xr netintro 4 ,
> +.Xr vlan 4 ,
> +.Xr hostname.if 5 ,
> +.Xr pf.conf 5 ,
> +.Xr ifconfig 8 ,
> +.Xr netstart 8
> +.Rs
> +.%T IEEE 802.1Q standard
> +.%U http://standards.ieee.org/getieee802/802.1.html
> +.Re
> +.Rs
> +.%Q Provider Backbone Bridges
> +.%T IEEE 802.1ah standard
> +.Re
> +.Sh HISTORY
> +The
> +.Nm
> +driver first appeared in
> +.Ox 6.6 .
> +.Sh AUTHORS
> +.An David Gwynne Aq Mt d...@openbsd.org .
> Index: sys/net/if_bpe.c
> ===================================================================
> RCS file: sys/net/if_bpe.c
> diff -N sys/net/if_bpe.c
> --- /dev/null   1 Jan 1970 00:00:00 -0000
> +++ sys/net/if_bpe.c    19 Dec 2018 04:16:07 -0000
> @@ -0,0 +1,1016 @@
> +/*     $OpenBSD$ */
> +/*
> + * Copyright (c) 2018 David Gwynne <d...@openbsd.org>
> + *
> + * 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 <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/mbuf.h>
> +#include <sys/socket.h>
> +#include <sys/ioctl.h>
> +#include <sys/timeout.h>
> +#include <sys/pool.h>
> +#include <sys/tree.h>
> +
> +#include <net/if.h>
> +#include <net/if_var.h>
> +#include <net/if_dl.h>
> +#include <net/if_media.h>
> +#include <net/if_types.h>
> +#include <net/rtable.h>
> +
> +#include <netinet/in.h>
> +#include <netinet/if_ether.h>
> +
> +/* for bridge stuff */
> +#include <net/if_bridge.h>
> +
> +
> +#if NBPFILTER > 0
> +#include <net/bpf.h>
> +#endif
> +
> +#include <net/if_bpe.h>
> +
> +#define PBB_ITAG_ISID          0x00ffffff
> +#define PBB_ITAG_ISID_MIN      0x00000000
> +#define PBB_ITAG_ISID_MAX      0x00ffffff
> +#define PBB_ITAG_RES2          0x03000000      /* must be zero on input */
> +#define PBB_ITAG_RES1          0x04000000      /* ignore on input */
> +#define PBB_ITAG_UCA           0x08000000
> +#define PBB_ITAG_DEI           0x10000000
> +#define PBB_ITAG_PCP_SHIFT     29
> +#define PBB_ITAG_PCP_MASK      (0x7U << PBB_ITAG_PCP_SHIFT)
> +
> +#define BPE_BRIDGE_AGE_TMO     100 /* seconds */
> +
> +struct bpe_key {
> +       int                     k_if;
> +       uint32_t                k_isid;
> +
> +       RBT_ENTRY(bpe_tunnel)   k_entry;
> +};
> +
> +RBT_HEAD(bpe_tree, bpe_key);
> +
> +static inline int bpe_cmp(const struct bpe_key *, const struct bpe_key *);
> +
> +RBT_PROTOTYPE(bpe_tree, bpe_key, k_entry, bpe_cmp);
> +RBT_GENERATE(bpe_tree, bpe_key, k_entry, bpe_cmp);
> +
> +struct bpe_entry {
> +       struct ether_addr       be_c_da; /* customer address - must be first 
> */
> +       struct ether_addr       be_b_da; /* bridge address */
> +       unsigned int            be_type;
> +#define BPE_ENTRY_DYNAMIC              0
> +#define BPE_ENTRY_STATIC               1
> +       struct refcnt           be_refs;
> +       time_t                  be_age;
> +
> +       RBT_ENTRY(bpe_entry)    be_entry;
> +};
> +
> +RBT_HEAD(bpe_map, bpe_entry);
> +
> +static inline int bpe_entry_cmp(const struct bpe_entry *,
> +    const struct bpe_entry *);
> +
> +RBT_PROTOTYPE(bpe_map, bpe_entry, be_entry, bpe_entry_cmp);
> +RBT_GENERATE(bpe_map, bpe_entry, be_entry, bpe_entry_cmp);
> +
> +struct bpe_softc {
> +       struct bpe_key          sc_key; /* must be first */
> +       struct arpcom           sc_ac;
> +       struct ifmedia          sc_media;
> +       int                     sc_txhprio;
> +       uint8_t                 sc_group[ETHER_ADDR_LEN];
> +
> +       void *                  sc_lh_cookie;
> +       void *                  sc_dh_cookie;
> +
> +       struct bpe_map          sc_bridge_map;
> +       struct rwlock           sc_bridge_lock;
> +       unsigned int            sc_bridge_num;
> +       unsigned int            sc_bridge_max;
> +       int                     sc_bridge_tmo; /* seconds */
> +       struct timeout          sc_bridge_age;
> +};
> +
> +void           bpeattach(int);
> +
> +static int     bpe_clone_create(struct if_clone *, int);
> +static int     bpe_clone_destroy(struct ifnet *);
> +
> +static void    bpe_start(struct ifnet *);
> +static int     bpe_ioctl(struct ifnet *, u_long, caddr_t);
> +static int     bpe_media_get(struct bpe_softc *, struct ifreq *);
> +static int     bpe_up(struct bpe_softc *);
> +static int     bpe_down(struct bpe_softc *);
> +static int     bpe_multi(struct bpe_softc *, struct ifnet *, u_long);
> +static int     bpe_set_vnetid(struct bpe_softc *, const struct ifreq *);
> +static void    bpe_set_group(struct bpe_softc *, uint32_t);
> +static int     bpe_set_parent(struct bpe_softc *, const struct if_parent *);
> +static int     bpe_get_parent(struct bpe_softc *, struct if_parent *);
> +static int     bpe_del_parent(struct bpe_softc *);
> +static void    bpe_link_hook(void *);
> +static void    bpe_link_state(struct bpe_softc *, u_char, uint64_t);
> +static void    bpe_detach_hook(void *);
> +
> +static void    bpe_input_map(struct bpe_softc *,
> +                   const uint8_t *, const uint8_t *);
> +static void    bpe_bridge_age(void *);
> +
> +static struct if_clone bpe_cloner =
> +    IF_CLONE_INITIALIZER("bpe", bpe_clone_create, bpe_clone_destroy);
> +
> +static struct bpe_tree bpe_interfaces = RBT_INITIALIZER();
> +static struct rwlock bpe_lock = RWLOCK_INITIALIZER("bpeifs");
> +static struct pool bpe_entry_pool;
> +
> +#define ether_cmp(_a, _b)      memcmp((_a), (_b), ETHER_ADDR_LEN)
> +#define ether_is_eq(_a, _b)    (ether_cmp((_a), (_b)) == 0)
> +#define ether_is_bcast(_a)     ether_is_eq((_a), etherbroadcastaddr)
> +
> +void
> +bpeattach(int count)
> +{
> +       if_clone_attach(&bpe_cloner);
> +}
> +
> +static int
> +bpe_clone_create(struct if_clone *ifc, int unit)
> +{
> +       struct bpe_softc *sc;
> +       struct ifnet *ifp;
> +
> +       if (bpe_entry_pool.pr_size == 0) {
> +               pool_init(&bpe_entry_pool, sizeof(struct bpe_entry), 0,
> +                   IPL_NONE, 0, "bpepl", NULL);
> +       }
> +
> +       sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
> +       ifp = &sc->sc_ac.ac_if;
> +
> +       snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
> +           ifc->ifc_name, unit);
> +
> +       sc->sc_key.k_if = 0;
> +       sc->sc_key.k_isid = 0;
> +       bpe_set_group(sc, 0);
> +
> +       sc->sc_txhprio = IF_HDRPRIO_PACKET;
> +
> +       rw_init(&sc->sc_bridge_lock, "bpebr");
> +       RBT_INIT(bpe_map, &sc->sc_bridge_map);
> +       sc->sc_bridge_num = 0;
> +       sc->sc_bridge_max = 100; /* XXX */
> +       sc->sc_bridge_tmo = 240;
> +       timeout_set_proc(&sc->sc_bridge_age, bpe_bridge_age, sc);
> +
> +       ifp->if_softc = sc;
> +       ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
> +       ifp->if_ioctl = bpe_ioctl;
> +       ifp->if_start = bpe_start;
> +       ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
> +       ifp->if_xflags = IFXF_CLONED;
> +       IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
> +       ether_fakeaddr(ifp);
> +
> +       if_attach(ifp);
> +       ether_ifattach(ifp);
> +
> +       return (0);
> +}
> +
> +static int
> +bpe_clone_destroy(struct ifnet *ifp)
> +{
> +       struct bpe_softc *sc = ifp->if_softc;
> +
> +       NET_LOCK();
> +       if (ISSET(ifp->if_flags, IFF_RUNNING))
> +               bpe_down(sc);
> +       NET_UNLOCK();
> +
> +       ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
> +       ether_ifdetach(ifp);
> +       if_detach(ifp);
> +
> +       free(sc, M_DEVBUF, sizeof(*sc));
> +
> +       return (0);
> +}
> +
> +static inline int
> +bpe_entry_valid(struct bpe_softc *sc, const struct bpe_entry *be)
> +{
> +       time_t diff;
> +
> +       if (be == NULL)
> +               return (0);
> +
> +       if (be->be_type == BPE_ENTRY_STATIC)
> +               return (1);
> +
> +       diff = time_uptime - be->be_age;
> +       if (diff < sc->sc_bridge_tmo)
> +               return (1);
> +
> +       return (0);
> +}
> +
> +static void
> +bpe_start(struct ifnet *ifp)
> +{
> +       struct bpe_softc *sc = ifp->if_softc;
> +       struct ifnet *ifp0;
> +       struct mbuf *m0, *m;
> +       struct ether_header *ceh;
> +       struct ether_header *beh;
> +       uint32_t itag, *itagp;
> +       int hlen = sizeof(*beh) + sizeof(*itagp);
> +#if NBPFILTER > 0
> +       caddr_t if_bpf;
> +#endif
> +       int txprio;
> +       uint8_t prio;
> +
> +       ifp0 = if_get(sc->sc_key.k_if);
> +       if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) {
> +               ifq_purge(&ifp->if_snd);
> +               goto done;
> +       }
> +
> +       txprio = sc->sc_txhprio;
> +
> +       while ((m0 = ifq_dequeue(&ifp->if_snd)) != NULL) {
> +#if NBPFILTER > 0
> +               if_bpf = ifp->if_bpf;
> +               if (if_bpf)
> +                       bpf_mtap_ether(if_bpf, m0, BPF_DIRECTION_OUT);
> +#endif
> +
> +               ceh = mtod(m0, struct ether_header *);
> +
> +               /* force prepend of a whole mbuf because of alignment */
> +               m = m_get(M_DONTWAIT, m0->m_type);
> +               if (m == NULL) {
> +                       m_freem(m0);
> +                       continue;
> +               }
> +
> +               M_MOVE_PKTHDR(m, m0);
> +               m->m_next = m0;
> +
> +               m_align(m, 0);
> +               m->m_len = 0;
> +
> +               m = m_prepend(m, hlen, M_DONTWAIT);
> +               if (m == NULL)
> +                       continue;
> +
> +               beh = mtod(m, struct ether_header *);
> +
> +               if (ether_is_bcast(ceh->ether_dhost)) {
> +                       memcpy(beh->ether_dhost, sc->sc_group,
> +                           sizeof(beh->ether_dhost));
> +               } else {
> +                       struct bpe_entry *be;
> +
> +                       rw_enter_read(&sc->sc_bridge_lock);
> +                       be = RBT_FIND(bpe_map, &sc->sc_bridge_map,
> +                           (struct bpe_entry *)ceh->ether_dhost);
> +                       if (bpe_entry_valid(sc, be)) {
> +                               memcpy(beh->ether_dhost, &be->be_b_da,
> +                                   sizeof(beh->ether_dhost));
> +                       } else {
> +                               /* "flood" to unknown hosts */
> +                               memcpy(beh->ether_dhost, sc->sc_group,
> +                                   sizeof(beh->ether_dhost));
> +                       }
> +                       rw_exit_read(&sc->sc_bridge_lock);
> +               }
> +
> +               memcpy(beh->ether_shost, ((struct arpcom *)ifp0)->ac_enaddr,
> +                   sizeof(beh->ether_shost));
> +               beh->ether_type = htons(ETHERTYPE_PBB);
> +
> +               prio = (txprio == IF_HDRPRIO_PACKET) ?
> +                   m->m_pkthdr.pf.prio : txprio;
> +
> +               itag = sc->sc_key.k_isid;
> +               itag |= prio << PBB_ITAG_PCP_SHIFT;
> +               itagp = (uint32_t *)(beh + 1);
> +
> +               htobem32(itagp, itag);
> +
> +               if_enqueue(ifp0, m);
> +       }
> +
> +done:
> +       if_put(ifp0);
> +}
> +
> +static void
> +bpe_bridge_age(void *arg)
> +{
> +       struct bpe_softc *sc = arg;
> +       struct bpe_entry *be, *nbe;
> +       time_t diff;
> +
> +       timeout_add_sec(&sc->sc_bridge_age, BPE_BRIDGE_AGE_TMO);
> +
> +       rw_enter_write(&sc->sc_bridge_lock);
> +       RBT_FOREACH_SAFE(be, bpe_map, &sc->sc_bridge_map, nbe) {
> +               if (be->be_type != BPE_ENTRY_DYNAMIC)
> +                       continue;
> +
> +               diff = time_uptime - be->be_age;
> +               if (diff < sc->sc_bridge_tmo)
> +                       continue;
> +
> +               sc->sc_bridge_num--;
> +               RBT_REMOVE(bpe_map, &sc->sc_bridge_map, be);
> +               if (refcnt_rele(&be->be_refs))
> +                       pool_put(&bpe_entry_pool, be);
> +       }
> +       rw_exit_write(&sc->sc_bridge_lock);
> +}
> +
> +static int
> +bpe_rtfind(struct bpe_softc *sc, struct ifbaconf *baconf)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +       struct bpe_entry *be;
> +       struct ifbareq bareq;
> +       caddr_t uaddr, end;
> +       int error;
> +       time_t age;
> +       struct sockaddr_dl *sdl;
> +
> +       if (baconf->ifbac_len == 0) {
> +               /* single read is atomic */
> +               baconf->ifbac_len = sc->sc_bridge_num * sizeof(bareq);
> +               return (0);
> +       }
> +
> +       uaddr = baconf->ifbac_buf;
> +       end = uaddr + baconf->ifbac_len;
> +
> +       rw_enter_read(&sc->sc_bridge_lock);
> +       RBT_FOREACH(be, bpe_map, &sc->sc_bridge_map) {
> +               if (uaddr >= end)
> +                       break;
> +
> +               memcpy(bareq.ifba_name, ifp->if_xname,
> +                   sizeof(bareq.ifba_name));
> +               memcpy(bareq.ifba_ifsname, ifp->if_xname,
> +                   sizeof(bareq.ifba_ifsname));
> +               memcpy(&bareq.ifba_dst, &be->be_c_da,
> +                   sizeof(bareq.ifba_dst));
> +
> +               memset(&bareq.ifba_dstsa, 0, sizeof(bareq.ifba_dstsa));
> +
> +               bzero(&bareq.ifba_dstsa, sizeof(bareq.ifba_dstsa));
> +               sdl = (struct sockaddr_dl *)&bareq.ifba_dstsa;
> +               sdl->sdl_len = sizeof(sdl);
> +               sdl->sdl_family = AF_LINK;
> +               sdl->sdl_index = 0;
> +               sdl->sdl_type = IFT_ETHER;
> +               sdl->sdl_nlen = 0;
> +               sdl->sdl_alen = sizeof(be->be_b_da);
> +               CTASSERT(sizeof(sdl->sdl_data) >= sizeof(be->be_b_da));
> +               memcpy(sdl->sdl_data, &be->be_b_da, sizeof(be->be_b_da));
> +
> +               switch (be->be_type) {
> +               case BPE_ENTRY_DYNAMIC:
> +                       age = time_uptime - be->be_age;
> +                       bareq.ifba_age = MIN(age, 0xff);
> +                       bareq.ifba_flags = IFBAF_DYNAMIC;
> +                       break;
> +               case BPE_ENTRY_STATIC:
> +                       bareq.ifba_age = 0;
> +                       bareq.ifba_flags = IFBAF_STATIC;
> +                       break;
> +               }
> +
> +               error = copyout(&bareq, uaddr, sizeof(bareq));
> +               if (error != 0) {
> +                       rw_exit_read(&sc->sc_bridge_lock);
> +                       return (error);
> +               }
> +
> +               uaddr += sizeof(bareq);
> +       }
> +       baconf->ifbac_len = sc->sc_bridge_num * sizeof(bareq);
> +       rw_exit_read(&sc->sc_bridge_lock);
> +
> +       return (0);
> +}
> +
> +static void
> +bpe_flush_map(struct bpe_softc *sc, uint32_t flags)
> +{
> +       struct bpe_entry *be, *nbe;
> +
> +       rw_enter_write(&sc->sc_bridge_lock);
> +       RBT_FOREACH_SAFE(be, bpe_map, &sc->sc_bridge_map, nbe) {
> +               if (flags == IFBF_FLUSHDYN &&
> +                   be->be_type != BPE_ENTRY_DYNAMIC)
> +                       continue;
> +
> +               RBT_REMOVE(bpe_map, &sc->sc_bridge_map, be);
> +               if (refcnt_rele(&be->be_refs))
> +                       pool_put(&bpe_entry_pool, be);
> +       }
> +       rw_exit_write(&sc->sc_bridge_lock);
> +}
> +
> +static int
> +bpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
> +{
> +       struct bpe_softc *sc = ifp->if_softc;
> +       struct ifreq *ifr = (struct ifreq *)data;
> +       struct ifbrparam *bparam = (struct ifbrparam *)data;
> +       int error = 0;
> +
> +       switch (cmd) {
> +       case SIOCSIFFLAGS:
> +               if (ISSET(ifp->if_flags, IFF_UP)) {
> +                       if (!ISSET(ifp->if_flags, IFF_RUNNING))
> +                               error = bpe_up(sc);
> +                       else
> +                               error = 0;
> +               } else {
> +                       if (ISSET(ifp->if_flags, IFF_RUNNING))
> +                               error = bpe_down(sc);
> +               }
> +               break;
> +
> +       case SIOCSVNETID:
> +               error = bpe_set_vnetid(sc, ifr);
> +       case SIOCGVNETID:
> +               ifr->ifr_vnetid = sc->sc_key.k_isid;
> +               break;
> +
> +       case SIOCSIFPARENT:
> +               error = bpe_set_parent(sc, (struct if_parent *)data);
> +               break;
> +       case SIOCGIFPARENT:
> +               error = bpe_get_parent(sc, (struct if_parent *)data);
> +               break;
> +       case SIOCDIFPARENT:
> +               error = bpe_del_parent(sc);
> +               break;
> +
> +       case SIOCSTXHPRIO:
> +               if (ifr->ifr_hdrprio == IF_HDRPRIO_PACKET) /* use mbuf prio */
> +                       ;
> +               else if (ifr->ifr_hdrprio < IF_HDRPRIO_MIN ||
> +                   ifr->ifr_hdrprio > IF_HDRPRIO_MAX) {
> +                       error = EINVAL;
> +                       break;
> +               }
> +
> +               sc->sc_txhprio = ifr->ifr_hdrprio;
> +               break;
> +       case SIOCGTXHPRIO:
> +               ifr->ifr_hdrprio = sc->sc_txhprio;
> +               break;
> +
> +       case SIOCGIFMEDIA:
> +               error = bpe_media_get(sc, ifr);
> +               break;
> +
> +       case SIOCBRDGSCACHE:
> +               error = suser(curproc);
> +               if (error != 0)
> +                       break;
> +
> +               if (bparam->ifbrp_csize < 1) {
> +                       error = EINVAL;
> +                       break;
> +               }
> +
> +               /* commit */
> +               sc->sc_bridge_max = bparam->ifbrp_csize;
> +               break;
> +       case SIOCBRDGGCACHE:
> +               bparam->ifbrp_csize = sc->sc_bridge_max;
> +               break;
> +
> +       case SIOCBRDGSTO:
> +               error = suser(curproc);
> +               if (error != 0)
> +                       break;
> +
> +               if (bparam->ifbrp_ctime < 8 ||
> +                   bparam->ifbrp_ctime > 3600) {
> +                       error = EINVAL;
> +                       break;
> +               }
> +               sc->sc_bridge_tmo = bparam->ifbrp_ctime;
> +               break;
> +       case SIOCBRDGGTO:
> +               bparam->ifbrp_ctime = sc->sc_bridge_tmo;
> +               break;
> +
> +       case SIOCBRDGRTS:
> +               error = bpe_rtfind(sc, (struct ifbaconf *)data);
> +               break;
> +       case SIOCBRDGFLUSH:
> +               error = suser(curproc);
> +               if (error != 0)
> +                       break;
> +
> +               bpe_flush_map(sc,
> +                   ((struct ifbreq *)data)->ifbr_ifsflags);
> +               break;
> +
> +       default:
> +               error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
> +               break;
> +       }
> +
> +       return (error);
> +}
> +
> +static int
> +bpe_media_get(struct bpe_softc *sc, struct ifreq *ifr)
> +{
> +       struct ifnet *ifp0;
> +       int error;
> +
> +       ifp0 = if_get(sc->sc_key.k_if);
> +       if (ifp0 != NULL)
> +               error = (*ifp0->if_ioctl)(ifp0, SIOCGIFMEDIA, (caddr_t)ifr);
> +       else
> +               error = ENOTTY;
> +       if_put(ifp0);
> +
> +       return (error);
> +}
> +
> +static int
> +bpe_up(struct bpe_softc *sc)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +       struct ifnet *ifp0;
> +       struct bpe_softc *osc;
> +       int error = 0;
> +       u_int hardmtu;
> +
> +       KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
> +       NET_ASSERT_LOCKED();
> +
> +       ifp0 = if_get(sc->sc_key.k_if);
> +       if (ifp0 == NULL)
> +               return (ENXIO);
> +
> +       /* check again if bpe will work on top of the parent */
> +       if (ifp0->if_type != IFT_ETHER) {
> +               error = EPROTONOSUPPORT;
> +               goto put;
> +       }
> +
> +       hardmtu = ifp0->if_hardmtu;
> +       /* check min mtu? */
> +       if (ifp->if_mtu > hardmtu) {
> +               error = ENOBUFS;
> +               goto put;
> +       }
> +
> +       /* parent is fine, let's prepare the bpe to handle packets */
> +       ifp->if_hardmtu = hardmtu;
> +       SET(ifp->if_flags, ifp0->if_flags & IFF_SIMPLEX);
> +
> +       /* commit the interface */
> +       error = rw_enter(&bpe_lock, RW_WRITE | RW_INTR);
> +       if (error != 0)
> +               goto scrub;
> +
> +       osc = (struct bpe_softc *)RBT_INSERT(bpe_tree, &bpe_interfaces,
> +           (struct bpe_key *)sc);
> +       rw_exit(&bpe_lock);
> +
> +       if (osc != NULL) {
> +               error = EADDRINUSE;
> +               goto scrub;
> +       }
> +
> +       if (bpe_multi(sc, ifp0, SIOCADDMULTI) != 0) {
> +               error = ENOTCONN;
> +               goto remove;
> +       }
> +
> +       /* Register callback for physical link state changes */
> +       sc->sc_lh_cookie = hook_establish(ifp0->if_linkstatehooks, 1,
> +           bpe_link_hook, sc);
> +
> +       /* Register callback if parent wants to unregister */
> +       sc->sc_dh_cookie = hook_establish(ifp0->if_detachhooks, 0,
> +           bpe_detach_hook, sc);
> +
> +       /* we're running now */
> +       SET(ifp->if_flags, IFF_RUNNING);
> +       bpe_link_state(sc, ifp0->if_link_state, ifp0->if_baudrate);
> +
> +       if_put(ifp0);
> +
> +       timeout_add_sec(&sc->sc_bridge_age, BPE_BRIDGE_AGE_TMO);
> +
> +       return (0);
> +
> +remove:
> +       rw_enter(&bpe_lock, RW_WRITE);
> +       RBT_REMOVE(bpe_tree, &bpe_interfaces, (struct bpe_key *)sc);
> +       rw_exit(&bpe_lock);
> +scrub:
> +       CLR(ifp->if_flags, IFF_SIMPLEX);
> +       ifp->if_hardmtu = 0xffff;
> +put:
> +       if_put(ifp0);
> +
> +       return (error);
> +}
> +
> +static int
> +bpe_down(struct bpe_softc *sc)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +       struct ifnet *ifp0;
> +
> +       NET_ASSERT_LOCKED();
> +
> +       CLR(ifp->if_flags, IFF_RUNNING);
> +
> +       ifp0 = if_get(sc->sc_key.k_if);
> +       if (ifp0 != NULL) {
> +               hook_disestablish(ifp0->if_detachhooks, sc->sc_dh_cookie);
> +               hook_disestablish(ifp0->if_linkstatehooks, sc->sc_lh_cookie);
> +               bpe_multi(sc, ifp0, SIOCDELMULTI);
> +       }
> +       if_put(ifp0);
> +
> +       rw_enter(&bpe_lock, RW_WRITE);
> +       RBT_REMOVE(bpe_tree, &bpe_interfaces, (struct bpe_key *)sc);
> +       rw_exit(&bpe_lock);
> +
> +       CLR(ifp->if_flags, IFF_SIMPLEX);
> +       ifp->if_hardmtu = 0xffff;
> +
> +       return (0);
> +}
> +
> +static int
> +bpe_multi(struct bpe_softc *sc, struct ifnet *ifp0, u_long cmd)
> +{
> +       struct ifreq ifr;
> +       struct sockaddr *sa;
> +
> +       /* make it convincing */
> +       CTASSERT(sizeof(ifr.ifr_name) == sizeof(ifp0->if_xname));
> +       memcpy(ifr.ifr_name, ifp0->if_xname, sizeof(ifr.ifr_name));
> +
> +       sa = &ifr.ifr_addr;
> +       CTASSERT(sizeof(sa->sa_data) >= sizeof(sc->sc_group));
> +
> +       sa->sa_family = AF_UNSPEC;
> +       memcpy(sa->sa_data, sc->sc_group, sizeof(sc->sc_group));
> +
> +       return ((*ifp0->if_ioctl)(ifp0, cmd, (caddr_t)&ifr));
> +}
> +
> +static void
> +bpe_set_group(struct bpe_softc *sc, uint32_t isid)
> +{
> +       uint8_t *group = sc->sc_group;
> +
> +       group[0] = 0x01;
> +       group[1] = 0x1e;
> +       group[2] = 0x83;
> +       group[3] = isid >> 16;
> +       group[4] = isid >> 8;
> +       group[5] = isid >> 0;
> +}
> +
> +static int
> +bpe_set_vnetid(struct bpe_softc *sc, const struct ifreq *ifr)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +       uint32_t isid;
> +
> +       if (ifr->ifr_vnetid < PBB_ITAG_ISID_MIN ||
> +           ifr->ifr_vnetid > PBB_ITAG_ISID_MAX)
> +               return (EINVAL);
> +
> +       isid = ifr->ifr_vnetid;
> +       if (isid == sc->sc_key.k_isid)
> +               return (0);
> +
> +       if (ISSET(ifp->if_flags, IFF_RUNNING))
> +               return (EBUSY);
> +
> +       /* commit */
> +       sc->sc_key.k_isid = isid;
> +       bpe_set_group(sc, isid);
> +       bpe_flush_map(sc, IFBF_FLUSHALL);
> +
> +       return (0);
> +}
> +
> +static int
> +bpe_set_parent(struct bpe_softc *sc, const struct if_parent *p)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +       struct ifnet *ifp0;
> +
> +       ifp0 = ifunit(p->ifp_parent); /* doesn't need an if_put */
> +       if (ifp0 == NULL)
> +               return (ENXIO);
> +
> +       if (ifp0->if_type != IFT_ETHER)
> +               return (ENXIO);
> +
> +       if (ifp0->if_index == sc->sc_key.k_if)
> +               return (0);
> +
> +       if (ISSET(ifp->if_flags, IFF_RUNNING))
> +               return (EBUSY);
> +
> +       /* commit */
> +       sc->sc_key.k_if = ifp0->if_index;
> +       bpe_flush_map(sc, IFBF_FLUSHALL);
> +
> +       return (0);
> +}
> +
> +static int
> +bpe_get_parent(struct bpe_softc *sc, struct if_parent *p)
> +{
> +       struct ifnet *ifp0;
> +       int error = 0;
> +
> +       ifp0 = if_get(sc->sc_key.k_if);
> +       if (ifp0 == NULL)
> +               error = EADDRNOTAVAIL;
> +       else
> +               memcpy(p->ifp_parent, ifp0->if_xname, sizeof(p->ifp_parent));
> +       if_put(ifp0);
> +
> +       return (error);
> +}
> +
> +static int
> +bpe_del_parent(struct bpe_softc *sc)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +       if (ISSET(ifp->if_flags, IFF_RUNNING))
> +               return (EBUSY);
> +
> +       /* commit */
> +       sc->sc_key.k_if = 0;
> +       bpe_flush_map(sc, IFBF_FLUSHALL);
> +
> +       return (0);
> +}
> +
> +static inline struct bpe_softc *
> +bpe_find(struct ifnet *ifp0, uint32_t isid)
> +{
> +       struct bpe_key k = { .k_if = ifp0->if_index, .k_isid = isid };
> +       struct bpe_softc *sc;
> +
> +       rw_enter_read(&bpe_lock);
> +       sc = (struct bpe_softc *)RBT_FIND(bpe_tree, &bpe_interfaces, &k);
> +       rw_exit_read(&bpe_lock);
> +
> +       return (sc);
> +}
> +
> +static void
> +bpe_input_map(struct bpe_softc *sc, const uint8_t *ba, const uint8_t *ca)
> +{
> +       struct bpe_entry *be;
> +       int new = 0;
> +
> +       if (ETHER_IS_MULTICAST(ca))
> +               return;
> +
> +       /* remember where it came from */
> +       rw_enter_read(&sc->sc_bridge_lock);
> +       be = RBT_FIND(bpe_map, &sc->sc_bridge_map, (struct bpe_entry *)ca);
> +       if (be == NULL)
> +               new = 1;
> +       else {
> +               be->be_age = time_uptime; /* only a little bit racy */
> +
> +               if (be->be_type != BPE_ENTRY_DYNAMIC ||
> +                   ether_is_eq(ba, &be->be_b_da))
> +                       be = NULL;
> +               else
> +                       refcnt_take(&be->be_refs);
> +       }
> +       rw_exit_read(&sc->sc_bridge_lock);
> +
> +       if (new) {
> +               struct bpe_entry *obe;
> +               unsigned int num;
> +
> +               be = pool_get(&bpe_entry_pool, PR_NOWAIT);
> +               if (be == NULL) {
> +                       /* oh well */
> +                       return;
> +               }
> +
> +               memcpy(&be->be_c_da, ca, sizeof(be->be_c_da));
> +               memcpy(&be->be_b_da, ba, sizeof(be->be_b_da));
> +               be->be_type = BPE_ENTRY_DYNAMIC;
> +               refcnt_init(&be->be_refs);
> +               be->be_age = time_uptime;
> +
> +               rw_enter_write(&sc->sc_bridge_lock);
> +               num = sc->sc_bridge_num;
> +               if (++num > sc->sc_bridge_max)
> +                       obe = be;
> +               else {
> +                       /* try and give the ref to the map */
> +                       obe = RBT_INSERT(bpe_map, &sc->sc_bridge_map, be);
> +                       if (obe == NULL) {
> +                               /* count the insert */
> +                               sc->sc_bridge_num = num;
> +                       }
> +               }
> +               rw_exit_write(&sc->sc_bridge_lock);
> +
> +               if (obe != NULL)
> +                       pool_put(&bpe_entry_pool, obe);
> +       } else if (be != NULL) {
> +               rw_enter_write(&sc->sc_bridge_lock);
> +               memcpy(&be->be_b_da, ba, sizeof(be->be_b_da));
> +               rw_exit_write(&sc->sc_bridge_lock);
> +
> +               if (refcnt_rele(&be->be_refs)) {
> +                       /* ioctl may have deleted the entry */
> +                       pool_put(&bpe_entry_pool, be);
> +               }
> +       }
> +}
> +
> +void
> +bpe_input(struct ifnet *ifp0, struct mbuf *m)
> +{
> +       struct mbuf_list ml = MBUF_LIST_INITIALIZER();
> +       struct bpe_softc *sc;
> +       struct ifnet *ifp;
> +       struct ether_header *beh, *ceh;
> +       uint32_t *itagp, itag;
> +       unsigned int hlen = sizeof(*beh) + sizeof(*itagp) + sizeof(*ceh);
> +       struct mbuf *n;
> +       int off;
> +
> +       if (m->m_len < hlen) {
> +               m = m_pullup(m, hlen);
> +               if (m == NULL) {
> +                       /* pbb short ++ */
> +                       return;
> +               }
> +       }
> +
> +       beh = mtod(m, struct ether_header *);
> +       itagp = (uint32_t *)(beh + 1);
> +       itag = bemtoh32(itagp);
> +
> +       if (itag & PBB_ITAG_RES2) {
> +               /* dropped by res2 ++ */
> +               goto drop;
> +       }
> +
> +       sc = bpe_find(ifp0, itag & PBB_ITAG_ISID);
> +       if (sc == NULL) {
> +               /* no interface found */
> +               goto drop;
> +       }
> +
> +       ceh = (struct ether_header *)(itagp + 1);
> +
> +       bpe_input_map(sc, beh->ether_shost, ceh->ether_shost);
> +
> +       m_adj(m, sizeof(*beh) + sizeof(*itagp));
> +
> +       n = m_getptr(m, sizeof(*ceh), &off);
> +       if (n == NULL) {
> +               /* no data ++ */
> +               goto drop;
> +       }
> +
> +       if (!ALIGNED_POINTER(mtod(n, caddr_t) + off, uint32_t)) {
> +               /* unaligned ++ */
> +               n = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
> +               m_freem(m);
> +               if (n == NULL)
> +                       return;
> +
> +               m = n;
> +       }
> +
> +       ifp = &sc->sc_ac.ac_if;
> +
> +       m->m_flags &= ~(M_BCAST|M_MCAST);
> +       m->m_pkthdr.ph_ifidx = ifp->if_index;
> +       m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
> +
> +#if NPF > 0
> +       pf_pkt_addr_changed(m);
> +#endif
> +
> +       ml_enqueue(&ml, m);
> +       if_input(ifp, &ml);
> +       return;
> +
> +drop:
> +       m_freem(m);
> +}
> +
> +void
> +bpe_detach_hook(void *arg)
> +{
> +       struct bpe_softc *sc = arg;
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +       if (ISSET(ifp->if_flags, IFF_RUNNING)) {
> +               bpe_down(sc);
> +               CLR(ifp->if_flags, IFF_UP);
> +       }
> +
> +       sc->sc_key.k_if = 0;
> +}
> +
> +static void
> +bpe_link_hook(void *arg)
> +{
> +       struct bpe_softc *sc = arg;
> +       struct ifnet *ifp0;
> +       u_char link = LINK_STATE_DOWN;
> +       uint64_t baud = 0;
> +
> +       ifp0 = if_get(sc->sc_key.k_if);
> +       if (ifp0 != NULL) {
> +               link = ifp0->if_link_state;
> +               baud = ifp0->if_baudrate;
> +       }
> +       if_put(ifp0);
> +
> +       bpe_link_state(sc, link, baud);
> +}
> +
> +void
> +bpe_link_state(struct bpe_softc *sc, u_char link, uint64_t baud)
> +{
> +       struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +       if (ifp->if_link_state == link)
> +               return;
> +
> +       ifp->if_link_state = link;
> +       ifp->if_baudrate = baud;
> +
> +       if_link_state_change(ifp);
> +}
> +
> +static inline int
> +bpe_cmp(const struct bpe_key *a, const struct bpe_key *b)
> +{
> +       if (a->k_if > b->k_if)
> +               return (1);
> +       if (a->k_if < b->k_if)
> +               return (-1);
> +       if (a->k_isid > b->k_isid)
> +               return (1);
> +       if (a->k_isid < b->k_isid)
> +               return (-1);
> +
> +       return (0);
> +}
> +
> +static inline int
> +bpe_entry_cmp(const struct bpe_entry *a, const struct bpe_entry *b)
> +{
> +       return memcmp(&a->be_c_da, &b->be_c_da, sizeof(a->be_c_da));
> +}
> Index: sys/net/if_bpe.h
> ===================================================================
> RCS file: sys/net/if_bpe.h
> diff -N sys/net/if_bpe.h
> --- /dev/null   1 Jan 1970 00:00:00 -0000
> +++ sys/net/if_bpe.h    19 Dec 2018 04:16:07 -0000
> @@ -0,0 +1,26 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2018 David Gwynne <d...@openbsd.org>
> + *
> + * 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.
> + */
> +
> +#ifndef _NET_IF_BPE_H
> +#define _NET_IF_BPE_H
> +
> +#ifdef _KERNEL
> +void   bpe_input(struct ifnet *, struct mbuf *);
> +#endif
> +
> +#endif /* _NET_IF_GRE_H_ */
> Index: sys/net/if_ethersubr.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_ethersubr.c,v
> retrieving revision 1.255
> diff -u -p -r1.255 if_ethersubr.c
> --- sys/net/if_ethersubr.c      12 Dec 2018 05:38:26 -0000      1.255
> +++ sys/net/if_ethersubr.c      19 Dec 2018 04:16:07 -0000
> @@ -108,6 +108,11 @@ didn't get a copy, you may request one f
>  #include <net/if_pppoe.h>
>  #endif
>
> +#include "bpe.h"
> +#if NBPE > 0
> +#include <net/if_bpe.h>
> +#endif
> +
>  #ifdef INET6
>  #include <netinet6/in6_var.h>
>  #include <netinet6/nd6.h>
> @@ -440,6 +445,11 @@ ether_input(struct ifnet *ifp, struct mb
>         case ETHERTYPE_MPLS_MCAST:
>                 input = mpls_input;
>                 break;
> +#endif
> +#if NBPE > 0
> +       case ETHERTYPE_PBB:
> +               bpe_input(ifp, m);
> +               return (1);
>  #endif
>         default:
>                 goto dropanyway;
> Index: sys/conf/GENERIC
> ===================================================================
> RCS file: /cvs/src/sys/conf/GENERIC,v
> retrieving revision 1.256
> diff -u -p -r1.256 GENERIC
> --- sys/conf/GENERIC    18 Oct 2018 02:10:54 -0000      1.256
> +++ sys/conf/GENERIC    19 Dec 2018 04:16:07 -0000
> @@ -94,6 +94,7 @@ pseudo-device mobileip        # MobileIP encaps
>  pseudo-device  loop            # network loopback
>  pseudo-device  mpe             # MPLS PE interface
>  pseudo-device  mpw             # MPLS pseudowire support
> +pseudo-device  bpe             # Provider Backbone Bridge edge interface
>  pseudo-device  pair            # Virtual Ethernet interface pair
>  pseudo-device  ppp             # PPP
>  pseudo-device  pppoe           # PPP over Ethernet (RFC 2516)
> Index: sys/conf/files
> ===================================================================
> RCS file: /cvs/src/sys/conf/files,v
> retrieving revision 1.665
> diff -u -p -r1.665 files
> --- sys/conf/files      21 Aug 2018 18:06:12 -0000      1.665
> +++ sys/conf/files      19 Dec 2018 04:16:07 -0000
> @@ -563,6 +563,7 @@ pseudo-device crypto: ifnet
>  pseudo-device trunk: ifnet, ether, ifmedia
>  pseudo-device mpe: ifnet, ether
>  pseudo-device mpw: ifnet, ether
> +pseudo-device bpe: ifnet, ether, ifmedia
>  pseudo-device vether: ifnet, ether
>  pseudo-device pppx: ifnet
>  pseudo-device vxlan: ifnet, ether, ifmedia
> @@ -813,6 +814,7 @@ file net/if_trunk.c                 trunk                 
>   needs-coun
>  file net/trunklacp.c                   trunk
>  file net/if_mpe.c                      mpe                     needs-count
>  file net/if_mpw.c                      mpw & bridge            needs-count
> +file net/if_bpe.c                      bpe                     needs-count
>  file net/if_vether.c                   vether                  needs-count
>  file net/if_pair.c                     pair                    needs-count
>  file net/if_pppx.c                     pppx                    needs-count
>

Reply via email to