summaryrefslogtreecommitdiff
path: root/sys/net/route.c
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2024-01-31 14:56:44 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2024-01-31 14:56:44 +0000
commit810df671ea56706f230d45c5b2d6fe134bcfadd0 (patch)
treeadf56b09f869cc38efc73aa30d659afc6293747e /sys/net/route.c
parentfcc2c44a34911182dff36f19827110bcb12bde9b (diff)
Add route generation number to route cache.
The outgoing route is cached at the inpcb. This cache was only invalidated when the socket closes or if the route gets invalid. More specific routes were not detected. Especially with dynamic routing protocols, sockets must be closed and reopened to use the correct route. Running ping during a route change shows the problem. To solve this, add a route generation number that is updated whenever the routing table changes. The lookup in struct route is put into the route_cache() function. If the generation number is too old, the cached route gets discarded. Implement route_cache() for ip_output() and ip_forward() first. IPv6 and more places will follow. OK claudio@
Diffstat (limited to 'sys/net/route.c')
-rw-r--r--sys/net/route.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/sys/net/route.c b/sys/net/route.c
index 0b97254765f..991b4805a49 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: route.c,v 1.426 2023/11/13 17:18:27 bluhm Exp $ */
+/* $OpenBSD: route.c,v 1.427 2024/01/31 14:56:42 bluhm Exp $ */
/* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */
/*
@@ -140,6 +140,7 @@
/*
* Locks used to protect struct members:
+ * a atomic operations
* I immutable after creation
* L rtlabel_mtx
* T rttimer_mtx
@@ -152,8 +153,9 @@ static uint32_t rt_hashjitter;
extern unsigned int rtmap_limit;
-struct cpumem * rtcounters;
-int rttrash; /* routes not in table but not freed */
+struct cpumem *rtcounters;
+int rttrash; /* [a] routes not in table but not freed */
+u_long rtgeneration; /* [a] generation number, routes changed */
struct pool rtentry_pool; /* pool for rtentry structures */
struct pool rttimer_pool; /* pool for rttimer structures */
@@ -199,6 +201,33 @@ route_init(void)
#endif
}
+void
+route_cache(struct route *ro, struct in_addr addr, u_int rtableid)
+{
+ u_long gen;
+
+ gen = atomic_load_long(&rtgeneration);
+ membar_consumer();
+
+ if (rtisvalid(ro->ro_rt) &&
+ ro->ro_generation == gen &&
+ ro->ro_tableid == rtableid &&
+ ro->ro_dst.sa_family == AF_INET &&
+ satosin(&ro->ro_dst)->sin_addr.s_addr == addr.s_addr) {
+ return;
+ }
+
+ rtfree(ro->ro_rt);
+ ro->ro_rt = NULL;
+ ro->ro_generation = gen;
+ ro->ro_tableid = rtableid;
+
+ memset(&ro->ro_dst, 0, sizeof(ro->ro_dst));
+ satosin(&ro->ro_dst)->sin_family = AF_INET;
+ satosin(&ro->ro_dst)->sin_len = sizeof(struct sockaddr_in);
+ satosin(&ro->ro_dst)->sin_addr = addr;
+}
+
/*
* Returns 1 if the (cached) ``rt'' entry is still valid, 0 otherwise.
*/
@@ -824,6 +853,9 @@ rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
else
rtfree(rt);
+ membar_producer();
+ atomic_inc_long(&rtgeneration);
+
return (0);
}
@@ -992,6 +1024,10 @@ rtrequest(int req, struct rt_addrinfo *info, u_int8_t prio,
*ret_nrt = rt;
else
rtfree(rt);
+
+ membar_producer();
+ atomic_inc_long(&rtgeneration);
+
break;
}
@@ -1829,6 +1865,9 @@ rt_if_linkstate_change(struct rtentry *rt, void *arg, u_int id)
}
if_group_routechange(rt_key(rt), rt_plen2mask(rt, &sa_mask));
+ membar_producer();
+ atomic_inc_long(&rtgeneration);
+
return (error);
}