diff options
author | ßingen <bingen@voltanet.io> | 2017-05-15 17:09:28 +0200 |
---|---|---|
committer | David Lamparter <equinox@opensourcerouting.org> | 2017-08-09 12:35:15 +0200 |
commit | 6833ae01bc8cb0c15579d7098f2d525a6e36c8bb (patch) | |
tree | f9495c40c9edc6fb7f68bf91a07e56eab003db6e | |
parent | zebra: add implicit-null labels to the rib (diff) | |
download | frr-6833ae01bc8cb0c15579d7098f2d525a6e36c8bb.tar.xz frr-6833ae01bc8cb0c15579d7098f2d525a6e36c8bb.zip |
zebra: add pseudowire manager
Base framework for supporting MPLS pseudowires in FRR.
A consistent zserv interface is provided so that any client daemon
(e.g. ldpd, bgpd) can install/uninstall pseudowires in a standard
way. Static pseudowires can also be implemented by using the same
interface.
When zebra receives a request to install a pseudowire and the installation
in the kernel or hardware fails, a notification is sent back to the
client daemon and a new install attempt is made every 60 seconds (until
it succeeds).
Support for external dataplanes is provided by the use of hooks to
install/uninstall pseudowires.
Signed-off-by: ßingen <bingen@voltanet.io>
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
-rw-r--r-- | ldpd/ldp.h | 3 | ||||
-rw-r--r-- | ldpd/ldpd.h | 1 | ||||
-rw-r--r-- | lib/log.c | 5 | ||||
-rw-r--r-- | lib/pw.h | 47 | ||||
-rw-r--r-- | lib/subdir.am | 1 | ||||
-rw-r--r-- | lib/zclient.c | 68 | ||||
-rw-r--r-- | lib/zclient.h | 37 | ||||
-rw-r--r-- | zebra/debug.c | 25 | ||||
-rw-r--r-- | zebra/debug.h | 4 | ||||
-rw-r--r-- | zebra/subdir.am | 2 | ||||
-rw-r--r-- | zebra/zebra_pw.c | 268 | ||||
-rw-r--r-- | zebra/zebra_pw.h | 67 | ||||
-rw-r--r-- | zebra/zebra_vrf.c | 2 | ||||
-rw-r--r-- | zebra/zebra_vrf.h | 4 | ||||
-rw-r--r-- | zebra/zserv.c | 121 | ||||
-rw-r--r-- | zebra/zserv.h | 3 |
16 files changed, 655 insertions, 3 deletions
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 00d2627f1..c09f62e63 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -30,6 +30,7 @@ #include "prefix.h" #include "filter.h" #include "vty.h" +#include "pw.h" #include "ldp.h" @@ -916,6 +916,11 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_MACIP_DEL), DESC_ENTRY(ZEBRA_REMOTE_MACIP_ADD), DESC_ENTRY(ZEBRA_REMOTE_MACIP_DEL), + 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..b34f681ac --- /dev/null +++ b/lib/pw.h @@ -0,0 +1,47 @@ +/* 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/subdir.am b/lib/subdir.am index 28a4ce557..15ce8ec19 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -123,6 +123,7 @@ pkginclude_HEADERS += \ lib/prefix.h \ lib/privs.h \ lib/ptm_lib.h \ + lib/pw.h \ lib/qobj.h \ lib/route_types.h \ lib/routemap.h \ diff --git a/lib/zclient.c b/lib/zclient.c index a6252af40..893d769e3 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1834,6 +1834,69 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, 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) { @@ -2061,6 +2124,11 @@ static int zclient_read(struct thread *thread) (*zclient->local_macip_del)(command, zclient, length, vrf_id); 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 435d26e2f..8a2729543 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -30,6 +30,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 @@ -106,6 +112,11 @@ typedef enum { ZEBRA_MACIP_DEL, ZEBRA_REMOTE_MACIP_ADD, ZEBRA_REMOTE_MACIP_DEL, + ZEBRA_PW_ADD, + ZEBRA_PW_DELETE, + ZEBRA_PW_SET, + ZEBRA_PW_UNSET, + ZEBRA_PW_STATUS_UPDATE, } zebra_message_types_t; struct redist_proto { @@ -187,6 +198,7 @@ struct zclient { int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_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. */ @@ -266,6 +278,25 @@ 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); @@ -338,6 +369,12 @@ extern int lm_get_label_chunk(struct zclient *zclient, u_char keep, 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/debug.c b/zebra/debug.c index 6aedea1e3..6728ed132 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -32,6 +32,7 @@ unsigned long zebra_debug_fpm; unsigned long zebra_debug_nht; unsigned long zebra_debug_mpls; unsigned long zebra_debug_vxlan; +unsigned long zebra_debug_pw; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, @@ -82,6 +83,8 @@ DEFUN (show_debugging_zebra, vty_out(vty, " Zebra next-hop tracking debugging is on\n"); if (IS_ZEBRA_DEBUG_MPLS) vty_out(vty, " Zebra MPLS debugging is on\n"); + if (IS_ZEBRA_DEBUG_PW) + vty_out(vty, " Zebra pseudowire debugging is on\n"); return CMD_SUCCESS; } @@ -130,6 +133,21 @@ DEFUN (debug_zebra_vxlan, return CMD_WARNING; } +DEFUN (debug_zebra_pw, + debug_zebra_pw_cmd, + "[no] debug zebra pseudowires", + "Negate a command or set its defaults\n" + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra pseudowires\n") +{ + if (strmatch(argv[0]->text, "no")) + UNSET_FLAG(zebra_debug_pw, ZEBRA_DEBUG_PW); + else + SET_FLAG(zebra_debug_pw, ZEBRA_DEBUG_PW); + return CMD_WARNING; +} + DEFUN (debug_zebra_packet, debug_zebra_packet_cmd, "debug zebra packet [<recv|send>] [detail]", @@ -419,6 +437,10 @@ static int config_write_debug(struct vty *vty) vty_out(vty, "debug zebra vxlan\n"); write++; } + if (IS_ZEBRA_DEBUG_PW) { + vty_out(vty, "debug zebra pseudowires%s", VTY_NEWLINE); + write++; + } return write; } @@ -431,6 +453,7 @@ void zebra_debug_init(void) zebra_debug_fpm = 0; zebra_debug_mpls = 0; zebra_debug_vxlan = 0; + zebra_debug_pw = 0; install_node(&debug_node, config_write_debug); @@ -440,6 +463,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_nht_cmd); install_element(ENABLE_NODE, &debug_zebra_mpls_cmd); install_element(ENABLE_NODE, &debug_zebra_vxlan_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); @@ -459,6 +483,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &debug_zebra_nht_cmd); install_element(CONFIG_NODE, &debug_zebra_mpls_cmd); install_element(CONFIG_NODE, &debug_zebra_vxlan_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 b52bb7d0e..987f9d012 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -44,6 +44,8 @@ #define ZEBRA_DEBUG_VXLAN 0x01 +#define ZEBRA_DEBUG_PW 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -66,6 +68,7 @@ #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_VXLAN (zebra_debug_vxlan & ZEBRA_DEBUG_VXLAN) +#define IS_ZEBRA_DEBUG_PW (zebra_debug_pw & ZEBRA_DEBUG_PW) extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; @@ -75,6 +78,7 @@ extern unsigned long zebra_debug_fpm; extern unsigned long zebra_debug_nht; extern unsigned long zebra_debug_mpls; extern unsigned long zebra_debug_vxlan; +extern unsigned long zebra_debug_pw; extern void zebra_debug_init(void); diff --git a/zebra/subdir.am b/zebra/subdir.am index ceffa863e..0391cab9f 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -56,6 +56,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_ns.c \ zebra/zebra_ptm.c \ zebra/zebra_ptm_redistribute.c \ + zebra/zebra_pw.c \ zebra/zebra_rib.c \ zebra/zebra_rnh.c \ zebra/zebra_routemap.c \ @@ -95,6 +96,7 @@ noinst_HEADERS += \ zebra/zebra_ns.h \ zebra/zebra_ptm.h \ zebra/zebra_ptm_redistribute.h \ + zebra/zebra_pw.h \ zebra/zebra_rnh.h \ zebra/zebra_routemap.h \ zebra/zebra_static.h \ diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 000000000..143224254 --- /dev/null +++ b/zebra/zebra_pw.c @@ -0,0 +1,268 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "thread.h" +#include "vrf.h" + +#include "zebra/debug.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_pw.h" + +DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire") + +DEFINE_HOOK(pw_install, (struct zebra_pw * pw), (pw)) +DEFINE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw)) + +extern struct zebra_t zebrad; + +static void zebra_pw_install(struct zebra_pw *); +static void zebra_pw_uninstall(struct zebra_pw *); +static int zebra_pw_install_retry(struct thread *); +static int zebra_pw_check_reachability(struct zebra_pw *); +static void zebra_pw_update_status(struct zebra_pw *, int); + +static inline int zebra_pw_compare(const struct zebra_pw *a, + const struct zebra_pw *b) +{ + return (strcmp(a->ifname, b->ifname)); +} + +RB_GENERATE(zebra_pw_head, zebra_pw, entry, zebra_pw_compare) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, + uint8_t protocol, struct zserv *client) +{ + struct zebra_pw *pw; + + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: adding pseudowire %s protocol %s", + zvrf_id(zvrf), ifname, zebra_route_string(protocol)); + + pw = XCALLOC(MTYPE_PW, sizeof(*pw)); + strlcpy(pw->ifname, ifname, sizeof(pw->ifname)); + pw->protocol = protocol; + pw->vrf_id = zvrf_id(zvrf); + pw->client = client; + pw->status = PW_STATUS_UP; + + RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw); + + return pw; +} + +void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id, + pw->ifname, zebra_route_string(pw->protocol)); + + /* uninstall */ + if (pw->status == PW_STATUS_UP) + hook_call(pw_uninstall, pw); + else if (pw->install_retry_timer) + THREAD_TIMER_OFF(pw->install_retry_timer); + + /* unlink and release memory */ + RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw); + XFREE(MTYPE_PW, pw); +} + +void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, + union g_addr *nexthop, uint32_t local_label, + uint32_t remote_label, uint8_t flags, + union pw_protocol_fields *data) +{ + pw->ifindex = ifindex; + pw->type = type; + pw->af = af; + pw->nexthop = *nexthop; + pw->local_label = local_label; + pw->remote_label = remote_label; + pw->flags = flags; + pw->data = *data; + + if (pw->enabled) + zebra_pw_update(pw); + else + zebra_pw_uninstall(pw); +} + +struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname) +{ + struct zebra_pw pw; + strlcpy(pw.ifname, ifname, sizeof(pw.ifname)); + return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw)); +} + +void zebra_pw_update(struct zebra_pw *pw) +{ + if (zebra_pw_check_reachability(pw) < 0) { + zebra_pw_uninstall(pw); + } else { + /* + * Install or reinstall the pseudowire (e.g. to update + * parameters like the nexthop or the use of the control word). + */ + zebra_pw_install(pw); + } +} + +static void zebra_pw_install(struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: installing pseudowire %s protocol %s", + pw->vrf_id, pw->ifname, + zebra_route_string(pw->protocol)); + + if (hook_call(pw_install, pw)) { + zebra_pw_install_failure(pw); + return; + } + + if (pw->status == PW_STATUS_DOWN) + zebra_pw_update_status(pw, PW_STATUS_UP); +} + +static void zebra_pw_uninstall(struct zebra_pw *pw) +{ + if (pw->status == PW_STATUS_DOWN) + return; + + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: uninstalling pseudowire %s protocol %s", + pw->vrf_id, pw->ifname, + zebra_route_string(pw->protocol)); + + /* ignore any possible error */ + hook_call(pw_uninstall, pw); + + if (pw->enabled) + zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +/* + * Installation of the pseudowire in the kernel or hardware has failed. This + * function will notify the pseudowire client about the failure and schedule + * to retry the installation later. This function can be called by an external + * agent that performs the pseudowire installation in an asynchronous way. + */ +void zebra_pw_install_failure(struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug( + "%u: failed installing pseudowire %s, " + "scheduling retry in %u seconds", + pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL); + + /* schedule to retry later */ + THREAD_TIMER_OFF(pw->install_retry_timer); + pw->install_retry_timer = + thread_add_timer(zebrad.master, zebra_pw_install_retry, pw, + PW_INSTALL_RETRY_INTERVAL); + + zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +static int zebra_pw_install_retry(struct thread *thread) +{ + struct zebra_pw *pw = THREAD_ARG(thread); + + pw->install_retry_timer = NULL; + zebra_pw_install(pw); + + return 0; +} + +static void zebra_pw_update_status(struct zebra_pw *pw, int status) +{ + pw->status = status; + if (pw->client) + zsend_pw_update(pw->client, pw); +} + +static int zebra_pw_check_reachability(struct zebra_pw *pw) +{ + struct rib *rib; + struct nexthop *nexthop, *tnexthop; + int recursing; + + /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ + + /* find route to the remote end of the pseudowire */ + rib = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (!rib) { + if (IS_ZEBRA_DEBUG_PW) + zlog_warn("%s: no route found for %s", __func__, + pw->ifname); + return -1; + } + + /* + * Need to ensure that there's a label binding for all nexthops. + * Otherwise, ECMP for this route could render the pseudowire unusable. + */ + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (!nexthop->nh_label) { + if (IS_ZEBRA_DEBUG_PW) + zlog_warn("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + } + + return 0; +} + +void zebra_pw_client_close(struct zserv *client) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + struct zebra_pw *pw, *tmp; + + RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) + { + zvrf = vrf->info; + RB_FOREACH_SAFE(pw, zebra_pw_head, &zvrf->pseudowires, tmp) + { + if (pw->client != client) + continue; + zebra_pw_del(zvrf, pw); + } + } +} + +void zebra_pw_init(struct zebra_vrf *zvrf) +{ + RB_INIT(&zvrf->pseudowires); +} + +void zebra_pw_exit(struct zebra_vrf *zvrf) +{ + struct zebra_pw *pw; + + while ((pw = RB_ROOT(&zvrf->pseudowires)) != NULL) + zebra_pw_del(zvrf, pw); +} diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h new file mode 100644 index 000000000..62f45db5c --- /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 <net/if.h> +#include <netinet/in.h> + +#include "hook.h" + +#define PW_INSTALL_RETRY_INTERVAL 30 + +struct zebra_pw { + RB_ENTRY(zebra_pw) entry; + vrf_id_t vrf_id; + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + int type; + int af; + union g_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + union pw_protocol_fields data; + int enabled; + int status; + uint8_t protocol; + struct zserv *client; + struct thread *install_retry_timer; +}; + +RB_HEAD(zebra_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_pw_head, zebra_pw, entry, zebra_pw_compare); + +DECLARE_HOOK(pw_install, (struct zebra_pw * pw), (pw)) +DECLARE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw)) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *, const char *, uint8_t, + struct zserv *); +void zebra_pw_del(struct zebra_vrf *, struct zebra_pw *); +void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *, + uint32_t, uint32_t, uint8_t, union pw_protocol_fields *); +struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *); +void zebra_pw_update(struct zebra_pw *); +void zebra_pw_install_failure(struct zebra_pw *); +void zebra_pw_client_close(struct zserv *); +void zebra_pw_init(struct zebra_vrf *); +void zebra_pw_exit(struct zebra_vrf *); + +#endif /* ZEBRA_PW_H_ */ diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 87c1389b4..ff140bad6 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -203,6 +203,7 @@ static int zebra_vrf_delete(struct vrf *vrf) zebra_vxlan_close_tables(zvrf); 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); @@ -372,6 +373,7 @@ struct zebra_vrf *zebra_vrf_alloc(void) zebra_vxlan_init_tables(zvrf); zebra_mpls_init_tables(zvrf); + zebra_pw_init(zvrf); return zvrf; } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 3fdbe96dd..a55c9d459 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -23,6 +23,7 @@ #define __ZEBRA_RIB_H__ #include <zebra/zebra_ns.h> +#include <zebra/zebra_pw.h> /* MPLS (Segment Routing) global block */ typedef struct mpls_srgb_t_ { @@ -89,6 +90,9 @@ struct zebra_vrf { /* MPLS Segment Routing Global block */ mpls_srgb_t mpls_srgb; + /* 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 dae6785ae..bdb7755b6 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1085,6 +1085,27 @@ int 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 zread_interface_add(struct zserv *client, u_short length, @@ -2037,6 +2058,97 @@ static void zread_label_manager_request(int cmd, struct zserv *client, } } +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) { @@ -2081,6 +2193,9 @@ static void zebra_client_close(struct zserv *client) zebra_mpls_cleanup_fecs_for_client(vrf_info_lookup(VRF_DEFAULT), client); + /* Remove pseudowires associated with this client */ + zebra_pw_client_close(client); + /* Close file descriptor. */ if (client->sock) { unsigned long nroutes; @@ -2434,6 +2549,12 @@ static int zebra_client_read(struct thread *thread) case ZEBRA_INTERFACE_SET_MASTER: zread_interface_set_master(client, sock, length); 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 a2cf5d9f4..f661572d5 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -30,6 +30,8 @@ #include "zclient.h" #include "zebra/zebra_ns.h" +#include "zebra/zebra_pw.h" + /* Default port information. */ #define ZEBRA_VTY_PORT 2601 @@ -175,6 +177,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; |