diff --git a/doc/bird.sgml b/doc/bird.sgml index b0d4e6a1..762834e3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -691,8 +691,8 @@ This argument can be omitted if there exists only a single instance.

You can also select just routes added by a specific protocol. protocol . -

If BIRD is configured to keep filtered routes (see If BIRD is configured to keep filtered routes (see The dnssl that just specifies one DNS search domain. + +

Interface specific options: @@ -2525,11 +2545,12 @@ interface definitions, prefix definitions and DNS definitions: This option specifies which value of Hop Limit should be used by hosts. Valid values are 0-255, 0 means unspecified. Default: 64 - default lifetime + default lifetime This option specifies the time (in seconds) how long (after the receipt of RA) hosts may use the router as a default - router. 0 means do not use as a default router. Default: 3 * - . + Default: 3 * rdnss local Use only local (interface-specific) RDNSS definitions for this @@ -2546,6 +2567,13 @@ interface definitions, prefix definitions and DNS definitions:

Prefix specific options: + skip + This option allows to specify that given prefix should not be + advertised. This is useful for making exceptions from a + default policy of advertising all prefixes. Note that for + withdrawing an already advertised prefix it is more useful to + advertise it with zero valid lifetime. Default: no + onlink This option specifies whether hosts may use the advertised prefix for onlink determination. Default: yes @@ -2554,18 +2582,20 @@ interface definitions, prefix definitions and DNS definitions: This option specifies whether hosts may use the advertised prefix for stateless autoconfiguration. Default: yes - valid lifetime + valid lifetime This option specifies the time (in seconds) how long (after the receipt of RA) the prefix information is valid, i.e., autoconfigured IP addresses can be assigned and hosts with that IP addresses are considered directly reachable. 0 means - the prefix is no longer valid. Default: 86400 (1 day) + the prefix is no longer valid. For . Default: 86400 (1 day), preferred lifetime + preferred lifetime This option specifies the time (in seconds) how long (after the receipt of RA) IP addresses generated from the prefix - using stateless autoconfiguration remain preferred. Default: - 14400 (4 hours) + using stateless autoconfiguration remain preferred. For + . + Default: 14400 (4 hours), diff --git a/filter/filter.c b/filter/filter.c index 4ce1dd50..65809a9e 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1429,6 +1429,12 @@ i_same(struct f_inst *f1, struct f_inst *f2) int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags) { + if (filter == FILTER_ACCEPT) + return F_ACCEPT; + + if (filter == FILTER_REJECT) + return F_REJECT; + int rte_cow = ((*rte)->flags & REF_COW); DBG( "Running filter `%s'...", filter->name ); diff --git a/nest/route.h b/nest/route.h index 4a0d3ddf..f515b6e6 100644 --- a/nest/route.h +++ b/nest/route.h @@ -277,6 +277,12 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); #define RA_ACCEPTED 2 /* Announcement of first accepted route */ #define RA_ANY 3 /* Announcement of any route change */ +/* Return value of import_control() callback */ +#define RIC_ACCEPT 1 /* Accepted by protocol */ +#define RIC_PROCESS 0 /* Process it through import filter */ +#define RIC_REJECT -1 /* Rejected by protocol */ +#define RIC_DROP -2 /* Silently dropped by protocol */ + struct config; void rt_init(void); @@ -292,6 +298,7 @@ rte *rte_get_temp(struct rta *); void rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src); static inline void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new) { rte_update2(p->main_ahook, net, new, src); } void rte_discard(rtable *tab, rte *old); +int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter); void rte_dump(rte *); void rte_free(rte *); rte *rte_do_cow(rte *); diff --git a/nest/rt-table.c b/nest/rt-table.c index 9677116b..db3a413d 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -216,7 +216,8 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, goto reject; stats->exp_updates_rejected++; - rte_trace_out(D_FILTERS, p, rt, "rejected by protocol"); + if (v == RIC_REJECT) + rte_trace_out(D_FILTERS, p, rt, "rejected by protocol"); goto reject; } if (v > 0) @@ -1050,6 +1051,34 @@ rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during gar rte_update_unlock(); } +/* Check rtable for best route to given net whether it would be exported do p */ +int +rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter) +{ + net *n = net_find(t, prefix, pxlen); + rte *rt = n ? n->routes : NULL; + + if (!rte_is_valid(rt)) + return 0; + + rte_update_lock(); + + /* Rest is stripped down export_filter() */ + struct proto *src = rt->attrs->proto; + ea_list *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL; + int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0; + if (v == RIC_PROCESS) + v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); + + /* Discard temporary rte */ + if (rt != n->routes) + rte_free(rt); + + rte_update_unlock(); + + return v > 0; +} + /** * rte_dump - dump a route * @e: &rte to be dumped @@ -2162,7 +2191,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) ee = e; rte_update_lock(); /* We use the update buffer for filtering */ tmpa = p0->make_tmp_attrs ? p0->make_tmp_attrs(e, rte_update_pool) : NULL; - ok = (d->filter == FILTER_ACCEPT || f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); + ok = f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT; if (p2 && p2 != p0) ok = 0; if (ok && d->export_mode) { @@ -2176,8 +2205,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) 'configure soft' command may change the export filter and do not update routes */ - if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) || - (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))) + if ((a = proto_find_announce_hook(p1, d->table)) && + (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)) ok = 0; } } diff --git a/proto/radv/config.Y b/proto/radv/config.Y index abccd2c7..fbec5a0a 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -30,9 +30,9 @@ CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, - LOCAL) + LOCAL, TRIGGER, SENSITIVE) -%type radv_mult +%type radv_mult radv_sensitive CF_GRAMMAR @@ -53,6 +53,11 @@ radv_proto_item: | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); } + | TRIGGER prefix { + RADV_CFG->trigger_prefix = $2.addr; + RADV_CFG->trigger_pxlen = $2.len; + RADV_CFG->trigger_valid = 1; + } ; radv_proto_opts: @@ -78,6 +83,7 @@ radv_iface_start: RADV_IFACE->min_delay = DEFAULT_MIN_DELAY; RADV_IFACE->current_hop_limit = DEFAULT_CURRENT_HOP_LIMIT; RADV_IFACE->default_lifetime = -1; + RADV_IFACE->default_lifetime_sensitive = 1; }; radv_iface_item: @@ -90,7 +96,11 @@ radv_iface_item: | 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"); } | 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_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); } + | 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; + } | 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); } @@ -147,14 +157,25 @@ radv_prefix_item: SKIP bool { RADV_PREFIX->skip = $2; } | ONLINK bool { RADV_PREFIX->onlink = $2; } | AUTONOMOUS bool { RADV_PREFIX->autonomous = $2; } - | VALID LIFETIME expr { RADV_PREFIX->valid_lifetime = $3; if ($3 < 0) cf_error("Valid lifetime must be 0 or positive"); } - | PREFERRED LIFETIME expr { RADV_PREFIX->preferred_lifetime = $3; if ($3 < 0) cf_error("Preferred lifetime must be 0 or positive"); } + | VALID LIFETIME expr radv_sensitive { + RADV_PREFIX->valid_lifetime = $3; + if ($3 < 0) cf_error("Valid lifetime must be 0 or positive"); + if ($4 != -1) RADV_PREFIX->valid_lifetime_sensitive = $4; + } + | PREFERRED LIFETIME expr radv_sensitive { + RADV_PREFIX->preferred_lifetime = $3; + if ($3 < 0) cf_error("Preferred lifetime must be 0 or positive"); + if ($4 != -1) RADV_PREFIX->preferred_lifetime_sensitive = $4; + } ; radv_prefix_finish: { if (RADV_PREFIX->preferred_lifetime > RADV_PREFIX->valid_lifetime) cf_error("Preferred lifetime must be at most Valid lifetime"); + + if (RADV_PREFIX->valid_lifetime_sensitive > RADV_PREFIX->preferred_lifetime_sensitive) + cf_error("Valid lifetime sensitive requires that Preferred lifetime is sensitive too"); }; radv_prefix_opts: @@ -268,6 +289,11 @@ radv_mult: | MULT expr { $$ = 0; radv_mult_val = $2; if (($2 < 1) || ($2 > 254)) cf_error("Multiplier must be in range 1-254"); } ; +radv_sensitive: + /* empty */ { $$ = -1 } + | SENSITIVE bool { $$ = $2 } + ; + CF_CODE CF_END diff --git a/proto/radv/packets.c b/proto/radv/packets.c index b83fb412..efaa69bb 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -240,6 +240,7 @@ 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_iface_config *ic = ifa->cf; char *buf = ifa->sk->tbuf; char *bufstart = buf; @@ -249,21 +250,22 @@ radv_prepare_ra(struct radv_iface *ifa) pkt->type = ICMPV6_RA; pkt->code = 0; pkt->checksum = 0; - pkt->current_hop_limit = ifa->cf->current_hop_limit; - pkt->flags = (ifa->cf->managed ? OPT_RA_MANAGED : 0) | - (ifa->cf->other_config ? OPT_RA_OTHER_CFG : 0); - pkt->router_lifetime = htons(ifa->cf->default_lifetime); - pkt->reachable_time = htonl(ifa->cf->reachable_time); - pkt->retrans_timer = htonl(ifa->cf->retrans_timer); + pkt->current_hop_limit = ic->current_hop_limit; + pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) | + (ic->other_config ? OPT_RA_OTHER_CFG : 0); + pkt->router_lifetime = (ra->active || !ic->default_lifetime_sensitive) ? + htons(ic->default_lifetime) : 0; + pkt->reachable_time = htonl(ic->reachable_time); + pkt->retrans_timer = htonl(ic->retrans_timer); buf += sizeof(*pkt); - if (ifa->cf->link_mtu) + if (ic->link_mtu) { struct radv_opt_mtu *om = (void *) buf; om->type = OPT_MTU; om->length = 1; om->reserved = 0; - om->mtu = htonl(ifa->cf->link_mtu); + om->mtu = htonl(ic->link_mtu); buf += sizeof (*om); } @@ -288,26 +290,28 @@ radv_prepare_ra(struct radv_iface *ifa) op->pxlen = addr->pxlen; op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); - op->valid_lifetime = htonl(pc->valid_lifetime); - op->preferred_lifetime = htonl(pc->preferred_lifetime); + 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 (! ifa->cf->rdnss_local) + if (! ic->rdnss_local) if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0) goto done; - if (radv_prepare_rdnss(ifa, &ifa->cf->rdnss_list, &buf, bufend) < 0) + if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0) goto done; - if (! ifa->cf->dnssl_local) + if (! ic->dnssl_local) if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0) goto done; - if (radv_prepare_dnssl(ifa, &ifa->cf->dnssl_list, &buf, bufend) < 0) + if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) goto done; done: diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 5e7296a3..a6b9b16c 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -30,6 +30,13 @@ * by RA_EV_* codes), and radv_timer(), which triggers sending RAs and * computes the next timeout. * + * The RAdv protocol could receive routes (through + * radv_import_control() and radv_rt_notify()), but only the + * configured trigger route is tracked (in &active var). When a radv + * protocol is reconfigured, the connected routing table is examined + * (in radv_check_active()) to have proper &active value in case of + * the specified trigger prefix was changed. + * * Supported standards: * - RFC 4861 - main RA standard * - RFC 6106 - DNS extensions (RDDNS, DNSSL) @@ -93,6 +100,16 @@ radv_iface_notify(struct radv_iface *ifa, int event) tm_start(ifa->timer, after); } +static void +radv_iface_notify_all(struct proto_radv *ra, int event) +{ + struct radv_iface *ifa; + + WALK_LIST(ifa, ra->iface_list) + radv_iface_notify(ifa, event); +} + + static struct radv_iface * radv_iface_find(struct proto_radv *ra, struct iface *what) { @@ -238,11 +255,68 @@ radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) radv_iface_notify(ifa, RA_EV_CHANGE); } +static inline int radv_net_match_trigger(struct radv_config *cf, net *n) +{ + return cf->trigger_valid && + (n->n.pxlen == cf->trigger_pxlen) && + ipa_equal(n->n.prefix, cf->trigger_prefix); +} + +int +radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED) +{ + // struct proto_radv *ra = (struct proto_radv *) p; + struct radv_config *cf = (struct radv_config *) (p->cf); + + if (radv_net_match_trigger(cf, (*new)->net)) + return RIC_PROCESS; + + return RIC_DROP; +} + +static void +radv_rt_notify(struct proto *p, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) +{ + struct proto_radv *ra = (struct proto_radv *) p; + struct radv_config *cf = (struct radv_config *) (p->cf); + + if (radv_net_match_trigger(cf, n)) + { + u8 old_active = ra->active; + ra->active = !!new; + + if (ra->active == old_active) + return; + + if (ra->active) + RADV_TRACE(D_EVENTS, "Triggered"); + else + RADV_TRACE(D_EVENTS, "Suppressed"); + + radv_iface_notify_all(ra, RA_EV_CHANGE); + } +} + +static int +radv_check_active(struct proto_radv *ra) +{ + struct radv_config *cf = (struct radv_config *) (ra->p.cf); + + if (! cf->trigger_valid) + return 1; + + return rt_examine(ra->p.table, cf->trigger_prefix, cf->trigger_pxlen, + &(ra->p), ra->p.cf->out_filter); +} + static struct proto * radv_init(struct proto_config *c) { struct proto *p = proto_new(c, sizeof(struct proto_radv)); + p->accept_ra_types = RA_OPTIMAL; + p->import_control = radv_import_control; + p->rt_notify = radv_rt_notify; p->if_notify = radv_if_notify; p->ifa_notify = radv_ifa_notify; return p; @@ -252,9 +326,10 @@ static int radv_start(struct proto *p) { struct proto_radv *ra = (struct proto_radv *) p; - // struct radv_config *cf = (struct radv_config *) (p->cf); + struct radv_config *cf = (struct radv_config *) (p->cf); init_list(&(ra->iface_list)); + ra->active = !cf->trigger_valid; return PS_UP; } @@ -293,6 +368,9 @@ radv_reconfigure(struct proto *p, struct proto_config *c) * causing nodes to temporary remove their default routes. */ + p->cf = c; /* radv_check_active() requires proper p->cf */ + ra->active = radv_check_active(ra); + struct iface *iface; WALK_LIST(iface, iface_list) { @@ -335,6 +413,14 @@ radv_copy_config(struct proto_config *dest, struct proto_config *src) cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config)); } +static void +radv_get_status(struct proto *p, byte *buf) +{ + struct proto_radv *ra = (struct proto_radv *) p; + + if (!ra->active) + strcpy(buf, "Suppressed"); +} struct protocol proto_radv = { .name = "RAdv", @@ -343,5 +429,6 @@ struct protocol proto_radv = { .start = radv_start, .shutdown = radv_shutdown, .reconfigure = radv_reconfigure, - .copy_config = radv_copy_config + .copy_config = radv_copy_config, + .get_status = radv_get_status }; diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 76c4bd50..8e4a1d43 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -49,6 +49,10 @@ struct radv_config list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */ list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */ list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */ + + ip_addr trigger_prefix; /* Prefix of a trigger route, if defined */ + u8 trigger_pxlen; /* Pxlen of a trigger route, if defined */ + u8 trigger_valid; /* Whether a trigger route is defined */ }; struct radv_iface_config @@ -72,6 +76,7 @@ struct radv_iface_config u32 retrans_timer; u32 current_hop_limit; u32 default_lifetime; + u8 default_lifetime_sensitive; /* Whether default_lifetime depends on trigger */ }; struct radv_prefix_config @@ -85,6 +90,8 @@ struct radv_prefix_config u8 autonomous; u32 valid_lifetime; u32 preferred_lifetime; + u8 valid_lifetime_sensitive; /* Whether valid_lifetime depends on trigger */ + u8 preferred_lifetime_sensitive; /* Whether preferred_lifetime depends on trigger */ }; struct radv_rdnss_config @@ -110,6 +117,7 @@ struct proto_radv { struct proto p; list iface_list; /* List of active ifaces */ + u8 active; /* Whether radv is active w.r.t. triggers */ }; struct radv_iface