From 1a60aa17a4b24aebeaee6a428e9a192ed80920c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Mon, 15 May 2017 17:09:28 +0200 Subject: [PATCH 1/3] Add Pseudowire management in Zebra MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Zebra receives a PW from ldpd, it tries to install it in kernel and in case of failure notifies back to ldpd daemon and enqueues the PW for retry. The installation to kernel is not yet implemented as Linux kernel doesn't have support for it yet. It will be done through a module hook, so it can be outsourced to a remote dataplane. Signed-off-by: ßingen --- ldpd/l2vpn.c | 37 ++++++++ ldpd/lde.c | 17 ++++ ldpd/lde.h | 4 + ldpd/ldp_zebra.c | 89 +++++++++++++++++++ ldpd/ldpd.h | 8 +- lib/log.c | 2 + lib/zclient.c | 4 + lib/zclient.h | 4 + zebra/Makefile.am | 5 +- zebra/main.c | 1 + zebra/zebra_pw.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_pw.h | 51 +++++++++++ zebra/zserv.c | 80 +++++++++++++++++ zebra/zserv.h | 7 ++ 14 files changed, 518 insertions(+), 3 deletions(-) create mode 100644 zebra/zebra_pw.c create mode 100644 zebra/zebra_pw.h diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 27948f5a1aa5..47bb092de782 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -600,3 +600,40 @@ ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw) tnbr_check(leconf, tnbr); } } + +/** + * Update PW status + * + * @return 0 on success, 1 on failure + */ +int +l2vpn_pw_status_update (struct kpw *kpw) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + + /* Find L2VPN */ + l2vpn = l2vpn_find(ldeconf, kpw->vpn_name); + if (!l2vpn) { + log_warn("%s: No L2VPN found for %s", __func__, kpw->vpn_name); + return 1; + } + + /* Find PW */ + pw = l2vpn_pw_find(l2vpn, kpw->ifname); + if (!pw) { + log_warn("%s: No PW found for VPN %s and interface %s", + __func__, kpw->vpn_name, kpw->ifname); + return 1; + } + + kpw->af = pw->af; + memcpy(&kpw->nexthop, &pw->addr, sizeof (union ldpd_addr)); + /* Update status */ + if (kpw->flags & F_PW_STATUS_UP) + pw->flags |= F_PW_STATUS_UP; + else + pw->flags &= ~F_PW_STATUS_UP; + + return 0; +} diff --git a/ldpd/lde.c b/ldpd/lde.c index 36998e7cf87f..d86ceb11d292 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -456,6 +456,7 @@ lde_dispatch_parent(struct thread *thread) ssize_t n; int shut = 0; struct fec fec; + struct kpw kpw; /* for PW status update */ iev->ev_read = NULL; @@ -629,6 +630,12 @@ lde_dispatch_parent(struct thread *thread) } memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); break; + case IMSG_PW_UPDATE: + memcpy (&kpw, imsg.data, sizeof (struct kpw)); + if (l2vpn_pw_status_update (&kpw) != 0) { + log_warnx("%s: Error updating PW status", __func__); + } + break; default: log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); @@ -769,13 +776,18 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) pw->flags |= F_PW_STATUS_UP; memset(&kpw, 0, sizeof(kpw)); + strncpy (kpw.ifname, pw->ifname, IF_NAMESIZE); kpw.ifindex = pw->ifindex; kpw.pw_type = fn->fec.u.pwid.type; + kpw.lsr_id = pw->lsr_id; kpw.af = pw->af; kpw.nexthop = pw->addr; kpw.local_label = fn->local_label; kpw.remote_label = fnh->remote_label; kpw.flags = pw->flags; + kpw.pwid = pw->pwid; + strncpy (kpw.vpn_name, pw->l2vpn->name, L2VPN_NAME_LEN); + kpw.ac_port_ifindex = 0; // TODO: LIST_FIRST(&pw->l2vpn->if_list)->ifindex; lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw, sizeof(kpw)); @@ -826,13 +838,18 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) pw->flags &= ~F_PW_STATUS_UP; memset(&kpw, 0, sizeof(kpw)); + strncpy (kpw.ifname, pw->ifname, IF_NAMESIZE); kpw.ifindex = pw->ifindex; kpw.pw_type = fn->fec.u.pwid.type; + kpw.lsr_id = pw->lsr_id; kpw.af = pw->af; kpw.nexthop = pw->addr; kpw.local_label = fn->local_label; kpw.remote_label = fnh->remote_label; kpw.flags = pw->flags; + kpw.pwid = pw->pwid; + strncpy (kpw.vpn_name, pw->l2vpn->name, L2VPN_NAME_LEN); + kpw.ac_port_ifindex = 0; //TODO: LIST_FIRST(&pw->l2vpn->if_list)->ifindex; lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw, sizeof(kpw)); diff --git a/ldpd/lde.h b/ldpd/lde.h index 57791cd1b0c6..9eb1865f16f2 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -25,6 +25,8 @@ #include "openbsd-tree.h" #include "if.h" +#define RETRY_SYNC_PW_INTERVAL 5 /* in seconds */ + enum fec_type { FEC_TYPE_IPV4, FEC_TYPE_IPV6, @@ -188,6 +190,7 @@ void fec_snap(struct lde_nbr *); void fec_tree_clear(void); struct fec_nh *fec_nh_find(struct fec_node *, int, union ldpd_addr *, ifindex_t, uint8_t); +void update_local_label (struct fec_node *fn, int connected); void lde_kernel_insert(struct fec *, int, union ldpd_addr *, ifindex_t, uint8_t, int, void *); void lde_kernel_remove(struct fec *, int, union ldpd_addr *, @@ -235,5 +238,6 @@ void l2vpn_recv_pw_status_wcard(struct lde_nbr *, struct notify_msg *); void l2vpn_pw_ctl(pid_t); void l2vpn_binding_ctl(pid_t); +int l2vpn_pw_status_update (struct kpw *kpw); #endif /* _LDE_H_ */ diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index fde6e56c64a8..c812caa2ce24 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -153,17 +153,65 @@ kr_delete(struct kroute *kr) return (zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, kr)); } +static int +zebra_send_kpw(u_char cmd, struct zclient *zclient, struct kpw *kpw) +{ + struct stream *s; + + debug_zebra_out("ILM %s PW %u (%s) ifindex %hu, type %d. %s -> nexthop %s label %s", + (cmd == ZEBRA_KPW_ADD) ? "add" : "delete", + kpw->pwid, kpw->vpn_name, kpw->ifindex, + kpw->pw_type, log_label(kpw->local_label), + log_addr(kpw->af, &kpw->nexthop), log_label(kpw->remote_label)); + + /* Reset stream. */ + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_write (s, kpw->ifname, IF_NAMESIZE); + stream_putw(s, kpw->ifindex); + stream_putl(s, kpw->pw_type); + stream_putl(s, kpw->lsr_id.s_addr); + stream_putl(s, kpw->af); + switch (kpw->af) { + case AF_INET: + stream_put_in_addr(s, &kpw->nexthop.v4); + break; + case AF_INET6: + stream_write (s, (u_char *)&kpw->nexthop.v6, 16); + break; + default: + fatalx("zebra_send_kpw: unknown af"); + } + stream_putl(s, kpw->local_label); + stream_putl(s, kpw->remote_label); + stream_putc(s, kpw->flags); + stream_putl(s, kpw->pwid); + stream_write(s, kpw->vpn_name, L2VPN_NAME_LEN); + stream_putw(s, kpw->ac_port_ifindex); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return (zclient_send_message(zclient)); +} + int kmpw_set(struct kpw *kpw) { + zebra_send_kpw (ZEBRA_KPW_ADD, zclient, kpw); /* TODO */ + return (0); } int kmpw_unset(struct kpw *kpw) { + zebra_send_kpw (ZEBRA_KPW_DELETE, zclient, kpw); /* TODO */ + return (0); } @@ -477,6 +525,46 @@ 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. + * + * Params and return type are the ones required by zclient interface. + * + * @param command It will always be ZEBRA_PW_STATUS_UPDATE + * @param zclient To get input stream from + * @param length + * @param vrf_id + * @return 0 on success + */ +static int +ldp_zebra_read_pw_status_update (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct stream *s; + uint8_t status; + struct kpw kpw; + + memset (&kpw, 0, sizeof (struct kpw)); + + /* Get input stream. */ + s = zclient->ibuf; + + /* Get data. */ + stream_get (kpw.ifname, s, IF_NAMESIZE); + /* ifindex = stream_getw (s); */ + /* pwid = stream_getl (s); */ + stream_get (kpw.vpn_name, s, L2VPN_NAME_LEN); + status = stream_getc (s); + if (status) + kpw.flags |= F_PW_STATUS_UP; + else + kpw.flags &= ~F_PW_STATUS_UP; + + main_imsg_compose_lde (IMSG_PW_UPDATE, 0, &kpw, sizeof (kpw)); + + return 0; +} + static void ldp_zebra_connected(struct zclient *zclient) { @@ -507,6 +595,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.h b/ldpd/ldpd.h index 10742cf0dc2d..0a42d49d82f2 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -147,7 +147,8 @@ enum imsg_type { IMSG_LOG, IMSG_ACL_CHECK, IMSG_GET_LABEL_CHUNK, - IMSG_RELEASE_LABEL_CHUNK + IMSG_RELEASE_LABEL_CHUNK, + IMSG_PW_UPDATE }; union ldpd_addr { @@ -531,13 +532,18 @@ struct kroute { }; struct kpw { + char ifname[IF_NAMESIZE]; unsigned short ifindex; int pw_type; + struct in_addr lsr_id; int af; union ldpd_addr nexthop; uint32_t local_label; uint32_t remote_label; uint8_t flags; + uint32_t pwid; + char vpn_name[L2VPN_NAME_LEN]; + unsigned short ac_port_ifindex; }; struct kaddr { diff --git a/lib/log.c b/lib/log.c index c7d4ca2d97fb..f69b97727f8a 100644 --- a/lib/log.c +++ b/lib/log.c @@ -967,6 +967,8 @@ 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_KPW_ADD), + DESC_ENTRY (ZEBRA_KPW_DELETE), }; #undef DESC_ENTRY diff --git a/lib/zclient.c b/lib/zclient.c index 6aea4bd0a31a..cbad34010d71 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1899,6 +1899,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 d3d0a202c5ca..ce17da6b04fb 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -94,6 +94,9 @@ typedef enum { ZEBRA_LABEL_MANAGER_CONNECT, ZEBRA_GET_LABEL_CHUNK, ZEBRA_RELEASE_LABEL_CHUNK, + ZEBRA_KPW_ADD, + ZEBRA_KPW_DELETE, + ZEBRA_PW_STATUS_UPDATE, } zebra_message_types_t; struct redist_proto @@ -164,6 +167,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. */ diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 3e0de3b4631c..a1735a59bd8a 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -33,7 +33,7 @@ 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 \ @@ -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/main.c b/zebra/main.c index 459e6148d845..6ff7ac44e2fb 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_init (); /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 000000000000..f2f1194afe63 --- /dev/null +++ b/zebra/zebra_pw.c @@ -0,0 +1,212 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your option) any + * later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "log.h" +#include "memory.h" +#include "workqueue.h" +#include "zserv.h" + +#include "zebra_pw.h" + +DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire") + +DEFINE_HOOK(pw_change, (struct zebra_pw_t *pw), (pw)) + +extern struct zebra_t zebrad; + +struct zebra_pw_t * +pw_add (void) +{ + return XCALLOC (MTYPE_PW, sizeof (struct zebra_pw_t)); +} + +void +pw_del (struct zebra_pw_t *pw) +{ + XFREE (MTYPE_PW, pw); +} + +/** + * Add PW to work queue + * + * @param pw Pseudowire to enqueue + */ +void +pw_queue_add (struct zebra_pw_t *pw) +{ + assert (pw); + + /* If already scheduled, exit. */ + if (CHECK_FLAG (pw->queue_flags, PW_FLAG_SCHEDULED)) + return; + + work_queue_add (zebrad.pwq, pw); + SET_FLAG (pw->queue_flags, PW_FLAG_SCHEDULED); + +} +/** + * Call on completion of a PseudWire processing. + * + * @param wq Workqueue + * @param data The pseudowire to be removed + */ +static void +pw_queue_del (struct work_queue *wq, void *data) +{ + struct zebra_pw_t *pw; + + pw = (struct zebra_pw_t *)data; + XFREE (MTYPE_PW, pw); + +} +/** + * Remove PWs from workqueue. + * + * It actually sets the ran counter over the max, so it will be + * deleted on next iteration + * + * @param pw Pseudowire to be removed from queue + */ +void +unqueue_pw (struct zebra_pw_t *pw) +{ + struct work_queue *wq; + struct work_queue_item *item; + struct listnode *node, *nnode; + struct zebra_pw_t *item_pw; + + wq = zebrad.pwq; + + for (ALL_LIST_ELEMENTS (wq->items, node, nnode, item)) + { + item_pw = (struct zebra_pw_t *)item->data; + if (item_pw->cmd != pw->cmd) + continue; + if (strncmp (item_pw->ifname, pw->ifname, IF_NAMESIZE) != 0) + continue; + if (item_pw->pwid != pw->pwid) + continue; + if (strncmp(item_pw->vpn_name, pw->vpn_name, L2VPN_NAME_LEN) != 0) + continue; + item->ran = PW_MAX_RETRIES + 1; + } + +} + +static int +check_lsp (struct zebra_pw_t *pw) +{ + struct rib *rib; + afi_t afi = 0; + struct nexthop *nexthop, *tnexthop; + int recursing; + + switch (pw->af) + { + case AF_INET: + afi = AFI_IP; + break; + case AF_INET6: + afi = AFI_IP6; + break; + default: + zlog_warn ("Wrong AF for PW %u at VPN %s!", pw->pwid, pw->vpn_name); + return 1; + } + + /* find route for PW */ + rib = rib_match (afi, SAFI_UNICAST, VRF_DEFAULT, + (union g_addr *)&pw->nexthop, NULL); + if (!rib) + { + zlog_warn ("No rib found for PW %u at VPN %s", pw->pwid, pw->vpn_name); + return 1; + } + /* check labels for each nexthop in Route */ + /* + * 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 || nexthop->nh_label->num_labels == 0) + { + zlog_warn ("No label found in rib for PW %u at VPN %s", pw->pwid, + pw->vpn_name); + return 1; + } + + return 0; +} +/** + * Process a PsuedoWire that is in the queue + * Send it to External Manager + * + * @param wq PW work queue + * @param data The PW itself + */ +static wq_item_status +pw_process (struct work_queue *wq, void *data) +{ + struct zebra_pw_t *pw; + int ret; + + pw = (struct zebra_pw_t *) data; + + ret = check_lsp (pw); + /* install in kernel */ + if (ret == 0) + ret = hook_call (pw_change, pw); + + if (ret != 0) + return WQ_RETRY_LATER; + + return WQ_SUCCESS; + +} +static void +pw_queue_init (struct zebra_t *zebra) +{ + assert (zebra); + + if (! (zebra->pwq = work_queue_new (zebra->master, + "Pseudowire processing"))) + { + zlog_err ("%s: could not initialize work queue!", __func__); + return; + } + + /* fill in the work queue spec */ + zebra->pwq->spec.workfunc = &pw_process; + zebra->pwq->spec.del_item_data = &pw_queue_del; + zebra->pwq->spec.errorfunc = NULL; + zebra->pwq->spec.max_retries = PW_MAX_RETRIES; + zebra->pwq->spec.hold = PW_PROCESS_HOLD_TIME; + + return; +} + +void +zebra_pw_init (void) +{ + pw_queue_init (&zebrad); +} diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h new file mode 100644 index 000000000000..064682eba094 --- /dev/null +++ b/zebra/zebra_pw.h @@ -0,0 +1,51 @@ +#ifndef ZEBRA_PW_H_ +#define ZEBRA_PW_H_ + +#include +#include + +#define PW_PROCESS_HOLD_TIME 10 +#define PW_MAX_RETRIES 3 + +#define PW_SET 1 +#define PW_UNSET 2 + +#define PW_STATUS_DOWN 0 +#define PW_STATUS_UP 1 + +#define L2VPN_NAME_LEN 32 /* must be synced with the one in ldpd/ldpd.h */ + +struct zebra_pw_t +{ + int cmd; /* set or unset */ + char ifname[IF_NAMESIZE]; + unsigned short ifindex; + int pw_type; + struct in_addr lsr_id; + int af; + union { + struct in_addr v4; + struct in6_addr v6; + } nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + uint32_t pwid; + char vpn_name[L2VPN_NAME_LEN]; + unsigned short ac_port_ifindex; + /* Work queue flags */ + u_int32_t queue_flags; +#define PW_FLAG_SCHEDULED (1 << 0) +#define PW_FLAG_INSTALLED (1 << 1) +#define PW_FLAG_CHANGED (1 << 2) +}; + +DECLARE_HOOK(pw_change, (struct zebra_pw_t *pw), (pw)) + +struct zebra_pw_t *pw_add (void); +void pw_del (struct zebra_pw_t *pw); +void pw_queue_add (struct zebra_pw_t *pw); +void unqueue_pw (struct zebra_pw_t *pw); +void zebra_pw_init (void); + +#endif /* ZEBRA_PW_H_ */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 533678f16da2..35bec63fb9a8 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1013,6 +1013,31 @@ 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 (int cmd, struct zserv *client, struct zebra_pw_t *pw, + u_short status, vrf_id_t vrf_id) +{ + struct stream *s; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, cmd, vrf_id); + stream_write (s, pw->ifname, IF_NAMESIZE); + /* stream_putw(s, pw->ifindex); */ + /* stream_putl(s, pw->pwid); */ + stream_put(s, pw->vpn_name, L2VPN_NAME_LEN); + stream_putc(s, 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 @@ -1933,6 +1958,57 @@ zread_label_manager_request (int cmd, struct zserv *client, vrf_id_t vrf_id) } } +static int +zread_kpw (int command, struct zserv *client, u_short length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct zebra_pw_t *pw; + + pw = pw_add(); + /* Get input stream. */ + s = client->ibuf; + + /* Get data. */ + stream_get (pw->ifname, s, IF_NAMESIZE); + pw->ifindex = stream_getw (s); + pw->pw_type = stream_getl (s); + pw->lsr_id.s_addr = stream_getl (s); + pw->af = stream_getl (s); + switch (pw->af) + { + case AF_INET: + pw->nexthop.v4.s_addr = stream_get_ipv4 (s); + break; + case AF_INET6: + stream_get (&pw->nexthop.v6, s, 16); + break; + default: + return (-1); + } + pw->local_label = stream_getl (s); + pw->remote_label = stream_getl (s); + pw->flags = stream_getc (s); + pw->pwid = stream_getl (s); + stream_get (pw->vpn_name, s, L2VPN_NAME_LEN); + pw->ac_port_ifindex = stream_getw (s); + pw->queue_flags = 0; + + /* + zvrf = vrf_info_lookup (vrf_id); + if (!zvrf) + return -1; + */ + + if (command == ZEBRA_KPW_ADD) + pw->cmd = PW_SET; + else if (command == ZEBRA_KPW_DELETE) + pw->cmd = PW_UNSET; + + pw_queue_add (pw); + return 0; +} + /* Cleanup registered nexthops (across VRFs) upon client disconnect. */ static void zebra_client_close_cleanup_rnh (struct zserv *client) @@ -2260,6 +2336,10 @@ zebra_client_read (struct thread *thread) case ZEBRA_RELEASE_LABEL_CHUNK: zread_label_manager_request (command, client, vrf_id); break; + case ZEBRA_KPW_ADD: + case ZEBRA_KPW_DELETE: + zread_kpw(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 cd1948373a8a..10a44a7a8113 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -31,6 +31,8 @@ #include "zclient.h" #include "zebra/zebra_ns.h" +#include "zebra_pw.h" + /* Default port information. */ #define ZEBRA_VTY_PORT 2601 @@ -134,6 +136,9 @@ struct zebra_t /* LSP work queue */ struct work_queue *lsp_process_q; + + /* PseudoWire work queue */ + struct work_queue *pwq; }; extern struct zebra_t zebrad; extern unsigned int multipath_num; @@ -170,6 +175,8 @@ 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 (int cmd, struct zserv *client, struct zebra_pw_t *pw, + u_short status, vrf_id_t vrf_id); extern pid_t pid; From 44d3e2a076a0f99d717f2c43382e8b347bc7e9e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Mon, 29 May 2017 14:48:27 +0200 Subject: [PATCH 2/3] Sync PW status in ldpd after check_lsp --- zebra/zebra_pw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index f2f1194afe63..62dcc6188f9e 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -176,6 +176,15 @@ pw_process (struct work_queue *wq, void *data) /* install in kernel */ if (ret == 0) ret = hook_call (pw_change, pw); + else + { + /* set PW status to DOWN */ + struct listnode *node, *nnode; + struct zserv *client; + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + zsend_pw_update (ZEBRA_PW_STATUS_UPDATE, client, + pw, PW_STATUS_DOWN, VRF_DEFAULT); + } if (ret != 0) return WQ_RETRY_LATER; From 1a9c8f1ff3fe695fa7d22de6862f212f8566f6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Fingen?= Date: Wed, 31 May 2017 14:45:57 +0200 Subject: [PATCH 3/3] zebra: Remove NH label check for PW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the label is implicit null it's not installed in Zebra, so this check doesn't make sense. Signed-off-by: ßingen --- zebra/zebra_pw.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 62dcc6188f9e..b4c092b9caf5 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -142,18 +142,6 @@ check_lsp (struct zebra_pw_t *pw) zlog_warn ("No rib found for PW %u at VPN %s", pw->pwid, pw->vpn_name); return 1; } - /* check labels for each nexthop in Route */ - /* - * 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 || nexthop->nh_label->num_labels == 0) - { - zlog_warn ("No label found in rib for PW %u at VPN %s", pw->pwid, - pw->vpn_name); - return 1; - } return 0; }