]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
RIP: Demand circuit support (RFC 2091)
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Fri, 21 Feb 2020 01:35:50 +0000 (02:35 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Fri, 21 Feb 2020 01:35:50 +0000 (02:35 +0100)
nest/route.h
nest/rt-fib.c
proto/rip/config.Y
proto/rip/packets.c
proto/rip/rip.c
proto/rip/rip.h

index b927db5f634ecfa8120b54529b54782000b01c3d..5421ece5217705be8288a3a136c99eefa407094a 100644 (file)
@@ -84,6 +84,8 @@ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't
 struct fib_node *fit_get(struct fib *, struct fib_iterator *);
 void fit_put(struct fib_iterator *, struct fib_node *);
 void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
+void fit_put_end(struct fib_iterator *i);
+void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
 
 
 #define FIB_WALK(fib, type, z) do {                            \
@@ -118,8 +120,12 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
 
 #define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
 
+#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
+
 #define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
 
+#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
+
 
 /*
  *     Master Routing Tables. Generally speaking, each of them contains a FIB
index 76a86e6eefa65b809c49efae0fc2689cbd507297..a7f703714bc80853654e37a576cfc025ff6ce780 100644 (file)
@@ -584,6 +584,40 @@ found:
   fit_put(i, n);
 }
 
+void
+fit_put_end(struct fib_iterator *i)
+{
+  i->prev = i->next = NULL;
+  i->node = NULL;
+  i->hash = ~0 - 1;
+}
+
+void
+fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src)
+{
+  struct fib_iterator *nxt = src->next;
+
+  fit_get(f, dst);
+
+  if (!src->prev)
+  {
+    /* We are at the end */
+    fit_put_end(dst);
+    return;
+  }
+
+  src->next = dst;
+  dst->prev = src;
+
+  dst->next = nxt;
+  if (nxt)
+    nxt->prev = dst;
+
+  dst->node = src->node;
+  dst->hash = src->hash;
+}
+
+
 #ifdef DEBUGGING
 
 /**
index 5b5f94a0b1943785ef2fb63cddaca8fcb379bf6a..6cea7dd0a62e8943973b399f04a16cf44a3e04d3 100644 (file)
@@ -36,7 +36,8 @@ CF_KEYWORDS(RIP, NG, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
            GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE,
            VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD,
            AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY,
-           RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG)
+           RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, DEMAND, CIRCUIT,
+           RIP_METRIC, RIP_TAG)
 
 %type <i> rip_variant rip_auth
 
@@ -102,6 +103,7 @@ rip_iface_start:
   RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME;
   RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME;
   RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME;
+  RIP_IFACE->rxmt_time = RIP_DEFAULT_RXMT_TIME;
 };
 
 rip_iface_finish:
@@ -149,9 +151,11 @@ rip_iface_item:
  | SPLIT HORIZON bool  { RIP_IFACE->split_horizon = $3; }
  | POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; }
  | CHECK ZERO bool     { RIP_IFACE->check_zero = $3; }
+ | DEMAND CIRCUIT bool { RIP_IFACE->demand_circuit = $3; }
  | UPDATE TIME expr    { RIP_IFACE->update_time = $3 S_; if ($3<=0) cf_error("Update time must be positive"); }
  | TIMEOUT TIME expr   { RIP_IFACE->timeout_time = $3 S_; if ($3<=0) cf_error("Timeout time must be positive"); }
  | GARBAGE TIME expr   { RIP_IFACE->garbage_time = $3 S_; if ($3<=0) cf_error("Garbage time must be positive"); }
+ | RETRANSMIT TIME expr_us { RIP_IFACE->rxmt_time = $3; if ($3<=0) cf_error("Retransmit time must be positive"); }
  | ECMP WEIGHT expr    { RIP_IFACE->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); }
  | RX BUFFER expr      { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX length must be in range 256-65535"); }
  | TX LENGTH expr      { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }
index 59ffd7c24f2414011b422752add65490e1e96ccd..8de691af532282bc48c1baa2490d5f0868dab20a 100644 (file)
 #define RIP_CMD_REQUEST                1       /* want info */
 #define RIP_CMD_RESPONSE       2       /* responding to request */
 
