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..b4c092b9caf5 --- /dev/null +++ b/zebra/zebra_pw.c @@ -0,0 +1,209 @@ +/* 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; + } + + 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); + 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; + + 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;