changeset 2960:26d4bee443de draft

After adding an address load the kernel routing table for the interface. When routes are rebuilt try not to remove any existing routes if they don't need changing.
author Roy Marples <roy@marples.name>
date Thu, 26 Feb 2015 13:22:41 +0000
parents eed2de3189ce
children 1ce021acb514
files defs.h dhcp.c dhcpcd.h if-bsd.c if-linux.c if.h ipv4.c ipv4.h ipv6.c ipv6.h ipv6nd.c
diffstat 11 files changed, 944 insertions(+), 413 deletions(-) [+]
line wrap: on
line diff
--- a/defs.h	Mon Feb 23 17:47:25 2015 +0000
+++ b/defs.h	Thu Feb 26 13:22:41 2015 +0000
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE			"dhcpcd"
-#define VERSION			"6.7.1"
+#define VERSION			"6.7.99.3"
 
 #ifndef CONFIG
 # define CONFIG			SYSCONFDIR "/" PACKAGE ".conf"
--- a/dhcp.c	Mon Feb 23 17:47:25 2015 +0000
+++ b/dhcp.c	Thu Feb 26 13:22:41 2015 +0000
@@ -2990,6 +2990,9 @@
 		/* 0 is a valid fd, so init to -1 */
 		state->raw_fd = state->arp_fd = -1;
 		TAILQ_INIT(&state->arp_states);
+
+		/* Now is a good time to find IPv4 routes */
+		if_initrt(ifp);
 	}
 
 	state->state = DHS_INIT;
--- a/dhcpcd.h	Mon Feb 23 17:47:25 2015 +0000
+++ b/dhcpcd.h	Thu Feb 26 13:22:41 2015 +0000
@@ -122,6 +122,7 @@
 	struct dhcp_opt *dhcp_opts;
 	size_t dhcp_opts_len;
 	struct rt_head *ipv4_routes;
+	struct rt_head *ipv4_kroutes;
 
 	int udp_fd;
 	uint8_t *packet;
--- a/if-bsd.c	Mon Feb 23 17:47:25 2015 +0000
+++ b/if-bsd.c	Thu Feb 26 13:22:41 2015 +0000
@@ -93,13 +93,15 @@
 #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
 #endif
 
-#define COPYOUT(sin, sa)						      \
-	if ((sa) && (sa)->sa_family == AF_INET)				      \
-		(sin) = ((struct sockaddr_in*)(void *)(sa))->sin_addr
+#define COPYOUT(sin, sa) do {						      \
+	if ((sa) && ((sa)->sa_family == AF_INET || (sa)->sa_family == 255))   \
+		(sin) = ((struct sockaddr_in*)(void *)(sa))->sin_addr;	      \
+	} while (0)
 
-#define COPYOUT6(sin, sa)						      \
-	if ((sa) && (sa)->sa_family == AF_INET6)			      \
-		(sin) = ((struct sockaddr_in6*)(void *)(sa))->sin6_addr
+#define COPYOUT6(sin, sa) do {						      \
+	if ((sa) && ((sa)->sa_family == AF_INET6 || (sa)->sa_family == 255))  \
+		(sin) = ((struct sockaddr_in6*)(void *)(sa))->sin6_addr;      \
+	} while (0)
 
 #ifndef CLLADDR
 #  define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
@@ -259,6 +261,33 @@
 	return 0;
 }
 
+static void
+get_addrs(int type, char *cp, struct sockaddr **sa)
+{
+	int i;
+
+	for (i = 0; i < RTAX_MAX; i++) {
+		if (type & (1 << i)) {
+			sa[i] = (struct sockaddr *)cp;
+			RT_ADVANCE(cp, sa[i]);
+		} else
+			sa[i] = NULL;
+	}
+}
+
+static struct interface *
+if_findsdl(struct dhcpcd_ctx *ctx, struct sockaddr_dl *sdl)
+{
+
+	if (sdl->sdl_nlen) {
+		char ifname[IF_NAMESIZE];
+		memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
+		ifname[sdl->sdl_nlen] = '\0';
+		return if_find(ctx, ifname);
+	}
+	return NULL;
+}
+
 #ifdef INET
 const char *if_pfname = "Berkley Packet Filter";
 
@@ -454,8 +483,67 @@
 	return r;
 }
 
+static int
+if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct rt_msghdr *rtm)
+{
+	char *cp;
+	struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+	cp = (char *)(void *)(rtm + 1);
+	sa = (struct sockaddr *)(void *)cp;
+	if (sa->sa_family != AF_INET)
+		return -1;
+	if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
+		return -1;
+#ifdef RTF_CLONED
+	if (rtm->rtm_flags & RTF_CLONED)
+		return -1;
+#endif
+#ifdef RTF_LOCAL
+	if (rtm->rtm_flags & RTF_LOCAL)
+		return -1;
+#endif
+#ifdef RTF_BROADCAST
+	if (rtm->rtm_flags & RTF_BROADCAST)
+		return -1;
+#endif
+
+	get_addrs(rtm->rtm_addrs, cp, rti_info);
+	memset(rt, 0, sizeof(*rt));
+	COPYOUT(rt->dest, rti_info[RTAX_DST]);
+	if (rtm->rtm_addrs & RTA_NETMASK)
+		COPYOUT(rt->net, rti_info[RTAX_NETMASK]);
+	else
+		rt->net.s_addr = INADDR_BROADCAST;
+	COPYOUT(rt->gate, rti_info[RTAX_GATEWAY]);
+#ifdef SIOCGIFPRIORITY
+	rt->metric = rtm->rtm_priority;
+#endif
+
+	if (rtm->rtm_index)
+		rt->iface = if_findindex(ctx, rtm->rtm_index);
+	else if (rtm->rtm_addrs & RTA_IFP) {
+		struct sockaddr_dl *sdl;
+
+		sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_IFP];
+		rt->iface = if_findsdl(ctx, sdl);
+	}
+	/* If we don't have an interface and it's a host route, it maybe
+	 * to a local ip via the loopback interface. */
+	if (rt->iface == NULL &&
+	    !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
+	{
+		struct ipv4_addr *ia;
+
+		if ((ia = ipv4_findaddr(ctx, &rt->dest)))
+			rt->iface = ia->iface;
+	}
+
+	return 0;
+}
+
 int
-if_route(const struct rt *rt, int action)
+if_route(unsigned char cmd, const struct rt *rt, struct rt *srt)
 {
 	const struct dhcp_state *state;
 	union sockunion {
@@ -467,7 +555,7 @@
 	struct rtm
 	{
 		struct rt_msghdr hdr;
-		char buffer[sizeof(su) * 5];
+		char buffer[sizeof(su) * RTAX_MAX];
 	} rtm;
 	char *bp = rtm.buffer;
 	size_t l;
@@ -489,45 +577,53 @@
 		ADDSU;							      \
 	}
 
-	state = D_CSTATE(rt->iface);
+	if (cmd != RTM_DELETE)
+		state = D_CSTATE(rt->iface);
+	else	/* appease GCC */
+		state = NULL;
 	memset(&rtm, 0, sizeof(rtm));
 	rtm.hdr.rtm_version = RTM_VERSION;
 	rtm.hdr.rtm_seq = 1;
+	rtm.hdr.rtm_type = cmd;
 	rtm.hdr.rtm_addrs = RTA_DST;
-	if (action == 0)
-		rtm.hdr.rtm_type = RTM_CHANGE;
-	else if (action > 0) {
-		rtm.hdr.rtm_type = RTM_ADD;
+	if (cmd == RTM_ADD || cmd == RTM_CHANGE)
 		rtm.hdr.rtm_addrs |= RTA_GATEWAY;
-	} else
-		rtm.hdr.rtm_type = RTM_DELETE;
 	rtm.hdr.rtm_flags = RTF_UP;
 #ifdef RTF_PINNED
-	if (rtm.hdr.rtm_type != RTM_ADD)
+	if (cmd != RTM_ADD)
 		rtm.hdr.rtm_flags |= RTF_PINNED;
 #endif
 #ifdef SIOCGIFPRIORITY
 	rtm.hdr.rtm_priority = rt->metric;
 #endif
 
-	/* None interface subnet routes are static. */
-	if (rt->gate.s_addr != INADDR_ANY ||
-	    rt->net.s_addr != state->net.s_addr ||
-	    rt->dest.s_addr != (state->addr.s_addr & state->net.s_addr))
-		rtm.hdr.rtm_flags |= RTF_STATIC;
+	if (cmd != RTM_DELETE) {
+		rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP;
+		/* None interface subnet routes are static. */
+		if (rt->gate.s_addr != INADDR_ANY ||
+		    rt->net.s_addr != state->net.s_addr ||
+		    rt->dest.s_addr != (state->addr.s_addr & state->net.s_addr))
+			rtm.hdr.rtm_flags |= RTF_STATIC;
+	}
 	if (rt->dest.s_addr == rt->gate.s_addr &&
 	    rt->net.s_addr == INADDR_BROADCAST)
 		rtm.hdr.rtm_flags |= RTF_HOST;
 	else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) &&
 	    rt->net.s_addr == INADDR_BROADCAST)