+#define RIP_CMD_UPDATE_REQUEST 9
+#define RIP_CMD_UPDATE_RESPONSE        10
+#define RIP_CMD_UPDATE_ACK     11
+
 #define RIP_BLOCK_LENGTH       20
 #define RIP_PASSWD_LENGTH      16
 
 #define RIP_AF_IPV4            2
 #define RIP_AF_AUTH            0xffff
 
+#define RIP_UPDATE_VERSION     1
+
 
 /* RIP packet header */
 struct rip_packet
@@ -33,6 +39,14 @@ struct rip_packet
   u16 unused;
 };
 
+/* Triggered RIP update header (RFC 2091) */
+struct rip_update_hdr
+{
+  u8 version;
+  u8 flush;
+  u16 seqnum;
+};
+
 /* RTE block for RIPv2 */
 struct rip_block_v2
 {
@@ -89,6 +103,8 @@ struct rip_block
   ip_addr next_hop;
 };
 
+static int
+rip_send_ack(struct rip_proto *p, struct rip_iface *ifa, uint flush, uint seqnum);
 
 #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
 #define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0)
@@ -107,8 +123,27 @@ struct rip_block
 static inline void * rip_tx_buffer(struct rip_iface *ifa)
 { return ifa->sk->tbuf; }
 
-static inline uint rip_pkt_hdrlen(struct rip_iface *ifa)
-{ return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); }
+static inline uint
+rip_pkt_hdrlen(struct rip_iface *ifa)
+{
+  return sizeof(struct rip_packet) +
+    (ifa->cf->demand_circuit ? sizeof(struct rip_update_hdr) : 0) +
+    (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0);
+}
+
+static inline struct rip_update_hdr *
+rip_get_update_hdr(struct rip_packet *pkt)
+{
+  return (void *) ((byte *) pkt + sizeof(struct rip_packet));
+}
+
+static inline struct rip_block_auth *
+rip_get_auth_block(struct rip_iface *ifa, struct rip_packet *pkt)
+{
+  return (void *) ((byte *) pkt + sizeof(struct rip_packet) +
+                  (ifa->cf->demand_circuit ? sizeof(struct rip_update_hdr) : 0));
+}
+
 
 static inline void
 rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte)
@@ -199,10 +234,31 @@ rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa)
   }
 }
 
+static inline void
+rip_fill_header(struct rip_iface *ifa, struct rip_packet *pkt, uint cmd)
+{
+  *pkt = (struct rip_packet) {
+    .command = cmd,
+    .version = ifa->cf->version
+  };
+}
+
+static inline void
+rip_fill_update_hdr(struct rip_packet *pkt, uint flush, uint seqnum)
+{
+  struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
+
+  *hdr = (struct rip_update_hdr) {
+    .version = RIP_UPDATE_VERSION,
+    .flush = flush,
+    .seqnum = htons(seqnum)
+  };
+}
+
 static void
 rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen)
 {
-  struct rip_block_auth *auth = (void *) (pkt + 1);
+  struct rip_block_auth *auth = rip_get_auth_block(ifa, pkt);
   struct password_item *pass = password_find(ifa->cf->passwords, 0);
 
   if (!pass)
@@ -271,16 +327,16 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p
 }
 
 static int
-rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, struct rip_neighbor *n)
+rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, uint hdr_len, struct rip_neighbor *n)
 {
-  struct rip_block_auth *auth = (void *) (pkt + 1);
+  struct rip_block_auth *auth = (void *) ((byte *) pkt + hdr_len);
   struct password_item *pass = NULL;
   const char *err_dsc = NULL;
   uint err_val = 0;
   uint auth_type = 0;
 
   /* Check for authentication entry */
-  if ((*plen >= (sizeof(struct rip_packet) + sizeof(struct rip_block_auth))) &&
+  if ((*plen >= (hdr_len + sizeof(struct rip_block_auth))) &&
       (auth->must_be_ffff == htons(0xffff)))
     auth_type = ntohs(auth->auth_type);
 
@@ -376,17 +432,31 @@ rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt,
   return sk_send_to(ifa->sk, plen, dst, 0);
 }
 
+static inline void
+rip_kick_rxmt_timer(struct rip_iface *ifa)
+{
+  if (! tm_active(ifa->rxmt_timer))
+    tm_start(ifa->rxmt_timer, ifa->cf->rxmt_time);
+}
+
 
 void
 rip_send_request(struct rip_proto *p, struct rip_iface *ifa)
 {
-  byte *pos = rip_tx_buffer(ifa);
+  struct rip_packet *pkt = rip_tx_buffer(ifa);
+  byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
+
+  rip_fill_header(ifa, pkt, RIP_CMD_REQUEST);
+
+  if (ifa->cf->demand_circuit)
+  {
+    pkt->command = RIP_CMD_UPDATE_REQUEST;
+    rip_fill_update_hdr(pkt, 0, 0);
 
-  struct rip_packet *pkt = (void *) pos;
-  pkt->command = RIP_CMD_REQUEST;
-  pkt->version = ifa->cf->version;
-  pkt->unused = 0;
-  pos += rip_pkt_hdrlen(ifa);
+    /* Must be acknowledged by update response */
+    ifa->req_pending = 1;
+    rip_kick_rxmt_timer(ifa);
+  }
 
   struct rip_block b = { .no_af = 1, .metric = p->infinity };
   rip_put_block(p, pos, &b);
@@ -437,18 +507,25 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
   if (! ifa->tx_active)
     return 0;
 
-  byte *pos = rip_tx_buffer(ifa);
-  byte *max = rip_tx_buffer(ifa) + ifa->tx_plen -
+  /* In demand circuit mode, we may wait for ACK */
+  if (ifa->tx_pending)
+    return 0;
+
+  struct rip_packet *pkt = rip_tx_buffer(ifa);
+  byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
+  byte *max = (byte *) pkt + ifa->tx_plen -
     (rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH);
   ip_addr last_next_hop = IPA_NONE;
   btime now_ = current_time();
   int send = 0;
 
-  struct rip_packet *pkt = (void *) pos;
-  pkt->command = RIP_CMD_RESPONSE;
-  pkt->version = ifa->cf->version;
-  pkt->unused = 0;
-  pos += rip_pkt_hdrlen(ifa);
+  rip_fill_header(ifa, pkt, RIP_CMD_RESPONSE);
+
+  if (ifa->cf->demand_circuit)
+  {
+    pkt->command = RIP_CMD_UPDATE_RESPONSE;
+    rip_fill_update_hdr(pkt, ifa->tx_flush, ifa->tx_seqnum);
+  }
 
   FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, struct rip_entry, en)
   {
@@ -469,7 +546,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
     if (pos > max)
     {
       FIB_ITERATE_PUT(&ifa->tx_fit);
-      goto break_loop;
+      goto send_pkt;
     }
 
     struct rip_block rte = {
@@ -522,13 +599,31 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
   next_entry: ;
   }
   FIB_ITERATE_END;
-  ifa->tx_active = 0;
+
+  if (send)
+  {
+    FIB_ITERATE_PUT_END(&ifa->tx_fit);
+    goto send_pkt;
+  }
 
   /* Do not send empty packet */
-  if (!send)
-    return 0;
 
-break_loop:
+  ifa->tx_active = 0;
+
+  /* Unlink second iterator */
+  FIB_ITERATE_UNLINK(&ifa->tx_done, &p->rtable);
+
+  return 0;
+
+send_pkt:
+
+  /* Waiting for ack or timeout */
+  if (ifa->cf->demand_circuit)
+  {
+    ifa->tx_pending = 1;
+    rip_kick_rxmt_timer(ifa);
+  }
+
   TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name);
   return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr);
 }
@@ -558,6 +653,10 @@ rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, btime c
   ifa->tx_addr = addr;
   ifa->tx_changed = changed;
   FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable);
+  FIB_ITERATE_INIT(&ifa->tx_done, &p->rtable);
+
+  if (ifa->cf->demand_circuit)
+    ifa->tx_flush = ! changed;
 
   rip_update_csn(p, ifa);
 
@@ -597,9 +696,27 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
 
   TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name);
 
-  byte *pos = (byte *) pkt + sizeof(struct rip_packet);
+  byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
   byte *end = (byte *) pkt + plen;
