| /* 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 (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 (void *p1, void *p2) |
| { |
| struct bgp_advertise_attr * baa1 = (struct bgp_advertise_attr *) p1; |
| struct bgp_advertise_attr * baa2 = (struct bgp_advertise_attr *) 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 () |
| { |
| 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; |
| |
| #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)); |
| 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; |
| |
| #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_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); |
| } |
| |
| 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_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; |
| } |
| } |