#include <sys/types.h>
#include <sys/queue.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rde.h"
struct rde_memstats rdemem;
struct rib dummy_rib = {
.name = "regress RIB",
.flags = 0,
};
struct rib flowrib;
struct pt_entry dummy_pt;
struct rib_entry dummy_re = { .prefix = &dummy_pt };
struct rde_peer peer1 = {
.conf.ebgp = 1,
.remote_bgpid = 1,
.remote_addr = { .aid = AID_INET, .v4.s_addr = 0xef000001 },
};
struct rde_peer peer2 = {
.conf.ebgp = 1,
.remote_bgpid = 2,
.remote_addr = { .aid = AID_INET, .v4.s_addr = 0xef000002 },
};
struct rde_peer peer3 = {
.conf.ebgp = 1,
.remote_bgpid = 3,
.remote_addr = { .aid = AID_INET, .v4.s_addr = 0xef000003 },
};
struct rde_peer peer1_a4 = {
.conf.ebgp = 1,
.remote_bgpid = 1,
.remote_addr = { .aid = AID_INET, .v4.s_addr = 0xef000004 },
};
struct rde_peer peer1_i = {
.conf.ebgp = 0,
.remote_bgpid = 3,
.remote_addr = { .aid = AID_INET, .v4.s_addr = 0xef000003 },
};
union a {
struct aspath a;
struct {
uint32_t source_as;
uint16_t len;
uint16_t ascnt;
uint8_t d[6];
} x;
} asdata[] = {
{ .x = { .len = 6, .ascnt = 2, .d = { 2, 1, 0, 0, 0, 1 } } },
{ .x = { .len = 6, .ascnt = 3, .d = { 2, 1, 0, 0, 0, 1 } } },
{ .x = { .len = 6, .ascnt = 2, .d = { 2, 1, 0, 0, 0, 2 } } },
{ .x = { .len = 6, .ascnt = 3, .d = { 2, 1, 0, 0, 0, 2 } } },
};
struct rde_aspath asp[] = {
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_IGP, .weight = 1000 },
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_IGP, .flags=F_ATTR_PARSE_ERR },
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_IGP, .flags=F_ATTR_LOOP },
{ .aspath = &asdata[0].a, .med = 100, .lpref = 50, .origin = ORIGIN_IGP },
{ .aspath = &asdata[1].a, .med = 100, .lpref = 100, .origin = ORIGIN_IGP },
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_EGP },
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_INCOMPLETE },
{ .aspath = &asdata[0].a, .med = 200, .lpref = 100, .origin = ORIGIN_IGP },
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_IGP, .weight = 100 },
};
#define T1 1610980000
#define T2 1610983600
struct test {
char *what;
struct prefix p;
} test_pfx[] = {
{ .what = "test prefix",
.p = { .re = &dummy_re, .aspath = &asp[0], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "prefix with error",
.p = { .re = &dummy_re, .aspath = &asp[1], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "prefix with loop",
.p = { .re = &dummy_re, .aspath = &asp[2], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "prefix with unreachable nexthop",
.p = { .re = &dummy_re, .aspath = &asp[0], .peer = &peer1, .nhflags = 0, .lastchange = T1, } },
{ .what = "local preference check",
.p = { .re = &dummy_re, .aspath = &asp[3], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "aspath count check",
.p = { .re = &dummy_re, .aspath = &asp[4], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "origin EGP",
.p = { .re = &dummy_re, .aspath = &asp[5], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "origin INCOMPLETE",
.p = { .re = &dummy_re, .aspath = &asp[6], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "MED",
.p = { .re = &dummy_re, .aspath = &asp[7], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "EBGP vs IBGP",
.p = { .re = &dummy_re, .aspath = &asp[0], .peer = &peer1_i, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "local weight",
.p = { .re = &dummy_re, .aspath = &asp[8], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "route age",
.p = { .re = &dummy_re, .aspath = &asp[0], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T2, } },
{ .what = "BGP ID",
.p = { .re = &dummy_re, .aspath = &asp[0], .peer = &peer2, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
{ .what = "remote peer address",
.p = { .re = &dummy_re, .aspath = &asp[0], .peer = &peer1_a4, .nhflags = NEXTHOP_VALID, .lastchange = T1, } },
};
struct rde_aspath med_asp[] = {
{ .aspath = &asdata[0].a, .med = 100, .lpref = 100, .origin = ORIGIN_EGP },
{ .aspath = &asdata[0].a, .med = 150, .lpref = 100, .origin = ORIGIN_EGP },
{ .aspath = &asdata[2].a, .med = 75, .lpref = 100, .origin = ORIGIN_EGP },
{ .aspath = &asdata[2].a, .med = 125, .lpref = 100, .origin = ORIGIN_EGP },
{ .aspath = &asdata[1].a, .med = 110, .lpref = 100, .origin = ORIGIN_EGP },
{ .aspath = &asdata[1].a, .med = 90, .lpref = 100, .origin = ORIGIN_EGP },
};
struct prefix med_pfx1 =
{ .re = &dummy_re, .aspath = &med_asp[0], .peer = &peer2, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix med_pfx2 =
{ .re = &dummy_re, .aspath = &med_asp[1], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix med_pfx3 =
{ .re = &dummy_re, .aspath = &med_asp[2], .peer = &peer3, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix med_pfx4 =
{ .re = &dummy_re, .aspath = &med_asp[3], .peer = &peer1_a4, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix med_pfx5 =
{ .re = &dummy_re, .aspath = &med_asp[5], .peer = &peer3, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix med_pfx6 =
{ .re = &dummy_re, .aspath = &med_asp[4], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix age_pfx1 =
{ .re = &dummy_re, .aspath = &asp[0], .peer = &peer2, .nhflags = NEXTHOP_VALID, .lastchange = T1, };
struct prefix age_pfx2 =
{ .re = &dummy_re, .aspath = &asp[0], .peer = &peer1, .nhflags = NEXTHOP_VALID, .lastchange = T2, };
int prefix_cmp(struct prefix *, struct prefix *, int *);
int decision_flags = BGPD_FLAG_DECISION_ROUTEAGE;
int failed;
static int
test(struct prefix *a, struct prefix *b, int v)
{
int rv = 0, dummy;
if (prefix_cmp(a, b, &dummy) < 0) {
if (v) printf(" FAILED\n");
failed = rv = 1;
} else if (prefix_cmp(b, a, &dummy) > 0) {
if (v) printf(" reverse cmp FAILED\n");
failed = rv = 1;
} else if (v)
printf(" OK\n");
return rv;
}
static size_t
which(struct prefix **orig, struct prefix *p)
{
size_t i;
for (i = 0; orig[i] != NULL; i++)
if (orig[i] == p)
return i;
return 9999;
}
static int
test_evaluate(struct prefix **orig, struct prefix **in, size_t nin)
{
struct prefix *next[nin - 1];
size_t i, j, n;
int r = 0;
if (nin == 0) {
struct prefix *xp;
j = 0;
TAILQ_FOREACH(xp, &dummy_re.prefix_h, rib_l)
if (which(orig, xp) != j++)
r = 1;
if (r != 0) {
printf("bad order");
TAILQ_FOREACH(xp, &dummy_re.prefix_h, rib_l)
printf(" %zu", which(orig, xp));
printf(" FAILED\n");
}
}
for (i = 0; i < nin; i++) {
prefix_evaluate(&dummy_re, in[i], NULL);
for (n = j = 0; j < nin; j++) {
if (j == i)
continue;
next[n++] = in[j];
}
r |= test_evaluate(orig, next, n);
prefix_evaluate(&dummy_re, NULL, in[i]);
}
return r;
}
int
main(int argc, char **argv)
{
struct prefix *med_strict[7] = {
&med_pfx1, &med_pfx2, &med_pfx3, &med_pfx4,
&med_pfx5, &med_pfx6, NULL
};
struct prefix *med_always[7] = {
&med_pfx3, &med_pfx1, &med_pfx4, &med_pfx2,
&med_pfx5, &med_pfx6, NULL
};
size_t i, ntest;
ntest = sizeof(test_pfx) / sizeof(*test_pfx);
for (i = 1; i < ntest; i++) {
printf("test %zu: %s", i, test_pfx[i].what);
test(&test_pfx[0].p, &test_pfx[i].p, 1);
}
printf("test NULL element");
test(&test_pfx[0].p, NULL, 1);
printf("test rde med compare strict 1");
test(&med_pfx1, &med_pfx2, 1);
printf("test rde med compare strict 2");
test(&med_pfx1, &med_pfx3, 1);
printf("test rde med compare strict 3");
test(&med_pfx4, &med_pfx1, 1);
decision_flags |= BGPD_FLAG_DECISION_MED_ALWAYS;
printf("test rde med compare always 1");
test(&med_pfx1, &med_pfx2, 1);
printf("test rde med compare always 2");
test(&med_pfx3, &med_pfx1, 1);
printf("test rde med compare always 3");
test(&med_pfx1, &med_pfx4, 1);
printf("test rde route-age evaluate");
test(&age_pfx1, &age_pfx2, 1);
decision_flags &= ~BGPD_FLAG_DECISION_ROUTEAGE;
printf("test rde route-age ignore");
test(&age_pfx2, &age_pfx1, 1);
decision_flags = 0;
printf("evaluate with rde med compare strict\n");
if (test_evaluate(med_strict, med_strict, 6) == 0)
printf("all OK\n");
decision_flags = BGPD_FLAG_DECISION_MED_ALWAYS;
printf("evaluate with rde med compare always\n");
if (test_evaluate(med_always, med_always, 6) == 0)
printf("all OK\n");
if (failed)
printf("some tests FAILED\n");
else
printf("all tests OK\n");
exit(failed);
}
int
rde_decisionflags(void)
{
return decision_flags;
}
uint32_t
rde_local_as(void)
{
return 65000;
}
int
rde_evaluate_all(void)
{
return 0;
}
int
as_set_match(const struct as_set *aset, uint32_t asnum)
{
errx(1, __func__);
}
struct rib *
rib_byid(uint16_t id)
{
return &dummy_rib;
}
void
rde_generate_updates(struct rib_entry *re, struct prefix *newpath,
uint32_t old_pathid_tx, enum eval_mode mode)
{
}
void
rde_send_kroute(struct rib *rib, struct prefix *new, struct prefix *old)
{
}
__dead void
fatalx(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
verrx(2, emsg, ap);
}
__dead void
fatal(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
verr(2, emsg, ap);
}
void
log_warnx(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vwarnx(emsg, ap);
va_end(ap);
}
void
log_debug(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vwarnx(emsg, ap);
va_end(ap);
}
void
pt_getaddr(struct pt_entry *pte, struct bgpd_addr *addr)
{
}