]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - proto/rip/rip.c
RIP: Fix handling of passive mode for demand circuit interfaces
[thirdparty/bird.git] / proto / rip / rip.c
index 157093aabaf3babaa01d07d6ba24e89032cc2061..d18ff5adfab6e06b09cf85e5aa5686f83a7d99ec 100644 (file)
@@ -167,7 +167,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
        struct nexthop *nh = allocz(sizeof(struct nexthop));
 
        nh->gw = rt->next_hop;
-       nh->iface = rt->from->nbr->iface;
+       nh->iface = rt->from->ifa->iface;
        nh->weight = rt->from->ifa->cf->ecmp_weight;
 
        nexthop_insert(&nhs, nh);
@@ -184,7 +184,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
       /* Unipath route */
       a0.from = rt->from->nbr->addr;
       a0.nh.gw = rt->next_hop;
-      a0.nh.iface = rt->from->nbr->iface;
+      a0.nh.iface = rt->from->ifa->iface;
     }
 
     rta *a = rta_lookup(&a0);
@@ -193,8 +193,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
     e->u.rip.from = a0.nh.iface;
     e->u.rip.metric = rt_metric;
     e->u.rip.tag = rt_tag;
-
-    e->pflags = 0;
+    e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
 
     rte_update(&p->p, en->n.addr, e);
   }
@@ -298,7 +297,7 @@ rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from)
  */
 static void
 rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, struct rte *new,
-             struct rte *old UNUSED, struct ea_list *attrs)
+             struct rte *old UNUSED)
 {
   struct rip_proto *p = (struct rip_proto *) P;
   struct rip_entry *en;
@@ -307,8 +306,8 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
   if (new)
   {
     /* Update */
-    u32 rt_metric = ea_get_int(attrs, EA_RIP_METRIC, 1);
-    u32 rt_tag = ea_get_int(attrs, EA_RIP_TAG, 0);
+    u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
+    u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0);
 
     if (rt_metric > p->infinity)
     {
@@ -364,11 +363,25 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
   /* Activate triggered updates */
   if (en->metric != old_metric)
   {
-    en->changed = now;
+    en->changed = current_time();
     rip_trigger_update(p);
   }
 }
 
+void
+rip_flush_table(struct rip_proto *p, struct rip_neighbor *n)
+{
+  btime expires = current_time() + n->ifa->cf->timeout_time;
+
+  FIB_WALK(&p->rtable, struct rip_entry, en)
+  {
+    for (struct rip_rte *e = en->routes; e; e = e->next)
+      if ((e->from == n) && (e->expires == TIME_INFINITY))
+       e->expires = expires;
+  }
+  FIB_WALK_END;
+}
+
 
 /*
  *     RIP neighbors
@@ -377,7 +390,7 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
 struct rip_neighbor *
 rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa)
 {
-  neighbor *nbr = neigh_find2(&p->p, a, ifa->iface, 0);
+  neighbor *nbr = neigh_find(&p->p, *a, ifa->iface, 0);
 
   if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa))
     return NULL;
@@ -403,7 +416,7 @@ rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n)
 {
   neighbor *nbr = n->nbr;
 
-  TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->iface->name);
+  TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->ifreq->name);
 
   rem_node(NODE n);
   n->ifa = NULL;
@@ -484,7 +497,8 @@ rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n)
      */
     ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip;
     n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr,
-                                    n->nbr->iface, rip_bfd_notify, n);
+                                    n->nbr->iface, p->p.vrf,
+                                    rip_bfd_notify, n);
   }
 
   if (!use_bfd && n->bfd_req)
@@ -506,14 +520,23 @@ rip_iface_start(struct rip_iface *ifa)
 
   TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name);
 
-  ifa->next_regular = now + (random() % ifa->cf->update_time) + 1;
-  ifa->next_triggered = now;   /* Available immediately */
-  ifa->want_triggered = 1;     /* All routes in triggered update */
-  tm_start(ifa->timer, 1);     /* Or 100 ms */
+  if (! ifa->cf->demand_circuit)
+  {
+    ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS;
+    tm_set(ifa->timer, ifa->next_regular);
+  }
+  else
+  {
+    ifa->next_regular = TIME_INFINITY;
+  }
+
   ifa->up = 1;
 
-  if (!ifa->cf->passive)
-    rip_send_request(ifa->rip, ifa);
+  if (ifa->cf->passive)
+    return;
+
+  rip_send_request(p, ifa);
+  rip_send_table(p, ifa, ifa->addr, 0);
 }
 
 static void
@@ -526,10 +549,24 @@ rip_iface_stop(struct rip_iface *ifa)
 
   rip_reset_tx_session(p, ifa);
 
+  ifa->next_regular = 0;
+  ifa->next_triggered = 0;
+  ifa->want_triggered = 0;
+
+  if (ifa->tx_pending)
+    ifa->tx_seqnum++;
+
+  ifa->tx_pending = 0;
+  ifa->req_pending = 0;
+
+  if (ifa->cf->demand_circuit && !ifa->cf->passive)
+    rip_send_flush(p, ifa);
+
   WALK_LIST_FIRST(n, ifa->neigh_list)
     rip_remove_neighbor(p, n);
 
   tm_stop(ifa->timer);
+  tm_stop(ifa->rxmt_timer);
   ifa->up = 0;
 }
 
@@ -630,13 +667,20 @@ rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config
   else if (ic->mode == RIP_IM_MULTICAST)
     ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS;
   else /* Broadcast */
-    ifa->addr = iface->addr->brd;
+    ifa->addr = iface->addr4->brd;
+  /*
+   * The above is just a workaround for BSD as it can't send broadcasts
+   * to 255.255.255.255. BSD systems need the network broadcast address instead.
+   *
+   * TODO: move this to sysdep code
+   */
 
   init_list(&ifa->neigh_list);
 
   add_tail(&p->iface_list, NODE ifa);
 
-  ifa->timer = tm_new_set(p->p.pool, rip_iface_timer, ifa, 0, 0);
+  ifa->timer = tm_new_init(p->p.pool, rip_iface_timer, ifa, 0, 0);
+  ifa->rxmt_timer = tm_new_init(p->p.pool, rip_rxmt_timeout, ifa, 0, 0);
 
   struct object_lock *lock = olock_new(p->p.pool);
   lock->type = OBJLOCK_UDP;
@@ -675,7 +719,8 @@ rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_ifa
       (new->port != old->port) ||
       (new->tx_tos != old->tx_tos) ||
       (new->tx_priority != old->tx_priority) ||
-      (new->ttl_security != old->ttl_security))
+      (new->ttl_security != old->ttl_security) ||
+      (new->demand_circuit != old->demand_circuit))
     return 0;
 
   TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name);
@@ -684,8 +729,20 @@ rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_ifa
 
   rip_iface_update_buffers(ifa);
 
-  if (ifa->next_regular > (now + new->update_time))
-    ifa->next_regular = now + (random() % new->update_time) + 1;
+  if ((! ifa->cf->demand_circuit) &&
+      (ifa->next_regular > (current_time() + new->update_time)))
+    ifa->next_regular = current_time() + (random() % new->update_time) + 100 MS;
+
+  if (ifa->up && new->demand_circuit && (new->passive != old->passive))
+  {
+    if (new->passive)
+      rip_send_flush(p, ifa);
+    else
+    {
+      rip_send_request(p, ifa);
+      rip_send_table(p, ifa, ifa->addr, 0);
+    }
+  }
 
   if (new->check_link != old->check_link)
     rip_iface_update_state(ifa);
@@ -706,7 +763,11 @@ rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf)
 
   WALK_LIST(iface, iface_list)
   {
-    if (! (iface->flags & IF_UP))
+    if (!(iface->flags & IF_UP))
+      continue;
+
+    /* Ignore ifaces without appropriate address */
+    if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6)
       continue;
 
     struct rip_iface *ifa = rip_find_iface(p, iface);
@@ -736,31 +797,34 @@ rip_if_notify(struct proto *P, unsigned flags, struct iface *iface)
 {
   struct rip_proto *p = (void *) P;
   struct rip_config *cf = (void *) P->cf;
+  struct rip_iface *ifa = rip_find_iface(p, iface);
 
   if (iface->flags & IF_IGNORE)
     return;
 
-  if (flags & IF_CHANGE_UP)
+  /* Add, remove or restart interface */
+  if (flags & (IF_CHANGE_UPDOWN | (rip_is_v2(p) ? IF_CHANGE_ADDR4 : IF_CHANGE_LLV6)))
   {
-    struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
+    if (ifa)
+      rip_remove_iface(p, ifa);
+
+    if (!(iface->flags & IF_UP))
+      return;
+
+    /* Ignore ifaces without appropriate address */
+    if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6)
+      return;
 
+    struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
     if (ic)
       rip_add_iface(p, iface, ic);
 
     return;
   }
 
-  struct rip_iface *ifa = rip_find_iface(p, iface);
-
   if (!ifa)
     return;
 
-  if (flags & IF_CHANGE_DOWN)
-  {
-    rip_remove_iface(p, ifa);
-    return;
-  }
-
   if (flags & IF_CHANGE_MTU)
     rip_iface_update_buffers(ifa);
 
@@ -802,8 +866,9 @@ rip_timer(timer *t)
   struct rip_iface *ifa;
   struct rip_neighbor *n, *nn;
   struct fib_iterator fit;
-  bird_clock_t next = now + MIN(cf->min_timeout_time, cf->max_garbage_time);
-  bird_clock_t expires = 0;
+  btime now_ = current_time();
+  btime next = now_ + MIN(cf->min_timeout_time, cf->max_garbage_time);
+  btime expires = 0;
 
   TRACE(D_EVENTS, "Main timer fired");
 
