mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-28 12:10:24 +00:00
1009 lines
28 KiB
C
1009 lines
28 KiB
C
/*
|
|
* This file is free software: you may copy, redistribute and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This file is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* This file incorporates work covered by the following copyright and
|
|
* permission notice:
|
|
*
|
|
|
|
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
#include "memory.h"
|
|
#include "log.h"
|
|
#include "command.h"
|
|
#include "prefix.h"
|
|
#include "vector.h"
|
|
#include "distribute.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"
|
|
|
|
|
|
#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). */
|
|
static struct cmd_node babel_interface_node = /* babeld's interface node. */
|
|
{
|
|
INTERFACE_NODE,
|
|
"%s(config-if)# ",
|
|
1 /* VTYSH */
|
|
};
|
|
|
|
|
|
int
|
|
babel_interface_up (int cmd, struct zclient *client, zebra_size_t length)
|
|
{
|
|
struct stream *s = NULL;
|
|
struct interface *ifp = NULL;
|
|
|
|
debugf(BABEL_DEBUG_IF, "receive a 'interface up'");
|
|
|
|
s = zclient->ibuf;
|
|
ifp = zebra_interface_state_read(s); /* it updates iflist */
|
|
|
|
if (ifp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
interface_recalculate(ifp);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
babel_interface_down (int cmd, struct zclient *client, zebra_size_t length)
|
|
{
|
|
struct stream *s = NULL;
|
|
struct interface *ifp = NULL;
|
|
|
|
debugf(BABEL_DEBUG_IF, "receive a 'interface down'");
|
|
|
|
s = zclient->ibuf;
|
|
ifp = zebra_interface_state_read(s); /* it updates iflist */
|
|
|
|
if (ifp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
interface_reset(ifp);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
babel_interface_add (int cmd, struct zclient *client, zebra_size_t length)
|
|
{
|
|
struct interface *ifp = NULL;
|
|
|
|
debugf(BABEL_DEBUG_IF, "receive a 'interface add'");
|
|
|
|
/* read and add the interface in the iflist. */
|
|
ifp = zebra_interface_add_read (zclient->ibuf);
|
|
|
|
if (ifp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
interface_recalculate(ifp);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length)
|
|
{
|
|
struct interface *ifp;
|
|
struct stream *s;
|
|
|
|
debugf(BABEL_DEBUG_IF, "receive a 'interface delete'");
|
|
|
|
s = zclient->ibuf;
|
|
ifp = zebra_interface_state_read(s); /* it updates iflist */
|
|
|
|
if (ifp == NULL)
|
|
return 0;
|
|
|
|
if (IS_ENABLE(ifp))
|
|
interface_reset(ifp);
|
|
|
|
/* To support pseudo interface do not free interface structure. */
|
|
/* if_delete(ifp); */
|
|
ifp->ifindex = IFINDEX_INTERNAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
babel_interface_address_add (int cmd, struct zclient *client,
|
|
zebra_size_t length)
|
|
{
|
|
babel_interface_nfo *babel_ifp;
|
|
struct connected *ifc;
|
|
struct prefix *prefix;
|
|
|
|
debugf(BABEL_DEBUG_IF, "receive a 'interface address add'");
|
|
|
|
ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD,
|
|
zclient->ibuf);
|
|
|
|
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) {
|
|
zlog_err("not einough 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 (int cmd, struct zclient *client,
|
|
zebra_size_t length)
|
|
{
|
|
babel_interface_nfo *babel_ifp;
|
|
struct connected *ifc;
|
|
struct prefix *prefix;
|
|
|
|
debugf(BABEL_DEBUG_IF, "receive a 'interface address add'");
|
|
|
|
ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD,
|
|
zclient->ibuf);
|
|
|
|
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, 4) == 0) {
|
|
free(babel_ifp->ipv4);
|
|
babel_ifp->ipv4 = NULL;
|
|
}
|
|
}
|
|
|
|
send_request(ifc->ifp, NULL, 0);
|
|
send_update(ifc->ifp, 0, NULL, 0);
|
|
|
|
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);
|
|
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);
|
|
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")
|
|
{
|
|
int ret;
|
|
struct prefix p;
|
|
|
|
ret = str2prefix (argv[0], &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[0]);
|
|
|
|
if (ret < 0) {
|
|
vty_out (vty, "There is same network configuration %s%s", argv[0],
|
|
VTY_NEWLINE);
|
|
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")
|
|
{
|
|
int ret;
|
|
struct prefix p;
|
|
|
|
ret = str2prefix (argv[0], &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[0]);
|
|
|
|
if (ret < 0) {
|
|
vty_out (vty, "can't find network %s%s", argv[0],
|
|
VTY_NEWLINE);
|
|
return CMD_WARNING;
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command] Tell the interface is wire. */
|
|
DEFUN (babel_set_wired,
|
|
babel_set_wired_cmd,
|
|
"babel wired",
|
|
"Babel interface commands\n"
|
|
"Enable wired optimisations")
|
|
{
|
|
struct interface *ifp;
|
|
babel_interface_nfo *babel_ifp;
|
|
|
|
ifp = vty->index;
|
|
babel_ifp = babel_get_if_nfo(ifp);
|
|
|
|
assert (babel_ifp != NULL);
|
|
babel_ifp->flags |= BABEL_IF_WIRED;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command] Tell the interface is wireless (default). */
|
|
DEFUN (babel_set_wireless,
|
|
babel_set_wireless_cmd,
|
|
"babel wireless",
|
|
"Babel interface commands\n"
|
|
"Disable wired optimiations (assume wireless)")
|
|
{
|
|
struct interface *ifp;
|
|
babel_interface_nfo *babel_ifp;
|
|
|
|
ifp = vty->index;
|
|
babel_ifp = babel_get_if_nfo(ifp);
|
|
|
|
assert (babel_ifp != NULL);
|
|
babel_ifp->flags &= ~BABEL_IF_WIRED;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command] Enable split horizon. */
|
|
DEFUN (babel_split_horizon,
|
|
babel_split_horizon_cmd,
|
|
"babel split-horizon",
|
|
"Babel interface commands\n"
|
|
"Enable split horizon processing")
|
|
{
|
|
struct interface *ifp;
|
|
babel_interface_nfo *babel_ifp;
|
|
|
|
ifp = vty->index;
|
|
babel_ifp = babel_get_if_nfo(ifp);
|
|
|
|
assert (babel_ifp != NULL);
|
|
babel_ifp->flags |= BABEL_IF_SPLIT_HORIZON;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command] Disable split horizon (default). */
|
|
DEFUN (no_babel_split_horizon,
|
|
no_babel_split_horizon_cmd,
|
|
"no babel split-horizon",
|
|
NO_STR
|
|
"Babel interface commands\n"
|
|
"Disable split horizon processing")
|
|
{
|
|
struct interface *ifp;
|
|
babel_interface_nfo *babel_ifp;
|
|
|
|
ifp = vty->index;
|
|
babel_ifp = babel_get_if_nfo(ifp);
|
|
|
|
assert (babel_ifp != NULL);
|
|
babel_ifp->flags &= ~BABEL_IF_SPLIT_HORIZON;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command]. */
|
|
DEFUN (babel_set_hello_interval,
|
|
babel_set_hello_interval_cmd,
|
|
"babel hello-interval <20-655340>",
|
|
"Babel interface commands\n"
|
|
"Time between scheduled hellos\n"
|
|
"Milliseconds\n")
|
|
{
|
|
struct interface *ifp;
|
|
babel_interface_nfo *babel_ifp;
|
|
int interval;
|
|
|
|
VTY_GET_INTEGER_RANGE("milliseconds", interval, argv[0], 20, 10 * 0xFFFE);
|
|
|
|
ifp = vty->index;
|
|
babel_ifp = babel_get_if_nfo(ifp);
|
|
assert (babel_ifp != NULL);
|
|
|
|
babel_ifp->hello_interval = interval;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command]. */
|
|
DEFUN (babel_set_update_interval,
|
|
babel_set_update_interval_cmd,
|
|
"babel update-interval <20-655340>",
|
|
"Babel interface commands\n"
|
|
"Time between scheduled updates\n"
|
|
"Milliseconds\n")
|
|
{
|
|
struct interface *ifp;
|
|
babel_interface_nfo *babel_ifp;
|
|
int interval;
|
|
|
|
VTY_GET_INTEGER_RANGE("milliseconds", interval, argv[0], 20, 10 * 0xFFFE);
|
|
|
|
ifp = vty->index;
|
|
babel_ifp = babel_get_if_nfo(ifp);
|
|
assert (babel_ifp != NULL);
|
|
|
|
babel_ifp->update_interval = interval;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command]. */
|
|
DEFUN (babel_passive_interface,
|
|
babel_passive_interface_cmd,
|
|
"babel passive-interface",
|
|
"Babel interface commands\n"
|
|
"Only announce redistributed routes on this interface\n")
|
|
{
|
|
if (allow_duplicates) {
|
|
return CMD_WARNING;
|
|
}
|
|
parasitic = 1;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* [Interface Command]. */
|
|
DEFUN (no_babel_passive_interface,
|
|
no_babel_passive_interface_cmd,
|
|
"no babel passive-interface",
|
|
NO_STR
|
|
"Babel interface commands\n"
|
|
"Announce all routes on this interface\n")
|
|
{
|
|
parasitic = 0;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 40 for IPv6 header, 8 for UDP header, 12 for good luck. */
|
|
babel_ifp->bufsize = mtu - sizeof(packet_header) - 60;
|
|
tmp = babel_ifp->sendbuf;
|
|
babel_ifp->sendbuf = realloc(babel_ifp->sendbuf, babel_ifp->bufsize);
|
|
if(babel_ifp->sendbuf == NULL) {
|
|
zlog_err("Couldn't reallocate sendbuf.");
|
|
free(tmp);
|
|
babel_ifp->bufsize = 0;
|
|
return -1;
|
|
}
|
|
tmp = NULL;
|
|
|
|
resize_receive_buffer(mtu);
|
|
|
|
if(!(babel_ifp->flags & BABEL_IF_WIRED)) { /* if (wired) */
|
|
babel_ifp->cost = 96;
|
|
babel_ifp->flags &= ~BABEL_IF_LQ;
|
|
} else {
|
|
babel_ifp->cost = 256;
|
|
babel_ifp->flags |= BABEL_IF_LQ;
|
|
}
|
|
|
|
/* Since the interface was marked as active above, the
|
|
idle_hello_interval cannot be the one being used here. */
|
|
babel_ifp->update_interval = babel_ifp->hello_interval * 4;
|
|
|
|
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) {
|
|
zlog_err("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,
|
|
(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 (!(babel_ifp->flags & BABEL_IF_IS_UP))
|
|
return 0;
|
|
|
|
debugf(BABEL_DEBUG_IF, "interface reset: %s", ifp->name);
|
|
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)
|
|
zlog_err("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,
|
|
(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 interface *ifp = NULL;
|
|
struct listnode *linklist_node = NULL;
|
|
|
|
FOR_ALL_INTERFACES(ifp, linklist_node) {
|
|
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(ifp, linklist_node) {
|
|
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, 16) == 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%s", ifp->name,
|
|
((is_up = if_is_operative(ifp)) ? "up" : "down"), VTY_NEWLINE);
|
|
vty_out (vty, " ifindex %u, MTU %u bytes %s%s",
|
|
ifp->ifindex, ifp->mtu, if_flag_dump(ifp->flags), VTY_NEWLINE);
|
|
|
|
if (babel_enable_if_lookup (ifp->name) < 0)
|
|
{
|
|
vty_out (vty, " Babel protocol is not enabled on this interface%s", VTY_NEWLINE);
|
|
return;
|
|
}
|
|
if (!is_up)
|
|
{
|
|
vty_out (vty, " Babel protocol is enabled, but not running on this interface%s", VTY_NEWLINE);
|
|
return;
|
|
}
|
|
babel_ifp = babel_get_if_nfo (ifp);
|
|
vty_out (vty, " Babel protocol is running on this interface%s", VTY_NEWLINE);
|
|
vty_out (vty, " Operating mode is \"%s\"%s",
|
|
CHECK_FLAG (babel_ifp->flags, BABEL_IF_WIRED) ? "wired" : "wireless", VTY_NEWLINE);
|
|
vty_out (vty, " Split horizon mode is %s%s",
|
|
CHECK_FLAG (babel_ifp->flags, BABEL_IF_SPLIT_HORIZON) ? "On" : "Off", VTY_NEWLINE);
|
|
vty_out (vty, " Hello interval is %u ms%s", babel_ifp->hello_interval, VTY_NEWLINE);
|
|
}
|
|
|
|
DEFUN (show_babel_interface,
|
|
show_babel_interface_cmd,
|
|
"show babel interface [INTERFACE]",
|
|
SHOW_STR
|
|
IP_STR
|
|
"Babel information\n"
|
|
"Interface information\n"
|
|
"Interface name\n")
|
|
{
|
|
struct interface *ifp;
|
|
struct listnode *node;
|
|
|
|
if (argc == 0)
|
|
{
|
|
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
|
|
show_babel_interface_sub (vty, ifp);
|
|
return CMD_SUCCESS;
|
|
}
|
|
if ((ifp = if_lookup_by_name (argv[0])) == NULL)
|
|
{
|
|
vty_out (vty, "No such interface name%s", VTY_NEWLINE);
|
|
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 %s.%s",
|
|
format_address(neigh->address),
|
|
neigh->ifp->name,
|
|
neigh->reach,
|
|
neighbour_rxcost(neigh),
|
|
neigh->txcost,
|
|
if_up(neigh->ifp) ? "" : " (down)",
|
|
VTY_NEWLINE);
|
|
}
|
|
|
|
DEFUN (show_babel_neighbour,
|
|
show_babel_neighbour_cmd,
|
|
"show babel neighbour [INTERFACE]",
|
|
SHOW_STR
|
|
IP_STR
|
|
"Babel information\n"
|
|
"Print neighbours\n"
|
|
"Interface name\n")
|
|
{
|
|
struct neighbour *neigh;
|
|
struct interface *ifp;
|
|
|
|
if (argc == 0) {
|
|
FOR_ALL_NEIGHBOURS(neigh) {
|
|
show_babel_neighbour_sub(vty, neigh);
|
|
}
|
|
return CMD_SUCCESS;
|
|
}
|
|
if ((ifp = if_lookup_by_name (argv[0])) == NULL)
|
|
{
|
|
vty_out (vty, "No such interface name%s", VTY_NEWLINE);
|
|
return CMD_WARNING;
|
|
}
|
|
FOR_ALL_NEIGHBOURS(neigh) {
|
|
if(ifp->ifindex == neigh->ifp->ifindex) {
|
|
show_babel_neighbour_sub(vty, neigh);
|
|
}
|
|
}
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
show_babel_routes_sub (struct babel_route *route, void *closure)
|
|
{
|
|
struct vty *vty = (struct vty*) closure;
|
|
const unsigned char *nexthop =
|
|
memcmp(route->nexthop, route->neigh->address, 16) == 0 ?
|
|
NULL : route->nexthop;
|
|
char channels[100];
|
|
|
|
if(route->channels[0] == 0)
|
|
channels[0] = '\0';
|
|
else {
|
|
int k, j = 0;
|
|
snprintf(channels, 100, " 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, "%d", 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%s",
|
|
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)" : "",
|
|
VTY_NEWLINE);
|
|
}
|
|
|
|
static void
|
|
show_babel_xroutes_sub (struct xroute *xroute, void *closure)
|
|
{
|
|
struct vty *vty = (struct vty *) closure;
|
|
vty_out(vty, "%s metric %d (exported)%s",
|
|
format_prefix(xroute->prefix, xroute->plen),
|
|
xroute->metric,
|
|
VTY_NEWLINE);
|
|
}
|
|
|
|
DEFUN (show_babel_database,
|
|
show_babel_database_cmd,
|
|
"show babel database",
|
|
SHOW_STR
|
|
IP_STR
|
|
"Babel information\n"
|
|
"Database information\n"
|
|
"No attributes\n")
|
|
{
|
|
for_all_routes(show_babel_routes_sub, vty);
|
|
for_all_xroutes(show_babel_xroutes_sub, vty);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN (show_babel_running_config,
|
|
show_babel_running_config_cmd,
|
|
"show babel running-config",
|
|
SHOW_STR
|
|
IP_STR
|
|
"Babel information\n"
|
|
"Configuration information\n"
|
|
"No attributes\n")
|
|
{
|
|
vty_out(vty, " -- Babel running configuration --%s", VTY_NEWLINE);
|
|
show_babel_main_configuration(vty);
|
|
show_babeld_configuration(vty);
|
|
vty_out(vty, " -- distribution lists --%s", VTY_NEWLINE);
|
|
config_show_distribute(vty);
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
void
|
|
babel_if_init ()
|
|
{
|
|
/* initialize interface list */
|
|
if_init();
|
|
if_add_hook (IF_NEW_HOOK, babel_if_new_hook);
|
|
if_add_hook (IF_DELETE_HOOK, babel_if_delete_hook);
|
|
|
|
babel_enable_if = vector_init (1);
|
|
|
|
/* install interface node and commands */
|
|
install_element (CONFIG_NODE, &interface_cmd);
|
|
install_element (CONFIG_NODE, &no_interface_cmd);
|
|
install_node (&babel_interface_node, interface_config_write);
|
|
install_default(INTERFACE_NODE);
|
|
install_element(INTERFACE_NODE, &interface_cmd);
|
|
install_element(INTERFACE_NODE, &no_interface_cmd);
|
|
|
|
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, &no_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_passive_interface_cmd);
|
|
install_element(INTERFACE_NODE, &no_babel_passive_interface_cmd);
|
|
|
|
/* "show babel ..." commands */
|
|
install_element(VIEW_NODE, &show_babel_interface_cmd);
|
|
install_element(ENABLE_NODE, &show_babel_interface_cmd);
|
|
install_element(VIEW_NODE, &show_babel_neighbour_cmd);
|
|
install_element(ENABLE_NODE, &show_babel_neighbour_cmd);
|
|
install_element(VIEW_NODE, &show_babel_database_cmd);
|
|
install_element(ENABLE_NODE, &show_babel_database_cmd);
|
|
install_element(VIEW_NODE, &show_babel_running_config_cmd);
|
|
install_element(ENABLE_NODE, &show_babel_running_config_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;
|
|
}
|
|
|
|
/* Configuration write function for babeld. */
|
|
static int
|
|
interface_config_write (struct vty *vty)
|
|
{
|
|
struct listnode *node;
|
|
struct interface *ifp;
|
|
int write = 0;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) {
|
|
/* Do not display the interface if there is no configuration about it */
|
|
if (ifp->desc == NULL)
|
|
continue;
|
|
|
|
vty_out (vty, "interface %s%s", ifp->name,
|
|
VTY_NEWLINE);
|
|
if (ifp->desc)
|
|
vty_out (vty, " description %s%s", ifp->desc,
|
|
VTY_NEWLINE);
|
|
|
|
/* TODO: to be completed... */
|
|
|
|
vty_out (vty, "!%s", VTY_NEWLINE);
|
|
|
|
write++;
|
|
}
|
|
return write;
|
|
}
|
|
|
|
/* 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 = XMALLOC(MTYPE_BABEL_IF, sizeof(babel_interface_nfo));
|
|
if(babel_ifp == NULL)
|
|
return NULL;
|
|
|
|
/* Here are set the default values for an interface. */
|
|
memset(babel_ifp, 0, 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 = (random() & 0xFFFF);
|
|
babel_ifp->hello_interval = BABELD_DEFAULT_HELLO_INTERVAL;
|
|
babel_ifp->channel = BABEL_IF_CHANNEL_INTERFERING;
|
|
|
|
return babel_ifp;
|
|
}
|
|
|
|
static void
|
|
babel_interface_free (babel_interface_nfo *babel_ifp)
|
|
{
|
|
XFREE(MTYPE_BABEL_IF, babel_ifp);
|
|
}
|