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.
This commit is contained in:
Michal 'vorner' Vaner 2017-08-31 15:40:23 +02:00 committed by Ondrej Zajicek (work)
parent 5a8b1fb047
commit 2a95e63343
6 changed files with 357 additions and 95 deletions

View file

@ -168,6 +168,7 @@ void val_format(struct f_val v, buffer *buf);
#define T_ENUM_RTC 0x33 #define T_ENUM_RTC 0x33
#define T_ENUM_RTD 0x34 #define T_ENUM_RTD 0x34
#define T_ENUM_ROA 0x35 #define T_ENUM_ROA 0x35
#define T_ENUM_RA_PREFERENCE 0x36
/* new enums go here */ /* new enums go here */
#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ #define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */

View file

@ -430,7 +430,8 @@ typedef struct eattr {
#define EAP_OSPF 3 /* OSPF */ #define EAP_OSPF 3 /* OSPF */
#define EAP_KRT 4 /* Kernel route attributes */ #define EAP_KRT 4 /* Kernel route attributes */
#define EAP_BABEL 5 /* Babel attributes */ #define EAP_BABEL 5 /* Babel attributes */
#define EAP_MAX 6 #define EAP_RADV 6 /* Router advertisment attributes */
#define EAP_MAX 7
#define EA_CODE(proto,id) (((proto) << 8) | (id)) #define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_PROTO(ea) ((ea) >> 8) #define EA_PROTO(ea) ((ea) >> 8)

View file

@ -30,7 +30,10 @@ CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, RETRANS, MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, RETRANS,
TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH) LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE,
ROUTE, ROUTES, RA_PREFERENCE, RA_LIFETIME)
CF_ENUM(T_ENUM_RA_PREFERENCE, RA_PREF_, LOW, MEDIUM, HIGH)
%type<i> radv_mult radv_sensitive radv_preference %type<i> radv_mult radv_sensitive radv_preference
@ -45,6 +48,8 @@ radv_proto_start: proto_start RADV
init_list(&RADV_CFG->pref_list); init_list(&RADV_CFG->pref_list);
init_list(&RADV_CFG->rdnss_list); init_list(&RADV_CFG->rdnss_list);
init_list(&RADV_CFG->dnssl_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: radv_proto_item:
@ -58,6 +63,16 @@ radv_proto_item:
RADV_CFG->trigger_pxlen = $2.len; RADV_CFG->trigger_pxlen = $2.len;
RADV_CFG->trigger_valid = 1; 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: radv_proto_opts:
@ -168,12 +183,10 @@ radv_prefix_item:
| AUTONOMOUS bool { RADV_PREFIX->autonomous = $2; } | AUTONOMOUS bool { RADV_PREFIX->autonomous = $2; }
| VALID LIFETIME expr radv_sensitive { | VALID LIFETIME expr radv_sensitive {
RADV_PREFIX->valid_lifetime = $3; 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; if ($4 != -1) RADV_PREFIX->valid_lifetime_sensitive = $4;
} }
| PREFERRED LIFETIME expr radv_sensitive { | PREFERRED LIFETIME expr radv_sensitive {
RADV_PREFIX->preferred_lifetime = $3; 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; if ($4 != -1) RADV_PREFIX->preferred_lifetime_sensitive = $4;
} }
; ;
@ -303,6 +316,9 @@ radv_sensitive:
| SENSITIVE bool { $$ = $2; } | SENSITIVE bool { $$ = $2; }
; ;
CF_ADDTO(dynamic_attr, RA_PREFERENCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_RA_PREFERENCE, EA_RA_PREFERENCE); })
CF_ADDTO(dynamic_attr, RA_LIFETIME { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_RA_LIFETIME); })
CF_CODE CF_CODE
CF_END CF_END

View file

