On Sun, Sep 14, 2014 at 11:51:07PM -0300, Rafael Zalamena wrote: > The following patch implements the basics of the wire network interface. > > --- snipped ---
I've added support for tcpdump'ing the wire interface, it will get all data flowing through the wire without the MPLS / VPLS labels and control word (just like enc(4) does for IPSec). If you want to see the full packet see the interface facing your MPLS network (tcpdump needs some work to identify the VPLS label and control word). Partial patch (if you applied the one before this): PATCH START ======================================================================== diff --git sys/net/if_wire.c sys/net/if_wire.c index 41840cf..b9301fc 100644 --- sys/net/if_wire.c +++ sys/net/if_wire.c @@ -30,6 +30,13 @@ #include <netinet/if_ether.h> #include <netmpls/mpls.h> +#include "bpfilter.h" + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif /* NBPFILTER */ + + void wireattach(int); int wire_clone_create(struct if_clone *, int); int wire_clone_destroy(struct ifnet *); @@ -90,6 +97,10 @@ wire_clone_create(struct if_clone *ifc, int unit) if_attach(ifp); if_alloc_sadl(ifp); +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN); +#endif + s = splnet(); LIST_INSERT_HEAD(&wire_list, sc, sc_list); splx(s); @@ -253,6 +264,11 @@ wire_input(struct ifnet *ifp, struct mbuf *m) } } +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf) + bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_IN); +#endif + eh = mtod(m, struct ether_header *); m_adj(m, ETHER_HDR_LEN); @@ -316,6 +332,11 @@ wire_start(struct ifnet *ifp) continue; } +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf) + bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_OUT); +#endif /* NBPFILTER */ + rt = rtalloc1(&sc->sc_nexthop, RT_REPORT, 0); if (rt == NULL) { m_freem(m); ======================================================================== Here is the full patch: (you still need to apply VPLS patch [1/3]) PATCH START ======================================================================== diff --git sys/conf/GENERIC sys/conf/GENERIC index 309528e..444dcbe 100644 --- sys/conf/GENERIC +++ sys/conf/GENERIC @@ -95,6 +95,7 @@ pseudo-device systrace 1 # system call tracing device # clonable devices pseudo-device bpfilter # packet filter pseudo-device bridge # network bridging support +pseudo-device wire # pseudowire support pseudo-device carp # CARP protocol support pseudo-device gif # IPv[46] over IPv[46] tunnel (RFC1933) pseudo-device gre # GRE encapsulation interface diff --git sys/conf/files sys/conf/files index 4220371..755b6cd 100644 --- sys/conf/files +++ sys/conf/files @@ -552,6 +552,7 @@ pseudo-device tun: ifnet pseudo-device bpfilter: ifnet pseudo-device enc: ifnet pseudo-device bridge: ifnet, ether +pseudo-device wire: ifnet, ether pseudo-device vlan: ifnet, ether pseudo-device carp: ifnet, ether pseudo-device sppp: ifnet @@ -790,6 +791,7 @@ file net/if_tun.c tun needs-count file net/if_bridge.c bridge needs-count file net/bridgestp.c bridge file net/if_vlan.c vlan needs-count +file net/if_wire.c wire needs-count file net/pipex.c pipex file net/radix.c file net/radix_mpath.c !small_kernel diff --git sys/net/if_bridge.c sys/net/if_bridge.c index fa40d36..6d09113 100644 --- sys/net/if_bridge.c +++ sys/net/if_bridge.c @@ -36,6 +36,7 @@ #include "pf.h" #include "carp.h" #include "vlan.h" +#include "wire.h" #include <sys/param.h> #include <sys/systm.h> @@ -365,6 +366,11 @@ bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) /* Nothing needed */ } #endif /* NGIF */ +#if NWIRE > 0 + else if (ifs->if_type == IFT_MPLSTUNNEL) { + /* Nothing needed */ + } +#endif /* NWIRE */ else { error = EINVAL; break; diff --git sys/net/if_wire.c sys/net/if_wire.c new file mode 100644 index 0000000..b9301fc --- /dev/null +++ sys/net/if_wire.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2014 Rafael Zalamena <rzalam...@gmail.com> + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> + +#include <netinet/if_ether.h> +#include <netmpls/mpls.h> + +#include "bpfilter.h" + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif /* NBPFILTER */ + + +void wireattach(int); +int wire_clone_create(struct if_clone *, int); +int wire_clone_destroy(struct ifnet *); +int wire_ioctl(struct ifnet *, u_long, caddr_t); +int wire_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +void wire_start(struct ifnet *); + +struct wire_softc { + struct ifnet sc_if; + u_int32_t sc_flags; + u_int32_t sc_type; + struct shim_hdr sc_lshim; + struct shim_hdr sc_rshim; + struct sockaddr sc_nexthop; + + LIST_ENTRY(wire_softc) sc_list; +}; + +LIST_HEAD(, wire_softc) wire_list; + +struct if_clone wire_cloner = + IF_CLONE_INITIALIZER("wire", wire_clone_create, wire_clone_destroy); + +/* ARGSUSED */ +void +wireattach(int n) +{ + LIST_INIT(&wire_list); + if_clone_attach(&wire_cloner); +} + +int +wire_clone_create(struct if_clone *ifc, int unit) +{ + struct wire_softc *sc; + struct ifnet *ifp; + int s; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc == NULL) + return (ENOMEM); + + ifp = &sc->sc_if; + snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", + ifc->ifc_name, unit); + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_POINTOPOINT; + ifp->if_ioctl = wire_ioctl; + ifp->if_output = wire_output; + ifp->if_start = wire_start; + ifp->if_type = IFT_MPLSTUNNEL; + ifp->if_hdrlen = ETHER_HDR_LEN; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + IFQ_SET_READY(&ifp->if_snd); + + if_attach(ifp); + if_alloc_sadl(ifp); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN); +#endif + + s = splnet(); + LIST_INSERT_HEAD(&wire_list, sc, sc_list); + splx(s); + + return (0); +} + +int +wire_clone_destroy(struct ifnet *ifp) +{ + struct wire_softc *sc = ifp->if_softc; + int s; + + ifp->if_flags &= ~IFF_RUNNING; + + s = splnet(); + LIST_REMOVE(sc, sc_list); + splx(s); + + if_detach(ifp); + free(sc, M_DEVBUF, 0); + + return (0); +} + +int +wire_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ifreq *ifr = (struct ifreq *) data; + struct ifwirereq iwr; + struct wire_softc *sc; + int error = 0, s; + + s = splnet(); + sc = ifp->if_softc; + + switch (cmd) { + case SIOCSIFMTU: + if (ifr->ifr_mtu < MPE_MTU_MIN || + ifr->ifr_mtu > MPE_MTU_MAX) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP)) + ifp->if_flags |= IFF_RUNNING; + else + ifp->if_flags &= ~IFF_RUNNING; + break; + + case SIOCSETWIRECFG: + error = suser(curproc, 0); + if (error != 0) + break; + + error = copyin(ifr->ifr_data, &iwr, sizeof(iwr)); + if (error != 0) + break; + + /* Teardown all configuration if got no nexthop */ + if (satosin(&iwr.iwr_nexthop)->sin_addr.s_addr == 0) { + mpls_shim_set(ifp, &iwr.iwr_lshim, &sc->sc_lshim); + bzero(&sc->sc_rshim, sizeof(sc->sc_rshim)); + bzero(&sc->sc_nexthop, sizeof(sc->sc_nexthop)); + sc->sc_flags = 0; + sc->sc_type = 0; + break; + } + + /* Validate input */ + if (satosin(&iwr.iwr_nexthop)->sin_family != AF_INET || + iwr.iwr_lshim.shim_label > MPLS_LABEL_MAX || + iwr.iwr_lshim.shim_label <= MPLS_LABEL_RESERVED_MAX || + iwr.iwr_rshim.shim_label > MPLS_LABEL_MAX || + iwr.iwr_rshim.shim_label <= MPLS_LABEL_RESERVED_MAX) { + error = EINVAL; + break; + } + + /* Setup labels and create inbound route */ + iwr.iwr_lshim.shim_label = + htonl(iwr.iwr_lshim.shim_label << MPLS_LABEL_OFFSET); + iwr.iwr_rshim.shim_label = + htonl(iwr.iwr_rshim.shim_label << MPLS_LABEL_OFFSET); + + error = mpls_shim_set(ifp, &iwr.iwr_lshim, &sc->sc_lshim); + if (error != 0) + break; + + /* Apply configuration */ + sc->sc_flags = iwr.iwr_flags; + sc->sc_type = iwr.iwr_type; + bcopy(&iwr.iwr_rshim, &sc->sc_rshim, sizeof(sc->sc_rshim)); + sc->sc_rshim.shim_label |= MPLS_BOS_MASK; + + bzero(&sc->sc_nexthop, sizeof(sc->sc_nexthop)); + satosin(&sc->sc_nexthop)->sin_family = + satosin(&iwr.iwr_nexthop)->sin_family; + satosin(&sc->sc_nexthop)->sin_len = + sizeof(struct sockaddr_in); + satosin(&sc->sc_nexthop)->sin_addr.s_addr = + satosin(&iwr.iwr_nexthop)->sin_addr.s_addr; + break; + + case SIOCGETWIRECFG: + iwr.iwr_flags = sc->sc_flags; + iwr.iwr_type = sc->sc_type; + iwr.iwr_lshim.shim_label = + ((ntohl(sc->sc_lshim.shim_label & MPLS_LABEL_MASK)) >> + MPLS_LABEL_OFFSET); + iwr.iwr_rshim.shim_label = + ((ntohl(sc->sc_rshim.shim_label & MPLS_LABEL_MASK)) >> + MPLS_LABEL_OFFSET); + bcopy(&sc->sc_nexthop, &iwr.iwr_nexthop, + sizeof(iwr.iwr_nexthop)); + + error = copyout(&iwr, ifr->ifr_data, sizeof(iwr)); + break; + + default: + error = ENOTTY; + break; + } + splx(s); + return (error); +} + +void +wire_input(struct ifnet *ifp, struct mbuf *m) +{ + struct ether_header *eh; + struct shim_hdr *shim; + struct wire_softc *sc; + + sc = ifp->if_softc; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.ph_rtableid = ifp->if_rdomain; + + if (sc->sc_flags & IWR_FLAG_CONTROLWORD) { + shim = mtod(m, struct shim_hdr *); + m_adj(m, MPLS_HDRLEN); + + /* + * The first 4 bits identifies that this packet is a + * control word. If the control word is configured and + * we received an IP datagram we shall drop it. + */ + if (shim->shim_label & CW_ZERO_MASK) { + ifp->if_ierrors++; + m_freem(m); + return; + } + + /* We don't support fragmentation just yet. */ + if (shim->shim_label & CW_FRAG_MASK) { + ifp->if_ierrors++; + m_freem(m); + return; + } + } + +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf) + bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_IN); +#endif + + eh = mtod(m, struct ether_header *); + m_adj(m, ETHER_HDR_LEN); + + ether_input(ifp, eh, m); +} + +int +wire_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, + struct rtentry *rt) +{ + int error, s; + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + m_freem(m); + return (0); + } + + s = splnet(); + IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); + if (error) { + splx(s); + ifp->if_oerrors++; + return (error); + } + splx(s); + + if_start(ifp); + + return (error); +} + +void +wire_start(struct ifnet *ifp) +{ + struct mbuf *m; + struct rtentry *rt; + struct wire_softc *sc = ifp->if_softc; + struct shim_hdr *shim; + struct sockaddr sa; + int s; + + /* Generate destination address */ + bzero(&sa, sizeof(sa)); + bcopy(&sc->sc_nexthop, &sa, sizeof(sc->sc_nexthop)); + + /* + * XXX: lie about being MPLS, so mpls_output() get the TTL from + * the right place. + */ + satosin(&sa)->sin_family = AF_MPLS; + + for (;;) { + s = splnet(); + IFQ_DEQUEUE(&ifp->if_snd, m); + splx(s); + if (m == NULL) + return; + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + m_freem(m); + continue; + } + +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf) + bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_OUT); +#endif /* NBPFILTER */ + + rt = rtalloc1(&sc->sc_nexthop, RT_REPORT, 0); + if (rt == NULL) { + m_freem(m); + continue; + } + + if (sc->sc_flags & IWR_FLAG_CONTROLWORD) { + M_PREPEND(m, sizeof(*shim), M_NOWAIT); + if (m == NULL) { + RTFREE(rt); + continue; + } + + shim = mtod(m, struct shim_hdr *); + bzero(shim, sizeof(*shim)); + } + + M_PREPEND(m, sizeof(*shim), M_NOWAIT); + if (m == NULL) { + RTFREE(rt); + continue; + } + + shim = mtod(m, struct shim_hdr *); + bzero(shim, sizeof(*shim)); + shim->shim_label |= htonl(mpls_defttl) & MPLS_TTL_MASK; + shim->shim_label |= sc->sc_rshim.shim_label; + + /* XXX: MPLS only uses domain 0 */ + m->m_pkthdr.ph_rtableid = 0; + + mpls_output(rt->rt_ifp, m, &sa, rt); + + RTFREE(rt); + } +} + +int +wire_label_exists(const struct shim_hdr *shim) +{ + struct wire_softc *wr_sc; + + LIST_FOREACH(wr_sc, &wire_list, sc_list) + if (shim->shim_label == wr_sc->sc_lshim.shim_label) + return (1); + + return (0); +} diff --git sys/netmpls/mpls.h sys/netmpls/mpls.h index 0363d86..31ce94a 100644 --- sys/netmpls/mpls.h +++ sys/netmpls/mpls.h @@ -71,6 +71,9 @@ struct shim_hdr { #define MPLS_BOS_OFFSET 8 #define MPLS_TTL_MASK __MADDR(0x000000ffU) +#define CW_ZERO_MASK __MADDR(0xf0000000U) +#define CW_FRAG_MASK __MADDR(0x00300000U) + #define MPLS_BOS_ISSET(l) (((l) & MPLS_BOS_MASK) == MPLS_BOS_MASK) /* Reserved lavel values (RFC3032) */ @@ -140,6 +143,20 @@ struct rt_mpls { &mpls_mapttl_ip6 \ } +#define IWR_TYPE_NONE 0 +#define IWR_TYPE_ETHERNET 1 +#define IWR_TYPE_ETHERNET_TAGGED 2 + +#define IWR_FLAG_CONTROLWORD 0x1 + +struct ifwirereq { + u_int32_t iwr_flags; + u_int32_t iwr_type; /* pseudowire type */ + struct shim_hdr iwr_lshim; /* local label */ + struct shim_hdr iwr_rshim; /* remote label */ + struct sockaddr iwr_nexthop; +}; + #endif #ifdef _KERNEL @@ -177,6 +194,8 @@ void mpls_init(void); void mplsintr(void); int mpe_label_exists(const struct shim_hdr *); +int wire_label_exists(const struct shim_hdr *); +void wire_input(struct ifnet *, struct mbuf *); struct mbuf *mpls_shim_pop(struct mbuf *); struct mbuf *mpls_shim_swap(struct mbuf *, struct rt_mpls *); diff --git sys/netmpls/mpls_input.c sys/netmpls/mpls_input.c index 768de85..ff141e5 100644 --- sys/netmpls/mpls_input.c +++ sys/netmpls/mpls_input.c @@ -17,6 +17,7 @@ */ #include "mpe.h" +#include "wire.h" #include <sys/param.h> #include <sys/mbuf.h> @@ -236,6 +237,14 @@ do_v6: /* redo lookup with next label */ break; + ifp = rt->rt_ifp; +#if NWIRE > 0 + if (ifp->if_type == IFT_MPLSTUNNEL) { + wire_input(ifp, m); + goto done; + } +#endif + if (!rt->rt_gateway) { m_freem(m); goto done; @@ -278,6 +287,12 @@ do_v6: goto done; } #endif +#if NWIRE > 0 + if (ifp->if_type == IFT_MPLSTUNNEL) { + wire_input(ifp, m); + goto done; + } +#endif if (!rt->rt_gateway) { m_freem(m); goto done; diff --git sys/netmpls/mpls_shim.c sys/netmpls/mpls_shim.c index 3275dfb..2e6eb44 100644 --- sys/netmpls/mpls_shim.c +++ sys/netmpls/mpls_shim.c @@ -31,6 +31,7 @@ */ #include "mpe.h" +#include "wire.h" #include <sys/param.h> #include <sys/mbuf.h> @@ -110,6 +111,10 @@ mpls_shim_label_exists(const struct shim_hdr *shim) if (mpe_label_exists(shim)) return (1); #endif /* NMPE */ +#if NWIRE > 0 + if (wire_label_exists(shim)) + return (1); +#endif /* NWIRE */ return (0); } diff --git sys/sys/sockio.h sys/sys/sockio.h index 625ee67..e81c6cf 100644 --- sys/sys/sockio.h +++ sys/sys/sockio.h @@ -190,6 +190,9 @@ #define SIOCSLIFPHYTTL _IOW('i', 168, struct ifreq) /* set tunnel ttl */ #define SIOCGLIFPHYTTL _IOWR('i', 169, struct ifreq) /* get tunnel ttl */ +#define SIOCSETWIRECFG _IOW('i', 170, struct ifreq) /* set wire config */ +#define SIOCGETWIRECFG _IOWR('i', 171, struct ifreq) /* get wire config */ + #define SIOCSVH _IOWR('i', 245, struct ifreq) /* set carp param */ #define SIOCGVH _IOWR('i', 246, struct ifreq) /* get carp param */