]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/ldp_zebra.c
Merge pull request #7090 from dslicenc/comm-list-replace
[mirror_frr.git] / ldpd / ldp_zebra.c
1 /*
2 * Copyright (C) 2016 by Open Source Routing.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 */
19
20 #include <zebra.h>
21
22 #include "prefix.h"
23 #include "stream.h"
24 #include "memory.h"
25 #include "zclient.h"
26 #include "command.h"
27 #include "network.h"
28 #include "linklist.h"
29 #include "mpls.h"
30
31 #include "ldpd.h"
32 #include "ldpe.h"
33 #include "lde.h"
34 #include "ldp_sync.h"
35 #include "log.h"
36 #include "ldp_debug.h"
37
38 static void ifp2kif(struct interface *, struct kif *);
39 static void ifc2kaddr(struct interface *, struct connected *,
40 struct kaddr *);
41 static int ldp_zebra_send_mpls_labels(int, struct kroute *);
42 static int ldp_router_id_update(ZAPI_CALLBACK_ARGS);
43 static int ldp_interface_address_add(ZAPI_CALLBACK_ARGS);
44 static int ldp_interface_address_delete(ZAPI_CALLBACK_ARGS);
45 static int ldp_zebra_read_route(ZAPI_CALLBACK_ARGS);
46 static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS);
47 static void ldp_zebra_connected(struct zclient *);
48 static void ldp_zebra_filter_update(struct access_list *access);
49
50 static void ldp_zebra_opaque_register(void);
51 static void ldp_zebra_opaque_unregister(void);
52 static int ldp_sync_zebra_send_announce(void);
53 static int ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS);
54 static void ldp_sync_zebra_start_hello_timer(void);
55 static int ldp_sync_zebra_hello(struct thread *thread);
56 static void ldp_sync_zebra_init(void);
57
58 static struct zclient *zclient;
59
60 static void
61 ifp2kif(struct interface *ifp, struct kif *kif)
62 {
63 memset(kif, 0, sizeof(*kif));
64 strlcpy(kif->ifname, ifp->name, sizeof(kif->ifname));
65 kif->ifindex = ifp->ifindex;
66 kif->operative = if_is_operative(ifp);
67 if (ifp->ll_type == ZEBRA_LLT_ETHER)
68 memcpy(kif->mac, ifp->hw_addr, ETH_ALEN);
69 }
70
71 static void
72 ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka)
73 {
74 memset(ka, 0, sizeof(*ka));
75 strlcpy(ka->ifname, ifp->name, sizeof(ka->ifname));
76 ka->ifindex = ifp->ifindex;
77 ka->af = ifc->address->family;
78 ka->prefixlen = ifc->address->prefixlen;
79
80 switch (ka->af) {
81 case AF_INET:
82 ka->addr.v4 = ifc->address->u.prefix4;
83 if (ifc->destination)
84 ka->dstbrd.v4 = ifc->destination->u.prefix4;
85 break;
86 case AF_INET6:
87 ka->addr.v6 = ifc->address->u.prefix6;
88 if (ifc->destination)
89 ka->dstbrd.v6 = ifc->destination->u.prefix6;
90 break;
91 default:
92 break;
93 }
94 }
95
96 void
97 pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw)
98 {
99 memset(zpw, 0, sizeof(*zpw));
100 strlcpy(zpw->ifname, pw->ifname, sizeof(zpw->ifname));
101 zpw->ifindex = pw->ifindex;
102 zpw->type = pw->l2vpn->pw_type;
103 zpw->af = pw->af;
104 zpw->nexthop.ipv6 = pw->addr.v6;
105 zpw->local_label = NO_LABEL;
106 zpw->remote_label = NO_LABEL;
107 if (pw->flags & F_PW_CWORD)
108 zpw->flags = F_PSEUDOWIRE_CWORD;
109 zpw->data.ldp.lsr_id = pw->lsr_id;
110 zpw->data.ldp.pwid = pw->pwid;
111 strlcpy(zpw->data.ldp.vpn_name, pw->l2vpn->name,
112 sizeof(zpw->data.ldp.vpn_name));
113 }
114
115 static void
116 ldp_zebra_opaque_register(void)
117 {
118 zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
119 }
120
121 static void
122 ldp_zebra_opaque_unregister(void)
123 {
124 zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
125 }
126
127 int
128 ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *state)
129 {
130 return zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE,
131 (const uint8_t *) state, sizeof(*state));
132 }
133
134 static int
135 ldp_sync_zebra_send_announce(void)
136 {
137 struct ldp_igp_sync_announce announce;
138 announce.proto = ZEBRA_ROUTE_LDP;
139
140 return zclient_send_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE,
141 (const uint8_t *) &announce, sizeof(announce));
142 }
143
144 static int
145 ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
146 {
147 struct stream *s;
148 struct zapi_opaque_msg info;
149 struct ldp_igp_sync_if_state_req state_req;
150
151 s = zclient->ibuf;
152
153 if (zclient_opaque_decode(s, &info) != 0)
154 return -1;
155
156 switch (info.type) {
157 case LDP_IGP_SYNC_IF_STATE_REQUEST:
158 STREAM_GET(&state_req, s, sizeof(state_req));
159 main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req,
160 sizeof(state_req));
161 break;
162 default:
163 break;
164 }
165
166 stream_failure:
167 return 0;
168 }
169
170 static void
171 ldp_sync_zebra_start_hello_timer(void)
172 {
173 thread_add_timer_msec(master, ldp_sync_zebra_hello, NULL, 250, NULL);
174 }
175
176 static int
177 ldp_sync_zebra_hello(struct thread *thread)
178 {
179 static unsigned int sequence = 0;
180 struct ldp_igp_sync_hello hello;
181
182 sequence++;
183
184 hello.proto = ZEBRA_ROUTE_LDP;
185 hello.sequence = sequence;
186
187 zclient_send_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE,
188 (const uint8_t *) &hello, sizeof(hello));
189
190 ldp_sync_zebra_start_hello_timer();
191
192 return (0);
193 }
194
195 static void
196 ldp_sync_zebra_init(void)
197 {
198 ldp_sync_zebra_send_announce();
199
200 ldp_sync_zebra_start_hello_timer();
201 }
202
203
204 static int
205 ldp_zebra_send_mpls_labels(int cmd, struct kroute *kr)
206 {
207 struct zapi_labels zl = {};
208 struct zapi_nexthop *znh;
209
210 if (kr->local_label < MPLS_LABEL_RESERVED_MAX)
211 return (0);
212
213 debug_zebra_out("prefix %s/%u nexthop %s ifindex %u labels %s/%s (%s)",
214 log_addr(kr->af, &kr->prefix), kr->prefixlen,
215 log_addr(kr->af, &kr->nexthop), kr->ifindex,
216 log_label(kr->local_label), log_label(kr->remote_label),
217 (cmd == ZEBRA_MPLS_LABELS_ADD) ? "add" : "delete");
218
219 zl.type = ZEBRA_LSP_LDP;
220 zl.local_label = kr->local_label;
221
222 /* Set prefix. */
223 if (kr->remote_label != NO_LABEL) {
224 SET_FLAG(zl.message, ZAPI_LABELS_FTN);
225 zl.route.prefix.family = kr->af;
226 switch (kr->af) {
227 case AF_INET:
228 zl.route.prefix.u.prefix4 = kr->prefix.v4;
229 break;
230 case AF_INET6:
231 zl.route.prefix.u.prefix6 = kr->prefix.v6;
232 break;
233 default:
234 fatalx("ldp_zebra_send_mpls_labels: unknown af");
235 }
236 zl.route.prefix.prefixlen = kr->prefixlen;
237 zl.route.type = kr->route_type;
238 zl.route.instance = kr->route_instance;
239 }
240
241 /*
242 * For broken LSPs, instruct the forwarding plane to pop the top-level
243 * label and forward packets normally. This is a best-effort attempt
244 * to deliver labeled IP packets to their final destination (instead of
245 * dropping them).
246 */
247 if (kr->remote_label == NO_LABEL)
248 kr->remote_label = MPLS_LABEL_IMPLICIT_NULL;
249
250 /* Set nexthop. */
251 zl.nexthop_num = 1;
252 znh = &zl.nexthops[0];
253 switch (kr->af) {
254 case AF_INET:
255 znh->gate.ipv4 = kr->nexthop.v4;
256 if (kr->ifindex)
257 znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
258 else
259 znh->type = NEXTHOP_TYPE_IPV4;
260 break;
261 case AF_INET6:
262 znh->gate.ipv6 = kr->nexthop.v6;
263 if (kr->ifindex)
264 znh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
265 else
266 znh->type = NEXTHOP_TYPE_IPV6;
267 break;
268 default:
269 break;
270 }
271 znh->ifindex = kr->ifindex;
272 znh->label_num = 1;
273 znh->labels[0] = kr->remote_label;
274
275 return zebra_send_mpls_labels(zclient, cmd, &zl);
276 }
277
278 int
279 kr_change(struct kroute *kr)
280 {
281 return (ldp_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, kr));
282 }
283
284 int
285 kr_delete(struct kroute *kr)
286 {
287 return (ldp_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, kr));
288 }
289
290 int
291 kmpw_add(struct zapi_pw *zpw)
292 {
293 debug_zebra_out("pseudowire %s nexthop %s (add)",
294 zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop));
295
296 return (zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw));
297 }
298
299 int
300 kmpw_del(struct zapi_pw *zpw)
301 {
302 debug_zebra_out("pseudowire %s nexthop %s (del)",
303 zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop));
304
305 return (zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw));
306 }
307
308 int
309 kmpw_set(struct zapi_pw *zpw)
310 {
311 debug_zebra_out("pseudowire %s nexthop %s labels %u/%u (set)",
312 zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop),
313 zpw->local_label, zpw->remote_label);
314
315 return (zebra_send_pw(zclient, ZEBRA_PW_SET, zpw));
316 }
317
318 int
319 kmpw_unset(struct zapi_pw *zpw)
320 {
321 debug_zebra_out("pseudowire %s nexthop %s (unset)",
322 zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop));
323
324 return (zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw));
325 }
326
327 void
328 kif_redistribute(const char *ifname)
329 {
330 struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
331 struct listnode *cnode;
332 struct interface *ifp;
333 struct connected *ifc;
334 struct kif kif;
335 struct kaddr ka;
336
337 FOR_ALL_INTERFACES (vrf, ifp) {
338 if (ifname && strcmp(ifname, ifp->name) != 0)
339 continue;
340
341 ifp2kif(ifp, &kif);
342 main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
343
344 for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) {
345 ifc2kaddr(ifp, ifc, &ka);
346 main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka,
347 sizeof(ka));
348 }
349 }
350 }
351
352 static int
353 ldp_router_id_update(ZAPI_CALLBACK_ARGS)
354 {
355 struct prefix router_id;
356
357 zebra_router_id_update_read(zclient->ibuf, &router_id);
358
359 if (bad_addr_v4(router_id.u.prefix4))
360 return (0);
361
362 debug_zebra_in("router-id update %s", inet_ntoa(router_id.u.prefix4));
363
364 global.rtr_id.s_addr = router_id.u.prefix4.s_addr;
365 main_imsg_compose_ldpe(IMSG_RTRID_UPDATE, 0, &global.rtr_id,
366 sizeof(global.rtr_id));
367
368 return (0);
369 }
370
371 static int
372 ldp_ifp_create(struct interface *ifp)
373 {
374 struct kif kif;
375
376 debug_zebra_in("interface add %s index %d mtu %d", ifp->name,
377 ifp->ifindex, ifp->mtu);
378
379 ifp2kif(ifp, &kif);
380 main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
381
382 return 0;
383 }
384
385 static int
386 ldp_ifp_destroy(struct interface *ifp)
387 {
388 struct kif kif;
389
390 debug_zebra_in("interface delete %s index %d mtu %d", ifp->name,
391 ifp->ifindex, ifp->mtu);
392
393 ifp2kif(ifp, &kif);
394 main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
395
396 return (0);
397 }
398
399 static int
400 ldp_interface_status_change(struct interface *ifp)
401 {
402 struct listnode *node;
403 struct connected *ifc;
404 struct kif kif;
405 struct kaddr ka;
406
407 debug_zebra_in("interface %s state update", ifp->name);
408
409 ifp2kif(ifp, &kif);
410 main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif));
411
412 if (if_is_operative(ifp)) {
413 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
414 ifc2kaddr(ifp, ifc, &ka);
415 main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka,
416 sizeof(ka));
417 }
418 } else {
419 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
420 ifc2kaddr(ifp, ifc, &ka);
421 main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka,
422 sizeof(ka));
423 }
424 }
425
426 return (0);
427 }
428
429 static int ldp_ifp_up(struct interface *ifp)
430 {
431 return ldp_interface_status_change(ifp);
432 }
433
434 static int ldp_ifp_down(struct interface *ifp)
435 {
436 return ldp_interface_status_change(ifp);
437 }
438
439 static int
440 ldp_interface_address_add(ZAPI_CALLBACK_ARGS)
441 {
442 struct connected *ifc;
443 struct interface *ifp;
444 struct kaddr ka;
445
446 ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
447 if (ifc == NULL)
448 return (0);
449
450 ifp = ifc->ifp;
451 ifc2kaddr(ifp, ifc, &ka);
452
453 /* Filter invalid addresses. */
454 if (bad_addr(ka.af, &ka.addr))
455 return (0);
456
457 debug_zebra_in("address add %s/%u interface %s",
458 log_addr(ka.af, &ka.addr), ka.prefixlen, ifp->name);
459
460 /* notify ldpe about new address */
461 main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka));
462
463 return (0);
464 }
465
466 static int
467 ldp_interface_address_delete(ZAPI_CALLBACK_ARGS)
468 {
469 struct connected *ifc;
470 struct interface *ifp;
471 struct kaddr ka;
472
473 ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
474 if (ifc == NULL)
475 return (0);
476
477 ifp = ifc->ifp;
478 ifc2kaddr(ifp, ifc, &ka);
479 connected_free(&ifc);
480
481 /* Filter invalid addresses. */
482 if (bad_addr(ka.af, &ka.addr))
483 return (0);
484
485 debug_zebra_in("address delete %s/%u interface %s",
486 log_addr(ka.af, &ka.addr), ka.prefixlen, ifp->name);
487
488 /* notify ldpe about removed address */
489 main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, sizeof(ka));
490
491 return (0);
492 }
493
494 static int
495 ldp_zebra_read_route(ZAPI_CALLBACK_ARGS)
496 {
497 struct zapi_route api;
498 struct zapi_nexthop *api_nh;
499 struct kroute kr;
500 int i, add = 0;
501
502 if (zapi_route_decode(zclient->ibuf, &api) < 0)
503 return -1;
504
505 /* we completely ignore srcdest routes for now. */
506 if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
507 return (0);
508
509 memset(&kr, 0, sizeof(kr));
510 kr.af = api.prefix.family;
511 switch (kr.af) {
512 case AF_INET:
513 kr.prefix.v4 = api.prefix.u.prefix4;
514 break;
515 case AF_INET6:
516 kr.prefix.v6 = api.prefix.u.prefix6;
517 break;
518 default:
519 break;
520 }
521 kr.prefixlen = api.prefix.prefixlen;
522 kr.route_type = api.type;
523 kr.route_instance = api.instance;
524
525 switch (api.type) {
526 case ZEBRA_ROUTE_CONNECT:
527 kr.flags |= F_CONNECTED;
528 break;
529 case ZEBRA_ROUTE_BGP:
530 /* LDP should follow the IGP and ignore BGP routes */
531 return (0);
532 default:
533 break;
534 }
535
536 if (bad_addr(kr.af, &kr.prefix) ||
537 (kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6)))
538 return (0);
539
540 if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
541 add = 1;
542
543 if (api.nexthop_num == 0)
544 debug_zebra_in("route %s %s/%d (%s)", (add) ? "add" : "delete",
545 log_addr(kr.af, &kr.prefix), kr.prefixlen,
546 zebra_route_string(api.type));
547
548 /* loop through all the nexthops */
549 for (i = 0; i < api.nexthop_num; i++) {
550 api_nh = &api.nexthops[i];
551 switch (api_nh->type) {
552 case NEXTHOP_TYPE_IPV4:
553 if (kr.af != AF_INET)
554 continue;
555 kr.nexthop.v4 = api_nh->gate.ipv4;
556 kr.ifindex = 0;
557 break;
558 case NEXTHOP_TYPE_IPV4_IFINDEX:
559 if (kr.af != AF_INET)
560 continue;
561 kr.nexthop.v4 = api_nh->gate.ipv4;
562 kr.ifindex = api_nh->ifindex;
563 break;
564 case NEXTHOP_TYPE_IPV6:
565 if (kr.af != AF_INET6)
566 continue;
567 kr.nexthop.v6 = api_nh->gate.ipv6;
568 kr.ifindex = 0;
569 break;
570 case NEXTHOP_TYPE_IPV6_IFINDEX:
571 if (kr.af != AF_INET6)
572 continue;
573 kr.nexthop.v6 = api_nh->gate.ipv6;
574 kr.ifindex = api_nh->ifindex;
575 break;
576 case NEXTHOP_TYPE_IFINDEX:
577 if (!(kr.flags & F_CONNECTED))
578 continue;
579 break;
580 default:
581 continue;
582 }
583
584 debug_zebra_in("route %s %s/%d nexthop %s ifindex %u (%s)",
585 (add) ? "add" : "delete", log_addr(kr.af, &kr.prefix),
586 kr.prefixlen, log_addr(kr.af, &kr.nexthop), kr.ifindex,
587 zebra_route_string(api.type));
588
589 if (add)
590 main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr,
591 sizeof(kr));
592 }
593
594 main_imsg_compose_lde(IMSG_NETWORK_UPDATE, 0, &kr, sizeof(kr));
595
596 return (0);
597 }
598
599 /*
600 * Receive PW status update from Zebra and send it to LDE process.
601 */
602 static int
603 ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS)
604 {
605 struct zapi_pw_status zpw;
606
607 zebra_read_pw_status_update(cmd, zclient, length, vrf_id, &zpw);
608
609 debug_zebra_in("pseudowire %s status %s 0x%x", zpw.ifname,
610 (zpw.status == PW_FORWARDING) ? "up" : "down",
611 zpw.status);
612
613 main_imsg_compose_lde(IMSG_PW_UPDATE, 0, &zpw, sizeof(zpw));
614
615 return (0);
616 }
617
618 static void
619 ldp_zebra_connected(struct zclient *zclient)
620 {
621 zclient_send_reg_requests(zclient, VRF_DEFAULT);
622 zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
623 ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);
624 zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6,
625 ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);
626
627 ldp_zebra_opaque_register();
628
629 ldp_sync_zebra_init();
630 }
631
632 static void
633 ldp_zebra_filter_update(struct access_list *access)
634 {
635 struct ldp_access laccess;
636
637 if (access && access->name[0] != '\0') {
638 strlcpy(laccess.name, access->name, sizeof(laccess.name));
639 laccess.type = access->type;
640 debug_evt("%s ACL update filter name %s type %d", __func__,
641 access->name, access->type);
642
643 main_imsg_compose_both(IMSG_FILTER_UPDATE, &laccess,
644 sizeof(laccess));
645 }
646 }
647
648 extern struct zebra_privs_t ldpd_privs;
649
650 void
651 ldp_zebra_init(struct thread_master *master)
652 {
653 if_zapi_callbacks(ldp_ifp_create, ldp_ifp_up,
654 ldp_ifp_down, ldp_ifp_destroy);
655
656 /* Set default values. */
657 zclient = zclient_new(master, &zclient_options_default);
658 zclient_init(zclient, ZEBRA_ROUTE_LDP, 0, &ldpd_privs);
659
660 /* set callbacks */
661 zclient->zebra_connected = ldp_zebra_connected;
662 zclient->router_id_update = ldp_router_id_update;
663 zclient->interface_address_add = ldp_interface_address_add;
664 zclient->interface_address_delete = ldp_interface_address_delete;
665 zclient->redistribute_route_add = ldp_zebra_read_route;
666 zclient->redistribute_route_del = ldp_zebra_read_route;
667 zclient->pw_status_update = ldp_zebra_read_pw_status_update;
668 zclient->opaque_msg_handler = ldp_zebra_opaque_msg_handler;
669
670 /* Access list initialize. */
671 access_list_add_hook(ldp_zebra_filter_update);
672 access_list_delete_hook(ldp_zebra_filter_update);
673 }
674
675 void
676 ldp_zebra_destroy(void)
677 {
678 ldp_zebra_opaque_unregister();
679 zclient_stop(zclient);
680 zclient_free(zclient);
681 zclient = NULL;
682 }