diff --git a/doc/bird.sgml b/doc/bird.sgml
index 3dbb40b..ac6596c 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -3476,6 +3476,16 @@ addresses and choose a default route. BIRD implements router behavior as defined
 in <rfc id="4861">, router preferences and specific routes (<rfc id="4191">),
 and DNS extensions (<rfc id="6106">).
 
+Prefixes that are considered for announcing on an interface are collected from
+the addresses configured on the interface and from all device routes exported
+to the protocol. Interface addresses are always exported as prefixes (unless
+a prefix section with <cf/skip/ is configured), whereas device routes are only
+exported as prefixes on their matching interface in case they match a prefix
+configuration which is not marked as <cf/skip/. In case they don't match a
+prefix configuration, the prefix configuration is marked as skip or the interface
+that the device route is on is another interface, the corresponding route will
+be handled by <cf/propagate routes/.
+
 <sect1>Configuration
 <label id="radv-config">
 
@@ -3519,12 +3529,11 @@ definitions, prefix definitions and DNS definitions:
 	options and there is a short variant <cf>dnssl <m/domain/</cf> that just
 	specifies one DNS search domain.
 
-	<tag><label id="radv-trigger">trigger <m/prefix/</tag>
-	RAdv protocol could be configured to change its behavior based on
+	<tag><label id="radv-trigger">trigger <m/trigger/ [keep <m/switch/] [local <m/switch/] [, <m/.../]</tag>
+	RAdv interfaces can be configured to change their behavior based on
 	availability of routes. When this option is used, the protocol waits in
-	suppressed state until a <it/trigger route/ (for the specified network)
-	is exported to the protocol, the protocol also returns to suppressed
-	state if the <it/trigger route/ disappears. Note that route export
+	suppressed state until all <it/trigger routes/ are exported to the protocol.
+	It returns to supressed state if the <it/trigger routes/ disappear. Note that route export
 	depends on specified export filter, as usual. This option could be used,
 	e.g., for handling failover in multihoming scenarios.
 
@@ -3537,22 +3546,28 @@ definitions, prefix definitions and DNS definitions:
 	also be configured as <cf/sensitive/ for a prefix, which would cause
 	autoconfigured IPs to be deprecated or even removed.
 
-	<tag><label id="radv-propagate-routes">propagate routes <m/switch/</tag>
-	This option controls propagation of more specific routes, as defined in
-	<rfc id="4191">. If enabled, all routes exported to the RAdv protocol,
-	with the exception of the trigger prefix, are added to advertisments as
-	additional options. The lifetime and preference of advertised routes can
-	be set individually by <cf/ra_lifetime/ and <cf/ra_preference/ route
-	attributes, or per interface by <cf/route lifetime/ and
-	<cf/route preference/ options. Default: disabled.
+	Normally, trigger routes are filtered from further processing, but when
+	marked with <cf/keep/, the route is processed as usual for <m/propagate routes/
+	and as a prefix route for an interface. When a trigger route is marked
+	as <cf/local/, the route is only considered triggered when it is a device
+	route matching the interface that the trigger is applied to. The default
+	for <cf/keep/ and <cf/local/ are no.
 
-	Note that the RFC discourages from sending more than 17 routes and
-	recommends the routes to be configured manually.
+	Global trigger routes apply to all interface ra instances that are
+	initialized by the radv protocol.
 </descrip>
 
 <p>Interface specific options:
 
 <descrip>
+	<tag><label id="radv-iface-trigger">trigger <m/trigger/ [keep <m/switch/] [local <m/switch/] [, <m/.../]</tag>
+	Triggers can be defined on interfaces and globally. An interface is only
+	considered up in case both globally and interface specific <it/trigger routes/
+	are exported to the protocol. The meaning of <cf/keep/ and <cf/local/ is
+	identical to the global <it/trigger routes/ configuration, and an interface
+	may override the flags for a trigger route over the global configuration
+	when specifying the same trigger.
+
 	<tag><label id="radv-iface-max-ra-interval">max ra interval <m/expr/</tag>
 	Unsolicited router advertisements are sent in irregular time intervals.
 	This option specifies the maximum length of these intervals, in seconds.
@@ -3642,6 +3657,37 @@ definitions, prefix definitions and DNS definitions:
 	<tag><label id="radv-iface-dnssl-local">dnssl local <m/switch/</tag>
 	Use only local DNSSL definitions for this interface. See <cf/rdnss local/
 	option above. Default: no.
+
+	<tag><label id="radv-iface-propagate-routes">propagate routes <m/switch/ [sensitive <m/switch/]</tag>
+	This option controls propagation of more specific routes, as defined in
+	<rfc id="4191">. If enabled, all routes exported to the RAdv protocol
+	except those trigger prefixes not marked as keep and the routes that
+	define prefixes on this interface are added to advertisements as
+	additional options. The lifetime and preference of advertised routes can
+	be set individually by <cf/ra_lifetime/ and <cf/ra_preference/ route
+	attributes, or per interface by <cf/route lifetime/ and
+	<cf/route preference/ options. Default: disabled.
+
+	When propagate routes is <cf/sensitive/ to the trigger routes, the
+	protocol stops exporting the propagated routes once the trigger
+	conditions are longer met. The routes then age out according to the
+	default rules for route aging and come back once the <cf/trigger routes/
+	are again fully exported to the protocol.
+
+	The default for <cf/propagate routes/ and <cf/sensitive/ is no.
+
+	Note that the RFC discourages from sending more than 17 routes and
+	recommends the routes to be configured manually.
+
+	<tag><label id="radv-iface-prefix-sensitive">prefix sensitive <m/switch/</tag>
+	Prefixes are normally advertised independently of the default gateway
+	on all interfaces. When an interface is marked as <cf/prefix sensitive/,
+	the interface stops exporting all prefixes when it is no longer active
+	according to the <cf/trigger routes/. The prefixes then age out
+	according to the default rules for prefix aging and come back once the
+	<cf/trigger routes/ are again fully exported to the protocol.
+
+	The default for <cf/prefix sensitive/ is no.
 </descrip>
 
 
@@ -3655,13 +3701,24 @@ definitions, prefix definitions and DNS definitions:
 	advertised prefix it is more useful to advertise it with zero valid
 	lifetime. Default: no
 
-	<tag><label id="radv-prefix-onlink">onlink <m/switch/</tag>
+	<tag><label id="radv-prefix-sensitive">sensitive <m/switch/</tag>
+	Individual prefixes may be marked as <cf/sensitive/, so that they are
+	removed from an interface according to the normal aging rules for the
+	interface when that interface becomes inactive according to the
+	<cf/trigger routes/ for the interface. This works similarly to the
+	corresponding interface configuration. Default: no.
+
+	<tag><label id="radv-prefix-onlink">onlink <m/switch/ [sensitive <m/switch/]</tag>
 	This option specifies whether hosts may use the advertised prefix for
