Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F159928213
D55398.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
D55398.diff
View Options
diff --git a/sys/net/if_gre.h b/sys/net/if_gre.h
--- a/sys/net/if_gre.h
+++ b/sys/net/if_gre.h
@@ -82,6 +82,29 @@
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_port;
+ 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 +112,10 @@
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;
@@ -122,16 +134,12 @@
#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_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_IP(priv) (priv)->priv_ip.gi_ip
+#define GRE_IP6(priv) (priv)->priv_ip6.gi6_ip6
+#define GRE_UDP(priv) (priv)->priv_udp.gi_udp
+#define GRE_UDP6(priv) (priv)->priv_udp6.gi6_udp
+#define GRE_HDR(priv, proto) (priv)->priv_##proto.gi_gre
+#define GRE_HDR6(priv, proto) (priv)->priv_##proto.gi6_gre
struct gre_list *gre_hashinit(void);
void gre_hashdestroy(struct gre_list *);
@@ -139,7 +147,8 @@
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/net/if_gre.c b/sys/net/if_gre.c
--- 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 <ae@FreeBSD.org>
+ * Copyright (c) 2014-2026 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -127,6 +127,8 @@
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 *);
static int gre_set_addr_nl(struct gre_softc *, struct nl_pstate *,
struct sockaddr *, struct sockaddr *);
@@ -355,6 +357,7 @@
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;
@@ -401,13 +404,14 @@
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);
return (0);
@@ -499,10 +503,6 @@
sc->gre_port = opt;
break;
}
- /*
- * XXX: Do we need to initiate change of interface
- * state here?
- */
return (error);
};
@@ -621,29 +621,51 @@
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);
+ 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);
+ NET_EPOCH_CALL(gre_free_priv, &priv->epoch_ctx);
+}
+
+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);
}
@@ -665,19 +687,28 @@
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)
{
@@ -690,6 +721,7 @@
void
gre_update_udphdr(struct gre_softc *sc, struct udphdr *udp, uint16_t csum)
{
+ struct gre_priv *priv = sc->gre_data;
sx_assert(&gre_ioctl_sx, SA_XLOCKED);
MPASS(sc->gre_options & GRE_UDPENCAP);
@@ -698,35 +730,41 @@
udp->uh_sport = htons(sc->gre_port);
udp->uh_sum = csum;
udp->uh_ulen = 0;
+
+ /* update priv_port, it will be used on transmit */
+ priv->priv_port = sc->gre_port;
}
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, it will be used on transmit */
+ priv->priv_options = sc->gre_options;
}
int
@@ -742,6 +780,8 @@
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)
@@ -756,7 +796,7 @@
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)
@@ -870,11 +910,11 @@
}
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 || priv->priv_port != 0)
return (flowid);
switch (af) {
#ifdef INET
@@ -912,6 +952,7 @@
{
GRE_RLOCK_TRACKER;
struct gre_softc *sc;
+ struct gre_priv *priv;
struct grehdr *gh;
struct udphdr *uh;
uint32_t af, flowid;
@@ -938,17 +979,18 @@
m_freem(m);
goto drop;
}
+ priv = sc->gre_data;
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
@@ -967,7 +1009,7 @@
goto drop;
}
/* Determine offset of GRE header */
- switch (sc->gre_family) {
+ switch (priv->priv_family) {
#ifdef INET
case AF_INET:
len = sizeof(struct ip);
@@ -983,7 +1025,7 @@
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);
@@ -992,28 +1034,28 @@
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:
@@ -1156,6 +1198,7 @@
NL_VERIFY_PARSERS(all_parsers);
break;
case MOD_UNLOAD:
+ GRE_WAIT();
break;
default:
return (EOPNOTSUPP);
diff --git a/sys/netinet/ip_gre.c b/sys/netinet/ip_gre.c
--- 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 <ae@FreeBSD.org>
+ * Copyright (c) 2014-2026 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -93,8 +93,8 @@
fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
#define GRE_SOCKHASH(src) (V_ipv4_sockets[\
fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
-#define GRE_HASH_SC(sc) GRE_HASH((sc)->gre_oip.ip_src.s_addr,\
- (sc)->gre_oip.ip_dst.s_addr)
+#define GRE_HASH_PRIV(p) GRE_HASH(GRE_IP(p).ip_src.s_addr,\
+ GRE_IP(p).ip_dst.s_addr)
static uint32_t
in_gre_hashval(in_addr_t src, in_addr_t dst)
@@ -127,9 +127,10 @@
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 &&
+ GRE_IP(sc->gre_data).ip_src.s_addr == src &&
+ GRE_IP(sc->gre_data).ip_dst.s_addr == dst &&
(sc->gre_options & GRE_UDPENCAP) == (opts & GRE_UDPENCAP))
return (EEXIST);
@@ -144,8 +145,8 @@
CK_LIST_FOREACH(tmp, head, chain) {
if (tmp == sc)
continue;
- if (tmp->gre_oip.ip_src.s_addr == src &&
- tmp->gre_oip.ip_dst.s_addr == dst)
+ if (GRE_IP(tmp->gre_data).ip_src.s_addr == src &&
+ GRE_IP(tmp->gre_data).ip_dst.s_addr == dst)
return (EADDRNOTAVAIL);
}
return (0);
@@ -168,8 +169,8 @@
* This is an inbound packet, its ip_dst is source address
* in softc.
*/
- if (sc->gre_oip.ip_src.s_addr == ip->ip_dst.s_addr &&
- sc->gre_oip.ip_dst.s_addr == ip->ip_src.s_addr) {
+ if (GRE_IP(sc->gre_data).ip_src.s_addr == ip->ip_dst.s_addr &&
+ GRE_IP(sc->gre_data).ip_dst.s_addr == ip->ip_src.s_addr) {
if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0)
return (0);
*arg = sc;
@@ -183,13 +184,12 @@
* 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;
}
/*
@@ -211,9 +211,9 @@
NET_EPOCH_ASSERT();
sin = (const struct sockaddr_in *)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)
+ if (GRE_IP(sc->gre_data).ip_src.s_addr != sin->sin_addr.s_addr)
continue;
- in_gre_set_running(sc);
+ in_gre_set_running(GRE2IFP(sc), sin->sin_addr);
}
}
@@ -230,7 +230,7 @@
gs = (struct gre_socket *)ctx;
dst = ((const struct sockaddr_in *)sa)->sin_addr.s_addr;
CK_LIST_FOREACH(sc, &gs->list, chain) {
- if (sc->gre_oip.ip_dst.s_addr == dst)
+ if (GRE_IP(sc->gre_data).ip_dst.s_addr == dst)
break;
}
if (sc != NULL && (GRE2IFP(sc)->if_flags & IFF_UP) != 0){
@@ -260,17 +260,13 @@
* If address is different, check that there are no other tunnels
* and close socket.
*/
- addr = sc->gre_oip.ip_src.s_addr;
+ addr = GRE_IP(sc->gre_data).ip_src.s_addr;
gs = sc->gre_so;
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;
}
}
@@ -348,42 +344,43 @@
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);
- sc->gre_oip.ip_p = IPPROTO_UDP;
- 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));
+ GRE_IP(priv).ip_p = IPPROTO_UDP;
+ priv->priv_csumflags = CSUM_UDP;
+ priv->priv_hlen = sizeof(struct greudp);
+ gh = &GRE_HDR(priv, udp);
+ gre_update_udphdr(sc, &GRE_UDP(priv),
+ in_pseudo(GRE_IP(priv).ip_src.s_addr,
+ GRE_IP(priv).ip_dst.s_addr, 0));
} else {
- sc->gre_hlen = sizeof(struct greip);
- sc->gre_oip.ip_p = IPPROTO_GRE;
- gh = &sc->gre_iphdr->gi_gre;
+ GRE_IP(priv).ip_p = IPPROTO_GRE;
+ priv->priv_hlen = sizeof(struct greip);
+ gh = &GRE_HDR(priv, ip);
}
- sc->gre_oip.ip_v = IPVERSION;
- sc->gre_oip.ip_hl = sizeof(struct ip) >> 2;
+ GRE_IP(priv).ip_v = IPVERSION;
+ GRE_IP(priv).ip_hl = sizeof(struct ip) >> 2;
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 = in_gre_setup_socket(sc);
if (error != 0)
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_HASH_PRIV(priv), sc, chain);
+ CK_LIST_INSERT_HEAD(&GRE_SRCHASH(GRE_IP(priv).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), GRE_IP(priv).ip_src);
NET_EPOCH_EXIT(et);
return (0);
}
@@ -404,13 +401,11 @@
*/
if (cmd == GRESOPTS &&
(sc->gre_options & GRE_UDPENCAP) != (value & GRE_UDPENCAP) &&
- in_gre_checkdup(sc, sc->gre_oip.ip_src.s_addr,
- sc->gre_oip.ip_dst.s_addr, value) == EADDRNOTAVAIL)
+ in_gre_checkdup(sc, GRE_IP(sc->gre_data).ip_src.s_addr,
+ GRE_IP(sc->gre_data).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;
@@ -423,10 +418,8 @@
break;
}
error = in_gre_attach(sc);
- if (error != 0) {
+ if (error != 0)
sc->gre_family = 0;
- free(sc->gre_hdr, M_GRE);
- }
return (error);
}
@@ -470,27 +463,17 @@
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 = &GRE_IP(sc->gre_data);
+ 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:
@@ -503,7 +486,7 @@
src->sin_family = AF_INET;
src->sin_len = sizeof(*src);
src->sin_addr = (cmd == SIOCGIFPSRCADDR) ?
- sc->gre_oip.ip_src: sc->gre_oip.ip_dst;
+ GRE_IP(sc->gre_data).ip_src: GRE_IP(sc->gre_data).ip_dst;
error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
if (error != 0)
memset(src, 0, sizeof(*src));
diff --git a/sys/netinet6/ip6_gre.c b/sys/netinet6/ip6_gre.c
--- a/sys/netinet6/ip6_gre.c
+++ b/sys/netinet6/ip6_gre.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2014, 2018 Andrey V. Elsukov <ae@FreeBSD.org>
+ * Copyright (c) 2014-2026 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -84,8 +84,7 @@
fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
#define GRE_SOCKHASH(src) (V_ipv6_sockets[\
fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
-#define GRE_HASH_SC(sc) GRE_HASH(&(sc)->gre_oip6.ip6_src,\
- &(sc)->gre_oip6.ip6_dst)
+#define GRE_HASH_PRIV(p) GRE_HASH(&GRE_IP6(p).ip6_src, &GRE_IP6(p).ip6_dst)
static uint32_t
in6_gre_hashval(const struct in6_addr *src, const struct in6_addr *dst)
@@ -118,9 +117,10 @@
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) &&
+ IN6_ARE_ADDR_EQUAL(&GRE_IP6(sc->gre_data).ip6_src, src) &&
+ IN6_ARE_ADDR_EQUAL(&GRE_IP6(sc->gre_data).ip6_dst, dst) &&
(sc->gre_options & GRE_UDPENCAP) == (opts & GRE_UDPENCAP))
return (EEXIST);
@@ -135,8 +135,8 @@
CK_LIST_FOREACH(tmp, head, chain) {
if (tmp == sc)
continue;
- if (IN6_ARE_ADDR_EQUAL(&tmp->gre_oip6.ip6_src, src) &&
- IN6_ARE_ADDR_EQUAL(&tmp->gre_oip6.ip6_dst, dst))
+ if (IN6_ARE_ADDR_EQUAL(&GRE_IP6(tmp->gre_data).ip6_src, src) &&
+ IN6_ARE_ADDR_EQUAL(&GRE_IP6(tmp->gre_data).ip6_dst, dst))
return (EADDRNOTAVAIL);
}
return (0);
@@ -158,9 +158,9 @@
* This is an inbound packet, its ip6_dst is source address
* in softc.
*/
- if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src,
+ if (IN6_ARE_ADDR_EQUAL(&GRE_IP6(sc->gre_data).ip6_src,
&ip6->ip6_dst) &&
- IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst,
+ IN6_ARE_ADDR_EQUAL(&GRE_IP6(sc->gre_data).ip6_dst,
&ip6->ip6_src)) {
if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0)
return (0);
@@ -175,13 +175,12 @@
* 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;
}
/*
@@ -203,10 +202,10 @@
NET_EPOCH_ASSERT();
sin = (const struct sockaddr_in6 *)sa;
CK_LIST_FOREACH(sc, &GRE_SRCHASH(&sin->sin6_addr), srchash) {
- if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src,
+ if (IN6_ARE_ADDR_EQUAL(&GRE_IP6(sc->gre_data).ip6_src,
&sin->sin6_addr) == 0)
continue;
- in6_gre_set_running(sc);
+ in6_gre_set_running(GRE2IFP(sc), &GRE_IP6(sc->gre_data).ip6_src);
}
}
@@ -227,7 +226,7 @@
return (true);
}
CK_LIST_FOREACH(sc, &gs->list, chain) {
- if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, &dst.sin6_addr))
+ if (IN6_ARE_ADDR_EQUAL(&GRE_IP6(sc->gre_data).ip6_dst, &dst.sin6_addr))
break;
}
if (sc != NULL && (GRE2IFP(sc)->if_flags & IFF_UP) != 0){
@@ -246,6 +245,7 @@
struct sockaddr_in6 sin6;
struct in6_gre_socket *s;
struct gre_socket *gs;
+ const struct in6_addr *addr;
int error, value;
/*
@@ -256,16 +256,13 @@
* If address is different, check that there are no other tunnels
* and close socket.
*/
+ addr = &GRE_IP6(sc->gre_data).ip6_src;
gs = sc->gre_so;
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;
+ if (!IN6_ARE_ADDR_EQUAL(&s->addr, addr)) {
+ gre_update_socket(sc);
+ gs = NULL;
}
}
@@ -274,10 +271,10 @@
* Check that socket for given address is already
* configured.
*/
- gs = in6_gre_lookup_socket(&sc->gre_oip6.ip6_src);
+ gs = in6_gre_lookup_socket(addr);
if (gs == NULL) {
s = malloc(sizeof(*s), M_GRE, M_WAITOK | M_ZERO);
- s->addr = sc->gre_oip6.ip6_src;
+ s->addr = *addr;
gs = &s->base;
error = socreate(sc->gre_family, &gs->so,
@@ -316,7 +313,7 @@
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(sin6);
- sin6.sin6_addr = sc->gre_oip6.ip6_src;
+ sin6.sin6_addr = *addr;
sin6.sin6_port = htons(GRE_UDPPORT);
error = sa6_recoverscope(&sin6);
if (error != 0) {
@@ -333,8 +330,7 @@
goto fail;
}
/* Add socket to the chain */
- CK_LIST_INSERT_HEAD(
- &GRE_SOCKHASH(&sc->gre_oip6.ip6_src), gs, chain);
+ CK_LIST_INSERT_HEAD(&GRE_SOCKHASH(addr), gs, chain);
}
}
@@ -351,38 +347,43 @@
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);
- sc->gre_oip6.ip6_nxt = IPPROTO_UDP;
- gh = &sc->gre_udp6hdr->gi6_gre;
- gre_update_udphdr(sc, &sc->gre_udp6,
- in6_cksum_pseudo(&sc->gre_oip6, 0, 0, 0));
+ GRE_IP6(priv).ip6_nxt = IPPROTO_UDP;
+ priv->priv_csumflags = CSUM_UDP_IPV6;
+ priv->priv_hlen = sizeof(struct greudp6);
+ gh = &GRE_HDR6(priv, udp6);
+ gre_update_udphdr(sc, &GRE_UDP6(priv),
+ in6_cksum_pseudo(&GRE_IP6(priv), 0, 0, 0));
} else {
- sc->gre_hlen = sizeof(struct greip6);
- sc->gre_oip6.ip6_nxt = IPPROTO_GRE;
- gh = &sc->gre_ip6hdr->gi6_gre;
+ GRE_IP6(priv).ip6_nxt = IPPROTO_GRE;
+ priv->priv_hlen = sizeof(struct greip6);
+ gh = &GRE_HDR6(priv, ip6);
}
- sc->gre_oip6.ip6_vfc = IPV6_VERSION;
+ GRE_IP6(priv).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);
if (error != 0)
return (error);
} else
- CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain);
- CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash);
+ CK_LIST_INSERT_HEAD(&GRE_HASH_PRIV(priv), sc, chain);
+ CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&GRE_IP6(priv).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), &GRE_IP6(priv).ip6_src);
+ NET_EPOCH_EXIT(et);
return (0);
}
@@ -402,13 +403,11 @@
*/
if (cmd == GRESOPTS &&
(sc->gre_options & GRE_UDPENCAP) != (value & GRE_UDPENCAP) &&
- in6_gre_checkdup(sc, &sc->gre_oip6.ip6_src,
- &sc->gre_oip6.ip6_dst, value) == EADDRNOTAVAIL)
+ in6_gre_checkdup(sc, &GRE_IP6(sc->gre_data).ip6_src,
+ &GRE_IP6(sc->gre_data).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;
@@ -421,10 +420,8 @@
break;
}
error = in6_gre_attach(sc);
- if (error != 0) {
+ if (error != 0)
sc->gre_family = 0;
- free(sc->gre_hdr, M_GRE);
- }
return (error);
}
@@ -477,27 +474,17 @@
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 = &GRE_IP6(sc->gre_data);
+ 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:
@@ -510,7 +497,7 @@
src->sin6_family = AF_INET6;
src->sin6_len = sizeof(*src);
src->sin6_addr = (cmd == SIOCGIFPSRCADDR_IN6) ?
- sc->gre_oip6.ip6_src: sc->gre_oip6.ip6_dst;
+ GRE_IP6(sc->gre_data).ip6_src: GRE_IP6(sc->gre_data).ip6_dst;
error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
if (error == 0)
error = sa6_recoverscope(src);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jun 20, 3:36 PM (2 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34120921
Default Alt Text
D55398.diff (27 KB)
Attached To
Mode
D55398: if_gre: make access to softc's data safe in network epoch
Attached
Detach File
Event Timeline
Log In to Comment