]> git.proxmox.com Git - mirror_frr.git/commitdiff
zebra: add pseudowire manager
authorßingen <bingen@voltanet.io>
Mon, 15 May 2017 15:09:28 +0000 (17:09 +0200)
committerRenato Westphal <renato@opensourcerouting.org>
Tue, 25 Jul 2017 03:53:23 +0000 (00:53 -0300)
Base framework for supporting MPLS pseudowires in FRR.

A consistent zserv interface is provided so that any client daemon
(e.g. ldpd, bgpd) can install/uninstall pseudowires in a standard
way. Static pseudowires can also be implemented by using the same
interface.

When zebra receives a request to install a pseudowire and the installation
in the kernel or hardware fails, a notification is sent back to the
client daemon and a new install attempt is made every 60 seconds (until
it succeeds).

Support for external dataplanes is provided by the use of hooks to
install/uninstall pseudowires.

Signed-off-by: ßingen <bingen@voltanet.io>
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
17 files changed:
ldpd/ldp.h
ldpd/ldpd.h
lib/Makefile.am
lib/log.c
lib/pw.h [new file with mode: 0644]
lib/zclient.c
lib/zclient.h
zebra/Makefile.am
zebra/debug.c
zebra/debug.h
zebra/zebra_pw.c [new file with mode: 0644]
zebra/zebra_pw.h [new file with mode: 0644]
zebra/zebra_pw_null.c [new file with mode: 0644]
zebra/zebra_vrf.c
zebra/zebra_vrf.h
zebra/zserv.c
zebra/zserv.h

index c2b64d20c67458cb9f413df9157ecfcb1edc62a4..cac3da7c55d0259a5310e6e92cc9b30f294dfd61 100644 (file)
@@ -285,9 +285,6 @@ struct address_list_tlv {
 #define        MAP_TYPE_GENPWID        0x81
 
 #define CONTROL_WORD_FLAG      0x8000
-#define PW_TYPE_ETHERNET_TAGGED        0x0004
-#define PW_TYPE_ETHERNET       0x0005
-#define PW_TYPE_WILDCARD       0x7FFF
 #define DEFAULT_PW_TYPE                PW_TYPE_ETHERNET
 
 #define PW_TWCARD_RESERVED_BIT 0x8000
index 97239ed08edb1a009e02c3840ef741c3641399a8..21f5e16f88e3fbbaed092e323f084e8f807872d4 100644 (file)
@@ -29,6 +29,7 @@
 #include "qobj.h"
 #include "prefix.h"
 #include "filter.h"
+#include "pw.h"
 
 #include "ldp.h"
 
index 14b7130c8a8d796ccff52eb65c256fa968ef1f09..1f31806973186f928b6d24d040c7cb22e1454b24 100644 (file)
@@ -73,6 +73,7 @@ pkginclude_HEADERS = \
        module.h \
        hook.h \
        libfrr.h \
+       pw.h \
        # end
 
 noinst_HEADERS = \
index 52d92de39226cd194d0acc7474782b32c5a31169..735ca71ed90548f9b2b2d4d9998df69add7a61c7 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -937,6 +937,11 @@ static const struct zebra_desc_table command_types[] = {
   DESC_ENTRY    (ZEBRA_LABEL_MANAGER_CONNECT),
   DESC_ENTRY    (ZEBRA_GET_LABEL_CHUNK),
   DESC_ENTRY    (ZEBRA_RELEASE_LABEL_CHUNK),
+  DESC_ENTRY    (ZEBRA_PW_ADD),
+  DESC_ENTRY    (ZEBRA_PW_DELETE),
+  DESC_ENTRY    (ZEBRA_PW_SET),
+  DESC_ENTRY    (ZEBRA_PW_UNSET),
+  DESC_ENTRY    (ZEBRA_PW_STATUS_UPDATE),
 };
 #undef DESC_ENTRY
 
diff --git a/lib/pw.h b/lib/pw.h
new file mode 100644 (file)
index 0000000..2fba996
--- /dev/null
+++ b/lib/pw.h
@@ -0,0 +1,48 @@
+/* Pseudowire definitions
+ * Copyright (C) 2016 Volta Networks, Inc.
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef _FRR_PW_H
+#define _FRR_PW_H
+
+/* Pseudowire type - LDP and BGP use the same values. */
+#define PW_TYPE_ETHERNET_TAGGED        0x0004  /* RFC 4446 */
+#define PW_TYPE_ETHERNET       0x0005  /* RFC 4446 */
+#define PW_TYPE_WILDCARD       0x7FFF  /* RFC 4863, RFC 6668 */
+
+/* Pseudowire flags. */
+#define F_PSEUDOWIRE_CWORD     0x01
+
+/* Pseudowire status. */
+#define PW_STATUS_DOWN         0
+#define PW_STATUS_UP           1
+
+/*
+ * Protocol-specific information about the pseudowire.
+ */
+union pw_protocol_fields
+{
+  struct {
+      /* TODO */
+  } ldp;
+  struct {
+      /* TODO */
+  } bgp;
+};
+
+#endif /* _FRR_PW_H */
index 6aea4bd0a31af683b747e2d70172f442ccdb3ce2..9338f9c98651e8cb6dcaae1eee58decdfc1ff1ac 100644 (file)
@@ -1715,6 +1715,72 @@ lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end)
   return 0;
 }
 
