753 lines
17 KiB
C
753 lines
17 KiB
C
/* $OpenBSD: config.c,v 1.108 2023/08/16 08:26:35 claudio Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <errno.h>
|
|
#include <ifaddrs.h>
|
|
#include <netdb.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bgpd.h"
|
|
#include "session.h"
|
|
#include "log.h"
|
|
|
|
int host_ip(const char *, struct bgpd_addr *, uint8_t *);
|
|
void free_networks(struct network_head *);
|
|
void free_flowspecs(struct flowspec_tree *);
|
|
|
|
struct bgpd_config *
|
|
new_config(void)
|
|
{
|
|
struct bgpd_config *conf;
|
|
|
|
if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL)
|
|
fatal(NULL);
|
|
|
|
if ((conf->filters = calloc(1, sizeof(struct filter_head))) == NULL)
|
|
fatal(NULL);
|
|
if ((conf->listen_addrs = calloc(1, sizeof(struct listen_addrs))) ==
|
|
NULL)
|
|
fatal(NULL);
|
|
if ((conf->mrt = calloc(1, sizeof(struct mrt_head))) == NULL)
|
|
fatal(NULL);
|
|
|
|
/* init the various list for later */
|
|
RB_INIT(&conf->peers);
|
|
TAILQ_INIT(&conf->networks);
|
|
RB_INIT(&conf->flowspecs);
|
|
SIMPLEQ_INIT(&conf->l3vpns);
|
|
SIMPLEQ_INIT(&conf->prefixsets);
|
|
SIMPLEQ_INIT(&conf->originsets);
|
|
SIMPLEQ_INIT(&conf->rde_prefixsets);
|
|
SIMPLEQ_INIT(&conf->rde_originsets);
|
|
RB_INIT(&conf->roa);
|
|
RB_INIT(&conf->aspa);
|
|
SIMPLEQ_INIT(&conf->as_sets);
|
|
SIMPLEQ_INIT(&conf->rtrs);
|
|
|
|
TAILQ_INIT(conf->filters);
|
|
TAILQ_INIT(conf->listen_addrs);
|
|
LIST_INIT(conf->mrt);
|
|
|
|
return (conf);
|
|
}
|
|
|
|
void
|
|
copy_config(struct bgpd_config *to, struct bgpd_config *from)
|
|
{
|
|
to->flags = from->flags;
|
|
to->log = from->log;
|
|
to->default_tableid = from->default_tableid;
|
|
to->bgpid = from->bgpid;
|
|
to->clusterid = from->clusterid;
|
|
to->as = from->as;
|
|
to->short_as = from->short_as;
|
|
to->holdtime = from->holdtime;
|
|
to->min_holdtime = from->min_holdtime;
|
|
to->connectretry = from->connectretry;
|
|
to->fib_priority = from->fib_priority;
|
|
}
|
|
|
|
void
|
|
network_free(struct network *n)
|
|
{
|
|
rtlabel_unref(n->net.rtlabel);
|
|
filterset_free(&n->net.attrset);
|
|
free(n);
|
|
}
|
|
|
|
void
|
|
free_networks(struct network_head *networks)
|
|
{
|
|
struct network *n;
|
|
|
|
while ((n = TAILQ_FIRST(networks)) != NULL) {
|
|
TAILQ_REMOVE(networks, n, entry);
|
|
network_free(n);
|
|
}
|
|
}
|
|
|
|
struct flowspec_config *
|
|
flowspec_alloc(uint8_t aid, int len)
|
|
{
|
|
struct flowspec_config *conf;
|
|
struct flowspec *flow;
|
|
|
|
flow = malloc(FLOWSPEC_SIZE + len);
|
|
if (flow == NULL)
|
|
return NULL;
|
|
memset(flow, 0, FLOWSPEC_SIZE);
|
|
|
|
conf = calloc(1, sizeof(*conf));
|
|
if (conf == NULL) {
|
|
free(flow);
|
|
return NULL;
|
|
}
|
|
|
|
conf->flow = flow;
|
|
TAILQ_INIT(&conf->attrset);
|
|
flow->len = len;
|
|
flow->aid = aid;
|
|
|
|
return conf;
|
|
}
|
|
|
|
void
|
|
flowspec_free(struct flowspec_config *f)
|
|
{
|
|
filterset_free(&f->attrset);
|
|
free(f->flow);
|
|
free(f);
|
|
}
|
|
|
|
void
|
|
free_flowspecs(struct flowspec_tree *flowspecs)
|
|
{
|
|
struct flowspec_config *f, *nf;
|
|
|
|
RB_FOREACH_SAFE(f, flowspec_tree, flowspecs, nf) {
|
|
RB_REMOVE(flowspec_tree, flowspecs, f);
|
|
flowspec_free(f);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_l3vpns(struct l3vpn_head *l3vpns)
|
|
{
|
|
struct l3vpn *vpn;
|
|
|
|
while ((vpn = SIMPLEQ_FIRST(l3vpns)) != NULL) {
|
|
SIMPLEQ_REMOVE_HEAD(l3vpns, entry);
|
|
filterset_free(&vpn->export);
|
|
filterset_free(&vpn->import);
|
|
free_networks(&vpn->net_l);
|
|
free(vpn);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_prefixsets(struct prefixset_head *psh)
|
|
{
|
|
struct prefixset *ps;
|
|
|
|
while (!SIMPLEQ_EMPTY(psh)) {
|
|
ps = SIMPLEQ_FIRST(psh);
|
|
free_roatree(&ps->roaitems);
|
|
free_prefixtree(&ps->psitems);
|
|
SIMPLEQ_REMOVE_HEAD(psh, entry);
|
|
free(ps);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_rde_prefixsets(struct rde_prefixset_head *psh)
|
|
{
|
|
struct rde_prefixset *ps;
|
|
|
|
if (psh == NULL)
|
|
return;
|
|
|
|
while (!SIMPLEQ_EMPTY(psh)) {
|
|
ps = SIMPLEQ_FIRST(psh);
|
|
trie_free(&ps->th);
|
|
SIMPLEQ_REMOVE_HEAD(psh, entry);
|
|
free(ps);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_prefixtree(struct prefixset_tree *p)
|
|
{
|
|
struct prefixset_item *psi, *npsi;
|
|
|
|
RB_FOREACH_SAFE(psi, prefixset_tree, p, npsi) {
|
|
RB_REMOVE(prefixset_tree, p, psi);
|
|
free(psi);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_roatree(struct roa_tree *r)
|
|
{
|
|
struct roa *roa, *nroa;
|
|
|
|
RB_FOREACH_SAFE(roa, roa_tree, r, nroa) {
|
|
RB_REMOVE(roa_tree, r, roa);
|
|
free(roa);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_aspa(struct aspa_set *aspa)
|
|
{
|
|
if (aspa == NULL)
|
|
return;
|
|
free(aspa->tas);
|
|
free(aspa);
|
|
}
|
|
|
|
void
|
|
free_aspatree(struct aspa_tree *a)
|
|
{
|
|
struct aspa_set *aspa, *naspa;
|
|
|
|
RB_FOREACH_SAFE(aspa, aspa_tree, a, naspa) {
|
|
RB_REMOVE(aspa_tree, a, aspa);
|
|
free_aspa(aspa);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_rtrs(struct rtr_config_head *rh)
|
|
{
|
|
struct rtr_config *r;
|
|
|
|
while (!SIMPLEQ_EMPTY(rh)) {
|
|
r = SIMPLEQ_FIRST(rh);
|
|
SIMPLEQ_REMOVE_HEAD(rh, entry);
|
|
free(r);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_config(struct bgpd_config *conf)
|
|
{
|
|
struct peer *p, *next;
|
|
struct listen_addr *la;
|
|
struct mrt *m;
|
|
|
|
free_l3vpns(&conf->l3vpns);
|
|
free_networks(&conf->networks);
|
|
free_flowspecs(&conf->flowspecs);
|
|
filterlist_free(conf->filters);
|
|
free_prefixsets(&conf->prefixsets);
|
|
free_prefixsets(&conf->originsets);
|
|
free_rde_prefixsets(&conf->rde_prefixsets);
|
|
free_rde_prefixsets(&conf->rde_originsets);
|
|
as_sets_free(&conf->as_sets);
|
|
free_roatree(&conf->roa);
|
|
free_aspatree(&conf->aspa);
|
|
free_rtrs(&conf->rtrs);
|
|
|
|
while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
|
|
TAILQ_REMOVE(conf->listen_addrs, la, entry);
|
|
free(la);
|
|
}
|
|
free(conf->listen_addrs);
|
|
|
|
while ((m = LIST_FIRST(conf->mrt)) != NULL) {
|
|
LIST_REMOVE(m, entry);
|
|
free(m);
|
|
}
|
|
free(conf->mrt);
|
|
|
|
RB_FOREACH_SAFE(p, peer_head, &conf->peers, next) {
|
|
RB_REMOVE(peer_head, &conf->peers, p);
|
|
free(p);
|
|
}
|
|
|
|
free(conf->csock);
|
|
free(conf->rcsock);
|
|
|
|
free(conf);
|
|
}
|
|
|
|
void
|
|
merge_config(struct bgpd_config *xconf, struct bgpd_config *conf)
|
|
{
|
|
struct listen_addr *nla, *ola, *next;
|
|
struct peer *p, *np, *nextp;
|
|
struct flowspec_config *f, *nextf, *xf;
|
|
|
|
/*
|
|
* merge the freshly parsed conf into the running xconf
|
|
*/
|
|
|
|
/* adjust FIB priority if changed */
|
|
/* if xconf is uninitialized we get RTP_NONE */
|
|
if (xconf->fib_priority != conf->fib_priority) {
|
|
kr_fib_decouple_all();
|
|
kr_fib_prio_set(conf->fib_priority);
|
|
kr_fib_couple_all();
|
|
}
|
|
|
|
/* take over the easy config changes */
|
|
copy_config(xconf, conf);
|
|
|
|
/* clear old control sockets and use new */
|
|
free(xconf->csock);
|
|
free(xconf->rcsock);
|
|
xconf->csock = conf->csock;
|
|
xconf->rcsock = conf->rcsock;
|
|
/* set old one to NULL so we don't double free */
|
|
conf->csock = NULL;
|
|
conf->rcsock = NULL;
|
|
|
|
/* clear all current filters and take over the new ones */
|
|
filterlist_free(xconf->filters);
|
|
xconf->filters = conf->filters;
|
|
conf->filters = NULL;
|
|
|
|
/* merge mrt config */
|
|
mrt_mergeconfig(xconf->mrt, conf->mrt);
|
|
|
|
/* switch the roa, first remove the old one */
|
|
free_roatree(&xconf->roa);
|
|
/* then move the RB tree root */
|
|
RB_ROOT(&xconf->roa) = RB_ROOT(&conf->roa);
|
|
RB_ROOT(&conf->roa) = NULL;
|
|
|
|
/* switch the aspa, first remove the old one */
|
|
free_aspatree(&xconf->aspa);
|
|
/* then move the RB tree root */
|
|
RB_ROOT(&xconf->aspa) = RB_ROOT(&conf->aspa);
|
|
RB_ROOT(&conf->aspa) = NULL;
|
|
|
|
/* switch the rtr_configs, first remove the old ones */
|
|
free_rtrs(&xconf->rtrs);
|
|
SIMPLEQ_CONCAT(&xconf->rtrs, &conf->rtrs);
|
|
|
|
/* switch the prefixsets, first remove the old ones */
|
|
free_prefixsets(&xconf->prefixsets);
|
|
SIMPLEQ_CONCAT(&xconf->prefixsets, &conf->prefixsets);
|
|
|
|
/* switch the originsets, first remove the old ones */
|
|
free_prefixsets(&xconf->originsets);
|
|
SIMPLEQ_CONCAT(&xconf->originsets, &conf->originsets);
|
|
|
|
/* switch the as_sets, first remove the old ones */
|
|
as_sets_free(&xconf->as_sets);
|
|
SIMPLEQ_CONCAT(&xconf->as_sets, &conf->as_sets);
|
|
|
|
/* switch the network statements, but first remove the old ones */
|
|
free_networks(&xconf->networks);
|
|
TAILQ_CONCAT(&xconf->networks, &conf->networks, entry);
|
|
|
|
/*
|
|
* Merge the flowspec statements. Mark the old ones for deletion
|
|
* which happens when the flowspec is sent to the RDE.
|
|
*/
|
|
RB_FOREACH(f, flowspec_tree, &xconf->flowspecs)
|
|
f->reconf_action = RECONF_DELETE;
|
|
|
|
RB_FOREACH_SAFE(f, flowspec_tree, &conf->flowspecs, nextf) {
|
|
RB_REMOVE(flowspec_tree, &conf->flowspecs, f);
|
|
|
|
xf = RB_INSERT(flowspec_tree, &xconf->flowspecs, f);
|
|
if (xf != NULL) {
|
|
filterset_free(&xf->attrset);
|
|
filterset_move(&f->attrset, &xf->attrset);
|
|
flowspec_free(f);
|
|
xf->reconf_action = RECONF_KEEP;
|
|
} else
|
|
f->reconf_action = RECONF_KEEP;
|
|
}
|
|
|
|
/* switch the l3vpn configs, first remove the old ones */
|
|
free_l3vpns(&xconf->l3vpns);
|
|
SIMPLEQ_CONCAT(&xconf->l3vpns, &conf->l3vpns);
|
|
|
|
/*
|
|
* merge new listeners:
|
|
* -flag all existing ones as to be deleted
|
|
* -those that are in both new and old: flag to keep
|
|
* -new ones get inserted and flagged as to reinit
|
|
* -remove all that are still flagged for deletion
|
|
*/
|
|
|
|
TAILQ_FOREACH(nla, xconf->listen_addrs, entry)
|
|
nla->reconf = RECONF_DELETE;
|
|
|
|
/* no new listeners? preserve default ones */
|
|
if (TAILQ_EMPTY(conf->listen_addrs))
|
|
TAILQ_FOREACH(ola, xconf->listen_addrs, entry)
|
|
if (ola->flags & DEFAULT_LISTENER)
|
|
ola->reconf = RECONF_KEEP;
|
|
/* else loop over listeners and merge configs */
|
|
for (nla = TAILQ_FIRST(conf->listen_addrs); nla != NULL; nla = next) {
|
|
next = TAILQ_NEXT(nla, entry);
|
|
|
|
TAILQ_FOREACH(ola, xconf->listen_addrs, entry)
|
|
if (!memcmp(&nla->sa, &ola->sa, sizeof(nla->sa)))
|
|
break;
|
|
|
|
if (ola == NULL) {
|
|
/* new listener, copy over */
|
|
TAILQ_REMOVE(conf->listen_addrs, nla, entry);
|
|
TAILQ_INSERT_TAIL(xconf->listen_addrs, nla, entry);
|
|
nla->reconf = RECONF_REINIT;
|
|
} else /* exists, just flag */
|
|
ola->reconf = RECONF_KEEP;
|
|
}
|
|
/* finally clean up the original list and remove all stale entries */
|
|
for (nla = TAILQ_FIRST(xconf->listen_addrs); nla != NULL; nla = next) {
|
|
next = TAILQ_NEXT(nla, entry);
|
|
if (nla->reconf == RECONF_DELETE) {
|
|
TAILQ_REMOVE(xconf->listen_addrs, nla, entry);
|
|
free(nla);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* merge peers:
|
|
* - need to know which peers are new, replaced and removed
|
|
* - walk over old peers and check if there is a corresponding new
|
|
* peer if so mark it RECONF_KEEP. Remove all old peers.
|
|
* - swap lists (old peer list is actually empty).
|
|
*/
|
|
RB_FOREACH_SAFE(p, peer_head, &xconf->peers, nextp) {
|
|
np = getpeerbyid(conf, p->conf.id);
|
|
if (np != NULL) {
|
|
np->reconf_action = RECONF_KEEP;
|
|
/* copy the auth state since parent uses it */
|
|
np->auth = p->auth;
|
|
} else {
|
|
/* peer no longer exists, clear pfkey state */
|
|
pfkey_remove(p);
|
|
}
|
|
|
|
RB_REMOVE(peer_head, &xconf->peers, p);
|
|
free(p);
|
|
}
|
|
RB_FOREACH_SAFE(np, peer_head, &conf->peers, nextp) {
|
|
RB_REMOVE(peer_head, &conf->peers, np);
|
|
if (RB_INSERT(peer_head, &xconf->peers, np) != NULL)
|
|
fatalx("%s: peer tree is corrupt", __func__);
|
|
}
|
|
|
|
/* conf is merged so free it */
|
|
free_config(conf);
|
|
}
|
|
|
|
uint32_t
|
|
get_bgpid(void)
|
|
{
|
|
struct ifaddrs *ifap, *ifa;
|
|
uint32_t ip = 0, cur, localnet;
|
|
|
|
localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
|
|
|
|
if (getifaddrs(&ifap) == -1)
|
|
fatal("getifaddrs");
|
|
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
|
if (ifa->ifa_addr == NULL ||
|
|
ifa->ifa_addr->sa_family != AF_INET)
|
|
continue;
|
|
cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
|
|
if ((cur & localnet) == localnet) /* skip 127/8 */
|
|
continue;
|
|
if (ntohl(cur) > ntohl(ip))
|
|
ip = cur;
|
|
}
|
|
freeifaddrs(ifap);
|
|
|
|
return (ip);
|
|
}
|
|
|
|
int
|
|
host(const char *s, struct bgpd_addr *h, uint8_t *len)
|
|
{
|
|
int mask = 128;
|
|
char *p, *ps;
|
|
const char *errstr;
|
|
|
|
if ((ps = strdup(s)) == NULL)
|
|
fatal("%s: strdup", __func__);
|
|
|
|
if ((p = strrchr(ps, '/')) != NULL) {
|
|
mask = strtonum(p+1, 0, 128, &errstr);
|
|
if (errstr) {
|
|
log_warnx("prefixlen is %s: %s", errstr, p);
|
|
free(ps);
|
|
return (0);
|
|
}
|
|
p[0] = '\0';
|
|
}
|
|
|
|
memset(h, 0, sizeof(*h));
|
|
|
|
if (host_ip(ps, h, len) == 0) {
|
|
free(ps);
|
|
return (0);
|
|
}
|
|
|
|
if (p != NULL)
|
|
*len = mask;
|
|
|
|
free(ps);
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
host_ip(const char *s, struct bgpd_addr *h, uint8_t *len)
|
|
{
|
|
struct addrinfo hints, *res;
|
|
int bits;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
if (getaddrinfo(s, NULL, &hints, &res) == 0) {
|
|
*len = res->ai_family == AF_INET6 ? 128 : 32;
|
|
sa2addr(res->ai_addr, h, NULL);
|
|
freeaddrinfo(res);
|
|
} else { /* ie. for 10/8 parsing */
|
|
if ((bits = inet_net_pton(AF_INET, s, &h->v4,
|
|
sizeof(h->v4))) == -1)
|
|
return (0);
|
|
*len = bits;
|
|
h->aid = AID_INET;
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
prepare_listeners(struct bgpd_config *conf)
|
|
{
|
|
struct listen_addr *la, *next;
|
|
int opt = 1;
|
|
int r = 0;
|
|
|
|
for (la = TAILQ_FIRST(conf->listen_addrs); la != NULL; la = next) {
|
|
next = TAILQ_NEXT(la, entry);
|
|
if (la->reconf != RECONF_REINIT)
|
|
continue;
|
|
|
|
if ((la->fd = socket(la->sa.ss_family,
|
|
SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
|
IPPROTO_TCP)) == -1) {
|
|
if (la->flags & DEFAULT_LISTENER && (errno ==
|
|
EAFNOSUPPORT || errno == EPROTONOSUPPORT)) {
|
|
TAILQ_REMOVE(conf->listen_addrs, la, entry);
|
|
free(la);
|
|
continue;
|
|
} else
|
|
fatal("socket");
|
|
}
|
|
|
|
opt = 1;
|
|
if (setsockopt(la->fd, SOL_SOCKET, SO_REUSEADDR,
|
|
&opt, sizeof(opt)) == -1)
|
|
fatal("setsockopt SO_REUSEADDR");
|
|
|
|
if (bind(la->fd, (struct sockaddr *)&la->sa, la->sa_len) ==
|
|
-1) {
|
|
switch (la->sa.ss_family) {
|
|
case AF_INET:
|
|
log_warn("cannot bind to %s:%u",
|
|
log_sockaddr((struct sockaddr *)&la->sa,
|
|
la->sa_len), ntohs(((struct sockaddr_in *)
|
|
&la->sa)->sin_port));
|
|
break;
|
|
case AF_INET6:
|
|
log_warn("cannot bind to [%s]:%u",
|
|
log_sockaddr((struct sockaddr *)&la->sa,
|
|
la->sa_len), ntohs(((struct sockaddr_in6 *)
|
|
&la->sa)->sin6_port));
|
|
break;
|
|
default:
|
|
log_warn("cannot bind to %s",
|
|
log_sockaddr((struct sockaddr *)&la->sa,
|
|
la->sa_len));
|
|
break;
|
|
}
|
|
close(la->fd);
|
|
TAILQ_REMOVE(conf->listen_addrs, la, entry);
|
|
free(la);
|
|
r = -1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return (r);
|
|
}
|
|
|
|
void
|
|
expand_networks(struct bgpd_config *c, struct network_head *nw)
|
|
{
|
|
struct network *n, *m, *tmp;
|
|
struct prefixset *ps;
|
|
struct prefixset_item *psi;
|
|
|
|
TAILQ_FOREACH_SAFE(n, nw, entry, tmp) {
|
|
if (n->net.type == NETWORK_PREFIXSET) {
|
|
TAILQ_REMOVE(nw, n, entry);
|
|
if ((ps = find_prefixset(n->net.psname, &c->prefixsets))
|
|
== NULL)
|
|
fatal("%s: prefixset %s not found", __func__,
|
|
n->net.psname);
|
|
RB_FOREACH(psi, prefixset_tree, &ps->psitems) {
|
|
if ((m = calloc(1, sizeof(struct network)))
|
|
== NULL)
|
|
fatal(NULL);
|
|
memcpy(&m->net.prefix, &psi->p.addr,
|
|
sizeof(m->net.prefix));
|
|
m->net.prefixlen = psi->p.len;
|
|
filterset_copy(&n->net.attrset,
|
|
&m->net.attrset);
|
|
TAILQ_INSERT_TAIL(nw, m, entry);
|
|
}
|
|
network_free(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline int
|
|
prefixset_cmp(struct prefixset_item *a, struct prefixset_item *b)
|
|
{
|
|
int i;
|
|
|
|
if (a->p.addr.aid < b->p.addr.aid)
|
|
return (-1);
|
|
if (a->p.addr.aid > b->p.addr.aid)
|
|
return (1);
|
|
|
|
switch (a->p.addr.aid) {
|
|
case AID_INET:
|
|
i = memcmp(&a->p.addr.v4, &b->p.addr.v4,
|
|
sizeof(struct in_addr));
|
|
break;
|
|
case AID_INET6:
|
|
i = memcmp(&a->p.addr.v6, &b->p.addr.v6,
|
|
sizeof(struct in6_addr));
|
|
break;
|
|
default:
|
|
fatalx("%s: unknown af", __func__);
|
|
}
|
|
if (i > 0)
|
|
return (1);
|
|
if (i < 0)
|
|
return (-1);
|
|
if (a->p.len < b->p.len)
|
|
return (-1);
|
|
if (a->p.len > b->p.len)
|
|
return (1);
|
|
if (a->p.len_min < b->p.len_min)
|
|
return (-1);
|
|
if (a->p.len_min > b->p.len_min)
|
|
return (1);
|
|
if (a->p.len_max < b->p.len_max)
|
|
return (-1);
|
|
if (a->p.len_max > b->p.len_max)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
RB_GENERATE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
|
|
|
|
static inline int
|
|
roa_cmp(struct roa *a, struct roa *b)
|
|
{
|
|
int i;
|
|
|
|
if (a->aid < b->aid)
|
|
return (-1);
|
|
if (a->aid > b->aid)
|
|
return (1);
|
|
|
|
switch (a->aid) {
|
|
case AID_INET:
|
|
i = memcmp(&a->prefix.inet, &b->prefix.inet,
|
|
sizeof(struct in_addr));
|
|
break;
|
|
case AID_INET6:
|
|
i = memcmp(&a->prefix.inet6, &b->prefix.inet6,
|
|
sizeof(struct in6_addr));
|
|
break;
|
|
default:
|
|
fatalx("%s: unknown af", __func__);
|
|
}
|
|
if (i > 0)
|
|
return (1);
|
|
if (i < 0)
|
|
return (-1);
|
|
if (a->prefixlen < b->prefixlen)
|
|
return (-1);
|
|
if (a->prefixlen > b->prefixlen)
|
|
return (1);
|
|
|
|
if (a->asnum < b->asnum)
|
|
return (-1);
|
|
if (a->asnum > b->asnum)
|
|
return (1);
|
|
|
|
if (a->maxlen < b->maxlen)
|
|
return (-1);
|
|
if (a->maxlen > b->maxlen)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
RB_GENERATE(roa_tree, roa, entry, roa_cmp);
|
|
|
|
static inline int
|
|
aspa_cmp(struct aspa_set *a, struct aspa_set *b)
|
|
{
|
|
if (a->as < b->as)
|
|
return (-1);
|
|
if (a->as > b->as)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
RB_GENERATE(aspa_tree, aspa_set, entry, aspa_cmp);
|
|
|
|
static inline int
|
|
flowspec_config_cmp(struct flowspec_config *a, struct flowspec_config *b)
|
|
{
|
|
if (a->flow->aid < b->flow->aid)
|
|
return -1;
|
|
if (a->flow->aid > b->flow->aid)
|
|
return 1;
|
|
|
|
return flowspec_cmp(a->flow->data, a->flow->len,
|
|
b->flow->data, b->flow->len, a->flow->aid == AID_FLOWSPECv6);
|
|
}
|
|
|
|
RB_GENERATE(flowspec_tree, flowspec_config, entry, flowspec_config_cmp);
|