1/* 2 * Copyright (C) 1999 Yasuhiro Ohara 3 * 4 * This file is part of GNU Zebra. 5 * 6 * GNU Zebra is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2, or (at your option) any 9 * later version. 10 * 11 * GNU Zebra is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with GNU Zebra; see the file COPYING. If not, write to the 18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22#include "ospf6d.h" 23 24#include "ospf6_interface.h" 25#include "ospf6_redistribute.h" 26 27#include "ospf6_linklist.h" 28 29/* information about zebra. */ 30struct zclient *zclient = NULL; 31 32/* redistribute function */ 33void 34ospf6_zebra_redistribute (int type) 35{ 36 int top_change = 0; 37 38 if (zclient->redist[type]) 39 return; 40 41 if (! ospf6_is_asbr (ospf6)) 42 top_change = 1; 43 44 zclient->redist[type] = 1; 45 46 if (zclient->sock > 0) 47 zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient->sock, type); 48 49 if (top_change) 50 CALL_CHANGE_HOOK (&top_hook, ospf6); 51} 52 53void 54ospf6_zebra_no_redistribute (int type) 55{ 56 int top_change = 0; 57 58 if (!zclient->redist[type]) 59 return; 60 61 if (ospf6_is_asbr (ospf6)) 62 top_change = 1; 63 64 zclient->redist[type] = 0; 65 66 if (zclient->sock > 0) 67 zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient->sock, type); 68 69 if (top_change) 70 CALL_CHANGE_HOOK (&top_hook, ospf6); 71} 72 73int 74ospf6_zebra_is_redistribute (int type) 75{ 76 return zclient->redist[type]; 77} 78 79 80/* Inteface addition message from zebra. */ 81int 82ospf6_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length) 83{ 84 struct interface *ifp; 85 86 ifp = zebra_interface_add_read (zclient->ibuf); 87 88 /* log */ 89 if (IS_OSPF6_DUMP_ZEBRA) 90 zlog_info ("ZEBRA: I/F add: %s index %d mtu %d", 91 ifp->name, ifp->ifindex, ifp->mtu); 92 93 ospf6_interface_if_add (ifp); 94 95 return 0; 96} 97 98int 99ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) 100{ 101#if 0 102 struct interface *ifp = NULL; 103 104 ifp = zebra_interface_delete_read (zclient->ibuf); 105 106 /* log */ 107 if (IS_OSPF6_DUMP_ZEBRA) 108 zlog_info ("ZEBRA: I/F delete: %s index %d mtu %d", 109 ifp->name, ifp->ifindex, ifp->mtu); 110 111 ospf6_interface_if_del (ifp); 112#endif 113 114 return 0; 115} 116 117int 118ospf6_zebra_if_state_update (int command, struct zclient *zclient, 119 zebra_size_t length) 120{ 121 struct interface *ifp; 122 123 ifp = zebra_interface_state_read (zclient->ibuf); 124 125 /* log */ 126 if (IS_OSPF6_DUMP_ZEBRA) 127 zlog_info ("ZEBRA: I/F %s state change: index %d flags %ld metric %d mtu %d", 128 ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu); 129 130 ospf6_interface_state_update (ifp); 131 return 0; 132} 133 134int 135ospf6_zebra_if_address_update_add (int command, struct zclient *zclient, 136 zebra_size_t length) 137{ 138 struct connected *c; 139 char buf[128]; 140 141 c = zebra_interface_address_add_read (zclient->ibuf); 142 if (c == NULL) 143 return 0; 144 145 if (IS_OSPF6_DUMP_ZEBRA) 146 zlog_info ("ZEBRA: I/F %s address add: %5s %s/%d", 147 c->ifp->name, prefix_family_str (c->address), 148 inet_ntop (c->address->family, &c->address->u.prefix, 149 buf, sizeof (buf)), c->address->prefixlen); 150 151 if (c->address->family == AF_INET6) 152 ospf6_interface_address_update (c->ifp); 153 154 return 0; 155} 156 157int 158ospf6_zebra_if_address_update_delete (int command, struct zclient *zclient, 159 zebra_size_t length) 160{ 161 struct connected *c; 162 char buf[128]; 163 164 c = zebra_interface_address_delete_read (zclient->ibuf); 165 if (c == NULL) 166 return 0; 167 168 if (IS_OSPF6_DUMP_ZEBRA) 169 zlog_info ("ZEBRA: I/F %s address del: %5s %s/%d", 170 c->ifp->name, prefix_family_str (c->address), 171 inet_ntop (c->address->family, &c->address->u.prefix, 172 buf, sizeof (buf)), c->address->prefixlen); 173 174 if (c->address->family == AF_INET6) 175 ospf6_interface_address_update (c->ifp); 176 177 return 0; 178} 179 180 181 182const char *zebra_route_name[ZEBRA_ROUTE_MAX] = 183{ 184 "System", 185 "Kernel", 186 "Connect", 187 "Static", 188 "RIP", 189 "RIPng", 190 "OSPF", 191 "OSPF6", 192 "BGP", 193}; 194 195const char *zebra_route_abname[ZEBRA_ROUTE_MAX] = 196 { "X", "K", "C", "S", "r", "R", "o", "O", "B" }; 197 198int 199ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, 200 zebra_size_t length) 201{ 202 struct stream *s; 203 struct zapi_ipv6 api; 204 unsigned long ifindex; 205 struct in6_addr nexthop; 206 struct prefix_ipv6 p; 207 char prefixstr[128], nexthopstr[128]; 208 209 s = zclient->ibuf; 210 ifindex = 0; 211 memset (&nexthop, 0, sizeof (struct in6_addr)); 212 213 /* Type, flags, message. */ 214 api.type = stream_getc (s); 215 api.flags = stream_getc (s); 216 api.message = stream_getc (s); 217 218 /* IPv6 prefix. */ 219 memset (&p, 0, sizeof (struct prefix_ipv6)); 220 p.family = AF_INET6; 221 p.prefixlen = stream_getc (s); 222 stream_get (&p.prefix, s, PSIZE (p.prefixlen)); 223 224 /* Nexthop, ifindex, distance, metric. */ 225 if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) 226 { 227 api.nexthop_num = stream_getc (s); 228 stream_get (&nexthop, s, 16); 229 } 230 if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) 231 { 232 api.ifindex_num = stream_getc (s); 233 ifindex = stream_getl (s); 234 } 235 if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) 236 api.distance = stream_getc (s); 237 else 238 api.distance = 0; 239 if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) 240 api.metric = stream_getl (s); 241 else 242 api.metric = 0; 243 244 /* log */ 245 if (IS_OSPF6_DUMP_ZEBRA) 246 { 247 prefix2str ((struct prefix *)&p, prefixstr, sizeof (prefixstr)); 248 inet_ntop (AF_INET6, &nexthop, nexthopstr, sizeof (nexthopstr)); 249 250 if (command == ZEBRA_IPV6_ROUTE_ADD) 251 zlog_info ("ZEBRA: Receive add %s route: %s nexthop:%s ifindex:%ld", 252 zebra_route_name [api.type], prefixstr, 253 nexthopstr, ifindex); 254 else 255 zlog_info ("ZEBRA: Receive remove %s route: %s nexthop:%s ifindex:%ld", 256 zebra_route_name [api.type], prefixstr, 257 nexthopstr, ifindex); 258 } 259 260 if (command == ZEBRA_IPV6_ROUTE_ADD) 261 ospf6_redistribute_route_add (api.type, ifindex, &p); 262 else 263 ospf6_redistribute_route_remove (api.type, ifindex, &p); 264 265 return 0; 266} 267 268 269DEFUN (show_zebra, 270 show_zebra_cmd, 271 "show zebra", 272 SHOW_STR 273 "Zebra information\n") 274{ 275 int i; 276 if (!zclient) 277 vty_out (vty, "Not connected to zebra%s", VTY_NEWLINE); 278 279 vty_out (vty, "Zebra Infomation%s", VTY_NEWLINE); 280 vty_out (vty, " enable: %d%s", zclient->enable, VTY_NEWLINE); 281 vty_out (vty, " fail: %d%s", zclient->fail, VTY_NEWLINE); 282 vty_out (vty, " redistribute default: %d%s", zclient->redist_default, 283 VTY_NEWLINE); 284 for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 285 vty_out (vty, " RouteType: %s - %s%s", zebra_route_name[i], 286 zclient->redist[i] ? "redistributed" : "not redistributed", 287 VTY_NEWLINE); 288 return CMD_SUCCESS; 289} 290 291DEFUN (router_zebra, 292 router_zebra_cmd, 293 "router zebra", 294 "Enable a routing process\n" 295 "Make connection to zebra daemon\n") 296{ 297 if (IS_OSPF6_DUMP_CONFIG) 298 zlog_info ("Config: router zebra"); 299 300 vty->node = ZEBRA_NODE; 301 zclient->enable = 1; 302 zclient_start (zclient); 303 return CMD_SUCCESS; 304} 305 306DEFUN (no_router_zebra, 307 no_router_zebra_cmd, 308 "no router zebra", 309 NO_STR 310 "Configure routing process\n" 311 "Disable connection to zebra daemon\n") 312{ 313 if (IS_OSPF6_DUMP_CONFIG) 314 zlog_info ("no router zebra"); 315 316 zclient->enable = 0; 317 zclient_stop (zclient); 318 return CMD_SUCCESS; 319} 320 321/* Zebra configuration write function. */ 322int 323ospf6_zebra_config_write (struct vty *vty) 324{ 325 if (! zclient->enable) 326 { 327 vty_out (vty, "no router zebra%s", VTY_NEWLINE); 328 return 1; 329 } 330 else if (! zclient->redist[ZEBRA_ROUTE_OSPF6]) 331 { 332 vty_out (vty, "router zebra%s", VTY_NEWLINE); 333 vty_out (vty, " no redistribute ospf6%s", VTY_NEWLINE); 334 return 1; 335 } 336 return 0; 337} 338 339/* Zebra node structure. */ 340struct cmd_node zebra_node = 341{ 342 ZEBRA_NODE, 343 "%s(config-zebra)# ", 344}; 345 346#define ADD 0 347#define CHANGE 1 348#define REMOVE 2 349 350static void 351ospf6_zebra_route_update (int type, struct ospf6_route_req *request) 352{ 353 char buf[96], ifname[IFNAMSIZ]; 354 355 struct zapi_ipv6 api; 356 struct ospf6_route_req route; 357 struct linklist *nexthop_list; 358 struct linklist_node node; 359 struct ospf6_nexthop *nexthop = NULL; 360 struct in6_addr **nexthops; 361 unsigned int *ifindexes; 362 struct prefix_ipv6 *p; 363 int i, ret = 0; 364 365 if (IS_OSPF6_DUMP_ZEBRA) 366 { 367 prefix2str (&request->route.prefix, buf, sizeof (buf)); 368 if (type == REMOVE) 369 zlog_info ("ZEBRA: Send remove route: %s", buf); 370 else 371 zlog_info ("ZEBRA: Send add route: %s", buf); 372 } 373 374 if (zclient->sock < 0) 375 { 376 if (IS_OSPF6_DUMP_ZEBRA) 377 zlog_info ("ZEBRA: failed: not connected to zebra"); 378 return; 379 } 380 381 if (request->path.origin.adv_router == ospf6->router_id && 382 (request->path.type == OSPF6_PATH_TYPE_EXTERNAL1 || 383 request->path.type == OSPF6_PATH_TYPE_EXTERNAL2)) 384 { 385 if (IS_OSPF6_DUMP_ZEBRA) 386 zlog_info ("ZEBRA: self originated external route, ignore"); 387 return; 388 } 389 390 /* Only the best path (i.e. the first path of the path-list 391 in 'struct ospf6_route') will be sent to zebra. */ 392 ospf6_route_lookup (&route, &request->route.prefix, request->table); 393 if (memcmp (&route.path, &request->path, sizeof (route.path))) 394 { 395 /* this is not preferred best route, ignore */ 396 if (IS_OSPF6_DUMP_ZEBRA) 397 zlog_info ("ZEBRA: not best path, ignore"); 398 return; 399 } 400 401 nexthop_list = linklist_create (); 402 403 /* for each nexthop */ 404 for (ospf6_route_lookup (&route, &request->route.prefix, request->table); 405 ! ospf6_route_end (&route); ospf6_route_next (&route)) 406 { 407 if (memcmp (&route.path, &request->path, sizeof (route.path))) 408 break; 409 410 #define IN6_IS_ILLEGAL_NEXTHOP(a)\ 411 ((*(u_int32_t *)(void *)(&(a)->s6_addr[0]) == 0xffffffff) &&\ 412 (*(u_int32_t *)(void *)(&(a)->s6_addr[4]) == 0xffffffff) &&\ 413 (*(u_int32_t *)(void *)(&(a)->s6_addr[8]) == 0xffffffff) &&\ 414 (*(u_int32_t *)(void *)(&(a)->s6_addr[12]) == 0xffffffff)) 415 if (IN6_IS_ILLEGAL_NEXTHOP (&route.nexthop.address)) 416 { 417 zlog_warn ("ZEBRA: Illegal nexthop"); 418 continue; 419 } 420 421 if (type == REMOVE && ! memcmp (&route.nexthop, &request->nexthop, 422 sizeof (struct ospf6_nexthop))) 423 continue; 424 425 nexthop = XCALLOC (MTYPE_OSPF6_OTHER, sizeof (struct ospf6_nexthop)); 426 if (! nexthop) 427 { 428 zlog_warn ("ZEBRA: Can't update nexthop: malloc failed"); 429 continue; 430 } 431 432 memcpy (nexthop, &route.nexthop, sizeof (struct ospf6_nexthop)); 433 linklist_add (nexthop, nexthop_list); 434 } 435 436 if (type == REMOVE && nexthop_list->count == 0) 437 { 438 if (! ospf6_route_end (&route)) 439 ospf6_route_next (&route); 440 if (! memcmp (&request->route, &route.route, 441 sizeof (struct ospf6_route))) 442 { 443 /* send 'add' of alternative route */ 444 struct ospf6_path seconde_path; 445 446 if (IS_OSPF6_DUMP_ZEBRA) 447 zlog_info ("ZEBRA: find alternative path to add"); 448 449 linklist_remove (nexthop, nexthop_list); 450 XFREE (MTYPE_OSPF6_OTHER, nexthop); 451 assert (nexthop_list->count == 0); 452 453 memcpy (&seconde_path, &route.path, sizeof (struct ospf6_path)); 454 type = ADD; 455 456 while (! memcmp (&seconde_path, &route.path, 457 sizeof (struct ospf6_path))) 458 { 459 nexthop = XCALLOC (MTYPE_OSPF6_OTHER, 460 sizeof (struct ospf6_nexthop)); 461 if (! nexthop) 462 zlog_warn ("ZEBRA: Can't update nexthop: malloc failed"); 463 else 464 { 465 memcpy (nexthop, &route.nexthop, 466 sizeof (struct ospf6_nexthop)); 467 linklist_add (nexthop, nexthop_list); 468 } 469 470 ospf6_route_next (&route); 471 } 472 } 473 else 474 { 475 /* there is no alternative route. send 'remove' to zebra for 476 requested route */ 477 if (IS_OSPF6_DUMP_ZEBRA) 478 zlog_info ("ZEBRA: can't find alternative path, remove"); 479 480 nexthop = XCALLOC (MTYPE_OSPF6_OTHER, 481 sizeof (struct ospf6_nexthop)); 482 if (! nexthop) 483 zlog_warn ("ZEBRA: Can't update nexthop: malloc failed"); 484 else 485 { 486 memcpy (nexthop, &request->nexthop, 487 sizeof (struct ospf6_nexthop)); 488 linklist_add (nexthop, nexthop_list); 489 } 490 } 491 } 492 493 if (nexthop_list->count == 0) 494 { 495 if (IS_OSPF6_DUMP_ZEBRA) 496 zlog_info ("ZEBRA: no nexthop, ignore"); 497 linklist_delete (nexthop_list); 498 return; 499 } 500 501 /* allocate memory for nexthop_list */ 502 nexthops = XCALLOC (MTYPE_OSPF6_OTHER, 503 nexthop_list->count * sizeof (struct in6_addr *)); 504 if (! nexthops) 505 { 506 zlog_warn ("ZEBRA: Can't update zebra route: malloc failed"); 507 for (linklist_head (nexthop_list, &node); !linklist_end (&node); 508 linklist_next (&node)) 509 XFREE (MTYPE_OSPF6_OTHER, node.data); 510 linklist_delete (nexthop_list); 511 return; 512 } 513 514 /* allocate memory for ifindex_list */ 515 ifindexes = XCALLOC (MTYPE_OSPF6_OTHER, 516 nexthop_list->count * sizeof (unsigned int)); 517 if (! ifindexes) 518 { 519 zlog_warn ("ZEBRA: Can't update zebra route: malloc failed"); 520 for (linklist_head (nexthop_list, &node); !linklist_end (&node); 521 linklist_next (&node)) 522 XFREE (MTYPE_OSPF6_OTHER, node.data); 523 linklist_delete (nexthop_list); 524 XFREE (MTYPE_OSPF6_OTHER, nexthops); 525 return; 526 } 527 528 i = 0; 529 for (linklist_head (nexthop_list, &node); ! linklist_end (&node); 530 linklist_next (&node)) 531 { 532 nexthop = node.data; 533 if (IS_OSPF6_DUMP_ZEBRA) 534 { 535 inet_ntop (AF_INET6, &nexthop->address, buf, sizeof (buf)); 536 if_indextoname (nexthop->ifindex, ifname); 537 zlog_info ("ZEBRA: nexthop: %s%%%s(%d)", 538 buf, ifname, nexthop->ifindex); 539 } 540 nexthops[i] = &nexthop->address; 541 ifindexes[i] = nexthop->ifindex; 542 i++; 543 } 544 545 api.type = ZEBRA_ROUTE_OSPF6; 546 api.flags = 0; 547 api.message = 0; 548 SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); 549 SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); 550 api.nexthop_num = nexthop_list->count; 551 api.nexthop = nexthops; 552 api.ifindex_num = nexthop_list->count; 553 api.ifindex = ifindexes; 554 555 p = (struct prefix_ipv6 *) &request->route.prefix; 556 if (type == REMOVE && nexthop_list->count == 1) 557 ret = zapi_ipv6_delete (zclient, p, &api); 558 else 559 ret = zapi_ipv6_add (zclient, p, &api); 560 561 if (ret < 0) 562 zlog_err ("ZEBRA: zapi_ipv6_add () failed: %s", strerror (errno)); 563 564 for (linklist_head (nexthop_list, &node); !linklist_end (&node); 565 linklist_next (&node)) 566 XFREE (MTYPE_OSPF6_OTHER, node.data); 567 linklist_delete (nexthop_list); 568 XFREE (MTYPE_OSPF6_OTHER, nexthops); 569 XFREE (MTYPE_OSPF6_OTHER, ifindexes); 570 571 return; 572} 573 574void 575ospf6_zebra_route_update_add (struct ospf6_route_req *request) 576{ 577 ospf6_zebra_route_update (ADD, request); 578} 579 580void 581ospf6_zebra_route_update_remove (struct ospf6_route_req *request) 582{ 583 ospf6_zebra_route_update (REMOVE, request); 584} 585 586static void 587ospf6_zebra_redistribute_ospf6 () 588{ 589 struct route_node *node; 590 591 for (node = route_top (ospf6->route_table->table); node; 592 node = route_next (node)) 593 { 594 if (! node || ! node->info) 595 continue; 596 ospf6_zebra_route_update_add (node->info); 597 } 598} 599 600static void 601ospf6_zebra_no_redistribute_ospf6 () 602{ 603 struct route_node *node; 604 605 if (! ospf6) 606 return; 607 608 for (node = route_top (ospf6->route_table->table); node; 609 node = route_next (node)) 610 { 611 if (! node || ! node->info) 612 continue; 613 614 ospf6_zebra_route_update_remove (node->info); 615 } 616} 617 618 619DEFUN (redistribute_ospf6, 620 redistribute_ospf6_cmd, 621 "redistribute ospf6", 622 "Redistribute control\n" 623 "OSPF6 route\n") 624{ 625 /* log */ 626 if (IS_OSPF6_DUMP_CONFIG) 627 zlog_info ("Config: redistribute ospf6"); 628 629 zclient->redist[ZEBRA_ROUTE_OSPF6] = 1; 630 631 /* set zebra route table */ 632 ospf6_zebra_redistribute_ospf6 (); 633 634 return CMD_SUCCESS; 635} 636 637DEFUN (no_redistribute_ospf6, 638 no_redistribute_ospf6_cmd, 639 "no redistribute ospf6", 640 NO_STR 641 "Redistribute control\n" 642 "OSPF6 route\n") 643{ 644 /* log */ 645 if (IS_OSPF6_DUMP_CONFIG) 646 zlog_info ("Config: no redistribute ospf6"); 647 648 zclient->redist[ZEBRA_ROUTE_OSPF6] = 0; 649 650 if (! ospf6) 651 return CMD_SUCCESS; 652 653 /* clean up zebra route table */ 654 ospf6_zebra_no_redistribute_ospf6 (); 655 656 ospf6_route_hook_unregister (ospf6_zebra_route_update_add, 657 ospf6_zebra_route_update_add, 658 ospf6_zebra_route_update_remove, 659 ospf6->route_table); 660 661 return CMD_SUCCESS; 662} 663 664void 665ospf6_zebra_init () 666{ 667 /* Allocate zebra structure. */ 668 zclient = zclient_new (); 669 zclient_init (zclient, ZEBRA_ROUTE_OSPF6); 670 zclient->interface_add = ospf6_zebra_if_add; 671 zclient->interface_delete = ospf6_zebra_if_del; 672 zclient->interface_up = ospf6_zebra_if_state_update; 673 zclient->interface_down = ospf6_zebra_if_state_update; 674 zclient->interface_address_add = ospf6_zebra_if_address_update_add; 675 zclient->interface_address_delete = ospf6_zebra_if_address_update_delete; 676 zclient->ipv4_route_add = NULL; 677 zclient->ipv4_route_delete = NULL; 678 zclient->ipv6_route_add = ospf6_zebra_read_ipv6; 679 zclient->ipv6_route_delete = ospf6_zebra_read_ipv6; 680 681 /* redistribute connected route by default */ 682 /* ospf6_zebra_redistribute (ZEBRA_ROUTE_CONNECT); */ 683 684 /* Install zebra node. */ 685 install_node (&zebra_node, ospf6_zebra_config_write); 686 687 /* Install command element for zebra node. */ 688 install_element (VIEW_NODE, &show_zebra_cmd); 689 install_element (ENABLE_NODE, &show_zebra_cmd); 690 install_element (CONFIG_NODE, &router_zebra_cmd); 691 install_element (CONFIG_NODE, &no_router_zebra_cmd); 692 install_default (ZEBRA_NODE); 693 install_element (ZEBRA_NODE, &redistribute_ospf6_cmd); 694 install_element (ZEBRA_NODE, &no_redistribute_ospf6_cmd); 695 696#if 0 697 hook.name = "ZebraRouteUpdate"; 698 hook.hook_add = ospf6_zebra_route_update_add; 699 hook.hook_change = ospf6_zebra_route_update_add; 700 hook.hook_remove = ospf6_zebra_route_update_remove; 701 ospf6_hook_register (&hook, &route_hook); 702#endif 703 704 return; 705} 706 707void 708ospf6_zebra_finish () 709{ 710 zclient_stop (zclient); 711 zclient_free (zclient); 712 zclient = (struct zclient *) NULL; 713} 714 715