Skip to content

Commit 46822c2

Browse files
authored
Update route if lifetime needs changing (#441)
Everything about IPv6 is lifetime, not expiry. Only linux routes have an expiry. OK, this is not consistent at all! In the kernel RTA_EXPIRES is only set for IPv4 multicast routes when emitting route changes and only read for IPv6 routes when userland makes route changes. We cannot set this for non IPv6 routes currently. To make it worse, we set a UINT32 for IPv6 routes but read a UINT64 for IPv4 multicast routes. To make this even more totally bonkers, the expiry we set for the IPv6 route can be read back via RTA_CACHEINE rta_expires but we need to convert to divide it by hz. Now we can read kernel route lifetime correctly, we can compare this to what we think it should be when building the routing table (allowing for some deviation due to processing time) and update if needed. Fixes #428.
1 parent 958505e commit 46822c2

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

src/if-linux.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -752,11 +752,34 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
752752
}
753753
break;
754754
}
755-
case RTA_EXPIRES:
755+
#ifdef HAVE_ROUTE_LIFETIME
756+
case RTA_CACHEINFO:
756757
{
757-
rt->rt_expires = *(uint32_t *)RTA_DATA(rta);
758+
struct rta_cacheinfo ci;
759+
static long hz;
760+
761+
if (hz == 0) {
762+
hz = sysconf(_SC_CLK_TCK);
763+
if (hz == -1)
764+
hz = CLOCKS_PER_SEC;
765+
}
766+
767+
memcpy(&ci, RTA_DATA(rta), sizeof(ci));
768+
rt->rt_lifetime = (uint32_t)(ci.rta_expires / hz);
758769
break;
759770
}
771+
#endif
772+
#if 0
773+
case RTA_EXPIRES:
774+
/* Reading the kernel source, this is only
775+
* emitted by IPv4 multicast routes as a UINT64.
776+
* Although we can set it for IPv6 routes as a UINT32,
777+
* the kernel will massage the value to HZ and put it
778+
* into RTA_CACHINFO as read above.
779+
* Gotta love that consistency! */
780+
rt->rt_lifetime = (uint32_t)*(uint64_t *)RTA_DATA(rta);
781+
break;
782+
#endif
760783
}
761784

762785
if (sa != NULL) {
@@ -1740,9 +1763,10 @@ if_route(unsigned char cmd, const struct rt *rt)
17401763
if (!sa_is_loopback(&rt->rt_gateway))
17411764
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->rt_ifp->index);
17421765

1743-
/* add route lifetime */
1744-
if (rt->rt_expires != 0)
1745-
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_EXPIRES, rt->rt_expires);
1766+
#ifdef HAVE_ROUTE_LIFETIME
1767+
if (rt->rt_lifetime != 0)
1768+
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_EXPIRES,rt->rt_lifetime);
1769+
#endif
17461770

17471771
if (rt->rt_metric != 0)
17481772
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY,

src/ipv6.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,9 +2301,10 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
23012301
#ifdef HAVE_ROUTE_PREF
23022302
rt->rt_pref = ipv6nd_rtpref(rinfo->flags);
23032303
#endif
2304-
rt->rt_expires = lifetime_left(rinfo->lifetime,
2304+
#ifdef HAVE_ROUTE_LIFETIME
2305+
rt->rt_lifetime = lifetime_left(rinfo->lifetime,
23052306
&rinfo->acquired, &now);
2306-
2307+
#endif
23072308
rt_proto_add(routes, rt);
23082309
}
23092310

@@ -2317,9 +2318,11 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
23172318
#ifdef HAVE_ROUTE_PREF
23182319
rt->rt_pref = ipv6nd_rtpref(rap->flags);
23192320
#endif
2320-
rt->rt_expires =
2321+
#ifdef HAVE_ROUTE_LIFETIME
2322+
rt->rt_lifetime =
23212323
lifetime_left(addr->prefix_vltime,
23222324
&addr->acquired, &now);
2325+
#endif
23232326

