summaryrefslogtreecommitdiff
path: root/net/openbgpd/files/patch-bgpd_rde_update.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/openbgpd/files/patch-bgpd_rde_update.c')
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_update.c706
1 files changed, 563 insertions, 143 deletions
diff --git a/net/openbgpd/files/patch-bgpd_rde_update.c b/net/openbgpd/files/patch-bgpd_rde_update.c
index fc034a3f7429..bbd2c19ce25f 100644
--- a/net/openbgpd/files/patch-bgpd_rde_update.c
+++ b/net/openbgpd/files/patch-bgpd_rde_update.c
@@ -1,18 +1,18 @@
Index: bgpd/rde_update.c
===================================================================
RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_update.c,v
-retrieving revision 1.1.1.1
-retrieving revision 1.4
-diff -u -p -r1.1.1.1 -r1.4
---- bgpd/rde_update.c 30 Jun 2009 05:46:15 -0000 1.1.1.1
-+++ bgpd/rde_update.c 10 Aug 2009 21:20:05 -0000 1.4
+retrieving revision 1.1.1.7
+retrieving revision 1.6
+diff -u -p -r1.1.1.7 -r1.6
+--- bgpd/rde_update.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/rde_update.c 14 Feb 2010 19:53:36 -0000 1.6
@@ -1,4 +1,4 @@
--/* $OpenBSD: rde_update.c,v 1.61 2007/11/27 01:13:54 claudio Exp $ */
-+/* $OpenBSD: rde_update.c,v 1.69 2009/08/06 08:53:11 claudio Exp $ */
+-/* $OpenBSD: rde_update.c,v 1.68 2009/06/06 01:10:29 claudio Exp $ */
++/* $OpenBSD: rde_update.c,v 1.77 2010/01/13 06:02:37 claudio Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
-@@ -17,10 +17,17 @@
+@@ -17,19 +17,26 @@
*/
#include <sys/types.h>
#include <sys/queue.h>
@@ -30,7 +30,120 @@ diff -u -p -r1.1.1.1 -r1.4
#include "bgpd.h"
#include "rde.h"
-@@ -270,6 +277,8 @@ up_test_update(struct rde_peer *peer, st
+
+ in_addr_t up_get_nexthop(struct rde_peer *, struct rde_aspath *);
+ int up_generate_mp_reach(struct rde_peer *, struct update_attr *,
+- struct rde_aspath *, sa_family_t);
++ struct rde_aspath *, u_int8_t);
+ int up_generate_attr(struct rde_peer *, struct update_attr *,
+- struct rde_aspath *, sa_family_t);
++ struct rde_aspath *, u_int8_t);
+
+ /* update stuff. */
+ struct update_prefix {
+@@ -65,10 +72,12 @@ RB_GENERATE(uptree_attr, update_attr, en
+ void
+ up_init(struct rde_peer *peer)
+ {
+- TAILQ_INIT(&peer->updates);
+- TAILQ_INIT(&peer->withdraws);
+- TAILQ_INIT(&peer->updates6);
+- TAILQ_INIT(&peer->withdraws6);
++ u_int8_t i;
++
++ for (i = 0; i < AID_MAX; i++) {
++ TAILQ_INIT(&peer->updates[i]);
++ TAILQ_INIT(&peer->withdraws[i]);
++ }
+ RB_INIT(&peer->up_prefix);
+ RB_INIT(&peer->up_attrs);
+ peer->up_pcnt = 0;
+@@ -103,8 +112,10 @@ up_clear(struct uplist_attr *updates, st
+ void
+ up_down(struct rde_peer *peer)
+ {
+- up_clear(&peer->updates, &peer->withdraws);
+- up_clear(&peer->updates6, &peer->withdraws6);
++ u_int8_t i;
++
++ for (i = 0; i < AID_MAX; i++)
++ up_clear(&peer->updates[i], &peer->withdraws[i]);
+
+ RB_INIT(&peer->up_prefix);
+ RB_INIT(&peer->up_attrs);
+@@ -120,19 +131,19 @@ up_prefix_cmp(struct update_prefix *a, s
+ {
+ int i;
+
+- if (a->prefix.af < b->prefix.af)
++ if (a->prefix.aid < b->prefix.aid)
+ return (-1);
+- if (a->prefix.af > b->prefix.af)
++ if (a->prefix.aid > b->prefix.aid)
+ return (1);
+
+- switch (a->prefix.af) {
+- case AF_INET:
++ switch (a->prefix.aid) {
++ case AID_INET:
+ if (ntohl(a->prefix.v4.s_addr) < ntohl(b->prefix.v4.s_addr))
+ return (-1);
+ if (ntohl(a->prefix.v4.s_addr) > ntohl(b->prefix.v4.s_addr))
+ return (1);
+ break;
+- case AF_INET6:
++ case AID_INET6:
+ i = memcmp(&a->prefix.v6, &b->prefix.v6,
+ sizeof(struct in6_addr));
+ if (i > 0)
+@@ -140,6 +151,25 @@ up_prefix_cmp(struct update_prefix *a, s
+ if (i < 0)
+ return (-1);
+ break;
++ case AID_VPN_IPv4:
++ if (betoh64(a->prefix.vpn4.rd) < betoh64(b->prefix.vpn4.rd))
++ return (-1);
++ if (betoh64(a->prefix.vpn4.rd) > betoh64(b->prefix.vpn4.rd))
++ return (1);
++ if (ntohl(a->prefix.v4.s_addr) < ntohl(b->prefix.v4.s_addr))
++ return (-1);
++ if (ntohl(a->prefix.v4.s_addr) > ntohl(b->prefix.v4.s_addr))
++ return (1);
++ if (a->prefixlen < b->prefixlen)
++ return (-1);
++ if (a->prefixlen > b->prefixlen)
++ return (1);
++ if (a->prefix.vpn4.labellen < b->prefix.vpn4.labellen)
++ return (-1);
++ if (a->prefix.vpn4.labellen > b->prefix.vpn4.labellen)
++ return (1);
++ return (memcmp(a->prefix.vpn4.labelstack,
++ b->prefix.vpn4.labelstack, a->prefix.vpn4.labellen));
+ default:
+ fatalx("pt_prefix_cmp: unknown af");
+ }
+@@ -174,18 +204,8 @@ up_add(struct rde_peer *peer, struct upd
+ 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");
+- }
++ upl = &peer->updates[p->prefix.aid];
++ wdl = &peer->withdraws[p->prefix.aid];
+
+ /* 1. search for attr */
+ if (a != NULL && (na = RB_FIND(uptree_attr, &peer->up_attrs, a)) ==
+@@ -270,21 +290,14 @@ up_test_update(struct rde_peer *peer, st
/* Do not send routes back to sender */
return (0);
@@ -39,154 +152,461 @@ diff -u -p -r1.1.1.1 -r1.4
if (p->aspath->flags & F_ATTR_LOOP)
fatalx("try to send out a looped path");
-@@ -360,7 +369,7 @@ up_generate(struct rde_peer *peer, struc
- if (asp) {
- ua = calloc(1, sizeof(struct update_attr));
+ pt_getaddr(p->prefix, &addr);
+- switch (addr.af) {
+- case AF_INET:
+- if (peer->capa_announced.mp_v4 == SAFI_NONE &&
+- peer->capa_received.mp_v6 != SAFI_NONE)
+- return (-1);
+- break;
+- case AF_INET6:
+- if (peer->capa_announced.mp_v6 == SAFI_NONE)
+- return (-1);
+- break;
+- }
++ if (peer->capa.mp[addr.aid] == 0)
++ return (-1);
+
+ if (p->aspath->peer->conf.ebgp == 0 && peer->conf.ebgp == 0) {
+ /*
+@@ -325,13 +338,13 @@ up_test_update(struct rde_peer *peer, st
+ }
+
+ /* well known communities */
+- if (rde_filter_community(p->aspath,
++ if (community_match(p->aspath,
+ COMMUNITY_WELLKNOWN, COMMUNITY_NO_ADVERTISE))
+ return (0);
+- if (peer->conf.ebgp && rde_filter_community(p->aspath,
++ if (peer->conf.ebgp && community_match(p->aspath,
+ COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPORT))
+ return (0);
+- if (peer->conf.ebgp && rde_filter_community(p->aspath,
++ if (peer->conf.ebgp && community_match(p->aspath,
+ COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPSUBCONFED))
+ return (0);
+
+@@ -362,7 +375,7 @@ up_generate(struct rde_peer *peer, struc
if (ua == NULL)
-- fatal("up_generate_updates");
-+ fatal("up_generate");
+ fatal("up_generate");
- if (up_generate_attr(peer, ua, asp, addr->af) == -1) {
+- if (up_generate_attr(peer, ua, asp, addr->af) == -1) {
++ if (up_generate_attr(peer, ua, asp, addr->aid) == -1) {
log_warnx("generation of bgp path attributes failed");
-@@ -379,7 +388,7 @@ up_generate(struct rde_peer *peer, struc
-
- up = calloc(1, sizeof(struct update_prefix));
- if (up == NULL)
-- fatal("up_generate_updates");
-+ fatal("up_generate");
- up->prefix = *addr;
- up->prefixlen = prefixlen;
-
-@@ -404,9 +413,9 @@ up_generate_updates(struct filter_head *
- return;
-
- pt_getaddr(old->prefix, &addr);
-- if (rde_filter(NULL, rules, peer, old->aspath, &addr,
-- old->prefix->prefixlen, old->aspath->peer, DIR_OUT) ==
-- ACTION_DENY)
-+ if (rde_filter(peer->ribid, NULL, rules, peer, old->aspath,
-+ &addr, old->prefix->prefixlen, old->aspath->peer,
-+ DIR_OUT) == ACTION_DENY)
- return;
-
- /* withdraw prefix */
-@@ -423,9 +432,9 @@ up_generate_updates(struct filter_head *
- }
+ free(ua);
+ return (-1);
+@@ -444,18 +457,12 @@ up_generate_updates(struct filter_head *
+ /* send a default route to the specified peer */
+ void
+ up_generate_default(struct filter_head *rules, struct rde_peer *peer,
+- sa_family_t af)
++ u_int8_t aid)
+ {
+ struct rde_aspath *asp, *fasp;
+ struct bgpd_addr addr;
- pt_getaddr(new->prefix, &addr);
-- if (rde_filter(&asp, rules, peer, new->aspath, &addr,
-- new->prefix->prefixlen, new->aspath->peer, DIR_OUT) ==
-- ACTION_DENY) {
-+ if (rde_filter(peer->ribid, &asp, rules, peer, new->aspath,
-+ &addr, new->prefix->prefixlen, new->aspath->peer,
-+ DIR_OUT) == ACTION_DENY) {
- path_put(asp);
- up_generate_updates(rules, peer, NULL, old);
- return;
-@@ -473,8 +482,8 @@ up_generate_default(struct filter_head *
- bzero(&addr, sizeof(addr));
- addr.af = af;
-
-- if (rde_filter(&fasp, rules, peer, asp, &addr, 0, NULL, DIR_OUT) ==
-- ACTION_DENY) {
-+ if (rde_filter(peer->ribid, &fasp, rules, peer, asp, &addr, 0, NULL,
-+ DIR_OUT) == ACTION_DENY) {
- path_put(fasp);
- path_put(asp);
+- if (peer->capa_received.mp_v4 == SAFI_NONE &&
+- peer->capa_received.mp_v6 != SAFI_NONE &&
+- af == AF_INET)
+- return;
+-
+- if (peer->capa_received.mp_v6 == SAFI_NONE &&
+- af == AF_INET6)
++ if (peer->capa.mp[aid] == 0)
return;
-@@ -617,7 +626,7 @@ up_generate_attr(struct rde_peer *peer,
- u_char *pdata;
- u_int32_t tmp32;
- in_addr_t nexthop;
-- int r, ismp = 0, neednewpath = 0;
-+ int flags, r, ismp = 0, neednewpath = 0;
- u_int16_t len = sizeof(up_attr_buf), wlen = 0, plen;
- u_int8_t l;
-
-@@ -629,7 +638,7 @@ up_generate_attr(struct rde_peer *peer,
-
- /* aspath */
- if (!peer->conf.ebgp ||
-- rde_decisionflags() & BGPD_FLAG_DECISION_TRANS_AS)
-+ peer->conf.flags & PEERFLAG_TRANS_AS)
- pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
- else
- pdata = aspath_prepend(a->aspath, rde_local_as(), 1, &plen);
-@@ -762,25 +771,29 @@ up_generate_attr(struct rde_peer *peer,
- /* NEW to OLD conversion when going sending stuff to a 2byte AS peer */
- if (neednewpath) {
- if (!peer->conf.ebgp ||
-- rde_decisionflags() & BGPD_FLAG_DECISION_TRANS_AS)
-+ peer->conf.flags & PEERFLAG_TRANS_AS)
- pdata = aspath_prepend(a->aspath, rde_local_as(), 0,
- &plen);
- else
- pdata = aspath_prepend(a->aspath, rde_local_as(), 1,
- &plen);
-+ flags = ATTR_OPTIONAL|ATTR_TRANSITIVE;
-+ if (!(a->flags & F_PREFIX_ANNOUNCED))
-+ flags |= ATTR_PARTIAL;
- if (plen == 0)
- r = 0;
-- else if ((r = attr_write(up_attr_buf + wlen, len,
-- ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_NEW_ASPATH,
-- pdata, plen)) == -1)
-+ else if ((r = attr_write(up_attr_buf + wlen, len, flags,
-+ ATTR_AS4_PATH, pdata, plen)) == -1)
- return (-1);
- wlen += r; len -= r;
- free(pdata);
+
+ asp = path_get();
+@@ -471,7 +478,7 @@ up_generate_default(struct filter_head *
+
+ /* filter as usual */
+ bzero(&addr, sizeof(addr));
+- addr.af = af;
++ addr.aid = aid;
+
+ if (rde_filter(peer->ribid, &fasp, rules, peer, asp, &addr, 0, NULL,
+ DIR_OUT) == ACTION_DENY) {
+@@ -491,6 +498,43 @@ up_generate_default(struct filter_head *
+ path_put(asp);
+ }
+
++/* generate a EoR marker in the update list. This is a horrible hack. */
++int
++up_generate_marker(struct rde_peer *peer, u_int8_t aid)
++{
++ struct update_attr *ua;
++ struct update_attr *na = NULL;
++ struct uplist_attr *upl = NULL;
++
++ ua = calloc(1, sizeof(struct update_attr));
++ if (ua == NULL)
++ fatal("up_generate_marker");
++
++ upl = &peer->updates[aid];
++
++ /* 1. search for attr */
++ if ((na = RB_FIND(uptree_attr, &peer->up_attrs, ua)) == NULL) {
++ /* 1.1 if not found -> add */
++ TAILQ_INIT(&ua->prefix_h);
++ if (RB_INSERT(uptree_attr, &peer->up_attrs, ua) != NULL) {
++ log_warnx("uptree_attr insert failed");
++ /* cleanup */
++ free(ua);
++ return (-1);
++ }
++ TAILQ_INSERT_TAIL(upl, ua, attr_l);
++ peer->up_acnt++;
++ } else {
++ /* 1.2 if found -> use that, free ua */
++ free(ua);
++ ua = na;
++ /* move to end of update queue */
++ TAILQ_REMOVE(upl, ua, attr_l);
++ TAILQ_INSERT_TAIL(upl, ua, attr_l);
++ }
++ return (0);
++}
++
+ u_char up_attr_buf[4096];
+
+ /* only for IPv4 */
+@@ -551,28 +595,41 @@ up_get_nexthop(struct rde_peer *peer, st
+
+ int
+ up_generate_mp_reach(struct rde_peer *peer, struct update_attr *upa,
+- struct rde_aspath *a, sa_family_t af)
++ struct rde_aspath *a, u_int8_t aid)
+ {
+ u_int16_t tmp;
+
+- switch (af) {
+- case AF_INET6:
++ switch (aid) {
++ case AID_INET6:
+ upa->mpattr_len = 21; /* AFI + SAFI + NH LEN + NH + Reserved */
+ upa->mpattr = malloc(upa->mpattr_len);
+ if (upa->mpattr == NULL)
+ fatal("up_generate_mp_reach");
+- tmp = htons(AFI_IPv6);
++ if (aid2afi(aid, &tmp, &upa->mpattr[2]))
++ fatalx("up_generate_mp_reachi: bad AID");
++ tmp = htons(tmp);
+ memcpy(upa->mpattr, &tmp, sizeof(tmp));
+- upa->mpattr[2] = SAFI_UNICAST;
+ upa->mpattr[3] = sizeof(struct in6_addr);
+ upa->mpattr[20] = 0; /* Reserved must be 0 */
+
+ /* nexthop dance see also up_get_nexthop() */
+- if (peer->conf.ebgp == 0) {
++ if (a->flags & F_NEXTHOP_NOMODIFY) {
++ /* no modify flag set */
++ if (a->nexthop == NULL)
++ memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
++ sizeof(struct in6_addr));
++ else
++ memcpy(&upa->mpattr[4],
++ &a->nexthop->exit_nexthop.v6,
++ sizeof(struct in6_addr));
++ } else if (a->flags & F_NEXTHOP_SELF)
++ memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
++ sizeof(struct in6_addr));
++ else if (!peer->conf.ebgp) {
+ /* ibgp */
+ if (a->nexthop == NULL ||
+- (a->nexthop->exit_nexthop.af == AF_INET6 &&
+- memcmp(&a->nexthop->exit_nexthop.v6,
++ (a->nexthop->exit_nexthop.aid == AID_INET6 &&
++ !memcmp(&a->nexthop->exit_nexthop.v6,
+ &peer->remote_addr.v6, sizeof(struct in6_addr))))
+ memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
+ sizeof(struct in6_addr));
+@@ -603,6 +660,68 @@ up_generate_mp_reach(struct rde_peer *pe
+ memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
+ sizeof(struct in6_addr));
+ return (0);
++ case AID_VPN_IPv4:
++ upa->mpattr_len = 17; /* AFI + SAFI + NH LEN + NH + Reserved */
++ upa->mpattr = calloc(upa->mpattr_len, 1);
++ if (upa->mpattr == NULL)
++ fatal("up_generate_mp_reach");
++ if (aid2afi(aid, &tmp, &upa->mpattr[2]))
++ fatalx("up_generate_mp_reachi: bad AID");
++ tmp = htons(tmp);
++ memcpy(upa->mpattr, &tmp, sizeof(tmp));
++ upa->mpattr[3] = sizeof(u_int64_t) + sizeof(struct in_addr);
++
++ /* nexthop dance see also up_get_nexthop() */
++ if (a->flags & F_NEXTHOP_NOMODIFY) {
++ /* no modify flag set */
++ if (a->nexthop == NULL)
++ memcpy(&upa->mpattr[12],
++ &peer->local_v4_addr.v4,
++ sizeof(struct in_addr));
++ else
++ /* nexthops are stored as IPv4 addrs */
++ memcpy(&upa->mpattr[12],
++ &a->nexthop->exit_nexthop.v4,
++ sizeof(struct in_addr));
++ } else if (a->flags & F_NEXTHOP_SELF)
++ memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
++ sizeof(struct in_addr));
++ else if (!peer->conf.ebgp) {
++ /* ibgp */
++ if (a->nexthop == NULL ||
++ (a->nexthop->exit_nexthop.aid == AID_INET &&
++ !memcmp(&a->nexthop->exit_nexthop.v4,
++ &peer->remote_addr.v4, sizeof(struct in_addr))))
++ memcpy(&upa->mpattr[12],
++ &peer->local_v4_addr.v4,
++ sizeof(struct in_addr));
++ else
++ memcpy(&upa->mpattr[12],
++ &a->nexthop->exit_nexthop.v4,
++ sizeof(struct in_addr));
++ } else if (peer->conf.distance == 1) {
++ /* ebgp directly connected */
++ if (a->nexthop != NULL &&
++ a->nexthop->flags & NEXTHOP_CONNECTED)
++ if (prefix_compare(&peer->remote_addr,
++ &a->nexthop->nexthop_net,
++ a->nexthop->nexthop_netlen) == 0) {
++ /*
++ * nexthop and peer are in the same
++ * subnet
++ */
++ memcpy(&upa->mpattr[12],
++ &a->nexthop->exit_nexthop.v4,
++ sizeof(struct in_addr));
++ return (0);
++ }
++ memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
++ sizeof(struct in_addr));
++ } else
++ /* ebgp multihop */
++ memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
++ sizeof(struct in_addr));
++ return (0);
+ default:
+ break;
}
- if (newaggr) {
-- if ((r = attr_write(up_attr_buf + wlen, len,
-- ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_NEW_AGGREGATOR,
-- newaggr->data, newaggr->len)) == -1)
-+ flags = ATTR_OPTIONAL|ATTR_TRANSITIVE;
-+ if (!(a->flags & F_PREFIX_ANNOUNCED))
-+ flags |= ATTR_PARTIAL;
-+ if ((r = attr_write(up_attr_buf + wlen, len, flags,
-+ ATTR_AS4_AGGREGATOR, newaggr->data, newaggr->len)) == -1)
+@@ -611,7 +730,7 @@ up_generate_mp_reach(struct rde_peer *pe
+
+ int
+ up_generate_attr(struct rde_peer *peer, struct update_attr *upa,
+- struct rde_aspath *a, sa_family_t af)
++ struct rde_aspath *a, u_int8_t aid)
+ {
+ struct attr *oa, *newaggr = NULL;
+ u_char *pdata;
+@@ -643,8 +762,8 @@ up_generate_attr(struct rde_peer *peer,
+ wlen += r; len -= r;
+ free(pdata);
+
+- switch (af) {
+- case AF_INET:
++ switch (aid) {
++ case AID_INET:
+ nexthop = up_get_nexthop(peer, a);
+ if ((r = attr_write(up_attr_buf + wlen, len, ATTR_WELL_KNOWN,
+ ATTR_NEXTHOP, &nexthop, 4)) == -1)
+@@ -659,9 +778,11 @@ up_generate_attr(struct rde_peer *peer,
+ /*
+ * The old MED from other peers MUST not be announced to others
+ * unless the MED is originating from us or the peer is an IBGP one.
++ * Only exception are routers with "transparent-as yes" set.
+ */
+ if (a->flags & F_ATTR_MED && (peer->conf.ebgp == 0 ||
+- a->flags & F_ATTR_MED_ANNOUNCE)) {
++ a->flags & F_ATTR_MED_ANNOUNCE ||
++ peer->conf.flags & PEERFLAG_TRANS_AS)) {
+ tmp32 = htonl(a->med);
+ if ((r = attr_write(up_attr_buf + wlen, len, ATTR_OPTIONAL,
+ ATTR_MED, &tmp32, 4)) == -1)
+@@ -791,7 +912,7 @@ up_generate_attr(struct rde_peer *peer,
+
+ /* write mp attribute to different buffer */
+ if (ismp)
+- if (up_generate_mp_reach(peer, upa, a, AF_INET6) == -1)
++ if (up_generate_mp_reach(peer, upa, a, aid) == -1)
return (-1);
- wlen += r; len -= r;
+
+ /* the bgp path attributes are now stored in the global buf */
+@@ -810,6 +931,7 @@ up_dump_prefix(u_char *buf, int len, str
+ {
+ struct update_prefix *upp;
+ int r, wpos = 0;
++ u_int8_t i;
+
+ while ((upp = TAILQ_FIRST(prefix_head)) != NULL) {
+ if ((r = prefix_write(buf + wpos, len - wpos,
+@@ -820,13 +942,14 @@ up_dump_prefix(u_char *buf, int len, str
+ log_warnx("dequeuing update failed.");
+ TAILQ_REMOVE(upp->prefix_h, upp, prefix_l);
+ peer->up_pcnt--;
+- if (upp->prefix_h == &peer->withdraws ||
+- upp->prefix_h == &peer->withdraws6) {
+- peer->up_wcnt--;
+- peer->prefix_sent_withdraw++;
+- } else {
+- peer->up_nlricnt--;
+- peer->prefix_sent_update++;
++ for (i = 0; i < AID_MAX; i++) {
++ if (upp->prefix_h == &peer->withdraws[i]) {
++ peer->up_wcnt--;
++ peer->prefix_sent_withdraw++;
++ } else {
++ peer->up_nlricnt--;
++ peer->prefix_sent_update++;
++ }
+ }
+ free(upp);
}
-@@ -913,13 +926,7 @@ up_dump_mp_unreach(u_char *buf, u_int16_
+@@ -844,16 +967,21 @@ up_dump_attrnlri(u_char *buf, int len, s
+ * It is possible that a queued path attribute has no nlri prefix.
+ * Ignore and remove those path attributes.
+ */
+- while ((upa = TAILQ_FIRST(&peer->updates)) != NULL)
++ while ((upa = TAILQ_FIRST(&peer->updates[AID_INET])) != NULL)
+ if (TAILQ_EMPTY(&upa->prefix_h)) {
++ attr_len = upa->attr_len;
+ if (RB_REMOVE(uptree_attr, &peer->up_attrs,
+ upa) == NULL)
+ log_warnx("dequeuing update failed.");
+- TAILQ_REMOVE(&peer->updates, upa, attr_l);
++ TAILQ_REMOVE(&peer->updates[AID_INET], upa, attr_l);
+ free(upa->attr);
+ free(upa->mpattr);
+ free(upa);
+ peer->up_acnt--;
++ /* XXX horrible hack,
++ * if attr_len is 0, it is a EoR marker */
++ if (attr_len == 0)
++ return (-1);
+ } else
+ break;
+
+@@ -884,7 +1012,7 @@ up_dump_attrnlri(u_char *buf, int len, s
+ if (TAILQ_EMPTY(&upa->prefix_h)) {
+ if (RB_REMOVE(uptree_attr, &peer->up_attrs, upa) == NULL)
+ log_warnx("dequeuing update failed.");
+- TAILQ_REMOVE(&peer->updates, upa, attr_l);
++ TAILQ_REMOVE(&peer->updates[AID_INET], upa, attr_l);
+ free(upa->attr);
+ free(upa->mpattr);
+ free(upa);
+@@ -895,12 +1023,13 @@ up_dump_attrnlri(u_char *buf, int len, s
+ }
+
+ u_char *
+-up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
++up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer,
++ u_int8_t aid)
+ {
+ int wpos;
+ u_int16_t datalen, tmp;
+ u_int16_t attrlen = 2; /* attribute header (without len) */
+- u_int8_t flags = ATTR_OPTIONAL;
++ u_int8_t flags = ATTR_OPTIONAL, safi;
+
+ /*
+ * reserve space for withdraw len, attr len, the attribute header
+@@ -912,7 +1041,7 @@ up_dump_mp_unreach(u_char *buf, u_int16_
return (NULL);
- datalen += 3; /* afi + safi */
-- if (datalen > 255) {
-- attrlen += 2 + datalen;
-- flags |= ATTR_EXTLEN;
-- } else {
-- attrlen += 1 + datalen;
-- buf++;
-- }
-+
+ datalen = up_dump_prefix(buf + wpos, *len - wpos,
+- &peer->withdraws6, peer);
++ &peer->withdraws[aid], peer);
+ if (datalen == 0)
+ return (NULL);
+
+@@ -920,9 +1049,11 @@ up_dump_mp_unreach(u_char *buf, u_int16_
+
/* prepend header, need to do it reverse */
/* safi & afi */
- buf[--wpos] = SAFI_UNICAST;
-@@ -929,11 +936,15 @@ up_dump_mp_unreach(u_char *buf, u_int16_
+- buf[--wpos] = SAFI_UNICAST;
++ if (aid2afi(aid, &tmp, &safi))
++ fatalx("up_dump_mp_unreach: bad AID");
++ buf[--wpos] = safi;
+ wpos -= sizeof(u_int16_t);
+- tmp = htons(AFI_IPv6);
++ tmp = htons(tmp);
+ memcpy(buf + wpos, &tmp, sizeof(u_int16_t));
/* attribute length */
- if (datalen > 255) {
-+ attrlen += 2 + datalen;
-+ flags |= ATTR_EXTLEN;
- wpos -= sizeof(u_int16_t);
- tmp = htons(datalen);
- memcpy(buf + wpos, &tmp, sizeof(u_int16_t));
-- } else
-+ } else {
-+ attrlen += 1 + datalen;
- buf[--wpos] = (u_char)datalen;
-+ }
+@@ -959,33 +1090,39 @@ up_dump_mp_unreach(u_char *buf, u_int16_
+ return (buf + wpos);
+ }
- /* mp attribute */
- buf[--wpos] = (u_char)ATTR_MP_UNREACH_NLRI;
-@@ -954,7 +965,7 @@ up_dump_mp_unreach(u_char *buf, u_int16_
- /* total length includes the two 2-bytes length fields. */
- *len = attrlen + 2 * sizeof(u_int16_t);
+-u_char *
+-up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
++int
++up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer,
++ u_int8_t aid)
+ {
+ struct update_attr *upa;
+ int wpos;
+- u_int16_t datalen, tmp;
++ u_int16_t attr_len, datalen, tmp;
+ u_int8_t flags = ATTR_OPTIONAL;
-- return (buf);
-+ return (buf + wpos);
- }
+ /*
+ * It is possible that a queued path attribute has no nlri prefix.
+ * Ignore and remove those path attributes.
+ */
+- while ((upa = TAILQ_FIRST(&peer->updates6)) != NULL)
++ while ((upa = TAILQ_FIRST(&peer->updates[aid])) != NULL)
+ if (TAILQ_EMPTY(&upa->prefix_h)) {
++ attr_len = upa->attr_len;
+ if (RB_REMOVE(uptree_attr, &peer->up_attrs,
+ upa) == NULL)
+ log_warnx("dequeuing update failed.");
+- TAILQ_REMOVE(&peer->updates6, upa, attr_l);
++ TAILQ_REMOVE(&peer->updates[aid], upa, attr_l);
+ free(upa->attr);
+ free(upa->mpattr);
+ free(upa);
+ peer->up_acnt--;
++ /* XXX horrible hack,
++ * if attr_len is 0, it is a EoR marker */
++ if (attr_len == 0)
++ return (-1);
+ } else
+ break;
- u_char *
+ if (upa == NULL)
+- return (NULL);
++ return (-2);
+
+ /*
+ * reserve space for attr len, the attributes, the
+@@ -993,12 +1130,12 @@ up_dump_mp_reach(u_char *buf, u_int16_t
+ */
+ wpos = 2 + 2 + upa->attr_len + 4 + upa->mpattr_len;
+ if (*len < wpos)
+- return (NULL);
++ return (-2);
+
+ datalen = up_dump_prefix(buf + wpos, *len - wpos,
+ &upa->prefix_h, peer);
+ if (datalen == 0)
+- return (NULL);
++ return (-2);
+
+ if (upa->mpattr_len == 0 || upa->mpattr == NULL)
+ fatalx("mulitprotocol update without MP attrs");
+@@ -1038,7 +1175,7 @@ up_dump_mp_reach(u_char *buf, u_int16_t
+ if (TAILQ_EMPTY(&upa->prefix_h)) {
+ if (RB_REMOVE(uptree_attr, &peer->up_attrs, upa) == NULL)
+ log_warnx("dequeuing update failed.");
+- TAILQ_REMOVE(&peer->updates6, upa, attr_l);
++ TAILQ_REMOVE(&peer->updates[aid], upa, attr_l);
+ free(upa->attr);
+ free(upa->mpattr);
+ free(upa);
+@@ -1046,6 +1183,5 @@ up_dump_mp_reach(u_char *buf, u_int16_t
+ }
+
+ *len = datalen + 4;
+- return (buf + wpos);
++ return (wpos);
+ }
+-