-	onlink determination. Default: yes
+	onlink determination. When marked as sensitive, the onlink flag in
+	exported prefixes changes to no when interface becomes inactive.
+	Default: yes, <cf/sensitive/ no
 
-	<tag><label id="radv-prefix-autonomous">autonomous <m/switch/</tag>
+	<tag><label id="radv-prefix-autonomous">autonomous <m/switch/ [sensitive <m/switch/]</tag>
 	This option specifies whether hosts may use the advertised prefix for
-	stateless autoconfiguration. Default: yes
+	stateless autoconfiguration. When marked as sensitive, the autonomous
+	flag in exported prefixes changes to no when interface becomes inactive.
+	Default: yes, <cf/sensitive/ no
 
 	<tag><label id="radv-prefix-valid-lifetime">valid lifetime <m/expr/ [sensitive <m/switch/]</tag>
 	This option specifies the time (in seconds) how long (after the
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index 84a2de0..7ad39e7 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -13,10 +13,12 @@ CF_DEFINES
 
 #define RADV_CFG ((struct radv_config *) this_proto)
 #define RADV_IFACE ((struct radv_iface_config *) this_ipatt)
+#define RADV_TRIGGER_LIST this_radv_trigger_list
 #define RADV_PREFIX this_radv_prefix
 #define RADV_RDNSS (&this_radv_rdnss)
 #define RADV_DNSSL (&this_radv_dnssl)
 
+static list *this_radv_trigger_list;
 static struct radv_prefix_config *this_radv_prefix;
 static struct radv_rdnss_config this_radv_rdnss;
 static struct radv_dnssl_config this_radv_dnssl;
@@ -35,7 +37,7 @@ CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
 
 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 radv_trigger_attrs
 
 CF_GRAMMAR
 
@@ -44,6 +46,8 @@ CF_ADDTO(proto, radv_proto)
 radv_proto_start: proto_start RADV
 {
   this_proto = proto_config_new(&proto_radv, $1);
+  this_radv_trigger_list = &RADV_CFG->trigger_list;
+  init_list(&RADV_CFG->trigger_list);
   init_list(&RADV_CFG->patt_list);
   init_list(&RADV_CFG->pref_list);
   init_list(&RADV_CFG->rdnss_list);
@@ -52,16 +56,11 @@ radv_proto_start: proto_start RADV
 
 radv_proto_item:
    proto_item
+ | TRIGGER radv_triggers
  | INTERFACE radv_iface
  | 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;
-   }
- | PROPAGATE ROUTES bool { RADV_CFG->propagate_routes = $3; }
  ;
 
 radv_proto_opts:
@@ -72,12 +71,13 @@ radv_proto_opts:
 radv_proto:
    radv_proto_start proto_name '{' radv_proto_opts '}';
 
-
 radv_iface_start:
 {
   this_ipatt = cfg_allocz(sizeof(struct radv_iface_config));
+  this_radv_trigger_list = &RADV_IFACE->trigger_list;
   add_tail(&RADV_CFG->patt_list, NODE this_ipatt);
   init_list(&this_ipatt->ipn_list);
+  init_list(&RADV_IFACE->trigger_list);
   init_list(&RADV_IFACE->pref_list);
   init_list(&RADV_IFACE->rdnss_list);
   init_list(&RADV_IFACE->dnssl_list);
@@ -94,10 +94,12 @@ radv_iface_start:
   RADV_IFACE->route_lifetime = -1;
   RADV_IFACE->route_lifetime_sensitive = 0;
   RADV_IFACE->route_preference = RA_PREF_MEDIUM;
+  RADV_IFACE->propagate_routes = 0;
 };
 
 radv_iface_item:
-   MIN RA INTERVAL expr { RADV_IFACE->min_ra_int = $4; if ($4 < 3) cf_error("Min RA interval must be at least 3"); }
+   TRIGGER radv_triggers
+ | MIN RA INTERVAL expr { RADV_IFACE->min_ra_int = $4; if ($4 < 3) cf_error("Min RA interval must be at least 3"); }
  | MAX RA INTERVAL expr { RADV_IFACE->max_ra_int = $4; if (($4 < 4) || ($4 > 1800)) cf_error("Max RA interval must be in range 4-1800"); }
  | MIN DELAY expr { RADV_IFACE->min_delay = $3; if ($3 <= 0) cf_error("Min delay must be positive"); }
  | MANAGED bool { RADV_IFACE->managed = $2; }
@@ -124,6 +126,13 @@ radv_iface_item:
  | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); }
  | RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; }
  | DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; }
+ | PROPAGATE ROUTES bool radv_sensitive {
+     RADV_IFACE->propagate_routes = $3;
+     if ($4 != -1) RADV_IFACE->propagate_routes_sensitive = $4;
+   }
+ | PREFIX SENSITIVE bool {
+     RADV_IFACE->prefix_sensitive = $3;
+   }
  ;
 
 radv_preference:
@@ -166,10 +175,9 @@ radv_iface_finish:
   if ((ic->route_linger_time > 0) && (ic->route_linger_time < ic->max_ra_int))
     cf_error("Route linger time must be either 0 or at least Max RA interval");
 
-  RADV_CFG->max_linger_time = MAX_(RADV_CFG->max_linger_time, ic->route_linger_time);
+  this_radv_trigger_list = &RADV_CFG->trigger_list;
 };
 
-
 radv_iface_opts:
    /* empty */
  | radv_iface_opts radv_iface_item ';'
@@ -183,6 +191,44 @@ radv_iface_opt_list:
 radv_iface:
   radv_iface_start iface_patt_list_nopx radv_iface_opt_list radv_iface_finish;
 
+radv_triggers:
+   radv_trigger
+ | radv_triggers ',' radv_trigger
+ ;
+
+radv_trigger:
+   prefix radv_trigger_attrs {
+     struct radv_trigger_config *trig;
+     WALK_LIST(trig, *RADV_TRIGGER_LIST)
+     {
+       if ($1.len == trig->pxlen && ipa_equal($1.addr, trig->prefix))
+	 cf_error("Duplicate trigger prefix definition");
+     }
+
+     trig = cfg_allocz(sizeof(struct radv_trigger_config));
+     add_tail(RADV_TRIGGER_LIST, NODE trig);
+
+     trig->prefix = $1.addr;
+     trig->pxlen = $1.len;
+     trig->attrs = $2;
+   }
+ ;
+
+radv_trigger_attrs:
+   /* empty */ { $$ = 0; }
+ | radv_trigger_attrs KEEP bool {
+     if ($3)
+       $$ = $1 | RA_TRIGGER_KEEP;
+     else
+       $$ = $1 & ~RA_TRIGGER_KEEP;
+   }
+ | radv_trigger_attrs LOCAL bool {
+     if ($3)
+       $$ = $1 | RA_TRIGGER_LOCAL;
+     else
+       $$ = $1 & ~RA_TRIGGER_LOCAL;
+   }
+ ;
 
 radv_prefix_start: prefix
 {
@@ -198,8 +244,15 @@ radv_prefix_start: prefix
 
 radv_prefix_item:
    SKIP bool { RADV_PREFIX->skip = $2; }
- | ONLINK bool { RADV_PREFIX->onlink = $2; }
- | AUTONOMOUS bool { RADV_PREFIX->autonomous = $2; }
+ | SENSITIVE bool { RADV_PREFIX->sensitive = $2; }
+ | ONLINK bool radv_sensitive {
+     RADV_PREFIX->onlink = $2;
+     if ($3 != -1) RADV_PREFIX->onlink_sensitive = $3;
+   }
+ | AUTONOMOUS bool radv_sensitive {
+     RADV_PREFIX->autonomous = $2;
+     if ($3 != -1) RADV_PREFIX->autonomous_sensitive = $3;
+   }
  | VALID LIFETIME expr radv_sensitive {
      RADV_PREFIX->valid_lifetime = $3;
      if ($4 != -1) RADV_PREFIX->valid_lifetime_sensitive = $4;
diff --git a/proto/radv/packets.c b/proto/radv/packets.c
index 7d54a82..b9d08d4 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -95,17 +95,15 @@ radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt,
     return -1;
   }
 
-  uint preference = rt->preference_set ? rt->preference : ifa->cf->route_preference;
-  uint lifetime = rt->lifetime_set ? rt->lifetime : ifa->cf->route_lifetime;
-  uint valid = rt->valid && p->valid && (p->active || !ifa->cf->route_lifetime_sensitive);
+  uint valid = rt->valid && p->valid && (ifa->active || !ifa->cf->route_lifetime_sensitive);
 
   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 = preference;
-  opt->lifetime = valid ? htonl(lifetime) : 0;
+  opt->flags = rt->preference;
+  opt->lifetime = valid ? htonl(rt->lifetime) : 0;
 
   /* Copy the relevant part of the prefix */
   ip6_addr px_addr = ip6_hton(rt->n.prefix);
@@ -269,15 +267,15 @@ radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *prefix,
   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) ?
+  op->pxlen = prefix->n.pxlen;
+  op->flags = (pc->onlink && (ifa->active || !pc->onlink_sensitive) ? OPT_PX_ONLINK : 0) |
+    (pc->autonomous && (ifa->active || !pc->autonomous_sensitive) ? OPT_PX_AUTONOMOUS : 0);
+  op->valid_lifetime = (ifa->active || !pc->valid_lifetime_sensitive) ?
     htonl(pc->valid_lifetime) : 0;
-  op->preferred_lifetime = (ifa->ra->active || !pc->preferred_lifetime_sensitive) ?
+  op->preferred_lifetime = (ifa->active || !pc->preferred_lifetime_sensitive) ?
     htonl(pc->preferred_lifetime) : 0;
   op->reserved = 0;
-  op->prefix = prefix->prefix;
+  op->prefix = prefix->n.prefix;
   ipa_hton(op->prefix);
   *buf += sizeof(*op);
 
@@ -294,6 +292,7 @@ radv_prepare_ra(struct radv_iface *ifa)
   struct radv_proto *p = ifa->ra;
   struct radv_config *cf = (struct radv_config *) (p->p.cf);
   struct radv_iface_config *ic = ifa->cf;
+  int err = 0;
 
   char *buf = ifa->sk->tbuf;
   char *bufstart = buf;
@@ -304,7 +303,7 @@ radv_prepare_ra(struct radv_iface *ifa)
   pkt->code = 0;
   pkt->checksum = 0;
   pkt->current_hop_limit = ic->current_hop_limit;
-  pkt->router_lifetime = (p->valid && (p->active || !ic->default_lifetime_sensitive)) ?
+  pkt->router_lifetime = (p->valid && (ifa->active || !ic->default_lifetime_sensitive)) ?
     htons(ic->default_lifetime) : 0;
   pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) |
     (ic->other_config ? OPT_RA_OTHER_CFG : 0) |
@@ -326,46 +325,51 @@ radv_prepare_ra(struct radv_iface *ifa)
   /* Keeping track of first linger timeout */
   ifa->valid_time = TIME_INFINITY;
 
-  struct radv_prefix *px;
-  WALK_LIST(px, ifa->prefixes)
+  FIB_WALK(&ifa->prefixes, node)
   {
     /* Skip invalid prefixes that are past linger timeout but still not pruned */
-    if (!px->valid && (px->changed + ic->prefix_linger_time <= now))
+    struct radv_prefix *pfx = (void *) node;
+    if (!pfx->valid && (pfx->changed + ic->prefix_linger_time <= now))
 	continue;
 
-    if (radv_prepare_prefix(ifa, px, &buf, bufend) < 0)
-      goto done;
+    err = radv_prepare_prefix(ifa, pfx, &buf, bufend);
+    if (err < 0)
+      break;
   }
+  FIB_WALK_END;
+
+  if (err < 0)
+    goto done;
 
-  if (! ic->rdnss_local)
+  if (!ic->rdnss_local)
     if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0)
       goto done;
 
   if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0)
     goto done;
 
-  if (! ic->dnssl_local)
+  if (!ic->dnssl_local)
     if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0)
       goto done;
 
   if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0)
     goto done;
 
-  if (p->fib_up)
+  FIB_WALK(&ifa->routes, node)
   {
-    FIB_WALK(&p->routes, n)
-    {
-      struct radv_route *rt = (void *) n;
-
-      /* Skip invalid routes that are past linger timeout but still not pruned */
-      if (!rt->valid && (rt->changed + ic->route_linger_time <= now))
-	continue;
-
-      if (radv_prepare_route(ifa, rt, &buf, bufend) < 0)
-	goto done;
-    }
-    FIB_WALK_END;
+    /* Skip invalid routes that are past linger timeout but still not pruned */
+    struct radv_route *rt = (void *) node;
+    if (!rt->valid && (rt->changed + ic->route_linger_time <= now))
+      continue;
+
+    err = radv_prepare_route(ifa, rt, &buf, bufend);
+    if (err < 0)
+      break;
   }
+  FIB_WALK_END;
+
+  if (err < 0)
+    goto done;
 
  done:
   ifa->plen = buf - bufstart;
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index 22e4b2f..09af945 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -28,11 +28,14 @@
  * processes asynchronous events (specified 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.
+ * The RAdv protocol could receive routes (through radv_rt_notify()), which it
+ * tracks and assigns device routes as prefixes to the corresponding interface.
+ * A configured trigger route is used to set up the active/inactive state of
+ * of the protocol, which in turn causes updates to interfaces when the settings
+ * change to remove/add prefixes. The routes that are collected by
+ * radv_rt_notify() are also used to export routes to the RA messages, where
+ * the routes that signal device prefixes are filtered on the respective
+ * interface that they are announced on.
  *
  * Supported standards:
  * - RFC 4861 - main RA standard
@@ -40,145 +43,278 @@
  * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
  */
 
-static void radv_prune_prefixes(struct radv_iface *ifa);
-static void radv_prune_routes(struct radv_proto *p);
+/*
+ * Default prefix setting.
+ */
+static struct radv_prefix_config default_prefix = {
+  .onlink = 1,
+  .autonomous = 1,
+  .valid_lifetime = DEFAULT_VALID_LIFETIME,
+  .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
+};
 
-/* Invalidate cached RA packet */
-static inline void radv_invalidate(struct radv_iface *ifa)
-{ ifa->plen = 0; }
+/*
+ * Dead prefix configuration.
+ */
+static struct radv_prefix_config dead_prefix = {
+};
 
-static void
-radv_timer(timer *tm)
+/*
+ * Helper to check whether any trigger route matches the corresponding
+ * prefix and return the specified trigger configuration.
+ */
+static struct radv_trigger_config *
+radv_trigger_match(struct radv_iface *ifa, struct radv_proto_route *rt)
 {
-  struct radv_iface *ifa = tm->data;
-  struct radv_proto *p = ifa->ra;
+  struct radv_config *cf = (struct radv_config *) (ifa->ra->p.cf);
+  struct radv_trigger_config *trig;
 
-  RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
+  WALK_LIST(trig, ifa->cf->trigger_list)
+    if (rt->n.pxlen == trig->pxlen && ipa_equal(rt->n.prefix, trig->prefix) &&
+	(!(trig->attrs & RA_TRIGGER_LOCAL) || rt->iface == ifa->iface))
+      return trig;
 
-  if (ifa->valid_time <= now)
-    radv_invalidate(ifa);
+  WALK_LIST(trig, cf->trigger_list)
+    if (rt->n.pxlen == trig->pxlen && ipa_equal(rt->n.prefix, trig->prefix) &&
+	(!(trig->attrs & RA_TRIGGER_LOCAL) || rt->iface == ifa->iface))
+      return trig;
 
-  if (ifa->prune_time <= now)
-    radv_prune_prefixes(ifa);
+  return NULL;
+}
 
-  if (p->prune_time <= now)
-    radv_prune_routes(p);
+/*
+ * Test whether network matches any trigger.
+ */
+static int
+radv_trigger_match_net(struct radv_iface *ifa, ip_addr *prefix, uint pxlen)
+{
+  struct radv_config *cf = (struct radv_config *) (ifa->ra->p.cf);
+  struct radv_trigger_config *trig;
 
-  radv_send_ra(ifa);
+  WALK_LIST(trig, ifa->cf->trigger_list)
+    if (pxlen == trig->pxlen && ipa_equal(*prefix, trig->prefix))
+      return 1;
 
-  /* Update timer */
-  ifa->last = now;
-  unsigned after = ifa->cf->min_ra_int;
-  after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
+  WALK_LIST(trig, cf->trigger_list)
+    if (pxlen == trig->pxlen && ipa_equal(*prefix, trig->prefix))
+      return 1;
 
-  if (ifa->initial)
-    ifa->initial--;
+  return 0;
+}
 
-  if (ifa->initial)
-    after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
+/*
+ * Helper to check whether all trigger routes are currently active.
+ */
+static int
+radv_trigger_match_all(struct radv_iface *ifa)
+{
+  struct radv_proto *p = ifa->ra;
+  struct radv_config *cf = (struct radv_config *) (p->p.cf);
+  struct radv_trigger_config *trig;
 
-  tm_start(ifa->timer, after);
-}
+  WALK_LIST(trig, ifa->cf->trigger_list)
+  {
+    struct radv_proto_route *rt = fib_find(&p->routes, &trig->prefix, trig->pxlen);
+    if (!rt || ((trig->attrs & RA_TRIGGER_LOCAL) && rt->iface != ifa->iface))
+    {
+      RADV_TRACE(D_EVENTS, "Interface config trigger route %I/%d (attrs %d) on %s missing",
+		 trig->prefix, trig->pxlen, trig->attrs, ifa->iface->name);
+      return 0;
+    }
+  }
 
-static struct radv_prefix_config default_prefix = {
-  .onlink = 1,
-  .autonomous = 1,
-  .valid_lifetime = DEFAULT_VALID_LIFETIME,
-  .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
-};
+  WALK_LIST(trig, cf->trigger_list)
+  {
+    struct radv_proto_route *rt = fib_find(&p->routes, &trig->prefix, trig->pxlen);
+    if (!rt || ((trig->attrs & RA_TRIGGER_LOCAL) && rt->iface != ifa->iface))
+    {
+      RADV_TRACE(D_EVENTS, "Global config trigger route %I/%d (attrs %d) on %s missing",
+		 trig->prefix, trig->pxlen, trig->attrs, ifa->iface->name);
+      return 0;
+    }
+  }
 
-static struct radv_prefix_config dead_prefix = {
-};
+  /* All trigger routes matched. */
+  RADV_TRACE(D_EVENTS, "All triggers on %s match", ifa->iface->name);
+  return 1;
+}
 
-/* Find a corresponding config for the given prefix */
+/*
+ * Find a corresponding config for the given prefix.
+ */
 static struct radv_prefix_config *
-radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
+radv_prefix_match(struct radv_iface *ifa, byte scope, ip_addr *prefix, uint pxlen, struct radv_prefix_config *def)
 {
-  struct radv_proto *p = ifa->ra;
-  struct radv_config *cf = (struct radv_config *) (p->p.cf);
+  struct radv_config *cf = (struct radv_config *) (ifa->ra->p.cf);
   struct radv_prefix_config *pc;
 
-  if (a->scope <= SCOPE_LINK)
+  if (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))
+    if ((pxlen >= pc->pxlen) && ipa_in_net(*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))
+    if ((pxlen >= pc->pxlen) && ipa_in_net(*prefix, pc->prefix, pc->pxlen))
       return pc;
 
-  return &default_prefix;
+  return def;
 }
 
 /*
- * Go through the list of prefixes, compare them with configs and decide if we
- * want them or not.
+ * Prepare recorded prefixes for updates.
  */
 static void
-radv_prepare_prefixes(struct radv_iface *ifa)
+radv_prepare_update_iface(struct radv_iface *ifa)
+{
+  FIB_WALK(&ifa->prefixes, node)
+  {
+    /* Mark all prefixes as unused. */
+    struct radv_prefix *pfx = (void *) node;
+    pfx->mark = 0;
+  }
+  FIB_WALK_END;
+
+  FIB_WALK(&ifa->routes, node)
+  {
+    /* Mark all routes as unused. */
+    struct radv_route *rt = (void *) node;
+    rt->mark = 0;
+  }
+  FIB_WALK_END;
+}
+
+/*
+ * Prepare prefix on an interface, attaching the prefix if it's valid and can
+ * be attached to the interface.
+ */
+static struct radv_prefix *
+radv_prepare_prefix(struct radv_iface *ifa, byte scope, ip_addr *prefix, uint pxlen, struct radv_prefix_config *def)
 {
   struct radv_proto *p = ifa->ra;
-  struct radv_prefix *pfx, *next;
 
-  /* First mark all the prefixes as unused */
-  WALK_LIST(pfx, ifa->prefixes)
-    pfx->mark = 0;
+  struct radv_prefix_config *pc = radv_prefix_match(ifa, scope, prefix, pxlen, def);
+  if (!pc || pc->skip || ((ifa->cf->prefix_sensitive || pc->sensitive) && !ifa->active))
+    return NULL;
+
+  struct radv_prefix *pfx = fib_get(&ifa->prefixes, prefix, pxlen);
+  if (!pfx->valid)
+    RADV_TRACE(D_EVENTS, "Refreshing/adding prefix %I/%d on %s",
+	       *prefix, pxlen, ifa->iface->name);
+
+  pfx->valid = 1;
+  pfx->mark = 1;
+  pfx->cf = pc;
+  pfx->changed = now;
+  return pfx;
+}
 
+/*
+ * Prepare route on interface to send out via RADV packets.
+ */
+static struct radv_route *
+radv_prepare_route(struct radv_iface *ifa, struct radv_proto_route *prt)
+{
+  struct radv_proto *p = ifa->ra;
+
+  if (ifa->cf->propagate_routes_sensitive && !ifa->active)
+    return NULL;
+
+  struct radv_route *rt = fib_get(&ifa->routes, &prt->n.prefix, prt->n.pxlen);
+  if (!rt->valid)
+    RADV_TRACE(D_EVENTS, "Refreshing/adding route %I/%d on %s",
+	       prt->n.prefix, prt->n.pxlen, ifa->iface->name);
+
+  rt->valid = 1;
+  rt->mark = 1;
+  rt->preference = prt->preference_set ? prt->preference : ifa->cf->route_preference;
+  rt->lifetime = prt->lifetime_set ? prt->lifetime : ifa->cf->route_lifetime;
+  rt->changed = now;
+  return rt;
+}
+
+/*
+ * Go through the list of prefixes, compare them with configs and decide if we
+ * want them or not.
+ */
+static void
+radv_prepare_iface_prefixes(struct radv_iface *ifa)
+{
   /* 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);
+    radv_prepare_prefix(ifa, addr->scope, &addr->prefix, addr->pxlen, &default_prefix);
+}
 
-    if (!pc || pc->skip)
+/*
+ * Go through announced device routes, deciding whether we want them or not.
+ */
+static void
+radv_prepare_iface_routes(struct radv_iface *ifa)
+{
+  /* Find all prefixes that exist as device routes. */
+  FIB_WALK(&ifa->ra->routes, node)
+  {
+    /* Invalid prefixes and explicitly rejected trigger routes don't make it here */
+    struct radv_proto_route *rt = (void *) node;
+    struct radv_trigger_config *trig = radv_trigger_match(ifa, rt);
+    if (trig && !(trig->attrs & RA_TRIGGER_KEEP))
       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);
-    }
+    if (rt->iface == ifa->iface && radv_prepare_prefix(ifa, rt->scope, &rt->n.prefix, rt->n.pxlen, NULL))
+      continue;
 
-    /*
-     * Update the information (it may have changed, or even bring a prefix back
-     * to life).
-     */
-    existing->valid = 1;
-    existing->changed = now;
-    existing->mark = 1;
-    existing->cf = pc;
+    if (ifa->cf->propagate_routes)
+      radv_prepare_route(ifa, rt);
   }
+  FIB_WALK_END;
+}
 
-  WALK_LIST_DELSAFE(pfx, next, ifa->prefixes)
+/*
+ * Finish updating prefix by resetting all prefixes that are now in the timeout
+ * state.
+ */
+static void
+radv_finish_update_iface(struct radv_iface *ifa)
+{
+  struct radv_proto *p = ifa->ra;
+
+  /* Clean up prefixes that are no longer valid. */
+  FIB_WALK(&ifa->prefixes, node)
   {
+    struct radv_prefix *pfx = (void *) node;
     if (pfx->valid && !pfx->mark)
     {
       RADV_TRACE(D_EVENTS, "Invalidating prefix %I/%d on %s",
-		 pfx->prefix, pfx->len, ifa->iface->name);
+		 pfx->n.prefix, pfx->n.pxlen, ifa->iface->name);
 
       pfx->valid = 0;
       pfx->changed = now;
       pfx->cf = &dead_prefix;
     }
   }
+  FIB_WALK_END;
+
+  /* Clean up routes that are no longer valid. */
+  FIB_WALK(&ifa->routes, node)
+  {
+    struct radv_route *rt = (void *) node;
+    if (rt->valid && !rt->mark)
+    {
+      RADV_TRACE(D_EVENTS, "Invalidating route %I/%d on %s",
+		 rt->n.prefix, rt->n.pxlen, ifa->iface->name);
+
+      rt->valid = 0;
+      rt->changed = now;
+    }
+  }
+  FIB_WALK_END;
 }
 
+/*
+ * Prune prefixes that are no longer valid.
+ */
 static void
 radv_prune_prefixes(struct radv_iface *ifa)
 {
@@ -186,29 +322,116 @@ radv_prune_prefixes(struct radv_iface *ifa)
   bird_clock_t next = TIME_INFINITY;
   bird_clock_t expires = 0;
 
-  struct radv_prefix *px, *pxn;
-  WALK_LIST_DELSAFE(px, pxn, ifa->prefixes)
+  struct fib_iterator fit;
+  FIB_ITERATE_INIT(&fit, &ifa->prefixes);
+
+again:
+  FIB_ITERATE_START(&ifa->prefixes, &fit, node)
   {
-    if (!px->valid)
+    struct radv_prefix *pfx = (void *) node;
+    if (!pfx->valid)
     {
-      expires = px->changed + ifa->cf->prefix_linger_time;
-
+      expires = pfx->changed + ifa->cf->prefix_linger_time;
       if (expires <= now)
       {
 	RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s",
-		   px->prefix, px->len, ifa->iface->name);
+		   pfx->n.prefix, pfx->n.pxlen, ifa->iface->name);
+	FIB_ITERATE_PUT(&fit, node);
+	fib_delete(&ifa->prefixes, node);
+	goto again;
+      }
+      else
+	next = MIN(next, expires);
+    }
+  }
+  FIB_ITERATE_END(node);
+
+  ifa->prune_time = next;
+}
 
-	rem_node(NODE px);
-	mb_free(px);
+/*
+ * 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_iface *ifa)
+{
+  struct radv_proto *p = ifa->ra;
+  bird_clock_t next = TIME_INFINITY;
+  bird_clock_t expires = 0;
+
+  struct fib_iterator fit;
+  FIB_ITERATE_INIT(&fit, &ifa->routes);
+
+again:
+  FIB_ITERATE_START(&ifa->routes, &fit, node)
+  {
+    struct radv_route *rt = (void *) node;
+    if (!rt->valid)
+    {
+      expires = rt->changed + ifa->cf->route_linger_time;
+      if (expires <= now)
+      {
+	RADV_TRACE(D_EVENTS, "Removing route %I/%d on %s",
+		   rt->n.prefix, rt->n.pxlen, ifa->iface->name);
+	FIB_ITERATE_PUT(&fit, node);
+	fib_delete(&ifa->routes, node);
+	goto again;
       }
       else
 	next = MIN(next, expires);
     }
   }
+  FIB_ITERATE_END(node);
 
   ifa->prune_time = next;
 }
 
+/*
+ * Invalidate cached RA packet.
+ */
+static inline void
+radv_invalidate(struct radv_iface *ifa)
+{
+  ifa->plen = 0;
+}
+
+/*
+ * Timer callback for interface to handle packet adjustments.
+ */
+static void
+radv_timer(timer *tm)
+{
+  struct radv_iface *ifa = tm->data;
+  struct radv_proto *p = ifa->ra;
+
+  RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
+
+  if (ifa->valid_time <= now)
+    radv_invalidate(ifa);
+
+  if (ifa->prune_time <= now)
+  {
+    radv_prune_prefixes(ifa);
+    radv_prune_routes(ifa);
+  }
+
+  radv_send_ra(ifa);
+
+  /* Update timer */
+  ifa->last = now;
+  unsigned after = ifa->cf->min_ra_int;
+  after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
+
+  if (ifa->initial)
+    ifa->initial--;
+
+  if (ifa->initial)
+    after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
+
+  tm_start(ifa->timer, after);
+}
+
 static char* ev_name[] = { NULL, "Init", "Change", "RS" };
 
 void
@@ -227,8 +450,12 @@ radv_iface_notify(struct radv_iface *ifa, int event)
     radv_invalidate(ifa);
   case RA_EV_INIT:
     ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
-    radv_prepare_prefixes(ifa);
+    radv_prepare_update_iface(ifa);
+    radv_prepare_iface_prefixes(ifa);
+    radv_prepare_iface_routes(ifa);
+    radv_finish_update_iface(ifa);
     radv_prune_prefixes(ifa);
+    radv_prune_routes(ifa);
     break;
 
   case RA_EV_RS:
@@ -245,15 +472,6 @@ radv_iface_notify(struct radv_iface *ifa, int event)
   tm_start(ifa->timer, after);
 }
 
-static void
-radv_iface_notify_all(struct radv_proto *p, int event)
-{
-  struct radv_iface *ifa;
-
-  WALK_LIST(ifa, p->iface_list)
-    radv_iface_notify(ifa, event);
-}
-
 static struct radv_iface *
 radv_iface_find(struct radv_proto *p, struct iface *what)
 {
@@ -272,7 +490,7 @@ radv_iface_add(struct object_lock *lock)
   struct radv_iface *ifa = lock->data;
   struct radv_proto *p = ifa->ra;
 
-  if (! radv_sk_open(ifa))
+  if (!radv_sk_open(ifa))
   {
     log(L_ERR "%s: Socket open failed on interface %s", p->p.name, ifa->iface->name);
     return;
@@ -284,6 +502,7 @@ radv_iface_add(struct object_lock *lock)
 static void
 radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf)
 {
+  struct radv_config *pcf = (struct radv_config *) (p->p.cf);
   struct radv_iface *ifa;
 
   RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
@@ -295,8 +514,10 @@ radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_conf
   ifa->cf = cf;
   ifa->iface = iface;
   ifa->addr = iface->llv6;
-  init_list(&ifa->prefixes);
+  fib_init(&ifa->prefixes, pool, sizeof(struct radv_prefix), 0, NULL);
+  fib_init(&ifa->routes, pool, sizeof(struct radv_route), 0, NULL);
   ifa->prune_time = TIME_INFINITY;
+  ifa->active = radv_trigger_match_all(ifa);
 
   add_tail(&p->iface_list, NODE ifa);
 
@@ -384,67 +605,26 @@ radv_ifa_notify(struct proto *P, unsigned flags UNUSED, 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 radv_proto *p = (struct radv_proto *) P;
-  struct radv_config *cf = (struct radv_config *) (P->cf);
-
-  if (radv_net_match_trigger(cf, (*new)->net))
-    return RIC_PROCESS;
-
-  if (cf->propagate_routes)
-    return RIC_PROCESS;
-  else
-    return RIC_DROP;
-}
-
 static void
 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_config *cf = (struct radv_config *) (P->cf);
-  struct radv_route *rt;
+  struct radv_proto_route *rt;
+  struct radv_iface *oldifa = NULL;
+  struct radv_iface *newifa = NULL;
   eattr *ea;
 
-  if (radv_net_match_trigger(cf, n))
-  {
-    u8 old_active = p->active;
-    p->active = !!new;
-
-    if (p->active == old_active)
-      return;
-
-    if (p->active)
-      RADV_TRACE(D_EVENTS, "Triggered");
-    else
-      RADV_TRACE(D_EVENTS, "Suppressed");
-
-    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 */
+    RADV_TRACE(D_EVENTS, "Notified of new route %I/%d", n->n.prefix, n->n.pxlen);
 
+    /* Update */
     ea = ea_find(attrs, EA_RA_PREFERENCE);
     uint preference = ea ? ea->u.data : RA_PREF_MEDIUM;
     uint preference_set = !!ea;
@@ -465,21 +645,27 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
       lifetime_set = 1;
     }
 
-    rt = fib_get(&p->routes, &n->n.prefix, n->n.pxlen);
+    u8 prefixroute = new->attrs->dest == RTD_DEVICE && new->attrs->cast == RTC_UNICAST;
+    struct iface *iface = prefixroute ? new->attrs->iface : NULL;
 
     /* Ignore update if nothing changed */
-    if (rt->valid &&
+    rt = fib_get(&p->routes, &n->n.prefix, n->n.pxlen);
+    if ((rt->iface == iface) &&
+	(rt->scope == new->attrs->scope) &&
 	(rt->preference == preference) &&
 	(rt->preference_set == preference_set) &&
 	(rt->lifetime == lifetime) &&
 	(rt->lifetime_set == lifetime_set))
       return;
 
+    oldifa = radv_iface_find(p, rt->iface);
+    newifa = radv_iface_find(p, iface);
+
     if (p->routes.entries == 18)
       log(L_WARN "%s: More than 17 routes exported to RAdv", p->p.name);
 
-    rt->valid = 1;
-    rt->changed = now;
+    rt->iface = iface;
+    rt->scope = new->attrs->scope;
     rt->preference = preference;
     rt->preference_set = preference_set;
     rt->lifetime = lifetime;
@@ -487,77 +673,41 @@ radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
   }
   else
   {
-    /* Withdraw */
-    rt = fib_find(&p->routes, &n->n.prefix, n->n.pxlen);
+    RADV_TRACE(D_EVENTS, "Notified of deleted route %I/%d", n->n.prefix, n->n.pxlen);
 
-    if (!rt || !rt->valid)
+    /* Withdraw if route is currently present */
+    rt = fib_find(&p->routes, &n->n.prefix, n->n.pxlen);
+    if (!rt)
       return;
 
-    /* Invalidate the route */
-    rt->valid = 0;
-    rt->changed = now;
-
-    /* Invalidated route will be pruned eventually */
-    bird_clock_t expires = rt->changed + cf->max_linger_time;
-    p->prune_time = MIN(p->prune_time, expires);
+    oldifa = radv_iface_find(p, rt->iface);
+    fib_delete(&p->routes, rt);
   }
 
-  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)
-{
-  struct radv_config *cf = (struct radv_config *) (p->p.cf);
-  bird_clock_t next = TIME_INFINITY;
-  bird_clock_t expires = 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_iface *ifa;
+  WALK_LIST(ifa, p->iface_list)
   {
-    struct radv_route *rt = (void *) node;
+    if (radv_trigger_match_net(ifa, &n->n.prefix, n->n.pxlen))
+    {
+      RADV_TRACE(D_EVENTS, "Updating active flag for %s", ifa->iface->name);
+      ifa->active = radv_trigger_match_all(ifa);
+      radv_iface_notify(ifa, RA_EV_CHANGE);
+      continue;
+    }
 
-    if (!rt->valid)
+    if (ifa->cf->propagate_routes)
     {
-      expires = rt->changed + cf->max_linger_time;
+      RADV_TRACE(D_EVENTS, "Updating propagated routes for %s", ifa->iface->name);
+      radv_iface_notify(ifa, RA_EV_CHANGE);
+      continue;
+    }
 
-      /* Delete expired nodes */
-      if (expires <= now)
-      {
-	FIB_ITERATE_PUT(&fit, node);
-	fib_delete(&p->routes, node);
-	goto again;
-      }
-      else
-	next = MIN(next, expires);
+    if (oldifa == ifa || newifa == ifa)
+    {
+      RADV_TRACE(D_EVENTS, "Updating interface route for %s", ifa->iface->name);
+      radv_iface_notify(ifa, RA_EV_CHANGE);
     }
   }
-  FIB_ITERATE_END(node);
-
-  p->prune_time = next;
-}
-
-static int
-radv_check_active(struct radv_proto *p)
-{
-  struct radv_config *cf = (struct radv_config *) (p->p.cf);
-
-  if (! cf->trigger_valid)
-    return 1;
-
-  return rt_examine(p->p.table, cf->trigger_prefix, cf->trigger_pxlen,
-		    &(p->p), p->p.cf->out_filter);
 }
 
 static struct proto *
@@ -566,7 +716,6 @@ radv_init(struct proto_config *c)
   struct proto *P = proto_new(c, sizeof(struct radv_proto));
 
   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;
@@ -574,21 +723,6 @@ radv_init(struct proto_config *c)
   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
 radv_start(struct proto *P)
 {
@@ -597,11 +731,7 @@ radv_start(struct proto *P)
 
   init_list(&(p->iface_list));
   p->valid = 1;
-  p->active = !cf->trigger_valid;
-
-  p->fib_up = 0;
-  radv_set_fib(p, cf->propagate_routes);
-  p->prune_time = TIME_INFINITY;
+  fib_init(&p->routes, p->p.pool, sizeof(struct radv_proto_route), 0, NULL);
 
   return PS_UP;
 }
@@ -637,15 +767,7 @@ radv_reconfigure(struct proto *P, struct proto_config *c)
   struct radv_config *old = (struct radv_config *) (P->cf);
   struct radv_config *new = (struct radv_config *) c;
 
-  P->cf = c; /* radv_check_active() requires proper P->cf */
-  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);
+  P->cf = c;
 
   struct iface *iface;
   WALK_LIST(iface, iface_list)
@@ -654,22 +776,21 @@ radv_reconfigure(struct proto *P, struct proto_config *c)
     struct radv_iface_config *ic = (struct radv_iface_config *)
       iface_patt_find(&new->patt_list, iface, NULL);
 
-    if (ifa && ic)
+    if (ifa)
     {
-      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 (ic)
+      {
+	ifa->cf = ic;
+	radv_iface_notify(ifa, RA_EV_CHANGE);
+      }
+      else
+      {
+	radv_iface_shutdown(ifa);
+	radv_iface_remove(ifa);
+      }
     }
-
-    if (!ifa && ic)
+    else if (ic)
+      /* TODO: check whether this requires same tests as if_notify */
       radv_iface_new(p, iface, ic);
   }
 
@@ -685,17 +806,16 @@ radv_copy_config(struct proto_config *dest, struct proto_config *src)
   /* We clean up patt_list, ifaces are non-sharable */
   init_list(&d->patt_list);
 
-  /* We copy pref_list, shallow copy suffices */
+  /* Copy elements in config. TODO: check dnssl_list */
+  cfg_copy_list(&d->trigger_list, &s->trigger_list, sizeof(struct radv_trigger_config));
   cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
+  cfg_copy_list(&d->rdnss_list, &s->rdnss_list, sizeof(struct radv_rdnss_config));
 }
 
 static void
 radv_get_status(struct proto *P, byte *buf)
 {
   struct radv_proto *p = (struct radv_proto *) P;
-
-  if (!p->active)
-    strcpy(buf, "Suppressed");
 }
 
 static const char *