+int
+zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw)
+{
+  struct stream *s;
+
+  /* Reset stream. */
+  s = zclient->obuf;
+  stream_reset(s);
+
+  zclient_create_header(s, command, VRF_DEFAULT);
+  stream_write(s, pw->ifname, IF_NAMESIZE);
+  stream_putl(s, pw->ifindex);
+
+  /* Put type */
+  stream_putl(s, pw->type);
+
+  /* Put nexthop */
+  stream_putl(s, pw->af);
+  switch (pw->af)
+    {
+    case AF_INET:
+      stream_put_in_addr(s, &pw->nexthop.ipv4);
+      break;
+    case AF_INET6:
+      stream_write(s, (u_char *)&pw->nexthop.ipv6, 16);
+      break;
+    default:
+      zlog_err ("%s: unknown af", __func__);
+      return -1;
+    }
+
+  /* Put labels */
+  stream_putl(s, pw->local_label);
+  stream_putl(s, pw->remote_label);
+
+  /* Put flags */
+  stream_putc(s, pw->flags);
+
+  /* Protocol specific fields */
+  stream_write(s, &pw->data, sizeof(union pw_protocol_fields));
+
+  /* Put length at the first point of the stream. */
+  stream_putw_at(s, 0, stream_get_endp(s));
+
+  return zclient_send_message(zclient);
+}
+
+/*
+ * Receive PW status update from Zebra and send it to LDE process.
+ */
+void
+zebra_read_pw_status_update(int command, struct zclient *zclient,
+                            zebra_size_t length, vrf_id_t vrf_id,
+                            struct zapi_pw_status *pw)
+{
+  struct stream        *s;
+
+  memset(pw, 0, sizeof(struct zapi_pw_status));
+  s = zclient->ibuf;
+
+  /* Get data. */
+  stream_get(pw->ifname, s, IF_NAMESIZE);
+  pw->ifindex = stream_getl(s);
+  pw->status = stream_getl(s);
+}
+
 /* Zebra client message read function. */
 static int
 zclient_read (struct thread *thread)
@@ -1899,6 +1965,10 @@ zclient_read (struct thread *thread)
       if (zclient->interface_link_params)
         (*zclient->interface_link_params) (command, zclient, length);
       break;
+    case ZEBRA_PW_STATUS_UPDATE:
+      if (zclient->pw_status_update)
+        (*zclient->pw_status_update) (command, zclient, length, vrf_id);
+      break;
     default:
       break;
     }
index d3d0a202c5caf379bcaf3d56324fb9bd6db33b87..489ed24c86e8650302fa3720f4f6dac94a7a31f1 100644 (file)
 /* For vrf_bitmap_t. */
 #include "vrf.h"
 
+/* For union g_addr */
+#include "nexthop.h"
+
+/* For union pw_protocol_fields */
+#include "pw.h"
+
 /* For input/output buffer to zebra. */
 #define ZEBRA_MAX_PACKET_SIZ          4096
 