+	{
 		rtm.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY;
-	else {
+		/* Going via lo0 so remove the interface flags */
+		if (cmd == RTM_ADD)
+			rtm.hdr.rtm_addrs &= ~(RTA_IFA | RTA_IFP);
+	} else {
 		rtm.hdr.rtm_addrs |= RTA_NETMASK;
 		if (rtm.hdr.rtm_flags & RTF_STATIC)
 			rtm.hdr.rtm_flags |= RTF_GATEWAY;
-		if (action >= 0)
-			rtm.hdr.rtm_addrs |= RTA_IFA;
 	}
+	if (((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
+	    !(rtm.hdr.rtm_flags & RTF_GATEWAY)) ||
+	    cmd == RTM_GET)
+		rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP;
 
 	ADDADDR(&rt->dest);
 	if (rtm.hdr.rtm_addrs & RTA_GATEWAY) {
@@ -544,23 +640,74 @@
 	if (rtm.hdr.rtm_addrs & RTA_NETMASK)
 		ADDADDR(&rt->net);
 
-	if (rtm.hdr.rtm_addrs & RTA_IFP) {
-		if_linkaddr(&su.sdl, rt->iface);
-		ADDSU;
+	if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
+	    (rtm.hdr.rtm_addrs & (RTA_IFP | RTA_IFA)))
+	{
+		rtm.hdr.rtm_index = (unsigned short)rt->iface->index;
+		if (rtm.hdr.rtm_addrs & RTA_IFP) {
+			if_linkaddr(&su.sdl, rt->iface);
+			ADDSU;
+		}
+
+		if (rtm.hdr.rtm_addrs & RTA_IFA)
+			ADDADDR(&state->addr);
 	}
 
-	if (rtm.hdr.rtm_addrs & RTA_IFA)
-		ADDADDR(&state->addr);
-
 #undef ADDADDR
 #undef ADDSU
 
 	rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
+	retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
 
-	retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
+	if (cmd == RTM_GET && retval == 0) {
+		retval = read(s, &rtm, sizeof(rtm));
+		if (retval < (int)sizeof(struct rt_msghdr) ||
+		    retval < rtm.hdr.rtm_msglen)
+			retval = -1;
+		else
+			retval = if_copyrt(rt->iface->ctx, srt, &rtm.hdr);
+	}
+
 	close(s);
 	return retval;
 }
+
+int
+if_initrt(struct interface *ifp)
+{
+	struct rt_msghdr *rtm;
+	int mib[6];
+	size_t needed;
+	char *buf, *p, *end;
+	struct rt rt;
+
+	ipv4_freerts(ifp->ctx->ipv4_kroutes);
+
+	mib[0] = CTL_NET;
+	mib[1] = PF_ROUTE;
+	mib[2] = 0;
+	mib[3] = AF_INET;
+	mib[4] = NET_RT_DUMP;
+	mib[5] = 0;
+
+	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+		return -1;
+	if (needed == 0)
+		return 0;
+	if ((buf = malloc(needed)) == NULL)
+		return -1;
+	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+		return -1;
+
+	end = buf + needed;
+	for (p = buf; p < end; p += rtm->rtm_msglen) {
+		rtm = (struct rt_msghdr *)(void *)p;
+		if (if_copyrt(ifp->ctx, &rt, rtm) == 0)
+			ipv4_handlert(ifp->ctx, RTM_ADD, &rt);
+	}
+	free(buf);
+	return 0;
+}
 #endif
 
 #ifdef INET6
@@ -642,8 +789,110 @@
 	return r;
 }
 
+
+static int
+if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, struct rt_msghdr *rtm)
+{
+	char *cp;
+	struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+	cp = (char *)(void *)(rtm + 1);
+	sa = (struct sockaddr *)(void *)cp;
+	if (sa->sa_family != AF_INET6)
+		return -1;
+	if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
+		return -1;
+#ifdef RTF_CLONED
+	if (rtm->rtm_flags & (RTF_CLONED | RTF_HOST))
+		return -1;
+#else
+	if (rtm->rtm_flags & RTF_HOST)
+		return -1;
+#endif
+#ifdef RTF_LOCAL
+	if (rtm->rtm_flags & RTF_LOCAL)
+		return -1;
+#endif
+
+	get_addrs(rtm->rtm_addrs, cp, rti_info);
+	memset(rt, 0, sizeof(*rt));
+	COPYOUT6(rt->dest, rti_info[RTAX_DST]);
+	if (rtm->rtm_addrs & RTA_NETMASK) {
+		/*
+		 * We need to zero out the struct beyond sin6_len and
+		 * ensure it's valid.
+		 * I have no idea what the invalid data is for, could be
+		 * a kernel bug or actually used for something.
+		 * Either way it needs to be zeroed out.
+		 */
+		struct sockaddr_in6 *sin6;
+		size_t e, i, len = 0, final = 0;
+
+		sin6 = (struct sockaddr_in6 *)(void *)rti_info[RTAX_NETMASK];
+		rt->net = sin6->sin6_addr;
+		e = sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr);
+		if (e > sizeof(struct in6_addr))
+			e = sizeof(struct in6_addr);
+		for (i = 0; i < e; i++) {
+			switch (rt->net.s6_addr[i] & 0xff) {
+			case 0xff:
+				/* We don't really want the length,
+				 * just that it's valid */
+				len++;
+				break;
+			case 0xfe:
+			case 0xfc:
+			case 0xf8:
+			case 0xf0:
+			case 0xe0:
+			case 0xc0:
+			case 0x80:
+				len++;
+				final = 1;
+				break;
+			default:
+				rt->net.s6_addr[i] = 0x00;
+				final = 1;
+				break;
+			}
+			if (final)
+				break;
+		}
+		if (len == 0)
+			i = 0;
+		while (i < sizeof(rt->net.s6_addr))
+			rt->net.s6_addr[i++] = 0x00;
+	} else
+		ipv6_mask(&rt->net, 128);
+	COPYOUT6(rt->gate, rti_info[RTAX_GATEWAY]);
+#ifdef SIOCGIFPRIORITY
+	rt->metric = rtm->rtm_priority;
+#endif
+
+	if (rtm->rtm_index)
+		rt->iface = if_findindex(ctx, rtm->rtm_index);
+	else if (rtm->rtm_addrs & RTA_IFP) {
+		struct sockaddr_dl *sdl;
+
+		sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_IFP];
+		rt->iface = if_findsdl(ctx, sdl);
+	}
+	/* If we don't have an interface and it's a host route, it maybe
+	 * to a local ip via the loopback interface. */
+	if (rt->iface == NULL &&
+	    !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
+	{
+		struct ipv6_addr *ia;
+
+		if ((ia = ipv6_findaddr(ctx, &rt->dest, 0)))
+			rt->iface = ia->iface;
+	}
+
+	return 0;
+}
+
 int
-if_route6(const struct rt6 *rt, int action)
+if_route6(unsigned char cmd, const struct rt6 *rt, struct rt6 *srt)
 {
 	union sockunion {
 		struct sockaddr sa;
@@ -654,12 +903,11 @@
 	struct rtm
 	{
 		struct rt_msghdr hdr;
-		char buffer[sizeof(su) * 5];
+		char buffer[sizeof(su) * RTAX_MAX];
 	} rtm;
 	char *bp = rtm.buffer;
 	size_t l;
 	int s, retval;
-	const struct ipv6_addr *lla;
 
 	if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
 		return -1;
@@ -683,12 +931,7 @@
 	memset(&rtm, 0, sizeof(rtm));
 	rtm.hdr.rtm_version = RTM_VERSION;
 	rtm.hdr.rtm_seq = 1;
-	if (action == 0)
-		rtm.hdr.rtm_type = RTM_CHANGE;
-	else if (action > 0)
-		rtm.hdr.rtm_type = RTM_ADD;
-	else
-		rtm.hdr.rtm_type = RTM_DELETE;
+	rtm.hdr.rtm_type = cmd;
 	rtm.hdr.rtm_flags = RTF_UP | (int)rt->flags;
 #ifdef RTF_PINNED
 	if (rtm.hdr.rtm_type != RTM_ADD)
@@ -706,14 +949,13 @@
 	 } else
 		rtm.hdr.rtm_flags |= RTF_GATEWAY | RTF_STATIC;
 
-	if (action >= 0) {
+	if (cmd == RTM_ADD)
 		rtm.hdr.rtm_addrs |= RTA_GATEWAY;
-		if (!(rtm.hdr.rtm_flags & RTF_REJECT))
-			rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA;
-	}
+	if (cmd == RTM_GET ||
+	    (cmd == RTM_ADD && !(rtm.hdr.rtm_flags & RTF_REJECT)))
+		rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA;
 
 	ADDADDR(&rt->dest);
-	lla = NULL;
 	if (rtm.hdr.rtm_addrs & RTA_GATEWAY) {
 		if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
 			if_linkaddr(&su.sdl, rt->iface);
@@ -726,51 +968,86 @@
 	if (rtm.hdr.rtm_addrs & RTA_NETMASK)
 		ADDADDR(&rt->net);
 
-	if (rtm.hdr.rtm_addrs & RTA_IFP) {
-		if_linkaddr(&su.sdl, rt->iface);
-		ADDSU;
-	}
+	if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
+	    (rtm.hdr.rtm_addrs & (RTA_IFP | RTA_IFA)))
+	{
+		rtm.hdr.rtm_index = (unsigned short)rt->iface->index;
+		if (rtm.hdr.rtm_addrs & RTA_IFP) {
+			if_linkaddr(&su.sdl, rt->iface);
+			ADDSU;
+		}
 
-	if (rtm.hdr.rtm_addrs & RTA_IFA) {
-		if (lla == NULL) {
+		if (rtm.hdr.rtm_addrs & RTA_IFA) {
+			const struct ipv6_addr *lla;
+
 			lla = ipv6_linklocal(rt->iface);
 			if (lla == NULL) /* unlikely */
-				return -1;
+					return -1;
+			ADDADDRS(&lla->addr, rt->iface->index);
 		}
-		ADDADDRS(&lla->addr, rt->iface->index);
+
+		if (rt->mtu) {
+			rtm.hdr.rtm_inits |= RTV_MTU;
+			rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu;
+		}
 	}
 
 #undef ADDADDR
 #undef ADDSU
 
-	if (action >= 0 && rt->mtu) {
-		rtm.hdr.rtm_inits |= RTV_MTU;
-		rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu;
+	rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
+	retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
+
+	if (cmd == RTM_GET && retval == 0) {
+		retval = read(s, &rtm, sizeof(rtm));
+		if (retval < (int)sizeof(struct rt_msghdr) ||
+		    retval < rtm.hdr.rtm_msglen)
+			retval = -1;
+		else
+			retval = if_copyrt6(rt->iface->ctx, srt, &rtm.hdr);
 	}
 
-	rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
-
-	retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
 	close(s);
 	return retval;
 }
-#endif
 
-static void
-get_addrs(int type, char *cp, struct sockaddr **sa)
+int
+if_initrt6(struct interface *ifp)
 {
-	int i;
+	struct rt_msghdr *rtm;
+	int mib[6];
+	size_t needed;
+	char *buf, *p, *end;
+	struct rt6 rt;
+
+	ipv6_freerts(&ifp->ctx->ipv6->kroutes);
 
-	for (i = 0; i < RTAX_MAX; i++) {
-		if (type & (1 << i)) {
-			sa[i] = (struct sockaddr *)cp;
-			RT_ADVANCE(cp, sa[i]);
-		} else
-			sa[i] = NULL;
+	mib[0] = CTL_NET;
+	mib[1] = PF_ROUTE;
+	mib[2] = 0;
+	mib[3] = AF_INET6;
+	mib[4] = NET_RT_DUMP;
+	mib[5] = 0;
+
+	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+		return -1;
+	if (needed == 0)
+		return 0;
+	if ((buf = malloc(needed)) == NULL)
+		return -1;
+	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+		return -1;
+
+	end = buf + needed;
+	for (p = buf; p < end; p += rtm->rtm_msglen) {
+		rtm = (struct rt_msghdr *)(void *)p;
+		if (if_copyrt6(ifp->ctx, &rt, rtm) == 0)
+			ipv6_handlert(ifp->ctx, RTM_ADD, &rt);
 	}
+	free(buf);
+	return 0;
 }
 
-#ifdef INET6
 int
 if_addrflags6(const struct in6_addr *addr, const struct interface *ifp)
 {
@@ -862,11 +1139,8 @@
 	int ifa_flags;
 #endif
 
-	bytes = read(ctx->link_fd, msg, sizeof(msg));
-	if (bytes == -1)
+	if ((bytes = read(ctx->link_fd, msg, sizeof(msg))) == -1)
 		return -1;
-	if (bytes == 0)
-		return 0;
 	e = msg + bytes;
 	for (p = msg; p < e; p += rtm->rtm_msglen) {
 		rtm = (struct rt_msghdr *)(void *)p;
@@ -919,27 +1193,16 @@
 		case RTM_DELETE:
 			cp = (char *)(void *)(rtm + 1);
 			sa = (struct sockaddr *)(void *)cp;
-			get_addrs(rtm->rtm_addrs, cp, rti_info);
 			switch (sa->sa_family) {
 #ifdef INET
 			case AF_INET:
-				if (rtm->rtm_type != RTM_DELETE)
-					break;
-				if (~rtm->rtm_addrs &
-				    (RTA_DST | RTA_GATEWAY | RTA_NETMASK))
-					break;
-				memset(&rt, 0, sizeof(rt));
-				rt.iface = NULL;
-				COPYOUT(rt.dest, rti_info[RTAX_DST]);
-				COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
-				COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
-				ipv4_routedeleted(ctx, &rt);
+				if (if_copyrt(ctx, &rt, rtm) == 0)
+					ipv4_handlert(ctx, rtm->rtm_type, &rt);
 				break;
 #endif
 #ifdef INET6
 			case AF_INET6:
-				if (~rtm->rtm_addrs &
-				    (RTA_DST | RTA_GATEWAY))
+				if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
 					break;
 				/*
 				 * BSD caches host routes in the
@@ -949,6 +1212,7 @@
 				 * with a hardware address
 				 */
 				if (rtm->rtm_flags & (RTF_HOST)) {
+					get_addrs(rtm->rtm_addrs, cp, rti_info);
 					COPYOUT6(ia6, rti_info[RTAX_DST]);
 					DESCOPE(&ia6);
 					if (rti_info[RTAX_GATEWAY]->sa_family
@@ -965,16 +1229,8 @@
 					break;
 				}
 
-				if (rtm->rtm_type != RTM_DELETE)
-					break;
-				if (!(rtm->rtm_addrs & RTA_NETMASK))
-					break;
-				memset(&rt6, 0, sizeof(rt6));
-				rt6.iface = NULL;
-				COPYOUT6(rt6.dest, rti_info[RTAX_DST]);
-				COPYOUT6(rt6.net, rti_info[RTAX_NETMASK]);
-				COPYOUT6(rt6.gate, rti_info[RTAX_GATEWAY]);
-				ipv6_routedeleted(ctx, &rt6);
+				if (if_copyrt6(ctx, &rt6, rtm) == 0)
+					ipv6_handlert(ctx, rtm->rtm_type, &rt6);
 				break;
 #endif
 			}
@@ -1042,7 +1298,6 @@
 			break;
 		}
 	}
-
 	return 0;
 }
 
--- a/if-linux.c	Mon Feb 23 17:47:25 2015 +0000
+++ b/if-linux.c	Thu Feb 26 13:22:41 2015 +0000
@@ -320,7 +320,8 @@
 
 static int
 get_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, int fd, int flags,
-    int (*callback)(struct dhcpcd_ctx *, struct interface *,struct nlmsghdr *))
+    int (*callback)(struct dhcpcd_ctx *, struct interface *,
+    struct nlmsghdr *, void *), void *data)
 {
 	char *buf = NULL, *nbuf;
 	ssize_t bytes;
@@ -363,8 +364,10 @@
 			goto eexit;
 		}
 		/* Ignore message if it is not from kernel */
-		if (nladdr.nl_pid != 0)
+		if (nladdr.nl_pid != 0) {
+			r = 0;
 			continue;
+		}
 
 		for (nlm = (struct nlmsghdr *)(void *)buf;
 		     nlm && NLMSG_OK(nlm, (size_t)bytes);
@@ -376,7 +379,7 @@
 			if (r)
 				continue;
 			if (callback) {
-				r = callback(ctx, ifp, nlm);
+				r = callback(ctx, ifp, nlm, data);
 				if (r != 0)
 					goto eexit;
 			}
@@ -388,6 +391,113 @@
 	return r;
 }
 
+#ifdef INET
+static int
+if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
+{
+	size_t len;
+	struct rtmsg *rtm;
+	struct rtattr *rta;
+	struct in_addr prefsrc;
+
+	len = nlm->nlmsg_len - sizeof(*nlm);
+	if (len < sizeof(*rtm)) {
+		errno = EBADMSG;
+		return -1;
+	}
+	rtm = (struct rtmsg *)NLMSG_DATA(nlm);
+	if (rtm->rtm_table != RT_TABLE_MAIN || rtm->rtm_family != AF_INET)
+		return -1;
+
+	memset(rt, 0, sizeof(*rt));
+	prefsrc.s_addr = INADDR_ANY;
+	rta = (struct rtattr *)RTM_RTA(rtm);
+	len = RTM_PAYLOAD(nlm);
+	while (RTA_OK(rta, len)) {
+		switch (rta->rta_type) {
+		case RTA_DST:
+			memcpy(&rt->dest.s_addr, RTA_DATA(rta),
+			    sizeof(rt->dest.s_addr));
+			break;
+		case RTA_GATEWAY:
+			memcpy(&rt->gate.s_addr, RTA_DATA(rta),
+			    sizeof(rt->gate.s_addr));
+			break;
+		case RTA_PREFSRC:
+			memcpy(&prefsrc.s_addr, RTA_DATA(rta),
+			    sizeof(prefsrc.s_addr));
+			break;
+		case RTA_OIF:
+			rt->iface = if_findindex(ctx,
+			    *(unsigned int *)RTA_DATA(rta));
+			break;
+		case RTA_PRIORITY:
+			rt->metric = *(unsigned int *)RTA_DATA(rta);
+			break;
+		}
+		rta = RTA_NEXT(rta, len);
+	}
+
+	inet_cidrtoaddr(rtm->rtm_dst_len, &rt->net);
+	if (rt->iface == NULL && prefsrc.s_addr != INADDR_ANY) {
+		struct ipv4_addr *ap;
+
+		/* For some reason the default route comes back with the
+		 * loopback interface in RTA_OIF? Lets find it by
+		 * preferred source address */
+		if ((ap = ipv4_findaddr(ctx, &prefsrc)))
+			rt->iface = ap->iface;
+	}
+	return 0;
+}
+#endif
+
+#ifdef INET6
+static int
+if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, struct nlmsghdr *nlm)
+{
+	size_t len;
+	struct rtmsg *rtm;
+	struct rtattr *rta;
+
+	len = nlm->nlmsg_len - sizeof(*nlm);
+	if (len < sizeof(*rtm)) {
+		errno = EBADMSG;
+		return -1;
+	}
+	rtm = (struct rtmsg *)NLMSG_DATA(nlm);
+	if (rtm->rtm_table != RT_TABLE_MAIN || rtm->rtm_family != AF_INET6)
+		return -1;
+
+	memset(rt, 0, sizeof(*rt));
+	rta = (struct rtattr *)RTM_RTA(rtm);
+	len = RTM_PAYLOAD(nlm);
+	while (RTA_OK(rta, len)) {
+		switch (rta->rta_type) {
+		case RTA_DST:
+			memcpy(&rt->dest.s6_addr, RTA_DATA(rta),
+			    sizeof(rt->dest.s6_addr));
+			break;
+		case RTA_GATEWAY:
+			memcpy(&rt->gate.s6_addr, RTA_DATA(rta),
+			    sizeof(rt->gate.s6_addr));
+			break;
+		case RTA_OIF:
+			rt->iface = if_findindex(ctx,
+			    *(unsigned int *)RTA_DATA(rta));
+			break;
+		case RTA_PRIORITY:
+			rt->metric = *(unsigned int *)RTA_DATA(rta);
+			break;
+		}
+		rta = RTA_NEXT(rta, len);
+	}
+
+	ipv6_mask(&rt->net, rtm->rtm_dst_len);
+	return 0;
+}
+#endif
+
 /* Work out the maximum pid size */
 static inline long long
 get_max_pid_t()
@@ -405,29 +515,31 @@
     struct nlmsghdr *nlm)
 {
 	size_t len;
-	unsigned int metric;
-	struct rtattr *rta;
 	struct rtmsg *rtm;
+	int cmd;
 #ifdef INET
 	struct rt rt;
 #endif
 #ifdef INET6
 	struct rt6 rt6;
 #endif
-
-	if (nlm->nlmsg_type != RTM_DELROUTE)
+	switch (nlm->nlmsg_type) {
+	case RTM_NEWROUTE:
+		cmd = RTM_ADD;
+		break;
+	case RTM_DELROUTE:
+		cmd = RTM_DELETE;
+		break;
+	default:
 		return 0;
+	}
 
 	len = nlm->nlmsg_len - sizeof(*nlm);
 	if (len < sizeof(*rtm)) {
 		errno = EBADMSG;
 		return -1;
 	}
-	rtm = NLMSG_DATA(nlm);
-	if (rtm->rtm_type != RTN_UNICAST ||
-	    rtm->rtm_table != RT_TABLE_MAIN ||
-	    (rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6))
-		return 1;
+
 	/* Ignore messages generated by us.
 	 * For some reason we get messages generated by us
 	 * with a very large value in nlmsg_pid that seems to be
@@ -435,83 +547,23 @@
 	if (nlm->nlmsg_pid > get_max_pid_t())
 		return 1;
 
-	rta = (struct rtattr *)(void *)((char *)rtm +NLMSG_ALIGN(sizeof(*rtm)));
-	len = NLMSG_PAYLOAD(nlm, sizeof(*rtm));
-#ifdef INET
-	if (rtm->rtm_family == AF_INET)
-		memset(&rt, 0, sizeof(rt));
-#endif
-#ifdef INET6
-	if (rtm->rtm_family == AF_INET6)
-		memset(&rt6, 0, sizeof(rt6));
-#endif
-	metric = 0;
-	while (RTA_OK(rta, len)) {
-		switch (rtm->rtm_family) {
+	rtm = NLMSG_DATA(nlm);
+	switch (rtm->rtm_family) {
 #ifdef INET
-		case AF_INET:
-			switch (rta->rta_type) {
-			case RTA_DST:
-				memcpy(&rt.dest.s_addr, RTA_DATA(rta),
-				    sizeof(rt.dest.s_addr));
-				break;
-			case RTA_GATEWAY:
-				memcpy(&rt.gate.s_addr, RTA_DATA(rta),
-				    sizeof(rt.gate.s_addr));
-				break;
-			case RTA_OIF:
-				rt.iface = if_findindex(ctx,
-				    *(unsigned int *)RTA_DATA(rta));
-				break;
-			}
-			break;
+	case AF_INET:
+		if (if_copyrt(ctx, &rt, nlm) == 0)
+			ipv4_handlert(ctx, cmd, &rt);
+		break;
 #endif
 #ifdef INET6
-		case AF_INET6:
-			switch (rta->rta_type) {
-			case RTA_DST:
-				memcpy(&rt6.dest.s6_addr, RTA_DATA(rta),
-				    sizeof(rt6.dest.s6_addr));
-				break;
-			case RTA_GATEWAY:
-				memcpy(&rt6.gate.s6_addr, RTA_DATA(rta),
-				    sizeof(rt6.gate.s6_addr));
-				break;
-			case RTA_OIF:
-				rt6.iface = if_findindex(ctx,
-				    *(unsigned int *)RTA_DATA(rta));
-				break;
-			}
-			break;
+	case AF_INET6:
+		if (if_copyrt6(ctx, &rt6, nlm) == 0)
+			ipv6_handlert(ctx, cmd, &rt6);
+		break;
 #endif
-		}
-		switch (rta->rta_type) {
-		case RTA_PRIORITY:
-			metric = *(unsigned int *)RTA_DATA(rta);
-			break;
-		}
-		rta = RTA_NEXT(rta, len);
 	}
 
-	switch (rtm->rtm_family) {
-#ifdef INET
-		case AF_INET:
-			if (rt.iface != NULL && metric == rt.iface->metric) {
-				inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
-				ipv4_routedeleted(ctx, &rt);
-			}
-			break;
-#endif
-#ifdef INET6
-		case AF_INET6:
-			if (rt6.iface != NULL && metric == rt6.iface->metric) {
-				ipv6_mask(&rt6.net, rtm->rtm_dst_len);
-				ipv6_routedeleted(ctx, &rt6);
-			}
-			break;
-#endif
-	}
-	return 1;
+	return 0;
 }
 
 static int
@@ -541,7 +593,7 @@
 		 * so it's not really an error */
 		return 1;
 	}
