diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 3 | ||||
-rw-r--r-- | usr.sbin/bgpd/mrt.c | 26 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 707 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 127 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 568 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_decide.c | 33 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 60 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_rib.c | 521 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_update.c | 213 |
9 files changed, 1151 insertions, 1107 deletions
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 1c12d39ee98..8fe5562dbe9 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.138 2004/08/05 15:58:21 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.139 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -160,6 +160,7 @@ struct filter_set { u_int16_t flags; u_int32_t localpref; u_int32_t med; + /*XXX this nexthop thing should be changed to one bgpd_addr */ struct in_addr nexthop; struct in6_addr nexthop6; u_int8_t prepend; diff --git a/usr.sbin/bgpd/mrt.c b/usr.sbin/bgpd/mrt.c index ddecc29a2ad..0c9b369123c 100644 --- a/usr.sbin/bgpd/mrt.c +++ b/usr.sbin/bgpd/mrt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mrt.c,v 1.38 2004/08/05 16:26:56 claudio Exp $ */ +/* $OpenBSD: mrt.c,v 1.39 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -32,8 +32,9 @@ #include "mrt.h" -static u_int16_t mrt_attr_length(struct attr_flags *); -static int mrt_attr_dump(void *, u_int16_t, struct attr_flags *); +static u_int16_t mrt_attr_length(struct rde_aspath *); +static int mrt_attr_dump(void *, u_int16_t, struct rde_aspath *, + struct bgpd_addr *); static int mrt_dump_entry(struct mrt *, struct prefix *, u_int16_t, struct peer_config *); static int mrt_dump_header(struct buf *, u_int16_t, u_int16_t, @@ -153,7 +154,7 @@ mrt_dump_state(struct mrt *mrt, u_int16_t old_state, u_int16_t new_state, } static u_int16_t -mrt_attr_length(struct attr_flags *a) +mrt_attr_length(struct rde_aspath *a) { struct attr *oa; u_int16_t alen, plen; @@ -171,7 +172,8 @@ mrt_attr_length(struct attr_flags *a) } static int -mrt_attr_dump(void *p, u_int16_t len, struct attr_flags *a) +mrt_attr_dump(void *p, u_int16_t len, struct rde_aspath *a, + struct bgpd_addr *nexthop) { struct attr *oa; u_char *buf = p; @@ -194,7 +196,7 @@ mrt_attr_dump(void *p, u_int16_t len, struct attr_flags *a) /* nexthop, already network byte order */ if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN, ATTR_NEXTHOP, - &a->nexthop, 4)) == -1) + &nexthop->v4.s_addr, 4)) == -1) return (-1); wlen += r; len -= r; @@ -231,10 +233,10 @@ mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum, { struct buf *buf; void *bptr; - struct bgpd_addr addr; + struct bgpd_addr addr, *nh; u_int16_t len, attr_len; - attr_len = mrt_attr_length(&p->aspath->flags); + attr_len = mrt_attr_length(p->aspath); len = MRT_DUMP_HEADER_SIZE + attr_len; pt_getaddr(p->prefix, &addr); @@ -264,7 +266,13 @@ mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum, return (-1); } - if (mrt_attr_dump(bptr, attr_len, &p->aspath->flags) == -1) { + if (p->aspath->nexthop == NULL) { + bzero(&addr, sizeof(struct bgpd_addr)); + addr.af = AF_INET; + nh = &addr; + } else + nh = &p->aspath->nexthop->exit_nexthop; + if (mrt_attr_dump(bptr, attr_len, p->aspath, nh) == -1) { log_warnx("mrt_dump_entry: mrt_attr_dump error"); buf_free(buf); return (-1); diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 22f51212ceb..e56d5b78237 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.138 2004/08/06 11:51:19 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.139 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -41,6 +41,13 @@ void rde_sighdlr(int); void rde_dispatch_imsg_session(struct imsgbuf *); void rde_dispatch_imsg_parent(struct imsgbuf *); int rde_update_dispatch(struct imsg *); +int rde_attr_parse(u_char *, u_int16_t, struct rde_aspath *, int, + enum enforce_as, u_int16_t, struct mpattr *); +u_char *rde_attr_error(u_char *, u_int16_t, struct rde_aspath *, + u_int8_t *, u_int16_t *); +u_int8_t rde_attr_missing(struct rde_aspath *, int, u_int16_t); +int rde_get_mp_nexthop(u_char *, u_int16_t, u_int16_t, + struct rde_aspath *); 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 *, @@ -48,9 +55,9 @@ int rde_update_get_prefix6(u_char *, u_int16_t, struct bgpd_addr *, void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, void *, u_int16_t); void rde_update_log(const char *, - const struct rde_peer *, const struct attr_flags *, + const struct rde_peer *, const struct bgpd_addr *, const struct bgpd_addr *, u_int8_t); -int rde_reflector(struct rde_peer *, struct attr_flags *); +int rde_reflector(struct rde_peer *, struct rde_aspath *); void rde_dump_rib_as(struct prefix *, pid_t); void rde_dump_rib_prefix(struct prefix *, pid_t); void rde_dump_upcall(struct pt_entry *, void *); @@ -501,16 +508,16 @@ int rde_update_dispatch(struct imsg *imsg) { struct rde_peer *peer; - u_char *p, *emsg, *mpp; - int pos; + struct rde_aspath *asp = NULL, *fasp; + u_char *p, *emsg, *mpp = NULL; + int pos = 0; 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, safi, subtype; struct bgpd_addr prefix; - struct attr_flags attrs, fattrs; - struct attr *mpattr; + struct mpattr mpa; peer = peer_get(imsg->hdr.peerid); if (peer == NULL) /* unknown peer, cannot happen */ @@ -537,18 +544,23 @@ rde_update_dispatch(struct imsg *imsg) rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); return (-1); } + + nlri_len = + imsg->hdr.len - IMSG_HEADER_SIZE - 4 - withdrawn_len - attrpath_len; + bzero(&mpa, sizeof(mpa)); + if (attrpath_len != 0) { /* 0 = no NLRI information in this message */ /* parse path attributes */ - attr_init(&attrs); + asp = path_get(); while (len > 0) { - if ((pos = attr_parse(p, len, &attrs, + if ((pos = rde_attr_parse(p, len, asp, peer->conf.ebgp, peer->conf.enforce_as, - peer->conf.remote_as)) < 0) { - emsg = attr_error(p, len, &attrs, + peer->conf.remote_as, &mpa)) < 0) { + emsg = rde_attr_error(p, len, asp, &subtype, &size); rde_update_err(peer, ERR_UPDATE, subtype, emsg, size); - attr_free(&attrs); + path_put(asp); return (-1); } p += pos; @@ -556,15 +568,16 @@ rde_update_dispatch(struct imsg *imsg) } /* check for missing but necessary attributes */ - if ((subtype = attr_missing(&attrs, peer->conf.ebgp)) != 0) { + if ((subtype = rde_attr_missing(asp, peer->conf.ebgp, + nlri_len))) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_MISSNG_WK_ATTR, &subtype, sizeof(u_int8_t)); - attr_free(&attrs); + path_put(asp); return (-1); } - if (rde_reflector(peer, &attrs) != 1) { - attr_free(&attrs); + if (rde_reflector(peer, asp) != 1) { + path_put(asp); return (0); } } @@ -584,7 +597,7 @@ rde_update_dispatch(struct imsg *imsg) rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); if (attrpath_len != 0) - attr_free(&attrs); + path_put(asp); return (-1); } if (prefixlen > 32) { @@ -592,7 +605,7 @@ rde_update_dispatch(struct imsg *imsg) rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); if (attrpath_len != 0) - attr_free(&attrs); + path_put(asp); return (-1); } @@ -608,11 +621,13 @@ rde_update_dispatch(struct imsg *imsg) prefix_remove(peer, &prefix, prefixlen); } + if (attrpath_len == 0) /* 0 = no NLRI information in this message */ + return (0); + /* 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; + if (mpa.unreach_len != 0) { + mpp = mpa.unreach; + mplen = mpa.unreach_len; memcpy(&afi, mpp, 2); mpp += 2; mplen -= 2; @@ -628,8 +643,8 @@ rde_update_dispatch(struct imsg *imsg) "bad IPv6 withdraw prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - mpattr->data, mpattr->len); - attr_free(&attrs); + mpa.unreach, mpa.unreach_len); + path_put(asp); return (-1); } if (prefixlen > 128) { @@ -637,8 +652,8 @@ rde_update_dispatch(struct imsg *imsg) "bad IPv6 withdraw prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - mpattr->data, mpattr->len); - attr_free(&attrs); + mpa.unreach, mpa.unreach_len); + path_put(asp); return (-1); } @@ -654,28 +669,23 @@ rde_update_dispatch(struct imsg *imsg) &prefix, prefixlen); prefix_remove(peer, &prefix, prefixlen); } + break; default: fatalx("unsupported multipath AF"); } } - if (attrpath_len == 0) /* 0 = no NLRI information in this message */ - return (0); - /* shift to NLRI information */ p += 2 + attrpath_len; - nlri_len = - imsg->hdr.len - IMSG_HEADER_SIZE - 4 - withdrawn_len - attrpath_len; - /* aspath needs to be loop free nota bene this is not a hard error */ - if (peer->conf.ebgp && !aspath_loopfree(attrs.aspath, conf->as)) { - attr_free(&attrs); + if (peer->conf.ebgp && !aspath_loopfree(asp->aspath, conf->as)) { + path_put(asp); return (0); } /* apply default overrides */ - rde_apply_set(&attrs, &peer->conf.attrset); + rde_apply_set(asp, &peer->conf.attrset); /* parse nlri prefix */ while (nlri_len > 0) { @@ -684,14 +694,14 @@ rde_update_dispatch(struct imsg *imsg) log_peer_warnx(&peer->conf, "bad nlri prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); - attr_free(&attrs); + path_put(asp); return (-1); } if (prefixlen > 32) { log_peer_warnx(&peer->conf, "bad nlri prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); - attr_free(&attrs); + path_put(asp); return (-1); } @@ -702,11 +712,11 @@ rde_update_dispatch(struct imsg *imsg) * We need to copy attrs before calling the filter because * the filter may change the attributes. */ - attr_copy(&fattrs, &attrs); + fasp = path_copy(asp); /* input filter */ - if (rde_filter(peer, &fattrs, &prefix, prefixlen, + if (rde_filter(peer, fasp, &prefix, prefixlen, DIR_IN) == ACTION_DENY) { - attr_free(&fattrs); + path_put(fasp); continue; } @@ -716,19 +726,20 @@ rde_update_dispatch(struct imsg *imsg) 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); + path_put(asp); + path_put(fasp); return (-1); } - rde_update_log("update", peer, &fattrs, &prefix, prefixlen); - path_update(peer, &fattrs, &prefix, prefixlen); + rde_update_log("update", peer, &asp->nexthop->exit_nexthop, + &prefix, prefixlen); + path_update(peer, fasp, &prefix, prefixlen); } /* add MP_REACH_NLRI if available */ - if ((mpattr = attr_optget(&attrs, ATTR_MP_REACH_NLRI)) != NULL) { - mpp = mpattr->data; - mplen = mpattr->len; + if (mpa.reach_len != 0) { + mpp = mpa.reach; + mplen = mpa.reach_len; memcpy(&afi, mpp, 2); mpp += 2; mplen -= 2; @@ -736,25 +747,19 @@ rde_update_dispatch(struct imsg *imsg) safi = *mpp++; mplen--; - if ((pos = attr_mp_nexthop_check(mpp, mplen, afi)) == -1) { + /* + * this works because asp is not linked. + */ + if ((pos = rde_get_mp_nexthop(mpp, mplen, afi, asp)) == -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); + mpa.reach, mpa.reach_len); + path_put(asp); 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) { @@ -764,30 +769,29 @@ rde_update_dispatch(struct imsg *imsg) "bad IPv6 nlri prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - mpattr->data, mpattr->len); - attr_free(&attrs); + mpa.reach, mpa.reach_len); + path_put(asp); return (-1); } if (prefixlen > 128) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - mpattr->data, mpattr->len); - attr_free(&attrs); + mpa.reach, mpa.reach_len); + path_put(asp); return (-1); } mpp += pos; mplen -= pos; - attr_copy(&fattrs, &attrs); + fasp = path_copy(asp); /* input filter */ - if (rde_filter(peer, &fattrs, &prefix, + if (rde_filter(peer, asp, &prefix, prefixlen, DIR_IN) == ACTION_DENY) { - attr_free(&fattrs); + path_put(fasp); continue; } - /* XXX IPv4 and IPv6 together */ /* max prefix checker */ if (peer->conf.max_prefix && peer->prefix_cnt >= peer->conf.max_prefix) { @@ -795,14 +799,15 @@ rde_update_dispatch(struct imsg *imsg) "prefix limit reached"); rde_update_err(peer, ERR_CEASE, ERR_CEASE_MAX_PREFIX, NULL, 0); - attr_free(&attrs); - attr_free(&fattrs); + path_put(asp); + path_put(fasp); return (-1); } - rde_update_log("update", peer, &fattrs, + rde_update_log("update", peer, + &asp->nexthop->exit_nexthop, &prefix, prefixlen); - path_update(peer, &fattrs, &prefix, prefixlen); + path_update(peer, fasp, &prefix, prefixlen); } default: fatalx("unsupported AF"); @@ -810,12 +815,437 @@ rde_update_dispatch(struct imsg *imsg) } /* need to free allocated attribute memory that is no longer used */ - attr_free(&attrs); + path_put(asp); + + return (0); +} + +/* + * BGP UPDATE parser functions + */ + +/* attribute parser specific makros */ +#define UPD_READ(t, p, plen, n) \ + do { \ + memcpy(t, p, n); \ + p += n; \ + plen += n; \ + } while (0) + +#define CHECK_FLAGS(s, t, m) \ + (((s) & ~(ATTR_EXTLEN | (m))) == (t)) + +#define WFLAG(s, t) \ + do { \ + if ((s) & (t)) \ + return (-1); \ + (s) |= (t); \ + } while (0) + +int +rde_attr_parse(u_char *p, u_int16_t len, struct rde_aspath *a, int ebgp, + enum enforce_as enforce_as, u_int16_t remote_as, struct mpattr *mpa) +{ + struct bgpd_addr nexthop; + u_int32_t tmp32; + u_int16_t attr_len; + u_int16_t plen = 0; + u_int8_t flags; + u_int8_t type; + u_int8_t tmp8; + + if (len < 3) + return (-1); + + UPD_READ(&flags, p, plen, 1); + UPD_READ(&type, p, plen, 1); + + if (flags & ATTR_EXTLEN) { + if (len - plen < 2) + return (-1); + UPD_READ(&attr_len, p, plen, 2); + attr_len = ntohs(attr_len); + } else { + UPD_READ(&tmp8, p, plen, 1); + attr_len = tmp8; + } + + if (len - plen < attr_len) + return (-1); + + switch (type) { + case ATTR_UNDEF: + /* error! */ + return (-1); + case ATTR_ORIGIN: + if (attr_len != 1) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) + return (-1); + UPD_READ(&a->origin, p, plen, 1); + if (a->origin > ORIGIN_INCOMPLETE) + return (-1); + WFLAG(a->flags, F_ATTR_ORIGIN); + break; + case ATTR_ASPATH: + if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) + return (-1); + if (aspath_verify(p, attr_len) != 0) + return (-1); + WFLAG(a->flags, F_ATTR_ASPATH); + a->aspath = aspath_get(p, attr_len); + if (enforce_as == ENFORCE_AS_ON && + remote_as != aspath_neighbor(a->aspath)) + return (-1); + + plen += attr_len; + break; + case ATTR_NEXTHOP: + if (attr_len != 4) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) + return (-1); + WFLAG(a->flags, F_ATTR_NEXTHOP); + bzero(&nexthop, sizeof(nexthop)); + nexthop.af = AF_INET; + UPD_READ(&nexthop.v4.s_addr, p, plen, 4); + /* + * Check if the nexthop is a valid IP address. We consider + * multicast and experimental addresses as invalid. + */ + tmp32 = ntohl(nexthop.v4.s_addr); + if (IN_MULTICAST(tmp32) || IN_BADCLASS(tmp32)) + return (-1); + + a->nexthop = nexthop_get(&nexthop, NULL); + break; + case ATTR_MED: + if (attr_len != 4) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) + return (-1); + WFLAG(a->flags, F_ATTR_MED); + UPD_READ(&tmp32, p, plen, 4); + a->med = ntohl(tmp32); + break; + case ATTR_LOCALPREF: + if (attr_len != 4) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) + return (-1); + if (ebgp) { + /* ignore local-pref attr for non ibgp peers */ + a->lpref = 0; /* set a default value ... */ + plen += 4; /* and ignore the real value */ + break; + } + WFLAG(a->flags, F_ATTR_LOCALPREF); + UPD_READ(&tmp32, p, plen, 4); + a->lpref = ntohl(tmp32); + break; + case ATTR_ATOMIC_AGGREGATE: + if (attr_len != 0) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) + return (-1); + goto optattr; + case ATTR_AGGREGATOR: + if (attr_len != 6) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, 0)) + return (-1); + goto optattr; + case ATTR_COMMUNITIES: + if ((attr_len & 0x3) != 0) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, + ATTR_PARTIAL)) + return (-1); + goto optattr; + case ATTR_ORIGINATOR_ID: + if (attr_len != 4) + return (-1); + 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, 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->flags, F_ATTR_MP_REACH); + + mpa->reach = p; + mpa->reach_len = attr_len; + plen += attr_len; + break; + 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->flags, F_ATTR_MP_UNREACH); + + mpa->unreach = p; + mpa->unreach_len = attr_len; + plen += attr_len; + break; + default: +optattr: + if (attr_optadd(a, flags, type, p, attr_len) == -1) + return (-1); + plen += attr_len; + break; + } + + return (plen); +} + +u_char * +rde_attr_error(u_char *p, u_int16_t len, struct rde_aspath *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; + u_int8_t type; + u_int8_t tmp8; + + *suberr = ERR_UPD_ATTRLEN; + *size = len; + op = p; + if (len < 3) + return (op); + + UPD_READ(&flags, p, plen, 1); + UPD_READ(&type, p, plen, 1); + + if (flags & ATTR_EXTLEN) { + if (len - plen < 2) + return (op); + UPD_READ(&attr_len, p, plen, 2); + } else { + UPD_READ(&tmp8, p, plen, 1); + attr_len = tmp8; + } + + if (len - plen < attr_len) + return (op); + *size = attr_len; + + switch (type) { + case ATTR_UNDEF: + /* error! */ + *suberr = ERR_UPD_UNSPECIFIC; + *size = 0; + return (NULL); + case ATTR_ORIGIN: + if (attr_len != 1) + return (op); + if (attr->flags & F_ATTR_ORIGIN) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + UPD_READ(&tmp8, p, plen, 1); + if (tmp8 > ORIGIN_INCOMPLETE) { + *suberr = ERR_UPD_ORIGIN; + return (op); + } + break; + case ATTR_ASPATH: + if (attr->flags & F_ATTR_ASPATH) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + if (CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) { + /* malformed aspath detected by exclusion method */ + *size = 0; + *suberr = ERR_UPD_ASPATH; + return (NULL); + } + break; + case ATTR_NEXTHOP: + if (attr_len != 4) + return (op); + if (attr->flags & F_ATTR_NEXTHOP) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + if (CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) { + /* malformed nexthop detected by exclusion method */ + *suberr = ERR_UPD_NETWORK; + return (op); + } + break; + case ATTR_MED: + if (attr_len != 4) + return (op); + if (attr->flags & F_ATTR_MED) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + break; + case ATTR_LOCALPREF: + if (attr_len != 4) + return (op); + if (attr->flags & F_ATTR_LOCALPREF) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + break; + case ATTR_ATOMIC_AGGREGATE: + if (attr_len != 0) + return (op); + break; + case ATTR_AGGREGATOR: + if (attr_len != 6) + return (op); + break; + case ATTR_COMMUNITIES: + if ((attr_len & 0x3) != 0) + return (op); + goto optattr; + case ATTR_ORIGINATOR_ID: + if (attr_len != 4) + return (op); + goto optattr; + case ATTR_CLUSTER_LIST: + if ((attr_len & 0x3) != 0) + return (op); + goto optattr; + case ATTR_MP_REACH_NLRI: + if (attr_len < 4) + return (op); + if (attr->flags & F_ATTR_MP_REACH) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + break; + case ATTR_MP_UNREACH_NLRI: + if (attr_len < 3) + return (op); + if (attr->flags & F_ATTR_MP_UNREACH) { + *suberr = ERR_UPD_ATTRLIST; + *size = 0; + return (NULL); + } + break; + default: +optattr: + if ((flags & ATTR_OPTIONAL) == 0) { + *suberr = ERR_UPD_UNKNWN_WK_ATTR; + return (op); + } + TAILQ_FOREACH(a, &attr->others, entry) + if (type == a->type) { + *size = 0; + *suberr = ERR_UPD_ATTRLIST; + return (NULL); + } + *suberr = ERR_UPD_OPTATTR; + return (op); + } + /* can only be a attribute flag error */ + *suberr = ERR_UPD_ATTRFLAGS; + return (op); +} +#undef UPD_READ +#undef WFLAG +u_int8_t +rde_attr_missing(struct rde_aspath *a, int ebgp, u_int16_t nlrilen) +{ + /* ATTR_MP_UNREACH_NLRI may be sent alone */ + if (nlrilen == 0 && a->flags & F_ATTR_MP_UNREACH && + (a->flags & F_ATTR_MP_REACH) == 0) + return (0); + + if ((a->flags & F_ATTR_ORIGIN) == 0) + return (ATTR_ORIGIN); + if ((a->flags & F_ATTR_ASPATH) == 0) + return (ATTR_ASPATH); + if ((a->flags & F_ATTR_MP_REACH) == 0 && + (a->flags & F_ATTR_NEXTHOP) == 0) + return (ATTR_NEXTHOP); + if (!ebgp) + if ((a->flags & F_ATTR_LOCALPREF) == 0) + return (ATTR_LOCALPREF); return (0); } int +rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int16_t afi, + struct rde_aspath *asp) +{ + struct bgpd_addr nexthop, llnh, *lp; + u_int8_t totlen, nhlen; + + if (len == 0) + return (-1); + + nhlen = *data++; + totlen = 1; + len--; + + if (nhlen > len) + return (-1); + + bzero(&nexthop, sizeof(nexthop)); + bzero(&llnh, sizeof(llnh)); + switch (afi) { + case AFI_IPv6: + if (nhlen != 16 && nhlen != 32) { + log_warnx("bad multiprotocol nexthop, bad size"); + return (-1); + } + nexthop.af = AF_INET6; + memcpy(&nexthop.v6.s6_addr, data, 16); + lp = NULL; + if (nhlen == 32) { + /* + * rfc 2545 describes that there may be a link local + * address carried in nexthop. Yikes. + */ + llnh.af = AF_INET6; + memcpy(&llnh.v6.s6_addr, data, 16); + /* XXX we need to set the scope_id */ + lp = &llnh; + } + asp->nexthop = nexthop_get(&nexthop, lp); + + totlen += nhlen; + data += nhlen; + + if (*data != 0) { + log_warnx("SNPA are not supported for IPv6"); + return (-1); + } + return (++totlen); + default: + log_warnx("bad multiprotocol nexthop, bad AF"); + break; + } + + return (-1); +} + +int rde_update_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix, u_int8_t *prefixlen) { @@ -901,7 +1331,7 @@ rde_update_err(struct rde_peer *peer, u_int8_t error, u_int8_t suberr, void rde_update_log(const char *message, - const struct rde_peer *peer, const struct attr_flags *attr, + const struct rde_peer *peer, const struct bgpd_addr *next, const struct bgpd_addr *prefix, u_int8_t prefixlen) { char *nexthop = NULL; @@ -910,16 +1340,10 @@ rde_update_log(const char *message, if (!(conf->log & BGPD_LOG_UPDATES)) return; - if (attr != NULL) - 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 (next != NULL) + if (asprintf(&nexthop, " via %s", + log_addr(next)) == -1) + nexthop = NULL; if (asprintf(&p, "%s/%u", log_addr(prefix), prefixlen) == -1) p = NULL; @@ -931,26 +1355,30 @@ rde_update_log(const char *message, free(p); } +/* + * route reflector helper function + */ + int -rde_reflector(struct rde_peer *peer, struct attr_flags *attrs) +rde_reflector(struct rde_peer *peer, struct rde_aspath *asp) { struct attr *a; u_int16_t len; /* check for originator id if eq router_id drop */ - if ((a = attr_optget(attrs, ATTR_ORIGINATOR_ID)) != NULL) { + if ((a = attr_optget(asp, ATTR_ORIGINATOR_ID)) != NULL) { if (memcmp(&conf->bgpid, a->data, sizeof(conf->bgpid)) == 0) /* this is coming from myself */ return (0); } else if ((conf->flags & BGPD_FLAG_REFLECTOR) && - attr_optadd(attrs, ATTR_OPTIONAL, ATTR_ORIGINATOR_ID, + attr_optadd(asp, ATTR_OPTIONAL, ATTR_ORIGINATOR_ID, peer->conf.ebgp == 0 ? &peer->remote_bgpid : &conf->bgpid, sizeof(u_int32_t)) == -1) fatalx("attr_optadd failed but impossible"); /* check for own id in the cluster list */ if (conf->flags & BGPD_FLAG_REFLECTOR) { - if ((a = attr_optget(attrs, ATTR_CLUSTER_LIST)) != NULL) { + if ((a = attr_optget(asp, ATTR_CLUSTER_LIST)) != NULL) { for (len = 0; len < a->len; len += sizeof(conf->clusterid)) /* check if coming from my cluster */ @@ -967,7 +1395,7 @@ rde_reflector(struct rde_peer *peer, struct attr_flags *attrs) a->len += sizeof(conf->clusterid); memcpy(a->data, &conf->clusterid, sizeof(conf->clusterid)); - } else if (attr_optadd(attrs, ATTR_OPTIONAL, ATTR_CLUSTER_LIST, + } else if (attr_optadd(asp, ATTR_OPTIONAL, ATTR_CLUSTER_LIST, &conf->clusterid, sizeof(conf->clusterid)) == -1) fatalx("attr_optadd failed but impossible"); } @@ -984,31 +1412,38 @@ rde_dump_rib_as(struct prefix *p, pid_t pid) struct buf *wbuf; rib.lastchange = p->lastchange; - rib.local_pref = p->aspath->flags.lpref; - rib.med = p->aspath->flags.med; + rib.local_pref = p->aspath->lpref; + rib.med = p->aspath->med; rib.prefix_cnt = p->aspath->prefix_cnt; rib.active_cnt = p->aspath->active_cnt; - memcpy(&rib.nexthop, &p->aspath->nexthop->true_nexthop, - sizeof(rib.nexthop)); + if (p->aspath->nexthop != NULL) + memcpy(&rib.nexthop, &p->aspath->nexthop->true_nexthop, + sizeof(rib.nexthop)); + else { + /* announced network may have a NULL nexthop */ + bzero(&rib.nexthop, sizeof(rib.nexthop)); + rib.nexthop.af = p->prefix->af; + } pt_getaddr(p->prefix, &rib.prefix); rib.prefixlen = p->prefix->prefixlen; - rib.origin = p->aspath->flags.origin; + rib.origin = p->aspath->origin; rib.flags = 0; if (p->prefix->active == p) rib.flags |= F_RIB_ACTIVE; if (p->peer->conf.ebgp == 0) rib.flags |= F_RIB_INTERNAL; - if (p->aspath->nexthop->state == NEXTHOP_REACH) - rib.flags |= F_RIB_ELIGIBLE; - if (p->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) + if (p->aspath->flags & F_PREFIX_ANNOUNCED) rib.flags |= F_RIB_ANNOUNCE; - rib.aspath_len = aspath_length(p->aspath->flags.aspath); + if (p->aspath->nexthop == NULL || + p->aspath->nexthop->state == NEXTHOP_REACH) + rib.flags |= F_RIB_ELIGIBLE; + rib.aspath_len = aspath_length(p->aspath->aspath); if ((wbuf = imsg_create_pid(&ibuf_se, IMSG_CTL_SHOW_RIB, pid, sizeof(rib) + rib.aspath_len)) == NULL) return; if (imsg_add(wbuf, &rib, sizeof(rib)) == -1 || - imsg_add(wbuf, aspath_dump(p->aspath->flags.aspath), + imsg_add(wbuf, aspath_dump(p->aspath->aspath), rib.aspath_len) == -1) return; if (imsg_close(&ibuf_se, wbuf) == -1) @@ -1028,10 +1463,11 @@ rde_dump_rib_prefix(struct prefix *p, pid_t pid) prefix.flags |= F_RIB_ACTIVE; if (p->peer->conf.ebgp == 0) prefix.flags |= F_RIB_INTERNAL; - if (p->aspath->nexthop->state == NEXTHOP_REACH) - prefix.flags |= F_RIB_ELIGIBLE; - if (p->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) + if (p->aspath->flags & F_PREFIX_ANNOUNCED) prefix.flags |= F_RIB_ANNOUNCE; + if (p->aspath->nexthop == NULL || + p->aspath->nexthop->state == NEXTHOP_REACH) + prefix.flags |= F_RIB_ELIGIBLE; if (imsg_compose_pid(&ibuf_se, IMSG_CTL_SHOW_RIB_PREFIX, pid, &prefix, sizeof(prefix)) == -1) log_warnx("rde_dump_as: imsg_compose error"); @@ -1060,7 +1496,7 @@ rde_dump_as(struct as_filter *a, pid_t pid) i = pathtable.path_hashmask; do { LIST_FOREACH(asp, &pathtable.path_hashtbl[i], path_l) { - if (!aspath_match(asp->flags.aspath, a->type, a->as)) + if (!aspath_match(asp->aspath, a->type, a->as)) continue; /* match found */ rde_dump_rib_as(LIST_FIRST(&asp->prefix_h), pid); @@ -1115,6 +1551,7 @@ rde_dump_prefix(struct ctl_show_rib_prefix *pref, pid_t pid) /* * kroute specific functions + * XXX notyet IPv6 ready */ void rde_send_kroute(struct prefix *new, struct prefix *old) @@ -1129,16 +1566,17 @@ rde_send_kroute(struct prefix *new, struct prefix *old) * On the other hand new may be UNREACH and then we should not * generate an update. */ - if ((old == NULL || old->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) && - (new == NULL || new->aspath->nexthop->state != NEXTHOP_REACH || - new->aspath->nexthop->flags & NEXTHOP_ANNOUNCE)) + if ((old == NULL || old->aspath->flags & F_PREFIX_ANNOUNCED) && + (new == NULL || new->aspath->nexthop == NULL || + new->aspath->nexthop->state != NEXTHOP_REACH || + new->aspath->flags & F_PREFIX_ANNOUNCED)) return; bzero(&kr, sizeof(kr)); if (new == NULL || new->aspath->nexthop == NULL || new->aspath->nexthop->state != NEXTHOP_REACH || - new->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) { + new->aspath->flags & F_PREFIX_ANNOUNCED) { type = IMSG_KROUTE_DELETE; p = old; } else { @@ -1150,9 +1588,9 @@ rde_send_kroute(struct prefix *new, struct prefix *old) pt_getaddr(p->prefix, &addr); kr.prefix.s_addr = addr.v4.s_addr; kr.prefixlen = p->prefix->prefixlen; - if (p->aspath->flags.nexthop_reject) + if (p->aspath->flags & F_NEXTHOP_REJECT) kr.flags |= F_REJECT; - if (p->aspath->flags.nexthop_blackhole) + if (p->aspath->flags & F_NEXTHOP_BLACKHOLE) kr.flags |= F_BLACKHOLE; if (imsg_compose(&ibuf_main, type, 0, &kr, sizeof(kr)) == -1) @@ -1193,18 +1631,31 @@ rde_send_pftable_commit(void) * nexthop specific functions */ void -rde_send_nexthop(struct bgpd_addr *next, int valid) +rde_send_nexthop(struct bgpd_addr *next, struct bgpd_addr *ll, int valid) { - int type; + struct buf *wbuf; + size_t size; + int type; if (valid) type = IMSG_NEXTHOP_ADD; else type = IMSG_NEXTHOP_REMOVE; - if (imsg_compose(&ibuf_main, type, 0, next, - sizeof(struct bgpd_addr)) == -1) - fatal("imsg_compose error"); + if (ll == NULL) + size = sizeof(struct bgpd_addr); + else + size = 2 * sizeof(struct bgpd_addr); + + if ((wbuf = imsg_create(&ibuf_main, type, 0, size)) == NULL) + fatal("imsg_create error"); + if (imsg_add(wbuf, next, sizeof(struct bgpd_addr)) == -1) + fatal("imsg_add error"); + if (ll != NULL) + if (imsg_add(wbuf, ll, sizeof(struct bgpd_addr)) == -1) + fatal("imsg_add error"); + if (imsg_close(&ibuf_main, wbuf) == -1) + fatal("imsg_close error"); } /* @@ -1222,8 +1673,8 @@ rde_generate_updates(struct prefix *new, struct prefix *old) * On the other hand new may be UNREACH and then we should not * generate an update. */ - if (old == NULL && (new == NULL || - new->aspath->nexthop->state != NEXTHOP_REACH)) + if (old == NULL && (new == NULL || (new->aspath->nexthop != NULL && + new->aspath->nexthop->state != NEXTHOP_REACH))) return; LIST_FOREACH(peer, &peerlist, peer_l) { @@ -1500,24 +1951,22 @@ network_init(struct network_head *net_l) void network_add(struct network_config *nc, int flagstatic) { - struct attr_flags attrs; - - bzero(&attrs, sizeof(attrs)); + struct rde_aspath *asp; - attrs.aspath = aspath_get(NULL, 0); - attrs.nexthop.s_addr = INADDR_ANY; - /* med = 0 */ - attrs.lpref = DEFAULT_LPREF; - attrs.origin = ORIGIN_IGP; - TAILQ_INIT(&attrs.others); + asp = path_get(); + asp->aspath = aspath_get(NULL, 0); + asp->origin = ORIGIN_IGP; + asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH | + F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED; + /* the nexthop is unset unless a default set overrides it */ /* apply default overrides */ - rde_apply_set(&attrs, &nc->attrset); + rde_apply_set(asp, &nc->attrset); if (flagstatic) - path_update(&peerself, &attrs, &nc->prefix, nc->prefixlen); + path_update(&peerself, asp, &nc->prefix, nc->prefixlen); else - path_update(&peerdynamic, &attrs, &nc->prefix, nc->prefixlen); + path_update(&peerdynamic, asp, &nc->prefix, nc->prefixlen); } void @@ -1540,10 +1989,10 @@ network_dump_upcall(struct pt_entry *pt, void *ptr) memcpy(&pid, ptr, sizeof(pid)); LIST_FOREACH(p, &pt->prefix_h, prefix_l) - if (p->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) { + if (p->aspath->flags & F_PREFIX_ANNOUNCED) { bzero(&k, sizeof(k)); pt_getaddr(p->prefix, &addr); - k.prefix = addr.v4; + k.prefix.s_addr = addr.v4.s_addr; k.prefixlen = p->prefix->prefixlen; if (p->peer == &peerself) k.flags = F_KERNEL; diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 17007e6cc8d..16612845a3b 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.50 2004/08/05 20:56:12 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.51 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -63,6 +63,8 @@ struct rde_peer { struct uptree_attr up_attrs; struct uplist_attr updates; struct uplist_prefix withdraws; + struct uplist_attr updates6; + struct uplist_prefix withdraws6; }; #define AS_SET 1 @@ -105,32 +107,64 @@ enum attrtypes { #define ATTR_WELL_KNOWN ATTR_TRANSITIVE struct attr { + TAILQ_ENTRY(attr) entry; u_int8_t flags; u_int8_t type; u_int16_t len; u_char *data; - TAILQ_ENTRY(attr) entry; +}; + +struct mpattr { + void *reach; + u_int16_t reach_len; + void *unreach; + u_int16_t unreach_len; }; TAILQ_HEAD(attr_list, attr); +struct path_table { + struct aspath_head *path_hashtbl; + u_int32_t path_hashmask; +}; + +LIST_HEAD(prefix_head, prefix); + +#define F_ATTR_ORIGIN 0x001 +#define F_ATTR_ASPATH 0x002 +#define F_ATTR_NEXTHOP 0x004 +#define F_ATTR_LOCALPREF 0x008 +#define F_ATTR_MED 0x010 +#define F_ATTR_MP_REACH 0x020 +#define F_ATTR_MP_UNREACH 0x040 +#define F_PREFIX_ANNOUNCED 0x080 +#define F_NEXTHOP_REJECT 0x100 +#define F_NEXTHOP_BLACKHOLE 0x200 +#define F_ATTR_LINKED 0x400 + #define ORIGIN_IGP 0 #define ORIGIN_EGP 1 #define ORIGIN_INCOMPLETE 2 #define DEFAULT_LPREF 100 -struct attr_flags { +struct rde_aspath { + LIST_ENTRY(rde_aspath) path_l, peer_l, nexthop_l; + struct prefix_head prefix_h; + struct rde_peer *peer; + + /* path attributes */ struct aspath *aspath; - struct in_addr nexthop; /* exit nexthop */ - u_int8_t nexthop_reject; - u_int8_t nexthop_blackhole; - char pftable[PFTABLE_LEN]; + struct nexthop *nexthop; /* may be NULL */ + struct attr_list others; u_int32_t med; /* multi exit disc */ u_int32_t lpref; /* local pref */ u_int8_t origin; - u_int8_t wflags; /* internally used */ - struct attr_list others; + + u_int16_t flags; /* internally used */ + u_int16_t prefix_cnt; /* # of prefixes */ + u_int16_t active_cnt; /* # of active prefixes */ + char pftable[PFTABLE_LEN]; }; enum nexthop_state { @@ -141,7 +175,7 @@ enum nexthop_state { struct nexthop { LIST_ENTRY(nexthop) nexthop_l; - enum nexthop_state state; + struct aspath_head path_h; #if 0 /* * currently we use the boolean nexthop state, this could be exchanged @@ -149,31 +183,14 @@ struct nexthop { */ u_int32_t costs; #endif - struct aspath_head path_h; + enum nexthop_state state; struct bgpd_addr exit_nexthop; struct bgpd_addr true_nexthop; struct bgpd_addr nexthop_net; u_int8_t nexthop_netlen; u_int8_t flags; -#define NEXTHOP_CONNECTED 0x1 -#define NEXTHOP_ANNOUNCE 0x2 -}; - -struct path_table { - struct aspath_head *path_hashtbl; - u_int32_t path_hashmask; -}; - -LIST_HEAD(prefix_head, prefix); - -struct rde_aspath { - LIST_ENTRY(rde_aspath) peer_l, path_l, nexthop_l; - struct prefix_head prefix_h; - struct rde_peer *peer; - struct nexthop *nexthop; - u_int16_t prefix_cnt; /* # of prefixes */ - u_int16_t active_cnt; /* # of active prefixes */ - struct attr_flags flags; +#define NEXTHOP_CONNECTED 0x01 +#define NEXTHOP_LINKLOCAL 0x02 }; /* generic entry without address specific part */ @@ -214,14 +231,12 @@ struct prefix { struct pt_entry *prefix; struct rde_peer *peer; time_t lastchange; -/* currently I can't think of additional prefix flags. - * NOTE: the selected route is stored in prefix->active */ }; /* prototypes */ /* rde.c */ void rde_send_kroute(struct prefix *, struct prefix *); -void rde_send_nexthop(struct bgpd_addr *, int); +void rde_send_nexthop(struct bgpd_addr *, struct bgpd_addr *, int); void rde_send_pftable(const char *, struct bgpd_addr *, u_int8_t, int); void rde_send_pftable_commit(void); @@ -231,25 +246,15 @@ u_int16_t rde_local_as(void); int rde_noevaluate(void); /* rde_attr.c */ -void attr_init(struct attr_flags *); -int attr_parse(u_char *, u_int16_t, struct attr_flags *, int, - enum enforce_as, u_int16_t); -u_char *attr_error(u_char *, u_int16_t, struct attr_flags *, - u_int8_t *, u_int16_t *); -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_move(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, +void attr_optcopy(struct rde_aspath *, struct rde_aspath *); +int attr_optadd(struct rde_aspath *, u_int8_t, u_int8_t, void *, u_int16_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 *); +struct attr *attr_optget(const struct rde_aspath *, u_int8_t); +void attr_optfree(struct rde_aspath *); +int attr_mp_nexthop(u_char *, u_int16_t, u_int16_t, + struct rde_aspath *); int aspath_verify(void *, u_int16_t); #define AS_ERR_LEN -1 @@ -277,18 +282,21 @@ int community_set(struct attr *, int, int); /* rde_rib.c */ void path_init(u_int32_t); void path_shutdown(void); -void path_update(struct rde_peer *, struct attr_flags *, +void path_update(struct rde_peer *, struct rde_aspath *, struct bgpd_addr *, int); -struct rde_aspath *path_get(struct aspath *, struct rde_peer *); -struct rde_aspath *path_add(struct rde_peer *, struct attr_flags *); +int path_compare(struct rde_aspath *, struct rde_aspath *); +struct rde_aspath *path_lookup(struct rde_aspath *, struct rde_peer *); void path_remove(struct rde_aspath *); void path_updateall(struct rde_aspath *, enum nexthop_state); void path_destroy(struct rde_aspath *); int path_empty(struct rde_aspath *); +struct rde_aspath *path_copy(struct rde_aspath *); +struct rde_aspath *path_get(void); +void path_put(struct rde_aspath *); int prefix_compare(const struct bgpd_addr *, const struct bgpd_addr *, int); -struct prefix *prefix_get(struct rde_aspath *, struct bgpd_addr *, int); +struct prefix *prefix_get(struct rde_peer *, struct bgpd_addr *, int); struct pt_entry *prefix_add(struct rde_aspath *, struct bgpd_addr *, int); struct pt_entry *prefix_move(struct rde_aspath *, struct prefix *); void prefix_remove(struct rde_peer *, struct bgpd_addr *, int); @@ -299,9 +307,12 @@ void prefix_network_clean(struct rde_peer *, time_t); void nexthop_init(u_int32_t); void nexthop_shutdown(void); -void nexthop_add(struct rde_aspath *); -void nexthop_remove(struct rde_aspath *); +void nexthop_modify(struct rde_aspath *, struct bgpd_addr *, int); +void nexthop_link(struct rde_aspath *); +void nexthop_unlink(struct rde_aspath *); void nexthop_update(struct kroute_nexthop *); +struct nexthop *nexthop_get(struct bgpd_addr *, struct bgpd_addr *); +int nexthop_compare(struct nexthop *, struct nexthop *); /* rde_decide.c */ void prefix_evaluate(struct prefix *, struct pt_entry *); @@ -328,9 +339,9 @@ void pt_dump(void (*)(struct pt_entry *, void *), void *, sa_family_t); /* rde_filter.c */ -enum filter_actions rde_filter(struct rde_peer *, struct attr_flags *, +enum filter_actions rde_filter(struct rde_peer *, struct rde_aspath *, struct bgpd_addr *, u_int8_t, enum directions); -void rde_apply_set(struct attr_flags *, struct filter_set *); -int rde_filter_community(struct attr_flags *, int, int); +void rde_apply_set(struct rde_aspath *, struct filter_set *); +int rde_filter_community(struct rde_aspath *, int, int); #endif /* __RDE_H__ */ diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index cbb7e925c35..fe1a6c1c492 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.41 2004/08/05 20:56:12 claudio Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.42 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -29,457 +29,6 @@ #include "rde.h" /* attribute specific functions */ -#define UPD_READ(t, p, plen, n) \ - do { \ - memcpy(t, p, n); \ - p += n; \ - plen += n; \ - } while (0) - -#define CHECK_FLAGS(s, t, m) \ - (((s) & ~(ATTR_EXTLEN | (m))) == (t)) - -#define F_ATTR_ORIGIN 0x01 -#define F_ATTR_ASPATH 0x02 -#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 { \ - if ((s) & (t)) \ - return (-1); \ - (s) |= (t); \ - } while (0) -void -attr_init(struct attr_flags *a) -{ - bzero(a, sizeof(struct attr_flags)); - a->origin = ORIGIN_INCOMPLETE; - a->lpref = DEFAULT_LPREF; - TAILQ_INIT(&a->others); -} - -int -attr_parse(u_char *p, u_int16_t len, struct attr_flags *a, int ebgp, - enum enforce_as enforce_as, u_int16_t remote_as) -{ - u_int32_t tmp32; - u_int16_t attr_len; - u_int16_t plen = 0; - u_int8_t flags; - u_int8_t type; - u_int8_t tmp8; - - if (len < 3) - return (-1); - - UPD_READ(&flags, p, plen, 1); - UPD_READ(&type, p, plen, 1); - - if (flags & ATTR_EXTLEN) { - if (len - plen < 2) - return (-1); - UPD_READ(&attr_len, p, plen, 2); - attr_len = ntohs(attr_len); - } else { - UPD_READ(&tmp8, p, plen, 1); - attr_len = tmp8; - } - - if (len - plen < attr_len) - return (-1); - - switch (type) { - case ATTR_UNDEF: - /* error! */ - return (-1); - case ATTR_ORIGIN: - if (attr_len != 1) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) - return (-1); - UPD_READ(&a->origin, p, plen, 1); - if (a->origin > ORIGIN_INCOMPLETE) - return (-1); - WFLAG(a->wflags, F_ATTR_ORIGIN); - break; - case ATTR_ASPATH: - if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) - return (-1); - if (aspath_verify(p, attr_len) != 0) - return (-1); - WFLAG(a->wflags, F_ATTR_ASPATH); - a->aspath = aspath_get(p, attr_len); - if (enforce_as == ENFORCE_AS_ON && - remote_as != aspath_neighbor(a->aspath)) - return (-1); - - plen += attr_len; - break; - case ATTR_NEXTHOP: - if (attr_len != 4) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) - return (-1); - WFLAG(a->wflags, F_ATTR_NEXTHOP); - UPD_READ(&a->nexthop, p, plen, 4); /* network byte order */ - /* - * Check if the nexthop is a valid IP address. We consider - * multicast and experimental addresses as invalid. - */ - tmp32 = ntohl(a->nexthop.s_addr); - if (IN_MULTICAST(tmp32) || IN_BADCLASS(tmp32)) - return (-1); - break; - case ATTR_MED: - if (attr_len != 4) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) - return (-1); - WFLAG(a->wflags, F_ATTR_MED); - UPD_READ(&tmp32, p, plen, 4); - a->med = ntohl(tmp32); - break; - case ATTR_LOCALPREF: - if (attr_len != 4) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) - return (-1); - if (ebgp) { - /* ignore local-pref attr for non ibgp peers */ - a->lpref = 0; /* set a default value ... */ - plen += 4; /* and ignore the real value */ - break; - } - WFLAG(a->wflags, F_ATTR_LOCALPREF); - UPD_READ(&tmp32, p, plen, 4); - a->lpref = ntohl(tmp32); - break; - case ATTR_ATOMIC_AGGREGATE: - if (attr_len != 0) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) - return (-1); - goto optattr; - case ATTR_AGGREGATOR: - if (attr_len != 6) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, 0)) - return (-1); - goto optattr; - case ATTR_COMMUNITIES: - if ((attr_len & 0x3) != 0) - return (-1); - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, - ATTR_PARTIAL)) - return (-1); - goto optattr; - case ATTR_ORIGINATOR_ID: - if (attr_len != 4) - return (-1); - 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, 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: - if (attr_optadd(a, flags, type, p, attr_len) == -1) - return (-1); - plen += attr_len; - break; - } - - return (plen); -} - -u_char * -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; - u_int8_t type; - u_int8_t tmp8; - - *suberr = ERR_UPD_ATTRLEN; - *size = len; - op = p; - if (len < 3) - return (op); - - UPD_READ(&flags, p, plen, 1); - UPD_READ(&type, p, plen, 1); - - if (flags & ATTR_EXTLEN) { - if (len - plen < 2) - return (op); - UPD_READ(&attr_len, p, plen, 2); - } else { - UPD_READ(&tmp8, p, plen, 1); - attr_len = tmp8; - } - - if (len - plen < attr_len) - return (op); - *size = attr_len; - - switch (type) { - case ATTR_UNDEF: - /* error! */ - *suberr = ERR_UPD_UNSPECIFIC; - *size = 0; - return (NULL); - case ATTR_ORIGIN: - if (attr_len != 1) - return (op); - if (attr->wflags & F_ATTR_ORIGIN) { - *suberr = ERR_UPD_ATTRLIST; - *size = 0; - return (NULL); - } - UPD_READ(&tmp8, p, plen, 1); - if (tmp8 > ORIGIN_INCOMPLETE) { - *suberr = ERR_UPD_ORIGIN; - return (op); - } - break; - case ATTR_ASPATH: - if (attr->wflags & F_ATTR_ASPATH) { - *suberr = ERR_UPD_ATTRLIST; - *size = 0; - return (NULL); - } - if (CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) { - /* malformed aspath detected by exclusion method */ - *size = 0; - *suberr = ERR_UPD_ASPATH; - return (NULL); - } - break; - case ATTR_NEXTHOP: - if (attr_len != 4) - return (op); - if (attr->wflags & F_ATTR_NEXTHOP) { - *suberr = ERR_UPD_ATTRLIST; - *size = 0; - return (NULL); - } - if (CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) { - /* malformed nexthop detected by exclusion method */ - *suberr = ERR_UPD_NETWORK; - return (op); - } - break; - case ATTR_MED: - if (attr_len != 4) - return (op); - if (attr->wflags & F_ATTR_MED) { - *suberr = ERR_UPD_ATTRLIST; - *size = 0; - return (NULL); - } - break; - case ATTR_LOCALPREF: - if (attr_len != 4) - return (op); - if (attr->wflags & F_ATTR_LOCALPREF) { - *suberr = ERR_UPD_ATTRLIST; - *size = 0; - return (NULL); - } - break; - case ATTR_ATOMIC_AGGREGATE: - if (attr_len != 0) - return (op); - break; - case ATTR_AGGREGATOR: - if (attr_len != 6) - return (op); - break; - case ATTR_COMMUNITIES: - if ((attr_len & 0x3) != 0) - return (op); - goto optattr; - case ATTR_ORIGINATOR_ID: - if (attr_len != 4) - return (op); - goto optattr; - case ATTR_CLUSTER_LIST: - if ((attr_len & 0x3) != 0) - 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 (op); - } - TAILQ_FOREACH(a, &attr->others, entry) - if (type == a->type) { - *size = 0; - *suberr = ERR_UPD_ATTRLIST; - return (NULL); - } - *suberr = ERR_UPD_OPTATTR; - return (op); - } - /* can only be a attribute flag error */ - *suberr = ERR_UPD_ATTRFLAGS; - return (op); -} -#undef UPD_READ -#undef WFLAG - -u_int8_t -attr_missing(struct attr_flags *a, int ebgp) -{ - if ((a->wflags & F_ATTR_ORIGIN) == 0) - return (ATTR_ORIGIN); - if ((a->wflags & F_ATTR_ASPATH) == 0) - return (ATTR_ASPATH); - 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) - return (ATTR_LOCALPREF); - return (0); -} - -int -attr_compare(struct attr_flags *a, struct attr_flags *b) -{ - struct attr *oa, *ob; - int r; - - if (a->origin > b->origin) - 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) - return (-1); - if (a->med > b->med) - return (1); - if (a->med < b->med) - return (-1); - if (a->lpref > b->lpref) - return (1); - if (a->lpref < b->lpref) - return (-1); - r = strcmp(a->pftable, b->pftable); - if (r == 0) - r = aspath_compare(a->aspath, b->aspath); - if (r > 0) - return (1); - if (r < 0) - return (-1); - - for (oa = TAILQ_FIRST(&a->others), ob = TAILQ_FIRST(&b->others); - oa != NULL && ob != NULL; - oa = TAILQ_NEXT(oa, entry), ob = TAILQ_NEXT(ob, entry)) { - if (oa->type > ob->type) - return (1); - if (oa->type < ob->type) - return (-1); - if (oa->len > ob->len) - return (1); - if (oa->len < ob->len) - return (-1); - r = memcmp(oa->data, ob->data, oa->len); - if (r > 0) - return (1); - if (r < 0) - return (-1); - } - if (oa != NULL) - return (1); - if (ob != NULL) - return (-1); - return (0); -} - -void -attr_copy(struct attr_flags *t, struct attr_flags *s) -{ - struct attr *os; - /* - * first copy the full struct, then replace the path and tags with - * a own copy. - */ - memcpy(t, s, sizeof(struct attr_flags)); - t->aspath = aspath_get(s->aspath->data, s->aspath->len); - TAILQ_INIT(&t->others); - TAILQ_FOREACH(os, &s->others, entry) - attr_optadd(t, os->flags, os->type, os->data, os->len); -} - -void -attr_move(struct attr_flags *t, struct attr_flags *s) -{ - struct attr *os; - /* - * first copy the full struct, then move the optional attributes. - */ - memcpy(t, s, sizeof(struct attr_flags)); - TAILQ_INIT(&t->others); - while ((os = TAILQ_FIRST(&s->others)) != NULL) { - TAILQ_REMOVE(&s->others, os, entry); - TAILQ_INSERT_TAIL(&t->others, os, entry); - } -} - -void -attr_free(struct attr_flags *a) -{ - /* - * free the aspath and all optional path attributes - * but not the attr_flags struct. - */ - aspath_put(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, @@ -513,7 +62,7 @@ attr_write(void *p, u_int16_t p_len, u_int8_t flags, u_int8_t type, } int -attr_optadd(struct attr_flags *attr, u_int8_t flags, u_int8_t type, +attr_optadd(struct rde_aspath *asp, u_int8_t flags, u_int8_t type, void *data, u_int16_t len) { struct attr *a, *p; @@ -522,6 +71,7 @@ attr_optadd(struct attr_flags *attr, u_int8_t flags, u_int8_t type, a = calloc(1, sizeof(struct attr)); if (a == NULL) fatal("attr_optadd"); + a->flags = flags; a->type = type; a->len = len; @@ -529,12 +79,13 @@ attr_optadd(struct attr_flags *attr, u_int8_t flags, u_int8_t type, a->data = malloc(len); if (a->data == NULL) fatal("attr_optadd"); + memcpy(a->data, data, len); } else a->data = NULL; /* keep a sorted list */ - TAILQ_FOREACH_REVERSE(p, &attr->others, attr_list, entry) { + TAILQ_FOREACH_REVERSE(p, &asp->others, attr_list, entry) { if (type == p->type) { /* attribute only once allowed */ free(a->data); @@ -542,20 +93,20 @@ attr_optadd(struct attr_flags *attr, u_int8_t flags, u_int8_t type, return (-1); } if (type > p->type) { - TAILQ_INSERT_AFTER(&attr->others, p, a, entry); + TAILQ_INSERT_AFTER(&asp->others, p, a, entry); return (0); } } - TAILQ_INSERT_HEAD(&attr->others, a, entry); + TAILQ_INSERT_HEAD(&asp->others, a, entry); return (0); } struct attr * -attr_optget(const struct attr_flags *attr, u_int8_t type) +attr_optget(const struct rde_aspath *asp, u_int8_t type) { struct attr *a; - TAILQ_FOREACH(a, &attr->others, entry) { + TAILQ_FOREACH(a, &asp->others, entry) { if (type == a->type) return (a); if (type < a->type) @@ -566,103 +117,24 @@ attr_optget(const struct attr_flags *attr, u_int8_t type) } void -attr_optfree(struct attr_flags *attr) +attr_optcopy(struct rde_aspath *t, struct rde_aspath *s) { - struct attr *a; - - while ((a = TAILQ_FIRST(&attr->others)) != NULL) { - TAILQ_REMOVE(&attr->others, a, entry); - free(a->data); - free(a); - } -} + struct attr *os; -int -attr_ismp(const struct attr_flags *attr) -{ - return (attr->wflags & F_ATTR_MP_REACH); + TAILQ_FOREACH(os, &s->others, entry) + attr_optadd(t, os->flags, os->type, os->data, os->len); } -int -attr_mp_nexthop_check(u_char *data, u_int16_t len, u_int16_t afi) +void +attr_optfree(struct rde_aspath *asp) { - 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); -} + struct attr *a; -/* - * 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"); + while ((a = TAILQ_FIRST(&asp->others)) != NULL) { + TAILQ_REMOVE(&asp->others, a, entry); + free(a->data); + free(a); } - - /* NOTREACHED */ - return (NULL); } /* aspath specific functions */ diff --git a/usr.sbin/bgpd/rde_decide.c b/usr.sbin/bgpd/rde_decide.c index 1d116f75bb3..cbac5c3849b 100644 --- a/usr.sbin/bgpd/rde_decide.c +++ b/usr.sbin/bgpd/rde_decide.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_decide.c,v 1.37 2004/08/05 18:44:19 claudio Exp $ */ +/* $OpenBSD: rde_decide.c,v 1.38 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -113,8 +113,6 @@ prefix_cmp(struct prefix *p1, struct prefix *p2) if (p1 == NULL) return (-1); - - /* p2 is allowed to be NULL */ if (p2 == NULL) return (1); @@ -122,31 +120,28 @@ prefix_cmp(struct prefix *p1, struct prefix *p2) asp2 = p2->aspath; /* 1. check if prefix is eligible a.k.a reachable */ - if (asp2->nexthop->state != NEXTHOP_REACH) + if (asp2->nexthop != NULL && asp2->nexthop->state != NEXTHOP_REACH) return (1); - if (asp1->nexthop->state != NEXTHOP_REACH) + if (asp1->nexthop != NULL && asp1->nexthop->state != NEXTHOP_REACH) return (-1); /* 2. preference of prefix, bigger is better */ - if ((asp1->flags.lpref - asp2->flags.lpref) != 0) - return (asp1->flags.lpref - asp2->flags.lpref); + if ((asp1->lpref - asp2->lpref) != 0) + return (asp1->lpref - asp2->lpref); /* 3. aspath count, the shorter the better */ - if ((asp2->flags.aspath->ascnt - - asp1->flags.aspath->ascnt) != 0) - return (asp2->flags.aspath->ascnt - - asp1->flags.aspath->ascnt); + if ((asp2->aspath->ascnt - asp1->aspath->ascnt) != 0) + return (asp2->aspath->ascnt - asp1->aspath->ascnt); /* 4. origin, the lower the better */ - if ((asp2->flags.origin - asp1->flags.origin) != 0) - return (asp2->flags.origin - asp1->flags.origin); + if ((asp2->origin - asp1->origin) != 0) + return (asp2->origin - asp1->origin); /* 5. MED decision, only comparable between the same neighboring AS */ - if (aspath_neighbor(asp1->flags.aspath) == - aspath_neighbor(asp2->flags.aspath)) + if (aspath_neighbor(asp1->aspath) == aspath_neighbor(asp2->aspath)) /* lowest value wins */ - if ((asp2->flags.med - asp1->flags.med) != 0) - return (asp2->flags.med - asp1->flags.med); + if ((asp2->med - asp1->med) != 0) + return (asp2->med - asp1->med); /* * 6. EBGP is cooler than IBGP @@ -231,8 +226,8 @@ prefix_evaluate(struct prefix *p, struct pt_entry *pte) rde_generate_updates(xp, pte->active); rde_send_kroute(xp, pte->active); - if (xp == NULL || xp->aspath->nexthop == NULL || - xp->aspath->nexthop->state != NEXTHOP_REACH) + if (xp == NULL || (xp->aspath->nexthop != NULL && + xp->aspath->nexthop->state != NEXTHOP_REACH)) pte->active = NULL; else { pte->active = xp; diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index 22d880364c5..785176d0788 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.15 2004/08/05 18:44:19 claudio Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.16 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -25,11 +25,11 @@ extern struct filter_head *rules_l; /* XXX ugly */ -int rde_filter_match(struct filter_rule *, struct attr_flags *, +int rde_filter_match(struct filter_rule *, struct rde_aspath *, struct bgpd_addr *, u_int8_t); enum filter_actions -rde_filter(struct rde_peer *peer, struct attr_flags *attrs, +rde_filter(struct rde_peer *peer, struct rde_aspath *asp, struct bgpd_addr *prefix, u_int8_t prefixlen, enum directions dir) { struct filter_rule *f; @@ -44,8 +44,8 @@ rde_filter(struct rde_peer *peer, struct attr_flags *attrs, 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 (rde_filter_match(f, asp, prefix, prefixlen)) { + rde_apply_set(asp, &f->set); if (f->action != ACTION_NONE) action = f->action; if (f->quick) @@ -56,39 +56,41 @@ rde_filter(struct rde_peer *peer, struct attr_flags *attrs, } void -rde_apply_set(struct attr_flags *attrs, struct filter_set *set) +rde_apply_set(struct rde_aspath *asp, struct filter_set *set) { + struct bgpd_addr addr; struct aspath *new; u_int16_t as; - if (attrs == NULL) + if (asp == NULL) return; if (set->flags & SET_LOCALPREF) - attrs->lpref = set->localpref; + asp->lpref = set->localpref; if (set->flags & SET_MED) - attrs->med = set->med; - if (set->flags & SET_NEXTHOP) - attrs->nexthop = set->nexthop; - if (set->flags & SET_NEXTHOP_REJECT) - attrs->nexthop_reject = 1; - if (set->flags & SET_NEXTHOP_BLACKHOLE) - attrs->nexthop_blackhole = 1; + asp->med = set->med; + + /* XXX and uglier */ + bzero(&addr, sizeof(addr)); + addr.af = AF_INET; + addr.v4.s_addr = set->nexthop.s_addr; + nexthop_modify(asp, &addr, set->flags); + if (set->flags & SET_PREPEND) { as = rde_local_as(); - new = aspath_prepend(attrs->aspath, as, set->prepend); - aspath_put(attrs->aspath); - attrs->aspath = new; + new = aspath_prepend(asp->aspath, as, set->prepend); + aspath_put(asp->aspath); + asp->aspath = new; } if (set->flags & SET_PFTABLE) - strlcpy(attrs->pftable, set->pftable, sizeof(attrs->pftable)); + strlcpy(asp->pftable, set->pftable, sizeof(asp->pftable)); if (set->flags & SET_COMMUNITY) { struct attr *a; - if ((a = attr_optget(attrs, ATTR_COMMUNITIES)) == NULL) { - attr_optadd(attrs, ATTR_OPTIONAL|ATTR_TRANSITIVE, + if ((a = attr_optget(asp, ATTR_COMMUNITIES)) == NULL) { + attr_optadd(asp, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_COMMUNITIES, NULL, 0); - if ((a = attr_optget(attrs, ATTR_COMMUNITIES)) == NULL) + if ((a = attr_optget(asp, ATTR_COMMUNITIES)) == NULL) fatalx("internal community bug"); } community_set(a, set->community.as, set->community.type); @@ -96,17 +98,17 @@ rde_apply_set(struct attr_flags *attrs, struct filter_set *set) } int -rde_filter_match(struct filter_rule *f, struct attr_flags *attrs, +rde_filter_match(struct filter_rule *f, struct rde_aspath *asp, struct bgpd_addr *prefix, u_int8_t plen) { - if (attrs != NULL && f->match.as.type != AS_NONE) - if (aspath_match(attrs->aspath, f->match.as.type, + if (asp != NULL && f->match.as.type != AS_NONE) + if (aspath_match(asp->aspath, f->match.as.type, f->match.as.as) == 0) return (0); - if (attrs != NULL && f->match.community.as != 0) - if (rde_filter_community(attrs, f->match.community.as, + if (asp != NULL && f->match.community.as != 0) + if (rde_filter_community(asp, f->match.community.as, f->match.community.type) == 0) return (0); @@ -178,11 +180,11 @@ rde_filter_match(struct filter_rule *f, struct attr_flags *attrs, } int -rde_filter_community(struct attr_flags *attr, int as, int type) +rde_filter_community(struct rde_aspath *asp, int as, int type) { struct attr *a; - a = attr_optget(attr, ATTR_COMMUNITIES); + a = attr_optget(asp, ATTR_COMMUNITIES); if (a == NULL) /* no communities, no match */ return (0); diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index b79ac85b514..ed127353780 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.54 2004/08/05 19:23:10 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.55 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -37,12 +37,10 @@ /* path specific functions */ static void path_link(struct rde_aspath *, struct rde_peer *); -static void path_unlink(struct rde_aspath *); -static struct rde_aspath *path_alloc(void); -static void path_free(struct rde_aspath *); struct path_table pathtable; +/* XXX the hash should also include communities and the other attrs */ #define PATH_HASH(x) \ &pathtable.path_hashtbl[aspath_hash((x)->data, (x)->len) & \ pathtable.path_hashmask] @@ -77,65 +75,106 @@ path_shutdown(void) } void -path_update(struct rde_peer *peer, struct attr_flags *attrs, +path_update(struct rde_peer *peer, struct rde_aspath *nasp, struct bgpd_addr *prefix, int prefixlen) { struct rde_aspath *asp; struct prefix *p; - struct pt_entry *pte; - rde_send_pftable(attrs->pftable, prefix, prefixlen, 0); + rde_send_pftable(nasp->pftable, prefix, prefixlen, 0); rde_send_pftable_commit(); - if ((asp = path_get(attrs->aspath, peer)) == NULL) { - /* path not available */ - asp = path_add(peer, attrs); - pte = prefix_add(asp, prefix, prefixlen); - } else { - if (attr_compare(&asp->flags, attrs) == 0) { - /* path are equal, just add prefix */ - pte = prefix_add(asp, prefix, prefixlen); - attr_free(attrs); - } else { + if ((p = prefix_get(peer, prefix, prefixlen)) != NULL) { + if (path_compare(nasp, p->aspath) != 0) { /* non equal path attributes create new path */ - if ((p = prefix_get(asp, prefix, prefixlen)) == NULL) { - asp = path_add(peer, attrs); - pte = prefix_add(asp, prefix, prefixlen); - } else { - asp = path_add(peer, attrs); - pte = prefix_move(asp, p); - } + path_link(nasp, peer); + prefix_move(nasp, p); + } else { + /* already registered */ + path_put(nasp); } + } else if ((asp = path_lookup(nasp, peer)) == NULL) { + /* path not available */ + path_link(nasp, peer); + prefix_add(nasp, prefix, prefixlen); + } else { + /* path found, just add prefix */ + prefix_add(asp, prefix, prefixlen); + path_put(nasp); + } +} + +int +path_compare(struct rde_aspath *a, struct rde_aspath *b) +{ + struct attr *oa, *ob; + int r; + + if (a->origin > b->origin) + return (1); + if (a->origin < b->origin) + return (-1); + if ((a->flags & ~F_ATTR_LINKED) > (b->flags & ~F_ATTR_LINKED)) + return (1); + if ((a->flags & ~F_ATTR_LINKED) < (b->flags & ~F_ATTR_LINKED)) + return (-1); + if (a->med > b->med) + return (1); + if (a->med < b->med) + return (-1); + if (a->lpref > b->lpref) + return (1); + if (a->lpref < b->lpref) + return (-1); + + r = strcmp(a->pftable, b->pftable); + if (r == 0) + r = aspath_compare(a->aspath, b->aspath); + if (r == 0) + r = nexthop_compare(a->nexthop, b->nexthop); + if (r > 0) + return (1); + if (r < 0) + return (-1); + + for (oa = TAILQ_FIRST(&a->others), ob = TAILQ_FIRST(&b->others); + oa != NULL && ob != NULL; + oa = TAILQ_NEXT(oa, entry), ob = TAILQ_NEXT(ob, entry)) { + if (oa->type > ob->type) + return (1); + if (oa->type < ob->type) + return (-1); + if (oa->len > ob->len) + return (1); + if (oa->len < ob->len) + return (-1); + r = memcmp(oa->data, ob->data, oa->len); + if (r > 0) + return (1); + if (r < 0) + return (-1); } + if (oa != NULL) + return (1); + if (ob != NULL) + return (-1); + return (0); } struct rde_aspath * -path_get(struct aspath *aspath, struct rde_peer *peer) +path_lookup(struct rde_aspath *aspath, struct rde_peer *peer) { struct aspath_head *head; struct rde_aspath *asp; - head = PATH_HASH(aspath); + head = PATH_HASH(aspath->aspath); LIST_FOREACH(asp, head, path_l) { - if (aspath_compare(asp->flags.aspath, aspath) == 0 && + if (path_compare(aspath, asp) == 0 && peer == asp->peer) - return asp; + return (asp); } - return NULL; -} - -struct rde_aspath * -path_add(struct rde_peer *peer, struct attr_flags *attr) -{ - struct rde_aspath *asp; - - asp = path_alloc(); - - attr_move(&asp->flags, attr); - - path_link(asp, peer); - return asp; + return (NULL); } void @@ -147,7 +186,7 @@ path_remove(struct rde_aspath *asp) while ((p = LIST_FIRST(&asp->prefix_h)) != NULL) { /* Commit is done in peer_down() */ pt_getaddr(p->prefix, &addr); - rde_send_pftable(p->aspath->flags.pftable, + rde_send_pftable(p->aspath->pftable, &addr, p->prefix->prefixlen, 1); prefix_destroy(p); @@ -171,9 +210,16 @@ path_destroy(struct rde_aspath *asp) { /* path_destroy can only unlink and free empty rde_aspath */ ENSURE(path_empty(asp)); + ENSURE(asp->prefix_cnt == 0 && asp->active_cnt == 0); - path_unlink(asp); - path_free(asp); + nexthop_unlink(asp); + LIST_REMOVE(asp, path_l); + LIST_REMOVE(asp, peer_l); + asp->peer = NULL; + asp->nexthop = NULL; + asp->flags &= ~F_ATTR_LINKED; + + path_put(asp); } int @@ -194,33 +240,44 @@ path_link(struct rde_aspath *asp, struct rde_peer *peer) { struct aspath_head *head; - head = PATH_HASH(asp->flags.aspath); + head = PATH_HASH(asp->aspath); LIST_INSERT_HEAD(head, asp, path_l); LIST_INSERT_HEAD(&peer->path_h, asp, peer_l); asp->peer = peer; + asp->flags |= F_ATTR_LINKED; - nexthop_add(asp); + nexthop_link(asp); } -static void -path_unlink(struct rde_aspath *asp) +/* + * copy asp to a new UNLINKED one manly for filtering + */ +struct rde_aspath * +path_copy(struct rde_aspath *asp) { - ENSURE(path_empty(asp)); - ENSURE(asp->prefix_cnt == 0 && asp->active_cnt == 0); + struct rde_aspath *nasp; - nexthop_remove(asp); - LIST_REMOVE(asp, path_l); - LIST_REMOVE(asp, peer_l); - asp->peer = NULL; - asp->nexthop = NULL; + nasp = path_get(); + nasp->aspath = asp->aspath; + if (nasp->aspath != NULL) + nasp->aspath->refcnt++; + nasp->nexthop = asp->nexthop; + nasp->med = asp->med; + nasp->lpref = asp->lpref; + nasp->origin = asp->origin; + + nasp->flags = asp->flags & ~F_ATTR_LINKED; + + TAILQ_INIT(&nasp->others); + attr_optcopy(nasp, asp); - attr_free(&asp->flags); + return (nasp); } /* alloc and initialize new entry. May not fail. */ -static struct rde_aspath * -path_alloc(void) +struct rde_aspath * +path_get(void) { struct rde_aspath *asp; @@ -228,16 +285,23 @@ path_alloc(void) if (asp == NULL) fatal("path_alloc"); LIST_INIT(&asp->prefix_h); - return asp; + TAILQ_INIT(&asp->others); + asp->origin = ORIGIN_INCOMPLETE; + asp->lpref = DEFAULT_LPREF; + /* med = 0 */ + + return (asp); } /* free a unlinked element */ -static void -path_free(struct rde_aspath *asp) +void +path_put(struct rde_aspath *asp) { - ENSURE(asp->peer == NULL && - asp->flags.aspath == NULL && - TAILQ_EMPTY(&asp->flags.others)); + if (asp->flags & F_ATTR_LINKED) + fatalx("path_put: linked object"); + + aspath_put(asp->aspath); + attr_optfree(asp); free(asp); } @@ -290,26 +354,17 @@ prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b, } /* - * search in the path list for specified prefix. Returns NULL if not found. + * search for specified prefix of a peer. Returns NULL if not found. */ struct prefix * -prefix_get(struct rde_aspath *asp, struct bgpd_addr *prefix, int prefixlen) +prefix_get(struct rde_peer *peer, struct bgpd_addr *prefix, int prefixlen) { - struct prefix *p; - struct bgpd_addr addr; - - LIST_FOREACH(p, &asp->prefix_h, path_l) { - ENSURE(p->prefix != NULL); - if (p->prefix->prefixlen != prefixlen) - continue; - pt_getaddr(p->prefix, &addr); - if (!prefix_compare(&addr, prefix, prefixlen)) { - ENSURE(p->aspath == asp); - return p; - } - } + struct pt_entry *pte; - return NULL; + pte = pt_get(prefix, prefixlen); + if (pte == NULL) + return (NULL); + return (prefix_bypeer(pte, peer)); } /* @@ -324,9 +379,9 @@ prefix_add(struct rde_aspath *asp, struct bgpd_addr *prefix, int prefixlen) int needlink = 0; pte = pt_get(prefix, prefixlen); - if (pte == NULL) { + if (pte == NULL) pte = pt_add(prefix, prefixlen); - } + p = prefix_bypeer(pte, asp->peer); if (p == NULL) { needlink = 1; @@ -422,7 +477,7 @@ prefix_remove(struct rde_peer *peer, struct bgpd_addr *prefix, int prefixlen) asp = p->aspath; - rde_send_pftable(asp->flags.pftable, prefix, prefixlen, 1); + rde_send_pftable(asp->pftable, prefix, prefixlen, 1); rde_send_pftable_commit(); prefix_unlink(p); @@ -587,20 +642,8 @@ prefix_free(struct prefix *pref) } /* nexthop functions */ - -/* - * XXX - * Storing the nexthop info in a hash table is not optimal. The problem is - * that updates (invalidate and validate) come in as prefixes and so storing - * the nexthops in a hash is not optimal. An (in)validate needs to do a table - * walk to find all candidates. - * Currently I think that there are many more adds and removes so that a - * hash table has more benefits and the table walk should not happen too often. - */ - -static struct nexthop *nexthop_get(struct in_addr); -static struct nexthop *nexthop_alloc(void); -static void nexthop_free(struct nexthop *); +struct nexthop_head *nexthop_hash(struct bgpd_addr *); +struct nexthop *nexthop_lookup(struct bgpd_addr *); /* * In BGP there exist two nexthops: the exit nexthop which was announced via @@ -612,18 +655,13 @@ static void nexthop_free(struct nexthop *); * reside in the same subnet -- directly connected. */ struct nexthop_table { - LIST_HEAD(, nexthop) *nexthop_hashtbl; - u_int32_t nexthop_hashmask; + LIST_HEAD(nexthop_head, nexthop) *nexthop_hashtbl; + u_int32_t nexthop_hashmask; } nexthoptable; -#define NEXTHOP_HASH(x) \ - &nexthoptable.nexthop_hashtbl[ntohl((x.s_addr)) & \ - nexthoptable.nexthop_hashmask] - void nexthop_init(u_int32_t hashsize) { - struct nexthop *nh; u_int32_t hs, i; for (hs = 1; hs < hashsize; hs <<= 1) @@ -636,169 +674,224 @@ nexthop_init(u_int32_t hashsize) LIST_INIT(&nexthoptable.nexthop_hashtbl[i]); nexthoptable.nexthop_hashmask = hs - 1; - - /* add dummy entry for connected networks */ - nh = nexthop_alloc(); - nh->state = NEXTHOP_REACH; - nh->exit_nexthop.af = AF_INET; - nh->exit_nexthop.v4.s_addr = INADDR_ANY; - - LIST_INSERT_HEAD(NEXTHOP_HASH(nh->exit_nexthop.v4), nh, - nexthop_l); - - memcpy(&nh->true_nexthop, &nh->exit_nexthop, - sizeof(nh->true_nexthop)); - nh->nexthop_netlen = 0; - nh->nexthop_net.af = AF_INET; - nh->nexthop_net.v4.s_addr = INADDR_ANY; - - nh->flags = NEXTHOP_ANNOUNCE; } void nexthop_shutdown(void) { - struct in_addr addr; - struct nexthop *nh; - u_int32_t i; - - /* remove the dummy entry for connected networks */ - addr.s_addr = INADDR_ANY; - nh = nexthop_get(addr); - if (nh != NULL) { - if (!LIST_EMPTY(&nh->path_h)) - log_warnx("nexthop_free: free non-free announce node"); - LIST_REMOVE(nh, nexthop_l); - nexthop_free(nh); - } + u_int32_t i; for (i = 0; i <= nexthoptable.nexthop_hashmask; i++) if (!LIST_EMPTY(&nexthoptable.nexthop_hashtbl[i])) - log_warnx("nexthop_free: free non-free table"); + log_warnx("nexthop_shutdown: non-free table"); free(nexthoptable.nexthop_hashtbl); } void -nexthop_add(struct rde_aspath *asp) +nexthop_update(struct kroute_nexthop *msg) { - struct nexthop *nh; + struct nexthop *nh; + struct rde_aspath *asp; - if ((nh = asp->nexthop) == NULL) - nh = nexthop_get(asp->flags.nexthop); + nh = nexthop_lookup(&msg->nexthop); if (nh == NULL) { - nh = nexthop_alloc(); - nh->state = NEXTHOP_LOOKUP; - nh->exit_nexthop.af = AF_INET; - nh->exit_nexthop.v4 = asp->flags.nexthop; - LIST_INSERT_HEAD(NEXTHOP_HASH(asp->flags.nexthop), nh, - nexthop_l); - rde_send_nexthop(&nh->exit_nexthop, 1); + log_warnx("nexthop_update: non-existent nexthop"); + return; + } + + if (msg->valid) + nh->state = NEXTHOP_REACH; + else + nh->state = NEXTHOP_UNREACH; + + if (msg->connected) { + if (!(nh->flags & NEXTHOP_LINKLOCAL)) + /* use linklocal address if provided */ + nh->true_nexthop = nh->exit_nexthop; + nh->flags |= NEXTHOP_CONNECTED; + } else { + nh->true_nexthop = msg->gateway; + nh->flags &= ~NEXTHOP_LINKLOCAL; } + + nh->nexthop_netlen = msg->kr.kr4.prefixlen; + nh->nexthop_net.af = AF_INET; + nh->nexthop_net.v4.s_addr = msg->kr.kr4.prefix.s_addr; + + if (rde_noevaluate()) + /* + * if the decision process is turned off there is no need + * for the aspath list walk. + */ + return; + + LIST_FOREACH(asp, &nh->path_h, nexthop_l) { + path_updateall(asp, nh->state); + } +} + +void +nexthop_modify(struct rde_aspath *asp, struct bgpd_addr *nexthop, int flags) +{ + struct nexthop *nh; + + if (flags & SET_NEXTHOP_REJECT) + asp->flags |= F_NEXTHOP_REJECT; + if (flags & SET_NEXTHOP_BLACKHOLE) + asp->flags |= F_NEXTHOP_BLACKHOLE; + if (!(flags & SET_NEXTHOP)) + return; + nh = nexthop_get(nexthop, NULL); + nexthop_unlink(asp); asp->nexthop = nh; - LIST_INSERT_HEAD(&nh->path_h, asp, nexthop_l); + nexthop_link(asp); } void -nexthop_remove(struct rde_aspath *asp) +nexthop_link(struct rde_aspath *asp) +{ + if (asp->nexthop == NULL) + return; + + LIST_INSERT_HEAD(&asp->nexthop->path_h, asp, nexthop_l); +} + +void +nexthop_unlink(struct rde_aspath *asp) { struct nexthop *nh; + if (asp->nexthop == NULL) + return; + LIST_REMOVE(asp, nexthop_l); /* see if list is empty */ nh = asp->nexthop; - - /* never remove the dummy announce entry */ - if (nh->flags & NEXTHOP_ANNOUNCE) - return; + asp->nexthop = NULL; if (LIST_EMPTY(&nh->path_h)) { LIST_REMOVE(nh, nexthop_l); - rde_send_nexthop(&nh->exit_nexthop, 0); - nexthop_free(nh); + if (nh->flags & NEXTHOP_LINKLOCAL) + rde_send_nexthop(&nh->exit_nexthop, + &nh->true_nexthop, 0); + else + rde_send_nexthop(&nh->exit_nexthop, NULL, 0); + + free(nh); } } -static struct nexthop * -nexthop_get(struct in_addr nexthop) +struct nexthop * +nexthop_get(struct bgpd_addr *nexthop, struct bgpd_addr *ll) { struct nexthop *nh; - LIST_FOREACH(nh, NEXTHOP_HASH(nexthop), nexthop_l) { - if (nh->exit_nexthop.v4.s_addr == nexthop.s_addr) - return nh; - } - return NULL; -} + nh = nexthop_lookup(nexthop); + if (nh == NULL) { + nh = calloc(1, sizeof(*nh)); + if (nh == NULL) + fatal("nexthop_alloc"); -void -nexthop_update(struct kroute_nexthop *msg) -{ - struct nexthop *nh; - struct rde_aspath *asp; + LIST_INIT(&nh->path_h); + nh->state = NEXTHOP_LOOKUP; + nh->exit_nexthop = *nexthop; + if (ll) { + /* + * The link local address should be used as nexthop + * if available but acctualy it does not care. + * There is only one link local address per nexthop + * if any. Therefor the key is still the nexthop. + */ + /* + * XXX unsure if this is the correct way. BTW. + * For link local address the scope is needed. + * I think we should use some nice hack with the kroute + * code and the nexthop updates to fix that. + */ + memcpy(&nh->true_nexthop, ll, sizeof(struct bgpd_addr)); + nh->flags |= NEXTHOP_LINKLOCAL; + } + LIST_INSERT_HEAD(nexthop_hash(nexthop), nh, + nexthop_l); - nh = nexthop_get(msg->nexthop.v4); - if (nh == NULL) { - log_warnx("nexthop_update: non-existent nexthop"); - return; - } - /* should I trust in the parent ??? */ - if (nh->exit_nexthop.af != msg->nexthop.af || - (nh->exit_nexthop.af == AF_INET && - nh->exit_nexthop.v4.s_addr != msg->nexthop.v4.s_addr)) { - log_warnx("nexthop_update: bad nexthop returned"); - return; + if (nh->flags & NEXTHOP_LINKLOCAL) + rde_send_nexthop(&nh->exit_nexthop, + &nh->true_nexthop, 1); + else + rde_send_nexthop(&nh->exit_nexthop, NULL, 1); } - if (msg->valid) - nh->state = NEXTHOP_REACH; - else - nh->state = NEXTHOP_UNREACH; + return (nh); +} - if (msg->connected) - memcpy(&nh->true_nexthop, &nh->exit_nexthop, - sizeof(nh->true_nexthop)); - else - memcpy(&nh->true_nexthop, &msg->gateway, - sizeof(nh->true_nexthop)); +int +nexthop_compare(struct nexthop *na, struct nexthop *nb) +{ + struct bgpd_addr *a, *b; - nh->nexthop_netlen = msg->kr.kr4.prefixlen; - nh->nexthop_net.af = AF_INET; - nh->nexthop_net.v4.s_addr = msg->kr.kr4.prefix.s_addr; + if (na == NULL && nb == NULL) + return (0); + if (na == NULL) + return (-1); + if (nb == NULL) + return (1); - if (msg->connected) - nh->flags |= NEXTHOP_CONNECTED; + a = &na->exit_nexthop; + b = &nb->exit_nexthop; - if (rde_noevaluate()) - /* - * if the decision process is turned off there is no need - * for the aspath list walk. - */ - return; + if (a->af != b->af) + return (a->af - b->af); - LIST_FOREACH(asp, &nh->path_h, nexthop_l) { - path_updateall(asp, nh->state); + switch (a->af) { + case AF_INET: + if (ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr)) + return (1); + if (ntohl(a->v4.s_addr) < ntohl(b->v4.s_addr)) + return (-1); + return (0); + case AF_INET6: + return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr))); + default: + fatalx("nexthop_cmp: unknown af"); } + return (-1); } -static struct nexthop * -nexthop_alloc(void) +struct nexthop * +nexthop_lookup(struct bgpd_addr *nexthop) { - struct nexthop *nh; + struct nexthop *nh; - nh = calloc(1, sizeof(*nh)); - if (nh == NULL) - fatal("nexthop_alloc"); - LIST_INIT(&nh->path_h); - return nh; + LIST_FOREACH(nh, nexthop_hash(nexthop), nexthop_l) { + if (memcmp(&nh->exit_nexthop, nexthop, + sizeof(struct bgpd_addr)) == 0) + return (nh); + } + return (NULL); } -static void -nexthop_free(struct nexthop *nh) +struct nexthop_head * +nexthop_hash(struct bgpd_addr *nexthop) { - ENSURE(LIST_EMPTY(&nh->path_h)); + u_int32_t i, h = 0; - free(nh); + switch (nexthop->af) { + case AF_INET: + h = (AF_INET ^ ntohl(nexthop->v4.s_addr) ^ + ntohl(nexthop->v4.s_addr) >> 13) & + nexthoptable.nexthop_hashmask; + break; + case AF_INET6: + h = 8271; + for (i = 0; i < sizeof(struct in6_addr); i++) + h = ((h << 5) + h) ^ nexthop->v6.s6_addr[i]; + h &= nexthoptable.nexthop_hashmask; + break; + default: + fatalx("nexthop_hash: unsupported AF"); + } + return (&nexthoptable.nexthop_hashtbl[h]); } diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 8b442352305..6ba93082554 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.26 2004/08/05 18:44:19 claudio Exp $ */ +/* $OpenBSD: rde_update.c,v 1.27 2004/08/06 12:04:08 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -25,7 +25,7 @@ #include "rde.h" int up_generate_attr(struct rde_peer *, struct update_attr *, - struct attr_flags *, struct nexthop *); + struct rde_aspath *); int up_set_prefix(u_char *, int, struct bgpd_addr *, u_int8_t); @@ -47,6 +47,7 @@ struct update_attr { RB_ENTRY(update_attr) entry; }; +void up_clear(struct uplist_attr *, struct uplist_prefix *); int up_prefix_cmp(struct update_prefix *, struct update_prefix *); int up_attr_cmp(struct update_attr *, struct update_attr *); int up_add(struct rde_peer *, struct update_prefix *, struct update_attr *); @@ -62,6 +63,8 @@ up_init(struct rde_peer *peer) { TAILQ_INIT(&peer->updates); TAILQ_INIT(&peer->withdraws); + TAILQ_INIT(&peer->updates6); + TAILQ_INIT(&peer->withdraws6); RB_INIT(&peer->up_prefix); RB_INIT(&peer->up_attrs); peer->up_pcnt = 0; @@ -71,13 +74,13 @@ up_init(struct rde_peer *peer) } void -up_down(struct rde_peer *peer) +up_clear(struct uplist_attr *updates, struct uplist_prefix *withdraws) { struct update_attr *ua; struct update_prefix *up; - while ((ua = TAILQ_FIRST(&peer->updates)) != NULL) { - TAILQ_REMOVE(&peer->updates, ua, attr_l); + while ((ua = TAILQ_FIRST(updates)) != NULL) { + TAILQ_REMOVE(updates, ua, attr_l); while ((up = TAILQ_FIRST(&ua->prefix_h)) != NULL) { TAILQ_REMOVE(&ua->prefix_h, up, prefix_l); free(up); @@ -86,13 +89,18 @@ up_down(struct rde_peer *peer) free(ua); } - while ((up = TAILQ_FIRST(&peer->withdraws)) != NULL) { - TAILQ_REMOVE(&peer->withdraws, up, prefix_l); + while ((up = TAILQ_FIRST(withdraws)) != NULL) { + TAILQ_REMOVE(withdraws, up, prefix_l); free(up); } +} + +void +up_down(struct rde_peer *peer) +{ + up_clear(&peer->updates, &peer->withdraws); + up_clear(&peer->updates6, &peer->withdraws6); - TAILQ_INIT(&peer->updates); - TAILQ_INIT(&peer->withdraws); RB_INIT(&peer->up_prefix); RB_INIT(&peer->up_attrs); @@ -148,7 +156,7 @@ up_attr_cmp(struct update_attr *a, struct update_attr *b) return (-1); if (a->attr_len > b->attr_len) return (1); - return memcmp(a->attr, b->attr, a->attr_len); + return (memcmp(a->attr, b->attr, a->attr_len)); } int @@ -156,6 +164,21 @@ up_add(struct rde_peer *peer, struct update_prefix *p, struct update_attr *a) { struct update_attr *na = NULL; struct update_prefix *np; + struct uplist_attr *upl = NULL; + struct uplist_prefix *wdl = NULL; + + switch (p->prefix.af) { + case AF_INET: + upl = &peer->updates; + wdl = &peer->withdraws; + break; + case AF_INET6: + upl = &peer->updates6; + wdl = &peer->withdraws6; + break; + default: + fatalx("up_add: unknown AF"); + } /* 1. search for attr */ if (a != NULL && (na = RB_FIND(uptree_attr, &peer->up_attrs, a)) == @@ -170,7 +193,7 @@ up_add(struct rde_peer *peer, struct update_prefix *p, struct update_attr *a) free(p); return (-1); } - TAILQ_INSERT_TAIL(&peer->updates, a, attr_l); + TAILQ_INSERT_TAIL(upl, a, attr_l); peer->up_acnt++; } else { /* 1.2 if found -> use that, free a */ @@ -179,8 +202,8 @@ up_add(struct rde_peer *peer, struct update_prefix *p, struct update_attr *a) free(a); a = na; /* move to end of update queue */ - TAILQ_REMOVE(&peer->updates, a, attr_l); - TAILQ_INSERT_TAIL(&peer->updates, a, attr_l); + TAILQ_REMOVE(upl, a, attr_l); + TAILQ_INSERT_TAIL(upl, a, attr_l); } } @@ -203,15 +226,15 @@ up_add(struct rde_peer *peer, struct update_prefix *p, struct update_attr *a) TAILQ_REMOVE(np->prefix_h, np, prefix_l); free(p); p = np; - if (p->prefix_h == &peer->withdraws) + if (p->prefix_h == wdl) peer->up_wcnt--; else peer->up_nlricnt--; } /* 3. link prefix to attr */ if (a == NULL) { - TAILQ_INSERT_TAIL(&peer->withdraws, p, prefix_l); - p->prefix_h = &peer->withdraws; + TAILQ_INSERT_TAIL(wdl, p, prefix_l); + p->prefix_h = wdl; peer->up_wcnt++; } else { TAILQ_INSERT_TAIL(&a->prefix_h, p, prefix_l); @@ -227,17 +250,17 @@ up_generate_updates(struct rde_peer *peer, { struct update_attr *a; struct update_prefix *p; - struct attr *atr; - struct attr_flags attrs; + struct attr *attr; + struct rde_aspath *fasp; struct bgpd_addr addr; if (peer->state != PEER_UP) return; - if (new == NULL || new->aspath->nexthop == NULL || - new->aspath->nexthop->state != NEXTHOP_REACH) { + if (new == NULL || (new->aspath->nexthop != NULL && + new->aspath->nexthop->state != NEXTHOP_REACH)) { if (old == NULL) - /* new prefix got filtered and no old prefex avail */ + /* new prefix got filtered and no old prefix avail */ return; if (peer == old->peer) @@ -245,7 +268,7 @@ up_generate_updates(struct rde_peer *peer, return; if (peer->conf.ebgp && - !aspath_loopfree(old->aspath->flags.aspath, + !aspath_loopfree(old->aspath->aspath, peer->conf.remote_as)) /* * Do not send routes back to sender which would @@ -264,8 +287,7 @@ up_generate_updates(struct rde_peer *peer, */ if (old->peer->conf.reflector_client == 0 && peer->conf.reflector_client == 0 && - (old->aspath->nexthop->flags & - NEXTHOP_ANNOUNCE) == 0) + (old->aspath->flags & F_PREFIX_ANNOUNCED) == 0) /* Do not redistribute updates to ibgp peers */ return; } @@ -283,44 +305,44 @@ up_generate_updates(struct rde_peer *peer, * pass only prefix that have a aspath count * of zero this is equal to the ^$ regex. */ - if (old->aspath->flags.aspath->ascnt != 0) + if (old->aspath->aspath->ascnt != 0) return; break; } /* well known communities */ - if (rde_filter_community(&old->aspath->flags, + if (rde_filter_community(old->aspath, COMMUNITY_WELLKNOWN, COMMUNITY_NO_ADVERTISE)) return; - if (peer->conf.ebgp && rde_filter_community(&old->aspath->flags, + if (peer->conf.ebgp && rde_filter_community(old->aspath, COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPORT)) return; - if (peer->conf.ebgp && rde_filter_community(&old->aspath->flags, + if (peer->conf.ebgp && rde_filter_community(old->aspath, COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPSUBCONFED)) return; /* - * don't send messages back to originator - * NOTE this is not specified in the RFC but seems logical. + * Don't send messages back to originator + * this is not specified in the RFC but seems logical. */ - if ((atr = attr_optget(&old->aspath->flags, + if ((attr = attr_optget(old->aspath, ATTR_ORIGINATOR_ID)) != NULL) { - if (memcmp(atr->data, &peer->remote_bgpid, + if (memcmp(attr->data, &peer->remote_bgpid, sizeof(peer->remote_bgpid)) == 0) /* would cause loop don't send */ return; } /* copy attributes for output filter */ - attr_copy(&attrs, &old->aspath->flags); + fasp = path_copy(old->aspath); pt_getaddr(old->prefix, &addr); - if (rde_filter(peer, &attrs, &addr, + if (rde_filter(peer, fasp, &addr, old->prefix->prefixlen, DIR_OUT) == ACTION_DENY) { - attr_free(&attrs); + path_put(fasp); return; } - attr_free(&attrs); + path_put(fasp); /* withdraw prefix */ p = calloc(1, sizeof(struct update_prefix)); @@ -339,7 +361,7 @@ up_generate_updates(struct rde_peer *peer, } if (peer->conf.ebgp && - !aspath_loopfree(new->aspath->flags.aspath, + !aspath_loopfree(new->aspath->aspath, peer->conf.remote_as)) { /* * Do not send routes back to sender which would @@ -360,8 +382,7 @@ up_generate_updates(struct rde_peer *peer, */ if (new->peer->conf.reflector_client == 0 && peer->conf.reflector_client == 0 && - (new->aspath->nexthop->flags & - NEXTHOP_ANNOUNCE) == 0) { + (new->aspath->flags & F_PREFIX_ANNOUNCED) == 0) { /* Do not redistribute updates to ibgp peers */ up_generate_updates(peer, NULL, old); return; @@ -385,7 +406,7 @@ up_generate_updates(struct rde_peer *peer, * pass only prefix that have a aspath count * of zero this is equal to the ^$ regex. */ - if (new->aspath->flags.aspath->ascnt != 0) { + if (new->aspath->aspath->ascnt != 0) { up_generate_updates(peer, NULL, old); return; } @@ -393,43 +414,43 @@ up_generate_updates(struct rde_peer *peer, } /* well known communities */ - if (rde_filter_community(&new->aspath->flags, + if (rde_filter_community(new->aspath, COMMUNITY_WELLKNOWN, COMMUNITY_NO_ADVERTISE)) { up_generate_updates(peer, NULL, old); return; } - if (peer->conf.ebgp && rde_filter_community(&new->aspath->flags, + if (peer->conf.ebgp && rde_filter_community(new->aspath, COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPORT)) { up_generate_updates(peer, NULL, old); return; } - if (peer->conf.ebgp && rde_filter_community(&new->aspath->flags, + if (peer->conf.ebgp && rde_filter_community(new->aspath, COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPSUBCONFED)) { up_generate_updates(peer, NULL, old); return; } /* copy attributes for output filter */ - attr_copy(&attrs, &new->aspath->flags); + fasp = path_copy(new->aspath); pt_getaddr(new->prefix, &addr); - if (rde_filter(peer, &attrs, &addr, + if (rde_filter(peer, fasp, &addr, new->prefix->prefixlen, DIR_OUT) == ACTION_DENY) { - attr_free(&attrs); + path_put(fasp); up_generate_updates(peer, NULL, old); return; } /* - * don't send messages back to originator - * NOTE this is not specified in the RFC but seems logical. + * Don't send messages back to originator + * this is not specified in the RFC but seems logical. */ - if ((atr = attr_optget(&new->aspath->flags, + if ((attr = attr_optget(new->aspath, ATTR_ORIGINATOR_ID)) != NULL) { - if (memcmp(atr->data, &peer->remote_bgpid, + if (memcmp(attr->data, &peer->remote_bgpid, sizeof(peer->remote_bgpid)) == 0) { /* would cause loop don't send */ - attr_free(&attrs); + path_put(fasp); return; } } @@ -443,8 +464,7 @@ up_generate_updates(struct rde_peer *peer, if (a == NULL) fatal("up_generate_updates"); - if (up_generate_attr(peer, a, &attrs, - new->aspath->nexthop) == -1) { + if (up_generate_attr(peer, a, fasp) == -1) { log_warnx("generation of bgp path attributes failed"); free(a); free(p); @@ -455,13 +475,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(attrs.aspath->data, - attrs.aspath->len); + a->attr_hash = aspath_hash(fasp->aspath->data, + fasp->aspath->len); p->prefix = addr; p->prefixlen = new->prefix->prefixlen; /* no longer needed */ - attr_free(&attrs); + path_put(fasp); if (up_add(peer, p, a) == -1) log_warnx("queuing update failed."); @@ -473,32 +493,21 @@ up_generate_default(struct rde_peer *peer, sa_family_t af) { struct update_attr *a; struct update_prefix *p; - struct attr_flags attrs; + struct rde_aspath *asp; struct bgpd_addr addr; - struct nexthop nexthop; - bzero(&attrs, sizeof(attrs)); - bzero(&addr, sizeof(addr)); - bzero(&nexthop, sizeof(nexthop)); - - attrs.aspath = aspath_get(NULL, 0); - attrs.nexthop.s_addr = INADDR_ANY; - /* med = 0 */ - attrs.lpref = DEFAULT_LPREF; - attrs.origin = ORIGIN_IGP; - TAILQ_INIT(&attrs.others); + asp = path_get(); + asp->aspath = aspath_get(NULL, 0); + asp->origin = ORIGIN_IGP; + /* the other default values are OK, nexthop is once again NULL */ - nexthop.state = NEXTHOP_REACH; - nexthop.flags = NEXTHOP_ANNOUNCE; - nexthop.exit_nexthop.af = af; - nexthop.true_nexthop.af = af; - - /* apply default overrides. Not yet possible */ + /* XXX apply default overrides. Not yet possible */ /* filter as usual */ - addr.af = AF_INET; - if (rde_filter(peer, &attrs, &addr, 0, DIR_OUT) == ACTION_DENY) { - attr_free(&attrs); + bzero(&addr, sizeof(addr)); + addr.af = af; + if (rde_filter(peer, asp, &addr, 0, DIR_OUT) == ACTION_DENY) { + path_put(asp); return; } @@ -511,7 +520,7 @@ up_generate_default(struct rde_peer *peer, sa_family_t af) if (a == NULL) fatal("up_generate_default"); - if (up_generate_attr(peer, a, &attrs, &nexthop) == -1) { + if (up_generate_attr(peer, a, asp) == -1) { log_warnx("generation of bgp path attributes failed"); free(a); free(p); @@ -522,23 +531,33 @@ up_generate_default(struct rde_peer *peer, sa_family_t af) * use aspath_hash as attr_hash, this may be unoptimal * but currently I don't care. */ - a->attr_hash = aspath_hash(attrs.aspath->data, attrs.aspath->len); + a->attr_hash = aspath_hash(asp->aspath->data, asp->aspath->len); p->prefix = addr; p->prefixlen = 0; /* default route */ /* no longer needed */ - attr_free(&attrs); + path_put(asp); if (up_add(peer, p, a) == -1) log_warnx("queuing update failed."); } +void +up_dump_upcall(struct pt_entry *pt, void *ptr) +{ + struct rde_peer *peer = ptr; + + if (pt->active == NULL) + return; + up_generate_updates(peer, pt->active, NULL); +} + u_char up_attr_buf[4096]; int up_generate_attr(struct rde_peer *peer, struct update_attr *upa, - struct attr_flags *a, struct nexthop *nh) + struct rde_aspath *a) { struct aspath *path; struct attr *oa; @@ -564,11 +583,13 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, /* nexthop, already network byte order */ if (peer->conf.ebgp == 0) { /* - * if directly connected use peer->local_addr + * If directly connected use peer->local_addr + * this is only true for announced networks. */ - if (nh->flags & NEXTHOP_ANNOUNCE) + if (a->nexthop == NULL) nexthop = peer->local_addr.v4.s_addr; - else if (a->nexthop.s_addr == peer->remote_addr.v4.s_addr) + else if (a->nexthop->exit_nexthop.v4.s_addr == + peer->remote_addr.v4.s_addr) /* * per rfc: if remote peer address is equal to * the nexthop set the nexthop to our local address. @@ -576,16 +597,17 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, */ nexthop = peer->local_addr.v4.s_addr; else - nexthop = nh->exit_nexthop.v4.s_addr; + nexthop = a->nexthop->exit_nexthop.v4.s_addr; } else if (peer->conf.distance == 1) { /* ebgp directly connected */ - if (nh->flags & NEXTHOP_CONNECTED) { - mask = 0xffffffff << (32 - nh->nexthop_netlen); + if (a->nexthop != NULL && + a->nexthop->flags & NEXTHOP_CONNECTED) { + mask = 0xffffffff << (32 - a->nexthop->nexthop_netlen); mask = htonl(mask); if ((peer->remote_addr.v4.s_addr & mask) == - (nh->nexthop_net.v4.s_addr & mask)) + (a->nexthop->nexthop_net.v4.s_addr & mask)) /* nexthop and peer are in the same net */ - nexthop = nh->exit_nexthop.v4.s_addr; + nexthop = a->nexthop->exit_nexthop.v4.s_addr; else nexthop = peer->local_addr.v4.s_addr; } else @@ -593,9 +615,9 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, } else /* ebgp multihop */ /* - * XXX for ebgp multihop nh->flags should never have + * For ebgp multihop nh->flags should never have * NEXTHOP_CONNECTED set so it should be possible to unify the - * two ebgp cases. + * two ebgp cases. But this is save and RFC compliant. */ nexthop = peer->local_addr.v4.s_addr; @@ -781,12 +803,3 @@ up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer) return (wpos); } -void -up_dump_upcall(struct pt_entry *pt, void *ptr) -{ - struct rde_peer *peer = ptr; - - if (pt->active == NULL) - return; - up_generate_updates(peer, pt->active, NULL); -} |