-  btime now_ = current_time();
+
+  btime expires = current_time() + ifa->cf->timeout_time;
+
+  if (pkt->command == RIP_CMD_UPDATE_RESPONSE)
+  {
+    struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
+    rip_send_ack(p, ifa, hdr->flush, ntohs(hdr->seqnum));
+    expires = TIME_INFINITY;
+
+    /* Handle flush bit */
+    if (hdr->flush)
+    {
+      /* Flush old routes */
+      rip_flush_table(p, from);
+
+      /* Acknowledge pending request */
+      ifa->req_pending = 0;
+    }
+  }
 
   for (; pos < end; pos += RIP_BLOCK_LENGTH)
   {
@@ -647,7 +764,7 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
        .next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr,
        .metric = rte.metric,
        .tag = rte.tag,
-       .expires = now_ + ifa->cf->timeout_time
+       .expires = expires
       };
 
       rip_update_rte(p, &rte.net, &new);
@@ -663,6 +780,85 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
   }
 }
 
+
+static int
+rip_send_ack(struct rip_proto *p, struct rip_iface *ifa, uint flush, uint seqnum)
+{
+  struct rip_packet *pkt = rip_tx_buffer(ifa);
+
+  rip_fill_header(ifa, pkt, RIP_CMD_UPDATE_ACK);
+  rip_fill_update_hdr(pkt, flush, seqnum);
+
+  TRACE(D_PACKETS, "Sending acknowledge via %s", ifa->iface->name);
+  return rip_send_to(p, ifa, pkt, rip_pkt_hdrlen(ifa), ifa->tx_addr);
+}
+
+static void
+rip_receive_ack(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen UNUSED, struct rip_neighbor *from)
+{
+  TRACE(D_PACKETS, "Acknowledge received from %I on %s", from->nbr->addr, ifa->iface->name);
+
+  struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
+  uint seqnum = ntohs(hdr->seqnum);
+
+  if (! ifa->tx_active || ! ifa->tx_pending)
+  {
+    LOG_PKT("Bad acknowledge packet from %I via %s - no pending response",
+           from->nbr->addr, ifa->iface->name);
+    return;
+  }
+
+  if (seqnum != ifa->tx_seqnum)
+  {
+    LOG_PKT("Bad acknowledge packet from %I via %s - "
+           "mismatched sequence number (rcv %u, old %u)",
+           from->nbr->addr, ifa->iface->name, seqnum, (uint) ifa->tx_seqnum);
+    return;
+  }
+
+  /* Move acked position */
+  FIB_ITERATE_COPY(&ifa->tx_done, &ifa->tx_fit, &p->rtable);
+
+  /* Packet is no longer pending */
+  ifa->tx_pending = 0;
+  ifa->tx_seqnum++;
+
+  /* Next one does not have flush bit */
+  ifa->tx_flush = 0;
+
+  rip_send_response(p, ifa);
+}
+
+/**
+ * rip_rxmt_timeout - RIP retransmission timer hook
+ * @t: timer
+ *
+ * In Demand Circuit mode, update packets must be acknowledged to ensure
+ * reliability. If they are not acknowledged, we need to retransmit them.
+ */
+void
+rip_rxmt_timeout(timer *t)
+{
+  struct rip_iface *ifa = t->data;
+  struct rip_proto *p = ifa->rip;
+
+  if (ifa->req_pending)
+    rip_send_request(p, ifa);
+
+  if (ifa->tx_pending)
+  {
+    /* Revert to acked position */
+    FIB_ITERATE_COPY(&ifa->tx_fit, &ifa->tx_done, &p->rtable);
+
+    /* Packet is no longer pending */
+    ifa->tx_pending = 0;
+    ifa->tx_seqnum++;
+
+    rip_send_response(p, ifa);
+  }
+}
+
+
 static int
 rip_rx_hook(sock *sk, uint len)
 {
@@ -702,17 +898,43 @@ rip_rx_hook(sock *sk, uint len)
     DROP("truncated", len);
 
   struct rip_packet *pkt = (struct rip_packet *) sk->rbuf;
-  uint plen = len;
+  uint pkt_len = len;
+  uint hdr_len = sizeof(struct rip_packet);
+  uint update_msg = 0;
 
   if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version)))
     DROP("wrong version", pkt->version);
 
