Commit ebbbc095 authored by Antonin Décimo's avatar Antonin Décimo

Remove disambiguation code.

Babel-users mailing list threads:
- [Babel-users] Higher CPU usage since 1.9.x leads to instability on slow devices
  https://alioth-lists.debian.net/pipermail/babel-users/2020-May/003734.html
- [Babel-users] Removing source-specific for older kernels [was: Higher CPU usage since 1.9.x leads to instability on slow devices]
  https://alioth-lists.debian.net/pipermail/babel-users/2020-June/003742.html

Rules are obsoleted by the removal of the disambiguation code.
See https://alioth-lists.debian.net/pipermail/babel-users/2020-June/003744.html
parent aa29624e
......@@ -10,12 +10,10 @@ CFLAGS = $(CDEBUGFLAGS) $(DEFINES) $(EXTRA_DEFINES)
LDLIBS = -lrt
SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \
route.c xroute.c message.c resend.c configuration.c local.c \
disambiguation.c rule.c
route.c xroute.c message.c resend.c configuration.c local.c
OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \
route.o xroute.o message.o resend.o configuration.o local.o \
disambiguation.o rule.o
route.o xroute.o message.o resend.o configuration.o local.o
babeld: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
......
......@@ -52,7 +52,6 @@ THE SOFTWARE.
#include "resend.h"
#include "configuration.h"
#include "local.h"
#include "rule.h"
#include "version.h"
struct timeval now;
......@@ -87,7 +86,6 @@ unsigned char protocol_group[16];
int protocol_socket = -1;
int kernel_socket = -1;
static int kernel_routes_changed = 0;
static int kernel_rules_changed = 0;
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
......@@ -126,22 +124,6 @@ kernel_link_notify(struct kernel_link *link, void *closure)
return 0;
}
static int
kernel_rule_notify(struct kernel_rule *rule, void *closure)
{
int i;
if(martian_prefix(rule->src, rule->src_plen))
return 0;
i = rule->priority - src_table_prio;
if(i < 0 || SRC_TABLE_NUM <= i)
return 0;
kernel_rules_changed = 1;
return -1;
}
int
main(int argc, char **argv)
{
......@@ -548,12 +530,8 @@ main(int argc, char **argv)
rc = check_xroutes(0);
if(rc < 0)
fprintf(stderr, "Warning: couldn't check exported routes.\n");
rc = check_rules();
if(rc < 0)
fprintf(stderr, "Warning: couldn't check rules.\n");
kernel_routes_changed = 0;
kernel_rules_changed = 0;
kernel_link_changed = 0;
kernel_addr_changed = 0;
kernel_dump_time = now.tv_sec + roughly(30);
......@@ -656,7 +634,6 @@ main(int argc, char **argv)
filter.route = kernel_route_notify;
filter.addr = kernel_addr_notify;
filter.link = kernel_link_notify;
filter.rule = kernel_rule_notify;
kernel_callback(&filter);
}
......@@ -721,15 +698,11 @@ main(int argc, char **argv)
}
if(kernel_routes_changed || kernel_addr_changed ||
kernel_rules_changed || now.tv_sec >= kernel_dump_time) {
now.tv_sec >= kernel_dump_time) {
rc = check_xroutes(1);
if(rc < 0)
fprintf(stderr, "Warning: couldn't check exported routes.\n");
rc = check_rules();
if(rc < 0)
fprintf(stderr, "Warning: couldn't check rules.\n");
kernel_routes_changed = kernel_rules_changed =
kernel_addr_changed = 0;
kernel_routes_changed = kernel_addr_changed = 0;
if(kernel_socket >= 0)
kernel_dump_time = now.tv_sec + roughly(300);
else
......@@ -830,7 +803,6 @@ main(int argc, char **argv)
gettime(&now);
interface_updown(ifp, 0);
}
release_tables();
kernel_setup_socket(0);
kernel_setup(0);
......
......@@ -309,14 +309,6 @@ This specifies the name of the file to which
.B babeld
writes out its process id, and is equivalent to the command-line option
.BR \-I .
.TP
.BI first-table-number " table"
This specifies the index of the first routing table to use for
source-specific routes. The default is 10.
.TP
.BI first-rule-priority " priority"
This specifies smallest (highest) rule priority used with source-specific
routes. The default is 100.
.SS Interface configuration
An interface is configured by a line with the following format:
.IP
......
......@@ -40,7 +40,6 @@ THE SOFTWARE.
#include "route.h"
#include "kernel.h"
#include "configuration.h"
#include "rule.h"
static struct filter *input_filters = NULL;
static struct filter *output_filters = NULL;
......@@ -939,18 +938,6 @@ parse_option(int c, gnc_t gnc, void *closure, char *token)
if(c < -1 || h < 0)
goto error;
change_smoothing_half_life(h);
} else if(strcmp(token, "first-table-number") == 0) {
int n;
c = getint(c, &n, gnc, closure);
if(c < -1 || n <= 0 || n + SRC_TABLE_NUM >= 254)
goto error;
src_table_idx = n;
} else if(strcmp(token, "first-rule-priority") == 0) {
int n;
c = getint(c, &n, gnc, closure);
if(c < -1 || n <= 0 || n + SRC_TABLE_NUM >= 32765)
goto error;
src_table_prio = n;
} else if(strcmp(token, "router-id") == 0) {
unsigned char *id = NULL;
c = getid(c, &id, gnc, closure);
......
/*
Copyright (c) 2014 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include "babeld.h"
#include "util.h"
#include "interface.h"
#include "kernel.h"
#include "route.h"
#include "source.h"
#include "neighbour.h"
#include "disambiguation.h"
#include "configuration.h"
#include "rule.h"
struct zone {
const unsigned char *dst_prefix;
unsigned char dst_plen;
const unsigned char *src_prefix;
unsigned char src_plen;
};
/* This function assumes rt1 and rt2 non disjoint. */
static int
rt_cmp(const struct babel_route *rt1, const struct babel_route *rt2)
{
enum prefix_status dst_st, src_st;
const struct source *r1 = rt1->src, *r2 = rt2->src;
dst_st = prefix_cmp(r1->prefix, r1->plen, r2->prefix, r2->plen);
if(dst_st == PST_MORE_SPECIFIC)
return -1;
else if(dst_st == PST_LESS_SPECIFIC)
return 1;
src_st = prefix_cmp(r1->src_prefix, r1->src_plen,
r2->src_prefix, r2->src_plen);
if(src_st == PST_MORE_SPECIFIC)
return -1;
else if(src_st == PST_LESS_SPECIFIC)
return 1;
return 0;
}
static const struct babel_route *
min_route(const struct babel_route *r1, const struct babel_route *r2)
{
int rc;
if(!r1) return r2;
if(!r2) return r1;
rc = rt_cmp(r1, r2);
return rc <= 0 ? r1 : r2;
}
static int
conflicts(const struct babel_route *rt, const struct babel_route *rt1)
{
enum prefix_status dst_st, src_st;
const struct source *r = rt->src, *r1 = rt1->src;
dst_st = prefix_cmp(r->prefix, r->plen, r1->prefix, r1->plen);
if(dst_st == PST_DISJOINT || dst_st == PST_EQUALS)
return 0;
src_st = prefix_cmp(r->src_prefix, r->src_plen,
r1->src_prefix, r1->src_plen);
return ((dst_st == PST_LESS_SPECIFIC && src_st == PST_MORE_SPECIFIC) ||
(dst_st == PST_MORE_SPECIFIC && src_st == PST_LESS_SPECIFIC));
}
static const struct zone*
to_zone(const struct babel_route *rt, struct zone *zone)
{
zone->dst_prefix = rt->src->prefix;
zone->dst_plen = rt->src->plen;
zone->src_prefix = rt->src->src_prefix;
zone->src_plen = rt->src->src_plen;
return zone;
}
/* fill zone with rt cap rt1, and returns a pointer to zone, or NULL if the
intersection is empty. */
static const struct zone*
inter(const struct babel_route *rt, const struct babel_route *rt1,
struct zone *zone)
{
enum prefix_status dst_st, src_st;
const struct source *r = rt->src, *r1 = rt1->src;
dst_st = prefix_cmp(r->prefix, r->plen, r1->prefix, r1->plen);
if(dst_st == PST_DISJOINT)
return NULL;
src_st = prefix_cmp(r->src_prefix, r->src_plen,
r1->src_prefix, r1->src_plen);
if(src_st == PST_DISJOINT)
return NULL;
if(dst_st == PST_MORE_SPECIFIC || dst_st == PST_EQUALS) {
zone->dst_prefix = r->prefix;
zone->dst_plen = r->plen;
} else {
zone->dst_prefix = r1->prefix;
zone->dst_plen = r1->plen;
}
if(src_st == PST_MORE_SPECIFIC || src_st == PST_EQUALS) {
zone->src_prefix = r->src_prefix;
zone->src_plen = r->src_plen;
} else {
zone->src_prefix = r1->src_prefix;
zone->src_plen = r1->src_plen;
}
return zone;
}
static int
zone_equal(const struct zone *z1, const struct zone *z2)
{
return z1 && z2 && z1->dst_plen == z2->dst_plen &&
memcmp(z1->dst_prefix, z2->dst_prefix, 16) == 0 &&
z1->src_plen == z2->src_plen &&
memcmp(z1->src_prefix, z2->src_prefix, 16) == 0;
}
static const struct babel_route *
min_conflict(const struct zone *zone, const struct babel_route *rt)
{
struct babel_route *rt1 = NULL;
const struct babel_route *min = NULL;
struct route_stream *stream = NULL;
struct zone curr_zone;
stream = route_stream(ROUTE_INSTALLED);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return NULL;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
if(!(conflicts(rt, rt1) &&
zone_equal(inter(rt, rt1, &curr_zone), zone)))
continue;
min = min_route(rt1, min);
}
route_stream_done(stream);
return min;
}
static const struct babel_route *
conflict_solution(const struct babel_route *rt)
{
const struct babel_route *rt1 = NULL, *rt2 = NULL;
struct route_stream *stream1 = NULL;
struct route_stream *stream2 = NULL;
const struct babel_route *min = NULL; /* == solution */
struct zone zone;
struct zone tmp;
/* Having a conflict requires at least one specific route. */
stream1 = route_stream(ROUTE_SS_INSTALLED);
if(!stream1) {
return NULL;
}
while(1) {
rt1 = route_stream_next(stream1);
if(rt1 == NULL) break;
stream2 = route_stream(ROUTE_INSTALLED);
if(!stream2) {
route_stream_done(stream1);
fprintf(stderr, "Couldn't allocate route stream.\n");
return NULL;
}
while(1) {
rt2 = route_stream_next(stream2);
if(rt2 == NULL) break;
if(!(conflicts(rt1, rt2) &&
zone_equal(inter(rt1, rt2, &tmp), to_zone(rt, &zone)) &&
rt_cmp(rt1, rt2) < 0))
continue;
min = min_route(rt1, min);
}
route_stream_done(stream2);
}
route_stream_done(stream1);
return min;
}
static int
is_installed(struct zone *zone)
{
return zone != NULL &&
find_installed_route(zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen) != NULL;
}
static int
change_route(int operation, const struct zone *zone,
const struct babel_route *route, int metric,
const unsigned char *new_next_hop,
int new_ifindex, int new_metric)
{
struct filter_result filter_result;
unsigned char *pref_src = NULL;
unsigned int ifindex = route->neigh->ifp->ifindex;
int m = install_filter(zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen,
ifindex, &filter_result);
if (m < INFINITY)
pref_src = filter_result.pref_src;
int table = filter_result.table ? filter_result.table :
find_table(zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen);
return kernel_route(operation, table, zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen, pref_src,
route->nexthop, ifindex,
metric, new_next_hop, new_ifindex, new_metric,
operation == ROUTE_MODIFY ? table : 0);
}
static int
add_route(const struct zone *zone, const struct babel_route *route)
{
return change_route(ROUTE_ADD, zone, route,
metric_to_kernel(route_metric(route)), NULL, 0, 0);
}
static int
del_route(const struct zone *zone, const struct babel_route *route)
{
return change_route(ROUTE_FLUSH, zone, route,
metric_to_kernel(route_metric(route)), NULL, 0, 0);
}
static int
chg_route(const struct zone *zone, const struct babel_route *old,
const struct babel_route *new)
{
return change_route(ROUTE_MODIFY, zone, old,
metric_to_kernel(route_metric(old)),
new->nexthop, new->neigh->ifp->ifindex,
metric_to_kernel(route_metric(new)));
}
static int
chg_route_metric(const struct zone *zone, const struct babel_route *route,
int old_metric, int new_metric)
{
return change_route(ROUTE_MODIFY, zone, route,
old_metric,
route->nexthop, route->neigh->ifp->ifindex,
new_metric);
}
int
kinstall_route(const struct babel_route *route)
{
int rc;
struct zone zone;
const struct babel_route *rt1 = NULL;
const struct babel_route *rt2 = NULL;
struct route_stream *stream = NULL;
int v4 = v4mapped(route->nexthop);
debugf("install_route(%s from %s)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen));
/* Install source-specific conflicting routes */
if(kernel_disambiguate(v4)) {
to_zone(route, &zone);
rc = add_route(&zone, route);
goto end;
}
stream = route_stream(ROUTE_INSTALLED);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
/* Install source-specific conflicting routes */
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(route, rt1, &zone);
if(!(conflicts(route, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, route)) == 0))
continue;
rt2 = min_conflict(&zone, rt1);
if(rt2 == NULL)
add_route(&zone, min_route(route, rt1));
else if(rt_cmp(route, rt2) < 0 && rt_cmp(route, rt1) < 0)
chg_route(&zone, rt2, route);
}
route_stream_done(stream);
/* Non conflicting case */
to_zone(route, &zone);
rt1 = conflict_solution(route);
if(rt1 == NULL)
rc = add_route(&zone, route);
else
rc = chg_route(&zone, rt1, route);
end:
if(rc < 0) {
int save = errno;
perror("kernel_route(ADD)");
if(save != EEXIST)
return -1;
}
return 0;
}
int
kuninstall_route(const struct babel_route *route)
{
int rc;
struct zone zone;
const struct babel_route *rt1 = NULL, *rt2 = NULL;
struct route_stream *stream = NULL;
int v4 = v4mapped(route->nexthop);
debugf("uninstall_route(%s from %s)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen));
to_zone(route, &zone);
if(kernel_disambiguate(v4)) {
rc = del_route(&zone, route);
if(rc < 0)
perror("kernel_route(FLUSH)");
return rc;
}
/* Remove the route, or change if the route was solving a conflict. */
rt1 = conflict_solution(route);
if(rt1 == NULL)
rc = del_route(&zone, route);
else
rc = chg_route(&zone, route, rt1);
if(rc < 0)
perror("kernel_route(FLUSH)");
/* Remove source-specific conflicting routes */
stream = route_stream(ROUTE_INSTALLED);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(route, rt1, &zone);
if(!(conflicts(route, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, route)) == 0))
continue;
rt2 = min_conflict(&zone, rt1);
if(rt2 == NULL)
del_route(&zone, min_route(route, rt1));
else if(rt_cmp(route, rt2) < 0 && rt_cmp(route, rt1) < 0)
chg_route(&zone, route, rt2);
}
route_stream_done(stream);
return rc;
}
int
kswitch_routes(const struct babel_route *old, const struct babel_route *new)
{
int rc;
struct zone zone;
struct babel_route *rt1 = NULL;
struct route_stream *stream = NULL;
debugf("switch_routes(%s from %s)\n",
format_prefix(old->src->prefix, old->src->plen),
format_prefix(old->src->src_prefix, old->src->src_plen));
to_zone(old, &zone);
rc = chg_route(&zone, old, new);
if(rc < 0) {
perror("kernel_route(MODIFY)");
return -1;
}
/* Remove source-specific conflicting routes */
if(!kernel_disambiguate(v4mapped(old->nexthop))) {
stream = route_stream(ROUTE_INSTALLED);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(old, rt1, &zone);
if(!(conflicts(old, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, old)) == 0 &&
rt_cmp(old, rt1) < 0 &&
rt_cmp(old, min_conflict(&zone, rt1)) == 0))
continue;
chg_route(&zone, old, new);
}
route_stream_done(stream);
}
return rc;
}
int
kchange_route_metric(const struct babel_route *route,
unsigned refmetric, unsigned cost, unsigned add)
{
int old_metric = metric_to_kernel(route_metric(route));
int new_metric = metric_to_kernel(MIN(refmetric + cost + add, INFINITY));
int rc;
struct babel_route *rt1 = NULL;
struct route_stream *stream = NULL;
struct zone zone;
debugf("change_route_metric(%s from %s, %d -> %d)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen),
old_metric, new_metric);
to_zone(route, &zone);
rc = chg_route_metric(&zone, route, old_metric, new_metric);
if(rc < 0) {
perror("kernel_route(MODIFY metric)");
return -1;
}
if(!kernel_disambiguate(v4mapped(route->nexthop))) {
stream = route_stream(ROUTE_INSTALLED);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(route, rt1, &zone);
if(!(conflicts(route, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, route)) == 0 &&
rt_cmp(route, rt1) < 0 &&
rt_cmp(route, min_conflict(&zone, rt1)) == 0))
continue;
chg_route_metric(&zone, route, old_metric, new_metric);
}
route_stream_done(stream);
}
return rc;
}
/*
Copyright (c) 2014 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.
*/
int kinstall_route(const struct babel_route *route);
int kuninstall_route(const struct babel_route *route);
int kswitch_routes(const struct babel_route *old, const struct babel_route *new);
int kchange_route_metric(const struct babel_route *route,
unsigned refmetric, unsigned cost, unsigned add);
......@@ -45,13 +45,6 @@ struct kernel_link {
char *ifname;
};
struct kernel_rule {
unsigned int priority;
unsigned int table;
unsigned char src[16];
unsigned char src_plen;
};
struct kernel_filter {
/* return -1 to interrupt search. */
int (*addr)(struct kernel_addr *, void *);
......@@ -60,8 +53,6 @@ struct kernel_filter {
void *route_closure;
int (*link)(struct kernel_link *, void *);
void *link_closure;
int (*rule)(struct kernel_rule *, void *);
void *rule_closure;
};
#define ROUTE_FLUSH 0
......@@ -90,7 +81,6 @@ int kernel_interface_ipv4(const char *ifname, int ifindex,
int kernel_interface_mtu(const char *ifname, int ifindex);
int kernel_interface_wireless(const char *ifname, int ifindex);
int kernel_interface_channel(const char *ifname, int ifindex);
int kernel_disambiguate(int v4);
int kernel_route(int operation, int table,
const unsigned char *dest, unsigned short plen,
const unsigned char *src, unsigned short src_plen,
......@@ -105,8 +95,3 @@ int gettime(struct timeval *tv);
int read_random_bytes(void *buf, int len);
int kernel_older_than(const char *sysname, int version, int sub_version);
int kernel_has_ipv6_subtrees(void);
int add_rule(int prio, const unsigned char *src_prefix, int src_plen,
int table);
int flush_rule(int prio, int family);
int change_rule(int new_prio, int old_prio, const unsigned char *src, int plen,
int table);
......@@ -934,14 +934,6 @@ kernel_interface_channel(const char *ifname, int ifindex)
return -1;
}
/* Return true if we cannot handle disambiguation ourselves. */
int
kernel_disambiguate(int v4)
{
return !v4 && has_ipv6_subtrees;
}
int
kernel_has_ipv6_subtrees(void)
{
......@@ -1024,7 +1016,7 @@ kernel_route(int operation, int table,
ipv4 = v4mapped(gate);
use_src = (!is_default(src, src_plen) && kernel_disambiguate(ipv4));
use_src = !is_default(src, src_plen);
kdebugf("kernel_route: %s %s from %s "
"table %d metric %d dev %d nexthop %s\n",
......@@ -1447,64 +1439,6 @@ filter_addresses(struct nlmsghdr *nh, struct kernel_addr *addr)
return 1;
}
static int
filter_kernel_rules(struct nlmsghdr *nh, struct kernel_rule *rule)
{
int len, has_priority = 0, has_table = 0;
struct rtmsg *rtm = NULL;
struct rtattr *rta = NULL;
int is_v4 = 0;
len = nh->nlmsg_len;
rtm = (struct rtmsg*)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
rule->src_plen = rtm->rtm_src_len;
memset(rule->src, 0, sizeof(rule->src));
rule->table = rtm->rtm_table;
if(rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6) {
kdebugf("filter_rules: Unknown family: %d\n", rtm->rtm_family);
return -1;
}
is_v4 = rtm->rtm_family == AF_INET;
rta = RTM_RTA(rtm);
len -= NLMSG_ALIGN(sizeof(*rtm));
for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
switch(rta->rta_type) {
case FRA_UNSPEC: break;
case FRA_SRC:
rule->src_plen = GET_PLEN(rtm->rtm_src_len, is_v4);
COPY_ADDR(rule->src, rta, is_v4);
break;
case FRA_PRIORITY:
rule->priority = *(unsigned int*)RTA_DATA(rta);
has_priority = 1;
break;
case FRA_TABLE:
rule->table = *(int*)RTA_DATA(rta);
has_table = 1;
break;
default:
kdebugf("filter_rules: Unknown rule attribute: %d.\n",
rta->rta_type);
break;
}
}
kdebugf("filter_rules: from %s prio %d table %d\n",
format_prefix(rule->src, rule->src_plen),
has_priority ? rule->priority : -1, rule->table);
if(!has_priority || !has_table)
return 0;
return 1;
}
static int
filter_netlink(struct nlmsghdr *nh, struct kernel_filter *filter)
{
......@@ -1513,7 +1447,6 @@ filter_netlink(struct nlmsghdr *nh, struct kernel_filter *filter)
struct kernel_route route;
struct kernel_addr addr;
struct kernel_link link;
struct kernel_rule rule;
} u;
switch(nh->nlmsg_type) {
......@@ -1535,12 +1468,6 @@ filter_netlink(struct nlmsghdr *nh, struct kernel_filter *filter)
rc = filter_addresses(nh, &u.addr);
if(rc <= 0) break;
return filter->addr(&u.addr, filter->addr_closure);
case RTM_NEWRULE:
case RTM_DELRULE:
if(!filter->rule) break;
rc = filter_kernel_rules(nh, &u.rule);
if(rc <= 0) break;
return filter->rule(&u.rule, filter->rule_closure);
default:
kdebugf("filter_netlink: unexpected message type %d\n",
nh->nlmsg_type);
......@@ -1570,142 +1497,3 @@ kernel_callback(struct kernel_filter *filter)
return 0;
}
/* Routing table's rules */
int
add_rule(int prio, const unsigned char *src_prefix, int src_plen, int table)
{
char buffer[64] = {0}; /* 56 needed */
struct nlmsghdr *message_header = (void*)buffer;
struct rtmsg *message = NULL;
struct rtattr *current_attribute = NULL;
int is_v4 = v4mapped(src_prefix);
int addr_size = is_v4 ? sizeof(struct in_addr) : sizeof(struct in6_addr);
kdebugf("Add rule v%c prio %d from %s\n", is_v4 ? '4' : '6', prio,
format_prefix(src_prefix, src_plen));
if(is_v4) {
src_prefix += 12;
src_plen -= 96;
if(src_plen < 0) {
errno = EINVAL;
return -1;
}
}
#if RTA_ALIGNTO != NLMSG_ALIGNTO
#error "RTA_ALIGNTO != NLMSG_ALIGNTO"
#endif
/* Set the header */
message_header->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
message_header->nlmsg_type = RTM_NEWRULE;
message_header->nlmsg_len = NLMSG_ALIGN(sizeof(struct nlmsghdr));
/* Append the message */
message = NLMSG_DATA(message_header);
message->rtm_family = is_v4 ? AF_INET : AF_INET6;
message->rtm_dst_len = 0;
message->rtm_src_len = src_plen;
message->rtm_tos = 0;
message->rtm_table = table;
message->rtm_protocol = RTPROT_BABEL;
message->rtm_scope = RT_SCOPE_UNIVERSE;
message->rtm_type = RTN_UNICAST;
message->rtm_flags = 0;
message_header->nlmsg_len += NLMSG_ALIGN(sizeof(struct rtmsg));
/* Append each attribute */
current_attribute = RTM_RTA(message);
/* prio */
current_attribute->rta_len = RTA_LENGTH(sizeof(int));
current_attribute->rta_type = FRA_PRIORITY;
*(int*)RTA_DATA(current_attribute) = prio;
message_header->nlmsg_len += current_attribute->rta_len;
current_attribute = (void*)
((char*)current_attribute + current_attribute->rta_len);
/* src */
current_attribute->rta_len = RTA_LENGTH(addr_size);
current_attribute->rta_type = FRA_SRC;
memcpy(RTA_DATA(current_attribute), src_prefix, addr_size);
message_header->nlmsg_len += current_attribute->rta_len;
current_attribute = (void*)
((char*)current_attribute + current_attribute->rta_len);
/* send message */
if(message_header->nlmsg_len > 64) {
errno = EINVAL;
return -1;
}
return netlink_talk(message_header);
}
int
flush_rule(int prio, int family)
{
char buffer[64] = {0}; /* 36 needed */
struct nlmsghdr *message_header = (void*)buffer;
struct rtmsg *message = NULL;
struct rtattr *current_attribute = NULL;
kdebugf("Flush rule v%c prio %d\n", family == AF_INET ? '4' : '6', prio);
#if RTA_ALIGNTO != NLMSG_ALIGNTO
#error "RTA_ALIGNTO != NLMSG_ALIGNTO"
#endif
/* Set the header */
message_header->nlmsg_flags = NLM_F_REQUEST;
message_header->nlmsg_type = RTM_DELRULE;
message_header->nlmsg_len = NLMSG_ALIGN(sizeof(struct nlmsghdr));
/* Append the message */
message = NLMSG_DATA(message_header);
message->rtm_family = family;
message->rtm_dst_len = 0;
message->rtm_src_len = 0;
message->rtm_tos = 0;
message->rtm_table = 0;
message->rtm_protocol = RTPROT_BABEL;
message->rtm_scope = RT_SCOPE_UNIVERSE;
message->rtm_type = RTN_UNSPEC;
message->rtm_flags = 0;
message_header->nlmsg_len += NLMSG_ALIGN(sizeof(struct rtmsg));
/* Append each attribute */
current_attribute = RTM_RTA(message);
/* prio */
current_attribute->rta_len = RTA_LENGTH(sizeof(int));
current_attribute->rta_type = FRA_PRIORITY;
*(int*)RTA_DATA(current_attribute) = prio;
message_header->nlmsg_len += current_attribute->rta_len;
current_attribute = (void*)
((char*)current_attribute + current_attribute->rta_len);
/* send message */
if(message_header->nlmsg_len > 64) {
errno = EINVAL;
return -1;
}
return netlink_talk(message_header);
}
int
change_rule(int new_prio, int old_prio,
const unsigned char *src, int plen, int table)
{
int rc;
kdebugf("/Swap: ");
rc = add_rule(new_prio, src, plen, table);
if(rc < 0)
return rc;
kdebugf("\\Swap: ");
return flush_rule(old_prio, v4mapped(src) ? AF_INET : AF_INET6);
}
......@@ -389,12 +389,6 @@ kernel_interface_channel(const char *ifname, int ifindex)
return -1;
}
int
kernel_disambiguate(int v4)
{
return 0;
}
int
kernel_has_ipv6_subtrees(void)
{
......@@ -852,29 +846,6 @@ kernel_callback(struct kernel_filter *filter)
}
int
add_rule(int prio, const unsigned char *src_prefix, int src_plen, int table)
{
errno = ENOSYS;
return -1;
}
int
flush_rule(int prio, int family)
{
errno = ENOSYS;
return -1;
}
int
change_rule(int new_prio, int old_prio,
const unsigned char *src, int plen, int table)
{
errno = ENOSYS;
return -1;
}
/* Local Variables: */
/* c-basic-offset: 4 */
/* indent-tabs-mode: nil */
......
......@@ -39,7 +39,6 @@ THE SOFTWARE.
#include "resend.h"
#include "configuration.h"
#include "local.h"
#include "disambiguation.h"
struct babel_route **routes = NULL;
static int route_slots = 0, max_route_slots = 0;
......@@ -431,7 +430,7 @@ route_stream_done(struct route_stream *stream)
free(stream);
}
int
static int
metric_to_kernel(int metric)
{
if(metric >= INFINITY) {
......@@ -462,6 +461,30 @@ move_installed_route(struct babel_route *route, int i)
}
}
static int
change_route(int operation, const struct babel_route *route, int metric,
const unsigned char *new_next_hop,
int new_ifindex, int new_metric)
{
struct filter_result filter_result;
unsigned char *pref_src = NULL;
unsigned int ifindex = route->neigh->ifp->ifindex;
int m = install_filter(route->src->prefix, route->src->plen,
route->src->src_prefix, route->src->src_plen,
ifindex, &filter_result);
if (m < INFINITY)
pref_src = filter_result.pref_src;
int table = filter_result.table ? filter_result.table : export_table;
return kernel_route(operation, table, route->src->prefix, route->src->plen,
route->src->src_prefix, route->src->src_plen, pref_src,
route->nexthop, ifindex,
metric, new_next_hop, new_ifindex, new_metric,
operation == ROUTE_MODIFY ? table : 0);
}
void
install_route(struct babel_route *route)
{
......@@ -484,9 +507,15 @@ install_route(struct babel_route *route)
return;
}
rc = kinstall_route(route);
if(rc < 0 && errno != EEXIST)
debugf("install_route(%s from %s)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen));
rc = change_route(ROUTE_ADD, route, metric_to_kernel(route_metric(route)),
NULL, 0, 0);
if(rc < 0 && errno != EEXIST) {
perror("kernel_route(ADD)");
return;
}
route->installed = 1;
move_installed_route(route, i);
......@@ -497,12 +526,22 @@ install_route(struct babel_route *route)
void
uninstall_route(struct babel_route *route)
{
int rc;
if(!route->installed)
return;
route->installed = 0;
kuninstall_route(route);
debugf("uninstall_route(%s from %s)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen));
rc = change_route(ROUTE_FLUSH, route, metric_to_kernel(route_metric(route)),
NULL, 0, 0);
if(rc < 0) {
perror("kernel_route(FLUSH)");
return;
}
local_notify_route(route, LOCAL_CHANGE);
}
......@@ -510,7 +549,6 @@ uninstall_route(struct babel_route *route)
/* 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)
{
......@@ -528,9 +566,16 @@ switch_routes(struct babel_route *old, struct babel_route *new)
fprintf(stderr, "WARNING: switching to unfeasible route "
"(this shouldn't happen).");
rc = kswitch_routes(old, new);
if(rc < 0)
debugf("switch_routes(%s from %s)\n",
format_prefix(old->src->prefix, old->src->plen),
format_prefix(old->src->src_prefix, old->src->src_plen));
rc = change_route(ROUTE_MODIFY, old, metric_to_kernel(route_metric(old)),
new->nexthop, new->neigh->ifp->ifindex,
metric_to_kernel(route_metric(new)));
if(rc < 0) {
perror("kernel_route(MODIFY)");
return;
}
old->installed = 0;
new->installed = 1;
......@@ -546,17 +591,21 @@ 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);
int old_metric = metric_to_kernel(route_metric(route)),
new_metric = metric_to_kernel(MIN(refmetric + cost + add, INFINITY));
if(route->installed && old != new) {
if(route->installed && old_metric != new_metric) {
int rc;
rc = kchange_route_metric(route, refmetric, cost, add);
if(rc < 0)
debugf("change_route_metric(%s from %s, %d -> %d)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen),
old_metric, new_metric);
rc = change_route(ROUTE_MODIFY, route, old_metric, route->nexthop,
route->neigh->ifp->ifindex, new_metric);
if(rc < 0) {
perror("kernel_route(MODIFY metric)");
return;
}
}
/* Update route->smoothed_metric using the old metric. */
......
......@@ -84,7 +84,6 @@ void flush_interface_routes(struct interface *ifp, int v4only);
struct route_stream *route_stream(int which);
struct babel_route *route_stream_next(struct route_stream *stream);
void route_stream_done(struct route_stream *stream);
int metric_to_kernel(int metric);
void install_route(struct babel_route *route);
void uninstall_route(struct babel_route *route);
int route_feasible(struct babel_route *route);
......
/*
Copyright (c) 2015 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "babeld.h"
#include "util.h"
#include "kernel.h"
#include "configuration.h"
#include "rule.h"
int src_table_idx = 10;
int src_table_prio = 100;
/* The table used for non-specific routes is "export_table", therefore, we can
take the convention of plen == 0 <=> empty table. */
struct rule {
unsigned char src[16];
unsigned char plen;
unsigned char table;
};
/* rules contains information about the rules we installed. It is an array
indexed by: <table priority> - src_table_prio.
(First entries are the most specific, since they have priority.) */
static struct rule rules[SRC_TABLE_NUM];
/* used tables is indexed by: <table number> - src_table_idx
used_tables[i] == 1 <=> the table number (i + src_table_idx) is used */
static char used_tables[SRC_TABLE_NUM] = {0};
static int
get_free_table(void)
{
int i;
for(i = 0; i < SRC_TABLE_NUM; i++)
if(!used_tables[i]) {
used_tables[i] = 1;
return i + src_table_idx;
}
return -1;
}
static void
release_table(int i)
{
used_tables[i - src_table_idx] = 0;
}
static int
find_hole_around(int i)
{
int j;
for(j = i; j < SRC_TABLE_NUM; j++)
if(rules[j].plen == 0)
return j;
for(j = i - 1; j >= 0; j--)
if(rules[j].plen == 0)
return j;
return -1;
}
static int
shift_rule(int from, int to)
{
int dec = (from < to) ? 1 /* right */ : -1 /* left */;
int rc;
while(to != from) {
to -= dec;
rc = change_rule(to + dec + src_table_prio, to + src_table_prio,
rules[to].src, rules[to].plen, rules[to].table);
if(rc < 0) {
perror("change_table_priority");
return -1;
}
rules[to+dec] = rules[to];
rules[to].plen = 0;
}
return 0;
}
/* Return a new table at index [idx] of rules. If cell at that index is not
free, we need to shift cells (and rules). If it's full, return NULL. */
static struct rule *
insert_table(const unsigned char *src, unsigned short src_plen, int idx)
{
int table;
int rc;
int hole;
if(idx < 0 || idx >= SRC_TABLE_NUM) {
fprintf(stderr, "Incorrect table number %d\n", idx);
return NULL;
}
table = get_free_table();
if(table < 0) {
kdebugf("All allowed routing tables are used!\n");
return NULL;
}
hole = find_hole_around(idx);
if(hole < 0) {
fprintf(stderr, "Have free table but not free rule.\n");
goto fail;
}
rc = shift_rule(idx, hole);
if(rc < 0)
goto fail;
rc = add_rule(idx + src_table_prio, src, src_plen, table);
if(rc < 0) {
perror("add rule");
goto fail;
}
memcpy(rules[idx].src, src, 16);
rules[idx].plen = src_plen;
rules[idx].table = table;
return &rules[idx];
fail:
release_table(table);
return NULL;
}
/* Sorting rules in a well ordered fashion will increase code complexity and
decrease performances, because more rule shifts will be required, so more
system calls invoked. */
static int
find_table_slot(const unsigned char *src, unsigned short src_plen, int *found)
{
struct rule *kr = NULL;
int i;
*found = 0;
for(i = 0; i < SRC_TABLE_NUM; i++) {
kr = &rules[i];
if(kr->plen == 0)
return i;
switch(prefix_cmp(src, src_plen, kr->src, kr->plen)) {
case PST_LESS_SPECIFIC:
case PST_DISJOINT:
continue;
case PST_MORE_SPECIFIC:
return i;
case PST_EQUALS:
*found = 1;
return i;
}
}
return -1;
}
int
find_table(const unsigned char *dest, unsigned short plen,
const unsigned char *src, unsigned short src_plen)
{
struct rule *kr = NULL;
int i, found;
if(is_default(src, src_plen)) {
return export_table;
} else if(kernel_disambiguate(v4mapped(dest))) {
return export_table;
}
i = find_table_slot(src, src_plen, &found);
if(found)
return rules[i].table;
if(i < 0)
return -1;
kr = insert_table(src, src_plen, i);
return kr == NULL ? -1 : kr->table;
}
void
release_tables(void)
{
int i;
for(i = 0; i < SRC_TABLE_NUM; i++) {
if(rules[i].plen != 0) {
flush_rule(i + src_table_prio,
v4mapped(rules[i].src) ? AF_INET : AF_INET6);
rules[i].plen = 0;
}
used_tables[i] = 0;
}
}
static int
filter_rule(struct kernel_rule *rule, void *data)
{
int i;
char (*rule_exists)[2][SRC_TABLE_NUM] = data;
int is_v4 = v4mapped(rule->src);
int r = is_v4 ? 0 : 1;
if(martian_prefix(rule->src, rule->src_plen))
return 0;
i = rule->priority - src_table_prio;
if(i < 0 || SRC_TABLE_NUM <= i)
return 0;
if(prefix_cmp(rule->src, rule->src_plen,
rules[i].src, rules[i].plen) == PST_EQUALS &&
rule->table == rules[i].table &&
(*rule_exists)[r][i] == 0)
(*rule_exists)[r][i] = 1;
else
(*rule_exists)[r][i] = -1;
return 1;
}
/* This functions should be executed wrt the code just bellow: [rule_exists]
contains is a boolean array telling whether the rules we should have
installed in the kernel are installed or not. If they aren't, then reinstall
them (this can append when rules are modified by third parties). */
static void
install_missing_rules(char rule_exists[SRC_TABLE_NUM], int v4)
{
int i, rc;
for(i = 0; i < SRC_TABLE_NUM; i++) {
int priority = i + src_table_prio;
if(rule_exists[i] == 1)
continue;
if(rule_exists[i] != 0) {
int rc;
do {
rc = flush_rule(priority, v4 ? AF_INET : AF_INET6);
} while(rc >= 0);
if(errno != ENOENT && errno != EEXIST)
fprintf(stderr,
"Cannot remove rule %d: from %s table %d (%s)\n",
priority,
format_prefix(rules[i].src, rules[i].plen),
rules[i].table, strerror(errno));
}
/* Be wise, our priority are both for v4 and v6 (does not overlap). */
if(!!v4mapped(rules[i].src) == !!v4 && rules[i].plen != 0) {
rc = add_rule(priority, rules[i].src,
rules[i].plen, rules[i].table);
if(rc < 0)
fprintf(stderr,
"Cannot install rule %d: from %s table %d (%s)\n",
priority,
format_prefix(rules[i].src, rules[i].plen),
rules[i].table, strerror(errno));
}
}
}
int
check_rules(void)
{
int rc;
char rule_exists[2][SRC_TABLE_NUM]; /* v4, v6 */
struct kernel_filter filter = {0};
filter.rule = filter_rule;
filter.rule_closure = (void*) rule_exists;
memset(rule_exists, 0, sizeof(rule_exists));
rc = kernel_dump(CHANGE_RULE, &filter);
if(rc < 0)
return -1;
install_missing_rules(rule_exists[0], 1);
install_missing_rules(rule_exists[1], 0);
return 0;
}
/*
Copyright (c) 2015 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.
*/
#define SRC_TABLE_NUM 10
extern int src_table_idx; /* number of the first table */
extern int src_table_prio; /* first prio range */
/* Return the number of the table using src_plen, allocate the table in the
kernel if necessary. */
int find_table(const unsigned char *dest, unsigned short plen,
const unsigned char *src, unsigned short src_plen);
void release_tables(void);
int check_rules(void);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment