Line data Source code
1 : /*
2 : * OSPFv3 virtual link implementation.
3 : *
4 : * Copyright (C) 2021 Network Device Education Foundation, Inc. ("NetDEF")
5 : * Rafael Zalamena
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 2 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * This program 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 : *
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
20 : */
21 :
22 : #include "lib/zebra.h"
23 : #include "lib/command.h"
24 : #include "lib/memory.h"
25 :
26 : #include "ospf6d/ospf6d.h"
27 : #include "ospf6d/ospf6_area.h"
28 : #include "ospf6d/ospf6_interface.h"
29 : #include "ospf6d/ospf6_lsa.h"
30 : #include "ospf6d/ospf6_message.h"
31 : #include "ospf6d/ospf6_neighbor.h"
32 : #include "ospf6d/ospf6_top.h"
33 : #include "ospf6d/ospf6_vlink.h"
34 : #include "ospf6d/ospf6_proto.h"
35 : #include "ospf6d/ospf6_lsdb.h"
36 : #include "ospf6d/ospf6_intra.h"
37 :
38 : #ifndef VTYSH_EXTRACT_PL
39 : #include "ospf6d/ospf6_vlink_clippy.c"
40 : #endif /* VTYSH_EXTRACT_PL */
41 :
42 : static uint8_t debug_vlink;
43 :
44 : /* implementation note/rosetta stone:
45 : *
46 : * The most non-obvious aspect of this virtual link implementation is that it
47 : * uses ONE dummy interface for ALL virtual links, and that dummy interface is
48 : * a member of the backbone area. Virtual links are neighbors on this dummy
49 : * interface, so everything works as usual for any neighbor in the backbone
50 : * area. This dummy interface also ensures that the backbone area isn't
51 : * "empty" if the router only has virtual links.
52 : *
53 : * As a downside, this means the timers that are normally an interface
54 : * property are now duplicated as a virtual link property, but that's not a
55 : * huge impact.
56 : */
57 :
58 : /* remaining TODOs:
59 : *
60 : * - verify router LSA updates from other routers that change their LA
61 : * correctly propagate into the address being used for virtual links. It
62 : * works for most cases but there may be some corner cases left that don't
63 : * update the address correctly.
64 : *
65 : * - authentication options for virtual links are completely missing
66 : *
67 : * - check if multiple parallel virtual links through different areas to the
68 : * same router make sense. (no clue if it does. not currently supported.)
69 : *
70 : * - show commands. There are none.
71 : */
72 :
73 48 : DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VIRTUAL_LINK, "OSPF6 virtual link data");
74 48 : DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VLINK_ADDR, "OSPF6 virtual link address base");
75 :
76 0 : static int ospf6_vlink_cmp(const struct ospf6_virtual_link *a,
77 : const struct ospf6_virtual_link *b)
78 : {
79 0 : return IPV4_ADDR_CMP(&a->remote, &b->remote);
80 : }
81 :
82 0 : DECLARE_RBTREE_UNIQ(ospf6_virtual_links, struct ospf6_virtual_link, item,
83 : ospf6_vlink_cmp);
84 :
85 147 : DECLARE_RBTREE_UNIQ(ospf6_area_vlinks, struct ospf6_virtual_link, areaitem,
86 : ospf6_vlink_cmp);
87 :
88 0 : static int ospf6_vlink_addr_cmp(const struct ospf6_vlink_addr *a,
89 : const struct ospf6_vlink_addr *b)
90 : {
91 0 : return IPV6_ADDR_CMP(&a->remote_addr, &b->remote_addr);
92 : }
93 :
94 0 : DECLARE_RBTREE_UNIQ(ospf6_vlink_addrs, struct ospf6_vlink_addr, item,
95 : ospf6_vlink_addr_cmp);
96 :
97 138 : size_t ospf6_vlink_area_vlcount(struct ospf6_area *oa)
98 : {
99 138 : return ospf6_area_vlinks_count(oa->vlinks);
100 : }
101 :
102 0 : static void ospf6_vlink_prep(struct ospf6 *o)
103 : {
104 0 : struct interface *ifp;
105 :
106 0 : if (o->vlink_oi)
107 : return;
108 :
109 0 : if (debug_vlink)
110 0 : zlog_debug("creating OSPFv3 virtual link interface for VRF %u",
111 : o->vrf_id);
112 :
113 : /* it is very intentional that the dummy interface has an empty
114 : * interface name; otherwise the user could try to "configure" things
115 : * on this interface (which would just wreak havoc.) With an empty
116 : * interface name, the CLI can't invoke "interface XYZ" commands.
117 : */
118 0 : ifp = if_create_virtual(vrf_lookup_by_id(o->vrf_id));
119 0 : UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
120 0 : SET_FLAG(ifp->flags, IFF_LOOPBACK | IFF_VIRTUAL);
121 :
122 0 : ifp->desc = asprintfrr(MTYPE_TMP, "OSPFv3 virtual link (VRF %u)",
123 : o->vrf_id);
124 :
125 0 : o->vlink_oi = ospf6_interface_basic_create(ifp);
126 0 : o->vlink_oi->type = OSPF_IFTYPE_VIRTUALLINK;
127 0 : o->vlink_oi->state = OSPF6_INTERFACE_VIRTUALLINK;
128 0 : o->vlink_oi->ifmtu = 65520;
129 0 : o->vlink_oi->mtu_ignore = true;
130 :
131 0 : o->vlink_oi->area_id = 0;
132 0 : o->vlink_oi->area_id_format = OSPF6_AREA_FMT_DOTTEDQUAD;
133 :
134 0 : o->last_vlink_ifindex = 0x80000000;
135 :
136 0 : ospf6_interface_start(o->vlink_oi);
137 : }
138 :
139 0 : static void ospf6_vlink_unprep(struct ospf6 *o)
140 : {
141 0 : struct interface *ifp;
142 :
143 0 : if (ospf6_virtual_links_count(o->vlinks))
144 0 : return;
145 :
146 0 : if (debug_vlink)
147 0 : zlog_debug("removing OSPFv3 virtual link interface for VRF %u",
148 : o->vrf_id);
149 :
150 0 : ospf6_interface_stop(o->vlink_oi);
151 :
152 0 : ifp = o->vlink_oi->interface;
153 0 : ospf6_interface_basic_delete(o->vlink_oi);
154 0 : o->vlink_oi = NULL;
155 :
156 0 : if_delete(&ifp);
157 : }
158 :
159 : /**
160 : * Find OSPF virtual link by attributes.
161 : */
162 0 : struct ospf6_virtual_link *ospf6_virtual_link_find(struct ospf6 *o,
163 : struct in_addr remote)
164 : {
165 0 : struct ospf6_virtual_link ref;
166 :
167 0 : ref.remote = remote;
168 0 : return ospf6_virtual_links_find(o->vlinks, &ref);
169 : }
170 :
171 0 : static struct ospf6_vlink_addr *ospf6_vlink_addr_new(
172 : struct ospf6_virtual_link *vlink, struct in6_addr *addr)
173 : {
174 0 : struct ospf6_vlink_addr *vaddr;
175 :
176 0 : vaddr = XCALLOC(MTYPE_OSPF6_VLINK_ADDR, sizeof(*vaddr));
177 0 : vaddr->remote_addr = *addr;
178 0 : return vaddr;
179 : }
180 :
181 0 : static void ospf6_vlink_addr_delall(struct ospf6_vlink_addrs_head *head)
182 : {
183 0 : struct ospf6_vlink_addr *vaddr;
184 :
185 0 : while ((vaddr = ospf6_vlink_addrs_pop(head))) {
186 0 : XFREE(MTYPE_OSPF6_VLINK_ADDR, vaddr);
187 : }
188 0 : }
189 :
190 0 : static void ospf6_vlink_hello(struct thread *t)
191 : {
192 0 : struct ospf6_virtual_link *vlink = THREAD_ARG(t);
193 :
194 0 : ospf6_hello_send_addr(vlink->ospf6->vlink_oi, vlink, &vlink->transport);
195 :
196 0 : thread_add_timer(master, ospf6_vlink_hello, vlink,
197 : vlink->hello_interval, &vlink->t_hello);
198 0 : }
199 :
200 0 : static void ospf6_vlink_refresh(struct ospf6_virtual_link *vlink)
201 : {
202 0 : struct ospf6_vlink_addrs_head oldaddrs[1];
203 0 : struct ospf6_vlink_addr *vaddr, ref, *bestaddr = NULL;
204 0 : struct ospf6_route *ort;
205 0 : struct ospf6_lsa *lsa;
206 0 : struct prefix pfx;
207 0 : bool changed = false;
208 :
209 0 : ospf6_vlink_addrs_init(oldaddrs);
210 0 : ospf6_vlink_addrs_swap_all(oldaddrs, vlink->addrs);
211 :
212 0 : for (ALL_LSDB_TYPED_ADVRTR(vlink->area->lsdb,
213 : htons(OSPF6_LSTYPE_INTRA_PREFIX),
214 : vlink->remote.s_addr, lsa)) {
215 0 : struct ospf6_intra_prefix_lsa *lsa_intra;
216 0 : struct ospf6_prefix *op;
217 0 : size_t i, count;
218 :
219 0 : lsa_intra = (struct ospf6_intra_prefix_lsa *)(lsa->header + 1);
220 0 : op = (struct ospf6_prefix *)(lsa_intra + 1);
221 0 : count = ntohs(lsa_intra->prefix_num);
222 :
223 0 : for (i = 0; i < count; i++, op = OSPF6_PREFIX_NEXT(op)) {
224 0 : struct in6_addr *addr = op->addr;
225 :
226 0 : if (!(op->prefix_options & OSPF6_PREFIX_OPTION_LA))
227 0 : continue;
228 :
229 0 : ref.remote_addr = *addr;
230 0 : vaddr = ospf6_vlink_addrs_find(oldaddrs, &ref);
231 0 : if (vaddr)
232 0 : ospf6_vlink_addrs_del(oldaddrs, vaddr);
233 : else
234 0 : vaddr = ospf6_vlink_addr_new(vlink, addr);
235 0 : ospf6_vlink_addrs_add(vlink->addrs, vaddr);
236 :
237 0 : if (!bestaddr)
238 0 : bestaddr = vaddr;
239 : }
240 : }
241 :
242 0 : ospf6_linkstate_prefix(vlink->remote.s_addr, INADDR_ANY, &pfx);
243 0 : ort = ospf6_route_lookup(&pfx, vlink->area->spf_table);
244 :
245 0 : if (!ort || !bestaddr) {
246 0 : const char *reason = ort ? "no routable address" : "no route";
247 :
248 0 : if (vlink->spf_cost == ~0U)
249 0 : goto out;
250 :
251 0 : changed = true;
252 0 : if (debug_vlink)
253 0 : zlog_debug(
254 : "Virtual link to %pI4 through area %pI4 down (%s)",
255 : &vlink->remote, &vlink->area->area_id, reason);
256 :
257 0 : vlink->spf_cost = ~0U;
258 0 : memset(&vlink->transport, 0, sizeof(vlink->transport));
259 0 : ospf6_neighbor_vlink_change(vlink->nbr, false);
260 :
261 0 : THREAD_OFF(vlink->t_hello);
262 0 : goto out;
263 : }
264 :
265 0 : if (debug_vlink)
266 0 : zlog_debug(
267 : "Virtual link to %pI4 (area %pI4): using %pI6, cost %u",
268 : &vlink->remote, &vlink->area->area_id,
269 : &bestaddr->remote_addr, ort->path.cost);
270 :
271 0 : if (!IPV6_ADDR_SAME(&vlink->transport, &bestaddr->remote_addr)) {
272 0 : if (debug_vlink)
273 0 : zlog_debug(
274 : "Virtual link to %pI4 (area %pI4) address changed",
275 : &vlink->remote, &vlink->area->area_id);
276 :
277 0 : vlink->transport = bestaddr->remote_addr;
278 0 : changed = true;
279 : }
280 0 : if (ort->path.cost != vlink->spf_cost) {
281 0 : if (debug_vlink)
282 0 : zlog_debug(
283 : "Virtual link to %pI4 (area %pI4) cost changed",
284 : &vlink->remote, &vlink->area->area_id);
285 :
286 0 : vlink->spf_cost = ort->path.cost;
287 0 : changed = true;
288 : }
289 :
290 0 : if (vlink->nbr->state < OSPF6_NEIGHBOR_ATTEMPT) {
291 0 : ospf6_neighbor_vlink_change(vlink->nbr, true);
292 0 : thread_add_timer(master, ospf6_vlink_hello, vlink,
293 : vlink->hello_interval, &vlink->t_hello);
294 : }
295 0 : out:
296 0 : if (changed)
297 0 : OSPF6_ROUTER_LSA_SCHEDULE(vlink->ospf6->backbone);
298 :
299 0 : ospf6_vlink_addr_delall(oldaddrs);
300 0 : }
301 :
302 86 : void ospf6_vlink_area_calculation(struct ospf6_area *oa)
303 : {
304 86 : struct ospf6_virtual_link *vlink;
305 :
306 86 : assert(oa->area_id != 0);
307 :
308 86 : if (debug_vlink)
309 68 : zlog_debug("recalculating %zu virtual links on area %pI4",
310 : ospf6_area_vlinks_count(oa->vlinks), &oa->area_id);
311 :
312 172 : frr_each (ospf6_area_vlinks, oa->vlinks, vlink)
313 0 : ospf6_vlink_refresh(vlink);
314 86 : }
315 :
316 193 : void ospf6_vlink_prefix_update(struct ospf6_area *oa, in_addr_t rtr)
317 : {
318 193 : struct ospf6_virtual_link *vlink, ref;
319 :
320 193 : if (oa->area_id == 0)
321 193 : return;
322 :
323 42 : ref.remote.s_addr = rtr;
324 42 : vlink = ospf6_area_vlinks_find(oa->vlinks, &ref);
325 42 : if (!vlink)
326 : return;
327 :
328 0 : if (debug_vlink)
329 0 : zlog_debug("recalculating virtual link addrs in %pI4 for %pI4",
330 : &oa->area_id, &rtr);
331 :
332 0 : ospf6_vlink_refresh(vlink);
333 : }
334 :
335 : /**
336 : * OSPF virtual link registration function.
337 : *
338 : * Allocates memory and registers virtual link in OSPF instance / area.
339 : */
340 0 : static struct ospf6_virtual_link *ospf6_virtual_link_new(struct ospf6_area *oa,
341 : struct in_addr remote)
342 : {
343 0 : struct ospf6_virtual_link *vlink;
344 :
345 0 : ospf6_vlink_prep(oa->ospf6);
346 :
347 0 : vlink = XCALLOC(MTYPE_OSPF6_VIRTUAL_LINK, sizeof(*vlink));
348 0 : vlink->ospf6 = oa->ospf6;
349 0 : vlink->area = oa;
350 0 : vlink->remote = remote;
351 0 : vlink->spf_cost = ~0U;
352 0 : ospf6_vlink_addrs_init(vlink->addrs);
353 :
354 0 : oa->ospf6->last_vlink_ifindex++;
355 0 : if (oa->ospf6->last_vlink_ifindex == 0)
356 0 : oa->ospf6->last_vlink_ifindex = 0x80000000;
357 0 : vlink->v_ifindex = htonl(oa->ospf6->last_vlink_ifindex);
358 :
359 0 : vlink->dead_interval = VLINK_DEFAULT_DEAD_INTERVAL;
360 0 : vlink->hello_interval = VLINK_DEFAULT_HELLO_INTERVAL;
361 0 : vlink->transmit_delay = VLINK_DEFAULT_TRANSMIT_DELAY;
362 0 : vlink->retransmit_interval = VLINK_DEFAULT_RETRANSMIT_INTERVAL;
363 :
364 0 : ospf6_virtual_links_add(oa->ospf6->vlinks, vlink);
365 0 : ospf6_area_vlinks_add(oa->vlinks, vlink);
366 :
367 0 : vlink->nbr = ospf6_neighbor_create(remote.s_addr, oa->ospf6->vlink_oi);
368 0 : vlink->nbr->vlink = vlink;
369 :
370 0 : if (ospf6_area_vlinks_count(oa->vlinks) == 1)
371 : /* make sure we have an intra-prefix with LA advertised */
372 0 : OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa);
373 :
374 0 : return vlink;
375 : }
376 :
377 : /**
378 : * OSPF virtual link removal function.
379 : *
380 : * Tears down the virtual link, remove area registration and free resources.
381 : */
382 0 : static void ospf6_virtual_link_free(struct ospf6_virtual_link **vlink)
383 : {
384 0 : struct ospf6_area *oa;
385 :
386 0 : if ((*vlink) == NULL)
387 : return;
388 :
389 0 : oa = (*vlink)->area;
390 0 : THREAD_OFF((*vlink)->t_hello);
391 :
392 0 : if (ospf6_area_vlinks_count(oa->vlinks) == 0)
393 : /* drop LA, maybe */
394 0 : OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa);
395 :
396 0 : (*vlink)->nbr->vlink = NULL;
397 0 : ospf6_neighbor_delete((*vlink)->nbr);
398 :
399 0 : ospf6_area_vlinks_del(oa->vlinks, (*vlink));
400 0 : ospf6_virtual_links_del(oa->ospf6->vlinks, (*vlink));
401 0 : ospf6_vlink_addr_delall((*vlink)->addrs);
402 0 : ospf6_vlink_addrs_fini((*vlink)->addrs);
403 0 : XFREE(MTYPE_OSPF6_VIRTUAL_LINK, (*vlink));
404 :
405 0 : ospf6_vlink_unprep(oa->ospf6);
406 : }
407 :
408 0 : void ospf6_vlink_init(struct ospf6 *o)
409 : {
410 0 : ospf6_virtual_links_init(o->vlinks);
411 0 : }
412 :
413 0 : void ospf6_vlink_fini(struct ospf6 *o)
414 : {
415 0 : struct ospf6_virtual_link *vlink;
416 :
417 0 : while ((vlink = ospf6_virtual_links_first(o->vlinks)))
418 0 : ospf6_virtual_link_free(&vlink);
419 :
420 0 : ospf6_virtual_links_fini(o->vlinks);
421 0 : }
422 :
423 19 : void ospf6_vlink_area_init(struct ospf6_area *oa)
424 : {
425 19 : ospf6_area_vlinks_init(oa->vlinks);
426 19 : }
427 :
428 19 : void ospf6_vlink_area_fini(struct ospf6_area *oa)
429 : {
430 19 : struct ospf6_virtual_link *vlink;
431 :
432 38 : while ((vlink = ospf6_area_vlinks_first(oa->vlinks)))
433 0 : ospf6_virtual_link_free(&vlink);
434 :
435 19 : ospf6_area_vlinks_fini(oa->vlinks);
436 19 : }
437 :
438 : /*
439 : * Commands
440 : */
441 8 : DEFPY (debug_ospf6_vlink,
442 : debug_ospf6_vlink_cmd,
443 : "[no] debug ospf6 virtual-link",
444 : NO_STR
445 : DEBUG_STR
446 : OSPF6_STR
447 : "Debug OSPFv3 Virtual link management\n"
448 : )
449 : {
450 8 : uint8_t flag = (vty->node == CONFIG_NODE) ? (1 << 1) : (1 << 0);
451 :
452 8 : if (no)
453 0 : debug_vlink &= ~flag;
454 : else
455 8 : debug_vlink |= flag;
456 8 : return CMD_SUCCESS;
457 : }
458 :
459 0 : void config_write_ospf6_debug_vlink(struct vty *vty)
460 : {
461 0 : if (debug_vlink & (1 << 1))
462 0 : vty_out(vty, "debug ospf6 virtual-link\n");
463 0 : }
464 :
465 0 : DEFPY(ospf6_vlink_config, ospf6_vlink_config_cmd,
466 : "[no] area <A.B.C.D$area_dot|(0-4294967295)$area_num> virtual-link A.B.C.D$peer "
467 : "[{hello-interval (1-65535)$hello|retransmit-interval (1-65535)$retx"
468 : "|transmit-delay (1-65535)$tx|dead-interval (1-65535)$dead}]",
469 : NO_STR
470 : "OSPF area parameters\n"
471 : "OSPF area ID in IP address format\n"
472 : "OSPF area ID as a decimal value\n"
473 : "Configure a virtual link\n"
474 : "Router ID of the remote ABR\n"
475 : "Hello packets interval\n"
476 : "Hello packets interval in seconds\n"
477 : "Retransmission interval between lost link state advertisements\n"
478 : "Retransmission interval between lost link state advertisements in seconds\n"
479 : "Link state transmission interval\n"
480 : "Link state transmission interval in seconds\n"
481 : "Interval before declaring a peer dead\n"
482 : "Interval before declaring a peer dead in seconds\n")
483 : {
484 0 : struct ospf6_virtual_link *vlink;
485 0 : struct ospf6_area *oa;
486 0 : uint32_t area;
487 0 : VTY_DECLVAR_CONTEXT(ospf6, o);
488 :
489 0 : if (area_dot_str)
490 0 : area = area_dot.s_addr;
491 : else
492 0 : area = htonl(area_num);
493 :
494 : /* Validations. */
495 0 : if (area == 0) {
496 0 : vty_out(vty,
497 : "Virtual links cannot be configured on backbone\n");
498 0 : return CMD_WARNING;
499 : }
500 :
501 0 : oa = ospf6_area_lookup(area, o);
502 0 : if (oa == NULL) {
503 0 : vty_out(vty, "OSPFv3 area %u does not exist\n", area);
504 0 : return CMD_WARNING;
505 : }
506 :
507 0 : if (IS_AREA_STUB(oa) || IS_AREA_NSSA(oa)) {
508 0 : vty_out(vty,
509 : "Virtual link can only be configured on regular areas");
510 0 : return CMD_WARNING;
511 : }
512 :
513 0 : vlink = ospf6_virtual_link_find(o, peer);
514 0 : if (vlink && vlink->area->area_id != area) {
515 0 : vty_out(vty,
516 : "Virtual link to %pI4 exists in different area %pI4",
517 : &peer, &vlink->area->area_id);
518 0 : return CMD_WARNING;
519 : }
520 :
521 : /* Handle configuration removal. */
522 0 : if (no) {
523 0 : if (vlink == NULL)
524 : return CMD_SUCCESS;
525 :
526 0 : ospf6_virtual_link_free(&vlink);
527 0 : return CMD_SUCCESS;
528 : }
529 :
530 : /* Create and apply. */
531 0 : if (vlink == NULL)
532 0 : vlink = ospf6_virtual_link_new(oa, peer);
533 :
534 0 : if (retx_str)
535 0 : vlink->retransmit_interval = retx;
536 0 : if (tx_str)
537 0 : vlink->transmit_delay = tx;
538 0 : if (hello_str)
539 0 : vlink->hello_interval = hello;
540 0 : if (dead_str)
541 0 : vlink->dead_interval = dead;
542 :
543 0 : ospf6_vlink_refresh(vlink);
544 :
545 0 : return CMD_SUCCESS;
546 : }
547 :
548 0 : void ospf6_vlink_area_config(struct ospf6_area *oa, struct vty *vty)
549 : {
550 0 : struct ospf6_virtual_link *vlink;
551 :
552 0 : frr_each (ospf6_area_vlinks, oa->vlinks, vlink) {
553 0 : vty_out(vty, " area %s virtual-link %pI4", oa->name,
554 : &vlink->remote);
555 0 : if (vlink->hello_interval != VLINK_DEFAULT_HELLO_INTERVAL)
556 0 : vty_out(vty, " hello-interval %u", vlink->hello_interval);
557 0 : if (vlink->dead_interval != VLINK_DEFAULT_DEAD_INTERVAL)
558 0 : vty_out(vty, " dead-interval %u", vlink->dead_interval);
559 0 : if (vlink->retransmit_interval != VLINK_DEFAULT_RETRANSMIT_INTERVAL)
560 0 : vty_out(vty, " retransmit-interval %u", vlink->retransmit_interval);
561 0 : if (vlink->transmit_delay != VLINK_DEFAULT_TRANSMIT_DELAY)
562 0 : vty_out(vty, " transmit-delay %u", vlink->transmit_delay);
563 :
564 0 : vty_out(vty, "\n");
565 : }
566 0 : }
567 :
568 16 : void ospf6_virtual_link_init(void)
569 : {
570 16 : install_element(ENABLE_NODE, &debug_ospf6_vlink_cmd);
571 16 : install_element(CONFIG_NODE, &debug_ospf6_vlink_cmd);
572 :
573 16 : install_element(OSPF6_NODE, &ospf6_vlink_config_cmd);
574 16 : }
|