0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-16 18:35:19 +00:00

Basic route aggregation

User can specify list of route attributes in the configuration file
and run route aggregation on the export side of the pipe protocol.
Routes are sorted and for every group of equivalent routes
new route is created and exported to the routing table.
It is also possible to specify filter which will run for every
route before aggregation.
Furthermore, it will be possible to set attributes of new routes
according to attributes of the aggregated routes.

This is a work in progress.
This commit is contained in:
Igor Putovny 2023-06-21 13:15:07 +02:00
parent 260de00383
commit c48de3cf85
24 changed files with 1239 additions and 56 deletions

View File

@ -23,6 +23,9 @@ INSTALL=@INSTALL@
INSTALL_PROGRAM=@INSTALL_PROGRAM@
INSTALL_DATA=@INSTALL_DATA@
STAYRTR_BINARY=@STAYRTR_BINARY@
NETLAB_DATA_SUFFIX=v2
client=$(addprefix $(exedir)/,@CLIENT@)
daemon=$(exedir)/bird
protocols=@protocols@
@ -186,6 +189,16 @@ check: tests tests_run
tests: $(tests_targets)
tests_run: $(tests_targets_ok)
aux-test-prepare: all
$(E)echo Preparing netlab test suite ...
$(Q)cd $(srcdir) && git submodule update --init --force --checkout aux-tools
$(Q)cd $(srcdir)/aux-tools && git clean -fxdq || true
$(Q)cd $(srcdir)/aux-tools/netlab/common && ln $(shell readlink -f $(exedir)/bird) && ln $(shell readlink -f $(exedir)/birdc)
$(Q)cd $(srcdir)/aux-tools/netlab/common && ln -s $(STAYRTR_BINARY) stayrtr
$(E)echo Netlab test suite prepared.
-include $(srcdir)/aux-tools/netlab/tests-$(NETLAB_DATA_SUFFIX).mk
STATIC_CHECKERS_ENABLE := nullability.NullableDereferenced nullability.NullablePassedToNonnull nullability.NullableReturnedFromNonnull optin.portability.UnixAPI valist.CopyToSelf valist.Uninitialized valist.Unterminated
STATIC_CHECKERS_DISABLE := deadcode.DeadStores
STATIC_SCAN_FLAGS := -o $(objdir)/static-scan/ $(addprefix -enable-checker ,$(STATIC_CHECKERS_ENABLE)) $(addprefix -disable-checker ,$(STATIC_CHECKERS_DISABLE))

View File

@ -97,6 +97,8 @@ CF_DECLS
struct settle_config settle;
mpls_label_stack *mls;
struct bytestring *bs;
struct aggr_item *ai;
struct aggr_item_linearized *ail;
}
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT

View File

@ -80,10 +80,18 @@ AC_ARG_WITH([iproutedir],
[given_iproutedir="yes"]
)
AC_ARG_WITH([stayrtr],
[AS_HELP_STRING([--with-stayrtr=PATH], [path to stayrtr built binary for RPKI testing @<:@/usr/bin/stayrtr@:>@])],
[],
[with_stayrtr="/usr/bin/stayrtr"]
)
AC_ARG_VAR([FLEX], [location of the Flex program])
AC_ARG_VAR([BISON], [location of the Bison program])
AC_ARG_VAR([M4], [location of the M4 program])
AC_SUBST([STAYRTR_BINARY], [${with_stayrtr}])
if test "$enable_debug_expensive" = yes; then
enable_debug=yes
fi

View File

@ -321,8 +321,8 @@ f_lval_getter(struct f_lval *lval)
{
switch (lval->type) {
case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_GET, lval->sym);
case F_LVAL_SA: return f_new_inst(FI_RTA_GET, lval->sa);
case F_LVAL_EA: return f_new_inst(FI_EA_GET, lval->da);
case F_LVAL_SA: return f_new_inst(FI_RTA_GET, lval->rte, lval->sa);
case F_LVAL_EA: return f_new_inst(FI_EA_GET, lval->rte, lval->da);
default: bug("Unknown lval type");
}
}
@ -356,6 +356,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR,
INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
ROUTES,
IF, THEN, ELSE, CASE,
FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
@ -462,6 +463,7 @@ type:
| CLIST { $$ = T_CLIST; }
| ECLIST { $$ = T_ECLIST; }
| LCLIST { $$ = T_LCLIST; }
| ROUTE { $$ = T_ROUTE; }
| type SET {
switch ($1) {
case T_INT:
@ -839,7 +841,7 @@ symbol_value: symbol_known
$$ = f_new_inst(FI_VAR_GET, $1);
break;
case SYM_ATTRIBUTE:
$$ = f_new_inst(FI_EA_GET, *$1->attribute);
$$ = f_new_inst(FI_EA_GET, f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL }), *$1->attribute);
break;
default:
cf_error("Can't get value of symbol %s", $1->name);
@ -900,9 +902,9 @@ term:
| constant { $$ = $1; }
| constructor { $$ = $1; }
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
| static_attr { $$ = f_new_inst(FI_RTA_GET, f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL }), $1); }
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL }), $1); }
| term_dot_method
@ -947,6 +949,16 @@ term:
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
| function_call
| ROUTES { $$ = f_new_inst(FI_ROUTES_GET); }
| CF_SYM_KNOWN '.' static_attr {
cf_assert_symbol($1, SYM_VARIABLE);
$$ = f_new_inst(FI_RTA_GET, f_new_inst(FI_VAR_GET, $1), $3);
}
| CF_SYM_KNOWN '.' dynamic_attr {
cf_assert_symbol($1, SYM_VARIABLE);
$$ = f_new_inst(FI_EA_GET, f_new_inst(FI_VAR_GET, $1), $3);
}
;
break_command:

View File

