]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: add second-level route_table for srcdest
authorDavid Lamparter <equinox@opensourcerouting.org>
Thu, 24 Apr 2014 17:29:07 +0000 (19:29 +0200)
committerChristian Franke <chris@opensourcerouting.org>
Mon, 30 Jan 2017 12:51:50 +0000 (13:51 +0100)
IPv6 srcdest routes need to be keyed by both destination and source
prefix.  Since the lookup order is destination first, the simplest thing
to do here is to add a second route_table to destination entries, which
then contain source entries.  Sadly, the result is somewhat confusing
since a route_node might now be either a source node or a destination
node.

There are helper functions to get source and destination prefix from a
given route node (which can be either a destination or a source route).

The following bits have been added by Christian Franke
<chris@opensourcerouting.org>:
- make srcdest routing table reusable by moving it into lib
- make the srcdest routing table structure more opaque
- implement a srcdest routing table iterator
- fix a refcounting issue in src_node_lookup
- match route_node_lookup behavior with srcdest_rnode_lookup
- add accessor for the route_node table and table_info
- add string formatter srcdest_rnode2str

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
[v3: adapted for cmaster-next as of 2016-12-05]

lib/Makefile.am
lib/srcdest_table.c [new file with mode: 0644]
lib/srcdest_table.h [new file with mode: 0644]
lib/table.c
lib/table.h

index ac1935d731d1da7b4fdfe9b66ae1fc7755d1ad11..b6de2c2cd6eb6c94d10770e85f5d15606c103537 100644 (file)
@@ -27,6 +27,7 @@ libzebra_la_SOURCES = \
        qobj.c wheel.c \
        event_counter.c \
        grammar_sandbox.c \
+       srcdest_table.c \
        strlcpy.c \
        strlcat.c
 
@@ -48,7 +49,8 @@ pkginclude_HEADERS = \
        fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h \
        skiplist.h qobj.h wheel.h \
        event_counter.h \
-       monotime.h
+       monotime.h \
+       srcdest_table.h
 
 noinst_HEADERS = \
        plist_int.h
diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c
new file mode 100644 (file)
index 0000000..dd148fa
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * SRC-DEST Routing Table
+ *
+ * Copyright (C) 2017 by David Lamparter & Christian Franke,
+ *                       Open Source Routing / NetDEF Inc.
+ *
+ * This file is part of FreeRangeRouting (FRR)
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "srcdest_table.h"
+
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+
+DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node")
+
+/* ----- functions to manage rnodes _with_ srcdest table ----- */
+struct srcdest_rnode
+{
+  /* must be first in structure for casting to/from route_node */
+  ROUTE_NODE_FIELDS;
+
+  struct route_table *src_table;
+};
+
+static struct srcdest_rnode *
+srcdest_rnode_from_rnode (struct route_node *rn)
+{
+  assert (rnode_is_dstnode (rn));
+  return (struct srcdest_rnode *) rn;
+}
+
+static struct route_node *
+srcdest_rnode_to_rnode (struct srcdest_rnode *srn)
+{
+  return (struct route_node *) srn;
+}
+
+static struct route_node *
+srcdest_rnode_create (route_table_delegate_t *delegate,
+                     struct route_table *table)
+{
+  struct srcdest_rnode *srn;
+  srn = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct srcdest_rnode));
+  return srcdest_rnode_to_rnode(srn);
+}
+
+static void
+srcdest_rnode_destroy (route_table_delegate_t *delegate,
+                      struct route_table *table, struct route_node *rn)
+{
+  struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn);
+  struct route_table *src_table;
+
+  /* Clear route node's src_table here already, otherwise the
+   * deletion of the last node in the src_table will trigger
+   * another call to route_table_finish for the src_table.
+   *
+   * (Compare with srcdest_srcnode_destroy)
+   */
+  src_table = srn->src_table;
+  srn->src_table = NULL;
+  route_table_finish(src_table);
+  XFREE (MTYPE_ROUTE_NODE, rn);
+}
+
+route_table_delegate_t _srcdest_dstnode_delegate = {
+  .create_node = srcdest_rnode_create,
+  .destroy_node = srcdest_rnode_destroy
+};
+
+/* ----- functions to manage rnodes _in_ srcdest table ----- */
+
+/* node creation / deletion for srcdest source prefix nodes.
+ * the route_node isn't actually different from the normal route_node,
+ * but the cleanup is special to free the table (and possibly the
+ * destination prefix's route_node) */
+
+static struct route_node *
+srcdest_srcnode_create (route_table_delegate_t *delegate,
+                  struct route_table *table)
+{
+  return XCALLOC (MTYPE_ROUTE_SRC_NODE, sizeof (struct route_node));
+}
+
+static void
+srcdest_srcnode_destroy (route_table_delegate_t *delegate,
+                   struct route_table *table, struct route_node *rn)
+{
+  struct srcdest_rnode *srn;
+
+  XFREE (MTYPE_ROUTE_SRC_NODE, rn);
+
+  srn = table->info;
+  if (srn->src_table && route_table_count (srn->src_table) == 0)
+    {
+      /* deleting the route_table from inside destroy_node is ONLY
+       * permitted IF table->count is 0!  see lib/table.c route_node_delete()
+       * for details */
+      route_table_finish (srn->src_table);
+      srn->src_table = NULL;
+
+      /* drop the ref we're holding in srcdest_node_get().  there might be
+       * non-srcdest routes, so the route_node may still exist.  hence, it's
+       * important to clear src_table above. */
+      route_unlock_node (srcdest_rnode_to_rnode (srn));
+    }
+}
+
+route_table_delegate_t _srcdest_srcnode_delegate = {
+  .create_node = srcdest_srcnode_create,
+  .destroy_node = srcdest_srcnode_destroy
+};
+
+/* NB: read comments in code for refcounting before using! */
+static struct route_node *
+srcdest_srcnode_get (struct route_node *rn, struct prefix_ipv6 *src_p)
+{
+  struct srcdest_rnode *srn;
+
+  if (!src_p || src_p->prefixlen == 0)
+    return rn;
+
+  srn = srcdest_rnode_from_rnode (rn);
+  if (!srn->src_table)
+    {
+      /* this won't use srcdest_rnode, we're already on the source here */
+      srn->src_table = route_table_init_with_delegate (&_srcdest_srcnode_delegate);
+      srn->src_table->info = srn;
+
+      /* there is no route_unlock_node on the original rn here.
+       * The reference is kept for the src_table. */
+    }
+  else
+    {
+      /* only keep 1 reference for the src_table, makes the refcounting
+       * more similar to the non-srcdest case.  Either way after return from
+       * function, the only reference held is the one on the return value.
+       *
+       * We can safely drop our reference here because src_table is holding
+       * another reference, so this won't free rn */
+      route_unlock_node (rn);
+    }
+
+  return route_node_get (srn->src_table, (struct prefix *)src_p);
+}
+
+static struct route_node *
+srcdest_srcnode_lookup (struct route_node *rn, struct prefix_ipv6 *src_p)
+{
+  struct srcdest_rnode *srn;
+
+  if (!rn || !src_p || src_p->prefixlen == 0)
+    return rn;
+
+  /* We got this rn from a lookup, so its refcnt was incremented. As we won't
+   * return return rn from any point beyond here, we should decrement its refcnt.
+   */
+  route_unlock_node (rn);
+
+  srn = srcdest_rnode_from_rnode (rn);
+  if (!srn->src_table)
+    return NULL;
+
+  return route_node_lookup (srn->src_table, (struct prefix *)src_p);
+}
+
+/* ----- exported functions ----- */
+
+struct route_table *
+srcdest_table_init(void)
+{
+  return route_table_init_with_delegate(&_srcdest_dstnode_delegate);
+}
+
+struct route_node *
+srcdest_route_next(struct route_node *rn)
+{
+  struct route_node *next, *parent;
+
+  /* For a non src-dest node, just return route_next */
+  if (!(rnode_is_dstnode(rn) || rnode_is_srcnode(rn)))
+    return route_next(rn);
+
+  if (rnode_is_dstnode(rn))
+    {
+      /* This means the route_node is part of the top hierarchy
+       * and refers to a destination prefix. */
+      struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn);
+
+      if (srn->src_table)
+        next = route_top(srn->src_table);
+      else
+        next = NULL;
+
+      if (next)
+        {
+          /* There is a source prefix. Return the node for it */
+          route_unlock_node(rn);
+          return next;
+        }
+      else
+        {
+          /* There is no source prefix, just continue as usual */
+          return route_next(rn);
+        }
+    }
+
+  /* This part handles the case of iterating source nodes. */
+  parent = route_lock_node(rn->table->info);
+  next = route_next(rn);
+
+  if (next)
+    {
+      /* There is another source node, continue in the source table */
+      route_unlock_node(parent);
+      return next;
+    }
+  else
+    {
+      /* The source table is complete, continue in the parent table */
+      return route_next(parent);
+    }
+}
+
+struct route_node *
+srcdest_rnode_get (struct route_table *table, union prefix46ptr dst_pu,
+                  struct prefix_ipv6 *src_p)
+{
+  struct prefix_ipv6 *dst_p = dst_pu.p6;
+  struct route_node *rn;
+
+  rn = route_node_get (table, (struct prefix *) dst_p);
+  return srcdest_srcnode_get (rn, src_p);
+}
+
+struct route_node *
+srcdest_rnode_lookup (struct route_table *table, union prefix46ptr dst_pu,
+                      struct prefix_ipv6 *src_p)
+{
+  struct prefix_ipv6 *dst_p = dst_pu.p6;
+  struct route_node *rn;
+  struct route_node *srn;
+
+  rn = route_node_lookup_maynull (table, (struct prefix *) dst_p);
+  srn = srcdest_srcnode_lookup (rn, src_p);
+
+  if (rn != NULL && rn == srn && !rn->info)
+    {
+      /* Match the behavior of route_node_lookup and don't return an
+       * empty route-node for a dest-route */
+      route_unlock_node(rn);
+      return NULL;
+    }
+  return srn;
+}
+
+void
+srcdest_rnode_prefixes (struct route_node *rn, struct prefix **p,
+                   struct prefix **src_p)
+{
+  if (rnode_is_srcnode (rn))
+    {
+      struct route_node *dst_rn = rn->table->info;
+      if (p)
+       *p = &dst_rn->p;
+      if (src_p)
+       *src_p = &rn->p;
+    }
+  else
+    {
+      if (p)
+       *p = &rn->p;
+      if (src_p)
+       *src_p = NULL;
+    }
+}
+
+const char *
+srcdest_rnode2str (struct route_node *rn, char *str, int size)
+{
+  struct prefix *dst_p, *src_p;
+  char dst_buf[PREFIX_STRLEN], src_buf[PREFIX_STRLEN];
+
+  srcdest_rnode_prefixes(rn, &dst_p, &src_p);
+
+  snprintf(str, size, "%s%s%s",
+           prefix2str(dst_p, dst_buf, sizeof(dst_buf)),
+           (src_p && src_p->prefixlen) ? " from " : "",
+           (src_p && src_p->prefixlen) ? prefix2str(src_p, src_buf,
+                                                    sizeof(src_buf))
+                                       : "");
+  return str;
+}
diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h
new file mode 100644 (file)
index 0000000..59111b5
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * SRC-DEST Routing Table
+ *
+ * Copyright (C) 2017 by David Lamparter & Christian Franke,
+ *                       Open Source Routing / NetDEF Inc.
+ *
+ * This file is part of FreeRangeRouting (FRR)
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_SRC_DEST_TABLE_H
+#define _ZEBRA_SRC_DEST_TABLE_H
+
+/* old/IPv4/non-srcdest:
+ * table -> route_node .info -> [obj]
+ *
+ * new/IPv6/srcdest:
+ * table -...-> srcdest_rnode [prefix = dest] .info -> [obj]
+ *                                            .src_table ->
+ *         srcdest table -...-> route_node [prefix = src] .info -> [obj]
+ *
+ * non-srcdest routes (src = ::/0) are treated just like before, their
+ * information being directly there in the info pointer.
+ *
+ * srcdest routes are found by looking up destination first, then looking
+ * up the source in the "src_table".  src_table contains normal route_nodes,
+ * whose prefix is the _source_ prefix.
+ *
+ * NB: info can be NULL on the destination rnode, if there are only srcdest
+ * routes for a particular destination prefix.
+ */
+
+#include "prefix.h"
+#include "table.h"
+
+#define SRCDEST2STR_BUFFER (2*PREFIX2STR_BUFFER + sizeof(" from "))
+
+/* extended route node for IPv6 srcdest routing */
+struct srcdest_rnode;
+
+extern route_table_delegate_t _srcdest_dstnode_delegate;
+extern route_table_delegate_t _srcdest_srcnode_delegate;
+
+extern struct route_table *srcdest_table_init(void);
+extern struct route_node *srcdest_rnode_get(struct route_table *table,
+                                            union prefix46ptr dst_pu,
+                                            struct prefix_ipv6 *src_p);
+extern struct route_node *srcdest_rnode_lookup(struct route_table *table,
+                                            union prefix46ptr dst_pu,
+                                            struct prefix_ipv6 *src_p);
+extern void srcdest_rnode_prefixes (struct route_node *rn, struct prefix **p,
+                                    struct prefix **src_p);
+extern const char *srcdest_rnode2str(struct route_node *rn, char *str, int size);
+extern struct route_node *srcdest_route_next(struct route_node *rn);
+
+static inline int
+rnode_is_dstnode (struct route_node *rn)
+{
+  return rn->table->delegate == &_srcdest_dstnode_delegate;
+}
+
+static inline int
+rnode_is_srcnode (struct route_node *rn)
+{
+  return rn->table->delegate == &_srcdest_srcnode_delegate;
+}
+
+static inline struct route_table *
+srcdest_rnode_table (struct route_node *rn)
+{
+  if (rnode_is_srcnode (rn))
+    {
+      struct route_node *dst_rn = rn->table->info;
+      return dst_rn->table;
+    }
+  else
+    {
+      return rn->table;
+    }
+}
+static inline void *
+srcdest_rnode_table_info (struct route_node *rn)
+{
+  return srcdest_rnode_table(rn)->info;
+}
+
+#endif /* _ZEBRA_SRC_DEST_TABLE_H */
index f80f3e81f056bafe675b0a55a4db44a2cd6ff3a9..075c5584a83c9f43b0be18d45306cf06b5abee46 100644 (file)
@@ -28,7 +28,7 @@
 #include "sockunion.h"
 
 DEFINE_MTYPE(       LIB, ROUTE_TABLE, "Route table")
-DEFINE_MTYPE_STATIC(LIB, ROUTE_NODE,  "Route node")
+DEFINE_MTYPE(       LIB, ROUTE_NODE,  "Route node")
 
 static void route_node_delete (struct route_node *);
 static void route_table_free (struct route_table *);
@@ -400,6 +400,14 @@ route_node_delete (struct route_node *node)
 
   node->table->count--;
 
+  /* WARNING: FRAGILE CODE!
+   * route_node_free may have the side effect of free'ing the entire table.
+   * this is permitted only if table->count got decremented to zero above,
+   * because in that case parent will also be NULL, so that we won't try to
+   * delete a now-stale parent below.
+   *
+   * cf. srcdest_srcnode_destroy() in zebra/zebra_rib.c */
+
   route_node_free (node->table, node);
 
   /* If parent node is stub then delete it also. */
index e35e55dc1c9698f92d146d8f5e485d9f7a26d090..6997b6bdd5301cfd56d1a9e5cdc65ab6373cae14 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "memory.h"
 DECLARE_MTYPE(ROUTE_TABLE)
+DECLARE_MTYPE(ROUTE_NODE)
 
 /*
  * Forward declarations.