@@ -94,6 +100,11 @@ typedef enum {
   ZEBRA_LABEL_MANAGER_CONNECT,
   ZEBRA_GET_LABEL_CHUNK,
   ZEBRA_RELEASE_LABEL_CHUNK,
+  ZEBRA_PW_ADD,
+  ZEBRA_PW_DELETE,
+  ZEBRA_PW_SET,
+  ZEBRA_PW_UNSET,
+  ZEBRA_PW_STATUS_UPDATE,
 } zebra_message_types_t;
 
 struct redist_proto
@@ -164,6 +175,7 @@ struct zclient
   int (*redistribute_route_ipv4_del) (int, struct zclient *, uint16_t, vrf_id_t);
   int (*redistribute_route_ipv6_add) (int, struct zclient *, uint16_t, vrf_id_t);
   int (*redistribute_route_ipv6_del) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*pw_status_update) (int, struct zclient *, uint16_t, vrf_id_t);
 };
 
 /* Zebra API message flag. */
@@ -217,6 +229,27 @@ struct zapi_ipv4
   vrf_id_t vrf_id;
 };
 
+struct zapi_pw
+{
+  char ifname[IF_NAMESIZE];
+  ifindex_t ifindex;
+  int type;
+  int af;
+  union g_addr nexthop;
+  uint32_t local_label;
+  uint32_t remote_label;
+  uint8_t flags;
+  union pw_protocol_fields data;
+  uint8_t protocol;
+};
+
+struct zapi_pw_status
+{
+  char ifname[IF_NAMESIZE];
+  ifindex_t ifindex;
+  uint32_t status;
+};
+
 /* Prototypes of zebra client service functions. */
 extern struct zclient *zclient_new (struct thread_master *);
 extern void zclient_init (struct zclient *, int, u_short);
@@ -278,6 +311,11 @@ extern int lm_label_manager_connect (struct zclient *zclient);
 extern int lm_get_label_chunk (struct zclient *zclient, u_char keep,
                                uint32_t chunk_size, uint32_t *start, uint32_t *end);
 extern int lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end);
+extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw);
+extern void zebra_read_pw_status_update(int command, struct zclient *zclient,
+                                        zebra_size_t length, vrf_id_t vrf_id,
+                                        struct zapi_pw_status *pw);
+
 /* IPv6 prefix add and delete function prototype. */
 
 struct zapi_ipv6
index 3e0de3b4631c7b913b111f7f46760e0d999a7ffa..9e9998ebbc06fb49624447cf1c0bdf1e32b7f9c7 100644 (file)
@@ -33,14 +33,14 @@ zebra_SOURCES = \
        zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \
        zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \
        zebra_mroute.c \
-       label_manager.c \
+       label_manager.c zebra_pw.c \
        # end
 
 testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
        zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \
        kernel_null.c  redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \
        zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \
-       zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c
+       zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c zebra_pw_null.c
 
 noinst_HEADERS = \
        zebra_memory.h \
@@ -49,7 +49,8 @@ noinst_HEADERS = \
        rt_netlink.h zebra_fpm_private.h zebra_rnh.h \
        zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \
        zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \
-       kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h
+       kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h zebra_pw.h \
+       # end
 
 zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP)
 
index a42d5aa3ef98763887c70bb74f19aea428db5e74..6f59dc0ac2347d2d54111e9957d9ed85afd479ff 100644 (file)
@@ -32,6 +32,7 @@ unsigned long zebra_debug_rib;
 unsigned long zebra_debug_fpm;
 unsigned long zebra_debug_nht;
 unsigned long zebra_debug_mpls;
