The following patch implements the basics of the wire network interface.
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..41840cf
--- /dev/null
+++ sys/net/if_wire.c
@@ -0,0 +1,366 @@
+/*
+ * 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>
+
+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);
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ 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 */