From b557e0b6df1d076b51de7d357746388a5c969803 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 5 Jun 2017 22:48:10 -0300 Subject: [PATCH 1/9] zebra: add implicit-null labels to the rib Implicit-null labels are never installed in the FIB but we need to keep track of them because of L2/L3 VPN nexthop resolution. Signed-off-by: Renato Westphal --- zebra/zserv.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/zebra/zserv.c b/zebra/zserv.c index 9beae9232e..f2d32d7798 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1762,16 +1762,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 */ From 6da80de983d313b6343e5a3e0a3b6a2bffbffbef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Mon, 15 May 2017 17:09:28 +0200 Subject: [PATCH 2/9] zebra: add pseudowire manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 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 c2b64d20c6..cac3da7c55 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 97239ed08e..21f5e16f88 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 14b7130c8a..1f31806973 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 52d92de392..735ca71ed9 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 0000000000..2fba996eae --- /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 6aea4bd0a3..9338f9c986 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 d3d0a202c5..489ed24c86 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 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 [] [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/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 0000000000..b8ce1826d3 --- /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 0000000000..9cd2b999ac --- /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 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 + +#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 6b36891056..1797ef080c 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 96d631d646..eab87e67d6 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 f2d32d7798..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 @@ -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 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; From 8cbeaaa2286b856ec66bc353c43cad882262fff9 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 30 Jun 2017 12:26:04 -0300 Subject: [PATCH 3/9] zebra: add nexthop tracking for pseudowires If the remote end of a pseudowire becomes unreachable (no route or an unlabeled route), then it must be uninstalled. In the same way, when the remote end becomes reachable, the pseudowire must be installed. Signed-off-by: Renato Westphal --- zebra/zebra_pw.c | 9 +++++- zebra/zebra_pw.h | 1 + zebra/zebra_rnh.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-- zebra/zebra_rnh.h | 3 ++ 4 files changed, 90 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index b8ce1826d3..bb8682b52a 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -27,6 +27,7 @@ #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" @@ -78,6 +79,9 @@ void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *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); @@ -94,6 +98,8 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, 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; @@ -104,7 +110,7 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, pw->data = *data; if (pw->enabled) - zebra_pw_update(pw); + zebra_register_rnh_pseudowire(pw->vrf_id, pw); else zebra_pw_uninstall(pw); } @@ -120,6 +126,7 @@ 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 diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h index 9cd2b999ac..9cb6ab416f 100644 --- a/zebra/zebra_pw.h +++ b/zebra/zebra_pw.h @@ -43,6 +43,7 @@ struct zebra_pw { int status; uint8_t protocol; struct zserv *client; + struct rnh *rnh; struct thread *install_retry_timer; }; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 5096e95395..2a1f69d7b8 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); } } @@ -1030,6 +1103,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, From 8d05ef64d8842af9eb91df6c8c4de1e073f99b2e Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 8 Jun 2017 20:29:12 -0300 Subject: [PATCH 4/9] zebra: add new flag to detect nexthop label updates With the introduction of the pseudowire manager, the NHT tracking code needs to detect label updates as well. Create a specific nexthop flag for that. We can't reuse the RIB_ENTRY_NEXTHOPS_CHANGED flag for this porpose because this flag is always cleared and reevaluated in rib_process(), setting it outside that function is a nop. Signed-off-by: Renato Westphal --- zebra/rib.h | 1 + zebra/zebra_mpls.c | 4 ++-- zebra/zebra_rnh.c | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) 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_rnh.c b/zebra/zebra_rnh.c index 2a1f69d7b8..ff3b94a25b 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -790,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) @@ -941,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; From 49416a99475a76a1f9f591c567d774e41724564b Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 30 Jun 2017 12:19:39 -0300 Subject: [PATCH 5/9] ldpd: integrate with the pseudowire manager in zebra If we receive a notification from zebra indicating that the installation of a pseudowire has failed (e.g. no reachability), send a PW Status notification to the remote peer (or a Label Withdraw if the remote peer doesn't support the PW Status TLV). Signed-off-by: Renato Westphal --- ldpd/l2vpn.c | 65 +++++++++++++++++++++++++++++++++++++++-- ldpd/lde.c | 54 +++++++++++++--------------------- ldpd/lde.h | 1 + ldpd/lde_lib.c | 7 +++++ ldpd/ldp_zebra.c | 76 ++++++++++++++++++++++++++++++++++++++++++++---- ldpd/ldpd.c | 41 +++++++++++++++++--------- ldpd/ldpd.h | 32 +++++++++----------- lib/pw.h | 7 ++++- 8 files changed, 210 insertions(+), 73 deletions(-) diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 27948f5a1a..afb9528d80 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -234,6 +234,7 @@ void l2vpn_pw_init(struct l2vpn_pw *pw) { struct fec fec; + struct zapi_pw zpw; l2vpn_pw_reset(pw); @@ -241,16 +242,23 @@ l2vpn_pw_init(struct l2vpn_pw *pw) lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0, 0, (void *)pw); lde_kernel_update(&fec); + + pw2zpw(pw, &zpw); + lde_imsg_compose_parent(IMSG_KPW_ADD, 0, &zpw, sizeof(zpw)); } void l2vpn_pw_exit(struct l2vpn_pw *pw) { struct fec fec; + struct zapi_pw zpw; l2vpn_pw_fec(pw, &fec); lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0); lde_kernel_update(&fec); + + pw2zpw(pw, &zpw); + lde_imsg_compose_parent(IMSG_KPW_DELETE, 0, &zpw, sizeof(zpw)); } static void @@ -268,7 +276,8 @@ l2vpn_pw_reset(struct l2vpn_pw *pw) { pw->remote_group = 0; pw->remote_mtu = 0; - pw->remote_status = 0; + pw->local_status = PW_FORWARDING; + pw->remote_status = PW_NOT_FORWARDING; if (pw->flags & F_PW_CWORD_CONF) pw->flags |= F_PW_CWORD; @@ -474,6 +483,56 @@ l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm) } } +int +l2vpn_pw_status_update(struct zapi_pw_status *zpw) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw = NULL; + struct lde_nbr *ln; + struct fec fec; + uint32_t local_status; + + RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) { + pw = l2vpn_pw_find(l2vpn, zpw->ifname); + if (pw) + break; + } + if (!pw) { + log_warnx("%s: pseudowire %s not found", __func__, zpw->ifname); + return (1); + } + + if (zpw->status == PW_STATUS_UP) + local_status = PW_FORWARDING; + else + local_status = PW_NOT_FORWARDING; + + /* local status didn't change */ + if (pw->local_status == local_status) + return (0); + pw->local_status = local_status; + + /* notify remote peer about the status update */ + ln = lde_nbr_find_by_lsrid(pw->lsr_id); + if (ln == NULL) + return (0); + l2vpn_pw_fec(pw, &fec); + if (pw->flags & F_PW_STATUSTLV) + l2vpn_send_pw_status(ln, local_status, &fec); + else { + struct fec_node *fn; + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn) { + if (pw->local_status == PW_FORWARDING) + lde_send_labelmapping(ln, fn, 1); + else + lde_send_labelwithdraw(ln, fn, NULL, NULL); + } + } + + return (0); +} + void l2vpn_pw_ctl(pid_t pid) { @@ -490,7 +549,9 @@ l2vpn_pw_ctl(pid_t pid) sizeof(pwctl.ifname)); pwctl.pwid = pw->pwid; pwctl.lsr_id = pw->lsr_id; - pwctl.status = pw->flags & F_PW_STATUS_UP; + if (pw->local_status == PW_FORWARDING && + pw->remote_status == PW_FORWARDING) + pwctl.status = 1; lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0, pid, &pwctl, sizeof(pwctl)); diff --git a/ldpd/lde.c b/ldpd/lde.c index 7540fc1cb4..a47351875b 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -491,6 +491,15 @@ lde_dispatch_parent(struct thread *thread) } } break; + case IMSG_PW_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct zapi_pw_status)) + fatalx("PW_UPDATE imsg with wrong len"); + + if (l2vpn_pw_status_update(imsg.data) != 0) + log_warnx("%s: error updating PW status", + __func__); + break; case IMSG_NETWORK_ADD: case IMSG_NETWORK_UPDATE: if (imsg.hdr.len != IMSG_HEADER_SIZE + @@ -730,8 +739,8 @@ lde_update_label(struct fec_node *fn) void lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) { - struct kroute kr; - struct kpw kpw; + struct kroute kr; + struct zapi_pw zpw; struct l2vpn_pw *pw; switch (fn->fec.type) { @@ -769,19 +778,10 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) return; pw = (struct l2vpn_pw *) fn->data; - pw->flags |= F_PW_STATUS_UP; - - memset(&kpw, 0, sizeof(kpw)); - kpw.ifindex = pw->ifindex; - kpw.pw_type = fn->fec.u.pwid.type; - kpw.af = pw->af; - kpw.nexthop = pw->addr; - kpw.local_label = fn->local_label; - kpw.remote_label = fnh->remote_label; - kpw.flags = pw->flags; - - lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw, - sizeof(kpw)); + pw2zpw(pw, &zpw); + zpw.local_label = fn->local_label; + zpw.remote_label = fnh->remote_label; + lde_imsg_compose_parent(IMSG_KPW_SET, 0, &zpw, sizeof(zpw)); break; } } @@ -790,7 +790,7 @@ void lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) { struct kroute kr; - struct kpw kpw; + struct zapi_pw zpw; struct l2vpn_pw *pw; switch (fn->fec.type) { @@ -824,21 +824,10 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; - if (!(pw->flags & F_PW_STATUS_UP)) - return; - pw->flags &= ~F_PW_STATUS_UP; - - memset(&kpw, 0, sizeof(kpw)); - kpw.ifindex = pw->ifindex; - kpw.pw_type = fn->fec.u.pwid.type; - kpw.af = pw->af; - kpw.nexthop = pw->addr; - kpw.local_label = fn->local_label; - kpw.remote_label = fnh->remote_label; - kpw.flags = pw->flags; - - lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw, - sizeof(kpw)); + pw2zpw(pw, &zpw); + zpw.local_label = fn->local_label; + zpw.remote_label = fnh->remote_label; + lde_imsg_compose_parent(IMSG_KPW_UNSET, 0, &zpw, sizeof(zpw)); break; } } @@ -966,8 +955,7 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) map.flags |= F_MAP_PW_CWORD; if (pw->flags & F_PW_STATUSTLV) { map.flags |= F_MAP_PW_STATUS; - /* VPLS are always up */ - map.pw_status = PW_FORWARDING; + map.pw_status = pw->local_status; } break; } diff --git a/ldpd/lde.h b/ldpd/lde.h index 1cce483832..43f1d36481 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -238,6 +238,7 @@ void l2vpn_send_pw_status_wcard(struct lde_nbr *, uint32_t, void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *); void l2vpn_recv_pw_status_wcard(struct lde_nbr *, struct notify_msg *); +int l2vpn_pw_status_update(struct zapi_pw_status *); void l2vpn_pw_ctl(pid_t); void l2vpn_binding_ctl(pid_t); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 37a670bc8c..2149945943 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -531,6 +531,8 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) pw->remote_mtu = map->fec.pwid.ifmtu; if (map->flags & F_MAP_PW_STATUS) pw->remote_status = map->pw_status; + else + pw->remote_status = PW_FORWARDING; fnh->remote_label = map->label; if (l2vpn_pw_ok(pw, fnh)) lde_send_change_klabel(fn, fnh); @@ -780,6 +782,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) pw = (struct l2vpn_pw *) fn->data; if (pw == NULL) continue; + pw->remote_status = PW_NOT_FORWARDING; break; default: break; @@ -808,6 +811,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) struct fec_node *fn; struct fec_nh *fnh; struct lde_map *me; + struct l2vpn_pw *pw; /* LWd.2: send label release */ lde_send_labelrelease(ln, NULL, map, map->label); @@ -831,6 +835,9 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) case FEC_TYPE_PWID: if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr) continue; + pw = (struct l2vpn_pw *) fn->data; + if (pw) + pw->remote_status = PW_NOT_FORWARDING; break; default: break; diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 3320238a05..f7d715e81c 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -54,6 +54,8 @@ static int ldp_interface_address_delete(int, struct zclient *, zebra_size_t, vrf_id_t); static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t, vrf_id_t); +static int ldp_zebra_read_pw_status_update(int, struct zclient *, + zebra_size_t, vrf_id_t); static void ldp_zebra_connected(struct zclient *); static struct zclient *zclient; @@ -94,6 +96,25 @@ ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka) } } +void +pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) +{ + memset(zpw, 0, sizeof(*zpw)); + strlcpy(zpw->ifname, pw->ifname, sizeof(zpw->ifname)); + zpw->ifindex = pw->ifindex; + zpw->type = pw->l2vpn->pw_type; + zpw->af = pw->af; + zpw->nexthop.ipv6 = pw->addr.v6; + zpw->local_label = NO_LABEL; + zpw->remote_label = NO_LABEL; + if (pw->flags & F_PW_CWORD) + zpw->flags = F_PSEUDOWIRE_CWORD; + zpw->data.ldp.lsr_id = pw->lsr_id; + zpw->data.ldp.pwid = pw->pwid; + strlcpy(zpw->data.ldp.vpn_name, pw->l2vpn->name, + sizeof(zpw->data.ldp.vpn_name)); +} + static int zebra_send_mpls_labels(int cmd, struct kroute *kr) { @@ -154,17 +175,40 @@ kr_delete(struct kroute *kr) } int -kmpw_set(struct kpw *kpw) +kmpw_add(struct zapi_pw *zpw) { - /* TODO */ - return (0); + debug_zebra_out("pseudowire %s nexthop %s (add)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + + return (zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw)); } int -kmpw_unset(struct kpw *kpw) +kmpw_del(struct zapi_pw *zpw) { - /* TODO */ - return (0); + debug_zebra_out("pseudowire %s nexthop %s (del)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + + return (zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw)); +} + +int +kmpw_set(struct zapi_pw *zpw) +{ + debug_zebra_out("pseudowire %s nexthop %s labels %u/%u (set)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop), + zpw->local_label, zpw->remote_label); + + return (zebra_send_pw(zclient, ZEBRA_PW_SET, zpw)); +} + +int +kmpw_unset(struct zapi_pw *zpw) +{ + debug_zebra_out("pseudowire %s nexthop %s (unset)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + + return (zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw)); } void @@ -466,6 +510,25 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, return (0); } +/* + * Receive PW status update from Zebra and send it to LDE process. + */ +static int +ldp_zebra_read_pw_status_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct zapi_pw_status zpw; + + zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw); + + debug_zebra_in("pseudowire %s status %s", zpw.ifname, + (zpw.status == PW_STATUS_UP) ? "up" : "down"); + + main_imsg_compose_lde(IMSG_PW_UPDATE, 0, &zpw, sizeof(zpw)); + + return (0); +} + static void ldp_zebra_connected(struct zclient *zclient) { @@ -496,6 +559,7 @@ ldp_zebra_init(struct thread_master *master) zclient->redistribute_route_ipv4_del = ldp_zebra_read_route; zclient->redistribute_route_ipv6_add = ldp_zebra_read_route; zclient->redistribute_route_ipv6_del = ldp_zebra_read_route; + zclient->pw_status_update = ldp_zebra_read_pw_status_update; } void diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index f9e44012ef..303baf463b 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -591,21 +591,36 @@ main_dispatch_lde(struct thread *thread) if (kr_delete(imsg.data)) log_warnx("%s: error deleting route", __func__); break; - case IMSG_KPWLABEL_CHANGE: + case IMSG_KPW_ADD: + case IMSG_KPW_DELETE: + case IMSG_KPW_SET: + case IMSG_KPW_UNSET: if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct kpw)) + sizeof(struct zapi_pw)) fatalx("invalid size of IMSG_KPWLABEL_CHANGE"); - if (kmpw_set(imsg.data)) - log_warnx("%s: error changing pseudowire", - __func__); - break; - case IMSG_KPWLABEL_DELETE: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct kpw)) - fatalx("invalid size of IMSG_KPWLABEL_DELETE"); - if (kmpw_unset(imsg.data)) - log_warnx("%s: error unsetting pseudowire", - __func__); + + switch (imsg.hdr.type) { + case IMSG_KPW_ADD: + if (kmpw_add(imsg.data)) + log_warnx("%s: error adding " + "pseudowire", __func__); + break; + case IMSG_KPW_DELETE: + if (kmpw_del(imsg.data)) + log_warnx("%s: error deleting " + "pseudowire", __func__); + break; + case IMSG_KPW_SET: + if (kmpw_set(imsg.data)) + log_warnx("%s: error setting " + "pseudowire", __func__); + break; + case IMSG_KPW_UNSET: + if (kmpw_unset(imsg.data)) + log_warnx("%s: error unsetting " + "pseudowire", __func__); + break; + } break; case IMSG_ACL_CHECK: if (imsg.hdr.len != IMSG_HEADER_SIZE + diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 21f5e16f88..fd7d5c5729 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -30,6 +30,7 @@ #include "prefix.h" #include "filter.h" #include "pw.h" +#include "zclient.h" #include "ldp.h" @@ -44,7 +45,6 @@ #define LDPD_OPT_NOACTION 0x00000004 #define TCP_MD5_KEY_LEN 80 -#define L2VPN_NAME_LEN 32 #define RT_BUF_SIZE 16384 #define MAX_RTSOCK_BUF 128 * 1024 @@ -102,8 +102,10 @@ enum imsg_type { IMSG_CTL_LOG_VERBOSE, IMSG_KLABEL_CHANGE, IMSG_KLABEL_DELETE, - IMSG_KPWLABEL_CHANGE, - IMSG_KPWLABEL_DELETE, + IMSG_KPW_ADD, + IMSG_KPW_DELETE, + IMSG_KPW_SET, + IMSG_KPW_UNSET, IMSG_IFSTATUS, IMSG_NEWADDR, IMSG_DELADDR, @@ -149,7 +151,8 @@ enum imsg_type { IMSG_ACL_CHECK, IMSG_GET_LABEL_CHUNK, IMSG_RELEASE_LABEL_CHUNK, - IMSG_INIT + IMSG_INIT, + IMSG_PW_UPDATE }; struct ldpd_init { @@ -409,6 +412,7 @@ struct l2vpn_pw { unsigned int ifindex; uint32_t remote_group; uint16_t remote_mtu; + uint32_t local_status; uint32_t remote_status; uint8_t flags; QOBJ_FIELDS @@ -420,8 +424,7 @@ DECLARE_QOBJ_TYPE(l2vpn_pw) #define F_PW_STATUSTLV 0x02 /* status tlv negotiated */ #define F_PW_CWORD_CONF 0x04 /* control word configured */ #define F_PW_CWORD 0x08 /* control word negotiated */ -#define F_PW_STATUS_UP 0x10 /* pseudowire is operational */ -#define F_PW_STATIC_NBR_ADDR 0x20 /* static neighbor address configured */ +#define F_PW_STATIC_NBR_ADDR 0x10 /* static neighbor address configured */ struct l2vpn { RB_ENTRY(l2vpn) entry; @@ -545,16 +548,6 @@ struct kroute { uint16_t flags; }; -struct kpw { - unsigned short ifindex; - int pw_type; - int af; - union ldpd_addr nexthop; - uint32_t local_label; - uint32_t remote_label; - uint8_t flags; -}; - struct kaddr { char ifname[IF_NAMESIZE]; unsigned short ifindex; @@ -671,11 +664,14 @@ struct ldpd_conf *parse_config(char *); int cmdline_symset(char *); /* kroute.c */ +void pw2zpw(struct l2vpn_pw *, struct zapi_pw *); void kif_redistribute(const char *); int kr_change(struct kroute *); int kr_delete(struct kroute *); -int kmpw_set(struct kpw *); -int kmpw_unset(struct kpw *); +int kmpw_add(struct zapi_pw *); +int kmpw_del(struct zapi_pw *); +int kmpw_set(struct zapi_pw *); +int kmpw_unset(struct zapi_pw *); /* util.c */ uint8_t mask2prefixlen(in_addr_t); diff --git a/lib/pw.h b/lib/pw.h index 2fba996eae..63d8e52ddd 100644 --- a/lib/pw.h +++ b/lib/pw.h @@ -20,6 +20,9 @@ #ifndef _FRR_PW_H #define _FRR_PW_H +/* L2VPN name length. */ +#define L2VPN_NAME_LEN 32 + /* Pseudowire type - LDP and BGP use the same values. */ #define PW_TYPE_ETHERNET_TAGGED 0x0004 /* RFC 4446 */ #define PW_TYPE_ETHERNET 0x0005 /* RFC 4446 */ @@ -38,7 +41,9 @@ union pw_protocol_fields { struct { - /* TODO */ + struct in_addr lsr_id; + uint32_t pwid; + char vpn_name[L2VPN_NAME_LEN]; } ldp; struct { /* TODO */ From a9389c97582d562950ef08a44b1eb9275838de32 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 30 Jun 2017 12:18:21 -0300 Subject: [PATCH 6/9] zebra: add support to openbsd's mpw(4) for pseudowires Thanks to rzalamena@ for writing mpw(4) :) Signed-off-by: Renato Westphal --- zebra/zebra_mpls_openbsd.c | 100 ++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) 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; } From d807f1c3fbbcdf18c854031409610dd957448dc2 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 6 Jul 2017 00:46:34 -0300 Subject: [PATCH 7/9] ldpd: fix missing label mappings In some scenarios, it's possible to send a Label Withdraw to a neighbor and not receive a corresponding Label Release right away. This can happen during reconvergence after a network failure or configuration change. When this happens, the list of upstream mappings of a given FEC might not be empty even after sending a Label Withdraw to all neighbors. This situation holds until all neighbors either send a Label Release or are torn down (e.g. keepalive timeout). With that said, we shouldn't check for 'RB_EMPTY(&fn->upstream)' in lde_kernel_update() because it can prevent ldpd from sending label mappings in such circumstances. This check was introduced to avoid sending the same label mapping more than once to the same neighbor, but we need to remove this optimization for now until we find a better solution (which probably involves refactoring the whole zebra<->ldpd communication). While here, add a new debug message in lde_send_labelmapping() which can aid in troubleshooting label problems in the future. Signed-off-by: Renato Westphal --- ldpd/lde.c | 6 +++++- ldpd/lde_lib.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ldpd/lde.c b/ldpd/lde.c index a47351875b..37d64eaf50 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -908,8 +908,12 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) */ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); if (lw) { - if (!fec_find(&ln->sent_map_pending, &fn->fec)) + if (!fec_find(&ln->sent_map_pending, &fn->fec)) { + debug_evt("%s: FEC %s: scheduling to send label " + "mapping later (waiting for pending label release)", + __func__, log_fec(&fn->fec)); lde_map_pending_add(ln, fn); + } return; } diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 2149945943..c24a57b56a 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -396,7 +396,7 @@ lde_kernel_update(struct fec *fec) lde_gc_start_timer(); } else { fn->local_label = lde_update_label(fn); - if (fn->local_label != NO_LABEL && RB_EMPTY(&fn->upstream)) + if (fn->local_label != NO_LABEL) /* FEC.1: perform lsr label distribution procedure */ RB_FOREACH(ln, nbr_tree, &lde_nbrs) lde_send_labelmapping(ln, fn, 1); From dc4ccdd901047789f1a7075860d4874de1c12549 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 25 Jul 2017 01:08:46 -0300 Subject: [PATCH 8/9] zebra: add label information to json output Signed-off-by: Renato Westphal --- zebra/zebra_vty.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index a8bee3cf50..180ecd64a5 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -932,6 +932,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); } From 1a99ca998dd05c41d4068ace4ca3ef8ac4ba309c Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 1 Aug 2017 20:16:28 -0300 Subject: [PATCH 9/9] zebra: add support for static pseudowires Signed-off-by: Renato Westphal --- lib/command.c | 2 + lib/command.h | 1 + lib/vty.c | 2 + vtysh/Makefile.am | 1 + vtysh/vtysh.c | 25 ++++ vtysh/vtysh_config.c | 2 + zebra/main.c | 1 + zebra/zebra_pw.c | 269 ++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_pw.h | 13 ++- zebra/zebra_rnh.c | 2 +- zebra/zebra_vrf.h | 1 + 11 files changed, 310 insertions(+), 9 deletions(-) diff --git a/lib/command.c b/lib/command.c index 4912461adb..c2ee79035a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1392,6 +1392,7 @@ cmd_exit (struct vty *vty) vty_config_unlock (vty); break; case INTERFACE_NODE: + case PW_NODE: case NS_NODE: case VRF_NODE: case ZEBRA_NODE: @@ -1471,6 +1472,7 @@ DEFUN (config_end, break; case CONFIG_NODE: case INTERFACE_NODE: + case PW_NODE: case NS_NODE: case VRF_NODE: case ZEBRA_NODE: diff --git a/lib/command.h b/lib/command.h index 223f028144..313d73f7c8 100644 --- a/lib/command.h +++ b/lib/command.h @@ -130,6 +130,7 @@ enum node_type FORWARDING_NODE, /* IP forwarding node. */ PROTOCOL_NODE, /* protocol filtering node */ MPLS_NODE, /* MPLS config node */ + PW_NODE, /* Pseudowire config node */ VTY_NODE, /* Vty node. */ LINK_PARAMS_NODE, /* Link-parameters node */ }; diff --git a/lib/vty.c b/lib/vty.c index e7e0f17e67..9b4badb096 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -731,6 +731,7 @@ vty_end_config (struct vty *vty) break; case CONFIG_NODE: case INTERFACE_NODE: + case PW_NODE: case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: @@ -1156,6 +1157,7 @@ vty_stop_input (struct vty *vty) break; case CONFIG_NODE: case INTERFACE_NODE: + case PW_NODE: case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 9a5008f9d7..2e928657f4 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -127,6 +127,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/zebra/zebra_fpm.c \ $(top_srcdir)/zebra/zebra_ptm.c \ $(top_srcdir)/zebra/zebra_mpls_vty.c \ + $(top_srcdir)/zebra/zebra_pw.c \ $(top_srcdir)/watchfrr/watchfrr_vty.c \ $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 17b95707d6..84907fb63d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -890,6 +890,12 @@ static struct cmd_node interface_node = "%s(config-if)# ", }; +static struct cmd_node pw_node = +{ + PW_NODE, + "%s(config-pw)# ", +}; + static struct cmd_node ns_node = { NS_NODE, @@ -1489,6 +1495,7 @@ vtysh_exit (struct vty *vty) vty->node = ENABLE_NODE; break; case INTERFACE_NODE: + case PW_NODE: case NS_NODE: case VRF_NODE: case ZEBRA_NODE: @@ -1776,6 +1783,17 @@ DEFUNSH (VTYSH_INTERFACE, return CMD_SUCCESS; } +DEFUNSH (VTYSH_ZEBRA, + vtysh_pseudowire, + vtysh_pseudowire_cmd, + "pseudowire IFNAME", + "Static pseudowire configuration\n" + "Pseudowire name\n") +{ + vty->node = PW_NODE; + return CMD_SUCCESS; +} + /* TODO Implement "no interface command in isisd. */ DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D, vtysh_no_interface_cmd, @@ -3094,6 +3112,7 @@ vtysh_init_vty (void) install_node (&bgp_node, NULL); install_node (&rip_node, NULL); install_node (&interface_node, NULL); + install_node (&pw_node, NULL); install_node (&link_params_node, NULL); install_node (&ns_node, NULL); install_node (&vrf_node, NULL); @@ -3130,6 +3149,7 @@ vtysh_init_vty (void) vtysh_install_default (BGP_NODE); vtysh_install_default (RIP_NODE); vtysh_install_default (INTERFACE_NODE); + vtysh_install_default (PW_NODE); vtysh_install_default (LINK_PARAMS_NODE); vtysh_install_default (NS_NODE); vtysh_install_default (VRF_NODE); @@ -3273,6 +3293,10 @@ vtysh_init_vty (void) install_element (LINK_PARAMS_NODE, &vtysh_exit_interface_cmd); install_element (INTERFACE_NODE, &vtysh_quit_interface_cmd); + install_element (PW_NODE, &vtysh_end_all_cmd); + install_element (PW_NODE, &vtysh_exit_interface_cmd); + install_element (PW_NODE, &vtysh_quit_interface_cmd); + install_element (NS_NODE, &vtysh_end_all_cmd); install_element (CONFIG_NODE, &vtysh_ns_cmd); @@ -3335,6 +3359,7 @@ vtysh_init_vty (void) install_element (CONFIG_NODE, &vtysh_interface_cmd); install_element (CONFIG_NODE, &vtysh_no_interface_cmd); install_element (CONFIG_NODE, &vtysh_no_interface_vrf_cmd); + install_element (CONFIG_NODE, &vtysh_pseudowire_cmd); install_element (INTERFACE_NODE, &vtysh_link_params_cmd); install_element (ENABLE_NODE, &vtysh_show_running_config_cmd); install_element (ENABLE_NODE, &vtysh_copy_running_config_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 94c4042dd4..4e4c241123 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -196,6 +196,8 @@ vtysh_config_parse_line (const char *line) default: if (strncmp (line, "interface", strlen ("interface")) == 0) config = config_get (INTERFACE_NODE, line); + else if (strncmp (line, "pseudowire", strlen ("pseudowire")) == 0) + config = config_get (PW_NODE, line); else if (strncmp (line, "logical-router", strlen ("ns")) == 0) config = config_get (NS_NODE, line); else if (strncmp (line, "vrf", strlen ("vrf")) == 0) 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/zebra_pw.c b/zebra/zebra_pw.c index bb8682b52a..2164ddf6ee 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -22,6 +22,7 @@ #include "log.h" #include "memory.h" #include "thread.h" +#include "command.h" #include "vrf.h" #include "zebra/debug.h" @@ -33,11 +34,14 @@ 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 *); @@ -45,12 +49,13 @@ 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) + const struct zebra_pw *b) { return (strcmp(a->ifname, b->ifname)); } -RB_GENERATE(zebra_pw_head, zebra_pw, entry, zebra_pw_compare) +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) @@ -67,8 +72,15 @@ struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, 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; } @@ -90,6 +102,8 @@ void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) /* 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); } @@ -109,7 +123,7 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, pw->flags = flags; pw->data = *data; - if (pw->enabled) + if (zebra_pw_enabled(pw)) zebra_register_rnh_pseudowire(pw->vrf_id, pw); else zebra_pw_uninstall(pw); @@ -122,6 +136,18 @@ struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *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) { @@ -165,7 +191,7 @@ static void zebra_pw_uninstall(struct zebra_pw *pw) /* ignore any possible error */ hook_call(pw_uninstall, pw); - if (pw->enabled) + if (zebra_pw_enabled(pw)) zebra_pw_update_status(pw, PW_STATUS_DOWN); } @@ -234,7 +260,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) if (!nexthop->nh_label) { if (IS_ZEBRA_DEBUG_PW) zlog_warn("%s: unlabeled route for %s", - __func__, pw->ifname); + __func__, pw->ifname); return -1; } } @@ -262,6 +288,7 @@ zebra_pw_client_close(struct zserv *client) 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) @@ -271,3 +298,235 @@ void zebra_pw_exit(struct zebra_vrf *zvrf) 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 ", + 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 ", + 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 index 9cb6ab416f..b588bac0a2 100644 --- a/zebra/zebra_pw.h +++ b/zebra/zebra_pw.h @@ -24,11 +24,12 @@ #include #include "hook.h" +#include "qobj.h" #define PW_INSTALL_RETRY_INTERVAL 30 struct zebra_pw { - RB_ENTRY(zebra_pw) entry; + RB_ENTRY(zebra_pw) pw_entry, static_pw_entry; vrf_id_t vrf_id; char ifname[IF_NAMESIZE]; ifindex_t ifindex; @@ -45,10 +46,15 @@ struct zebra_pw { 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, entry, zebra_pw_compare); +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)) @@ -64,5 +70,6 @@ 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_ */ +#endif /* ZEBRA_PW_H_ */ diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index ff3b94a25b..d5ebbbc466 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -348,7 +348,7 @@ zebra_deregister_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) { struct rnh *rnh; - rnh =pw->rnh; + rnh = pw->rnh; if (!rnh) return; diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index eab87e67d6..56c98931f2 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -82,6 +82,7 @@ struct zebra_vrf /* Pseudowires. */ struct zebra_pw_head pseudowires; + struct zebra_static_pw_head static_pseudowires; /* MPLS processing flags */ u_int16_t mpls_flags;