+unsigned long zebra_debug_pw;
 
 DEFUN (show_debugging_zebra,
        show_debugging_zebra_cmd,
@@ -85,6 +86,8 @@ DEFUN (show_debugging_zebra,
     vty_out (vty, "  Zebra next-hop tracking debugging is on%s", VTY_NEWLINE);
   if (IS_ZEBRA_DEBUG_MPLS)
     vty_out (vty, "  Zebra MPLS debugging is on%s", VTY_NEWLINE);
+  if (IS_ZEBRA_DEBUG_PW)
+    vty_out (vty, "  Zebra pseudowire debugging is on%s", VTY_NEWLINE);
 
   return CMD_SUCCESS;
 }
@@ -122,6 +125,21 @@ DEFUN (debug_zebra_mpls,
   return CMD_WARNING;
 }
 
+DEFUN (debug_zebra_pw,
+       debug_zebra_pw_cmd,
+       "[no] debug zebra pseudowires",
+       "Negate a command or set its defaults\n"
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra pseudowires\n")
+{
+  if (strmatch (argv[0]->text, "no"))
+    UNSET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW);
+  else
+    SET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW);
+  return CMD_WARNING;
+}
+
 DEFUN (debug_zebra_packet,
        debug_zebra_packet_cmd,
        "debug zebra packet [<recv|send>] [detail]",
@@ -410,6 +428,11 @@ config_write_debug (struct vty *vty)
       vty_out (vty, "debug zebra mpls%s", VTY_NEWLINE);
       write++;
     }
+  if (IS_ZEBRA_DEBUG_PW)
+    {
+      vty_out (vty, "debug zebra pseudowires%s", VTY_NEWLINE);
+      write++;
+    }
   return write;
 }
 
@@ -422,6 +445,7 @@ zebra_debug_init (void)
   zebra_debug_rib = 0;
   zebra_debug_fpm = 0;
   zebra_debug_mpls = 0;
+  zebra_debug_pw = 0;
 
   install_node (&debug_node, config_write_debug);
 
@@ -430,6 +454,7 @@ zebra_debug_init (void)
   install_element (ENABLE_NODE, &debug_zebra_events_cmd);
   install_element (ENABLE_NODE, &debug_zebra_nht_cmd);
   install_element (ENABLE_NODE, &debug_zebra_mpls_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_pw_cmd);
   install_element (ENABLE_NODE, &debug_zebra_packet_cmd);
   install_element (ENABLE_NODE, &debug_zebra_kernel_cmd);
   install_element (ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd);
@@ -449,6 +474,7 @@ zebra_debug_init (void)
   install_element (CONFIG_NODE, &debug_zebra_events_cmd);
   install_element (CONFIG_NODE, &debug_zebra_nht_cmd);
   install_element (CONFIG_NODE, &debug_zebra_mpls_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_pw_cmd);
   install_element (CONFIG_NODE, &debug_zebra_packet_cmd);
   install_element (CONFIG_NODE, &debug_zebra_kernel_cmd);
   install_element (CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd);
index f8ebf3d616e9f418765511d055f137705f8bd52d..9a196d2f3455c7d370768f611e2c1457c3fa68a5 100644 (file)
@@ -43,6 +43,8 @@
 
 #define ZEBRA_DEBUG_MPLS    0x01
 
+#define ZEBRA_DEBUG_PW      0x01
+
 /* Debug related macro. */
 #define IS_ZEBRA_DEBUG_EVENT  (zebra_debug_event & ZEBRA_DEBUG_EVENT)
 
@@ -64,6 +66,7 @@
 #define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM)
 #define IS_ZEBRA_DEBUG_NHT  (zebra_debug_nht & ZEBRA_DEBUG_NHT)
 #define IS_ZEBRA_DEBUG_MPLS  (zebra_debug_mpls & ZEBRA_DEBUG_MPLS)
+#define IS_ZEBRA_DEBUG_PW  (zebra_debug_pw & ZEBRA_DEBUG_PW)
 
 extern unsigned long zebra_debug_event;
 extern unsigned long zebra_debug_packet;
@@ -72,6 +75,7 @@ extern unsigned long zebra_debug_rib;
 extern unsigned long zebra_debug_fpm;
 extern unsigned long zebra_debug_nht;
 extern unsigned long zebra_debug_mpls;
