summaryrefslogtreecommitdiff
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/Makefile.am7
-rw-r--r--zebra/debug.c26
-rw-r--r--zebra/debug.h4
-rw-r--r--zebra/main.c1
-rw-r--r--zebra/rib.h1
-rw-r--r--zebra/zebra_mpls.c4
-rw-r--r--zebra/zebra_mpls_openbsd.c100
-rw-r--r--zebra/zebra_pw.c532
-rw-r--r--zebra/zebra_pw.h75
-rw-r--r--zebra/zebra_pw_null.c29
-rw-r--r--zebra/zebra_rnh.c89
-rw-r--r--zebra/zebra_rnh.h3
-rw-r--r--zebra/zebra_vrf.c2
-rw-r--r--zebra/zebra_vrf.h5
-rw-r--r--zebra/zebra_vty.c7
-rw-r--r--zebra/zserv.c138
-rw-r--r--zebra/zserv.h3
17 files changed, 1008 insertions, 18 deletions
diff --git a/zebra/Makefile.am b/zebra/Makefile.am
index 3e0de3b463..9e9998ebbc 100644
--- a/zebra/Makefile.am
+++ b/zebra/Makefile.am
@@ -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)
diff --git a/zebra/debug.c b/zebra/debug.c
index a42d5aa3ef..6f59dc0ac2 100644
--- a/zebra/debug.c
+++ b/zebra/debug.c
@@ -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);
diff --git a/zebra/debug.h b/zebra/debug.h
index f8ebf3d616..9a196d2f34 100644
--- a/zebra/debug.h
+++ b/zebra/debug.h
@@ -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/main.c b/zebra/main.c
index 459e6148d8..84d71c33b9 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -314,6 +314,7 @@ main (int argc, char **argv)
zebra_mpls_init ();
zebra_mpls_vty_init ();
+ zebra_pw_vty_init ();
/* For debug purpose. */
/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
diff --git a/zebra/rib.h b/zebra/rib.h
index 5381d76b98..e910facb44 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -91,6 +91,7 @@ struct rib
#define RIB_ENTRY_NEXTHOPS_CHANGED 0x2
#define RIB_ENTRY_CHANGED 0x4
#define RIB_ENTRY_SELECTED_FIB 0x8
+#define RIB_ENTRY_LABELS_CHANGED 0x10
/* Nexthop information. */
u_char nexthop_num;
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 5a3ed7545d..ba500cac27 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -1346,7 +1346,7 @@ mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
return 0;
SET_FLAG (rib->status, RIB_ENTRY_CHANGED);
- SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED);
rib_queue_add (rn);
return 0;
@@ -1542,7 +1542,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi)
{
nexthop_del_labels (nexthop);
SET_FLAG (rib->status, RIB_ENTRY_CHANGED);
- SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED);
update = 1;
}
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index 5dfe16caf5..0fcd28e0ee 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -35,6 +35,7 @@ extern struct zebra_privs_t zserv_privs;
struct {
u_int32_t rtseq;
int fd;
+ int ioctl_fd;
} kr_state;
static int
@@ -337,6 +338,90 @@ kernel_del_lsp (zebra_lsp_t *lsp)
return ret;
}
+static int
+kmpw_install (struct zebra_pw *pw)
+{
+ struct ifreq ifr;
+ struct ifmpwreq imr;
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sa_in = (struct sockaddr_in *) &ss;
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) &ss;
+
+ memset (&imr, 0, sizeof (imr));
+ switch (pw->type)
+ {
+ case PW_TYPE_ETHERNET:
+ imr.imr_type = IMR_TYPE_ETHERNET;
+ break;
+ case PW_TYPE_ETHERNET_TAGGED:
+ imr.imr_type = IMR_TYPE_ETHERNET_TAGGED;
+ break;
+ default:
+ zlog_err ("%s: unhandled pseudowire type (%#X)", __func__,
+ pw->type);
+ return -1;
+ }
+
+ if (pw->flags & F_PSEUDOWIRE_CWORD)
+ imr.imr_flags |= IMR_FLAG_CONTROLWORD;
+
+ /* pseudowire nexthop */
+ memset (&ss, 0, sizeof (ss));
+ switch (pw->af) {
+ case AF_INET:
+ sa_in->sin_family = AF_INET;
+ sa_in->sin_len = sizeof (struct sockaddr_in);
+ sa_in->sin_addr = pw->nexthop.ipv4;
+ break;
+ case AF_INET6:
+ sa_in6->sin6_family = AF_INET6;
+ sa_in6->sin6_len = sizeof (struct sockaddr_in6);
+ sa_in6->sin6_addr = pw->nexthop.ipv6;
+ break;
+ default:
+ zlog_err ("%s: unhandled pseudowire address-family (%u)", __func__,
+ pw->af);
+ return -1;
+ }
+ memcpy (&imr.imr_nexthop, (struct sockaddr *) &ss,
+ sizeof (imr.imr_nexthop));
+
+ /* pseudowire local/remote labels */
+ imr.imr_lshim.shim_label = pw->local_label;
+ imr.imr_rshim.shim_label = pw->remote_label;
+
+ /* ioctl */
+ memset (&ifr, 0, sizeof (ifr));
+ strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_data = (caddr_t) &imr;
+ if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1)
+ {
+ zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+kmpw_uninstall (struct zebra_pw *pw)
+{
+ struct ifreq ifr;
+ struct ifmpwreq imr;
+
+ memset(&ifr, 0, sizeof (ifr));
+ memset(&imr, 0, sizeof (imr));
+ strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_data = (caddr_t) &imr;
+ if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1)
+ {
+ zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
#define MAX_RTSOCK_BUF 128 * 1024
int
mpls_kernel_init (void)
@@ -344,10 +429,17 @@ mpls_kernel_init (void)
int rcvbuf, default_rcvbuf;
socklen_t optlen;
- if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
+ if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
+ {
zlog_warn("%s: socket", __func__);
return -1;
- }
+ }
+
+ if ((kr_state.ioctl_fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
+ {
+ zlog_warn("%s: ioctl socket", __func__);
+ return -1;
+ }
/* grow receive buffer, don't wanna miss messages */
optlen = sizeof (default_rcvbuf);
@@ -364,5 +456,9 @@ mpls_kernel_init (void)
kr_state.rtseq = 1;
+ /* register hook to install/uninstall pseudowires */
+ hook_register (pw_install, kmpw_install);
+ hook_register (pw_uninstall, kmpw_uninstall);
+
return 0;
}
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
new file mode 100644
index 0000000000..2164ddf6ee
--- /dev/null
+++ b/zebra/zebra_pw.c
@@ -0,0 +1,532 @@
+/* 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 "command.h"
+#include "vrf.h"
+
+#include "zebra/debug.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/zebra_rnh.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_pw.h"
+
+DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire")
+
+DEFINE_QOBJ_TYPE(zebra_pw)
+
+DEFINE_HOOK(pw_install, (struct zebra_pw *pw), (pw))
+DEFINE_HOOK(pw_uninstall, (struct zebra_pw *pw), (pw))
+
+extern struct zebra_t zebrad;
+
+static int zebra_pw_enabled(struct zebra_pw *);
+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, pw_entry, zebra_pw_compare)
+RB_GENERATE(zebra_static_pw_head, zebra_pw, static_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;
+ pw->local_label = MPLS_NO_LABEL;
+ pw->remote_label = MPLS_NO_LABEL;
+ pw->flags = F_PSEUDOWIRE_CWORD;
+
+ RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw);
+ if (pw->protocol == ZEBRA_ROUTE_STATIC) {
+ RB_INSERT(zebra_static_pw_head, &zvrf->static_pseudowires, pw);
+ QOBJ_REG(pw, zebra_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));
+
+ /* remove nexthop tracking */
+ zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
+
+ /* 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);
+ if (pw->protocol == ZEBRA_ROUTE_STATIC)
+ RB_REMOVE(zebra_static_pw_head, &zvrf->static_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)
+{
+ zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
+
+ 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 (zebra_pw_enabled(pw))
+ zebra_register_rnh_pseudowire(pw->vrf_id, 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));
+}
+
+static int zebra_pw_enabled(struct zebra_pw *pw)
+{
+ if (pw->protocol == ZEBRA_ROUTE_STATIC) {
+ if (pw->local_label == MPLS_NO_LABEL
+ || pw->remote_label == MPLS_NO_LABEL
+ || pw->af == AF_UNSPEC)
+ return 0;
+ return 1;
+ } else
+ return pw->enabled;
+}
+
+void zebra_pw_update(struct zebra_pw *pw)
+{
+ if (zebra_pw_check_reachability(pw) < 0) {
+ zebra_pw_uninstall(pw);
+ /* wait for NHT and try again later */
+ } 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 (zebra_pw_enabled(pw))
+ 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);
+ RB_INIT(&zvrf->static_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);
+}
+
+DEFUN_NOSH (pseudowire_if,
+ pseudowire_if_cmd,
+ "[no] pseudowire IFNAME",
+ NO_STR
+ "Static pseudowire configuration\n"
+ "Pseudowire name\n")
+{
+ struct zebra_vrf *zvrf;
+ struct zebra_pw *pw;
+ int idx = 0;
+ const char *ifname;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (!zvrf)
+ return CMD_WARNING;
+
+ argv_find(argv, argc, "IFNAME", &idx);
+ ifname = argv[idx]->arg;
+ pw = zebra_pw_find(zvrf, ifname);
+ if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) {
+ vty_out(vty, "%% Pseudowire is not static%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "no", &idx)) {
+ if (!pw)
+ return CMD_SUCCESS;
+ zebra_pw_del(zvrf, pw);
+ }
+
+ if (!pw)
+ pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL);
+ VTY_PUSH_CONTEXT(PW_NODE, pw);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pseudowire_labels,
+ pseudowire_labels_cmd,
+ "[no] mpls label local (16-1048575) remote (16-1048575)",
+ NO_STR
+ "MPLS L2VPN PW command\n"
+ "MPLS L2VPN static labels\n"
+ "Local pseudowire label\n"
+ "Local pseudowire label\n"
+ "Remote pseudowire label\n"
+ "Remote pseudowire label\n")
+{
+ VTY_DECLVAR_CONTEXT(zebra_pw, pw);
+ int idx = 0;
+ mpls_label_t local_label, remote_label;
+
+ if (argv_find(argv, argc, "no", &idx)) {
+ local_label = MPLS_NO_LABEL;
+ remote_label = MPLS_NO_LABEL;
+ } else {
+ argv_find(argv, argc, "local", &idx);
+ local_label = atoi(argv[idx + 1]->arg);
+ argv_find(argv, argc, "remote", &idx);
+ remote_label = atoi(argv[idx + 1]->arg);
+ }
+
+ zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop,
+ local_label, remote_label, pw->flags, &pw->data);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pseudowire_neighbor,
+ pseudowire_neighbor_cmd,
+ "[no] neighbor <A.B.C.D|X:X::X:X>",
+ NO_STR
+ "Specify the IPv4 or IPv6 address of the remote endpoint\n"
+ "IPv4 address\n"
+ "IPv6 address\n")
+{
+ VTY_DECLVAR_CONTEXT(zebra_pw, pw);
+ int idx = 0;
+ const char *address;
+ int af;
+ union g_addr nexthop;
+
+ af = AF_UNSPEC;
+ memset(&nexthop, 0, sizeof(nexthop));
+
+ if (!argv_find(argv, argc, "no", &idx)) {
+ argv_find(argv, argc, "neighbor", &idx);
+ address = argv[idx + 1]->arg;
+
+ if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1)
+ af = AF_INET;
+ else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1)
+ af = AF_INET6;
+ else {
+ vty_out(vty, "%% Malformed address%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop,
+ pw->local_label, pw->remote_label, pw->flags,
+ &pw->data);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pseudowire_control_word,
+ pseudowire_control_word_cmd,
+ "[no] control-word <exclude|include>",
+ NO_STR
+ "Control-word options\n"
+ "Exclude control-word in pseudowire packets\n"
+ "Include control-word in pseudowire packets\n")
+{
+ VTY_DECLVAR_CONTEXT(zebra_pw, pw);
+ int idx = 0;
+ uint8_t flags = 0;
+
+ if (argv_find(argv, argc, "no", &idx))
+ flags = F_PSEUDOWIRE_CWORD;
+ else {
+ argv_find(argv, argc, "control-word", &idx);
+ if (argv[idx + 1]->text[0] == 'i')
+ flags = F_PSEUDOWIRE_CWORD;
+ }
+
+ zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop,
+ pw->local_label, pw->remote_label, flags, &pw->data);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_pseudowires,
+ show_pseudowires_cmd,
+ "show pseudowires",
+ SHOW_STR
+ "Pseudowires")
+{
+ struct zebra_vrf *zvrf;
+ struct zebra_pw *pw;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (!zvrf)
+ return 0;
+
+ vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s",
+ "Interface", "Neighbor", "Labels", "Protocol", "Status",
+ VTY_NEWLINE);
+
+ RB_FOREACH(pw, zebra_pw_head, &zvrf->pseudowires) {
+ char buf_nbr[INET6_ADDRSTRLEN];
+ char buf_labels[64];
+
+ inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
+
+ if (pw->local_label != MPLS_NO_LABEL
+ && pw->remote_label != MPLS_NO_LABEL)
+ snprintf(buf_labels, sizeof(buf_labels), "%u/%u",
+ pw->local_label, pw->remote_label);
+ else
+ snprintf(buf_labels, sizeof(buf_labels), "-");
+
+ vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s",
+ pw->ifname, (pw->af != AF_UNSPEC) ? buf_nbr : "-",
+ buf_labels, zebra_route_string(pw->protocol),
+ (zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) ?
+ "UP" : "DOWN", VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Pseudowire configuration write function. */
+static int zebra_pw_config(struct vty *vty)
+{
+ int write = 0;
+ struct zebra_vrf *zvrf;
+ struct zebra_pw *pw;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (!zvrf)
+ return 0;
+
+ RB_FOREACH(pw, zebra_static_pw_head, &zvrf->static_pseudowires) {
+ vty_out(vty, "pseudowire %s%s", pw->ifname, VTY_NEWLINE);
+ if (pw->local_label != MPLS_NO_LABEL
+ && pw->remote_label != MPLS_NO_LABEL)
+ vty_out(vty, " mpls label local %u remote %u%s",
+ pw->local_label, pw->remote_label,
+ VTY_NEWLINE);
+ else
+ vty_out(vty, " ! Incomplete config, specify the static "
+ "MPLS labels%s", VTY_NEWLINE);
+
+ if (pw->af != AF_UNSPEC) {
+ char buf[INET6_ADDRSTRLEN];
+ inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf));
+ vty_out(vty, " neighbor %s%s", buf, VTY_NEWLINE);
+ } else
+ vty_out(vty, " ! Incomplete config, specify a neighbor "
+ "address%s", VTY_NEWLINE);
+
+ if (!(pw->flags & F_PSEUDOWIRE_CWORD))
+ vty_out(vty, " control-word exclude%s", VTY_NEWLINE);
+
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ write = 1;
+ }
+
+ return write;
+}
+
+static struct cmd_node pw_node =
+{
+ PW_NODE,
+ "%s(config-pw)# ",
+ 1,
+};
+
+void zebra_pw_vty_init(void)
+{
+ install_node(&pw_node, zebra_pw_config);
+ install_default(PW_NODE);
+
+ install_element(CONFIG_NODE, &pseudowire_if_cmd);
+ install_element(PW_NODE, &pseudowire_labels_cmd);
+ install_element(PW_NODE, &pseudowire_neighbor_cmd);
+ install_element(PW_NODE, &pseudowire_control_word_cmd);
+
+ install_element(VIEW_NODE, &show_pseudowires_cmd);
+}
diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h
new file mode 100644
index 0000000000..b588bac0a2
--- /dev/null
+++ b/zebra/zebra_pw.h
@@ -0,0 +1,75 @@
+/* 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"
+#include "qobj.h"
+
+#define PW_INSTALL_RETRY_INTERVAL 30
+
+struct zebra_pw {
+ RB_ENTRY(zebra_pw) pw_entry, static_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 rnh *rnh;
+ struct thread *install_retry_timer;
+ QOBJ_FIELDS
+};
+DECLARE_QOBJ_TYPE(zebra_pw)
+
+RB_HEAD(zebra_pw_head, zebra_pw);
+RB_PROTOTYPE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare);
+
+RB_HEAD(zebra_static_pw_head, zebra_pw);
+RB_PROTOTYPE(zebra_static_pw_head, zebra_pw, static_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 *);
+void zebra_pw_vty_init(void);
+
+#endif /* ZEBRA_PW_H_ */
diff --git a/zebra/zebra_pw_null.c b/zebra/zebra_pw_null.c
new file mode 100644
index 0000000000..d19d80b8c9
--- /dev/null
+++ b/zebra/zebra_pw_null.c
@@ -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) {}
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index 5096e95395..d5ebbbc466 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -131,6 +131,7 @@ zebra_add_rnh (struct prefix *p, vrf_id_t vrfid, rnh_type_t type)
rnh->client_list = list_new();
rnh->vrf_id = vrfid;
rnh->zebra_static_route_list = list_new();
+ rnh->zebra_pseudowire_list = list_new();
route_lock_node (rn);
rn->info = rnh;
rnh->node = rn;
@@ -168,6 +169,7 @@ zebra_free_rnh (struct rnh *rnh)
rnh->flags |= ZEBRA_NHT_DELETED;
list_free (rnh->client_list);
list_free (rnh->zebra_static_route_list);
+ list_free (rnh->zebra_pseudowire_list);
free_state (rnh->vrf_id, rnh->state, rnh->node);
XFREE (MTYPE_RNH, rnh);
}
@@ -222,7 +224,8 @@ zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client, rnh_type_t type)
}
listnode_delete(rnh->client_list, client);
if (list_isempty(rnh->client_list) &&
- list_isempty(rnh->zebra_static_route_list))
+ list_isempty(rnh->zebra_static_route_list) &&
+ list_isempty(rnh->zebra_pseudowire_list))
zebra_delete_rnh(rnh, type);
}
@@ -252,7 +255,8 @@ zebra_deregister_rnh_static_nh(vrf_id_t vrf_id, struct prefix *nh,
listnode_delete(rnh->zebra_static_route_list, static_rn);
if (list_isempty(rnh->client_list) &&
- list_isempty(rnh->zebra_static_route_list))
+ list_isempty(rnh->zebra_static_route_list) &&
+ list_isempty(rnh->zebra_pseudowire_list))
zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE);
}
@@ -301,6 +305,62 @@ zebra_deregister_rnh_static_nexthops (vrf_id_t vrf_id, struct nexthop *nexthop,
}
}
+/* XXX move this utility function elsewhere? */
+static void
+addr2hostprefix (int af, const union g_addr *addr, struct prefix *prefix)
+{
+ switch (af)
+ {
+ case AF_INET:
+ prefix->family = AF_INET;
+ prefix->prefixlen = IPV4_MAX_BITLEN;
+ prefix->u.prefix4 = addr->ipv4;
+ break;
+ case AF_INET6:
+ prefix->family = AF_INET6;
+ prefix->prefixlen = IPV6_MAX_BITLEN;
+ prefix->u.prefix6 = addr->ipv6;
+ break;
+ default:
+ zlog_warn ("%s: unknown address family %d", __func__, af);
+ break;
+ }
+}
+
+void
+zebra_register_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw)
+{
+ struct prefix nh;
+ struct rnh *rnh;
+
+ addr2hostprefix (pw->af, &pw->nexthop, &nh);
+ rnh = zebra_add_rnh (&nh, vrf_id, RNH_NEXTHOP_TYPE);
+ if (rnh && !listnode_lookup (rnh->zebra_pseudowire_list, pw))
+ {
+ listnode_add (rnh->zebra_pseudowire_list, pw);
+ pw->rnh = rnh;
+ zebra_evaluate_rnh (vrf_id, pw->af, 1, RNH_NEXTHOP_TYPE, &nh);
+ }
+}
+
+void
+zebra_deregister_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw)
+{
+ struct rnh *rnh;
+
+ rnh = pw->rnh;
+ if (!rnh)
+ return;
+
+ listnode_delete (rnh->zebra_pseudowire_list, pw);
+ pw->rnh = NULL;
+
+ if (list_isempty (rnh->client_list) &&
+ list_isempty (rnh->zebra_static_route_list) &&
+ list_isempty (rnh->zebra_pseudowire_list))
+ zebra_delete_rnh (rnh, RNH_NEXTHOP_TYPE);
+}
+
/* Apply the NHT route-map for a client to the route (and nexthops)
* resolving a NH.
*/
@@ -611,6 +671,16 @@ zebra_rnh_process_static_routes (vrf_id_t vrfid, int family,
}
}
+static void
+zebra_rnh_process_pseudowires (vrf_id_t vrfid, struct rnh *rnh)
+{
+ struct zebra_pw *pw;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO (rnh->zebra_pseudowire_list, node, pw))
+ zebra_pw_update (pw);
+}
+
/*
* See if a tracked nexthop entry has undergone any change, and if so,
* take appropriate action; this involves notifying any clients and/or
@@ -655,6 +725,9 @@ zebra_rnh_eval_nexthop_entry (vrf_id_t vrfid, int family, int force,
/* Process static routes attached to this nexthop */
zebra_rnh_process_static_routes (vrfid, family, nrn, rnh,
prn, rnh->state);
+
+ /* Process pseudowires attached to this nexthop */
+ zebra_rnh_process_pseudowires (vrfid, rnh);
}
}
@@ -717,7 +790,10 @@ zebra_rnh_clear_nhc_flag (vrf_id_t vrfid, int family, rnh_type_t type,
rib = zebra_rnh_resolve_entry (vrfid, family, type, nrn, rnh, &prn);
if (rib)
- UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ {
+ UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ UNSET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED);
+ }
}
/* Evaluate all tracked entries (nexthops or routes for import into BGP)
@@ -868,7 +944,8 @@ compare_state (struct rib *r1, struct rib *r2)
if (r1->nexthop_num != r2->nexthop_num)
return 1;
- if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED))
+ if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED) ||
+ CHECK_FLAG(r1->status, RIB_ENTRY_LABELS_CHANGED))
return 1;
return 0;
@@ -1030,6 +1107,8 @@ print_rnh (struct route_node *rn, struct vty *vty)
vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto),
client->sock, rnh->filtered[client->proto] ? "(filtered)" : "");
if (!list_isempty(rnh->zebra_static_route_list))
- vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : "");
+ vty_out(vty, " zebra[static routes]%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : "");
+ if (!list_isempty(rnh->zebra_pseudowire_list))
+ vty_out(vty, " zebra[pseudowires]");
vty_out(vty, "%s", VTY_NEWLINE);
}
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index 4394fde4f3..9da5138e93 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -42,6 +42,7 @@ struct rnh
struct prefix resolved_route;
struct list *client_list;
struct list *zebra_static_route_list; /* static routes dependent on this NH */
+ struct list *zebra_pseudowire_list; /* pseudowires dependent on this NH */
struct route_node *node;
int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */
};
@@ -67,6 +68,8 @@ extern void zebra_register_rnh_static_nh(vrf_id_t, struct prefix *, struct route
extern void zebra_deregister_rnh_static_nexthops (vrf_id_t, struct nexthop *nexthop,
struct route_node *rn);
extern void zebra_deregister_rnh_static_nh(vrf_id_t, struct prefix *, struct route_node *);
+extern void zebra_register_rnh_pseudowire (vrf_id_t, struct zebra_pw *);
+extern void zebra_deregister_rnh_pseudowire (vrf_id_t, struct zebra_pw *);
extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
rnh_type_t type);
extern void zebra_evaluate_rnh(vrf_id_t vrfid, int family, int force, rnh_type_t type,
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index 889c57e6ff..06d87a468e 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -202,6 +202,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);
@@ -379,6 +380,7 @@ zebra_vrf_alloc (void)
}
zebra_mpls_init_tables (zvrf);
+ zebra_pw_init (zvrf);
return zvrf;
}
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index 8864f13052..cfe7cde75c 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -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,10 @@ struct zebra_vrf
/* MPLS label forwarding table */
struct hash *lsp_table;
+ /* Pseudowires. */
+ struct zebra_pw_head pseudowires;
+ struct zebra_static_pw_head static_pseudowires;
+
/* MPLS processing flags */
u_int16_t mpls_flags;
#define MPLS_FLAG_SCHEDULE_LSPS (1 << 0)
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index aec8fd1ddf..aeb01be070 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -923,6 +923,13 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib,
break;
}
+ if (nexthop->nh_label && nexthop->nh_label->num_labels)
+ {
+ json_object_string_add(json_nexthop, "labels",
+ mpls_label2str (nexthop->nh_label->num_labels,
+ nexthop->nh_label->label, buf, BUFSIZ));
+ }
+
json_object_array_add(json_nexthops, json_nexthop);
}
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 9beae9232e..fb02d60ba4 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -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
@@ -1762,16 +1784,14 @@ zread_mpls_labels (int command, struct zserv *client, u_short length,
{
mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate,
NULL, ifindex);
- if (out_label != MPLS_IMP_NULL_LABEL)
- mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex,
- distance, out_label);
+ mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex,
+ distance, out_label);
}
else if (command == ZEBRA_MPLS_LABELS_DELETE)
{
mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, NULL, ifindex);
- if (out_label != MPLS_IMP_NULL_LABEL)
- mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex,
- distance, out_label);
+ mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex,
+ distance, out_label);
}
}
/* Send response to a label manager connect request to client */
@@ -1933,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)
@@ -1972,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)
{
@@ -2269,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;
diff --git a/zebra/zserv.h b/zebra/zserv.h
index cd1948373a..42e762caa3 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -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;