]> git.proxmox.com Git - mirror_frr.git/blob - babeld/babeld.c
Merge branch 'cmaster' of ssh://stash.cumulusnetworks.com:7999/quag/quagga into cmaster
[mirror_frr.git] / babeld / babeld.c
1 /*
2 * This file is free software: you may copy, redistribute and/or modify it
3 * under the terms of the GNU General Public License as published by the
4 * Free Software Foundation, either version 2 of the License, or (at your
5 * option) any later version.
6 *
7 * This file is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * This file incorporates work covered by the following copyright and
16 * permission notice:
17 *
18
19 Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
20
21 Permission is hereby granted, free of charge, to any person obtaining a copy
22 of this software and associated documentation files (the "Software"), to deal
23 in the Software without restriction, including without limitation the rights
24 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 copies of the Software, and to permit persons to whom the Software is
26 furnished to do so, subject to the following conditions:
27
28 The above copyright notice and this permission notice shall be included in
29 all copies or substantial portions of the Software.
30
31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
37 THE SOFTWARE.
38 */
39
40 #include <zebra.h>
41 #include "command.h"
42 #include "prefix.h"
43 #include "memory.h"
44 #include "memtypes.h"
45 #include "table.h"
46 #include "distribute.h"
47 #include "prefix.h"
48 #include "filter.h"
49 #include "plist.h"
50
51 #include "babel_main.h"
52 #include "babeld.h"
53 #include "util.h"
54 #include "net.h"
55 #include "kernel.h"
56 #include "babel_interface.h"
57 #include "neighbour.h"
58 #include "route.h"
59 #include "message.h"
60 #include "resend.h"
61 #include "babel_filter.h"
62 #include "babel_zebra.h"
63 #include "vrf.h"
64
65
66 static int babel_init_routing_process(struct thread *thread);
67 static void babel_get_myid(void);
68 static void babel_initial_noise(void);
69 static int babel_read_protocol (struct thread *thread);
70 static int babel_main_loop(struct thread *thread);
71 static void babel_set_timer(struct timeval *timeout);
72 static void babel_fill_with_next_timeout(struct timeval *tv);
73
74
75 /* Informations relative to the babel running daemon. */
76 static struct babel *babel_routing_process = NULL;
77 static unsigned char *receive_buffer = NULL;
78 static int receive_buffer_size = 0;
79
80 /* timeouts */
81 struct timeval check_neighbours_timeout;
82 static time_t expiry_time;
83 static time_t source_expiry_time;
84
85 /* Babel node structure. */
86 static struct cmd_node cmd_babel_node =
87 {
88 .node = BABEL_NODE,
89 .prompt = "%s(config-router)# ",
90 .vtysh = 1,
91 };
92
93 /* print current babel configuration on vty */
94 static int
95 babel_config_write (struct vty *vty)
96 {
97 int lines = 0;
98 int i;
99 afi_t afi;
100
101 /* list enabled debug modes */
102 lines += debug_babel_config_write (vty);
103
104 if (!babel_routing_process)
105 return lines;
106 vty_out (vty, "router babel%s", VTY_NEWLINE);
107 if (resend_delay != BABEL_DEFAULT_RESEND_DELAY)
108 {
109 vty_out (vty, " babel resend-delay %u%s", resend_delay, VTY_NEWLINE);
110 lines++;
111 }
112 /* list enabled interfaces */
113 lines = 1 + babel_enable_if_config_write (vty);
114 /* list redistributed protocols */
115 for (afi = AFI_IP; afi < AFI_MAX; afi++)
116 for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
117 if (i != zclient->redist_default &&
118 vrf_bitmap_check (zclient->redist[afi][i], VRF_DEFAULT) )
119 {
120 vty_out (vty, " redistribute %s%s", zebra_route_string (i), VTY_NEWLINE);
121 lines++;
122 }
123
124 return lines;
125 }
126
127
128 static int
129 babel_create_routing_process (void)
130 {
131 assert (babel_routing_process == NULL);
132
133 /* Allocaste Babel instance. */
134 babel_routing_process = XCALLOC (MTYPE_BABEL, sizeof (struct babel));
135
136 /* Initialize timeouts */
137 gettime(&babel_now);
138 expiry_time = babel_now.tv_sec + roughly(30);
139 source_expiry_time = babel_now.tv_sec + roughly(300);
140
141 /* Make socket for Babel protocol. */
142 protocol_socket = babel_socket(protocol_port);
143 if (protocol_socket < 0) {
144 zlog_err("Couldn't create link local socket: %s", safe_strerror(errno));
145 goto fail;
146 }
147
148 /* Threads. */
149 babel_routing_process->t_read =
150 thread_add_read(master, &babel_read_protocol, NULL, protocol_socket);
151 /* wait a little: zebra will announce interfaces, addresses, routes... */
152 babel_routing_process->t_update =
153 thread_add_timer_msec(master, &babel_init_routing_process, NULL, 200L);
154 return 0;
155
156 fail:
157 XFREE(MTYPE_BABEL, babel_routing_process);
158 babel_routing_process = NULL;
159 return -1;
160 }
161
162 /* thread reading entries form others babel daemons */
163 static int
164 babel_read_protocol (struct thread *thread)
165 {
166 int rc;
167 struct interface *ifp = NULL;
168 struct sockaddr_in6 sin6;
169 struct listnode *linklist_node = NULL;
170
171 assert(babel_routing_process != NULL);
172 assert(protocol_socket >= 0);
173
174 rc = babel_recv(protocol_socket,
175 receive_buffer, receive_buffer_size,
176 (struct sockaddr*)&sin6, sizeof(sin6));
177 if(rc < 0) {
178 if(errno != EAGAIN && errno != EINTR) {
179 zlog_err("recv: %s", safe_strerror(errno));
180 }
181 } else {
182 FOR_ALL_INTERFACES(ifp, linklist_node) {
183 if(!if_up(ifp))
184 continue;
185 if(ifp->ifindex == sin6.sin6_scope_id) {
186 parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
187 receive_buffer, rc);
188 break;
189 }
190 }
191 }
192
193 /* re-add thread */
194 babel_routing_process->t_read =
195 thread_add_read(master, &babel_read_protocol, NULL, protocol_socket);
196 return 0;
197 }
198
199 /* Zebra will give some information, especially about interfaces. This function
200 must be call with a litte timeout wich may give zebra the time to do his job,
201 making these inits have sense. */
202 static int
203 babel_init_routing_process(struct thread *thread)
204 {
205 myseqno = (random() & 0xFFFF);
206 babel_get_myid();
207 babel_load_state_file();
208 debugf(BABEL_DEBUG_COMMON, "My ID is : %s.", format_eui64(myid));
209 babel_initial_noise();
210 babel_main_loop(thread);/* this function self-add to the t_update thread */
211 return 0;
212 }
213
214 /* fill "myid" with an unique id (only if myid != {0}). */
215 static void
216 babel_get_myid(void)
217 {
218 struct interface *ifp = NULL;
219 struct listnode *linklist_node = NULL;
220 int rc;
221 int i;
222
223 /* if we already have an id (from state file), we return. */
224 if (memcmp(myid, zeroes, 8) != 0) {
225 return;
226 }
227
228 FOR_ALL_INTERFACES(ifp, linklist_node) {
229 /* ifp->ifindex is not necessarily valid at this point */
230 int ifindex = if_nametoindex(ifp->name);
231 if(ifindex > 0) {
232 unsigned char eui[8];
233 rc = if_eui64(ifp->name, ifindex, eui);
234 if(rc < 0)
235 continue;
236 memcpy(myid, eui, 8);
237 return;
238 }
239 }
240
241 /* We failed to get a global EUI64 from the interfaces we were given.
242 Let's try to find an interface with a MAC address. */
243 for(i = 1; i < 256; i++) {
244 char buf[IF_NAMESIZE], *ifname;
245 unsigned char eui[8];
246 ifname = if_indextoname(i, buf);
247 if(ifname == NULL)
248 continue;
249 rc = if_eui64(ifname, i, eui);
250 if(rc < 0)
251 continue;
252 memcpy(myid, eui, 8);
253 return;
254 }
255
256 zlog_err("Warning: couldn't find router id -- using random value.");
257
258 rc = read_random_bytes(myid, 8);
259 if(rc < 0) {
260 zlog_err("read(random): %s (cannot assign an ID)",safe_strerror(errno));
261 exit(1);
262 }
263 /* Clear group and global bits */
264 myid[0] &= ~3;
265 }
266
267 /* Make some noise so that others notice us, and send retractions in
268 case we were restarted recently */
269 static void
270 babel_initial_noise(void)
271 {
272 struct interface *ifp = NULL;
273 struct listnode *linklist_node = NULL;
274
275 FOR_ALL_INTERFACES(ifp, linklist_node) {
276 if(!if_up(ifp))
277 continue;
278 /* Apply jitter before we send the first message. */
279 usleep(roughly(10000));
280 gettime(&babel_now);
281 send_hello(ifp);
282 send_wildcard_retraction(ifp);
283 }
284
285 FOR_ALL_INTERFACES(ifp, linklist_node) {
286 if(!if_up(ifp))
287 continue;
288 usleep(roughly(10000));
289 gettime(&babel_now);
290 send_hello(ifp);
291 send_wildcard_retraction(ifp);
292 send_self_update(ifp);
293 send_request(ifp, NULL, 0);
294 flushupdates(ifp);
295 flushbuf(ifp);
296 }
297 }
298
299 /* Delete all the added babel routes, make babeld only speak to zebra. */
300 static void
301 babel_clean_routing_process()
302 {
303 flush_all_routes();
304 babel_interface_close_all();
305
306 /* cancel threads */
307 if (babel_routing_process->t_read != NULL) {
308 thread_cancel(babel_routing_process->t_read);
309 }
310 if (babel_routing_process->t_update != NULL) {
311 thread_cancel(babel_routing_process->t_update);
312 }
313
314 XFREE(MTYPE_BABEL, babel_routing_process);
315 babel_routing_process = NULL;
316 }
317
318 /* Function used with timeout. */
319 static int
320 babel_main_loop(struct thread *thread)
321 {
322 struct timeval tv;
323 struct interface *ifp = NULL;
324 struct listnode *linklist_node = NULL;
325
326 while(1) {
327 gettime(&babel_now);
328
329 /* timeouts --------------------------------------------------------- */
330 /* get the next timeout */
331 babel_fill_with_next_timeout(&tv);
332 /* if there is no timeout, we must wait. */
333 if(timeval_compare(&tv, &babel_now) > 0) {
334 timeval_minus(&tv, &tv, &babel_now);
335 debugf(BABEL_DEBUG_TIMEOUT, "babel main loop : timeout: %ld msecs",
336 tv.tv_sec * 1000 + tv.tv_usec / 1000);
337 /* it happens often to have less than 1 ms, it's bad. */
338 timeval_add_msec(&tv, &tv, 300);
339 babel_set_timer(&tv);
340 return 0;
341 }
342
343 gettime(&babel_now);
344
345 /* update database -------------------------------------------------- */
346 if(timeval_compare(&check_neighbours_timeout, &babel_now) < 0) {
347 int msecs;
348 msecs = check_neighbours();
349 msecs = MAX(msecs, 10);
350 schedule_neighbours_check(msecs, 1);
351 }
352
353 if(babel_now.tv_sec >= expiry_time) {
354 expire_routes();
355 expire_resend();
356 expiry_time = babel_now.tv_sec + roughly(30);
357 }
358
359 if(babel_now.tv_sec >= source_expiry_time) {
360 expire_sources();
361 source_expiry_time = babel_now.tv_sec + roughly(300);
362 }
363
364 FOR_ALL_INTERFACES(ifp, linklist_node) {
365 babel_interface_nfo *babel_ifp = NULL;
366 if(!if_up(ifp))
367 continue;
368 babel_ifp = babel_get_if_nfo(ifp);
369 if(timeval_compare(&babel_now, &babel_ifp->hello_timeout) >= 0)
370 send_hello(ifp);
371 if(timeval_compare(&babel_now, &babel_ifp->update_timeout) >= 0)
372 send_update(ifp, 0, NULL, 0);
373 if(timeval_compare(&babel_now,
374 &babel_ifp->update_flush_timeout) >= 0)
375 flushupdates(ifp);
376 }
377
378 if(resend_time.tv_sec != 0) {
379 if(timeval_compare(&babel_now, &resend_time) >= 0)
380 do_resend();
381 }
382
383 if(unicast_flush_timeout.tv_sec != 0) {
384 if(timeval_compare(&babel_now, &unicast_flush_timeout) >= 0)
385 flush_unicast(1);
386 }
387
388 FOR_ALL_INTERFACES(ifp, linklist_node) {
389 babel_interface_nfo *babel_ifp = NULL;
390 if(!if_up(ifp))
391 continue;
392 babel_ifp = babel_get_if_nfo(ifp);
393 if(babel_ifp->flush_timeout.tv_sec != 0) {
394 if(timeval_compare(&babel_now, &babel_ifp->flush_timeout) >= 0)
395 flushbuf(ifp);
396 }
397 }
398 }
399
400 assert(0); /* this line should never be reach */
401 }
402
403 static void
404 printIfMin(struct timeval *tv, int cmd, const char *tag, const char *ifname)
405 {
406 static struct timeval curr_tv;
407 static char buffer[200];
408 static const char *curr_tag = NULL;
409
410 switch (cmd) {
411 case 0: /* reset timeval */
412 curr_tv = *tv;
413 if(ifname != NULL) {
414 snprintf(buffer, 200L, "interface: %s; %s", ifname, tag);
415 curr_tag = buffer;
416 } else {
417 curr_tag = tag;
418 }
419 break;
420 case 1: /* take the min */
421 if (tv->tv_sec == 0 && tv->tv_usec == 0) { /* if (tv == ∞) */
422 break;
423 }
424 if (tv->tv_sec < curr_tv.tv_sec ||(tv->tv_sec == curr_tv.tv_sec &&
425 tv->tv_usec < curr_tv.tv_usec)) {
426 curr_tv = *tv;
427 if(ifname != NULL) {
428 snprintf(buffer, 200L, "interface: %s; %s", ifname, tag);
429 curr_tag = buffer;
430 } else {
431 curr_tag = tag;
432 }
433 }
434 break;
435 case 2: /* print message */
436 debugf(BABEL_DEBUG_TIMEOUT, "next timeout due to: %s", curr_tag);
437 break;
438 default:
439 break;
440 }
441 }
442
443 static void
444 babel_fill_with_next_timeout(struct timeval *tv)
445 {
446 #if (defined NO_DEBUG)
447 #define printIfMin(a,b,c,d)
448 #else
449 #define printIfMin(a,b,c,d) \
450 if (UNLIKELY(debug & BABEL_DEBUG_TIMEOUT)) {printIfMin(a,b,c,d);}
451
452 struct interface *ifp = NULL;
453 struct listnode *linklist_node = NULL;
454
455 *tv = check_neighbours_timeout;
456 printIfMin(tv, 0, "check_neighbours_timeout", NULL);
457 timeval_min_sec(tv, expiry_time);
458 printIfMin(tv, 1, "expiry_time", NULL);
459 timeval_min_sec(tv, source_expiry_time);
460 printIfMin(tv, 1, "source_expiry_time", NULL);
461 timeval_min(tv, &resend_time);
462 printIfMin(tv, 1, "resend_time", NULL);
463 FOR_ALL_INTERFACES(ifp, linklist_node) {
464 babel_interface_nfo *babel_ifp = NULL;
465 if(!if_up(ifp))
466 continue;
467 babel_ifp = babel_get_if_nfo(ifp);
468 timeval_min(tv, &babel_ifp->flush_timeout);
469 printIfMin(tv, 1, "flush_timeout", ifp->name);
470 timeval_min(tv, &babel_ifp->hello_timeout);
471 printIfMin(tv, 1, "hello_timeout", ifp->name);
472 timeval_min(tv, &babel_ifp->update_timeout);
473 printIfMin(tv, 1, "update_timeout", ifp->name);
474 timeval_min(tv, &babel_ifp->update_flush_timeout);
475 printIfMin(tv, 1, "update_flush_timeout",ifp->name);
476 }
477 timeval_min(tv, &unicast_flush_timeout);
478 printIfMin(tv, 1, "unicast_flush_timeout", NULL);
479 printIfMin(tv, 2, NULL, NULL);
480 #undef printIfMin
481 #endif
482 }
483
484 /* set the t_update thread of the babel routing process to be launch in
485 'timeout' (approximate at the milisecond) */
486 static void
487 babel_set_timer(struct timeval *timeout)
488 {
489 long msecs = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
490 if (babel_routing_process->t_update != NULL) {
491 thread_cancel(babel_routing_process->t_update);
492 }
493 babel_routing_process->t_update =
494 thread_add_timer_msec(master, &babel_main_loop, NULL, msecs);
495 }
496
497 /* Schedule a neighbours check after roughly 3/2 times msecs have elapsed. */
498 void
499 schedule_neighbours_check(int msecs, int override)
500 {
501 struct timeval timeout;
502
503 timeval_add_msec(&timeout, &babel_now, roughly(msecs * 3 / 2));
504 if(override)
505 check_neighbours_timeout = timeout;
506 else
507 timeval_min(&check_neighbours_timeout, &timeout);
508 }
509
510 int
511 resize_receive_buffer(int size)
512 {
513 if(size <= receive_buffer_size)
514 return 0;
515
516 if(receive_buffer == NULL) {
517 receive_buffer = malloc(size);
518 if(receive_buffer == NULL) {
519 zlog_err("malloc(receive_buffer): %s", safe_strerror(errno));
520 return -1;
521 }
522 receive_buffer_size = size;
523 } else {
524 unsigned char *new;
525 new = realloc(receive_buffer, size);
526 if(new == NULL) {
527 zlog_err("realloc(receive_buffer): %s", safe_strerror(errno));
528 return -1;
529 }
530 receive_buffer = new;
531 receive_buffer_size = size;
532 }
533 return 1;
534 }
535
536 static void
537 babel_distribute_update (struct distribute *dist)
538 {
539 struct interface *ifp;
540 babel_interface_nfo *babel_ifp;
541 struct access_list *alist;
542 struct prefix_list *plist;
543
544 if (! dist->ifname)
545 return;
546
547 ifp = if_lookup_by_name (dist->ifname);
548 if (ifp == NULL)
549 return;
550
551 babel_ifp = babel_get_if_nfo(ifp);
552
553 if (dist->list[DISTRIBUTE_IN]) {
554 alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]);
555 if (alist)
556 babel_ifp->list[BABEL_FILTER_IN] = alist;
557 else
558 babel_ifp->list[BABEL_FILTER_IN] = NULL;
559 } else {
560 babel_ifp->list[BABEL_FILTER_IN] = NULL;
561 }
562
563 if (dist->list[DISTRIBUTE_OUT]) {
564 alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]);
565 if (alist)
566 babel_ifp->list[BABEL_FILTER_OUT] = alist;
567 else
568 babel_ifp->list[BABEL_FILTER_OUT] = NULL;
569 } else {
570 babel_ifp->list[BABEL_FILTER_OUT] = NULL;
571 }
572
573 if (dist->prefix[DISTRIBUTE_IN]) {
574 plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]);
575 if (plist)
576 babel_ifp->prefix[BABEL_FILTER_IN] = plist;
577 else
578 babel_ifp->prefix[BABEL_FILTER_IN] = NULL;
579 } else {
580 babel_ifp->prefix[BABEL_FILTER_IN] = NULL;
581 }
582
583 if (dist->prefix[DISTRIBUTE_OUT]) {
584 plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]);
585 if (plist)
586 babel_ifp->prefix[BABEL_FILTER_OUT] = plist;
587 else
588 babel_ifp->prefix[BABEL_FILTER_OUT] = NULL;
589 } else {
590 babel_ifp->prefix[BABEL_FILTER_OUT] = NULL;
591 }
592 }
593
594 static void
595 babel_distribute_update_interface (struct interface *ifp)
596 {
597 struct distribute *dist;
598
599 dist = distribute_lookup (ifp->name);
600 if (dist)
601 babel_distribute_update (dist);
602 }
603
604 /* Update all interface's distribute list. */
605 static void
606 babel_distribute_update_all (struct prefix_list *notused)
607 {
608 struct interface *ifp;
609 struct listnode *node;
610
611 for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
612 babel_distribute_update_interface (ifp);
613 }
614
615 static void
616 babel_distribute_update_all_wrapper (struct access_list *notused)
617 {
618 babel_distribute_update_all(NULL);
619 }
620
621
622 /* [Command] */
623 DEFUN (router_babel,
624 router_babel_cmd,
625 "router babel",
626 "Enable a routing process\n"
627 "Make Babel instance command\n"
628 "No attributes\n")
629 {
630 int ret;
631
632 vty->node = BABEL_NODE;
633
634 if (!babel_routing_process) {
635 ret = babel_create_routing_process ();
636
637 /* Notice to user we couldn't create Babel. */
638 if (ret < 0) {
639 zlog_warn ("can't create Babel");
640 return CMD_WARNING;
641 }
642 }
643
644 return CMD_SUCCESS;
645 }
646
647 /* [Command] */
648 DEFUN (no_router_babel,
649 no_router_babel_cmd,
650 "no router babel",
651 NO_STR
652 "Disable a routing process\n"
653 "Remove Babel instance command\n"
654 "No attributes\n")
655 {
656 if(babel_routing_process)
657 babel_clean_routing_process();
658 return CMD_SUCCESS;
659 }
660
661 /* [Babel Command] */
662 DEFUN (babel_set_resend_delay,
663 babel_set_resend_delay_cmd,
664 "babel resend-delay <20-655340>",
665 "Babel commands\n"
666 "Time before resending a message\n"
667 "Milliseconds\n")
668 {
669 int interval;
670
671 VTY_GET_INTEGER_RANGE("milliseconds", interval, argv[0], 20, 10 * 0xFFFE);
672
673 resend_delay = interval;
674 return CMD_SUCCESS;
675 }
676
677 void
678 babeld_quagga_init(void)
679 {
680
681 install_node(&cmd_babel_node, &babel_config_write);
682
683 install_element(CONFIG_NODE, &router_babel_cmd);
684 install_element(CONFIG_NODE, &no_router_babel_cmd);
685
686 install_default(BABEL_NODE);
687 install_element(BABEL_NODE, &babel_set_resend_delay_cmd);
688
689 babel_if_init();
690
691 /* Access list install. */
692 access_list_init ();
693 access_list_add_hook (babel_distribute_update_all_wrapper);
694 access_list_delete_hook (babel_distribute_update_all_wrapper);
695
696 /* Prefix list initialize.*/
697 prefix_list_init ();
698 prefix_list_add_hook (babel_distribute_update_all);
699 prefix_list_delete_hook (babel_distribute_update_all);
700
701 /* Distribute list install. */
702 distribute_list_init (BABEL_NODE);
703 distribute_list_add_hook (babel_distribute_update);
704 distribute_list_delete_hook (babel_distribute_update);
705 }
706
707 /* Stubs to adapt Babel's filtering calls to Quagga's infrastructure. */
708
709 int
710 input_filter(const unsigned char *id,
711 const unsigned char *prefix, unsigned short plen,
712 const unsigned char *neigh, unsigned int ifindex)
713 {
714 return babel_filter(0, prefix, plen, ifindex);
715 }
716
717 int
718 output_filter(const unsigned char *id, const unsigned char *prefix,
719 unsigned short plen, unsigned int ifindex)
720 {
721 return babel_filter(1, prefix, plen, ifindex);
722 }
723
724 /* There's no redistribute filter in Quagga -- the zebra daemon does its
725 own filtering. */
726 int
727 redistribute_filter(const unsigned char *prefix, unsigned short plen,
728 unsigned int ifindex, int proto)
729 {
730 return 0;
731 }
732