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