Mercurial > hg > dhcpcd
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;