+extern unsigned long zebra_debug_pw;
 
 extern void zebra_debug_init (void);
 
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
new file mode 100644 (file)
index 0000000..b8ce182
--- /dev/null
@@ -0,0 +1,266 @@
+/* Zebra PW code
+ * Copyright (C) 2016 Volta Networks, Inc.
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "thread.h"
+#include "vrf.h"
+
+#include "zebra/debug.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_pw.h"
+
+DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire")
+
+DEFINE_HOOK(pw_install, (struct zebra_pw *pw), (pw))
+DEFINE_HOOK(pw_uninstall, (struct zebra_pw *pw), (pw))
+
+extern struct zebra_t zebrad;
+
+static void zebra_pw_install(struct zebra_pw *);
+static void zebra_pw_uninstall(struct zebra_pw *);
+static int zebra_pw_install_retry(struct thread *);
+static int zebra_pw_check_reachability(struct zebra_pw *);
+static void zebra_pw_update_status(struct zebra_pw *, int);
+
+static inline int zebra_pw_compare(const struct zebra_pw *a,
+                                    const struct zebra_pw *b)
+{
+       return (strcmp(a->ifname, b->ifname));
+}
+
+RB_GENERATE(zebra_pw_head, zebra_pw, entry, zebra_pw_compare)
+
+struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname,
+                             uint8_t protocol, struct zserv *client)
+{
+       struct zebra_pw *pw;
+
+       if (IS_ZEBRA_DEBUG_PW)
+               zlog_debug("%u: adding pseudowire %s protocol %s",
+                          zvrf_id(zvrf), ifname, zebra_route_string(protocol));
+
+       pw = XCALLOC(MTYPE_PW, sizeof(*pw));
+       strlcpy(pw->ifname, ifname, sizeof(pw->ifname));
+       pw->protocol = protocol;
+       pw->vrf_id = zvrf_id(zvrf);
+       pw->client = client;
+       pw->status = PW_STATUS_UP;
+
+       RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw);
+
+       return pw;
+}
+
+void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw)
+{
+       if (IS_ZEBRA_DEBUG_PW)
+               zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id,
+                          pw->ifname, zebra_route_string(pw->protocol));
+
+       /* uninstall */
+       if (pw->status == PW_STATUS_UP)
+               hook_call(pw_uninstall, pw);
+       else if (pw->install_retry_timer)
+               THREAD_TIMER_OFF(pw->install_retry_timer);
+
+       /* unlink and release memory */
+       RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw);
+       XFREE(MTYPE_PW, pw);
+}
+
+void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af,
+                    union g_addr *nexthop, uint32_t local_label,
+                    uint32_t remote_label, uint8_t flags,
+                    union pw_protocol_fields *data)
+{
+       pw->ifindex = ifindex;
+       pw->type = type;
+       pw->af = af;
+       pw->nexthop = *nexthop;
+       pw->local_label = local_label;
+       pw->remote_label = remote_label;
+       pw->flags = flags;
+       pw->data = *data;
+
+       if (pw->enabled)
+               zebra_pw_update(pw);
+       else
+               zebra_pw_uninstall(pw);
+}
+
+struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname)
+{
+       struct zebra_pw pw;
+       strlcpy(pw.ifname, ifname, sizeof(pw.ifname));
+       return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw));
+}
+
+void zebra_pw_update(struct zebra_pw *pw)
+{
+       if (zebra_pw_check_reachability(pw) < 0) {
+               zebra_pw_uninstall(pw);
+       } else {
+               /*
+                * Install or reinstall the pseudowire (e.g. to update
+                * parameters like the nexthop or the use of the control word).
+                */
+               zebra_pw_install(pw);
+       }
+}
+
+static void zebra_pw_install(struct zebra_pw *pw)
+{
+       if (IS_ZEBRA_DEBUG_PW)
+               zlog_debug("%u: installing pseudowire %s protocol %s",
+                          pw->vrf_id, pw->ifname,
+                          zebra_route_string(pw->protocol));
+
+       if (hook_call(pw_install, pw)) {
+               zebra_pw_install_failure(pw);
+               return;
+       }
+
+       if (pw->status == PW_STATUS_DOWN)
+               zebra_pw_update_status(pw, PW_STATUS_UP);
+}
+
+static void zebra_pw_uninstall(struct zebra_pw *pw)
+{
+       if (pw->status == PW_STATUS_DOWN)
+               return;
+
+       if (IS_ZEBRA_DEBUG_PW)
+               zlog_debug("%u: uninstalling pseudowire %s protocol %s",
+                          pw->vrf_id, pw->ifname,
+                          zebra_route_string(pw->protocol));
+
+       /* ignore any possible error */
+       hook_call(pw_uninstall, pw);
+
+       if (pw->enabled)
+               zebra_pw_update_status(pw, PW_STATUS_DOWN);
+}
+
+/*
+ * Installation of the pseudowire in the kernel or hardware has failed. This
+ * function will notify the pseudowire client about the failure and schedule
+ * to retry the installation later. This function can be called by an external
+ * agent that performs the pseudowire installation in an asynchronous way.
+ */
+void zebra_pw_install_failure(struct zebra_pw *pw)
+{
+       if (IS_ZEBRA_DEBUG_PW)
+               zlog_debug("%u: failed installing pseudowire %s, "
+                          "scheduling retry in %u seconds", pw->vrf_id,
+                          pw->ifname, PW_INSTALL_RETRY_INTERVAL);
+
+       /* schedule to retry later */
+       THREAD_TIMER_OFF(pw->install_retry_timer);
+       pw->install_retry_timer =
+               thread_add_timer(zebrad.master, zebra_pw_install_retry,
+               pw, PW_INSTALL_RETRY_INTERVAL);
+
+       zebra_pw_update_status(pw, PW_STATUS_DOWN);
+}
+
+static int zebra_pw_install_retry(struct thread *thread)
+{
+       struct zebra_pw *pw = THREAD_ARG(thread);
+
+       pw->install_retry_timer = NULL;
+       zebra_pw_install(pw);
+
+       return 0;
+}
+
+static void zebra_pw_update_status(struct zebra_pw *pw, int status)
+{
+       pw->status = status;
+       if (pw->client)
+               zsend_pw_update(pw->client, pw);
+}
+
+static int zebra_pw_check_reachability(struct zebra_pw *pw)
+{
+       struct rib *rib;
+       struct nexthop *nexthop, *tnexthop;
+       int recursing;
+
+       /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
+
+       /* find route to the remote end of the pseudowire */
+       rib = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
+                       &pw->nexthop, NULL);
+       if (!rib) {
+               if (IS_ZEBRA_DEBUG_PW)
+                       zlog_warn("%s: no route found for %s", __func__,
+                                 pw->ifname);
+               return -1;
+       }
+
+       /*
+        * Need to ensure that there's a label binding for all nexthops.
+        * Otherwise, ECMP for this route could render the pseudowire unusable.
+        */
+       for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) {
+               if (!nexthop->nh_label) {
+                       if (IS_ZEBRA_DEBUG_PW)
+                               zlog_warn("%s: unlabeled route for %s",
+                                   __func__, pw->ifname);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+void
+zebra_pw_client_close(struct zserv *client)
+{
+       struct vrf *vrf;
+       struct zebra_vrf *zvrf;
+       struct zebra_pw *pw, *tmp;
+
+       RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) {
+               zvrf = vrf->info;
+               RB_FOREACH_SAFE(pw, zebra_pw_head, &zvrf->pseudowires, tmp) {
+                       if (pw->client != client)
+                               continue;
+                       zebra_pw_del(zvrf, pw);
+               }
+       }
+}
+
+void zebra_pw_init(struct zebra_vrf *zvrf)
+{
+       RB_INIT(&zvrf->pseudowires);
+}
+
+void zebra_pw_exit(struct zebra_vrf *zvrf)
+{
+       struct zebra_pw *pw;
+
+       while ((pw = RB_ROOT(&zvrf->pseudowires)) != NULL)
+               zebra_pw_del(zvrf, pw);
+}
diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h
new file mode 100644 (file)
index 0000000..9cd2b99
--- /dev/null
@@ -0,0 +1,67 @@
+/* Zebra PW code
+ * Copyright (C) 2016 Volta Networks, Inc.
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef ZEBRA_PW_H_
+#define ZEBRA_PW_H_
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include "hook.h"
+
+#define PW_INSTALL_RETRY_INTERVAL      30
+
+struct zebra_pw {
+       RB_ENTRY(zebra_pw) entry;
+       vrf_id_t vrf_id;
+       char ifname[IF_NAMESIZE];
+       ifindex_t ifindex;
+       int type;
+       int af;
+       union g_addr nexthop;
+       uint32_t local_label;
+       uint32_t remote_label;
+       uint8_t flags;
+       union pw_protocol_fields data;
+       int enabled;
+       int status;
+       uint8_t protocol;
+       struct zserv *client;
+       struct thread *install_retry_timer;
+};
+
+RB_HEAD(zebra_pw_head, zebra_pw);
+RB_PROTOTYPE(zebra_pw_head, zebra_pw, entry, zebra_pw_compare);
+
+DECLARE_HOOK(pw_install, (struct zebra_pw * pw), (pw))
+DECLARE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw))
+
+struct zebra_pw *zebra_pw_add(struct zebra_vrf *, const char *,
+                             uint8_t, struct zserv *);
+void zebra_pw_del(struct zebra_vrf *, struct zebra_pw *);
+void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *,
+                    uint32_t, uint32_t, uint8_t, union pw_protocol_fields *);
+struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *);
+void zebra_pw_update(struct zebra_pw *);
+void zebra_pw_install_failure(struct zebra_pw *);
+void zebra_pw_client_close(struct zserv *);
+void zebra_pw_init(struct zebra_vrf *);
+void zebra_pw_exit(struct zebra_vrf *);
+
+#endif                         /* ZEBRA_PW_H_ */
diff --git a/zebra/zebra_pw_null.c b/zebra/zebra_pw_null.c
new file mode 100644 (file)
index 0000000..d19d80b
--- /dev/null
@@ -0,0 +1,29 @@
+/* Zebra PW code
+ * Copyright (C) 2016 Volta Networks, Inc.
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "vrf.h"
+
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/zebra_vrf.h"
+
+void zebra_pw_init(struct zebra_vrf *zvrf) {}
+void zebra_pw_exit(struct zebra_vrf *zvrf) {}
index 6b36891056944cf2e8c1fb60aedd94129cd68021..1797ef080c87599778881aa3139d2d7eb3355809 100644 (file)
@@ -246,6 +246,7 @@ zebra_vrf_delete (struct vrf *vrf)
        }
 
       zebra_mpls_close_tables (zvrf);
+      zebra_pw_exit (zvrf);
 
       for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp))
        if_nbr_ipv6ll_to_ipv4ll_neigh_del_all (ifp);
@@ -423,6 +424,7 @@ zebra_vrf_alloc (void)
     }
 
   zebra_mpls_init_tables (zvrf);
+  zebra_pw_init (zvrf);
 
   return zvrf;
 }
index 96d631d646e7d91ace47903c59fc660fc13f8754..eab87e67d6d0b2cbbe09a95cbe1ea9ddb6a7d00e 100644 (file)
@@ -24,6 +24,7 @@
 #define __ZEBRA_RIB_H__
 
 #include <zebra/zebra_ns.h>
+#include <zebra/zebra_pw.h>
 
 /* Routing table instance.  */
 struct zebra_vrf
@@ -79,6 +80,9 @@ struct zebra_vrf
   /* MPLS label forwarding table */
   struct hash *lsp_table;
 
+  /* Pseudowires. */
+  struct zebra_pw_head pseudowires;
+
   /* MPLS processing flags */
   u_int16_t mpls_flags;
 #define MPLS_FLAG_SCHEDULE_LSPS    (1 << 0)
index f2d32d77982608b8aeea9828d03956593befdbcb..fb02d60ba48cf9000b55a261535f339e86b6c1f6 100644 (file)
@@ -1013,6 +1013,28 @@ zsend_router_id_update (struct zserv *client, struct prefix *p,
   return zebra_server_send_message(client);
 }
 
+/*
+ * Function used by Zebra to send a PW status update to LDP daemon
+ */
+int
+zsend_pw_update (struct zserv *client, struct zebra_pw *pw)
+{
+  struct stream *s;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id);
+  stream_write (s, pw->ifname, IF_NAMESIZE);
+  stream_putl (s, pw->ifindex);
+  stream_putl (s, pw->status);
+
+  /* Put length at the first point of the stream. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zebra_server_send_message (client);
+}
+
 /* Register zebra server interface information.  Send current all
    interface and address information. */
 static int
@@ -1931,6 +1953,103 @@ zread_label_manager_request (int cmd, struct zserv *client, vrf_id_t vrf_id)
     }
 }
 
