diff options
Diffstat (limited to 'proto/radv')
-rw-r--r-- | proto/radv/config.Y | 4 | ||||
-rw-r--r-- | proto/radv/packets.c | 97 | ||||
-rw-r--r-- | proto/radv/radv.c | 152 | ||||
-rw-r--r-- | proto/radv/radv.h | 25 |
4 files changed, 208 insertions, 70 deletions
diff --git a/proto/radv/config.Y b/proto/radv/config.Y index da300667..0ff84aeb 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -27,7 +27,7 @@ static u8 radv_mult_val; /* Used by radv_mult for second return value */ CF_DECLS CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, - MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS, + MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH) @@ -82,6 +82,7 @@ radv_iface_start: RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT; RADV_IFACE->min_delay = DEFAULT_MIN_DELAY; 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; @@ -96,6 +97,7 @@ 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; diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 1c9837a4..19d71f97 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -70,36 +70,6 @@ struct radv_opt_dnssl char domain[]; }; - -static struct radv_prefix_config default_prefix = { - .onlink = 1, - .autonomous = 1, - .valid_lifetime = DEFAULT_VALID_LIFETIME, - .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME -}; - - -static struct radv_prefix_config * -radv_prefix_match(struct radv_iface *ifa, struct ifa *a) -{ - struct radv_proto *p = ifa->ra; - struct radv_config *cf = (struct radv_config *) (p->p.cf); - struct radv_prefix_config *pc; - - if (a->scope <= SCOPE_LINK) - return NULL; - - WALK_LIST(pc, ifa->cf->pref_list) - if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) - return pc; - - 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; -} - static int radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend) { @@ -123,7 +93,7 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *b 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 +106,7 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *b rcf = NODE_NEXT(rcf); } - + op->length = 1+2*i; *buf += 8 * op->length; } @@ -235,6 +205,37 @@ radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *b 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); + + return 0; +} + static void radv_prepare_ra(struct radv_iface *ifa) { @@ -270,35 +271,11 @@ radv_prepare_ra(struct radv_iface *ifa) buf += sizeof (*om); } - struct ifa *addr; - WALK_LIST(addr, ifa->iface->addrs) + struct radv_prefix *prefix; + WALK_LIST(prefix, ifa->prefixes) { - struct radv_prefix_config *pc; - pc = radv_prefix_match(ifa, addr); - - if (!pc || pc->skip) - continue; - - if (buf + sizeof(struct radv_opt_prefix) > bufend) - { - log(L_WARN "%s: Too many prefixes on interface %s", p->p.name, ifa->iface->name); + if (radv_prepare_prefix(ifa, prefix, &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 = (p->active || !pc->valid_lifetime_sensitive) ? - htonl(pc->valid_lifetime) : 0; - op->preferred_lifetime = (p->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) @@ -407,7 +384,7 @@ 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; diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 91f9853d..227c8ef6 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -51,6 +51,16 @@ radv_timer(timer *tm) RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name); + /* + * If some dead prefixes expired, regenerate the prefix list and the packet. + * We do so by pretending there was a change on the interface. + * + * This sets the timer, but we replace it just at the end of this function + * (replacing a timer is fine). + */ + if (ifa->prefix_expires && (ifa->prefix_expires <= now)) + radv_iface_notify(ifa, RA_EV_GC); + radv_send_ra(ifa, 0); /* Update timer */ @@ -67,7 +77,134 @@ radv_timer(timer *tm) tm_start(ifa->timer, after); } -static char* ev_name[] = { NULL, "Init", "Change", "RS" }; +static struct radv_prefix_config default_prefix = { + .onlink = 1, + .autonomous = 1, + .valid_lifetime = DEFAULT_VALID_LIFETIME, + .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME +}; + +static struct radv_prefix_config dead_prefix = { +}; + +/* Find a corresponding config for the given prefix */ +static struct radv_prefix_config * +radv_prefix_match(struct radv_iface *ifa, struct ifa *a) +{ + struct radv_proto *p = ifa->ra; + struct radv_config *cf = (struct radv_config *) (p->p.cf); + struct radv_prefix_config *pc; + + if (a->scope <= SCOPE_LINK) + return NULL; + + WALK_LIST(pc, ifa->cf->pref_list) + if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) + return pc; + + 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; +} + +/* + * Go through the list of prefixes, compare them with configs and decide if we + * want them or not. + */ +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; + + /* First mark all the prefixes as unused */ + WALK_LIST(pfx, ifa->prefixes) + pfx->mark = 0; + + /* Find all the prefixes we want to use and make sure they are in the list. */ + struct ifa *addr; + WALK_LIST(addr, ifa->iface->addrs) + { + struct radv_prefix_config *pc = radv_prefix_match(ifa, addr); + + if (!pc || pc->skip) + continue; + + /* Do we have it already? */ + struct radv_prefix *existing = NULL; + WALK_LIST(pfx, ifa->prefixes) + if ((pfx->len == addr->pxlen) && ipa_equal(pfx->prefix, addr->prefix)) + { + existing = pfx; + break; + } + + if (!existing) + { + RADV_TRACE(D_EVENTS, "Adding new prefix %I/%d on %s", + addr->prefix, addr->pxlen, ifa->iface->name); + + existing = mb_allocz(ifa->pool, sizeof *existing); + existing->prefix = addr->prefix; + existing->len = addr->pxlen; + add_tail(&ifa->prefixes, NODE existing); + } + + /* + * Update the information (it may have changed, or even bring a prefix back + * to life). + */ + existing->alive = 1; + existing->mark = 1; + existing->cf = pc; + } + + /* + * Garbage-collect the prefixes. If something isn't used, it dies (but isn't + * dropped just yet). If something is dead and rots there for long enough, + * clean it up. + */ + bird_clock_t expires = now + cf->linger_time; + bird_clock_t expires_min = 0; + struct radv_prefix *next; + WALK_LIST_DELSAFE(pfx, next, ifa->prefixes) + { + if (pfx->alive && !pfx->mark) + { + RADV_TRACE(D_EVENTS, "Marking prefix %I/$d on %s as dead", + pfx->prefix, pfx->len, ifa->iface->name); + + pfx->alive = 0; + pfx->expires = expires; + pfx->cf = &dead_prefix; + } + + if (!pfx->alive) + { + if (pfx->expires <= now) + { + RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s", + pfx->prefix, pfx->len, ifa->iface->name); + + rem_node(NODE pfx); + mb_free(pfx); + } + else + { + /* Find minimum expiration time */ + if (!expires_min || (pfx->expires < expires_min)) + expires_min = pfx->expires; + } + } + } + + ifa->prefix_expires = expires_min; +} + +static char* ev_name[] = { NULL, "Init", "Change", "RS", "Garbage collect" }; void radv_iface_notify(struct radv_iface *ifa, int event) @@ -82,6 +219,7 @@ radv_iface_notify(struct radv_iface *ifa, int event) switch (event) { case RA_EV_CHANGE: + case RA_EV_GC: ifa->plen = 0; case RA_EV_INIT: ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS; @@ -91,6 +229,8 @@ radv_iface_notify(struct radv_iface *ifa, int event) break; } + radv_prepare_prefixes(ifa); + /* Update timer */ unsigned delta = now - ifa->last; unsigned after = 0; @@ -152,15 +292,17 @@ find_lladdr(struct iface *iface) static void radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf) { - pool *pool = p->p.pool; struct radv_iface *ifa; RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name); + pool *pool = rp_new(p->p.pool, iface->name); ifa = mb_allocz(pool, sizeof(struct radv_iface)); + ifa->pool = pool; ifa->ra = p; ifa->cf = cf; ifa->iface = iface; + init_list(&ifa->prefixes); add_tail(&p->iface_list, NODE ifa); @@ -198,11 +340,7 @@ radv_iface_remove(struct radv_iface *ifa) rem_node(NODE ifa); - rfree(ifa->sk); - rfree(ifa->timer); - rfree(ifa->lock); - - mb_free(ifa); + rfree(ifa->pool); } static void diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 559a3f3f..60b9980f 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -35,6 +35,7 @@ #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 @@ -66,6 +67,9 @@ 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 */ + u8 rdnss_local; /* Global list is not used for RDNSS */ u8 dnssl_local; /* Global list is not used for DNSSL */ @@ -77,7 +81,7 @@ struct radv_iface_config u32 current_hop_limit; u32 default_lifetime; u8 default_lifetime_sensitive; /* Whether default_lifetime depends on trigger */ - u8 default_preference; /* Default Router Preference (RFC 4191) */ + u8 default_preference; /* Default Router Preference (RFC 4191) */ }; struct radv_prefix_config @@ -121,6 +125,19 @@ struct radv_proto u8 active; /* Whether radv is active w.r.t. triggers */ }; +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 + 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. */ + struct radv_prefix_config *cf; /* The config tied to this prefix */ +}; + struct radv_iface { node n; @@ -128,6 +145,9 @@ struct radv_iface struct radv_iface_config *cf; /* Related config, must be updated in reconfigure */ struct iface *iface; struct ifa *addr; /* Link-local address of iface */ + struct pool *pool; /* A pool for interface-specific things */ + list prefixes; /* The prefixes we advertise (struct radv_prefix) */ + bird_clock_t prefix_expires; /* When the soonest prefix expires (0 = none dead) */ timer *timer; struct object_lock *lock; @@ -135,12 +155,13 @@ struct radv_iface bird_clock_t last; /* Time of last sending of RA */ u16 plen; /* Length of prepared RA in tbuf, or 0 if not valid */ - byte initial; /* List of active ifaces */ + byte initial; /* How many RAs are still to be sent as initial */ }; #define RA_EV_INIT 1 /* Switch to initial mode */ #define RA_EV_CHANGE 2 /* Change of options or prefixes */ #define RA_EV_RS 3 /* Received RS */ +#define RA_EV_GC 4 /* Internal garbage collection of prefixes */ /* Default Router Preferences (RFC 4191) */ #define RA_PREF_LOW 0x18 |