diff --git a/share/man/man4/if_bridge.4 b/share/man/man4/if_bridge.4 index 0dc4b05a5811..66ca881711da 100644 --- a/share/man/man4/if_bridge.4 +++ b/share/man/man4/if_bridge.4 @@ -173,7 +173,24 @@ ifconfig bridge0 \e addm fxp7 stp fxp7 \e up .Ed +.Pp +The bridge can tunnel Ethernet across an IP internet using the EtherIP +protocol. +This can be combined with +.Xr ipsec 4 +to provide an encrypted connection. +Create a +.Xr gif 4 +interface and set the local and remote IP addresses for the +tunnel, these are reversed on the remote bridge. +.Bd -literal -offset indent +ifconfig gif0 create +ifconfig gif0 tunnel 1.2.3.4 5.6.7.8 up +ifconfig bridge0 create +ifconfig bridge0 addm fxp0 addm gif0 up +.Ed .Sh SEE ALSO +.Xr gif 4 , .Xr ipf 4 , .Xr ipfw 4 , .Xr pf 4 , diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index a91adbdf0799..d98052f3edb4 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -724,6 +724,9 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, (void) ifpromisc(ifs, 0); break; + case IFT_GIF: + break; + default: #ifdef DIAGNOSTIC panic("bridge_delete_member: impossible"); @@ -781,12 +784,15 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) if (ifs == bif->bif_ifp) return (EBUSY); - /* Allow the first member to define the MTU */ - if (LIST_EMPTY(&sc->sc_iflist)) - sc->sc_ifp->if_mtu = ifs->if_mtu; - else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { - if_printf(sc->sc_ifp, "invalid MTU for %s\n", ifs->if_xname); - return (EINVAL); + /* Allow the first Ethernet member to define the MTU */ + if (ifs->if_type != IFT_GIF) { + if (LIST_EMPTY(&sc->sc_iflist)) + sc->sc_ifp->if_mtu = ifs->if_mtu; + else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { + if_printf(sc->sc_ifp, "invalid MTU for %s\n", + ifs->if_xname); + return (EINVAL); + } } if (ifs->if_bridge == sc) @@ -810,6 +816,9 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) goto out; break; + case IFT_GIF: + break; + default: error = EINVAL; goto out; @@ -1553,6 +1562,9 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; + + if (dst_if->if_type == IFT_GIF) + continue; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; @@ -1944,6 +1956,8 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) * Unicast. Make sure it's not for us. */ LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if(bif->bif_ifp->if_type == IFT_GIF) + continue; /* It is destined for us. */ if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_dhost, ETHER_ADDR_LEN) == 0) { diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c index c946a1cb96ab..940e00eafed7 100644 --- a/sys/net/if_gif.c +++ b/sys/net/if_gif.c @@ -80,6 +80,8 @@ #endif /* INET6 */ #include +#include +#include #include #include @@ -99,6 +101,7 @@ void (*ng_gif_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, int af); void (*ng_gif_attach_p)(struct ifnet *ifp); void (*ng_gif_detach_p)(struct ifnet *ifp); +static void gif_start(struct ifnet *); static int gif_clone_create(struct if_clone *, int); static void gif_clone_destroy(struct ifnet *); @@ -177,6 +180,7 @@ gifattach0(sc) GIF2IFP(sc)->if_flags |= IFF_LINK2; #endif GIF2IFP(sc)->if_ioctl = gif_ioctl; + GIF2IFP(sc)->if_start = gif_start; GIF2IFP(sc)->if_output = gif_output; GIF2IFP(sc)->if_snd.ifq_maxlen = IFQ_MAXLEN; if_attach(GIF2IFP(sc)); @@ -289,6 +293,9 @@ gif_encapcheck(m, off, proto, arg) case IPPROTO_IPV6: break; #endif + case IPPROTO_ETHERIP: + break; + default: return 0; } @@ -321,6 +328,28 @@ gif_encapcheck(m, off, proto, arg) } } +static void +gif_start(struct ifnet *ifp) +{ + struct gif_softc *sc; + struct mbuf *m; + + sc = ifp->if_softc; + + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + for (;;) { + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; + + gif_output(ifp, m, sc->gif_pdst, NULL); + + } + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + return; +} + int gif_output(ifp, m, dst, rt) struct ifnet *ifp; @@ -395,13 +424,17 @@ gif_output(ifp, m, dst, rt) dst->sa_family = af; } + af = dst->sa_family; if (ifp->if_bpf) { - af = dst->sa_family; bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); } ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; + /* override to IPPROTO_ETHERIP for bridged traffic */ + if (ifp->if_bridge) + af = AF_LINK; + /* inner AF-specific encapsulation */ /* XXX should we check if our outer source is legal? */ @@ -410,12 +443,12 @@ gif_output(ifp, m, dst, rt) switch (sc->gif_psrc->sa_family) { #ifdef INET case AF_INET: - error = in_gif_output(ifp, dst->sa_family, m); + error = in_gif_output(ifp, af, m); break; #endif #ifdef INET6 case AF_INET6: - error = in6_gif_output(ifp, dst->sa_family, m); + error = in6_gif_output(ifp, af, m); break; #endif default: @@ -436,7 +469,8 @@ gif_input(m, af, ifp) int af; struct ifnet *ifp; { - int isr; + int isr, n; + struct etherip_header *eip; if (ifp == NULL) { /* just in case */ @@ -483,6 +517,35 @@ gif_input(m, af, ifp) isr = NETISR_IPV6; break; #endif + case AF_LINK: + n = sizeof(struct etherip_header) + sizeof(struct ether_header); + if (n > m->m_len) { + m = m_pullup(m, n); + if (m == NULL) { + ifp->if_ierrors++; + return; + } + } + + eip = mtod(m, struct etherip_header *); + if (eip->eip_ver != + (ETHERIP_VERSION & ETHERIP_VER_VERS_MASK)) { + /* discard unknown versions */ + m_freem(m); + return; + } + m_adj(m, sizeof(struct etherip_header)); + + m->m_flags &= ~(M_BCAST|M_MCAST); + m->m_pkthdr.rcvif = ifp; + + if (ifp->if_bridge) + BRIDGE_INPUT(ifp, m); + + if (m != NULL) + m_freem(m); + return; + default: if (ng_gif_input_orphan_p != NULL) (*ng_gif_input_orphan_p)(ifp, m, af); diff --git a/sys/net/if_gif.h b/sys/net/if_gif.h index 831c7f7d306a..698c67030ee6 100644 --- a/sys/net/if_gif.h +++ b/sys/net/if_gif.h @@ -85,6 +85,14 @@ struct gif_softc { #define MTAG_GIF 1080679712 #define MTAG_GIF_CALLED 0 +struct etherip_header { + u_int8_t eip_ver; /* version/reserved */ + u_int8_t eip_pad; /* required padding byte */ +}; +#define ETHERIP_VER_VERS_MASK 0x0f +#define ETHERIP_VER_RSVD_MASK 0xf0 +#define ETHERIP_VERSION 0x03 + /* Prototypes */ void gifattach0(struct gif_softc *); void gif_input(struct mbuf *, int, struct ifnet *); diff --git a/sys/netinet/in_gif.c b/sys/netinet/in_gif.c index 0eae2280991d..cc6b64d84ac9 100644 --- a/sys/netinet/in_gif.c +++ b/sys/netinet/in_gif.c @@ -100,6 +100,7 @@ in_gif_output(ifp, family, m) struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; struct ip iphdr; /* capsule IP header, host byte ordered */ + struct etherip_header eiphdr; int proto, error; u_int8_t tos; @@ -142,6 +143,20 @@ in_gif_output(ifp, family, m) break; } #endif /* INET6 */ + case AF_LINK: + proto = IPPROTO_ETHERIP; + eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK; + eiphdr.eip_pad = 0; + /* prepend Ethernet-in-IP header */ + M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); + if (m && m->m_len < sizeof(struct etherip_header)) + m = m_pullup(m, sizeof(struct etherip_header)); + if (m == NULL) + return ENOBUFS; + bcopy(&eiphdr, mtod(m, struct etherip_header *), + sizeof(struct etherip_header)); + break; + default: #ifdef DEBUG printf("in_gif_output: warning: unknown family %d passed\n", @@ -302,6 +317,10 @@ in_gif_input(m, off) break; } #endif /* INET6 */ + case IPPROTO_ETHERIP: + af = AF_LINK; + break; + default: ipstat.ips_nogif++; m_freem(m); diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index fefb4bcd7b4a..d8caddd7e6b6 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -257,6 +257,16 @@ struct protosw inetsw[] = { .pr_init = encap_init, .pr_usrreqs = &rip_usrreqs }, +{ + .pr_type = SOCK_RAW, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_ETHERIP, + .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, + .pr_input = encap4_input, + .pr_ctloutput = rip_ctloutput, + .pr_init = encap_init, + .pr_usrreqs = &rip_usrreqs +}, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index 632ff8f11f5c..41c34f2051af 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -93,6 +93,7 @@ in6_gif_output(ifp, family, m) struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; struct ip6_hdr *ip6; + struct etherip_header eiphdr; int proto, error; u_int8_t itos, otos; @@ -135,6 +136,20 @@ in6_gif_output(ifp, family, m) break; } #endif + case AF_LINK: + proto = IPPROTO_ETHERIP; + eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK; + eiphdr.eip_pad = 0; + /* prepend Ethernet-in-IP header */ + M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); + if (m && m->m_len < sizeof(struct etherip_header)) + m = m_pullup(m, sizeof(struct etherip_header)); + if (m == NULL) + return ENOBUFS; + bcopy(&eiphdr, mtod(m, struct etherip_header *), + sizeof(struct etherip_header)); + break; + default: #ifdef DEBUG printf("in6_gif_output: warning: unknown family %d passed\n", @@ -301,6 +316,10 @@ in6_gif_input(mp, offp, proto) break; } #endif + case IPPROTO_ETHERIP: + af = AF_LINK; + break; + default: ip6stat.ip6s_nogif++; m_freem(m);