Index: if_gre.c =================================================================== RCS file: /cvs/src/sys/net/if_gre.c,v retrieving revision 1.105 diff -u -p -r1.105 if_gre.c --- if_gre.c 19 Feb 2018 00:46:27 -0000 1.105 +++ if_gre.c 19 Feb 2018 01:27:01 -0000 @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include @@ -161,7 +163,10 @@ struct gre_tunnel { sa_family_t t_af; }; -static inline int +static int + gre_src_cmp(const struct gre_tunnel *, + const struct gre_tunnel *); +static int gre_cmp(const struct gre_tunnel *, const struct gre_tunnel *); static int gre_set_tunnel(struct gre_tunnel *, struct if_laddrreq *); @@ -173,8 +178,12 @@ static int gre_get_vnetid(struct gre_tun static int gre_del_vnetid(struct gre_tunnel *); static struct mbuf * - gre_encap(const struct gre_tunnel *, struct mbuf *, uint16_t, - uint8_t, uint8_t); + gre_encap_dst(const struct gre_tunnel *, const void *, + struct mbuf *, uint16_t, uint8_t, uint8_t); + +#define gre_encap(_t, _m, _p, _ttl, _tos) \ + gre_encap_dst((_t), &(_t)->t_dst, (_m), (_p), (_ttl), (_tos)) + static int gre_ip_output(const struct gre_tunnel *, struct mbuf *); @@ -185,10 +194,13 @@ static int gre_tunnel_ioctl(struct ifnet * layer 3 GRE tunnels */ -struct gre_softc { - struct ifnet sc_if; +struct greif { + struct gre_tunnel gi_tunnel; /* must be first */ + struct ifnet gi_if; +}; - struct gre_tunnel sc_tunnel; +struct gre_softc { + struct greif sc_gi; TAILQ_ENTRY(gre_softc) sc_entry; struct timeout sc_ka_send; @@ -206,6 +218,9 @@ struct gre_softc { int sc_ka_recvtm; }; +#define gre_if(_sc) (&(_sc)->sc_gi.gi_if) +#define gre_tunnel(_sc) (&(_sc)->sc_gi.gi_tunnel) + TAILQ_HEAD(gre_list, gre_softc); struct gre_keepalive { @@ -233,13 +248,62 @@ static void gre_link_state(struct gre_so static int gre_input_key(struct mbuf **, int *, int, int, struct gre_tunnel *); -static struct gre_softc * +static struct greif * gre_find(const struct gre_tunnel *); static void gre_keepalive_send(void *); static void gre_keepalive_recv(struct ifnet *ifp, struct mbuf *); static void gre_keepalive_hold(void *); +static struct mbuf * + gre_l3_encap_dst(const struct gre_tunnel *, const void *, + struct mbuf *m, sa_family_t); +#define gre_l3_encap(_t, _m, _af) \ + gre_l3_encap_dst((_t), &(_t)->t_dst, (_m), (_af)) + +/* Point to Multipoint GRE */ + +struct mgre_llinfo { + TAILQ_ENTRY(mgre_llinfo) ll_entry; + struct rtentry *ll_rt; /* backpointer to rtentry */ +}; +TAILQ_HEAD(mgre_peers, mgre_llinfo); + +struct mgre_softc { + struct greif sc_gi; /* must be first */ + RBT_ENTRY(mgre_softc) sc_entry; + + struct rwlock sc_pointlk; + struct mgre_peers sc_points; +}; + +RBT_HEAD(mgre_tree, mgre_softc); + +static inline int + mgre_cmp(const struct mgre_softc *, const struct mgre_softc *); + +RBT_PROTOTYPE(mgre_tree, mgre_softc, sc_entry, mgre_cmp); + +static int mgre_clone_create(struct if_clone *, int); +static int mgre_clone_destroy(struct ifnet *); + +struct if_clone mgre_cloner = + IF_CLONE_INITIALIZER("mgre", mgre_clone_create, mgre_clone_destroy); + +static void mgre_rtrequest(struct ifnet *, int, struct rtentry *); +static int mgre_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +static void mgre_start(struct ifnet *); +static int mgre_ioctl(struct ifnet *, u_long, caddr_t); + +static int mgre_up(struct mgre_softc *); +static int mgre_down(struct mgre_softc *); +static struct greif * + mgre_find(const struct gre_tunnel *); + +struct mgre_tree mgre_tree = RBT_INITIALIZER(); +struct pool mgre_pool; + /* * Ethernet GRE tunnels */ @@ -254,6 +318,9 @@ struct egre_softc { RBT_HEAD(egre_tree, egre_softc); +static inline int + egre_cmp(const struct egre_softc *, const struct egre_softc *); + RBT_PROTOTYPE(egre_tree, egre_softc, sc_entry, egre_cmp); static int egre_clone_create(struct if_clone *, int); @@ -296,6 +363,7 @@ void greattach(int n) { if_clone_attach(&gre_cloner); + if_clone_attach(&mgre_cloner); if_clone_attach(&egre_cloner); } @@ -306,10 +374,10 @@ gre_clone_create(struct if_clone *ifc, i struct ifnet *ifp; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); - snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d", + ifp = gre_if(sc); + snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", ifc->ifc_name, unit); - ifp = &sc->sc_if; ifp->if_softc = sc; ifp->if_type = IFT_TUNNEL; ifp->if_hdrlen = 24; /* IP + GRE */ @@ -321,8 +389,8 @@ gre_clone_create(struct if_clone *ifc, i ifp->if_ioctl = gre_ioctl; ifp->if_rtrequest = p2p_rtrequest; - sc->sc_tunnel.t_ttl = ip_defttl; - sc->sc_tunnel.t_df = htons(0); + gre_tunnel(sc)->t_ttl = ip_defttl; + gre_tunnel(sc)->t_df = htons(0); timeout_set(&sc->sc_ka_send, gre_keepalive_send, sc); timeout_set_proc(&sc->sc_ka_hold, gre_keepalive_hold, sc); @@ -362,11 +430,71 @@ gre_clone_destroy(struct ifnet *ifp) } static int +mgre_clone_create(struct if_clone *ifc, int unit) +{ + struct mgre_softc *sc; + struct ifnet *ifp; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); + ifp = gre_if(sc); + + rw_init(&sc->sc_pointlk, "mgrert"); + TAILQ_INIT(&sc->sc_points); + + snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", + ifc->ifc_name, unit); + + ifp->if_softc = sc; + ifp->if_mtu = GREMTU; + ifp->if_rtrequest = mgre_rtrequest; + ifp->if_output = mgre_output; + ifp->if_start = mgre_start; + ifp->if_ioctl = mgre_ioctl; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_xflags = IFXF_CLONED; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + + gre_tunnel(sc)->t_ttl = ip_defttl; + gre_tunnel(sc)->t_df = htons(0); + + if_attach(ifp); + if_alloc_sadl(ifp); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t)); +#endif + + return (0); +} + +static int +mgre_clone_destroy(struct ifnet *ifp) +{ + struct mgre_softc *sc = ifp->if_softc; + + NET_LOCK(); + if (ISSET(ifp->if_flags, IFF_RUNNING)) + mgre_down(sc); + NET_UNLOCK(); + + if_detach(ifp); + + free(sc, M_DEVBUF, sizeof(*sc)); + + return (0); +} + +static int egre_clone_create(struct if_clone *ifc, int unit) { struct egre_softc *sc; struct ifnet *ifp; + if (!mgre_pool.pr_size) { + pool_init(&mgre_pool, sizeof(struct mgre_llinfo), 0, + IPL_SOFTNET, 0, "mgrell", NULL); + } + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); ifp = &sc->sc_ac.ac_if; @@ -456,31 +584,43 @@ gre_input6(struct mbuf **mp, int *offp, } #endif /* INET6 */ -static struct gre_softc * +static struct greif * gre_find(const struct gre_tunnel *key) { struct gre_softc *sc; TAILQ_FOREACH(sc, &gre_list, sc_entry) { - if (gre_cmp(key, &sc->sc_tunnel) != 0) + if (gre_cmp(key, gre_tunnel(sc)) != 0) continue; - if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING)) + if (!ISSET(gre_if(sc)->if_flags, IFF_RUNNING)) continue; - return (sc); + return (&sc->sc_gi); } return (NULL); } +static struct greif * +mgre_find(const struct gre_tunnel *key) +{ + struct mgre_softc *sc; + + sc = RBT_FIND(mgre_tree, &mgre_tree, (const struct mgre_softc *)key); + if (sc == NULL) + return (NULL); + + return (&sc->sc_gi); +} + static int gre_input_key(struct mbuf **mp, int *offp, int type, int af, struct gre_tunnel *key) { struct mbuf *m = *mp; int iphlen = *offp, hlen; - struct gre_softc *sc; + struct greif *gi; struct ifnet *ifp; caddr_t buf; struct gre_header *gh; @@ -554,11 +694,14 @@ gre_input_key(struct mbuf **mp, int *off goto decline; } - sc = gre_find(key); - if (sc == NULL) - goto decline; + gi = gre_find(key); + if (gi == NULL) { + gi = mgre_find(key); + if (gi == NULL) + goto decline; + } - ifp = &sc->sc_if; + ifp = &gi->gi_if; switch (gh->gre_proto) { case htons(GRE_WCCP): { @@ -620,19 +763,22 @@ gre_input_key(struct mbuf **mp, int *off break; #endif case htons(0): + if (ifp->if_type == IFT_TUNNEL) { #if NBPFILTER > 0 - bpf_af = AF_UNSPEC; + bpf_af = AF_UNSPEC; #endif - input = gre_keepalive_recv; - break; + input = gre_keepalive_recv; + break; + } + /* FALLTHROUGH */ default: goto decline; } m_adj(m, hlen); - if (sc->sc_tunnel.t_ttl == -1) { + if (gi->gi_tunnel.t_ttl == -1) { m = m_pullup(m, ttloff + 1); if (m == NULL) return (IPPROTO_DONE); @@ -726,7 +872,7 @@ gre_keepalive_recv(struct ifnet *ifp, st int tick = ticks; if (sc->sc_ka_state == GRE_KA_NONE || - sc->sc_tunnel.t_rtableid != sc->sc_if.if_rdomain) + gre_tunnel(sc)->t_rtableid != gre_if(sc)->if_rdomain) goto drop; if (m->m_pkthdr.len < sizeof(*gk)) @@ -854,73 +1000,292 @@ gre_start(struct ifnet *ifp) { struct gre_softc *sc = ifp->if_softc; struct mbuf *m; - uint8_t ttl, tos; - int tttl; - uint16_t proto; + sa_family_t af; #if NBPFILTER > 0 caddr_t if_bpf; #endif - int ttloff; - tttl = sc->sc_tunnel.t_ttl; while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) { + af = m->m_pkthdr.ph_family; + #if NBPFILTER > 0 if_bpf = ifp->if_bpf; - if (if_bpf) { - int af = m->m_pkthdr.ph_family; + if (if_bpf) bpf_mtap_af(if_bpf, af, m, BPF_DIRECTION_OUT); +#endif + + m = gre_l3_encap(gre_tunnel(sc), m, af); + if (m == NULL || gre_ip_output(gre_tunnel(sc), m) != 0) { + ifp->if_oerrors++; + continue; } + } +} + +static struct mbuf * +gre_l3_encap_dst(const struct gre_tunnel *tunnel, const void *dst, + struct mbuf *m, sa_family_t af) +{ + uint16_t proto; + uint8_t ttl, tos; + int tttl = tunnel->t_ttl; + int ttloff; + + switch (af) { + case AF_INET: { + struct ip *ip; + + m = m_pullup(m, sizeof(*ip)); + if (m == NULL) + return (NULL); + + ip = mtod(m, struct ip *); + tos = ip->ip_tos; + + ttloff = offsetof(struct ip, ip_ttl); + proto = htons(ETHERTYPE_IP); + break; + } +#ifdef INET6 + case AF_INET6: + tos = 0; + ttloff = offsetof(struct ip6_hdr, ip6_hlim); + proto = htons(ETHERTYPE_IPV6); + break; #endif +#ifdef MPLS + case AF_MPLS: + ttloff = 3; + tos = 0; - switch (m->m_pkthdr.ph_family) { - case AF_INET: { - struct ip *ip; + if (m->m_flags & (M_BCAST | M_MCAST)) + proto = htons(ETHERTYPE_MPLS_MCAST); + else + proto = htons(ETHERTYPE_MPLS); + break; +#endif + default: + unhandled_af(af); + } - m = m_pullup(m, sizeof(*ip)); - if (m == NULL) - continue; + if (tttl == -1) { + m = m_pullup(m, ttloff + 1); + if (m == NULL) + return (NULL); - ip = mtod(m, struct ip *); - tos = ip->ip_tos; + ttl = *(m->m_data + ttloff); + } else + ttl = tttl; + + return (gre_encap_dst(tunnel, dst, m, proto, ttl, tos)); +} - ttloff = offsetof(struct ip, ip_ttl); - proto = htons(ETHERTYPE_IP); +static void +mgre_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) +{ + struct mgre_softc *sc = ifp->if_softc; + struct sockaddr *dest = rt_key(rt); + struct sockaddr *gate = rt->rt_gateway; + struct mgre_llinfo *ll = (struct mgre_llinfo *)rt->rt_llinfo; + sa_family_t af = gre_tunnel(sc)->t_af; + struct ifaddr *ifa; + + if (af == AF_UNSPEC) + return; + + if (ISSET(rt->rt_flags, RTF_GATEWAY|RTF_BROADCAST|RTF_MULTICAST)) + return; + + switch (req) { + case RTM_ADD: + if (ISSET(rt->rt_flags, RTF_CLONING) || + (ISSET(rt->rt_flags, RTF_LLINFO|RTF_LOCAL) && !ll)) { + /* + * Give this route an expiration time, even though + * it's a "permanent" route, so that routes cloned + * from it do not need their expiration time set. + */ + rt->rt_expire = time_uptime; + if (!ISSET(rt->rt_flags, RTF_CLONING)) + break; + } + + /*FALLTHROUGH*/ + case RTM_RESOLVE: + /* if ll already exists, this is a route change */ + if (ll != NULL) + break; + + /* otherwise it's a cloning route, or a manual route */ + ll = pool_get(&mgre_pool, PR_NOWAIT); + if (ll == NULL) break; + + ll->ll_rt = rt; + rt->rt_llinfo = (caddr_t)ll; + + SET(rt->rt_flags, RTF_LLINFO); + + rw_enter_write(&sc->sc_pointlk); + TAILQ_INSERT_TAIL(&sc->sc_points, ll, ll_entry); + rw_exit_write(&sc->sc_pointlk); + + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { + if (memcmp(dest, ifa->ifa_addr, dest->sa_len) == 0) { + KASSERT(ifa == rt->rt_ifa); + rt->rt_expire = 0; + break; + } } -#ifdef INET6 - case AF_INET6: - tos = 0; - ttloff = offsetof(struct ip6_hdr, ip6_hlim); - proto = htons(ETHERTYPE_IPV6); + break; + + case RTM_DELETE: + case RTM_INVALIDATE: + if (ll == NULL) break; + + rw_enter_write(&sc->sc_pointlk); + TAILQ_REMOVE(&sc->sc_points, ll, ll_entry); + rw_exit_write(&sc->sc_pointlk); + + rt->rt_llinfo = NULL; + CLR(rt->rt_flags, RTF_LLINFO); + pool_put(&mgre_pool, ll); + break; + } +} + +static int +mgre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt0) +{ + struct mgre_softc *sc = ifp->if_softc; + struct rtentry *rt; + struct m_tag *mtag; + int error = 0; + sa_family_t af; + const void *dstip; + + if (!gre_allow) { + error = EACCES; + goto drop; + } + + 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: - ttloff = 3; - tos = 0; + case AF_MPLS: +#endif + break; + default: + error = EAFNOSUPPORT; + goto drop; + } - if (m->m_flags & (M_BCAST | M_MCAST)) - proto = htons(ETHERTYPE_MPLS_MCAST); - else - proto = htons(ETHERTYPE_MPLS); + /* Try to limit infinite recursion through misconfiguration. */ + for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag; + mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) { + if (memcmp((caddr_t)(mtag + 1), &ifp->if_index, + sizeof(ifp->if_index)) == 0) { + error = EIO; + goto drop; + } + } + + if (ISSET(m->m_flags, M_MCAST|M_BCAST)) { + dstip = &gre_tunnel(sc)->t_dst; + } else { + struct sockaddr *gate; + + rt = rt_getll(rt0); + + /* chech rt_expire */ + if (ISSET(rt->rt_flags, RTF_REJECT)) { + error = (rt == rt0) ? EHOSTDOWN : EHOSTUNREACH; + goto drop; + } + if (!ISSET(rt->rt_flags, RTF_LLINFO)) { + error = EINVAL; + goto drop; + } + + gate = rt->rt_gateway; + af = gate->sa_family; + if (af != gre_tunnel(sc)->t_af) { + error = EAFNOSUPPORT; + goto drop; + } + switch (af) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)gate; + dstip = &sin->sin_addr; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)gate; + dstip = &sin6->sin6_addr; break; + } #endif default: - unhandled_af(m->m_pkthdr.ph_family); + unhandled_af(af); + /* NOTREACHED */ } + } - if (tttl == -1) { - m = m_pullup(m, ttloff + 1); - if (m == NULL) - continue; + m = gre_l3_encap_dst(gre_tunnel(sc), dstip, m, dst->sa_family); + if (m == NULL) + return (ENOBUFS); - ttl = *(m->m_data + ttloff); - } else - ttl = tttl; + mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); + if (mtag == NULL) { + error = ENOBUFS; + goto drop; + } + memcpy((caddr_t)(mtag + 1), &ifp->if_index, sizeof(ifp->if_index)); + m_tag_prepend(m, mtag); - m = gre_encap(&sc->sc_tunnel, m, proto, ttl, tos); - if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) { + m->m_pkthdr.ph_family = dst->sa_family; + + error = if_enqueue(ifp, m); + if (error) + ifp->if_oerrors++; + return (error); + +drop: + m_freem(m); + return (error); +} + +static void +mgre_start(struct ifnet *ifp) +{ + struct mgre_softc *sc = ifp->if_softc; + struct mbuf *m; +#if NBPFILTER > 0 + caddr_t if_bpf; +#endif + + while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) { +#if NBPFILTER > 0 + if_bpf = ifp->if_bpf; + if (if_bpf) {/* XXX */ + bpf_mtap_af(if_bpf, gre_tunnel(sc)->t_af, m, + BPF_DIRECTION_OUT); + } +#endif + + if (gre_ip_output(gre_tunnel(sc), m) != 0) { ifp->if_oerrors++; continue; } @@ -968,9 +1333,10 @@ egre_start(struct ifnet *ifp) } static struct mbuf * -gre_encap(const struct gre_tunnel *tunnel, struct mbuf *m, uint16_t proto, - uint8_t ttl, uint8_t tos) +gre_encap_dst(const struct gre_tunnel *tunnel, const void *d, struct mbuf *m, + uint16_t proto, uint8_t ttl, uint8_t tos) { + const union gre_addr *dst = d; struct gre_header *gh; struct gre_h_key *gkh; int hlen; @@ -1008,7 +1374,7 @@ gre_encap(const struct gre_tunnel *tunne ip->ip_ttl = ttl; ip->ip_p = IPPROTO_GRE; ip->ip_src = tunnel->t_src4; - ip->ip_dst = tunnel->t_dst4; + ip->ip_dst = dst->in4; break; } #ifdef INET6 @@ -1028,7 +1394,7 @@ gre_encap(const struct gre_tunnel *tunne ip6->ip6_nxt = IPPROTO_GRE; ip6->ip6_hlim = ttl; ip6->ip6_src = tunnel->t_src6; - ip6->ip6_dst = tunnel->t_dst6; + ip6->ip6_dst = dst->in6; if (tunnel->t_df) SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT); @@ -1195,15 +1561,72 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, } /* commit */ - sc->sc_tunnel.t_ttl = ifr->ifr_ttl; + gre_tunnel(sc)->t_ttl = ifr->ifr_ttl; break; case SIOCGLIFPHYTTL: - ifr->ifr_ttl = sc->sc_tunnel.t_ttl; + ifr->ifr_ttl = gre_tunnel(sc)->t_ttl; break; default: - error = gre_tunnel_ioctl(ifp, &sc->sc_tunnel, cmd, data); + error = gre_tunnel_ioctl(ifp, gre_tunnel(sc), cmd, data); + break; + } + + return (error); +} + +static int +mgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct mgre_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 = mgre_up(sc); + else + error = 0; + } else { + if (ISSET(ifp->if_flags, IFF_RUNNING)) + error = mgre_down(sc); + } + break; + + case SIOCSLIFPHYTTL: + if (ifr->ifr_ttl != -1 && + (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff)) { + error = EINVAL; + break; + } + + /* commit */ + gre_tunnel(sc)->t_ttl = (uint8_t)ifr->ifr_ttl; + break; + + case SIOCGLIFPHYTTL: + ifr->ifr_ttl = gre_tunnel(sc)->t_ttl; + break; + + case SIOCSLIFPHYADDR: + /* XXX */ + case SIOCSVNETID: + case SIOCDVNETID: + case SIOCDIFPHYADDR: + case SIOCSLIFPHYRTABLE: + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + error = EBUSY; + break; + } + + /* FALLTHROUGH */ + default: + error = gre_tunnel_ioctl(ifp, gre_tunnel(sc), cmd, data); break; } @@ -1271,7 +1694,7 @@ static int gre_up(struct gre_softc *sc) { NET_ASSERT_LOCKED(); - SET(sc->sc_if.if_flags, IFF_RUNNING); + SET(gre_if(sc)->if_flags, IFF_RUNNING); if (sc->sc_ka_state != GRE_KA_NONE) { arc4random_buf(&sc->sc_ka_key, sizeof(sc->sc_ka_key)); @@ -1290,7 +1713,7 @@ static int gre_down(struct gre_softc *sc) { NET_ASSERT_LOCKED(); - CLR(sc->sc_if.if_flags, IFF_RUNNING); + CLR(gre_if(sc)->if_flags, IFF_RUNNING); if (sc->sc_ka_state != GRE_KA_NONE) { if (!timeout_del(&sc->sc_ka_hold)) @@ -1309,7 +1732,7 @@ gre_down(struct gre_softc *sc) static void gre_link_state(struct gre_softc *sc) { - struct ifnet *ifp = &sc->sc_if; + struct ifnet *ifp = gre_if(sc); int link_state = LINK_STATE_UNKNOWN; if (ISSET(ifp->if_flags, IFF_RUNNING)) { @@ -1337,6 +1760,8 @@ gre_keepalive_send(void *arg) { struct gre_tunnel t; struct gre_softc *sc = arg; + struct gre_tunnel *tunnel = gre_tunnel(sc); + struct ifnet *ifp = gre_if(sc); struct mbuf *m; struct gre_keepalive *gk; SIPHASH_CTX ctx; @@ -1344,9 +1769,9 @@ gre_keepalive_send(void *arg) uint16_t proto; uint8_t ttl; - if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING) || + if (!ISSET(ifp->if_flags, IFF_RUNNING) || sc->sc_ka_state == GRE_KA_NONE || - sc->sc_tunnel.t_rtableid != sc->sc_if.if_rdomain) + tunnel->t_rtableid != ifp->if_rdomain) return; /* this is really conservative */ @@ -1381,23 +1806,24 @@ gre_keepalive_send(void *arg) SipHash24_Update(&ctx, &gk->gk_random, sizeof(gk->gk_random)); SipHash24_Final(gk->gk_digest, &ctx); - ttl = sc->sc_tunnel.t_ttl == -1 ? ip_defttl : sc->sc_tunnel.t_ttl; + ttl = tunnel->t_ttl == -1 ? ip_defttl : tunnel->t_ttl; - t.t_af = sc->sc_tunnel.t_af; - t.t_df = sc->sc_tunnel.t_df; - t.t_src = sc->sc_tunnel.t_dst; - t.t_dst = sc->sc_tunnel.t_src; - t.t_key = sc->sc_tunnel.t_key; - t.t_key_mask = sc->sc_tunnel.t_key_mask; + t.t_af = tunnel->t_af; + t.t_df = tunnel->t_df; + t.t_src = tunnel->t_dst; + t.t_dst = tunnel->t_src; + t.t_key = tunnel->t_key; + t.t_key_mask = tunnel->t_key_mask; m = gre_encap(&t, m, htons(0), ttl, 0); if (m == NULL) return; - switch (sc->sc_tunnel.t_af) { + switch (tunnel->t_af) { case AF_INET: { struct ip *ip; + /* ip_output isn't able to do this for us */ ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(*ip) >> 2; @@ -1418,11 +1844,11 @@ gre_keepalive_send(void *arg) /* * put it in the tunnel */ - m = gre_encap(&sc->sc_tunnel, m, proto, ttl, 0); + m = gre_encap(tunnel, m, proto, ttl, 0); if (m == NULL) return; - gre_ip_output(&sc->sc_tunnel, m); + gre_ip_output(tunnel, m); timeout_add_sec(&sc->sc_ka_send, sc->sc_ka_timeo); } @@ -1432,7 +1858,7 @@ gre_keepalive_hold(void *arg) { struct gre_softc *sc = arg; - if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING) || + if (!ISSET(gre_if(sc)->if_flags, IFF_RUNNING) || sc->sc_ka_state == GRE_KA_NONE) return; @@ -1612,6 +2038,36 @@ gre_del_vnetid(struct gre_tunnel *tunnel } static int +mgre_up(struct mgre_softc *sc) +{ + if (gre_tunnel(sc)->t_af == AF_UNSPEC) + return (EDESTADDRREQ); + + NET_ASSERT_LOCKED(); + + if (RBT_INSERT(mgre_tree, &mgre_tree, sc) != NULL) + return (EADDRINUSE); + + SET(gre_if(sc)->if_flags, IFF_RUNNING); + + return (0); +} + +static int +mgre_down(struct mgre_softc *sc) +{ + NET_ASSERT_LOCKED(); + + CLR(gre_if(sc)->if_flags, IFF_RUNNING); + + RBT_REMOVE(mgre_tree, &mgre_tree, sc); + + /* barrier? */ + + return (0); +} + +static int egre_up(struct egre_softc *sc) { if (sc->sc_tunnel.t_af == AF_UNSPEC) @@ -1699,32 +2155,12 @@ gre_ip_cmp(int af, const union gre_addr } static int -gre_cmp(const struct gre_tunnel *a, const struct gre_tunnel *b) +gre_src_cmp(const struct gre_tunnel *a, const struct gre_tunnel *b) { uint32_t ka, kb; uint32_t mask; int rv; - /* sort by routing table */ - if (a->t_rtableid > b->t_rtableid) - return (1); - if (a->t_rtableid < b->t_rtableid) - return (-1); - - /* sort by address */ - if (a->t_af > b->t_af) - return (1); - if (a->t_af < b->t_af) - return (-1); - - rv = gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst); - if (rv != 0) - return (rv); - - rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src); - if (rv != 0) - return (rv); - /* is K set at all? */ ka = a->t_key_mask & GRE_KEY_ENTROPY; kb = b->t_key_mask & GRE_KEY_ENTROPY; @@ -1750,13 +2186,52 @@ gre_cmp(const struct gre_tunnel *a, cons return (-1); } + /* sort by routing table */ + if (a->t_rtableid > b->t_rtableid) + return (1); + if (a->t_rtableid < b->t_rtableid) + return (-1); + + /* sort by address */ + if (a->t_af > b->t_af) + return (1); + if (a->t_af < b->t_af) + return (-1); + + rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src); + if (rv != 0) + return (rv); + return (0); } static int +gre_cmp(const struct gre_tunnel *a, const struct gre_tunnel *b) +{ + int rv; + + rv = gre_src_cmp(a, b); + if (rv != 0) + return (rv); + + rv = gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst); + if (rv != 0) + return (rv); + + return (0); +} + +static inline int +mgre_cmp(const struct mgre_softc *a, const struct mgre_softc *b) +{ + return (gre_src_cmp(gre_tunnel(a), gre_tunnel(b))); +} + +static inline int egre_cmp(const struct egre_softc *a, const struct egre_softc *b) { return (gre_cmp(&a->sc_tunnel, &b->sc_tunnel)); } RBT_GENERATE(egre_tree, egre_softc, sc_entry, egre_cmp); +RBT_GENERATE(mgre_tree, mgre_softc, sc_entry, mgre_cmp);