+static int
+zread_pseudowire (int command, struct zserv *client, u_short length,
+                 vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zebra_vrf *zvrf;
+  char ifname[IF_NAMESIZE];
+  ifindex_t ifindex;
+  int type;
+  int af;
+  union g_addr nexthop;
+  uint32_t local_label;
+  uint32_t remote_label;
+  uint8_t flags;
+  union pw_protocol_fields data;
+  uint8_t protocol;
+  struct zebra_pw *pw;
+
+  zvrf = vrf_info_lookup (vrf_id);
+  if (!zvrf)
+    return -1;
+
+  /* Get input stream.  */
+  s = client->ibuf;
+
+  /* Get data. */
+  stream_get (ifname, s, IF_NAMESIZE);
+  ifindex = stream_getl (s);
+  type = stream_getl (s);
+  af = stream_getl (s);
+  switch (af)
+    {
+    case AF_INET:
+      nexthop.ipv4.s_addr = stream_get_ipv4 (s);
+      break;
+    case AF_INET6:
+      stream_get (&nexthop.ipv6, s, 16);
+      break;
+    default:
+      return -1;
+    }
+  local_label = stream_getl (s);
+  remote_label = stream_getl (s);
+  flags = stream_getc (s);
+  stream_get (&data, s, sizeof(data));
+  protocol = client->proto;
+
+  pw = zebra_pw_find(zvrf, ifname);
+  switch (command)
+    {
+    case ZEBRA_PW_ADD:
+      if (pw)
+       {
+         zlog_warn ("%s: pseudowire %s already exists [%s]", __func__, ifname,
+                    zserv_command_string (command));
+         return -1;
+       }
+
+      zebra_pw_add (zvrf, ifname, protocol, client);
+      break;
+    case ZEBRA_PW_DELETE:
+      if (!pw)
+       {
+         zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname,
+                    zserv_command_string (command));
+         return -1;
+       }
+
+      zebra_pw_del (zvrf, pw);
+      break;
+    case ZEBRA_PW_SET:
+    case ZEBRA_PW_UNSET:
+      if (!pw)
+       {
+         zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname,
+                    zserv_command_string (command));
+         return -1;
+       }
+
+      switch (command)
+       {
+       case ZEBRA_PW_SET:
+         pw->enabled = 1;
+         break;
+       case ZEBRA_PW_UNSET:
+         pw->enabled = 0;
+         break;
+       }
+
+      zebra_pw_change (pw, ifindex, type, af, &nexthop, local_label,
+                      remote_label, flags, &data);
+      break;
+    }
+
+  return 0;
+}
+
 /* Cleanup registered nexthops (across VRFs) upon client disconnect. */
 static void
 zebra_client_close_cleanup_rnh (struct zserv *client)
