summaryrefslogtreecommitdiff
path: root/sysdep/linux
diff options
context:
space:
mode:
Diffstat (limited to 'sysdep/linux')
-rw-r--r--sysdep/linux/netlink.c54
1 files changed, 41 insertions, 13 deletions
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 6cea2fa9..279f3c9c 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -83,22 +83,26 @@ struct rtvia {
/*
* Structure nl_parse_state keeps state of received route processing. Ideally,
* we could just independently parse received Netlink messages and immediately
- * propagate received routes to the rest of BIRD, but Linux kernel represents
- * and announces IPv6 ECMP routes not as one route with multiple next hops (like
- * RTA_MULTIPATH in IPv4 ECMP), but as a set of routes with the same prefix.
+ * propagate received routes to the rest of BIRD, but older Linux kernel (before
+ * version 4.11) represents and announces IPv6 ECMP routes not as one route with
+ * multiple next hops (like RTA_MULTIPATH in IPv4 ECMP), but as a sequence of
+ * routes with the same prefix. More recent kernels work as with IPv4.
*
* Therefore, BIRD keeps currently processed route in nl_parse_state structure
* and postpones its propagation until we expect it to be final; i.e., when
* non-matching route is received or when the scan ends. When another matching
* route is received, it is merged with the already processed route to form an
* ECMP route. Note that merging is done only for IPv6 (merge == 1), but the
- * postponing is done in both cases (for simplicity). All IPv4 routes are just
- * considered non-matching.
+ * postponing is done in both cases (for simplicity). All IPv4 routes or IPv6
+ * routes with RTA_MULTIPATH set are just considered non-matching.
*
* This is ignored for asynchronous notifications (every notification is handled
* as a separate route). It is not an issue for our routes, as we ignore such
* notifications anyways. But importing alien IPv6 ECMP routes does not work
- * properly.
+ * properly with older kernels.
+ *
+ * Whatever the kernel version is, IPv6 ECMP routes are sent as multiple routes
+ * for the same prefix.
*/
struct nl_parse_state
@@ -348,6 +352,12 @@ static struct nl_want_attrs nexthop_attr_want4[BIRD_RTA_MAX] = {
[RTA_ENCAP] = { 1, 0, 0 },
};
+static struct nl_want_attrs nexthop_attr_want6[BIRD_RTA_MAX] = {
+ [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) },
+ [RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
+ [RTA_ENCAP] = { 1, 0, 0 },
+};
+
static struct nl_want_attrs encap_mpls_want[BIRD_RTA_MAX] = {
[RTA_DST] = { 1, 0, 0 },
};
@@ -374,6 +384,7 @@ static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
[RTA_PRIORITY] = { 1, 1, sizeof(u32) },
[RTA_PREFSRC] = { 1, 1, sizeof(ip6_addr) },
[RTA_METRICS] = { 1, 0, 0 },
+ [RTA_MULTIPATH] = { 1, 0, 0 },
[RTA_FLOW] = { 1, 1, sizeof(u32) },
[RTA_TABLE] = { 1, 1, sizeof(u32) },
[RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
@@ -631,7 +642,7 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh, int af)
}
static struct nexthop *
-nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
+nl_parse_multipath(struct krt_proto *p, struct rtattr *ra, int af)
{
/* Temporary buffer for multicast nexthops */
static struct nexthop *nh_buffer;
@@ -670,7 +681,22 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
/* Nonexistent RTNH_PAYLOAD ?? */
nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
- nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a));
+ switch (af)
+ {
+ case AF_INET:
+ if (!nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a)))
+ return NULL;
+ break;
+
+ case AF_INET6:
+ if (!nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want6, a, sizeof(a)))
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
if (a[RTA_GATEWAY])
{
rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
@@ -1520,9 +1546,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
case RTN_UNICAST:
ra->dest = RTD_UNICAST;
- if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
- {
- struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH]);
+ if (a[RTA_MULTIPATH])
+ {
+ struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH], i->rtm_family);
if (!nh)
{
log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
@@ -1699,8 +1725,10 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
/*
* Ideally, now we would send the received route to the rest of kernel code.
- * But IPv6 ECMP routes are sent as a sequence of routes, so we postpone it
- * and merge next hops until the end of the sequence.
+ * But IPv6 ECMP routes before 4.11 are sent as a sequence of routes, so we
+ * postpone it and merge next hops until the end of the sequence. Note that
+ * proper multipath updates are rejected by nl_mergable_route(), so it is
+ * always the first case for them.
*/
if (!s->net)