--- embedaddon/bird/proto/radv/packets.c 2017/08/22 12:33:54 1.1.1.1 +++ embedaddon/bird/proto/radv/packets.c 2021/03/17 19:50:23 1.1.1.2 @@ -26,6 +26,7 @@ struct radv_ra_packet #define OPT_PREFIX 3 #define OPT_MTU 5 +#define OPT_ROUTE 24 #define OPT_RDNSS 25 #define OPT_DNSSL 31 @@ -52,6 +53,15 @@ struct radv_opt_mtu u32 mtu; }; +struct radv_opt_route { + u8 type; + u8 length; + u8 pxlen; + u8 flags; + u32 lifetime; + u8 prefix[]; +}; + struct radv_opt_rdnss { u8 type; @@ -70,34 +80,42 @@ struct radv_opt_dnssl char domain[]; }; +static int +radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, + char **buf, char *bufend) +{ + struct radv_proto *p = ifa->ra; + u8 px_blocks = (rt->n.pxlen + 63) / 64; + u8 opt_len = 8 * (1 + px_blocks); -static struct radv_prefix_config default_prefix = { - .onlink = 1, - .autonomous = 1, - .valid_lifetime = DEFAULT_VALID_LIFETIME, - .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME -}; + if (*buf + opt_len > bufend) + { + log(L_WARN, "%s: Too many RA options on interface %s", + p->p.name, ifa->iface->name); + return -1; + } + uint preference = rt->preference_set ? rt->preference : ifa->cf->route_preference; + uint lifetime = rt->lifetime_set ? rt->lifetime : ifa->cf->route_lifetime; + uint valid = rt->valid && p->valid && (p->active || !ifa->cf->route_lifetime_sensitive); -static struct radv_prefix_config * -radv_prefix_match(struct radv_iface *ifa, struct ifa *a) -{ - struct proto *p = &ifa->ra->p; - struct radv_config *cf = (struct radv_config *) (p->cf); - struct radv_prefix_config *pc; + struct radv_opt_route *opt = (void *) *buf; + *buf += opt_len; + opt->type = OPT_ROUTE; + opt->length = 1 + px_blocks; + opt->pxlen = rt->n.pxlen; + opt->flags = preference; + opt->lifetime = valid ? htonl(lifetime) : 0; - if (a->scope <= SCOPE_LINK) - return NULL; + /* Copy the relevant part of the prefix */ + ip6_addr px_addr = ip6_hton(rt->n.prefix); + memcpy(opt->prefix, &px_addr, 8 * px_blocks); - WALK_LIST(pc, ifa->cf->pref_list) - if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) - return pc; + /* Keeping track of first linger timeout */ + if (!rt->valid) + ifa->valid_time = MIN(ifa->valid_time, rt->changed + ifa->cf->route_linger_time); - WALK_LIST(pc, cf->pref_list) - if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) - return pc; - - return &default_prefix; + return 0; } static int @@ -123,7 +141,7 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss else op->lifetime = htonl(rcf->lifetime); - while(NODE_VALID(rcf) && + while(NODE_VALID(rcf) && (rcf->lifetime == rcf_base->lifetime) && (rcf->lifetime_mult == rcf_base->lifetime_mult)) { @@ -136,7 +154,7 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss rcf = NODE_NEXT(rcf); } - + op->length = 1+2*i; *buf += 8 * op->length; } @@ -207,7 +225,7 @@ radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl else op->lifetime = htonl(dcf->lifetime); - while(NODE_VALID(dcf) && + while(NODE_VALID(dcf) && (dcf->lifetime == dcf_base->lifetime) && (dcf->lifetime_mult == dcf_base->lifetime_mult)) { @@ -235,11 +253,46 @@ radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl return -1; } +static int +radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *prefix, + char **buf, char *bufend) +{ + struct radv_prefix_config *pc = prefix->cf; + + if (*buf + sizeof(struct radv_opt_prefix) > bufend) + { + log(L_WARN "%s: Too many prefixes on interface %s", + ifa->ra->p.name, ifa->iface->name); + return -1; + } + + struct radv_opt_prefix *op = (void *) *buf; + op->type = OPT_PREFIX; + op->length = 4; + op->pxlen = prefix->len; + op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | + (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); + op->valid_lifetime = (ifa->ra->active || !pc->valid_lifetime_sensitive) ? + htonl(pc->valid_lifetime) : 0; + op->preferred_lifetime = (ifa->ra->active || !pc->preferred_lifetime_sensitive) ? + htonl(pc->preferred_lifetime) : 0; + op->reserved = 0; + op->prefix = prefix->prefix; + ipa_hton(op->prefix); + *buf += sizeof(*op); + + /* Keeping track of first linger timeout */ + if (!prefix->valid) + ifa->valid_time = MIN(ifa->valid_time, prefix->changed + ifa->cf->prefix_linger_time); + + return 0; +} + static void radv_prepare_ra(struct radv_iface *ifa) { - struct proto_radv *ra = ifa->ra; - struct radv_config *cf = (struct radv_config *) (ra->p.cf); + struct radv_proto *p = ifa->ra; + struct radv_config *cf = (struct radv_config *) (p->p.cf); struct radv_iface_config *ic = ifa->cf; char *buf = ifa->sk->tbuf; @@ -251,7 +304,7 @@ radv_prepare_ra(struct radv_iface *ifa) pkt->code = 0; pkt->checksum = 0; pkt->current_hop_limit = ic->current_hop_limit; - pkt->router_lifetime = (ra->active || !ic->default_lifetime_sensitive) ? + pkt->router_lifetime = (p->valid && (p->active || !ic->default_lifetime_sensitive)) ? htons(ic->default_lifetime) : 0; pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) | (ic->other_config ? OPT_RA_OTHER_CFG : 0) | @@ -270,35 +323,18 @@ radv_prepare_ra(struct radv_iface *ifa) buf += sizeof (*om); } - struct ifa *addr; - WALK_LIST(addr, ifa->iface->addrs) + /* Keeping track of first linger timeout */ + ifa->valid_time = TIME_INFINITY; + + struct radv_prefix *px; + WALK_LIST(px, ifa->prefixes) { - struct radv_prefix_config *pc; - pc = radv_prefix_match(ifa, addr); + /* Skip invalid prefixes that are past linger timeout but still not pruned */ + if (!px->valid && (px->changed + ic->prefix_linger_time <= now)) + continue; - if (!pc || pc->skip) - continue; - - if (buf + sizeof(struct radv_opt_prefix) > bufend) - { - log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name); + if (radv_prepare_prefix(ifa, px, &buf, bufend) < 0) goto done; - } - - struct radv_opt_prefix *op = (void *) buf; - op->type = OPT_PREFIX; - op->length = 4; - op->pxlen = addr->pxlen; - op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | - (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); - op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ? - htonl(pc->valid_lifetime) : 0; - op->preferred_lifetime = (ra->active || !pc->preferred_lifetime_sensitive) ? - htonl(pc->preferred_lifetime) : 0; - op->reserved = 0; - op->prefix = addr->prefix; - ipa_hton(op->prefix); - buf += sizeof(*op); } if (! ic->rdnss_local) @@ -315,33 +351,36 @@ radv_prepare_ra(struct radv_iface *ifa) if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) goto done; + if (p->fib_up) + { + FIB_WALK(&p->routes, n) + { + struct radv_route *rt = (void *) n; + + /* Skip invalid routes that are past linger timeout but still not pruned */ + if (!rt->valid && (rt->changed + ic->route_linger_time <= now)) + continue; + + if (radv_prepare_route(ifa, rt, &buf, bufend) < 0) + goto done; + } + FIB_WALK_END; + } + done: ifa->plen = buf - bufstart; } void -radv_send_ra(struct radv_iface *ifa, int shutdown) +radv_send_ra(struct radv_iface *ifa) { - struct proto_radv *ra = ifa->ra; + struct radv_proto *p = ifa->ra; /* We store prepared RA in tbuf */ if (!ifa->plen) radv_prepare_ra(ifa); - if (shutdown) - { - /* - * Modify router lifetime to 0, it is not restored because we suppose that - * the iface will be removed. The preference value also has to be zeroed. - * (RFC 4191 2.2: If router lifetime is 0, the preference value must be 0.) - */ - - struct radv_ra_packet *pkt = (void *) ifa->sk->tbuf; - pkt->router_lifetime = 0; - pkt->flags &= ~RA_PREF_MASK; - } - RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name); sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0); } @@ -351,7 +390,7 @@ static int radv_rx_hook(sock *sk, uint size) { struct radv_iface *ifa = sk->data; - struct proto_radv *ra = ifa->ra; + struct radv_proto *p = ifa->ra; /* We want just packets from sk->iface */ if (sk->lifindex != sk->iface->index) @@ -407,10 +446,11 @@ radv_err_hook(sock *sk, int err) int radv_sk_open(struct radv_iface *ifa) { - sock *sk = sk_new(ifa->ra->p.pool); + sock *sk = sk_new(ifa->pool); sk->type = SK_IP; sk->dport = ICMPV6_PROTO; sk->saddr = ifa->addr->ip; + sk->vrf = ifa->ra->p.vrf; sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */ sk->rx_hook = radv_rx_hook;