@ -26,6 +26,7 @@ struct radv_ra_packet
#define OPT_PREFIX 3 #define OPT_PREFIX 3
#define OPT_MTU 5 #define OPT_MTU 5
#define OPT_ROUTE 24
#define OPT_RDNSS 25 #define OPT_RDNSS 25
#define OPT_DNSSL 31 #define OPT_DNSSL 31
@ -52,6 +53,15 @@ struct radv_opt_mtu
u32 mtu; u32 mtu;
}; };
struct radv_opt_route {
u8 type;
u8 length;
u8 pxlen;
u8 flags;
u32 lifetime;
u8 prefix[];
};
struct radv_opt_rdnss struct radv_opt_rdnss
{ {
u8 type; u8 type;
@ -70,6 +80,41 @@ struct radv_opt_dnssl
char domain[]; 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 static int
radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend) 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->code = 0;
pkt->checksum = 0; pkt->checksum = 0;
pkt->current_hop_limit = ic->current_hop_limit; 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; htons(ic->default_lifetime) : 0;
pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) | pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) |
(ic->other_config ? OPT_RA_OTHER_CFG : 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) if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0)
goto done; 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: done:
ifa->plen = buf - bufstart; ifa->plen = buf - bufstart;
} }
void void
radv_send_ra(struct radv_iface *ifa, int shutdown) radv_send_ra(struct radv_iface *ifa)
{ {
struct radv_proto *p = ifa->ra; struct radv_proto *p = ifa->ra;
@ -306,19 +361,6 @@ radv_send_ra(struct radv_iface *ifa, int shutdown)
if (!ifa->plen) if (!ifa->plen)
radv_prepare_ra(ifa); 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); RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name);
sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0); sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0);
} }

View file