-	rta = (struct rtattr *) IFA_RTA(ifa);
+	rta = (struct rtattr *)IFA_RTA(ifa);
 	len = NLMSG_PAYLOAD(nlm, sizeof(*ifa));
 	switch (ifa->ifa_family) {
 #ifdef INET
@@ -585,7 +637,7 @@
 		break;
 #endif
 	}
-	return 1;
+	return 0;
 }
 
 static uint8_t
@@ -665,13 +717,13 @@
 		ipv6nd_neighbour(ctx, &addr6, flags);
 	}
 
-	return 1;
+	return 0;
 }
 #endif
 
 static int
 link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
-    struct nlmsghdr *nlm)
+    struct nlmsghdr *nlm, __unused void *data)
 {
 	int r;
 	size_t len;
@@ -700,7 +752,7 @@
 	}
 	ifi = NLMSG_DATA(nlm);
 	if (ifi->ifi_flags & IFF_LOOPBACK)
-		return 1;
+		return 0;
 	rta = (struct rtattr *)(void *)((char *)ifi +NLMSG_ALIGN(sizeof(*ifi)));
 	len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
 	*ifn = '\0';
@@ -712,7 +764,7 @@
 			/* Ignore wireless messages */
 			if (nlm->nlmsg_type == RTM_NEWLINK &&
 			    ifi->ifi_change == 0)
-				return 1;
+				return 0;
 			break;
 		case IFLA_IFNAME:
 			strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
@@ -726,7 +778,7 @@
 
 	if (nlm->nlmsg_type == RTM_DELLINK) {
 		dhcpcd_handleinterface(ctx, -1, ifn);
-		return 1;
+		return 0;
 	}
 
 	/* Virtual interfaces may not get a valid hardware address
@@ -735,12 +787,12 @@
 	 * that that don't exist until they have one. */
 	if (ifi->ifi_flags & IFF_MASTER && !hwaddr) {
 		dhcpcd_handleinterface(ctx, -1, ifn);
-		return 1;
+		return 0;
 	}
 
 	/* Check for interface name change */
 	if (handle_rename(ctx, (unsigned int)ifi->ifi_index, ifn))
-		    return 1;
+		return 0;
 
 	/* Check for a new interface */
 	ifp = if_find(ctx, ifn);
@@ -749,7 +801,7 @@
 		 * the interface rather than the kernel. */
 		if (dev_listening(ctx) < 1)
 			dhcpcd_handleinterface(ctx, 1, ifn);
-		return 1;
+		return 0;
 	}
 
 	/* Re-read hardware address and friends */
@@ -764,7 +816,7 @@
 	dhcpcd_handlecarrier(ctx,
 	    ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN,
 	    ifi->ifi_flags, ifn);
-	return 1;
+	return 0;
 }
 
 int
@@ -772,13 +824,14 @@
 {
 
 	return get_netlink(ctx, NULL,
-	    ctx->link_fd, MSG_DONTWAIT, &link_netlink);
+	    ctx->link_fd, MSG_DONTWAIT, &link_netlink, NULL);
 }
 
 static int
 send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
     int protocol, struct nlmsghdr *hdr,
-    int (*callback)(struct dhcpcd_ctx *, struct interface *,struct nlmsghdr *))
+    int (*callback)(struct dhcpcd_ctx *, struct interface *,
+    struct nlmsghdr *, void *), void *data)
 {
 	int s, r;
 	struct sockaddr_nl snl;
@@ -802,7 +855,7 @@
 	hdr->nlmsg_seq = ++seq;
 
 	if (sendmsg(s, &msg, 0) != -1)
-		r = get_netlink(ctx, ifp, s, 0, callback);
+		r = get_netlink(ctx, ifp, s, 0, callback, data);
 	else
 		r = -1;
 	close(s);
@@ -861,7 +914,7 @@
 {
 
 	*rem -= NLA_ALIGN(nla->nla_len);
-	return (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len));
+	return (struct nlattr *)(void *)((char *)nla + NLA_ALIGN(nla->nla_len));
 }
 
 #define NLA_TYPE(nla) ((nla)->nla_type & NLA_TYPE_MASK)
@@ -935,7 +988,7 @@
 
 	memset(tb, 0, sizeof(*tb) * ((unsigned int)maxtype + 1));
 	ghdr = NLMSG_DATA(nlm);
-	head = (struct nlattr *)((char *) ghdr + GENL_HDRLEN);
+	head = (struct nlattr *)(void *)((char *) ghdr + GENL_HDRLEN);
 	len = nlm->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
 	NLA_FOR_EACH_ATTR(nla, head, len, rem) {
 		type = NLA_TYPE(nla);
@@ -948,7 +1001,7 @@
 
 static int
 _gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
-    struct nlmsghdr *nlm)
+    struct nlmsghdr *nlm, __unused void *data)
 {
 	struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
 	uint16_t family;
@@ -959,7 +1012,7 @@
 		errno = ENOENT;
 		return -1;
 	}
-	family = *(uint16_t *)NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
+	family = *(uint16_t *)(void *)NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
 	return (int)family;
 }
 
@@ -978,12 +1031,12 @@
 	    CTRL_ATTR_FAMILY_NAME, name) == -1)
 		return -1;
 	return send_netlink(ctx, NULL, NETLINK_GENERIC, &nlm.hdr,
-	    &_gnl_getfamily);
+	    &_gnl_getfamily, NULL);
 }
 
 static int
 _if_getssid(__unused struct dhcpcd_ctx *ctx, struct interface *ifp,
-    struct nlmsghdr *nlm)
+    struct nlmsghdr *nlm, __unused void *data)
 {
 	struct nlattr *tb[NL80211_ATTR_SSID + 1];
 
@@ -1028,7 +1081,7 @@
 	nla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index);
 
 	return send_netlink(ifp->ctx, ifp,
-	    NETLINK_GENERIC, &nlm.hdr, &_if_getssid);
+	    NETLINK_GENERIC, &nlm.hdr, &_if_getssid, NULL);
 }
 #endif
 
@@ -1246,13 +1299,22 @@
 		add_attr_l(&nlm.hdr, sizeof(nlm), IFA_BROADCAST,
 		    &broadcast->s_addr, sizeof(broadcast->s_addr));
 
-	if (send_netlink(iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL) == -1)
+	if (send_netlink(iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr,
+	    NULL, NULL) == -1)
 		retval = -1;
 	return retval;
 }
 
+static int
+_if_copyrt(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+    struct nlmsghdr *nlm, void *data)
+{
+
+	return if_copyrt(ctx, (struct rt *)data, nlm);
+}
+
 int