diff --git a/proto/radv/radv.h b/proto/radv/radv.h
index ab08139..fc0153e 100644
--- a/proto/radv/radv.h
+++ b/proto/radv/radv.h
@@ -46,20 +46,16 @@ struct radv_config
 {
   struct proto_config c;
   list patt_list;		/* List of iface configs (struct radv_iface_config) */
+  list trigger_list;		/* List of trigger routes (struct radv_trigger) */
   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 */
-  u8 propagate_routes;		/* Do we propagate more specific routes (RFC 4191)? */
-  u32 max_linger_time;		/* Maximum of interface route_linger_time */
 };
 
 struct radv_iface_config
 {
   struct iface_patt i;
+  list trigger_list;		/* List of trigger routes (struct radv_trigger) */
   list pref_list;		/* Local list of prefix configs (struct radv_prefix_config) */
   list rdnss_list;		/* Local list of RDNSS configs (struct radv_rdnss_config) */
   list dnssl_list;		/* Local list of DNSSL configs (struct radv_dnssl_config) */
@@ -86,6 +82,18 @@ struct radv_iface_config
   u8 route_lifetime_sensitive;	/* Whether route_lifetime depends on trigger */
   u8 default_preference;	/* Default Router Preference (RFC 4191) */
   u8 route_preference;		/* Specific Route Preference (RFC 4191) */
+  u8 propagate_routes;		/* Do we propagate more specific routes (RFC 4191)? */
+  u8 prefix_sensitive;		/* Prefixes are sensitive to trigger */
+  u8 propagate_routes_sensitive; /* Propagated routes are sensitive to trigger */
+};
+
+struct radv_trigger_config
+{
+  node n;
+  ip_addr prefix;		/* Prefix of a trigger route, if defined */
+  uint pxlen;			/* Pxlen of a trigger route, if defined */
+
+  uint attrs;			/* Trigger attributes */
 };
 
 struct radv_prefix_config
@@ -94,11 +102,14 @@ struct radv_prefix_config
   ip_addr prefix;
   uint pxlen;
 
-  u8 skip;			/* Do not include this prefix to RA */
+  u8 skip;			/* Do not include this prefix to RA ever */
+  u8 sensitive;			/* Prefix is sensitive */
   u8 onlink;			/* Standard options from RFC 4861 */
   u8 autonomous;
   u32 valid_lifetime;
   u32 preferred_lifetime;
+  u8 onlink_sensitive;		/* Onlink flag is trigger sensitive */
+  u8 autonomous_sensitive;	/* Autonomous flag is trigger sensitive */
   u8 valid_lifetime_sensitive;	 /* Whether valid_lifetime depends on trigger */
   u8 preferred_lifetime_sensitive; /* Whether preferred_lifetime depends on trigger */
 };
@@ -128,38 +139,24 @@ struct radv_dnssl_config
  * 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;		/* Whether lifetime is defined */
-  u8 preference;		/* Preference of the route, RA_PREF_* */
-  u8 preference_set;		/* Whether preference is defined */
-  u8 valid;			/* Whethe route is valid or withdrawn */
-  bird_clock_t changed;		/* Last time when the route changed */
-};
 
 struct radv_proto
 {
   struct proto p;
   list iface_list;		/* List of active ifaces */
+  struct fib routes;		/* FIB table of active protocol routes (struct radv_proto_route) */
   u8 valid;			/* Router is valid for forwarding, used for shutdown */
-  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_proto_route
 {
-  node n;
-  ip_addr prefix;
-  u8 len;
-  u8 valid;			/* Is the prefix valid? If not, we advertise it
-				   with 0 lifetime, so clients stop using it */
-  u8 mark;			/* A temporary mark for processing */
-  bird_clock_t changed;		/* Last time when the prefix changed */
-  struct radv_prefix_config *cf; /* The config tied to this prefix */
+  struct fib_node n;
+  struct iface *iface;		/* Interface route is bound to */
+  byte scope;			/* Scope of the route */
+  u32 lifetime;			/* Lifetime from an attribute */
+  u8 lifetime_set;		/* Whether lifetime is defined */
+  u8 preference;		/* Preference of the route, RA_PREF_* */
+  u8 preference_set;		/* Whether preference is defined */
 };
 
 struct radv_iface
@@ -170,8 +167,9 @@ struct radv_iface
   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 prune_time;	/* Next time of prefix list pruning */
+  struct fib prefixes;		/* The prefixes we advertise (struct radv_prefix) */
+  struct fib routes;		/* The routes we advertise (struct radv_route) */
+  bird_clock_t prune_time;	/* Next time of prefix/route pruning */
   bird_clock_t valid_time;	/* Cached packet is valid until first linger timeout */
 
   timer *timer;
@@ -181,6 +179,27 @@ 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;			/* How many RAs are still to be sent as initial */
+  u8 active;			/* Whether iface is active w.r.t. triggers */
+};
+
+struct radv_prefix		/* One prefix we advertise */
+{
+  struct fib_node n;
+  u8 valid;			/* Is the prefix valid? If not, we advertise it
+				   with 0 lifetime, so clients stop using it */
+  u8 mark;			/* A temporary mark for processing */
+  bird_clock_t changed;		/* Last time when the prefix changed */
+  struct radv_prefix_config *cf; /* The config tied to this prefix */
+};
+
+struct radv_route		/* One specific device route */
+{
+  struct fib_node n;
+  u32 lifetime;			/* Lifetime from an attribute */
+  u8 preference;		/* Preference of the route, RA_PREF_* */
+  u8 valid;			/* Whether route is valid or withdrawn */
+  u8 mark;			/* A temporary mark for processing */
+  bird_clock_t changed;		/* Last time when the route changed */
 };
 
 #define RA_EV_INIT 1		/* Switch to initial mode */
@@ -188,10 +207,14 @@ struct radv_iface
 #define RA_EV_RS 3		/* Received RS */
 
 /* Default Router Preferences (RFC 4191) */
-#define RA_PREF_LOW	0x18
-#define RA_PREF_MEDIUM	0x00
-#define RA_PREF_HIGH	0x08
-#define RA_PREF_MASK	0x18
+#define RA_PREF_LOW		0x18
+#define RA_PREF_MEDIUM		0x00
+#define RA_PREF_HIGH		0x08
+#define RA_PREF_MASK		0x18
+
+/* Trigger attributes */
+#define RA_TRIGGER_KEEP		0x01
+#define RA_TRIGGER_LOCAL	0x02
 
 /* Attributes */
 #define EA_RA_PREFERENCE	EA_CODE(EAP_RADV, 0)
@@ -214,6 +237,4 @@ int radv_process_domain(struct radv_dnssl_config *cf);
 void radv_send_ra(struct radv_iface *ifa);
 int radv_sk_open(struct radv_iface *ifa);
 
-
-
 #endif /* _BIRD_RADV_H_ */
