diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2004-02-19 23:07:01 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2004-02-19 23:07:01 +0000 |
commit | 49f8c3fdbbe774bfbfa3545849045a6c406ed026 (patch) | |
tree | 7d170651e7f9d33ac46f5de5e58fdc0d76a504b3 | |
parent | e45a0a2546dfa029ffe0b93d1a4bc268f2e37c42 (diff) |
Add support for basic filters. Nothing optimized and it has some issues but
this is a huge step forward. OK henning@
-rw-r--r-- | usr.sbin/bgpd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 41 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 12 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 21 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 157 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_rib.c | 7 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_update.c | 32 |
7 files changed, 244 insertions, 30 deletions
diff --git a/usr.sbin/bgpd/Makefile b/usr.sbin/bgpd/Makefile index 063080ab23c..4e0aecdf3d1 100644 --- a/usr.sbin/bgpd/Makefile +++ b/usr.sbin/bgpd/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.16 2004/02/08 23:44:57 henning Exp $ +# $OpenBSD: Makefile,v 1.17 2004/02/19 23:07:00 claudio Exp $ .PATH: ${.CURDIR}/.. PROG= bgpd SRCS= bgpd.c buffer.c session.c log.c parse.y config.c imsg.c \ rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c \ - control.c pfkey.c rde_update.c rde_attr.c printconf.c + control.c pfkey.c rde_update.c rde_attr.c printconf.c rde_filter.c CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CLFAGS+= -Wmissing-declarations -Wredundant-decls diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 6f0636d4535..7f337846c8f 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.82 2004/02/19 13:54:58 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.83 2004/02/19 23:07:00 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -377,6 +377,7 @@ rde_update_dispatch(struct imsg *imsg) return (-1); } + /* withdraw prefix */ while (withdrawn_len > 0) { if ((pos = rde_update_get_prefix(p, withdrawn_len, &prefix, &prefixlen)) == -1) { @@ -398,6 +399,12 @@ rde_update_dispatch(struct imsg *imsg) p += pos; withdrawn_len -= pos; + + /* input filter */ + if (rde_filter(peer, NULL, &prefix, prefixlen, + DIR_IN) == ACTION_DENY) + continue; + rde_update_log("withdraw", peer, NULL, &prefix, prefixlen); prefix_remove(peer, &prefix, prefixlen); } @@ -415,6 +422,7 @@ rde_update_dispatch(struct imsg *imsg) if (attrpath_len == 0) /* 0 = no NLRI information in this message */ return (0); + /* parse path attributes */ attr_init(&attrs); while (attrpath_len > 0) { if ((pos = attr_parse(p, attrpath_len, &attrs, @@ -422,19 +430,18 @@ rde_update_dispatch(struct imsg *imsg) emsg = attr_error(p, attrpath_len, &attrs, &subtype, &size); rde_update_err(peer, ERR_UPDATE, subtype, emsg, size); - aspath_destroy(attrs.aspath); - attr_optfree(&attrs); + attr_free(&attrs); return (-1); } p += pos; attrpath_len -= pos; } + /* check for missing but necessary attributes */ if ((subtype = attr_missing(&attrs, peer->conf.ebgp)) != 0) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_MISSNG_WK_ATTR, &subtype, sizeof(u_int8_t)); - aspath_destroy(attrs.aspath); - attr_optfree(&attrs); + attr_free(&attrs); return (-1); } @@ -449,13 +456,13 @@ rde_update_dispatch(struct imsg *imsg) return (0); } + /* parse nlri prefix */ while (nlri_len > 0) { if ((pos = rde_update_get_prefix(p, nlri_len, &prefix, &prefixlen)) == -1) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); - aspath_destroy(attrs.aspath); - attr_optfree(&attrs); + attr_free(&attrs); return (-1); } if (prefixlen > 32) { @@ -468,22 +475,32 @@ rde_update_dispatch(struct imsg *imsg) p += pos; nlri_len -= pos; - rde_update_log("update", peer, &attrs, &prefix, prefixlen); + + /* input filter */ + /* + * XXX we need to copy attrs befor calling the filter + * but that stinks, because we copy it again in path_update. + */ + if (rde_filter(peer, &attrs, &prefix, prefixlen, + DIR_IN) == ACTION_DENY) + continue; + + /* max prefix checker */ if (peer->conf.max_prefix && peer->prefix_cnt >= peer->conf.max_prefix) { log_peer_warnx(&peer->conf, "prefix limit reached"); rde_update_err(peer, ERR_CEASE, ERR_CEASE_MAX_PREFIX, NULL, 0); - aspath_destroy(attrs.aspath); - attr_optfree(&attrs); + attr_free(&attrs); return (-1); } + + rde_update_log("update", peer, &attrs, &prefix, prefixlen); path_update(peer, &attrs, &prefix, prefixlen); } /* need to free allocated attribute memory that is no longer used */ - aspath_destroy(attrs.aspath); - attr_optfree(&attrs); + attr_free(&attrs); return (0); } diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index b1428b28a1f..eb12f60dae2 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.28 2004/02/18 23:18:16 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.29 2004/02/19 23:07:00 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -82,9 +82,8 @@ struct rde_peer { struct aspath_hdr { u_int16_t len; /* total length of aspath in octets */ - u_int16_t as_cnt; /* total number of AS's */ - /* probably we should switch these to int or something similar */ - /* char *str; string representation of aspath for regex search. */ + u_int16_t as_cnt; /* number of AS's in data */ + u_int16_t prepend; }; struct aspath { @@ -220,6 +219,7 @@ u_char *attr_error(u_char *, u_int16_t, struct attr_flags *, u_int8_t attr_missing(struct attr_flags *, int); int attr_compare(struct attr_flags *, struct attr_flags *); void attr_copy(struct attr_flags *, struct attr_flags *); +void attr_free(struct attr_flags *); int attr_write(void *, u_int16_t, u_int8_t, u_int8_t, void *, u_int16_t); int attr_optadd(struct attr_flags *, u_int8_t, u_int8_t, @@ -291,5 +291,9 @@ void pt_remove(struct pt_entry *); struct pt_entry *pt_lookup(struct bgpd_addr *); void pt_dump(void (*)(struct pt_entry *, void *), void *); +/* rde_filter.c */ +enum filter_actions rde_filter(struct rde_peer *, struct attr_flags *, + struct bgpd_addr *, u_int8_t, enum directions); +void rde_apply_set(struct attr_flags *, struct filter_set *); #endif /* __RDE_H__ */ diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index e4185f45923..98279164539 100644 --- a/usr.sbin/bgpd/rde_attr.c +++ b/usr.sbin/bgpd/rde_attr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_attr.c,v 1.11 2004/02/19 13:54:58 claudio Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.12 2004/02/19 23:07:00 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -377,6 +377,18 @@ attr_copy(struct attr_flags *t, struct attr_flags *s) attr_optadd(t, os->flags, os->type, os->data, os->len); } +void +attr_free(struct attr_flags *a) +{ + /* + * free the aspath and all optional path attributes + * but not the attr_flags struct. + */ + aspath_destroy(a->aspath); + a->aspath = NULL; + attr_optfree(a); +} + int attr_write(void *p, u_int16_t p_len, u_int8_t flags, u_int8_t type, void *data, u_int16_t data_len) @@ -534,19 +546,22 @@ aspath_create(void *data, u_int16_t len) memcpy(aspath->data, data, len); aspath->hdr.as_cnt = aspath_count(aspath); + aspath->hdr.prepend = 0; return aspath; } int aspath_write(void *p, u_int16_t len, struct aspath *aspath, u_int16_t myAS, - int prepend) + int ebgp) { u_char *b = p; - int tot_len, as_len, size, wpos = 0; + int tot_len, as_len, prepend, size, wpos = 0; u_int16_t tmp; u_int8_t type, attr_flag = ATTR_WELL_KNOWN; + prepend = aspath->hdr.prepend + (ebgp ? 1 : 0); + if (prepend > 255) /* lunatic prepends need to be blocked in the parser */ return (-1); diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c new file mode 100644 index 00000000000..b6e10347188 --- /dev/null +++ b/usr.sbin/bgpd/rde_filter.c @@ -0,0 +1,157 @@ +/* $OpenBSD: rde_filter.c,v 1.1 2004/02/19 23:07:00 claudio Exp $ */ + +/* + * Copyright (c) 2004 Claudio Jeker <claudio@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/queue.h> + +#include "bgpd.h" +#include "rde.h" + +extern struct filter_head *rules_l; /* XXX ugly */ + +int rde_filter_match(struct filter_rule *, struct attr_flags *, + struct bgpd_addr *, u_int8_t); + +enum filter_actions +rde_filter(struct rde_peer *peer, struct attr_flags *attrs, + struct bgpd_addr *prefix, u_int8_t prefixlen, enum directions dir) +{ + struct filter_rule *f; + enum filter_actions action = ACTION_ALLOW; /* default allow */ + + TAILQ_FOREACH(f, rules_l, entries) { + if (dir != f->dir) + continue; + if (f->peer.groupid != 0 && + f->peer.groupid != peer->conf.groupid) + continue; + if (f->peer.peerid != 0 && + f->peer.peerid != peer->conf.id) + continue; + if (rde_filter_match(f, attrs, prefix, prefixlen)) { + rde_apply_set(attrs, &f->set); + if (f->action != ACTION_NONE) + action = f->action; + if (f->quick) + return (action); + } + } + return (action); +} + +void +rde_apply_set(struct attr_flags *attrs, struct filter_set *set) +{ + if (attrs == NULL) + return; + + if (set->flags & SET_LOCALPREF) + attrs->lpref = set->localpref; + if (set->flags & SET_MED) + attrs->med = set->med; + if (set->flags & SET_NEXTHOP) + /* TODO switch attr->nexthop to struct in_addr */ + attrs->nexthop = set->nexthop.s_addr; + if (set->flags & SET_PREPEND) { + /* + * The acctual prepending is done afterwards because + * This could overflow but somebody that uses that many + * prepends is loony and needs professional help. + */ + attrs->aspath->hdr.prepend += set->prepend; + attrs->aspath->hdr.as_cnt += set->prepend; + } +} + +int +rde_filter_match(struct filter_rule *f, struct attr_flags *attrs, + struct bgpd_addr *prefix, u_int8_t plen) +{ + in_addr_t mask; + + if (attrs != NULL && f->match.as.type != AS_NONE) + if (aspath_match(attrs->aspath, f->match.as.type, + f->match.as.as) == 0) + return (0); + + if (f->match.prefix.addr.af != 0 && + f->match.prefix.addr.af == prefix->af) { + switch (f->match.prefix.addr.af) { + case AF_INET: + mask = 0xffffffff << (32 - f->match.prefix.len); + mask = htonl(mask); + if ((prefix->v4.s_addr & mask) != + (f->match.prefix.addr.v4.s_addr & mask)) + return (0); + break; + default: + fatalx("rde_filter_match: unsupported address family"); + } + + /* test prefixlen stuff too */ + switch (f->match.prefixlen.op) { + case OP_NONE: + /* perfect match */ + return (plen == f->match.prefix.len); + case OP_RANGE: + return ((plen >= f->match.prefixlen.len_min) && + (plen <= f->match.prefixlen.len_max)); + case OP_EQ: + return (plen == f->match.prefixlen.len_min); + case OP_NE: + return (plen != f->match.prefixlen.len_min); + case OP_LE: + return (plen <= f->match.prefixlen.len_min); + case OP_LT: + return (plen < f->match.prefixlen.len_min); + case OP_GE: + return (plen >= f->match.prefixlen.len_min); + case OP_GT: + return (plen > f->match.prefixlen.len_min); + } + /* NOTREACHED */ + } else if (f->match.prefixlen.op != OP_NONE) { + /* only prefixlen without a prefix */ + /* + * XXX IPv4 and IPv6 will cause trouble here. + * XXX need to store the af. + */ + switch (f->match.prefixlen.op) { + case OP_NONE: + fatalx("internal filter bug"); + case OP_RANGE: + return ((plen >= f->match.prefixlen.len_min) && + (plen <= f->match.prefixlen.len_max)); + case OP_EQ: + return (plen == f->match.prefixlen.len_min); + case OP_NE: + return (plen != f->match.prefixlen.len_min); + case OP_LE: + return (plen <= f->match.prefixlen.len_min); + case OP_LT: + return (plen < f->match.prefixlen.len_min); + case OP_GE: + return (plen >= f->match.prefixlen.len_min); + case OP_GT: + return (plen > f->match.prefixlen.len_min); + } + /* NOTREACHED */ + } + + /* matched somewhen or is anymatch rule */ + return (1); +} diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index 3c2948dc2a6..31268f03876 100644 --- a/usr.sbin/bgpd/rde_rib.c +++ b/usr.sbin/bgpd/rde_rib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_rib.c,v 1.34 2004/02/19 13:54:58 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.35 2004/02/19 23:07:00 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -250,10 +250,7 @@ path_unlink(struct rde_aspath *asp) asp->peer = NULL; asp->nexthop = NULL; - /* free the aspath and all other path attributes */ - aspath_destroy(asp->flags.aspath); - asp->flags.aspath = NULL; - attr_optfree(&asp->flags); + attr_free(&asp->flags); } /* alloc and initialize new entry. May not fail. */ diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 7af1146a6ac..828e9ec2b5f 100644 --- a/usr.sbin/bgpd/rde_update.c +++ b/usr.sbin/bgpd/rde_update.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_update.c,v 1.5 2004/02/18 23:25:17 claudio Exp $ */ +/* $OpenBSD: rde_update.c,v 1.6 2004/02/19 23:07:00 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -201,6 +201,7 @@ up_generate_updates(struct rde_peer *peer, { struct update_attr *a; struct update_prefix *p; + struct attr_flags attrs; ENSURE(peer->state == PEER_UP); /* @@ -246,6 +247,17 @@ up_generate_updates(struct rde_peer *peer, break; } + /* copy attributes for output filter */ + attr_copy(&attrs, &old->aspath->flags); + + if (rde_filter(peer, &attrs, &old->prefix->prefix, + old->prefix->prefixlen, DIR_OUT) == ACTION_DENY) { + attr_free(&attrs); + return; + } + + attr_free(&attrs); + /* withdraw prefix */ p = calloc(1, sizeof(struct update_prefix)); if (p == NULL) @@ -290,6 +302,15 @@ up_generate_updates(struct rde_peer *peer, break; } + /* copy attributes for output filter */ + attr_copy(&attrs, &new->aspath->flags); + + if (rde_filter(peer, &attrs, &new->prefix->prefix, + new->prefix->prefixlen, DIR_OUT) == ACTION_DENY) { + attr_free(&attrs); + return; + } + /* generate update */ p = calloc(1, sizeof(struct update_prefix)); if (p == NULL) @@ -299,7 +320,7 @@ up_generate_updates(struct rde_peer *peer, if (a == NULL) fatal("up_queue_update"); - if (up_generate_attr(peer, a, &new->aspath->flags, + if (up_generate_attr(peer, a, &attrs, new->aspath->nexthop) == -1) log_warnx("generation of bgp path attributes failed"); @@ -307,10 +328,13 @@ up_generate_updates(struct rde_peer *peer, * use aspath_hash as attr_hash, this may be unoptimal * but currently I don't care. */ - a->attr_hash = aspath_hash(new->aspath->flags.aspath); + a->attr_hash = aspath_hash(attrs.aspath); p->prefix = new->prefix->prefix; p->prefixlen = new->prefix->prefixlen; + /* no longer needed */ + attr_free(&attrs); + if (up_add(peer, p, a) == -1) log_warnx("queuing update failed."); } @@ -336,7 +360,7 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, /* aspath */ if ((r = aspath_write(up_attr_buf + wlen, len, a->aspath, - rde_local_as(), peer->conf.ebgp == 0 ? 0 : 1)) == -1) + rde_local_as(), peer->conf.ebgp)) == -1) return (-1); wlen += r; len -= r; |