Page MenuHomeFreeBSD

D55398.diff
No OneTemporary

D55398.diff

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

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)

Event Timeline