]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Some preliminary IS-IS commit. isis
authorOndrej Zajicek <santiago@crfreenet.org>
Wed, 29 Aug 2012 10:35:28 +0000 (12:35 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Wed, 29 Aug 2012 10:35:28 +0000 (12:35 +0200)
13 files changed:
conf/conf.h
conf/confbase.Y
configure.in
nest/proto.c
nest/protocol.h
proto/isis/Makefile [new file with mode: 0644]
proto/isis/config.Y [new file with mode: 0644]
proto/isis/ifaces.c [new file with mode: 0644]
proto/isis/isis.c [new file with mode: 0644]
proto/isis/isis.h [new file with mode: 0644]
proto/isis/lsdb.c [new file with mode: 0644]
proto/isis/packets.c [new file with mode: 0644]
sysdep/autoconf.h.in

index c76832b607c85f1c5cbb95c9ef565f5aa46f9c41..9e9dc8c5d0adeee6e80a3712350cdfec6b9d5538 100644 (file)
@@ -72,6 +72,14 @@ void config_add_obstacle(struct config *);
 void config_del_obstacle(struct config *);
 void order_shutdown(void);
 
+static inline void
+cf_range(const char *opt, int val, int min, int max)
+{ 
+  if ((val < min) || (val > max))
+    cf_error("%s must be in range %d-%d", opt, min, max);
+}
+
+
 #define CONF_DONE 0
 #define CONF_PROGRESS 1
 #define CONF_QUEUED 2
index dcb0719f87599dde2827f38f340b19bdf03c9225..df5a9a4d467eaa7b8b1925c0d87779e0af669107 100644 (file)
@@ -26,6 +26,13 @@ CF_HDR
 
 CF_DEFINES
 
+static void
+check_u8(unsigned val)
+{
+  if (val > 0xFF)
+    cf_error("Value %d out of range (0-255)", val);
+}
+
 static void
 check_u16(unsigned val)
 {
index 54993dfce457e020a1949b2974a702711340b86f..32225de9308da95b45950cc376614e1d472d9754 100644 (file)
@@ -51,7 +51,7 @@ if test "$enable_ipv6" = yes ; then
 else
        ip=ipv4
        SUFFIX=""
-       all_protocols=bgp,ospf,pipe,rip,static
+       all_protocols=bgp,ospf,isis,pipe,rip,static
 fi
 
 if test "$given_suffix" = yes ; then
index 53d3f1a2ed9f4af5f131be9fa32195a1453df9ef..5ae84f475ced86c1d45e48b3ed5ee399190754f3 100644 (file)
@@ -701,6 +701,9 @@ protos_build(void)
 #ifdef CONFIG_OSPF
   proto_build(&proto_ospf);
 #endif
+#ifdef CONFIG_ISIS
+  proto_build(&proto_isis);
+#endif
 #ifdef CONFIG_PIPE
   proto_build(&proto_pipe);
 #endif
index 11fcb16423815d0af8a25569056f19696338ccc2..fd77cefef1764860d87d5f1f73a1dac2445a3387 100644 (file)
@@ -75,7 +75,7 @@ void protos_dump_all(void);
 
 extern struct protocol
   proto_device, proto_radv, proto_rip, proto_static,
-  proto_ospf, proto_pipe, proto_bgp;
+  proto_ospf, proto_isis, proto_pipe, proto_bgp;
 
 /*
  *     Routing Protocol Instance
diff --git a/proto/isis/Makefile b/proto/isis/Makefile
new file mode 100644 (file)
index 0000000..d022e9b
--- /dev/null
@@ -0,0 +1,5 @@
+source=isis.c ifaces.c lsdb.c packets.c
+root-rel=../../
+dir-name=proto/isis
+
+include ../../Rules
diff --git a/proto/isis/config.Y b/proto/isis/config.Y
new file mode 100644 (file)
index 0000000..221f04a
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *     BIRD -- IS-IS Configuration
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/isis/isis.h"
+
+CF_DEFINES
+
+#define ISIS_CFG ((struct isis_config *) this_proto)
+#define ISIS_IFACE ((struct isis_iface_config *) this_ipatt)
+
+static byte area_id_buffer[13];
+static byte *area_id_bpos;
+
+CF_DECLS
+
+CF_KEYWORDS(AREA, BCAST, BROADCAST, BUFFER, CSNP, HELLO, HOLD, ID, INTERFACE, ISIS, LEVEL, LIFETIME,
+       LSP, METRIC, MULT, PASSIVE, POINTOPOINT, PRIORITY, PSNP, PTP, RETRANSMIT, RX, SIZE, SYSTEM,
+       TX, TYPE)
+
+
+%type<i> isis_level
+
+CF_GRAMMAR
+
+CF_ADDTO(proto, isis_proto)
+
+isis_proto_start: proto_start ISIS
+{
+  this_proto = proto_config_new(&proto_isis, sizeof(struct isis_config), $1);
+  init_list(&ISIS_CFG->patt_list);
+  ISIS_CFG->lsp_lifetime = ISIS_DEFAULT_LSP_LIFETIME;
+  ISIS_CFG->rx_buffer_size = ISIS_DEFAULT_RX_BUFFER_SIZE;
+  ISIS_CFG->tx_buffer_size = ISIS_DEFAULT_TX_BUFFER_SIZE;
+};
+
+isis_proto_item:
+   proto_item
+ | INTERFACE isis_iface
+ | AREA ID isis_area_id
+ | SYSTEM ID isis_system_id
+ | LSP LIFETIME expr { ISIS_CFG->lsp_lifetime = $3; cf_range("LSP lifetime", $3, 300, 65535); }
+ | RX BUFFER SIZE expr { ISIS_CFG->rx_buffer_size = $4; cf_range("RX buffer size", $4, 256, 65535); }
+ | TX BUFFER SIZE expr { ISIS_CFG->tx_buffer_size = $4; cf_range("TX buffer size", $4, 256, 65535); }
+ ;
+
+isis_proto_finish:
+{
+  struct isis_config *cf = ISIS_CFG;
+
+  if (! cf->areas[0])
+    cf_error("Missing area ID");
+
+  if (cf->rx_buffer_size < cf->tx_buffer_size)
+    cf_error("RX buffer size must be greater than TX buffer size");
+}
+
+isis_proto_opts:
+   /* empty */
+ | isis_proto_opts isis_proto_item ';'
+ ;
+
+isis_proto:
+   isis_proto_start proto_name '{' isis_proto_opts '}';
+
+
+isis_iface_start:
+{
+  this_ipatt = cfg_allocz(sizeof(struct isis_iface_config));
+  add_tail(&ISIS_CFG->patt_list, NODE this_ipatt);
+
+  ISIS_IFACE->levels[ISIS_L1] = ISIS_DEFAULT_LEVEL_1;
+  ISIS_IFACE->levels[ISIS_L2] = ISIS_DEFAULT_LEVEL_2;
+  ISIS_IFACE->metric = ISIS_DEFAULT_METRIC;
+  ISIS_IFACE->priority = ISIS_DEFAULT_PRIORITY;
+
+  ISIS_IFACE->hello_int = ISIS_DEFAULT_HELLO_INT;
+  ISIS_IFACE->hold_int  = ISIS_DEFAULT_HOLD_INT;
+  ISIS_IFACE->hold_mult = ISIS_DEFAULT_HOLD_MULT;
+  ISIS_IFACE->rxmt_int = ISIS_DEFAULT_RXMT_INT;
+  ISIS_IFACE->csnp_int = ISIS_DEFAULT_CSNP_INT;
+  ISIS_IFACE->psnp_int = ISIS_DEFAULT_PSNP_INT;
+};
+
+isis_level: expr { $$ = $1 - 1; if (($1 < 1) || ($1 > 2)) cf_error("Level must be 1 or 2"); }
+
+isis_iface_item:
+   LEVEL isis_level bool { ISIS_IFACE->levels[$2] = $3; }
+ | LEVEL isis_level PASSIVE { ISIS_IFACE->levels[$2] = ISIS_LEVEL_PASSIVE; }
+ | PASSIVE bool { ISIS_IFACE->passive = $2; }
+
+ | TYPE BROADCAST { ISIS_IFACE->type = ISIS_IT_BCAST; }
+ | TYPE BCAST { ISIS_IFACE->type = ISIS_IT_BCAST; }
+ | TYPE POINTOPOINT { ISIS_IFACE->type = ISIS_IT_PTP; }
+ | TYPE PTP { ISIS_IFACE->type = ISIS_IT_PTP; }
+
+ | METRIC expr { ISIS_IFACE->metric = $2; cf_range("Metric", $2, 1, 63); }
+ | PRIORITY expr { ISIS_IFACE->priority = $2; cf_range("Priority", $2, 1, 127); }
+
+ | HELLO expr { ISIS_IFACE->hello_int = $2; cf_range("Hello interval", $2, 1, 65535); }
+ | HOLD expr { ISIS_IFACE->hold_int = $2; cf_range("Hold interval", $2, 3, 65535); }
+ | HOLD MULT expr { ISIS_IFACE->hold_mult = $3; cf_range("Hold multiplier", $3, 3, 255); }
+ | RETRANSMIT expr { ISIS_IFACE->rxmt_int = $2; cf_range("Retransmit interval", $2, 3, 65535); }
+ | CSNP expr { ISIS_IFACE->csnp_int = $2; cf_range("CSNP interval", $2, 1, 65535); }
+ | PSNP expr { ISIS_IFACE->psnp_int = $2; cf_range("PSNP interval", $2, 1, 65535); }
+ ;
+
+isis_iface_finish:
+{
+  struct isis_iface_config *ic = ISIS_IFACE;
+
+  if (! ic->hold_int)
+  {
+    u32 hold_int = ic->hold_mult * (u32) ic->hello_int;
+    if (hold_int > 65535)
+      cf_error("Hello interval times Hold multiplier greater than 65535");
+    ic->hold_int = hold_int;
+  }
+};
+
+isis_iface_opts:
+   /* empty */
+ | isis_iface_opts isis_iface_item ';'
+ ;
+
+isis_iface_opt_list:
+   /* empty */
+ | '{' isis_iface_opts '}'
+ ;
+
+isis_iface:
+  isis_iface_start iface_patt_list isis_iface_opt_list isis_iface_finish;
+
+
+
+isis_area_id_read:
+   NUM 
+   {
+     check_u8($1);
+     if ($1 == 0)
+       cf_error("Area ID must not start with 0");
+     area_id_bpos = area_id_buffer;
+     *area_id_bpos++ = $1;
+   }
+ | isis_area_id_read '-' NUM
+   { 
+     check_u16($3);
+     if ((area_id_bpos + 2 - area_id_buffer) > sizeof(area_id_buffer))
+       cf_error("Area ID too long");
+     put_u16(area_id_bpos, $3);
+     area_id_bpos += 2;
+   }
+ ;
+
+isis_area_id: isis_area_id_read
+{
+  struct isis_area_id *area_id;
+  int i, blen;
+
+  for (i = 0; i < ISIS_AREAS; i++)
+    if (! ISIS_CFG->areas[i])
+      goto found;
+
+  cf_error("Too many areas");
+
+found:
+  blen = area_id_bpos - area_id_buffer;
+  area_id = cfg_allocz(1 + blen);
+  area_id->length = blen;
+  memcpy(area_id->body, area_id_buffer, blen);
+  ISIS_CFG->areas[i] = area_id;
+}
+
+isis_system_id: NUM '-' NUM '-' NUM
+{
+  check_u16($1); check_u16($3); check_u16($5);
+  ISIS_CFG->system_id = (((u64) $1) << 48) | (((u64) $3) << 32) | (((u64) $5) << 16);
+}
+
+CF_CODE
+
+CF_END
diff --git a/proto/isis/ifaces.c b/proto/isis/ifaces.c
new file mode 100644 (file)
index 0000000..9869dba
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ *     BIRD -- IS-IS Interfaces and Neighbors
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+
+#include <stdlib.h>
+#include "isis.h"
+
+static char* ev_name[] = { NULL, "Init", "Change", "RS" };
+
+#ifdef XXX
+static void
+isis_timer(timer *tm)
+{
+  struct isis_iface *ifa = tm->data;
+  struct isis_proto *p = ifa->ra;
+
+  ISIS_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
+
+  isis_send_ra(ifa, 0);
+
+  /* Update timer */
+  ifa->last = now;
+  unsigned after = ifa->cf->min_ra_int;
+  after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
+
+  if (ifa->initial)
+    ifa->initial--;
+
+  if (ifa->initial)
+    after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
+
+  tm_start(ifa->timer, after);
+}
+
+void
+isis_iface_notify(struct isis_iface *ifa, int event)
+{
+  struct isis_proto *p = ifa->p;
+
+  if (!ifa->sk)
+    return;
+
+  ISIS_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
+
+  switch (event)
+  {
+  case RA_EV_CHANGE:
+    ifa->plen = 0;
+  case RA_EV_INIT:
+    ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
+    break;
+
+  case RA_EV_RS:
+    break;
+  }
+
+  /* Update timer */
+  unsigned delta = now - ifa->last;
+  unsigned after = 0;
+
+  if (delta < ifa->cf->min_delay)
+    after = ifa->cf->min_delay - delta;
+
+  tm_start(ifa->timer, after);
+}
+#endif
+
+/*
+isis_dr_election()
+{
+  
+    at least one up, all relevant neighbors and myself
+
+    local system becomes or resign -> event lANLevel1/2DesignatedIntermediateSystemChange 
+    becomes ->
+    set lan-id
+    originate new and purge old pseudonode LSP
+
+    zmena dr -> zmenit lan-id
+    zmena lan-id -> zmena me LSP
+}
+   */
+
+static struct isis_iface *
+isis_iface_find(struct isis_proto *p, struct iface *what)
+{
+  struct isis_iface *ifa;
+
+  WALK_LIST(ifa, p->iface_list)
+    if (ifa->iface == what)
+      return ifa;
+
+  return NULL;
+}
+
+static void
+iface_olock_hook(struct object_lock *lock)
+{
+  struct isis_iface *ifa = lock->data;
+  struct isis_proto *p = ifa->p;
+
+  if (! isis_sk_open(ifa))
+  {
+    log(L_ERR "%s: Socket open failed on interface %s", p->p.name, ifa->iface->name);
+    return;
+  }
+
+  // XXX isis_iface_notify(ifa, RA_EV_INIT);
+}
+
+
+static void
+l1_hello_timer_hook(timer *timer)
+{
+  struct isis_iface *ifa = (struct isis_iface *) timer->data;
+
+  isis_send_lan_hello(ifa, ISIS_L1);
+}
+
+static void
+l2_hello_timer_hook(timer *timer)
+{
+  struct isis_iface *ifa = (struct isis_iface *) timer->data;
+
+  isis_send_lan_hello(ifa, ISIS_L2);
+}
+
+static void
+ptp_hello_timer_hook(timer *timer)
+{
+  struct isis_iface *ifa = (struct isis_iface *) timer->data;
+
+  isis_send_ptp_hello(ifa);
+}
+
+static void
+csnp_timer_hook(timer *timer)
+{
+  struct isis_iface *ifa = (struct isis_iface *) timer->data;
+  struct isis_proto *p = ifa->p;
+  struct isis_lsp *lsp;
+  int n;
+
+  /* FIXME: CSNP rate limiting */
+
+  if (XXX)
+  {
+    n = 0;
+    lsp = isis_lsdb_first(p->lsdb[ISIS_L1], lsp, ifa);
+    while (lsp)
+      isis_send_csnp(ifa, ISIS_L1, &lsp, n++ == 0);
+  }
+
+  if (XXX)
+  {
+    n = 0;
+    lsp = isis_lsdb_first(p->lsdb[ISIS_L2], lsp, ifa);
+    while (lsp)
+      isis_send_csnp(ifa, ISIS_L2, &lsp, n++ == 0);
+  }
+}
+
+static void
+psnp_timer_hook(timer *timer)
+{
+  struct isis_iface *ifa = (struct isis_iface *) timer->data;
+  struct isis_proto *p = ifa->p;
+  struct isis_lsp *lsp;
+
+  if (XXX)
+  {
+    lsp = isis_lsdb_first_ssn(p->lsdb[ISIS_L1], lsp, ifa);
+    while (lsp)
+      isis_send_psnp(ifa, ISIS_L1, &lsp);
+  }
+
+  if (XXX)
+  {
+    lsp = isis_lsdb_first_ssn(p->lsdb[ISIS_L2], lsp, ifa);
+    while (lsp)
+      isis_send_psnp(ifa, ISIS_L2, &lsp);
+  }
+}
+
+
+static void
+isis_iface_new(struct isis_proto *p, struct iface *iface, struct isis_iface_config *cf)
+{
+  ISIS_TRACE(D_EVENTS, "Adding interface %s", iface->name);
+
+  pool *pool = rp_new(p->p.pool, "ISIS Interface");
+  struct isis_iface *ifa = mb_allocz(pool, sizeof(struct isis_iface));
+
+  add_tail(&p->iface_list, NODE ifa);
+  ifa->p = p;
+  ifa->cf = cf;
+  ifa->iface = iface;
+
+  ifa->pool = pool;
+  init_list(&ifa->neigh_list);
+
+  ifa->type = ifa->cf->type;
+  ifa->levels = ifa->cf->levels;
+  ifa->priority = ifa->cf->priority;
+  ifa->hello_int = ifa->cf->hello_int;
+  ifa->hold_int = ifa->cf->hold_int;
+
+  if (ifa->type == ISIS_IT_PASSIVE)
+    return;
+
+  ifa->hello_timer = tm_new_set(pool, hello_timer_hook, ifa, xxx, xxx);
+
+  struct object_lock *lock = olock_new(pool);
+  lock->addr = IPA_NONE;
+  lock->type = OBJLOCK_IP;
+  lock->port = ISIS_PROTO;
+  lock->iface = iface;
+  lock->data = ifa;
+  lock->hook = iface_olock_hook;
+  ifa->lock = lock;
+
+  olock_acquire(lock);
+}
+
+/*
+static inline void
+isis_iface_shutdown(struct isis_iface *ifa)
+{
+  if (ifa->sk)
+    isis_send_ra(ifa, 1);
+}
+*/
+
+static void
+isis_iface_remove(struct isis_iface *ifa)
+{
+  struct isis_proto *p = ifa->p;
+
+  ISIS_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
+
+  // XXX isis_iface_sm(ifa, ISM_DOWN);
+  rem_node(NODE ifa);
+  rfree(ifa->pool);
+}
+
+void
+isis_if_notify(struct proto *pp, unsigned flags, struct iface *iface)
+{ 
+  struct isis_proto *p = (struct isis_proto *) pp;
+  struct isis_config *cf = (struct isis_config *) (pp->cf);
+
+  if (iface->flags & IF_IGNORE)
+    return;
+
+  if (flags & IF_CHANGE_UP)
+  {
+    struct isis_iface_config *ic = (struct isis_iface_config *)
+      iface_patt_find(&cf->patt_list, iface, NULL);
+
+    if (ic)
+      isis_iface_new(p, iface, ic);
+
+    return;
+  }
+
+  struct isis_iface *ifa = isis_iface_find(p, iface);
+  if (!ifa)
+    return;
+
+  if (flags & IF_CHANGE_DOWN)
+  {
+    isis_iface_remove(ifa);
+    return;
+  }
+
+  if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
+    isis_iface_notify(ifa, RA_EV_INIT);
+}
+
+/*
+void
+isis_ifa_notify(struct proto *pp, unsigned flags, struct ifa *a)
+{
+  struct isis_proto *p = (struct isis_proto *) pp;
+
+  if (a->flags & IA_SECONDARY)
+    return;
+
+  if (a->scope <= SCOPE_LINK)
+    return;
+
+  struct isis_iface *ifa = isis_iface_find(ra, a->iface);
+
+  if (ifa)
+    isis_iface_notify(ifa, RA_EV_CHANGE);
+}
+
+*/
+
+
+
+
+static void
+hold_timer_hook(timer *timer)
+{
+  struct isis_neighbor *n = (struct isis_neighbor *) timer->data;
+  struct isis_iface *ifa = n->ifa;
+  struct isis_proto *p = ifa->p;
+
+  // xxx ISIS_TRACE(D_EVENTS, "Hold timer expired for neighbor %I", n->ip);
+  isis_neighbor_remove(n);
+}
+
+
+struct isis_neighbor *
+isis_neighbor_add(struct isis_iface *ifa)
+{
+  struct isis_proto *p = ifa->p;
+  struct isis_neighbor *n = mb_allocz(ifa->pool, sizeof(struct isis_neighbor));
+
+  add_tail(&ifa->neigh_list, NODE n);
+  n->ifa = ifa;
+
+  n->hold_timer = tm_new_set(ifa->pool, hold_timer_hook, n, xxx, xxx);
+
+  return (n);
+}
+
+void
+isis_neighbor_remove(struct isis_neighbor *n)
+{
+  struct isis_iface *ifa = n->ifa;
+  struct isis_proto *p = ifa->p;
+
+  // xxx ISIS_TRACE(D_EVENTS, "Removing neigbor");
+
+  rem_node(NODE n);
+  rfree(n->hold_timer);
+}
+
+/*
+  new:
+  t neighbourSystemType - podle typu paketu
+  holdingTimer, priorityOfNeighbour, neighbour-SystemID and areaAddressesOfNeighbour - podle obsahu
+  mac_addr
+  state -> init
+  checknout my sysID in list -> up
+
+
+ */
diff --git a/proto/isis/isis.c b/proto/isis/isis.c
new file mode 100644 (file)
index 0000000..e98b7cb
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *     BIRD -- IS-IS
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+
+#include <stdlib.h>
+#include "isis.h"
+
+/**
+ * DOC: Intermediate System to Intermediate System (IS-IS)
+ *
+ * Intermediate System to Intermediate System 
+ * intra-domain routeing information exchange protocol
+ *
+ * XXXX
+ *
+ * Supported standards:
+ * - ISO 10589 - main IS-IS standard
+ * - RFC xxxx - 
+ */
+
+
+static struct proto *
+isis_init(struct proto_config *c)
+{
+  struct proto *pp = proto_new(c, sizeof(struct isis_proto));
+
+  pp->if_notify = isis_if_notify;
+  pp->ifa_notify = isis_ifa_notify;
+  return pp;
+}
+
+static int
+isis_start(struct proto *pp)
+{
+  struct isis_proto *p = (struct isis_proto *) pp;
+  // struct isis_config *cf = (struct isis_config *) (pp->cf);
+
+  init_list(&(p->iface_list));
+
+  return PS_UP;
+}
+
+static int
+isis_shutdown(struct proto *pp)
+{
+  struct isis_proto *p = (struct isis_proto *) pp;
+
+  struct isis_iface *ifa;
+  WALK_LIST(ifa, p->iface_list)
+    isis_iface_shutdown(ifa);
+
+  return PS_DOWN;
+}
+
+#ifdef XXX
+static int
+isis_reconfigure(struct proto *pp, struct proto_config *c)
+{
+  struct isis_proto *p = (struct isis_proto *) pp;
+  // struct isis_config *old = (struct isis_config *) (p->cf);
+  struct isis_config *new = (struct isis_config *) c;
+
+  /* 
+   * The question is why there is a reconfigure function for RAdv if
+   * it has almost none internal state so restarting the protocol
+   * would probably suffice. One small reason is that restarting the
+   * protocol would lead to sending a RA with Router Lifetime 0
+   * causing nodes to temporary remove their default routes.
+   */
+
+  struct iface *iface;
+  WALK_LIST(iface, iface_list)
+  {
+    struct isis_iface *ifa = isis_iface_find(ra, iface);
+    struct isis_iface_config *ic = (struct isis_iface_config *)
+      iface_patt_find(&new->patt_list, iface, NULL);
+
+    if (ifa && ic)
+    {
+      ifa->cf = ic;
+
+      /* We cheat here - always notify the change even if there isn't
+        any. That would leads just to a few unnecessary RAs. */
+      isis_iface_notify(ifa, RA_EV_CHANGE);
+    }
+
+    if (ifa && !ic)
+    {
+      isis_iface_shutdown(ifa);
+      isis_iface_remove(ifa);
+    }
+
+    if (!ifa && ic)
+      isis_iface_new(ra, iface, ic);
+  }
+
+  return 1;
+}
+
+static void
+isis_copy_config(struct proto_config *dest, struct proto_config *src)
+{
+  struct isis_config *d = (struct isis_config *) dest;
+  struct isis_config *s = (struct isis_config *) src;
+
+  /* We clean up patt_list, ifaces are non-sharable */
+  init_list(&d->patt_list);
+}
+#endif
+
+
+struct protocol isis_proto = {
+  .name =              "IS-IS",
+  .template =          "isis%d",
+  .init =              isis_init,
+  .start =             isis_start,
+  .shutdown =          isis_shutdown,
+  // .reconfigure =    isis_reconfigure,
+  // .copy_config =    isis_copy_config
+};
diff --git a/proto/isis/isis.h b/proto/isis/isis.h
new file mode 100644 (file)
index 0000000..9a1c476
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *     BIRD -- Router Advertisement
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_ISIS_H_
+#define _BIRD_ISIS_H_
+
+#include "nest/bird.h"
+
+#include "lib/ip.h"
+#include "lib/lists.h"
+#include "lib/socket.h"
+#include "lib/timer.h"
+#include "lib/resource.h"
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "nest/route.h"
+#include "nest/cli.h"
+#include "nest/locks.h"
+#include "conf/conf.h"
+#include "lib/string.h"
+
+
+#define ISIS_PROTO             90      // XXX
+
+#define ISIS_LEVELS            2
+#define ISIS_L1                        0
+#define ISIS_L2                        1
+
+#define ISIS_AREAS             3
+
+
+#define mac_addr ip_addr // XXX
+
+#define ETH_ALL_ISS             ipa_from_u32(0xe0000005)       /* 224.0.0.5 */
+#define ETH_ALL_L1_ISS          ipa_from_u32(0xe0000005)       /* 224.0.0.5 */
+#define ETH_ALL_L2_ISS          ipa_from_u32(0xe0000005)       /* 224.0.0.5 */
+
+
+struct isis_area_id
+{
+  byte length;
+  byte body[];
+};
+
+struct isis_config
+{
+  struct proto_config c;
+  list patt_list;              /* List of iface configs (struct isis_iface_config) */
+  struct isis_area_id *areas[ISIS_AREAS];
+
+  u64 system_id;
+
+  u16 lsp_lifetime;
+  u16 rx_buffer_size;
+  u16 tx_buffer_size;
+};
+
+#define ISIS_DEFAULT_LSP_LIFETIME      1200
+#define ISIS_DEFAULT_RX_BUFFER_SIZE    1492
+#define ISIS_DEFAULT_TX_BUFFER_SIZE    1492
+
+struct isis_iface_config
+{
+  struct iface_patt i;
+
+  u8 levels[ISIS_LEVELS];
+  u8 passive;
+  u8 type;
+  u8 metric;
+  u8 priority;
+
+  u16 hello_int;
+  u16 hold_int;
+  u16 hold_mult;
+  u16 rxmt_int;
+  u16 csnp_int;
+  u16 psnp_int;
+};
+
+#define ISIS_LEVEL_PASSIVE     2
+
+#define ISIS_DEFAULT_LEVEL_1   1
+#define ISIS_DEFAULT_LEVEL_2   0
+#define ISIS_DEFAULT_METRIC    1
+#define ISIS_DEFAULT_PRIORITY  64
+
+#define ISIS_DEFAULT_HELLO_INT 10
+#define ISIS_DEFAULT_HOLD_INT  0
+#define ISIS_DEFAULT_HOLD_MULT 3
+#define ISIS_DEFAULT_RXMT_INT  5
+#define ISIS_DEFAULT_CSNP_INT  10
+#define ISIS_DEFAULT_PSNP_INT  2
+
+
+struct isis_proto
+{
+  struct proto p;
+  list iface_list;             /* List of active ifaces */
+
+  u64 system_id;
+  u16 lsp_max_age;
+  u16 lsp_refresh;
+  u16 buffer_size;
+};
+
+struct isis_iface
+{
+  node n;
+  struct isis_proto *p;
+  struct isis_iface_config *cf;        /* Related config, must be updated in reconfigure */
+  struct iface *iface;
+
+  pool *pool;
+  struct object_lock *lock;
+  sock *sk;
+  list neigh_list;             /* List of neigbours */
+
+  u8 type;
+  u8 levels;
+  u8 priority;
+
+  u16 hello_int;
+  u16 hold_int;
+};
+
+#define ISIS_IT_UNDEF          0
+#define ISIS_IT_BCAST          1
+#define ISIS_IT_PTP            2
+
+
+struct isis_neighbor
+{
+  node n;
+  struct isis_iface *ifa;
+  mac_addr addr;
+  u64 id;
+
+  timer *hold_timer;
+
+  u8 levels;
+  u8 priority;
+};
+
+struct isis_lsdb
+{
+  pool *pool;
+  slab *slab;
+  list list;
+};
+
+struct isis_lsp_hdr
+{
+  u64 id;
+  u32 seqnum;
+  u16 lifetime;
+  u16 checksum;
+}
+
+struct isis_lsp
+{
+  node n;
+  struct isis_lsp_hdr hdr;
+  void *body;
+  u16 blen;
+};
+
+
+
+#define RA_EV_INIT 1           /* Switch to initial mode */
+#define RA_EV_CHANGE 2         /* Change of options or prefixes */
+#define RA_EV_RS 3             /* Received RS */
+
+
+
+#ifdef LOCAL_DEBUG
+#define ISIS_FORCE_DEBUG 1
+#else
+#define ISIS_FORCE_DEBUG 0
+#endif
+#define ISIS_TRACE(flags, msg, args...) do { if ((p->p.debug & flags) || ISIS_FORCE_DEBUG) \
+        log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
+
+
+/* isis.c */
+void isis_iface_notify(struct isis_iface *ifa, int event);
+
+/* ifaces.c */
+void isis_if_notify(struct proto *pp, unsigned flags, struct iface *iface);
+void isis_ifa_notify(struct proto *pp, unsigned flags, struct ifa *a);
+
+/* packets.c */
+void isis_send_lan_hello(struct isis_iface *ifa, int level);
+void isis_send_ptp_hello(struct isis_iface *ifa);
+void isis_send_lsp(struct isis_iface *ifa, struct lsp_entry *en);
+void isis_send_csnp(struct isis_iface *ifa, int level);
+void isis_send_psnp(struct isis_iface *ifa, int level);
+
+int isis_sk_open(struct isis_iface *ifa);
+
+
+
+#endif /* _BIRD_ISIS_H_ */
diff --git a/proto/isis/lsdb.c b/proto/isis/lsdb.c
new file mode 100644 (file)
index 0000000..3fb6e69
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *     BIRD -- IS-IS LSP database
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+
+static inline int
+isis_lsp_comp(struct isis_lsp_hdr *l1, struct isis_lsp_hdr *l2)
+{
+  if (l1->seqnum != l2->seqnum)
+    return (l1->seqnum > l2->seqnum) ? LSP_NEWER : LSP_OLDER;
+
+  // XXX review
+  if (l1->checksum != l2->checksum)
+    return (l1->checksum > l2->checksum) ? LSP_NEWER : LSP_OLDER;
+
+  return LSP_SAME;
+}
+
+struct isis_lsp *
+isis_get_lsp(struct isis_lsdb *db, xxx)
+{
+}
+
+
+void
+isis_lsp_received(struct isis_lsdb *db, struct isis_iface *ifa,
+                 struct isis_lsp_hdr *hdr, byte *body, int blen)
+{
+  struct isis_lsp *lsp = isis_get_lsp(db, hdr);
+
+  int cmp = isis_lsp_comp(hdr, &lsp->hdr);
+  switch (cmp)
+  {
+  LSP_NEWER:
+    xxx();
+  LSP_SAME:
+    isis_lsp_clear_srm(lsp, ifa);
+    isis_lsp_set_ack(lsp, ifa);
+    break;
+
+  LSP_OLDER:
+    isis_lsp_set_srm(lsp, ifa);
+    isis_lsp_clear_ssn(lsp, ifa);
+    break;
+  }
+}
+
+void
+isis_snp_received(struct isis_lsdb *db, struct isis_iface *ifa, struct isis_lsp_hdr *hdr)
+{
+  struct isis_lsp *lsp = isis_get_lsp(db, hdr);
+
+  int cmp = isis_lsp_comp(hdr, &lsp->hdr);
+  switch (cmp)
+  {
+  LSP_NEWER:
+    isis_lsp_set_ssn(lsp, ifa);
+  LSP_SAME:
+    isis_lsp_clear_ack(lsp, ifa);
+    break;
+
+  LSP_OLDER:
+    isis_lsp_set_srm(lsp, ifa);
+    isis_lsp_clear_ssn(lsp, ifa);
+    break;
+  }
+
+  isis_lsp_clear_csnp(lsp);
+}
+
+struct isis_lsdb *
+isis_lsdb_new(struct isis_proto *p)
+{
+  pool *pool = rp_new(p->p.pool, "ISIS LSP database");
+  struct isis_lsdb *db = mb_allocz(pool, sizeof(struct isis_lsdb));
+
+  db->pool = pool;
+  db->slab = sl_new(pool, sizeof(struct lsp_entry));
+  init_list(&db->list);
+}
+
+void
+isis_lsdb_free(struct isis_lsdb *db)
+{
+  rfree(db->pool);
+}
diff --git a/proto/isis/packets.c b/proto/isis/packets.c
new file mode 100644 (file)
index 0000000..ee3d58e
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ *     BIRD -- IS-IS Packet Processing
+ *
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+
+#include <stdlib.h>
+#include "isis.h"
+
+
+/*
+ * IS-IS common packet header
+ *
+ * 1B  Protocol ID,            constant
+ * 1B  Fixed header length     depends on packet type
+ * 1B  Version 1               fixed 1
+ * 1B  System ID length        fixed 0 or 6
+ * 1B  Packet type             0xe0 bits reserved
+ * 1B  Version 2               fixed 1
+ * 1B  Reserved                ignored
+ * 1B  Max area addrs          fixed 0 or 3
+ *
+ *
+ * IS-IS Hello header
+ *
+ * 8B  Common header
+ * 1B  Circuit type            levels (1, 2 or 1+2), 0xfc bits reserved
+ * 6B  System ID
+ * 2B  Hold time
+ * 2B  Packet length           hdr + data
+ * LAN:
+ * 1B  Priority                0x80 bit reserved
+ * 7B  LAN ID                  for DR selection
+ * PTP:
+ * 1B  Circuit ID              we could ignore this (?)
+ *
+ *
+ *
+ * IS-IS LSP header
+ *
+ * 8B  Common header
+ * 2B  Packet length           hdr + data
+ * 2B  LSP lifetime
+ * 8B  LSP ID
+ * 4B  LSP sequence number
+ * 2B  LSP checksum
+ * 1B  LSP flags
+ *
+ *
+ * IS-IS CSNP header
+ *
+ * 8B  Common header
+ * 2B  Packet length           hdr + data
+ * 7B  System ID
+ * 8B  Start LSP ID
+ * 8B  End LSP ID
+ *
+ *
+ * IS-IS PSNP header
+ *
+ * 8B  Common header
+ * 2B  Packet length           hdr + data
+ * 7B  System ID
+ */
+
+
+
+#define ISIS_PROTO_ID          0x83
+
+#define ISIS_L1_HELLO          15
+#define ISIS_L2_HELLO          16
+#define ISIS_PTP_HELLO         17
+#define ISIS_L1_LSP            18
+#define ISIS_L2_LSP            20
+#define ISIS_L1_CSNP           24
+#define ISIS_L2_CSNP           25
+#define ISIS_L1_PSNP           26
+#define ISIS_L2_PSNP           27
+
+#define ISIS_COMMON_HLEN       8
+#define ISIS_LHELLO_HLEN       27
+#define ISIS_PHELLO_HLEN       20
+#define ISIS_LSP_HLEN          27
+#define ISIS_CSNP_HLEN         33
+#define ISIS_PSNP_HLEN         17
+
+#define ISIS_TYPE_MASK         0x1f
+
+
+IntradomainRouteingPD
+
+static inline byte * isis_tx_buffer(struct isis_iface *ifa) { return ifa->sk->tbuf; }
+
+static void
+isis_fill_hdr(byte *pkt, u8 pdu_type, u8 hdr_len)
+{
+  pkt[0] = ISIS_PROTO_ID;
+  pkt[1] = hdr_len;
+  pkt[2] = 1;          // Version 1
+  pkt[3] = 6;          // System ID length
+  pkt[4] = pdu_type;
+  pkt[5] = 1;          // Version 2
+  pkt[6] = 0;          // Reserved
+  pkt[7] = 3;          // Max area addresses
+}
+
+static inline byte *
+put_lsp_hdr(byte *buf, struct isis_lsp_hdr *hdr)
+{
+  put_u16(buf+ 0, hdr->lifetime);
+  put_u64(buf+ 2, hdr->id);
+  put_u32(buf+10, hdr->seqnum);
+  put_u16(buf+14, hdr->checksum);
+  return buf+16;
+}
+
+static byte *
+isis_put_tlv_areas(struct isis_proto *p, byte *buf)
+{
+  byte *bp = buf + 2;
+  int i;
+
+  for (i = 0; i < ISIS_AREAS; i++)
+    if (cf->areas[i])
+    {
+      int alen = 1 + cf->areas[i]->length;
+      memcpy(bp, cf->areas[i], alen);
+      bp += alen;
+    }
+
+  buf[0] = ISIS_TLV_AREAS;
+  buf[1] = bp - (buf + 2);
+
+  return bp;
+}
+
+static byte *
+isis_put_tlv_protocols(struct isis_proto *p, byte *buf)
+{
+  buf[0] = ISIS_TLV_PROTOCOLS;
+  buf[1] = 1;
+  buf[2] = ISIS_NLPID_IPv4;
+
+  return buf + 3;
+}
+
+static byte *
+isis_put_tlv_ip4_iface_addrs(struct isis_iface *ifa, byte *buf)
+{
+  struct ifa *a;
+  byte *bp = buf + 2;
+  int i;
+
+  WALK_LIST(a, ifa->iface->addrs)
+  {
+    bp = ipa_put_addr(bp, a);
+  }
+
+  buf[0] = ISIS_TLV_IP4_IFACE_ADDRS;
+  buf[1] = bp - (buf + 2);
+
+  return bp;
+}
+
+
+void
+isis_send_lan_hello(struct isis_iface *ifa, int level)
+{
+  struct isis_proto *p = ifa->p;
+
+  byte *pkt = isis_tx_buffer(ifa);
+  isis_fill_hdr(pkt, !level ? ISIS_L1_HELLO : ISIS_L2_HELLO, ISIS_LHELLO_HLEN);
+  put_u8 (pkt+ 8, ifa->levels);
+  put_id6(pkt+ 9, p->system_id);
+  put_u16(pkt+15, ifa->hold_int);
+  // put_u16(pkt+17, ISIS_ + en->blen);
+  put_u8 (pkt+19, ifa->priority);
+  put_id7(pkt+20, 0); // xxx DR
+}
+
+void
+isis_send_ptp_hello(struct isis_iface *ifa)
+{
+  struct isis_proto *p = ifa->p;
+
+  byte *pkt = isis_tx_buffer(ifa);
+  byte *bp = pkt + ISIS_PHELLO_HLEN;
+  byte *be = isis_tx_buffer_end(ifa);
+
+  isis_fill_hdr(pkt, ISIS_PTP_HELLO, ISIS_PHELLO_HLEN);
+  put_u8 (pkt+ 8, ifa->levels);
+  put_id6(pkt+ 9, p->system_id);
+  put_u16(pkt+15, ifa->hold_int);
+  // put_u16(pkt+17, 0);       /* Length postponed */
+  put_u8 (pkt+19, 0);          /* Fake circuit-id */
+
+  bp = isis_put_tlv_areas(p, bp, be);
+  bp = isis_put_tlv_protocols(p, bp, be);
+  bp = isis_put_tlv_ip4_iface_addrs(ifa, bp, be);
+  
+  put_u16(pkt+17, bp - pkt);   /* Packet length */
+}
+
+void
+isis_send_lsp(struct isis_iface *ifa, struct isis_lsp *lsp)
+{
+  struct isis_proto *p = ifa->p;
+
+  byte *pkt = isis_tx_buffer(ifa);
+  isis_fill_hdr(pkt, xxx ? ISIS_L1_LSP : ISIS_L2_LSP, ISIS_LSP_HLEN);
+  put_u16(pkt+ 8, ISIS_LSP_HLEN - 1 + lsp->blen);
+  put_lsp_hdr(pkt+10, &lsp->hdr);
+
+  if (lsp->body)
+    memcpy(pkt+26, lsp->body, lsp->blen);
+
+  ISIS_TRACE(D_PACKETS, "Sending LSP via %s", ifa->iface->name);
+  // xxx sk_send_to(ifa->sk, ifa->plen, AllNodes, 0);
+}
+
+void
+isis_process_lsp(struct isis_iface *ifa, int level, byte *pkt, int len)
+{
+  struct isis_proto *p = ifa->p;
+
+  if ((pkt[ISIS_HLEN_POS] != ISIS_LSP_HLEN) || (pkt[8] != len))
+    XXX;
+
+  put_u16(pkt+ 8, ISIS_LSP_HLEN - 1 + lsp->blen);
+  get_lsp_hdr(pkt+10, &hdr);
+  isis_lsp_received(db, ifa, &hdr, pkt+26, len-26)
+
+  if (lsp->body)
+    memcpy(pkt+26, lsp->body, lsp->blen);
+
+}
+
+
+static byte *
+isis_put_tlv_lsp_entries(struct isis_iface *ifa, byte *buf, byte *be,
+                        struct isis_lsdb *db, struct isis_lsp **lspp, int psnp)
+{
+  struct isis_lsp *lsp = *lspp;
+  byte *bp = buf + 2;
+  int i = 0;
+
+  be -= 2 + 16;        /* TLV header + sizeof(struct isis_lsp_hdr) */
+
+  while (lsp && bp <= be)
+  {
+    if (i == 15)
+    {
+      buf[0] = ISIS_TLV_LSP_ENTRIES;
+      buf[1] = i * 16;
+      i = 0;
+      buf = bp;
+      bp += 2;
+    }
+
+    bp = put_lsp_hdr(bp, &lsp->hdr);
+
+    if (psnp)
+    {
+      // XXX check
+      isis_lsdb_clear_ssn(db, lsp, ifa);
+      lsp = isis_lsdb_next_ssn(db, lsp, ifa);
+    }
+    else
+      lsp = isis_lsdb_next(db, lsp); 
+
+  }
+  buf[0] = ISIS_TLV_LSP_ENTRIES;
+  buf[1] = i * 16;
+  *lspp = lsp;
+}
+
+void
+isis_send_csnp(struct isis_iface *ifa, int level, struct isis_lsp **lspp, int first)
+{
+  struct isis_proto *p = ifa->p;
+
+  byte *pkt = isis_tx_buffer(ifa);
+  byte *bp = pkt + ISIS_CSNP_HLEN;
+  byte *be = isis_tx_buffer_end(ifa);
+
+  /* First put TLVs */
+  u64 start_id = first ? ISIS_MIN_LSP_ID : (*lspp)->hdr.id;
+  bp = isis_put_tlv_lsp_entries(p, bp, be, p->lsdb[level], lspp, 0);
+  u64 end_id = *lspp ? (*lspp)->hdr.id - 1 : ISIS_MAX_LSP_ID;
+
+  /* Then put header */
+  isis_fill_hdr(pkt, !level ? ISIS_L1_CSNP : ISIS_L2_CSNP, ISIS_CSNP_HLEN);
+  put_u16(pkt+08, bp - pkt);
+  put_id7(pkt+10, p->system_id);
+  put_u64(pkt+17, start_id);
+  put_u64(pkt+25, end_id);
+
+  ISIS_TRACE(D_PACKETS, "Sending CSNP via %s", ifa->iface->name);
+  // xxx sk_send_to(ifa->sk, ifa->plen, AllNodes, 0);
+}
+
+void
+isis_send_psnp(struct isis_iface *ifa, int level, struct isis_lsp **lspp)
+{
+  struct isis_proto *p = ifa->p;
+
+  byte *pkt = isis_tx_buffer(ifa);
+  byte *bp = pkt + ISIS_PSNP_HLEN;
+  byte *be = isis_tx_buffer_end(ifa);
+
+  /* First put TLVs */
+  bp = isis_put_tlv_lsp_entries(p, bp, be, p->lsdb[level], lspp, 1);
+
+  /* Then put header */
+  isis_fill_hdr(pkt, !level ? ISIS_L1_PSNP : ISIS_L2_PSNP, ISIS_PSNP_HLEN);
+  put_u16(pkt+08, bp - pkt);
+  put_id7(pkt+10, p->system_id);
+
+  ISIS_TRACE(D_PACKETS, "Sending PSNP via %s", ifa->iface->name);
+  // xxx sk_send_to(ifa->sk, ifa->plen, AllNodes, 0);
+}
+
+static inline void
+isis_process_tlv_lsp_entries(struct isis_iface *ifa, byte *tlv, byte *te, struct isis_lsdb *db)
+{
+  struct isis_proto *p = ifa->p;
+  struct isis_lsp_hdr hdr;
+  byte *bp = tlv + 2;
+
+  while (bp + 16 <= te)
+  {
+    bp = get_lsp_hdr(bp, &hdr);
+    isis_snp_received(db, ifa, &hdr);
+  }
+
+  if (bp < te)
+    XXX;
+}
+
+
+static void
+isis_process_csnp(struct isis_iface *ifa, int level, byte *pkt, int len)
+{
+  struct isis_proto *p = ifa->p;
+
+  if ((pkt[ISIS_HLEN_POS] != ISIS_CSNP_HLEN) || (pkt[8] != len))
+    XXX;
+
+  u64 start_id = get_u64(pkt+17);
+  u64 end_id = get_u64(pkt+25);
+  XXX;
+
+  byte *pe = pkt + len;
+  byte *tlv = pkt + ISIS_CSNP_HLEN;
+
+  while (tlv < pe)
+  {
+    byte *te = tlv + 2 + tlv[1];
+    if (te > pe)
+      XXX;
+
+    if (tlv[0] == ISIS_TLV_LSP_ENTRIES)
+      isis_process_tlv_lsp_entries(ifa, tlv, te, p->lsdb[level]);
+
+    tlv = te;
+  }
+
+}
+
+static void
+isis_process_psnp(struct isis_iface *ifa, int level, byte *pkt, int len)
+{
+  struct isis_proto *p = ifa->p;
+
+  if ((pkt[ISIS_HLEN_POS] != ISIS_PSNP_HLEN) || (pkt[8] != len))
+    XXX;
+
+  byte *pe = pkt + len;
+  byte *tlv = pkt + ISIS_PSNP_HLEN;
+
+  while (tlv < pe)
+  {
+    byte *te = tlv + 2 + tlv[1];
+    if (te > pe)
+      XXX;
+
+    if (tlv[0] == ISIS_TLV_LSP_ENTRIES)
+      isis_process_tlv_lsp_entries(ifa, tlv, te, p->lsdb[level]);
+
+    tlv = te;
+  }
+
+}
+
+
+
+#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
+
+static int
+isis_rx_hook(sock *sk, int len)
+{
+  struct isis_iface *ifa = sk->data;
+  struct isis_proto *p = ifa->p;
+  const char *err_dsc = NULL;
+  unsigned err_val = 0;
+
+  if (sk->lifindex != sk->iface->index)
+    return 1;
+
+  DBG("ISIS: RX hook called (iface %s, src %I, dst %I)\n",
+      sk->iface->name, sk->faddr, sk->laddr); // XXX
+
+  // XXX check src addr
+
+  // XXX skip eth header
+  byte *pkt = ip_skip_header(sk->rbuf, &len);
+  if (pkt == NULL)
+    DROP("too short", len);
+
+  if ((len < ISIS_COMMON_HLEN) || (len < pkt[ISIS_HLEN_POS]))
+    DROP("too short", len);
+
+  if (len > sk->rbsize)  // XXX
+    DROP("too large", len);
+
+  if (pkt[0] != ISIS_PROTO_ID)
+    DROP("protocol ID mismatch", pkt[0]);
+
+  if (pkt[2] != 1)
+    DROP("version1 mismatch", pkt[2]);
+
+  if (pkt[3] != 0 && pkt[3] != 6)
+    DROP("id_length mismatch", pkt[3]);
+
+  if (pkt[5] != 1)
+    DROP("version2 mismatch", pkt[5]);
+
+  if (pkt[7] != 0 && pkt[7] != 3)
+    DROP("max_area_addrs mismatch", pkt[7]);
+
+
+  // XXX find neighbor?
+
+  int type = pkt[4] & ISIS_TYPE_MASK;
+  int level = ISIS_L1;
+  switch (type)
+  {
+    // XXX ptp
+    // case ISIS_PTP_HELLO:
+
+  case ISIS_L2_HELLO:
+    level = ISIS_L2;
+  case ISIS_L1_HELLO:
+    ISIS_TRACE(D_PACKETS, "Received Hello from xxx via %s", ifa->iface->name);
+    isis_lan_hello_rx(pkt, ifa, n, level);
+    break;
+
+  case ISIS_L2_LSP:
+    level = ISIS_L2;
+  case ISIS_L1_LSP:
+    ISIS_TRACE(D_PACKETS, "Received LSP from xxx via %s", ifa->iface->name);
+    isis_lsp_rx(pkt, ifa, n, level);
+    break;
+
+  case ISIS_L2_CSNP:
+    level = ISIS_L2;
+  case ISIS_L1_CSNP:
+    ISIS_TRACE(D_PACKETS, "Received CSNP from xxx via %s", ifa->iface->name);
+    isis_process_csnp(pkt, ifa, n, level);
+    break;
+
+  case ISIS_L2_PSNP:
+    level = ISIS_L2;
+  case ISIS_L1_PSNP:
+    ISIS_TRACE(D_PACKETS, "Received PSNP from xxx via %s", ifa->iface->name);
+    isis_psnp_received(pkt, ifa, n, level);
+    break;
+
+  default:
+    DROP("unknown type", type);
+  };
+  return 1;
+
+ drop:
+  log(L_WARN "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val);
+  return 1;
+}
+
+static void
+isis_tx_hook(sock *sk)
+{
+  struct isis_iface *ifa = sk->data;
+  log(L_WARN "%s: TX hook called", ifa->p->p.name);
+}
+
+static void
+isis_err_hook(sock *sk, int err)
+{
+  struct isis_iface *ifa = sk->data;
+  log(L_ERR "%s: Socket error: %m", ifa->p->p.name, err);
+}
+
+int
+isis_sk_open(struct isis_iface *ifa)
+{
+  sock *sk = sk_new(ifa->pool);
+  sk->type = SK_IP;
+  sk->dport = ISIS_PROTO;
+  sk->saddr = IPA_NONE;
+
+  sk->ttl = 0;
+  sk->rx_hook = isis_rx_hook;
+  sk->tx_hook = isis_tx_hook;
+  sk->err_hook = isis_err_hook;
+  sk->iface = ifa->iface;
+  sk->rbsize = p->rx_buffer_size + 64; // XXX
+  sk->tbsize = p->tx_buffer_size + 64; // XXX
+  sk->data = ifa;
+  sk->flags = SKF_LADDR_RX;
+
+  if (sk_open(sk) != 0)
+    goto err;
+
+  sk->saddr = ifa->addr->ip;
+
+  if (sk_setup_multicast(sk) < 0)
+    goto err;
+
+  if (sk_join_group(sk, AllRouters) < 0)
+    goto err;
+
+  ifa->sk = sk;
+  return 1;
+
+ err:
+  rfree(sk);
+  return 0;
+}
+
index ac6f7a8765b4cf48f0375eed8c273014fc16e67e..a08d22da2388558fd698b61c87c52addca3b7ef6 100644 (file)
@@ -41,6 +41,7 @@
 #undef CONFIG_RADV
 #undef CONFIG_BGP
 #undef CONFIG_OSPF
+#undef CONFIG_ISIS
 #undef CONFIG_PIPE
 
 /* We have <syslog.h> and syslog() */