summaryrefslogtreecommitdiffstats
path: root/babeld
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /babeld
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'babeld')
-rw-r--r--babeld/.gitignore8
-rw-r--r--babeld/Makefile10
-rw-r--r--babeld/babel_errors.c48
-rw-r--r--babeld/babel_errors.h22
-rw-r--r--babeld/babel_filter.c90
-rw-r--r--babeld/babel_filter.h16
-rw-r--r--babeld/babel_interface.c1349
-rw-r--r--babeld/babel_interface.h121
-rw-r--r--babeld/babel_main.c370
-rw-r--r--babeld/babel_main.h29
-rw-r--r--babeld/babel_zebra.c253
-rw-r--r--babeld/babel_zebra.h17
-rw-r--r--babeld/babeld.c848
-rw-r--r--babeld/babeld.h103
-rw-r--r--babeld/kernel.c238
-rw-r--r--babeld/kernel.h32
-rw-r--r--babeld/message.c1948
-rw-r--r--babeld/message.h86
-rw-r--r--babeld/neighbour.c354
-rw-r--r--babeld/neighbour.h47
-rw-r--r--babeld/net.c205
-rw-r--r--babeld/net.h16
-rw-r--r--babeld/resend.c301
-rw-r--r--babeld/resend.h48
-rw-r--r--babeld/route.c1129
-rw-r--r--babeld/route.h112
-rw-r--r--babeld/source.c152
-rw-r--r--babeld/source.h34
-rw-r--r--babeld/subdir.am50
-rw-r--r--babeld/util.c429
-rw-r--r--babeld/util.h139
-rw-r--r--babeld/xroute.c214
-rw-r--r--babeld/xroute.h29
33 files changed, 8847 insertions, 0 deletions
diff --git a/babeld/.gitignore b/babeld/.gitignore
new file mode 100644
index 0000000..abb4d93
--- /dev/null
+++ b/babeld/.gitignore
@@ -0,0 +1,8 @@
+*
+!*.c
+!*.h
+!LICENCE
+!Makefile
+!subdir.am
+!.gitignore
+*_clippy.c
diff --git a/babeld/Makefile b/babeld/Makefile
new file mode 100644
index 0000000..ae125e6
--- /dev/null
+++ b/babeld/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. babeld/babeld
+%: ALWAYS
+ @$(MAKE) -s -C .. babeld/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/babeld/babel_errors.c b/babeld/babel_errors.c
new file mode 100644
index 0000000..b093bdb
--- /dev/null
+++ b/babeld/babel_errors.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Babel-specific error messages.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+
+#include <zebra.h>
+
+#include "lib/ferr.h"
+#include "babel_errors.h"
+
+/* clang-format off */
+static struct log_ref ferr_babel_err[] = {
+ {
+ .code = EC_BABEL_MEMORY,
+ .title = "BABEL Memory Errors",
+ .description = "Babel has failed to allocate memory, the system is about to run out of memory",
+ .suggestion = "Find the process that is causing memory shortages, remediate that process and restart FRR"
+ },
+ {
+ .code = EC_BABEL_PACKET,
+ .title = "BABEL Packet Error",
+ .description = "Babel has detected a packet encode/decode problem",
+ .suggestion = "Collect relevant log files and file an Issue"
+ },
+ {
+ .code = EC_BABEL_CONFIG,
+ .title = "BABEL Configuration Error",
+ .description = "Babel has detected a configuration error of some sort",
+ .suggestion = "Ensure that the configuration is correct"
+ },
+ {
+ .code = EC_BABEL_ROUTE,
+ .title = "BABEL Route Error",
+ .description = "Babel has detected a routing error and has an inconsistent state",
+ .suggestion = "Gather data for filing an Issue and then restart FRR"
+ },
+ {
+ .code = END_FERR,
+ }
+};
+/* clang-format on */
+
+void babel_error_init(void)
+{
+ log_ref_add(ferr_babel_err);
+}
diff --git a/babeld/babel_errors.h b/babeld/babel_errors.h
new file mode 100644
index 0000000..47539c7
--- /dev/null
+++ b/babeld/babel_errors.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Babel-specific error messages.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+
+#ifndef __BABEL_ERRORS_H__
+#define __BABEL_ERRORS_H__
+
+#include "lib/ferr.h"
+
+enum babel_log_refs {
+ EC_BABEL_MEMORY = BABEL_FERR_START,
+ EC_BABEL_PACKET,
+ EC_BABEL_CONFIG,
+ EC_BABEL_ROUTE,
+};
+
+extern void babel_error_init(void);
+
+#endif
diff --git a/babeld/babel_filter.c b/babeld/babel_filter.c
new file mode 100644
index 0000000..0534932
--- /dev/null
+++ b/babeld/babel_filter.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "babel_filter.h"
+#include "vty.h"
+#include "filter.h"
+#include "log.h"
+#include "plist.h"
+#include "distribute.h"
+#include "util.h"
+
+int
+babel_filter(int output, const unsigned char *prefix, unsigned short plen,
+ unsigned int ifindex)
+{
+ struct interface *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
+ babel_interface_nfo *babel_ifp = ifp ? babel_get_if_nfo(ifp) : NULL;
+ struct prefix p;
+ struct distribute *dist = NULL;
+ struct access_list *alist;
+ struct prefix_list *plist;
+ int distribute;
+ struct babel *babel;
+ afi_t family;
+
+ p.family = v4mapped(prefix) ? AF_INET : AF_INET6;
+ p.prefixlen = v4mapped(prefix) ? plen - 96 : plen;
+ if (p.family == AF_INET) {
+ uchar_to_inaddr(&p.u.prefix4, prefix);
+ distribute = output ? DISTRIBUTE_V4_OUT : DISTRIBUTE_V4_IN;
+ family = AFI_IP;
+ } else {
+ uchar_to_in6addr(&p.u.prefix6, prefix);
+ distribute = output ? DISTRIBUTE_V6_OUT : DISTRIBUTE_V6_IN;
+ family = AFI_IP6;
+ }
+
+ if (babel_ifp != NULL && babel_ifp->list[distribute]) {
+ if (access_list_apply (babel_ifp->list[distribute], &p)
+ == FILTER_DENY) {
+ debugf(BABEL_DEBUG_FILTER,
+ "%pFX filtered by distribute %s",
+ &p, output ? "out" : "in");
+ return INFINITY;
+ }
+ }
+ if (babel_ifp != NULL && babel_ifp->prefix[distribute]) {
+ if (prefix_list_apply (babel_ifp->prefix[distribute], &p)
+ == PREFIX_DENY) {
+ debugf(BABEL_DEBUG_FILTER, "%pFX filtered by distribute %s",
+ &p, output ? "out" : "in");
+ return INFINITY;
+ }
+ }
+
+ /* All interface filter check. */
+ babel = babel_lookup();
+ if (babel)
+ dist = distribute_lookup (babel->distribute_ctx, NULL);
+ if (dist) {
+ if (dist->list[distribute]) {
+ alist = access_list_lookup (family, dist->list[distribute]);
+
+ if (alist) {
+ if (access_list_apply (alist, &p) == FILTER_DENY) {
+ debugf(BABEL_DEBUG_FILTER,"%pFX filtered by distribute %s",
+ &p, output ? "out" : "in");
+ return INFINITY;
+ }
+ }
+ }
+ if (dist->prefix[distribute]) {
+ plist = prefix_list_lookup (family, dist->prefix[distribute]);
+ if (plist) {
+ if (prefix_list_apply (plist, &p) == PREFIX_DENY) {
+ debugf(BABEL_DEBUG_FILTER,"%pFX filtered by distribute %s",
+ &p, output ? "out" : "in");
+ return INFINITY;
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/babeld/babel_filter.h b/babeld/babel_filter.h
new file mode 100644
index 0000000..d253543
--- /dev/null
+++ b/babeld/babel_filter.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABELD_BABEL_FILTER_H
+#define BABELD_BABEL_FILTER_H
+
+#include <zebra.h>
+#include "prefix.h"
+#include "babel_interface.h"
+
+int babel_filter(int output, const unsigned char *prefix, unsigned short plen,
+ unsigned int index);
+
+#endif /* BABELD_BABEL_FILTER_H */
diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c
new file mode 100644
index 0000000..ceff472
--- /dev/null
+++ b/babeld/babel_interface.c
@@ -0,0 +1,1349 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#include <zebra.h>
+#include "memory.h"
+#include "log.h"
+#include "command.h"
+#include "prefix.h"
+#include "vector.h"
+#include "distribute.h"
+#include "lib_errors.h"
+#include "network.h"
+
+#include "babel_main.h"
+#include "util.h"
+#include "kernel.h"
+#include "babel_interface.h"
+#include "message.h"
+#include "route.h"
+#include "babel_zebra.h"
+#include "neighbour.h"
+#include "route.h"
+#include "xroute.h"
+#include "babel_errors.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "babeld/babel_interface_clippy.c"
+#endif
+
+DEFINE_MTYPE_STATIC(BABELD, BABEL_IF, "Babel Interface");
+
+#define IS_ENABLE(ifp) (babel_enable_if_lookup(ifp->name) >= 0)
+
+static int babel_enable_if_lookup (const char *ifname);
+static int babel_enable_if_add (const char *ifname);
+static int babel_enable_if_delete (const char *ifname);
+static int interface_recalculate(struct interface *ifp);
+static int interface_reset(struct interface *ifp);
+static int babel_if_new_hook (struct interface *ifp);
+static int babel_if_delete_hook (struct interface *ifp);
+static int interface_config_write (struct vty *vty);
+static babel_interface_nfo * babel_interface_allocate (void);
+static void babel_interface_free (babel_interface_nfo *bi);
+
+
+static vector babel_enable_if; /* enable interfaces (by cmd). */
+
+int babel_ifp_up(struct interface *ifp)
+{
+ debugf(BABEL_DEBUG_IF, "receive an 'interface up'");
+
+ interface_recalculate(ifp);
+ return 0;
+}
+
+int
+babel_ifp_down(struct interface *ifp)
+{
+ debugf(BABEL_DEBUG_IF, "receive an 'interface down'");
+
+ if (ifp == NULL) {
+ return 0;
+ }
+
+ interface_reset(ifp);
+ return 0;
+}
+
+int babel_ifp_create (struct interface *ifp)
+{
+ debugf(BABEL_DEBUG_IF, "receive an 'interface add'");
+
+ interface_recalculate(ifp);
+
+ return 0;
+ }
+
+int
+babel_ifp_destroy(struct interface *ifp)
+{
+ debugf(BABEL_DEBUG_IF, "receive an 'interface delete'");
+
+ if (IS_ENABLE(ifp))
+ interface_reset(ifp);
+
+ return 0;
+}
+
+int
+babel_interface_address_add (ZAPI_CALLBACK_ARGS)
+{
+ babel_interface_nfo *babel_ifp;
+ struct connected *ifc;
+ struct prefix *prefix;
+
+ debugf(BABEL_DEBUG_IF, "receive an 'interface address add'");
+
+ ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD,
+ zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ prefix = ifc->address;
+
+ if (prefix->family == AF_INET) {
+ flush_interface_routes(ifc->ifp, 0);
+ babel_ifp = babel_get_if_nfo(ifc->ifp);
+ if (babel_ifp->ipv4 == NULL) {
+ babel_ifp->ipv4 = malloc(4);
+ if (babel_ifp->ipv4 == NULL) {
+ flog_err(EC_BABEL_MEMORY, "not enough memory");
+ } else {
+ memcpy(babel_ifp->ipv4, &prefix->u.prefix4, 4);
+ }
+ }
+ }
+
+ send_request(ifc->ifp, NULL, 0);
+ send_update(ifc->ifp, 0, NULL, 0);
+
+ return 0;
+}
+
+int
+babel_interface_address_delete (ZAPI_CALLBACK_ARGS)
+{
+ babel_interface_nfo *babel_ifp;
+ struct connected *ifc;
+ struct prefix *prefix;
+
+ debugf(BABEL_DEBUG_IF, "receive an 'interface address delete'");
+
+ ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE,
+ zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ prefix = ifc->address;
+
+ if (prefix->family == AF_INET) {
+ flush_interface_routes(ifc->ifp, 0);
+ babel_ifp = babel_get_if_nfo(ifc->ifp);
+ if (babel_ifp->ipv4 != NULL
+ && memcmp(babel_ifp->ipv4, &prefix->u.prefix4, IPV4_MAX_BYTELEN)
+ == 0) {
+ free(babel_ifp->ipv4);
+ babel_ifp->ipv4 = NULL;
+ }
+ }
+
+ send_request(ifc->ifp, NULL, 0);
+ send_update(ifc->ifp, 0, NULL, 0);
+
+ connected_free(&ifc);
+ return 0;
+}
+
+/* Lookup function. */
+static int
+babel_enable_if_lookup (const char *ifname)
+{
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < vector_active (babel_enable_if); i++)
+ if ((str = vector_slot (babel_enable_if, i)) != NULL)
+ if (strcmp (str, ifname) == 0)
+ return i;
+ return -1;
+}
+
+/* Add interface to babel_enable_if. */
+static int
+babel_enable_if_add (const char *ifname)
+{
+ int ret;
+ struct interface *ifp = NULL;
+
+ ret = babel_enable_if_lookup (ifname);
+ if (ret >= 0)
+ return -1;
+
+ vector_set (babel_enable_if, strdup (ifname));
+
+ ifp = if_lookup_by_name(ifname, VRF_DEFAULT);
+ if (ifp != NULL)
+ interface_recalculate(ifp);
+
+ return 1;
+}
+
+/* Delete interface from babel_enable_if. */
+static int
+babel_enable_if_delete (const char *ifname)
+{
+ int babel_enable_if_index;
+ char *str;
+ struct interface *ifp = NULL;
+
+ babel_enable_if_index = babel_enable_if_lookup (ifname);
+ if (babel_enable_if_index < 0)
+ return -1;
+
+ str = vector_slot (babel_enable_if, babel_enable_if_index);
+ free (str);
+ vector_unset (babel_enable_if, babel_enable_if_index);
+
+ ifp = if_lookup_by_name(ifname, VRF_DEFAULT);
+ if (ifp != NULL)
+ interface_reset(ifp);
+
+ return 1;
+}
+
+/* [Babel Command] Babel enable on specified interface or matched network. */
+DEFUN (babel_network,
+ babel_network_cmd,
+ "network IF_OR_ADDR",
+ "Enable Babel protocol on specified interface or network.\n"
+ "Interface or address\n")
+{
+ int ret;
+ struct prefix p;
+
+ ret = str2prefix (argv[1]->arg, &p);
+
+ /* Given string is: */
+ if (ret) /* an IPv4 or v6 network */
+ return CMD_ERR_NO_MATCH; /* not implemented yet */
+ else /* an interface name */
+ ret = babel_enable_if_add (argv[1]->arg);
+
+ if (ret < 0) {
+ vty_out (vty, "There is same network configuration %s\n",
+ argv[1]->arg);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* [Babel Command] Babel enable on specified interface or matched network. */
+DEFUN (no_babel_network,
+ no_babel_network_cmd,
+ "no network IF_OR_ADDR",
+ NO_STR
+ "Disable Babel protocol on specified interface or network.\n"
+ "Interface or address\n")
+{
+ int ret;
+ struct prefix p;
+
+ ret = str2prefix (argv[2]->arg, &p);
+
+ /* Given string is: */
+ if (ret) /* an IPv4 or v6 network */
+ return CMD_ERR_NO_MATCH; /* not implemented yet */
+ else /* an interface name */
+ ret = babel_enable_if_delete (argv[2]->arg);
+
+ if (ret < 0) {
+ vty_out (vty, "can't find network %s\n",argv[2]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* There are a number of interface parameters that must be changed when
+ an interface becomes wired/wireless. In Quagga, they cannot be
+ configured separately. */
+
+static void
+babel_set_wired_internal(babel_interface_nfo *babel_ifp, int wired)
+{
+ if(wired) {
+ SET_FLAG(babel_ifp->flags, BABEL_IF_WIRED);
+ SET_FLAG(babel_ifp->flags, BABEL_IF_SPLIT_HORIZON);
+ babel_ifp->cost = BABEL_DEFAULT_RXCOST_WIRED;
+ babel_ifp->channel = BABEL_IF_CHANNEL_NONINTERFERING;
+ UNSET_FLAG(babel_ifp->flags, BABEL_IF_LQ);
+ }
+ else {
+ UNSET_FLAG(babel_ifp->flags, BABEL_IF_WIRED);
+ UNSET_FLAG(babel_ifp->flags, BABEL_IF_SPLIT_HORIZON);
+ babel_ifp->cost = BABEL_DEFAULT_RXCOST_WIRELESS;
+ babel_ifp->channel = BABEL_IF_CHANNEL_INTERFERING;
+ SET_FLAG(babel_ifp->flags, BABEL_IF_LQ);
+ }
+
+}
+
+/* [Interface Command] Tell the interface is wire. */
+DEFPY (babel_set_wired,
+ babel_set_wired_cmd,
+ "[no] babel wired",
+ NO_STR
+ "Babel interface commands\n"
+ "Enable wired optimizations\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+
+ assert (babel_ifp != NULL);
+ babel_set_wired_internal(babel_ifp, no ? 0 : 1);
+ return CMD_SUCCESS;
+}
+
+/* [Interface Command] Tell the interface is wireless (default). */
+DEFPY (babel_set_wireless,
+ babel_set_wireless_cmd,
+ "[no] babel wireless",
+ NO_STR
+ "Babel interface commands\n"
+ "Disable wired optimizations (assume wireless)\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+
+ assert (babel_ifp != NULL);
+ babel_set_wired_internal(babel_ifp, no ? 1 : 0);
+ return CMD_SUCCESS;
+}
+
+/* [Interface Command] Enable split horizon. */
+DEFPY (babel_split_horizon,
+ babel_split_horizon_cmd,
+ "[no] babel split-horizon",
+ NO_STR
+ "Babel interface commands\n"
+ "Enable split horizon processing\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+
+ assert (babel_ifp != NULL);
+ if (!no)
+ SET_FLAG(babel_ifp->flags, BABEL_IF_SPLIT_HORIZON);
+ else
+ UNSET_FLAG(babel_ifp->flags, BABEL_IF_SPLIT_HORIZON);
+ return CMD_SUCCESS;
+}
+
+/* [Interface Command]. */
+DEFPY (babel_set_hello_interval,
+ babel_set_hello_interval_cmd,
+ "[no] babel hello-interval (20-655340)",
+ NO_STR
+ "Babel interface commands\n"
+ "Time between scheduled hellos\n"
+ "Milliseconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ babel_ifp->hello_interval = no ?
+ BABEL_DEFAULT_HELLO_INTERVAL : hello_interval;
+ return CMD_SUCCESS;
+}
+
+/* [Interface Command]. */
+DEFPY (babel_set_update_interval,
+ babel_set_update_interval_cmd,
+ "[no] babel update-interval (20-655340)",
+ NO_STR
+ "Babel interface commands\n"
+ "Time between scheduled updates\n"
+ "Milliseconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ babel_ifp->update_interval = no ?
+ BABEL_DEFAULT_UPDATE_INTERVAL : update_interval;
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_rxcost,
+ babel_set_rxcost_cmd,
+ "[no] babel rxcost (1-65534)",
+ NO_STR
+ "Babel interface commands\n"
+ "Rxcost multiplier\n"
+ "Units\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ if (no)
+ rxcost = CHECK_FLAG(babel_ifp->flags, BABEL_IF_WIRED) ?
+ BABEL_DEFAULT_RXCOST_WIRED : BABEL_DEFAULT_RXCOST_WIRELESS;
+
+ babel_ifp->cost = rxcost;
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_rtt_decay,
+ babel_set_rtt_decay_cmd,
+ "[no] babel rtt-decay (1-256)",
+ NO_STR
+ "Babel interface commands\n"
+ "Decay factor for exponential moving average of RTT samples\n"
+ "Units of 1/256\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ babel_ifp->rtt_decay = no ? BABEL_DEFAULT_RTT_DECAY : rtt_decay;
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_rtt_min,
+ babel_set_rtt_min_cmd,
+ "[no] babel rtt-min (1-65535)",
+ NO_STR
+ "Babel interface commands\n"
+ "Minimum RTT starting for increasing cost\n"
+ "Milliseconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ /* The value is entered in milliseconds but stored as microseconds. */
+ babel_ifp->rtt_min = no ? BABEL_DEFAULT_RTT_MIN : rtt_min * 1000;
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_rtt_max,
+ babel_set_rtt_max_cmd,
+ "[no] babel rtt-max (1-65535)",
+ NO_STR
+ "Babel interface commands\n"
+ "Maximum RTT\n"
+ "Milliseconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ /* The value is entered in milliseconds but stored as microseconds. */
+ babel_ifp->rtt_max = no ? BABEL_DEFAULT_RTT_MAX : rtt_max * 1000;
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_max_rtt_penalty,
+ babel_set_max_rtt_penalty_cmd,
+ "[no] babel max-rtt-penalty (0-65535)",
+ NO_STR
+ "Babel interface commands\n"
+ "Maximum additional cost due to RTT\n"
+ "Milliseconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ babel_ifp->max_rtt_penalty = no ?
+ BABEL_DEFAULT_MAX_RTT_PENALTY : max_rtt_penalty;
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_enable_timestamps,
+ babel_set_enable_timestamps_cmd,
+ "[no] babel enable-timestamps",
+ NO_STR
+ "Babel interface commands\n"
+ "Enable timestamps\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+ if (!no)
+ SET_FLAG(babel_ifp->flags, BABEL_IF_TIMESTAMPS);
+ else
+ UNSET_FLAG(babel_ifp->flags, BABEL_IF_TIMESTAMPS);
+ return CMD_SUCCESS;
+}
+
+DEFPY (babel_set_channel,
+ babel_set_channel_cmd,
+ "[no] babel channel <(1-254)$ch|interfering$interfering|"
+ "noninterfering$noninterfering>",
+ NO_STR
+ "Babel interface commands\n"
+ "Channel number for diversity routing\n"
+ "Number\n"
+ "Mark channel as interfering\n"
+ "Mark channel as noninterfering\n"
+ )
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ babel_interface_nfo *babel_ifp;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ assert (babel_ifp != NULL);
+
+ if (no)
+ ch = CHECK_FLAG(babel_ifp->flags, BABEL_IF_WIRED) ?
+ BABEL_IF_CHANNEL_NONINTERFERING : BABEL_IF_CHANNEL_INTERFERING;
+ else if (interfering)
+ ch = BABEL_IF_CHANNEL_INTERFERING;
+ else if (noninterfering)
+ ch = BABEL_IF_CHANNEL_NONINTERFERING;
+
+ babel_ifp->channel = ch;
+ return CMD_SUCCESS;
+}
+
+/* This should be no more than half the hello interval, so that hellos
+ aren't sent late. The result is in milliseconds. */
+unsigned
+jitter(babel_interface_nfo *babel_ifp, int urgent)
+{
+ unsigned interval = babel_ifp->hello_interval;
+ if(urgent)
+ interval = MIN(interval, 100);
+ else
+ interval = MIN(interval, 4000);
+ return roughly(interval) / 4;
+}
+
+unsigned
+update_jitter(babel_interface_nfo *babel_ifp, int urgent)
+{
+ unsigned interval = babel_ifp->hello_interval;
+ if(urgent)
+ interval = MIN(interval, 100);
+ else
+ interval = MIN(interval, 4000);
+ return roughly(interval);
+}
+
+/* calculate babeld's specific datas of an interface (change when the interface
+ change) */
+static int
+interface_recalculate(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ unsigned char *tmp = NULL;
+ int mtu, rc;
+ struct ipv6_mreq mreq;
+
+ if (!IS_ENABLE(ifp))
+ return -1;
+
+ if (!if_is_operative(ifp) || !CHECK_FLAG(ifp->flags, IFF_RUNNING)) {
+ interface_reset(ifp);
+ return -1;
+ }
+
+ SET_FLAG(babel_ifp->flags, BABEL_IF_IS_UP);
+
+ mtu = MIN(ifp->mtu, ifp->mtu6);
+
+ /* We need to be able to fit at least two messages into a packet,
+ so MTUs below 116 require lower layer fragmentation. */
+ /* In IPv6, the minimum MTU is 1280, and every host must be able
+ to reassemble up to 1500 bytes, but I'd rather not rely on this. */
+ if(mtu < 128) {
+ debugf(BABEL_DEBUG_IF, "Suspiciously low MTU %d on interface %s (%d).",
+ mtu, ifp->name, ifp->ifindex);
+ mtu = 128;
+ }
+
+ /* 4 for Babel header; 40 for IPv6 header, 8 for UDP header, 12 for good luck. */
+ babel_ifp->bufsize = mtu - 4 - 60;
+ tmp = babel_ifp->sendbuf;
+ babel_ifp->sendbuf = realloc(babel_ifp->sendbuf, babel_ifp->bufsize);
+ if(babel_ifp->sendbuf == NULL) {
+ flog_err(EC_BABEL_MEMORY, "Couldn't reallocate sendbuf.");
+ free(tmp);
+ babel_ifp->bufsize = 0;
+ return -1;
+ }
+ tmp = NULL;
+
+ rc = resize_receive_buffer(mtu);
+ if(rc < 0)
+ zlog_warn("couldn't resize receive buffer for interface %s (%d) (%d bytes).",
+ ifp->name, ifp->ifindex, mtu);
+
+ memset(&mreq, 0, sizeof(mreq));
+ memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16);
+ mreq.ipv6mr_interface = ifp->ifindex;
+
+ rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char*)&mreq, sizeof(mreq));
+ if (rc < 0 && errno != EADDRINUSE) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "setsockopt(IPV6_JOIN_GROUP) on interface '%s': %s",
+ ifp->name, safe_strerror(errno));
+ /* This is probably due to a missing link-local address,
+ so down this interface, and wait until the main loop
+ tries to up it again. */
+ interface_reset(ifp);
+ return -1;
+ }
+
+ set_timeout(&babel_ifp->hello_timeout, babel_ifp->hello_interval);
+ set_timeout(&babel_ifp->update_timeout, babel_ifp->update_interval);
+ send_hello(ifp);
+ send_request(ifp, NULL, 0);
+
+ update_interface_metric(ifp);
+
+ debugf(BABEL_DEBUG_COMMON,
+ "Upped interface %s (%s, cost=%d, channel=%d%s).",
+ ifp->name,
+ CHECK_FLAG(babel_ifp->flags, BABEL_IF_WIRED) ? "wired" : "wireless",
+ babel_ifp->cost,
+ babel_ifp->channel,
+ babel_ifp->ipv4 ? ", IPv4" : "");
+
+ if(rc > 0)
+ send_update(ifp, 0, NULL, 0);
+
+ return 1;
+}
+
+/* Reset the interface as it was new: it's not removed from the interface list,
+ and may be considered as a upped interface. */
+static int
+interface_reset(struct interface *ifp)
+{
+ int rc;
+ struct ipv6_mreq mreq;
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+
+ if (!CHECK_FLAG(babel_ifp->flags, BABEL_IF_IS_UP))
+ return 0;
+
+ debugf(BABEL_DEBUG_IF, "interface reset: %s", ifp->name);
+
+ UNSET_FLAG(babel_ifp->flags, BABEL_IF_IS_UP);
+
+ flush_interface_routes(ifp, 0);
+ babel_ifp->buffered = 0;
+ babel_ifp->bufsize = 0;
+ free(babel_ifp->sendbuf);
+ babel_ifp->num_buffered_updates = 0;
+ babel_ifp->update_bufsize = 0;
+ if(babel_ifp->buffered_updates)
+ free(babel_ifp->buffered_updates);
+ babel_ifp->buffered_updates = NULL;
+ babel_ifp->sendbuf = NULL;
+
+ if(ifp->ifindex > 0) {
+ memset(&mreq, 0, sizeof(mreq));
+ memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16);
+ mreq.ipv6mr_interface = ifp->ifindex;
+ rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (char*)&mreq, sizeof(mreq));
+ if(rc < 0)
+ flog_err_sys(EC_LIB_SOCKET,
+ "setsockopt(IPV6_LEAVE_GROUP) on interface '%s': %s",
+ ifp->name, safe_strerror(errno));
+ }
+
+ update_interface_metric(ifp);
+
+ debugf(BABEL_DEBUG_COMMON,"Upped network %s (%s, cost=%d%s).",
+ ifp->name,
+ CHECK_FLAG(babel_ifp->flags, BABEL_IF_WIRED) ? "wired" : "wireless",
+ babel_ifp->cost,
+ babel_ifp->ipv4 ? ", IPv4" : "");
+
+ return 1;
+}
+
+/* Send retraction to all, and reset all interfaces statistics. */
+void
+babel_interface_close_all(void)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp = NULL;
+
+ FOR_ALL_INTERFACES(vrf, ifp) {
+ if(!if_up(ifp))
+ continue;
+ send_wildcard_retraction(ifp);
+ /* Make sure that we expire quickly from our neighbours'
+ association caches. */
+ send_hello_noupdate(ifp, 10);
+ flushbuf(ifp);
+ usleep(roughly(1000));
+ gettime(&babel_now);
+ }
+ FOR_ALL_INTERFACES(vrf, ifp) {
+ if(!if_up(ifp))
+ continue;
+ /* Make sure they got it. */
+ send_wildcard_retraction(ifp);
+ send_hello_noupdate(ifp, 1);
+ flushbuf(ifp);
+ usleep(roughly(10000));
+ gettime(&babel_now);
+ interface_reset(ifp);
+ }
+}
+
+/* return "true" if address is one of our ipv6 addresses */
+int
+is_interface_ll_address(struct interface *ifp, const unsigned char *address)
+{
+ struct connected *connected;
+ struct listnode *node;
+
+ if(!if_up(ifp))
+ return 0;
+
+ FOR_ALL_INTERFACES_ADDRESSES(ifp, connected, node) {
+ if (connected->address->family == AF_INET6
+ && memcmp(&connected->address->u.prefix6, address,
+ IPV6_MAX_BYTELEN)
+ == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+show_babel_interface_sub (struct vty *vty, struct interface *ifp)
+{
+ int is_up;
+ babel_interface_nfo *babel_ifp;
+
+ vty_out (vty, "%s is %s\n", ifp->name,
+ ((is_up = if_is_operative(ifp)) ? "up" : "down"));
+ vty_out (vty, " ifindex %u, MTU %u bytes %s\n",
+ ifp->ifindex, MIN(ifp->mtu, ifp->mtu6), if_flag_dump(ifp->flags));
+
+ if (!IS_ENABLE(ifp))
+ {
+ vty_out (vty, " Babel protocol is not enabled on this interface\n");
+ return;
+ }
+ if (!is_up)
+ {
+ vty_out (vty,
+ " Babel protocol is enabled, but not running on this interface\n");
+ return;
+ }
+ babel_ifp = babel_get_if_nfo (ifp);
+ vty_out (vty, " Babel protocol is running on this interface\n");
+ vty_out (vty, " Operating mode is \"%s\"\n",
+ CHECK_FLAG(babel_ifp->flags, BABEL_IF_WIRED) ? "wired" : "wireless");
+ vty_out (vty, " Split horizon mode is %s\n",
+ CHECK_FLAG(babel_ifp->flags, BABEL_IF_SPLIT_HORIZON) ? "On" : "Off");
+ vty_out (vty, " Hello interval is %u ms\n", babel_ifp->hello_interval);
+ vty_out (vty, " Update interval is %u ms\n", babel_ifp->update_interval);
+ vty_out (vty, " Rxcost multiplier is %u\n", babel_ifp->cost);
+}
+
+DEFUN (show_babel_interface,
+ show_babel_interface_cmd,
+ "show babel interface [IFNAME]",
+ SHOW_STR
+ "Babel information\n"
+ "Interface information\n"
+ "Interface\n")
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp;
+
+ if (argc == 3)
+ {
+ FOR_ALL_INTERFACES (vrf, ifp)
+ show_babel_interface_sub (vty, ifp);
+ return CMD_SUCCESS;
+ }
+ if ((ifp = if_lookup_by_name (argv[3]->arg, VRF_DEFAULT)) == NULL)
+ {
+ vty_out (vty, "No such interface name\n");
+ return CMD_WARNING;
+ }
+ show_babel_interface_sub (vty, ifp);
+ return CMD_SUCCESS;
+}
+
+static void
+show_babel_neighbour_sub (struct vty *vty, struct neighbour *neigh)
+{
+ vty_out (vty,
+ "Neighbour %s dev %s reach %04x rxcost %d txcost %d rtt %s rttcost %d%s.\n",
+ format_address(neigh->address),
+ neigh->ifp->name,
+ neigh->reach,
+ neighbour_rxcost(neigh),
+ neigh->txcost,
+ format_thousands(neigh->rtt),
+ neighbour_rttcost(neigh),
+ if_up(neigh->ifp) ? "" : " (down)");
+}
+
+DEFUN (show_babel_neighbour,
+ show_babel_neighbour_cmd,
+ "show babel neighbor [IFNAME]",
+ SHOW_STR
+ "Babel information\n"
+ "Print neighbors\n"
+ "Interface\n")
+{
+ struct neighbour *neigh;
+ struct interface *ifp;
+
+ if (argc == 3) {
+ FOR_ALL_NEIGHBOURS(neigh) {
+ show_babel_neighbour_sub(vty, neigh);
+ }
+ return CMD_SUCCESS;
+ }
+ if ((ifp = if_lookup_by_name (argv[3]->arg, VRF_DEFAULT)) == NULL)
+ {
+ vty_out (vty, "No such interface name\n");
+ return CMD_WARNING;
+ }
+ FOR_ALL_NEIGHBOURS(neigh) {
+ if(ifp->ifindex == neigh->ifp->ifindex) {
+ show_babel_neighbour_sub(vty, neigh);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+static int
+babel_prefix_eq(struct prefix *prefix, unsigned char *p, int plen)
+{
+ if(prefix->family == AF_INET6) {
+ if (prefix->prefixlen != plen
+ || memcmp(&prefix->u.prefix6, p, IPV6_MAX_BYTELEN) != 0)
+ return 0;
+ } else if(prefix->family == AF_INET) {
+ if (plen < 96 || !v4mapped(p) || prefix->prefixlen != plen - 96
+ || memcmp(&prefix->u.prefix4, p + 12, IPV4_MAX_BYTELEN) != 0)
+ return 0;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+show_babel_routes_sub(struct babel_route *route, struct vty *vty,
+ struct prefix *prefix)
+{
+ const unsigned char *nexthop =
+ memcmp(route->nexthop, route->neigh->address, IPV6_MAX_BYTELEN)
+ == 0
+ ? NULL
+ : route->nexthop;
+ char channels[100];
+
+ if (prefix
+ && !babel_prefix_eq(prefix, route->src->prefix, route->src->plen))
+ return;
+
+ if (route->channels[0] == 0)
+ channels[0] = '\0';
+ else {
+ int k, j = 0;
+ snprintf(channels, sizeof(channels), " chan (");
+ j = strlen(channels);
+ for (k = 0; k < DIVERSITY_HOPS; k++) {
+ if (route->channels[k] == 0)
+ break;
+ if (k > 0)
+ channels[j++] = ',';
+ snprintf(channels + j, 100 - j, "%u",
+ route->channels[k]);
+ j = strlen(channels);
+ }
+ snprintf(channels + j, 100 - j, ")");
+ if (k == 0)
+ channels[0] = '\0';
+ }
+
+ vty_out (vty,
+ "%s metric %d refmetric %d id %s seqno %d%s age %d via %s neigh %s%s%s%s\n",
+ format_prefix(route->src->prefix, route->src->plen),
+ route_metric(route), route->refmetric,
+ format_eui64(route->src->id),
+ (int)route->seqno,
+ channels,
+ (int)(babel_now.tv_sec - route->time),
+ route->neigh->ifp->name,
+ format_address(route->neigh->address),
+ nexthop ? " nexthop " : "",
+ nexthop ? format_address(nexthop) : "",
+ route->installed ? " (installed)" : route_feasible(route) ? " (feasible)" : "");
+}
+
+static void
+show_babel_xroutes_sub (struct xroute *xroute, struct vty *vty,
+ struct prefix *prefix)
+{
+ if(prefix && !babel_prefix_eq(prefix, xroute->prefix, xroute->plen))
+ return;
+
+ vty_out (vty, "%s metric %d (exported)\n",
+ format_prefix(xroute->prefix, xroute->plen),
+ xroute->metric);
+}
+
+DEFUN (show_babel_route,
+ show_babel_route_cmd,
+ "show babel route",
+ SHOW_STR
+ "Babel information\n"
+ "Babel internal routing table\n")
+{
+ struct route_stream *routes = NULL;
+ struct xroute_stream *xroutes = NULL;
+ routes = route_stream(0);
+ if(routes) {
+ while(1) {
+ struct babel_route *route = route_stream_next(routes);
+ if(route == NULL)
+ break;
+ show_babel_routes_sub(route, vty, NULL);
+ }
+ route_stream_done(routes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ xroutes = xroute_stream();
+ if(xroutes) {
+ while(1) {
+ struct xroute *xroute = xroute_stream_next(xroutes);
+ if(xroute == NULL)
+ break;
+ show_babel_xroutes_sub(xroute, vty, NULL);
+ }
+ xroute_stream_done(xroutes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_babel_route_prefix,
+ show_babel_route_prefix_cmd,
+ "show babel route <A.B.C.D/M|X:X::X:X/M>",
+ SHOW_STR
+ "Babel information\n"
+ "Babel internal routing table\n"
+ "IPv4 prefix <network>/<length>\n"
+ "IPv6 prefix <network>/<length>\n")
+{
+ struct route_stream *routes = NULL;
+ struct xroute_stream *xroutes = NULL;
+ struct prefix prefix;
+ int ret;
+
+ ret = str2prefix(argv[3]->arg, &prefix);
+ if(ret == 0) {
+ vty_out (vty, "%% Malformed address\n");
+ return CMD_WARNING;
+ }
+
+ routes = route_stream(0);
+ if(routes) {
+ while(1) {
+ struct babel_route *route = route_stream_next(routes);
+ if(route == NULL)
+ break;
+ show_babel_routes_sub(route, vty, &prefix);
+ }
+ route_stream_done(routes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ xroutes = xroute_stream();
+ if(xroutes) {
+ while(1) {
+ struct xroute *xroute = xroute_stream_next(xroutes);
+ if(xroute == NULL)
+ break;
+ show_babel_xroutes_sub(xroute, vty, &prefix);
+ }
+ xroute_stream_done(xroutes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (show_babel_route_addr,
+ show_babel_route_addr_cmd,
+ "show babel route A.B.C.D",
+ SHOW_STR
+ "Babel information\n"
+ "Babel internal routing table\n"
+ "IPv4 address <network>/<length>\n")
+{
+ struct in_addr addr;
+ char buf[INET_ADDRSTRLEN + 8];
+ char buf1[INET_ADDRSTRLEN + 8];
+ struct route_stream *routes = NULL;
+ struct xroute_stream *xroutes = NULL;
+ struct prefix prefix;
+ int ret;
+
+ ret = inet_aton (argv[3]->arg, &addr);
+ if (ret <= 0) {
+ vty_out (vty, "%% Malformed address\n");
+ return CMD_WARNING;
+ }
+
+ /* Quagga has no convenient prefix constructors. */
+ snprintf(buf, sizeof(buf), "%s/%d",
+ inet_ntop(AF_INET, &addr, buf1, sizeof(buf1)), 32);
+
+ ret = str2prefix(buf, &prefix);
+ if (ret == 0) {
+ vty_out (vty, "%% Parse error -- this shouldn't happen\n");
+ return CMD_WARNING;
+ }
+
+ routes = route_stream(0);
+ if(routes) {
+ while(1) {
+ struct babel_route *route = route_stream_next(routes);
+ if(route == NULL)
+ break;
+ show_babel_routes_sub(route, vty, &prefix);
+ }
+ route_stream_done(routes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ xroutes = xroute_stream();
+ if(xroutes) {
+ while(1) {
+ struct xroute *xroute = xroute_stream_next(xroutes);
+ if(xroute == NULL)
+ break;
+ show_babel_xroutes_sub(xroute, vty, &prefix);
+ }
+ xroute_stream_done(xroutes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_babel_route_addr6,
+ show_babel_route_addr6_cmd,
+ "show babel route X:X::X:X",
+ SHOW_STR
+ "Babel information\n"
+ "Babel internal routing table\n"
+ "IPv6 address <network>/<length>\n")
+{
+ struct in6_addr addr;
+ char buf1[INET6_ADDRSTRLEN];
+ char buf[INET6_ADDRSTRLEN + 8];
+ struct route_stream *routes = NULL;
+ struct xroute_stream *xroutes = NULL;
+ struct prefix prefix;
+ int ret;
+
+ ret = inet_pton (AF_INET6, argv[3]->arg, &addr);
+ if (ret <= 0) {
+ vty_out (vty, "%% Malformed address\n");
+ return CMD_WARNING;
+ }
+
+ /* Quagga has no convenient prefix constructors. */
+ snprintf(buf, sizeof(buf), "%s/%d",
+ inet_ntop(AF_INET6, &addr, buf1, sizeof(buf1)), 128);
+
+ ret = str2prefix(buf, &prefix);
+ if (ret == 0) {
+ vty_out (vty, "%% Parse error -- this shouldn't happen\n");
+ return CMD_WARNING;
+ }
+
+ routes = route_stream(0);
+ if(routes) {
+ while(1) {
+ struct babel_route *route = route_stream_next(routes);
+ if(route == NULL)
+ break;
+ show_babel_routes_sub(route, vty, &prefix);
+ }
+ route_stream_done(routes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ xroutes = xroute_stream();
+ if(xroutes) {
+ while(1) {
+ struct xroute *xroute = xroute_stream_next(xroutes);
+ if(xroute == NULL)
+ break;
+ show_babel_xroutes_sub(xroute, vty, &prefix);
+ }
+ xroute_stream_done(xroutes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_babel_parameters,
+ show_babel_parameters_cmd,
+ "show babel parameters",
+ SHOW_STR
+ "Babel information\n"
+ "Configuration information\n")
+{
+ struct babel *babel_ctx;
+
+ vty_out (vty, " -- Babel running configuration --\n");
+ show_babel_main_configuration(vty);
+
+ babel_ctx = babel_lookup();
+ if (babel_ctx) {
+ vty_out (vty, " -- distribution lists --\n");
+ config_show_distribute(vty, babel_ctx->distribute_ctx);
+ }
+ return CMD_SUCCESS;
+}
+
+void
+babel_if_init(void)
+{
+ /* initialize interface list */
+ hook_register_prio(if_add, 0, babel_if_new_hook);
+ hook_register_prio(if_del, 0, babel_if_delete_hook);
+
+ babel_enable_if = vector_init (1);
+
+ /* install interface node and commands */
+ if_cmd_init(interface_config_write);
+
+ install_element(BABEL_NODE, &babel_network_cmd);
+ install_element(BABEL_NODE, &no_babel_network_cmd);
+ install_element(INTERFACE_NODE, &babel_split_horizon_cmd);
+ install_element(INTERFACE_NODE, &babel_set_wired_cmd);
+ install_element(INTERFACE_NODE, &babel_set_wireless_cmd);
+ install_element(INTERFACE_NODE, &babel_set_hello_interval_cmd);
+ install_element(INTERFACE_NODE, &babel_set_update_interval_cmd);
+ install_element(INTERFACE_NODE, &babel_set_rxcost_cmd);
+ install_element(INTERFACE_NODE, &babel_set_channel_cmd);
+ install_element(INTERFACE_NODE, &babel_set_rtt_decay_cmd);
+ install_element(INTERFACE_NODE, &babel_set_rtt_min_cmd);
+ install_element(INTERFACE_NODE, &babel_set_rtt_max_cmd);
+ install_element(INTERFACE_NODE, &babel_set_max_rtt_penalty_cmd);
+ install_element(INTERFACE_NODE, &babel_set_enable_timestamps_cmd);
+
+ /* "show babel ..." commands */
+ install_element(VIEW_NODE, &show_babel_interface_cmd);
+ install_element(VIEW_NODE, &show_babel_neighbour_cmd);
+ install_element(VIEW_NODE, &show_babel_route_cmd);
+ install_element(VIEW_NODE, &show_babel_route_prefix_cmd);
+ install_element(VIEW_NODE, &show_babel_route_addr_cmd);
+ install_element(VIEW_NODE, &show_babel_route_addr6_cmd);
+ install_element(VIEW_NODE, &show_babel_parameters_cmd);
+}
+
+/* hooks: functions called respectively when struct interface is
+ created or deleted. */
+static int
+babel_if_new_hook (struct interface *ifp)
+{
+ ifp->info = babel_interface_allocate();
+ return 0;
+}
+
+static int
+babel_if_delete_hook (struct interface *ifp)
+{
+ babel_interface_free(ifp->info);
+ ifp->info = NULL;
+ return 0;
+}
+
+/* Output an "interface" section for each of the known interfaces with
+babeld-specific statement lines where appropriate. */
+static int
+interface_config_write (struct vty *vty)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp;
+ int write = 0;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if_vty_config_start(vty, ifp);
+ if (ifp->desc)
+ vty_out (vty, " description %s\n",ifp->desc);
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo (ifp);
+ /* wireless is the default*/
+ if (CHECK_FLAG (babel_ifp->flags, BABEL_IF_WIRED))
+ {
+ vty_out (vty, " babel wired\n");
+ write++;
+ }
+ if (babel_ifp->hello_interval != BABEL_DEFAULT_HELLO_INTERVAL)
+ {
+ vty_out (vty, " babel hello-interval %u\n",
+ babel_ifp->hello_interval);
+ write++;
+ }
+ if (babel_ifp->update_interval != BABEL_DEFAULT_UPDATE_INTERVAL)
+ {
+ vty_out (vty, " babel update-interval %u\n",
+ babel_ifp->update_interval);
+ write++;
+ }
+ if (CHECK_FLAG(babel_ifp->flags, BABEL_IF_TIMESTAMPS)) {
+ vty_out(vty, " babel enable-timestamps\n");
+ write++;
+ }
+ if (babel_ifp->max_rtt_penalty != BABEL_DEFAULT_MAX_RTT_PENALTY) {
+ vty_out(vty, " babel max-rtt-penalty %u\n",
+ babel_ifp->max_rtt_penalty);
+ write++;
+ }
+ if (babel_ifp->rtt_decay != BABEL_DEFAULT_RTT_DECAY) {
+ vty_out(vty, " babel rtt-decay %u\n", babel_ifp->rtt_decay);
+ write++;
+ }
+ if (babel_ifp->rtt_min != BABEL_DEFAULT_RTT_MIN) {
+ vty_out(vty, " babel rtt-min %u\n", babel_ifp->rtt_min / 1000);
+ write++;
+ }
+ if (babel_ifp->rtt_max != BABEL_DEFAULT_RTT_MAX) {
+ vty_out(vty, " babel rtt-max %u\n", babel_ifp->rtt_max / 1000);
+ write++;
+ }
+ /* Some parameters have different defaults for wired/wireless. */
+ if (CHECK_FLAG (babel_ifp->flags, BABEL_IF_WIRED)) {
+ if (!CHECK_FLAG (babel_ifp->flags, BABEL_IF_SPLIT_HORIZON)) {
+ vty_out (vty, " no babel split-horizon\n");
+ write++;
+ }
+ if (babel_ifp->cost != BABEL_DEFAULT_RXCOST_WIRED) {
+ vty_out (vty, " babel rxcost %u\n", babel_ifp->cost);
+ write++;
+ }
+ if (babel_ifp->channel == BABEL_IF_CHANNEL_INTERFERING) {
+ vty_out (vty, " babel channel interfering\n");
+ write++;
+ } else if(babel_ifp->channel != BABEL_IF_CHANNEL_NONINTERFERING) {
+ vty_out (vty, " babel channel %d\n",babel_ifp->channel);
+ write++;
+ }
+ } else {
+ if (CHECK_FLAG (babel_ifp->flags, BABEL_IF_SPLIT_HORIZON)) {
+ vty_out (vty, " babel split-horizon\n");
+ write++;
+ }
+ if (babel_ifp->cost != BABEL_DEFAULT_RXCOST_WIRELESS) {
+ vty_out (vty, " babel rxcost %u\n", babel_ifp->cost);
+ write++;
+ }
+ if (babel_ifp->channel == BABEL_IF_CHANNEL_NONINTERFERING) {
+ vty_out (vty, " babel channel noninterfering\n");
+ write++;
+ } else if(babel_ifp->channel != BABEL_IF_CHANNEL_INTERFERING) {
+ vty_out (vty, " babel channel %d\n",babel_ifp->channel);
+ write++;
+ }
+ }
+ if_vty_config_end(vty);
+ write++;
+ }
+ return write;
+}
+
+/* Output a "network" statement line for each of the enabled interfaces. */
+int
+babel_enable_if_config_write (struct vty * vty)
+{
+ unsigned int i, lines = 0;
+ char *str;
+
+ for (i = 0; i < vector_active (babel_enable_if); i++)
+ if ((str = vector_slot (babel_enable_if, i)) != NULL)
+ {
+ vty_out (vty, " network %s\n", str);
+ lines++;
+ }
+ return lines;
+}
+
+/* functions to allocate or free memory for a babel_interface_nfo, filling
+ needed fields */
+static babel_interface_nfo *
+babel_interface_allocate (void)
+{
+ babel_interface_nfo *babel_ifp;
+ babel_ifp = XCALLOC(MTYPE_BABEL_IF, sizeof(babel_interface_nfo));
+ /* All flags are unset */
+ babel_ifp->bucket_time = babel_now.tv_sec;
+ babel_ifp->bucket = BUCKET_TOKENS_MAX;
+ babel_ifp->hello_seqno = (frr_weak_random() & 0xFFFF);
+ babel_ifp->rtt_decay = BABEL_DEFAULT_RTT_DECAY;
+ babel_ifp->rtt_min = BABEL_DEFAULT_RTT_MIN;
+ babel_ifp->rtt_max = BABEL_DEFAULT_RTT_MAX;
+ babel_ifp->max_rtt_penalty = BABEL_DEFAULT_MAX_RTT_PENALTY;
+ babel_ifp->hello_interval = BABEL_DEFAULT_HELLO_INTERVAL;
+ babel_ifp->update_interval = BABEL_DEFAULT_UPDATE_INTERVAL;
+ babel_ifp->channel = BABEL_IF_CHANNEL_INTERFERING;
+ babel_set_wired_internal(babel_ifp, 0);
+
+ return babel_ifp;
+}
+
+static void
+babel_interface_free (babel_interface_nfo *babel_ifp)
+{
+ XFREE(MTYPE_BABEL_IF, babel_ifp);
+}
diff --git a/babeld/babel_interface.h b/babeld/babel_interface.h
new file mode 100644
index 0000000..12fa6e2
--- /dev/null
+++ b/babeld/babel_interface.h
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_INTERFACE_H
+#define BABEL_INTERFACE_H
+
+#include <zebra.h>
+#include "zclient.h"
+#include "vty.h"
+#include "distribute.h"
+
+#define CONFIG_DEFAULT 0
+#define CONFIG_NO 1
+#define CONFIG_YES 2
+
+/* babeld interface information */
+struct babel_interface {
+ unsigned short flags; /* see below */
+ unsigned short cost;
+ int channel;
+ struct timeval hello_timeout;
+ struct timeval update_timeout;
+ struct timeval flush_timeout;
+ struct timeval update_flush_timeout;
+ unsigned char *ipv4;
+ int buffered;
+ int bufsize;
+ /* Relative position of the Hello message in the send buffer, or
+ (-1) if there is none. */
+ int buffered_hello;
+ char have_buffered_id;
+ char have_buffered_nh;
+ char have_buffered_prefix;
+ unsigned char buffered_id[8];
+ unsigned char buffered_nh[4];
+ unsigned char buffered_prefix[16];
+ unsigned char *sendbuf;
+ struct buffered_update *buffered_updates;
+ int num_buffered_updates;
+ int update_bufsize;
+ time_t bucket_time;
+ unsigned int bucket;
+ time_t last_update_time;
+ unsigned short hello_seqno;
+ unsigned hello_interval;
+ unsigned update_interval;
+ /* A higher value means we forget old RTT samples faster. Must be
+ between 1 and 256, inclusive. */
+ unsigned int rtt_decay;
+ /* Parameters for computing the cost associated to RTT. */
+ unsigned int rtt_min;
+ unsigned int rtt_max;
+ unsigned int max_rtt_penalty;
+
+ /* For filter type slot. */
+ struct access_list *list[DISTRIBUTE_MAX]; /* Access-list. */
+ struct prefix_list *prefix[DISTRIBUTE_MAX]; /* Prefix-list. */
+};
+
+typedef struct babel_interface babel_interface_nfo;
+static inline babel_interface_nfo* babel_get_if_nfo(struct interface *ifp)
+{
+ return ((babel_interface_nfo*) ifp->info);
+}
+
+/* babel_interface_nfo flags */
+#define BABEL_IF_IS_UP (1 << 0)
+#define BABEL_IF_WIRED (1 << 1)
+#define BABEL_IF_SPLIT_HORIZON (1 << 2)
+#define BABEL_IF_LQ (1 << 3)
+#define BABEL_IF_FARAWAY (1 << 4)
+#define BABEL_IF_TIMESTAMPS (1 << 5)
+
+/* Only INTERFERING can appear on the wire. */
+#define BABEL_IF_CHANNEL_UNKNOWN 0
+#define BABEL_IF_CHANNEL_INTERFERING 255
+#define BABEL_IF_CHANNEL_NONINTERFERING -2
+
+static inline int
+if_up(struct interface *ifp)
+{
+ return (if_is_operative(ifp) &&
+ ifp->connected != NULL &&
+ CHECK_FLAG(babel_get_if_nfo(ifp)->flags, BABEL_IF_IS_UP));
+}
+
+struct buffered_update {
+ unsigned char id[8];
+ unsigned char prefix[16];
+ unsigned char plen;
+ unsigned char pad[3];
+};
+
+/* init function */
+void babel_if_init(void);
+
+/* Callback functions for zebra client */
+int babel_interface_up (int, struct zclient *, zebra_size_t, vrf_id_t);
+int babel_interface_down (int, struct zclient *, zebra_size_t, vrf_id_t);
+int babel_interface_add (int, struct zclient *, zebra_size_t, vrf_id_t);
+int babel_interface_delete (int, struct zclient *, zebra_size_t, vrf_id_t);
+int babel_interface_address_add (int, struct zclient *, zebra_size_t, vrf_id_t);
+int babel_interface_address_delete (int, struct zclient *, zebra_size_t, vrf_id_t);
+
+int babel_ifp_create(struct interface *ifp);
+int babel_ifp_up(struct interface *ifp);
+int babel_ifp_down(struct interface *ifp);
+int babel_ifp_destroy(struct interface *ifp);
+
+unsigned jitter(babel_interface_nfo *, int);
+unsigned update_jitter(babel_interface_nfo *babel_ifp, int urgent);
+/* return "true" if "address" is one of our ipv6 addresses */
+int is_interface_ll_address(struct interface *ifp, const unsigned char *address);
+/* Send retraction to all, and reset all interfaces statistics. */
+void babel_interface_close_all(void);
+extern int babel_enable_if_config_write (struct vty *);
+
+
+#endif
diff --git a/babeld/babel_main.c b/babeld/babel_main.c
new file mode 100644
index 0000000..b6126d5
--- /dev/null
+++ b/babeld/babel_main.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+/* include zebra library */
+#include <zebra.h>
+#include "getopt.h"
+#include "if.h"
+#include "log.h"
+#include "frrevent.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "lib/version.h"
+#include "command.h"
+#include "vty.h"
+#include "memory.h"
+#include "libfrr.h"
+#include "lib_errors.h"
+
+#include "babel_main.h"
+#include "babeld.h"
+#include "util.h"
+#include "kernel.h"
+#include "babel_interface.h"
+#include "neighbour.h"
+#include "route.h"
+#include "xroute.h"
+#include "message.h"
+#include "resend.h"
+#include "babel_zebra.h"
+#include "babel_errors.h"
+
+static void babel_fail(void);
+static void babel_init_random(void);
+static void babel_exit_properly(void);
+static void babel_save_state_file(void);
+
+
+struct event_loop *master; /* quagga's threads handler */
+struct timeval babel_now; /* current time */
+
+unsigned char myid[8]; /* unique id (mac address of an interface) */
+int debug = 0;
+
+int resend_delay = -1;
+
+const unsigned char zeroes[16] = {0};
+const unsigned char ones[16] =
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+static char state_file[1024];
+
+unsigned char protocol_group[16]; /* babel's link-local multicast address */
+int protocol_port; /* babel's port */
+int protocol_socket = -1; /* socket: communicate with others babeld */
+
+static const char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG;
+static char *babel_vty_addr = NULL;
+static int babel_vty_port = BABEL_VTY_PORT;
+
+/* babeld privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_NET_RAW,
+ ZCAP_BIND
+};
+
+struct zebra_privs_t babeld_privs =
+{
+#if defined(FRR_USER)
+ .user = FRR_USER,
+#endif
+#if defined FRR_GROUP
+ .group = FRR_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0
+};
+
+static void
+babel_sigexit(void)
+{
+ zlog_notice("Terminating on signal");
+
+ babel_exit_properly();
+}
+
+static void
+babel_sigusr1 (void)
+{
+ zlog_rotate ();
+}
+
+static struct frr_signal_t babel_signals[] =
+ {
+ {
+ .signal = SIGUSR1,
+ .handler = &babel_sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &babel_sigexit,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &babel_sigexit,
+ },
+ };
+
+struct option longopts[] =
+ {
+ { 0 }
+ };
+
+static const struct frr_yang_module_info *const babeld_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_vrf_info,
+};
+
+FRR_DAEMON_INFO(babeld, BABELD,
+ .vty_port = BABEL_VTY_PORT,
+ .proghelp = "Implementation of the BABEL routing protocol.",
+
+ .signals = babel_signals,
+ .n_signals = array_size(babel_signals),
+
+ .privs = &babeld_privs,
+
+ .yang_modules = babeld_yang_modules,
+ .n_yang_modules = array_size(babeld_yang_modules),
+);
+
+int
+main(int argc, char **argv)
+{
+ int rc;
+
+ frr_preinit (&babeld_di, argc, argv);
+ frr_opt_add ("", longopts, "");
+
+ babel_init_random();
+
+ /* set the Babel's default link-local multicast address and Babel's port */
+ parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
+ protocol_port = 6696;
+
+ /* get options */
+ while(1) {
+ int opt;
+
+ opt = frr_getopt (argc, argv, NULL);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt)
+ {
+ case 0:
+ break;
+ default:
+ frr_help_exit(1);
+ }
+ }
+
+ snprintf(state_file, sizeof(state_file), "%s/%s",
+ frr_vtydir, "babel-state");
+
+ /* create the threads handler */
+ master = frr_init ();
+
+ /* Library inits. */
+ babel_error_init();
+
+ resend_delay = BABEL_DEFAULT_RESEND_DELAY;
+ change_smoothing_half_life(BABEL_DEFAULT_SMOOTHING_HALF_LIFE);
+
+ /* init some quagga's dependencies, and babeld's commands */
+ if_zapi_callbacks(babel_ifp_create, babel_ifp_up,
+ babel_ifp_down, babel_ifp_destroy);
+ babeld_quagga_init();
+ /* init zebra client's structure and it's commands */
+ /* this replace kernel_setup && kernel_setup_socket */
+ babelz_zebra_init ();
+
+ /* init buffer */
+ rc = resize_receive_buffer(1500);
+ if(rc < 0)
+ babel_fail();
+
+ schedule_neighbours_check(5000, 1);
+
+ frr_config_fork();
+ frr_run(master);
+
+ return 0;
+}
+
+static void
+babel_fail(void)
+{
+ exit(1);
+}
+
+/* initialize random value, and set 'babel_now' by the way. */
+static void
+babel_init_random(void)
+{
+ gettime(&babel_now);
+ int rc;
+ unsigned int seed;
+
+ rc = read_random_bytes(&seed, sizeof(seed));
+ if(rc < 0) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL, "read(random): %s",
+ safe_strerror(errno));
+ seed = 42;
+ }
+
+ seed ^= (babel_now.tv_sec ^ babel_now.tv_usec);
+ srandom(seed);
+}
+
+/*
+ Load the state file: check last babeld's running state, usefull in case of
+ "/etc/init.d/babeld restart"
+ */
+void
+babel_load_state_file(void)
+{
+ int fd;
+ int rc;
+
+ fd = open(state_file, O_RDONLY);
+ if(fd < 0 && errno != ENOENT)
+ flog_err_sys(EC_LIB_SYSTEM_CALL, "open(babel-state: %s)",
+ safe_strerror(errno));
+ rc = unlink(state_file);
+ if(fd >= 0 && rc < 0) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL, "unlink(babel-state): %s",
+ safe_strerror(errno));
+ /* If we couldn't unlink it, it's probably stale. */
+ goto fini;
+ }
+ if(fd >= 0) {
+ char buf[100];
+ char buf2[100];
+ int s;
+ long t;
+ rc = read(fd, buf, 99);
+ if(rc < 0) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL, "read(babel-state): %s",
+ safe_strerror(errno));
+ } else {
+ buf[rc] = '\0';
+ rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
+ if(rc == 3 && s >= 0 && s <= 0xFFFF) {
+ unsigned char sid[8];
+ rc = parse_eui64(buf2, sid);
+ if(rc < 0) {
+ flog_err(EC_BABEL_CONFIG, "Couldn't parse babel-state.");
+ } else {
+ struct timeval realnow;
+ debugf(BABEL_DEBUG_COMMON,
+ "Got %s %d %ld from babel-state.",
+ format_eui64(sid), s, t);
+ gettimeofday(&realnow, NULL);
+ if(memcmp(sid, myid, 8) == 0)
+ myseqno = seqno_plus(s, 1);
+ else
+ flog_err(EC_BABEL_CONFIG,
+ "ID mismatch in babel-state. id=%s; old=%s",
+ format_eui64(myid),
+ format_eui64(sid));
+ }
+ } else {
+ flog_err(EC_BABEL_CONFIG, "Couldn't parse babel-state.");
+ }
+ }
+ goto fini;
+ }
+fini:
+ if (fd >= 0)
+ close(fd);
+ return ;
+}
+
+static void
+babel_exit_properly(void)
+{
+ debugf(BABEL_DEBUG_COMMON, "Exiting...");
+ usleep(roughly(10000));
+ gettime(&babel_now);
+
+ /* Uninstall and flush all routes. */
+ debugf(BABEL_DEBUG_COMMON, "Uninstall routes.");
+ flush_all_routes();
+ babel_interface_close_all();
+ babel_zebra_close_connexion();
+ babel_save_state_file();
+ debugf(BABEL_DEBUG_COMMON, "Remove pid file.");
+ debugf(BABEL_DEBUG_COMMON, "Done.");
+ frr_fini();
+
+ exit(0);
+}
+
+static void
+babel_save_state_file(void)
+{
+ int fd;
+ int rc;
+
+ debugf(BABEL_DEBUG_COMMON, "Save state file.");
+ fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ if(fd < 0) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL, "creat(babel-state): %s",
+ safe_strerror(errno));
+ unlink(state_file);
+ } else {
+ struct timeval realnow;
+ char buf[100];
+ gettimeofday(&realnow, NULL);
+ rc = snprintf(buf, 100, "%s %d %ld\n",
+ format_eui64(myid), (int)myseqno,
+ (long)realnow.tv_sec);
+ if(rc < 0 || rc >= 100) {
+ flog_err(EC_BABEL_CONFIG, "write(babel-state): overflow.");
+ unlink(state_file);
+ } else {
+ rc = write(fd, buf, rc);
+ if(rc < 0) {
+ flog_err(EC_BABEL_CONFIG, "write(babel-state): %s",
+ safe_strerror(errno));
+ unlink(state_file);
+ }
+ fsync(fd);
+ }
+ close(fd);
+ }
+}
+
+void
+show_babel_main_configuration (struct vty *vty)
+{
+ vty_out (vty,
+ "state file = %s\n"
+ "configuration file = %s\n"
+ "protocol information:\n"
+ " multicast address = %s\n"
+ " port = %d\n"
+ "vty address = %s\n"
+ "vty port = %d\n"
+ "id = %s\n"
+ "kernel_metric = %d\n",
+ state_file,
+ babeld_di.config_file ? babeld_di.config_file : babel_config_default,
+ format_address(protocol_group),
+ protocol_port,
+ babel_vty_addr ? babel_vty_addr : "None",
+ babel_vty_port,
+ format_eui64(myid),
+ kernel_metric);
+}
diff --git a/babeld/babel_main.h b/babeld/babel_main.h
new file mode 100644
index 0000000..0f9792b
--- /dev/null
+++ b/babeld/babel_main.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_MAIN_H
+#define BABEL_MAIN_H
+
+#include "vty.h"
+
+extern struct timeval babel_now; /* current time */
+extern struct event_loop *master; /* quagga's threads handler */
+extern int debug;
+extern int resend_delay;
+
+extern unsigned char myid[8];
+
+extern const unsigned char zeroes[16], ones[16];
+
+extern int protocol_port;
+extern unsigned char protocol_group[16];
+extern int protocol_socket;
+extern int kernel_socket;
+extern int max_request_hopcount;
+
+void babel_load_state_file(void);
+void show_babel_main_configuration (struct vty *vty);
+
+#endif /* BABEL_MAIN_H */
diff --git a/babeld/babel_zebra.c b/babeld/babel_zebra.c
new file mode 100644
index 0000000..bead9f2
--- /dev/null
+++ b/babeld/babel_zebra.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+/* FRR's includes */
+#include <zebra.h>
+#include "command.h"
+#include "zclient.h"
+#include "stream.h"
+
+/* babel's includes*/
+#include "babel_zebra.h"
+#include "babel_interface.h"
+#include "xroute.h"
+#include "util.h"
+
+void babelz_zebra_init(void);
+
+
+/* we must use a pointer because of zclient.c's functions (new, free). */
+struct zclient *zclient;
+
+/* Debug types */
+static const struct {
+ int type;
+ int str_min_len;
+ const char *str;
+} debug_type[] = {
+ {BABEL_DEBUG_COMMON, 1, "common"},
+ {BABEL_DEBUG_KERNEL, 1, "kernel"},
+ {BABEL_DEBUG_FILTER, 1, "filter"},
+ {BABEL_DEBUG_TIMEOUT, 1, "timeout"},
+ {BABEL_DEBUG_IF, 1, "interface"},
+ {BABEL_DEBUG_ROUTE, 1, "route"},
+ {BABEL_DEBUG_ALL, 1, "all"},
+ {0, 0, NULL}
+};
+
+/* Zebra route add and delete treatment. */
+static int
+babel_zebra_read_route (ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_route api;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ return -1;
+
+ /* we completely ignore srcdest routes for now. */
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
+ return 0;
+
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
+ babel_route_add(&api);
+ } else {
+ babel_route_delete(&api);
+ }
+
+ return 0;
+}
+
+/* [Babel Command] */
+DEFUN (babel_redistribute_type,
+ babel_redistribute_type_cmd,
+ "[no] redistribute <ipv4 " FRR_IP_REDIST_STR_BABELD "|ipv6 " FRR_IP6_REDIST_STR_BABELD ">",
+ NO_STR
+ "Redistribute\n"
+ "Redistribute IPv4 routes\n"
+ FRR_IP_REDIST_HELP_STR_BABELD
+ "Redistribute IPv6 routes\n"
+ FRR_IP6_REDIST_HELP_STR_BABELD)
+{
+ int negate = 0;
+ int family;
+ int afi;
+ int type;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "no", &idx))
+ negate = 1;
+ argv_find(argv, argc, "redistribute", &idx);
+ family = str2family(argv[idx + 1]->text);
+ if (family < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ afi = family2afi(family);
+ if (!afi)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ type = proto_redistnum(afi, argv[idx + 2]->text);
+ if (type < 0) {
+ vty_out (vty, "Invalid type %s\n", argv[idx + 2]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!negate)
+ zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, afi, type, 0, VRF_DEFAULT);
+ else {
+ zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, afi, type, 0, VRF_DEFAULT);
+ /* perhaps should we remove xroutes having the same type... */
+ }
+ return CMD_SUCCESS;
+}
+
+#ifndef NO_DEBUG
+/* [Babel Command] */
+DEFUN (debug_babel,
+ debug_babel_cmd,
+ "debug babel <common|kernel|filter|timeout|interface|route|all>",
+ "Enable debug messages for specific or all part.\n"
+ "Babel information\n"
+ "Common messages (default)\n"
+ "Kernel messages\n"
+ "Filter messages\n"
+ "Timeout messages\n"
+ "Interface messages\n"
+ "Route messages\n"
+ "All messages\n")
+{
+ int i;
+
+ for(i = 0; debug_type[i].str != NULL; i++) {
+ if (strncmp (debug_type[i].str, argv[2]->arg,
+ debug_type[i].str_min_len) == 0) {
+ SET_FLAG(debug, debug_type[i].type);
+ return CMD_SUCCESS;
+ }
+ }
+
+ vty_out (vty, "Invalid type %s\n", argv[2]->arg);
+
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+/* [Babel Command] */
+DEFUN (no_debug_babel,
+ no_debug_babel_cmd,
+ "no debug babel <common|kernel|filter|timeout|interface|route|all>",
+ NO_STR
+ "Disable debug messages for specific or all part.\n"
+ "Babel information\n"
+ "Common messages (default)\n"
+ "Kernel messages\n"
+ "Filter messages\n"
+ "Timeout messages\n"
+ "Interface messages\n"
+ "Route messages\n"
+ "All messages\n")
+{
+ int i;
+
+ for (i = 0; debug_type[i].str; i++) {
+ if (strncmp(debug_type[i].str, argv[3]->arg,
+ debug_type[i].str_min_len) == 0) {
+ UNSET_FLAG(debug, debug_type[i].type);
+ return CMD_SUCCESS;
+ }
+ }
+
+ vty_out (vty, "Invalid type %s\n", argv[3]->arg);
+
+ return CMD_WARNING_CONFIG_FAILED;
+}
+#endif /* NO_DEBUG */
+
+/* Output "debug" statement lines, if necessary. */
+int
+debug_babel_config_write (struct vty * vty)
+{
+#ifdef NO_DEBUG
+ return 0;
+#else
+ int i, lines = 0;
+
+ if (debug == BABEL_DEBUG_ALL)
+ {
+ vty_out (vty, "debug babel all\n");
+ lines++;
+ }
+ else
+ {
+ for (i = 0; debug_type[i].str != NULL; i++)
+ {
+ if (debug_type[i].type != BABEL_DEBUG_ALL
+ && CHECK_FLAG (debug, debug_type[i].type))
+ {
+ vty_out (vty, "debug babel %s\n", debug_type[i].str);
+ lines++;
+ }
+ }
+ }
+
+ if (lines)
+ {
+ vty_out (vty, "!\n");
+ lines++;
+ }
+ return lines;
+#endif /* NO_DEBUG */
+}
+
+DEFUN_NOSH (show_debugging_babel,
+ show_debugging_babel_cmd,
+ "show debugging [babel]",
+ SHOW_STR
+ DEBUG_STR
+ "Babel")
+{
+ vty_out(vty, "BABEL debugging status\n");
+
+ debug_babel_config_write(vty);
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+static void
+babel_zebra_connected (struct zclient *zclient)
+{
+ zclient_send_reg_requests (zclient, VRF_DEFAULT);
+}
+
+static zclient_handler *const babel_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = babel_interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = babel_interface_address_delete,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = babel_zebra_read_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = babel_zebra_read_route,
+};
+
+void babelz_zebra_init(void)
+{
+ zclient = zclient_new(master, &zclient_options_default, babel_handlers,
+ array_size(babel_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_BABEL, 0, &babeld_privs);
+
+ zclient->zebra_connected = babel_zebra_connected;
+
+ install_element(BABEL_NODE, &babel_redistribute_type_cmd);
+ install_element(ENABLE_NODE, &debug_babel_cmd);
+ install_element(ENABLE_NODE, &no_debug_babel_cmd);
+ install_element(CONFIG_NODE, &debug_babel_cmd);
+ install_element(CONFIG_NODE, &no_debug_babel_cmd);
+
+ install_element(ENABLE_NODE, &show_debugging_babel_cmd);
+}
+
+void
+babel_zebra_close_connexion(void)
+{
+ zclient_stop(zclient);
+ zclient_free(zclient);
+}
diff --git a/babeld/babel_zebra.h b/babeld/babel_zebra.h
new file mode 100644
index 0000000..7f960d3
--- /dev/null
+++ b/babeld/babel_zebra.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_ZEBRA_H
+#define BABEL_ZEBRA_H
+
+#include "vty.h"
+
+extern struct zclient *zclient;
+
+void babelz_zebra_init(void);
+void babel_zebra_close_connexion(void);
+extern int debug_babel_config_write (struct vty *);
+
+#endif
diff --git a/babeld/babeld.c b/babeld/babeld.c
new file mode 100644
index 0000000..ebf8474
--- /dev/null
+++ b/babeld/babeld.c
@@ -0,0 +1,848 @@
+// 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 event *thread);
+static void babel_get_myid(void);
+static void babel_initial_noise(void);
+static void babel_read_protocol(struct event *thread);
+static void babel_main_loop(struct event *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. */
+ event_add_read(master, babel_read_protocol, NULL, protocol_socket,
+ &babel_routing_process->t_read);
+ /* wait a little: zebra will announce interfaces, addresses, routes... */
+ event_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 event *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 */
+ event_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 event *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 */
+ event_cancel(&babel_routing_process->t_read);
+ event_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 event *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;
+ event_cancel(&(babel_routing_process->t_update));
+ event_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;
+}
diff --git a/babeld/babeld.h b/babeld/babeld.h
new file mode 100644
index 0000000..619550f
--- /dev/null
+++ b/babeld/babeld.h
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_BABELD_H
+#define BABEL_BABELD_H
+
+#include <zebra.h>
+#include "vty.h"
+
+#define INFINITY ((unsigned short)(~0))
+
+#ifndef RTPROT_BABEL
+#define RTPROT_BABEL 42
+#endif
+
+#define RTPROT_BABEL_LOCAL -2
+
+#undef MAX
+#undef MIN
+
+#define MAX(x,y) ((x)<=(y)?(y):(x))
+#define MIN(x,y) ((x)<=(y)?(x):(y))
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define ATTRIBUTE(x) __attribute__ (x)
+#else
+#define ATTRIBUTE(x) /**/
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
+#define COLD __attribute__ ((cold))
+#else
+#define COLD /**/
+#endif
+
+#ifndef IF_NAMESIZE
+#include <sys/socket.h>
+#include <net/if.h>
+#endif
+
+#ifdef HAVE_VALGRIND
+#include <valgrind/memcheck.h>
+#else
+#ifndef VALGRIND_MAKE_MEM_UNDEFINED
+#define VALGRIND_MAKE_MEM_UNDEFINED(a, b) do {} while(0)
+#endif
+#ifndef VALGRIND_CHECK_MEM_IS_DEFINED
+#define VALGRIND_CHECK_MEM_IS_DEFINED(a, b) do {} while(0)
+#endif
+#endif
+
+
+#define BABEL_VTY_PORT 2609
+#define BABEL_DEFAULT_CONFIG "babeld.conf"
+
+/* Values in milliseconds */
+#define BABEL_DEFAULT_HELLO_INTERVAL 4000
+#define BABEL_DEFAULT_UPDATE_INTERVAL 16000
+#define BABEL_DEFAULT_RESEND_DELAY 2000
+#define BABEL_DEFAULT_RTT_DECAY 42
+
+/* Values in microseconds */
+#define BABEL_DEFAULT_RTT_MIN 10000
+#define BABEL_DEFAULT_RTT_MAX 120000
+
+/* In units of seconds */
+#define BABEL_DEFAULT_SMOOTHING_HALF_LIFE 4
+
+/* In units of 1/256. */
+#define BABEL_DEFAULT_DIVERSITY_FACTOR 256
+
+#define BABEL_DEFAULT_RXCOST_WIRED 96
+#define BABEL_DEFAULT_RXCOST_WIRELESS 256
+#define BABEL_DEFAULT_MAX_RTT_PENALTY 150
+
+/* Babel structure. */
+struct babel
+{
+ /* Babel threads. */
+ struct event *t_read; /* on Babel protocol's socket */
+ struct event *t_update; /* timers */
+ /* distribute_ctx */
+ struct distribute_ctx *distribute_ctx;
+};
+
+extern struct zebra_privs_t babeld_privs;
+
+extern void babeld_quagga_init(void);
+extern int input_filter(const unsigned char *id,
+ const unsigned char *prefix, unsigned short plen,
+ const unsigned char *neigh, unsigned int ifindex);
+extern int output_filter(const unsigned char *id, const unsigned char *prefix,
+ unsigned short plen, unsigned int ifindex);
+extern int redistribute_filter(const unsigned char *prefix, unsigned short plen,
+ unsigned int ifindex, int proto);
+extern int resize_receive_buffer(int size);
+extern void schedule_neighbours_check(int msecs, int override);
+extern struct babel *babel_lookup(void);
+
+#endif /* BABEL_BABELD_H */
diff --git a/babeld/kernel.c b/babeld/kernel.c
new file mode 100644
index 0000000..4fe5bcf
--- /dev/null
+++ b/babeld/kernel.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright 2007, 2008 by Grégoire Henry, Julien Cristau and Juliusz Chroboczek
+Copyright 2011, 2012 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <time.h>
+
+#include "babeld.h"
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <zebra.h>
+#include "prefix.h"
+#include "zclient.h"
+#include "kernel.h"
+#include "privs.h"
+#include "command.h"
+#include "vty.h"
+#include "memory.h"
+#include "frrevent.h"
+#include "nexthop.h"
+
+#include "util.h"
+#include "babel_interface.h"
+#include "babel_zebra.h"
+
+
+static int
+zebra_route(int add, int familt, const unsigned char *pref, unsigned short plen,
+ const unsigned char *gate, int ifindex, unsigned int metric);
+
+int
+kernel_interface_operational(struct interface *interface)
+{
+ return if_is_operative(interface);
+}
+
+int
+kernel_interface_mtu(struct interface *interface)
+{
+ return MIN(interface->mtu, interface->mtu6);
+}
+
+int
+kernel_interface_wireless(struct interface *interface)
+{
+ return 0;
+}
+
+int
+kernel_route(enum babel_kernel_routes operation, const unsigned char *pref,
+ unsigned short plen, const unsigned char *gate, int ifindex,
+ unsigned int metric, const unsigned char *newgate, int newifindex,
+ unsigned int newmetric)
+{
+ int rc;
+ int family;
+
+ /* Check that the protocol family is consistent. */
+ if(plen >= 96 && v4mapped(pref)) {
+ if(!v4mapped(gate)) {
+ errno = EINVAL;
+ return -1;
+ }
+ family = AF_INET;
+ } else {
+ if(v4mapped(gate)) {
+ errno = EINVAL;
+ return -1;
+ }
+ family = AF_INET6;
+ }
+
+ switch (operation) {
+ case ROUTE_ADD:
+ return zebra_route(1, family, pref, plen, gate, ifindex, metric);
+ case ROUTE_FLUSH:
+ return zebra_route(0, family, pref, plen, gate, ifindex, metric);
+ case ROUTE_MODIFY:
+ if(newmetric == metric && memcmp(newgate, gate, 16) == 0 &&
+ newifindex == ifindex)
+ return 0;
+ debugf(BABEL_DEBUG_ROUTE, "Modify route: delete old; add new.");
+ rc = zebra_route(0, family, pref, plen, gate, ifindex, metric);
+ if (rc < 0)
+ return -1;
+
+ rc = zebra_route(1, family, pref, plen, newgate, newifindex,
+ newmetric);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+zebra_route(int add, int family, const unsigned char *pref, unsigned short plen,
+ const unsigned char *gate, int ifindex, unsigned int metric)
+{
+ struct zapi_route api; /* quagga's communication system */
+ struct prefix quagga_prefix; /* quagga's prefix */
+ union g_addr babel_prefix_addr; /* babeld's prefix addr */
+ struct zapi_nexthop *api_nh; /* next router to go - no ECMP */
+
+ api_nh = &api.nexthops[0];
+
+ /* convert to be understandable by quagga */
+ /* convert given addresses */
+ switch (family) {
+ case AF_INET:
+ uchar_to_inaddr(&babel_prefix_addr.ipv4, pref);
+ break;
+ case AF_INET6:
+ uchar_to_in6addr(&babel_prefix_addr.ipv6, pref);
+ break;
+ }
+
+ /* make prefix structure */
+ memset (&quagga_prefix, 0, sizeof(quagga_prefix));
+ quagga_prefix.family = family;
+ switch (family) {
+ case AF_INET:
+ IPV4_ADDR_COPY (&quagga_prefix.u.prefix4, &babel_prefix_addr.ipv4);
+ /* our plen is for v4mapped's addr */
+ quagga_prefix.prefixlen = plen - 96;
+ break;
+ case AF_INET6:
+ IPV6_ADDR_COPY (&quagga_prefix.u.prefix6, &babel_prefix_addr.ipv6);
+ quagga_prefix.prefixlen = plen;
+ break;
+ }
+ apply_mask(&quagga_prefix);
+
+ memset(&api, 0, sizeof(api));
+ api.type = ZEBRA_ROUTE_BABEL;
+ api.safi = SAFI_UNICAST;
+ api.vrf_id = VRF_DEFAULT;
+ api.prefix = quagga_prefix;
+
+ if(metric >= KERNEL_INFINITY) {
+ zapi_route_set_blackhole(&api, BLACKHOLE_REJECT);
+ } else {
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ api.nexthop_num = 1;
+ api_nh->ifindex = ifindex;
+ api_nh->vrf_id = VRF_DEFAULT;
+ switch (family) {
+ case AF_INET:
+ uchar_to_inaddr(&api_nh->gate.ipv4, gate);
+ if (IPV4_ADDR_SAME(&api_nh->gate.ipv4, &quagga_prefix.u.prefix4)
+ && quagga_prefix.prefixlen == IPV4_MAX_BITLEN) {
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ } else {
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ }
+ break;
+ case AF_INET6:
+ uchar_to_in6addr(&api_nh->gate.ipv6, gate);
+ /* difference to IPv4: always leave the linklocal as nexthop */
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ break;
+ }
+ SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+ api.metric = metric;
+ }
+
+ debugf(BABEL_DEBUG_ROUTE, "%s route (%s) to zebra",
+ add ? "adding" : "removing",
+ (family == AF_INET) ? "ipv4" : "ipv6");
+ return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+}
+
+int
+if_eui64(int ifindex, unsigned char *eui)
+{
+ struct interface *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
+ if (ifp == NULL) {
+ return -1;
+ }
+
+ uint8_t len = (uint8_t)ifp->hw_addr_len;
+ char *tmp = (void*) ifp->hw_addr;
+
+ if (len == 8) {
+ memcpy(eui, tmp, 8);
+ eui[0] ^= 2;
+ } else if (len == 6) {
+ memcpy(eui, tmp, 3);
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ memcpy(eui+5, tmp+3, 3);
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+/* Like gettimeofday, but returns monotonic time. If POSIX clocks are not
+ available, falls back to gettimeofday but enforces monotonicity. */
+void
+gettime(struct timeval *tv)
+{
+ monotime(tv);
+}
+
+/* If /dev/urandom doesn't exist, this will fail with ENOENT, which the
+ caller will deal with gracefully. */
+
+int
+read_random_bytes(void *buf, size_t len)
+{
+ int fd;
+ int rc;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if(fd < 0) {
+ rc = -1;
+ } else {
+ rc = read(fd, buf, len);
+ if(rc < 0 || (unsigned) rc < len)
+ rc = -1;
+ close(fd);
+ }
+ return rc;
+}
diff --git a/babeld/kernel.h b/babeld/kernel.h
new file mode 100644
index 0000000..4145843
--- /dev/null
+++ b/babeld/kernel.h
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifndef BABEL_KERNEL_H
+#define BABEL_KERNEL_H
+
+#include <netinet/in.h>
+#include "babel_main.h"
+#include "if.h"
+
+#define KERNEL_INFINITY 0xFFFF
+
+enum babel_kernel_routes {
+ ROUTE_FLUSH,
+ ROUTE_ADD,
+ ROUTE_MODIFY,
+};
+
+int kernel_interface_operational(struct interface *interface);
+int kernel_interface_mtu(struct interface *interface);
+int kernel_interface_wireless(struct interface *interface);
+int kernel_route(enum babel_kernel_routes operation, const unsigned char *dest,
+ unsigned short plen, const unsigned char *gate, int ifindex,
+ unsigned int metric, const unsigned char *newgate,
+ int newifindex, unsigned int newmetric);
+int if_eui64(int ifindex, unsigned char *eui);
+void gettime(struct timeval *tv);
+int read_random_bytes(void *buf, size_t len);
+
+#endif /* BABEL_KERNEL_H */
diff --git a/babeld/message.c b/babeld/message.c
new file mode 100644
index 0000000..f854932
--- /dev/null
+++ b/babeld/message.c
@@ -0,0 +1,1948 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#include <zebra.h>
+#include "if.h"
+
+#include "babeld.h"
+#include "util.h"
+#include "net.h"
+#include "babel_interface.h"
+#include "source.h"
+#include "neighbour.h"
+#include "route.h"
+#include "xroute.h"
+#include "resend.h"
+#include "message.h"
+#include "kernel.h"
+#include "babel_main.h"
+#include "babel_errors.h"
+
+static unsigned char packet_header[4] = {42, 2};
+
+int split_horizon = 1;
+
+unsigned short myseqno = 0;
+
+#define UNICAST_BUFSIZE 1024
+static int unicast_buffered = 0;
+static unsigned char *unicast_buffer = NULL;
+struct neighbour *unicast_neighbour = NULL;
+struct timeval unicast_flush_timeout = {0, 0};
+
+/* Minimum TLV _body_ length for TLVs of particular types (0 = no limit). */
+static const unsigned char tlv_min_length[MESSAGE_MAX + 1] =
+{
+ [ MESSAGE_PAD1 ] = 0,
+ [ MESSAGE_PADN ] = 0,
+ [ MESSAGE_ACK_REQ ] = 6,
+ [ MESSAGE_ACK ] = 2,
+ [ MESSAGE_HELLO ] = 6,
+ [ MESSAGE_IHU ] = 6,
+ [ MESSAGE_ROUTER_ID ] = 10,
+ [ MESSAGE_NH ] = 2,
+ [ MESSAGE_UPDATE ] = 10,
+ [ MESSAGE_REQUEST ] = 2,
+ [ MESSAGE_MH_REQUEST ] = 14,
+};
+
+/* Checks whether an AE exists or must be silently ignored */
+static bool
+known_ae(int ae)
+{
+ return ae <= 4;
+}
+
+/* Parse a network prefix, encoded in the somewhat baroque compressed
+ representation used by Babel. Return the number of bytes parsed. */
+static int
+network_prefix(int ae, int plen, unsigned int omitted,
+ const unsigned char *p, const unsigned char *dp,
+ unsigned int len, unsigned char *p_r)
+{
+ unsigned pb;
+ unsigned char prefix[16];
+ int ret = -1;
+
+ if(plen >= 0)
+ pb = (plen + 7) / 8;
+ else if(ae == 1)
+ pb = 4;
+ else
+ pb = 16;
+
+ if(pb > 16)
+ return -1;
+
+ memset(prefix, 0, 16);
+
+ switch(ae) {
+ case 0:
+ ret = 0;
+ break;
+ case 1:
+ if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted))
+ return -1;
+ memcpy(prefix, v4prefix, 12);
+ if(omitted) {
+ if (dp == NULL || !v4mapped(dp)) return -1;
+ memcpy(prefix, dp, 12 + omitted);
+ }
+ if(pb > omitted) memcpy(prefix + 12 + omitted, p, pb - omitted);
+ ret = pb - omitted;
+ break;
+ case 2:
+ if(omitted > 16 || (pb > omitted && len < pb - omitted)) return -1;
+ if(omitted) {
+ if (dp == NULL || v4mapped(dp)) return -1;
+ memcpy(prefix, dp, omitted);
+ }
+ if(pb > omitted) memcpy(prefix + omitted, p, pb - omitted);
+ ret = pb - omitted;
+ break;
+ case 3:
+ if(pb > 8 && len < pb - 8) return -1;
+ prefix[0] = 0xfe;
+ prefix[1] = 0x80;
+ if(pb > 8) memcpy(prefix + 8, p, pb - 8);
+ ret = pb - 8;
+ break;
+ default:
+ return -1;
+ }
+
+ mask_prefix(p_r, prefix, plen < 0 ? 128 : ae == 1 ? plen + 96 : plen);
+ return ret;
+}
+
+static bool parse_update_subtlv(const unsigned char *a, int alen,
+ unsigned char *channels)
+{
+ int type, len, i = 0;
+
+ while(i < alen) {
+ type = a[i];
+ if(type == SUBTLV_PAD1) {
+ i++;
+ continue;
+ }
+
+ if(i + 1 >= alen) {
+ flog_err(EC_BABEL_PACKET, "Received truncated attributes.");
+ return false;
+ }
+ len = a[i + 1];
+ if(i + len + 2 > alen) {
+ flog_err(EC_BABEL_PACKET, "Received truncated attributes.");
+ return false;
+ }
+
+ if (type & SUBTLV_MANDATORY) {
+ /*
+ * RFC 8966 - 4.4
+ * If the mandatory bit is set, then the whole enclosing
+ * TLV MUST be silently ignored (except for updating the
+ * parser state by a Router-Id, Next Hop, or Update TLV,
+ * as described in the next section).
+ */
+ debugf(BABEL_DEBUG_COMMON,
+ "Received Mandatory bit set but this FRR version is not prepared to handle it at this point");
+ return true;
+ } else if (type == SUBTLV_PADN) {
+ /* Nothing. */
+ } else if (type == SUBTLV_DIVERSITY) {
+ if (len > DIVERSITY_HOPS) {
+ flog_err(
+ EC_BABEL_PACKET,
+ "Received overlong channel information (%d > %d).n",
+ len, DIVERSITY_HOPS);
+ len = DIVERSITY_HOPS;
+ }
+ if (memchr(a + i + 2, 0, len) != NULL) {
+ /* 0 is reserved. */
+ flog_err(EC_BABEL_PACKET,
+ "Channel information contains 0!");
+ return false;
+ }
+ memset(channels, 0, DIVERSITY_HOPS);
+ memcpy(channels, a + i + 2, len);
+ } else {
+ debugf(BABEL_DEBUG_COMMON,
+ "Received unknown route attribute %d.", type);
+ }
+
+ i += len + 2;
+ }
+ return false;
+}
+
+static int
+parse_hello_subtlv(const unsigned char *a, int alen,
+ unsigned int *hello_send_us)
+{
+ int type, len, i = 0, ret = 0;
+
+ while(i < alen) {
+ type = a[i];
+ if(type == SUBTLV_PAD1) {
+ i++;
+ continue;
+ }
+
+ if(i + 1 >= alen) {
+ flog_err(EC_BABEL_PACKET,
+ "Received truncated sub-TLV on Hello message.");
+ return -1;
+ }
+ len = a[i + 1];
+ if(i + len + 2 > alen) {
+ flog_err(EC_BABEL_PACKET,
+ "Received truncated sub-TLV on Hello message.");
+ return -1;
+ }
+
+ if (type & SUBTLV_MANDATORY) {
+ /*
+ * RFC 8966 4.4
+ * If the mandatory bit is set, then the whole enclosing
+ * TLV MUST be silently ignored (except for updating the
+ * parser state by a Router-Id, Next Hop, or Update TLV, as
+ * described in the next section).
+ */
+ debugf(BABEL_DEBUG_COMMON,
+ "Received subtlv with Mandatory bit, this version of FRR is not prepared to handle this currently");
+ return -2;
+ } else if (type == SUBTLV_PADN) {
+ /* Nothing to do. */
+ } else if (type == SUBTLV_TIMESTAMP) {
+ if (len >= 4) {
+ DO_NTOHL(*hello_send_us, a + i + 2);
+ ret = 1;
+ } else {
+ flog_err(
+ EC_BABEL_PACKET,
+ "Received incorrect RTT sub-TLV on Hello message.");
+ }
+ } else {
+ debugf(BABEL_DEBUG_COMMON,
+ "Received unknown Hello sub-TLV type %d.", type);
+ }
+
+ i += len + 2;
+ }
+ return ret;
+}
+
+static int
+parse_ihu_subtlv(const unsigned char *a, int alen,
+ unsigned int *hello_send_us,
+ unsigned int *hello_rtt_receive_time)
+{
+ int type, len, i = 0, ret = 0;
+
+ while(i < alen) {
+ type = a[i];
+ if(type == SUBTLV_PAD1) {
+ i++;
+ continue;
+ }
+
+ if(i + 1 >= alen) {
+ flog_err(EC_BABEL_PACKET,
+ "Received truncated sub-TLV on IHU message.");
+ return -1;
+ }
+ len = a[i + 1];
+ if(i + len + 2 > alen) {
+ flog_err(EC_BABEL_PACKET,
+ "Received truncated sub-TLV on IHU message.");
+ return -1;
+ }
+
+ if(type == SUBTLV_PADN) {
+ /* Nothing to do. */
+ } else if(type == SUBTLV_TIMESTAMP) {
+ if(len >= 8) {
+ DO_NTOHL(*hello_send_us, a + i + 2);
+ DO_NTOHL(*hello_rtt_receive_time, a + i + 6);
+ ret = 1;
+ }
+ else {
+ flog_err(EC_BABEL_PACKET,
+ "Received incorrect RTT sub-TLV on IHU message.");
+ }
+ } else {
+ debugf(BABEL_DEBUG_COMMON,
+ "Received unknown IHU sub-TLV type %d.", type);
+ }
+
+ i += len + 2;
+ }
+ return ret;
+}
+
+static int
+parse_request_subtlv(int ae, const unsigned char *a, int alen,
+ unsigned char *src_prefix, unsigned char *src_plen)
+{
+ int type, len, i = 0;
+ int have_src_prefix = 0;
+
+ while(i < alen) {
+ type = a[0];
+ if(type == SUBTLV_PAD1) {
+ i++;
+ continue;
+ }
+
+ if(i + 2 > alen)
+ goto fail;
+
+ len = a[i + 1];
+ if(i + 2 + len > alen)
+ goto fail;
+
+ if(type == SUBTLV_PADN) {
+ /* Nothing to do. */
+ } else if(type == SUBTLV_SOURCE_PREFIX) {
+ int rc;
+ if(len < 1)
+ goto fail;
+ if(a[i + 2] == 0)
+ goto fail;
+ if(have_src_prefix != 0)
+ goto fail;
+ rc = network_prefix(ae, a[i + 2], 0, a + i + 3, NULL,
+ len - 1, src_prefix);
+ if(rc < 0)
+ goto fail;
+ if(ae==1)
+ *src_plen = a[i + 2] + 96;
+ else
+ *src_plen = a[i + 2];
+ have_src_prefix = 1;
+ } else {
+ debugf(BABEL_DEBUG_COMMON,"Received unknown%s Route Request sub-TLV %d.",
+ ((type & 0x80) != 0) ? " mandatory" : "", type);
+ if((type & 0x80) != 0)
+ return -1;
+ }
+
+ i += len + 2;
+ }
+ return 1;
+
+ fail:
+ flog_err(EC_BABEL_PACKET, "Received truncated sub-TLV on Route Request.");
+ return -1;
+}
+
+static int
+network_address(int ae, const unsigned char *a, unsigned int len,
+ unsigned char *a_r)
+{
+ return network_prefix(ae, -1, 0, a, NULL, len, a_r);
+}
+
+static int
+channels_len(unsigned char *channels)
+{
+ unsigned char *p = memchr(channels, 0, DIVERSITY_HOPS);
+ return p ? (p - channels) : DIVERSITY_HOPS;
+}
+
+/* Check, that the provided frame consists of a valid Babel packet header
+ followed by a sequence of TLVs. TLVs of known types are also checked to meet
+ minimum length constraints defined for each. Return 0 for no errors. */
+static int
+babel_packet_examin(const unsigned char *packet, int packetlen, int *blength)
+{
+ int i = 0, bodylen;
+ const unsigned char *message;
+ unsigned char type, len;
+
+ if(packetlen < 4 || packet[0] != 42 || packet[1] != 2)
+ return 1;
+ DO_NTOHS(bodylen, packet + 2);
+ if(bodylen + 4 > packetlen) {
+ debugf(BABEL_DEBUG_COMMON, "Received truncated packet (%d + 4 > %d).",
+ bodylen, packetlen);
+ return 1;
+ }
+ while (i < bodylen){
+ message = packet + 4 + i;
+ type = message[0];
+ if(type == MESSAGE_PAD1) {
+ i++;
+ continue;
+ }
+ if(i + 2 > bodylen) {
+ debugf(BABEL_DEBUG_COMMON,"Received truncated message.");
+ return 1;
+ }
+ len = message[1];
+ if(i + len + 2 > bodylen) {
+ debugf(BABEL_DEBUG_COMMON,"Received truncated message.");
+ return 1;
+ }
+ /* not Pad1 */
+ if(type <= MESSAGE_MAX && tlv_min_length[type] && len < tlv_min_length[type]) {
+ debugf(BABEL_DEBUG_COMMON,"Undersized %u TLV", type);
+ return 1;
+ }
+ i += len + 2;
+ }
+
+ *blength = bodylen;
+ return 0;
+}
+
+void
+parse_packet(const unsigned char *from, struct interface *ifp,
+ const unsigned char *packet, int packetlen)
+{
+ int i;
+ const unsigned char *message;
+ unsigned char type, len;
+ int bodylen;
+ struct neighbour *neigh;
+ int have_router_id = 0, have_v4_prefix = 0, have_v6_prefix = 0,
+ have_v4_nh = 0, have_v6_nh = 0;
+ unsigned char router_id[8], v4_prefix[16], v6_prefix[16],
+ v4_nh[16], v6_nh[16];
+ int have_hello_rtt = 0;
+ /* Content of the RTT sub-TLV on IHU messages. */
+ unsigned int hello_send_us = 0, hello_rtt_receive_time = 0;
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+
+ if(babel_ifp->flags & BABEL_IF_TIMESTAMPS) {
+ /* We want to track exactly when we received this packet. */
+ gettime(&babel_now);
+ }
+
+ if(!linklocal(from)) {
+ flog_err(EC_BABEL_PACKET,
+ "Received packet from non-local address %s.",
+ format_address(from));
+ return;
+ }
+
+ if (babel_packet_examin (packet, packetlen, &bodylen)) {
+ flog_err(EC_BABEL_PACKET,
+ "Received malformed packet on %s from %s.",
+ ifp->name, format_address(from));
+ return;
+ }
+
+ neigh = find_neighbour(from, ifp);
+ if(neigh == NULL) {
+ flog_err(EC_BABEL_PACKET, "Couldn't allocate neighbour.");
+ return;
+ }
+
+ i = 0;
+ while(i < bodylen) {
+ message = packet + 4 + i;
+ type = message[0];
+ if(type == MESSAGE_PAD1) {
+ debugf(BABEL_DEBUG_COMMON,"Received pad1 from %s on %s.",
+ format_address(from), ifp->name);
+ i++;
+ continue;
+ }
+ len = message[1];
+
+ if(type == MESSAGE_PADN) {
+ debugf(BABEL_DEBUG_COMMON,"Received pad%d from %s on %s.",
+ len, format_address(from), ifp->name);
+ } else if(type == MESSAGE_ACK_REQ) {
+ unsigned short nonce, interval;
+ DO_NTOHS(nonce, message + 4);
+ DO_NTOHS(interval, message + 6);
+ debugf(BABEL_DEBUG_COMMON,"Received ack-req (%04X %d) from %s on %s.",
+ nonce, interval, format_address(from), ifp->name);
+ send_ack(neigh, nonce, interval);
+ } else if(type == MESSAGE_ACK) {
+ debugf(BABEL_DEBUG_COMMON,"Received ack from %s on %s.",
+ format_address(from), ifp->name);
+ /* Nothing right now */
+ } else if(type == MESSAGE_HELLO) {
+ unsigned short seqno, interval, flags;
+ int changed;
+ unsigned int timestamp = 0;
+
+#define BABEL_UNICAST_HELLO 0x8000
+ DO_NTOHS(flags, message + 2);
+
+ /*
+ * RFC 8966 Appendix F
+ * TL;DR -> Please ignore Unicast hellos until FRR's
+ * BABEL is brought up to date
+ */
+ if (CHECK_FLAG(flags, BABEL_UNICAST_HELLO)) {
+ debugf(BABEL_DEBUG_COMMON,
+ "Received Unicast Hello from %s on %s that FRR is not prepared to understand yet",
+ format_address(from), ifp->name);
+ goto done;
+ }
+
+ DO_NTOHS(seqno, message + 4);
+ DO_NTOHS(interval, message + 6);
+ debugf(BABEL_DEBUG_COMMON,
+ "Received hello %d (%d) from %s on %s.", seqno, interval,
+ format_address(from), ifp->name);
+
+ /*
+ * RFC 8966 Appendix F
+ * TL;DR -> Please ignore any Hello packets with the interval
+ * field set to 0
+ */
+ if (interval == 0) {
+ debugf(BABEL_DEBUG_COMMON,
+ "Received hello from %s on %s should be ignored as that this version of FRR does not know how to properly handle interval == 0",
+ format_address(from), ifp->name);
+ goto done;
+ }
+
+ changed = update_neighbour(neigh, seqno, interval);
+ update_neighbour_metric(neigh, changed);
+ if (interval > 0)
+ /* Multiply by 3/2 to allow hellos to expire. */
+ schedule_neighbours_check(interval * 15, 0);
+ /* Sub-TLV handling. */
+ if (len > 8) {
+ if (parse_hello_subtlv(message + 8, len - 6,
+ &timestamp) > 0) {
+ neigh->hello_send_us = timestamp;
+ neigh->hello_rtt_receive_time = babel_now;
+ have_hello_rtt = 1;
+ }
+ }
+ } else if(type == MESSAGE_IHU) {
+ unsigned short txcost, interval;
+ unsigned char address[16];
+ int rc;
+ DO_NTOHS(txcost, message + 4);
+ DO_NTOHS(interval, message + 6);
+ rc = network_address(message[2], message + 8, len - 6, address);
+ if(rc < 0) goto fail;
+ debugf(BABEL_DEBUG_COMMON,"Received ihu %d (%d) from %s on %s for %s.",
+ txcost, interval,
+ format_address(from), ifp->name,
+ format_address(address));
+ if(message[2] == 0 || is_interface_ll_address(ifp, address)) {
+ int changed = txcost != neigh->txcost;
+ neigh->txcost = txcost;
+ neigh->ihu_time = babel_now;
+ neigh->ihu_interval = interval;
+ update_neighbour_metric(neigh, changed);
+ if(interval > 0)
+ /* Multiply by 3/2 to allow neighbours to expire. */
+ schedule_neighbours_check(interval * 45, 0);
+ /* RTT sub-TLV. */
+ if(len > 10 + rc)
+ parse_ihu_subtlv(message + 8 + rc, len - 6 - rc,
+ &hello_send_us, &hello_rtt_receive_time);
+ }
+ } else if(type == MESSAGE_ROUTER_ID) {
+ memcpy(router_id, message + 4, 8);
+ have_router_id = 1;
+ debugf(BABEL_DEBUG_COMMON,"Received router-id %s from %s on %s.",
+ format_eui64(router_id), format_address(from), ifp->name);
+ } else if(type == MESSAGE_NH) {
+ unsigned char nh[16];
+ int rc;
+ rc = network_address(message[2], message + 4, len - 2,
+ nh);
+ if(rc <= 0) {
+ have_v4_nh = 0;
+ have_v6_nh = 0;
+ goto fail;
+ }
+ debugf(BABEL_DEBUG_COMMON,"Received nh %s (%d) from %s on %s.",
+ format_address(nh), message[2],
+ format_address(from), ifp->name);
+ if(message[2] == 1) {
+ memcpy(v4_nh, nh, 16);
+ have_v4_nh = 1;
+ } else {
+ memcpy(v6_nh, nh, 16);
+ have_v6_nh = 1;
+ }
+ } else if(type == MESSAGE_UPDATE) {
+ unsigned char prefix[16], *nh;
+ unsigned char plen;
+ unsigned char channels[DIVERSITY_HOPS];
+ unsigned short interval, seqno, metric;
+ int rc, parsed_len;
+ bool ignore_update = false;
+
+ DO_NTOHS(interval, message + 6);
+ DO_NTOHS(seqno, message + 8);
+ DO_NTOHS(metric, message + 10);
+ if(message[5] == 0 ||
+ (message[2] == 1 ? have_v4_prefix : have_v6_prefix))
+ rc = network_prefix(message[2], message[4], message[5],
+ message + 12,
+ message[2] == 1 ? v4_prefix : v6_prefix,
+ len - 10, prefix);
+ else
+ rc = -1;
+ if(rc < 0) {
+ if(message[3] & 0x80)
+ have_v4_prefix = have_v6_prefix = 0;
+ goto fail;
+ }
+ parsed_len = 10 + rc;
+
+ plen = message[4] + (message[2] == 1 ? 96 : 0);
+
+ if(message[3] & 0x80) {
+ if(message[2] == 1) {
+ memcpy(v4_prefix, prefix, 16);
+ have_v4_prefix = 1;
+ } else {
+ memcpy(v6_prefix, prefix, 16);
+ have_v6_prefix = 1;
+ }
+ }
+ if(message[3] & 0x40) {
+ if(message[2] == 1) {
+ memset(router_id, 0, 4);
+ memcpy(router_id + 4, prefix + 12, 4);
+ } else {
+ memcpy(router_id, prefix + 8, 8);
+ }
+ have_router_id = 1;
+ }
+ if(!have_router_id && message[2] != 0) {
+ flog_err(EC_BABEL_PACKET,
+ "Received prefix with no router id.");
+ goto fail;
+ }
+ debugf(BABEL_DEBUG_COMMON,"Received update%s%s for %s from %s on %s.",
+ (message[3] & 0x80) ? "/prefix" : "",
+ (message[3] & 0x40) ? "/id" : "",
+ format_prefix(prefix, plen),
+ format_address(from), ifp->name);
+
+ if(message[2] == 0) {
+ if(metric < 0xFFFF) {
+ flog_err(EC_BABEL_PACKET,
+ "Received wildcard update with finite metric.");
+ goto done;
+ }
+ retract_neighbour_routes(neigh);
+ goto done;
+ } else if(message[2] == 1) {
+ if(!have_v4_nh)
+ goto fail;
+ nh = v4_nh;
+ } else if(have_v6_nh) {
+ nh = v6_nh;
+ } else {
+ nh = neigh->address;
+ }
+
+ if(message[2] == 1) {
+ if(!babel_get_if_nfo(ifp)->ipv4)
+ goto done;
+ }
+
+ if((babel_get_if_nfo(ifp)->flags & BABEL_IF_FARAWAY)) {
+ channels[0] = 0;
+ } else {
+ /* This will be overwritten by parse_update_subtlv below. */
+ if(metric < 256) {
+ /* Assume non-interfering (wired) link. */
+ channels[0] = 0;
+ } else {
+ /* Assume interfering. */
+ channels[0] = BABEL_IF_CHANNEL_INTERFERING;
+ channels[1] = 0;
+ }
+
+ if(parsed_len < len)
+ ignore_update =
+ parse_update_subtlv(message + 2 + parsed_len,
+ len - parsed_len, channels);
+ }
+
+ if (!ignore_update)
+ update_route(router_id, prefix, plen, seqno, metric,
+ interval, neigh, nh, channels,
+ channels_len(channels));
+ } else if(type == MESSAGE_REQUEST) {
+ unsigned char prefix[16], src_prefix[16], plen, src_plen;
+ int rc, is_ss;
+ if(len < 2) goto fail;
+ if(!known_ae(message[2])) {
+ debugf(BABEL_DEBUG_COMMON,"Received request with unknown AE %d. Ignoring.",
+ message[2]);
+ goto done;
+ }
+ rc = network_prefix(message[2], message[3], 0,
+ message + 4, NULL, len - 2, prefix);
+ if(rc < 0) goto fail;
+ plen = message[3] + (message[2] == 1 ? 96 : 0);
+ debugf(BABEL_DEBUG_COMMON,"Received request for %s from %s on %s.",
+ message[2] == 0 ? "any" : format_prefix(prefix, plen),
+ format_address(from), ifp->name);
+ if(message[2] == 1) {
+ v4tov6(src_prefix, zeroes);
+ src_plen = 96;
+ } else {
+ memcpy(src_prefix, zeroes, 16);
+ src_plen = 0;
+ }
+ rc = parse_request_subtlv(message[2], message + 4 + rc,
+ len - 2 - rc, src_prefix, &src_plen);
+ if(rc < 0)
+ goto done;
+ is_ss = !is_default(src_prefix, src_plen);
+ if(message[2] == 0) {
+ struct babel_interface *neigh_ifp =babel_get_if_nfo(neigh->ifp);
+ if(is_ss) {
+ /* Wildcard requests don't carry a source prefix. */
+ flog_err(EC_BABEL_PACKET,
+ "Received source-specific wildcard request.");
+ goto done;
+ }
+ /* If a neighbour is requesting a full route dump from us,
+ we might as well send it an IHU. */
+ send_ihu(neigh, NULL);
+ /* Since nodes send wildcard requests on boot, booting
+ a large number of nodes at the same time may cause an
+ update storm. Ignore a wildcard request that happens
+ shortly after we sent a full update. */
+ if(neigh_ifp->last_update_time <
+ (time_t)(babel_now.tv_sec -
+ MAX(neigh_ifp->hello_interval / 100, 1)))
+ send_update(neigh->ifp, 0, NULL, 0);
+ } else {
+ send_update(neigh->ifp, 0, prefix, plen);
+ }
+ } else if(type == MESSAGE_MH_REQUEST) {
+ unsigned char prefix[16], plen;
+ unsigned short seqno;
+ int rc;
+ DO_NTOHS(seqno, message + 4);
+ rc = network_prefix(message[2], message[3], 0,
+ message + 16, NULL, len - 14, prefix);
+ if(rc <= 0) goto fail;
+ plen = message[3] + (message[2] == 1 ? 96 : 0);
+ debugf(BABEL_DEBUG_COMMON,"Received request (%d) for %s from %s on %s (%s, %d).",
+ message[6],
+ format_prefix(prefix, plen),
+ format_address(from), ifp->name,
+ format_eui64(message + 8), seqno);
+ handle_request(neigh, prefix, plen, message[6],
+ seqno, message + 8);
+ } else {
+ debugf(BABEL_DEBUG_COMMON,"Received unknown packet type %d from %s on %s.",
+ type, format_address(from), ifp->name);
+ }
+ done:
+ i += len + 2;
+ continue;
+
+ fail:
+ flog_err(EC_BABEL_PACKET,
+ "Couldn't parse packet (%d, %d) from %s on %s.",
+ message[0], message[1], format_address(from), ifp->name);
+ goto done;
+ }
+
+ /* We can calculate the RTT to this neighbour. */
+ if(have_hello_rtt && hello_send_us && hello_rtt_receive_time) {
+ int remote_waiting_us, local_waiting_us;
+ unsigned int rtt, smoothed_rtt;
+ unsigned int old_rttcost;
+ int changed = 0;
+ remote_waiting_us = neigh->hello_send_us - hello_rtt_receive_time;
+ local_waiting_us = time_us(neigh->hello_rtt_receive_time) -
+ hello_send_us;
+
+ /* Sanity checks (validity window of 10 minutes). */
+ if(remote_waiting_us < 0 || local_waiting_us < 0 ||
+ remote_waiting_us > 600000000 || local_waiting_us > 600000000)
+ return;
+
+ rtt = MAX(0, local_waiting_us - remote_waiting_us);
+ debugf(BABEL_DEBUG_COMMON, "RTT to %s on %s sample result: %d us.",
+ format_address(from), ifp->name, rtt);
+
+ old_rttcost = neighbour_rttcost(neigh);
+ if (valid_rtt(neigh)) {
+ /* Running exponential average. */
+ smoothed_rtt = (babel_ifp->rtt_decay * rtt +
+ (256 - babel_ifp->rtt_decay) * neigh->rtt);
+ /* Rounding (up or down) to get closer to the sample. */
+ neigh->rtt = (neigh->rtt >= rtt) ? smoothed_rtt / 256 :
+ (smoothed_rtt + 255) / 256;
+ } else {
+ /* We prefer to be conservative with new neighbours
+ (higher RTT) */
+ assert(rtt <= 0x7FFFFFFF);
+ neigh->rtt = 2*rtt;
+ }
+ changed = (neighbour_rttcost(neigh) == old_rttcost ? 0 : 1);
+ update_neighbour_metric(neigh, changed);
+ neigh->rtt_time = babel_now;
+ }
+ return;
+}
+
+/* Under normal circumstances, there are enough moderation mechanisms
+ elsewhere in the protocol to make sure that this last-ditch check
+ should never trigger. But I'm superstitious. */
+
+static int
+check_bucket(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->bucket == 0) {
+ int seconds = babel_now.tv_sec - babel_ifp->bucket_time;
+ if(seconds > 0) {
+ babel_ifp->bucket = MIN(BUCKET_TOKENS_MAX,
+ seconds * BUCKET_TOKENS_PER_SEC);
+ }
+ /* Reset bucket time unconditionally, in case clock is stepped. */
+ babel_ifp->bucket_time = babel_now.tv_sec;
+ }
+
+ if(babel_ifp->bucket > 0) {
+ babel_ifp->bucket--;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int
+fill_rtt_message(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if((babel_ifp->flags & BABEL_IF_TIMESTAMPS) &&
+ (babel_ifp->buffered_hello >= 0)) {
+ if(babel_ifp->sendbuf[babel_ifp->buffered_hello + 8] == SUBTLV_PADN &&
+ babel_ifp->sendbuf[babel_ifp->buffered_hello + 9] == 4) {
+ unsigned int time;
+ /* Change the type of sub-TLV. */
+ babel_ifp->sendbuf[babel_ifp->buffered_hello + 8] =
+ SUBTLV_TIMESTAMP;
+ gettime(&babel_now);
+ time = time_us(babel_now);
+ DO_HTONL(babel_ifp->sendbuf + babel_ifp->buffered_hello + 10, time);
+ return 1;
+ } else {
+ flog_err(EC_BABEL_PACKET, "No space left for timestamp sub-TLV (this shouldn't happen)");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+flushbuf(struct interface *ifp)
+{
+ int rc;
+ struct sockaddr_in6 sin6;
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+
+ assert(babel_ifp->buffered <= babel_ifp->bufsize);
+
+ flushupdates(ifp);
+
+ if(babel_ifp->buffered > 0) {
+ debugf(BABEL_DEBUG_COMMON," (flushing %d buffered bytes on %s)",
+ babel_ifp->buffered, ifp->name);
+ if(check_bucket(ifp)) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, protocol_group, 16);
+ sin6.sin6_port = htons(protocol_port);
+ sin6.sin6_scope_id = ifp->ifindex;
+ DO_HTONS(packet_header + 2, babel_ifp->buffered);
+ fill_rtt_message(ifp);
+ rc = babel_send(protocol_socket,
+ packet_header, sizeof(packet_header),
+ babel_ifp->sendbuf, babel_ifp->buffered,
+ (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ flog_err(EC_BABEL_PACKET, "send: %s", safe_strerror(errno));
+ } else {
+ flog_err(EC_BABEL_PACKET, "Bucket full, dropping packet to %s.",
+ ifp->name);
+ }
+ }
+ VALGRIND_MAKE_MEM_UNDEFINED(babel_ifp->sendbuf, babel_ifp->bufsize);
+ babel_ifp->buffered = 0;
+ babel_ifp->buffered_hello = -1;
+ babel_ifp->have_buffered_id = 0;
+ babel_ifp->have_buffered_nh = 0;
+ babel_ifp->have_buffered_prefix = 0;
+ babel_ifp->flush_timeout.tv_sec = 0;
+ babel_ifp->flush_timeout.tv_usec = 0;
+}
+
+static void
+schedule_flush(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ unsigned msecs = jitter(babel_ifp, 0);
+ if(babel_ifp->flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&babel_ifp->flush_timeout, &babel_now) < msecs)
+ return;
+ set_timeout(&babel_ifp->flush_timeout, msecs);
+}
+
+static void
+schedule_flush_now(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ /* Almost now */
+ unsigned msecs = roughly(10);
+ if(babel_ifp->flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&babel_ifp->flush_timeout, &babel_now) < msecs)
+ return;
+ set_timeout(&babel_ifp->flush_timeout, msecs);
+}
+
+static void
+schedule_unicast_flush(unsigned msecs)
+{
+ if(!unicast_neighbour)
+ return;
+ if(unicast_flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&unicast_flush_timeout, &babel_now) < msecs)
+ return;
+ unicast_flush_timeout.tv_usec = (babel_now.tv_usec + msecs * 1000) %1000000;
+ unicast_flush_timeout.tv_sec =
+ babel_now.tv_sec + (babel_now.tv_usec / 1000 + msecs) / 1000;
+}
+
+static void
+ensure_space(struct interface *ifp, int space)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->bufsize - babel_ifp->buffered < space)
+ flushbuf(ifp);
+}
+
+static void
+start_message(struct interface *ifp, int type, int len)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->bufsize - babel_ifp->buffered < len + 2)
+ flushbuf(ifp);
+ babel_ifp->sendbuf[babel_ifp->buffered++] = type;
+ babel_ifp->sendbuf[babel_ifp->buffered++] = len;
+}
+
+static void
+end_message(struct interface *ifp, int type, int bytes)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ assert(babel_ifp->buffered >= bytes + 2 &&
+ babel_ifp->sendbuf[babel_ifp->buffered - bytes - 2] == type &&
+ babel_ifp->sendbuf[babel_ifp->buffered - bytes - 1] == bytes);
+ schedule_flush(ifp);
+}
+
+static void
+accumulate_byte(struct interface *ifp, unsigned char value)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ babel_ifp->sendbuf[babel_ifp->buffered++] = value;
+}
+
+static void
+accumulate_short(struct interface *ifp, unsigned short value)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ DO_HTONS(babel_ifp->sendbuf + babel_ifp->buffered, value);
+ babel_ifp->buffered += 2;
+}
+
+static void
+accumulate_int(struct interface *ifp, unsigned int value)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ DO_HTONL(babel_ifp->sendbuf + babel_ifp->buffered, value);
+ babel_ifp->buffered += 4;
+}
+
+static void
+accumulate_bytes(struct interface *ifp,
+ const unsigned char *value, unsigned len)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ memcpy(babel_ifp->sendbuf + babel_ifp->buffered, value, len);
+ babel_ifp->buffered += len;
+}
+
+static int
+start_unicast_message(struct neighbour *neigh, int type, int len)
+{
+ if(unicast_neighbour) {
+ if(neigh != unicast_neighbour ||
+ unicast_buffered + len + 2 >=
+ MIN(UNICAST_BUFSIZE, babel_get_if_nfo(neigh->ifp)->bufsize))
+ flush_unicast(0);
+ }
+ if(!unicast_buffer)
+ unicast_buffer = malloc(UNICAST_BUFSIZE);
+ if(!unicast_buffer) {
+ flog_err(EC_BABEL_MEMORY, "malloc(unicast_buffer): %s",
+ safe_strerror(errno));
+ return -1;
+ }
+
+ unicast_neighbour = neigh;
+
+ unicast_buffer[unicast_buffered++] = type;
+ unicast_buffer[unicast_buffered++] = len;
+ return 1;
+}
+
+static void
+end_unicast_message(struct neighbour *neigh, int type, int bytes)
+{
+ assert(unicast_neighbour == neigh && unicast_buffered >= bytes + 2 &&
+ unicast_buffer[unicast_buffered - bytes - 2] == type &&
+ unicast_buffer[unicast_buffered - bytes - 1] == bytes);
+ schedule_unicast_flush(jitter(babel_get_if_nfo(neigh->ifp), 0));
+}
+
+static void
+accumulate_unicast_byte(struct neighbour *neigh, unsigned char value)
+{
+ unicast_buffer[unicast_buffered++] = value;
+}
+
+static void
+accumulate_unicast_short(struct neighbour *neigh, unsigned short value)
+{
+ DO_HTONS(unicast_buffer + unicast_buffered, value);
+ unicast_buffered += 2;
+}
+
+static void
+accumulate_unicast_int(struct neighbour *neigh, unsigned int value)
+{
+ DO_HTONL(unicast_buffer + unicast_buffered, value);
+ unicast_buffered += 4;
+}
+
+static void
+accumulate_unicast_bytes(struct neighbour *neigh,
+ const unsigned char *value, unsigned len)
+{
+ memcpy(unicast_buffer + unicast_buffered, value, len);
+ unicast_buffered += len;
+}
+
+void
+send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval)
+{
+ int rc;
+ debugf(BABEL_DEBUG_COMMON,"Sending ack (%04x) to %s on %s.",
+ nonce, format_address(neigh->address), neigh->ifp->name);
+ rc = start_unicast_message(neigh, MESSAGE_ACK, 2); if(rc < 0) return;
+ accumulate_unicast_short(neigh, nonce);
+ end_unicast_message(neigh, MESSAGE_ACK, 2);
+ /* Roughly yields a value no larger than 3/2, so this meets the deadline */
+ schedule_unicast_flush(roughly(interval * 6));
+}
+
+void
+send_hello_noupdate(struct interface *ifp, unsigned interval)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ /* This avoids sending multiple hellos in a single packet, which breaks
+ link quality estimation. */
+ if(babel_ifp->buffered_hello >= 0)
+ flushbuf(ifp);
+
+ babel_ifp->hello_seqno = seqno_plus(babel_ifp->hello_seqno, 1);
+ set_timeout(&babel_ifp->hello_timeout, babel_ifp->hello_interval);
+
+ if(!if_up(ifp))
+ return;
+
+ debugf(BABEL_DEBUG_COMMON,"Sending hello %d (%d) to %s.",
+ babel_ifp->hello_seqno, interval, ifp->name);
+
+ start_message(ifp, MESSAGE_HELLO,
+ (babel_ifp->flags & BABEL_IF_TIMESTAMPS) ? 12 : 6);
+ babel_ifp->buffered_hello = babel_ifp->buffered - 2;
+ accumulate_short(ifp, 0);
+ accumulate_short(ifp, babel_ifp->hello_seqno);
+ accumulate_short(ifp, interval > 0xFFFF ? 0xFFFF : interval);
+ if(babel_ifp->flags & BABEL_IF_TIMESTAMPS) {
+ /* Sub-TLV containing the local time of emission. We use a
+ Pad4 sub-TLV, which we'll fill just before sending. */
+ accumulate_byte(ifp, SUBTLV_PADN);
+ accumulate_byte(ifp, 4);
+ accumulate_int(ifp, 0);
+ }
+ end_message(ifp, MESSAGE_HELLO,
+ (babel_ifp->flags & BABEL_IF_TIMESTAMPS) ? 12 : 6);
+}
+
+void
+send_hello(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ send_hello_noupdate(ifp, (babel_ifp->hello_interval + 9) / 10);
+ /* Send full IHU every 3 hellos, and marginal IHU each time */
+ if(babel_ifp->hello_seqno % 3 == 0)
+ send_ihu(NULL, ifp);
+ else
+ send_marginal_ihu(ifp);
+}
+
+void
+flush_unicast(int dofree)
+{
+ struct sockaddr_in6 sin6;
+ int rc;
+
+ if(unicast_buffered == 0)
+ goto done;
+
+ if(!if_up(unicast_neighbour->ifp))
+ goto done;
+
+ /* Preserve ordering of messages */
+ flushbuf(unicast_neighbour->ifp);
+
+ if(check_bucket(unicast_neighbour->ifp)) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, unicast_neighbour->address, 16);
+ sin6.sin6_port = htons(protocol_port);
+ sin6.sin6_scope_id = unicast_neighbour->ifp->ifindex;
+ DO_HTONS(packet_header + 2, unicast_buffered);
+ fill_rtt_message(unicast_neighbour->ifp);
+ rc = babel_send(protocol_socket,
+ packet_header, sizeof(packet_header),
+ unicast_buffer, unicast_buffered,
+ (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ flog_err(EC_BABEL_PACKET, "send(unicast): %s",
+ safe_strerror(errno));
+ } else {
+ flog_err(EC_BABEL_PACKET,
+ "Bucket full, dropping unicast packet to %s if %s.",
+ format_address(unicast_neighbour->address),
+ unicast_neighbour->ifp->name);
+ }
+
+ done:
+ VALGRIND_MAKE_MEM_UNDEFINED(unicast_buffer, UNICAST_BUFSIZE);
+ unicast_buffered = 0;
+ if(dofree && unicast_buffer) {
+ free(unicast_buffer);
+ unicast_buffer = NULL;
+ }
+ unicast_neighbour = NULL;
+ unicast_flush_timeout.tv_sec = 0;
+ unicast_flush_timeout.tv_usec = 0;
+}
+
+static void
+really_send_update(struct interface *ifp,
+ const unsigned char *id,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned short metric,
+ unsigned char *channels, int channels_len)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ int add_metric, v4, real_plen, omit = 0;
+ const unsigned char *real_prefix;
+ unsigned short flags = 0;
+ int channels_size;
+
+ if(diversity_kind != DIVERSITY_CHANNEL)
+ channels_len = -1;
+
+ channels_size = channels_len >= 0 ? channels_len + 2 : 0;
+
+ if(!if_up(ifp))
+ return;
+
+ add_metric = output_filter(id, prefix, plen, ifp->ifindex);
+ if(add_metric >= INFINITY)
+ return;
+
+ metric = MIN(metric + add_metric, INFINITY);
+ /* Worst case */
+ ensure_space(ifp, 20 + 12 + 28);
+
+ v4 = plen >= 96 && v4mapped(prefix);
+
+ if(v4) {
+ if(!babel_ifp->ipv4)
+ return;
+ if(!babel_ifp->have_buffered_nh ||
+ memcmp(babel_ifp->buffered_nh, babel_ifp->ipv4, 4) != 0) {
+ start_message(ifp, MESSAGE_NH, 6);
+ accumulate_byte(ifp, 1);
+ accumulate_byte(ifp, 0);
+ accumulate_bytes(ifp, babel_ifp->ipv4, 4);
+ end_message(ifp, MESSAGE_NH, 6);
+ memcpy(babel_ifp->buffered_nh, babel_ifp->ipv4, 4);
+ babel_ifp->have_buffered_nh = 1;
+ }
+
+ real_prefix = prefix + 12;
+ real_plen = plen - 96;
+ } else {
+ if(babel_ifp->have_buffered_prefix) {
+ while(omit < plen / 8 &&
+ babel_ifp->buffered_prefix[omit] == prefix[omit])
+ omit++;
+ }
+ if(!babel_ifp->have_buffered_prefix || plen >= 48)
+ flags |= 0x80;
+ real_prefix = prefix;
+ real_plen = plen;
+ }
+
+ if(!babel_ifp->have_buffered_id
+ || memcmp(id, babel_ifp->buffered_id, 8) != 0) {
+ if(real_plen == 128 && memcmp(real_prefix + 8, id, 8) == 0) {
+ flags |= 0x40;
+ } else {
+ start_message(ifp, MESSAGE_ROUTER_ID, 10);
+ accumulate_short(ifp, 0);
+ accumulate_bytes(ifp, id, 8);
+ end_message(ifp, MESSAGE_ROUTER_ID, 10);
+ }
+ memcpy(babel_ifp->buffered_id, id, sizeof(babel_ifp->buffered_id));
+ babel_ifp->have_buffered_id = 1;
+ }
+
+ start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit +
+ channels_size);
+ accumulate_byte(ifp, v4 ? 1 : 2);
+ accumulate_byte(ifp, flags);
+ accumulate_byte(ifp, real_plen);
+ accumulate_byte(ifp, omit);
+ accumulate_short(ifp, (babel_ifp->update_interval + 5) / 10);
+ accumulate_short(ifp, seqno);
+ accumulate_short(ifp, metric);
+ accumulate_bytes(ifp, real_prefix + omit, (real_plen + 7) / 8 - omit);
+ /* Note that an empty channels TLV is different from no such TLV. */
+ if(channels_len >= 0) {
+ accumulate_byte(ifp, 2);
+ accumulate_byte(ifp, channels_len);
+
+ if (channels && channels_len > 0)
+ accumulate_bytes(ifp, channels, channels_len);
+ }
+ end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit +
+ channels_size);
+
+ if(flags & 0x80) {
+ memcpy(babel_ifp->buffered_prefix, prefix, 16);
+ babel_ifp->have_buffered_prefix = 1;
+ }
+}
+
+static int
+compare_buffered_updates(const void *av, const void *bv)
+{
+ const struct buffered_update *a = av, *b = bv;
+ int rc, v4a, v4b, ma, mb;
+
+ rc = memcmp(a->id, b->id, 8);
+ if(rc != 0)
+ return rc;
+
+ v4a = (a->plen >= 96 && v4mapped(a->prefix));
+ v4b = (b->plen >= 96 && v4mapped(b->prefix));
+
+ if(v4a > v4b)
+ return 1;
+ else if(v4a < v4b)
+ return -1;
+
+ ma = (!v4a && a->plen == 128 && memcmp(a->prefix + 8, a->id, 8) == 0);
+ mb = (!v4b && b->plen == 128 && memcmp(b->prefix + 8, b->id, 8) == 0);
+
+ if(ma > mb)
+ return -1;
+ else if(mb > ma)
+ return 1;
+
+ if(a->plen < b->plen)
+ return 1;
+ else if(a->plen > b->plen)
+ return -1;
+
+ return memcmp(a->prefix, b->prefix, 16);
+}
+
+void
+flushupdates(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ struct xroute *xroute;
+ struct babel_route *route;
+ const unsigned char *last_prefix = NULL;
+ unsigned char last_plen = 0xFF;
+ int i;
+
+ if(ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ FOR_ALL_INTERFACES(vrf, ifp_aux)
+ flushupdates(ifp_aux);
+ return;
+ }
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->num_buffered_updates > 0) {
+ struct buffered_update *b = babel_ifp->buffered_updates;
+ int n = babel_ifp->num_buffered_updates;
+
+ babel_ifp->buffered_updates = NULL;
+ babel_ifp->update_bufsize = 0;
+ babel_ifp->num_buffered_updates = 0;
+
+ if(!if_up(ifp))
+ goto done;
+
+ debugf(BABEL_DEBUG_COMMON," (flushing %d buffered updates on %s (%d))",
+ n, ifp->name, ifp->ifindex);
+
+ /* In order to send fewer update messages, we want to send updates
+ with the same router-id together, with IPv6 going out before IPv4. */
+
+ for(i = 0; i < n; i++) {
+ route = find_installed_route(b[i].prefix, b[i].plen);
+ if(route)
+ memcpy(b[i].id, route->src->id, 8);
+ else
+ memcpy(b[i].id, myid, 8);
+ }
+
+ qsort(b, n, sizeof(struct buffered_update), compare_buffered_updates);
+
+ for(i = 0; i < n; i++) {
+ /* The same update may be scheduled multiple times before it is
+ sent out. Since our buffer is now sorted, it is enough to
+ compare with the previous update. */
+
+ if(last_prefix) {
+ if(b[i].plen == last_plen &&
+ memcmp(b[i].prefix, last_prefix, 16) == 0)
+ continue;
+ }
+
+ xroute = find_xroute(b[i].prefix, b[i].plen);
+ route = find_installed_route(b[i].prefix, b[i].plen);
+
+ if(xroute && (!route || xroute->metric <= kernel_metric)) {
+ really_send_update(ifp, myid,
+ xroute->prefix, xroute->plen,
+ myseqno, xroute->metric,
+ NULL, 0);
+ last_prefix = xroute->prefix;
+ last_plen = xroute->plen;
+ } else if(route) {
+ unsigned char channels[DIVERSITY_HOPS];
+ int chlen;
+ struct interface *route_ifp = route->neigh->ifp;
+ struct babel_interface *babel_route_ifp = NULL;
+ unsigned short metric;
+ unsigned short seqno;
+
+ seqno = route->seqno;
+ metric =
+ route_interferes(route, ifp) ?
+ route_metric(route) :
+ route_metric_noninterfering(route);
+
+ if(metric < INFINITY)
+ satisfy_request(route->src->prefix, route->src->plen,
+ seqno, route->src->id, ifp);
+ if((babel_ifp->flags & BABEL_IF_SPLIT_HORIZON) &&
+ route->neigh->ifp == ifp)
+ continue;
+
+ babel_route_ifp = babel_get_if_nfo(route_ifp);
+ if(babel_route_ifp->channel ==BABEL_IF_CHANNEL_NONINTERFERING) {
+ memcpy(channels, route->channels, DIVERSITY_HOPS);
+ } else {
+ if(babel_route_ifp->channel == BABEL_IF_CHANNEL_UNKNOWN)
+ channels[0] = BABEL_IF_CHANNEL_INTERFERING;
+ else {
+ assert(babel_route_ifp->channel > 0 &&
+ babel_route_ifp->channel <= 255);
+ channels[0] = babel_route_ifp->channel;
+ }
+ memcpy(channels + 1, route->channels, DIVERSITY_HOPS - 1);
+ }
+
+ chlen = channels_len(channels);
+ really_send_update(ifp, route->src->id,
+ route->src->prefix,
+ route->src->plen,
+ seqno, metric,
+ channels, chlen);
+ update_source(route->src, seqno, metric);
+ last_prefix = route->src->prefix;
+ last_plen = route->src->plen;
+ } else {
+ /* There's no route for this prefix. This can happen shortly
+ after an xroute has been retracted, so send a retraction. */
+ really_send_update(ifp, myid, b[i].prefix, b[i].plen,
+ myseqno, INFINITY, NULL, -1);
+ }
+ }
+ schedule_flush_now(ifp);
+ done:
+ free(b);
+ }
+ babel_ifp->update_flush_timeout.tv_sec = 0;
+ babel_ifp->update_flush_timeout.tv_usec = 0;
+}
+
+static void
+schedule_update_flush(struct interface *ifp, int urgent)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ unsigned msecs;
+ msecs = update_jitter(babel_ifp, urgent);
+ if(babel_ifp->update_flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&babel_ifp->update_flush_timeout, &babel_now) < msecs)
+ return;
+ set_timeout(&babel_ifp->update_flush_timeout, msecs);
+}
+
+static void
+buffer_update(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->num_buffered_updates > 0 &&
+ babel_ifp->num_buffered_updates >= babel_ifp->update_bufsize)
+ flushupdates(ifp);
+
+ if(babel_ifp->update_bufsize == 0) {
+ int n;
+ assert(babel_ifp->buffered_updates == NULL);
+ /* Allocate enough space to hold a full update. Since the
+ number of installed routes will grow over time, make sure we
+ have enough space to send a full-ish frame. */
+ n = installed_routes_estimate() + xroutes_estimate() + 4;
+ n = MAX(n, babel_ifp->bufsize / 16);
+ again:
+ babel_ifp->buffered_updates = malloc(n *sizeof(struct buffered_update));
+ if(babel_ifp->buffered_updates == NULL) {
+ flog_err(EC_BABEL_MEMORY, "malloc(buffered_updates): %s",
+ safe_strerror(errno));
+ if(n > 4) {
+ /* Try again with a tiny buffer. */
+ n = 4;
+ goto again;
+ }
+ return;
+ }
+ babel_ifp->update_bufsize = n;
+ babel_ifp->num_buffered_updates = 0;
+ }
+
+ memcpy(babel_ifp->buffered_updates[babel_ifp->num_buffered_updates].prefix,
+ prefix, 16);
+ babel_ifp->buffered_updates[babel_ifp->num_buffered_updates].plen = plen;
+ babel_ifp->num_buffered_updates++;
+}
+
+void
+send_update(struct interface *ifp, int urgent,
+ const unsigned char *prefix, unsigned char plen)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+
+ if(ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ struct babel_route *route;
+ FOR_ALL_INTERFACES(vrf, ifp_aux)
+ send_update(ifp_aux, urgent, prefix, plen);
+ if(prefix) {
+ /* Since flushupdates only deals with non-wildcard interfaces, we
+ need to do this now. */
+ route = find_installed_route(prefix, plen);
+ if(route && route_metric(route) < INFINITY)
+ satisfy_request(prefix, plen, route->src->seqno, route->src->id,
+ NULL);
+ }
+ return;
+ }
+
+ if(!if_up(ifp))
+ return;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(prefix) {
+ debugf(BABEL_DEBUG_COMMON,"Sending update to %s for %s.",
+ ifp->name, format_prefix(prefix, plen));
+ buffer_update(ifp, prefix, plen);
+ } else {
+ struct route_stream *routes = NULL;
+ send_self_update(ifp);
+ debugf(BABEL_DEBUG_COMMON,"Sending update to %s for any.", ifp->name);
+ routes = route_stream(1);
+ if(routes) {
+ while(1) {
+ struct babel_route *route = route_stream_next(routes);
+ if(route == NULL)
+ break;
+ buffer_update(ifp, route->src->prefix, route->src->plen);
+ }
+ route_stream_done(routes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate route stream.");
+ }
+ set_timeout(&babel_ifp->update_timeout, babel_ifp->update_interval);
+ babel_ifp->last_update_time = babel_now.tv_sec;
+ }
+ schedule_update_flush(ifp, urgent);
+}
+
+void
+send_update_resend(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen)
+{
+ assert(prefix != NULL);
+
+ send_update(ifp, 1, prefix, plen);
+ record_resend(RESEND_UPDATE, prefix, plen, 0, NULL, NULL, resend_delay);
+}
+
+void
+send_wildcard_retraction(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ if(ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ FOR_ALL_INTERFACES(vrf, ifp_aux)
+ send_wildcard_retraction(ifp_aux);
+ return;
+ }
+
+ if(!if_up(ifp))
+ return;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ start_message(ifp, MESSAGE_UPDATE, 10);
+ accumulate_byte(ifp, 0);
+ accumulate_byte(ifp, 0x40);
+ accumulate_byte(ifp, 0);
+ accumulate_byte(ifp, 0);
+ accumulate_short(ifp, 0xFFFF);
+ accumulate_short(ifp, myseqno);
+ accumulate_short(ifp, 0xFFFF);
+ end_message(ifp, MESSAGE_UPDATE, 10);
+
+ babel_ifp->have_buffered_id = 0;
+}
+
+void
+update_myseqno(void)
+{
+ myseqno = seqno_plus(myseqno, 1);
+}
+
+void
+send_self_update(struct interface *ifp)
+{
+ struct xroute_stream *xroutes;
+ if(ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ FOR_ALL_INTERFACES(vrf, ifp_aux) {
+ if(!if_up(ifp_aux))
+ continue;
+ send_self_update(ifp_aux);
+ }
+ return;
+ }
+
+ debugf(BABEL_DEBUG_COMMON,"Sending self update to %s.", ifp->name);
+ xroutes = xroute_stream();
+ if(xroutes) {
+ while(1) {
+ struct xroute *xroute = xroute_stream_next(xroutes);
+ if(xroute == NULL) break;
+ send_update(ifp, 0, xroute->prefix, xroute->plen);
+ }
+ xroute_stream_done(xroutes);
+ } else {
+ flog_err(EC_BABEL_MEMORY, "Couldn't allocate xroute stream.");
+ }
+}
+
+void
+send_ihu(struct neighbour *neigh, struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ int rxcost, interval;
+ int ll;
+ int send_rtt_data;
+ int msglen;
+
+ if(neigh == NULL && ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ FOR_ALL_INTERFACES(vrf, ifp_aux) {
+ if(if_up(ifp_aux))
+ continue;
+ send_ihu(NULL, ifp_aux);
+ }
+ return;
+ }
+
+ if(neigh == NULL) {
+ struct neighbour *ngh;
+ FOR_ALL_NEIGHBOURS(ngh) {
+ if(ngh->ifp == ifp)
+ send_ihu(ngh, ifp);
+ }
+ return;
+ }
+
+
+ if(ifp && neigh->ifp != ifp)
+ return;
+
+ ifp = neigh->ifp;
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(!if_up(ifp))
+ return;
+
+ rxcost = neighbour_rxcost(neigh);
+ interval = (babel_ifp->hello_interval * 3 + 9) / 10;
+
+ /* Conceptually, an IHU is a unicast message. We usually send them as
+ multicast, since this allows aggregation into a single packet and
+ avoids an ARP exchange. If we already have a unicast message queued
+ for this neighbour, however, we might as well piggyback the IHU. */
+ debugf(BABEL_DEBUG_COMMON,"Sending %sihu %d on %s to %s.",
+ unicast_neighbour == neigh ? "unicast " : "",
+ rxcost,
+ neigh->ifp->name,
+ format_address(neigh->address));
+
+ ll = linklocal(neigh->address);
+
+ if((babel_ifp->flags & BABEL_IF_TIMESTAMPS) && neigh->hello_send_us
+ /* Checks whether the RTT data is not too old to be sent. */
+ && timeval_minus_msec(&babel_now,
+ &neigh->hello_rtt_receive_time) < 1000000) {
+ send_rtt_data = 1;
+ } else {
+ neigh->hello_send_us = 0;
+ send_rtt_data = 0;
+ }
+
+ /* The length depends on the format of the address, and then an
+ optional 10-bytes sub-TLV for timestamps (used to compute a RTT). */
+ msglen = (ll ? 14 : 22) + (send_rtt_data ? 10 : 0);
+
+ if(unicast_neighbour != neigh) {
+ start_message(ifp, MESSAGE_IHU, msglen);
+ accumulate_byte(ifp, ll ? 3 : 2);
+ accumulate_byte(ifp, 0);
+ accumulate_short(ifp, rxcost);
+ accumulate_short(ifp, interval);
+ if(ll)
+ accumulate_bytes(ifp, neigh->address + 8, 8);
+ else
+ accumulate_bytes(ifp, neigh->address, 16);
+ if (send_rtt_data) {
+ accumulate_byte(ifp, SUBTLV_TIMESTAMP);
+ accumulate_byte(ifp, 8);
+ accumulate_int(ifp, neigh->hello_send_us);
+ accumulate_int(ifp, time_us(neigh->hello_rtt_receive_time));
+ }
+ end_message(ifp, MESSAGE_IHU, msglen);
+ } else {
+ int rc;
+ rc = start_unicast_message(neigh, MESSAGE_IHU, msglen);
+ if(rc < 0) return;
+ accumulate_unicast_byte(neigh, ll ? 3 : 2);
+ accumulate_unicast_byte(neigh, 0);
+ accumulate_unicast_short(neigh, rxcost);
+ accumulate_unicast_short(neigh, interval);
+ if(ll)
+ accumulate_unicast_bytes(neigh, neigh->address + 8, 8);
+ else
+ accumulate_unicast_bytes(neigh, neigh->address, 16);
+ if (send_rtt_data) {
+ accumulate_unicast_byte(neigh, SUBTLV_TIMESTAMP);
+ accumulate_unicast_byte(neigh, 8);
+ accumulate_unicast_int(neigh, neigh->hello_send_us);
+ accumulate_unicast_int(neigh,
+ time_us(neigh->hello_rtt_receive_time));
+ }
+ end_unicast_message(neigh, MESSAGE_IHU, msglen);
+ }
+}
+
+/* Send IHUs to all marginal neighbours */
+void
+send_marginal_ihu(struct interface *ifp)
+{
+ struct neighbour *neigh;
+ FOR_ALL_NEIGHBOURS(neigh) {
+ if(ifp && neigh->ifp != ifp)
+ continue;
+ if(neigh->txcost >= 384 || (neigh->reach & 0xF000) != 0xF000)
+ send_ihu(neigh, ifp);
+ }
+}
+
+void
+send_request(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen)
+{
+ int v4, pb, len;
+
+ if(ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ FOR_ALL_INTERFACES(vrf, ifp_aux) {
+ if(if_up(ifp_aux))
+ continue;
+ send_request(ifp_aux, prefix, plen);
+ }
+ return;
+ }
+
+ /* make sure any buffered updates go out before this request. */
+ flushupdates(ifp);
+
+ if(!if_up(ifp))
+ return;
+
+ debugf(BABEL_DEBUG_COMMON,"sending request to %s for %s.",
+ ifp->name, prefix ? format_prefix(prefix, plen) : "any");
+ v4 = plen >= 96 && v4mapped(prefix);
+ pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
+ len = !prefix ? 2 : 2 + pb;
+
+ start_message(ifp, MESSAGE_REQUEST, len);
+ accumulate_byte(ifp, !prefix ? 0 : v4 ? 1 : 2);
+ accumulate_byte(ifp, !prefix ? 0 : v4 ? plen - 96 : plen);
+ if(prefix) {
+ if(v4)
+ accumulate_bytes(ifp, prefix + 12, pb);
+ else
+ accumulate_bytes(ifp, prefix, pb);
+ }
+ end_message(ifp, MESSAGE_REQUEST, len);
+}
+
+void
+send_unicast_request(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen)
+{
+ int rc, v4, pb, len;
+
+ /* make sure any buffered updates go out before this request. */
+ flushupdates(neigh->ifp);
+
+ debugf(BABEL_DEBUG_COMMON,"sending unicast request to %s for %s.",
+ format_address(neigh->address),
+ prefix ? format_prefix(prefix, plen) : "any");
+ v4 = plen >= 96 && v4mapped(prefix);
+ pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
+ len = !prefix ? 2 : 2 + pb;
+
+ rc = start_unicast_message(neigh, MESSAGE_REQUEST, len);
+ if(rc < 0) return;
+ accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? 1 : 2);
+ accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? plen - 96 : plen);
+ if(prefix) {
+ if(v4)
+ accumulate_unicast_bytes(neigh, prefix + 12, pb);
+ else
+ accumulate_unicast_bytes(neigh, prefix, pb);
+ }
+ end_unicast_message(neigh, MESSAGE_REQUEST, len);
+}
+
+void
+send_multihop_request(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ unsigned short hop_count)
+{
+ int v4, pb, len;
+
+ /* Make sure any buffered updates go out before this request. */
+ flushupdates(ifp);
+
+ if(ifp == NULL) {
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct interface *ifp_aux;
+ FOR_ALL_INTERFACES(vrf, ifp_aux) {
+ if(!if_up(ifp_aux))
+ continue;
+ send_multihop_request(ifp_aux, prefix, plen, seqno, id, hop_count);
+ }
+ return;
+ }
+
+ if(!if_up(ifp))
+ return;
+
+ debugf(BABEL_DEBUG_COMMON,"Sending request (%d) on %s for %s.",
+ hop_count, ifp->name, format_prefix(prefix, plen));
+ v4 = plen >= 96 && v4mapped(prefix);
+ pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
+ len = 6 + 8 + pb;
+
+ start_message(ifp, MESSAGE_MH_REQUEST, len);
+ accumulate_byte(ifp, v4 ? 1 : 2);
+ accumulate_byte(ifp, v4 ? plen - 96 : plen);
+ accumulate_short(ifp, seqno);
+ accumulate_byte(ifp, hop_count);
+ accumulate_byte(ifp, 0);
+ accumulate_bytes(ifp, id, 8);
+ if(prefix) {
+ if(v4)
+ accumulate_bytes(ifp, prefix + 12, pb);
+ else
+ accumulate_bytes(ifp, prefix, pb);
+ }
+ end_message(ifp, MESSAGE_MH_REQUEST, len);
+}
+
+void
+send_unicast_multihop_request(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ unsigned short hop_count)
+{
+ int rc, v4, pb, len;
+
+ /* Make sure any buffered updates go out before this request. */
+ flushupdates(neigh->ifp);
+
+ debugf(BABEL_DEBUG_COMMON,"Sending multi-hop request to %s for %s (%d hops).",
+ format_address(neigh->address),
+ format_prefix(prefix, plen), hop_count);
+ v4 = plen >= 96 && v4mapped(prefix);
+ pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
+ len = 6 + 8 + pb;
+
+ rc = start_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
+ if(rc < 0) return;
+ accumulate_unicast_byte(neigh, v4 ? 1 : 2);
+ accumulate_unicast_byte(neigh, v4 ? plen - 96 : plen);
+ accumulate_unicast_short(neigh, seqno);
+ accumulate_unicast_byte(neigh, hop_count);
+ accumulate_unicast_byte(neigh, 0);
+ accumulate_unicast_bytes(neigh, id, 8);
+ if(prefix) {
+ if(v4)
+ accumulate_unicast_bytes(neigh, prefix + 12, pb);
+ else
+ accumulate_unicast_bytes(neigh, prefix, pb);
+ }
+ end_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
+}
+
+void
+send_request_resend(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned char *id)
+{
+ if(neigh)
+ send_unicast_multihop_request(neigh, prefix, plen, seqno, id, 127);
+ else
+ send_multihop_request(NULL, prefix, plen, seqno, id, 127);
+
+ record_resend(RESEND_REQUEST, prefix, plen, seqno, id,
+ neigh ? neigh->ifp : NULL, resend_delay);
+}
+
+void
+handle_request(struct neighbour *neigh, const unsigned char *prefix,
+ unsigned char plen, unsigned char hop_count,
+ unsigned short seqno, const unsigned char *id)
+{
+ struct xroute *xroute;
+ struct babel_route *route;
+ struct neighbour *successor = NULL;
+
+ xroute = find_xroute(prefix, plen);
+ route = find_installed_route(prefix, plen);
+
+ if(xroute && (!route || xroute->metric <= kernel_metric)) {
+ if(hop_count > 0 && memcmp(id, myid, 8) == 0) {
+ if(seqno_compare(seqno, myseqno) > 0) {
+ if(seqno_minus(seqno, myseqno) > 100) {
+ /* Hopelessly out-of-date request */
+ return;
+ }
+ update_myseqno();
+ }
+ }
+ send_update(neigh->ifp, 1, prefix, plen);
+ return;
+ }
+
+ if(route &&
+ (memcmp(id, route->src->id, 8) != 0 ||
+ seqno_compare(seqno, route->seqno) <= 0)) {
+ send_update(neigh->ifp, 1, prefix, plen);
+ return;
+ }
+
+ if(hop_count <= 1)
+ return;
+
+ if(route && memcmp(id, route->src->id, 8) == 0 &&
+ seqno_minus(seqno, route->seqno) > 100) {
+ /* Hopelessly out-of-date */
+ return;
+ }
+
+ if(request_redundant(neigh->ifp, prefix, plen, seqno, id))
+ return;
+
+ /* Let's try to forward this request. */
+ if(route && route_metric(route) < INFINITY)
+ successor = route->neigh;
+
+ if(!successor || successor == neigh) {
+ /* We were about to forward a request to its requestor. Try to
+ find a different neighbour to forward the request to. */
+ struct babel_route *other_route;
+
+ other_route = find_best_route(prefix, plen, 0, neigh);
+ if(other_route && route_metric(other_route) < INFINITY)
+ successor = other_route->neigh;
+ }
+
+ if(!successor || successor == neigh)
+ /* Give up */
+ return;
+
+ send_unicast_multihop_request(successor, prefix, plen, seqno, id,
+ hop_count - 1);
+ record_resend(RESEND_REQUEST, prefix, plen, seqno, id,
+ neigh->ifp, 0);
+}
diff --git a/babeld/message.h b/babeld/message.h
new file mode 100644
index 0000000..7cf062a
--- /dev/null
+++ b/babeld/message.h
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifndef BABEL_MESSAGE_H
+#define BABEL_MESSAGE_H
+
+#include "babel_interface.h"
+
+#define MAX_BUFFERED_UPDATES 200
+
+#define BUCKET_TOKENS_MAX 200
+#define BUCKET_TOKENS_PER_SEC 40
+
+/* A registry of assigned TLV and sub-TLV types is available at
+ http://www.pps.univ-paris-diderot.fr/~jch/software/babel/babel-tlv-registry.text
+*/
+#define MESSAGE_PAD1 0
+#define MESSAGE_PADN 1
+#define MESSAGE_ACK_REQ 2
+#define MESSAGE_ACK 3
+#define MESSAGE_HELLO 4
+#define MESSAGE_IHU 5
+#define MESSAGE_ROUTER_ID 6
+#define MESSAGE_NH 7
+#define MESSAGE_UPDATE 8
+#define MESSAGE_REQUEST 9
+#define MESSAGE_MH_REQUEST 10
+#define MESSAGE_MAX 10
+
+/* Protocol extension through sub-TLVs. */
+#define SUBTLV_PAD1 0
+#define SUBTLV_PADN 1
+#define SUBTLV_DIVERSITY 2 /* Also known as babelz. */
+#define SUBTLV_TIMESTAMP 3 /* Used to compute RTT. */
+#define SUBTLV_SOURCE_PREFIX 128 /* Source-specific routing. */
+#define SUBTLV_MANDATORY 0x80
+
+extern unsigned short myseqno;
+
+extern int broadcast_ihu;
+extern int split_horizon;
+
+extern struct neighbour *unicast_neighbour;
+extern struct timeval unicast_flush_timeout;
+
+void parse_packet(const unsigned char *from, struct interface *ifp,
+ const unsigned char *packet, int packetlen);
+void flushbuf(struct interface *ifp);
+void flushupdates(struct interface *ifp);
+void send_ack(struct neighbour *neigh, unsigned short nonce,
+ unsigned short interval);
+void send_hello_noupdate(struct interface *ifp, unsigned interval);
+void send_hello(struct interface *ifp);
+void flush_unicast(int dofree);
+void send_update(struct interface *ifp, int urgent,
+ const unsigned char *prefix, unsigned char plen);
+void send_update_resend(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen);
+void send_wildcard_retraction(struct interface *ifp);
+void update_myseqno(void);
+void send_self_update(struct interface *ifp);
+void send_ihu(struct neighbour *neigh, struct interface *ifp);
+void send_marginal_ihu(struct interface *ifp);
+void send_request(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen);
+void send_unicast_request(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen);
+void send_multihop_request(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ unsigned short hop_count);
+void
+send_unicast_multihop_request(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ unsigned short hop_count);
+void send_request_resend(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned char *id);
+void handle_request(struct neighbour *neigh, const unsigned char *prefix,
+ unsigned char plen, unsigned char hop_count,
+ unsigned short seqno, const unsigned char *id);
+
+#endif
diff --git a/babeld/neighbour.c b/babeld/neighbour.c
new file mode 100644
index 0000000..51e595a
--- /dev/null
+++ b/babeld/neighbour.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <zebra.h>
+#include "if.h"
+
+#include "babel_main.h"
+#include "babeld.h"
+#include "util.h"
+#include "babel_interface.h"
+#include "neighbour.h"
+#include "source.h"
+#include "route.h"
+#include "message.h"
+#include "resend.h"
+#include "babel_errors.h"
+
+struct neighbour *neighs = NULL;
+
+static struct neighbour *
+find_neighbour_nocreate(const unsigned char *address, struct interface *ifp)
+{
+ struct neighbour *neigh;
+ FOR_ALL_NEIGHBOURS(neigh) {
+ if(memcmp(address, neigh->address, 16) == 0 &&
+ neigh->ifp == ifp)
+ return neigh;
+ }
+ return NULL;
+}
+
+void
+flush_neighbour(struct neighbour *neigh)
+{
+ debugf(BABEL_DEBUG_COMMON,"Flushing neighbour %s (reach 0x%04x)",
+ format_address(neigh->address), neigh->reach);
+ flush_neighbour_routes(neigh);
+ if(unicast_neighbour == neigh)
+ flush_unicast(1);
+ flush_resends(neigh);
+
+ if(neighs == neigh) {
+ neighs = neigh->next;
+ } else {
+ struct neighbour *previous = neighs;
+ while(previous->next != neigh)
+ previous = previous->next;
+ previous->next = neigh->next;
+ }
+ free(neigh);
+}
+
+struct neighbour *
+find_neighbour(const unsigned char *address, struct interface *ifp)
+{
+ struct neighbour *neigh;
+ const struct timeval zero = {0, 0};
+
+ neigh = find_neighbour_nocreate(address, ifp);
+ if(neigh)
+ return neigh;
+
+ debugf(BABEL_DEBUG_COMMON,"Creating neighbour %s on %s.",
+ format_address(address), ifp->name);
+
+ neigh = malloc(sizeof(struct neighbour));
+ if(neigh == NULL) {
+ flog_err(EC_BABEL_MEMORY, "malloc(neighbour): %s",
+ safe_strerror(errno));
+ return NULL;
+ }
+
+ neigh->hello_seqno = -1;
+ memcpy(neigh->address, address, 16);
+ neigh->reach = 0;
+ neigh->txcost = INFINITY;
+ neigh->ihu_time = babel_now;
+ neigh->hello_time = zero;
+ neigh->hello_interval = 0;
+ neigh->ihu_interval = 0;
+ neigh->hello_send_us = 0;
+ neigh->hello_rtt_receive_time = zero;
+ neigh->rtt = 0;
+ neigh->rtt_time = zero;
+ neigh->ifp = ifp;
+ neigh->next = neighs;
+ neighs = neigh;
+ send_hello(ifp);
+ return neigh;
+}
+
+/* Recompute a neighbour's rxcost. Return true if anything changed. */
+int
+update_neighbour(struct neighbour *neigh, int hello, int hello_interval)
+{
+ int missed_hellos;
+ int rc = 0;
+
+ if(hello < 0) {
+ if(neigh->hello_interval == 0)
+ return rc;
+ missed_hellos =
+ ((int)timeval_minus_msec(&babel_now, &neigh->hello_time) -
+ neigh->hello_interval * 7) /
+ (neigh->hello_interval * 10);
+ if(missed_hellos <= 0)
+ return rc;
+ timeval_add_msec(&neigh->hello_time, &neigh->hello_time,
+ missed_hellos * neigh->hello_interval * 10);
+ } else {
+ if(neigh->hello_seqno >= 0 && neigh->reach > 0) {
+ missed_hellos = seqno_minus(hello, neigh->hello_seqno) - 1;
+ if(missed_hellos < -8) {
+ /* Probably a neighbour that rebooted and lost its seqno.
+ Reboot the universe. */
+ neigh->reach = 0;
+ missed_hellos = 0;
+ rc = 1;
+ } else if(missed_hellos < 0) {
+ if(hello_interval > neigh->hello_interval) {
+ /* This neighbour has increased its hello interval,
+ and we didn't notice. */
+ neigh->reach <<= -missed_hellos;
+ missed_hellos = 0;
+ } else {
+ /* Late hello. Probably due to the link layer buffering
+ packets during a link outage. Ignore it, but reset
+ the expected seqno. */
+ neigh->hello_seqno = hello;
+ hello = -1;
+ missed_hellos = 0;
+ }
+ rc = 1;
+ }
+ } else {
+ missed_hellos = 0;
+ }
+ neigh->hello_time = babel_now;
+ neigh->hello_interval = hello_interval;
+ }
+
+ if(missed_hellos > 0) {
+ neigh->reach >>= missed_hellos;
+ neigh->hello_seqno = seqno_plus(neigh->hello_seqno, missed_hellos);
+ rc = 1;
+ }
+
+ if(hello >= 0) {
+ neigh->hello_seqno = hello;
+ neigh->reach >>= 1;
+ neigh->reach |= 0x8000;
+ if((neigh->reach & 0xFC00) != 0xFC00)
+ rc = 1;
+ }
+
+ /* Make sure to give neighbours some feedback early after association */
+ if((neigh->reach & 0xBF00) == 0x8000) {
+ /* A new neighbour */
+ send_hello(neigh->ifp);
+ } else {
+ /* Don't send hellos, in order to avoid a positive feedback loop. */
+ int a = (neigh->reach & 0xC000);
+ int b = (neigh->reach & 0x3000);
+ if((a == 0xC000 && b == 0) || (a == 0 && b == 0x3000)) {
+ /* Reachability is either 1100 or 0011 */
+ send_self_update(neigh->ifp);
+ }
+ }
+
+ if((neigh->reach & 0xFC00) == 0xC000) {
+ /* This is a newish neighbour, let's request a full route dump.
+ We ought to avoid this when the network is dense */
+ send_unicast_request(neigh, NULL, 0);
+ send_ihu(neigh, NULL);
+ }
+ return rc;
+}
+
+static int
+reset_txcost(struct neighbour *neigh)
+{
+ unsigned delay;
+
+ delay = timeval_minus_msec(&babel_now, &neigh->ihu_time);
+
+ if(neigh->ihu_interval > 0 && delay < neigh->ihu_interval * 10U * 3U)
+ return 0;
+
+ /* If we're losing a lot of packets, we probably lost an IHU too */
+ if(delay >= 180000 || (neigh->reach & 0xFFF0) == 0 ||
+ (neigh->ihu_interval > 0 &&
+ delay >= neigh->ihu_interval * 10U * 10U)) {
+ neigh->txcost = INFINITY;
+ neigh->ihu_time = babel_now;
+ return 1;
+ }
+
+ return 0;
+}
+
+unsigned
+neighbour_txcost(struct neighbour *neigh)
+{
+ return neigh->txcost;
+}
+
+unsigned
+check_neighbours(void)
+{
+ struct neighbour *neigh;
+ int changed, rc;
+ unsigned msecs = 50000;
+
+ debugf(BABEL_DEBUG_COMMON,"Checking neighbours.");
+
+ neigh = neighs;
+ while(neigh) {
+ changed = update_neighbour(neigh, -1, 0);
+
+ if(neigh->reach == 0 ||
+ neigh->hello_time.tv_sec > babel_now.tv_sec || /* clock stepped */
+ timeval_minus_msec(&babel_now, &neigh->hello_time) > 300000) {
+ struct neighbour *old = neigh;
+ neigh = neigh->next;
+ flush_neighbour(old);
+ continue;
+ }
+
+ rc = reset_txcost(neigh);
+ changed = changed || rc;
+
+ update_neighbour_metric(neigh, changed);
+
+ if(neigh->hello_interval > 0)
+ msecs = MIN(msecs, neigh->hello_interval * 10U);
+ if(neigh->ihu_interval > 0)
+ msecs = MIN(msecs, neigh->ihu_interval * 10U);
+ neigh = neigh->next;
+ }
+
+ return msecs;
+}
+
+unsigned
+neighbour_rxcost(struct neighbour *neigh)
+{
+ unsigned delay;
+ unsigned short reach = neigh->reach;
+
+ delay = timeval_minus_msec(&babel_now, &neigh->hello_time);
+
+ if((reach & 0xFFF0) == 0 || delay >= 180000) {
+ return INFINITY;
+ } else if(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ) {
+ int sreach =
+ ((reach & 0x8000) >> 2) +
+ ((reach & 0x4000) >> 1) +
+ (reach & 0x3FFF);
+ /* 0 <= sreach <= 0x7FFF */
+ int cost = (0x8000 * babel_get_if_nfo(neigh->ifp)->cost) / (sreach + 1);
+ /* cost >= interface->cost */
+ if(delay >= 40000)
+ cost = (cost * (delay - 20000) + 10000) / 20000;
+ return MIN(cost, INFINITY);
+ } else {
+ /* To lose one hello is a misfortune, to lose two is carelessness. */
+ if((reach & 0xC000) == 0xC000)
+ return babel_get_if_nfo(neigh->ifp)->cost;
+ else if((reach & 0xC000) == 0)
+ return INFINITY;
+ else if((reach & 0x2000))
+ return babel_get_if_nfo(neigh->ifp)->cost;
+ else
+ return INFINITY;
+ }
+}
+
+unsigned
+neighbour_rttcost(struct neighbour *neigh)
+{
+ struct interface *ifp = neigh->ifp;
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+
+ if(!babel_ifp->max_rtt_penalty || !valid_rtt(neigh))
+ return 0;
+
+ /* Function: linear behaviour between rtt_min and rtt_max. */
+ if(neigh->rtt <= babel_ifp->rtt_min) {
+ return 0;
+ } else if(neigh->rtt <= babel_ifp->rtt_max) {
+ unsigned long long tmp =
+ (unsigned long long)babel_ifp->max_rtt_penalty *
+ (neigh->rtt - babel_ifp->rtt_min) /
+ (babel_ifp->rtt_max - babel_ifp->rtt_min);
+ assert((tmp & 0x7FFFFFFF) == tmp);
+ return tmp;
+ } else {
+ return babel_ifp->max_rtt_penalty;
+ }
+}
+
+unsigned
+neighbour_cost(struct neighbour *neigh)
+{
+ unsigned a, b, cost;
+
+ if(!if_up(neigh->ifp))
+ return INFINITY;
+
+ a = neighbour_txcost(neigh);
+
+ if(a >= INFINITY)
+ return INFINITY;
+
+ b = neighbour_rxcost(neigh);
+ if(b >= INFINITY)
+ return INFINITY;
+
+ if(!(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ)
+ || (a < 256 && b < 256)) {
+ cost = a;
+ } else {
+ /* a = 256/alpha, b = 256/beta, where alpha and beta are the expected
+ probabilities of a packet getting through in the direct and reverse
+ directions. */
+ a = MAX(a, 256);
+ b = MAX(b, 256);
+ /* 1/(alpha * beta), which is just plain ETX. */
+ /* Since a and b are capped to 16 bits, overflow is impossible. */
+ cost = (a * b + 128) >> 8;
+ }
+
+ cost += neighbour_rttcost(neigh);
+
+ return MIN(cost, INFINITY);
+}
+
+int
+valid_rtt(struct neighbour *neigh)
+{
+ return (timeval_minus_msec(&babel_now, &neigh->rtt_time) < 180000) ? 1 : 0;
+}
diff --git a/babeld/neighbour.h b/babeld/neighbour.h
new file mode 100644
index 0000000..2111663
--- /dev/null
+++ b/babeld/neighbour.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifndef BABEL_NEIGHBOUR_H
+#define BABEL_NEIGHBOUR_H
+
+struct neighbour {
+ struct neighbour *next;
+ /* This is -1 when unknown, so don't make it unsigned */
+ int hello_seqno;
+ unsigned char address[16];
+ unsigned short reach;
+ unsigned short txcost;
+ struct timeval hello_time;
+ struct timeval ihu_time;
+ unsigned short hello_interval; /* in centiseconds */
+ unsigned short ihu_interval; /* in centiseconds */
+ /* Used for RTT estimation. */
+ /* Absolute time (modulo 2^32) at which the Hello was sent,
+ according to remote clock. */
+ unsigned int hello_send_us;
+ struct timeval hello_rtt_receive_time;
+ unsigned int rtt;
+ struct timeval rtt_time;
+ struct interface *ifp;
+};
+
+extern struct neighbour *neighs;
+
+#define FOR_ALL_NEIGHBOURS(_neigh) \
+ for(_neigh = neighs; _neigh; _neigh = _neigh->next)
+
+int neighbour_valid(struct neighbour *neigh);
+void flush_neighbour(struct neighbour *neigh);
+struct neighbour *find_neighbour(const unsigned char *address,
+ struct interface *ifp);
+int update_neighbour(struct neighbour *neigh, int hello, int hello_interval);
+unsigned check_neighbours(void);
+unsigned neighbour_txcost(struct neighbour *neigh);
+unsigned neighbour_rxcost(struct neighbour *neigh);
+unsigned neighbour_rttcost(struct neighbour *neigh);
+unsigned neighbour_cost(struct neighbour *neigh);
+int valid_rtt(struct neighbour *neigh);
+
+#endif /* BABEL_NEIGHBOUR_H */
diff --git a/babeld/net.c b/babeld/net.c
new file mode 100644
index 0000000..15ea2de
--- /dev/null
+++ b/babeld/net.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "babeld.h"
+#include "util.h"
+#include "net.h"
+#include "sockopt.h"
+
+int
+babel_socket(int port)
+{
+ struct sockaddr_in6 sin6;
+ int s, rc;
+ int saved_errno;
+ int one = 1, zero = 0;
+
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if(s < 0)
+ return -1;
+
+ rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+ if(rc < 0)
+ goto fail;
+
+ rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ if(rc < 0)
+ goto fail;
+
+ rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &zero, sizeof(zero));
+ if(rc < 0)
+ goto fail;
+
+ rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &one, sizeof(one));
+ if(rc < 0)
+ goto fail;
+
+ rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &one, sizeof(one));
+ if(rc < 0)
+ goto fail;
+
+ setsockopt_ipv6_tclass (s, IPTOS_PREC_INTERNETCONTROL);
+
+ rc = fcntl(s, F_GETFL, 0);
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_GETFD, 0);
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
+ if(rc < 0)
+ goto fail;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+ rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ goto fail;
+
+ return s;
+
+ fail:
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ return -1;
+}
+
+int
+babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
+{
+ struct iovec iovec;
+ struct msghdr msg;
+ int rc;
+
+ memset(&msg, 0, sizeof(msg));
+ iovec.iov_base = buf;
+ iovec.iov_len = buflen;
+ msg.msg_name = sin;
+ msg.msg_namelen = slen;
+ msg.msg_iov = &iovec;
+ msg.msg_iovlen = 1;
+
+ rc = recvmsg(s, &msg, 0);
+ return rc;
+}
+
+int
+babel_send(int s,
+ void *buf1, int buflen1, void *buf2, int buflen2,
+ struct sockaddr *sin, int slen)
+{
+ struct iovec iovec[2];
+ struct msghdr msg;
+ int rc;
+
+ iovec[0].iov_base = buf1;
+ iovec[0].iov_len = buflen1;
+ iovec[1].iov_base = buf2;
+ iovec[1].iov_len = buflen2;
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = sin;
+ msg.msg_namelen = slen;
+ msg.msg_iov = iovec;
+ msg.msg_iovlen = 2;
+
+ again:
+ rc = sendmsg(s, &msg, 0);
+ if(rc < 0) {
+ if(errno == EINTR)
+ goto again;
+ else if(errno == EAGAIN) {
+ int rc2;
+ rc2 = wait_for_fd(1, s, 5);
+ if(rc2 > 0)
+ goto again;
+ errno = EAGAIN;
+ }
+ }
+ return rc;
+}
+
+int
+tcp_server_socket(int port, int local)
+{
+ struct sockaddr_in6 sin6;
+ int s, rc, saved_errno;
+ int one = 1;
+
+ s = socket(PF_INET6, SOCK_STREAM, 0);
+ if(s < 0)
+ return -1;
+
+ rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_GETFL, 0);
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_GETFD, 0);
+ if(rc < 0)
+ goto fail;
+
+ rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
+ if(rc < 0)
+ goto fail;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+ if(local) {
+ rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr);
+ if(rc < 0)
+ goto fail;
+ }
+ rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ goto fail;
+
+ rc = listen(s, 2);
+ if(rc < 0)
+ goto fail;
+
+ return s;
+
+ fail:
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ return -1;
+}
diff --git a/babeld/net.h b/babeld/net.h
new file mode 100644
index 0000000..2559602
--- /dev/null
+++ b/babeld/net.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifndef BABEL_NET_H
+#define BABEL_NET_H
+
+int babel_socket(int port);
+int babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen);
+int babel_send(int s,
+ void *buf1, int buflen1, void *buf2, int buflen2,
+ struct sockaddr *sin, int slen);
+int tcp_server_socket(int port, int local);
+
+#endif /* BABEL_NET_H */
diff --git a/babeld/resend.c b/babeld/resend.c
new file mode 100644
index 0000000..254faac
--- /dev/null
+++ b/babeld/resend.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <zebra.h>
+#include "if.h"
+
+#include "babel_main.h"
+#include "babeld.h"
+#include "util.h"
+#include "neighbour.h"
+#include "resend.h"
+#include "message.h"
+#include "babel_interface.h"
+
+struct timeval resend_time = {0, 0};
+struct resend *to_resend = NULL;
+
+static int
+resend_match(struct resend *resend,
+ int kind, const unsigned char *prefix, unsigned char plen)
+{
+ return (resend->kind == kind &&
+ resend->plen == plen && memcmp(resend->prefix, prefix, 16) == 0);
+}
+
+/* This is called by neigh.c when a neighbour is flushed */
+
+void
+flush_resends(struct neighbour *neigh)
+{
+ /* Nothing for now */
+}
+
+static struct resend *
+find_resend(int kind, const unsigned char *prefix, unsigned char plen,
+ struct resend **previous_return)
+{
+ struct resend *current, *previous;
+
+ previous = NULL;
+ current = to_resend;
+ while(current) {
+ if(resend_match(current, kind, prefix, plen)) {
+ if(previous_return)
+ *previous_return = previous;
+ return current;
+ }
+ previous = current;
+ current = current->next;
+ }
+
+ return NULL;
+}
+
+struct resend *
+find_request(const unsigned char *prefix, unsigned char plen,
+ struct resend **previous_return)
+{
+ return find_resend(RESEND_REQUEST, prefix, plen, previous_return);
+}
+
+int
+record_resend(int kind, const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ struct interface *ifp, int delay)
+{
+ struct resend *resend;
+ unsigned int ifindex = ifp ? ifp->ifindex : 0;
+
+ if((kind == RESEND_REQUEST &&
+ input_filter(NULL, prefix, plen, NULL, ifindex) >= INFINITY) ||
+ (kind == RESEND_UPDATE &&
+ output_filter(NULL, prefix, plen, ifindex) >= INFINITY))
+ return 0;
+
+ if(delay >= 0xFFFF)
+ delay = 0xFFFF;
+
+ resend = find_resend(kind, prefix, plen, NULL);
+ if(resend) {
+ if(resend->delay && delay)
+ resend->delay = MIN(resend->delay, delay);
+ else if(delay)
+ resend->delay = delay;
+ resend->time = babel_now;
+ resend->max = RESEND_MAX;
+ if(id && memcmp(resend->id, id, 8) == 0 &&
+ seqno_compare(resend->seqno, seqno) > 0) {
+ return 0;
+ }
+ if(id)
+ memcpy(resend->id, id, 8);
+ else
+ memset(resend->id, 0, 8);
+ resend->seqno = seqno;
+ if(resend->ifp != ifp)
+ resend->ifp = NULL;
+ } else {
+ resend = malloc(sizeof(struct resend));
+ if(resend == NULL)
+ return -1;
+ resend->kind = kind;
+ resend->max = RESEND_MAX;
+ resend->delay = delay;
+ memcpy(resend->prefix, prefix, 16);
+ resend->plen = plen;
+ resend->seqno = seqno;
+ if(id)
+ memcpy(resend->id, id, 8);
+ else
+ memset(resend->id, 0, 8);
+ resend->ifp = ifp;
+ resend->time = babel_now;
+ resend->next = to_resend;
+ to_resend = resend;
+ }
+
+ if(resend->delay) {
+ struct timeval timeout;
+ timeval_add_msec(&timeout, &resend->time, resend->delay);
+ timeval_min(&resend_time, &timeout);
+ }
+ return 1;
+}
+
+static int
+resend_expired(struct resend *resend)
+{
+ switch(resend->kind) {
+ case RESEND_REQUEST:
+ return timeval_minus_msec(&babel_now, &resend->time) >= REQUEST_TIMEOUT;
+ default:
+ return resend->max <= 0;
+ }
+}
+
+int
+unsatisfied_request(const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id)
+{
+ struct resend *request;
+
+ request = find_request(prefix, plen, NULL);
+ if(request == NULL || resend_expired(request))
+ return 0;
+
+ if(memcmp(request->id, id, 8) != 0 ||
+ seqno_compare(request->seqno, seqno) <= 0)
+ return 1;
+
+ return 0;
+}
+
+/* Determine whether a given request should be forwarded. */
+int
+request_redundant(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id)
+{
+ struct resend *request;
+
+ request = find_request(prefix, plen, NULL);
+ if(request == NULL || resend_expired(request))
+ return 0;
+
+ if(memcmp(request->id, id, 8) == 0 &&
+ seqno_compare(request->seqno, seqno) > 0)
+ return 0;
+
+ if(request->ifp != NULL && request->ifp != ifp)
+ return 0;
+
+ if(request->max > 0)
+ /* Will be resent. */
+ return 1;
+
+ if(timeval_minus_msec(&babel_now, &request->time) <
+ (ifp ? MIN(babel_get_if_nfo(ifp)->hello_interval, 1000) : 1000))
+ /* Fairly recent. */
+ return 1;
+
+ return 0;
+}
+
+int
+satisfy_request(const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ struct interface *ifp)
+{
+ struct resend *request, *previous;
+
+ request = find_request(prefix, plen, &previous);
+ if(request == NULL)
+ return 0;
+
+ if(ifp != NULL && request->ifp != ifp)
+ return 0;
+
+ if(memcmp(request->id, id, 8) != 0 ||
+ seqno_compare(request->seqno, seqno) <= 0) {
+ /* We cannot remove the request, as we may be walking the list right
+ now. Mark it as expired, so that expire_resend will remove it. */
+ request->max = 0;
+ request->time.tv_sec = 0;
+ recompute_resend_time();
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+expire_resend(void)
+{
+ struct resend *current, *previous;
+ int recompute = 0;
+
+ previous = NULL;
+ current = to_resend;
+ while(current) {
+ if(resend_expired(current)) {
+ if(previous == NULL) {
+ to_resend = current->next;
+ free(current);
+ current = to_resend;
+ } else {
+ previous->next = current->next;
+ free(current);
+ current = previous->next;
+ }
+ recompute = 1;
+ } else {
+ previous = current;
+ current = current->next;
+ }
+ }
+ if(recompute)
+ recompute_resend_time();
+}
+
+void
+recompute_resend_time(void)
+{
+ struct resend *request;
+ struct timeval resend = {0, 0};
+
+ request = to_resend;
+ while(request) {
+ if(!resend_expired(request) && request->delay > 0 && request->max > 0) {
+ struct timeval timeout;
+ timeval_add_msec(&timeout, &request->time, request->delay);
+ timeval_min(&resend, &timeout);
+ }
+ request = request->next;
+ }
+
+ resend_time = resend;
+}
+
+void
+do_resend(void)
+{
+ struct resend *resend;
+
+ resend = to_resend;
+ while(resend) {
+ if(!resend_expired(resend) && resend->delay > 0 && resend->max > 0) {
+ struct timeval timeout;
+ timeval_add_msec(&timeout, &resend->time, resend->delay);
+ if(timeval_compare(&babel_now, &timeout) >= 0) {
+ switch(resend->kind) {
+ case RESEND_REQUEST:
+ send_multihop_request(resend->ifp,
+ resend->prefix, resend->plen,
+ resend->seqno, resend->id, 127);
+ break;
+ case RESEND_UPDATE:
+ send_update(resend->ifp, 1,
+ resend->prefix, resend->plen);
+ break;
+ default: abort();
+ }
+ resend->delay = MIN(0xFFFF, resend->delay * 2);
+ resend->max--;
+ }
+ }
+ resend = resend->next;
+ }
+ recompute_resend_time();
+}
diff --git a/babeld/resend.h b/babeld/resend.h
new file mode 100644
index 0000000..d0ec046
--- /dev/null
+++ b/babeld/resend.h
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+#ifndef BABEL_RESEND_H
+#define BABEL_RESEND_H
+
+#define REQUEST_TIMEOUT 65000
+#define RESEND_MAX 3
+
+#define RESEND_REQUEST 1
+#define RESEND_UPDATE 2
+
+struct resend {
+ unsigned char kind;
+ unsigned char max;
+ unsigned short delay;
+ struct timeval time;
+ unsigned char prefix[16];
+ unsigned char plen;
+ unsigned short seqno;
+ unsigned char id[8];
+ struct interface *ifp;
+ struct resend *next;
+};
+
+extern struct timeval resend_time;
+
+struct resend *find_request(const unsigned char *prefix, unsigned char plen,
+ struct resend **previous_return);
+void flush_resends(struct neighbour *neigh);
+int record_resend(int kind, const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ struct interface *ifp, int delay);
+int unsatisfied_request(const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id);
+int request_redundant(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id);
+int satisfy_request(const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ struct interface *ifp);
+
+void expire_resend(void);
+void recompute_resend_time(void);
+void do_resend(void);
+
+#endif /* BABEL_RESEND_H */
diff --git a/babeld/route.c b/babeld/route.c
new file mode 100644
index 0000000..2c7e923
--- /dev/null
+++ b/babeld/route.c
@@ -0,0 +1,1129 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#include <zebra.h>
+#include "if.h"
+
+#include "babeld.h"
+#include "util.h"
+#include "kernel.h"
+#include "babel_interface.h"
+#include "source.h"
+#include "neighbour.h"
+#include "route.h"
+#include "xroute.h"
+#include "message.h"
+#include "resend.h"
+#include "babel_errors.h"
+
+static void consider_route(struct babel_route *route);
+
+struct babel_route **routes = NULL;
+static int route_slots = 0, max_route_slots = 0;
+int kernel_metric = 0;
+enum babel_diversity diversity_kind = DIVERSITY_NONE;
+int diversity_factor = BABEL_DEFAULT_DIVERSITY_FACTOR;
+int keep_unfeasible = 0;
+
+int smoothing_half_life = 0;
+static int two_to_the_one_over_hl = 0; /* 2^(1/hl) * 0x10000 */
+
+/* We maintain a list of "slots", ordered by prefix. Every slot
+ contains a linked list of the routes to this prefix, with the
+ installed route, if any, at the head of the list. */
+
+static int
+route_compare(const unsigned char *prefix, unsigned char plen,
+ struct babel_route *route)
+{
+ int i = memcmp(prefix, route->src->prefix, 16);
+ if(i != 0)
+ return i;
+
+ if(plen < route->src->plen)
+ return -1;
+ else if(plen > route->src->plen)
+ return 1;
+ else
+ return 0;
+}
+
+/* Performs binary search, returns -1 in case of failure. In the latter
+ case, new_return is the place where to insert the new element. */
+
+static int
+find_route_slot(const unsigned char *prefix, unsigned char plen,
+ int *new_return)
+{
+ int p, m, g, c;
+
+ if(route_slots < 1) {
+ if(new_return)
+ *new_return = 0;
+ return -1;
+ }
+
+ p = 0; g = route_slots - 1;
+
+ do {
+ m = (p + g) / 2;
+ c = route_compare(prefix, plen, routes[m]);
+ if(c == 0)
+ return m;
+ else if(c < 0)
+ g = m - 1;
+ else
+ p = m + 1;
+ } while(p <= g);
+
+ if(new_return)
+ *new_return = p;
+
+ return -1;
+}
+
+struct babel_route *
+find_route(const unsigned char *prefix, unsigned char plen,
+ struct neighbour *neigh, const unsigned char *nexthop)
+{
+ struct babel_route *route;
+ int i = find_route_slot(prefix, plen, NULL);
+
+ if(i < 0)
+ return NULL;
+
+ route = routes[i];
+
+ while(route) {
+ if(route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0)
+ return route;
+ route = route->next;
+ }
+
+ return NULL;
+}
+
+struct babel_route *
+find_installed_route(const unsigned char *prefix, unsigned char plen)
+{
+ int i = find_route_slot(prefix, plen, NULL);
+
+ if(i >= 0 && routes[i]->installed)
+ return routes[i];
+
+ return NULL;
+}
+
+/* Returns an overestimate of the number of installed routes. */
+int
+installed_routes_estimate(void)
+{
+ return route_slots;
+}
+
+static int
+resize_route_table(int new_slots)
+{
+ struct babel_route **new_routes;
+ assert(new_slots >= route_slots);
+
+ if(new_slots == 0) {
+ new_routes = NULL;
+ free(routes);
+ } else {
+ new_routes = realloc(routes, new_slots * sizeof(struct babel_route*));
+ if(new_routes == NULL)
+ return -1;
+ }
+
+ max_route_slots = new_slots;
+ routes = new_routes;
+ return 1;
+}
+
+/* Insert a route into the table. If successful, retains the route.
+ On failure, caller must free the route. */
+static struct babel_route *
+insert_route(struct babel_route *route)
+{
+ int i, n = 0;
+
+ assert(!route->installed);
+
+ i = find_route_slot(route->src->prefix, route->src->plen, &n);
+
+ if(i < 0) {
+ if(route_slots >= max_route_slots)
+ resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
+ if(route_slots >= max_route_slots)
+ return NULL;
+ assert(routes);
+ route->next = NULL;
+ if(n < route_slots)
+ memmove(routes + n + 1, routes + n,
+ (route_slots - n) * sizeof(struct babel_route*));
+ route_slots++;
+ routes[n] = route;
+ } else {
+ struct babel_route *r;
+ r = routes[i];
+ while(r->next)
+ r = r->next;
+ r->next = route;
+ route->next = NULL;
+ }
+
+ return route;
+}
+
+void
+flush_route(struct babel_route *route)
+{
+ int i;
+ struct source *src;
+ unsigned oldmetric;
+ int lost = 0;
+
+ oldmetric = route_metric(route);
+ src = route->src;
+
+ if(route->installed) {
+ uninstall_route(route);
+ lost = 1;
+ }
+
+ i = find_route_slot(route->src->prefix, route->src->plen, NULL);
+ assert(i >= 0 && i < route_slots);
+
+ if(route == routes[i]) {
+ routes[i] = route->next;
+ route->next = NULL;
+ free(route);
+
+ if(routes[i] == NULL) {
+ if(i < route_slots - 1)
+ memmove(routes + i, routes + i + 1,
+ (route_slots - i - 1) * sizeof(struct babel_route*));
+ routes[route_slots - 1] = NULL;
+ route_slots--;
+ }
+
+ if(route_slots == 0)
+ resize_route_table(0);
+ else if(max_route_slots > 8 && route_slots < max_route_slots / 4)
+ resize_route_table(max_route_slots / 2);
+ } else {
+ struct babel_route *r = routes[i];
+ while(r->next != route)
+ r = r->next;
+ r->next = route->next;
+ route->next = NULL;
+ free(route);
+ }
+
+ if(lost)
+ route_lost(src, oldmetric);
+
+ release_source(src);
+}
+
+void
+flush_all_routes(void)
+{
+ int i;
+
+ /* Start from the end, to avoid shifting the table. */
+ i = route_slots - 1;
+ while(i >= 0) {
+ while(i < route_slots) {
+ /* Uninstall first, to avoid calling route_lost. */
+ if(routes[i]->installed)
+ uninstall_route(routes[i]);
+ flush_route(routes[i]);
+ }
+ i--;
+ }
+
+ check_sources_released();
+}
+
+void
+flush_neighbour_routes(struct neighbour *neigh)
+{
+ int i;
+
+ i = 0;
+ while(i < route_slots) {
+ struct babel_route *r;
+ r = routes[i];
+ while(r) {
+ if(r->neigh == neigh) {
+ flush_route(r);
+ goto again;
+ }
+ r = r->next;
+ }
+ i++;
+ again:
+ ;
+ }
+}
+
+void
+flush_interface_routes(struct interface *ifp, int v4only)
+{
+ int i;
+
+ i = 0;
+ while(i < route_slots) {
+ struct babel_route *r;
+ r = routes[i];
+ while(r) {
+ if(r->neigh->ifp == ifp &&
+ (!v4only || v4mapped(r->nexthop))) {
+ flush_route(r);
+ goto again;
+ }
+ r = r->next;
+ }
+ i++;
+ again:
+ ;
+ }
+}
+
+struct route_stream {
+ int installed;
+ int index;
+ struct babel_route *next;
+};
+
+
+struct route_stream *
+route_stream(int installed)
+{
+ struct route_stream *stream;
+
+ stream = malloc(sizeof(struct route_stream));
+ if(stream == NULL)
+ return NULL;
+
+ stream->installed = installed;
+ stream->index = installed ? 0 : -1;
+ stream->next = NULL;
+
+ return stream;
+}
+
+struct babel_route *
+route_stream_next(struct route_stream *stream)
+{
+ if(stream->installed) {
+ while(stream->index < route_slots && !routes[stream->index]->installed)
+ stream->index++;
+
+ if(stream->index < route_slots)
+ return routes[stream->index++];
+ else
+ return NULL;
+ } else {
+ struct babel_route *next;
+ if(!stream->next) {
+ stream->index++;
+ if(stream->index >= route_slots)
+ return NULL;
+ stream->next = routes[stream->index];
+ }
+ next = stream->next;
+ stream->next = next->next;
+ return next;
+ }
+}
+
+void
+route_stream_done(struct route_stream *stream)
+{
+ free(stream);
+}
+
+static int
+metric_to_kernel(int metric)
+{
+ return metric < INFINITY ? kernel_metric : KERNEL_INFINITY;
+}
+
+/* This is used to maintain the invariant that the installed route is at
+ the head of the list. */
+static void
+move_installed_route(struct babel_route *route, int i)
+{
+ assert(i >= 0 && i < route_slots);
+ assert(route->installed);
+
+ if(route != routes[i]) {
+ struct babel_route *r = routes[i];
+ while(r->next != route)
+ r = r->next;
+ r->next = route->next;
+ route->next = routes[i];
+ routes[i] = route;
+ }
+}
+
+void
+install_route(struct babel_route *route)
+{
+ int i, rc;
+
+ if(route->installed)
+ return;
+
+ if(!route_feasible(route))
+ flog_err(EC_BABEL_ROUTE,
+ "Installing unfeasible route (this shouldn't happen).");
+
+ i = find_route_slot(route->src->prefix, route->src->plen, NULL);
+ assert(i >= 0 && i < route_slots);
+
+ if(routes[i] != route && routes[i]->installed) {
+ flog_err(
+ EC_BABEL_ROUTE,
+ "Attempting to install duplicate route (this shouldn't happen).");
+ return;
+ }
+
+ rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen,
+ route->nexthop,
+ route->neigh->ifp->ifindex,
+ metric_to_kernel(route_metric(route)), NULL, 0, 0);
+ if(rc < 0) {
+ int save = errno;
+ flog_err(EC_BABEL_ROUTE, "kernel_route(ADD): %s",
+ safe_strerror(errno));
+ if(save != EEXIST)
+ return;
+ }
+ route->installed = 1;
+ move_installed_route(route, i);
+
+}
+
+void
+uninstall_route(struct babel_route *route)
+{
+ int rc;
+
+ if(!route->installed)
+ return;
+
+ rc = kernel_route(ROUTE_FLUSH, route->src->prefix, route->src->plen,
+ route->nexthop,
+ route->neigh->ifp->ifindex,
+ metric_to_kernel(route_metric(route)), NULL, 0, 0);
+ if(rc < 0)
+ flog_err(EC_BABEL_ROUTE, "kernel_route(FLUSH): %s",
+ safe_strerror(errno));
+
+ route->installed = 0;
+}
+
+/* This is equivalent to uninstall_route followed with install_route,
+ but without the race condition. The destination of both routes
+ must be the same. */
+
+static void
+switch_routes(struct babel_route *old, struct babel_route *new)
+{
+ int rc;
+
+ if(!old) {
+ install_route(new);
+ return;
+ }
+
+ if(!old->installed)
+ return;
+
+ if(!route_feasible(new))
+ flog_err(EC_BABEL_ROUTE,
+ "Switching to unfeasible route (this shouldn't happen).");
+
+ rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen,
+ old->nexthop, old->neigh->ifp->ifindex,
+ metric_to_kernel(route_metric(old)),
+ new->nexthop, new->neigh->ifp->ifindex,
+ metric_to_kernel(route_metric(new)));
+ if(rc < 0) {
+ flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY): %s",
+ safe_strerror(errno));
+ return;
+ }
+
+ old->installed = 0;
+ new->installed = 1;
+ move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen,
+ NULL));
+}
+
+static void
+change_route_metric(struct babel_route *route,
+ unsigned refmetric, unsigned cost, unsigned add)
+{
+ int old, new;
+ int newmetric = MIN(refmetric + cost + add, INFINITY);
+
+ old = metric_to_kernel(route_metric(route));
+ new = metric_to_kernel(newmetric);
+
+ if(route->installed && old != new) {
+ int rc;
+ rc = kernel_route(ROUTE_MODIFY, route->src->prefix, route->src->plen,
+ route->nexthop, route->neigh->ifp->ifindex,
+ old,
+ route->nexthop, route->neigh->ifp->ifindex,
+ new);
+ if(rc < 0) {
+ flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY metric): %s",
+ safe_strerror(errno));
+ return;
+ }
+ }
+
+ /* Update route->smoothed_metric using the old metric. */
+ route_smoothed_metric(route);
+
+ route->refmetric = refmetric;
+ route->cost = cost;
+ route->add_metric = add;
+
+ if(smoothing_half_life == 0) {
+ route->smoothed_metric = route_metric(route);
+ route->smoothed_metric_time = babel_now.tv_sec;
+ }
+}
+
+static void
+retract_route(struct babel_route *route)
+{
+ /* We cannot simply remove the route from the kernel, as that might
+ cause a routing loop -- see RFC 6126 Sections 2.8 and 3.5.5. */
+ change_route_metric(route, INFINITY, INFINITY, 0);
+}
+
+int
+route_feasible(struct babel_route *route)
+{
+ return update_feasible(route->src, route->seqno, route->refmetric);
+}
+
+int
+route_old(struct babel_route *route)
+{
+ return route->time < babel_now.tv_sec - route->hold_time * 7 / 8;
+}
+
+int
+route_expired(struct babel_route *route)
+{
+ return route->time < babel_now.tv_sec - route->hold_time;
+}
+
+static int
+channels_interfere(int ch1, int ch2)
+{
+ if(ch1 == BABEL_IF_CHANNEL_NONINTERFERING
+ || ch2 == BABEL_IF_CHANNEL_NONINTERFERING)
+ return 0;
+ if(ch1 == BABEL_IF_CHANNEL_INTERFERING
+ || ch2 == BABEL_IF_CHANNEL_INTERFERING)
+ return 1;
+ return ch1 == ch2;
+}
+
+int
+route_interferes(struct babel_route *route, struct interface *ifp)
+{
+ struct babel_interface *babel_ifp = NULL;
+ switch(diversity_kind) {
+ case DIVERSITY_NONE:
+ return 1;
+ case DIVERSITY_INTERFACE_1:
+ return route->neigh->ifp == ifp;
+ case DIVERSITY_CHANNEL_1:
+ case DIVERSITY_CHANNEL:
+ if(route->neigh->ifp == ifp)
+ return 1;
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(channels_interfere(babel_ifp->channel,
+ babel_get_if_nfo(route->neigh->ifp)->channel))
+ return 1;
+ if(diversity_kind == DIVERSITY_CHANNEL) {
+ int i;
+ for(i = 0; i < DIVERSITY_HOPS; i++) {
+ if(route->channels[i] == 0)
+ break;
+ if(channels_interfere(babel_ifp->channel, route->channels[i]))
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+update_feasible(struct source *src,
+ unsigned short seqno, unsigned short refmetric)
+{
+ if(src == NULL)
+ return 1;
+
+ if(src->time < babel_now.tv_sec - SOURCE_GC_TIME)
+ /* Never mind what is probably stale data */
+ return 1;
+
+ if(refmetric >= INFINITY)
+ /* Retractions are always feasible */
+ return 1;
+
+ return (seqno_compare(seqno, src->seqno) > 0 ||
+ (src->seqno == seqno && refmetric < src->metric));
+}
+
+void
+change_smoothing_half_life(int half_life)
+{
+ if(half_life <= 0) {
+ smoothing_half_life = 0;
+ two_to_the_one_over_hl = 0;
+ return;
+ }
+
+ smoothing_half_life = half_life;
+ switch(smoothing_half_life) {
+ case 1: two_to_the_one_over_hl = 131072; break;
+ case 2: two_to_the_one_over_hl = 92682; break;
+ case 3: two_to_the_one_over_hl = 82570; break;
+ case 4: two_to_the_one_over_hl = 77935; break;
+ default:
+ /* 2^(1/x) is 1 + log(2)/x + O(1/x^2) at infinity. */
+ two_to_the_one_over_hl = 0x10000 + 45426 / half_life;
+ }
+}
+
+/* Update the smoothed metric, return the new value. */
+int
+route_smoothed_metric(struct babel_route *route)
+{
+ int metric = route_metric(route);
+
+ if(smoothing_half_life <= 0 || /* no smoothing */
+ metric >= INFINITY || /* route retracted */
+ route->smoothed_metric_time > babel_now.tv_sec || /* clock stepped */
+ route->smoothed_metric == metric) { /* already converged */
+ route->smoothed_metric = metric;
+ route->smoothed_metric_time = babel_now.tv_sec;
+ } else {
+ int diff;
+ /* We randomise the computation, to minimise global synchronisation
+ and hence oscillations. */
+ while(route->smoothed_metric_time <=
+ babel_now.tv_sec - smoothing_half_life) {
+ diff = metric - route->smoothed_metric;
+ route->smoothed_metric += roughly(diff) / 2;
+ route->smoothed_metric_time += smoothing_half_life;
+ }
+ while(route->smoothed_metric_time < babel_now.tv_sec) {
+ diff = metric - route->smoothed_metric;
+ route->smoothed_metric +=
+ roughly(diff) * (two_to_the_one_over_hl - 0x10000) / 0x10000;
+ route->smoothed_metric_time++;
+ }
+
+ diff = metric - route->smoothed_metric;
+ if(diff > -4 && diff < 4)
+ route->smoothed_metric = metric;
+ }
+
+ /* change_route_metric relies on this */
+ assert(route->smoothed_metric_time == babel_now.tv_sec);
+ return route->smoothed_metric;
+}
+
+static int
+route_acceptable(struct babel_route *route, int feasible,
+ struct neighbour *exclude)
+{
+ if(route_expired(route))
+ return 0;
+ if(feasible && !route_feasible(route))
+ return 0;
+ if(exclude && route->neigh == exclude)
+ return 0;
+ return 1;
+}
+
+/* Find the best route according to the weak ordering. Any
+ linearisation of the strong ordering (see consider_route) will do,
+ we use sm <= sm'. We could probably use a lexical ordering, but
+ that's probably overkill. */
+
+struct babel_route *
+find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
+ struct neighbour *exclude)
+{
+ struct babel_route *route = NULL, *r = NULL;
+ int i = find_route_slot(prefix, plen, NULL);
+
+ if(i < 0)
+ return NULL;
+
+ route = routes[i];
+ while(route && !route_acceptable(route, feasible, exclude))
+ route = route->next;
+
+ if(!route)
+ return NULL;
+
+ r = route->next;
+ while(r) {
+ if(route_acceptable(r, feasible, exclude) &&
+ (route_smoothed_metric(r) < route_smoothed_metric(route)))
+ route = r;
+ r = r->next;
+ }
+
+ return route;
+}
+
+void
+update_route_metric(struct babel_route *route)
+{
+ int oldmetric = route_metric(route);
+ int old_smoothed_metric = route_smoothed_metric(route);
+
+ if(route_expired(route)) {
+ if(route->refmetric < INFINITY) {
+ route->seqno = seqno_plus(route->src->seqno, 1);
+ retract_route(route);
+ if(oldmetric < INFINITY)
+ route_changed(route, route->src, oldmetric);
+ }
+ } else {
+ struct neighbour *neigh = route->neigh;
+ int add_metric = input_filter(route->src->id,
+ route->src->prefix, route->src->plen,
+ neigh->address,
+ neigh->ifp->ifindex);
+ change_route_metric(route, route->refmetric,
+ neighbour_cost(route->neigh), add_metric);
+ if(route_metric(route) != oldmetric ||
+ route_smoothed_metric(route) != old_smoothed_metric)
+ route_changed(route, route->src, oldmetric);
+ }
+}
+
+/* Called whenever a neighbour's cost changes, to update the metric of
+ all routes through that neighbour. */
+void
+update_neighbour_metric(struct neighbour *neigh, int changed)
+{
+
+ if(changed) {
+ int i;
+
+ for(i = 0; i < route_slots; i++) {
+ struct babel_route *r = routes[i];
+ while(r) {
+ if(r->neigh == neigh)
+ update_route_metric(r);
+ r = r->next;
+ }
+ }
+ }
+}
+
+void
+update_interface_metric(struct interface *ifp)
+{
+ int i;
+
+ for(i = 0; i < route_slots; i++) {
+ struct babel_route *r = routes[i];
+ while(r) {
+ if(r->neigh->ifp == ifp)
+ update_route_metric(r);
+ r = r->next;
+ }
+ }
+}
+
+/* This is called whenever we receive an update. */
+struct babel_route *
+update_route(const unsigned char *router_id,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned short refmetric,
+ unsigned short interval,
+ struct neighbour *neigh, const unsigned char *nexthop,
+ const unsigned char *channels, int channels_len)
+{
+ struct babel_route *route;
+ struct source *src;
+ int metric, feasible;
+ int add_metric;
+ int hold_time = MAX((4 * interval) / 100 + interval / 50, 15);
+
+ if(memcmp(router_id, myid, 8) == 0)
+ return NULL;
+
+ if(martian_prefix(prefix, plen)) {
+ flog_err(EC_BABEL_ROUTE, "Rejecting martian route to %s through %s.",
+ format_prefix(prefix, plen), format_address(nexthop));
+ return NULL;
+ }
+
+ add_metric = input_filter(router_id, prefix, plen,
+ neigh->address, neigh->ifp->ifindex);
+ if(add_metric >= INFINITY)
+ return NULL;
+
+ route = find_route(prefix, plen, neigh, nexthop);
+
+ if(route && memcmp(route->src->id, router_id, 8) == 0)
+ /* Avoid scanning the source table. */
+ src = route->src;
+ else
+ src = find_source(router_id, prefix, plen, 1, seqno);
+
+ if(src == NULL)
+ return NULL;
+
+ feasible = update_feasible(src, seqno, refmetric);
+ metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY);
+
+ if(route) {
+ struct source *oldsrc;
+ unsigned short oldmetric;
+ int lost = 0;
+
+ oldsrc = route->src;
+ oldmetric = route_metric(route);
+
+ /* If a successor switches sources, we must accept his update even
+ if it makes a route unfeasible in order to break any routing loops
+ in a timely manner. If the source remains the same, we ignore
+ the update. */
+ if(!feasible && route->installed) {
+ debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s (%s %d %d -> %s %d %d).",
+ format_prefix(src->prefix, src->plen),
+ format_eui64(route->src->id),
+ route->seqno, route->refmetric,
+ format_eui64(src->id), seqno, refmetric);
+ if(src != route->src) {
+ uninstall_route(route);
+ lost = 1;
+ }
+ }
+
+ route->src = retain_source(src);
+ if((feasible || keep_unfeasible) && refmetric < INFINITY)
+ route->time = babel_now.tv_sec;
+ route->seqno = seqno;
+
+ memset(&route->channels, 0, sizeof(route->channels));
+ if(channels_len > 0)
+ memcpy(&route->channels, channels,
+ MIN(channels_len, DIVERSITY_HOPS));
+
+ change_route_metric(route,
+ refmetric, neighbour_cost(neigh), add_metric);
+ route->hold_time = hold_time;
+
+ route_changed(route, oldsrc, oldmetric);
+ if(lost)
+ route_lost(oldsrc, oldmetric);
+
+ if(!feasible)
+ send_unfeasible_request(neigh, route->installed && route_old(route),
+ seqno, metric, src);
+ release_source(oldsrc);
+ } else {
+ struct babel_route *new_route;
+
+ if(refmetric >= INFINITY)
+ /* Somebody's retracting a route we never saw. */
+ return NULL;
+ if(!feasible) {
+ send_unfeasible_request(neigh, 0, seqno, metric, src);
+ if(!keep_unfeasible)
+ return NULL;
+ }
+
+ route = malloc(sizeof(struct babel_route));
+ if(route == NULL) {
+ perror("malloc(route)");
+ return NULL;
+ }
+
+ route->src = retain_source(src);
+ route->refmetric = refmetric;
+ route->cost = neighbour_cost(neigh);
+ route->add_metric = add_metric;
+ route->seqno = seqno;
+ route->neigh = neigh;
+ memcpy(route->nexthop, nexthop, 16);
+ route->time = babel_now.tv_sec;
+ route->hold_time = hold_time;
+ route->smoothed_metric = MAX(route_metric(route), INFINITY / 2);
+ route->smoothed_metric_time = babel_now.tv_sec;
+ route->installed = 0;
+ memset(&route->channels, 0, sizeof(route->channels));
+ if(channels_len > 0)
+ memcpy(&route->channels, channels,
+ MIN(channels_len, DIVERSITY_HOPS));
+ route->next = NULL;
+ new_route = insert_route(route);
+ if(new_route == NULL) {
+ flog_err(EC_BABEL_ROUTE, "Couldn't insert route.");
+ free(route);
+ return NULL;
+ }
+ consider_route(route);
+ }
+ return route;
+}
+
+/* We just received an unfeasible update. If it's any good, send
+ a request for a new seqno. */
+void
+send_unfeasible_request(struct neighbour *neigh, int force,
+ unsigned short seqno, unsigned short metric,
+ struct source *src)
+{
+ struct babel_route *route = find_installed_route(src->prefix, src->plen);
+
+ if(seqno_minus(src->seqno, seqno) > 100) {
+ /* Probably a source that lost its seqno. Let it time-out. */
+ return;
+ }
+
+ if(force || !route || route_metric(route) >= metric + 512) {
+ send_unicast_multihop_request(neigh, src->prefix, src->plen,
+ src->metric >= INFINITY ?
+ src->seqno :
+ seqno_plus(src->seqno, 1),
+ src->id, 127);
+ }
+}
+
+/* This takes a feasible route and decides whether to install it.
+ This uses the strong ordering, which is defined by sm <= sm' AND
+ m <= m'. This ordering is not total, which is what causes
+ hysteresis. */
+
+static void
+consider_route(struct babel_route *route)
+{
+ struct babel_route *installed;
+ struct xroute *xroute;
+
+ if(route->installed)
+ return;
+
+ if(!route_feasible(route))
+ return;
+
+ xroute = find_xroute(route->src->prefix, route->src->plen);
+ if(xroute)
+ return;
+
+ installed = find_installed_route(route->src->prefix, route->src->plen);
+
+ if(installed == NULL)
+ goto install;
+
+ if(route_metric(route) >= INFINITY)
+ return;
+
+ if(route_metric(installed) >= INFINITY)
+ goto install;
+
+ if(route_metric(installed) >= route_metric(route) &&
+ route_smoothed_metric(installed) > route_smoothed_metric(route))
+ goto install;
+
+ return;
+
+ install:
+ switch_routes(installed, route);
+ if(installed && route->installed)
+ send_triggered_update(route, installed->src, route_metric(installed));
+ else
+ send_update(NULL, 1, route->src->prefix, route->src->plen);
+ return;
+}
+
+void
+retract_neighbour_routes(struct neighbour *neigh)
+{
+ int i;
+
+ for(i = 0; i < route_slots; i++) {
+ struct babel_route *r = routes[i];
+ while(r) {
+ if(r->neigh == neigh) {
+ if(r->refmetric != INFINITY) {
+ unsigned short oldmetric = route_metric(r);
+ retract_route(r);
+ if(oldmetric != INFINITY)
+ route_changed(r, r->src, oldmetric);
+ }
+ }
+ r = r->next;
+ }
+ }
+}
+
+void
+send_triggered_update(struct babel_route *route, struct source *oldsrc,
+ unsigned oldmetric)
+{
+ unsigned newmetric, diff;
+ /* 1 means send speedily, 2 means resend */
+ int urgent;
+
+ if(!route->installed)
+ return;
+
+ newmetric = route_metric(route);
+ diff =
+ newmetric >= oldmetric ? newmetric - oldmetric : oldmetric - newmetric;
+
+ if(route->src != oldsrc || (oldmetric < INFINITY && newmetric >= INFINITY))
+ /* Switching sources can cause transient routing loops.
+ Retractions can cause blackholes. */
+ urgent = 2;
+ else if(newmetric > oldmetric && oldmetric < 6 * 256 && diff >= 512)
+ /* Route getting significantly worse */
+ urgent = 1;
+ else if(unsatisfied_request(route->src->prefix, route->src->plen,
+ route->seqno, route->src->id))
+ /* Make sure that requests are satisfied speedily */
+ urgent = 1;
+ else if(oldmetric >= INFINITY && newmetric < INFINITY)
+ /* New route */
+ urgent = 0;
+ else if(newmetric < oldmetric && diff < 1024)
+ /* Route getting better. This may be a transient fluctuation, so
+ don't advertise it to avoid making routes unfeasible later on. */
+ return;
+ else if(diff < 384)
+ /* Don't fret about trivialities */
+ return;
+ else
+ urgent = 0;
+
+ if(urgent >= 2)
+ send_update_resend(NULL, route->src->prefix, route->src->plen);
+ else
+ send_update(NULL, urgent, route->src->prefix, route->src->plen);
+
+ if(oldmetric < INFINITY) {
+ if(newmetric >= oldmetric + 512) {
+ send_request_resend(NULL, route->src->prefix, route->src->plen,
+ route->src->metric >= INFINITY ?
+ route->src->seqno :
+ seqno_plus(route->src->seqno, 1),
+ route->src->id);
+ } else if(newmetric >= oldmetric + 288) {
+ send_request(NULL, route->src->prefix, route->src->plen);
+ }
+ }
+}
+
+/* A route has just changed. Decide whether to switch to a different route or
+ send an update. */
+void
+route_changed(struct babel_route *route,
+ struct source *oldsrc, unsigned short oldmetric)
+{
+ if(route->installed) {
+ struct babel_route *better_route;
+ /* Do this unconditionally -- microoptimisation is not worth it. */
+ better_route =
+ find_best_route(route->src->prefix, route->src->plen, 1, NULL);
+ if(better_route && route_metric(better_route) < route_metric(route))
+ consider_route(better_route);
+ }
+
+ if(route->installed) {
+ /* We didn't change routes after all. */
+ send_triggered_update(route, oldsrc, oldmetric);
+ } else {
+ /* Reconsider routes even when their metric didn't decrease,
+ they may not have been feasible before. */
+ consider_route(route);
+ }
+}
+
+/* We just lost the installed route to a given destination. */
+void
+route_lost(struct source *src, unsigned oldmetric)
+{
+ struct babel_route *new_route;
+ new_route = find_best_route(src->prefix, src->plen, 1, NULL);
+ if(new_route) {
+ consider_route(new_route);
+ } else if(oldmetric < INFINITY) {
+ /* Avoid creating a blackhole. */
+ send_update_resend(NULL, src->prefix, src->plen);
+ /* If the route was usable enough, try to get an alternate one.
+ If it was not, we could be dealing with oscillations around
+ the value of INFINITY. */
+ if(oldmetric <= INFINITY / 2)
+ send_request_resend(NULL, src->prefix, src->plen,
+ src->metric >= INFINITY ?
+ src->seqno : seqno_plus(src->seqno, 1),
+ src->id);
+ }
+}
+
+/* This is called periodically to flush old routes. It will also send
+ requests for routes that are about to expire. */
+void
+expire_routes(void)
+{
+ struct babel_route *r;
+ int i;
+
+ debugf(BABEL_DEBUG_COMMON,"Expiring old routes.");
+
+ i = 0;
+ while(i < route_slots) {
+ r = routes[i];
+ while(r) {
+ /* Protect against clock being stepped. */
+ if(r->time > babel_now.tv_sec || route_old(r)) {
+ flush_route(r);
+ goto again;
+ }
+
+ update_route_metric(r);
+
+ if(r->installed && r->refmetric < INFINITY) {
+ if(route_old(r))
+ /* Route about to expire, send a request. */
+ send_unicast_request(r->neigh,
+ r->src->prefix, r->src->plen);
+ }
+ r = r->next;
+ }
+ i++;
+ again:
+ ;
+ }
+}
diff --git a/babeld/route.h b/babeld/route.h
new file mode 100644
index 0000000..89427b8
--- /dev/null
+++ b/babeld/route.h
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_ROUTE_H
+#define BABEL_ROUTE_H
+
+#include "babel_interface.h"
+#include "source.h"
+
+enum babel_diversity {
+ DIVERSITY_NONE,
+ DIVERSITY_INTERFACE_1,
+ DIVERSITY_CHANNEL_1,
+ DIVERSITY_CHANNEL,
+};
+
+#define DIVERSITY_HOPS 8
+
+struct babel_route {
+ struct source *src;
+ unsigned short refmetric;
+ unsigned short cost;
+ unsigned short add_metric;
+ unsigned short seqno;
+ struct neighbour *neigh;
+ unsigned char nexthop[16];
+ time_t time;
+ unsigned short hold_time; /* in seconds */
+ unsigned short smoothed_metric; /* for route selection */
+ time_t smoothed_metric_time;
+ short installed;
+ unsigned char channels[DIVERSITY_HOPS];
+ struct babel_route *next;
+};
+
+struct route_stream;
+
+extern struct babel_route **routes;
+extern int kernel_metric;
+extern enum babel_diversity diversity_kind;
+extern int diversity_factor;
+extern int keep_unfeasible;
+extern int smoothing_half_life;
+
+static inline int
+route_metric(const struct babel_route *route)
+{
+ int m = (int)route->refmetric + route->cost + route->add_metric;
+ return MIN(m, INFINITY);
+}
+
+static inline int
+route_metric_noninterfering(const struct babel_route *route)
+{
+ int m =
+ (int)route->refmetric +
+ (diversity_factor * route->cost + 128) / 256 +
+ route->add_metric;
+ m = MAX(m, route->refmetric + 1);
+ return MIN(m, INFINITY);
+}
+
+struct babel_route *find_route(const unsigned char *prefix, unsigned char plen,
+ struct neighbour *neigh, const unsigned char *nexthop);
+struct babel_route *find_installed_route(const unsigned char *prefix,
+ unsigned char plen);
+int installed_routes_estimate(void);
+void flush_route(struct babel_route *route);
+void flush_all_routes(void);
+void flush_neighbour_routes(struct neighbour *neigh);
+void flush_interface_routes(struct interface *ifp, int v4only);
+struct route_stream *route_stream(int installed);
+struct babel_route *route_stream_next(struct route_stream *stream);
+void route_stream_done(struct route_stream *stream);
+void install_route(struct babel_route *route);
+void uninstall_route(struct babel_route *route);
+int route_feasible(struct babel_route *route);
+int route_old(struct babel_route *route);
+int route_expired(struct babel_route *route);
+int route_interferes(struct babel_route *route, struct interface *ifp);
+int update_feasible(struct source *src,
+ unsigned short seqno, unsigned short refmetric);
+void change_smoothing_half_life(int half_life);
+int route_smoothed_metric(struct babel_route *route);
+struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen,
+ int feasible, struct neighbour *exclude);
+struct babel_route *install_best_route(const unsigned char prefix[16],
+ unsigned char plen);
+void update_neighbour_metric(struct neighbour *neigh, int change);
+void update_interface_metric(struct interface *ifp);
+void update_route_metric(struct babel_route *route);
+struct babel_route *update_route(const unsigned char *id,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned short refmetric,
+ unsigned short interval, struct neighbour *neigh,
+ const unsigned char *nexthop,
+ const unsigned char *channels, int channels_len);
+void retract_neighbour_routes(struct neighbour *neigh);
+void send_unfeasible_request(struct neighbour *neigh, int force,
+ unsigned short seqno, unsigned short metric,
+ struct source *src);
+void send_triggered_update(struct babel_route *route,
+ struct source *oldsrc, unsigned oldmetric);
+void route_changed(struct babel_route *route,
+ struct source *oldsrc, unsigned short oldmetric);
+void route_lost(struct source *src, unsigned oldmetric);
+void expire_routes(void);
+
+#endif
diff --git a/babeld/source.c b/babeld/source.c
new file mode 100644
index 0000000..049fa32
--- /dev/null
+++ b/babeld/source.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "babel_main.h"
+#include "babeld.h"
+#include "util.h"
+#include "source.h"
+#include "babel_interface.h"
+#include "route.h"
+#include "babel_errors.h"
+
+struct source *srcs = NULL;
+
+struct source*
+find_source(const unsigned char *id, const unsigned char *p, unsigned char plen,
+ int create, unsigned short seqno)
+{
+ struct source *src;
+
+ for(src = srcs; src; src = src->next) {
+ /* This should really be a hash table. For now, check the
+ last byte first. */
+ if(src->id[7] != id[7])
+ continue;
+ if(memcmp(src->id, id, 8) != 0)
+ continue;
+ if(src->plen != plen)
+ continue;
+ if(memcmp(src->prefix, p, 16) == 0)
+ return src;
+ }
+
+ if(!create)
+ return NULL;
+
+ src = malloc(sizeof(struct source));
+ if(src == NULL) {
+ flog_err(EC_BABEL_MEMORY, "malloc(source): %s", safe_strerror(errno));
+ return NULL;
+ }
+
+ memcpy(src->id, id, 8);
+ memcpy(src->prefix, p, 16);
+ src->plen = plen;
+ src->seqno = seqno;
+ src->metric = INFINITY;
+ src->time = babel_now.tv_sec;
+ src->route_count = 0;
+ src->next = srcs;
+ srcs = src;
+ return src;
+}
+
+struct source *
+retain_source(struct source *src)
+{
+ assert(src->route_count < 0xffff);
+ src->route_count++;
+ return src;
+}
+
+void
+release_source(struct source *src)
+{
+ assert(src->route_count > 0);
+ src->route_count--;
+}
+
+int
+flush_source(struct source *src)
+{
+ if(src->route_count > 0)
+ /* The source is in use by a route. */
+ return 0;
+
+ if(srcs == src) {
+ srcs = src->next;
+ } else {
+ struct source *previous = srcs;
+ while(previous->next != src)
+ previous = previous->next;
+ previous->next = src->next;
+ }
+
+ free(src);
+ return 1;
+}
+
+void
+update_source(struct source *src,
+ unsigned short seqno, unsigned short metric)
+{
+ if(metric >= INFINITY)
+ return;
+
+ /* If a source is expired, pretend that it doesn't exist and update
+ it unconditionally. This makes ensures that old data will
+ eventually be overridden, and prevents us from getting stuck if
+ a router loses its sequence number. */
+ if(src->time < babel_now.tv_sec - SOURCE_GC_TIME ||
+ seqno_compare(src->seqno, seqno) < 0 ||
+ (src->seqno == seqno && src->metric > metric)) {
+ src->seqno = seqno;
+ src->metric = metric;
+ }
+ src->time = babel_now.tv_sec;
+}
+
+void
+expire_sources(void)
+{
+ struct source *src;
+
+ src = srcs;
+ while(src) {
+ if(src->time > babel_now.tv_sec)
+ /* clock stepped */
+ src->time = babel_now.tv_sec;
+ if(src->time < babel_now.tv_sec - SOURCE_GC_TIME) {
+ struct source *old = src;
+ src = src->next;
+ flush_source(old);
+ continue;
+ }
+ src = src->next;
+ }
+}
+
+void
+check_sources_released(void)
+{
+ struct source *src;
+
+ for(src = srcs; src; src = src->next) {
+ if(src->route_count != 0)
+ fprintf(stderr, "Warning: source %s %s has refcount %d.\n",
+ format_eui64(src->id),
+ format_prefix(src->prefix, src->plen),
+ (int)src->route_count);
+ }
+}
diff --git a/babeld/source.h b/babeld/source.h
new file mode 100644
index 0000000..5b37ca9
--- /dev/null
+++ b/babeld/source.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+*/
+
+#ifndef BABEL_SOURCE_H
+#define BABEL_SOURCE_H
+
+#define SOURCE_GC_TIME 200
+
+struct source {
+ struct source *next;
+ unsigned char id[8];
+ unsigned char prefix[16];
+ unsigned char plen;
+ unsigned short seqno;
+ unsigned short metric;
+ unsigned short route_count;
+ time_t time;
+};
+
+struct source *find_source(const unsigned char *id,
+ const unsigned char *p,
+ unsigned char plen,
+ int create, unsigned short seqno);
+struct source *retain_source(struct source *src);
+void release_source(struct source *src);
+int flush_source(struct source *src);
+void update_source(struct source *src,
+ unsigned short seqno, unsigned short metric);
+void expire_sources(void);
+void check_sources_released(void);
+
+#endif
diff --git a/babeld/subdir.am b/babeld/subdir.am
new file mode 100644
index 0000000..d2d4252
--- /dev/null
+++ b/babeld/subdir.am
@@ -0,0 +1,50 @@
+#
+# babeld
+#
+
+if BABELD
+sbin_PROGRAMS += babeld/babeld
+vtysh_daemons += babeld
+endif
+
+babeld_babeld_SOURCES = \
+ babeld/babel_errors.c \
+ babeld/babel_filter.c \
+ babeld/babel_interface.c \
+ babeld/babel_main.c \
+ babeld/babel_zebra.c \
+ babeld/babeld.c \
+ babeld/kernel.c \
+ babeld/message.c \
+ babeld/neighbour.c \
+ babeld/net.c \
+ babeld/resend.c \
+ babeld/route.c \
+ babeld/source.c \
+ babeld/util.c \
+ babeld/xroute.c \
+ # end
+
+noinst_HEADERS += \
+ babeld/babel_errors.h \
+ babeld/babel_filter.h \
+ babeld/babel_interface.h \
+ babeld/babel_main.h \
+ babeld/babel_zebra.h \
+ babeld/babeld.h \
+ babeld/kernel.h \
+ babeld/message.h \
+ babeld/neighbour.h \
+ babeld/net.h \
+ babeld/resend.h \
+ babeld/route.h \
+ babeld/source.h \
+ babeld/util.h \
+ babeld/xroute.h \
+ # end
+
+clippy_scan += \
+ babeld/babel_interface.c \
+ babeld/babeld.c
+
+babeld_babeld_LDADD = lib/libfrr.la $(LIBCAP)
diff --git a/babeld/util.c b/babeld/util.c
new file mode 100644
index 0000000..4facdab
--- /dev/null
+++ b/babeld/util.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "lib/network.h"
+
+#include "babel_main.h"
+#include "babeld.h"
+#include "util.h"
+
+int
+roughly(int value)
+{
+ if(value < 0)
+ return -roughly(-value);
+ else if(value <= 1)
+ return value;
+ else
+ return value * 3 / 4 + frr_weak_random() % (value / 2);
+}
+
+/* d = s1 - s2 */
+void
+timeval_minus(struct timeval *d,
+ const struct timeval *s1, const struct timeval *s2)
+{
+ if(s1->tv_usec >= s2->tv_usec) {
+ d->tv_usec = s1->tv_usec - s2->tv_usec;
+ d->tv_sec = s1->tv_sec - s2->tv_sec;
+ } else {
+ d->tv_usec = s1->tv_usec + 1000000 - s2->tv_usec;
+ d->tv_sec = s1->tv_sec - s2->tv_sec - 1;
+ }
+}
+
+unsigned
+timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
+{
+ if(s1->tv_sec < s2->tv_sec)
+ return 0;
+
+ /* Avoid overflow. */
+ if(s1->tv_sec - s2->tv_sec > 2000000)
+ return 2000000000;
+
+ if(s1->tv_sec > s2->tv_sec)
+ return
+ (unsigned)((unsigned)(s1->tv_sec - s2->tv_sec) * 1000 +
+ ((int)s1->tv_usec - s2->tv_usec) / 1000);
+
+ if(s1->tv_usec <= s2->tv_usec)
+ return 0;
+
+ return (unsigned)(s1->tv_usec - s2->tv_usec) / 1000u;
+}
+
+/* d = s + msecs */
+void
+timeval_add_msec(struct timeval *d, const struct timeval *s, int msecs)
+{
+ int usecs;
+ d->tv_sec = s->tv_sec + msecs / 1000;
+ usecs = s->tv_usec + (msecs % 1000) * 1000;
+ if(usecs < 1000000) {
+ d->tv_usec = usecs;
+ } else {
+ d->tv_usec = usecs - 1000000;
+ d->tv_sec++;
+ }
+}
+
+void
+set_timeout(struct timeval *timeout, int msecs)
+{
+ timeval_add_msec(timeout, &babel_now, roughly(msecs));
+}
+
+/* returns <0 if "s1" < "s2", etc. */
+int
+timeval_compare(const struct timeval *s1, const struct timeval *s2)
+{
+ if(s1->tv_sec < s2->tv_sec)
+ return -1;
+ else if(s1->tv_sec > s2->tv_sec)
+ return 1;
+ else if(s1->tv_usec < s2->tv_usec)
+ return -1;
+ else if(s1->tv_usec > s2->tv_usec)
+ return 1;
+ else
+ return 0;
+}
+
+/* set d at min(d, s) */
+/* {0, 0} represents infinity */
+void
+timeval_min(struct timeval *d, const struct timeval *s)
+{
+ if(s->tv_sec == 0)
+ return;
+
+ if(d->tv_sec == 0 || timeval_compare(d, s) > 0) {
+ *d = *s;
+ }
+}
+
+/* set d to min(d, x) with x in [secs, secs+1] */
+void
+timeval_min_sec(struct timeval *d, time_t secs)
+{
+ if(d->tv_sec == 0 || d->tv_sec > secs) {
+ d->tv_sec = secs;
+ d->tv_usec = frr_weak_random() % 1000000;
+ }
+}
+
+/* parse a float value in second and return the corresponding mili-seconds.
+ For example:
+ parse_msec("12.342345") returns 12342 */
+int
+parse_msec(const char *string)
+{
+ unsigned int in, fl;
+ int i, j;
+
+ in = fl = 0;
+ i = 0;
+ while(string[i] == ' ' || string[i] == '\t')
+ i++;
+ while(string[i] >= '0' && string[i] <= '9') {
+ in = in * 10 + string[i] - '0';
+ i++;
+ }
+ if(string[i] == '.') {
+ i++;
+ j = 0;
+ while(string[i] >= '0' && string[i] <= '9') {
+ fl = fl * 10 + string[i] - '0';
+ i++;
+ j++;
+ }
+
+ while(j > 3) {
+ fl /= 10;
+ j--;
+ }
+ while(j < 3) {
+ fl *= 10;
+ j++;
+ }
+ }
+
+ while(string[i] == ' ' || string[i] == '\t')
+ i++;
+
+ if(string[i] == '\0')
+ return in * 1000 + fl;
+
+ return -1;
+}
+
+/* There's no good name for a positive int in C, call it nat. */
+int
+parse_nat(const char *string)
+{
+ long l;
+ char *end;
+
+ l = strtol(string, &end, 0);
+
+ while(*end == ' ' || *end == '\t')
+ end++;
+ if(*end != '\0')
+ return -1;
+
+ if(l < 0 || l > INT_MAX)
+ return -1;
+
+ return (int)l;
+}
+
+unsigned char *
+mask_prefix(unsigned char *restrict ret,
+ const unsigned char *restrict prefix, unsigned char plen)
+{
+ if (plen >= IPV6_MAX_BITLEN) {
+ memcpy(ret, prefix, IPV6_MAX_BYTELEN);
+ return ret;
+ }
+
+ memset(ret, 0, 16);
+ memcpy(ret, prefix, plen / 8);
+ if(plen % 8 != 0)
+ ret[plen / 8] =
+ (prefix[plen / 8] & ((0xFF << (8 - (plen % 8))) & 0xFF));
+ return ret;
+}
+
+const unsigned char v4prefix[16] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
+
+static const unsigned char llprefix[16] =
+ {0xFE, 0x80};
+
+const char *
+format_address(const unsigned char *address)
+{
+ static char buf[4][INET6_ADDRSTRLEN];
+ static int i = 0;
+ i = (i + 1) % 4;
+ if(v4mapped(address))
+ inet_ntop(AF_INET, address + 12, buf[i], INET6_ADDRSTRLEN);
+ else
+ inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN);
+ return buf[i];
+}
+
+const char *
+format_prefix(const unsigned char *prefix, unsigned char plen)
+{
+ static char buf[4][INET6_ADDRSTRLEN + 4];
+ static int i = 0;
+ int n;
+ i = (i + 1) % 4;
+ if(plen >= 96 && v4mapped(prefix)) {
+ inet_ntop(AF_INET, prefix + 12, buf[i], INET6_ADDRSTRLEN);
+ n = strlen(buf[i]);
+ snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen - 96);
+ } else {
+ inet_ntop(AF_INET6, prefix, buf[i], INET6_ADDRSTRLEN);
+ n = strlen(buf[i]);
+ snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen);
+ }
+ return buf[i];
+}
+
+const char *
+format_eui64(const unsigned char *eui)
+{
+ static char buf[4][28];
+ static int i = 0;
+ i = (i + 1) % 4;
+ snprintf(buf[i], 28, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ eui[0], eui[1], eui[2], eui[3],
+ eui[4], eui[5], eui[6], eui[7]);
+ return buf[i];
+}
+
+const char *
+format_thousands(unsigned int value)
+{
+ static char buf[4][15];
+ static int i = 0;
+ i = (i + 1) % 4;
+ snprintf(buf[i], 15, "%u.%.3u", value / 1000, value % 1000);
+ return buf[i];
+}
+
+int
+parse_address(const char *address, unsigned char *addr_r, int *af_r)
+{
+ struct in_addr ina;
+ struct in6_addr ina6;
+ int rc;
+
+ rc = inet_pton(AF_INET, address, &ina);
+ if(rc > 0) {
+ v4tov6(addr_r, (const unsigned char *)&ina);
+ if(af_r) *af_r = AF_INET;
+ return 0;
+ }
+
+ rc = inet_pton(AF_INET6, address, &ina6);
+ if(rc > 0) {
+ memcpy(addr_r, &ina6, IPV6_MAX_BYTELEN);
+ if (af_r)
+ *af_r = AF_INET6;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+parse_eui64(const char *eui, unsigned char *eui_r)
+{
+ int n;
+ n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3],
+ &eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
+ if(n == 8)
+ return 0;
+
+ n = sscanf(eui, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx",
+ &eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3],
+ &eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
+ if(n == 8)
+ return 0;
+
+ n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &eui_r[0], &eui_r[1], &eui_r[2],
+ &eui_r[5], &eui_r[6], &eui_r[7]);
+ if(n == 6) {
+ eui_r[3] = 0xFF;
+ eui_r[4] = 0xFE;
+ return 0;
+ }
+ return -1;
+}
+
+int
+wait_for_fd(int direction, int fd, int msecs)
+{
+ fd_set fds;
+ int rc;
+ struct timeval tv;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ if(direction)
+ rc = select(fd + 1, NULL, &fds, NULL, &tv);
+ else
+ rc = select(fd + 1, &fds, NULL, NULL, &tv);
+
+ return rc;
+}
+
+int
+martian_prefix(const unsigned char *prefix, int plen)
+{
+ return
+ (plen >= 8 && prefix[0] == 0xFF) ||
+ (plen >= 10 && prefix[0] == 0xFE && (prefix[1] & 0xC0) == 0x80) ||
+ (plen >= 128 && memcmp(prefix, zeroes, 15) == 0 &&
+ (prefix[15] == 0 || prefix[15] == 1)) ||
+ (plen >= 96 && v4mapped(prefix) &&
+ ((plen >= 104 && (prefix[12] == 127 || prefix[12] == 0)) ||
+ (plen >= 100 && (prefix[12] & 0xE0) == 0xE0)));
+}
+
+int
+linklocal(const unsigned char *address)
+{
+ return memcmp(address, llprefix, 8) == 0;
+}
+
+int
+v4mapped(const unsigned char *address)
+{
+ return memcmp(address, v4prefix, 12) == 0;
+}
+
+void
+v4tov6(unsigned char *dst, const unsigned char *src)
+{
+ memcpy(dst, v4prefix, 12);
+ memcpy(dst + 12, src, 4);
+}
+
+void
+inaddr_to_uchar(unsigned char *dest, const struct in_addr *src)
+{
+ v4tov6(dest, (const unsigned char *)src);
+ assert(v4mapped(dest));
+}
+
+void
+uchar_to_inaddr(struct in_addr *dest, const unsigned char *src)
+{
+ assert(v4mapped(src));
+ memcpy(dest, src + 12, 4);
+}
+
+void
+in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src)
+{
+ memcpy(dest, src, IPV6_MAX_BYTELEN);
+}
+
+void
+uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src)
+{
+ memcpy(dest, src, IPV6_MAX_BYTELEN);
+}
+
+int
+daemonise(void)
+{
+ int rc;
+
+ fflush(stdout);
+ fflush(stderr);
+
+ rc = fork();
+ if(rc < 0)
+ return -1;
+
+ if(rc > 0)
+ exit(0);
+
+ rc = setsid();
+ if(rc < 0)
+ return -1;
+
+ return 1;
+}
diff --git a/babeld/util.h b/babeld/util.h
new file mode 100644
index 0000000..ddc6a70
--- /dev/null
+++ b/babeld/util.h
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_UTIL_H
+#define BABEL_UTIL_H
+
+#include "babeld.h"
+#include "babel_main.h"
+#include "log.h"
+#include "memory.h"
+
+DECLARE_MGROUP(BABELD);
+
+#if defined(i386) || defined(__mc68020__) || defined(__x86_64__)
+#define DO_NTOHS(_d, _s) do{ _d = ntohs(*(const unsigned short*)(_s)); }while(0)
+#define DO_NTOHL(_d, _s) do{ _d = ntohl(*(const unsigned*)(_s)); } while(0)
+#define DO_HTONS(_d, _s) do{ *(unsigned short*)(_d) = htons(_s); } while(0)
+#define DO_HTONL(_d, _s) do{ *(unsigned*)(_d) = htonl(_s); } while(0)
+/* Some versions of gcc seem to be buggy, and ignore the packed attribute.
+ Disable this code until the issue is clarified. */
+/* #elif defined __GNUC__*/
+#else
+#define DO_NTOHS(_d, _s) \
+ do { short _dd; \
+ memcpy(&(_dd), (_s), 2); \
+ _d = ntohs(_dd); } while(0)
+#define DO_NTOHL(_d, _s) \
+ do { int _dd; \
+ memcpy(&(_dd), (_s), 4); \
+ _d = ntohl(_dd); } while(0)
+#define DO_HTONS(_d, _s) \
+ do { unsigned short _dd; \
+ _dd = htons(_s); \
+ memcpy((_d), &(_dd), 2); } while(0)
+#define DO_HTONL(_d, _s) \
+ do { unsigned _dd; \
+ _dd = htonl(_s); \
+ memcpy((_d), &(_dd), 4); } while(0)
+#endif
+
+static inline int
+seqno_compare(unsigned short s1, unsigned short s2)
+{
+ if(s1 == s2)
+ return 0;
+ else
+ return ((s2 - s1) & 0x8000) ? 1 : -1;
+}
+
+static inline short
+seqno_minus(unsigned short s1, unsigned short s2)
+{
+ return (short)((s1 - s2) & 0xFFFF);
+}
+
+static inline unsigned short
+seqno_plus(unsigned short s, int plus)
+{
+ return ((s + plus) & 0xFFFF);
+}
+
+/* Returns a time in microseconds on 32 bits (thus modulo 2^32,
+ i.e. about 4295 seconds). */
+static inline unsigned int
+time_us(const struct timeval t)
+{
+ return (unsigned int) (t.tv_sec * 1000000 + t.tv_usec);
+}
+
+int roughly(int value);
+void timeval_minus(struct timeval *d,
+ const struct timeval *s1, const struct timeval *s2);
+unsigned timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
+ ATTRIBUTE ((pure));
+void timeval_add_msec(struct timeval *d, const struct timeval *s, int msecs);
+void set_timeout (struct timeval *timeout, int msecs);
+int timeval_compare(const struct timeval *s1, const struct timeval *s2)
+ ATTRIBUTE ((pure));
+void timeval_min(struct timeval *d, const struct timeval *s);
+void timeval_min_sec(struct timeval *d, time_t secs);
+int parse_nat(const char *string) ATTRIBUTE ((pure));
+int parse_msec(const char *string) ATTRIBUTE ((pure));
+unsigned char *mask_prefix(unsigned char *restrict ret,
+ const unsigned char *restrict prefix,
+ unsigned char plen);
+const char *format_address(const unsigned char *address);
+const char *format_prefix(const unsigned char *address, unsigned char prefix);
+const char *format_eui64(const unsigned char *eui);
+const char *format_thousands(unsigned int value);
+int parse_address(const char *address, unsigned char *addr_r, int *af_r);
+int parse_eui64(const char *eui, unsigned char *eui_r);
+int wait_for_fd(int direction, int fd, int msecs);
+int martian_prefix(const unsigned char *prefix, int plen) ATTRIBUTE ((pure));
+int linklocal(const unsigned char *address) ATTRIBUTE ((pure));
+int v4mapped(const unsigned char *address) ATTRIBUTE ((pure));
+void v4tov6(unsigned char *dst, const unsigned char *src);
+void inaddr_to_uchar(unsigned char *dest, const struct in_addr *src);
+void uchar_to_inaddr(struct in_addr *dest, const unsigned char *src);
+void in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src);
+void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src);
+int daemonise(void);
+extern const unsigned char v4prefix[16];
+
+static inline bool
+is_default(const unsigned char *prefix, int plen)
+{
+ return plen == 0 || (plen == 96 && v4mapped(prefix));
+}
+
+/* If debugging is disabled, we want to avoid calling format_address
+ for every omitted debugging message. So debug is a macro. But
+ vararg macros are not portable. */
+#if defined NO_DEBUG
+
+#define debugf(...) do {} while(0)
+
+#else /* NO_DEBUG */
+
+/* some levels */
+#define BABEL_DEBUG_COMMON (1 << 0)
+#define BABEL_DEBUG_KERNEL (1 << 1)
+#define BABEL_DEBUG_FILTER (1 << 2)
+#define BABEL_DEBUG_TIMEOUT (1 << 3)
+#define BABEL_DEBUG_IF (1 << 4)
+#define BABEL_DEBUG_ROUTE (1 << 5)
+#define BABEL_DEBUG_ALL (0xFFFF)
+
+#define debugf(level, ...) \
+ do { \
+ if (unlikely(debug & level)) \
+ zlog_debug(__VA_ARGS__); \
+ } while (0)
+
+#endif /* NO_DEBUG */
+
+#endif /* BABEL_UTIL_H */
diff --git a/babeld/xroute.c b/babeld/xroute.c
new file mode 100644
index 0000000..30204cd
--- /dev/null
+++ b/babeld/xroute.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#include <zebra.h>
+#include "if.h"
+#include "log.h"
+
+#include "babeld.h"
+#include "kernel.h"
+#include "neighbour.h"
+#include "message.h"
+#include "route.h"
+#include "xroute.h"
+#include "util.h"
+#include "babel_interface.h"
+
+static int xroute_add_new_route(unsigned char prefix[16], unsigned char plen,
+ unsigned short metric, unsigned int ifindex,
+ int proto, int send_updates);
+
+static struct xroute *xroutes;
+static int numxroutes = 0, maxxroutes = 0;
+
+/* Add redistributed route to Babel table. */
+int
+babel_route_add (struct zapi_route *api)
+{
+ unsigned char uchar_prefix[16];
+
+ switch (api->prefix.family) {
+ case AF_INET:
+ inaddr_to_uchar(uchar_prefix, &api->prefix.u.prefix4);
+ debugf(BABEL_DEBUG_ROUTE, "Adding new ipv4 route coming from Zebra.");
+ xroute_add_new_route(uchar_prefix, api->prefix.prefixlen + 96,
+ api->metric, api->nexthops[0].ifindex, 0, 1);
+ break;
+ case AF_INET6:
+ in6addr_to_uchar(uchar_prefix, &api->prefix.u.prefix6);
+ debugf(BABEL_DEBUG_ROUTE, "Adding new ipv6 route coming from Zebra.");
+ xroute_add_new_route(uchar_prefix, api->prefix.prefixlen,
+ api->metric, api->nexthops[0].ifindex, 0, 1);
+ break;
+ }
+
+ return 0;
+}
+
+/* Remove redistributed route from Babel table. */
+int
+babel_route_delete (struct zapi_route *api)
+{
+ unsigned char uchar_prefix[16];
+ struct xroute *xroute = NULL;
+
+ switch (api->prefix.family) {
+ case AF_INET:
+ inaddr_to_uchar(uchar_prefix, &api->prefix.u.prefix4);
+ xroute = find_xroute(uchar_prefix, api->prefix.prefixlen + 96);
+ if (xroute != NULL) {
+ debugf(BABEL_DEBUG_ROUTE, "Removing ipv4 route (from zebra).");
+ flush_xroute(xroute);
+ }
+ break;
+ case AF_INET6:
+ in6addr_to_uchar(uchar_prefix, &api->prefix.u.prefix6);
+ xroute = find_xroute(uchar_prefix, api->prefix.prefixlen);
+ if (xroute != NULL) {
+ debugf(BABEL_DEBUG_ROUTE, "Removing ipv6 route (from zebra).");
+ flush_xroute(xroute);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+struct xroute *
+find_xroute(const unsigned char *prefix, unsigned char plen)
+{
+ int i;
+ for(i = 0; i < numxroutes; i++) {
+ if(xroutes[i].plen == plen &&
+ memcmp(xroutes[i].prefix, prefix, 16) == 0)
+ return &xroutes[i];
+ }
+ return NULL;
+}
+
+void
+flush_xroute(struct xroute *xroute)
+{
+ int i;
+
+ i = xroute - xroutes;
+ assert(i >= 0 && i < numxroutes);
+
+ if(i != numxroutes - 1)
+ memcpy(xroutes + i, xroutes + numxroutes - 1, sizeof(struct xroute));
+ numxroutes--;
+ VALGRIND_MAKE_MEM_UNDEFINED(xroutes + numxroutes, sizeof(struct xroute));
+
+ if(numxroutes == 0) {
+ free(xroutes);
+ xroutes = NULL;
+ maxxroutes = 0;
+ } else if(maxxroutes > 8 && numxroutes < maxxroutes / 4) {
+ struct xroute *new_xroutes;
+ int n = maxxroutes / 2;
+ new_xroutes = realloc(xroutes, n * sizeof(struct xroute));
+ if(new_xroutes == NULL)
+ return;
+ xroutes = new_xroutes;
+ maxxroutes = n;
+ }
+}
+
+static int
+add_xroute(unsigned char prefix[16], unsigned char plen,
+ unsigned short metric, unsigned int ifindex, int proto)
+{
+ struct xroute *xroute = find_xroute(prefix, plen);
+ if(xroute) {
+ if(xroute->metric <= metric)
+ return 0;
+ xroute->metric = metric;
+ return 1;
+ }
+
+ if(numxroutes >= maxxroutes) {
+ struct xroute *new_xroutes;
+ int n = maxxroutes < 1 ? 8 : 2 * maxxroutes;
+ new_xroutes = xroutes == NULL ?
+ malloc(n * sizeof(struct xroute)) :
+ realloc(xroutes, n * sizeof(struct xroute));
+ if(new_xroutes == NULL)
+ return -1;
+ maxxroutes = n;
+ xroutes = new_xroutes;
+ }
+
+ memcpy(xroutes[numxroutes].prefix, prefix, 16);
+ xroutes[numxroutes].plen = plen;
+ xroutes[numxroutes].metric = metric;
+ xroutes[numxroutes].ifindex = ifindex;
+ xroutes[numxroutes].proto = proto;
+ numxroutes++;
+ return 1;
+}
+
+/* Returns an overestimate of the number of xroutes. */
+int
+xroutes_estimate(void)
+{
+ return numxroutes;
+}
+
+struct xroute_stream {
+ int index;
+};
+
+struct
+xroute_stream *
+xroute_stream(void)
+{
+ struct xroute_stream *stream = malloc(sizeof(struct xroute_stream));
+ if(stream == NULL)
+ return NULL;
+
+ stream->index = 0;
+ return stream;
+}
+
+struct xroute *
+xroute_stream_next(struct xroute_stream *stream)
+{
+ if(stream->index < numxroutes)
+ return &xroutes[stream->index++];
+ else
+ return NULL;
+}
+
+void
+xroute_stream_done(struct xroute_stream *stream)
+{
+ free(stream);
+}
+
+/* add an xroute, verifying some conditions; return 0 if there is no changes */
+static int
+xroute_add_new_route(unsigned char prefix[16], unsigned char plen,
+ unsigned short metric, unsigned int ifindex,
+ int proto, int send_updates)
+{
+ int rc;
+ if(martian_prefix(prefix, plen))
+ return 0;
+ metric = redistribute_filter(prefix, plen, ifindex, proto);
+ if(metric < INFINITY) {
+ rc = add_xroute(prefix, plen, metric, ifindex, proto);
+ if(rc > 0) {
+ struct babel_route *route;
+ route = find_installed_route(prefix, plen);
+ if(route)
+ uninstall_route(route);
+ if(send_updates)
+ send_update(NULL, 0, prefix, plen);
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/babeld/xroute.h b/babeld/xroute.h
new file mode 100644
index 0000000..d2d191c
--- /dev/null
+++ b/babeld/xroute.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT
+/*
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+*/
+
+#ifndef BABEL_XROUTE_H
+#define BABEL_XROUTE_H
+
+struct xroute {
+ unsigned char prefix[16];
+ unsigned char plen;
+ unsigned short metric;
+ unsigned int ifindex;
+ int proto;
+};
+
+struct xroute_stream;
+
+struct xroute *find_xroute(const unsigned char *prefix, unsigned char plen);
+void flush_xroute(struct xroute *xroute);
+int babel_route_add (struct zapi_route *api);
+int babel_route_delete (struct zapi_route *api);
+int xroutes_estimate(void);
+struct xroute_stream *xroute_stream(void);
+struct xroute *xroute_stream_next(struct xroute_stream *stream);
+void xroute_stream_done(struct xroute_stream *stream);
+
+#endif /* BABEL_XROUTE_H */