@@ -818,7 +883,7 @@ rip_timer(timer *t)
     /* Checking received routes for timeout and for dead neighbors */
     for (rp = &en->routes; rt = *rp; /* rp = &rt->next */)
     {
-      if (!rip_valid_rte(rt) || (rt->expires <= now))
+      if (!rip_valid_rte(rt) || (rt->expires <= now_))
       {
        rip_remove_rte(p, rp);
        changed = 1;
@@ -848,7 +913,7 @@ rip_timer(timer *t)
     {
       expires = en->changed + cf->max_garbage_time;
 
-      if (expires <= now)
+      if (expires <= now_)
       {
        // TRACE(D_EVENTS, "entry is too old: %N", en->n.addr);
        en->valid = 0;
@@ -871,25 +936,31 @@ rip_timer(timer *t)
 
   /* Handling neighbor expiration */
   WALK_LIST(ifa, p->iface_list)
+  {
+    /* No expiration for demand circuit ifaces */
+    if (ifa->cf->demand_circuit)
+      continue;
+
     WALK_LIST_DELSAFE(n, nn, ifa->neigh_list)
       if (n->last_seen)
       {
        expires = n->last_seen + n->ifa->cf->timeout_time;
 
-       if (expires <= now)
+       if (expires <= now_)
          rip_remove_neighbor(p, n);
        else
          next = MIN(next, expires);
       }
+  }
 
-  tm_start(p->timer, MAX(next - now, 1));
+  tm_start(p->timer, MAX(next - now_, 100 MS));
 }
 
 static inline void
 rip_kick_timer(struct rip_proto *p)
 {
-  if (p->timer->expires > (now + 1))
-    tm_start(p->timer, 1);     /* Or 100 ms */
+  if ((p->timer->expires > (current_time() + 100 MS)))
+    tm_start(p->timer, 100 MS);
 }
 
 /**
@@ -907,7 +978,8 @@ rip_iface_timer(timer *t)
 {
   struct rip_iface *ifa = t->data;
   struct rip_proto *p = ifa->rip;
-  bird_clock_t period = ifa->cf->update_time;
+  btime now_ = current_time();
+  btime period = ifa->cf->update_time;
 
   if (ifa->cf->passive)
     return;
@@ -916,40 +988,41 @@ rip_iface_timer(timer *t)
 
   if (ifa->tx_active)
   {
-    if (now < (ifa->next_regular + period))
-      { tm_start(ifa->timer, 1); return; }
-
-    /* We are too late, reset is done by rip_send_table() */
-    log(L_WARN "%s: Too slow update on %s, resetting", p->p.name, ifa->iface->name);
+    tm_start(ifa->timer, 100 MS);
+    return;
   }
 
-  if (now >= ifa->next_regular)
+  if (now_ >= ifa->next_regular)
   {
     /* Send regular update, set timer for next period (or following one if necessay) */
     TRACE(D_EVENTS, "Sending regular updates for %s", ifa->iface->name);
     rip_send_table(p, ifa, ifa->addr, 0);
-    ifa->next_regular += period * (1 + ((now - ifa->next_regular) / period));
+    ifa->next_regular += period * (1 + ((now_ - ifa->next_regular) / period));
     ifa->want_triggered = 0;
     p->triggered = 0;
   }
-  else if (ifa->want_triggered && (now >= ifa->next_triggered))
+  else if (ifa->want_triggered && (now_ >= ifa->next_triggered))
   {
     /* Send triggered update, enforce interval between triggered updates */
     TRACE(D_EVENTS, "Sending triggered updates for %s", ifa->iface->name);
     rip_send_table(p, ifa, ifa->addr, ifa->want_triggered);
-    ifa->next_triggered = now + MIN(5, period / 2 + 1);
+    ifa->next_triggered = now_ + MIN(5 S, period / 2);
     ifa->want_triggered = 0;
     p->triggered = 0;
   }
 
-  tm_start(ifa->timer, ifa->want_triggered ? 1 : (ifa->next_regular - now));
+  if (ifa->want_triggered && (ifa->next_triggered < ifa->next_regular))
+    tm_set(ifa->timer, ifa->next_triggered);
+  else if (ifa->next_regular != TIME_INFINITY)
+    tm_set(ifa->timer, ifa->next_regular);
 }
 
+
 static inline void
 rip_iface_kick_timer(struct rip_iface *ifa)
 {
-  if (ifa->timer->expires > (now + 1))
-    tm_start(ifa->timer, 1);   /* Or 100 ms */
+  if ((! tm_active(ifa->timer)) || (ifa->timer->expires > (current_time() + 100 MS)))
+    tm_start(ifa->timer, 100 MS);
 }
 
 static void
@@ -970,11 +1043,10 @@ rip_trigger_update(struct rip_proto *p)
       continue;
 
     TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name);
-    ifa->want_triggered = now;
+    ifa->want_triggered = current_time();
     rip_iface_kick_timer(ifa);
+    p->triggered = 1;
   }
-
-  p->triggered = 1;
 }
 
 
@@ -982,38 +1054,6 @@ rip_trigger_update(struct rip_proto *p)
  *     RIP protocol glue
  */
 
