-/* $OpenBSD: route.c,v 1.423 2023/11/10 20:05:22 bluhm Exp $ */
+/* $OpenBSD: route.c,v 1.424 2023/11/12 15:42:05 dlg Exp $ */
/* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */
/*
struct pool rtentry_pool; /* pool for rtentry structures */
struct pool rttimer_pool; /* pool for rttimer structures */
-int rt_setgwroute(struct rtentry *, u_int);
-void rt_putgwroute(struct rtentry *);
+int rt_setgwroute(struct rtentry *, struct sockaddr *, u_int);
+void rt_putgwroute(struct rtentry *, struct rtentry *);
int rtflushclone1(struct rtentry *, void *, u_int);
int rtflushclone(struct rtentry *, unsigned int);
int rt_ifa_purge_walker(struct rtentry *, void *, unsigned int);
* the gateway entry ``rt''.
*/
int
-rt_setgwroute(struct rtentry *rt, u_int rtableid)
+rt_setgwroute(struct rtentry *rt, struct sockaddr *gate, u_int rtableid)
{
struct rtentry *prt, *nhrt;
unsigned int rdomain = rtable_l2(rtableid);
NET_ASSERT_LOCKED();
- KASSERT(ISSET(rt->rt_flags, RTF_GATEWAY));
-
/* If we cannot find a valid next hop bail. */
- nhrt = rt_match(rt->rt_gateway, NULL, RT_RESOLVE, rdomain);
+ nhrt = rt_match(gate, NULL, RT_RESOLVE, rdomain);
if (nhrt == NULL)
return (ENOENT);
return (EHOSTUNREACH);
}
- error = rt_clone(&prt, rt->rt_gateway, rdomain);
+ error = rt_clone(&prt, gate, rdomain);
if (error) {
rtfree(prt);
return (error);
return (ENETUNREACH);
}
- /* Next hop is valid so remove possible old cache. */
- rt_putgwroute(rt);
- KASSERT(rt->rt_gwroute == NULL);
-
/*
* If the MTU of next hop is 0, this will reset the MTU of the
* route to run PMTUD again from scratch.
nhrt->rt_flags |= RTF_CACHED;
nhrt->rt_cachecnt++;
- rt->rt_gwroute = nhrt;
+ /* commit */
+ rt_putgwroute(rt, nhrt);
return (0);
}
* Invalidate the cached route entry of the gateway entry ``rt''.
*/
void
-rt_putgwroute(struct rtentry *rt)
+rt_putgwroute(struct rtentry *rt, struct rtentry *nhrt)
{
- struct rtentry *nhrt = rt->rt_gwroute;
+ struct rtentry *onhrt;
NET_ASSERT_LOCKED();
- if (!ISSET(rt->rt_flags, RTF_GATEWAY) || nhrt == NULL)
+ if (!ISSET(rt->rt_flags, RTF_GATEWAY))
return;
- KASSERT(ISSET(nhrt->rt_flags, RTF_CACHED));
- KASSERT(nhrt->rt_cachecnt > 0);
+ /* this is protected as per [X] in route.h */
+ onhrt = rt->rt_gwroute;
+ rt->rt_gwroute = nhrt;
+
+ if (onhrt != NULL) {
+ KASSERT(onhrt->rt_cachecnt > 0);
+ KASSERT(ISSET(onhrt->rt_flags, RTF_CACHED));
- --nhrt->rt_cachecnt;
- if (nhrt->rt_cachecnt == 0)
- nhrt->rt_flags &= ~RTF_CACHED;
+ --onhrt->rt_cachecnt;
+ if (onhrt->rt_cachecnt == 0)
+ CLR(onhrt->rt_flags, RTF_CACHED);
- rtfree(rt->rt_gwroute);
- rt->rt_gwroute = NULL;
+ rtfree(onhrt);
+ }
}
void
}
/* Release next hop cache before flushing cloned entries. */
- rt_putgwroute(rt);
+ rt_putgwroute(rt, NULL);
/* Clean up any cloned children. */
if (ISSET(rt->rt_flags, RTF_CLONING))
tableid))) {
ifafree(ifa);
rtfree(rt->rt_parent);
- rt_putgwroute(rt);
+ rt_putgwroute(rt, NULL);
free(rt->rt_gateway, M_RTABLE,
ROUNDUP(rt->rt_gateway->sa_len));
free(ndst, M_RTABLE, ndst->sa_len);
if (error != 0) {
ifafree(ifa);
rtfree(rt->rt_parent);
- rt_putgwroute(rt);
+ rt_putgwroute(rt, NULL);
free(rt->rt_gateway, M_RTABLE,
ROUNDUP(rt->rt_gateway->sa_len));
free(ndst, M_RTABLE, ndst->sa_len);
rt_setgate(struct rtentry *rt, struct sockaddr *gate, u_int rtableid)
{
int glen = ROUNDUP(gate->sa_len);
- struct sockaddr *sa;
+ struct sockaddr *sa, *osa;
- if (rt->rt_gateway == NULL || glen != ROUNDUP(rt->rt_gateway->sa_len)) {
- sa = malloc(glen, M_RTABLE, M_NOWAIT);
- if (sa == NULL)
- return (ENOBUFS);
- if (rt->rt_gateway != NULL) {
- free(rt->rt_gateway, M_RTABLE,
- ROUNDUP(rt->rt_gateway->sa_len));
+ KASSERT(gate != NULL);
+ if (rt->rt_gateway == gate) {
+ /* nop */
+ return (0);
+ };
+
+ sa = malloc(glen, M_RTABLE, M_NOWAIT | M_ZERO);
+ if (sa == NULL)
+ return (ENOBUFS);
+ memcpy(sa, gate, gate->sa_len);
+
+ KERNEL_LOCK(); /* see [X] in route.h */
+ if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
+ int error = rt_setgwroute(rt, gate, rtableid);
+ if (error != 0) {
+ KERNEL_UNLOCK();
+ free(sa, M_RTABLE, glen);
+ return (error);
}
- rt->rt_gateway = sa;
}
- memmove(rt->rt_gateway, gate, glen);
- if (ISSET(rt->rt_flags, RTF_GATEWAY))
- return (rt_setgwroute(rt, rtableid));
+ osa = rt->rt_gateway;
+ rt->rt_gateway = sa;
+ KERNEL_UNLOCK();
+
+ if (osa != NULL)
+ free(osa, M_RTABLE, ROUNDUP(osa->sa_len));
return (0);
}