From 6da80de983d313b6343e5a3e0a3b6a2bffbffbef Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=9Fingen?= Date: Mon, 15 May 2017 17:09:28 +0200 Subject: [PATCH] zebra: add pseudowire manager MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Renato Westphal --- ldpd/ldp.h | 3 - ldpd/ldpd.h | 1 + lib/Makefile.am | 1 + lib/log.c | 5 + lib/pw.h | 48 ++++++++ lib/zclient.c | 70 +++++++++++ lib/zclient.h | 38 ++++++ zebra/Makefile.am | 7 +- zebra/debug.c | 26 +++++ zebra/debug.h | 4 + zebra/zebra_pw.c | 266 ++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_pw.h | 67 +++++++++++ zebra/zebra_pw_null.c | 29 +++++ zebra/zebra_vrf.c | 2 + zebra/zebra_vrf.h | 4 + zebra/zserv.c | 128 ++++++++++++++++++++ zebra/zserv.h | 3 + 17 files changed, 696 insertions(+), 6 deletions(-) create mode 100644 lib/pw.h create mode 100644 zebra/zebra_pw.c create mode 100644 zebra/zebra_pw.h create mode 100644 zebra/zebra_pw_null.c diff --git a/ldpd/ldp.h b/ldpd/ldp.h index c2b64d20c..cac3da7c5 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -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 diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 97239ed08..21f5e16f8 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -29,6 +29,7 @@ #include "qobj.h" #include "prefix.h" #include "filter.h" +#include "pw.h" #include "ldp.h" diff --git a/lib/Makefile.am b/lib/Makefile.am index 14b7130c8..1f3180697 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -73,6 +73,7 @@ pkginclude_HEADERS = \ module.h \ hook.h \ libfrr.h \ + pw.h \ # end noinst_HEADERS = \ diff --git a/lib/log.c b/lib/log.c index 52d92de39..735ca71ed 100644 --- 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 index 000000000..2fba996ea --- /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 */ diff --git a/lib/zclient.c b/lib/zclient.c index 6aea4bd0a..9338f9c98 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -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; } diff --git a/lib/zclient.h b/lib/zclient.h index d3d0a202c..489ed24c8 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -31,6 +31,12 @@ /* 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 diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 3e0de3b46..9e9998ebb 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 a42d5aa3e..6f59dc0ac 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 [] [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 f8ebf3d61..9a196d2f3 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/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 000000000..b8ce1826d --- /dev/null +++ b/zebra/zebra_pw.c @@ -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 + +#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 index 000000000..9cd2b999a --- /dev/null +++ b/zebra/zebra_pw.h @@ -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 +#include + +#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 index 000000000..d19d80b8c --- /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 + +#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_vrf.c b/zebra/zebra_vrf.c index 6b3689105..1797ef080 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -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; } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 96d631d64..eab87e67d 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@ #define __ZEBRA_RIB_H__ #include +#include /* 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) diff --git a/zebra/zserv.c b/zebra/zserv.c index f2d32d779..fb02d60ba 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 @@ -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; diff --git a/zebra/zserv.h b/zebra/zserv.h index cd1948373..42e762caa 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; -- 2.39.5