diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/Makefile.am | 7 | ||||
| -rw-r--r-- | zebra/debug.c | 26 | ||||
| -rw-r--r-- | zebra/debug.h | 4 | ||||
| -rw-r--r-- | zebra/main.c | 1 | ||||
| -rw-r--r-- | zebra/rib.h | 1 | ||||
| -rw-r--r-- | zebra/zebra_mpls.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_mpls_openbsd.c | 100 | ||||
| -rw-r--r-- | zebra/zebra_pw.c | 532 | ||||
| -rw-r--r-- | zebra/zebra_pw.h | 75 | ||||
| -rw-r--r-- | zebra/zebra_pw_null.c | 29 | ||||
| -rw-r--r-- | zebra/zebra_rnh.c | 89 | ||||
| -rw-r--r-- | zebra/zebra_rnh.h | 3 | ||||
| -rw-r--r-- | zebra/zebra_vrf.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_vrf.h | 5 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 7 | ||||
| -rw-r--r-- | zebra/zserv.c | 138 | ||||
| -rw-r--r-- | zebra/zserv.h | 3 |
17 files changed, 1008 insertions, 18 deletions
diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 3e0de3b463..9e9998ebbc 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -33,14 +33,14 @@ zebra_SOURCES = \ zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \ zebra_mroute.c \ - label_manager.c \ + label_manager.c zebra_pw.c \ # end testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \ zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ - zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c + zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c zebra_pw_null.c noinst_HEADERS = \ zebra_memory.h \ @@ -49,7 +49,8 @@ noinst_HEADERS = \ rt_netlink.h zebra_fpm_private.h zebra_rnh.h \ zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \ zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \ - kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h + kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h zebra_pw.h \ + # end zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP) diff --git a/zebra/debug.c b/zebra/debug.c index a42d5aa3ef..6f59dc0ac2 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -32,6 +32,7 @@ unsigned long zebra_debug_rib; unsigned long zebra_debug_fpm; unsigned long zebra_debug_nht; unsigned long zebra_debug_mpls; +unsigned long zebra_debug_pw; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, @@ -85,6 +86,8 @@ DEFUN (show_debugging_zebra, vty_out (vty, " Zebra next-hop tracking debugging is on%s", VTY_NEWLINE); if (IS_ZEBRA_DEBUG_MPLS) vty_out (vty, " Zebra MPLS debugging is on%s", VTY_NEWLINE); + if (IS_ZEBRA_DEBUG_PW) + vty_out (vty, " Zebra pseudowire debugging is on%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -122,6 +125,21 @@ DEFUN (debug_zebra_mpls, return CMD_WARNING; } +DEFUN (debug_zebra_pw, + debug_zebra_pw_cmd, + "[no] debug zebra pseudowires", + "Negate a command or set its defaults\n" + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra pseudowires\n") +{ + if (strmatch (argv[0]->text, "no")) + UNSET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW); + else + SET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW); + return CMD_WARNING; +} + DEFUN (debug_zebra_packet, debug_zebra_packet_cmd, "debug zebra packet [<recv|send>] [detail]", @@ -410,6 +428,11 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug zebra mpls%s", VTY_NEWLINE); write++; } + if (IS_ZEBRA_DEBUG_PW) + { + vty_out (vty, "debug zebra pseudowires%s", VTY_NEWLINE); + write++; + } return write; } @@ -422,6 +445,7 @@ zebra_debug_init (void) zebra_debug_rib = 0; zebra_debug_fpm = 0; zebra_debug_mpls = 0; + zebra_debug_pw = 0; install_node (&debug_node, config_write_debug); @@ -430,6 +454,7 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &debug_zebra_events_cmd); install_element (ENABLE_NODE, &debug_zebra_nht_cmd); install_element (ENABLE_NODE, &debug_zebra_mpls_cmd); + install_element (ENABLE_NODE, &debug_zebra_pw_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_cmd); install_element (ENABLE_NODE, &debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd); @@ -449,6 +474,7 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_events_cmd); install_element (CONFIG_NODE, &debug_zebra_nht_cmd); install_element (CONFIG_NODE, &debug_zebra_mpls_cmd); + install_element (CONFIG_NODE, &debug_zebra_pw_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); install_element (CONFIG_NODE, &debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index f8ebf3d616..9a196d2f34 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -43,6 +43,8 @@ #define ZEBRA_DEBUG_MPLS 0x01 +#define ZEBRA_DEBUG_PW 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -64,6 +66,7 @@ #define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM) #define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT) #define IS_ZEBRA_DEBUG_MPLS (zebra_debug_mpls & ZEBRA_DEBUG_MPLS) +#define IS_ZEBRA_DEBUG_PW (zebra_debug_pw & ZEBRA_DEBUG_PW) extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; @@ -72,6 +75,7 @@ extern unsigned long zebra_debug_rib; extern unsigned long zebra_debug_fpm; extern unsigned long zebra_debug_nht; extern unsigned long zebra_debug_mpls; +extern unsigned long zebra_debug_pw; extern void zebra_debug_init (void); diff --git a/zebra/main.c b/zebra/main.c index 459e6148d8..84d71c33b9 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -314,6 +314,7 @@ main (int argc, char **argv) zebra_mpls_init (); zebra_mpls_vty_init (); + zebra_pw_vty_init (); /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/rib.h b/zebra/rib.h index 5381d76b98..e910facb44 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -91,6 +91,7 @@ struct rib #define RIB_ENTRY_NEXTHOPS_CHANGED 0x2 #define RIB_ENTRY_CHANGED 0x4 #define RIB_ENTRY_SELECTED_FIB 0x8 +#define RIB_ENTRY_LABELS_CHANGED 0x10 /* Nexthop information. */ u_char nexthop_num; diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5a3ed7545d..ba500cac27 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1346,7 +1346,7 @@ mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type, return 0; SET_FLAG (rib->status, RIB_ENTRY_CHANGED); - SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); rib_queue_add (rn); return 0; @@ -1542,7 +1542,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi) { nexthop_del_labels (nexthop); SET_FLAG (rib->status, RIB_ENTRY_CHANGED); - SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); update = 1; } diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 5dfe16caf5..0fcd28e0ee 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -35,6 +35,7 @@ extern struct zebra_privs_t zserv_privs; struct { u_int32_t rtseq; int fd; + int ioctl_fd; } kr_state; static int @@ -337,6 +338,90 @@ kernel_del_lsp (zebra_lsp_t *lsp) return ret; } +static int +kmpw_install (struct zebra_pw *pw) +{ + struct ifreq ifr; + struct ifmpwreq imr; + struct sockaddr_storage ss; + struct sockaddr_in *sa_in = (struct sockaddr_in *) &ss; + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) &ss; + + memset (&imr, 0, sizeof (imr)); + switch (pw->type) + { + case PW_TYPE_ETHERNET: + imr.imr_type = IMR_TYPE_ETHERNET; + break; + case PW_TYPE_ETHERNET_TAGGED: + imr.imr_type = IMR_TYPE_ETHERNET_TAGGED; + break; + default: + zlog_err ("%s: unhandled pseudowire type (%#X)", __func__, + pw->type); + return -1; + } + + if (pw->flags & F_PSEUDOWIRE_CWORD) + imr.imr_flags |= IMR_FLAG_CONTROLWORD; + + /* pseudowire nexthop */ + memset (&ss, 0, sizeof (ss)); + switch (pw->af) { + case AF_INET: + sa_in->sin_family = AF_INET; + sa_in->sin_len = sizeof (struct sockaddr_in); + sa_in->sin_addr = pw->nexthop.ipv4; + break; + case AF_INET6: + sa_in6->sin6_family = AF_INET6; + sa_in6->sin6_len = sizeof (struct sockaddr_in6); + sa_in6->sin6_addr = pw->nexthop.ipv6; + break; + default: + zlog_err ("%s: unhandled pseudowire address-family (%u)", __func__, + pw->af); + return -1; + } + memcpy (&imr.imr_nexthop, (struct sockaddr *) &ss, + sizeof (imr.imr_nexthop)); + + /* pseudowire local/remote labels */ + imr.imr_lshim.shim_label = pw->local_label; + imr.imr_rshim.shim_label = pw->remote_label; + + /* ioctl */ + memset (&ifr, 0, sizeof (ifr)); + strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name)); + ifr.ifr_data = (caddr_t) &imr; + if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) + { + zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno)); + return -1; + } + + return 0; +} + +static int +kmpw_uninstall (struct zebra_pw *pw) +{ + struct ifreq ifr; + struct ifmpwreq imr; + + memset(&ifr, 0, sizeof (ifr)); + memset(&imr, 0, sizeof (imr)); + strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name)); + ifr.ifr_data = (caddr_t) &imr; + if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) + { + zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno)); + return -1; + } + + return 0; +} + #define MAX_RTSOCK_BUF 128 * 1024 int mpls_kernel_init (void) @@ -344,10 +429,17 @@ mpls_kernel_init (void) int rcvbuf, default_rcvbuf; socklen_t optlen; - if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) + { zlog_warn("%s: socket", __func__); return -1; - } + } + + if ((kr_state.ioctl_fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) + { + zlog_warn("%s: ioctl socket", __func__); + return -1; + } /* grow receive buffer, don't wanna miss messages */ optlen = sizeof (default_rcvbuf); @@ -364,5 +456,9 @@ mpls_kernel_init (void) kr_state.rtseq = 1; + /* register hook to install/uninstall pseudowires */ + hook_register (pw_install, kmpw_install); + hook_register (pw_uninstall, kmpw_uninstall); + return 0; } diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 0000000000..2164ddf6ee --- /dev/null +++ b/zebra/zebra_pw.c @@ -0,0 +1,532 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "thread.h" +#include "command.h" +#include "vrf.h" + +#include "zebra/debug.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_pw.h" + +DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire") + +DEFINE_QOBJ_TYPE(zebra_pw) + +DEFINE_HOOK(pw_install, (struct zebra_pw *pw), (pw)) +DEFINE_HOOK(pw_uninstall, (struct zebra_pw *pw), (pw)) + +extern struct zebra_t zebrad; + +static int zebra_pw_enabled(struct zebra_pw *); +static void zebra_pw_install(struct zebra_pw *); +static void zebra_pw_uninstall(struct zebra_pw *); +static int zebra_pw_install_retry(struct thread *); +static int zebra_pw_check_reachability(struct zebra_pw *); +static void zebra_pw_update_status(struct zebra_pw *, int); + +static inline int zebra_pw_compare(const struct zebra_pw *a, + const struct zebra_pw *b) +{ + return (strcmp(a->ifname, b->ifname)); +} + +RB_GENERATE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare) +RB_GENERATE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, + uint8_t protocol, struct zserv *client) +{ + struct zebra_pw *pw; + + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: adding pseudowire %s protocol %s", + zvrf_id(zvrf), ifname, zebra_route_string(protocol)); + + pw = XCALLOC(MTYPE_PW, sizeof(*pw)); + strlcpy(pw->ifname, ifname, sizeof(pw->ifname)); + pw->protocol = protocol; + pw->vrf_id = zvrf_id(zvrf); + pw->client = client; + pw->status = PW_STATUS_UP; + pw->local_label = MPLS_NO_LABEL; + pw->remote_label = MPLS_NO_LABEL; + pw->flags = F_PSEUDOWIRE_CWORD; + + RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw); + if (pw->protocol == ZEBRA_ROUTE_STATIC) { + RB_INSERT(zebra_static_pw_head, &zvrf->static_pseudowires, pw); + QOBJ_REG(pw, zebra_pw); + } + + return pw; +} + +void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id, + pw->ifname, zebra_route_string(pw->protocol)); + + /* remove nexthop tracking */ + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + + /* uninstall */ + if (pw->status == PW_STATUS_UP) + hook_call(pw_uninstall, pw); + else if (pw->install_retry_timer) + THREAD_TIMER_OFF(pw->install_retry_timer); + + /* unlink and release memory */ + RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw); + if (pw->protocol == ZEBRA_ROUTE_STATIC) + RB_REMOVE(zebra_static_pw_head, &zvrf->static_pseudowires, pw); + XFREE(MTYPE_PW, pw); +} + +void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, + union g_addr *nexthop, uint32_t local_label, + uint32_t remote_label, uint8_t flags, + union pw_protocol_fields *data) +{ + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + + pw->ifindex = ifindex; + pw->type = type; + pw->af = af; + pw->nexthop = *nexthop; + pw->local_label = local_label; + pw->remote_label = remote_label; + pw->flags = flags; + pw->data = *data; + + if (zebra_pw_enabled(pw)) + zebra_register_rnh_pseudowire(pw->vrf_id, pw); + else + zebra_pw_uninstall(pw); +} + +struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname) +{ + struct zebra_pw pw; + strlcpy(pw.ifname, ifname, sizeof(pw.ifname)); + return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw)); +} + +static int zebra_pw_enabled(struct zebra_pw *pw) +{ + if (pw->protocol == ZEBRA_ROUTE_STATIC) { + if (pw->local_label == MPLS_NO_LABEL + || pw->remote_label == MPLS_NO_LABEL + || pw->af == AF_UNSPEC) + return 0; + return 1; + } else + return pw->enabled; +} + +void zebra_pw_update(struct zebra_pw *pw) +{ + if (zebra_pw_check_reachability(pw) < 0) { + zebra_pw_uninstall(pw); + /* wait for NHT and try again later */ + } else { + /* + * Install or reinstall the pseudowire (e.g. to update + * parameters like the nexthop or the use of the control word). + */ + zebra_pw_install(pw); + } +} + +static void zebra_pw_install(struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: installing pseudowire %s protocol %s", + pw->vrf_id, pw->ifname, + zebra_route_string(pw->protocol)); + + if (hook_call(pw_install, pw)) { + zebra_pw_install_failure(pw); + return; + } + + if (pw->status == PW_STATUS_DOWN) + zebra_pw_update_status(pw, PW_STATUS_UP); +} + +static void zebra_pw_uninstall(struct zebra_pw *pw) +{ + if (pw->status == PW_STATUS_DOWN) + return; + + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: uninstalling pseudowire %s protocol %s", + pw->vrf_id, pw->ifname, + zebra_route_string(pw->protocol)); + + /* ignore any possible error */ + hook_call(pw_uninstall, pw); + + if (zebra_pw_enabled(pw)) + zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +/* + * Installation of the pseudowire in the kernel or hardware has failed. This + * function will notify the pseudowire client about the failure and schedule + * to retry the installation later. This function can be called by an external + * agent that performs the pseudowire installation in an asynchronous way. + */ +void zebra_pw_install_failure(struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: failed installing pseudowire %s, " + "scheduling retry in %u seconds", pw->vrf_id, + pw->ifname, PW_INSTALL_RETRY_INTERVAL); + + /* schedule to retry later */ + THREAD_TIMER_OFF(pw->install_retry_timer); + pw->install_retry_timer = + thread_add_timer(zebrad.master, zebra_pw_install_retry, + pw, PW_INSTALL_RETRY_INTERVAL); + + zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +static int zebra_pw_install_retry(struct thread *thread) +{ + struct zebra_pw *pw = THREAD_ARG(thread); + + pw->install_retry_timer = NULL; + zebra_pw_install(pw); + + return 0; +} + +static void zebra_pw_update_status(struct zebra_pw *pw, int status) +{ + pw->status = status; + if (pw->client) + zsend_pw_update(pw->client, pw); +} + +static int zebra_pw_check_reachability(struct zebra_pw *pw) +{ + struct rib *rib; + struct nexthop *nexthop, *tnexthop; + int recursing; + + /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ + + /* find route to the remote end of the pseudowire */ + rib = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (!rib) { + if (IS_ZEBRA_DEBUG_PW) + zlog_warn("%s: no route found for %s", __func__, + pw->ifname); + return -1; + } + + /* + * Need to ensure that there's a label binding for all nexthops. + * Otherwise, ECMP for this route could render the pseudowire unusable. + */ + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (!nexthop->nh_label) { + if (IS_ZEBRA_DEBUG_PW) + zlog_warn("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + } + + return 0; +} + +void +zebra_pw_client_close(struct zserv *client) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + struct zebra_pw *pw, *tmp; + + RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + RB_FOREACH_SAFE(pw, zebra_pw_head, &zvrf->pseudowires, tmp) { + if (pw->client != client) + continue; + zebra_pw_del(zvrf, pw); + } + } +} + +void zebra_pw_init(struct zebra_vrf *zvrf) +{ + RB_INIT(&zvrf->pseudowires); + RB_INIT(&zvrf->static_pseudowires); +} + +void zebra_pw_exit(struct zebra_vrf *zvrf) +{ + struct zebra_pw *pw; + + while ((pw = RB_ROOT(&zvrf->pseudowires)) != NULL) + zebra_pw_del(zvrf, pw); +} + +DEFUN_NOSH (pseudowire_if, + pseudowire_if_cmd, + "[no] pseudowire IFNAME", + NO_STR + "Static pseudowire configuration\n" + "Pseudowire name\n") +{ + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + int idx = 0; + const char *ifname; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return CMD_WARNING; + + argv_find(argv, argc, "IFNAME", &idx); + ifname = argv[idx]->arg; + pw = zebra_pw_find(zvrf, ifname); + if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) { + vty_out(vty, "%% Pseudowire is not static%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv_find(argv, argc, "no", &idx)) { + if (!pw) + return CMD_SUCCESS; + zebra_pw_del(zvrf, pw); + } + + if (!pw) + pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL); + VTY_PUSH_CONTEXT(PW_NODE, pw); + + return CMD_SUCCESS; +} + +DEFUN (pseudowire_labels, + pseudowire_labels_cmd, + "[no] mpls label local (16-1048575) remote (16-1048575)", + NO_STR + "MPLS L2VPN PW command\n" + "MPLS L2VPN static labels\n" + "Local pseudowire label\n" + "Local pseudowire label\n" + "Remote pseudowire label\n" + "Remote pseudowire label\n") +{ + VTY_DECLVAR_CONTEXT(zebra_pw, pw); + int idx = 0; + mpls_label_t local_label, remote_label; + + if (argv_find(argv, argc, "no", &idx)) { + local_label = MPLS_NO_LABEL; + remote_label = MPLS_NO_LABEL; + } else { + argv_find(argv, argc, "local", &idx); + local_label = atoi(argv[idx + 1]->arg); + argv_find(argv, argc, "remote", &idx); + remote_label = atoi(argv[idx + 1]->arg); + } + + zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, + local_label, remote_label, pw->flags, &pw->data); + + return CMD_SUCCESS; +} + +DEFUN (pseudowire_neighbor, + pseudowire_neighbor_cmd, + "[no] neighbor <A.B.C.D|X:X::X:X>", + NO_STR + "Specify the IPv4 or IPv6 address of the remote endpoint\n" + "IPv4 address\n" + "IPv6 address\n") +{ + VTY_DECLVAR_CONTEXT(zebra_pw, pw); + int idx = 0; + const char *address; + int af; + union g_addr nexthop; + + af = AF_UNSPEC; + memset(&nexthop, 0, sizeof(nexthop)); + + if (!argv_find(argv, argc, "no", &idx)) { + argv_find(argv, argc, "neighbor", &idx); + address = argv[idx + 1]->arg; + + if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1) + af = AF_INET; + else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1) + af = AF_INET6; + else { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop, + pw->local_label, pw->remote_label, pw->flags, + &pw->data); + + return CMD_SUCCESS; +} + +DEFUN (pseudowire_control_word, + pseudowire_control_word_cmd, + "[no] control-word <exclude|include>", + NO_STR + "Control-word options\n" + "Exclude control-word in pseudowire packets\n" + "Include control-word in pseudowire packets\n") +{ + VTY_DECLVAR_CONTEXT(zebra_pw, pw); + int idx = 0; + uint8_t flags = 0; + + if (argv_find(argv, argc, "no", &idx)) + flags = F_PSEUDOWIRE_CWORD; + else { + argv_find(argv, argc, "control-word", &idx); + if (argv[idx + 1]->text[0] == 'i') + flags = F_PSEUDOWIRE_CWORD; + } + + zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, + pw->local_label, pw->remote_label, flags, &pw->data); + + return CMD_SUCCESS; +} + +DEFUN (show_pseudowires, + show_pseudowires_cmd, + "show pseudowires", + SHOW_STR + "Pseudowires") +{ + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s", + "Interface", "Neighbor", "Labels", "Protocol", "Status", + VTY_NEWLINE); + + RB_FOREACH(pw, zebra_pw_head, &zvrf->pseudowires) { + char buf_nbr[INET6_ADDRSTRLEN]; + char buf_labels[64]; + + inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + + if (pw->local_label != MPLS_NO_LABEL + && pw->remote_label != MPLS_NO_LABEL) + snprintf(buf_labels, sizeof(buf_labels), "%u/%u", + pw->local_label, pw->remote_label); + else + snprintf(buf_labels, sizeof(buf_labels), "-"); + + vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s", + pw->ifname, (pw->af != AF_UNSPEC) ? buf_nbr : "-", + buf_labels, zebra_route_string(pw->protocol), + (zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) ? + "UP" : "DOWN", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/* Pseudowire configuration write function. */ +static int zebra_pw_config(struct vty *vty) +{ + int write = 0; + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + RB_FOREACH(pw, zebra_static_pw_head, &zvrf->static_pseudowires) { + vty_out(vty, "pseudowire %s%s", pw->ifname, VTY_NEWLINE); + if (pw->local_label != MPLS_NO_LABEL + && pw->remote_label != MPLS_NO_LABEL) + vty_out(vty, " mpls label local %u remote %u%s", + pw->local_label, pw->remote_label, + VTY_NEWLINE); + else + vty_out(vty, " ! Incomplete config, specify the static " + "MPLS labels%s", VTY_NEWLINE); + + if (pw->af != AF_UNSPEC) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf)); + vty_out(vty, " neighbor %s%s", buf, VTY_NEWLINE); + } else + vty_out(vty, " ! Incomplete config, specify a neighbor " + "address%s", VTY_NEWLINE); + + if (!(pw->flags & F_PSEUDOWIRE_CWORD)) + vty_out(vty, " control-word exclude%s", VTY_NEWLINE); + + vty_out(vty, "!%s", VTY_NEWLINE); + write = 1; + } + + return write; +} + +static struct cmd_node pw_node = +{ + PW_NODE, + "%s(config-pw)# ", + 1, +}; + +void zebra_pw_vty_init(void) +{ + install_node(&pw_node, zebra_pw_config); + install_default(PW_NODE); + + install_element(CONFIG_NODE, &pseudowire_if_cmd); + install_element(PW_NODE, &pseudowire_labels_cmd); + install_element(PW_NODE, &pseudowire_neighbor_cmd); + install_element(PW_NODE, &pseudowire_control_word_cmd); + + install_element(VIEW_NODE, &show_pseudowires_cmd); +} diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h new file mode 100644 index 0000000000..b588bac0a2 --- /dev/null +++ b/zebra/zebra_pw.h @@ -0,0 +1,75 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef ZEBRA_PW_H_ +#define ZEBRA_PW_H_ + +#include <net/if.h> +#include <netinet/in.h> + +#include "hook.h" +#include "qobj.h" + +#define PW_INSTALL_RETRY_INTERVAL 30 + +struct zebra_pw { + RB_ENTRY(zebra_pw) pw_entry, static_pw_entry; + vrf_id_t vrf_id; + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + int type; + int af; + union g_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + union pw_protocol_fields data; + int enabled; + int status; + uint8_t protocol; + struct zserv *client; + struct rnh *rnh; + struct thread *install_retry_timer; + QOBJ_FIELDS +}; +DECLARE_QOBJ_TYPE(zebra_pw) + +RB_HEAD(zebra_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare); + +RB_HEAD(zebra_static_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare); + +DECLARE_HOOK(pw_install, (struct zebra_pw * pw), (pw)) +DECLARE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw)) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *, const char *, + uint8_t, struct zserv *); +void zebra_pw_del(struct zebra_vrf *, struct zebra_pw *); +void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *, + uint32_t, uint32_t, uint8_t, union pw_protocol_fields *); +struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *); +void zebra_pw_update(struct zebra_pw *); +void zebra_pw_install_failure(struct zebra_pw *); +void zebra_pw_client_close(struct zserv *); +void zebra_pw_init(struct zebra_vrf *); +void zebra_pw_exit(struct zebra_vrf *); +void zebra_pw_vty_init(void); + +#endif /* ZEBRA_PW_H_ */ diff --git a/zebra/zebra_pw_null.c b/zebra/zebra_pw_null.c new file mode 100644 index 0000000000..d19d80b8c9 --- /dev/null +++ b/zebra/zebra_pw_null.c @@ -0,0 +1,29 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "vrf.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" + +void zebra_pw_init(struct zebra_vrf *zvrf) {} +void zebra_pw_exit(struct zebra_vrf *zvrf) {} diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 5096e95395..d5ebbbc466 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -131,6 +131,7 @@ zebra_add_rnh (struct prefix *p, vrf_id_t vrfid, rnh_type_t type) rnh->client_list = list_new(); rnh->vrf_id = vrfid; rnh->zebra_static_route_list = list_new(); + rnh->zebra_pseudowire_list = list_new(); route_lock_node (rn); rn->info = rnh; rnh->node = rn; @@ -168,6 +169,7 @@ zebra_free_rnh (struct rnh *rnh) rnh->flags |= ZEBRA_NHT_DELETED; list_free (rnh->client_list); list_free (rnh->zebra_static_route_list); + list_free (rnh->zebra_pseudowire_list); free_state (rnh->vrf_id, rnh->state, rnh->node); XFREE (MTYPE_RNH, rnh); } @@ -222,7 +224,8 @@ zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client, rnh_type_t type) } listnode_delete(rnh->client_list, client); if (list_isempty(rnh->client_list) && - list_isempty(rnh->zebra_static_route_list)) + list_isempty(rnh->zebra_static_route_list) && + list_isempty(rnh->zebra_pseudowire_list)) zebra_delete_rnh(rnh, type); } @@ -252,7 +255,8 @@ zebra_deregister_rnh_static_nh(vrf_id_t vrf_id, struct prefix *nh, listnode_delete(rnh->zebra_static_route_list, static_rn); if (list_isempty(rnh->client_list) && - list_isempty(rnh->zebra_static_route_list)) + list_isempty(rnh->zebra_static_route_list) && + list_isempty(rnh->zebra_pseudowire_list)) zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE); } @@ -301,6 +305,62 @@ zebra_deregister_rnh_static_nexthops (vrf_id_t vrf_id, struct nexthop *nexthop, } } +/* XXX move this utility function elsewhere? */ +static void +addr2hostprefix (int af, const union g_addr *addr, struct prefix *prefix) +{ + switch (af) + { + case AF_INET: + prefix->family = AF_INET; + prefix->prefixlen = IPV4_MAX_BITLEN; + prefix->u.prefix4 = addr->ipv4; + break; + case AF_INET6: + prefix->family = AF_INET6; + prefix->prefixlen = IPV6_MAX_BITLEN; + prefix->u.prefix6 = addr->ipv6; + break; + default: + zlog_warn ("%s: unknown address family %d", __func__, af); + break; + } +} + +void +zebra_register_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) +{ + struct prefix nh; + struct rnh *rnh; + + addr2hostprefix (pw->af, &pw->nexthop, &nh); + rnh = zebra_add_rnh (&nh, vrf_id, RNH_NEXTHOP_TYPE); + if (rnh && !listnode_lookup (rnh->zebra_pseudowire_list, pw)) + { + listnode_add (rnh->zebra_pseudowire_list, pw); + pw->rnh = rnh; + zebra_evaluate_rnh (vrf_id, pw->af, 1, RNH_NEXTHOP_TYPE, &nh); + } +} + +void +zebra_deregister_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) +{ + struct rnh *rnh; + + rnh = pw->rnh; + if (!rnh) + return; + + listnode_delete (rnh->zebra_pseudowire_list, pw); + pw->rnh = NULL; + + if (list_isempty (rnh->client_list) && + list_isempty (rnh->zebra_static_route_list) && + list_isempty (rnh->zebra_pseudowire_list)) + zebra_delete_rnh (rnh, RNH_NEXTHOP_TYPE); +} + /* Apply the NHT route-map for a client to the route (and nexthops) * resolving a NH. */ @@ -611,6 +671,16 @@ zebra_rnh_process_static_routes (vrf_id_t vrfid, int family, } } +static void +zebra_rnh_process_pseudowires (vrf_id_t vrfid, struct rnh *rnh) +{ + struct zebra_pw *pw; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (rnh->zebra_pseudowire_list, node, pw)) + zebra_pw_update (pw); +} + /* * See if a tracked nexthop entry has undergone any change, and if so, * take appropriate action; this involves notifying any clients and/or @@ -655,6 +725,9 @@ zebra_rnh_eval_nexthop_entry (vrf_id_t vrfid, int family, int force, /* Process static routes attached to this nexthop */ zebra_rnh_process_static_routes (vrfid, family, nrn, rnh, prn, rnh->state); + + /* Process pseudowires attached to this nexthop */ + zebra_rnh_process_pseudowires (vrfid, rnh); } } @@ -717,7 +790,10 @@ zebra_rnh_clear_nhc_flag (vrf_id_t vrfid, int family, rnh_type_t type, rib = zebra_rnh_resolve_entry (vrfid, family, type, nrn, rnh, &prn); if (rib) - UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + { + UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + UNSET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); + } } /* Evaluate all tracked entries (nexthops or routes for import into BGP) @@ -868,7 +944,8 @@ compare_state (struct rib *r1, struct rib *r2) if (r1->nexthop_num != r2->nexthop_num) return 1; - if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED)) + if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED) || + CHECK_FLAG(r1->status, RIB_ENTRY_LABELS_CHANGED)) return 1; return 0; @@ -1030,6 +1107,8 @@ print_rnh (struct route_node *rn, struct vty *vty) vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto), client->sock, rnh->filtered[client->proto] ? "(filtered)" : ""); if (!list_isempty(rnh->zebra_static_route_list)) - vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); + vty_out(vty, " zebra[static routes]%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); + if (!list_isempty(rnh->zebra_pseudowire_list)) + vty_out(vty, " zebra[pseudowires]"); vty_out(vty, "%s", VTY_NEWLINE); } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 4394fde4f3..9da5138e93 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -42,6 +42,7 @@ struct rnh struct prefix resolved_route; struct list *client_list; struct list *zebra_static_route_list; /* static routes dependent on this NH */ + struct list *zebra_pseudowire_list; /* pseudowires dependent on this NH */ struct route_node *node; int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */ }; @@ -67,6 +68,8 @@ extern void zebra_register_rnh_static_nh(vrf_id_t, struct prefix *, struct route extern void zebra_deregister_rnh_static_nexthops (vrf_id_t, struct nexthop *nexthop, struct route_node *rn); extern void zebra_deregister_rnh_static_nh(vrf_id_t, struct prefix *, struct route_node *); +extern void zebra_register_rnh_pseudowire (vrf_id_t, struct zebra_pw *); +extern void zebra_deregister_rnh_pseudowire (vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, rnh_type_t type); extern void zebra_evaluate_rnh(vrf_id_t vrfid, int family, int force, rnh_type_t type, diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 889c57e6ff..06d87a468e 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -202,6 +202,7 @@ zebra_vrf_delete (struct vrf *vrf) } zebra_mpls_close_tables (zvrf); + zebra_pw_exit (zvrf); for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp)) if_nbr_ipv6ll_to_ipv4ll_neigh_del_all (ifp); @@ -379,6 +380,7 @@ zebra_vrf_alloc (void) } zebra_mpls_init_tables (zvrf); + zebra_pw_init (zvrf); return zvrf; } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 8864f13052..cfe7cde75c 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@ #define __ZEBRA_RIB_H__ #include <zebra/zebra_ns.h> +#include <zebra/zebra_pw.h> /* Routing table instance. */ struct zebra_vrf @@ -79,6 +80,10 @@ struct zebra_vrf /* MPLS label forwarding table */ struct hash *lsp_table; + /* Pseudowires. */ + struct zebra_pw_head pseudowires; + struct zebra_static_pw_head static_pseudowires; + /* MPLS processing flags */ u_int16_t mpls_flags; #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index aec8fd1ddf..aeb01be070 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -923,6 +923,13 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, break; } + if (nexthop->nh_label && nexthop->nh_label->num_labels) + { + json_object_string_add(json_nexthop, "labels", + mpls_label2str (nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, BUFSIZ)); + } + json_object_array_add(json_nexthops, json_nexthop); } diff --git a/zebra/zserv.c b/zebra/zserv.c index 9beae9232e..fb02d60ba4 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1013,6 +1013,28 @@ zsend_router_id_update (struct zserv *client, struct prefix *p, return zebra_server_send_message(client); } +/* + * Function used by Zebra to send a PW status update to LDP daemon + */ +int +zsend_pw_update (struct zserv *client, struct zebra_pw *pw) +{ + struct stream *s; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id); + stream_write (s, pw->ifname, IF_NAMESIZE); + stream_putl (s, pw->ifindex); + stream_putl (s, pw->status); + + /* Put length at the first point of the stream. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + return zebra_server_send_message (client); +} + /* Register zebra server interface information. Send current all interface and address information. */ static int @@ -1762,16 +1784,14 @@ zread_mpls_labels (int command, struct zserv *client, u_short length, { mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate, NULL, ifindex); - if (out_label != MPLS_IMP_NULL_LABEL) - mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex, - distance, out_label); + mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex, + distance, out_label); } else if (command == ZEBRA_MPLS_LABELS_DELETE) { mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, NULL, ifindex); - if (out_label != MPLS_IMP_NULL_LABEL) - mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, - distance, out_label); + mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, + distance, out_label); } } /* Send response to a label manager connect request to client */ @@ -1933,6 +1953,103 @@ zread_label_manager_request (int cmd, struct zserv *client, vrf_id_t vrf_id) } } +static int +zread_pseudowire (int command, struct zserv *client, u_short length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct zebra_vrf *zvrf; + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + int type; + int af; + union g_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + union pw_protocol_fields data; + uint8_t protocol; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup (vrf_id); + if (!zvrf) + return -1; + + /* Get input stream. */ + s = client->ibuf; + + /* Get data. */ + stream_get (ifname, s, IF_NAMESIZE); + ifindex = stream_getl (s); + type = stream_getl (s); + af = stream_getl (s); + switch (af) + { + case AF_INET: + nexthop.ipv4.s_addr = stream_get_ipv4 (s); + break; + case AF_INET6: + stream_get (&nexthop.ipv6, s, 16); + break; + default: + return -1; + } + local_label = stream_getl (s); + remote_label = stream_getl (s); + flags = stream_getc (s); + stream_get (&data, s, sizeof(data)); + protocol = client->proto; + + pw = zebra_pw_find(zvrf, ifname); + switch (command) + { + case ZEBRA_PW_ADD: + if (pw) + { + zlog_warn ("%s: pseudowire %s already exists [%s]", __func__, ifname, + zserv_command_string (command)); + return -1; + } + + zebra_pw_add (zvrf, ifname, protocol, client); + break; + case ZEBRA_PW_DELETE: + if (!pw) + { + zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname, + zserv_command_string (command)); + return -1; + } + + zebra_pw_del (zvrf, pw); + break; + case ZEBRA_PW_SET: + case ZEBRA_PW_UNSET: + if (!pw) + { + zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname, + zserv_command_string (command)); + return -1; + } + + switch (command) + { + case ZEBRA_PW_SET: + pw->enabled = 1; + break; + case ZEBRA_PW_UNSET: + pw->enabled = 0; + break; + } + + zebra_pw_change (pw, ifindex, type, af, &nexthop, local_label, + remote_label, flags, &data); + break; + } + + return 0; +} + /* Cleanup registered nexthops (across VRFs) upon client disconnect. */ static void zebra_client_close_cleanup_rnh (struct zserv *client) @@ -1972,6 +2089,9 @@ zebra_client_close (struct zserv *client) /* Release Label Manager chunks */ release_daemon_chunks (client->proto, client->instance); + /* Remove pseudowires associated with this client */ + zebra_pw_client_close (client); + /* Close file descriptor. */ if (client->sock) { @@ -2269,6 +2389,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_RELEASE_LABEL_CHUNK: zread_label_manager_request (command, client, vrf_id); break; + case ZEBRA_PW_ADD: + case ZEBRA_PW_DELETE: + case ZEBRA_PW_SET: + case ZEBRA_PW_UNSET: + zread_pseudowire (command, client, length, vrf_id); + break; default: zlog_info ("Zebra received unknown command %d", command); break; diff --git a/zebra/zserv.h b/zebra/zserv.h index cd1948373a..42e762caa3 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -31,6 +31,8 @@ #include "zclient.h" #include "zebra/zebra_ns.h" +#include "zebra/zebra_pw.h" + /* Default port information. */ #define ZEBRA_VTY_PORT 2601 @@ -170,6 +172,7 @@ extern int zsend_interface_vrf_update (struct zserv *, struct interface *, vrf_id_t); extern int zsend_interface_link_params (struct zserv *, struct interface *); +extern int zsend_pw_update (struct zserv *, struct zebra_pw *); extern pid_t pid; |