23242327
rt_proto_add(routes, rt);
23252328
}
@@ -2352,8 +2355,10 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
23522355
#ifdef HAVE_ROUTE_PREF
23532356
rt->rt_pref = ipv6nd_rtpref(rap->flags);
23542357
#endif
2355-
rt->rt_expires = lifetime_left(rap->lifetime,
2358+
#ifdef HAVE_ROUTE_LIFETIME
2359+
rt->rt_lifetime = lifetime_left(rap->lifetime,
23562360
&rap->acquired, &now);
2361+
#endif
23572362

23582363
rt_proto_add(routes, rt);
23592364
}

src/route.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,32 @@ rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
502502
#endif
503503
}
504504

505+
/* Compare miscellaneous route details */
506+
static bool
507+
rt_cmp_misc(struct rt *nrt, struct rt *ort)
508+
{
509+
/* MTU changed */
510+
if (ort->rt_mtu != nrt->rt_mtu)
511+
return false;
512+
513+
#ifdef HAVE_ROUTE_LIFETIME
514+
uint32_t deviation;
515+
516+
/* There might be a minor difference between kernel route
517+
* lifetime and our lifetime due to processing times.
518+
* We allow a small deviation to avoid needless route changes.
519+
* dhcpcd will expire the route regardless of route lifetime support. */
520+
if (nrt->rt_lifetime > ort->rt_lifetime)
521+
deviation = nrt->rt_lifetime - ort->rt_lifetime;
522+
else
523+
deviation = ort->rt_lifetime - nrt->rt_lifetime;
524+
if (deviation > RTLIFETIME_DEV_MAX)
525+
return false;
526+
#endif
527+
528+
return true;
529+
}
530+
505531
static bool
506532
rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
507533
{
@@ -540,7 +566,7 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
540566
#endif
541567
sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
542568
{
543-
if (ort->rt_mtu == nrt->rt_mtu)
569+
if (rt_cmp_misc(nrt, ort))
544570
return true;
545571
change = true;
546572
kroute = true;
@@ -555,7 +581,7 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
555581
rt_cmp_netmask(ort, nrt) == 0 &&
556582
sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
557583
{
558-
if (ort->rt_mtu == nrt->rt_mtu)
584+
if (rt_cmp_misc(nrt, ort))
559585
return true;
560586
change = true;
561587
}
@@ -678,7 +704,7 @@ rt_doroute(rb_tree_t *kroutes, struct rt *rt)
678704
!rt_cmp(rt, or) ||
679705
(rt->rt_ifa.sa_family != AF_UNSPEC &&
680706
sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
681-
or->rt_mtu != rt->rt_mtu)
707+
!rt_cmp_misc(rt, or))
682708
{
683709
if (!rt_add(kroutes, rt, or))
684710
return false;

src/route.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
# define HAVE_ROUTE_METRIC 1
6060
# endif
6161
#endif
62+
#ifndef HAVE_ROUTE_LIFETIME
63+
# if defined(__linux__)
64+
# define HAVE_ROUTE_LIFETIME 1 /* For IPv6 routes only */
65+
# endif
66+
#endif
6267

6368
#ifdef __linux__
6469
# include <linux/version.h> /* RTA_PREF is only an enum.... */
@@ -120,7 +125,10 @@ struct rt {
120125
#define RTDF_GATELINK 0x40 /* Gateway is on link */
121126
size_t rt_order;
122127
rb_node_t rt_tree;
123-
uint32_t rt_expires; /* current lifetime of route */
128+
#ifdef HAVE_ROUTE_LIFETIME
129+
uint32_t rt_lifetime; /* current lifetime of route */
130+
#define RTLIFETIME_DEV_MAX 2 /* max deviation for cmp */
131+
#endif
124132
};
125133

126134
extern const rb_tree_ops_t rt_compare_list_ops;

0 commit comments

Comments
 (0)