]>
Commit | Line | Data |
---|---|---|
7c551956 DS |
1 | /* |
2 | * Copyright (C) 2016 CumulusNetworks | |
3 | * Donald Sharp | |
4 | * | |
5 | * This file is part of Quagga | |
6 | * | |
7 | * Quagga is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2, or (at your option) any | |
10 | * later version. | |
11 | * | |
12 | * Quagga is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
896014f4 DL |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; see the file COPYING; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
7c551956 DS |
20 | */ |
21 | #include <zebra.h> | |
22 | ||
23 | #include "log.h" | |
24 | #include "linklist.h" | |
f30c50b9 | 25 | #include "command.h" |
4a1ab8e4 | 26 | #include "memory.h" |
05737783 | 27 | #include "srcdest_table.h" |
7c551956 | 28 | |
82f97584 | 29 | #include "vty.h" |
7c551956 DS |
30 | #include "zebra/debug.h" |
31 | #include "zebra/zserv.h" | |
32 | #include "zebra/rib.h" | |
33 | #include "zebra/zebra_vrf.h" | |
5a8dfcd8 | 34 | #include "zebra/zebra_rnh.h" |
7c551956 | 35 | #include "zebra/router-id.h" |
4a1ab8e4 | 36 | #include "zebra/zebra_memory.h" |
28f6dde8 | 37 | #include "zebra/zebra_static.h" |
5a8dfcd8 | 38 | #include "zebra/interface.h" |
7758e3f3 | 39 | #include "zebra/zebra_mpls.h" |
13d60d35 | 40 | #include "zebra/zebra_vxlan.h" |
7c551956 DS |
41 | |
42 | extern struct zebra_t zebrad; | |
43 | ||
44 | /* VRF information update. */ | |
d62a17ae | 45 | static void zebra_vrf_add_update(struct zebra_vrf *zvrf) |
7c551956 | 46 | { |
d62a17ae | 47 | struct listnode *node, *nnode; |
48 | struct zserv *client; | |
7c551956 | 49 | |
d62a17ae | 50 | if (IS_ZEBRA_DEBUG_EVENT) |
51 | zlog_debug("MESSAGE: ZEBRA_VRF_ADD %s", zvrf_name(zvrf)); | |
7c551956 | 52 | |
d62a17ae | 53 | for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client)) |
54 | zsend_vrf_add(client, zvrf); | |
7c551956 DS |
55 | } |
56 | ||
d62a17ae | 57 | static void zebra_vrf_delete_update(struct zebra_vrf *zvrf) |
7c551956 | 58 | { |
d62a17ae | 59 | struct listnode *node, *nnode; |
60 | struct zserv *client; | |
7c551956 | 61 | |
d62a17ae | 62 | if (IS_ZEBRA_DEBUG_EVENT) |
63 | zlog_debug("MESSAGE: ZEBRA_VRF_DELETE %s", zvrf_name(zvrf)); | |
7c551956 | 64 | |
d62a17ae | 65 | for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client)) |
66 | zsend_vrf_delete(client, zvrf); | |
7c551956 DS |
67 | } |
68 | ||
d62a17ae | 69 | void zebra_vrf_update_all(struct zserv *client) |
7c551956 | 70 | { |
d62a17ae | 71 | struct vrf *vrf; |
7c551956 | 72 | |
d62a17ae | 73 | RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) |
74 | { | |
75 | if (vrf->vrf_id) | |
76 | zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id)); | |
77 | } | |
7c551956 DS |
78 | } |
79 | ||
80 | /* Callback upon creating a new VRF. */ | |
d62a17ae | 81 | static int zebra_vrf_new(struct vrf *vrf) |
7c551956 | 82 | { |
d62a17ae | 83 | struct zebra_vrf *zvrf; |
7c551956 | 84 | |
d62a17ae | 85 | if (IS_ZEBRA_DEBUG_EVENT) |
86 | zlog_info("ZVRF %s with id %u", vrf->name, vrf->vrf_id); | |
7c551956 | 87 | |
d62a17ae | 88 | zvrf = zebra_vrf_alloc(); |
89 | zvrf->zns = zebra_ns_lookup( | |
90 | NS_DEFAULT); /* Point to the global (single) NS */ | |
91 | router_id_init(zvrf); | |
92 | vrf->info = zvrf; | |
93 | zvrf->vrf = vrf; | |
34f8e6af | 94 | |
d62a17ae | 95 | return 0; |
7c551956 DS |
96 | } |
97 | ||
fb148af4 DS |
98 | /* |
99 | * Moving an interface amongst different vrf's | |
100 | * causes the interface to get a new ifindex | |
101 | * so we need to find static routes with | |
102 | * the old ifindex and replace with new | |
103 | * ifindex to insert back into the table | |
104 | */ | |
d62a17ae | 105 | void zebra_vrf_static_route_interface_fixup(struct interface *ifp) |
fb148af4 | 106 | { |
d62a17ae | 107 | afi_t afi; |
108 | safi_t safi; | |
109 | struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); | |
110 | struct route_table *stable = NULL; | |
111 | struct route_node *rn = NULL; | |
112 | struct static_route *si = NULL; | |
113 | ||
114 | if (!zvrf) | |
115 | return; | |
116 | ||
117 | for (afi = AFI_IP; afi < AFI_MAX; afi++) { | |
118 | for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { | |
119 | stable = zvrf->stable[afi][safi]; | |
120 | if (stable) | |
121 | for (rn = route_top(stable); rn; | |
122 | rn = route_next(rn)) { | |
123 | if (rn->info) { | |
124 | si = rn->info; | |
125 | if ((strcmp(si->ifname, | |
126 | ifp->name) | |
127 | == 0) | |
128 | && (si->ifindex | |
129 | != ifp->ifindex)) { | |
130 | si->ifindex = | |
131 | ifp->ifindex; | |
132 | static_install_route( | |
133 | afi, safi, | |
134 | &rn->p, NULL, | |
135 | si); | |
136 | } | |
137 | } | |
138 | } | |
139 | } | |
fb148af4 | 140 | } |
fb148af4 DS |
141 | } |
142 | ||
7c551956 | 143 | /* Callback upon enabling a VRF. */ |
d62a17ae | 144 | static int zebra_vrf_enable(struct vrf *vrf) |
7c551956 | 145 | { |
d62a17ae | 146 | struct zebra_vrf *zvrf = vrf->info; |
147 | struct route_table *stable; | |
148 | struct route_node *rn; | |
149 | struct static_route *si; | |
150 | struct interface *ifp; | |
151 | afi_t afi; | |
152 | safi_t safi; | |
153 | ||
154 | assert(zvrf); | |
155 | ||
156 | zebra_vrf_add_update(zvrf); | |
157 | ||
158 | for (afi = AFI_IP; afi < AFI_MAX; afi++) | |
159 | for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { | |
160 | stable = zvrf->stable[afi][safi]; | |
161 | if (!stable) | |
162 | continue; | |
163 | ||
164 | for (rn = route_top(stable); rn; rn = route_next(rn)) | |
165 | for (si = rn->info; si; si = si->next) { | |
166 | si->vrf_id = vrf->vrf_id; | |
167 | if (si->ifindex) { | |
168 | ifp = if_lookup_by_name( | |
169 | si->ifname, si->vrf_id); | |
170 | if (ifp) | |
171 | si->ifindex = | |
172 | ifp->ifindex; | |
173 | else | |
174 | continue; | |
175 | } | |
176 | static_install_route(afi, safi, &rn->p, | |
177 | NULL, si); | |
178 | } | |
fb148af4 | 179 | } |
2414ffe5 | 180 | |
d62a17ae | 181 | return 0; |
7c551956 DS |
182 | } |
183 | ||
184 | /* Callback upon disabling a VRF. */ | |
d62a17ae | 185 | static int zebra_vrf_disable(struct vrf *vrf) |
7c551956 | 186 | { |
d62a17ae | 187 | struct zebra_vrf *zvrf = vrf->info; |
188 | struct route_table *stable; | |
189 | struct route_node *rn; | |
190 | struct static_route *si; | |
191 | afi_t afi; | |
192 | safi_t safi; | |
193 | ||
194 | if (IS_ZEBRA_DEBUG_KERNEL) | |
195 | zlog_debug("VRF %s id %u is now disabled.", zvrf_name(zvrf), | |
196 | zvrf_id(zvrf)); | |
197 | ||
198 | for (afi = AFI_IP; afi < AFI_MAX; afi++) | |
199 | for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { | |
200 | stable = zvrf->stable[afi][safi]; | |
201 | if (!stable) | |
202 | continue; | |
203 | ||
204 | for (rn = route_top(stable); rn; rn = route_next(rn)) | |
205 | for (si = rn->info; si; si = si->next) | |
206 | static_uninstall_route( | |
207 | afi, safi, &rn->p, NULL, si); | |
208 | } | |
209 | ||
210 | return 0; | |
7c551956 DS |
211 | } |
212 | ||
d62a17ae | 213 | static int zebra_vrf_delete(struct vrf *vrf) |
7c551956 | 214 | { |
d62a17ae | 215 | struct zebra_vrf *zvrf = vrf->info; |
216 | struct route_table *table; | |
217 | u_int32_t table_id; | |
218 | afi_t afi; | |
219 | safi_t safi; | |
220 | unsigned i; | |
221 | ||
222 | assert(zvrf); | |
223 | ||
224 | zebra_vrf_delete_update(zvrf); | |
225 | ||
226 | /* uninstall everything */ | |
227 | if (!CHECK_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN)) { | |
228 | struct listnode *node; | |
229 | struct interface *ifp; | |
230 | ||
231 | for (afi = AFI_IP; afi <= AFI_IP6; afi++) { | |
232 | for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; | |
233 | safi++) | |
234 | rib_close_table(zvrf->table[afi][safi]); | |
235 | ||
236 | if (vrf->vrf_id == VRF_DEFAULT) | |
237 | for (table_id = 0; | |
238 | table_id < ZEBRA_KERNEL_TABLE_MAX; | |
239 | table_id++) | |
240 | if (zvrf->other_table[afi][table_id]) | |
241 | rib_close_table( | |
242 | zvrf->other_table | |
243 | [afi] | |
244 | [table_id]); | |
245 | } | |
7c551956 | 246 | |
d62a17ae | 247 | /* Cleanup Vxlan table and update kernel */ |
248 | zebra_vxlan_close_tables(zvrf); | |
7c551956 | 249 | |
d62a17ae | 250 | zebra_mpls_close_tables(zvrf); |
5a8dfcd8 | 251 | |
d62a17ae | 252 | for (ALL_LIST_ELEMENTS_RO(vrf->iflist, node, ifp)) |
253 | if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp); | |
5a8dfcd8 RW |
254 | } |
255 | ||
d62a17ae | 256 | /* clean-up work queues */ |
257 | for (i = 0; i < MQ_SIZE; i++) { | |
258 | struct listnode *lnode, *nnode; | |
259 | struct route_node *rnode; | |
260 | rib_dest_t *dest; | |
261 | ||
262 | for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, | |
263 | rnode)) { | |
264 | dest = rib_dest_from_rnode(rnode); | |
265 | if (dest && rib_dest_vrf(dest) == zvrf) { | |
266 | route_unlock_node(rnode); | |
267 | list_delete_node(zebrad.mq->subq[i], lnode); | |
268 | zebrad.mq->size--; | |
269 | } | |
270 | } | |
5a8dfcd8 | 271 | } |
5a8dfcd8 | 272 | |
d62a17ae | 273 | /* release allocated memory */ |
274 | for (afi = AFI_IP; afi <= AFI_IP6; afi++) { | |
275 | void *table_info; | |
0f124559 | 276 | |
d62a17ae | 277 | for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { |
278 | table = zvrf->table[afi][safi]; | |
279 | table_info = table->info; | |
280 | route_table_finish(table); | |
281 | XFREE(MTYPE_RIB_TABLE_INFO, table_info); | |
282 | ||
283 | table = zvrf->stable[afi][safi]; | |
284 | route_table_finish(table); | |
285 | } | |
5a8dfcd8 | 286 | |
d62a17ae | 287 | for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; |
288 | table_id++) | |
289 | if (zvrf->other_table[afi][table_id]) { | |
290 | table = zvrf->other_table[afi][table_id]; | |
291 | table_info = table->info; | |
292 | route_table_finish(table); | |
293 | XFREE(MTYPE_RIB_TABLE_INFO, table_info); | |
294 | } | |
295 | ||
296 | route_table_finish(zvrf->rnh_table[afi]); | |
297 | route_table_finish(zvrf->import_check_table[afi]); | |
5a8dfcd8 | 298 | } |
d62a17ae | 299 | list_delete_all_node(zvrf->rid_all_sorted_list); |
300 | list_delete_all_node(zvrf->rid_lo_sorted_list); | |
301 | XFREE(MTYPE_ZEBRA_VRF, zvrf); | |
302 | vrf->info = NULL; | |
5a8dfcd8 | 303 | |
d62a17ae | 304 | return 0; |
7c551956 DS |
305 | } |
306 | ||
307 | /* Lookup the routing table in a VRF based on both VRF-Id and table-id. | |
308 | * NOTE: Table-id is relevant only in the Default VRF. | |
309 | */ | |
d62a17ae | 310 | struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, |
311 | vrf_id_t vrf_id, | |
312 | u_int32_t table_id) | |
7c551956 | 313 | { |
d62a17ae | 314 | struct route_table *table = NULL; |
315 | ||
316 | if (afi >= AFI_MAX || safi >= SAFI_MAX) | |
317 | return NULL; | |
318 | ||
319 | if (vrf_id == VRF_DEFAULT) { | |
320 | if (table_id == RT_TABLE_MAIN | |
321 | || table_id == zebrad.rtm_table_default) | |
322 | table = zebra_vrf_table(afi, safi, vrf_id); | |
323 | else | |
324 | table = zebra_vrf_other_route_table(afi, table_id, | |
325 | vrf_id); | |
326 | } else | |
327 | table = zebra_vrf_table(afi, safi, vrf_id); | |
328 | ||
329 | return table; | |
7c551956 DS |
330 | } |
331 | ||
d62a17ae | 332 | static void zebra_rtable_node_cleanup(struct route_table *table, |
333 | struct route_node *node) | |
5a8dfcd8 | 334 | { |
d62a17ae | 335 | struct route_entry *re, *next; |
5a8dfcd8 | 336 | |
d62a17ae | 337 | RNODE_FOREACH_RE_SAFE(node, re, next) |
338 | rib_unlink(node, re); | |
5a8dfcd8 | 339 | |
d62a17ae | 340 | if (node->info) |
341 | XFREE(MTYPE_RIB_DEST, node->info); | |
5a8dfcd8 RW |
342 | } |
343 | ||
d62a17ae | 344 | static void zebra_stable_node_cleanup(struct route_table *table, |
345 | struct route_node *node) | |
5a8dfcd8 | 346 | { |
d62a17ae | 347 | struct static_route *si, *next; |
348 | ||
349 | if (node->info) | |
350 | for (si = node->info; si; si = next) { | |
351 | next = si->next; | |
352 | XFREE(MTYPE_STATIC_ROUTE, si); | |
353 | } | |
5a8dfcd8 RW |
354 | } |
355 | ||
d62a17ae | 356 | static void zebra_rnhtable_node_cleanup(struct route_table *table, |
357 | struct route_node *node) | |
5a8dfcd8 | 358 | { |
d62a17ae | 359 | if (node->info) |
360 | zebra_free_rnh(node->info); | |
5a8dfcd8 RW |
361 | } |
362 | ||
7c551956 DS |
363 | /* |
364 | * Create a routing table for the specific AFI/SAFI in the given VRF. | |
365 | */ | |
d62a17ae | 366 | static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, |
367 | safi_t safi) | |
7c551956 | 368 | { |
d62a17ae | 369 | rib_table_info_t *info; |
370 | struct route_table *table; | |
371 | ||
372 | assert(!zvrf->table[afi][safi]); | |
373 | ||
374 | if (afi == AFI_IP6) | |
375 | table = srcdest_table_init(); | |
376 | else | |
377 | table = route_table_init(); | |
378 | table->cleanup = zebra_rtable_node_cleanup; | |
379 | zvrf->table[afi][safi] = table; | |
380 | ||
381 | info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info)); | |
382 | info->zvrf = zvrf; | |
383 | info->afi = afi; | |
384 | info->safi = safi; | |
385 | table->info = info; | |
7c551956 DS |
386 | } |
387 | ||
388 | /* Allocate new zebra VRF. */ | |
d62a17ae | 389 | struct zebra_vrf *zebra_vrf_alloc(void) |
7c551956 | 390 | { |
d62a17ae | 391 | struct zebra_vrf *zvrf; |
392 | afi_t afi; | |
393 | safi_t safi; | |
394 | struct route_table *table; | |
395 | ||
396 | zvrf = XCALLOC(MTYPE_ZEBRA_VRF, sizeof(struct zebra_vrf)); | |
397 | ||
398 | for (afi = AFI_IP; afi <= AFI_IP6; afi++) { | |
399 | for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { | |
400 | zebra_vrf_table_create(zvrf, afi, safi); | |
401 | if (afi == AFI_IP6) | |
402 | table = srcdest_table_init(); | |
403 | else | |
404 | table = route_table_init(); | |
405 | table->cleanup = zebra_stable_node_cleanup; | |
406 | zvrf->stable[afi][safi] = table; | |
407 | } | |
408 | ||
409 | table = route_table_init(); | |
410 | table->cleanup = zebra_rnhtable_node_cleanup; | |
411 | zvrf->rnh_table[afi] = table; | |
412 | ||
413 | table = route_table_init(); | |
414 | table->cleanup = zebra_rnhtable_node_cleanup; | |
415 | zvrf->import_check_table[afi] = table; | |
416 | } | |
417 | ||
418 | zebra_vxlan_init_tables(zvrf); | |
419 | zebra_mpls_init_tables(zvrf); | |
420 | ||
421 | return zvrf; | |
7c551956 DS |
422 | } |
423 | ||
424 | /* Lookup VRF by identifier. */ | |
d62a17ae | 425 | struct zebra_vrf *zebra_vrf_lookup_by_id(vrf_id_t vrf_id) |
7c551956 | 426 | { |
d62a17ae | 427 | return vrf_info_lookup(vrf_id); |
7c551956 DS |
428 | } |
429 | ||
51bdc5f8 | 430 | /* Lookup VRF by name. */ |
d62a17ae | 431 | struct zebra_vrf *zebra_vrf_lookup_by_name(const char *name) |
871d39b3 | 432 | { |
d62a17ae | 433 | struct vrf *vrf; |
871d39b3 | 434 | |
d62a17ae | 435 | if (!name) |
436 | name = VRF_DEFAULT_NAME; | |
a3d21ef3 | 437 | |
d62a17ae | 438 | vrf = vrf_lookup_by_name(name); |
439 | if (vrf) | |
440 | return ((struct zebra_vrf *)vrf->info); | |
51bdc5f8 | 441 | |
d62a17ae | 442 | return NULL; |
871d39b3 DS |
443 | } |
444 | ||
7c551956 | 445 | /* Lookup the routing table in an enabled VRF. */ |
d62a17ae | 446 | struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id) |
7c551956 | 447 | { |
d62a17ae | 448 | struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id); |
7c551956 | 449 | |
d62a17ae | 450 | if (!zvrf) |
451 | return NULL; | |
7c551956 | 452 | |
d62a17ae | 453 | if (afi >= AFI_MAX || safi >= SAFI_MAX) |
454 | return NULL; | |
7c551956 | 455 | |
d62a17ae | 456 | return zvrf->table[afi][safi]; |
7c551956 DS |
457 | } |
458 | ||
459 | /* Lookup the static routing table in a VRF. */ | |
d62a17ae | 460 | struct route_table *zebra_vrf_static_table(afi_t afi, safi_t safi, |
461 | struct zebra_vrf *zvrf) | |
7c551956 | 462 | { |
d62a17ae | 463 | if (!zvrf) |
464 | return NULL; | |
7c551956 | 465 | |
d62a17ae | 466 | if (afi >= AFI_MAX || safi >= SAFI_MAX) |
467 | return NULL; | |
7c551956 | 468 | |
d62a17ae | 469 | return zvrf->stable[afi][safi]; |
7c551956 DS |
470 | } |
471 | ||
d62a17ae | 472 | struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, |
473 | vrf_id_t vrf_id) | |
7c551956 | 474 | { |
d62a17ae | 475 | struct zebra_vrf *zvrf; |
476 | rib_table_info_t *info; | |
477 | struct route_table *table; | |
478 | ||
479 | zvrf = vrf_info_lookup(vrf_id); | |
480 | if (!zvrf) | |
481 | return NULL; | |
482 | ||
483 | if (afi >= AFI_MAX) | |
484 | return NULL; | |
485 | ||
486 | if (table_id >= ZEBRA_KERNEL_TABLE_MAX) | |
487 | return NULL; | |
488 | ||
489 | if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN) | |
490 | && (table_id != zebrad.rtm_table_default)) { | |
491 | if (zvrf->other_table[afi][table_id] == NULL) { | |
492 | table = (afi == AFI_IP6) ? srcdest_table_init() | |
493 | : route_table_init(); | |
494 | info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info)); | |
495 | info->zvrf = zvrf; | |
496 | info->afi = afi; | |
497 | info->safi = SAFI_UNICAST; | |
498 | table->info = info; | |
499 | zvrf->other_table[afi][table_id] = table; | |
500 | } | |
501 | ||
502 | return (zvrf->other_table[afi][table_id]); | |
503 | } | |
504 | ||
505 | return zvrf->table[afi][SAFI_UNICAST]; | |
7c551956 DS |
506 | } |
507 | ||
d62a17ae | 508 | static int vrf_config_write(struct vty *vty) |
f30c50b9 | 509 | { |
d62a17ae | 510 | struct vrf *vrf; |
511 | struct zebra_vrf *zvrf; | |
512 | ||
513 | RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) | |
514 | { | |
515 | zvrf = vrf->info; | |
516 | if (!zvrf || strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) { | |
517 | vty_out(vty, "vrf %s\n", zvrf_name(zvrf)); | |
518 | vty_out(vty, "!\n"); | |
519 | } | |
520 | } | |
521 | return 0; | |
f30c50b9 RW |
522 | } |
523 | ||
7c551956 | 524 | /* Zebra VRF initialization. */ |
d62a17ae | 525 | void zebra_vrf_init(void) |
7c551956 | 526 | { |
d62a17ae | 527 | vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable, |
528 | zebra_vrf_delete); | |
7c551956 | 529 | |
d62a17ae | 530 | vrf_cmd_init(vrf_config_write); |
7c551956 | 531 | } |