]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Initial BFD commit, work in progress.
authorOndrej Zajicek <santiago@crfreenet.org>
Tue, 10 Sep 2013 10:09:36 +0000 (12:09 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 10 Sep 2013 10:09:36 +0000 (12:09 +0200)
16 files changed:
lib/buffer.h [new file with mode: 0644]
lib/hash.h [new file with mode: 0644]
lib/heap.h [new file with mode: 0644]
lib/lists.c
lib/resource.c
lib/socket.h
proto/Doc
proto/bfd/Doc [new file with mode: 0644]
proto/bfd/Makefile [new file with mode: 0644]
proto/bfd/bfd.c [new file with mode: 0644]
proto/bfd/bfd.h [new file with mode: 0644]
proto/bfd/config.Y [new file with mode: 0644]
proto/bfd/io.c [new file with mode: 0644]
proto/bfd/io.h [new file with mode: 0644]
proto/bfd/packets.c [new file with mode: 0644]
sysdep/unix/io.c

diff --git a/lib/buffer.h b/lib/buffer.h
new file mode 100644 (file)
index 0000000..cf073e8
--- /dev/null
@@ -0,0 +1,35 @@
+
+#define BUFFER(type)           struct { type *data; uint used, size; }
+
+#define BUFFER_SIZE(v)         ((v).size * sizeof(* (v).data))
+
+#define BUFFER_INIT(v,pool,isize)                                      \
+  ({                                                                   \
+    (v).used = 0;                                                      \
+    (v).size = (isize);                                                        \
+    (v).data = mb_alloc(pool, BUFFER_SIZE(v));                         \
+  })
+
+#define BUFFER_SET(v,nsize)                                            \
+  ({                                                                   \
+    (v).used = (nsize);                                                        \
+    if ((v).used > (v).size)                                           \
+      buffer_realloc((void **) &((v).data), &((v).size), (v).used, sizeof(* (v).data)); \
+  })
+
+#define BUFFER_INC(v,step)                                             \
+  ({                                                                   \
+    uint _o = (v).used;                                                        \
+    BUFFER_SET(v, (v).used + (step));                                  \
+    (v).data + _o;                                                     \
+  })
+
+#define BUFFER_DEC(v,step)     ({ (v).used -= (step); })
+
+#define BUFFER_PUSH(v)         (*BUFFER_INC(v,1))
+
+#define BUFFER_POP(v)          BUFFER_DEC(v,1)
+
+#define BUFFER_FLUSH(v)                ({ (v).used = 0; })
+
+
diff --git a/lib/hash.h b/lib/hash.h
new file mode 100644 (file)
index 0000000..7464f14
--- /dev/null
@@ -0,0 +1,83 @@
+
+
+#define HASH(type)             struct { type **data; uint used, size; }
+#define HASH_TYPE(v)           typeof(** (v).data)
+#define HASH_SIZE(v)           ((v).size * sizeof(* (v).data))
+
+#define HASH_INIT(v,pool,isize)                                                \
+  ({                                                                   \
+    (v).used = 0;                                                      \
+    (v).size = (isize);                                                        \
+    (v).data = mb_allocz(pool, HASH_SIZE(v));                          \
+  })
+
+#define HASH_FIND(v,id,key)                                            \
+  ({                                                                   \
+    HASH_TYPE(v) *_n = (v).data[id##_FN(key, (v).size)];               \
+    while (_n && !id##_EQ(_n, key))                                    \
+      _n = _n->id##_NEXT;                                              \
+    _n;                                                                        \
+  })
+
+#define HASH_INSERT(v,id,key,node)                                     \
+  ({                                                                   \
+    HASH_TYPE(v) **_nn = (v).data + id##_FN(key, (v).size);            \
+    node->id##_NEXT = *_nn;                                            \
+    *_nn = node;                                                       \
+  })
+
+#define HASH_DELETE(v,id,key)                                          \
+  ({                                                                   \
+    HASH_TYPE(v) **_nn = (v).data + id##_FN(key, (v).size);            \
+    while ((*_nn) && !id##_EQ(*_nn, key))                              \
+      _nn = &((*_nn)->id##_NEXT);                                      \
+                                                                       \
+    HASH_TYPE(v) *_n = *_nn;                                           \
+    if (_n)                                                            \
+      *_nn = _n->id##_NEXT;                                            \
+    _n;                                                                        \
+  })
+
+#define HASH_REMOVE(v,id,node)                                         \
+  ({                                                                   \
+    HASH_TYPE(v) **_nn = (v).data + id##_FN(key, (v).size);            \
+    while ((*_nn) && (*_nn != (node)))                                 \
+      _nn = &((*_nn)->id##_NEXT);                                      \
+                                                                       \
+    HASH_TYPE(v) *_n = *_nn;                                           \
+    if (_n)                                                            \
+      *_nn = _n->id##_NEXT;                                            \
+    _n;                                                                        \
+  })
+
+
+
+#define HASH_WALK(v,next,n)                                            \
+  do {                                                                 \
+    HASH_TYPE(v) *n;                                                   \
+    uint _i;                                                           \
+    for (_i = 0; _i < ((v).size); _i++)                                        \
+      for (n = (v).data[_i]; n; n = n->next)
+
+#define HASH_WALK_END } while (0)
+
+
+#define HASH_WALK_DELSAFE(v,next,n)                                    \
+  do {                                                                 \
+    HASH_TYPE(v) *n, *_next;                                           \
+    uint _i;                                                           \
+    for (_i = 0; _i < ((v).size); _i++)                                        \
+      for (n = (v).data[_i]; n && (_next = n->next, 1); n = _next)
+
+#define HASH_WALK_DELSAFE_END } while (0)
+
+/*
+define HASH_REHASH(s)                                                  \
+  ({                                                                   \
+    type *_n;                                                          \
+    uint _i;                                                           \
+    for (_i = 0; _i < (size_f); _i++)                                  \
+      for (_n = (hash)[_i]; _n != NULL; _n = 
+
+*/
+  
diff --git a/lib/heap.h b/lib/heap.h
new file mode 100644 (file)
index 0000000..ecb9a1b
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *     UCW Library -- Universal Heap Macros
+ *
+ *     (c) 2001 Martin Mares <mj@ucw.cz>
+ *     (c) 2005 Tomas Valla <tom@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
+ */
+
+/**
+ * [[intro]]
+ * Introduction
+ * ------------
+ *
+ * Binary heap is a simple data structure, which for example supports efficient insertions, deletions
+ * and access to the minimal inserted item. We define several macros for such operations.
+ * Note that because of simplicity of heaps, we have decided to define direct macros instead
+ * of a <<generic:,macro generator>> as for several other data structures in the Libucw.
+ *
+ * A heap is represented by a number of elements and by an array of values. Beware that we
+ * index this array from one, not from zero as do the standard C arrays.
+ *
+ * Most macros use these parameters:
+ *
+ * - @type - the type of elements
+ * - @num - a variable (signed or unsigned integer) with the number of elements
+ * - @heap - a C array of type @type; the heap is stored in `heap[1] .. heap[num]`; `heap[0]` is unused
+ * - @less - a callback to compare two element values; `less(x, y)` shall return a non-zero value iff @x is lower than @y
+ * - @swap - a callback to swap two array elements; `swap(heap, i, j, t)` must swap `heap[i]` with `heap[j]` with possible help of temporary variable @t (type @type).
+ *
+ * A valid heap must follow these rules:
+ *
+ * - `num >= 0`
+ * - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]`
+ *
+ * The first element `heap[1]` is always lower or equal to all other elements.
+ *
+ * [[macros]]
+ * Macros
+ * ------
+ */
+
+/* For internal usage. */
+#define HEAP_BUBBLE_DOWN_J(heap,num,less,swap)                                         \
+  for (;;)                                                                             \
+    {                                                                                  \
+      _l = 2*_j;                                                                       \
+      if (_l > num)                                                                    \
+       break;                                                                          \
+      if (less(heap[_j],heap[_l]) && (_l == num || less(heap[_j],heap[_l+1])))         \
+       break;                                                                          \
+      if (_l != num && less(heap[_l+1],heap[_l]))                                      \
+       _l++;                                                                           \
+      swap(heap,_j,_l,x);                                                              \
+      _j = _l;                                                                         \
+    }
+
+/* For internal usage. */
+#define HEAP_BUBBLE_UP_J(heap,num,less,swap)                                           \
+  while (_j > 1)                                                                       \
+    {                                                                                  \
+      _u = _j/2;                                                                       \
+      if (less(heap[_u], heap[_j]))                                                    \
+       break;                                                                          \
+      swap(heap,_u,_j,x);                                                              \
+      _j = _u;                                                                         \
+    }
+
+/**
+ * Shuffle the unordered array @heap of @num elements to become a valid heap. The time complexity is linear.
+ **/
+#define HEAP_INIT(heap,num,type,less,swap)                                             \
+  do {                                                                                 \
+    uns _i = num;                                                                      \
+    uns _j, _l;                                                                                \
+    type x;                                                                            \
+    while (_i >= 1)                                                                    \
+      {                                                                                        \
+       _j = _i;                                                                        \
+        HEAP_BUBBLE_DOWN_J(heap,num,less,swap)                                         \
+       _i--;                                                                           \
+      }                                                                                        \
+  } while(0)
+
+/**
+ * Delete the minimum element `heap[1]` in `O(log(n))` time.
+ * The removed value is moved just after the resulting heap (`heap[num + 1]`).
+ **/
+#define HEAP_DELMIN(heap,num,type,less,swap)                                           \
+  do {                                                                                 \
+    uns _j, _l;                                                                                \
+    type x;                                                                            \
+    swap(heap,1,num,x);                                                                        \
+    num--;                                                                             \
+    _j = 1;                                                                            \
+    HEAP_BUBBLE_DOWN_J(heap,num,less,swap);                                            \
+  } while(0)
+
+/**
+ * Insert `heap[num]` in `O(log(n))` time. The value of @num must be increased before.
+ **/
+#define HEAP_INSERT(heap,num,type,less,swap)                                           \
+  do {                                                                                 \
+    uns _j, _u;                                                                                \
+    type x;                                                                            \
+    _j = num;                                                                          \
+    HEAP_BUBBLE_UP_J(heap,num,less,swap);                                              \
+  } while(0)
+
+/**
+ * If you need to increase the value of `heap[pos]`, just do it and then call this macro to rebuild the heap.
+ * Only `heap[pos]` can be changed, the rest of the array must form a valid heap.
+ * The time complexity is `O(log(n))`.
+ **/
+#define HEAP_INCREASE(heap,num,type,less,swap,pos)                                     \
+  do {                                                                                 \
+    uns _j, _l;                                                                                \
+    type x;                                                                            \
+    _j = pos;                                                                          \
+    HEAP_BUBBLE_DOWN_J(heap,num,less,swap);                                            \
+  } while(0)
+
+/**
+ * If you need to decrease the value of `heap[pos]`, just do it and then call this macro to rebuild the heap.
+ * Only `heap[pos]` can be changed, the rest of the array must form a valid heap.
+ * The time complexity is `O(log(n))`.
+ **/
+#define HEAP_DECREASE(heap,num,type,less,swap,pos)                                     \
+  do {                                                                                 \
+    uns _j, _u;                                                                                \
+    type x;                                                                            \
+    _j = pos;                                                                          \
+    HEAP_BUBBLE_UP_J(heap,num,less,swap);                                              \
+  } while(0)
+
+/**
+ * Delete `heap[pos]` in `O(log(n))` time.
+ **/
+#define HEAP_DELETE(heap,num,type,less,swap,pos)                                       \
+  do {                                                                                 \
+    uns _j, _l, _u;                                                                    \
+    type x;                                                                            \
+    _j = pos;                                                                          \
+    swap(heap,_j,num,x);                                                               \
+    num--;                                                                             \
+    if (less(heap[_j], heap[num+1]))                                                   \
+      HEAP_BUBBLE_UP_J(heap,num,less,swap)                                             \
+    else                                                                               \
+      HEAP_BUBBLE_DOWN_J(heap,num,less,swap);                                          \
+  } while(0)
+
+/**
+ * Default swapping macro.
+ **/
+#define HEAP_SWAP(heap,a,b,t) (t=heap[a], heap[a]=heap[b], heap[b]=t)
index 6d97ff50c51639b903089d3bb263d2519c547eb3..58ffd2308707711ed467081f3bfc2d82b7f7de20 100644 (file)
@@ -100,6 +100,27 @@ rem_node(node *n)
   x->prev = z;
 }
 
+/**
+ * replace_node - replace a node in a list with another one
+ * @old: node to be removed
+ * @new: node to be inserted
+ *
+ * Replaces node @old in the list it's linked in with node @new.  Node
+ * @old may be a copy of the original node, which is not accessed
+ * through the list. The function could be called with @old == @new,
+ * which just fixes neighbors' pointers in the case that the node
+ * was reallocated.
+ */
+LIST_INLINE void
+replace_node(node *old, node *new)
+{
+  old->next->prev = new;
+  old->prev->next = new;
+
+  new->prev = old->prev;
+  new->next = old->next;
+}
+
 /**
  * init_list - create an empty list
  * @l: list
index 42243aa2a6b20ec100f92d2b292a9fcbaa1f167c..775b0c53f7c2a5906be489c2cef69029b8e3d2b6 100644 (file)
@@ -366,21 +366,21 @@ mb_allocz(pool *p, unsigned size)
 
 /**
  * mb_realloc - reallocate a memory block
- * @p: pool
  * @m: memory block
  * @size: new size of the block
  *
  * mb_realloc() changes the size of the memory block @m to a given size.
  * The contents will be unchanged to the minimum of the old and new sizes;
- * newly allocated memory will be uninitialized. If @m is NULL, the call
- * is equivalent to mb_alloc(@p, @size).
+ * newly allocated memory will be uninitialized. Contrary to realloc()
+ * behavior, @m must be non-NULL, because the resource pool is inherited
+ * from it.
  *
  * Like mb_alloc(), mb_realloc() also returns a pointer to the memory
- * chunk , not to the resource, hence you have to free it using
+ * chunk, not to the resource, hence you have to free it using
  * mb_free(), not rfree().
  */
 void *
-mb_realloc(pool *p, void *m, unsigned size)
+mb_realloc(void *m, unsigned size)
 {
   struct mblock *ob = NULL;
 
@@ -392,9 +392,7 @@ mb_realloc(pool *p, void *m, unsigned size)
     }
 
   struct mblock *b = xrealloc(ob, sizeof(struct mblock) + size);
-
-  b->r.class = &mb_class;
-  add_tail(&p->inside, &b->r.n);
+  replace_node(&b->r.n, &b->r.n);
   b->size = size;
   return b->data;
 }
@@ -413,3 +411,18 @@ mb_free(void *m)
   rfree(b);
 }
 
+
+
+#define STEP_UP(x) ((x) + (x)/2 + 4)
+
+void
+buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size)
+{
+  unsigned nsize = MIN(*size, need);
+
+  while (nsize < need)
+    nsize = STEP_UP(nsize);
+
+  *buf = mb_realloc(*buf, nsize*isize);
+  *size = nsize;
+}
index 6e0a769bb08058bcadbb6da12b5fed15773ba562..780d596bfe5fdf946589087ea6c171535fb32bf3 100644 (file)
@@ -44,6 +44,7 @@ typedef struct birdsock {
   /* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */
 
   int fd;                              /* System-dependent data */
+  int index;                           /* Index in poll buffer */
   node n;
   void *rbuf_alloc, *tbuf_alloc;
   char *password;                              /* Password for MD5 authentication */
@@ -91,6 +92,7 @@ extern int sk_priority_control;       /* Suggested priority for control traffic, shoul
 #define SKF_LADDR_TX   4       /* Allow to specify local address for TX packets */
 #define SKF_TTL_RX     8       /* Report TTL / Hop Limit for RX packets */
 
+#define SKF_THREAD     0x100   /* Socked used in thread, Do not add to main loop */
 
 /*
  *     Socket types                 SA SP DA DP IF  TTL SendTo (?=may, -=must not, *=must)
index 16b084fb787c525606c3b8caa67d543eec9344e7..7863472f3c36dfa725d93f32357dfe04a2821a7c 100644 (file)
--- a/proto/Doc
+++ b/proto/Doc
@@ -1,4 +1,5 @@
 H Protocols
+C bfd
 C bgp
 C ospf
 C pipe
diff --git a/proto/bfd/Doc b/proto/bfd/Doc
new file mode 100644 (file)
index 0000000..7ee5d3e
--- /dev/null
@@ -0,0 +1 @@
+S bfd.c
diff --git a/proto/bfd/Makefile b/proto/bfd/Makefile
new file mode 100644 (file)
index 0000000..77b8bd2
--- /dev/null
@@ -0,0 +1,5 @@
+source=bfd.c
+root-rel=../../
+dir-name=proto/bfd
+
+include ../../Rules
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
new file mode 100644 (file)
index 0000000..4c7fe1f
--- /dev/null
@@ -0,0 +1,496 @@
+#include "nest/bird.h"
+#include "nest/iface.h"
+#include "nest/protocol.h"
+#include "nest/route.h"
+#include "nest/cli.h"
+#include "conf/conf.h"
+#include "lib/socket.h"
+#include "lib/resource.h"
+#include "lib/string.h"
+
+#include "bfd.h"
+
+
+#define HASH_ID_KEY    loc_id
+#define HASH_ID_NEXT   next_id
+#define HASH_ID_EQ(a,b)        ((a)==(b))
+#define HASH_ID_FN(a)  (a)
+
+#define HASH_IP_KEY    addr
+#define HASH_IP_NEXT   next_ip
+#define HASH_IP_EQ(a,b)        ((a)==(b))
+#define HASH_IP_FN(a)  (a == b)
+
+static u32
+bfd_get_free_id(struct bfd_proto *p)
+{
+  u32 id;
+  for (id = random_u32(); 1; id++)
+    if (id && !bfd_find_session_by_id(p, id))
+      break;
+
+  return id;
+}
+
+static void
+bfd_add_session(struct bfd_proto *p, ip_addr addr, struct bfd_session_config *opts)
+{
+  birdloop_enter(p->loop);
+
+  struct bfd_session *s = sl_alloc(p->session_slab);
+  bzero(s, sizeof(struct bfd_session));
+
+  /* Initialization of state variables - see RFC 5880 3.8.1 */
+  s->loc_state = BFD_STATE_DOWN;
+  s->rem_state = BFD_STATE_DOWN;
+  s->loc_id = bfd_get_free_id(p);
+  s->des_min_tx_int = s->des_min_tx_new = s->opts->idle_tx_int;
+  s->req_min_rx_int = s->req_min_rx_new = s->opts->min_rx_int;
+  s->detect_mult = s->opts->multiplier;
+  s->rem_min_rx_int = 1;
+
+  HASH_INSERT(p->session_hash_id, HASH_ID, s);
+  HASH_INSERT(p->session_hash_ip, HASH_IP, s);
+
+  s->tx_timer = tm2_new_set(xxx, bfd_rx_timer_hook, s, 0, 0);
+  s->hold_timer = tm2_new_set(xxx, bfd_hold_timer_hook, s, 0, 0);
+  bfd_session_update_tx_interval(s);
+
+  birdloop_leave(p->loop);
+}
+
+static void
+bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
+{
+  birdloop_enter(p->loop);
+
+  s->bsock = bfd_get_socket(p, local, ifa);
+  s->local = local;
+  s->iface = ifa;
+  s->opened = 1;
+
+  bfd_session_control_tx_timer(s);
+
+  birdloop_leave(p->loop);
+}
+
+static void
+bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
+{
+  birdloop_enter(p->loop);
+
+  bfd_free_socket(s->bsock);
+  s->bsock = NULL;
+  s->local = IPA_NONE;
+  s->iface = NULL;
+  s->opened = 0;
+
+  bfd_session_control_tx_timer(s);
+
+  birdloop_leave(p->loop);
+}
+
+static void
+bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
+{
+  birdloop_enter(p->loop);
+
+  bfd_free_socket(s->bsock);
+
+  rfree(s->tx_timer);
+  rfree(s->hold_timer);
+
+  HASH_REMOVE(p->session_hash_id, HASH_ID, s);
+  HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
+
+  sl_free(p->session_slab, s);
+
+  birdloop_leave(p->loop);
+}
+
+struct bfd_session *
+bfd_find_session_by_id(struct bfd_proto *p, u32 id)
+{
+  return HASH_FIND(p->session_hash_id, HASH_ID, id);
+}
+
+struct bfd_session *
+bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
+{
+  return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
+}
+
+static void
+bfd_rx_timer_hook(timer2 *t)
+{
+  struct bfd_session *s = timer->data;
+
+  s->last_tx = xxx_now;
+  bfd_send_ctl(s->bfd, s, 0);
+}
+
+static void
+bfd_hold_timer_hook(timer2 *t)
+{
+  bfd_session_timeout(timer->data);
+}
+
+static void 
+bfd_session_timeout(struct bfd_session *s)
+{
+  s->rem_state = BFD_STATE_DOWN;
+  s->rem_id = 0;
+  s->rem_min_tx_int = 0;
+  s->rem_min_rx_int = 1;
+  s->rem_demand_mode = 0;
+
+  bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
+}
+
+static void
+bfd_session_control_tx_timer(struct bfd_session *s)
+{
+  if (!s->opened)
+    goto stop;
+
+  if (s->passive && (s->rem_id == 0))
+    goto stop;
+
+  if (s->rem_demand_mode && 
+      !s->poll_active && 
+      (s->loc_state == BFD_STATE_UP) &&
+      (s->rem_state == BFD_STATE_UP))
+    goto stop;
+
+  if (s->rem_min_rx_int == 0)
+    goto stop;
+
+  /* So TX timer should run */
+  if (tm2_active(s->tx_timer))
+    return;
+
+  tm2_start(s->tx_timer, 0);
+  return;
+
+ stop:
+  tm2_stop(s->tx_timer);
+  s->last_tx = 0;
+}
+
+static void
+bfd_session_update_tx_interval(struct bfd_session *s)
+{
+  u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int);
+  u32 tx_int_l = tx_int - (tx_int / 4);         // 75 %
+  u32 tx_int_h = tx_int - (tx_int / 10); // 90 %
+
+  s->tx_timer->recurrent = tx_int_l;
+  s->tx_timer->randomize = tx_int_h - tx_int_l;
+
+  /* Do not set timer if no previous event */
+  if (!s->last_tx)
+    return;
+
+  /* Set timer relative to last tx_timer event */
+  tm2_set(s->tx_timer, s->last_tx + tx_int_l);
+}
+
+static void
+bfd_session_update_detection_time(struct bfd_session *s, int kick)
+{
+  xxx_time timeout = (xxx_time) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
+
+  if (kick)
+    s->last_rx = xxx_now;
+
+  if (!s->last_rx)
+    return;
+
+  tm2_set(s->hold_timer, s->last_rx + timeout);
+}
+
+void
+bfd_session_request_poll(struct bfd_session *s, u8 request)
+{
+  s->poll_scheduled |= request;
+
+  if (s->poll_active)
+    return;
+
+  s->poll_active = s->poll_scheduled;
+  s->poll_scheduled = 0;
+  bfd_send_ctl(s->bfd, s, 0);
+}
+
+void
+bfd_session_terminate_poll(struct bfd_session *s)
+{
+  u8 poll_done = s->poll_active & ~s->poll_scheduled;
+
+  if (poll_done & BFD_POLL_TX)
+    s->des_min_tx_int = s->des_min_tx_new;
+
+  if (poll_done & BFD_POLL_RX)
+    s->req_min_rx_int = s->req_min_rx_new;
+
+  s->poll_active = 0;
+
+  /* Timers are updated by caller - bfd_session_process_ctl() */
+
+  xxx_restart_poll();
+}
+
+void
+bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_rx_int, u32 old_tx_int)
+{
+  if (s->poll_active && (flags & BFD_FLAG_FINAL))
+    bfd_session_terminate_poll(s);
+
+  if ((s->des_min_tx_int != old_rx_int) || (s->rem_min_rx_int != old_tx_int))
+    bfd_session_update_tx_interval(s);
+
+  bfd_session_update_detection_time(s, 1);
+
+  /* Update session state */
+  int next_state = 0;
+  int diag = BFD_DIAG_NOTHING;
+
+  switch (s->loc_state)
+  {
+  case BFD_STATE_ADMIN_DOWN:
+    return;
+
+  case BFD_STATE_DOWN:
+    if (s->rem_state == BFD_STATE_DOWN)                next_state = BFD_STATE_INIT;
+    else if (s->rem_state == BFD_STATE_INIT)   next_state = BFD_STATE_UP;
+    break;
+
+  case BFD_STATE_INIT:
+    if (s->rem_state == BFD_STATE_ADMIN_DOWN)  next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
+    else if (s->rem_state >= BFD_STATE_INIT)   next_state = BFD_STATE_UP;
+    break;
+
+  case BFD_STATE_UP:
+    if (s->rem_state <= BFD_STATE_DOWN)                next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
+    break;
+  }
+
+  if (next_state)
+    bfd_session_update_state(s, next_state, diag);
+
+  bfd_session_control_tx_timer(s);
+
+  if (flags & BFD_FLAG_POLL)
+    bfd_send_ctl(p, s, 1);
+}
+
+
+static void
+bfd_session_set_min_tx(struct bfd_session *s, u32 val)
+{
+  /* Note that des_min_tx_int <= des_min_tx_new */
+
+  if (val == s->des_min_tx_new)
+    return;
+
+  s->des_min_tx_new = val;
+
+  /* Postpone timer update if des_min_tx_int increases and the session is up */
+  if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int))
+  {
+    s->des_min_tx_int = val;
+    bfd_session_update_tx_interval(s);
+  }
+
+  bfd_session_request_poll(s, BFD_POLL_TX);
+}
+
+static void
+bfd_session_set_min_rx(struct bfd_session *s, u32 val)
+{
+  /* Note that req_min_rx_int >= req_min_rx_new */
+
+  if (val == s->req_min_rx_new)
+    return;
+
+  s->req_min_rx_new = val; 
+
+  /* Postpone timer update if req_min_rx_int decreases and the session is up */
+  if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int))
+  {
+    s->req_min_rx_int = val;
+    bfd_session_update_detection_time(s, 0);
+  }
+
+  bfd_session_request_poll(s, BFD_POLL_RX);
+}
+
+static void
+bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
+{
+  n->session = bfd_add_session(p, n->addr, n->opts);
+
+  if (n->opts->multihop)
+  {
+    bfd_open_session(p, n->session, n->local, NULL);
+    return;
+  }
+
+  struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY);
+  if (!nb)
+  {
+    log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
+    return;
+  }
+
+  if (nb->data)
+  {
+    log(L_ERR "%s: Duplicate remote address %I", p->p.name, n->addr);
+    return;
+  }
+
+  nb->data = n->session;
+
+  if (nb->scope > 0)
+    bfd_open_session(p, n->session, nb->iface->addr->ip, nb->iface);
+  else
+    TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, cf->iface);
+}
+
+static void
+bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
+{
+  if (!n->opts->multihop)
+  {
+    struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, 0);
+    if (nb)
+      nb->data = NULL;
+  }
+
+  bfd_remove_session(p, n->session);
+}
+
+
+static void
+bfd_neigh_notify(struct neighbor *nb)
+{
+  struct bfd_proto *p = (struct bfd_proto *) nb->proto;
+  struct bfd_session *s = nb->data;
+
+  if (!s)
+    return;
+
+  if ((nb->scope > 0) && !s->opened)
+    bfd_open_session(p, s, nb->iface->addr->ip, nb->iface);
+
+  if ((nb->scope <= 0) && s->opened)
+    bfd_close_session(p, s);
+}
+
+
+
+static struct proto *
+bfd_init(struct proto_config *c)
+{
+  struct proto *p = proto_new(c, sizeof(struct bfd_proto));
+
+  p->if_notify = bfd_if_notify;
+  p->ifa_notify = bfd_ifa_notify;
+
+  return p;
+}
+
+static int
+bfd_start(struct proto *P)
+{
+  struct bfd_proto *p = (struct bfd_proto *) P;
+  struct bfd_config *cf = (struct bfd_config *) (P->cf);
+
+  p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
+  init_list(&p->sockets);
+
+  HASH_INIT(p->session_hash_id, P->pool, 16);
+  HASH_INIT(p->session_hash_ip, P->pool, 16);
+
+  struct bfd_neighbor *n;
+  WALK_LIST(n, cf->neighbors)
+    bfd_start_neighbor(p, n);
+
+  return PS_UP;
+}
+
+
+static int
+bfd_shutdown(struct proto *P)
+{
+  struct bfd_proto *p = (struct bfd_proto *) P;
+
+  return PS_DOWN;
+}
+
+static inline int
+bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
+{
+  return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
+    (x->iface == y->iface) && (x->opts->multihop == y->opts->multihop);
+}
+
+static void
+bfd_match_neighbor(struct bfd_proto *p, struct bfd_neighbor *on, struct bfd_config *new)
+{
+  struct bfd_neighbor *nn;
+
+  if (r->neigh)
+    r->neigh->data = NULL;
+
+  WALK_LIST(nn, new->neighbors)
+    if (bfd_same_neighbor(nn, on))
+    {
+      nn->session = on->session;
+      // XXX reconfiguration of session?
+      return;
+    }
+
+  bfd_stop_neighbor(p, on);
+}
+
+static int
+bfd_reconfigure(struct proto *P, struct proto_config *c)
+{
+  struct bfd_proto *p = (struct bfd_proto *) P;
+  struct bfd_config *old = (struct bfd_config *) (P->cf);
+  struct bfd_config *new = (struct bfd_config *) c;
+  struct bfd_neighbor *n;
+
+  WALK_LIST(n, old->neighbors)
+    bfd_match_neighbor(p, n, new);
+
+  WALK_LIST(n, new->neighbors)
+    if (!n->session)
+      bfd_start_neighbor(p, n);
+
+  return 1;
+}
+
+static void
+bfd_copy_config(struct proto_config *dest, struct proto_config *src)
+{
+  struct bfd_config *d = (struct bfd_config *) dest;
+  struct bfd_config *s = (struct bfd_config *) src;
+
+  /* We clean up patt_list, ifaces are non-sharable */
+  init_list(&d->patt_list);
+
+  /* We copy pref_list, shallow copy suffices */
+  cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct bfd_prefix_config));
+}
+
+struct protocol proto_bfd = {
+  .name =              "BFD",
+  .template =          "bfd%d",
+  .init =              bfd_init,
+  .start =             bfd_start,
+  .shutdown =          bfd_shutdown,
+  .reconfigure =       bfd_reconfigure,
+  .copy_config =       bfd_copy_config,
+};
diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h
new file mode 100644 (file)
index 0000000..a54053d
--- /dev/null
@@ -0,0 +1,118 @@
+
+#ifndef _BIRD_BFD_H_
+#define _BIRD_BFD_H_
+
+#define BFD_CONTROL_PORT       3784
+#define BFD_ECHO_PORT          3785
+#define BFD_MULTI_CTL_PORT     4784
+
+#define BFD_DEFAULT_MIN_RX_INT (10 MS)
+#define BFD_DEFAULT_MIN_TX_INT (100 MS)
+#define BFD_DEFAULT_IDLE_TX_INT        (1 S)
+#define BFD_DEFAULT_MULTIPLIER 5
+
+
+struct bfd_config
+{
+  struct proto_config c;
+  list neighbors;              /* List of struct bfd_neighbor */
+};
+
+struct bfd_session_config
+{
+  u32 min_rx_int;
+  u32 min_tx_int;
+  u32 idle_tx_int;
+  u8 multiplier;
+  u8 multihop;
+  u8 passive;
+};
+
+struct bfd_neighbor
+{
+  node n;
+  ip_addr addr;
+  ip_addr local;
+  struct iface *iface;
+  struct bfd_session_config *opts;
+
+  struct bfd_session *session;
+};
+
+struct bfd_proto
+{
+  struct proto p;
+
+  slab *session_slab;
+  HASH(struct bfd_session) session_hash_id;
+  HASH(struct bfd_session) session_hash_ip;
+
+  list sockets;
+};
+
+struct bfd_socket
+{
+  node n;
+  sock *sk;
+  u32 uc;
+};
+
+struct bfd_session
+{
+  node n;
+  struct bfd_session *next_id;         /* Next in bfd.session_hash_id */
+  struct bfd_session *next_ip;         /* Next in bfd.session_hash_ip */
+
+  u8 opened;
+  u8 poll_active;
+  u8 poll_scheduled;
+  
+  u8 loc_state;
+  u8 rem_state;
+  u8 loc_diag;
+  u32 loc_id;                          /* Local session ID (local discriminator) */
+  u32 rem_id;                          /* Remote session ID (remote discriminator) */
+  u32 des_min_tx_int;                  /* Desired min rx interval, local option */
+  u32 des_min_tx_new;                  /* Used for des_min_tx_int change */
+  u32 req_min_rx_int;                  /* Required min tx interval, local option */
+  u32 req_min_rx_new;                  /* Used for req_min_rx_int change */
+  u32 rem_min_tx_int;                  /* Last received des_min_tx_int */
+  u32 rem_min_rx_int;                  /* Last received req_min_rx_int */
+  u8 demand_mode;                      /* Currently unused */
+  u8 rem_demand_mode;
+  u8 detect_mult;                      /* Announced detect_mult, local option */
+  u8 rem_detect_mult;                  /* Last received detect_mult */
+
+  xxx_time last_tx;                    /* Time of last sent periodic control packet */
+  xxx_time last_rx;                    /* Time of last received valid control packet */
+
+  timer2 *tx_timer;                    /* Periodic control packet timer */
+  timer2 *hold_timer;                  /* Timer for session down detection time */
+};
+
+
+
+#define BFD_STATE_ADMIN_DOWN   0
+#define BFD_STATE_DOWN         1
+#define BFD_STATE_INIT         2
+#define BFD_STATE_UP           3
+
+#define BFD_DIAG_NOTHING       0
+#define BFD_DIAG_TIMEOUT       1
+#define BFD_DIAG_ECHO_FAILED   2
+#define BFD_DIAG_NEIGHBOR_DOWN 3
+#define BFD_DIAG_FWD_RESET     4
+#define BFD_DIAG_PATH_DOWN     5
+#define BFD_DIAG_C_PATH_DOWN   6
+#define BFD_DIAG_ADMIN_DOWN    7
+#define BFD_DIAG_RC_PATH_DOWN  8
+
+#define BFD_POLL_TX            1
+#define BFD_POLL_RX            2
+
+
+
+
+
+
+#endif _BIRD_BFD_H_
diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y
new file mode 100644 (file)
index 0000000..a5414d4
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *     BIRD -- Router Advertisement Configuration
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/bfd/bfd.h"
+
+CF_DEFINES
+
+#define BFD_CFG ((struct bfd_config *) this_proto)
+#define BFD_SESSION this_bfd_session
+#define BFD_NEIGHBOR this_bfd_neighbor
+
+static struct bfd_session_config *this_bfd_session;
+static struct bfd_neighbor *this_bfd_neighbor;
+
+
+CF_DECLS
+
+CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, MULTIHOP, PASSIVE,
+       NEIGHBOR)
+
+%type <iface> bfd_neigh_iface
+%type <a> bfd_neigh_local
+
+CF_GRAMMAR
+
+CF_ADDTO(proto, bfd_proto)
+
+bfd_proto_start: proto_start BFD
+{
+  this_proto = proto_config_new(&proto_bfd, sizeof(struct bfd_config), $1);
+  init_list(&BFD_CFG->neighbors);
+};
+
+bfd_proto_item:
+   proto_item
+ | bfd_neighbor
+ ;
+
+bfd_proto_opts:
+   /* empty */
+ | bfd_proto_opts bfd_proto_item ';'
+ ;
+
+bfd_proto:
+   bfd_proto_start proto_name '{' bfd_proto_opts '}';
+
+
+bfd_session_start:
+{
+  this_bfd_session = cfg_allocz(sizeof(struct bfd_session_config));
+
+  BFD_SESSION->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
+  BFD_SESSION->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
+  BFD_SESSION->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
+  BFD_SESSION->multiplier = BFD_DEFAULT_MULTIPLIER;
+};
+
+bfd_session_item:
+   INTERVAL expr_us { BFD_SESSION->min_rx_int = BFD_SESSION->min_tx_int = $2; }
+ | MIN RX INTERVAL expr_us { BFD_SESSION->min_rx_int = $4; }
+ | MIN TX INTERVAL expr_us { BFD_SESSION->min_tx_int = $4; }
+ | IDLE TX INTERVAL expr_us { BFD_SESSION->idle_tx_int = $4; }
+ | MULTIPLIER expr { BFD_SESSION->multiplier = $2; }
+ | MULTIHOP bool { BFD_SESSION->multihop = $2; }
+ | PASSIVE bool { BFD_SESSION->passive = $2; }
+ ;
+
+bfd_session_opts:
+   /* empty */
+ | bfd_session_opts bfd_session_item ';'
+ ;
+
+bfd_session_opt_list:
+   /* empty */
+ | '{' bfd_session_opts '}'
+ ;
+
+bfd_session: 
+   bfd_session_start bfd_session_opt_list;
+
+
+bfd_neigh_iface:
+   /* empty */ { $$ = NULL; }
+ | '%' SYM { $$ = if_get_by_name($2->name); }
+ | DEV TEXT { $$ = if_get_by_name($2); }
+ ;
+
+bfd_neigh_local:
+   /* empty */ { $$ = IPA_NONE; }
+ | LOCAL ipa { $$ = $2; }
+ ;
+
+bfd_neighbor: NEIGHBOR ipa bfd_neigh_iface bfd_neigh_local bfd_session
+{
+  this_bfd_neighbor = cfg_allocz(sizeof(struct bfd_neighbor));
+  add_tail(&BFD_CFG->neighbors, NODE this_bfd_neighbor);
+
+  BFD_NEIGHBOR->addr = $2;
+  BFD_NEIGHBOR->local = $4;
+  BFD_NEIGHBOR->iface = $3;
+  BFD_NEIGHBOR->opts = BFD_SESSION;
+};
+
+
+CF_CODE
+
+CF_END
diff --git a/proto/bfd/io.c b/proto/bfd/io.c
new file mode 100644 (file)
index 0000000..f7fbc67
--- /dev/null
@@ -0,0 +1,587 @@
+
+#include "lib/buffer.h"
+#include "lib/heap.h"
+
+struct birdloop
+{
+  pool *pool;
+
+  int wakeup_fds[2];
+  u8 poll_active;
+
+  xxx_time last_time;
+  xxx_time real_time;
+  u8 use_monotonic_clock;
+
+  BUFFER(timer2 *) timers;
+  list event_list;
+  list sock_list;
+  uint sock_num;
+
+  BUFFER(sock *) poll_sk;
+  BUFFER(struct pollfd) poll_fd;
+  u8 poll_changed;
+  u8 close_scheduled;
+
+};
+
+
+static void times_update_alt(struct birdloop *loop);
+
+static int
+times_init(struct birdloop *loop)
+{
+  struct timespec ts;
+  int rv;
+
+  rv = clock_gettime(CLOCK_MONOTONIC, &ts);
+  if (rv < 0)
+  {
+    // log(L_WARN "Monotonic clock is missing");
+
+    loop->use_monotonic_clock = 0;
+    loop->last_time = 0;
+    loop->real_time = 0;
+    times_update_alt(loop);
+    return;
+  }
+
+  /*
+  if ((tv.tv_sec < 0) || (((s64) tv.tv_sec) > ((s64) 1 << 40)))
+    log(L_WARN "Monotonic clock is crazy");
+  */
+
+  loop->use_monotonic_clock = 1;
+  loop->last_time = (tv.tv_sec S) + (tv.tv_nsec / 1000);
+  loop->real_time = 0;
+}
+
+static void
+times_update_pri(struct birdloop *loop)
+{
+  struct timespec ts;
+  int rv;
+
+  rv = clock_gettime(CLOCK_MONOTONIC, &ts);
+  if (rv < 0)
+    die("clock_gettime: %m");
+
+  xxx_time new_time = (tv.tv_sec S) + (tv.tv_nsec / 1000);
+
+  /*
+  if (new_time < loop->last_time)
+    log(L_ERR "Monotonic clock is broken");
+  */
+
+  loop->last_time = new_time;
+  loop->real_time = 0;
+}
+
+static void
+times_update_alt(struct birdloop *loop)
+{
+  struct timeval tv;
+  int rv;
+
+  rv = gettimeofday(&tv, NULL);
+  if (rv < 0)
+    die("gettimeofday: %m");
+
+  xxx_time new_time = (tv.tv_sec S) + tv.tv_usec;
+  xxx_time delta = new_time - loop->real_time;
+
+  if ((delta < 0) || (delta > (60 S)))
+  {
+    /*
+    if (loop->real_time)
+      log(L_WARN "Time jump, delta %d us", (int) delta);
+    */
+
+    delta = 100 MS;
+  }
+
+  loop->last_time += delta;
+  loop->real_time = new_time;
+}
+
+static void
+times_update(struct birdloop *loop)
+{
+  if (loop->use_monotonic_clock)
+    times_update_pri(loop);
+  else
+    times_update_alt(loop);
+}
+
+
+
+static void
+pipe_new(int *pfds)
+{
+  int pfds[2], rv;
+  sock *sk;
+  
+  rv = pipe(pfds);
+  if (rv < 0)
+    die("pipe: %m");
+
+  if (fcntl(pfds[0], F_SETFL, O_NONBLOCK) < 0)
+    die("fcntl(O_NONBLOCK): %m");
+
+  if (fcntl(pfds[1], F_SETFL, O_NONBLOCK) < 0)
+    die("fcntl(O_NONBLOCK): %m");
+}
+
+static void
+wakeup_init(struct birdloop *loop)
+{
+  pipe_new(loop->wakeup_fds);
+}
+
+static void
+wakeup_drain(struct birdloop *loop)
+{
+  char buf[64];
+  int rv;
+  
+ try:
+  rv = read(loop->wakeup_fds[0], buf, 64);
+  if (rv < 0)
+  {
+    if (errno == EINTR)
+      goto try;
+    if (errno == EAGAIN)
+      return;
+    die("wakeup read: %m");
+  }
+  if (rv == 64)
+    goto try;
+}
+
+static void
+wakeup_kick(struct birdloop *loop)
+{
+  u64 v = 1;
+  int rv;
+
+ try:
+  rv = write(loop->wakeup_fds[1], &v, sizeof(u64));
+  if (rv < 0)
+  {
+    if (errno == EINTR)
+      goto try;
+    if (errno == EAGAIN)
+      return;
+    die("wakeup write: %m");
+  }
+}
+
+
+
+
+static inline uint events_waiting(struct birdloop *loop)
+{ return !EMPTY_LIST(loop->event_list); }
+
+static void
+events_init(struct birdloop *loop)
+{
+  list_init(&poll->event_list);
+}
+
+static void
+events_fire(struct birdloop *loop)
+{
+  times_update(loop);
+  ev_run_list(&loop->event_list);
+}
+
+void
+ev2_schedule(event *e)
+{
+  if (loop->poll_active && EMPTY_LIST(loop->event_list))
+    wakeup_kick(loop);
+
+  if (e->n.next)
+    rem_node(&e->n);
+
+  add_tail(&loop->event_list, &e->n);
+}
+
+
+#define TIMER_LESS(a,b)                ((a)->expires < (b)->expires)
+#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
+                                  heap[a]->index = (a), heap[b]->index = (b))
+
+
+static inline uint timers_count(struct birdloop *loop)
+{ return loop->timers.used - 1; }
+
+static inline timer2 *timers_first(struct birdloop *loop)
+{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
+
+
+static void
+tm2_free(resource *r)
+{
+  timer2 *t = (timer2 *) r;
+
+  tm2_stop(t);
+}
+
+static void
+tm2_dump(resource *r)
+{
+  timer2 *t = (timer2 *) r;
+
+  debug("(code %p, data %p, ", t->hook, t->data);
+  if (t->randomize)
+    debug("rand %d, ", t->randomize);
+  if (t->recurrent)
+    debug("recur %d, ", t->recurrent);
+  if (t->expires)
+    debug("expires in %d sec)\n", t->expires - xxx_now);
+  else
+    debug("inactive)\n");
+}
+
+static struct resclass tm2_class = {
+  "Timer",
+  sizeof(timer),
+  tm2_free,
+  tm2_dump,
+  NULL,
+  NULL
+};
+
+timer2 *
+tm2_new(pool *p)
+{
+  timer2 *t = ralloc(p, &tm2_class);
+  t->index = -1;
+  return t;
+}
+
+void
+tm2_start(timer2 *t, xxx_time after)
+{
+  xxx_time when = loop->last_time + after;
+  uint tc = timers_count(loop);
+
+  if (!t->expires)
+  {
+    t->index = ++tc;
+    t->expires = when;
+    BUFFER_PUSH(loop->timers) = t;
+    HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP);
+  }
+  else if (t->expires < when)
+  {
+    t->expires = when;
+    HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
+  }
+  else if (t->expires > when)
+  {
+    t->expires = when;
+    HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
+  }
+
+  if (loop->poll_active && (t->index == 1))
+    wakeup_kick(loop);
+}
+
+void
+tm2_stop(timer2 *t)
+{
+  if (!t->expires)
+    return;
+
+  uint tc = timers_count(XXX);
+  HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
+  BUFFER_POP(loop->timers);
+
+  t->index = -1;
+  t->expires = 0;
+}
+
+static void
+timers_init(struct birdloop *loop)
+{
+  BUFFER_INIT(loop->timers, loop->pool, 4);
+  BUFFER_PUSH(loop->timers) = NULL;
+}
+
+static void
+timers_fire(struct birdloop *loop)
+{
+  xxx_time base_time;
+  timer2 *t;
+
+  times_update(loop);
+  base_time = loop->last_time;
+
+  while (t = timers_first(loop))
+  {
+    if (t->expires > base_time)
+      return;
+
+    if (t->recurrent)
+    {
+      xxx_time after = t->recurrent;
+      xxx_time delta = loop->last_time - t->expires;
+      
+      if (t->randomize)
+       after += random() % (t->randomize + 1);
+
+      if (delta > after)
+       delta = 0;
+
+      tm2_start(t, after - delta);
+    }
+    else
+      tm2_stop(t);
+
+    t->hook(t);
+  }
+}
+
+
+static void
+sockets_init(struct birdloop *loop)
+{
+  list_init(&poll->sock_list);
+  poll->sock_num = 0;
+
+  BUFFER_INIT(loop->poll_sk, loop->pool, 4);
+  BUFFER_INIT(loop->poll_fd, loop->pool, 4);
+  poll_changed = 0;
+}
+
+static void
+sockets_add(struct birdloop *loop, sock *s)
+{
+  add_tail(&loop->sock_list, &s->n);
+  loop->sock_num++;
+
+  s->index = -1;
+  loop->poll_changed = 1;
+
+  if (loop->poll_active)
+    wakeup_kick(loop);
+}
+
+void
+sk_start(sock *s)
+{
+  sockets_add(xxx_loop, s);
+}
+
+static void
+sockets_remove(struct birdloop *loop, sock *s)
+{
+  rem_node(&s->n);
+  loop->sock_num--;
+
+  if (s->index >= 0)
+    s->poll_sk.data[sk->index] = NULL;
+
+  s->index = -1;
+  loop->poll_changed = 1;
+
+  /* Wakeup moved to sk_stop() */
+}
+
+void
+sk_stop(sock *s)
+{
+  sockets_remove(xxx_loop, s);
+
+  if (loop->poll_active)
+  {
+    loop->close_scheduled = 1;
+    wakeup_kick(loop);
+  }
+  else
+    close(s->fd);
+
+  s->fd = -1;
+}
+
+static inline uint sk_want_events(sock *s)
+{ return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
+
+static void
+sockets_update(struct birdloop *loop, sock *s)
+{
+  if (s->index >= 0)
+    s->poll_fd.data[s->index].events = sk_want_events(s);
+}
+
+static void
+sockets_prepare(struct birdloop *loop)
+{
+  BUFFER_SET(loop->poll_sk, loop->sock_num + 1);
+  BUFFER_SET(loop->poll_fd, loop->sock_num + 1);
+
+  struct pollfd *pfd = loop->poll_fd.data;
+  sock **psk = loop->poll_sk.data;
+  int i = 0;
+  node *n;
+
+  WALK_LIST(n, &loop->sock_list)
+  {
+    sock *s = SKIP_BACK(sock, n, n);
+
+    ASSERT(i < loop->sock_num);
+
+    s->index = i;
+    *psk = s;
+    pfd->fd = s->fd;
+    pfd->events = sk_want_events(s);
+    pfd->revents = 0;
+
+    pfd++;
+    psk++;
+    i++;
+  }
+
+  ASSERT(i == loop->sock_num);
+
+  /* Add internal wakeup fd */
+  *psk = NULL;
+  pfd->fd = loop->wakeup_fds[0];
+  pfd->events = POLLIN;
+  pfd->revents = 0;
+
+  loop->poll_changed = 0;
+}
+
+static void
+sockets_close_fds(struct birdloop *loop)
+{
+  struct pollfd *pfd = loop->poll_fd.data;
+  sock **psk = loop->poll_sk.data;
+  int poll_num = loop->poll_fd.used - 1;
+
+  int i;
+  for (i = 0; i < poll_num; i++)
+    if (psk[i] == NULL)
+      close(pfd[i].fd);
+
+  loop->close_scheduled = 0;
+}
+
+
+static void
+sockets_fire(struct birdloop *loop)
+{
+  struct pollfd *pfd = loop->poll_fd.data;
+  sock **psk = loop->poll_sk.data;
+  int poll_num = loop->poll_fd.used - 1;
+
+  times_update(loop);
+
+  /* Last fd is internal wakeup fd */
+  if (pfd[loop->sock_num].revents & POLLIN)
+    wakeup_drain(loop);
+
+  int i;
+  for (i = 0; i < poll_num; pfd++, psk++, i++)
+  {
+    int e = 1;
+
+    if (! pfd->revents)
+      continue;
+
+    if (pfd->revents & POLLNVAL)
+      die("poll: invalid fd %d", pfd->fd);
+
+    if (pfd->revents & POLLIN)
+      while (e && *psk && (*psk)->rx_hook)
+       e = sk_read(*psk);
+
+    e = 1;
+    if (pfd->revents & POLLOUT)
+      while (e && *psk)
+       e = sk_write(*psk);
+  }
+}
+
+
+struct birdloop *
+birdloop_new(pool *p)
+{
+  struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
+  p->pool = p;
+
+  times_init(loop);
+  wakeup_init(loop);
+
+  events_init(loop);
+  timers_init(loop);
+  sockets_init(loop);
+
+  return loop;
+}
+
+void
+birdloop_enter(struct birdloop *loop)
+{
+  pthread_mutex_lock(loop->mutex);
+}
+
+void
+birdloop_leave(struct birdloop *loop)
+{
+  pthread_mutex_unlock(loop->mutex);
+}
+
+
+void
+birdloop_main(struct birdloop *loop)
+{
+  timer2 *t;
+  int timeout;
+
+  while (1)
+  {
+    events_fire(loop);
+    timers_fire(loop);
+
+    times_update(loop);
+    if (events_waiting(loop))
+      timeout = 0;
+    else if (t = timers_first(loop))
+      timeout = (tm2_remains(t) TO_MS) + 1;
+    else
+      timeout = -1;
+
+    if (loop->poll_changed)
+      sockets_prepare(loop);
+
+    loop->poll_active = 1;
+    pthread_mutex_unlock(loop->mutex);
+
+  try:
+    rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
+    if (rv < 0)
+    {
+      if (errno == EINTR || errno == EAGAIN)
+       goto try;
+      die("poll: %m");
+    }
+
+    pthread_mutex_lock(loop->mutex);
+    loop->poll_active = 0;
+
+    if (loop->close_scheduled)
+      sockets_close_fds(loop);
+
+    if (rv)
+      sockets_fire(loop);
+
+    timers_fire(loop);
+  }
+}
+
+
+
diff --git a/proto/bfd/io.h b/proto/bfd/io.h
new file mode 100644 (file)
index 0000000..7fc4519
--- /dev/null
@@ -0,0 +1,62 @@
+
+typedef s64 xxx_time;
+
+typedef struct timer
+{
+  resource r;
+  void (*hook)(struct timer2 *);
+  void *data;
+
+  xxx_time expires;                    /* 0=inactive */
+  unsigned randomize;                  /* Amount of randomization */
+  unsigned recurrent;                  /* Timer recurrence */
+
+  int index;
+} timer;
+
+
+
+void ev2_schedule(event *e);
+
+
+
+timer2 *tm2_new(pool *p);
+void tm2_start(timer2 *t, xxx_time after);
+void tm2_stop(timer2 *t);
+
+static inline xxx_time
+tm2_remains(timer2 *t)
+{
+  return (t->expires > xxxnow) ? t->expires - xxxnow : 0;
+}
+
+static inline void
+tm2_start_max(timer2 *t, xxx_time after)
+{
+  xxx_time rem = tm2_remains(t);
+  tm2_start(t, MAX(rem, after));
+}
+
+static inline timer2 *
+tm2_new_set(pool *p, void (*hook)(struct timer2 *), void *data, uint rec, uint rand)
+{
+  timer2 *t = tm2_new(p);
+  t->hook = hook;
+  t->data = data;
+  t->recurrent = rec;
+  t->randomize = rand;
+  return t;
+}
+
+
+
+void sk_start(sock *s);
+void sk_stop(sock *s);
+
+
+
+struct birdloop *birdloop_new(pool *p);
+void birdloop_enter(struct birdloop *loop);
+void birdloop_leave(struct birdloop *loop);
+void birdloop_main(struct birdloop *loop);
+
diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c
new file mode 100644 (file)
index 0000000..e48a5aa
--- /dev/null
@@ -0,0 +1,208 @@
+
+
+#define BFD_FLAG_POLL          (1 << 5)
+#define BFD_FLAG_FINAL         (1 << 4)
+#define BFD_FLAG_CPI           (1 << 3)
+#define BFD_FLAG_AP            (1 << 2)
+#define BFD_FLAG_DEMAND                (1 << 1)
+#define BFD_FLAG_MULTIPOINT    (1 << 0)
+
+
+struct bfd_ctl_packet
+{
+  u8 vdiag;                    /* version and diagnostic */
+  u8 flags;                    /* state and flags */
+  u8 detect_mult;
+  u8 length;
+  u32 snd_id;                  /* sender ID, aka 'my discriminator' */
+  u32 rcv_id;                  /* receiver ID, aka 'your discriminator' */
+  u32 des_min_tx_int;
+  u32 req_min_rx_int;
+  u32 req_min_echo_rx_int;
+};
+
+
+static inline void bfd_pack_vdiag(u8 version, u8 diag)
+{ return (version << 5) | diag; }
+
+static inline void bfd_pack_flags(u8 state, u8 flags)
+{ return (state << 6) | diag; }
+
+static inline u8 bfd_pkt_get_version(struct bfd_ctl_packet *pkt)
+{ return pkt->vdiag >> 5; }
+
+static inline u8 bfd_pkt_get_diag(struct bfd_ctl_packet *pkt)
+{ return pkt->vdiag && 0x1f; }
+
+
+static inline u8 bfd_pkt_get_state(struct bfd_ctl_packet *pkt)
+{ return pkt->flags >> 6; }
+
+static inline void bfd_pkt_set_state(struct bfd_ctl_packet *pkt, u8 val)
+{ pkt->flags = val << 6; }
+
+
+void
+bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
+{
+  sock *sk = p->skX;
+  struct bfd_ctl_packet *pkt = (struct ospf_packet *) sk->tbuf;
+
+  pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag);
+  pkt->flags = bfd_pack_flags(s->loc_state, 0);
+  pkt->detect_mult = s->detect_mult;
+  pkt->length = 24;
+  pkt->snd_id = htonl(s->loc_id);
+  pkt->rcv_id = htonl(s->rem_id);
+  pkt->des_min_tx_int = htonl(s->des_min_tx_int);
+  pkt->req_min_rx_int = htonl(s->req_min_rx_int);
+  pkt->req_min_echo_rx_int = 0;
+
+  if (final)
+    pkt->flags |= BFD_FLAG_FINAL;
+  else if (s->poll_active)
+    pkt->flags |= BFD_FLAG_POLL;
+
+  // XXX
+  sk_send_to(sk, len, dst, 0);  
+}
+
+int
+bfd_ctl_rx_hook(sock *sk, int len)
+{
+  struct bfd_proto *p = sk->data;
+  struct bfd_ctl_packet *pkt =sk->rbuf;
+
+  if (len < BFD_BASE_LEN)
+    DROP("too short", len);
+
+  u8 version = bfd_pkt_get_version(pkt);
+  if (version != 1)
+    DROP("version mismatch", version);
+
+  if ((pkt->length < BFD_BASE_LEN) || (pkt->length > len))
+    DROP("length mismatch", pkt->length);
+
+  if (pkt->detect_mult == 0)
+    DROP("invalid detect mult", 0);
+
+  if (pkt->flags & BFD_FLAG_MULTIPOINT)
+    DROP("invalid flags", pkt->flags);
+
+  if (pkt->snd_id == 0)
+    DROP("invalid my discriminator", 0);
+
+  struct bfd_session *s;
+  u32 id = ntohl(pkt->rcv_id);
+
+  if (id)
+  {
+    s = bfd_find_session_by_id(p, id);
+
+    if (!s)
+      DROP("unknown session", id);
+  }
+  else
+  {
+    u8 ps = bfd_pkt_get_state(pkt);
+    if (ps > BFD_STATE_DOWN)
+      DROP("invalid init state", ps);
+      
+    s = bfd_find_session_by_ip(p, sk->faddr);
+
+    /* FIXME: better session matching and message */
+    if (!s || !s->opened)
+      return;
+  }
+
+  /* FIXME: better authentication handling and message */
+  if (pkt->flags & BFD_FLAG_AP)
+    DROP("authentication not supported", 0);
+
+
+  u32 old_rx_int = s->des_min_tx_int;
+  u32 old_tx_int = s->rem_min_rx_int;
+
+  s->rem_id = ntohl(pkt->snd_id);
+  s->rem_state = bfd_pkt_get_state(pkt);
+  s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND;
+  s->rem_min_tx_int = ntohl(pkt->des_min_tx_int);
+  s->rem_min_rx_int = ntohl(pkt->req_min_rx_int);
+  s->rem_detect_mult = pkt->detect_mult;
+
+  bfd_session_process_ctl(s, pkt->flags, xxx);
+  return 1;
+
+ drop:
+  // log(L_WARN "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val);
+  return 1;
+}
+
+sock *
+bfd_open_rx_sk(struct bfd_proto *p, int multihop)
+{
+  sock *sk = sk_new(p->p.pool);
+  sk->type = SK_UDP;
+  sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
+  sk->data = p;
+
+  sk->rbsize = 64; // XXX
+  sk->rx_hook = bfd_rx_hook;
+  sk->err_hook = bfd_err_hook;
+  
+  sk->flags = SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
+
+  if (sk_open(sk) < 0)
+    goto err;
+}
+
+static inline sock *
+bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
+{
+  sock *sk = sk_new(p->p.pool);
+  sk->type = SK_UDP;
+  sk->saddr = local;
+  sk->data = p;
+
+  sk->tbsize = 64; // XXX
+  sk->err_hook = bfd_err_hook;
+  sk->iface = new;
+
+  sk->tos = PATT->tx_tos;
+  sk->priority = PATT->tx_priority;
+  sk->ttl = PATT->ttl_security ? 255 : 1;
+
+  if (sk_open(sk) < 0)
+    goto err;
+
+}
+
+struct bfd_socket *
+bfd_get_socket(struct bfd_proto *p, ip_addr local, struct iface *ifa)
+{
+  struct bfd_socket *sk;
+
+  WALK_LIST(sk, p->sockets)
+    if (ipa_equal(sk->sk->saddr, local) && (sk->sk->iface == ifa))
+      return sk->uc++, sk;
+
+  sk = mb_allocz(p->p.pool, sizeof(struct bfd_socket));
+  sk->sk = bfd_open_tx_sk(p, local, ifa);
+  sk->uc = 1;
+  add_tail(&p->sockets, &sk->n);
+
+  return sk;
+}
+
+void
+bfd_free_socket(struct bfd_socket *sk)
+{
+  if (!sk || --sk->uc)
+    return;
+
+  rem_node(&sk->n);
+  sk_stop(sk->sk);
+  rfree(sk->sk);
+  mb_free(sk);
+}
index fcf5dd1d9b283baa522850a8df67922ec40e8bf2..c1c2168e21ef5533368917306263b033862294eb 100644 (file)
@@ -1231,7 +1231,8 @@ sk_open(sock *s)
 #endif
     }
 
-  sk_insert(s);
+  if (!(s->flags & SKF_THREAD))
+    sk_insert(s);
   return 0;
 
 bad:
@@ -1514,7 +1515,8 @@ sk_write(sock *s)
     default:
       if (s->ttx != s->tpos && sk_maybe_write(s) > 0)
        {
-         s->tx_hook(s);
+         if (s->tx_hook)
+           s->tx_hook(s);
          return 1;
        }
       return 0;