commit b10d3201cebfa192a681e867558a81ff5b3b136e Author: Andrey V. Elsukov Date: Tue Jan 13 22:03:38 2026 +0300 if_gre: rework ioctl code to avoid waiting diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c index ca9c4835daf6..f58155715f8e 100644 --- a/sys/net/if_gre.c +++ b/sys/net/if_gre.c @@ -2,7 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1998 The NetBSD Foundation, Inc. - * Copyright (c) 2014, 2018 Andrey V. Elsukov + * Copyright (c) 2014-2026 Andrey V. Elsukov * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -114,6 +114,8 @@ static int gre_transmit(struct ifnet *, struct mbuf *); static int gre_ioctl(struct ifnet *, u_long, caddr_t); static int gre_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); +static void gre_free_socket(epoch_context_t); +static void gre_free_priv(epoch_context_t); static void gre_delete_tunnel(struct gre_softc *); SYSCTL_DECL(_net_link); @@ -139,7 +141,6 @@ SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, static void vnet_gre_init(const void *unused __unused) { - V_gre_cloner = if_clone_simple(grename, gre_clone_create, gre_clone_destroy, 0); #ifdef INET @@ -155,7 +156,6 @@ VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, static void vnet_gre_uninit(const void *unused __unused) { - if_clone_detach(V_gre_cloner); #ifdef INET in_gre_uninit(); @@ -174,6 +174,7 @@ gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) struct gre_softc *sc; sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); + sc->gre_data = malloc(sizeof(struct gre_priv), M_GRE, M_WAITOK | M_ZERO); sc->gre_fibnum = curthread->td_proc->p_fibnum; GRE2IFP(sc) = if_alloc(IFT_TUNNEL); GRE2IFP(sc)->if_softc = sc; @@ -218,13 +219,14 @@ gre_clone_destroy(struct ifnet *ifp) sx_xlock(&gre_ioctl_sx); sc = ifp->if_softc; gre_delete_tunnel(sc); + sx_xunlock(&gre_ioctl_sx); + GRE_WAIT(); bpfdetach(ifp); if_detach(ifp); ifp->if_softc = NULL; - sx_xunlock(&gre_ioctl_sx); - GRE_WAIT(); if_free(ifp); + free(sc->gre_data, M_GRE); free(sc, M_GRE); } @@ -303,6 +305,7 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if ((error = copyin(ifr_data_get_ptr(ifr), &opt, sizeof(opt))) != 0) break; + /* Check if any option has actually been changed */ if (cmd == GRESKEY) { if (sc->gre_key == opt) break; @@ -354,10 +357,6 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) sc->gre_port = opt; break; } - /* - * XXX: Do we need to initiate change of interface - * state here? - */ break; case GREGKEY: error = copyout(&sc->gre_key, ifr_data_get_ptr(ifr), @@ -392,29 +391,51 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) return (error); } -static void -gre_delete_tunnel(struct gre_softc *sc) +void +gre_update_priv(struct gre_softc *sc, int hlen) { - struct gre_socket *gs; + struct gre_priv *priv = sc->gre_data; + MPASS(sc->gre_family != 0); sx_assert(&gre_ioctl_sx, SA_XLOCKED); - if (sc->gre_family != 0) { - CK_LIST_REMOVE(sc, chain); - CK_LIST_REMOVE(sc, srchash); - GRE_WAIT(); - free(sc->gre_hdr, M_GRE); - sc->gre_family = 0; + + /* Detach gre_data, schedule deferred free, and allocate new priv */ + CK_LIST_REMOVE(sc, chain); /* hashtbl or sockhash */ + CK_LIST_REMOVE(sc, srchash); + NET_EPOCH_CALL(gre_free_priv, &priv->epoch_ctx); + sc->gre_family = 0; + sc->gre_data = malloc(sizeof(struct gre_priv), M_GRE, M_WAITOK | M_ZERO); + if (hlen > 0) + bcopy(priv, sc->gre_data, hlen); +} + +void +gre_update_socket(struct gre_softc *sc) +{ + struct gre_socket *gs = sc->gre_so; + + MPASS(gs != NULL); + sx_assert(&gre_ioctl_sx, SA_XLOCKED); + + if (CK_LIST_EMPTY(&gs->list)) { + CK_LIST_REMOVE(gs, chain); + NET_EPOCH_CALL(gre_free_socket, &gs->epoch_ctx); + sc->gre_so = NULL; } +} + +static void +gre_delete_tunnel(struct gre_softc *sc) +{ + sx_assert(&gre_ioctl_sx, SA_XLOCKED); + if (sc->gre_family != 0) + gre_update_priv(sc, 0); /* * If this Tunnel was the last one that could use UDP socket, * we should unlink socket from hash table and close it. */ - if ((gs = sc->gre_so) != NULL && CK_LIST_EMPTY(&gs->list)) { - CK_LIST_REMOVE(gs, chain); - soclose(gs->so); - NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); - sc->gre_so = NULL; - } + if (sc->gre_so != NULL) + gre_update_socket(sc); GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; if_link_state_change(GRE2IFP(sc), LINK_STATE_DOWN); } @@ -436,19 +457,28 @@ gre_hashinit(void) void gre_hashdestroy(struct gre_list *hash) { - free(hash, M_GRE); } void -gre_sofree(epoch_context_t ctx) +gre_free_socket(epoch_context_t ctx) { struct gre_socket *gs; gs = __containerof(ctx, struct gre_socket, epoch_ctx); + soclose(gs->so); free(gs, M_GRE); } +static void +gre_free_priv(epoch_context_t ctx) +{ + struct gre_priv *priv; + + priv = __containerof(ctx, struct gre_priv, epoch_ctx); + free(priv, M_GRE); +} + static __inline uint16_t gre_cksum_add(uint16_t sum, uint16_t a) { @@ -461,7 +491,6 @@ gre_cksum_add(uint16_t sum, uint16_t a) void gre_update_udphdr(struct gre_softc *sc, struct udphdr *udp, uint16_t csum) { - sx_assert(&gre_ioctl_sx, SA_XLOCKED); MPASS(sc->gre_options & GRE_UDPENCAP); @@ -474,30 +503,33 @@ gre_update_udphdr(struct gre_softc *sc, struct udphdr *udp, uint16_t csum) void gre_update_hdr(struct gre_softc *sc, struct grehdr *gh) { + struct gre_priv *priv = sc->gre_data; uint32_t *opts; uint16_t flags; sx_assert(&gre_ioctl_sx, SA_XLOCKED); - flags = 0; opts = gh->gre_opts; if (sc->gre_options & GRE_ENABLE_CSUM) { flags |= GRE_FLAGS_CP; - sc->gre_hlen += 2 * sizeof(uint16_t); + priv->priv_hlen += 2 * sizeof(uint16_t); *opts++ = 0; } if (sc->gre_key != 0) { flags |= GRE_FLAGS_KP; - sc->gre_hlen += sizeof(uint32_t); + priv->priv_hlen += sizeof(uint32_t); *opts++ = htonl(sc->gre_key); } if (sc->gre_options & GRE_ENABLE_SEQ) { flags |= GRE_FLAGS_SP; - sc->gre_hlen += sizeof(uint32_t); + priv->priv_hlen += sizeof(uint32_t); *opts++ = 0; } else sc->gre_oseq = 0; gh->gre_flags = htons(flags); + + /* update priv_options, this field will be used on transmit */ + priv->priv_options = sc->gre_options; } int @@ -513,6 +545,8 @@ gre_input(struct mbuf *m, int off, int proto, void *arg) uint16_t flags; int hlen, isr, af; + NET_EPOCH_ASSERT(); + ifp = GRE2IFP(sc); hlen = off + sizeof(struct grehdr) + 4 * sizeof(uint32_t); if (m->m_pkthdr.len < hlen) @@ -527,7 +561,7 @@ gre_input(struct mbuf *m, int off, int proto, void *arg) if (flags & ~GRE_FLAGS_MASK) goto drop; opts = gh->gre_opts; - hlen = 2 * sizeof(uint16_t); + hlen = sizeof(struct grehdr); if (flags & GRE_FLAGS_CP) { /* reserved1 field must be zero */ if (((uint16_t *)opts)[1] != 0) @@ -641,11 +675,11 @@ gre_setseqn(struct grehdr *gh, uint32_t seq) } static uint32_t -gre_flowid(struct gre_softc *sc, struct mbuf *m, uint32_t af) +gre_flowid(struct gre_priv *priv, struct mbuf *m, uint32_t af) { uint32_t flowid = 0; - if ((sc->gre_options & GRE_UDPENCAP) == 0 || sc->gre_port != 0) + if ((priv->priv_options & GRE_UDPENCAP) == 0) return (flowid); switch (af) { #ifdef INET @@ -683,6 +717,7 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m) { GRE_RLOCK_TRACKER; struct gre_softc *sc; + struct gre_priv *priv; struct grehdr *gh; struct udphdr *uh; uint32_t af, flowid; @@ -700,10 +735,11 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m) #endif error = ENETDOWN; sc = ifp->if_softc; + priv = sc->gre_data; if ((ifp->if_flags & IFF_MONITOR) != 0 || (ifp->if_flags & IFF_UP) == 0 || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || - sc->gre_family == 0 || + priv->priv_family == 0 || (error = if_tunnel_check_nesting(ifp, m, MTAG_GRE, V_max_gre_nesting)) != 0) { m_freem(m); @@ -712,14 +748,14 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m) af = m->m_pkthdr.csum_data; BPF_MTAP2(ifp, &af, sizeof(af), m); m->m_flags &= ~(M_BCAST|M_MCAST); - flowid = gre_flowid(sc, m, af); + flowid = gre_flowid(priv, m, af); M_SETFIB(m, sc->gre_fibnum); - M_PREPEND(m, sc->gre_hlen, M_NOWAIT); + M_PREPEND(m, priv->priv_hlen, M_NOWAIT); if (m == NULL) { error = ENOBUFS; goto drop; } - bcopy(sc->gre_hdr, mtod(m, void *), sc->gre_hlen); + bcopy(priv, mtod(m, void *), priv->priv_hlen); /* Determine GRE proto */ switch (af) { #ifdef INET @@ -738,7 +774,7 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m) goto drop; } /* Determine offset of GRE header */ - switch (sc->gre_family) { + switch (priv->priv_family) { #ifdef INET case AF_INET: len = sizeof(struct ip); @@ -754,7 +790,7 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m) error = ENETDOWN; goto drop; } - if (sc->gre_options & GRE_UDPENCAP) { + if (priv->priv_options & GRE_UDPENCAP) { uh = (struct udphdr *)mtodo(m, len); uh->uh_sport |= htons(V_ipport_hifirstauto) | (flowid >> 16) | (flowid & 0xFFFF); @@ -763,28 +799,28 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m) uh->uh_ulen = htons(m->m_pkthdr.len - len); uh->uh_sum = gre_cksum_add(uh->uh_sum, htons(m->m_pkthdr.len - len + IPPROTO_UDP)); - m->m_pkthdr.csum_flags = sc->gre_csumflags; + m->m_pkthdr.csum_flags = priv->priv_csumflags; m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); len += sizeof(struct udphdr); } gh = (struct grehdr *)mtodo(m, len); gh->gre_proto = proto; - if (sc->gre_options & GRE_ENABLE_SEQ) + if (priv->priv_options & GRE_ENABLE_SEQ) gre_setseqn(gh, sc->gre_oseq++); - if (sc->gre_options & GRE_ENABLE_CSUM) { + if (priv->priv_options & GRE_ENABLE_CSUM) { *(uint16_t *)gh->gre_opts = in_cksum_skip(m, m->m_pkthdr.len, len); } len = m->m_pkthdr.len - len; - switch (sc->gre_family) { + switch (priv->priv_family) { #ifdef INET case AF_INET: - error = in_gre_output(m, af, sc->gre_hlen); + error = in_gre_output(m, af, priv->priv_hlen); break; #endif #ifdef INET6 case AF_INET6: - error = in6_gre_output(m, af, sc->gre_hlen, flowid); + error = in6_gre_output(m, af, priv->priv_hlen, flowid); break; #endif default: @@ -811,10 +847,11 @@ gre_qflush(struct ifnet *ifp __unused) static int gremodevent(module_t mod, int type, void *data) { - switch (type) { case MOD_LOAD: + break; case MOD_UNLOAD: + GRE_WAIT(); break; default: return (EOPNOTSUPP); diff --git a/sys/net/if_gre.h b/sys/net/if_gre.h index 67e4d88426fb..f8898fdacc55 100644 --- a/sys/net/if_gre.h +++ b/sys/net/if_gre.h @@ -82,6 +82,28 @@ struct gre_socket { struct epoch_context epoch_ctx; }; +struct gre_priv { + union { +#ifdef INET + struct greip priv_ip; + struct greudp priv_udp; +#endif +#ifdef INET6 + struct greip6 priv_ip6; + struct greudp6 priv_udp6; +#endif + }; + uint16_t priv_csum, _reserved; + uint32_t priv_key, priv_seq; + + /* the following fields are not part of header */ + int priv_family; + uint32_t priv_options; + uint32_t priv_csumflags; + uint32_t priv_hlen; + struct epoch_context epoch_ctx; +}; + struct gre_softc { struct ifnet *gre_ifp; int gre_family; /* AF of delivery header */ @@ -89,21 +111,10 @@ struct gre_softc { uint32_t gre_oseq; uint32_t gre_key; uint32_t gre_options; - uint32_t gre_csumflags; uint32_t gre_port; u_int gre_fibnum; - u_int gre_hlen; /* header size */ - union { - void *hdr; -#ifdef INET - struct greip *iphdr; - struct greudp *udphdr; -#endif -#ifdef INET6 - struct greip6 *ip6hdr; - struct greudp6 *udp6hdr; -#endif - } gre_uhdr; + + struct gre_priv *gre_data; struct gre_socket *gre_so; CK_LIST_ENTRY(gre_softc) chain; @@ -121,16 +132,15 @@ MALLOC_DECLARE(M_GRE); #define GRE_RUNLOCK() epoch_exit_preempt(net_epoch_preempt, &gre_et) #define GRE_WAIT() epoch_wait_preempt(net_epoch_preempt) -#define gre_hdr gre_uhdr.hdr -#define gre_iphdr gre_uhdr.iphdr -#define gre_ip6hdr gre_uhdr.ip6hdr -#define gre_udphdr gre_uhdr.udphdr -#define gre_udp6hdr gre_uhdr.udp6hdr +#define gre_iphdr gre_data->priv_ip +#define gre_ip6hdr gre_data->priv_ip6 +#define gre_udphdr gre_data->priv_udp +#define gre_udp6hdr gre_data->priv_udp6 -#define gre_oip gre_iphdr->gi_ip -#define gre_udp gre_udphdr->gi_udp -#define gre_oip6 gre_ip6hdr->gi6_ip6 -#define gre_udp6 gre_udp6hdr->gi6_udp +#define gre_oip gre_iphdr.gi_ip +#define gre_udp gre_udphdr.gi_udp +#define gre_oip6 gre_ip6hdr.gi6_ip6 +#define gre_udp6 gre_udp6hdr.gi6_udp struct gre_list *gre_hashinit(void); void gre_hashdestroy(struct gre_list *); @@ -138,7 +148,8 @@ void gre_hashdestroy(struct gre_list *); int gre_input(struct mbuf *, int, int, void *); void gre_update_hdr(struct gre_softc *, struct grehdr *); void gre_update_udphdr(struct gre_softc *, struct udphdr *, uint16_t); -void gre_sofree(epoch_context_t); +void gre_update_priv(struct gre_softc *, int); +void gre_update_socket(struct gre_softc *); void in_gre_init(void); void in_gre_uninit(void); diff --git a/sys/netinet/ip_gre.c b/sys/netinet/ip_gre.c index c9356edb0608..ce915a04173b 100644 --- a/sys/netinet/ip_gre.c +++ b/sys/netinet/ip_gre.c @@ -2,7 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1998 The NetBSD Foundation, Inc. - * Copyright (c) 2014, 2018 Andrey V. Elsukov + * Copyright (c) 2014-2026 Andrey V. Elsukov * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -128,6 +128,7 @@ in_gre_checkdup(const struct gre_softc *sc, in_addr_t src, in_addr_t dst, struct gre_softc *tmp; struct gre_socket *gs; + /* NOTE: we are protected with gre_ioctl_sx lock */ if (sc->gre_family == AF_INET && sc->gre_oip.ip_src.s_addr == src && sc->gre_oip.ip_dst.s_addr == dst && @@ -184,13 +185,12 @@ in_gre_lookup(const struct mbuf *m, int off, int proto, void **arg) * Check that ingress address belongs to local host. */ static void -in_gre_set_running(struct gre_softc *sc) +in_gre_set_running(struct ifnet *ifp, struct in_addr src) { - - if (in_localip(sc->gre_oip.ip_src)) - GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; + if (in_localip(src)) + ifp->if_drv_flags |= IFF_DRV_RUNNING; else - GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } /* @@ -214,7 +214,7 @@ in_gre_srcaddr(void *arg __unused, const struct sockaddr *sa, CK_LIST_FOREACH(sc, &GRE_SRCHASH(sin->sin_addr.s_addr), srchash) { if (sc->gre_oip.ip_src.s_addr != sin->sin_addr.s_addr) continue; - in_gre_set_running(sc); + in_gre_set_running(GRE2IFP(sc), sin->sin_addr); } } @@ -266,12 +266,8 @@ in_gre_setup_socket(struct gre_softc *sc) if (gs != NULL) { s = __containerof(gs, struct in_gre_socket, base); if (s->addr != addr) { - if (CK_LIST_EMPTY(&gs->list)) { - CK_LIST_REMOVE(gs, chain); - soclose(gs->so); - NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); - } - gs = sc->gre_so = NULL; + gre_update_socket(sc); + gs = NULL; } } @@ -349,21 +345,23 @@ static int in_gre_attach(struct gre_softc *sc) { struct epoch_tracker et; + struct gre_priv *priv = sc->gre_data; struct grehdr *gh; int error; + sc->gre_family = priv->priv_family = AF_INET; if (sc->gre_options & GRE_UDPENCAP) { - sc->gre_csumflags = CSUM_UDP; - sc->gre_hlen = sizeof(struct greudp); + priv->priv_csumflags = CSUM_UDP; + priv->priv_hlen = sizeof(struct greudp); sc->gre_oip.ip_p = IPPROTO_UDP; - gh = &sc->gre_udphdr->gi_gre; + gh = &sc->gre_udphdr.gi_gre; gre_update_udphdr(sc, &sc->gre_udp, in_pseudo(sc->gre_oip.ip_src.s_addr, sc->gre_oip.ip_dst.s_addr, 0)); } else { - sc->gre_hlen = sizeof(struct greip); + priv->priv_hlen = sizeof(struct greip); sc->gre_oip.ip_p = IPPROTO_GRE; - gh = &sc->gre_iphdr->gi_gre; + gh = &sc->gre_iphdr.gi_gre; } sc->gre_oip.ip_v = IPVERSION; sc->gre_oip.ip_hl = sizeof(struct ip) >> 2; @@ -371,7 +369,7 @@ in_gre_attach(struct gre_softc *sc) /* * If we return error, this means that sc is not linked, - * and caller should reset gre_family and free(sc->gre_hdr). + * and caller should reset gre_family. */ if (sc->gre_options & GRE_UDPENCAP) { error = in_gre_setup_socket(sc); @@ -379,12 +377,11 @@ in_gre_attach(struct gre_softc *sc) return (error); } else CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain); - CK_LIST_INSERT_HEAD(&GRE_SRCHASH(sc->gre_oip.ip_src.s_addr), - sc, srchash); + CK_LIST_INSERT_HEAD(&GRE_SRCHASH(sc->gre_oip.ip_src.s_addr), sc, srchash); /* Set IFF_DRV_RUNNING if interface is ready */ NET_EPOCH_ENTER(et); - in_gre_set_running(sc); + in_gre_set_running(GRE2IFP(sc), sc->gre_oip.ip_src); NET_EPOCH_EXIT(et); return (0); } @@ -409,9 +406,7 @@ in_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) sc->gre_oip.ip_dst.s_addr, value) == EADDRNOTAVAIL) return (EEXIST); - CK_LIST_REMOVE(sc, chain); - CK_LIST_REMOVE(sc, srchash); - GRE_WAIT(); + gre_update_priv(sc, sizeof(struct ip)); switch (cmd) { case GRESKEY: sc->gre_key = value; @@ -424,10 +419,8 @@ in_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) break; } error = in_gre_attach(sc); - if (error != 0) { + if (error != 0) sc->gre_family = 0; - free(sc->gre_hdr, M_GRE); - } return (error); } @@ -471,27 +464,17 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t data) error = 0; break; } - ip = malloc(sizeof(struct greudp) + 3 * sizeof(uint32_t), - M_GRE, M_WAITOK | M_ZERO); - ip->ip_src.s_addr = src->sin_addr.s_addr; - ip->ip_dst.s_addr = dst->sin_addr.s_addr; - if (sc->gre_family != 0) { - /* Detach existing tunnel first */ - CK_LIST_REMOVE(sc, chain); - CK_LIST_REMOVE(sc, srchash); - GRE_WAIT(); - free(sc->gre_hdr, M_GRE); - /* XXX: should we notify about link state change? */ - } - sc->gre_family = AF_INET; - sc->gre_hdr = ip; + if (sc->gre_family != 0) + gre_update_priv(sc, 0); sc->gre_oseq = 0; sc->gre_iseq = UINT32_MAX; + + ip = &sc->gre_oip; + ip->ip_src.s_addr = src->sin_addr.s_addr; + ip->ip_dst.s_addr = dst->sin_addr.s_addr; error = in_gre_attach(sc); - if (error != 0) { + if (error != 0) sc->gre_family = 0; - free(sc->gre_hdr, M_GRE); - } break; case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: diff --git a/sys/netinet6/ip6_gre.c b/sys/netinet6/ip6_gre.c index 9ba45cb294e9..f934558908bb 100644 --- a/sys/netinet6/ip6_gre.c +++ b/sys/netinet6/ip6_gre.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2014, 2018 Andrey V. Elsukov + * Copyright (c) 2014-2026 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -119,6 +119,7 @@ in6_gre_checkdup(const struct gre_softc *sc, const struct in6_addr *src, struct gre_softc *tmp; struct gre_socket *gs; + /* NOTE: we are protected with gre_ioctl_sx lock */ if (sc->gre_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, src) && IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, dst) && @@ -176,13 +177,12 @@ in6_gre_lookup(const struct mbuf *m, int off, int proto, void **arg) * Check that ingress address belongs to local host. */ static void -in6_gre_set_running(struct gre_softc *sc) +in6_gre_set_running(struct ifnet *ifp, struct in6_addr *src) { - - if (in6_localip(&sc->gre_oip6.ip6_src)) - GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; + if (in6_localip(src)) + ifp->if_drv_flags |= IFF_DRV_RUNNING; else - GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } /* @@ -207,7 +207,7 @@ in6_gre_srcaddr(void *arg __unused, const struct sockaddr *sa, if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, &sin->sin6_addr) == 0) continue; - in6_gre_set_running(sc); + in6_gre_set_running(GRE2IFP(sc), &sc->gre_oip6.ip6_src); } } @@ -261,12 +261,8 @@ in6_gre_setup_socket(struct gre_softc *sc) if (gs != NULL) { s = __containerof(gs, struct in6_gre_socket, base); if (!IN6_ARE_ADDR_EQUAL(&s->addr, &sc->gre_oip6.ip6_src)) { - if (CK_LIST_EMPTY(&gs->list)) { - CK_LIST_REMOVE(gs, chain); - soclose(gs->so); - NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); - } - gs = sc->gre_so = NULL; + gre_update_socket(sc); + gs = NULL; } } @@ -352,27 +348,30 @@ in6_gre_setup_socket(struct gre_softc *sc) static int in6_gre_attach(struct gre_softc *sc) { + struct epoch_tracker et; + struct gre_priv *priv = sc->gre_data; struct grehdr *gh; int error; + sc->gre_family = priv->priv_family = AF_INET6; if (sc->gre_options & GRE_UDPENCAP) { - sc->gre_csumflags = CSUM_UDP_IPV6; - sc->gre_hlen = sizeof(struct greudp6); + priv->priv_csumflags = CSUM_UDP_IPV6; + priv->priv_hlen = sizeof(struct greudp6); sc->gre_oip6.ip6_nxt = IPPROTO_UDP; - gh = &sc->gre_udp6hdr->gi6_gre; + gh = &sc->gre_udp6hdr.gi6_gre; gre_update_udphdr(sc, &sc->gre_udp6, in6_cksum_pseudo(&sc->gre_oip6, 0, 0, 0)); } else { - sc->gre_hlen = sizeof(struct greip6); + priv->priv_hlen = sizeof(struct greip6); sc->gre_oip6.ip6_nxt = IPPROTO_GRE; - gh = &sc->gre_ip6hdr->gi6_gre; + gh = &sc->gre_ip6hdr.gi6_gre; } sc->gre_oip6.ip6_vfc = IPV6_VERSION; gre_update_hdr(sc, gh); /* * If we return error, this means that sc is not linked, - * and caller should reset gre_family and free(sc->gre_hdr). + * and caller should reset gre_family. */ if (sc->gre_options & GRE_UDPENCAP) { error = in6_gre_setup_socket(sc); @@ -383,7 +382,9 @@ in6_gre_attach(struct gre_softc *sc) CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash); /* Set IFF_DRV_RUNNING if interface is ready */ - in6_gre_set_running(sc); + NET_EPOCH_ENTER(et); + in6_gre_set_running(GRE2IFP(sc), &sc->gre_oip6.ip6_src); + NET_EPOCH_EXIT(et); return (0); } @@ -407,9 +408,7 @@ in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) &sc->gre_oip6.ip6_dst, value) == EADDRNOTAVAIL) return (EEXIST); - CK_LIST_REMOVE(sc, chain); - CK_LIST_REMOVE(sc, srchash); - GRE_WAIT(); + gre_update_priv(sc, sizeof(struct ip6_hdr)); switch (cmd) { case GRESKEY: sc->gre_key = value; @@ -422,10 +421,8 @@ in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) break; } error = in6_gre_attach(sc); - if (error != 0) { + if (error != 0) sc->gre_family = 0; - free(sc->gre_hdr, M_GRE); - } return (error); } @@ -478,27 +475,17 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t data) error = 0; break; } - ip6 = malloc(sizeof(struct greudp6) + 3 * sizeof(uint32_t), - M_GRE, M_WAITOK | M_ZERO); - ip6->ip6_src = src->sin6_addr; - ip6->ip6_dst = dst->sin6_addr; - if (sc->gre_family != 0) { - /* Detach existing tunnel first */ - CK_LIST_REMOVE(sc, chain); - CK_LIST_REMOVE(sc, srchash); - GRE_WAIT(); - free(sc->gre_hdr, M_GRE); - /* XXX: should we notify about link state change? */ - } - sc->gre_family = AF_INET6; - sc->gre_hdr = ip6; + if (sc->gre_family != 0) + gre_update_priv(sc, 0); sc->gre_oseq = 0; sc->gre_iseq = UINT32_MAX; + + ip6 = &sc->gre_ip6hdr.gi6_ip6; + ip6->ip6_src = src->sin6_addr; + ip6->ip6_dst = dst->sin6_addr; error = in6_gre_attach(sc); - if (error != 0) { + if (error != 0) sc->gre_family = 0; - free(sc->gre_hdr, M_GRE); - } break; case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: