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.
This commit is contained in:
Roy Marples 2015-02-26 13:22:41 +00:00
parent a3fb51a3dd
commit a19fe49202
11 changed files with 944 additions and 413 deletions

2
defs.h
View File

@ -28,7 +28,7 @@
#define CONFIG_H #define CONFIG_H
#define PACKAGE "dhcpcd" #define PACKAGE "dhcpcd"
#define VERSION "6.7.1" #define VERSION "6.7.99.3"
#ifndef CONFIG #ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf" # define CONFIG SYSCONFDIR "/" PACKAGE ".conf"

3
dhcp.c
View File

@ -2990,6 +2990,9 @@ dhcp_init(struct interface *ifp)
/* 0 is a valid fd, so init to -1 */ /* 0 is a valid fd, so init to -1 */
state->raw_fd = state->arp_fd = -1; state->raw_fd = state->arp_fd = -1;
TAILQ_INIT(&state->arp_states); TAILQ_INIT(&state->arp_states);
/* Now is a good time to find IPv4 routes */
if_initrt(ifp);
} }
state->state = DHS_INIT; state->state = DHS_INIT;

View File

@ -122,6 +122,7 @@ struct dhcpcd_ctx {
struct dhcp_opt *dhcp_opts; struct dhcp_opt *dhcp_opts;
size_t dhcp_opts_len; size_t dhcp_opts_len;
struct rt_head *ipv4_routes; struct rt_head *ipv4_routes;
struct rt_head *ipv4_kroutes;
int udp_fd; int udp_fd;
uint8_t *packet; uint8_t *packet;

453
if-bsd.c
View File

@ -93,13 +93,15 @@
#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len)) #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
#endif #endif
#define COPYOUT(sin, sa) \ #define COPYOUT(sin, sa) do { \
if ((sa) && (sa)->sa_family == AF_INET) \ if ((sa) && ((sa)->sa_family == AF_INET || (sa)->sa_family == 255)) \
(sin) = ((struct sockaddr_in*)(void *)(sa))->sin_addr (sin) = ((struct sockaddr_in*)(void *)(sa))->sin_addr; \
} while (0)
#define COPYOUT6(sin, sa) \ #define COPYOUT6(sin, sa) do { \
if ((sa) && (sa)->sa_family == AF_INET6) \ if ((sa) && ((sa)->sa_family == AF_INET6 || (sa)->sa_family == 255)) \
(sin) = ((struct sockaddr_in6*)(void *)(sa))->sin6_addr (sin) = ((struct sockaddr_in6*)(void *)(sa))->sin6_addr; \
} while (0)
#ifndef CLLADDR #ifndef CLLADDR
# define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen)) # define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
@ -259,6 +261,33 @@ if_vimaster(const char *ifname)
return 0; 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 #ifdef INET
const char *if_pfname = "Berkley Packet Filter"; const char *if_pfname = "Berkley Packet Filter";
@ -454,8 +483,67 @@ if_address(const struct interface *ifp, const struct in_addr *address,
return r; 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 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; const struct dhcp_state *state;
union sockunion { union sockunion {
@ -467,7 +555,7 @@ if_route(const struct rt *rt, int action)
struct rtm struct rtm
{ {
struct rt_msghdr hdr; struct rt_msghdr hdr;
char buffer[sizeof(su) * 5]; char buffer[sizeof(su) * RTAX_MAX];
} rtm; } rtm;
char *bp = rtm.buffer; char *bp = rtm.buffer;
size_t l; size_t l;
@ -489,45 +577,53 @@ if_route(const struct rt *rt, int action)
ADDSU; \ 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)); memset(&rtm, 0, sizeof(rtm));
rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_version = RTM_VERSION;
rtm.hdr.rtm_seq = 1; rtm.hdr.rtm_seq = 1;
rtm.hdr.rtm_type = cmd;
rtm.hdr.rtm_addrs = RTA_DST; rtm.hdr.rtm_addrs = RTA_DST;
if (action == 0) if (cmd == RTM_ADD || cmd == RTM_CHANGE)
rtm.hdr.rtm_type = RTM_CHANGE;
else if (action > 0) {
rtm.hdr.rtm_type = RTM_ADD;
rtm.hdr.rtm_addrs |= RTA_GATEWAY; rtm.hdr.rtm_addrs |= RTA_GATEWAY;
} else
rtm.hdr.rtm_type = RTM_DELETE;
rtm.hdr.rtm_flags = RTF_UP; rtm.hdr.rtm_flags = RTF_UP;
#ifdef RTF_PINNED #ifdef RTF_PINNED
if (rtm.hdr.rtm_type != RTM_ADD) if (cmd != RTM_ADD)
rtm.hdr.rtm_flags |= RTF_PINNED; rtm.hdr.rtm_flags |= RTF_PINNED;
#endif #endif
#ifdef SIOCGIFPRIORITY #ifdef SIOCGIFPRIORITY
rtm.hdr.rtm_priority = rt->metric; rtm.hdr.rtm_priority = rt->metric;
#endif #endif
/* None interface subnet routes are static. */ if (cmd != RTM_DELETE) {
if (rt->gate.s_addr != INADDR_ANY || rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP;
rt->net.s_addr != state->net.s_addr || /* None interface subnet routes are static. */
rt->dest.s_addr != (state->addr.s_addr & state->net.s_addr)) if (rt->gate.s_addr != INADDR_ANY ||
rtm.hdr.rtm_flags |= RTF_STATIC; 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 && if (rt->dest.s_addr == rt->gate.s_addr &&
rt->net.s_addr == INADDR_BROADCAST) rt->net.s_addr == INADDR_BROADCAST)
rtm.hdr.rtm_flags |= RTF_HOST; rtm.hdr.rtm_flags |= RTF_HOST;
else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) && else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) &&
rt->net.s_addr == INADDR_BROADCAST) rt->net.s_addr == INADDR_BROADCAST)
{
rtm.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY; 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; rtm.hdr.rtm_addrs |= RTA_NETMASK;
if (rtm.hdr.rtm_flags & RTF_STATIC) if (rtm.hdr.rtm_flags & RTF_STATIC)
rtm.hdr.rtm_flags |= RTF_GATEWAY; 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); ADDADDR(&rt->dest);
if (rtm.hdr.rtm_addrs & RTA_GATEWAY) { if (rtm.hdr.rtm_addrs & RTA_GATEWAY) {
@ -544,23 +640,74 @@ if_route(const struct rt *rt, int action)
if (rtm.hdr.rtm_addrs & RTA_NETMASK) if (rtm.hdr.rtm_addrs & RTA_NETMASK)
ADDADDR(&rt->net); ADDADDR(&rt->net);
if (rtm.hdr.rtm_addrs & RTA_IFP) { if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
if_linkaddr(&su.sdl, rt->iface); (rtm.hdr.rtm_addrs & (RTA_IFP | RTA_IFA)))
ADDSU; {
} 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 (rtm.hdr.rtm_addrs & RTA_IFA)
ADDADDR(&state->addr); ADDADDR(&state->addr);
}
#undef ADDADDR #undef ADDADDR
#undef ADDSU #undef ADDSU
rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm); 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); close(s);
return retval; 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 #endif
#ifdef INET6 #ifdef INET6
@ -642,8 +789,110 @@ if_address6(const struct ipv6_addr *a, int action)
return r; 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 int
if_route6(const struct rt6 *rt, int action) if_route6(unsigned char cmd, const struct rt6 *rt, struct rt6 *srt)
{ {
union sockunion { union sockunion {
struct sockaddr sa; struct sockaddr sa;
@ -654,12 +903,11 @@ if_route6(const struct rt6 *rt, int action)
struct rtm struct rtm
{ {
struct rt_msghdr hdr; struct rt_msghdr hdr;
char buffer[sizeof(su) * 5]; char buffer[sizeof(su) * RTAX_MAX];
} rtm; } rtm;
char *bp = rtm.buffer; char *bp = rtm.buffer;
size_t l; size_t l;
int s, retval; int s, retval;
const struct ipv6_addr *lla;
if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
return -1; return -1;
@ -683,12 +931,7 @@ if_route6(const struct rt6 *rt, int action)
memset(&rtm, 0, sizeof(rtm)); memset(&rtm, 0, sizeof(rtm));
rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_version = RTM_VERSION;
rtm.hdr.rtm_seq = 1; rtm.hdr.rtm_seq = 1;
if (action == 0) rtm.hdr.rtm_type = cmd;
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_flags = RTF_UP | (int)rt->flags; rtm.hdr.rtm_flags = RTF_UP | (int)rt->flags;
#ifdef RTF_PINNED #ifdef RTF_PINNED
if (rtm.hdr.rtm_type != RTM_ADD) if (rtm.hdr.rtm_type != RTM_ADD)
@ -706,14 +949,13 @@ if_route6(const struct rt6 *rt, int action)
} else } else
rtm.hdr.rtm_flags |= RTF_GATEWAY | RTF_STATIC; rtm.hdr.rtm_flags |= RTF_GATEWAY | RTF_STATIC;
if (action >= 0) { if (cmd == RTM_ADD)
rtm.hdr.rtm_addrs |= RTA_GATEWAY; rtm.hdr.rtm_addrs |= RTA_GATEWAY;
if (!(rtm.hdr.rtm_flags & RTF_REJECT)) if (cmd == RTM_GET ||
rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA; (cmd == RTM_ADD && !(rtm.hdr.rtm_flags & RTF_REJECT)))
} rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA;
ADDADDR(&rt->dest); ADDADDR(&rt->dest);
lla = NULL;
if (rtm.hdr.rtm_addrs & RTA_GATEWAY) { if (rtm.hdr.rtm_addrs & RTA_GATEWAY) {
if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) { if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
if_linkaddr(&su.sdl, rt->iface); if_linkaddr(&su.sdl, rt->iface);
@ -726,51 +968,86 @@ if_route6(const struct rt6 *rt, int action)
if (rtm.hdr.rtm_addrs & RTA_NETMASK) if (rtm.hdr.rtm_addrs & RTA_NETMASK)
ADDADDR(&rt->net); ADDADDR(&rt->net);
if (rtm.hdr.rtm_addrs & RTA_IFP) { if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
if_linkaddr(&su.sdl, rt->iface); (rtm.hdr.rtm_addrs & (RTA_IFP | RTA_IFA)))
ADDSU; {
} 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) {
const struct ipv6_addr *lla;
if (rtm.hdr.rtm_addrs & RTA_IFA) {
if (lla == NULL) {
lla = ipv6_linklocal(rt->iface); lla = ipv6_linklocal(rt->iface);
if (lla == NULL) /* unlikely */ if (lla == NULL) /* unlikely */
return -1; return -1;
ADDADDRS(&lla->addr, rt->iface->index);
}
if (rt->mtu) {
rtm.hdr.rtm_inits |= RTV_MTU;
rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu;
} }
ADDADDRS(&lla->addr, rt->iface->index);
} }
#undef ADDADDR #undef ADDADDR
#undef ADDSU #undef ADDSU
if (action >= 0 && rt->mtu) { rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
rtm.hdr.rtm_inits |= RTV_MTU; retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu;
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); close(s);
return retval; return retval;
} }
#endif
static void int
get_addrs(int type, char *cp, struct sockaddr **sa) if_initrt6(struct interface *ifp)
{ {
int i; struct rt_msghdr *rtm;
int mib[6];
size_t needed;
char *buf, *p, *end;
struct rt6 rt;
for (i = 0; i < RTAX_MAX; i++) { ipv6_freerts(&ifp->ctx->ipv6->kroutes);
if (type & (1 << i)) {
sa[i] = (struct sockaddr *)cp; mib[0] = CTL_NET;
RT_ADVANCE(cp, sa[i]); mib[1] = PF_ROUTE;
} else mib[2] = 0;
sa[i] = NULL; 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 int
if_addrflags6(const struct in6_addr *addr, const struct interface *ifp) if_addrflags6(const struct in6_addr *addr, const struct interface *ifp)
{ {
@ -862,11 +1139,8 @@ if_managelink(struct dhcpcd_ctx *ctx)
int ifa_flags; int ifa_flags;
#endif #endif
bytes = read(ctx->link_fd, msg, sizeof(msg)); if ((bytes = read(ctx->link_fd, msg, sizeof(msg))) == -1)
if (bytes == -1)
return -1; return -1;
if (bytes == 0)
return 0;
e = msg + bytes; e = msg + bytes;
for (p = msg; p < e; p += rtm->rtm_msglen) { for (p = msg; p < e; p += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)(void *)p; rtm = (struct rt_msghdr *)(void *)p;
@ -919,27 +1193,16 @@ if_managelink(struct dhcpcd_ctx *ctx)
case RTM_DELETE: case RTM_DELETE:
cp = (char *)(void *)(rtm + 1); cp = (char *)(void *)(rtm + 1);
sa = (struct sockaddr *)(void *)cp; sa = (struct sockaddr *)(void *)cp;
get_addrs(rtm->rtm_addrs, cp, rti_info);
switch (sa->sa_family) { switch (sa->sa_family) {
#ifdef INET #ifdef INET
case AF_INET: case AF_INET:
if (rtm->rtm_type != RTM_DELETE) if (if_copyrt(ctx, &rt, rtm) == 0)
break; ipv4_handlert(ctx, rtm->rtm_type, &rt);
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);
break; break;
#endif #endif
#ifdef INET6 #ifdef INET6
case AF_INET6: case AF_INET6:
if (~rtm->rtm_addrs & if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
(RTA_DST | RTA_GATEWAY))
break; break;
/* /*
* BSD caches host routes in the * BSD caches host routes in the
@ -949,6 +1212,7 @@ if_managelink(struct dhcpcd_ctx *ctx)
* with a hardware address * with a hardware address
*/ */
if (rtm->rtm_flags & (RTF_HOST)) { if (rtm->rtm_flags & (RTF_HOST)) {
get_addrs(rtm->rtm_addrs, cp, rti_info);
COPYOUT6(ia6, rti_info[RTAX_DST]); COPYOUT6(ia6, rti_info[RTAX_DST]);
DESCOPE(&ia6); DESCOPE(&ia6);
if (rti_info[RTAX_GATEWAY]->sa_family if (rti_info[RTAX_GATEWAY]->sa_family
@ -965,16 +1229,8 @@ if_managelink(struct dhcpcd_ctx *ctx)
break; break;
} }
if (rtm->rtm_type != RTM_DELETE) if (if_copyrt6(ctx, &rt6, rtm) == 0)
break; ipv6_handlert(ctx, rtm->rtm_type, &rt6);
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);
break; break;
#endif #endif
} }
@ -1042,7 +1298,6 @@ if_managelink(struct dhcpcd_ctx *ctx)
break; break;
} }
} }
return 0; return 0;
} }

