frr/babeld/babeld.c

843 lines
24 KiB
C

// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#include <zebra.h>
#include "command.h"
#include "prefix.h"
#include "memory.h"
#include "table.h"
#include "distribute.h"
#include "prefix.h"
#include "filter.h"
#include "plist.h"
#include "lib_errors.h"
#include "network.h"
#include "if.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "net.h"
#include "kernel.h"
#include "babel_interface.h"
#include "neighbour.h"
#include "route.h"
#include "message.h"
#include "resend.h"
#include "babel_filter.h"
#include "babel_zebra.h"
#include "babel_errors.h"
#ifndef VTYSH_EXTRACT_PL
#include "babeld/babeld_clippy.c"
#endif
DEFINE_MGROUP(BABELD, "babeld");
DEFINE_MTYPE_STATIC(BABELD, BABEL, "Babel Structure");
static void babel_init_routing_process(struct thread *thread);
static void babel_get_myid(void);
static void babel_initial_noise(void);
static void babel_read_protocol(struct thread *thread);
static void babel_main_loop(struct thread *thread);
static void babel_set_timer(struct timeval *timeout);
static void babel_fill_with_next_timeout(struct timeval *tv);
static void
babel_distribute_update (struct distribute_ctx *ctx, struct distribute *dist);
/* Informations relative to the babel running daemon. */
static struct babel *babel_routing_process = NULL;
static unsigned char *receive_buffer = NULL;
static int receive_buffer_size = 0;
/* timeouts */
struct timeval check_neighbours_timeout;
static time_t expiry_time;
static time_t source_expiry_time;
/* Babel node structure. */
static int babel_config_write (struct vty *vty);
static struct cmd_node cmd_babel_node =
{
.name = "babel",
.node = BABEL_NODE,
.parent_node = CONFIG_NODE,
.prompt = "%s(config-router)# ",
.config_write = babel_config_write,
};
/* print current babel configuration on vty */
static int
babel_config_write (struct vty *vty)
{
int lines = 0;
int afi;
int i;
/* list enabled debug modes */
lines += debug_babel_config_write (vty);
if (!babel_routing_process)
return lines;
vty_out (vty, "router babel\n");
if (diversity_kind != DIVERSITY_NONE)
{
vty_out (vty, " babel diversity\n");
lines++;
}
if (diversity_factor != BABEL_DEFAULT_DIVERSITY_FACTOR)
{
vty_out (vty, " babel diversity-factor %d\n",diversity_factor);
lines++;
}
if (resend_delay != BABEL_DEFAULT_RESEND_DELAY)
{
vty_out (vty, " babel resend-delay %u\n", resend_delay);
lines++;
}
if (smoothing_half_life != BABEL_DEFAULT_SMOOTHING_HALF_LIFE)
{
vty_out (vty, " babel smoothing-half-life %u\n",
smoothing_half_life);
lines++;
}
/* list enabled interfaces */
lines = 1 + babel_enable_if_config_write (vty);
/* list redistributed protocols */
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
if (i != zclient->redist_default &&
vrf_bitmap_check (zclient->redist[afi][i], VRF_DEFAULT)) {
vty_out (vty, " redistribute %s %s\n",
(afi == AFI_IP) ? "ipv4" : "ipv6",
zebra_route_string(i));
lines++;
}
}
}
lines += config_write_distribute (vty, babel_routing_process->distribute_ctx);
vty_out (vty, "exit\n");
return lines;
}
static int
babel_create_routing_process (void)
{
assert (babel_routing_process == NULL);
/* Allocaste Babel instance. */
babel_routing_process = XCALLOC(MTYPE_BABEL, sizeof(struct babel));
/* Initialize timeouts */
gettime(&babel_now);
expiry_time = babel_now.tv_sec + roughly(30);
source_expiry_time = babel_now.tv_sec + roughly(300);
/* Make socket for Babel protocol. */
protocol_socket = babel_socket(protocol_port);
if (protocol_socket < 0) {
flog_err_sys(EC_LIB_SOCKET, "Couldn't create link local socket: %s",
safe_strerror(errno));
goto fail;
}
/* Threads. */
thread_add_read(master, babel_read_protocol, NULL, protocol_socket, &babel_routing_process->t_read);
/* wait a little: zebra will announce interfaces, addresses, routes... */
thread_add_timer_msec(master, babel_init_routing_process, NULL, 200L, &babel_routing_process->t_update);
/* Distribute list install. */
babel_routing_process->distribute_ctx = distribute_list_ctx_create (vrf_lookup_by_id(VRF_DEFAULT));
distribute_list_add_hook (babel_routing_process->distribute_ctx, babel_distribute_update);
distribute_list_delete_hook (babel_routing_process->distribute_ctx, babel_distribute_update);
return 0;
fail:
XFREE(MTYPE_BABEL, babel_routing_process);
return -1;
}
/* thread reading entries form others babel daemons */
static void babel_read_protocol(struct thread *thread)
{
int rc;
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
struct sockaddr_in6 sin6;
assert(babel_routing_process != NULL);
assert(protocol_socket >= 0);
rc = babel_recv(protocol_socket,
receive_buffer, receive_buffer_size,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0) {
if(errno != EAGAIN && errno != EINTR) {
flog_err_sys(EC_LIB_SOCKET, "recv: %s", safe_strerror(errno));
}
} else {
FOR_ALL_INTERFACES(vrf, ifp) {
if(!if_up(ifp))
continue;
if(ifp->ifindex == (ifindex_t)sin6.sin6_scope_id) {
parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
receive_buffer, rc);
break;
}
}
}
/* re-add thread */
thread_add_read(master, &babel_read_protocol, NULL, protocol_socket, &babel_routing_process->t_read);
}
/* Zebra will give some information, especially about interfaces. This function
must be call with a litte timeout wich may give zebra the time to do his job,
making these inits have sense. */
static void babel_init_routing_process(struct thread *thread)
{
myseqno = (frr_weak_random() & 0xFFFF);
babel_get_myid();
babel_load_state_file();
debugf(BABEL_DEBUG_COMMON, "My ID is : %s.", format_eui64(myid));
babel_initial_noise();
babel_main_loop(thread);/* this function self-add to the t_update thread */
}
/* fill "myid" with an unique id (only if myid != {0}). */
static void
babel_get_myid(void)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
int rc;
int i;
/* if we already have an id (from state file), we return. */
if (memcmp(myid, zeroes, 8) != 0) {
return;
}
FOR_ALL_INTERFACES(vrf, ifp) {
/* ifp->ifindex is not necessarily valid at this point */
int ifindex = if_nametoindex(ifp->name);
if(ifindex > 0) {
unsigned char eui[8];
rc = if_eui64(ifindex, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
return;
}
}
/* We failed to get a global EUI64 from the interfaces we were given.
Let's try to find an interface with a MAC address. */
for(i = 1; i < 256; i++) {
char buf[INTERFACE_NAMSIZ], *ifname;
unsigned char eui[8];
ifname = if_indextoname(i, buf);
if(ifname == NULL)
continue;
rc = if_eui64(i, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
return;
}
flog_err(EC_BABEL_CONFIG, "Couldn't find router id -- using random value.");
rc = read_random_bytes(myid, 8);
if(rc < 0) {
flog_err(EC_BABEL_CONFIG, "read(random): %s (cannot assign an ID)",
safe_strerror(errno));
exit(1);
}
/* Clear group and global bits */
UNSET_FLAG (myid[0], 3);
}
/* Make some noise so that others notice us, and send retractions in
case we were restarted recently */
static void
babel_initial_noise(void)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
FOR_ALL_INTERFACES(vrf, ifp) {
if(!if_up(ifp))
continue;
/* Apply jitter before we send the first message. */
usleep(roughly(10000));
gettime(&babel_now);
send_hello(ifp);
send_wildcard_retraction(ifp);
}
FOR_ALL_INTERFACES(vrf, ifp) {
if(!if_up(ifp))
continue;
usleep(roughly(10000));
gettime(&babel_now);
send_hello(ifp);
send_wildcard_retraction(ifp);
send_self_update(ifp);
send_request(ifp, NULL, 0);
flushupdates(ifp);
flushbuf(ifp);
}
}
/* Delete all the added babel routes, make babeld only speak to zebra. */
static void
babel_clean_routing_process(void)
{
flush_all_routes();
babel_interface_close_all();
/* cancel events */
thread_cancel(&babel_routing_process->t_read);
thread_cancel(&babel_routing_process->t_update);
distribute_list_delete(&babel_routing_process->distribute_ctx);
XFREE(MTYPE_BABEL, babel_routing_process);
}
/* Function used with timeout. */
static void babel_main_loop(struct thread *thread)
{
struct timeval tv;
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
while(1) {
gettime(&babel_now);
/* timeouts --------------------------------------------------------- */
/* get the next timeout */
babel_fill_with_next_timeout(&tv);
/* if there is no timeout, we must wait. */
if(timeval_compare(&tv, &babel_now) > 0) {
timeval_minus(&tv, &tv, &babel_now);
debugf(BABEL_DEBUG_TIMEOUT, "babel main loop : timeout: %lld msecs",
(long long)tv.tv_sec * 1000 + tv.tv_usec / 1000);
/* it happens often to have less than 1 ms, it's bad. */
timeval_add_msec(&tv, &tv, 300);
babel_set_timer(&tv);
return;
}
gettime(&babel_now);
/* update database -------------------------------------------------- */
if(timeval_compare(&check_neighbours_timeout, &babel_now) < 0) {
int msecs;
msecs = check_neighbours();
/* Multiply by 3/2 to allow neighbours to expire. */
msecs = MAX(3 * msecs / 2, 10);
schedule_neighbours_check(msecs, 1);
}
if(babel_now.tv_sec >= expiry_time) {
expire_routes();
expire_resend();
expiry_time = babel_now.tv_sec + roughly(30);
}
if(babel_now.tv_sec >= source_expiry_time) {
expire_sources();
source_expiry_time = babel_now.tv_sec + roughly(300);
}
FOR_ALL_INTERFACES(vrf, ifp) {
babel_interface_nfo *babel_ifp = NULL;
if(!if_up(ifp))
continue;
babel_ifp = babel_get_if_nfo(ifp);
if(timeval_compare(&babel_now, &babel_ifp->hello_timeout) >= 0)
send_hello(ifp);
if(timeval_compare(&babel_now, &babel_ifp->update_timeout) >= 0)
send_update(ifp, 0, NULL, 0);
if(timeval_compare(&babel_now,
&babel_ifp->update_flush_timeout) >= 0)
flushupdates(ifp);
}
if(resend_time.tv_sec != 0) {
if(timeval_compare(&babel_now, &resend_time) >= 0)
do_resend();
}
if(unicast_flush_timeout.tv_sec != 0) {
if(timeval_compare(&babel_now, &unicast_flush_timeout) >= 0)
flush_unicast(1);
}
FOR_ALL_INTERFACES(vrf, ifp) {
babel_interface_nfo *babel_ifp = NULL;
if(!if_up(ifp))
continue;
babel_ifp = babel_get_if_nfo(ifp);
if(babel_ifp->flush_timeout.tv_sec != 0) {
if(timeval_compare(&babel_now, &babel_ifp->flush_timeout) >= 0)
flushbuf(ifp);
}
}
}
assert(0); /* this line should never be reach */
}
static void
printIfMin(struct timeval *tv, int cmd, const char *tag, const char *ifname)
{
static struct timeval curr_tv;
static char buffer[200];
static const char *curr_tag = NULL;
switch (cmd) {
case 0: /* reset timeval */
curr_tv = *tv;
if(ifname != NULL) {
snprintf(buffer, 200L, "interface: %s; %s", ifname, tag);
curr_tag = buffer;
} else {
curr_tag = tag;
}
break;
case 1: /* take the min */
if (tv->tv_sec == 0 && tv->tv_usec == 0) { /* if (tv == ∞) */
break;
}
if (tv->tv_sec < curr_tv.tv_sec ||(tv->tv_sec == curr_tv.tv_sec &&
tv->tv_usec < curr_tv.tv_usec)) {
curr_tv = *tv;
if(ifname != NULL) {
snprintf(buffer, 200L, "interface: %s; %s", ifname, tag);
curr_tag = buffer;
} else {
curr_tag = tag;
}
}
break;
case 2: /* print message */
debugf(BABEL_DEBUG_TIMEOUT, "next timeout due to: %s", curr_tag);
break;
default:
break;
}
}
static void
babel_fill_with_next_timeout(struct timeval *tv)
{
#if (defined NO_DEBUG)
#define printIfMin(a,b,c,d)
#else
#define printIfMin(a,b,c,d) \
if (UNLIKELY(debug & BABEL_DEBUG_TIMEOUT)) {printIfMin(a,b,c,d);}
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
*tv = check_neighbours_timeout;
printIfMin(tv, 0, "check_neighbours_timeout", NULL);
timeval_min_sec(tv, expiry_time);
printIfMin(tv, 1, "expiry_time", NULL);
timeval_min_sec(tv, source_expiry_time);
printIfMin(tv, 1, "source_expiry_time", NULL);
timeval_min(tv, &resend_time);
printIfMin(tv, 1, "resend_time", NULL);
FOR_ALL_INTERFACES(vrf, ifp) {
babel_interface_nfo *babel_ifp = NULL;
if(!if_up(ifp))
continue;
babel_ifp = babel_get_if_nfo(ifp);
timeval_min(tv, &babel_ifp->flush_timeout);
printIfMin(tv, 1, "flush_timeout", ifp->name);
timeval_min(tv, &babel_ifp->hello_timeout);
printIfMin(tv, 1, "hello_timeout", ifp->name);
timeval_min(tv, &babel_ifp->update_timeout);
printIfMin(tv, 1, "update_timeout", ifp->name);
timeval_min(tv, &babel_ifp->update_flush_timeout);
printIfMin(tv, 1, "update_flush_timeout",ifp->name);
}
timeval_min(tv, &unicast_flush_timeout);
printIfMin(tv, 1, "unicast_flush_timeout", NULL);
printIfMin(tv, 2, NULL, NULL);
#undef printIfMin
#endif
}
/* set the t_update thread of the babel routing process to be launch in
'timeout' (approximate at the milisecond) */
static void
babel_set_timer(struct timeval *timeout)
{
long msecs = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
thread_cancel(&(babel_routing_process->t_update));
thread_add_timer_msec(master, babel_main_loop, NULL, msecs, &babel_routing_process->t_update);
}
void
schedule_neighbours_check(int msecs, int override)
{
struct timeval timeout;
timeval_add_msec(&timeout, &babel_now, msecs);
if(override)
check_neighbours_timeout = timeout;
else
timeval_min(&check_neighbours_timeout, &timeout);
}
int
resize_receive_buffer(int size)
{
if(size <= receive_buffer_size)
return 0;
if(receive_buffer == NULL) {
receive_buffer = malloc(size);
if(receive_buffer == NULL) {
flog_err(EC_BABEL_MEMORY, "malloc(receive_buffer): %s",
safe_strerror(errno));
return -1;
}
receive_buffer_size = size;
} else {
unsigned char *new;
new = realloc(receive_buffer, size);
if(new == NULL) {
flog_err(EC_BABEL_MEMORY, "realloc(receive_buffer): %s",
safe_strerror(errno));
return -1;
}
receive_buffer = new;
receive_buffer_size = size;
}
return 1;
}
static void
babel_distribute_update (struct distribute_ctx *ctx, struct distribute *dist)
{
struct interface *ifp;
babel_interface_nfo *babel_ifp;
int type;
int family;
if (! dist->ifname)
return;
ifp = if_lookup_by_name (dist->ifname, VRF_DEFAULT);
if (ifp == NULL)
return;
babel_ifp = babel_get_if_nfo(ifp);
for (type = 0; type < DISTRIBUTE_MAX; type++) {
family = type == DISTRIBUTE_V4_IN || type == DISTRIBUTE_V4_OUT ?
AFI_IP : AFI_IP6;
if (dist->list[type])
babel_ifp->list[type] = access_list_lookup (family,
dist->list[type]);
else
babel_ifp->list[type] = NULL;
if (dist->prefix[type])
babel_ifp->prefix[type] = prefix_list_lookup (family,
dist->prefix[type]);
else
babel_ifp->prefix[type] = NULL;
}
}
static void
babel_distribute_update_interface (struct interface *ifp)
{
struct distribute *dist = NULL;
if (babel_routing_process)
dist = distribute_lookup(babel_routing_process->distribute_ctx, ifp->name);
if (dist)
babel_distribute_update (babel_routing_process->distribute_ctx, dist);
}
/* Update all interface's distribute list. */
static void
babel_distribute_update_all (struct prefix_list *notused)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp;
FOR_ALL_INTERFACES (vrf, ifp)
babel_distribute_update_interface (ifp);
}
static void
babel_distribute_update_all_wrapper (struct access_list *notused)
{
babel_distribute_update_all(NULL);
}
/* [Command] */
DEFUN_NOSH (router_babel,
router_babel_cmd,
"router babel",
"Enable a routing process\n"
"Make Babel instance command\n")
{
int ret;
vty->node = BABEL_NODE;
if (!babel_routing_process) {
ret = babel_create_routing_process ();
/* Notice to user we couldn't create Babel. */
if (ret < 0) {
zlog_warn ("can't create Babel");
return CMD_WARNING;
}
}
return CMD_SUCCESS;
}
/* [Command] */
DEFUN (no_router_babel,
no_router_babel_cmd,
"no router babel",
NO_STR
"Disable a routing process\n"
"Remove Babel instance command\n")
{
if(babel_routing_process)
babel_clean_routing_process();
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFUN (babel_diversity,
babel_diversity_cmd,
"babel diversity",
"Babel commands\n"
"Enable diversity-aware routing.\n")
{
diversity_kind = DIVERSITY_CHANNEL;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFUN (no_babel_diversity,
no_babel_diversity_cmd,
"no babel diversity",
NO_STR
"Babel commands\n"
"Disable diversity-aware routing.\n")
{
diversity_kind = DIVERSITY_NONE;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFPY (babel_diversity_factor,
babel_diversity_factor_cmd,
"[no] babel diversity-factor (1-256)$factor",
NO_STR
"Babel commands\n"
"Set the diversity factor.\n"
"Factor in units of 1/256.\n")
{
diversity_factor = no ? BABEL_DEFAULT_DIVERSITY_FACTOR : factor;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFPY (babel_set_resend_delay,
babel_set_resend_delay_cmd,
"[no] babel resend-delay (20-655340)$delay",
NO_STR
"Babel commands\n"
"Time before resending a message\n"
"Milliseconds\n")
{
resend_delay = no ? BABEL_DEFAULT_RESEND_DELAY : delay;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFPY (babel_set_smoothing_half_life,
babel_set_smoothing_half_life_cmd,
"[no] babel smoothing-half-life (0-65534)$seconds",
NO_STR
"Babel commands\n"
"Smoothing half-life\n"
"Seconds (0 to disable)\n")
{
change_smoothing_half_life(no ? BABEL_DEFAULT_SMOOTHING_HALF_LIFE
: seconds);
return CMD_SUCCESS;
}
DEFUN (babel_distribute_list,
babel_distribute_list_cmd,
"distribute-list [prefix] ACCESSLIST4_NAME <in|out> [WORD]",
"Filter networks in routing updates\n"
"Specify a prefix\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[1]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_parser(prefix, true, argv[2 + prefix]->text,
argv[1 + prefix]->arg, ifname);
}
DEFUN (babel_no_distribute_list,
babel_no_distribute_list_cmd,
"no distribute-list [prefix] ACCESSLIST4_NAME <in|out> [WORD]",
NO_STR
"Filter networks in routing updates\n"
"Specify a prefix\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_no_parser(vty, prefix, true,
argv[3 + prefix]->text,
argv[2 + prefix]->arg, ifname);
}
DEFUN (babel_ipv6_distribute_list,
babel_ipv6_distribute_list_cmd,
"ipv6 distribute-list [prefix] ACCESSLIST6_NAME <in|out> [WORD]",
"IPv6\n"
"Filter networks in routing updates\n"
"Specify a prefix\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_parser(prefix, false, argv[3 + prefix]->text,
argv[2 + prefix]->arg, ifname);
}
DEFUN (babel_no_ipv6_distribute_list,
babel_no_ipv6_distribute_list_cmd,
"no ipv6 distribute-list [prefix] ACCESSLIST6_NAME <in|out> [WORD]",
NO_STR
"IPv6\n"
"Filter networks in routing updates\n"
"Specify a prefix\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[3]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_no_parser(vty, prefix, false,
argv[4 + prefix]->text,
argv[3 + prefix]->arg, ifname);
}
void
babeld_quagga_init(void)
{
install_node(&cmd_babel_node);
install_element(CONFIG_NODE, &router_babel_cmd);
install_element(CONFIG_NODE, &no_router_babel_cmd);
install_default(BABEL_NODE);
install_element(BABEL_NODE, &babel_diversity_cmd);
install_element(BABEL_NODE, &no_babel_diversity_cmd);
install_element(BABEL_NODE, &babel_diversity_factor_cmd);
install_element(BABEL_NODE, &babel_set_resend_delay_cmd);
install_element(BABEL_NODE, &babel_set_smoothing_half_life_cmd);
install_element(BABEL_NODE, &babel_distribute_list_cmd);
install_element(BABEL_NODE, &babel_no_distribute_list_cmd);
install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd);
install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd);
vrf_cmd_init(NULL);
babel_if_init();
/* Access list install. */
access_list_init ();
access_list_add_hook (babel_distribute_update_all_wrapper);
access_list_delete_hook (babel_distribute_update_all_wrapper);
/* Prefix list initialize.*/
prefix_list_init ();
prefix_list_add_hook (babel_distribute_update_all);
prefix_list_delete_hook (babel_distribute_update_all);
}
/* Stubs to adapt Babel's filtering calls to Quagga's infrastructure. */
int
input_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex)
{
return babel_filter(0, prefix, plen, ifindex);
}
int
output_filter(const unsigned char *id, const unsigned char *prefix,
unsigned short plen, unsigned int ifindex)
{
return babel_filter(1, prefix, plen, ifindex);
}
/* There's no redistribute filter in Quagga -- the zebra daemon does its
own filtering. */
int
redistribute_filter(const unsigned char *prefix, unsigned short plen,
unsigned int ifindex, int proto)
{
return 0;
}
struct babel *babel_lookup(void)
{
return babel_routing_process;
}