changeset 958:7f0ccb87d638 draft

On Linux, fix replacing the kernel added subnet route correctly for metrics.
author Roy Marples <roy@marples.name>
date Mon, 08 Sep 2008 11:30:31 +0000
parents 2e99ade041ca
children 7e2114d0071d
files configure.c if-linux.c net.h
diffstat 3 files changed, 22 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/configure.c	Mon Sep 08 11:28:12 2008 +0000
+++ b/configure.c	Mon Sep 08 11:30:31 2008 +0000
@@ -301,6 +301,7 @@
 delete_address(struct interface *iface)
 {
 	int retval;
+
 	syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
 	       iface->name,
 	       inet_ntoa(iface->addr),
@@ -323,7 +324,6 @@
 #ifdef __linux__
 	struct in_addr dest;
 	struct in_addr gate;
-	struct rt *rt = NULL;
 #endif
 
 	/* Grab our IP config */
@@ -336,6 +336,10 @@
 			net.s_addr = get_netmask(addr.s_addr);
 		if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1)
 			brd.s_addr = addr.s_addr | ~net.s_addr;
+#ifdef __linux__
+		dest.s_addr = addr.s_addr & net.s_addr;
+		gate.s_addr = 0;
+#endif
 	} else {
 		/* Only reset things if we had set them before */
 		if (iface->addr.s_addr != 0) {
@@ -367,31 +371,20 @@
 
 #ifdef __linux__
 	/* On linux, we need to change the subnet route to have our metric. */
-	if (iface->addr.s_addr != iface->state->lease.addr.s_addr &&
-	    iface->metric > 0 &&
-	    net.s_addr != INADDR_BROADCAST)
+	if (iface->metric > 0 && 
+	    (net.s_addr != iface->net.s_addr ||
+	     dest.s_addr != (iface->addr.s_addr & iface->net.s_addr)))
 	{
-		dest.s_addr = addr.s_addr & net.s_addr;
-		gate.s_addr = 0;
-		add_route(iface, &dest, &net, &gate, iface->metric);
+		iface->addr.s_addr = addr.s_addr;
+		iface->net.s_addr = net.s_addr;
+		change_route(iface, &dest, &net, &gate, iface->metric);
 		del_route(iface, &dest, &net, &gate, 0);
-		rt = xmalloc(sizeof(*rt));
-		rt->dest.s_addr = dest.s_addr;
-		rt->net.s_addr = net.s_addr;
-		rt->gate.s_addr = gate.s_addr;
 	}
 #endif
 
-	configure_routes(iface, dhcp);
 	iface->addr.s_addr = addr.s_addr;
 	iface->net.s_addr = net.s_addr;
-
-#ifdef __linux__
-	if (rt) {
-		rt->next = iface->routes;
-		iface->routes = rt;
-	}
-#endif
+	configure_routes(iface, dhcp);
 
 	if (!iface->state->lease.frominfo)
 		if (write_lease(iface, dhcp) == -1)
--- a/if-linux.c	Mon Sep 08 11:28:12 2008 +0000
+++ b/if-linux.c	Mon Sep 08 11:30:31 2008 +0000
@@ -351,7 +351,7 @@
 	nlm->hdr.nlmsg_type = RTM_NEWROUTE;
 	if (action == 0)
 		nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
-	else if (action > 0)
+	else if (action  == 1)
 		nlm->hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
 	else
 		nlm->hdr.nlmsg_type = RTM_DELROUTE;
@@ -359,11 +359,12 @@
 	nlm->rt.rtm_family = AF_INET;
 	nlm->rt.rtm_table = RT_TABLE_MAIN;
 
-	if (action < 0)
+	if (action == -1 || action == -2)
 		nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
 	else {
 		nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
-		nlm->rt.rtm_protocol = RTPROT_BOOT;
+		/* We only change route metrics for kernel routes */
+		nlm->rt.rtm_protocol = action ? RTPROT_BOOT : RTPROT_KERNEL;
 		if (gateway->s_addr == INADDR_ANY)
 			nlm->rt.rtm_scope = RT_SCOPE_LINK;
 		else
@@ -374,6 +375,10 @@
 	nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
 	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
 		   &destination->s_addr, sizeof(destination->s_addr));
+	if (action != 1) {
+		add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC,
+			   &iface->addr.s_addr, sizeof(iface->addr.s_addr));
+	}
 	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
 		   &gateway->s_addr, sizeof(gateway->s_addr));
 
--- a/net.h	Mon Sep 08 11:28:12 2008 +0000
+++ b/net.h	Mon Sep 08 11:30:31 2008 +0000
@@ -132,6 +132,8 @@
 	if_route(iface, dest, mask, gate, metric, 0)
 #define del_route(iface, dest, mask, gate, metric) \
 	if_route(iface, dest, mask, gate, metric, -1)
+#define del_src_route(iface, dest, mask, gate, metric) \
+	if_route(iface, dest, mask, gate, metric, -2)
 void free_routes(struct rt *);
 
 int open_udp_socket(struct interface *);