diff options
Diffstat (limited to 'usr.sbin/bgpd')
-rw-r--r-- | usr.sbin/bgpd/rde.c | 197 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 13 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 165 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 16 |
4 files changed, 344 insertions, 47 deletions
diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index d953570ea01..1bc3ee8895c 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.120 2004/06/23 07:10:05 henning Exp $ */ +/* $OpenBSD: rde.c,v 1.121 2004/06/24 23:15:58 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -42,6 +42,8 @@ void rde_dispatch_imsg_parent(struct imsgbuf *); int rde_update_dispatch(struct imsg *); int rde_update_get_prefix(u_char *, u_int16_t, struct bgpd_addr *, u_int8_t *); +int rde_update_get_prefix6(u_char *, u_int16_t, struct bgpd_addr *, + u_int8_t *); void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, void *, u_int16_t); void rde_update_log(const char *, @@ -424,15 +426,16 @@ int rde_update_dispatch(struct imsg *imsg) { struct rde_peer *peer; - u_char *p, *emsg; + u_char *p, *emsg, *mpp; int pos; - u_int16_t len; + u_int16_t afi, len, mplen; u_int16_t withdrawn_len; u_int16_t attrpath_len; u_int16_t nlri_len, size; - u_int8_t prefixlen, subtype; + u_int8_t prefixlen, safi, subtype; struct bgpd_addr prefix; struct attr_flags attrs, fattrs; + struct attr *mpattr; peer = peer_get(imsg->hdr.peerid); if (peer == NULL) /* unknown peer, cannot happen */ @@ -530,6 +533,57 @@ rde_update_dispatch(struct imsg *imsg) prefix_remove(peer, &prefix, prefixlen); } + /* withdraw MP_UNREACH_NRLI if available */ + if (attrpath_len != 0 && + (mpattr = attr_optget(&attrs, ATTR_MP_UNREACH_NLRI)) != NULL) { + mpp = mpattr->data; + mplen = mpattr->len; + memcpy(&afi, mpp, 2); + mpp += 2; + mplen -= 2; + afi = ntohs(afi); + safi = *mpp++; + mplen--; + switch (afi) { + case AFI_IPv6: + while (mplen > 0) { + if ((pos = rde_update_get_prefix6(mpp, mplen, + &prefix, &prefixlen)) == -1) { + log_peer_warnx(&peer->conf, + "bad IPv6 withdraw prefix"); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_OPTATTR, + mpattr->data, mpattr->len); + attr_free(&attrs); + return (-1); + } + if (prefixlen > 128) { + log_peer_warnx(&peer->conf, + "bad IPv6 withdraw prefix"); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_OPTATTR, + mpattr->data, mpattr->len); + attr_free(&attrs); + return (-1); + } + + mpp += pos; + mplen -= 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); + } + default: + fatalx("unsupported multipath AF"); + } + } + if (attrpath_len == 0) /* 0 = no NLRI information in this message */ return (0); @@ -599,6 +653,90 @@ rde_update_dispatch(struct imsg *imsg) path_update(peer, &fattrs, &prefix, prefixlen); } + /* add MP_REACH_NLRI if available */ + if ((mpattr = attr_optget(&attrs, ATTR_MP_REACH_NLRI)) != NULL) { + mpp = mpattr->data; + mplen = mpattr->len; + memcpy(&afi, mpp, 2); + mpp += 2; + mplen -= 2; + afi = ntohs(afi); + safi = *mpp++; + mplen--; + + if ((pos = attr_mp_nexthop_check(mpp, mplen, afi)) == -1) { + log_peer_warnx(&peer->conf, "bad IPv6 nlri prefix"); + rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, + mpattr->data, mpattr->len); + attr_free(&attrs); + return (-1); + } + + mpp += pos; + mplen -= pos; + + if (*p++ != NULL) { + /* XXX this is ugly */ + log_peer_warnx(&peer->conf, "SNPA are not supported"); + rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, + mpattr->data, mpattr->len); + attr_free(&attrs); + return (-1); + } + switch (afi) { + case AFI_IPv6: + while (mplen > 0) { + if ((pos = rde_update_get_prefix6(mpp, mplen, + &prefix, &prefixlen)) == -1) { + log_peer_warnx(&peer->conf, + "bad IPv6 nlri prefix"); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_OPTATTR, + mpattr->data, mpattr->len); + attr_free(&attrs); + return (-1); + } + if (prefixlen > 128) { + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_OPTATTR, + mpattr->data, mpattr->len); + attr_free(&attrs); + return (-1); + } + + mpp += pos; + mplen -= pos; + + attr_copy(&fattrs, &attrs); + /* input filter */ + if (rde_filter(peer, &fattrs, &prefix, + prefixlen, DIR_IN) == ACTION_DENY) { + attr_free(&fattrs); + continue; + } + + /* XXX IPv4 and IPv6 together */ + /* 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); + attr_free(&attrs); + attr_free(&fattrs); + return (-1); + } + + rde_update_log("update", peer, &fattrs, + &prefix, prefixlen); + path_update(peer, &fattrs, &prefix, prefixlen); + } + default: + fatalx("unsupported AF"); + } + } + /* need to free allocated attribute memory that is no longer used */ attr_free(&attrs); @@ -624,7 +762,7 @@ rde_update_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix, p += 1; plen = 1; - addr.a32.s_addr = 0; + bzero(prefix, sizeof(struct bgpd_addr)); for (i = 0; i <= 3; i++) { if (pfxlen > i * 8) { if (len - plen < 1) @@ -640,6 +778,36 @@ rde_update_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix, return (plen); } +int +rde_update_get_prefix6(u_char *p, u_int16_t len, struct bgpd_addr *prefix, + u_int8_t *prefixlen) +{ + int i; + u_int8_t pfxlen; + u_int16_t plen; + + if (len < 1) + return (-1); + + memcpy(&pfxlen, p, 1); + p += 1; + plen = 1; + + bzero(prefix, sizeof(struct bgpd_addr)); + for (i = 0; i <= 15; i++) { + if (pfxlen > i * 8) { + if (len - plen < 1) + return (-1); + memcpy(&prefix->v6.s6_addr[i], p++, 1); + plen++; + } + } + prefix->af = AF_INET6; + *prefixlen = pfxlen; + + return (plen); +} + void rde_update_err(struct rde_peer *peer, u_int8_t error, u_int8_t suberr, void *data, u_int16_t size) @@ -664,19 +832,30 @@ rde_update_log(const char *message, const struct bgpd_addr *prefix, u_int8_t prefixlen) { char *nexthop = NULL; + char *p = NULL; - if (! (conf->log & BGPD_LOG_UPDATES)) + if (!(conf->log & BGPD_LOG_UPDATES)) return; if (attr != NULL) - asprintf(&nexthop, " via %s", inet_ntoa(attr->nexthop)); + if (attr_ismp(attr)) { + if (asprintf(&nexthop, " via %s", + log_addr(attr_mp_nexthop(attr))) == -1) + nexthop = NULL; + else + if (asprintf(&nexthop, " via %s", + inet_ntoa(attr->nexthop)) == -1) + nexthop = NULL; + } + if (asprintf(&p, "%s/%u", log_addr(prefix), prefixlen) == -1) + p = NULL; log_debug("neighbor %s (AS%u) %s %s/%u %s", log_addr(&peer->conf.remote_addr), peer->conf.remote_as, message, - inet_ntoa(prefix->v4), prefixlen, - nexthop ? nexthop : ""); + p ? p : "out of memory", nexthop ? nexthop : ""); free(nexthop); + free(p); } int diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index e82c3b548de..7170224de9f 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.42 2004/06/22 20:28:58 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.43 2004/06/24 23:15:58 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -99,7 +99,9 @@ enum attrtypes { ATTR_AGGREGATOR, ATTR_COMMUNITIES, ATTR_ORIGINATOR_ID, - ATTR_CLUSTER_LIST + ATTR_CLUSTER_LIST, + ATTR_MP_REACH_NLRI=14, + ATTR_MP_UNREACH_NLRI=15 }; /* attribute flags. 4 low order bits reserved */ @@ -148,7 +150,7 @@ struct nexthop { #if 0 /* * currently we use the boolean nexthop state, this could be exchanged - * with a variable coast with a max for unreachable. + * with a variable cost with a max for unreachable. */ u_int32_t costs; #endif @@ -248,8 +250,11 @@ 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, void *, u_int16_t); -struct attr *attr_optget(struct attr_flags *, u_int8_t); +struct attr *attr_optget(const struct attr_flags *, u_int8_t); void attr_optfree(struct attr_flags *); +int attr_ismp(const struct attr_flags *); +int attr_mp_nexthop_check(u_char *, u_int16_t, u_int16_t); +struct bgpd_addr *attr_mp_nexthop(const struct attr_flags *); int aspath_verify(void *, u_int16_t); #define AS_ERR_LEN -1 diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index 0502f04118b..9bfaa5393ef 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.34 2004/06/24 22:01:54 claudio Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.35 2004/06/24 23:15:58 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -44,6 +44,8 @@ #define F_ATTR_NEXTHOP 0x04 #define F_ATTR_LOCALPREF 0x08 #define F_ATTR_MED 0x10 +#define F_ATTR_MP_REACH 0x20 +#define F_ATTR_MP_UNREACH 0x40 #define WFLAG(s, t) \ do { \ @@ -178,14 +180,30 @@ attr_parse(u_char *p, u_int16_t len, struct attr_flags *a, int ebgp, case ATTR_ORIGINATOR_ID: if (attr_len != 4) return (-1); - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, ATTR_PARTIAL)) + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) return (-1); goto optattr; case ATTR_CLUSTER_LIST: if ((attr_len & 0x3) != 0) return (-1); - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, ATTR_PARTIAL)) + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) + return (-1); + goto optattr; + case ATTR_MP_REACH_NLRI: + if (attr_len < 4) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) + return (-1); + /* the actually validity is checked in rde_update_dispatch() */ + WFLAG(a->wflags, F_ATTR_MP_REACH); + goto optattr; + case ATTR_MP_UNREACH_NLRI: + if (attr_len < 3) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) return (-1); + /* the actually validity is checked in rde_update_dispatch() */ + WFLAG(a->wflags, F_ATTR_MP_UNREACH); goto optattr; default: optattr: @@ -203,6 +221,7 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, u_int8_t *suberr, u_int16_t *size) { struct attr *a; + u_char *op; u_int16_t attr_len; u_int16_t plen = 0; u_int8_t flags; @@ -211,15 +230,16 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, *suberr = ERR_UPD_ATTRLEN; *size = len; + op = p; if (len < 3) - return (p); + return (op); UPD_READ(&flags, p, plen, 1); UPD_READ(&type, p, plen, 1); if (flags & ATTR_EXTLEN) { if (len - plen < 2) - return (p); + return (op); UPD_READ(&attr_len, p, plen, 2); } else { UPD_READ(&tmp8, p, plen, 1); @@ -227,7 +247,7 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, } if (len - plen < attr_len) - return (p); + return (op); *size = attr_len; switch (type) { @@ -238,7 +258,7 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, return (NULL); case ATTR_ORIGIN: if (attr_len != 1) - return (p); + return (op); if (attr->wflags & F_ATTR_ORIGIN) { *suberr = ERR_UPD_ATTRLIST; *size = 0; @@ -247,7 +267,7 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, UPD_READ(&tmp8, p, plen, 1); if (tmp8 > ORIGIN_INCOMPLETE) { *suberr = ERR_UPD_ORIGIN; - return (p); + return (op); } break; case ATTR_ASPATH: @@ -265,7 +285,7 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, break; case ATTR_NEXTHOP: if (attr_len != 4) - return (p); + return (op); if (attr->wflags & F_ATTR_NEXTHOP) { *suberr = ERR_UPD_ATTRLIST; *size = 0; @@ -274,12 +294,12 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, if (CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) { /* malformed nexthop detected by exclusion method */ *suberr = ERR_UPD_NETWORK; - return (p); + return (op); } break; case ATTR_MED: if (attr_len != 4) - return (p); + return (op); if (attr->wflags & F_ATTR_MED) { *suberr = ERR_UPD_ATTRLIST; *size = 0; @@ -288,7 +308,7 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, break; case ATTR_LOCALPREF: if (attr_len != 4) - return (p); + return (op); if (attr->wflags & F_ATTR_LOCALPREF) { *suberr = ERR_UPD_ATTRLIST; *size = 0; @@ -297,29 +317,37 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, break; case ATTR_ATOMIC_AGGREGATE: if (attr_len != 0) - return (p); + return (op); break; case ATTR_AGGREGATOR: if (attr_len != 6) - return (p); + return (op); break; case ATTR_COMMUNITIES: if ((attr_len & 0x3) != 0) - return (p); + return (op); goto optattr; case ATTR_ORIGINATOR_ID: if (attr_len != 4) - return (p); + return (op); goto optattr; case ATTR_CLUSTER_LIST: if ((attr_len & 0x3) != 0) - return (p); + return (op); + goto optattr; + case ATTR_MP_REACH_NLRI: + if (attr_len < 4) + return (op); + goto optattr; + case ATTR_MP_UNREACH_NLRI: + if (attr_len < 3) + return (op); goto optattr; default: optattr: if ((flags & ATTR_OPTIONAL) == 0) { *suberr = ERR_UPD_UNKNWN_WK_ATTR; - return (p); + return (op); } TAILQ_FOREACH(a, &attr->others, entry) if (type == a->type) { @@ -328,11 +356,11 @@ optattr: return (NULL); } *suberr = ERR_UPD_OPTATTR; - return (p); + return (op); } /* can only be a attribute flag error */ *suberr = ERR_UPD_ATTRFLAGS; - return (p); + return (op); } #undef UPD_READ #undef WFLAG @@ -344,7 +372,8 @@ attr_missing(struct attr_flags *a, int ebgp) return (ATTR_ORIGIN); if ((a->wflags & F_ATTR_ASPATH) == 0) return (ATTR_ASPATH); - if ((a->wflags & F_ATTR_NEXTHOP) == 0) + if ((a->wflags & F_ATTR_MP_REACH) == 0 && + (a->wflags & F_ATTR_NEXTHOP) == 0) return (ATTR_NEXTHOP); if (!ebgp) if ((a->wflags & F_ATTR_LOCALPREF) == 0) @@ -362,6 +391,10 @@ attr_compare(struct attr_flags *a, struct attr_flags *b) return (1); if (a->origin < b->origin) return (-1); + if ((a->wflags & F_ATTR_NEXTHOP) && (b->wflags & F_ATTR_NEXTHOP) == 0) + return (1); + if ((b->wflags & F_ATTR_NEXTHOP) && (a->wflags & F_ATTR_NEXTHOP) == 0) + return (-1); if (a->nexthop.s_addr > b->nexthop.s_addr) return (1); if (a->nexthop.s_addr < b->nexthop.s_addr) @@ -518,7 +551,7 @@ attr_optadd(struct attr_flags *attr, u_int8_t flags, u_int8_t type, } struct attr * -attr_optget(struct attr_flags *attr, u_int8_t type) +attr_optget(const struct attr_flags *attr, u_int8_t type) { struct attr *a; @@ -544,6 +577,94 @@ attr_optfree(struct attr_flags *attr) } } +int +attr_ismp(const struct attr_flags *attr) +{ + return (attr->wflags & F_ATTR_MP_REACH); +} + +int +attr_mp_nexthop_check(u_char *data, u_int16_t len, u_int16_t afi) +{ + u_int8_t nh_len; + + if (len == 0) + return (-1); + + nh_len = *data++; + len--; + + if (nh_len > len) + return (-1); + + switch (afi) { + case AFI_IPv6: + if (nh_len != 16 && nh_len != 32) { + log_warnx("bad multiprotocol nexthop, bad size"); + return (-1); + } + return (nh_len + 1); + default: + log_warnx("bad multiprotocol nexthop, bad AF"); + break; + } + + return (-1); +} + +/* + * this function may only be called after attr_mp_nexthop_check() + */ +struct bgpd_addr * +attr_mp_nexthop(const struct attr_flags *attrs) +{ + static struct bgpd_addr address; + struct attr *mpattr; + u_int8_t *p; + u_int16_t afi; + u_int8_t nhlen; + + if ((mpattr = attr_optget(attrs, ATTR_MP_REACH_NLRI)) == NULL) { + log_warnx("attr_mp_nexthop: no MP_REACH_NLRI available"); + return (NULL); + } + p = mpattr->data; + if (mpattr->len < 4) + fatalx("Who fucked up the code? King Bula?"); + + memcpy(&afi, p, 2); + afi = ntohs(afi); + p += 3; + + nhlen = *p++; + if (nhlen > mpattr->len) + fatalx("Who fucked up the code? King Bula?"); + + bzero(&address, sizeof(address)); + switch (afi) { + case AFI_IPv6: + address.af = AF_INET6; + if (nhlen == 16) { + memcpy(&address.v6.s6_addr, p, 16); + return (&address); + } + if (nhlen == 32) { + /* + * XXX acctually the link lokal address should be used + * for kroute and the global one updates. + */ + memcpy(&address.v6.s6_addr, p, 16); + return (&address); + } + fatalx("Who fucked up the code? King Bula?"); + default: + fatalx("attr_mp_nexthop: unsupported AF"); + } + + /* NOTREACHED */ + return (NULL); +} + /* aspath specific functions */ static u_int16_t aspath_extract(void *, int); diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index 38704d1ba0e..cd01ce65082 100644 --- a/usr.sbin/bgpd/rde_filter.c +++ b/usr.sbin/bgpd/rde_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_filter.c,v 1.10 2004/06/20 18:35:12 henning Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.11 2004/06/24 23:15:58 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -95,7 +95,6 @@ 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, @@ -109,16 +108,9 @@ rde_filter_match(struct filter_rule *f, struct attr_flags *attrs, if (f->match.prefix.addr.af != 0 && f->match.prefix.addr.af == prefix->af) { - switch (f->match.prefix.addr.af) { - case AF_INET: - mask = htonl(0xffffffff << (32 - f->match.prefix.len)); - 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"); - } + if (prefix_equal(prefix, &f->match.prefix.addr, + f->match.prefix.len) != 0) + return (0); /* test prefixlen stuff too */ switch (f->match.prefixlen.op) { |