@ -54,6 +54,9 @@ static const char * const f_type_str[] = {
[T_LC] = "lc",
[T_LCLIST] = "lclist",
[T_RD] = "rd",
[T_ROUTE] = "route",
[T_ROUTES_BLOCK] = "block of routes",
};
const char *
@ -76,6 +79,7 @@ f_type_element_type(enum f_type t)
case T_CLIST: return T_PAIR;
case T_ECLIST: return T_EC;
case T_LCLIST: return T_LC;
case T_ROUTES_BLOCK: return T_ROUTE;
default: return T_VOID;
};
}
@ -204,6 +208,11 @@ val_compare(const struct f_val *v1, const struct f_val *v2)
return net_compare(v1->val.net, v2->val.net);
case T_STRING:
return strcmp(v1->val.s, v2->val.s);
case T_PATH:
return as_path_compare(v1->val.ad, v2->val.ad);
case T_ROUTE:
/* Fall through */
case T_ROUTES_BLOCK:
default:
return F_CMP_ERROR;
}
@ -286,6 +295,10 @@ val_same(const struct f_val *v1, const struct f_val *v2)
return same_tree(v1->val.t, v2->val.t);
case T_PREFIX_SET:
return trie_same(v1->val.ti, v2->val.ti);
case T_ROUTE:
return v1->val.rte == v2->val.rte;
case T_ROUTES_BLOCK:
return v1->val.ad == v2->val.ad;
default:
bug("Invalid type in val_same(): %x", v1->type);
}
@ -559,6 +572,36 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
return F_CMP_ERROR;
}
/*
* rte_format - format route information
*/
static void
rte_format(const struct rte *rte, buffer *buf)
{
if (rte)
buffer_print(buf, "Route [%d] to %N from %s.%s via %s",
rte->src->global_id, rte->net->n.addr,
rte->sender->proto->name, rte->sender->name,
rte->src->proto->name);
else
buffer_puts(buf, "[No route]");
}
static void
rte_block_format(const struct adata *ad, buffer *buf)
{
struct rte **routes = ((struct rte_block *)ad)->routes;
const int count = (ad->length - sizeof(struct rte_block) + sizeof(struct adata)) / sizeof(struct rte *);
buffer_print(buf, "Block of %d routes:");
for (int i = 0; i < count; i++)
{
buffer_print(buf, "\t%d: ", i);
rte_format(routes[i], buf);
}
}
/*
* val_format - format filter value
*/
@ -587,6 +630,8 @@ val_format(const struct f_val *v, buffer *buf)
case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
case T_PATH_MASK: pm_format(v->val.path_mask, buf); return;
case T_ROUTE: rte_format(v->val.rte, buf); return;
case T_ROUTES_BLOCK: rte_block_format(v->val.ad, buf); return;
default: buffer_print(buf, "[unknown type %x]", v->type); return;
}
}

View File