-static struct ea_list *
-rip_prepare_attrs(struct linpool *pool, ea_list *next, u8 metric, u16 tag)
-{
-  struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
-
-  l->next = next;
-  l->flags = EALF_SORTED;
-  l->count = 2;
-
-  l->attrs[0].id = EA_RIP_METRIC;
-  l->attrs[0].flags = 0;
-  l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
-  l->attrs[0].u.data = metric;
-
-  l->attrs[1].id = EA_RIP_TAG;
-  l->attrs[1].flags = 0;
-  l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
-  l->attrs[1].u.data = tag;
-
-  return l;
-}
-
-static int
-rip_import_control(struct proto *P UNUSED, struct rte **rt, struct ea_list **attrs, struct linpool *pool)
-{
-  /* Prepare attributes with initial values */
-  if ((*rt)->attrs->source != RTS_RIP)
-    *attrs = rip_prepare_attrs(pool, *attrs, 1, 0);
-
-  return 0;
-}
-
 static void
 rip_reload_routes(struct channel *C)
 {
@@ -1027,17 +1067,20 @@ rip_reload_routes(struct channel *C)
   rip_kick_timer(p);
 }
 
-static struct ea_list *
+static void
 rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
 {
-  return rip_prepare_attrs(pool, NULL, rt->u.rip.metric, rt->u.rip.tag);
+  rte_init_tmp_attrs(rt, pool, 2);
+  rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
+  rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
 }
 
 static void
-rip_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
+rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
 {
-  rt->u.rip.metric = ea_get_int(attrs, EA_RIP_METRIC, 1);
-  rt->u.rip.tag = ea_get_int(attrs, EA_RIP_TAG, 0);
+  rte_init_tmp_attrs(rt, pool, 2);
+  rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
+  rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
 }
 
 static int
@@ -1062,7 +1105,7 @@ rip_postconfig(struct proto_config *CF)
 
   /* Define default channel */
   if (EMPTY_LIST(CF->channels))
-    channel_config_new(NULL, CF->net_type, CF);
+    channel_config_new(NULL, net_label[CF->net_type], CF->net_type, CF);
 }
 
 static struct proto *
@@ -1075,7 +1118,6 @@ rip_init(struct proto_config *CF)
   P->if_notify = rip_if_notify;
   P->rt_notify = rip_rt_notify;
   P->neigh_notify = rip_neigh_notify;
-  P->import_control = rip_import_control;
   P->reload_routes = rip_reload_routes;
   P->make_tmp_attrs = rip_make_tmp_attrs;
   P->store_tmp_attrs = rip_store_tmp_attrs;
@@ -1095,7 +1137,7 @@ rip_start(struct proto *P)
   fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6,
           sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL);
   p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte));
-  p->timer = tm_new_set(P->pool, rip_timer, p, 0, 0);
+  p->timer = tm_new_init(P->pool, rip_timer, p, 0, 0);
 
   p->rip2 = cf->rip2;
   p->ecmp = cf->ecmp;
@@ -1110,6 +1152,20 @@ rip_start(struct proto *P)
   return PS_UP;
 }
 
+static int
+rip_shutdown(struct proto *P)
+{
+  struct rip_proto *p = (void *) P;
+
+  TRACE(D_EVENTS, "Shutdown requested");
+
+  struct rip_iface *ifa;
+  WALK_LIST(ifa, p->iface_list)
+    rip_iface_stop(ifa);
+
+  return PS_DOWN;
+}
+
 static int
 rip_reconfigure(struct proto *P, struct proto_config *CF)
 {
@@ -1139,7 +1195,7 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
 }
 
 static void
-rip_get_route_info(rte *rte, byte *buf, ea_list *attrs UNUSED)
+rip_get_route_info(rte *rte, byte *buf)
 {
   buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric);
 
@@ -1148,7 +1204,7 @@ rip_get_route_info(rte *rte, byte *buf, ea_list *attrs UNUSED)
 }
 
 static int
