2011-03-13 10:33:50 +00:00
|
|
|
/*
|
|
|
|
* BIRD -- Router Advertisement
|
|
|
|
*
|
2019-08-11 22:41:36 +00:00
|
|
|
* (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org>
|
|
|
|
* (c) 2011--2019 CZ.NIC z.s.p.o.
|
2011-03-13 10:33:50 +00:00
|
|
|
*
|
|
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "radv.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOC: Router Advertisements
|
|
|
|
*
|
2017-08-31 13:40:23 +00:00
|
|
|
* The RAdv protocol is implemented in two files: |radv.c| containing the
|
|
|
|
* interface with BIRD core and the protocol logic and |packets.c| handling low
|
|
|
|
* level protocol stuff (RX, TX and packet formats). The protocol does not
|
|
|
|
* export any routes.
|
2011-03-13 10:33:50 +00:00
|
|
|
*
|
2017-08-31 13:40:23 +00:00
|
|
|
* The RAdv is structured in the usual way - for each handled interface there is
|
|
|
|
* a structure &radv_iface that contains a state related to that interface
|
|
|
|
* together with its resources (a socket, a timer). There is also a prepared RA
|
|
|
|
* stored in a TX buffer of the socket associated with an iface. These iface
|
|
|
|
* structures are created and removed according to iface events from BIRD core
|
|
|
|
* handled by radv_if_notify() callback.
|
2011-03-13 10:33:50 +00:00
|
|
|
*
|
2017-08-31 13:40:23 +00:00
|
|
|
* The main logic of RAdv consists of two functions: radv_iface_notify(), which
|
|
|
|
* processes asynchronous events (specified by RA_EV_* codes), and radv_timer(),
|
|
|
|
* which triggers sending RAs and computes the next timeout.
|
2012-07-07 08:40:00 +00:00
|
|
|
*
|
Terminology cleanup: The import_control hook is now called preexport.
Once upon a time, far far away, there were the old Bird developers
discussing what direction of route flow shall be called import and
export. They decided to say "import to protocol" and "export to table"
when speaking about a protocol. When speaking about a table, they
spoke about "importing to table" and "exporting to protocol".
The latter terminology was adopted in configuration, then also the
bird CLI in commit ea2ae6dd0 started to use it (in year 2009). Now
it's 2018 and the terminology is the latter. Import is from protocol to
table, export is from table to protocol. Anyway, there was still an
import_control hook which executed right before route export.
One thing is funny. There are two commits in April 1999 with just two
minutes between them. The older announces the final settlement
on config terminology, the newer uses the other definition. Let's see
their commit messages as the git-log tool shows them (the newer first):
commit 9e0e485e50ea74c4f1c5cb65bdfe6ce819c2cee2
Author: Martin Mares <mj@ucw.cz>
Date: Mon Apr 5 20:17:59 1999 +0000
Added some new protocol hooks (look at the comments for better explanation):
make_tmp_attrs Convert inline attributes to ea_list
store_tmp_attrs Convert ea_list to inline attributes
import_control Pre-import decisions
commit 5056c559c4eb253a4eee10cf35b694faec5265eb
Author: Martin Mares <mj@ucw.cz>
Date: Mon Apr 5 20:15:31 1999 +0000
Changed syntax of attaching filters to protocols to hopefully the final
version:
EXPORT <filter-spec> for outbound routes (i.e., those announced
by BIRD to the rest of the world).
IMPORT <filter-spec> for inbound routes (i.e., those imported
by BIRD from the rest of the world).
where <filter-spec> is one of:
ALL pass all routes
NONE drop all routes
FILTER <name> use named filter
FILTER { <filter> } use explicitly defined filter
For all protocols, the default is IMPORT ALL, EXPORT NONE. This includes
the kernel protocol, so that you need to add EXPORT ALL to get the previous
configuration of kernel syncer (as usually, see doc/bird.conf.example for
a bird.conf example :)).
Let's say RIP to this almost 19-years-old inconsistency. For now, if you
import a route, it is always from protocol to table. If you export a
route, it is always from table to protocol.
And they lived happily ever after.
2018-02-14 12:42:53 +00:00
|
|
|
* The RAdv protocol could receive routes (through radv_preexport() and
|
2017-08-31 13:40:23 +00:00
|
|
|
* 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.
|
2013-02-08 22:58:27 +00:00
|
|
|
*
|
2012-07-07 08:40:00 +00:00
|
|
|
* Supported standards:
|
2019-08-11 22:41:36 +00:00
|
|
|
* RFC 4861 - main RA standard
|
|
|
|
* RFC 4191 - Default Router Preferences and More-Specific Routes
|
|
|
|
* RFC 6106 - DNS extensions (RDDNS, DNSSL)
|
2011-03-13 10:33:50 +00:00
|
|
|
*/
|
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
static void radv_prune_prefixes(struct radv_iface *ifa);
|
|
|
|
static void radv_prune_routes(struct radv_proto *p);
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
static void
|
|
|
|
radv_timer(timer *tm)
|
|
|
|
{
|
|
|
|
struct radv_iface *ifa = tm->data;
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = ifa->ra;
|
2017-12-07 20:54:47 +00:00
|
|
|
btime now = current_time();
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
|
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
if (ifa->prune_time <= now)
|
|
|
|
radv_prune_prefixes(ifa);
|
2017-08-11 10:25:36 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
if (p->prune_time <= now)
|
|
|
|
radv_prune_routes(p);
|
2017-08-11 10:25:36 +00:00
|
|
|
|
2019-08-11 22:41:36 +00:00
|
|
|
radv_send_ra(ifa, IPA_NONE);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
/* Update timer */
|
|
|
|
ifa->last = now;
|
2017-06-20 13:55:39 +00:00
|
|
|
btime t = ifa->cf->min_ra_int S;
|
|
|
|
btime r = (ifa->cf->max_ra_int - ifa->cf->min_ra_int) S;
|
2017-06-14 15:02:11 +00:00
|
|
|
t += random() % (r + 1);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
if (ifa->initial)
|
2017-06-14 15:02:11 +00:00
|
|
|
{
|
|
|
|
t = MIN(t, MAX_INITIAL_RTR_ADVERT_INTERVAL);
|
2011-03-13 10:33:50 +00:00
|
|
|
ifa->initial--;
|
2017-06-14 15:02:11 +00:00
|
|
|
}
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-11-28 16:43:20 +00:00
|
|
|
tm_start(ifa->timer, t);
|
2011-03-13 10:33:50 +00:00
|
|
|
}
|
|
|
|
|
2017-08-11 10:25:36 +00:00
|
|
|
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 *
|
2017-12-07 16:41:09 +00:00
|
|
|
radv_prefix_match(struct radv_iface *ifa, net_addr_ip6 *px)
|
2017-08-11 10:25:36 +00:00
|
|
|
{
|
|
|
|
struct radv_proto *p = ifa->ra;
|
|
|
|
struct radv_config *cf = (struct radv_config *) (p->p.cf);
|
|
|
|
struct radv_prefix_config *pc;
|
|
|
|
|
|
|
|
WALK_LIST(pc, ifa->cf->pref_list)
|
2017-12-07 16:41:09 +00:00
|
|
|
if (net_in_net_ip6(px, &pc->prefix))
|
2017-08-11 10:25:36 +00:00
|
|
|
return pc;
|
|
|
|
|
|
|
|
WALK_LIST(pc, cf->pref_list)
|
2017-12-07 16:41:09 +00:00
|
|
|
if (net_in_net_ip6(px, &pc->prefix))
|
2017-08-11 10:25:36 +00:00
|
|
|
return pc;
|
|
|
|
|
|
|
|
return &default_prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go through the list of prefixes, compare them with configs and decide if we
|
2017-08-29 17:17:35 +00:00
|
|
|
* want them or not.
|
|
|
|
*/
|
2017-08-11 10:25:36 +00:00
|
|
|
static void
|
2017-08-29 17:17:35 +00:00
|
|
|
radv_prepare_prefixes(struct radv_iface *ifa)
|
2017-08-11 10:25:36 +00:00
|
|
|
{
|
|
|
|
struct radv_proto *p = ifa->ra;
|
2017-08-31 13:40:23 +00:00
|
|
|
struct radv_prefix *pfx, *next;
|
2017-12-07 20:54:47 +00:00
|
|
|
btime now = current_time();
|
2017-08-11 10:25:36 +00:00
|
|
|
|
2017-08-29 17:17:35 +00:00
|
|
|
/* First mark all the prefixes as unused */
|
2017-08-11 10:25:36 +00:00
|
|
|
WALK_LIST(pfx, ifa->prefixes)
|
|
|
|
pfx->mark = 0;
|
|
|
|
|
2017-08-29 17:17:35 +00:00
|
|
|
/* Find all the prefixes we want to use and make sure they are in the list. */
|
2017-08-11 10:25:36 +00:00
|
|
|
struct ifa *addr;
|
|
|
|
WALK_LIST(addr, ifa->iface->addrs)
|
|
|
|
{
|
2017-12-07 16:41:09 +00:00
|
|
|
if ((addr->prefix.type != NET_IP6) ||
|
|
|
|
(addr->scope <= SCOPE_LINK))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
net_addr_ip6 *prefix = (void *) &addr->prefix;
|
|
|
|
struct radv_prefix_config *pc = radv_prefix_match(ifa, prefix);
|
2017-08-11 10:25:36 +00:00
|
|
|
|
|
|
|
if (!pc || pc->skip)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Do we have it already? */
|
|
|
|
struct radv_prefix *existing = NULL;
|
|
|
|
WALK_LIST(pfx, ifa->prefixes)
|
2017-12-07 16:41:09 +00:00
|
|
|
if (net_equal_ip6(&pfx->prefix, prefix))
|
2017-08-11 10:25:36 +00:00
|
|
|
{
|
|
|
|
existing = pfx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!existing)
|
|
|
|
{
|
2017-12-07 16:41:09 +00:00
|
|
|
RADV_TRACE(D_EVENTS, "Adding new prefix %N on %s",
|
|
|
|
prefix, ifa->iface->name);
|
2017-08-29 17:17:35 +00:00
|
|
|
|
2017-08-11 10:25:36 +00:00
|
|
|
existing = mb_allocz(ifa->pool, sizeof *existing);
|
2017-12-07 16:41:09 +00:00
|
|
|
net_copy_ip6(&existing->prefix, prefix);
|
2017-08-11 10:25:36 +00:00
|
|
|
add_tail(&ifa->prefixes, NODE existing);
|
|
|
|
}
|
2017-08-29 17:17:35 +00:00
|
|
|
|
2017-08-11 10:25:36 +00:00
|
|
|
/*
|
|
|
|
* Update the information (it may have changed, or even bring a prefix back
|
|
|
|
* to life).
|
|
|
|
*/
|
2017-10-06 10:22:18 +00:00
|
|
|
existing->valid = 1;
|
|
|
|
existing->changed = now;
|
2017-08-11 10:25:36 +00:00
|
|
|
existing->mark = 1;
|
2017-08-29 17:17:35 +00:00
|
|
|
existing->cf = pc;
|
2017-08-11 10:25:36 +00:00
|
|
|
}
|
|
|
|
|
2017-08-29 17:17:35 +00:00
|
|
|
WALK_LIST_DELSAFE(pfx, next, ifa->prefixes)
|
|
|
|
{
|
2017-10-06 10:22:18 +00:00
|
|
|
if (pfx->valid && !pfx->mark)
|
2017-08-11 10:25:36 +00:00
|
|
|
{
|
2017-12-07 20:54:47 +00:00
|
|
|
RADV_TRACE(D_EVENTS, "Invalidating prefix %N on %s",
|
2018-07-19 18:54:10 +00:00
|
|
|
&pfx->prefix, ifa->iface->name);
|
2017-08-29 17:17:35 +00:00
|
|
|
|
2017-10-06 10:22:18 +00:00
|
|
|
pfx->valid = 0;
|
|
|
|
pfx->changed = now;
|
2017-08-29 17:17:35 +00:00
|
|
|
pfx->cf = &dead_prefix;
|
2017-08-11 10:25:36 +00:00
|
|
|
}
|
2017-08-31 13:40:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
radv_prune_prefixes(struct radv_iface *ifa)
|
|
|
|
{
|
|
|
|
struct radv_proto *p = ifa->ra;
|
2017-12-07 20:54:47 +00:00
|
|
|
btime now = current_time();
|
|
|
|
btime next = TIME_INFINITY;
|
|
|
|
btime expires = 0;
|
2017-08-29 17:17:35 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
struct radv_prefix *px, *pxn;
|
|
|
|
WALK_LIST_DELSAFE(px, pxn, ifa->prefixes)
|
|
|
|
{
|
2017-10-06 10:22:18 +00:00
|
|
|
if (!px->valid)
|
2017-08-29 17:17:35 +00:00
|
|
|
{
|
2017-12-07 20:54:47 +00:00
|
|
|
expires = px->changed + ifa->cf->prefix_linger_time S;
|
2017-10-06 10:22:18 +00:00
|
|
|
|
|
|
|
if (expires <= now)
|
2017-08-11 10:25:36 +00:00
|
|
|
{
|
2017-12-07 16:41:09 +00:00
|
|
|
RADV_TRACE(D_EVENTS, "Removing prefix %N on %s",
|
2018-07-19 18:54:10 +00:00
|
|
|
&px->prefix, ifa->iface->name);
|
2017-08-29 17:17:35 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
rem_node(NODE px);
|
|
|
|
mb_free(px);
|
2017-08-11 10:25:36 +00:00
|
|
|
}
|
|
|
|
else
|
2017-10-06 10:22:18 +00:00
|
|
|
next = MIN(next, expires);
|
2017-08-29 17:17:35 +00:00
|
|
|
}
|
2017-08-11 10:25:36 +00:00
|
|
|
}
|
2017-08-29 17:17:35 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
ifa->prune_time = next;
|
2017-08-11 10:25:36 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
static char* ev_name[] = { NULL, "Init", "Change", "RS" };
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
radv_iface_notify(struct radv_iface *ifa, int event)
|
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = ifa->ra;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
if (!ifa->sk)
|
|
|
|
return;
|
|
|
|
|
|
|
|
RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
|
|
|
|
|
|
|
|
switch (event)
|
|
|
|
{
|
|
|
|
case RA_EV_CHANGE:
|
2017-08-31 13:40:23 +00:00
|
|
|
radv_invalidate(ifa);
|
2018-09-18 12:21:11 +00:00
|
|
|
/* fallthrough */
|
2011-03-13 10:33:50 +00:00
|
|
|
case RA_EV_INIT:
|
|
|
|
ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
|
2017-08-31 13:40:23 +00:00
|
|
|
radv_prepare_prefixes(ifa);
|
|
|
|
radv_prune_prefixes(ifa);
|
2011-03-13 10:33:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RA_EV_RS:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update timer */
|
2017-06-20 13:55:39 +00:00
|
|
|
btime t = ifa->last + ifa->cf->min_delay S - current_time();
|
2017-11-28 16:43:20 +00:00
|
|
|
tm_start(ifa->timer, t);
|
2011-03-13 10:33:50 +00:00
|
|
|
}
|
|
|
|
|
2013-02-08 22:58:27 +00:00
|
|
|
static void
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_iface_notify_all(struct radv_proto *p, int event)
|
2013-02-08 22:58:27 +00:00
|
|
|
{
|
|
|
|
struct radv_iface *ifa;
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
WALK_LIST(ifa, p->iface_list)
|
2013-02-08 22:58:27 +00:00
|
|
|
radv_iface_notify(ifa, event);
|
|
|
|
}
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
static struct radv_iface *
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_iface_find(struct radv_proto *p, struct iface *what)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
|
|
|
struct radv_iface *ifa;
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
WALK_LIST(ifa, p->iface_list)
|
2011-03-13 10:33:50 +00:00
|
|
|
if (ifa->iface == what)
|
|
|
|
return ifa;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
radv_iface_add(struct object_lock *lock)
|
|
|
|
{
|
|
|
|
struct radv_iface *ifa = lock->data;
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = ifa->ra;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
if (! radv_sk_open(ifa))
|
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
log(L_ERR "%s: Socket open failed on interface %s", p->p.name, ifa->iface->name);
|
2011-03-13 10:33:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
radv_iface_notify(ifa, RA_EV_INIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
|
|
|
struct radv_iface *ifa;
|
|
|
|
|
|
|
|
RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
|
|
|
|
|
2017-08-11 10:25:36 +00:00
|
|
|
pool *pool = rp_new(p->p.pool, iface->name);
|
2011-03-13 10:33:50 +00:00
|
|
|
ifa = mb_allocz(pool, sizeof(struct radv_iface));
|
2017-08-11 10:25:36 +00:00
|
|
|
ifa->pool = pool;
|
2017-08-08 12:40:51 +00:00
|
|
|
ifa->ra = p;
|
2011-03-13 10:33:50 +00:00
|
|
|
ifa->cf = cf;
|
|
|
|
ifa->iface = iface;
|
2017-12-07 12:06:01 +00:00
|
|
|
ifa->addr = iface->llv6;
|
2017-08-11 10:25:36 +00:00
|
|
|
init_list(&ifa->prefixes);
|
2017-08-31 13:40:23 +00:00
|
|
|
ifa->prune_time = TIME_INFINITY;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
add_tail(&p->iface_list, NODE ifa);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-11-28 16:43:20 +00:00
|
|
|
ifa->timer = tm_new_init(pool, radv_timer, ifa, 0, 0);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
struct object_lock *lock = olock_new(pool);
|
|
|
|
lock->type = OBJLOCK_IP;
|
|
|
|
lock->port = ICMPV6_PROTO;
|
|
|
|
lock->iface = iface;
|
|
|
|
lock->data = ifa;
|
|
|
|
lock->hook = radv_iface_add;
|
|
|
|
ifa->lock = lock;
|
|
|
|
|
|
|
|
olock_acquire(lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
radv_iface_remove(struct radv_iface *ifa)
|
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = ifa->ra;
|
2011-03-13 10:33:50 +00:00
|
|
|
RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
|
|
|
|
|
|
|
|
rem_node(NODE ifa);
|
|
|
|
|
2017-08-11 10:25:36 +00:00
|
|
|
rfree(ifa->pool);
|
2011-03-13 10:33:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_if_notify(struct proto *P, unsigned flags, struct iface *iface)
|
2015-02-21 20:08:23 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
|
|
|
struct radv_config *cf = (struct radv_config *) (P->cf);
|
2018-11-28 15:43:17 +00:00
|
|
|
struct radv_iface *ifa = radv_iface_find(p, iface);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
if (iface->flags & IF_IGNORE)
|
|
|
|
return;
|
|
|
|
|
2018-11-28 15:43:17 +00:00
|
|
|
/* Add, remove or restart interface */
|
|
|
|
if (flags & (IF_CHANGE_UPDOWN | IF_CHANGE_LLV6))
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
2018-11-28 15:43:17 +00:00
|
|
|
if (ifa)
|
|
|
|
radv_iface_remove(ifa);
|
|
|
|
|
|
|
|
if (!(iface->flags & IF_UP))
|
|
|
|
return;
|
2017-12-07 12:06:01 +00:00
|
|
|
|
|
|
|
/* Ignore non-multicast ifaces */
|
|
|
|
if (!(iface->flags & IF_MULTICAST))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Ignore ifaces without link-local address */
|
|
|
|
if (!iface->llv6)
|
|
|
|
return;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2018-11-28 15:43:17 +00:00
|
|
|
struct radv_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
|
2011-03-13 10:33:50 +00:00
|
|
|
if (ic)
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_iface_new(p, iface, ic);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifa)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
|
|
|
|
radv_iface_notify(ifa, RA_EV_INIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_ifa_notify(struct proto *P, unsigned flags UNUSED, struct ifa *a)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
if (a->flags & IA_SECONDARY)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (a->scope <= SCOPE_LINK)
|
|
|
|
return;
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_iface *ifa = radv_iface_find(p, a->iface);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
|
|
|
if (ifa)
|
|
|
|
radv_iface_notify(ifa, RA_EV_CHANGE);
|
|
|
|
}
|
|
|
|
|
2016-01-26 10:48:58 +00:00
|
|
|
static inline int
|
|
|
|
radv_trigger_valid(struct radv_config *cf)
|
2013-02-08 22:58:27 +00:00
|
|
|
{
|
2016-01-26 10:48:58 +00:00
|
|
|
return cf->trigger.type != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
radv_net_match_trigger(struct radv_config *cf, net *n)
|
|
|
|
{
|
|
|
|
return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
|
2013-02-08 22:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2022-06-27 17:04:22 +00:00
|
|
|
radv_preexport(struct channel *C, rte **new, struct linpool *pool UNUSED)
|
2013-02-08 22:58:27 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
// struct radv_proto *p = (struct radv_proto *) P;
|
2022-06-27 17:04:22 +00:00
|
|
|
struct radv_config *cf = (struct radv_config *) (C->proto->cf);
|
2013-02-08 22:58:27 +00:00
|
|
|
|
|
|
|
if (radv_net_match_trigger(cf, (*new)->net))
|
|
|
|
return RIC_PROCESS;
|
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
if (cf->propagate_routes)
|
|
|
|
return RIC_PROCESS;
|
|
|
|
else
|
|
|
|
return RIC_DROP;
|
2013-02-08 22:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-05-29 10:08:12 +00:00
|
|
|
radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED)
|
2013-02-08 22:58:27 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
|
|
|
struct radv_config *cf = (struct radv_config *) (P->cf);
|
2017-08-31 13:40:23 +00:00
|
|
|
struct radv_route *rt;
|
2017-10-06 10:22:18 +00:00
|
|
|
eattr *ea;
|
2013-02-08 22:58:27 +00:00
|
|
|
|
|
|
|
if (radv_net_match_trigger(cf, n))
|
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
u8 old_active = p->active;
|
|
|
|
p->active = !!new;
|
2013-02-08 22:58:27 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
if (p->active == old_active)
|
2013-02-08 22:58:27 +00:00
|
|
|
return;
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
if (p->active)
|
2013-02-08 22:58:27 +00:00
|
|
|
RADV_TRACE(D_EVENTS, "Triggered");
|
|
|
|
else
|
|
|
|
RADV_TRACE(D_EVENTS, "Suppressed");
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_iface_notify_all(p, RA_EV_CHANGE);
|
2017-08-31 13:40:23 +00:00
|
|
|
return;
|
2013-02-08 22:58:27 +00:00
|
|
|
}
|
2017-08-31 13:40:23 +00:00
|
|
|
|
|
|
|
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 */
|
2017-10-06 10:22:18 +00:00
|
|
|
|
2018-05-29 10:08:12 +00:00
|
|
|
ea = ea_find(new->attrs->eattrs, EA_RA_PREFERENCE);
|
2017-10-06 10:22:18 +00:00
|
|
|
uint preference = ea ? ea->u.data : RA_PREF_MEDIUM;
|
|
|
|
uint preference_set = !!ea;
|
|
|
|
|
2018-05-29 10:08:12 +00:00
|
|
|
ea = ea_find(new->attrs->eattrs, EA_RA_LIFETIME);
|
2017-10-06 10:22:18 +00:00
|
|
|
uint lifetime = ea ? ea->u.data : 0;
|
|
|
|
uint lifetime_set = !!ea;
|
2017-08-31 13:40:23 +00:00
|
|
|
|
|
|
|
if ((preference != RA_PREF_LOW) &&
|
|
|
|
(preference != RA_PREF_MEDIUM) &&
|
|
|
|
(preference != RA_PREF_HIGH))
|
|
|
|
{
|
2017-12-07 20:54:47 +00:00
|
|
|
log(L_WARN "%s: Invalid ra_preference value %u on route %N",
|
|
|
|
p->p.name, preference, n->n.addr);
|
2017-08-31 13:40:23 +00:00
|
|
|
preference = RA_PREF_MEDIUM;
|
2017-10-06 10:22:18 +00:00
|
|
|
preference_set = 1;
|
2017-08-31 13:40:23 +00:00
|
|
|
lifetime = 0;
|
|
|
|
lifetime_set = 1;
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:54:47 +00:00
|
|
|
rt = fib_get(&p->routes, n->n.addr);
|
2017-08-31 13:40:23 +00:00
|
|
|
|
|
|
|
/* Ignore update if nothing changed */
|
2017-10-06 10:22:18 +00:00
|
|
|
if (rt->valid &&
|
2017-08-31 13:40:23 +00:00
|
|
|
(rt->preference == preference) &&
|
2017-10-06 10:22:18 +00:00
|
|
|
(rt->preference_set == preference_set) &&
|
2017-08-31 13:40:23 +00:00
|
|
|
(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);
|
|
|
|
|
2017-10-06 10:22:18 +00:00
|
|
|
rt->valid = 1;
|
2017-12-07 20:54:47 +00:00
|
|
|
rt->changed = current_time();
|
2017-08-31 13:40:23 +00:00
|
|
|
rt->preference = preference;
|
2017-10-06 10:22:18 +00:00
|
|
|
rt->preference_set = preference_set;
|
2017-08-31 13:40:23 +00:00
|
|
|
rt->lifetime = lifetime;
|
|
|
|
rt->lifetime_set = lifetime_set;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Withdraw */
|
2017-12-07 20:54:47 +00:00
|
|
|
rt = fib_find(&p->routes, n->n.addr);
|
2017-08-31 13:40:23 +00:00
|
|
|
|
2017-10-06 10:22:18 +00:00
|
|
|
if (!rt || !rt->valid)
|
2017-08-31 13:40:23 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Invalidate the route */
|
2017-10-06 10:22:18 +00:00
|
|
|
rt->valid = 0;
|
2017-12-07 20:54:47 +00:00
|
|
|
rt->changed = current_time();
|
2017-10-06 10:22:18 +00:00
|
|
|
|
|
|
|
/* Invalidated route will be pruned eventually */
|
2017-12-07 20:54:47 +00:00
|
|
|
btime expires = rt->changed + cf->max_linger_time S;
|
2017-10-06 10:22:18 +00:00
|
|
|
p->prune_time = MIN(p->prune_time, expires);
|
2013-02-08 22:58:27 +00:00
|
|
|
}
|
2017-08-31 13:40:23 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-10-06 10:22:18 +00:00
|
|
|
struct radv_config *cf = (struct radv_config *) (p->p.cf);
|
2017-12-07 20:54:47 +00:00
|
|
|
btime now = current_time();
|
|
|
|
btime next = TIME_INFINITY;
|
|
|
|
btime expires = 0;
|
2017-08-31 13:40:23 +00:00
|
|
|
|
|
|
|
/* Should not happen */
|
|
|
|
if (!p->fib_up)
|
|
|
|
return;
|
|
|
|
|
|
|
|
struct fib_iterator fit;
|
|
|
|
FIB_ITERATE_INIT(&fit, &p->routes);
|
|
|
|
|
|
|
|
again:
|
2017-12-07 20:54:47 +00:00
|
|
|
FIB_ITERATE_START(&p->routes, &fit, struct radv_route, rt)
|
2017-08-31 13:40:23 +00:00
|
|
|
{
|
2017-10-06 10:22:18 +00:00
|
|
|
if (!rt->valid)
|
2017-08-31 13:40:23 +00:00
|
|
|
{
|
2017-12-07 20:54:47 +00:00
|
|
|
expires = rt->changed + cf->max_linger_time S;
|
2017-10-06 10:22:18 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
/* Delete expired nodes */
|
2017-10-06 10:22:18 +00:00
|
|
|
if (expires <= now)
|
2017-08-31 13:40:23 +00:00
|
|
|
{
|
2017-12-07 20:54:47 +00:00
|
|
|
FIB_ITERATE_PUT(&fit);
|
|
|
|
fib_delete(&p->routes, rt);
|
2017-08-31 13:40:23 +00:00
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
else
|
2017-10-06 10:22:18 +00:00
|
|
|
next = MIN(next, expires);
|
2017-08-31 13:40:23 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-07 20:54:47 +00:00
|
|
|
FIB_ITERATE_END;
|
2017-08-31 13:40:23 +00:00
|
|
|
|
|
|
|
p->prune_time = next;
|
2013-02-08 22:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_check_active(struct radv_proto *p)
|
2013-02-08 22:58:27 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_config *cf = (struct radv_config *) (p->p.cf);
|
2013-02-08 22:58:27 +00:00
|
|
|
|
2016-01-26 10:48:58 +00:00
|
|
|
if (!radv_trigger_valid(cf))
|
2013-02-08 22:58:27 +00:00
|
|
|
return 1;
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
struct channel *c = p->p.main_channel;
|
2022-06-27 17:04:22 +00:00
|
|
|
return rt_examine(c->table, &cf->trigger, c, c->out_filter);
|
2016-01-26 10:48:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
radv_postconfig(struct proto_config *CF)
|
|
|
|
{
|
|
|
|
// struct radv_config *cf = (void *) CF;
|
|
|
|
|
|
|
|
/* Define default channel */
|
2021-06-17 14:56:51 +00:00
|
|
|
if (! proto_cf_main_channel(CF))
|
2018-01-09 17:42:22 +00:00
|
|
|
channel_config_new(NULL, net_label[NET_IP6], NET_IP6, CF);
|
2013-02-08 22:58:27 +00:00
|
|
|
}
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
static struct proto *
|
2016-01-26 10:48:58 +00:00
|
|
|
radv_init(struct proto_config *CF)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct proto *P = proto_new(CF);
|
2016-01-26 10:48:58 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
|
2011-03-13 10:33:50 +00:00
|
|
|
|
Terminology cleanup: The import_control hook is now called preexport.
Once upon a time, far far away, there were the old Bird developers
discussing what direction of route flow shall be called import and
export. They decided to say "import to protocol" and "export to table"
when speaking about a protocol. When speaking about a table, they
spoke about "importing to table" and "exporting to protocol".
The latter terminology was adopted in configuration, then also the
bird CLI in commit ea2ae6dd0 started to use it (in year 2009). Now
it's 2018 and the terminology is the latter. Import is from protocol to
table, export is from table to protocol. Anyway, there was still an
import_control hook which executed right before route export.
One thing is funny. There are two commits in April 1999 with just two
minutes between them. The older announces the final settlement
on config terminology, the newer uses the other definition. Let's see
their commit messages as the git-log tool shows them (the newer first):
commit 9e0e485e50ea74c4f1c5cb65bdfe6ce819c2cee2
Author: Martin Mares <mj@ucw.cz>
Date: Mon Apr 5 20:17:59 1999 +0000
Added some new protocol hooks (look at the comments for better explanation):
make_tmp_attrs Convert inline attributes to ea_list
store_tmp_attrs Convert ea_list to inline attributes
import_control Pre-import decisions
commit 5056c559c4eb253a4eee10cf35b694faec5265eb
Author: Martin Mares <mj@ucw.cz>
Date: Mon Apr 5 20:15:31 1999 +0000
Changed syntax of attaching filters to protocols to hopefully the final
version:
EXPORT <filter-spec> for outbound routes (i.e., those announced
by BIRD to the rest of the world).
IMPORT <filter-spec> for inbound routes (i.e., those imported
by BIRD from the rest of the world).
where <filter-spec> is one of:
ALL pass all routes
NONE drop all routes
FILTER <name> use named filter
FILTER { <filter> } use explicitly defined filter
For all protocols, the default is IMPORT ALL, EXPORT NONE. This includes
the kernel protocol, so that you need to add EXPORT ALL to get the previous
configuration of kernel syncer (as usually, see doc/bird.conf.example for
a bird.conf example :)).
Let's say RIP to this almost 19-years-old inconsistency. For now, if you
import a route, it is always from protocol to table. If you export a
route, it is always from table to protocol.
And they lived happily ever after.
2018-02-14 12:42:53 +00:00
|
|
|
P->preexport = radv_preexport;
|
2017-08-08 12:40:51 +00:00
|
|
|
P->rt_notify = radv_rt_notify;
|
|
|
|
P->if_notify = radv_if_notify;
|
|
|
|
P->ifa_notify = radv_ifa_notify;
|
2016-01-26 10:48:58 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
return P;
|
2011-03-13 10:33:50 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
static void
|
|
|
|
radv_set_fib(struct radv_proto *p, int up)
|
|
|
|
{
|
|
|
|
if (up == p->fib_up)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (up)
|
2017-12-07 20:54:47 +00:00
|
|
|
fib_init(&p->routes, p->p.pool, NET_IP6, sizeof(struct radv_route),
|
|
|
|
OFFSETOF(struct radv_route, n), 4, NULL);
|
2017-08-31 13:40:23 +00:00
|
|
|
else
|
|
|
|
fib_free(&p->routes);
|
|
|
|
|
|
|
|
p->fib_up = up;
|
|
|
|
p->prune_time = TIME_INFINITY;
|
|
|
|
}
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
static int
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_start(struct proto *P)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
|
|
|
struct radv_config *cf = (struct radv_config *) (P->cf);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
init_list(&(p->iface_list));
|
2017-08-31 13:40:23 +00:00
|
|
|
p->valid = 1;
|
2017-08-08 12:40:51 +00:00
|
|
|
p->active = !radv_trigger_valid(cf);
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
p->fib_up = 0;
|
|
|
|
radv_set_fib(p, cf->propagate_routes);
|
|
|
|
p->prune_time = TIME_INFINITY;
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
return PS_UP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
radv_iface_shutdown(struct radv_iface *ifa)
|
|
|
|
{
|
|
|
|
if (ifa->sk)
|
2017-08-31 13:40:23 +00:00
|
|
|
{
|
|
|
|
radv_invalidate(ifa);
|
2019-08-11 22:41:36 +00:00
|
|
|
radv_send_ra(ifa, IPA_NONE);
|
2017-08-31 13:40:23 +00:00
|
|
|
}
|
2011-03-13 10:33:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_shutdown(struct proto *P)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
p->valid = 0;
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
struct radv_iface *ifa;
|
2017-08-08 12:40:51 +00:00
|
|
|
WALK_LIST(ifa, p->iface_list)
|
2011-03-13 10:33:50 +00:00
|
|
|
radv_iface_shutdown(ifa);
|
|
|
|
|
|
|
|
return PS_DOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_reconfigure(struct proto *P, struct proto_config *CF)
|
2011-03-13 10:33:50 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
2017-08-31 13:40:23 +00:00
|
|
|
struct radv_config *old = (struct radv_config *) (P->cf);
|
2016-01-26 10:48:58 +00:00
|
|
|
struct radv_config *new = (struct radv_config *) CF;
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
|
2016-01-26 10:48:58 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-08-09 10:46:27 +00:00
|
|
|
P->cf = CF; /* radv_check_active() requires proper P->cf */
|
2017-08-08 12:40:51 +00:00
|
|
|
p->active = radv_check_active(p);
|
2013-02-08 22:58:27 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
/* 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)
|
2017-12-07 20:54:47 +00:00
|
|
|
channel_request_feeding(p->p.main_channel);
|
2017-08-31 13:40:23 +00:00
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
struct iface *iface;
|
|
|
|
WALK_LIST(iface, iface_list)
|
|
|
|
{
|
2017-12-07 12:06:01 +00:00
|
|
|
if (!(iface->flags & IF_UP))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Ignore non-multicast ifaces */
|
|
|
|
if (!(iface->flags & IF_MULTICAST))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Ignore ifaces without link-local address */
|
|
|
|
if (!iface->llv6)
|
|
|
|
continue;
|
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_iface *ifa = radv_iface_find(p, iface);
|
2011-03-13 10:33:50 +00:00
|
|
|
struct radv_iface_config *ic = (struct radv_iface_config *)
|
|
|
|
iface_patt_find(&new->patt_list, iface, NULL);
|
|
|
|
|
|
|
|
if (ifa && ic)
|
|
|
|
{
|
|
|
|
ifa->cf = ic;
|
|
|
|
|
|
|
|
/* We cheat here - always notify the change even if there isn't
|
|
|
|
any. That would leads just to a few unnecessary RAs. */
|
|
|
|
radv_iface_notify(ifa, RA_EV_CHANGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ifa && !ic)
|
|
|
|
{
|
|
|
|
radv_iface_shutdown(ifa);
|
|
|
|
radv_iface_remove(ifa);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifa && ic)
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_iface_new(p, iface, ic);
|
2011-03-13 10:33:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-11-06 23:31:23 +00:00
|
|
|
static void
|
|
|
|
radv_copy_config(struct proto_config *dest, struct proto_config *src)
|
|
|
|
{
|
|
|
|
struct radv_config *d = (struct radv_config *) dest;
|
|
|
|
struct radv_config *s = (struct radv_config *) src;
|
|
|
|
|
|
|
|
/* We clean up patt_list, ifaces are non-sharable */
|
|
|
|
init_list(&d->patt_list);
|
|
|
|
|
|
|
|
/* We copy pref_list, shallow copy suffices */
|
2011-11-17 20:12:23 +00:00
|
|
|
cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
|
2011-11-06 23:31:23 +00:00
|
|
|
}
|
|
|
|
|
2013-02-08 22:58:27 +00:00
|
|
|
static void
|
2017-08-08 12:40:51 +00:00
|
|
|
radv_get_status(struct proto *P, byte *buf)
|
2013-02-08 22:58:27 +00:00
|
|
|
{
|
2017-08-08 12:40:51 +00:00
|
|
|
struct radv_proto *p = (struct radv_proto *) P;
|
2013-02-08 22:58:27 +00:00
|
|
|
|
2017-08-08 12:40:51 +00:00
|
|
|
if (!p->active)
|
2013-02-08 22:58:27 +00:00
|
|
|
strcpy(buf, "Suppressed");
|
|
|
|
}
|
2011-03-13 10:33:50 +00:00
|
|
|
|
2017-08-31 13:40:23 +00:00
|
|
|
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
|
2019-08-17 11:36:36 +00:00
|
|
|
radv_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
|
2017-08-31 13:40:23 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
struct protocol proto_radv = {
|
|
|
|
.name = "RAdv",
|
|
|
|
.template = "radv%d",
|
2018-05-07 12:47:00 +00:00
|
|
|
.class = PROTOCOL_RADV,
|
2016-01-26 10:48:58 +00:00
|
|
|
.channel_mask = NB_IP6,
|
2017-08-08 12:40:51 +00:00
|
|
|
.proto_size = sizeof(struct radv_proto),
|
2015-02-21 20:08:23 +00:00
|
|
|
.config_size = sizeof(struct radv_config),
|
2016-01-26 10:48:58 +00:00
|
|
|
.postconfig = radv_postconfig,
|
2011-03-13 10:33:50 +00:00
|
|
|
.init = radv_init,
|
|
|
|
.start = radv_start,
|
|
|
|
.shutdown = radv_shutdown,
|
2011-11-06 23:31:23 +00:00
|
|
|
.reconfigure = radv_reconfigure,
|
2013-02-08 22:58:27 +00:00
|
|
|
.copy_config = radv_copy_config,
|
2017-08-31 13:40:23 +00:00
|
|
|
.get_status = radv_get_status,
|
|
|
|
.get_attr = radv_get_attr
|
2011-03-13 10:33:50 +00:00
|
|
|
};
|