diff options
Diffstat (limited to 'proto')
-rw-r--r-- | proto/radv/config.Y | 36 | ||||
-rw-r--r-- | proto/radv/packets.c | 32 | ||||
-rw-r--r-- | proto/radv/radv.c | 91 | ||||
-rw-r--r-- | proto/radv/radv.h | 8 |
4 files changed, 146 insertions, 21 deletions
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<i> radv_mult +%type<i> 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 6fdfcaa3..dd839536 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 48af8c00..f80e4530 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -52,6 +52,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 @@ -75,6 +79,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 @@ -88,6 +93,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 @@ -113,6 +120,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 |