@ -59,6 +59,8 @@ enum f_type {
T_RD = 0x2a, /* Route distinguisher for VPN addresses */
T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */
T_ROUTE = 0x78,
T_ROUTES_BLOCK = 0x79,
T_SET = 0x80,
T_PREFIX_SET = 0x81,
} PACKED;
@ -69,6 +71,38 @@ struct f_method {
uint arg_num;
};
typedef struct adata {
uint length; /* Length of data */
byte data[0];
} adata;
extern const adata null_adata; /* adata of length 0 */
/* Large community value */
typedef struct lcomm {
u32 asn;
u32 ldp1;
u32 ldp2;
} lcomm;
struct f_path_mask_item {
union {
u32 asn; /* PM_ASN */
const struct f_line *expr; /* PM_ASN_EXPR */
const struct f_tree *set; /* PM_ASN_SET */
struct { /* PM_ASN_RANGE */
u32 from;
u32 to;
};
};
int kind;
};
struct f_path_mask {
uint len;
struct f_path_mask_item item[0];
};
/* Filter value; size of this affects filter memory consumption */
struct f_val {
enum f_type type; /* T_* */
@ -84,6 +118,7 @@ struct f_val {
const struct adata *ad;
const struct f_path_mask *path_mask;
struct f_path_mask_item pmi;
struct rte *rte;
} val;
};
@ -129,6 +164,7 @@ enum f_lval_type {
/* Filter l-value */
struct f_lval {
enum f_lval_type type;
struct f_inst *rte;
union {
struct symbol *sym;
struct f_dynamic_attr da;
@ -202,6 +238,11 @@ struct f_trie_walk_state
const struct f_trie_node *stack[TRIE_STACK_LENGTH];
};
struct rte_block {
struct adata ad;
struct rte *routes[];
};
struct f_tree *f_new_tree(void);
struct f_tree *build_tree(struct f_tree *);
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);

View File

@ -541,6 +541,12 @@
RESULT_VAL(val);
}
INST(FI_ROUTES_GET, 0, 1) {
NEVER_CONSTANT;
FID_INTERPRET_BODY()
RESULT(T_ROUTES_BLOCK, ad, fs->val->val.ad);
}
METHOD_R(T_PATH, empty, 0, T_PATH, ad, &null_adata);
METHOD_R(T_CLIST, empty, 0, T_CLIST, ad, &null_adata);
METHOD_R(T_ECLIST, empty, 0, T_ECLIST, ad, &null_adata);
@ -605,6 +611,15 @@
METHOD_CONSTRUCTOR("!for_next");
}
INST(FI_ROUTES_BLOCK_FOR_NEXT, 3, 0) {
NEVER_CONSTANT;
ARG(1, T_ROUTE);
if (rte_set_walk(v1.val.ad, &v2.val.i, &v3.val.rte))
LINE(2,0);
METHOD_CONSTRUCTOR("!for_next");
}
INST(FI_CONDITION, 1, 0) {
ARG(1, T_BOOL);
if (v1.val.i)
@ -642,11 +657,13 @@
}
}
INST(FI_RTA_GET, 0, 1) {
INST(FI_RTA_GET, 1, 1) {
{
STATIC_ATTR;
ACCESS_RTE;
struct rta *rta = (*fs->rte)->attrs;
ARG(1, T_ROUTE);
STATIC_ATTR;
struct rta *rta = v1.val.rte ? v1.val.rte->attrs : (*fs->rte)->attrs;
switch (sa.sa_code)
{
@ -785,13 +802,15 @@
}
}
INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */
DYNAMIC_ATTR;
INST(FI_EA_GET, 1, 1) { /* Access to extended attributes */
ACCESS_RTE;
ACCESS_EATTRS;
ARG(1, T_ROUTE);
DYNAMIC_ATTR;
RESULT_TYPE(da.f_type);
{
eattr *e = ea_find(*fs->eattrs, da.ea_code);
struct ea_list *eal = v1.val.rte ? v1.val.rte->attrs->eattrs : *fs->eattrs;
eattr *e = ea_find(eal, da.ea_code);
if (!e) {
/* A special case: undefined as_path looks like empty as_path */

View File

@ -90,6 +90,9 @@ struct filter_state {
/* Buffer for log output */
struct buffer buf;
/* Pointers to routes we are aggregating */
const struct f_val *val;
/* Filter execution flags */
int flags;
};
@ -237,6 +240,29 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
return F_ERROR;
}
/**
* Evaluation for attributes comparison
*/
enum filter_return
f_aggr_eval_line(const struct f_line *line, struct rte **rte, struct linpool *pool, struct f_val *pres)
{
/* Initialize the filter state */
filter_state = (struct filter_state) {
.stack = &filter_stack,
.rte = rte,
.pool = pool,
};
LOG_BUFFER_INIT(filter_state.buf);
/* Run the interpreter itself */
enum filter_return fret = interpret(&filter_state, line, pres);
if (filter_state.old_rta)
log("Warning: route changed during filter evaluation");
return fret;
}
/**
* f_run - run a filter for a route
@ -271,6 +297,12 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
if (filter == FILTER_REJECT)
return F_REJECT;
return f_run_val(filter, rte, tmp_pool, NULL, flags);
}
enum filter_return
f_run_val(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, const struct f_val *val, int flags)
{
int rte_cow = ((*rte)->flags & REF_COW);
DBG( "Running filter `%s'...", filter->name );
@ -279,6 +311,7 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
.stack = &filter_stack,
.rte = rte,
.pool = tmp_pool,
.val = val,
.flags = flags,
};

View File

@ -51,7 +51,9 @@ struct filter {
struct rte;
enum filter_return f_aggr_eval_line(const struct f_line *line, struct rte **rte, struct linpool *pool, struct f_val *pres);
enum filter_return f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
enum filter_return f_run_val(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, const struct f_val *val, int flags);
enum filter_return f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool);
uint f_eval_int(const struct f_line *expr);
enum filter_return f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf);

View File

@ -1,4 +1,4 @@
src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c aggregator.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)

View File

@ -669,6 +669,29 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_va
return res;
}
int
as_path_compare(const struct adata *path1, const struct adata *path2)
{
uint pos1 = 0;
uint pos2 = 0;
uint val1 = 0;
uint val2 = 0;
while (1)
{
int res1 = as_path_walk(path1, &pos1, &val1);
int res2 = as_path_walk(path2, &pos2, &val2);
if (res1 == 0 && res2 == 0)
return 0;
if (val1 == val2)
continue;
return val1 < val2 ? -1 : 1;
}
}
int
as_path_walk(const struct adata *path, uint *pos, uint *val)
{

View File

@ -741,3 +741,19 @@ lc_set_walk(const struct adata *list, uint *pos, lcomm *val)
return 1;
}
int
rte_set_walk(const struct adata *list, u32 *pos, struct rte **val)
{
if (!list)
return 0;
if (*pos >= (u32)rte_set_get_size(list))
return 0;
struct rte *res = rte_set_get_data(list, *pos);
*val = res;
*pos += 1;
return 1;
}

671
nest/aggregator.c Normal file
View File

@ -0,0 +1,671 @@
/*
* BIRD Internet Routing Daemon -- Route aggregation
*
* (c) 2023
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* DOC: Route aggregation
*
* This is an implementation of route aggregation functionality.
* It enables user to specify a set of route attributes in the configuarion file
* and then, for a given destination (net), aggregate routes with the same
* values of these attributes into a single multi-path route.
*
* Structure &channel contains pointer to aggregation list which is represented
* by &aggr_list_linearized. In rt_notify_aggregated(), attributes from this
* list are evaluated for every route of a given net and results are stored
* in &rte_val_list which contains pointer to this route and array of &f_val.
* Array of pointers to &rte_val_list entries is sorted using
* sort_rte_val_list(). For comparison of &f_val structures, val_compare()
* is used. Comparator function is written so that sorting is stable. If all
* attributes have the same values, routes are compared by their global IDs.
*
* After sorting, &rte_val_list entries containing equivalent routes will be
* adjacent to each other. Function process_rte_list() iterates through these
* entries to identify sequences of equivalent routes. New route will be
* created for each such sequence, even if only from a single route.
* Only attributes from the aggreagation list will be set for the new route.
* New &rta is created and prepare_rta() is used to copy static and dynamic
* attributes to new &rta from &rta of the original route. New route is created
* by create_merged_rte() from new &rta and exported to the routing table.
*/
#undef LOCAL_DEBUG
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/string.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/data.h"
#include "lib/hash.h"
#include "lib/string.h"
#include "lib/alloca.h"
#include "lib/flowspec.h"
#include <stdlib.h>
/* Context of &f_val comparison. */
struct cmp_ctx {
const struct channel *c;
const struct network *net;
const int val_count;
u32 failed:1;
};
static linpool *rte_update_pool;
/*
* Set static attribute in @rta from static attribute in @old according to @sa.
*/
static void
rta_set_static_attr(struct rta *rta, const struct rta *old, struct f_static_attr sa)
{
switch (sa.sa_code)
{
case SA_FROM:
rta->from = old->from;
break;
case SA_GW:
rta->dest = RTD_UNICAST;
rta->nh.gw = old->nh.gw;
rta->nh.iface = old->nh.iface;
rta->nh.next = NULL;
rta->hostentry = NULL;
rta->nh.labels = 0;
break;
case SA_SCOPE:
rta->scope = old->scope;
break;
case SA_DEST:
rta->dest = old->dest;
rta->nh.gw = IPA_NONE;
rta->nh.iface = NULL;
rta->nh.next = NULL;
rta->hostentry = NULL;
rta->nh.labels = 0;
break;
case SA_IFNAME:
rta->dest = RTD_UNICAST;
rta->nh.gw = IPA_NONE;
rta->nh.iface = old->nh.iface;
rta->nh.next = NULL;
rta->hostentry = NULL;
rta->nh.labels = 0;
break;
case SA_GW_MPLS:
rta->nh.labels = old->nh.labels;
memcpy(&rta->nh.label, &old->nh.label, sizeof(u32) * old->nh.labels);
break;
case SA_WEIGHT:
rta->nh.weight = old->nh.weight;
break;
case SA_PREF:
rta->pref = old->pref;
break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
}
}
static int
get_dynamic_attr_count(const struct aggr_item_linearized *ail)
{
int ea_count = 0;
for (int i = 0; i < ail->count; i++)
if (ail->items[i].type == AGGR_ITEM_DYNAMIC_ATTR)
ea_count++;
return ea_count;
}
/*
* Copy static and dynamic attributes from @old to @new according to
* aggregation list @ail. Because route may not have any extended
* attributes, return real number of attributes that were copied.
*/
static int
prepare_rta(struct rta *new, const struct rta *old, const struct aggr_item_linearized *ail)
{
int pos = 0;
for (int i = 0; i < ail->count; i++)
{
if (ail->items[i].type == AGGR_ITEM_DYNAMIC_ATTR)
{
u32 ea_code = ail->items[i].da.ea_code;
const struct eattr *e = ea_find(old->eattrs, ea_code);
if (e)
new->eattrs->attrs[pos++] = *e;
}
else if (ail->items[i].type == AGGR_ITEM_STATIC_ATTR)
rta_set_static_attr(new, old, ail->items[i].sa);
}
return pos;
}
/*
* Find route with lowest ID in a sequence of rte_val_list entries
* within range [start, start + length).
* @start: first element in a sequence of equivalent routes
*/
static const struct rte *
find_rte_lowest_id(const struct rte_val_list **start, int length)
{
const struct rte *rte = start[0]->rte;
u32 id = rte->src->global_id;
for (int i = 1; i < length; i++)
{
u32 current = start[i]->rte->src->global_id;
if (current < id)
{
id = current;
rte = start[i]->rte;
}
}
log("Lowest ID: %d", id);
return rte;
}
static int
compare_f_val(const struct f_val *v1, const struct f_val *v2, struct cmp_ctx *ctx)
{
int result = val_compare(v1, v2);
if (result != F_CMP_ERROR)
return result;
ctx->failed = 1;
struct buffer buf;
LOG_BUFFER_INIT(buf);
buffer_puts(&buf, "v1 = ");
val_format(v1, &buf);
buffer_puts(&buf, ", v2 = ");
val_format(v2, &buf);
log(L_WARN "%s.%s: Error comparing values while aggregating routes to %N: %s",
ctx->c->proto->name, ctx->c->name, ctx->net->n.addr, buf.start);
bug("Sorting routes according to aggregation list: F_CMP_ERROR");
}
/*
* Compare list of &f_val entries.
* @count: number of &f_val entries
*/
static int
compare_val_list(const struct f_val *v1, const struct f_val *v2, struct cmp_ctx *ctx)
{
for (int i = 0; i < ctx->val_count; i++)
{
int res = compare_f_val(&v1[i], &v2[i], ctx);
if (res != 0)
return res;
}
return 0;
}
/*
* Comparator function for sorting array of pointers to &rte_val_list structures.
* Compare lists of &f_val associated with routes.
* If all values are equal, compare route's global IDs.
* @count: pointer to number of f_val entries
*/
static int
compare_val_list_id(const void *fst, const void *snd, void *context)
{
struct cmp_ctx *ctx = (struct cmp_ctx *)context;
for (int i = 0; i < ctx->val_count; i++)
{
/*
* This function receives two void pointers.
* Since we are sorting array of pointers, we have to cast this void
* pointer to struct rte_val_list** (pointer to first array element,
* which is a pointer). Dereference it once to access this element,
* which is struct rte_val_list*. Finally access f_val at position i
* and take its address, thus getting struct f_val*.
*/
const struct f_val *v1 = &(*(struct rte_val_list **)fst)->values[i];
const struct f_val *v2 = &(*(struct rte_val_list **)snd)->values[i];
int result = compare_f_val(v1, v2, ctx);
if (result != 0)
return result;
}
u32 id1 = (*(struct rte_val_list **)fst)->rte->src->global_id;
u32 id2 = (*(struct rte_val_list **)snd)->rte->src->global_id;
return id1 < id2 ? -1 : 1;
}
/*
* Sort array of pointers to &rte_val_list entries.
* @rte_val: first element in array of pointers to &rte_val_list
* @rte_count: number of &rte_val_list entries
* @val_count: number of &f_val entries in each &rte_val_list entry
*/
static void
sort_rte_val_list(const struct rte_val_list **rte_val, int rte_count, struct cmp_ctx *ctx)
{
log("======== Sorting routes... ========");
qsort_r(rte_val, rte_count, sizeof(struct rte_val_list *), compare_val_list_id, (void *)ctx);
for (int i = 0; i < rte_count; i++)
log("route ID: %d", rte_val[i]->rte->src->global_id);
}
/*
* Create and export new merged route.
* @old: first route in a sequence of equivalent routes that are to be merged
* @rte_val: first element in a sequence of equivalent rte_val_list entries
* @length: number of equivalent routes that are to be merged (at least 1)
* @ail: aggregation list
*/
static void
create_merged_rte(struct channel *c, struct network *net, const struct rte_val_list **rte_val,
int length, const struct aggr_item_linearized *ail, int refeed)
{
const struct rte *old = rte_val[0]->rte;
const struct rta *rta_old = old->attrs;
struct rta *rta_new = allocz(rta_size(rta_old));
int ea_count = get_dynamic_attr_count(ail);
struct ea_list *eal = allocz(sizeof(struct ea_list) + sizeof(struct eattr) * ea_count);
rta_new->dest = RTD_UNREACHABLE;
rta_new->eattrs = eal;
eal->next = NULL;
eal->count = prepare_rta(rta_new, rta_old, ail);
const struct rte *rte_lowest = find_rte_lowest_id(rte_val, length);
struct rte *new = rte_get_temp(rta_new, rte_lowest->src);
new->net = net;
do_rt_notify(c, net, new, NULL, refeed);
log("=============== CREATE MERGED ROUTE ===============");
log("New route created: id = %d, protocol: %s", new->src->global_id, new->src->proto->name);
log("===================================================");
struct rte_block *rb = allocz(sizeof(struct rte_block) + sizeof(struct rte *) * length);
rb->ad.length = sizeof(struct rte_block) + sizeof(struct rte *) * length - sizeof(struct adata);
for (int i = 0; i < length; i++)
rb->routes[i] = (struct rte *)rte_val[i]->rte;
struct f_val val = {
.type = T_ROUTES_BLOCK,
.val.ad = &rb->ad,
};
f_run_val(ail->merge_filter, &new, rte_update_pool, &val, 0);
}
/*
* Iterate through &rte_val_list entries and identify all sequences of
* equivalent routes.
* @rte_count: total number of routes being processed
* @val_count: number of &f_val entries with each route
* @ail: aggregation list
*/
static void
process_rte_list(struct channel *c, struct network *net, const struct rte_val_list **rte_val,
int rte_count, int val_count, const struct aggr_item_linearized *ail, int refeed)
{
if (rte_count == 1)
{
create_merged_rte(c, net, rte_val, 1, ail, refeed);
return;
}
struct cmp_ctx ctx = {
.c = c,
.net = net,
.val_count = val_count,
.failed = 0,
};
/*
* &start and &current are initially indices to first and second of
* &rte_val_list entries. If these entries contain equivalent routes,
* &current is incremented until non-equivalent route is found.
* [start, current) then define a range of routes that are to be merged.
* When non-equivalent route is found, &start is updated and the process
* continues until all entries are processed.
*/
int start = 0;
int current = 1;
log("RTE count: %d", rte_count);
log("VAL count: %d", val_count);
while (start < rte_count && current < rte_count)
{
int res = compare_val_list(&rte_val[start]->values[0], &rte_val[current]->values[0], &ctx);
/* At least two equivalent routes were found, try to find more. */
if (res == 0)
{
int merged = 1;
while (current < rte_count && res == 0)
{
log("Routes %d and %d are equal", rte_val[start]->rte->src->global_id, rte_val[current]->rte->src->global_id);
current++;
merged++;
if (current < rte_count)
res = compare_val_list(&rte_val[start]->values[0], &rte_val[current]->values[0], &ctx);
}
log("Creating merged route from %d routes", merged);
create_merged_rte(c, net, &rte_val[start], merged, ail, refeed);
start = current;
current++;
}
else
{
log("Route %d and %d are NOT equal", rte_val[start]->rte->src->global_id, rte_val[current]->rte->src->global_id);
log("Creating standalone route from route %d", rte_val[start]->rte->src->global_id);
create_merged_rte(c, net, &rte_val[start], 1, ail, refeed);
start = current;
current++;
}
}
if (start < rte_count)
{
log("Creating standalone route from route %d", rte_val[start]->rte->src->global_id);
create_merged_rte(c, net, &rte_val[start], 1, ail, refeed);
}
}
static int
get_rte_count(const struct rte *rte)
{
int count = 0;
for (; rte; rte = rte->next)
count++;
return count;
}
static void
log_attributes(const struct f_val *val, int count)
{
struct buffer buf;
LOG_BUFFER_INIT(buf);
for (int i = 0; i < count; i++)
{
val_format(&val[i], &buf);
log("%s", buf.start);
}
}
/*
* Evaluate static attribute of @rt1 according to @sa
* and store result in @pos.
*/
static void
eval_static_attr(const struct rte *rt1, struct f_static_attr sa, struct f_val *pos)
{
const struct rta *rta = rt1->attrs;
#define RESULT(_type, value, result) \
do { \
pos->type = _type; \
pos->val.value = result; \
} while (0)
switch (sa.sa_code)
{
case SA_FROM: RESULT(sa.f_type, ip, rta->from); break;
case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break;
case SA_PROTO: RESULT(sa.f_type, s, rt1->src->proto->name); break;
case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break;
case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break;
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
case SA_PREF: RESULT(sa.f_type, i, rta->pref); break;
case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
}
#undef RESULT
}
/*
* Evaluate dynamic attribute of @rt1 according to @da
* and store result in @pos.
*/
static void
eval_dynamic_attr(const struct rte *rt1, struct f_dynamic_attr da, struct f_val *pos)
{
const struct rta *rta = rt1->attrs;
const struct eattr *e = ea_find(rta->eattrs, da.ea_code);
#define RESULT(_type, value, result) \
do { \
pos->type = _type; \
pos->val.value = result; \
} while (0)
#define RESULT_VOID \
do { \
pos->type = T_VOID; \
} while (0)
if (!e)
{
/* A special case: undefined as_path looks like empty as_path */
if (da.type == EAF_TYPE_AS_PATH)
{
RESULT(T_PATH, ad, &null_adata);
return;
}
/* The same special case for int_set */
if (da.type == EAF_TYPE_INT_SET)
{
RESULT(T_CLIST, ad, &null_adata);
return;
}
/* The same special case for ec_set */
if (da.type == EAF_TYPE_EC_SET)
{
RESULT(T_ECLIST, ad, &null_adata);
return;
}
/* The same special case for lc_set */
if (da.type == EAF_TYPE_LC_SET)
{
RESULT(T_LCLIST, ad, &null_adata);
return;
}
/* Undefined value */
RESULT_VOID;
return;
}
switch (e->type & EAF_TYPE_MASK)
{
case EAF_TYPE_INT:
RESULT(da.f_type, i, e->u.data);
break;
case EAF_TYPE_ROUTER_ID:
RESULT(T_QUAD, i, e->u.data);
break;
case EAF_TYPE_OPAQUE:
RESULT(T_ENUM_EMPTY, i, 0);
break;
case EAF_TYPE_IP_ADDRESS:
RESULT(T_IP, ip, *((ip_addr *) e->u.ptr->data));
break;
case EAF_TYPE_AS_PATH:
RESULT(T_PATH, ad, e->u.ptr);
break;
case EAF_TYPE_BITFIELD:
RESULT(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
break;
case EAF_TYPE_INT_SET:
RESULT(T_CLIST, ad, e->u.ptr);
break;
case EAF_TYPE_EC_SET:
RESULT(T_ECLIST, ad, e->u.ptr);
break;
case EAF_TYPE_LC_SET:
RESULT(T_LCLIST, ad, e->u.ptr);
break;
default:
bug("Unknown dynamic attribute type");
}
#undef RESULT
#undef RESULT_VOID
}
void
rt_notify_aggregated(struct channel *c, struct network *net, struct rte *new_changed, struct rte *old_changed,
struct rte *new_best, struct rte *old_best, int refeed)
{
const struct aggr_item_linearized *ail = c->ai_aggr;
const int attr_count = ail->count;
if (net->routes == NULL)
return;
struct rte *best0 = net->routes;
const int rte_count = get_rte_count(best0);
if (rte_count == 0)
return;
log("---- RT NOTIFY AGGREGATED ----");
log("Routes count: %d", rte_count);
log("Aggregation list attributes count: %d", attr_count);
log("aggr_item_linearized: %p", ail);
struct rte **rte_temp = allocz(sizeof(struct rte *) * rte_count);
struct rte **rte_free_temp = allocz(sizeof(struct rte *) * rte_count);
int rte_temp_count = 0;
int rte_free_count = 0;
/* Run filter for all routes before aggregation. */
for (struct rte *rt0 = best0; rt0; rt0 = rt0->next)
{
struct rte *rte_free = NULL;
struct rte *filtered = export_filter(c, rt0, &rte_free, 0);
if (filtered)
rte_temp[rte_temp_count++] = filtered;
if (rte_free)
rte_free_temp[rte_free_count++] = rte_free;
}
const struct rte_val_list **rte_val_list_ptrs = allocz(sizeof(struct rte_val_list *) * rte_count);
int rte_val_list_pos = 0;
for (int rte_idx = 0; rte_idx < rte_temp_count; rte_idx++)
{
struct rte *rt0 = rte_temp[rte_idx];
struct rte_val_list *rte_val = allocz(sizeof(struct rte_val_list) + sizeof(struct f_val) * attr_count);
rte_val->rte = rt0;
rte_val_list_ptrs[rte_val_list_pos++] = rte_val;
for (int val_idx = 0; val_idx < attr_count; val_idx++)
{
int type = ail->items[val_idx].type;
/* Evaluate route attributes. */
switch (type)
{
case AGGR_ITEM_TERM: {
const struct f_line *line = ail->items[val_idx].line;
struct rte *rt1 = rt0;
enum filter_return fret = f_aggr_eval_line(line, &rt1, rte_update_pool, &rte_val->values[val_idx]);
if (rt1 != rt0)
{
rte_free(rt1);
log(L_WARN "rt1 != rt0");
}
if (fret > F_RETURN)
log(L_WARN "%s.%s: Wrong number of items left on stack after evaluation of aggregation list", rt1->src->proto->name, rt1->sender);
break;
}
case AGGR_ITEM_STATIC_ATTR: {
struct f_val *pos = &rte_val->values[val_idx];
eval_static_attr(rt0, ail->items[val_idx].sa, pos);
break;
}
case AGGR_ITEM_DYNAMIC_ATTR: {
struct f_val *pos = &rte_val->values[val_idx];
eval_dynamic_attr(rt0, ail->items[val_idx].da, pos);
break;
}
default:
break;
}
}
log_attributes(&rte_val->values[0], attr_count);
}
struct cmp_ctx ctx = {
.c = c,
.net = net,
.val_count = attr_count,
.failed = 0,
};
sort_rte_val_list(rte_val_list_ptrs, rte_temp_count, &ctx);
if (ctx.failed)
log(L_WARN "%s.%s: Could not aggregate routes to %N due to previous errors", c->proto->name, c->name, net->n.addr);
else
process_rte_list(c, net, rte_val_list_ptrs, rte_temp_count, attr_count, ail, refeed);
for (int i = 0; i < rte_free_count; i++)
rte_free(rte_free_temp[i]);
}

View File

@ -51,6 +51,7 @@ u32 as_path_get_last_nonaggregated(const struct adata *path);
int as_path_contains(const struct adata *path, u32 as, int min);
int as_path_match_set(const struct adata *path, const struct f_tree *set);
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
int as_path_compare(const struct adata *path1, const struct adata *path2);
int as_path_walk(const struct adata *path, uint *pos, uint *val);
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
@ -65,24 +66,6 @@ static inline struct adata *as_path_prepend(struct linpool *pool, const struct a
#define PM_ASN_SET 5
#define PM_LOOP 6
struct f_path_mask_item {
union {
u32 asn; /* PM_ASN */
const struct f_line *expr; /* PM_ASN_EXPR */
const struct f_tree *set; /* PM_ASN_SET */
struct { /* PM_ASN_RANGE */
u32 from;
u32 to;
};
};
int kind;
};
struct f_path_mask {
uint len;
struct f_path_mask_item item[0];
};
int as_path_match(const struct adata *path, const struct f_path_mask *mask);
@ -157,9 +140,15 @@ static inline int ec_set_get_size(const struct adata *list)
static inline int lc_set_get_size(const struct adata *list)
{ return list->length / 12; }
static inline int rte_set_get_size(const struct adata *list)
{ return list->length / sizeof(struct rte *); }
static inline u32 *int_set_get_data(const struct adata *list)
{ return (u32 *) list->data; }
static inline struct rte *rte_set_get_data(const struct adata *list, u32 idx)
{ return ((struct rte_block *)list)->routes[idx]; }
static inline u32 ec_hi(u64 ec) { return ec >> 32; }
static inline u32 ec_lo(u64 ec) { return ec; }
@ -184,13 +173,6 @@ static inline u64 ec_ip4(enum ec_subtype kind, u64 key, u64 val)
static inline u64 ec_generic(u64 key, u64 val)
{ return (key << 32) | val; }
/* Large community value */
typedef struct lcomm {
u32 asn;
u32 ldp1;
u32 ldp2;
} lcomm;
#define LCOMM_LENGTH 12
static inline lcomm lc_get(const u32 *l, int i)
@ -238,6 +220,7 @@ int lc_set_max(const struct adata *list, lcomm *val);
int int_set_walk(const struct adata *list, uint *pos, u32 *val);
int ec_set_walk(const struct adata *list, uint *pos, u64 *val);
int lc_set_walk(const struct adata *list, uint *pos, lcomm *val);
int rte_set_walk(const struct adata *list, u32 *pos, struct rte **val);
void ec_set_sort_x(struct adata *set); /* Sort in place */

View File

@ -16,6 +16,7 @@ CF_HDR
#include "lib/mac.h"
CF_DEFINES
#define AGGR_ITEM_ALLOC cfg_allocz(sizeof(struct aggr_item))
static struct rtable_config *this_table;
static struct proto_config *this_proto;
@ -126,6 +127,7 @@ CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
CF_KEYWORDS(CHECK, LINK)
CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
CF_KEYWORDS(AGGREGATE, ON, MERGE, BY)
/* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
@ -151,6 +153,8 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
%type <net_ptr> r_args_for
%type <t> channel_sym
%type <c> channel_arg
%type <ai> aggr_item aggr_list
%type <ail> aggr_definition
CF_GRAMMAR
@ -298,6 +302,111 @@ proto_item:
;
aggr_list:
aggr_item
| aggr_list ',' aggr_item {
if ($3 == NULL) {
$$ = $1;
} else {
$$ = $3;
$$->next = $1;
}
}
;
aggr_item:
'(' term ')' {
$$ = AGGR_ITEM_ALLOC;
$$->internal.type = AGGR_ITEM_TERM;
$$->internal.line = f_linearize($2, 1);
}
| CF_SYM_KNOWN {
switch ($1->class) {
case SYM_ATTRIBUTE:
$$ = AGGR_ITEM_ALLOC;
$$->internal.type = AGGR_ITEM_DYNAMIC_ATTR;
$$->internal.da = *$1->attribute;
break;
case SYM_CONSTANT_RANGE:
$$ = NULL;
break;
default:
cf_error("Can't aggregate on symbol type %s.", cf_symbol_class_name($1));
}
}
| dynamic_attr {
$$ = AGGR_ITEM_ALLOC;
$$->internal.type = AGGR_ITEM_DYNAMIC_ATTR;
$$->internal.da = $1;
}
| static_attr {
$$ = AGGR_ITEM_ALLOC;
$$->internal.type = AGGR_ITEM_STATIC_ATTR;
$$->internal.sa = $1;
}
;
aggr_definition:
AGGREGATE ON aggr_list MERGE BY filter {
_Bool net_present = 0;
int count = 0;
for (const struct aggr_item *item = $3; item; item = item->next) {
if (item->internal.type == AGGR_ITEM_STATIC_ATTR && item->internal.sa.sa_code == SA_NET) {
net_present = 1;
continue;
}
count++;
}
if (!net_present)
cf_error("'NET' must be present");
size_t allocated = sizeof(struct aggr_item_linearized) + sizeof(struct aggr_item_internal) * count;
struct aggr_item_linearized *linear = cfg_allocz(allocated);
log("nest/config.Y: linear: %p, allocated: %d, count: %d", linear, allocated, count);
int pos = 0;
for (const struct aggr_item *item = $3; item; item = item->next) {
if (item->internal.type == AGGR_ITEM_STATIC_ATTR && item->internal.sa.sa_code == SA_NET)
continue;
linear->items[pos++] = item->internal;
}
linear->count = pos;
linear->merge_filter = $6;
int node = 1;
if (!linear) {
for (int i = 0; i < linear->count; i++) {
switch (linear->items[i].type) {
case AGGR_ITEM_TERM:
log("node %d, type: term", node);
break;
case AGGR_ITEM_STATIC_ATTR:
log("node %d, type: static", node);
break;
case AGGR_ITEM_DYNAMIC_ATTR:
log("node %d, type: dynamic", node);
break;
default:
log("node %d, type: other", node);
break;
}
node++;
}
}
$$ = linear;
}
;
channel_start: net_type
{
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
@ -311,6 +420,7 @@ channel_item_:
}
| IMPORT imexport { this_channel->in_filter = $2; }
| EXPORT imexport { this_channel->out_filter = $2; }
| EXPORT imexport aggr_definition { this_channel->out_filter = $2; this_channel->ai_aggr = $3; this_channel->ra_mode = RA_AGGREGATED; }
| RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; }
| IMPORT LIMIT limit_spec { this_channel->in_limit = $3; }
| EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }

View File

@ -90,6 +90,40 @@ proto_log_state_change(struct proto *p)
p->last_state_name_announced = NULL;
}
static int
aggr_item_same(const struct aggr_item_internal *fst, const struct aggr_item_internal *snd)
{
if (fst->type != snd->type)
return 0;
switch (fst->type)
{
case AGGR_ITEM_TERM:
return f_same(fst->line, snd->line);
case AGGR_ITEM_STATIC_ATTR:
return memcmp(&fst->sa, &snd->sa, sizeof(struct f_static_attr)) == 0;
case AGGR_ITEM_DYNAMIC_ATTR:
return memcmp(&fst->da, &snd->da, sizeof(struct f_dynamic_attr)) == 0;
default:
bug("Broken aggregating data");
}
}
static int
aggr_item_linearized_same(const struct aggr_item_linearized *fst, const struct aggr_item_linearized *snd)
{
if (!fst || !snd)
return 0;
if (fst->count != snd->count)
return 0;
for (int i = 0; i < fst->count; i++)
if (!aggr_item_same(&fst->items[i], &snd->items[i]))
return 0;
return 1;
}
struct channel_config *
proto_cf_find_channel(struct proto_config *pc, uint net_type)
@ -168,6 +202,7 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
c->ai_aggr = cf->ai_aggr;
c->rx_limit = cf->rx_limit;
c->in_limit = cf->in_limit;
c->out_limit = cf->out_limit;
@ -840,6 +875,14 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
/* Note that filter_same() requires arguments in (new, old) order */
int import_changed = !filter_same(cf->in_filter, c->in_filter);
int export_changed = !filter_same(cf->out_filter, c->out_filter);
if (cf->ra_mode == RA_AGGREGATED)
{
export_changed |= !aggr_item_linearized_same(cf->ai_aggr, c->ai_aggr);
export_changed |= !filter_same(cf->ai_aggr->merge_filter, c->ai_aggr->merge_filter);
c->ai_aggr = cf->ai_aggr;
}
int rpki_reload_changed = (cf->rpki_reload != c->rpki_reload);
if (c->preference != cf->preference)
@ -852,6 +895,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
c->rx_limit = cf->rx_limit;
c->ai_aggr = cf->ai_aggr;
c->in_limit = cf->in_limit;
c->out_limit = cf->out_limit;

View File

@ -487,6 +487,7 @@ struct channel_config {
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
const struct filter *in_filter, *out_filter; /* Attached filters */
const struct aggr_item_linearized *ai_aggr;
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep_filtered is active) */
struct channel_limit in_limit; /* Limit for importing routes from protocol */
@ -513,6 +514,7 @@ struct channel {
struct rtable *table;
const struct filter *in_filter; /* Input filter */
const struct filter *out_filter; /* Output filter */
const struct aggr_item_linearized *ai_aggr;
struct bmap export_map; /* Keeps track which routes passed export filter */
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct channel_limit in_limit; /* Input limit */

View File

@ -14,6 +14,8 @@
#include "lib/resource.h"
#include "lib/net.h"
#include "filter/data.h"
struct ea_list;
struct protocol;
struct proto;
@ -284,6 +286,7 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */
#define RA_MERGED 4 /* Announcement of optimal route merged with next ones */
#define RA_AGGREGATED 5 /* Announcement of merged routes */
/* Return value of preexport() callback */
#define RIC_ACCEPT 1 /* Accepted by protocol */
@ -322,6 +325,7 @@ void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src
/* rte_update() moved to protocol.h to avoid dependency conflicts */
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
rte *rt_export_aggregated(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c);
void rt_modify_stale(rtable *t, struct channel *c);
@ -546,13 +550,6 @@ const char *ea_custom_name(uint ea);
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
typedef struct adata {
uint length; /* Length of data */
byte data[0];
} adata;
extern const adata null_adata; /* adata of length 0 */
static inline struct adata *
lp_alloc_adata(struct linpool *pool, uint len)
{
@ -762,4 +759,50 @@ int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *
#define ROA_VALID 1
#define ROA_INVALID 2
/*
* Aggregating routes
*/
enum aggr_item_type {
AGGR_ITEM_TERM,
AGGR_ITEM_STATIC_ATTR,
AGGR_ITEM_DYNAMIC_ATTR,
};
struct aggr_item_internal {
enum aggr_item_type type;
union {
struct f_static_attr sa;
struct f_dynamic_attr da;
const struct f_line *line;
};
};
struct aggr_item {
const struct aggr_item *next;
struct aggr_item_internal internal;
};
struct aggr_item_linearized {
int count;
const struct filter *merge_filter;
struct aggr_item_internal items[];
};
struct rte_val_list {
const struct rte *rte;
struct f_val values[];
};
void rt_notify_aggregated(struct channel *c, struct network *net, struct rte *new_changed, struct rte *old_changed,
struct rte *new_best, struct rte *old_best, int refeed);
rte *export_filter(struct channel *c, struct rte *rt0, struct rte **rt_free, int silent);
/*
* This is an internal function of the nest. It is not supposed to be used
* directly. This is a temporary solution.
*/
void do_rt_notify(struct channel *c, struct network *net, struct rte *new, struct rte *old, int refeed);
#endif

View File

@ -143,19 +143,28 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
{
/* Special case for merged export */
rte *rt_free;
e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
rte *rte_free;
e = rt_export_merged(ec, n, &rte_free, c->show_pool, 1);
pass = 1;
if (!e)
{ e = ee; goto skip; }
}
else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_AGGREGATED))
{
rte *rte_free;
e = rt_export_aggregated(ec, n, &rte_free, c->show_pool, 1);
pass = 1;
if (!e)
{ e = ee; goto skip; }
}
else if (d->export_mode)
{
struct proto *ep = ec->proto;
int ic = ep->preexport ? ep->preexport(ec, e) : 0;
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED || ec->ra_mode == RA_AGGREGATED)
pass = 1;
if (ic < 0)

View File

@ -94,6 +94,10 @@
#undef LOCAL_DEBUG
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
@ -109,6 +113,7 @@
#include "lib/string.h"
#include "lib/alloca.h"
#include "lib/flowspec.h"
#include <stdlib.h>
#ifdef CONFIG_BGP
#include "proto/bgp/bgp.h"
@ -749,13 +754,13 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
return NULL;
}
static inline rte *
rte *
export_filter(struct channel *c, rte *rt0, rte **rt_free, int silent)
{
return export_filter_(c, rt0, rt_free, rte_update_pool, silent);
}
static void
void
do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
{
struct proto *p = c->proto;
@ -975,6 +980,57 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int
return best;
}
rte *
rt_export_aggregated(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent)
{
// struct proto *p = c->proto;
struct nexthop *nhs = NULL;
rte *best0, *best, *rt0, *rt, *tmp;
best0 = net->routes;
*rt_free = NULL;
if (!rte_is_valid(best0))
return NULL;
best = export_filter_(c, best0, rt_free, pool, silent);
if (!best || !rte_is_reachable(best))
return best;
for (rt0 = best0->next; rt0; rt0 = rt0->next)
{
if (!rte_mergable(best0, rt0))
continue;
rt = export_filter_(c, rt0, &tmp, pool, 1);
if (!rt)
continue;
if (rte_is_reachable(rt))
nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
if (tmp)
rte_free(tmp);
}
if (nhs)
{
nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
if (nhs->next)
{
best = rte_cow_rta(best, pool);
nexthop_link(best->attrs, nhs);
}
}
if (best != best0)
*rt_free = best;
return best;
}
static void
rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed,
@ -1019,7 +1075,6 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
rte_free(new_free);
}
/**
* rte_announce - announce a routing table change
* @tab: table the route has been added to
@ -1135,6 +1190,10 @@ rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
case RA_MERGED:
rt_notify_merged(c, net, new, old, new_best, old_best, 0);
break;
case RA_AGGREGATED:
rt_notify_aggregated(c, net, new, old, new_best, old_best, 0);
break;
}
}
}
@ -2969,6 +3028,8 @@ do_feed_channel(struct channel *c, net *n, rte *e)
rt_notify_accepted(c, n, NULL, NULL, c->refeeding);
else if (c->ra_mode == RA_MERGED)
rt_notify_merged(c, n, NULL, NULL, e, e, c->refeeding);
else if (c->ra_mode == RA_AGGREGATED)
rt_notify_aggregated(c, n, NULL, NULL, e, e, c->refeeding);
else /* RA_BASIC */
rt_notify_basic(c, n, e, e, c->refeeding);
rte_update_unlock();
@ -3008,7 +3069,8 @@ rt_feed_channel(struct channel *c)
if ((c->ra_mode == RA_OPTIMAL) ||
(c->ra_mode == RA_ACCEPTED) ||
(c->ra_mode == RA_MERGED))
(c->ra_mode == RA_MERGED) ||
(c->ra_mode == RA_AGGREGATED))
if (rte_is_valid(e))
{
/* In the meantime, the protocol may fell down */

View File

@ -9,6 +9,7 @@
CF_HDR
#include "proto/pipe/pipe.h"
#include "nest/route.h"
CF_DEFINES
@ -16,7 +17,7 @@ CF_DEFINES
CF_DECLS
CF_KEYWORDS(PIPE, PEER, TABLE)
CF_KEYWORDS(PIPE, PEER, TABLE, AGGREGATED, IMPORT, EXPORT, AGGREGATE)
CF_GRAMMAR
@ -41,6 +42,8 @@ pipe_proto:
| pipe_proto proto_item ';'
| pipe_proto channel_item_ ';'
| pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
| pipe_proto MERGE PATHS IMPORT bool kern_mp_limit { PIPE_CFG->config_import.limit = $5 ? $6 : 0; PIPE_CFG->config_import.use_aggregator = 0; }
| pipe_proto MERGE PATHS EXPORT bool kern_mp_limit { PIPE_CFG->config_export.limit = $5 ? $6 : 0; PIPE_CFG->config_export.use_aggregator = 0; }
;
CF_CODE

View File

@ -1,4 +1,4 @@
/*
/*
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
*
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
@ -81,7 +81,7 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
}
src_ch->table->pipe_busy = 1;
rte_update2(dst, n->n.addr, e, src);
rte_update2(dst, n->n.addr, e, old ? old->src : new->src);
src_ch->table->pipe_busy = 0;
}
@ -138,13 +138,16 @@ pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf)
{
struct channel_config *cc = proto_cf_main_channel(&cf->c);
log("pipe configure channels: ra_mode: %d", cc->ra_mode);
log("pipe configure channels: ai_aggr: %p", cc->ai_aggr);
struct channel_config pri_cf = {
.name = "pri",
.channel = cc->channel,
.table = cc->table,
.out_filter = cc->out_filter,
.in_limit = cc->in_limit,
.ra_mode = RA_ANY,
.ra_mode = cc->ra_mode,
.ai_aggr = cc->ai_aggr,
.debug = cc->debug,
.rpki_reload = cc->rpki_reload,
};
@ -160,6 +163,30 @@ pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf)
.rpki_reload = cc->rpki_reload,
};
log("ai_aggr = %p", cc->ai_aggr);
const struct aggr_item_linearized *ail = cc->ai_aggr;
int node = 1;
if (ail != NULL) {
for (int i = 0; i < ail->count; i++) {
switch (ail->items[i].type) {
case AGGR_ITEM_TERM:
log("node %d, type: term", node);
break;
case AGGR_ITEM_STATIC_ATTR:
log("node %d, type: static", node);
break;
case AGGR_ITEM_DYNAMIC_ATTR:
log("node %d, type: dynamic", node);
break;
default:
log("node %d, type: other", node);
break;
}
node++;
}
}
return
proto_configure_channel(&p->p, &p->pri, &pri_cf) &&
proto_configure_channel(&p->p, &p->sec, &sec_cf);

View File

@ -9,9 +9,21 @@
#ifndef _BIRD_PIPE_H_
#define _BIRD_PIPE_H_
struct merging_import {
uint limit;
uint use_aggregator;
};
struct merging_export {
uint limit;
uint use_aggregator;
};
struct pipe_config {
struct proto_config c;
struct rtable_config *peer; /* Table we're connected to */
struct rtable_config *peer; /* Table we're connected to */
struct merging_import config_import; /* From peer table to primary table */
struct merging_export config_export; /* From primary table to peer table */
};
struct pipe_proto {

View File

@ -581,6 +581,9 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
if (c->ra_mode == RA_MERGED)
return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
if (c->ra_mode == RA_AGGREGATED)
return rt_export_aggregated(c, net, rt_free, krt_filter_lp, 1);
rt = net->routes;
*rt_free = NULL;