--- embedaddon/bird/sysdep/linux/netlink.c 2017/08/22 12:33:54 1.1.1.1 +++ embedaddon/bird/sysdep/linux/netlink.c 2021/03/17 19:50:23 1.1.1.2 @@ -59,22 +59,26 @@ /* * 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 @@ -296,6 +300,7 @@ struct nl_want_attrs { static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = { [IFLA_IFNAME] = { 1, 0, 0 }, [IFLA_MTU] = { 1, 1, sizeof(u32) }, + [IFLA_MASTER] = { 1, 1, sizeof(u32) }, [IFLA_WIRELESS] = { 1, 0, 0 }, }; @@ -320,9 +325,15 @@ static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MA #define BIRD_RTA_MAX (RTA_TABLE+1) +#ifndef IPV6 static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = { [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, }; +#else +static struct nl_want_attrs mpnh_attr_want6[BIRD_RTA_MAX] = { + [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) }, +}; +#endif #ifndef IPV6 static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = { @@ -345,6 +356,7 @@ static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MA [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) }, }; @@ -477,7 +489,7 @@ nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, } static struct mpnh * -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 mpnh *nh_buffer; @@ -515,10 +527,26 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr /* Nonexistent RTNH_PAYLOAD ?? */ nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0); - nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a)); + switch (af) + { +#ifndef IPV6 + case AF_INET: + if (!nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a))) + return NULL; + break; +#else + case AF_INET6: + if (!nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want6, a, sizeof(a))) + return NULL; + break; +#endif + default: + return NULL; + } + if (a[RTA_GATEWAY]) { - memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr)); + memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(rv->gw)); ipa_ntoh(rv->gw); neighbor *ng = neigh_find2(&p->p, &rv->gw, rv->iface, @@ -591,7 +619,7 @@ nl_parse_link(struct nlmsghdr *h, int scan) struct iface f = {}; struct iface *ifi; char *name; - u32 mtu; + u32 mtu, master = 0; uint fl; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), ifla_attr_want, a, sizeof(a))) @@ -614,6 +642,9 @@ nl_parse_link(struct nlmsghdr *h, int scan) name = RTA_DATA(a[IFLA_IFNAME]); mtu = rta_get_u32(a[IFLA_MTU]); + if (a[IFLA_MASTER]) + master = rta_get_u32(a[IFLA_MASTER]); + ifi = if_find_by_index(i->ifi_index); if (!new) { @@ -633,6 +664,9 @@ nl_parse_link(struct nlmsghdr *h, int scan) f.index = i->ifi_index; f.mtu = mtu; + f.master_index = master; + f.master = if_find_by_index(master); + fl = i->ifi_flags; if (fl & IFF_UP) f.flags |= IF_ADMIN_UP; @@ -808,6 +842,26 @@ kif_do_scan(struct kif_proto *p UNUSED) else log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); + /* Re-resolve master interface for slaves */ + struct iface *i; + WALK_LIST(i, iface_list) + if (i->master_index) + { + struct iface f = { + .flags = i->flags, + .mtu = i->mtu, + .index = i->index, + .master_index = i->master_index, + .master = if_find_by_index(i->master_index) + }; + + if (f.master != i->master) + { + memcpy(f.name, i->name, sizeof(f.name)); + if_update(&f); + } + } + nl_request_dump(BIRD_AF, RTM_GETADDR); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) @@ -885,22 +939,25 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_l struct { struct nlmsghdr h; struct rtmsg r; - char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)]; - } r; + char buf[0]; + } *r; + uint rsize = sizeof(*r) + 128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops); + r = alloca(rsize); + DBG("nl_send_route(%I/%d,op=%x)\n", net->n.prefix, net->n.pxlen, op); - bzero(&r.h, sizeof(r.h)); - bzero(&r.r, sizeof(r.r)); - r.h.nlmsg_type = op ? RTM_NEWROUTE : RTM_DELROUTE; - r.h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - r.h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK; + bzero(&r->h, sizeof(r->h)); + bzero(&r->r, sizeof(r->r)); + r->h.nlmsg_type = op ? RTM_NEWROUTE : RTM_DELROUTE; + r->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + r->h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK; - r.r.rtm_family = BIRD_AF; - r.r.rtm_dst_len = net->n.pxlen; - r.r.rtm_protocol = RTPROT_BIRD; - r.r.rtm_scope = RT_SCOPE_NOWHERE; - nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); + r->r.rtm_family = BIRD_AF; + r->r.rtm_dst_len = net->n.pxlen; + r->r.rtm_protocol = RTPROT_BIRD; + r->r.rtm_scope = RT_SCOPE_NOWHERE; + nl_add_attr_ipa(&r->h, rsize, RTA_DST, net->n.prefix); /* * Strange behavior for RTM_DELROUTE: @@ -910,9 +967,9 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_l */ if (krt_table_id(p) < 256) - r.r.rtm_table = krt_table_id(p); + r->r.rtm_table = krt_table_id(p); else - nl_add_attr_u32(&r.h, sizeof(r), RTA_TABLE, krt_table_id(p)); + nl_add_attr_u32(&r->h, rsize, RTA_TABLE, krt_table_id(p)); if (a->source == RTS_DUMMY) priority = e->u.krt.metric; @@ -922,7 +979,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_l priority = ea->u.data; if (priority) - nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, priority); + nl_add_attr_u32(&r->h, rsize, RTA_PRIORITY, priority); /* For route delete, we do not specify remaining route attributes */ if (op == NL_OP_DELETE) @@ -930,15 +987,15 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_l /* Default scope is LINK for device routes, UNIVERSE otherwise */ if (ea = ea_find(eattrs, EA_KRT_SCOPE)) - r.r.rtm_scope = ea->u.data; + r->r.rtm_scope = ea->u.data; else - r.r.rtm_scope = (dest == RTD_DEVICE) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; + r->r.rtm_scope = (dest == RTD_DEVICE) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) - nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); + nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); if (ea = ea_find(eattrs, EA_KRT_REALM)) - nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data); + nl_add_attr_u32(&r->h, rsize, RTA_FLOW, ea->u.data); u32 metrics[KRT_METRICS_MAX]; @@ -953,7 +1010,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_l } if (metrics[0]) - nl_add_metrics(&r.h, sizeof(r), metrics, KRT_METRICS_MAX); + nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX); dest: @@ -961,26 +1018,26 @@ dest: switch (dest) { case RTD_ROUTER: - r.r.rtm_type = RTN_UNICAST; - nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, iface->index); - nl_add_attr_ipa(&r.h, sizeof(r), RTA_GATEWAY, gw); + r->r.rtm_type = RTN_UNICAST; + nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index); + nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, gw); break; case RTD_DEVICE: - r.r.rtm_type = RTN_UNICAST; - nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, iface->index); + r->r.rtm_type = RTN_UNICAST; + nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index); break; case RTD_BLACKHOLE: - r.r.rtm_type = RTN_BLACKHOLE; + r->r.rtm_type = RTN_BLACKHOLE; break; case RTD_UNREACHABLE: - r.r.rtm_type = RTN_UNREACHABLE; + r->r.rtm_type = RTN_UNREACHABLE; break; case RTD_PROHIBIT: - r.r.rtm_type = RTN_PROHIBIT; + r->r.rtm_type = RTN_PROHIBIT; break; case RTD_MULTIPATH: - r.r.rtm_type = RTN_UNICAST; - nl_add_multipath(&r.h, sizeof(r), a->nexthops); + r->r.rtm_type = RTN_UNICAST; + nl_add_multipath(&r->h, rsize, a->nexthops); break; case RTD_NONE: break; @@ -989,7 +1046,7 @@ dest: } /* Ignore missing for DELETE */ - return nl_exchange(&r.h, (op == NL_OP_DELETE)); + return nl_exchange(&r->h, (op == NL_OP_DELETE)); } static inline int @@ -1240,10 +1297,10 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsgh { case RTN_UNICAST: - if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET)) + if (a[RTA_MULTIPATH]) { ra->dest = RTD_MULTIPATH; - ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]); + ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH], i->rtm_family); if (!ra->nexthops) { log(L_ERR "KRT: Received strange multipath route %I/%d", @@ -1385,8 +1442,10 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsgh /* * 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) @@ -1694,4 +1753,4 @@ kif_sys_start(struct kif_proto *p UNUSED) void kif_sys_shutdown(struct kif_proto *p UNUSED) { -} +} \ No newline at end of file