-rip_get_attr(eattr *a, byte *buf, int buflen UNUSED)
+rip_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
 {
   switch (a->id)
   {
@@ -1166,7 +1222,7 @@ rip_get_attr(eattr *a, byte *buf, int buflen UNUSED)
 }
 
 void
-rip_show_interfaces(struct proto *P, char *iff)
+rip_show_interfaces(struct proto *P, const char *iff)
 {
   struct rip_proto *p = (void *) P;
   struct rip_iface *ifa = NULL;
@@ -1180,7 +1236,7 @@ rip_show_interfaces(struct proto *P, char *iff)
   }
 
   cli_msg(-1021, "%s:", p->p.name);
-  cli_msg(-1021, "%-10s %-6s %6s %6s %6s",
+  cli_msg(-1021, "%-10s %-6s %6s %6s %7s",
          "Interface", "State", "Metric", "Nbrs", "Timer");
 
   WALK_LIST(ifa, p->iface_list)
@@ -1193,8 +1249,10 @@ rip_show_interfaces(struct proto *P, char *iff)
       if (n->last_seen)
        nbrs++;
 
-    int timer = MAX(ifa->next_regular - now, 0);
-    cli_msg(-1021, "%-10s %-6s %6u %6u %6u",
+    btime now_ = current_time();
+    btime timer = ((ifa->next_regular < TIME_INFINITY) && (ifa->next_regular > now_)) ?
+      (ifa->next_regular - now_) : 0;
+    cli_msg(-1021, "%-10s %-6s %6u %6u %7t",
            ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer);
   }
 
@@ -1202,7 +1260,7 @@ rip_show_interfaces(struct proto *P, char *iff)
 }
 
 void
-rip_show_neighbors(struct proto *P, char *iff)
+rip_show_neighbors(struct proto *P, const char *iff)
 {
   struct rip_proto *p = (void *) P;
   struct rip_iface *ifa = NULL;
@@ -1216,7 +1274,7 @@ rip_show_neighbors(struct proto *P, char *iff)
   }
 
   cli_msg(-1022, "%s:", p->p.name);
-  cli_msg(-1022, "%-25s %-10s %6s %6s %6s",
+  cli_msg(-1022, "%-25s %-10s %6s %6s %7s",
          "IP address", "Interface", "Metric", "Routes", "Seen");
 
   WALK_LIST(ifa, p->iface_list)
@@ -1229,8 +1287,8 @@ rip_show_neighbors(struct proto *P, char *iff)
       if (!n->last_seen)
        continue;
 
-      int timer = now - n->last_seen;
-      cli_msg(-1022, "%-25I %-10s %6u %6u %6u",
+      btime timer = current_time() - n->last_seen;
+      cli_msg(-1022, "%-25I %-10s %6u %6u %7t",
              n->nbr->addr, ifa->iface->name, ifa->cf->metric, n->uc, timer);
     }
   }
@@ -1248,9 +1306,13 @@ rip_dump(struct proto *P)
   i = 0;
   FIB_WALK(&p->rtable, struct rip_entry, en)
   {
-    debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %d s\n",
-         i++, en->n.addr, en->next_hop, en->iface->name,
-         en->valid, en->metric, now - en->changed);
+    debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %t\n",
+         i++, en->n.addr, en->next_hop, en->iface ? en->iface->name : "(null)",
+         en->valid, en->metric, current_time() - en->changed);
+
+    for (struct rip_rte *e = en->routes; e; e = e->next)
+      debug("RIP:   via %I metric %d expires %t\n",
+           e->next_hop, e->metric, e->expires - current_time());
   }
   FIB_WALK_END;
 
@@ -1267,7 +1329,7 @@ rip_dump(struct proto *P)
 struct protocol proto_rip = {
   .name =              "RIP",
   .template =          "rip%d",
-  .attr_class =                EAP_RIP,
+  .class =             PROTOCOL_RIP,
   .preference =                DEF_PREF_RIP,
   .channel_mask =      NB_IP,
   .proto_size =                sizeof(struct rip_proto),
@@ -1276,6 +1338,7 @@ struct protocol proto_rip = {
   .init =              rip_init,
   .dump =              rip_dump,
   .start =             rip_start,
+  .shutdown =          rip_shutdown,
   .reconfigure =       rip_reconfigure,
   .get_route_info =    rip_get_route_info,
   .get_attr =          rip_get_attr