View File

@ -320,7 +320,8 @@ err_netlink(struct nlmsghdr *nlm)
static int static int
get_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, int fd, int flags, 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; char *buf = NULL, *nbuf;
ssize_t bytes; ssize_t bytes;
@ -363,8 +364,10 @@ get_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, int fd, int flags,
goto eexit; goto eexit;
} }
/* Ignore message if it is not from kernel */ /* Ignore message if it is not from kernel */
if (nladdr.nl_pid != 0) if (nladdr.nl_pid != 0) {
r = 0;
continue; continue;
}
for (nlm = (struct nlmsghdr *)(void *)buf; for (nlm = (struct nlmsghdr *)(void *)buf;
nlm && NLMSG_OK(nlm, (size_t)bytes); nlm && NLMSG_OK(nlm, (size_t)bytes);
@ -376,7 +379,7 @@ get_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, int fd, int flags,
if (r) if (r)
continue; continue;
if (callback) { if (callback) {
r = callback(ctx, ifp, nlm); r = callback(ctx, ifp, nlm, data);
if (r != 0) if (r != 0)
goto eexit; goto eexit;
} }
@ -388,6 +391,113 @@ eexit:
return r; 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 */ /* Work out the maximum pid size */
static inline long long static inline long long
get_max_pid_t() get_max_pid_t()
@ -405,29 +515,31 @@ link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
struct nlmsghdr *nlm) struct nlmsghdr *nlm)
{ {
size_t len; size_t len;
unsigned int metric;
struct rtattr *rta;
struct rtmsg *rtm; struct rtmsg *rtm;
int cmd;
#ifdef INET #ifdef INET
struct rt rt; struct rt rt;
#endif #endif
#ifdef INET6 #ifdef INET6
struct rt6 rt6; struct rt6 rt6;
#endif #endif
switch (nlm->nlmsg_type) {
if (nlm->nlmsg_type != RTM_DELROUTE) case RTM_NEWROUTE:
cmd = RTM_ADD;
break;
case RTM_DELROUTE:
cmd = RTM_DELETE;
break;
default:
return 0; return 0;
}
len = nlm->nlmsg_len - sizeof(*nlm); len = nlm->nlmsg_len - sizeof(*nlm);
if (len < sizeof(*rtm)) { if (len < sizeof(*rtm)) {
errno = EBADMSG; errno = EBADMSG;
return -1; 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. /* Ignore messages generated by us.
* For some reason we get 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 * with a very large value in nlmsg_pid that seems to be
@ -435,83 +547,23 @@ link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
if (nlm->nlmsg_pid > get_max_pid_t()) if (nlm->nlmsg_pid > get_max_pid_t())
return 1; return 1;
rta = (struct rtattr *)(void *)((char *)rtm +NLMSG_ALIGN(sizeof(*rtm))); rtm = NLMSG_DATA(nlm);
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) {
#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;
#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;
#endif
}
switch (rta->rta_type) {
case RTA_PRIORITY:
metric = *(unsigned int *)RTA_DATA(rta);
break;
}
rta = RTA_NEXT(rta, len);
}
switch (rtm->rtm_family) { switch (rtm->rtm_family) {
#ifdef INET #ifdef INET
case AF_INET: case AF_INET:
if (rt.iface != NULL && metric == rt.iface->metric) { if (if_copyrt(ctx, &rt, nlm) == 0)
inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net); ipv4_handlert(ctx, cmd, &rt);
ipv4_routedeleted(ctx, &rt); break;
}
break;
#endif #endif
#ifdef INET6 #ifdef INET6
case AF_INET6: case AF_INET6:
if (rt6.iface != NULL && metric == rt6.iface->metric) { if (if_copyrt6(ctx, &rt6, nlm) == 0)
ipv6_mask(&rt6.net, rtm->rtm_dst_len); ipv6_handlert(ctx, cmd, &rt6);
ipv6_routedeleted(ctx, &rt6); break;
}
break;
#endif #endif
} }
return 1;
return 0;
} }
static int static int
@ -541,7 +593,7 @@ link_addr(struct dhcpcd_ctx *ctx, struct interface *ifp, struct nlmsghdr *nlm)
* so it's not really an error */ * so it's not really an error */
return 1; return 1;
} }
rta = (struct rtattr *) IFA_RTA(ifa); rta = (struct rtattr *)IFA_RTA(ifa);
len = NLMSG_PAYLOAD(nlm, sizeof(*ifa)); len = NLMSG_PAYLOAD(nlm, sizeof(*ifa));
switch (ifa->ifa_family) { switch (ifa->ifa_family) {
#ifdef INET #ifdef INET
@ -585,7 +637,7 @@ link_addr(struct dhcpcd_ctx *ctx, struct interface *ifp, struct nlmsghdr *nlm)
break; break;
#endif #endif
} }
return 1; return 0;
} }
static uint8_t static uint8_t
@ -665,13 +717,13 @@ link_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
ipv6nd_neighbour(ctx, &addr6, flags); ipv6nd_neighbour(ctx, &addr6, flags);
} }
return 1; return 0;
} }
#endif #endif
static int static int
link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
struct nlmsghdr *nlm) struct nlmsghdr *nlm, __unused void *data)
{ {
int r; int r;
size_t len; size_t len;
@ -700,7 +752,7 @@ link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
} }
ifi = NLMSG_DATA(nlm); ifi = NLMSG_DATA(nlm);
if (ifi->ifi_flags & IFF_LOOPBACK) if (ifi->ifi_flags & IFF_LOOPBACK)
return 1; return 0;
rta = (struct rtattr *)(void *)((char *)ifi +NLMSG_ALIGN(sizeof(*ifi))); rta = (struct rtattr *)(void *)((char *)ifi +NLMSG_ALIGN(sizeof(*ifi)));
len = NLMSG_PAYLOAD(nlm, sizeof(*ifi)); len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
*ifn = '\0'; *ifn = '\0';
@ -712,7 +764,7 @@ link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
/* Ignore wireless messages */ /* Ignore wireless messages */
if (nlm->nlmsg_type == RTM_NEWLINK && if (nlm->nlmsg_type == RTM_NEWLINK &&
ifi->ifi_change == 0) ifi->ifi_change == 0)
return 1; return 0;
break; break;
case IFLA_IFNAME: case IFLA_IFNAME:
strlcpy(ifn, RTA_DATA(rta), sizeof(ifn)); strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
@ -726,7 +778,7 @@ link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
if (nlm->nlmsg_type == RTM_DELLINK) { if (nlm->nlmsg_type == RTM_DELLINK) {
dhcpcd_handleinterface(ctx, -1, ifn); dhcpcd_handleinterface(ctx, -1, ifn);
return 1; return 0;
} }
/* Virtual interfaces may not get a valid hardware address /* Virtual interfaces may not get a valid hardware address
@ -735,12 +787,12 @@ link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
* that that don't exist until they have one. */ * that that don't exist until they have one. */
if (ifi->ifi_flags & IFF_MASTER && !hwaddr) { if (ifi->ifi_flags & IFF_MASTER && !hwaddr) {
dhcpcd_handleinterface(ctx, -1, ifn); dhcpcd_handleinterface(ctx, -1, ifn);
return 1; return 0;
} }
/* Check for interface name change */ /* Check for interface name change */
if (handle_rename(ctx, (unsigned int)ifi->ifi_index, ifn)) if (handle_rename(ctx, (unsigned int)ifi->ifi_index, ifn))
return 1; return 0;
/* Check for a new interface */ /* Check for a new interface */
ifp = if_find(ctx, ifn); ifp = if_find(ctx, ifn);
@ -749,7 +801,7 @@ link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
* the interface rather than the kernel. */ * the interface rather than the kernel. */
if (dev_listening(ctx) < 1) if (dev_listening(ctx) < 1)
dhcpcd_handleinterface(ctx, 1, ifn); dhcpcd_handleinterface(ctx, 1, ifn);
return 1; return 0;
} }
/* Re-read hardware address and friends */ /* Re-read hardware address and friends */
@ -764,7 +816,7 @@ link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
dhcpcd_handlecarrier(ctx, dhcpcd_handlecarrier(ctx,
ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN, ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN,
ifi->ifi_flags, ifn); ifi->ifi_flags, ifn);
return 1; return 0;
} }
int int
@ -772,13 +824,14 @@ if_managelink(struct dhcpcd_ctx *ctx)
{ {
return get_netlink(ctx, NULL, return get_netlink(ctx, NULL,
ctx->link_fd, MSG_DONTWAIT, &link_netlink); ctx->link_fd, MSG_DONTWAIT, &link_netlink, NULL);
} }
static int static int
send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
int protocol, struct nlmsghdr *hdr, 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; int s, r;
struct sockaddr_nl snl; struct sockaddr_nl snl;
@ -802,7 +855,7 @@ send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
hdr->nlmsg_seq = ++seq; hdr->nlmsg_seq = ++seq;
if (sendmsg(s, &msg, 0) != -1) if (sendmsg(s, &msg, 0) != -1)
r = get_netlink(ctx, ifp, s, 0, callback); r = get_netlink(ctx, ifp, s, 0, callback, data);
else else
r = -1; r = -1;
close(s); close(s);
@ -861,7 +914,7 @@ nla_next(struct nlattr *nla, size_t *rem)
{ {
*rem -= NLA_ALIGN(nla->nla_len); *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) #define NLA_TYPE(nla) ((nla)->nla_type & NLA_TYPE_MASK)
@ -935,7 +988,7 @@ gnl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype)
memset(tb, 0, sizeof(*tb) * ((unsigned int)maxtype + 1)); memset(tb, 0, sizeof(*tb) * ((unsigned int)maxtype + 1));
ghdr = NLMSG_DATA(nlm); 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; len = nlm->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
NLA_FOR_EACH_ATTR(nla, head, len, rem) { NLA_FOR_EACH_ATTR(nla, head, len, rem) {
type = NLA_TYPE(nla); type = NLA_TYPE(nla);
@ -948,7 +1001,7 @@ gnl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype)
static int static int
_gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp, _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]; struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
uint16_t family; uint16_t family;
@ -959,7 +1012,7 @@ _gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
errno = ENOENT; errno = ENOENT;
return -1; 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; return (int)family;
} }
@ -978,12 +1031,12 @@ gnl_getfamily(struct dhcpcd_ctx *ctx, const char *name)
CTRL_ATTR_FAMILY_NAME, name) == -1) CTRL_ATTR_FAMILY_NAME, name) == -1)
return -1; return -1;
return send_netlink(ctx, NULL, NETLINK_GENERIC, &nlm.hdr, return send_netlink(ctx, NULL, NETLINK_GENERIC, &nlm.hdr,
&_gnl_getfamily); &_gnl_getfamily, NULL);
} }
static int static int
_if_getssid(__unused struct dhcpcd_ctx *ctx, struct interface *ifp, _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]; struct nlattr *tb[NL80211_ATTR_SSID + 1];
@ -1028,7 +1081,7 @@ if_getssid_nl80211(struct interface *ifp)
nla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index); nla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index);
return send_netlink(ifp->ctx, ifp, return send_netlink(ifp->ctx, ifp,
NETLINK_GENERIC, &nlm.hdr, &_if_getssid); NETLINK_GENERIC, &nlm.hdr, &_if_getssid, NULL);
} }
#endif #endif
@ -1246,13 +1299,22 @@ if_address(const struct interface *iface,
add_attr_l(&nlm.hdr, sizeof(nlm), IFA_BROADCAST, add_attr_l(&nlm.hdr, sizeof(nlm), IFA_BROADCAST,
&broadcast->s_addr, sizeof(broadcast->s_addr)); &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; retval = -1;
return retval; 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 int
if_route(const struct rt *rt, int action) if_route(unsigned char cmd, const struct rt *rt, struct rt *srt)
{ {
struct nlmr nlm; struct nlmr nlm;
int retval = 0; int retval = 0;
@ -1260,22 +1322,30 @@ if_route(const struct rt *rt, int action)
memset(&nlm, 0, sizeof(nlm)); memset(&nlm, 0, sizeof(nlm));
nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nlm.hdr.nlmsg_type = RTM_NEWROUTE; switch (cmd) {
if (action == 0) case RTM_GET:
nlm.hdr.nlmsg_flags = NLM_F_REPLACE; nlm.hdr.nlmsg_type = RTM_GETROUTE;
else if (action == 1) 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; nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
else break;
case RTM_DELETE:
nlm.hdr.nlmsg_type = RTM_DELROUTE; nlm.hdr.nlmsg_type = RTM_DELROUTE;
break;
}
nlm.hdr.nlmsg_flags |= NLM_F_REQUEST; nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
nlm.rt.rtm_family = AF_INET; nlm.rt.rtm_family = AF_INET;
nlm.rt.rtm_table = RT_TABLE_MAIN; nlm.rt.rtm_table = RT_TABLE_MAIN;
state = D_STATE(rt->iface); state = D_STATE(rt->iface);
if (action == -1 || action == -2) if (cmd == RTM_DELETE)
nlm.rt.rtm_scope = RT_SCOPE_NOWHERE; nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
else { else {
nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
/* We only change route metrics for kernel routes */ /* We only change route metrics for kernel routes */
if (rt->dest.s_addr == if (rt->dest.s_addr ==
(state->addr.s_addr & state->net.s_addr) && (state->addr.s_addr & state->net.s_addr) &&
@ -1302,20 +1372,53 @@ if_route(const struct rt *rt, int action)
&state->addr.s_addr, sizeof(state->addr.s_addr)); &state->addr.s_addr, sizeof(state->addr.s_addr));
} }
/* If destination == gateway then don't add the gateway */ /* If destination == gateway then don't add the gateway */
if (rt->dest.s_addr != rt->gate.s_addr || if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
rt->net.s_addr != INADDR_BROADCAST) (rt->dest.s_addr != rt->gate.s_addr ||
rt->net.s_addr != INADDR_BROADCAST))
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY, add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
&rt->gate.s_addr, sizeof(rt->gate.s_addr)); &rt->gate.s_addr, sizeof(rt->gate.s_addr));
if (rt->gate.s_addr != htonl(INADDR_LOOPBACK)) 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_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, if (send_netlink(rt->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL) == -1) NETLINK_ROUTE, &nlm.hdr, &_if_copyrt, srt) == -1)
retval = -1; retval = -1;
return retval; 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 #endif
#ifdef INET6 #ifdef INET6
@ -1327,7 +1430,7 @@ if_address6(const struct ipv6_addr *ap, int action)
int retval = 0; int retval = 0;
/* IFA_FLAGS is not a define, but is was added at the same time /* IFA_FLAGS is not a define, but is was added at the same time
* IFA_F_NOPREFIXROUTE was do use that. */ * IFA_F_NOPREFIXROUTE was do use that. */
#ifdef IFA_F_NOPREFIXROUTE #if defined(IFA_F_NOPREFIXROUTE) || defined(IFA_F_MANAGETEMPADDR)
uint32_t flags = 0; uint32_t flags = 0;
#endif #endif
@ -1374,13 +1477,13 @@ if_address6(const struct ipv6_addr *ap, int action)
if (!IN6_IS_ADDR_LINKLOCAL(&ap->addr)) if (!IN6_IS_ADDR_LINKLOCAL(&ap->addr))
flags |= IFA_F_NOPREFIXROUTE; flags |= IFA_F_NOPREFIXROUTE;
#endif #endif
#ifdef IFA_F_NOPREFIXROUTE #if defined(IFA_F_NOPREFIXROUTE) || defined(IFA_F_MANAGETEMPADDR)
if (flags) if (flags)
add_attr_32(&nlm.hdr, sizeof(nlm), IFA_FLAGS, flags); add_attr_32(&nlm.hdr, sizeof(nlm), IFA_FLAGS, flags);
#endif #endif
if (send_netlink(ap->iface->ctx, NULL, if (send_netlink(ap->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL) == -1) NETLINK_ROUTE, &nlm.hdr, NULL, NULL) == -1)
retval = -1; retval = -1;
return retval; return retval;
} }
@ -1397,7 +1500,8 @@ rta_add_attr_32(struct rtattr *rta, unsigned short maxlen,
return -1; 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_type = type;
subrta->rta_len = len; subrta->rta_len = len;
memcpy(RTA_DATA(subrta), &data, sizeof(data)); memcpy(RTA_DATA(subrta), &data, sizeof(data));
@ -1405,31 +1509,45 @@ rta_add_attr_32(struct rtattr *rta, unsigned short maxlen,
return 0; 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 int
if_route6(const struct rt6 *rt, int action) if_route6(unsigned char cmd, const struct rt6 *rt, struct rt6 *srt)
{ {
struct nlmr nlm; struct nlmr nlm;
char metricsbuf[32];
struct rtattr *metrics = (void *)metricsbuf;
int retval = 0; int retval = 0;
memset(&nlm, 0, sizeof(nlm)); memset(&nlm, 0, sizeof(nlm));
nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nlm.hdr.nlmsg_type = RTM_NEWROUTE; switch (cmd) {
nlm.hdr.nlmsg_flags = NLM_F_REQUEST; case RTM_GET:
if (action == 0) nlm.hdr.nlmsg_type = RTM_GETROUTE;
nlm.hdr.nlmsg_flags |= NLM_F_REPLACE; break;
else if (action == 1) case RTM_CHANGE:
nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; nlm.hdr.nlmsg_type = RTM_NEWROUTE;
else 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; nlm.hdr.nlmsg_type = RTM_DELROUTE;
break;
}
nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
nlm.rt.rtm_family = AF_INET6; nlm.rt.rtm_family = AF_INET6;
nlm.rt.rtm_table = RT_TABLE_MAIN; nlm.rt.rtm_table = RT_TABLE_MAIN;
if (action == -1 || action == -2) if (cmd == RTM_DELETE)
nlm.rt.rtm_scope = RT_SCOPE_NOWHERE; nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
else { else {
nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
/* None interface subnet routes are static. */ /* None interface subnet routes are static. */
if (rt->iface->flags & IFF_LOOPBACK) if (rt->iface->flags & IFF_LOOPBACK)
nlm.rt.rtm_scope = RT_SCOPE_HOST; nlm.rt.rtm_scope = RT_SCOPE_HOST;
@ -1448,29 +1566,65 @@ if_route6(const struct rt6 *rt, int action)
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_DST, add_attr_l(&nlm.hdr, sizeof(nlm), RTA_DST,
&rt->dest.s6_addr, sizeof(rt->dest.s6_addr)); &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, add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
&rt->gate.s6_addr, sizeof(rt->gate.s6_addr)); &rt->gate.s6_addr, sizeof(rt->gate.s6_addr));
if (!(rt->flags & RTF_REJECT)) { 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_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_type = RTA_METRICS;
metrics->rta_len = RTA_LENGTH(0); metrics->rta_len = RTA_LENGTH(0);
rta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU, rt->mtu); rta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU, rt->mtu);
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS, 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, if (send_netlink(rt->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL) == -1) NETLINK_ROUTE, &nlm.hdr, &_if_copyrt6, srt) == -1)
retval = -1; retval = -1;
return retval; 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 int
if_addrflags6(const struct in6_addr *addr, const struct interface *ifp) if_addrflags6(const struct in6_addr *addr, const struct interface *ifp)
{ {
@ -1570,7 +1724,7 @@ if_disable_autolinklocal(struct dhcpcd_ctx *ctx, int ifindex)
add_attr_nest_end(&nlm.hdr, afs6); add_attr_nest_end(&nlm.hdr, afs6);
add_attr_nest_end(&nlm.hdr, afs); 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"; static const char *prefix = "/proc/sys/net/ipv6/conf";

33
if.h
View File

@ -31,23 +31,21 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <net/if.h> #include <net/if.h>
//#include <net/route.h>
#include <netinet/in.h> #include <netinet/in.h>
#include "config.h"
#include "dhcpcd.h"
#include "ipv4.h"
#include "ipv6.h"
/* Some systems have route metrics */ /* Some systems have route metrics */
#ifndef HAVE_ROUTE_METRIC #ifndef HAVE_ROUTE_METRIC
# if defined(__linux__) || defined(SIOCGIFPRIORITY) # if defined(__linux__) || defined(SIOCGIFPRIORITY)
# define HAVE_ROUTE_METRIC 1 # define HAVE_ROUTE_METRIC 1
# endif # endif
# ifndef HAVE_ROUTE_METRIC
# define HAVE_ROUTE_METRIC 0
# endif
#endif #endif
#include "config.h"
#include "dhcpcd.h"
#include "ipv4.h"
#include "ipv6.h"
#define EUI64_ADDR_LEN 8 #define EUI64_ADDR_LEN 8
#define INFINIBAND_ADDR_LEN 20 #define INFINIBAND_ADDR_LEN 20
@ -102,6 +100,13 @@ int if_vimaster(const char *);
int if_openlinksocket(void); int if_openlinksocket(void);
int if_managelink(struct dhcpcd_ctx *); 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 #ifdef INET
extern const char *if_pfname; extern const char *if_pfname;
int if_openrawsocket(struct interface *, int); int if_openrawsocket(struct interface *, int);
@ -117,10 +122,8 @@ int if_address(const struct interface *,
#define if_deladdress(ifp, addr, net) \ #define if_deladdress(ifp, addr, net) \
if_address(ifp, addr, net, NULL, -1) if_address(ifp, addr, net, NULL, -1)
int if_route(const struct rt *rt, int); int if_route(unsigned char, const struct rt *rt, struct rt *);
#define if_addroute(rt) if_route(rt, 1) int if_initrt(struct interface *);
#define if_chgroute(rt) if_route(rt, 0)
#define if_delroute(rt) if_route(rt, -1)
#endif #endif
#ifdef INET6 #ifdef INET6
@ -140,10 +143,8 @@ int if_address6(const struct ipv6_addr *, int);
int if_addrflags6(const struct in6_addr *, const struct interface *); int if_addrflags6(const struct in6_addr *, const struct interface *);
int if_getlifetime6(struct ipv6_addr *); int if_getlifetime6(struct ipv6_addr *);
int if_route6(const struct rt6 *rt, int); int if_route6(unsigned char, const struct rt6 *rt, struct rt6 *);
#define if_addroute6(rt) if_route6(rt, 1) int if_initrt6(struct interface *);
#define if_chgroute6(rt) if_route6(rt, 0)
#define if_delroute6(rt) if_route6(rt, -1)
#else #else
#define if_checkipv6(a, b, c) (-1) #define if_checkipv6(a, b, c) (-1)
#endif #endif

230
ipv4.c
View File

@ -180,13 +180,9 @@ ipv4_addrexists(struct dhcpcd_ctx *ctx, const struct in_addr *addr)
void void
ipv4_freeroutes(struct rt_head *rts) ipv4_freeroutes(struct rt_head *rts)
{ {
struct rt *r;
if (rts) { if (rts) {
while ((r = TAILQ_FIRST(rts))) { ipv4_freerts(rts);
TAILQ_REMOVE(rts, r, next);
free(r);
}
free(rts); free(rts);
} }
} }
@ -201,10 +197,15 @@ ipv4_init(struct dhcpcd_ctx *ctx)
return -1; return -1;
TAILQ_INIT(ctx->ipv4_routes); 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; return 0;
} }
/* Interface comparer for working out ordering. */ /* Interface comparer for working out ordering. */
static int static int
ipv4_ifcmp(const struct interface *si, const struct interface *ti) ipv4_ifcmp(const struct interface *si, const struct interface *ti)
@ -291,7 +292,7 @@ find_route(struct rt_head *rts, const struct rt *r, const struct rt *srt)
return NULL; return NULL;
TAILQ_FOREACH(rt, rts, next) { TAILQ_FOREACH(rt, rts, next) {
if (rt->dest.s_addr == r->dest.s_addr && if (rt->dest.s_addr == r->dest.s_addr &&
#if HAVE_ROUTE_METRIC #ifdef HAVE_ROUTE_METRIC
(srt || (!rt->iface || (srt || (!rt->iface ||
rt->iface->metric == r->iface->metric)) && rt->iface->metric == r->iface->metric)) &&
#endif #endif
@ -328,27 +329,78 @@ desc_route(const char *cmd, const struct rt *rt)
addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); 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, /* If something other than dhcpcd removes a route,
* we need to remove it from our internal table. */ * we need to remove it from our internal table. */
int 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; struct rt *f;
f = find_route(ctx->ipv4_routes, rt, NULL); f = ipv4_findrt(ctx, rt, 1);
if (f == NULL) switch (cmd) {
return 0; case RTM_ADD:
desc_route("removing", f); if (f == NULL) {
TAILQ_REMOVE(ctx->ipv4_routes, f, next); if ((f = malloc(sizeof(*f))) == NULL)
free(f); return -1;
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 n_route(a) nc_route(NULL, a)
#define c_route(a, b) nc_route(0, a, b) #define c_route(a, b) nc_route(a, b)
static int 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 */ /* Don't set default routes if not asked to */
if (nrt->dest.s_addr == 0 && if (nrt->dest.s_addr == 0 &&
@ -356,17 +408,44 @@ nc_route(int add, struct rt *ort, struct rt *nrt)
!(nrt->iface->options->options & DHCPCD_GATEWAY)) !(nrt->iface->options->options & DHCPCD_GATEWAY))
return -1; return -1;
desc_route(add ? "adding" : "changing", nrt); desc_route(ort == NULL ? "adding" : "changing", nrt);
/* We delete and add the route so that we can change metric and
* prefer the interface. if (ort == NULL) {
* This also has the nice side effect of flushing ARP entries so ort = ipv4_findrt(nrt->iface->ctx, nrt, 0);
* we don't have to do that manually. */ if (ort && ort->iface == nrt->iface &&
if (if_delroute(ort) == -1 && errno != ESRCH) #ifdef HAVE_ROUTE_METRIC
syslog(LOG_ERR, "%s: ipv4_delroute: %m", ort->iface->name); ort->metric == nrt->metric &&
if (!if_addroute(nrt)) #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; 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; return -1;
#endif
} }
static int static int
@ -375,57 +454,44 @@ d_route(struct rt *rt)
int retval; int retval;
desc_route("deleting", rt); desc_route("deleting", rt);
retval = if_delroute(rt); retval = if_route(RTM_DELETE, rt, NULL);
if (retval != 0 && errno != ENOENT && errno != ESRCH) if (retval != 0 && errno != ENOENT && errno != ESRCH)
syslog(LOG_ERR,"%s: if_delroute: %m", rt->iface->name); syslog(LOG_ERR,"%s: if_delroute: %m", rt->iface->name);
return retval; return retval;
} }
static struct rt * 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; const struct dhcp_state *s;
struct in_addr net; struct rt *r;
struct rt *rt;
addr = dhcp->yiaddr; s = D_CSTATE(ifp);
if (addr == 0) if (s->net.s_addr == INADDR_BROADCAST ||
addr = dhcp->ciaddr; s->net.s_addr == INADDR_ANY)
/* 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)
return NULL; return NULL;
rt = malloc(sizeof(*rt));
rt->dest.s_addr = addr & net.s_addr; r = malloc(sizeof(*r));
rt->net.s_addr = net.s_addr; if (r == NULL) {
rt->gate.s_addr = 0; syslog(LOG_ERR, "%s: %m", __func__);
return rt; 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 = INADDR_ANY;
return r;
} }
static struct rt_head * static struct rt_head *
add_subnet_route(struct rt_head *rt, const struct interface *ifp) add_subnet_route(struct rt_head *rt, const struct interface *ifp)
{ {
struct rt *r; struct rt *r;
const struct dhcp_state *s;
if (rt == NULL) /* earlier malloc failed */ if (rt == NULL) /* earlier malloc failed */
return NULL; return NULL;
s = D_CSTATE(ifp); if ((r = make_subnet_route(ifp)) == NULL)
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);
return 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); TAILQ_INSERT_HEAD(rt, r, next);
return rt; return rt;
} }
@ -598,6 +664,7 @@ ipv4_buildroutes(struct dhcpcd_ctx *ctx)
return; return;
} }
TAILQ_INIT(nrs); TAILQ_INIT(nrs);
TAILQ_FOREACH(ifp, ctx->ifaces, next) { TAILQ_FOREACH(ifp, ctx->ifaces, next) {
state = D_CSTATE(ifp); state = D_CSTATE(ifp);
if (state == NULL || state->new == NULL || !state->added) if (state == NULL || state->new == NULL || !state->added)
@ -616,7 +683,10 @@ ipv4_buildroutes(struct dhcpcd_ctx *ctx)
continue; continue;
TAILQ_FOREACH_SAFE(rt, dnr, next, rtn) { TAILQ_FOREACH_SAFE(rt, dnr, next, rtn) {
rt->iface = ifp; rt->iface = ifp;
#ifdef HAVE_ROUTE_METRIC
rt->metric = ifp->metric; rt->metric = ifp->metric;
#endif
rt->flags = state->added & STATE_FAKE;
/* Is this route already in our table? */ /* Is this route already in our table? */
if ((find_route(nrs, rt, NULL)) != NULL) if ((find_route(nrs, rt, NULL)) != NULL)
continue; continue;
@ -627,9 +697,11 @@ ipv4_buildroutes(struct dhcpcd_ctx *ctx)
continue; continue;
if (or->flags & STATE_FAKE || if (or->flags & STATE_FAKE ||
or->iface != ifp || or->iface != ifp ||
#ifdef HAVE_ROUTE_METRIC
rt->metric != or->metric ||
#endif
or->src.s_addr != state->addr.s_addr || or->src.s_addr != state->addr.s_addr ||
rt->gate.s_addr != or->gate.s_addr || rt->gate.s_addr != or->gate.s_addr)
rt->metric != or->metric)
{ {
if (c_route(or, rt) != 0) if (c_route(or, rt) != 0)
continue; continue;
@ -637,13 +709,15 @@ ipv4_buildroutes(struct dhcpcd_ctx *ctx)
TAILQ_REMOVE(ctx->ipv4_routes, or, next); TAILQ_REMOVE(ctx->ipv4_routes, or, next);
free(or); free(or);
} else { } else {
if (!(state->added & STATE_FAKE) && if (state->added & STATE_FAKE) {
n_route(rt) != 0) if (!ipv4_findrt(ctx, rt, 1))
continue; continue;
} else {
if (n_route(rt) != 0)
continue;
}
} }
rt->flags = STATE_ADDED; rt->flags |= STATE_ADDED;
if (state->added & STATE_FAKE)
rt->flags |= STATE_FAKE;
TAILQ_REMOVE(dnr, rt, next); TAILQ_REMOVE(dnr, rt, next);
TAILQ_INSERT_TAIL(nrs, rt, next); TAILQ_INSERT_TAIL(nrs, rt, next);
} }
@ -659,7 +733,6 @@ ipv4_buildroutes(struct dhcpcd_ctx *ctx)
d_route(rt); d_route(rt);
} }
ipv4_freeroutes(ctx->ipv4_routes); ipv4_freeroutes(ctx->ipv4_routes);
ctx->ipv4_routes = nrs; ctx->ipv4_routes = nrs;
} }
@ -724,6 +797,7 @@ ipv4_getstate(struct interface *ifp)
return NULL; return NULL;
} }
TAILQ_INIT(&state->addrs); TAILQ_INIT(&state->addrs);
TAILQ_INIT(&state->routes);
} }
return state; return state;
} }
@ -762,8 +836,7 @@ ipv4_applyaddr(void *arg)
struct dhcp_lease *lease; struct dhcp_lease *lease;
struct if_options *ifo = ifp->options; struct if_options *ifo = ifp->options;
struct ipv4_addr *ap; struct ipv4_addr *ap;
struct ipv4_state *istate; struct ipv4_state *istate = NULL;
struct rt *rt;
int r; int r;
/* As we are now adjusting an interface, we need to ensure /* As we are now adjusting an interface, we need to ensure
@ -866,6 +939,7 @@ ipv4_applyaddr(void *arg)
return; return;
istate = ipv4_getstate(ifp); istate = ipv4_getstate(ifp);
ap = malloc(sizeof(*ap)); ap = malloc(sizeof(*ap));
ap->iface = ifp;
ap->addr = lease->addr; ap->addr = lease->addr;
ap->net = lease->net; ap->net = lease->net;
ap->dst.s_addr = INADDR_ANY; ap->dst.s_addr = INADDR_ANY;
@ -883,18 +957,11 @@ ipv4_applyaddr(void *arg)
state->addr.s_addr = lease->addr.s_addr; state->addr.s_addr = lease->addr.s_addr;
state->net.s_addr = lease->net.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: 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); ipv4_buildroutes(ifp->ctx);
script_runreason(ifp, state->reason); script_runreason(ifp, state->reason);
} }
@ -941,6 +1008,7 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx,
syslog(LOG_ERR, "%s: %m", __func__); syslog(LOG_ERR, "%s: %m", __func__);
return; return;
} }
ap->iface = ifp;
ap->addr.s_addr = addr->s_addr; ap->addr.s_addr = addr->s_addr;
ap->net.s_addr = net->s_addr; ap->net.s_addr = net->s_addr;
if (dst) if (dst)
@ -971,6 +1039,7 @@ ipv4_free(struct interface *ifp)
TAILQ_REMOVE(&state->addrs, addr, next); TAILQ_REMOVE(&state->addrs, addr, next);
free(addr); free(addr);
} }
ipv4_freerts(&state->routes);
free(state); free(state);
} }
} }
@ -981,4 +1050,5 @@ ipv4_ctxfree(struct dhcpcd_ctx *ctx)
{ {
ipv4_freeroutes(ctx->ipv4_routes); ipv4_freeroutes(ctx->ipv4_routes);
ipv4_freeroutes(ctx->ipv4_kroutes);
} }

7
ipv4.h
View File

@ -36,7 +36,9 @@ struct rt {
struct in_addr net; struct in_addr net;
struct in_addr gate; struct in_addr gate;
const struct interface *iface; const struct interface *iface;
#ifdef HAVE_ROUTE_METRIC
unsigned int metric; unsigned int metric;
#endif
struct in_addr src; struct in_addr src;
uint8_t flags; uint8_t flags;
}; };
@ -47,11 +49,13 @@ struct ipv4_addr {
struct in_addr addr; struct in_addr addr;
struct in_addr net; struct in_addr net;
struct in_addr dst; struct in_addr dst;
struct interface *iface;
}; };
TAILQ_HEAD(ipv4_addrhead, ipv4_addr); TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
struct ipv4_state { struct ipv4_state {
struct ipv4_addrhead addrs; struct ipv4_addrhead addrs;
struct rt_head routes;
}; };
#define IPV4_STATE(ifp) \ #define IPV4_STATE(ifp) \
@ -72,7 +76,8 @@ int ipv4_addrexists(struct dhcpcd_ctx *, const struct in_addr *);
void ipv4_buildroutes(struct dhcpcd_ctx *); void ipv4_buildroutes(struct dhcpcd_ctx *);
void ipv4_applyaddr(void *); 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 *, struct ipv4_addr *ipv4_iffindaddr(struct interface *,
const struct in_addr *, const struct in_addr *); const struct in_addr *, const struct in_addr *);

182
ipv6.c
View File

@ -58,10 +58,10 @@
#define ELOOP_QUEUE 7 #define ELOOP_QUEUE 7
#include "common.h" #include "common.h"
#include "if.h"
#include "dhcpcd.h" #include "dhcpcd.h"
#include "dhcp6.h" #include "dhcp6.h"
#include "eloop.h" #include "eloop.h"
#include "if.h"
#include "ipv6.h" #include "ipv6.h"
#include "ipv6nd.h" #include "ipv6nd.h"
@ -151,6 +151,8 @@ ipv6_init(struct dhcpcd_ctx *dhcpcd_ctx)
} }
TAILQ_INIT(ctx->ra_routers); TAILQ_INIT(ctx->ra_routers);
TAILQ_INIT(&ctx->kroutes);
ctx->sndhdr.msg_namelen = sizeof(struct sockaddr_in6); ctx->sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
ctx->sndhdr.msg_iov = ctx->sndiov; ctx->sndhdr.msg_iov = ctx->sndiov;
ctx->sndhdr.msg_iovlen = 1; ctx->sndhdr.msg_iovlen = 1;
@ -725,9 +727,6 @@ ipv6_addaddr(struct ipv6_addr *ap, const struct timeval *now)
ap->flags |= IPV6_AF_ADDED; ap->flags |= IPV6_AF_ADDED;
if (ap->delegating_iface) if (ap->delegating_iface)
ap->flags |= IPV6_AF_DELEGATED; 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 #ifdef IPV6_POLLADDRFLAG
eloop_timeout_delete(ap->iface->ctx->eloop, eloop_timeout_delete(ap->iface->ctx->eloop,
@ -1223,6 +1222,9 @@ ipv6_start(struct interface *ifp)
if (ap == NULL && ipv6_addlinklocal(ifp) == -1) if (ap == NULL && ipv6_addlinklocal(ifp) == -1)
return -1; return -1;
/* Load existing routes */
if_initrt6(ifp);
return 0; return 0;
} }
@ -1256,17 +1258,14 @@ ipv6_freedrop(struct interface *ifp, int drop)
void void
ipv6_ctxfree(struct dhcpcd_ctx *ctx) ipv6_ctxfree(struct dhcpcd_ctx *ctx)
{ {
struct rt6 *rt;
if (ctx->ipv6 == NULL) if (ctx->ipv6 == NULL)
return; return;
while ((rt = TAILQ_FIRST(ctx->ipv6->routes))) { ipv6_freerts(ctx->ipv6->routes);
TAILQ_REMOVE(ctx->ipv6->routes, rt, next);
free(rt);
}
free(ctx->ipv6->routes); free(ctx->ipv6->routes);
free(ctx->ipv6->ra_routers); free(ctx->ipv6->ra_routers);
ipv6_freerts(&ctx->ipv6->kroutes);
free(ctx->ipv6); free(ctx->ipv6);
} }
@ -1693,7 +1692,7 @@ find_route6(struct rt6_head *rts, const struct rt6 *r)
TAILQ_FOREACH(rt, rts, next) { TAILQ_FOREACH(rt, rts, next) {
if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) && if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) &&
#if HAVE_ROUTE_METRIC #ifdef HAVE_ROUTE_METRIC
(r->iface == NULL || rt->iface == NULL || (r->iface == NULL || rt->iface == NULL ||
rt->iface->metric == r->iface->metric) && rt->iface->metric == r->iface->metric) &&
#endif #endif
@ -1726,30 +1725,75 @@ desc_route(const char *cmd, const struct rt6 *rt)
dest, ipv6_prefixlen(&rt->net), gate); 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, /* If something other than dhcpcd removes a route,
* we need to remove it from our internal table. */ * we need to remove it from our internal table. */
int 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; struct rt6 *f;
if (ctx->ipv6 == NULL) f = ipv6_findrt(ctx, rt, 1);
return 0; switch(cmd) {
case RTM_ADD:
f = find_route6(ctx->ipv6->routes, rt); if (f == NULL) {
if (f == NULL) if ((f = malloc(sizeof(*f))) == NULL)
return 0; return -1;
desc_route("removing", f); *f = *rt;
TAILQ_REMOVE(ctx->ipv6->routes, f, next); TAILQ_INSERT_TAIL(&ctx->ipv6->kroutes, f, next);
free(f); }
return 1; 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 n_route(a) nc_route(NULL, a)
#define c_route(a, b) nc_route(0, a, b) #define c_route(a, b) nc_route(a, b)
static int 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 */ /* Don't set default routes if not asked to */
if (IN6_IS_ADDR_UNSPECIFIED(&nrt->dest) && if (IN6_IS_ADDR_UNSPECIFIED(&nrt->dest) &&
@ -1757,15 +1801,37 @@ nc_route(int add, struct rt6 *ort, struct rt6 *nrt)
!(nrt->iface->options->options & DHCPCD_GATEWAY)) !(nrt->iface->options->options & DHCPCD_GATEWAY))
return -1; return -1;
desc_route(add ? "adding" : "changing", nrt); desc_route(ort == NULL ? "adding" : "changing", nrt);
/* We delete and add the route so that we can change metric and
* prefer the interface. */ if (ort == NULL) {
if (if_delroute6(ort) == -1 && errno != ESRCH) ort = ipv6_findrt(nrt->iface->ctx, nrt, 0);
syslog(LOG_ERR, "%s: if_delroute6: %m", ort->iface->name); if (ort && ort->iface == nrt->iface &&
if (if_addroute6(nrt) == 0) #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; return 0;
syslog(LOG_ERR, "%s: if_addroute6: %m", nrt->iface->name); syslog(LOG_ERR, "if_route6 (ADD): %m");
return -1; return -1;
#endif
} }
static int static int
@ -1774,7 +1840,7 @@ d_route(struct rt6 *rt)
int retval; int retval;
desc_route("deleting", rt); desc_route("deleting", rt);
retval = if_delroute6(rt); retval = if_route6(RTM_DELETE, rt, NULL);
if (retval != 0 && errno != ENOENT && errno != ESRCH) if (retval != 0 && errno != ENOENT && errno != ESRCH)
syslog(LOG_ERR,"%s: if_delroute6: %m", rt->iface->name); syslog(LOG_ERR,"%s: if_delroute6: %m", rt->iface->name);
return retval; return retval;
@ -1791,7 +1857,9 @@ make_route(const struct interface *ifp, const struct ra *rap)
return NULL; return NULL;
} }
r->iface = ifp; r->iface = ifp;
#ifdef HAVE_ROUTE_METRIC
r->metric = ifp->metric; r->metric = ifp->metric;
#endif
if (rap) if (rap)
r->mtu = rap->mtu; r->mtu = rap->mtu;
else else
@ -1848,45 +1916,6 @@ make_router(const struct ra *rap)
return r; 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) \ #define RT_IS_DEFAULT(rtp) \
(IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) && \ (IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) && \
IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any)) IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any))
@ -1951,7 +1980,7 @@ ipv6_buildroutes(struct dhcpcd_ctx *ctx)
/* First add reachable routers and their prefixes */ /* First add reachable routers and their prefixes */
ipv6_build_ra_routes(ctx->ipv6, &dnr, 0); ipv6_build_ra_routes(ctx->ipv6, &dnr, 0);
#if HAVE_ROUTE_METRIC #ifdef HAVE_ROUTE_METRIC
have_default = (TAILQ_FIRST(&dnr) != NULL); have_default = (TAILQ_FIRST(&dnr) != NULL);
#endif #endif
@ -1961,7 +1990,7 @@ ipv6_buildroutes(struct dhcpcd_ctx *ctx)
ipv6_build_dhcp_routes(ctx, &dnr, DH6S_BOUND); ipv6_build_dhcp_routes(ctx, &dnr, DH6S_BOUND);
ipv6_build_dhcp_routes(ctx, &dnr, DH6S_DELEGATED); 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 /* 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 * 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 * router. Of course, we should at least have some routers if all
@ -1980,6 +2009,7 @@ ipv6_buildroutes(struct dhcpcd_ctx *ctx)
} }
TAILQ_INIT(nrs); TAILQ_INIT(nrs);
have_default = 0; have_default = 0;
TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) { TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) {
/* Is this route already in our table? */ /* Is this route already in our table? */
if (find_route6(nrs, rt) != NULL) if (find_route6(nrs, rt) != NULL)
@ -1988,9 +2018,11 @@ ipv6_buildroutes(struct dhcpcd_ctx *ctx)
/* Do we already manage it? */ /* Do we already manage it? */
if ((or = find_route6(ctx->ipv6->routes, rt))) { if ((or = find_route6(ctx->ipv6->routes, rt))) {
if (or->iface != rt->iface || if (or->iface != rt->iface ||
#ifdef HAVE_ROUTE_METRIC
rt->metric != or->metric ||
#endif
// or->src.s_addr != ifp->addr.s_addr || // or->src.s_addr != ifp->addr.s_addr ||
!IN6_ARE_ADDR_EQUAL(&rt->gate, &or->gate) || !IN6_ARE_ADDR_EQUAL(&rt->gate, &or->gate))
rt->metric != or->metric)
{ {
if (c_route(or, rt) != 0) if (c_route(or, rt) != 0)
continue; continue;

8
ipv6.h
View File

@ -146,7 +146,9 @@ struct rt6 {
struct in6_addr gate; struct in6_addr gate;
const struct interface *iface; const struct interface *iface;
unsigned int flags; unsigned int flags;
#ifdef HAVE_ROUTE_METRIC
unsigned int metric; unsigned int metric;
#endif
unsigned int mtu; unsigned int mtu;
}; };
TAILQ_HEAD(rt6_head, rt6); TAILQ_HEAD(rt6_head, rt6);
@ -218,6 +220,8 @@ struct ipv6_ctx {
struct ra_head *ra_routers; struct ra_head *ra_routers;
struct rt6_head *routes; struct rt6_head *routes;
struct rt6_head kroutes;
int dhcp_fd; int dhcp_fd;
}; };
@ -266,8 +270,8 @@ void ipv6_addtempaddrs(struct interface *, const struct timeval *);
int ipv6_start(struct interface *); int ipv6_start(struct interface *);
void ipv6_ctxfree(struct dhcpcd_ctx *); void ipv6_ctxfree(struct dhcpcd_ctx *);
int ipv6_routedeleted(struct dhcpcd_ctx *, const struct rt6 *); int ipv6_handlert(struct dhcpcd_ctx *, int cmd, struct rt6 *);
int ipv6_removesubnet(struct interface *, struct ipv6_addr *); void ipv6_freerts(struct rt6_head *);
void ipv6_buildroutes(struct dhcpcd_ctx *); void ipv6_buildroutes(struct dhcpcd_ctx *);
#else #else

View File

@ -1118,6 +1118,12 @@ extra_opt:
#ifdef IPV6_MANAGETEMPADDR #ifdef IPV6_MANAGETEMPADDR
ipv6_addtempaddrs(ifp, &rap->received); ipv6_addtempaddrs(ifp, &rap->received);
#endif #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); ipv6_buildroutes(ifp->ctx);
if (ipv6nd_scriptrun(rap)) if (ipv6nd_scriptrun(rap))
return; return;