@@ -1970,6 +2089,9 @@ zebra_client_close (struct zserv *client)
   /* Release Label Manager chunks */
   release_daemon_chunks (client->proto, client->instance);
 
+  /* Remove pseudowires associated with this client */
+  zebra_pw_client_close (client);
+
   /* Close file descriptor. */
   if (client->sock)
     {
@@ -2267,6 +2389,12 @@ zebra_client_read (struct thread *thread)
     case ZEBRA_RELEASE_LABEL_CHUNK:
       zread_label_manager_request (command, client, vrf_id);
       break;
+    case ZEBRA_PW_ADD:
+    case ZEBRA_PW_DELETE:
+    case ZEBRA_PW_SET:
+    case ZEBRA_PW_UNSET:
+      zread_pseudowire (command, client, length, vrf_id);
+      break;
     default:
       zlog_info ("Zebra received unknown command %d", command);
       break;
index cd1948373a8a63ce79d5245cdced0b9522dc69c0..42e762caa3985b3297552d38004c820c7b0ca4b7 100644 (file)
@@ -31,6 +31,8 @@
 #include "zclient.h"
 
 #include "zebra/zebra_ns.h"
+#include "zebra/zebra_pw.h"
+
 /* Default port information. */
 #define ZEBRA_VTY_PORT                2601
 
@@ -170,6 +172,7 @@ extern int zsend_interface_vrf_update (struct zserv *, struct interface *,
                                        vrf_id_t);
 
 extern int zsend_interface_link_params (struct zserv *, struct interface *);
+extern int zsend_pw_update (struct zserv *, struct zebra_pw *);
 
 extern pid_t pid;