-if_route(const struct rt *rt, int action)
+if_route(unsigned char cmd, const struct rt *rt, struct rt *srt)
 {
 	struct nlmr nlm;
 	int retval = 0;
@@ -1260,22 +1322,30 @@
 
 	memset(&nlm, 0, sizeof(nlm));
 	nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-	nlm.hdr.nlmsg_type = RTM_NEWROUTE;
-	if (action == 0)
-		nlm.hdr.nlmsg_flags = NLM_F_REPLACE;
-	else if (action == 1)
+	switch (cmd) {
+	case RTM_GET:
+		nlm.hdr.nlmsg_type = RTM_GETROUTE;
+		break;
+	case RTM_CHANGE:
+		nlm.hdr.nlmsg_type = RTM_NEWROUTE;
+		nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE;
+		break;
+	case RTM_ADD:
+		nlm.hdr.nlmsg_type = RTM_NEWROUTE;
 		nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
-	else
+		break;
+	case RTM_DELETE:
 		nlm.hdr.nlmsg_type = RTM_DELROUTE;
+		break;
+	}
 	nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
 	nlm.rt.rtm_family = AF_INET;
 	nlm.rt.rtm_table = RT_TABLE_MAIN;
 
 	state = D_STATE(rt->iface);
-	if (action == -1 || action == -2)
+	if (cmd == RTM_DELETE)
 		nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
 	else {
-		nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
 		/* We only change route metrics for kernel routes */
 		if (rt->dest.s_addr ==
 		    (state->addr.s_addr & state->net.s_addr) &&
@@ -1302,20 +1372,53 @@
 		    &state->addr.s_addr, sizeof(state->addr.s_addr));
 	}
 	/* If destination == gateway then don't add the gateway */
-	if (rt->dest.s_addr != rt->gate.s_addr ||
-	    rt->net.s_addr != INADDR_BROADCAST)
+	if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
+	    (rt->dest.s_addr != rt->gate.s_addr ||
+	    rt->net.s_addr != INADDR_BROADCAST))
 		add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
 		    &rt->gate.s_addr, sizeof(rt->gate.s_addr));
 
 	if (rt->gate.s_addr != htonl(INADDR_LOOPBACK))
 		add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->iface->index);
-	add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric);
+	if (rt->metric)
+		add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric);
 
 	if (send_netlink(rt->iface->ctx, NULL,
-	    NETLINK_ROUTE, &nlm.hdr, NULL) == -1)
+	    NETLINK_ROUTE, &nlm.hdr, &_if_copyrt, srt) == -1)
 		retval = -1;
 	return retval;
 }
+
+static int
+_if_initrt(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+    struct nlmsghdr *nlm, __unused void *data)
+{
+	struct rt rt;
+
+	if (if_copyrt(ctx, &rt, nlm) == 0)
+		ipv4_handlert(ctx, RTM_ADD, &rt);
+	return 0;
+}
+
+int
+if_initrt(struct interface *ifp)
+{
+	struct nlmr nlm;
+
+	ipv4_freerts(ifp->ctx->ipv4_kroutes);
+
+	memset(&nlm, 0, sizeof(nlm));
+	nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	nlm.hdr.nlmsg_type = RTM_GETROUTE;
+	nlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
+	nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
+	nlm.rt.rtm_family = AF_INET;
+	nlm.rt.rtm_table = RT_TABLE_MAIN;
+	add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, ifp->index);
+
+	return send_netlink(ifp->ctx, ifp,
+	    NETLINK_ROUTE, &nlm.hdr, &_if_initrt, NULL);
+}
 #endif
 
 #ifdef INET6
@@ -1327,7 +1430,7 @@
 	int retval = 0;
 /* IFA_FLAGS is not a define, but is was added at the same time
  * IFA_F_NOPREFIXROUTE was do use that. */
-#ifdef IFA_F_NOPREFIXROUTE
+#if defined(IFA_F_NOPREFIXROUTE) || defined(IFA_F_MANAGETEMPADDR)
 	uint32_t flags = 0;
 #endif
 
@@ -1374,13 +1477,13 @@
 	if (!IN6_IS_ADDR_LINKLOCAL(&ap->addr))
 		flags |= IFA_F_NOPREFIXROUTE;
 #endif
-#ifdef IFA_F_NOPREFIXROUTE
+#if defined(IFA_F_NOPREFIXROUTE) || defined(IFA_F_MANAGETEMPADDR)
 	if (flags)
 		add_attr_32(&nlm.hdr, sizeof(nlm), IFA_FLAGS, flags);
 #endif
 
 	if (send_netlink(ap->iface->ctx, NULL,
-	    NETLINK_ROUTE, &nlm.hdr, NULL) == -1)
+	    NETLINK_ROUTE, &nlm.hdr, NULL, NULL) == -1)
 		retval = -1;
 	return retval;
 }
@@ -1397,7 +1500,8 @@
 		return -1;
 	}
 
-	subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+	subrta = (struct rtattr*)(void *)
+	    (((char*)rta) + RTA_ALIGN(rta->rta_len));
 	subrta->rta_type = type;
 	subrta->rta_len = len;
 	memcpy(RTA_DATA(subrta), &data, sizeof(data));
@@ -1405,31 +1509,45 @@
 	return 0;
 }
 
+static int
+_if_copyrt6(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+    struct nlmsghdr *nlm, void *data)
+{
+
+	return if_copyrt6(ctx, (struct rt6 *)data, nlm);
+}
+
 int
-if_route6(const struct rt6 *rt, int action)
+if_route6(unsigned char cmd, const struct rt6 *rt, struct rt6 *srt)
 {
 	struct nlmr nlm;
-	char metricsbuf[32];
-	struct rtattr *metrics = (void *)metricsbuf;
 	int retval = 0;
 
 	memset(&nlm, 0, sizeof(nlm));
 	nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-	nlm.hdr.nlmsg_type = RTM_NEWROUTE;
-	nlm.hdr.nlmsg_flags = NLM_F_REQUEST;
-	if (action == 0)
-		nlm.hdr.nlmsg_flags |= NLM_F_REPLACE;
-	else if (action == 1)
-		nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
-	else
+	switch (cmd) {
+	case RTM_GET:
+		nlm.hdr.nlmsg_type = RTM_GETROUTE;
+		break;
+	case RTM_CHANGE:
+		nlm.hdr.nlmsg_type = RTM_NEWROUTE;
+		nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE;
+		break;
+	case RTM_ADD:
+		nlm.hdr.nlmsg_type = RTM_NEWROUTE;
+		nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
+		break;
+	case RTM_DELETE:
 		nlm.hdr.nlmsg_type = RTM_DELROUTE;
+		break;
+	}
+	nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
 	nlm.rt.rtm_family = AF_INET6;
 	nlm.rt.rtm_table = RT_TABLE_MAIN;
 
-	if (action == -1 || action == -2)
+	if (cmd == RTM_DELETE)
 		nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
 	else {
-		nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
 		/* None interface subnet routes are static. */
 		if (rt->iface->flags & IFF_LOOPBACK)
 			nlm.rt.rtm_scope = RT_SCOPE_HOST;
@@ -1448,29 +1566,65 @@
 	add_attr_l(&nlm.hdr, sizeof(nlm), RTA_DST,
 	    &rt->dest.s6_addr, sizeof(rt->dest.s6_addr));
 
-	if (action >= 0 && !IN6_IS_ADDR_UNSPECIFIED(&rt->gate))
+	if (cmd == RTM_ADD && !IN6_IS_ADDR_UNSPECIFIED(&rt->gate))
 		add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
 		    &rt->gate.s6_addr, sizeof(rt->gate.s6_addr));
 
 	if (!(rt->flags & RTF_REJECT)) {
 		add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->iface->index);
-		add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric);
+		if (rt->metric)
+			add_attr_32(&nlm.hdr, sizeof(nlm),
+			    RTA_PRIORITY, rt->metric);
 	}
 
-	if (rt->mtu) {
+	if (cmd == RTM_ADD && rt->mtu) {
+		char metricsbuf[32];
+		struct rtattr *metrics = (void *)metricsbuf;
+
 		metrics->rta_type = RTA_METRICS;
 		metrics->rta_len = RTA_LENGTH(0);
 		rta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU, rt->mtu);
 		add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,
-		    RTA_DATA(metrics), RTA_PAYLOAD(metrics));
+		    RTA_DATA(metrics), (unsigned short)RTA_PAYLOAD(metrics));
 	}
 
 	if (send_netlink(rt->iface->ctx, NULL,
-	    NETLINK_ROUTE, &nlm.hdr, NULL) == -1)
+	    NETLINK_ROUTE, &nlm.hdr, &_if_copyrt6, srt) == -1)
 		retval = -1;
 	return retval;
 }
 
+static int
+_if_initrt6(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+    struct nlmsghdr *nlm, __unused void *data)
+{
+	struct rt6 rt;
+
+	if (if_copyrt6(ctx, &rt, nlm) == 0)
+		ipv6_handlert(ctx, RTM_ADD, &rt);
+	return 0;
+}
+
+int
+if_initrt6(struct interface *ifp)
+{
+	struct nlmr nlm;
+
+	ipv6_freerts(&ifp->ctx->ipv6->kroutes);
+
+	memset(&nlm, 0, sizeof(nlm));
+	nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	nlm.hdr.nlmsg_type = RTM_GETROUTE;
+	nlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
+	nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
+	nlm.rt.rtm_family = AF_INET6;
+	nlm.rt.rtm_table = RT_TABLE_MAIN;
+	add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, ifp->index);
+
+	return send_netlink(ifp->ctx, ifp,
+	    NETLINK_ROUTE, &nlm.hdr, &_if_initrt6, NULL);
+}
+
 int
 if_addrflags6(const struct in6_addr *addr, const struct interface *ifp)
 {
@@ -1570,7 +1724,7 @@
 	add_attr_nest_end(&nlm.hdr, afs6);
 	add_attr_nest_end(&nlm.hdr, afs);
 
-	return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL);
+	return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL, NULL);
 }
 
 static const char *prefix = "/proc/sys/net/ipv6/conf";
--- a/if.h	Mon Feb 23 17:47:25 2015 +0000
+++ b/if.h	Thu Feb 26 13:22:41 2015 +0000
@@ -31,23 +31,21 @@
 #include <sys/socket.h>
 
 #include <net/if.h>
+//#include <net/route.h>
 #include <netinet/in.h>
 
-#include "config.h"
-#include "dhcpcd.h"
-#include "ipv4.h"
-#include "ipv6.h"
-
 /* Some systems have route metrics */
 #ifndef HAVE_ROUTE_METRIC
 # if defined(__linux__) || defined(SIOCGIFPRIORITY)
 #  define HAVE_ROUTE_METRIC 1
 # endif
-# ifndef HAVE_ROUTE_METRIC
-#  define HAVE_ROUTE_METRIC 0
-# endif
 #endif
 
