From 2a95e63343a94243745e5d7000bb3e0cb61a4a0f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 31 Aug 2017 15:40:23 +0200 Subject: RAdv: Support for more specific routes (RFC 4191) The patch implements Default Router Preferences and More-Specific Routes (RFC 4191) for RAdv protocol, allowing to announce router preference and more specific routes in router advertisements. Routes can be exported to RAdv like to regular routing protocols. Some cleanups, bugfixes and other changes done by Ondrej Zajicek. --- proto/radv/packets.c | 72 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 15 deletions(-) (limited to 'proto/radv/packets.c') diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 8a301854..fbe02060 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -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,6 +80,41 @@ 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; + struct radv_config *cf = (void *) p->p.cf; + u8 px_blocks = (rt->n.pxlen + 63) / 64; + u8 opt_len = 8 * (1 + px_blocks); + + if (*buf + opt_len > bufend) + { + log(L_WARN, "%s: Too many RA options on interface %s", + p->p.name, ifa->iface->name); + return -1; + } + + 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 = rt->preference; + + if (p->valid && (p->active || !cf->route_lifetime_sensitive) && rt->alive) + opt->lifetime = htonl(rt->lifetime_set ? rt->lifetime : cf->route_lifetime); + else + opt->lifetime = 0; + + /* Copy the relevant part of the prefix */ + ip6_addr px_addr = ip6_hton(rt->n.prefix); + memcpy(opt->prefix, &px_addr, 8 * px_blocks); + + return 0; +} + static int radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend) { @@ -252,7 +297,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 = (p->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) | @@ -292,13 +337,23 @@ 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, rt) + { + if (radv_prepare_route(ifa, (struct radv_route *) 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 radv_proto *p = ifa->ra; @@ -306,19 +361,6 @@ radv_send_ra(struct radv_iface *ifa, int shutdown) 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); } -- cgit v1.2.3 From 7c0bab3a3987b42bc699c4417c7b2e838f189158 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 6 Oct 2017 12:22:18 +0200 Subject: RAdv: Change specific route options to be per-interface And change default values of specific route options to be consistent with values of default router options. --- proto/radv/config.Y | 45 +++++++++++++++++++++--------- proto/radv/packets.c | 44 ++++++++++++++++++++++-------- proto/radv/radv.c | 77 +++++++++++++++++++++++++++++----------------------- proto/radv/radv.h | 27 +++++++++--------- 4 files changed, 121 insertions(+), 72 deletions(-) (limited to 'proto/radv/packets.c') diff --git a/proto/radv/config.Y b/proto/radv/config.Y index 2fa11ce2..84a2de0e 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -48,8 +48,6 @@ radv_proto_start: proto_start RADV init_list(&RADV_CFG->pref_list); init_list(&RADV_CFG->rdnss_list); init_list(&RADV_CFG->dnssl_list); - RADV_CFG->route_lifetime = DEFAULT_VALID_LIFETIME; - RADV_CFG->route_linger_time = DEFAULT_LINGER_TIME; }; radv_proto_item: @@ -64,15 +62,6 @@ radv_proto_item: RADV_CFG->trigger_valid = 1; } | PROPAGATE ROUTES bool { RADV_CFG->propagate_routes = $3; } - | ROUTE LIFETIME expr radv_sensitive { - RADV_CFG->route_lifetime = $3; - if ($4 != -1) RADV_CFG->route_lifetime_sensitive = $4; - } - | ROUTE LINGER TIME expr { - RADV_CFG->route_linger_time = $4; - if (($4 < 0) || ($4 > 3600)) - cf_error("Linger time must be in range 0-3600"); - } ; radv_proto_opts: @@ -96,11 +85,15 @@ radv_iface_start: RADV_IFACE->min_ra_int = -1; /* undefined */ RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT; RADV_IFACE->min_delay = DEFAULT_MIN_DELAY; + RADV_IFACE->prefix_linger_time = -1; + RADV_IFACE->route_linger_time = -1; RADV_IFACE->current_hop_limit = DEFAULT_CURRENT_HOP_LIMIT; - RADV_IFACE->linger_time = DEFAULT_LINGER_TIME; RADV_IFACE->default_lifetime = -1; RADV_IFACE->default_lifetime_sensitive = 1; RADV_IFACE->default_preference = RA_PREF_MEDIUM; + RADV_IFACE->route_lifetime = -1; + RADV_IFACE->route_lifetime_sensitive = 0; + RADV_IFACE->route_preference = RA_PREF_MEDIUM; }; radv_iface_item: @@ -112,14 +105,20 @@ radv_iface_item: | LINK MTU expr { RADV_IFACE->link_mtu = $3; if ($3 < 0) cf_error("Link MTU must be 0 or positive"); } | REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if (($3 < 0) || ($3 > 3600000)) cf_error("Reachable time must be in range 0-3600000"); } | RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; if ($3 < 0) cf_error("Retrans timer must be 0 or positive"); } - | LINGER TIME expr { RADV_IFACE->linger_time = $3; if (($3 < 0) || ($3 > 3600)) cf_error("Linger time must be in range 0-3600"); } | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255)) cf_error("Current hop limit must be in range 0-255"); } | DEFAULT LIFETIME expr radv_sensitive { RADV_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); if ($4 != -1) RADV_IFACE->default_lifetime_sensitive = $4; } + | ROUTE LIFETIME expr radv_sensitive { + RADV_IFACE->route_lifetime = $3; + if ($4 != -1) RADV_IFACE->route_lifetime_sensitive = $4; + } | DEFAULT PREFERENCE radv_preference { RADV_IFACE->default_preference = $3; } + | ROUTE PREFERENCE radv_preference { RADV_IFACE->route_preference = $3; } + | PREFIX LINGER TIME expr { RADV_IFACE->prefix_linger_time = $4; } + | ROUTE LINGER TIME expr { RADV_IFACE->route_linger_time = $4; } | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); } @@ -142,12 +141,32 @@ radv_iface_finish: if (ic->default_lifetime == (u32) -1) ic->default_lifetime = 3 * ic->max_ra_int; + if (ic->route_lifetime == (u32) -1) + ic->route_lifetime = 3 * ic->max_ra_int; + + if (ic->prefix_linger_time == (u32) -1) + ic->prefix_linger_time = 3 * ic->max_ra_int; + + if (ic->route_linger_time == (u32) -1) + ic->route_linger_time = 3 * ic->max_ra_int; + if ((ic->min_ra_int > 3) && (ic->min_ra_int > (ic->max_ra_int * 3 / 4))) cf_error("Min RA interval must be at most 3/4 * Max RA interval %d %d", ic->min_ra_int, ic->max_ra_int); if ((ic->default_lifetime > 0) && (ic->default_lifetime < ic->max_ra_int)) cf_error("Default lifetime must be either 0 or at least Max RA interval"); + + if ((ic->route_lifetime > 0) && (ic->route_lifetime < ic->max_ra_int)) + cf_error("Route lifetime must be either 0 or at least Max RA interval"); + + if ((ic->prefix_linger_time > 0) && (ic->prefix_linger_time < ic->max_ra_int)) + cf_error("Prefix linger time must be either 0 or at least Max RA interval"); + + if ((ic->route_linger_time > 0) && (ic->route_linger_time < ic->max_ra_int)) + cf_error("Route linger time must be either 0 or at least Max RA interval"); + + RADV_CFG->max_linger_time = MAX_(RADV_CFG->max_linger_time, ic->route_linger_time); }; diff --git a/proto/radv/packets.c b/proto/radv/packets.c index fbe02060..7d54a827 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -85,7 +85,6 @@ radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, char **buf, char *bufend) { struct radv_proto *p = ifa->ra; - struct radv_config *cf = (void *) p->p.cf; u8 px_blocks = (rt->n.pxlen + 63) / 64; u8 opt_len = 8 * (1 + px_blocks); @@ -96,22 +95,26 @@ radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, 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); + 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 = rt->preference; - - if (p->valid && (p->active || !cf->route_lifetime_sensitive) && rt->alive) - opt->lifetime = htonl(rt->lifetime_set ? rt->lifetime : cf->route_lifetime); - else - opt->lifetime = 0; + opt->flags = preference; + opt->lifetime = valid ? htonl(lifetime) : 0; /* Copy the relevant part of the prefix */ ip6_addr px_addr = ip6_hton(rt->n.prefix); memcpy(opt->prefix, &px_addr, 8 * px_blocks); + /* Keeping track of first linger timeout */ + if (!rt->valid) + ifa->valid_time = MIN(ifa->valid_time, rt->changed + ifa->cf->route_linger_time); + return 0; } @@ -278,6 +281,10 @@ radv_prepare_prefix(struct radv_iface *ifa, struct radv_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; } @@ -316,10 +323,17 @@ radv_prepare_ra(struct radv_iface *ifa) buf += sizeof (*om); } - struct radv_prefix *prefix; - WALK_LIST(prefix, ifa->prefixes) + /* Keeping track of first linger timeout */ + ifa->valid_time = TIME_INFINITY; + + struct radv_prefix *px; + WALK_LIST(px, ifa->prefixes) { - if (radv_prepare_prefix(ifa, prefix, &buf, bufend) < 0) + /* Skip invalid prefixes that are past linger timeout but still not pruned */ + if (!px->valid && (px->changed + ic->prefix_linger_time <= now)) + continue; + + if (radv_prepare_prefix(ifa, px, &buf, bufend) < 0) goto done; } @@ -339,9 +353,15 @@ radv_prepare_ra(struct radv_iface *ifa) if (p->fib_up) { - FIB_WALK(&p->routes, rt) + FIB_WALK(&p->routes, n) { - if (radv_prepare_route(ifa, (struct radv_route *) rt, &buf, bufend) < 0) + 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; diff --git a/proto/radv/radv.c b/proto/radv/radv.c index d799dab5..d53b65e3 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -55,6 +55,9 @@ radv_timer(timer *tm) RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name); + if (ifa->valid_time <= now) + radv_invalidate(ifa); + if (ifa->prune_time <= now) radv_prune_prefixes(ifa); @@ -117,7 +120,6 @@ static void radv_prepare_prefixes(struct radv_iface *ifa) { struct radv_proto *p = ifa->ra; - struct radv_iface_config *cf = ifa->cf; struct radv_prefix *pfx, *next; /* First mark all the prefixes as unused */ @@ -157,20 +159,21 @@ radv_prepare_prefixes(struct radv_iface *ifa) * Update the information (it may have changed, or even bring a prefix back * to life). */ - existing->alive = 1; + existing->valid = 1; + existing->changed = now; existing->mark = 1; existing->cf = pc; } WALK_LIST_DELSAFE(pfx, next, ifa->prefixes) { - if (pfx->alive && !pfx->mark) + if (pfx->valid && !pfx->mark) { RADV_TRACE(D_EVENTS, "Invalidating prefix %I/$d on %s", pfx->prefix, pfx->len, ifa->iface->name); - pfx->alive = 0; - pfx->expires = now + cf->linger_time; + pfx->valid = 0; + pfx->changed = now; pfx->cf = &dead_prefix; } } @@ -181,30 +184,28 @@ radv_prune_prefixes(struct radv_iface *ifa) { struct radv_proto *p = ifa->ra; bird_clock_t next = TIME_INFINITY; - int changed = 0; + bird_clock_t expires = 0; struct radv_prefix *px, *pxn; WALK_LIST_DELSAFE(px, pxn, ifa->prefixes) { - if (!px->alive) + if (!px->valid) { - if (px->expires <= now) + expires = px->changed + ifa->cf->prefix_linger_time; + + if (expires <= now) { RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s", px->prefix, px->len, ifa->iface->name); rem_node(NODE px); mb_free(px); - changed = 1; } else - next = MIN(next, px->expires); + next = MIN(next, expires); } } - if (changed) - radv_invalidate(ifa); - ifa->prune_time = next; } @@ -424,6 +425,7 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U struct radv_proto *p = (struct radv_proto *) P; struct radv_config *cf = (struct radv_config *) (P->cf); struct radv_route *rt; + eattr *ea; if (radv_net_match_trigger(cf, n)) { @@ -455,9 +457,14 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U if (new) { /* Update */ - uint preference = ea_get_int(attrs, EA_RA_PREFERENCE, RA_PREF_MEDIUM); - uint lifetime = ea_get_int(attrs, EA_RA_LIFETIME, 0); - uint lifetime_set = !!ea_find(attrs, EA_RA_LIFETIME); + + ea = ea_find(attrs, EA_RA_PREFERENCE); + uint preference = ea ? ea->u.data : RA_PREF_MEDIUM; + uint preference_set = !!ea; + + ea = ea_find(attrs, EA_RA_LIFETIME); + uint lifetime = ea ? ea->u.data : 0; + uint lifetime_set = !!ea; if ((preference != RA_PREF_LOW) && (preference != RA_PREF_MEDIUM) && @@ -466,6 +473,7 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U log(L_WARN "%s: Invalid ra_preference value %u on route %I/%d", p->p.name, preference, n->n.prefix, n->n.pxlen); preference = RA_PREF_MEDIUM; + preference_set = 1; lifetime = 0; lifetime_set = 1; } @@ -473,8 +481,9 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U rt = fib_get(&p->routes, &n->n.prefix, n->n.pxlen); /* Ignore update if nothing changed */ - if (rt->alive && + if (rt->valid && (rt->preference == preference) && + (rt->preference_set == preference_set) && (rt->lifetime == lifetime) && (rt->lifetime_set == lifetime_set)) return; @@ -482,8 +491,10 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U if (p->routes.entries == 18) log(L_WARN "%s: More than 17 routes exported to RAdv", p->p.name); - rt->alive = 1; + rt->valid = 1; + rt->changed = now; rt->preference = preference; + rt->preference_set = preference_set; rt->lifetime = lifetime; rt->lifetime_set = lifetime_set; } @@ -492,13 +503,16 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U /* Withdraw */ rt = fib_find(&p->routes, &n->n.prefix, n->n.pxlen); - if (!rt || !rt->alive) + if (!rt || !rt->valid) return; /* Invalidate the route */ - rt->alive = 0; - rt->expires = now + cf->route_linger_time; - p->prune_time = MIN(p->prune_time, rt->expires); + rt->valid = 0; + rt->changed = now; + + /* Invalidated route will be pruned eventually */ + bird_clock_t expires = rt->changed + cf->max_linger_time; + p->prune_time = MIN(p->prune_time, expires); } radv_iface_notify_all(p, RA_EV_CHANGE); @@ -511,8 +525,9 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U static void radv_prune_routes(struct radv_proto *p) { + struct radv_config *cf = (struct radv_config *) (p->p.cf); bird_clock_t next = TIME_INFINITY; - int changed = 0; + bird_clock_t expires = 0; /* Should not happen */ if (!p->fib_up) @@ -526,29 +541,23 @@ again: { struct radv_route *rt = (void *) node; - if (!rt->alive) + if (!rt->valid) { + expires = rt->changed + cf->max_linger_time; + /* Delete expired nodes */ - if (rt->expires <= now) + if (expires <= now) { FIB_ITERATE_PUT(&fit, node); fib_delete(&p->routes, node); - changed = 1; goto again; } else - next = MIN(next, rt->expires); + next = MIN(next, expires); } } FIB_ITERATE_END(node); - if (changed) - { - struct radv_iface *ifa; - WALK_LIST(ifa, p->iface_list) - radv_invalidate(ifa); - } - p->prune_time = next; } diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 3bc18249..ab081397 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -35,7 +35,6 @@ #define DEFAULT_MAX_RA_INT 600 #define DEFAULT_MIN_DELAY 3 #define DEFAULT_CURRENT_HOP_LIMIT 64 -#define DEFAULT_LINGER_TIME 300 #define DEFAULT_VALID_LIFETIME 86400 #define DEFAULT_PREFERRED_LIFETIME 14400 @@ -55,9 +54,7 @@ struct radv_config u8 trigger_pxlen; /* Pxlen of a trigger route, if defined */ u8 trigger_valid; /* Whether a trigger route is defined */ u8 propagate_routes; /* Do we propagate more specific routes (RFC 4191)? */ - u32 route_lifetime; /* Lifetime for the RFC 4191 routes */ - u32 route_lifetime_sensitive; /* Whether route_lifetime depends on trigger */ - u32 route_linger_time; /* For how long we advertise dead routes with lifetime = 0 */ + u32 max_linger_time; /* Maximum of interface route_linger_time */ }; struct radv_iface_config @@ -71,8 +68,8 @@ struct radv_iface_config u32 max_ra_int; u32 min_delay; - u32 linger_time; /* How long a dead prefix should still be advertised with 0 - lifetime */ + u32 prefix_linger_time; /* How long we advertise dead prefixes with lifetime 0 */ + u32 route_linger_time; /* How long we advertise dead routes with lifetime 0 */ u8 rdnss_local; /* Global list is not used for RDNSS */ u8 dnssl_local; /* Global list is not used for DNSSL */ @@ -84,8 +81,11 @@ struct radv_iface_config u32 retrans_timer; u32 current_hop_limit; u32 default_lifetime; + u32 route_lifetime; /* Lifetime for the RFC 4191 routes */ u8 default_lifetime_sensitive; /* Whether default_lifetime depends on trigger */ - u8 default_preference; /* Default Router Preference (RFC 4191) */ + u8 route_lifetime_sensitive; /* Whether route_lifetime depends on trigger */ + u8 default_preference; /* Default Router Preference (RFC 4191) */ + u8 route_preference; /* Specific Route Preference (RFC 4191) */ }; struct radv_prefix_config @@ -132,10 +132,11 @@ struct radv_route { struct fib_node n; u32 lifetime; /* Lifetime from an attribute */ - u8 lifetime_set; /* Is the lifetime set by an attribute? */ + u8 lifetime_set; /* Whether lifetime is defined */ u8 preference; /* Preference of the route, RA_PREF_* */ - u8 alive; - bird_clock_t expires; /* Time to remove when !alive */ + u8 preference_set; /* Whether preference is defined */ + u8 valid; /* Whethe route is valid or withdrawn */ + bird_clock_t changed; /* Last time when the route changed */ }; struct radv_proto @@ -154,11 +155,10 @@ struct radv_prefix /* One prefix we advertise */ node n; ip_addr prefix; u8 len; - u8 alive; /* Is the prefix alive? If not, we advertise it + u8 valid; /* Is the prefix valid? If not, we advertise it with 0 lifetime, so clients stop using it */ u8 mark; /* A temporary mark for processing */ - bird_clock_t expires; /* The time when we drop this prefix from - advertising. It is valid only if !alive. */ + bird_clock_t changed; /* Last time when the prefix changed */ struct radv_prefix_config *cf; /* The config tied to this prefix */ }; @@ -172,6 +172,7 @@ struct radv_iface struct pool *pool; /* A pool for interface-specific things */ list prefixes; /* The prefixes we advertise (struct radv_prefix) */ bird_clock_t prune_time; /* Next time of prefix list pruning */ + bird_clock_t valid_time; /* Cached packet is valid until first linger timeout */ timer *timer; struct object_lock *lock; -- cgit v1.2.3