@ -12,37 +12,41 @@
/** /**
* DOC: Router Advertisements * DOC: Router Advertisements
* *
* The RAdv protocol is implemented in two files: |radv.c| containing * The RAdv protocol is implemented in two files: |radv.c| containing the
* the interface with BIRD core and the protocol logic and |packets.c| * interface with BIRD core and the protocol logic and |packets.c| handling low
* handling low level protocol stuff (RX, TX and packet formats). * level protocol stuff (RX, TX and packet formats). The protocol does not
* The protocol does not export any routes. * export any routes.
* *
* The RAdv is structured in the usual way - for each handled interface * The RAdv is structured in the usual way - for each handled interface there is
* there is a structure &radv_iface that contains a state related to * a structure &radv_iface that contains a state related to that interface
* that interface together with its resources (a socket, a timer). * together with its resources (a socket, a timer). There is also a prepared RA
* There is also a prepared RA stored in a TX buffer of the socket * stored in a TX buffer of the socket associated with an iface. These iface
* associated with an iface. These iface structures are created * structures are created and removed according to iface events from BIRD core
* and removed according to iface events from BIRD core handled by * handled by radv_if_notify() callback.
* radv_if_notify() callback.
* *
* The main logic of RAdv consists of two functions: * The main logic of RAdv consists of two functions: radv_iface_notify(), which
* radv_iface_notify(), which processes asynchronous events (specified * processes asynchronous events (specified by RA_EV_* codes), and radv_timer(),
* by RA_EV_* codes), and radv_timer(), which triggers sending RAs and * which triggers sending RAs and computes the next timeout.
* computes the next timeout.
* *
* The RAdv protocol could receive routes (through * The RAdv protocol could receive routes (through radv_import_control() and
* radv_import_control() and radv_rt_notify()), but only the * radv_rt_notify()), but only the configured trigger route is tracked (in
* configured trigger route is tracked (in &active var). When a radv * &active var). When a radv protocol is reconfigured, the connected routing
* protocol is reconfigured, the connected routing table is examined * table is examined (in radv_check_active()) to have proper &active value in
* (in radv_check_active()) to have proper &active value in case of * case of the specified trigger prefix was changed.
* the specified trigger prefix was changed.
* *
* Supported standards: * Supported standards:
* - RFC 4861 - main RA standard * - RFC 4861 - main RA standard
* - RFC 4191 - Default Router Preferences and More-Specific Routes
* - RFC 6106 - DNS extensions (RDDNS, DNSSL) * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
* - RFC 4191 (partial) - Default Router Preference
*/ */
static void radv_prune_prefixes(struct radv_iface *ifa);
static void radv_prune_routes(struct radv_proto *p);
/* Invalidate cached RA packet */
static inline void radv_invalidate(struct radv_iface *ifa)
{ ifa->plen = 0; }
static void static void
radv_timer(timer *tm) radv_timer(timer *tm)
{ {
@ -51,17 +55,13 @@ radv_timer(timer *tm)
RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name); RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
/* if (ifa->prune_time <= now)
* If some dead prefixes expired, regenerate the prefix list and the packet. radv_prune_prefixes(ifa);
* 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); if (p->prune_time <= now)
radv_prune_routes(p);
radv_send_ra(ifa);
/* Update timer */ /* Update timer */
ifa->last = now; ifa->last = now;
@ -118,7 +118,7 @@ radv_prepare_prefixes(struct radv_iface *ifa)
{ {
struct radv_proto *p = ifa->ra; struct radv_proto *p = ifa->ra;
struct radv_iface_config *cf = ifa->cf; struct radv_iface_config *cf = ifa->cf;
struct radv_prefix *pfx; struct radv_prefix *pfx, *next;
/* First mark all the prefixes as unused */ /* First mark all the prefixes as unused */
WALK_LIST(pfx, ifa->prefixes) WALK_LIST(pfx, ifa->prefixes)
@ -162,49 +162,53 @@ radv_prepare_prefixes(struct radv_iface *ifa)
existing->cf = pc; 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) WALK_LIST_DELSAFE(pfx, next, ifa->prefixes)
{ {
if (pfx->alive && !pfx->mark) if (pfx->alive && !pfx->mark)
{ {
RADV_TRACE(D_EVENTS, "Marking prefix %I/$d on %s as dead", RADV_TRACE(D_EVENTS, "Invalidating prefix %I/$d on %s",
pfx->prefix, pfx->len, ifa->iface->name); pfx->prefix, pfx->len, ifa->iface->name);
pfx->alive = 0; pfx->alive = 0;
pfx->expires = expires; pfx->expires = now + cf->linger_time;
pfx->cf = &dead_prefix; pfx->cf = &dead_prefix;
} }
}
}
if (!pfx->alive) static void
radv_prune_prefixes(struct radv_iface *ifa)
{ {
if (pfx->expires <= now) struct radv_proto *p = ifa->ra;
bird_clock_t next = TIME_INFINITY;
int changed = 0;
struct radv_prefix *px, *pxn;
WALK_LIST_DELSAFE(px, pxn, ifa->prefixes)
{
if (!px->alive)
{
if (px->expires <= now)
{ {
RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s", RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s",
pfx->prefix, pfx->len, ifa->iface->name); px->prefix, px->len, ifa->iface->name);
rem_node(NODE pfx); rem_node(NODE px);
mb_free(pfx); mb_free(px);
changed = 1;
} }
else else
{ next = MIN(next, px->expires);
/* Find minimum expiration time */
if (!expires_min || (pfx->expires < expires_min))
expires_min = pfx->expires;
}
} }
} }
ifa->prefix_expires = expires_min; if (changed)
radv_invalidate(ifa);
ifa->prune_time = next;
} }
static char* ev_name[] = { NULL, "Init", "Change", "RS", "Garbage collect" }; static char* ev_name[] = { NULL, "Init", "Change", "RS" };
void void
radv_iface_notify(struct radv_iface *ifa, int event) radv_iface_notify(struct radv_iface *ifa, int event)
@ -219,18 +223,17 @@ radv_iface_notify(struct radv_iface *ifa, int event)
switch (event) switch (event)
{ {
case RA_EV_CHANGE: case RA_EV_CHANGE:
case RA_EV_GC: radv_invalidate(ifa);
ifa->plen = 0;
case RA_EV_INIT: case RA_EV_INIT:
ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS; ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
radv_prepare_prefixes(ifa);
radv_prune_prefixes(ifa);
break; break;
case RA_EV_RS: case RA_EV_RS:
break; break;
} }
radv_prepare_prefixes(ifa);
/* Update timer */ /* Update timer */
unsigned delta = now - ifa->last; unsigned delta = now - ifa->last;
unsigned after = 0; unsigned after = 0;
@ -250,7 +253,6 @@ radv_iface_notify_all(struct radv_proto *p, int event)
radv_iface_notify(ifa, event); radv_iface_notify(ifa, event);
} }
static struct radv_iface * static struct radv_iface *
radv_iface_find(struct radv_proto *p, struct iface *what) radv_iface_find(struct radv_proto *p, struct iface *what)
{ {
@ -303,6 +305,7 @@ radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_conf
ifa->cf = cf; ifa->cf = cf;
ifa->iface = iface; ifa->iface = iface;
init_list(&ifa->prefixes); init_list(&ifa->prefixes);
ifa->prune_time = TIME_INFINITY;
add_tail(&p->iface_list, NODE ifa); add_tail(&p->iface_list, NODE ifa);
@ -409,14 +412,18 @@ radv_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct l
if (radv_net_match_trigger(cf, (*new)->net)) if (radv_net_match_trigger(cf, (*new)->net))
return RIC_PROCESS; return RIC_PROCESS;
if (cf->propagate_routes)
return RIC_PROCESS;
else
return RIC_DROP; return RIC_DROP;
} }
static void static void
radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs)
{ {
struct radv_proto *p = (struct radv_proto *) P; struct radv_proto *p = (struct radv_proto *) P;
struct radv_config *cf = (struct radv_config *) (P->cf); struct radv_config *cf = (struct radv_config *) (P->cf);
struct radv_route *rt;
if (radv_net_match_trigger(cf, n)) if (radv_net_match_trigger(cf, n))
{ {
@ -432,7 +439,117 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
RADV_TRACE(D_EVENTS, "Suppressed"); RADV_TRACE(D_EVENTS, "Suppressed");
radv_iface_notify_all(p, RA_EV_CHANGE); radv_iface_notify_all(p, RA_EV_CHANGE);
return;
} }
if (!cf->propagate_routes)
return;
/*
* Some other route we want to send (or stop sending). Update the cache,
* with marking a removed one as dead or creating a new one as needed.
*
* And yes, we exclude the trigger route on purpose.
*/
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);
if ((preference != RA_PREF_LOW) &&
(preference != RA_PREF_MEDIUM) &&
(preference != RA_PREF_HIGH))
{
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;
lifetime = 0;
lifetime_set = 1;
}
rt = fib_get(&p->routes, &n->n.prefix, n->n.pxlen);
/* Ignore update if nothing changed */
if (rt->alive &&
(rt->preference == preference) &&
(rt->lifetime == lifetime) &&
(rt->lifetime_set == lifetime_set))
return;
if (p->routes.entries == 18)
log(L_WARN "%s: More than 17 routes exported to RAdv", p->p.name);
rt->alive = 1;
rt->preference = preference;
rt->lifetime = lifetime;
rt->lifetime_set = lifetime_set;
}
else
{
/* Withdraw */
rt = fib_find(&p->routes, &n->n.prefix, n->n.pxlen);
if (!rt || !rt->alive)
return;
/* Invalidate the route */
rt->alive = 0;
rt->expires = now + cf->route_linger_time;
p->prune_time = MIN(p->prune_time, rt->expires);
}
radv_iface_notify_all(p, RA_EV_CHANGE);
}
/*
* Cleans up all the dead routes that expired and schedules itself to be run
* again if there are more routes waiting for expiration.
*/
static void
radv_prune_routes(struct radv_proto *p)
{
bird_clock_t next = TIME_INFINITY;
int changed = 0;
/* Should not happen */
if (!p->fib_up)
return;
struct fib_iterator fit;
FIB_ITERATE_INIT(&fit, &p->routes);
again:
FIB_ITERATE_START(&p->routes, &fit, node)
{
struct radv_route *rt = (void *) node;
if (!rt->alive)
{
/* Delete expired nodes */
if (rt->expires <= now)
{
FIB_ITERATE_PUT(&fit, node);
fib_delete(&p->routes, node);
changed = 1;
goto again;
}
else
next = MIN(next, rt->expires);
}
}
FIB_ITERATE_END(node);
if (changed)
{
struct radv_iface *ifa;
WALK_LIST(ifa, p->iface_list)
radv_invalidate(ifa);
}
p->prune_time = next;
} }
static int static int
@ -461,6 +578,21 @@ radv_init(struct proto_config *c)
return P; return P;
} }
static void
radv_set_fib(struct radv_proto *p, int up)
{
if (up == p->fib_up)
return;
if (up)
fib_init(&p->routes, p->p.pool, sizeof(struct radv_route), 4, NULL);
else
fib_free(&p->routes);
p->fib_up = up;
p->prune_time = TIME_INFINITY;
}
static int static int
radv_start(struct proto *P) radv_start(struct proto *P)
{ {
@ -468,8 +600,13 @@ radv_start(struct proto *P)
struct radv_config *cf = (struct radv_config *) (P->cf); struct radv_config *cf = (struct radv_config *) (P->cf);
init_list(&(p->iface_list)); init_list(&(p->iface_list));
p->valid = 1;
p->active = !cf->trigger_valid; p->active = !cf->trigger_valid;
p->fib_up = 0;
radv_set_fib(p, cf->propagate_routes);
p->prune_time = TIME_INFINITY;
return PS_UP; return PS_UP;
} }
@ -477,7 +614,10 @@ static inline void
radv_iface_shutdown(struct radv_iface *ifa) radv_iface_shutdown(struct radv_iface *ifa)
{ {
if (ifa->sk) if (ifa->sk)
radv_send_ra(ifa, 1); {
radv_invalidate(ifa);
radv_send_ra(ifa);
}
} }
static int static int
@ -485,6 +625,8 @@ radv_shutdown(struct proto *P)
{ {
struct radv_proto *p = (struct radv_proto *) P; struct radv_proto *p = (struct radv_proto *) P;
p->valid = 0;
struct radv_iface *ifa; struct radv_iface *ifa;
WALK_LIST(ifa, p->iface_list) WALK_LIST(ifa, p->iface_list)
radv_iface_shutdown(ifa); radv_iface_shutdown(ifa);
@ -496,20 +638,19 @@ static int
radv_reconfigure(struct proto *P, struct proto_config *c) radv_reconfigure(struct proto *P, struct proto_config *c)
{ {
struct radv_proto *p = (struct radv_proto *) P; struct radv_proto *p = (struct radv_proto *) P;
// struct radv_config *old = (struct radv_config *) (p->cf); struct radv_config *old = (struct radv_config *) (P->cf);
struct radv_config *new = (struct radv_config *) c; struct radv_config *new = (struct radv_config *) c;
/*
* The question is why there is a reconfigure function for RAdv if
* it has almost none internal state so restarting the protocol
* would probably suffice. One small reason is that restarting the
* protocol would lead to sending a RA with Router Lifetime 0
* causing nodes to temporary remove their default routes.
*/
P->cf = c; /* radv_check_active() requires proper P->cf */ P->cf = c; /* radv_check_active() requires proper P->cf */
p->active = radv_check_active(p); p->active = radv_check_active(p);
/* Allocate or free FIB */
radv_set_fib(p, new->propagate_routes);
/* We started to accept routes so we need to refeed them */
if (!old->propagate_routes && new->propagate_routes)
proto_request_feeding(&p->p);
struct iface *iface; struct iface *iface;
WALK_LIST(iface, iface_list) WALK_LIST(iface, iface_list)
{ {
@ -561,14 +702,49 @@ radv_get_status(struct proto *P, byte *buf)
strcpy(buf, "Suppressed"); strcpy(buf, "Suppressed");
} }
static const char *
radv_pref_str(u32 pref)
{
switch (pref)
{
case RA_PREF_LOW:
return "low";
case RA_PREF_MEDIUM:
return "medium";
case RA_PREF_HIGH:
return "high";
default:
return "??";
}
}
/* The buffer has some minimal size */
static int
radv_get_attr(eattr *a, byte *buf, int buflen UNUSED)
{
switch (a->id)
{
case EA_RA_PREFERENCE:
bsprintf(buf, "preference: %s", radv_pref_str(a->u.data));
return GA_FULL;
case EA_RA_LIFETIME:
bsprintf(buf, "lifetime");
return GA_NAME;
default:
return GA_UNKNOWN;
}
}
struct protocol proto_radv = { struct protocol proto_radv = {
.name = "RAdv", .name = "RAdv",
.template = "radv%d", .template = "radv%d",
.attr_class = EAP_RADV,
.config_size = sizeof(struct radv_config), .config_size = sizeof(struct radv_config),
.init = radv_init, .init = radv_init,
.start = radv_start, .start = radv_start,
.shutdown = radv_shutdown, .shutdown = radv_shutdown,
.reconfigure = radv_reconfigure, .reconfigure = radv_reconfigure,
.copy_config = radv_copy_config, .copy_config = radv_copy_config,
.get_status = radv_get_status .get_status = radv_get_status,
.get_attr = radv_get_attr
}; };

View file

@ -54,6 +54,10 @@ struct radv_config
ip_addr trigger_prefix; /* Prefix of a trigger route, if defined */ ip_addr trigger_prefix; /* Prefix of a trigger route, if defined */
u8 trigger_pxlen; /* Pxlen 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 */ 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 */
}; };
struct radv_iface_config struct radv_iface_config
@ -117,12 +121,32 @@ struct radv_dnssl_config
char *domain; /* Domain for DNS search list, in processed form */ char *domain; /* Domain for DNS search list, in processed form */
}; };
/*
* One more specific route as per RFC 4191.
*
* Note that it does *not* contain the next hop field. The next hop is always
* the router sending the advertisment and the more specific route only allows
* overriding the preference of the route.
*/
struct radv_route
{
struct fib_node n;
u32 lifetime; /* Lifetime from an attribute */
u8 lifetime_set; /* Is the lifetime set by an attribute? */
u8 preference; /* Preference of the route, RA_PREF_* */
u8 alive;
bird_clock_t expires; /* Time to remove when !alive */
};
struct radv_proto struct radv_proto
{ {
struct proto p; struct proto p;
list iface_list; /* List of active ifaces */ list iface_list; /* List of active ifaces */
u8 valid; /* Router is valid for forwarding, used for shutdown */
u8 active; /* Whether radv is active w.r.t. triggers */ u8 active; /* Whether radv is active w.r.t. triggers */
u8 fib_up; /* FIB table (routes) is initialized */
struct fib routes; /* FIB table of specific routes (struct radv_route) */
bird_clock_t prune_time; /* Next time of route table pruning */
}; };
struct radv_prefix /* One prefix we advertise */ struct radv_prefix /* One prefix we advertise */
@ -147,7 +171,7 @@ struct radv_iface
struct ifa *addr; /* Link-local address of iface */ struct ifa *addr; /* Link-local address of iface */
struct pool *pool; /* A pool for interface-specific things */ struct pool *pool; /* A pool for interface-specific things */
list prefixes; /* The prefixes we advertise (struct radv_prefix) */ list prefixes; /* The prefixes we advertise (struct radv_prefix) */
bird_clock_t prefix_expires; /* When the soonest prefix expires (0 = none dead) */ bird_clock_t prune_time; /* Next time of prefix list pruning */
timer *timer; timer *timer;
struct object_lock *lock; struct object_lock *lock;
@ -161,7 +185,6 @@ struct radv_iface
#define RA_EV_INIT 1 /* Switch to initial mode */ #define RA_EV_INIT 1 /* Switch to initial mode */
#define RA_EV_CHANGE 2 /* Change of options or prefixes */ #define RA_EV_CHANGE 2 /* Change of options or prefixes */
#define RA_EV_RS 3 /* Received RS */ #define RA_EV_RS 3 /* Received RS */
#define RA_EV_GC 4 /* Internal garbage collection of prefixes */
/* Default Router Preferences (RFC 4191) */ /* Default Router Preferences (RFC 4191) */
#define RA_PREF_LOW 0x18 #define RA_PREF_LOW 0x18
@ -169,6 +192,9 @@ struct radv_iface
#define RA_PREF_HIGH 0x08 #define RA_PREF_HIGH 0x08
#define RA_PREF_MASK 0x18 #define RA_PREF_MASK 0x18
/* Attributes */
#define EA_RA_PREFERENCE EA_CODE(EAP_RADV, 0)
#define EA_RA_LIFETIME EA_CODE(EAP_RADV, 1)
#ifdef LOCAL_DEBUG #ifdef LOCAL_DEBUG
#define RADV_FORCE_DEBUG 1 #define RADV_FORCE_DEBUG 1
@ -184,7 +210,7 @@ void radv_iface_notify(struct radv_iface *ifa, int event);
/* packets.c */ /* packets.c */
int radv_process_domain(struct radv_dnssl_config *cf); int radv_process_domain(struct radv_dnssl_config *cf);
void radv_send_ra(struct radv_iface *ifa, int shutdown); void radv_send_ra(struct radv_iface *ifa);
int radv_sk_open(struct radv_iface *ifa); int radv_sk_open(struct radv_iface *ifa);