From 574bd409f2c24cd362fe27ec7e43afb8befca78a Mon Sep 17 00:00:00 2001 From: Claudio Jeker Date: Sat, 10 Jan 2004 22:25:43 +0000 Subject: Implement as path prepends. At least one prepend is needed for ebgp neighbors. Fix a bug in the update generation. If no path attributes are available e.g. a packet with only withdraws we need to set (and write) the bgp path attribute field to zero. With this change we are able to send valid updates to our neighbors with one exception: the nexthop field which needs to be changed for ebgp neighbors. OK henning@ --- usr.sbin/bgpd/rde.c | 23 +++++++++++--- usr.sbin/bgpd/rde.h | 5 ++- usr.sbin/bgpd/rde_decide.c | 28 ++++++++++------- usr.sbin/bgpd/rde_rib.c | 78 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 116 insertions(+), 18 deletions(-) diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 84614366952..09470f5dce2 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.51 2004/01/10 16:20:29 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.52 2004/01/10 22:25:42 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -618,8 +618,17 @@ rde_send_nexthop(in_addr_t next, int valid) fatal("imsg_compose error"); } +/* + * update specific functions + */ u_char queue_buf[4096]; +u_int16_t +rde_local_as(void) +{ + return conf->as; +} + void rde_update_queue_runner(void) { @@ -634,10 +643,11 @@ rde_update_queue_runner(void) if (peer->state != PEER_UP) continue; /* first withdraws */ - wpos = 2; - r = up_dump_prefix(queue_buf + wpos, len - wpos, + wpos = 2; /* reserve space for the lenght field */ + r = up_dump_prefix(queue_buf + wpos, len - wpos - 2, &peer->withdraws, peer); wd_len = r; + /* write withdraws lenght filed */ wd_len = htons(wd_len); memcpy(queue_buf, &wd_len, 2); wpos += r; @@ -647,8 +657,11 @@ rde_update_queue_runner(void) peer); wpos += r; - if (wpos == 2) - /* no packet to send */ + if (wpos == 4) + /* + * No packet to send. The 4 bytes are the + * needed withdraw and path attribute lenght. + */ continue; /* finally send message to SE */ diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index b151c31553b..b0110dd2c88 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.12 2004/01/10 16:20:29 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.13 2004/01/10 22:25:42 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -218,6 +218,7 @@ struct prefix { /* rde.c */ void rde_send_kroute(struct prefix *, struct prefix *); void rde_send_nexthop(in_addr_t, int); +u_int16_t rde_local_as(void); /* rde_rib.c */ int attr_compare(struct attr_flags *, struct attr_flags *); @@ -233,6 +234,8 @@ int aspath_verify(void *, u_int16_t, u_int16_t); #define AS_ERR_LOOP -3 struct aspath *aspath_create(void *, u_int16_t); void aspath_destroy(struct aspath *); +int aspath_write(void *, u_int16_t, struct aspath *, u_int16_t, + int); u_char *aspath_dump(struct aspath *); u_int16_t aspath_length(struct aspath *); u_int16_t aspath_count(struct aspath *); diff --git a/usr.sbin/bgpd/rde_decide.c b/usr.sbin/bgpd/rde_decide.c index 4e66e58515b..08c18bc3bac 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.12 2004/01/10 16:20:29 claudio Exp $ */ +/* $OpenBSD: rde_decide.c,v 1.13 2004/01/10 22:25:42 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -475,7 +475,7 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, struct attr *oa; u_int32_t tmp32; int r; - u_int16_t aslen, len = sizeof(up_attr_buf), wlen = 0; + u_int16_t len = sizeof(up_attr_buf), wlen = 0; /* origin */ if ((r = attr_write(up_attr_buf + wlen, len, ATTR_WELL_KNOWN, @@ -484,10 +484,8 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, wlen += r; len -= r; /* aspath */ - /* XXX XXX aspath prepends */ - aslen = aspath_length(a->aspath); - if ((r = attr_write(up_attr_buf + wlen, len, ATTR_WELL_KNOWN, - ATTR_ASPATH, aspath_dump(a->aspath), aslen)) == -1) + if ((r = aspath_write(up_attr_buf + wlen, len, a->aspath, + rde_local_as(), peer->conf.ebgp == 0 ? 0 : 1)) == -1) return (-1); wlen += r; len -= r; @@ -613,18 +611,26 @@ up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer) u_int16_t attr_len; upa = TAILQ_FIRST(&peer->updates); - if (upa == NULL || upa->attr_len + 5 > len) - /* either no packet or not enough space */ - return (0); + if (upa == NULL || upa->attr_len + 5 > len) { + /* + * either no packet or not enough space. + * The length field needs to be set to zero else it would be + * an invalid bgp update. + */ + bzero(buf, 2); + return (2); + } - /* first dump the attributes */ + /* first dump the 2-byte path attribute length */ attr_len = htons(upa->attr_len); memcpy(buf, &attr_len, 2); wpos = 2; + + /* then the path attributes them self */ memcpy(buf + wpos, upa->attr, upa->attr_len); wpos += upa->attr_len; - /* now dump the nlri */ + /* last but not least dump the nlri */ r = up_dump_prefix(buf + wpos, len - wpos, &upa->prefix_h, peer); wpos += r; diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index 097d18e02e6..f4712ffef3b 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.15 2004/01/10 16:20:29 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.16 2004/01/10 22:25:42 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -297,6 +297,82 @@ aspath_create(void *data, u_int16_t len) return aspath; } +int +aspath_write(void *p, u_int16_t len, struct aspath *aspath, u_int16_t myAS, + int prepend) +{ + u_char *b = p; + int tot_len, as_len, size, wpos = 0; + u_int16_t tmp; + u_int8_t type, attr_flag = ATTR_WELL_KNOWN; + + if (prepend > 255) + /* lunatic prepends need to be blocked in the parser */ + return (-1); + + /* first calculate new size */ + type = aspath->data[0]; + size = aspath->data[1]; + if (prepend == 0) + as_len = aspath->hdr.len; + else if (type == AS_SET || size + prepend > 255) + /* need to attach a new AS_SEQUENCE */ + as_len = 2 + prepend * 2 + aspath->hdr.len; + else + as_len = prepend * 2 + aspath->hdr.len; + + /* check buffer size */ + tot_len = 2 + as_len; + if (as_len > 255) { + attr_flag |= ATTR_EXTLEN; + tot_len += 2; + } else + tot_len += 1; + + if (tot_len > len) + return (-1); + + /* header */ + b[wpos++] = attr_flag; + b[wpos++] = ATTR_ASPATH; + if (as_len > 0xff) { + tmp = as_len; + tmp = htons(tmp); + memcpy(b, &tmp, 2); + wpos += 2; + } else + b[wpos++] = (u_char)(as_len & 0xff); + + /* first prepends */ + myAS = htons(myAS); + if (type == AS_SET) { + b[wpos++] = AS_SEQUENCE; + b[wpos++] = prepend; + for (; prepend > 0; prepend--) { + memcpy(b + wpos, &myAS, 2); + wpos += 2; + } + memcpy(b + wpos, aspath->data, aspath->hdr.len); + } else { + if (size + prepend > 255) { + b[wpos++] = AS_SEQUENCE; + b[wpos++] = size + prepend - 255; + for (; prepend + size > 255; prepend--) { + memcpy(b + wpos, &myAS, 2); + wpos += 2; + } + } + b[wpos++] = AS_SEQUENCE; + b[wpos++] = size + prepend; + for (; prepend > 0; prepend--) { + memcpy(b + wpos, &myAS, 2); + wpos += 2; + } + memcpy(b + wpos, aspath->data + 2, aspath->hdr.len - 2); + } + return (tot_len); +} + void aspath_destroy(struct aspath *aspath) { -- cgit v1.2.3