+#include "config.h"
+#include "dhcpcd.h"
+#include "ipv4.h"
+#include "ipv6.h"
+
 #define EUI64_ADDR_LEN			8
 #define INFINIBAND_ADDR_LEN		20
 
@@ -102,6 +100,13 @@
 int if_openlinksocket(void);
 int if_managelink(struct dhcpcd_ctx *);
 
+#ifndef RTM_ADD
+#define RTM_ADD		0x1	/* Add Route */
+#define RTM_DELETE	0x2	/* Delete Route */
+#define RTM_CHANGE	0x3	/* Change Metrics or flags */
+#define RTM_GET		0x4	/* Report Metrics */
+#endif
+
 #ifdef INET
 extern const char *if_pfname;
 int if_openrawsocket(struct interface *, int);
@@ -117,10 +122,8 @@
 #define if_deladdress(ifp, addr, net)		\
 	if_address(ifp, addr, net, NULL, -1)
 
-int if_route(const struct rt *rt, int);
-#define if_addroute(rt) if_route(rt, 1)
-#define if_chgroute(rt) if_route(rt, 0)
-#define if_delroute(rt) if_route(rt, -1)
+int if_route(unsigned char, const struct rt *rt, struct rt *);
+int if_initrt(struct interface *);
 #endif
 
 #ifdef INET6
@@ -140,10 +143,8 @@
 int if_addrflags6(const struct in6_addr *, const struct interface *);
 int if_getlifetime6(struct ipv6_addr *);
 
-int if_route6(const struct rt6 *rt, int);
-#define if_addroute6(rt) if_route6(rt, 1)
-#define if_chgroute6(rt) if_route6(rt, 0)
-#define if_delroute6(rt) if_route6(rt, -1)
+int if_route6(unsigned char, const struct rt6 *rt, struct rt6 *);
+int if_initrt6(struct interface *);
 #else
 #define if_checkipv6(a, b, c) (-1)
 #endif
--- a/ipv4.c	Mon Feb 23 17:47:25 2015 +0000
+++ b/ipv4.c	Thu Feb 26 13:22:41 2015 +0000
@@ -180,13 +180,9 @@
 void
 ipv4_freeroutes(struct rt_head *rts)
 {
-	struct rt *r;
 
 	if (rts) {
-		while ((r = TAILQ_FIRST(rts))) {
-			TAILQ_REMOVE(rts, r, next);
-			free(r);
-		}
+		ipv4_freerts(rts);
 		free(rts);
 	}
 }
@@ -201,10 +197,15 @@
 			return -1;
 		TAILQ_INIT(ctx->ipv4_routes);
 	}
+	if (ctx->ipv4_kroutes == NULL) {
+		ctx->ipv4_kroutes = malloc(sizeof(*ctx->ipv4_kroutes));
+		if (ctx->ipv4_kroutes == NULL)
+			return -1;
+		TAILQ_INIT(ctx->ipv4_kroutes);
+	}
 	return 0;
 }
 
-
 /* Interface comparer for working out ordering. */
 static int
 ipv4_ifcmp(const struct interface *si, const struct interface *ti)
@@ -291,7 +292,7 @@
 		return NULL;
 	TAILQ_FOREACH(rt, rts, next) {
 		if (rt->dest.s_addr == r->dest.s_addr &&
-#if HAVE_ROUTE_METRIC
+#ifdef HAVE_ROUTE_METRIC
 		    (srt || (!rt->iface ||
 			rt->iface->metric == r->iface->metric)) &&
 #endif
@@ -328,27 +329,78 @@
 		    addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
 }
 
+static struct rt *
+ipv4_findrt(struct dhcpcd_ctx *ctx, const struct rt *rt, int flags)
+{
+	struct rt *r;
+
+	if (ctx->ipv4_kroutes == NULL)
+		return NULL;
+	TAILQ_FOREACH(r, ctx->ipv4_kroutes, next) {
+		if (rt->dest.s_addr == r->dest.s_addr &&
+		    (!flags || rt->iface == r->iface) &&
+#ifdef HAVE_ROUTE_METRIC
+		    (!flags || rt->metric == r->metric) &&
+#endif
+		    rt->net.s_addr == r->net.s_addr)
+			return r;
+	}
+	return NULL;
+}
+
+void
+ipv4_freerts(struct rt_head *routes)
+{
+	struct rt *rt;
+
+	while ((rt = TAILQ_FIRST(routes))) {
+		TAILQ_REMOVE(routes, rt, next);
+		free(rt);
+	}
+}
+
 /* If something other than dhcpcd removes a route,
  * we need to remove it from our internal table. */
 int
-ipv4_routedeleted(struct dhcpcd_ctx *ctx, const struct rt *rt)
+ipv4_handlert(struct dhcpcd_ctx *ctx, int cmd, struct rt *rt)
 {
 	struct rt *f;
 
-	f = find_route(ctx->ipv4_routes, rt, NULL);
-	if (f == NULL)
-		return 0;
-	desc_route("removing", f);
-	TAILQ_REMOVE(ctx->ipv4_routes, f, next);
-	free(f);
-	return 1;
+	f = ipv4_findrt(ctx, rt, 1);
+	switch (cmd) {
+	case RTM_ADD:
+		if (f == NULL) {
+			if ((f = malloc(sizeof(*f))) == NULL)
+				return -1;
+			*f = *rt;
+			TAILQ_INSERT_TAIL(ctx->ipv4_kroutes, f, next);
+		}
+		break;
+	case RTM_DELETE:
+		if (f) {
+			TAILQ_REMOVE(ctx->ipv4_kroutes, f, next);
+			free(f);
+		}
+
+		/* If we manage the route, remove it */
+		if ((f = find_route(rt->iface->ctx->ipv4_routes, rt, NULL))) {
+			desc_route("removing", f);
+			TAILQ_REMOVE(rt->iface->ctx->ipv4_routes, f, next);
+			free(f);
+		}
+		break;
+	}
+	return 0;
 }
 
-#define n_route(a)	 nc_route(1, a, a)
-#define c_route(a, b)	 nc_route(0, a, b)
+#define n_route(a)	 nc_route(NULL, a)
+#define c_route(a, b)	 nc_route(a, b)
 static int
-nc_route(int add, struct rt *ort, struct rt *nrt)
+nc_route(struct rt *ort, struct rt *nrt)
 {
+#ifdef HAVE_ROUTE_METRIC
+	int retval;
+#endif
 
 	/* Don't set default routes if not asked to */
 	if (nrt->dest.s_addr == 0 &&
@@ -356,17 +408,44 @@
 	    !(nrt->iface->options->options & DHCPCD_GATEWAY))
 		return -1;
 
-	desc_route(add ? "adding" : "changing", nrt);
-	/* We delete and add the route so that we can change metric and
-	 * prefer the interface.
-	 * This also has the nice side effect of flushing ARP entries so
-	 * we don't have to do that manually. */
-	if (if_delroute(ort) == -1 && errno != ESRCH)
-		syslog(LOG_ERR, "%s: ipv4_delroute: %m", ort->iface->name);
-	if (!if_addroute(nrt))
+	desc_route(ort == NULL ? "adding" : "changing", nrt);
+
+	if (ort == NULL) {
+		ort = ipv4_findrt(nrt->iface->ctx, nrt, 0);
+		if (ort && ort->iface == nrt->iface &&
+#ifdef HAVE_ROUTE_METRIC
+		    ort->metric == nrt->metric &&
+#endif
+		    ort->gate.s_addr == nrt->gate.s_addr)
+			return 0;
+	} else if (ort->flags & STATE_FAKE && !(nrt->flags & STATE_FAKE) &&
+	    ort->iface == nrt->iface &&
+#ifdef HAVE_ROUTE_METRIC
+	    ort->metric == nrt->metric &&
+#endif
+	    ort->dest.s_addr == nrt->dest.s_addr &&
+	    ort->net.s_addr ==  nrt->net.s_addr &&
+	    ort->gate.s_addr == nrt->gate.s_addr)
 		return 0;
-	syslog(LOG_ERR, "%s: if_addroute: %m", nrt->iface->name);
+
+#ifdef HAVE_ROUTE_METRIC
+	/* With route metrics, we can safely add the new route before
+	 * deleting the old route. */
+	if ((retval = if_route(RTM_ADD, nrt, NULL))  == -1)
+		syslog(LOG_ERR, "if_route (ADD): %m");
+	if (ort && if_route(RTM_DELETE, ort, NULL) == -1 && errno != ESRCH)
+		syslog(LOG_ERR, "if_route (DEL): %m");
+	return retval;
+#else
+	/* No route metrics, we need to delete the old route before
+	 * adding the new one. */
+	if (ort && if_route(RTM_DELETE, ort, NULL) == -1 && errno != ESRCH)
+		syslog(LOG_ERR, "if_route (DEL): %m");
+	if (if_route(RTM_ADD, nrt, NULL) == 0)
+		return 0;
+	syslog(LOG_ERR, "if_route (ADD): %m");
 	return -1;
+#endif
 }
 
 static int
@@ -375,57 +454,44 @@
 	int retval;
 
 	desc_route("deleting", rt);
-	retval = if_delroute(rt);
+	retval = if_route(RTM_DELETE, rt, NULL);
 	if (retval != 0 && errno != ENOENT && errno != ESRCH)
 		syslog(LOG_ERR,"%s: if_delroute: %m", rt->iface->name);
 	return retval;
 }
 
 static struct rt *