+  /* Update packets (RFC 2091) have additional header even before auth data */
+  if ((pkt->command == RIP_CMD_UPDATE_REQUEST) ||
+      (pkt->command == RIP_CMD_UPDATE_RESPONSE) ||
+      (pkt->command == RIP_CMD_UPDATE_ACK))
+  {
+    hdr_len += sizeof(struct rip_update_hdr);
+
+    if (len < hdr_len)
+      DROP("too short", len);
+
+    struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
+
+    if (hdr->version != RIP_UPDATE_VERSION)
+      DROP("wrong update header version", hdr->version);
+
+    if (hdr->flush > 1)
+      DROP("wrong flush value", hdr->flush);
+
+    update_msg = 1;
+  }
+
   /* rip_check_authentication() has its own error logging */
-  if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &plen, n))
+  if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &pkt_len, hdr_len, n))
     return 1;
 
-  if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH)
-    DROP("invalid length", plen);
+  if ((pkt_len - hdr_len) % RIP_BLOCK_LENGTH)
+    DROP("invalid length", pkt_len);
+
+  if (update_msg != ifa->cf->demand_circuit)
+    DROP("demand circuit mode mismatch", update_msg);
 
   n->last_seen = current_time();
   rip_update_bfd(p, n);
@@ -720,11 +942,17 @@ rip_rx_hook(sock *sk, uint len)
   switch (pkt->command)
   {
   case RIP_CMD_REQUEST:
-    rip_receive_request(p, ifa, pkt, plen, n);
+  case RIP_CMD_UPDATE_REQUEST:
+    rip_receive_request(p, ifa, pkt, pkt_len, n);
     break;
 
   case RIP_CMD_RESPONSE:
-    rip_receive_response(p, ifa, pkt, plen, n);
+  case RIP_CMD_UPDATE_RESPONSE:
+    rip_receive_response(p, ifa, pkt, pkt_len, n);
+    break;
+
+  case RIP_CMD_UPDATE_ACK:
+    rip_receive_ack(p, ifa, pkt, pkt_len, n);
     break;
 
   default:
index e811a3d84329d4e6a81ddbafd11ca4f6ace40625..80e8d082ba28a3a2b7671ce91b7a5d4d68b4175b 100644 (file)
@@ -368,6 +368,20 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
   }
 }
 
+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
@@ -506,14 +520,20 @@ rip_iface_start(struct rip_iface *ifa)
 
   TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name);
 
-  ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS;
-  ifa->next_triggered = current_time();        /* Available immediately */
-  ifa->want_triggered = 1;             /* All routes in triggered update */
-  tm_start(ifa->timer, 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);
+  rip_send_request(p, ifa);
+  rip_send_table(p, ifa, ifa->addr, 0);
 }
 
 static void
@@ -526,10 +546,18 @@ 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;
+
+  ifa->tx_pending = 0;
+  ifa->req_pending = 0;
+
   WALK_LIST_FIRST(n, ifa->neigh_list)
     rip_remove_neighbor(p, n);
 
   tm_stop(ifa->timer);
+  tm_stop(ifa->rxmt_timer);
   ifa->up = 0;
 }
 
@@ -643,6 +671,7 @@ rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config
   add_tail(&p->iface_list, NODE ifa);
 
   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;
@@ -885,6 +914,11 @@ 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)
       {
@@ -895,6 +929,7 @@ rip_timer(timer *t)
        else
          next = MIN(next, expires);
       }
+  }
 
   tm_start(p->timer, MAX(next - now_, 100 MS));
 }
@@ -902,7 +937,7 @@ rip_timer(timer *t)
 static inline void
 rip_kick_timer(struct rip_proto *p)
 {
-  if (p->timer->expires > (current_time() + 100 MS))
+  if ((p->timer->expires > (current_time() + 100 MS)))
     tm_start(p->timer, 100 MS);
 }
 
@@ -931,11 +966,8 @@ rip_iface_timer(timer *t)
 
   if (ifa->tx_active)
   {
-    if (now_ < (ifa->next_regular + period))
-    { tm_start(ifa->timer, 100 MS); 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)
@@ -957,13 +989,17 @@ rip_iface_timer(timer *t)
     p->triggered = 0;
   }
 
