summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
Diffstat (limited to 'proto')
-rw-r--r--proto/radv/config.Y36
-rw-r--r--proto/radv/packets.c32
-rw-r--r--proto/radv/radv.c91
-rw-r--r--proto/radv/radv.h8
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