-get_subnet_route(struct dhcpcd_ctx *ctx, struct dhcp_message *dhcp)
+make_subnet_route(const struct interface *ifp)
 {
-	in_addr_t addr;
-	struct in_addr net;
-	struct rt *rt;
+	const struct dhcp_state *s;
+	struct rt *r;
+
+	s = D_CSTATE(ifp);
+	if (s->net.s_addr == INADDR_BROADCAST ||
+	    s->net.s_addr == INADDR_ANY)
+		return NULL;
 
-	addr = dhcp->yiaddr;
-	if (addr == 0)
-		addr = dhcp->ciaddr;
-	/* Ensure we have all the needed values */
-	if (get_option_addr(ctx, &net, dhcp, DHO_SUBNETMASK) == -1)
-		net.s_addr = ipv4_getnetmask(addr);
-	if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY)
+	r = malloc(sizeof(*r));
+	if (r == NULL) {
+		syslog(LOG_ERR, "%s: %m", __func__);
 		return NULL;
-	rt = malloc(sizeof(*rt));
-	rt->dest.s_addr = addr & net.s_addr;
-	rt->net.s_addr = net.s_addr;
-	rt->gate.s_addr = 0;
-	return rt;
+	}
+	r->dest.s_addr = s->addr.s_addr & s->net.s_addr;
+	r->net.s_addr = s->net.s_addr;
+	r->gate.s_addr = INADDR_ANY;
+	return r;
 }
 
 static struct rt_head *
 add_subnet_route(struct rt_head *rt, const struct interface *ifp)
 {
 	struct rt *r;
-	const struct dhcp_state *s;
 
 	if (rt == NULL) /* earlier malloc failed */
 		return NULL;
 
-	s = D_CSTATE(ifp);
-	if (s->net.s_addr == INADDR_BROADCAST ||
-	    s->net.s_addr == INADDR_ANY)
-		return rt;
-
-	r = malloc(sizeof(*r));
-	if (r == NULL) {
-		syslog(LOG_ERR, "%s: %m", __func__);
-		ipv4_freeroutes(rt);
+	if ((r = make_subnet_route(ifp)) == NULL)
 		return NULL;
-	}
-	r->dest.s_addr = s->addr.s_addr & s->net.s_addr;
-	r->net.s_addr = s->net.s_addr;
-	r->gate.s_addr = 0;
 	TAILQ_INSERT_HEAD(rt, r, next);
 	return rt;
 }
@@ -598,6 +664,7 @@
 		return;
 	}
 	TAILQ_INIT(nrs);
+
 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
 		state = D_CSTATE(ifp);
 		if (state == NULL || state->new == NULL || !state->added)
@@ -616,7 +683,10 @@
 			continue;
 		TAILQ_FOREACH_SAFE(rt, dnr, next, rtn) {
 			rt->iface = ifp;
+#ifdef HAVE_ROUTE_METRIC
 			rt->metric = ifp->metric;
+#endif
+			rt->flags = state->added & STATE_FAKE;
 			/* Is this route already in our table? */
 			if ((find_route(nrs, rt, NULL)) != NULL)
 				continue;
@@ -627,9 +697,11 @@
 					continue;
 				if (or->flags & STATE_FAKE ||
 				    or->iface != ifp ||
+#ifdef HAVE_ROUTE_METRIC
+				    rt->metric != or->metric ||
+#endif
 				    or->src.s_addr != state->addr.s_addr ||
-				    rt->gate.s_addr != or->gate.s_addr ||
-				    rt->metric != or->metric)
+				    rt->gate.s_addr != or->gate.s_addr)
 				{
 					if (c_route(or, rt) != 0)
 						continue;
@@ -637,13 +709,15 @@
 				TAILQ_REMOVE(ctx->ipv4_routes, or, next);
 				free(or);
 			} else {
-				if (!(state->added & STATE_FAKE) &&
-				    n_route(rt) != 0)
-					continue;
+				if (state->added & STATE_FAKE) {
+					if (!ipv4_findrt(ctx, rt, 1))
+						continue;
+				} else {
+					if (n_route(rt) != 0)
+						continue;
+				}
 			}
-			rt->flags = STATE_ADDED;
-			if (state->added & STATE_FAKE)
-				rt->flags |= STATE_FAKE;
+			rt->flags |= STATE_ADDED;
 			TAILQ_REMOVE(dnr, rt, next);
 			TAILQ_INSERT_TAIL(nrs, rt, next);
 		}
@@ -659,7 +733,6 @@
 			d_route(rt);
 	}
 	ipv4_freeroutes(ctx->ipv4_routes);
-
 	ctx->ipv4_routes = nrs;
 }
 
@@ -724,6 +797,7 @@
 			return NULL;
 		}
 		TAILQ_INIT(&state->addrs);
+		TAILQ_INIT(&state->routes);
 	}
 	return state;
 }
@@ -762,8 +836,7 @@
 	struct dhcp_lease *lease;
 	struct if_options *ifo = ifp->options;
 	struct ipv4_addr *ap;
-	struct ipv4_state *istate;
-	struct rt *rt;
+	struct ipv4_state *istate = NULL;
 	int r;
 
 	/* As we are now adjusting an interface, we need to ensure
@@ -866,6 +939,7 @@
 			return;
 		istate = ipv4_getstate(ifp);
 		ap = malloc(sizeof(*ap));
+		ap->iface = ifp;
 		ap->addr = lease->addr;
 		ap->net = lease->net;
 		ap->dst.s_addr = INADDR_ANY;
@@ -883,18 +957,11 @@
 	state->addr.s_addr = lease->addr.s_addr;
 	state->net.s_addr = lease->net.s_addr;
 
-	/* We need to delete the subnet route to have our metric or
-	 * prefer the interface. */
-	rt = get_subnet_route(ifp->ctx, dhcp);
-	if (rt != NULL) {
-		rt->iface = ifp;
-		rt->metric = 0;
-		if (!find_route(ifp->ctx->ipv4_routes, rt, NULL))
-			if_delroute(rt);
-		free(rt);
-	}
-
 routes:
+	/* Find any freshly added routes, such as the subnet route.
+	 * We do this because we cannot rely on recieving the kernel
+	 * notification right now via our link socket. */
+	if_initrt(ifp);
 	ipv4_buildroutes(ifp->ctx);
 	script_runreason(ifp, state->reason);
 }
@@ -941,6 +1008,7 @@
 			syslog(LOG_ERR, "%s: %m", __func__);
 			return;
 		}
+		ap->iface = ifp;
 		ap->addr.s_addr = addr->s_addr;
 		ap->net.s_addr = net->s_addr;
 		if (dst)
@@ -971,6 +1039,7 @@
 				TAILQ_REMOVE(&state->addrs, addr, next);
 				free(addr);
 			}
+			ipv4_freerts(&state->routes);
 			free(state);
 		}
 	}
@@ -981,4 +1050,5 @@
 {
 
 	ipv4_freeroutes(ctx->ipv4_routes);
+	ipv4_freeroutes(ctx->ipv4_kroutes);
 }
--- a/ipv4.h	Mon Feb 23 17:47:25 2015 +0000
+++ b/ipv4.h	Thu Feb 26 13:22:41 2015 +0000
@@ -36,7 +36,9 @@
 	struct in_addr net;
 	struct in_addr gate;
 	const struct interface *iface;
+#ifdef HAVE_ROUTE_METRIC
 	unsigned int metric;
+#endif
 	struct in_addr src;
 	uint8_t flags;
 };
@@ -47,11 +49,13 @@
 	struct in_addr addr;
 	struct in_addr net;
 	struct in_addr dst;
+	struct interface *iface;
 };
 TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
 
 struct ipv4_state {
 	struct ipv4_addrhead addrs;
+	struct rt_head routes;
 };
 
 #define IPV4_STATE(ifp)							       \
@@ -72,7 +76,8 @@
 
 void ipv4_buildroutes(struct dhcpcd_ctx *);
 void ipv4_applyaddr(void *);
-int ipv4_routedeleted(struct dhcpcd_ctx *, const struct rt *);
+int ipv4_handlert(struct dhcpcd_ctx *, int, struct rt *);
+void ipv4_freerts(struct rt_head *);
 
 struct ipv4_addr *ipv4_iffindaddr(struct interface *,
     const struct in_addr *, const struct in_addr *);
--- a/ipv6.c	Mon Feb 23 17:47:25 2015 +0000
+++ b/ipv6.c	Thu Feb 26 13:22:41 2015 +0000
@@ -58,10 +58,10 @@
 
 #define ELOOP_QUEUE 7
 #include "common.h"
+#include "if.h"
 #include "dhcpcd.h"
 #include "dhcp6.h"
 #include "eloop.h"
-#include "if.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
 
@@ -151,6 +151,8 @@
 	}
 	TAILQ_INIT(ctx->ra_routers);
 
+	TAILQ_INIT(&ctx->kroutes);
+
 	ctx->sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
 	ctx->sndhdr.msg_iov = ctx->sndiov;
 	ctx->sndhdr.msg_iovlen = 1;
@@ -725,9 +727,6 @@
 	ap->flags |= IPV6_AF_ADDED;
 	if (ap->delegating_iface)
 		ap->flags |= IPV6_AF_DELEGATED;
-	if (ap->iface->options->options & DHCPCD_IPV6RA_OWN &&
-	    ipv6_removesubnet(ap->iface, ap) == -1)
-		syslog(LOG_ERR,"ipv6_removesubnet: %m");
 
 #ifdef IPV6_POLLADDRFLAG
 	eloop_timeout_delete(ap->iface->ctx->eloop,
@@ -1223,6 +1222,9 @@
 
 	if (ap == NULL && ipv6_addlinklocal(ifp) == -1)
 		return -1;
+
+	/* Load existing routes */
+	if_initrt6(ifp);
 	return 0;
 }
 
@@ -1256,17 +1258,14 @@
 void
 ipv6_ctxfree(struct dhcpcd_ctx *ctx)
 {
-	struct rt6 *rt;
 
 	if (ctx->ipv6 == NULL)
 		return;
 
-	while ((rt = TAILQ_FIRST(ctx->ipv6->routes))) {
-		TAILQ_REMOVE(ctx->ipv6->routes, rt, next);
-		free(rt);
-	}
+	ipv6_freerts(ctx->ipv6->routes);
 	free(ctx->ipv6->routes);
 	free(ctx->ipv6->ra_routers);
+	ipv6_freerts(&ctx->ipv6->kroutes);
 	free(ctx->ipv6);
 }
 
@@ -1693,7 +1692,7 @@
 
 	TAILQ_FOREACH(rt, rts, next) {
 		if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) &&
-#if HAVE_ROUTE_METRIC
+#ifdef HAVE_ROUTE_METRIC
 		    (r->iface == NULL || rt->iface == NULL ||
 		    rt->iface->metric == r->iface->metric) &&
 #endif
@@ -1726,30 +1725,75 @@
 		    dest, ipv6_prefixlen(&rt->net), gate);
 }
 