-  tm_start(ifa->timer, ifa->want_triggered ? (1 S) : (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 > (current_time() + 100 MS))
+  if ((! tm_active(ifa->timer)) || (ifa->timer->expires > (current_time() + 100 MS)))
     tm_start(ifa->timer, 100 MS);
 }
 
@@ -987,9 +1023,8 @@ rip_trigger_update(struct rip_proto *p)
     TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name);
     ifa->want_triggered = current_time();
     rip_iface_kick_timer(ifa);
+    p->triggered = 1;
   }
-
-  p->triggered = 1;
 }
 
 
@@ -1179,7 +1214,8 @@ rip_show_interfaces(struct proto *P, char *iff)
        nbrs++;
 
     btime now_ = current_time();
-    btime timer = (ifa->next_regular > now_) ? (ifa->next_regular - now_) : 0;
+    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);
   }
index 2dc348623e52a9d78305a9c9c4ac02e7747b2c47..bf59735001cd2c1753ab0a5bd2b3dc0e5d009a5f 100644 (file)
@@ -41,6 +41,7 @@
 #define RIP_DEFAULT_UPDATE_TIME          (30 S_)
 #define RIP_DEFAULT_TIMEOUT_TIME (180 S_)
 #define RIP_DEFAULT_GARBAGE_TIME (120 S_)
+#define RIP_DEFAULT_RXMT_TIME (1 S_)
 
 
 struct rip_config
@@ -73,6 +74,7 @@ struct rip_iface_config
   u8 auth_type;                                /* Authentication type (RIP_AUTH_*) */
   u8 ttl_security;                     /* bool + 2 for TX only (send, but do not check on RX) */
   u8 check_link;                       /* Whether iface link change is used */
+  u8 demand_circuit;                   /* Use demand circuit extensions (RFC 2091) */
   u8 bfd;                              /* Use BFD on iface */
   u16 rx_buffer;                       /* RX buffer size, 0 for MTU */
   u16 tx_length;                       /* TX packet length limit (including headers), 0 for MTU */
@@ -81,6 +83,7 @@ struct rip_iface_config
   btime update_time;                   /* Periodic update interval */
   btime timeout_time;                  /* Route expiration timeout */
   btime garbage_time;                  /* Unreachable entry GC timeout */
+  btime rxmt_time;                     /* Retransmit timeout for demand circuit */
   list *passwords;                     /* Passwords for authentication */
 };
 
@@ -110,6 +113,7 @@ struct rip_iface
   struct rip_iface_config *cf;         /* Related config, must be updated in reconfigure */
   struct object_lock *lock;            /* Interface lock */
   timer *timer;                                /* Interface timer */
+  timer *rxmt_timer;                   /* Retransmission timer */
   sock *sk;                            /* UDP socket */
 
   u8 up;                               /* Interface is active */
@@ -126,9 +130,18 @@ struct rip_iface
 
   /* Active update */
   int tx_active;                       /* Update session is active */
+  int tx_waiting;
   ip_addr tx_addr;                     /* Update session destination address */
   btime tx_changed;                    /* Minimal changed time for triggered update */
   struct fib_iterator tx_fit;          /* FIB iterator in RIP routing table (p.rtable) */
+  struct fib_iterator tx_done;         /* FIB iterator for acked routes (p.rtable) */
+
+  /* Update message */
+  u8 tx_pending;
+  u8 tx_flush;
+  u16 tx_seqnum;
+
+  u8 req_pending;
 };
 
 struct rip_neighbor
@@ -197,6 +210,7 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
   if (ifa->tx_active)
   {
     FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable);
+    FIB_ITERATE_UNLINK(&ifa->tx_done, &p->rtable);
     ifa->tx_active = 0;
   }
 }
@@ -204,6 +218,7 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
 /* rip.c */
 void rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new);
 void rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from);
+void rip_flush_table(struct rip_proto *p, struct rip_neighbor *n);
 struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa);
 void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n);
 void rip_show_interfaces(struct proto *P, char *iff);
@@ -212,6 +227,7 @@ void rip_show_neighbors(struct proto *P, char *iff);
 /* packets.c */
 void rip_send_request(struct rip_proto *p, struct rip_iface *ifa);
 void rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, btime changed);
+void rip_rxmt_timeout(timer *t);
 int rip_open_socket(struct rip_iface *ifa);