diff options
Diffstat (limited to 'bgpd/bgp_advertise.c')
-rw-r--r-- | bgpd/bgp_advertise.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c new file mode 100644 index 000000000..4778a9773 --- /dev/null +++ b/bgpd/bgp_advertise.c @@ -0,0 +1,405 @@ +/* BGP advertisement and adjacency + Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro + +This file is part of GNU Zebra. + +GNU Zebra is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GNU Zebra is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Zebra; see the file COPYING. If not, write to the Free +Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +#include <zebra.h> + +#include "command.h" +#include "memory.h" +#include "prefix.h" +#include "hash.h" +#include "thread.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_mplsvpn.h" + +/* BGP advertise attribute is used for pack same attribute update into + one packet. To do that we maintain attribute hash in struct + peer. */ +static struct bgp_advertise_attr * +baa_new () +{ + return (struct bgp_advertise_attr *) + XCALLOC (MTYPE_BGP_ADVERTISE_ATTR, sizeof (struct bgp_advertise_attr)); +} + +static void +baa_free (struct bgp_advertise_attr *baa) +{ + XFREE (MTYPE_BGP_ADVERTISE_ATTR, baa); +} + +static void * +baa_hash_alloc (struct bgp_advertise_attr *ref) +{ + struct bgp_advertise_attr *baa; + + baa = baa_new (); + baa->attr = ref->attr; + return baa; +} + +static unsigned int +baa_hash_key (struct bgp_advertise_attr *baa) +{ + return attrhash_key_make (baa->attr); +} + +static int +baa_hash_cmp (struct bgp_advertise_attr *baa1, struct bgp_advertise_attr *baa2) +{ + return attrhash_cmp (baa1->attr, baa2->attr); +} + +/* BGP update and withdraw information is stored in BGP advertise + structure. This structure is referred from BGP adjacency + information. */ +static struct bgp_advertise * +bgp_advertise_new () +{ + return (struct bgp_advertise *) + XCALLOC (MTYPE_BGP_ADVERTISE, sizeof (struct bgp_advertise)); +} + +void +bgp_advertise_free (struct bgp_advertise *adv) +{ + XFREE (MTYPE_BGP_ADVERTISE, adv); +} + +void +bgp_advertise_add (struct bgp_advertise_attr *baa, + struct bgp_advertise *adv) +{ + adv->next = baa->adv; + if (baa->adv) + baa->adv->prev = adv; + baa->adv = adv; +} + +void +bgp_advertise_delete (struct bgp_advertise_attr *baa, + struct bgp_advertise *adv) +{ + if (adv->next) + adv->next->prev = adv->prev; + if (adv->prev) + adv->prev->next = adv->next; + else + baa->adv = adv->next; +} + +static struct bgp_advertise_attr * +bgp_advertise_intern (struct hash *hash, struct attr *attr) +{ + struct bgp_advertise_attr ref; + struct bgp_advertise_attr *baa; + + ref.attr = bgp_attr_intern (attr); + baa = (struct bgp_advertise_attr *) hash_get (hash, &ref, baa_hash_alloc); + baa->refcnt++; + + return baa; +} + +void +bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa) +{ + if (baa->refcnt) + baa->refcnt--; + + if (baa->refcnt && baa->attr) + bgp_attr_unintern (baa->attr); + else + { + if (baa->attr) + { + hash_release (hash, baa); + bgp_attr_unintern (baa->attr); + } + baa_free (baa); + } +} + +/* BGP adjacency keeps minimal advertisement information. */ +void +bgp_adj_out_free (struct bgp_adj_out *adj) +{ + XFREE (MTYPE_BGP_ADJ_OUT, adj); +} + +int +bgp_adj_out_lookup (struct peer *peer, struct prefix *p, + afi_t afi, safi_t safi, struct bgp_node *rn) +{ + struct bgp_adj_out *adj; + + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + break; + + if (! adj) + return 0; + + return (adj->adv + ? (adj->adv->baa ? 1 : 0) + : (adj->attr ? 1 : 0)); +} + +struct bgp_advertise * +bgp_advertise_clean (struct peer *peer, struct bgp_adj_out *adj, + afi_t afi, safi_t safi) +{ + struct bgp_advertise *adv; + struct bgp_advertise_attr *baa; + struct bgp_advertise *next; + + adv = adj->adv; + baa = adv->baa; + next = NULL; + + if (baa) + { + /* Unlink myself from advertise attribute FIFO. */ + bgp_advertise_delete (baa, adv); + + /* Fetch next advertise candidate. */ + next = baa->adv; + + /* Unintern BGP advertise attribute. */ + bgp_advertise_unintern (peer->hash[afi][safi], baa); + adv->baa = NULL; + adv->rn = NULL; + } + + /* Unlink myself from advertisement FIFO. */ + FIFO_DEL (adv); + + /* Free memory. */ + bgp_advertise_free (adj->adv); + adj->adv = NULL; + + return next; +} + +void +bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p, + struct attr *attr, afi_t afi, safi_t safi, + struct bgp_info *binfo) +{ + struct bgp_adj_out *adj = NULL; + struct bgp_advertise *adv; + +#ifdef DISABLE_BGP_ANNOUNCE + return; +#endif /* DISABLE_BGP_ANNOUNCE */ + + /* Look for adjacency information. */ + if (rn) + { + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + break; + } + + if (! adj) + { + adj = XCALLOC (MTYPE_BGP_ADJ_OUT, sizeof (struct bgp_adj_out)); + + if (rn) + { + BGP_ADJ_OUT_ADD (rn, adj); + bgp_lock_node (rn); + } + } + + if (adj->adv) + bgp_advertise_clean (peer, adj, afi, safi); + + adj->peer = peer; + adj->adv = bgp_advertise_new (); + + adv = adj->adv; + adv->rn = rn; + adv->binfo = binfo; + if (attr) + adv->baa = bgp_advertise_intern (peer->hash[afi][safi], attr); + else + adv->baa = baa_new (); + adv->adj = adj; + + /* Add new advertisement to advertisement attribute list. */ + bgp_advertise_add (adv->baa, adv); + + FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo); +} + +void +bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p, + afi_t afi, safi_t safi) +{ + struct bgp_adj_out *adj; + struct bgp_advertise *adv; + +#ifdef DISABLE_BGP_ANNOUNCE + return; +#endif /* DISABLE_BGP_ANNOUNCE */ + + /* Lookup existing adjacency, if it is not there return immediately. */ + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + break; + + if (! adj) + return; + + /* Clearn up previous advertisement. */ + if (adj->adv) + bgp_advertise_clean (peer, adj, afi, safi); + + if (adj->attr) + { + /* We need advertisement structure. */ + adj->adv = bgp_advertise_new (); + adv = adj->adv; + adv->rn = rn; + adv->adj = adj; + + /* Add to synchronization entry for withdraw announcement. */ + FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo); + + /* Schedule packet write. */ + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + } + else + { + /* Remove myself from adjacency. */ + BGP_ADJ_OUT_DEL (rn, adj); + + /* Free allocated information. */ + bgp_adj_out_free (adj); + + bgp_unlock_node (rn); + } +} + +void +bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, + struct peer *peer, afi_t afi, safi_t safi) +{ + if (adj->attr) + bgp_attr_unintern (adj->attr); + + if (adj->adv) + bgp_advertise_clean (peer, adj, afi, safi); + + BGP_ADJ_OUT_DEL (rn, adj); + bgp_adj_out_free (adj); +} + +void +bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr) +{ + struct bgp_adj_in *adj; + + for (adj = rn->adj_in; adj; adj = adj->next) + { + if (adj->peer == peer) + { + if (adj->attr != attr) + { + bgp_attr_unintern (adj->attr); + adj->attr = bgp_attr_intern (attr); + } + return; + } + } + adj = XCALLOC (MTYPE_BGP_ADJ_IN, sizeof (struct bgp_adj_in)); + adj->peer = peer; + adj->attr = bgp_attr_intern (attr); + BGP_ADJ_IN_ADD (rn, adj); + bgp_lock_node (rn); +} + +void +bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai) +{ + bgp_attr_unintern (bai->attr); + BGP_ADJ_IN_DEL (rn, bai); + XFREE (MTYPE_BGP_ADJ_IN, bai); +} + +void +bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer) +{ + struct bgp_adj_in *adj; + + for (adj = rn->adj_in; adj; adj = adj->next) + if (adj->peer == peer) + break; + + if (! adj) + return; + + bgp_adj_in_remove (rn, adj); + bgp_unlock_node (rn); +} + +void +bgp_sync_init (struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct bgp_synchronize *sync; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + sync = XCALLOC (MTYPE_TMP, sizeof (struct bgp_synchronize)); + FIFO_INIT (&sync->update); + FIFO_INIT (&sync->withdraw); + FIFO_INIT (&sync->withdraw_low); + peer->sync[afi][safi] = sync; + peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp); + } +} + +void +bgp_sync_delete (struct peer *peer) +{ + afi_t afi; + safi_t safi; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (peer->sync[afi][safi]) + XFREE (MTYPE_TMP, peer->sync[afi][safi]); + peer->sync[afi][safi] = NULL; + + hash_free (peer->hash[afi][safi]); + } +} |