+static struct rt6*
+ipv6_findrt(struct dhcpcd_ctx *ctx, const struct rt6 *rt, int flags)
+{
+	struct rt6 *r;
+
+	TAILQ_FOREACH(r, &ctx->ipv6->kroutes, next) {
+		if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) &&
+		    (!flags || rt->iface == r->iface) &&
+#ifdef HAVE_ROUTE_METRIC
+		    (!flags || rt->metric == r->metric) &&
+#endif
+		    IN6_ARE_ADDR_EQUAL(&rt->net, &r->net))
+			return r;
+	}
+	return NULL;
+}
+
+void
+ipv6_freerts(struct rt6_head *routes)
+{
+	struct rt6 *rt;
+
+	while ((rt = TAILQ_FIRST(routes))) {
+		TAILQ_REMOVE(routes, rt, next);
+		free(rt);
+	}
+}
+
 /* If something other than dhcpcd removes a route,
  * we need to remove it from our internal table. */
 int
-ipv6_routedeleted(struct dhcpcd_ctx *ctx, const struct rt6 *rt)
+ipv6_handlert(struct dhcpcd_ctx *ctx, int cmd, struct rt6 *rt)
 {
 	struct rt6 *f;
 
-	if (ctx->ipv6 == NULL)
-		return 0;
-
-	f = find_route6(ctx->ipv6->routes, rt);
-	if (f == NULL)
-		return 0;
-	desc_route("removing", f);
-	TAILQ_REMOVE(ctx->ipv6->routes, f, next);
-	free(f);
-	return 1;
+	f = ipv6_findrt(ctx, rt, 1);
+	switch(cmd) {
+	case RTM_ADD:
+		if (f == NULL) {
+			if ((f = malloc(sizeof(*f))) == NULL)
+				return -1;
+			*f = *rt;
+			TAILQ_INSERT_TAIL(&ctx->ipv6->kroutes, f, next);
+		}
+		break;
+	case RTM_DELETE:
+		if (f) {
+			TAILQ_REMOVE(&ctx->ipv6->kroutes, f, next);
+			free(f);
+		}
+		/* If we manage the route, remove it */
+		if ((f = find_route6(ctx->ipv6->routes, rt))) {
+			desc_route("removing", f);
+			TAILQ_REMOVE(ctx->ipv6->routes, f, next);
+			free(f);
+		}
+		break;
+	}
+	return 0;
 }
 
-#define n_route(a)	 nc_route(1, a, a)
-#define c_route(a, b)	 nc_route(0, a, b)
+#define n_route(a)	 nc_route(NULL, a)
+#define c_route(a, b)	 nc_route(a, b)
 static int
-nc_route(int add, struct rt6 *ort, struct rt6 *nrt)
+nc_route(struct rt6 *ort, struct rt6 *nrt)
 {
+#ifdef HAVE_ROUTE_METRIC
+	int retval;
+#endif
 
 	/* Don't set default routes if not asked to */
 	if (IN6_IS_ADDR_UNSPECIFIED(&nrt->dest) &&
@@ -1757,15 +1801,37 @@
 	    !(nrt->iface->options->options & DHCPCD_GATEWAY))
 		return -1;
 
-	desc_route(add ? "adding" : "changing", nrt);
-	/* We delete and add the route so that we can change metric and
-	 * prefer the interface. */
-	if (if_delroute6(ort) == -1 && errno != ESRCH)
-		syslog(LOG_ERR, "%s: if_delroute6: %m", ort->iface->name);
-	if (if_addroute6(nrt) == 0)
+	desc_route(ort == NULL ? "adding" : "changing", nrt);
+
+	if (ort == NULL) {
+		ort = ipv6_findrt(nrt->iface->ctx, nrt, 0);
+		if (ort && ort->iface == nrt->iface &&
+#ifdef HAVE_ROUTE_METRIC
+		    ort->metric == nrt->metric &&
+#endif
+		    IN6_ARE_ADDR_EQUAL(&ort->gate, &nrt->gate))
+			return 0;
+	}
+
+#ifdef HAVE_ROUTE_METRIC
+	/* With route metrics, we can safely add the new route before
+	 * deleting the old route. */
+	if ((retval = if_route6(RTM_ADD, nrt, NULL)) == -1)
+		syslog(LOG_ERR, "if_route6 (ADD): %m");
+	if (ort && if_route6(RTM_DELETE, ort, NULL) == -1 &&
+	    errno != ESRCH)
+		syslog(LOG_ERR, "if_route6 (DEL): %m");
+	return retval;
+#else
+	/* No route metrics, we need to delete the old route before
+	 * adding the new one. */
+	if (ort && if_route6(RTM_DELETE, ort, NULL) == -1 && errno != ESRCH)
+		syslog(LOG_ERR, "if_route6: %m");
+	if (if_route6(RTM_ADD, nrt, NULL) == 0)
 		return 0;
-	syslog(LOG_ERR, "%s: if_addroute6: %m", nrt->iface->name);
+	syslog(LOG_ERR, "if_route6 (ADD): %m");
 	return -1;
+#endif
 }
 
 static int
@@ -1774,7 +1840,7 @@
 	int retval;
 
 	desc_route("deleting", rt);
-	retval = if_delroute6(rt);
+	retval = if_route6(RTM_DELETE, rt, NULL);
 	if (retval != 0 && errno != ENOENT && errno != ESRCH)
 		syslog(LOG_ERR,"%s: if_delroute6: %m", rt->iface->name);
 	return retval;
@@ -1791,7 +1857,9 @@
 		return NULL;
 	}
 	r->iface = ifp;
+#ifdef HAVE_ROUTE_METRIC
 	r->metric = ifp->metric;
+#endif
 	if (rap)
 		r->mtu = rap->mtu;
 	else
@@ -1848,45 +1916,6 @@
 	return r;
 }
 
-int
-ipv6_removesubnet(struct interface *ifp, struct ipv6_addr *addr)
-{
-	struct rt6 *rt;
-#if HAVE_ROUTE_METRIC
-	struct rt6 *ort;
-#endif
-	int r;
-
-	/* We need to delete the subnet route to have our metric or
-	 * prefer the interface. */
-	r = 0;
-	rt = make_prefix(ifp, NULL, addr);
-	if (rt) {
-		rt->iface = ifp;
-#ifdef __linux__
-		rt->metric = 256;
-#else
-		rt->metric = 0;
-#endif
-#if HAVE_ROUTE_METRIC
-		/* For some reason, Linux likes to re-add the subnet
-		   route under the original metric.
-		   I would love to find a way of stopping this! */
-		if ((ort = find_route6(ifp->ctx->ipv6->routes, rt)) == NULL ||
-		    ort->metric != rt->metric)
-#else
-		if (!find_route6(ifp->ctx->ipv6->routes, rt))
-#endif
-		{
-			r = if_delroute6(rt);
-			if (r == -1 && errno == ESRCH)
-				r = 0;
-		}
-		free(rt);
-	}
-	return r;
-}
-
 #define RT_IS_DEFAULT(rtp) \
 	(IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) &&		      \
 	    IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any))
@@ -1951,7 +1980,7 @@
 
 	/* First add reachable routers and their prefixes */
 	ipv6_build_ra_routes(ctx->ipv6, &dnr, 0);
-#if HAVE_ROUTE_METRIC
+#ifdef HAVE_ROUTE_METRIC
 	have_default = (TAILQ_FIRST(&dnr) != NULL);
 #endif
 
@@ -1961,7 +1990,7 @@
 	ipv6_build_dhcp_routes(ctx, &dnr, DH6S_BOUND);
 	ipv6_build_dhcp_routes(ctx, &dnr, DH6S_DELEGATED);
 
-#if HAVE_ROUTE_METRIC
+#ifdef HAVE_ROUTE_METRIC
 	/* If we have an unreachable router, we really do need to remove the
 	 * route to it beause it could be a lower metric than a reachable
 	 * router. Of course, we should at least have some routers if all
@@ -1980,6 +2009,7 @@
 	}
 	TAILQ_INIT(nrs);
 	have_default = 0;
+
 	TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) {
 		/* Is this route already in our table? */
 		if (find_route6(nrs, rt) != NULL)
@@ -1988,9 +2018,11 @@
 		/* Do we already manage it? */
 		if ((or = find_route6(ctx->ipv6->routes, rt))) {
 			if (or->iface != rt->iface ||
+#ifdef HAVE_ROUTE_METRIC
+			    rt->metric != or->metric ||
+#endif
 		//	    or->src.s_addr != ifp->addr.s_addr ||
-			    !IN6_ARE_ADDR_EQUAL(&rt->gate, &or->gate) ||
-			    rt->metric != or->metric)
+			    !IN6_ARE_ADDR_EQUAL(&rt->gate, &or->gate))
 			{
 				if (c_route(or, rt) != 0)
 					continue;
--- a/ipv6.h	Mon Feb 23 17:47:25 2015 +0000
+++ b/ipv6.h	Thu Feb 26 13:22:41 2015 +0000
@@ -146,7 +146,9 @@
 	struct in6_addr gate;
 	const struct interface *iface;
 	unsigned int flags;
+#ifdef HAVE_ROUTE_METRIC
 	unsigned int metric;
+#endif
 	unsigned int mtu;
 };
 TAILQ_HEAD(rt6_head, rt6);
@@ -218,6 +220,8 @@
 	struct ra_head *ra_routers;
 	struct rt6_head *routes;
 
+	struct rt6_head kroutes;
+
 	int dhcp_fd;
 };
 
@@ -266,8 +270,8 @@
 
 int ipv6_start(struct interface *);
 void ipv6_ctxfree(struct dhcpcd_ctx *);
-int ipv6_routedeleted(struct dhcpcd_ctx *, const struct rt6 *);
-int ipv6_removesubnet(struct interface *, struct ipv6_addr *);
+int ipv6_handlert(struct dhcpcd_ctx *, int cmd, struct rt6 *);
+void ipv6_freerts(struct rt6_head *);
 void ipv6_buildroutes(struct dhcpcd_ctx *);
 
 #else
--- a/ipv6nd.c	Mon Feb 23 17:47:25 2015 +0000
+++ b/ipv6nd.c	Thu Feb 26 13:22:41 2015 +0000
@@ -1118,6 +1118,12 @@
 #ifdef IPV6_MANAGETEMPADDR
 	ipv6_addtempaddrs(ifp, &rap->received);
 #endif
+
+	/* Find any freshly added routes, such as the subnet route.
+	 * We do this because we cannot rely on recieving the kernel
+	 * notification right now via our link socket. */
+	if_initrt6(ifp);
+
 	ipv6_buildroutes(ifp->ctx);
 	if (ipv6nd_scriptrun(rap))
 		return;