summaryrefslogtreecommitdiffstats
path: root/sys/net/route.c
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2005-09-21 11:58:10 +0000
committerglebius <glebius@FreeBSD.org>2005-09-21 11:58:10 +0000
commit1fc277e12320230562f51d9b4af50700ed133e82 (patch)
tree1e3f60525305979b406eac00f97d8b61788d6ec2 /sys/net/route.c
parent24f904188013c2a799498038f6404176105e142a (diff)
downloadFreeBSD-src-1fc277e12320230562f51d9b4af50700ed133e82.zip
FreeBSD-src-1fc277e12320230562f51d9b4af50700ed133e82.tar.gz
Several fixes to rt_setgate(), that fix problems with route changing:
- Rearrange code so that in a case of failure the affected route is not changed. Otherwise, a bogus rtentry will be left and later rt_check() can recurse on its lock. [1] - Remove comment about protocol cloning. - Fix two places where rtentry mutex was recursed on, because accessed via two different pointers, that were actually pointing to the same rtentry in some cases. [1] - Return EADDRINUSE instead of bogus EDQUOT, in case when gateway uses the same route. [2] Reported & tested by: ps, Andrej Zverev <az inec.ru> [1] PR: kern/64090 [2]
Diffstat (limited to 'sys/net/route.c')
-rw-r--r--sys/net/route.c65
1 files changed, 30 insertions, 35 deletions
diff --git a/sys/net/route.c b/sys/net/route.c
index 447d56d..6fc33f2 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -1016,6 +1016,36 @@ rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate)
}
/*
+ * Cloning loop avoidance in case of bad configuration.
+ */
+ if (rt->rt_flags & RTF_GATEWAY) {
+ struct rtentry *gwrt;
+
+ RT_UNLOCK(rt); /* XXX workaround LOR */
+ gwrt = rtalloc1(gate, 1, 0);
+ if (gwrt == rt) {
+ RT_LOCK_ASSERT(rt);
+ RT_REMREF(rt);
+ return (EADDRINUSE); /* failure */
+ }
+ RT_LOCK(rt);
+ /*
+ * If there is already a gwroute, then drop it. If we
+ * are asked to replace route with itself, then do
+ * not leak its refcounter.
+ */
+ if (rt->rt_gwroute != NULL) {
+ if (rt->rt_gwroute == gwrt) {
+ RT_REMREF(rt->rt_gwroute);
+ } else
+ RTFREE(rt->rt_gwroute);
+ }
+
+ if ((rt->rt_gwroute = gwrt) != NULL)
+ RT_UNLOCK(rt->rt_gwroute);
+ }
+
+ /*
* Prepare to store the gateway in rt->rt_gateway.
* Both dst and gateway are stored one after the other in the same
* malloc'd chunk. If we have room, we can reuse the old buffer,
@@ -1047,41 +1077,6 @@ rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate)
bcopy(gate, rt->rt_gateway, glen);
/*
- * If there is already a gwroute, it's now almost definitly wrong
- * so drop it.
- */
- if (rt->rt_gwroute != NULL) {
- RTFREE(rt->rt_gwroute);
- rt->rt_gwroute = NULL;
- }
- /*
- * Cloning loop avoidance:
- * In the presence of protocol-cloning and bad configuration,
- * it is possible to get stuck in bottomless mutual recursion
- * (rtrequest rt_setgate rtalloc1). We avoid this by not allowing
- * protocol-cloning to operate for gateways (which is probably the
- * correct choice anyway), and avoid the resulting reference loops
- * by disallowing any route to run through itself as a gateway.
- * This is obviously mandatory when we get rt->rt_output().
- * XXX: After removal of PRCLONING this is probably not needed anymore.
- */
- if (rt->rt_flags & RTF_GATEWAY) {
- struct rtentry *gwrt;
-
- RT_UNLOCK(rt); /* XXX workaround LOR */
- gwrt = rtalloc1(gate, 1, 0);
- RT_LOCK(rt);
- rt->rt_gwroute = gwrt;
- if (rt->rt_gwroute == rt) {
- RTFREE_LOCKED(rt->rt_gwroute);
- rt->rt_gwroute = NULL;
- return EDQUOT; /* failure */
- }
- if (rt->rt_gwroute != NULL)
- RT_UNLOCK(rt->rt_gwroute);
- }
-
- /*
* This isn't going to do anything useful for host routes, so
* don't bother. Also make sure we have a reasonable mask
* (we don't yet have one during adds).
OpenPOWER on IntegriCloud