/* 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 #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 (void) { 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 (void *p) { struct bgp_advertise_attr * ref = (struct bgp_advertise_attr *) p; struct bgp_advertise_attr *baa; baa = baa_new (); baa->attr = ref->attr; return baa; } static unsigned int baa_hash_key (void *p) { struct bgp_advertise_attr * baa = (struct bgp_advertise_attr *) p; return attrhash_key_make (baa->attr); } static int baa_hash_cmp (const void *p1, const void *p2) { const struct bgp_advertise_attr * baa1 = p1; const struct bgp_advertise_attr * baa2 = p2; 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 (void) { return (struct bgp_advertise *) XCALLOC (MTYPE_BGP_ADVERTISE, sizeof (struct bgp_advertise)); } static void bgp_advertise_free (struct bgp_advertise *adv) { if (adv->binfo) bgp_info_unlock (adv->binfo); /* bgp_advertise bgp_info reference */ XFREE (MTYPE_BGP_ADVERTISE, adv); } static 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; } static 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; } static 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. */ static void bgp_adj_out_free (struct bgp_adj_out *adj) { peer_unlock (adj->peer); /* adj_out peer reference */ 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); } /* 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; if (DISABLE_BGP_ANNOUNCE) return; /* 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)); adj->peer = peer_lock (peer); /* adj_out peer reference */ if (rn) { BGP_ADJ_OUT_ADD (rn, adj); bgp_lock_node (rn); } } if (adj->adv) bgp_advertise_clean (peer, adj, afi, safi); adj->adv = bgp_advertise_new (); adv = adj->adv; adv->rn = rn; assert (adv->binfo == NULL); adv->binfo = bgp_info_lock (binfo); /* bgp_info adj_out reference */ 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; if (DISABLE_BGP_ANNOUNCE) return; /* 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_lock (peer); /* adj_in peer reference */ 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); peer_unlock (bai->peer); /* adj_in peer reference */ XFREE (MTYPE_BGP_ADJ_IN, bai); } int 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 0; bgp_adj_in_remove (rn, adj); bgp_unlock_node (rn); return 1; } 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_BGP_SYNCHRONISE, 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_BGP_SYNCHRONISE, peer->sync[afi][safi]); peer->sync[afi][safi] = NULL; if (peer->hash[afi][safi]) hash_free (peer->hash[afi][safi]); peer->hash[afi][safi] = NULL; } }