/* BGP-4, BGP-4+ daemon program
   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro

This file is part of GNU Kroute.

GNU Kroute 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 Kroute 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 Kroute; see the file COPYING.  If not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include <kroute.h>

#include "prefix.h"
#include "thread.h"
#include "buffer.h"
#include "stream.h"
#include "command.h"
#include "sockunion.h"
#include "network.h"
#include "memory.h"
#include "filter.h"
#include "routemap.h"
#include "str.h"
#include "log.h"
#include "plist.h"
#include "linklist.h"
#include "workqueue.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_regex.h"
#include "bgpd/bgp_clist.h"
#include "bgpd/bgp_fsm.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_kroute.h"
#include "bgpd/bgp_open.h"
#include "bgpd/bgp_filter.h"
#include "bgpd/bgp_nexthop.h"
#include "bgpd/bgp_damp.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_advertise.h"
#include "bgpd/bgp_network.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_mpath.h"
#ifdef HAVE_SNMP
#include "bgpd/bgp_snmp.h"
#endif /* HAVE_SNMP */

/* BGP process wide configuration.  */
static struct bgp_master bgp_master;

extern struct in_addr router_id_kroute;

/* BGP process wide configuration pointer to export.  */
struct bgp_master *bm;

/* BGP community-list.  */
struct community_list_handler *bgp_clist;

/* BGP global flag manipulation.  */
int
bgp_option_set (int flag)
{
  switch (flag)
    {
    case BGP_OPT_NO_FIB:
    case BGP_OPT_MULTIPLE_INSTANCE:
    case BGP_OPT_CONFIG_CISCO:
      SET_FLAG (bm->options, flag);
      break;
    default:
      return BGP_ERR_INVALID_FLAG;
    }
  return 0;
}

int
bgp_option_unset (int flag)
{
  switch (flag)
    {
    case BGP_OPT_MULTIPLE_INSTANCE:
      if (listcount (bm->bgp) > 1)
	return BGP_ERR_MULTIPLE_INSTANCE_USED;
      /* Fall through.  */
    case BGP_OPT_NO_FIB:
    case BGP_OPT_CONFIG_CISCO:
      UNSET_FLAG (bm->options, flag);
      break;
    default:
      return BGP_ERR_INVALID_FLAG;
    }
  return 0;
}

int
bgp_option_check (int flag)
{
  return CHECK_FLAG (bm->options, flag);
}

/* BGP flag manipulation.  */
int
bgp_flag_set (struct bgp *bgp, int flag)
{
  SET_FLAG (bgp->flags, flag);
  return 0;
}

int
bgp_flag_unset (struct bgp *bgp, int flag)
{
  UNSET_FLAG (bgp->flags, flag);
  return 0;
}

int
bgp_flag_check (struct bgp *bgp, int flag)
{
  return CHECK_FLAG (bgp->flags, flag);
}

/* Internal function to set BGP structure configureation flag.  */
static void
bgp_config_set (struct bgp *bgp, int config)
{
  SET_FLAG (bgp->config, config);
}

static void
bgp_config_unset (struct bgp *bgp, int config)
{
  UNSET_FLAG (bgp->config, config);
}

static int
bgp_config_check (struct bgp *bgp, int config)
{
  return CHECK_FLAG (bgp->config, config);
}

/* Set BGP router identifier. */
int
bgp_router_id_set (struct bgp *bgp, struct in_addr *id)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  if (bgp_config_check (bgp, BGP_CONFIG_ROUTER_ID)
      && IPV4_ADDR_SAME (&bgp->router_id, id))
    return 0;

  IPV4_ADDR_COPY (&bgp->router_id, id);
  bgp_config_set (bgp, BGP_CONFIG_ROUTER_ID);

  /* Set all peer's local identifier with this value. */
  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      IPV4_ADDR_COPY (&peer->local_id, id);

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_RID_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
    }
  return 0;
}

/* BGP's cluster-id control. */
int
bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  if (bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID)
      && IPV4_ADDR_SAME (&bgp->cluster_id, cluster_id))
    return 0;

  IPV4_ADDR_COPY (&bgp->cluster_id, cluster_id);
  bgp_config_set (bgp, BGP_CONFIG_CLUSTER_ID);

  /* Clear all IBGP peer. */
  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      if (peer_sort (peer) != BGP_PEER_IBGP)
	continue;

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_CLID_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
    }
  return 0;
}

int
bgp_cluster_id_unset (struct bgp *bgp)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  if (! bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID))
    return 0;

  bgp->cluster_id.s_addr = 0;
  bgp_config_unset (bgp, BGP_CONFIG_CLUSTER_ID);

  /* Clear all IBGP peer. */
  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      if (peer_sort (peer) != BGP_PEER_IBGP)
	continue;

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_CLID_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
    }
  return 0;
}

/* time_t value that is monotonicly increasing
 * and uneffected by adjustments to system clock
 */
time_t bgp_clock (void)
{
  struct timeval tv;

  bane_gettime(BANE_CLK_MONOTONIC, &tv);
  return tv.tv_sec;
}

/* BGP timer configuration.  */
int
bgp_timers_set (struct bgp *bgp, u_int32_t keepalive, u_int32_t holdtime)
{
  bgp->default_keepalive = (keepalive < holdtime / 3 
			    ? keepalive : holdtime / 3);
  bgp->default_holdtime = holdtime;

  return 0;
}

int
bgp_timers_unset (struct bgp *bgp)
{
  bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
  bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;

  return 0;
}

/* BGP confederation configuration.  */
int
bgp_confederation_id_set (struct bgp *bgp, as_t as)
{
  struct peer *peer;
  struct listnode *node, *nnode;
  int already_confed;

  if (as == 0)
    return BGP_ERR_INVALID_AS;

  /* Remember - were we doing confederation before? */
  already_confed = bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION);
  bgp->confed_id = as;
  bgp_config_set (bgp, BGP_CONFIG_CONFEDERATION);

  /* If we were doing confederation already, this is just an external
     AS change.  Just Reset EBGP sessions, not CONFED sessions.  If we
     were not doing confederation before, reset all EBGP sessions.  */
  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      /* We're looking for peers who's AS is not local or part of our
	 confederation.  */
      if (already_confed)
	{
	  if (peer_sort (peer) == BGP_PEER_EBGP)
	    {
	      peer->local_as = as;
	      if (peer->status == Established)
               {
                 peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
               }

	      else
		BGP_EVENT_ADD (peer, BGP_Stop);
	    }
	}
      else
	{
	  /* Not doign confederation before, so reset every non-local
	     session */
	  if (peer_sort (peer) != BGP_PEER_IBGP)
	    {
	      /* Reset the local_as to be our EBGP one */
	      if (peer_sort (peer) == BGP_PEER_EBGP)
		peer->local_as = as;
	      if (peer->status == Established)
               {
                 peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
               }
	      else
		BGP_EVENT_ADD (peer, BGP_Stop);
	    }
	}
    }
  return 0;
}

int
bgp_confederation_id_unset (struct bgp *bgp)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  bgp->confed_id = 0;
  bgp_config_unset (bgp, BGP_CONFIG_CONFEDERATION);
      
  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      /* We're looking for peers who's AS is not local */
      if (peer_sort (peer) != BGP_PEER_IBGP)
	{
	  peer->local_as = bgp->as;
	  if (peer->status == Established)
           {
             peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
             bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
           }

	  else
	    BGP_EVENT_ADD (peer, BGP_Stop);
	}
    }
  return 0;
}

/* Is an AS part of the confed or not? */
int
bgp_confederation_peers_check (struct bgp *bgp, as_t as)
{
  int i;

  if (! bgp)
    return 0;

  for (i = 0; i < bgp->confed_peers_cnt; i++)
    if (bgp->confed_peers[i] == as)
      return 1;
  
  return 0;
}

/* Add an AS to the confederation set.  */
int
bgp_confederation_peers_add (struct bgp *bgp, as_t as)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  if (! bgp)
    return BGP_ERR_INVALID_BGP;

  if (bgp->as == as)
    return BGP_ERR_INVALID_AS;

  if (bgp_confederation_peers_check (bgp, as))
    return -1;

  if (bgp->confed_peers)
    bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST, 
				  bgp->confed_peers,
				  (bgp->confed_peers_cnt + 1) * sizeof (as_t));
  else
    bgp->confed_peers = XMALLOC (MTYPE_BGP_CONFED_LIST, 
				 (bgp->confed_peers_cnt + 1) * sizeof (as_t));

  bgp->confed_peers[bgp->confed_peers_cnt] = as;
  bgp->confed_peers_cnt++;

  if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION))
    {
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
	{
	  if (peer->as == as)
	    {
	      peer->local_as = bgp->as;
	      if (peer->status == Established)
               {
                 peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE;
                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
               }
	      else
	        BGP_EVENT_ADD (peer, BGP_Stop);
	    }
	}
    }
  return 0;
}

/* Delete an AS from the confederation set.  */
int
bgp_confederation_peers_remove (struct bgp *bgp, as_t as)
{
  int i;
  int j;
  struct peer *peer;
  struct listnode *node, *nnode;

  if (! bgp)
    return -1;

  if (! bgp_confederation_peers_check (bgp, as))
    return -1;

  for (i = 0; i < bgp->confed_peers_cnt; i++)
    if (bgp->confed_peers[i] == as)
      for(j = i + 1; j < bgp->confed_peers_cnt; j++)
	bgp->confed_peers[j - 1] = bgp->confed_peers[j];

  bgp->confed_peers_cnt--;

  if (bgp->confed_peers_cnt == 0)
    {
      if (bgp->confed_peers)
	XFREE (MTYPE_BGP_CONFED_LIST, bgp->confed_peers);
      bgp->confed_peers = NULL;
    }
  else
    bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST,
				  bgp->confed_peers,
				  bgp->confed_peers_cnt * sizeof (as_t));

  /* Now reset any peer who's remote AS has just been removed from the
     CONFED */
  if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION))
    {
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
	{
	  if (peer->as == as)
	    {
	      peer->local_as = bgp->confed_id;
	      if (peer->status == Established)
               {
                 peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE;
                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
               }
	      else
		BGP_EVENT_ADD (peer, BGP_Stop);
	    }
	}
    }

  return 0;
}

/* Local preference configuration.  */
int
bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref)
{
  if (! bgp)
    return -1;

  bgp->default_local_pref = local_pref;

  return 0;
}

int
bgp_default_local_preference_unset (struct bgp *bgp)
{
  if (! bgp)
    return -1;

  bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;

  return 0;
}

/* If peer is RSERVER_CLIENT in at least one address family and is not member
    of a peer_group for that family, return 1.
    Used to check wether the peer is included in list bgp->rsclient. */
int
peer_rsclient_active (struct peer *peer)
{
  int i;
  int j;

  for (i=AFI_IP; i < AFI_MAX; i++)
    for (j=SAFI_UNICAST; j < SAFI_MAX; j++)
      if (CHECK_FLAG(peer->af_flags[i][j], PEER_FLAG_RSERVER_CLIENT)
            && ! peer->af_group[i][j])
        return 1;
  return 0;
}

/* Peer comparison function for sorting.  */
static int
peer_cmp (struct peer *p1, struct peer *p2)
{
  return sockunion_cmp (&p1->su, &p2->su);
}

int
peer_af_flag_check (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
{
  return CHECK_FLAG (peer->af_flags[afi][safi], flag);
}

/* Reset all address family specific configuration.  */
static void
peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi)
{
  int i;
  struct bgp_filter *filter;
  char orf_name[BUFSIZ];

  filter = &peer->filter[afi][safi];

  /* Clear neighbor filter and route-map */
  for (i = FILTER_IN; i < FILTER_MAX; i++)
    {
      if (filter->dlist[i].name)
	{
	  free (filter->dlist[i].name);
	  filter->dlist[i].name = NULL;
	}
      if (filter->plist[i].name)
	{
	  free (filter->plist[i].name);
	  filter->plist[i].name = NULL; 
	}
      if (filter->aslist[i].name)
	{
	  free (filter->aslist[i].name);
	  filter->aslist[i].name = NULL;
	}
   }
 for (i = RMAP_IN; i < RMAP_MAX; i++)
       {
      if (filter->map[i].name)
	{
	  free (filter->map[i].name);
	  filter->map[i].name = NULL;
	}
    }

  /* Clear unsuppress map.  */
  if (filter->usmap.name)
    free (filter->usmap.name);
  filter->usmap.name = NULL;
  filter->usmap.map = NULL;

  /* Clear neighbor's all address family flags.  */
  peer->af_flags[afi][safi] = 0;

  /* Clear neighbor's all address family sflags. */
  peer->af_sflags[afi][safi] = 0;

  /* Clear neighbor's all address family capabilities. */
  peer->af_cap[afi][safi] = 0;

  /* Clear ORF info */
  peer->orf_plist[afi][safi] = NULL;
  sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
  prefix_bgp_orf_remove_all (orf_name);

  /* Set default neighbor send-community.  */
  if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
    {
      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
    }

  /* Clear neighbor default_originate_rmap */
  if (peer->default_rmap[afi][safi].name)
    free (peer->default_rmap[afi][safi].name);
  peer->default_rmap[afi][safi].name = NULL;
  peer->default_rmap[afi][safi].map = NULL;

  /* Clear neighbor maximum-prefix */
  peer->pmax[afi][safi] = 0;
  peer->pmax_threshold[afi][safi] = MAXIMUM_PREFIX_THRESHOLD_DEFAULT;
}

/* peer global config reset */
static void
peer_global_config_reset (struct peer *peer)
{
  peer->weight = 0;
  peer->change_local_as = 0;
  peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1);
  if (peer->update_source)
    {
      sockunion_free (peer->update_source);
      peer->update_source = NULL;
    }
  if (peer->update_if)
    {
      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
      peer->update_if = NULL;
    }

  if (peer_sort (peer) == BGP_PEER_IBGP)
    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
  else
    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;

  peer->flags = 0;
  peer->config = 0;
  peer->holdtime = 0;
  peer->keepalive = 0;
  peer->connect = 0;
  peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
}

/* Check peer's AS number and determin is this peer IBGP or EBGP */
int
peer_sort (struct peer *peer)
{
  struct bgp *bgp;

  bgp = peer->bgp;

  /* Peer-group */
  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->as)
	return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP);
      else
	{
	  struct peer *peer1;
	  peer1 = listnode_head (peer->group->peer);
	  if (peer1)
	    return (peer1->local_as == peer1->as 
		    ? BGP_PEER_IBGP : BGP_PEER_EBGP);
	} 
      return BGP_PEER_INTERNAL;
    }

  /* Normal peer */
  if (bgp && CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
    {
      if (peer->local_as == 0)
	return BGP_PEER_INTERNAL;

      if (peer->local_as == peer->as)
	{
	  if (peer->local_as == bgp->confed_id)
	    return BGP_PEER_EBGP;
	  else
	    return BGP_PEER_IBGP;
	}

      if (bgp_confederation_peers_check (bgp, peer->as))
	return BGP_PEER_CONFED;

      return BGP_PEER_EBGP;
    }
  else
    {
      return (peer->local_as == 0
	      ? BGP_PEER_INTERNAL : peer->local_as == peer->as
	      ? BGP_PEER_IBGP : BGP_PEER_EBGP);
    }
}

static void
peer_free (struct peer *peer)
{
  assert (peer->status == Deleted);

  bgp_unlock(peer->bgp);

  /* this /ought/ to have been done already through bgp_stop earlier,
   * but just to be sure.. 
   */
  bgp_timer_set (peer);
  BGP_READ_OFF (peer->t_read);
  BGP_WRITE_OFF (peer->t_write);
  BGP_EVENT_FLUSH (peer);
  
  if (peer->desc)
    XFREE (MTYPE_PEER_DESC, peer->desc);
  
  /* Free allocated host character. */
  if (peer->host)
    XFREE (MTYPE_BGP_PEER_HOST, peer->host);
  
  /* Update source configuration.  */
  if (peer->update_source)
    sockunion_free (peer->update_source);
  
  if (peer->update_if)
    XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
    
  if (peer->clear_node_queue)
    work_queue_free (peer->clear_node_queue);
  
  bgp_sync_delete (peer);
  memset (peer, 0, sizeof (struct peer));
  
  XFREE (MTYPE_BGP_PEER, peer);
}
                                                
/* increase reference count on a struct peer */
struct peer *
peer_lock (struct peer *peer)
{
  assert (peer && (peer->lock >= 0));
    
  peer->lock++;
  
  return peer;
}

/* decrease reference count on a struct peer
 * struct peer is freed and NULL returned if last reference
 */
struct peer *
peer_unlock (struct peer *peer)
{
  assert (peer && (peer->lock > 0));
  
  peer->lock--;
  
  if (peer->lock == 0)
    {
#if 0
      zlog_debug ("unlocked and freeing");
      zlog_backtrace (LOG_DEBUG);
#endif
      peer_free (peer);
      return NULL;
    }

#if 0
  if (peer->lock == 1)
    {
      zlog_debug ("unlocked to 1");
      zlog_backtrace (LOG_DEBUG);
    }
#endif

  return peer;
}
  
/* Allocate new peer object, implicitely locked.  */
static struct peer *
peer_new (struct bgp *bgp)
{
  afi_t afi;
  safi_t safi;
  struct peer *peer;
  struct servent *sp;
  
  /* bgp argument is absolutely required */
  assert (bgp);
  if (!bgp)
    return NULL;
  
  /* Allocate new peer. */
  peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer));

  /* Set default value. */
  peer->fd = -1;
  peer->v_start = BGP_INIT_START_TIMER;
  peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
  peer->v_asorig = BGP_DEFAULT_ASORIGINATE;
  peer->status = Idle;
  peer->ostatus = Idle;
  peer->weight = 0;
  peer->password = NULL;
  peer->bgp = bgp;
  peer = peer_lock (peer); /* initial reference */
  bgp_lock (bgp);

  /* Set default flags.  */
  for (afi = AFI_IP; afi < AFI_MAX; afi++)
    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
      {
	if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
	  {
	    SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
	    SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
	  }
	peer->orf_plist[afi][safi] = NULL;
      }
  SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);

  /* Create buffers.  */
  peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE);
  peer->obuf = stream_fifo_new ();
  peer->work = stream_new (BGP_MAX_PACKET_SIZE);

  bgp_sync_init (peer);

  /* Get service port number.  */
  sp = getservbyname ("bgp", "tcp");
  peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);

  return peer;
}

/* Create new BGP peer.  */
static struct peer *
peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
	     as_t remote_as, afi_t afi, safi_t safi)
{
  int active;
  struct peer *peer;
  char buf[SU_ADDRSTRLEN];

  peer = peer_new (bgp);
  peer->su = *su;
  peer->local_as = local_as;
  peer->as = remote_as;
  peer->local_id = bgp->router_id;
  peer->v_holdtime = bgp->default_holdtime;
  peer->v_keepalive = bgp->default_keepalive;
  if (peer_sort (peer) == BGP_PEER_IBGP)
    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
  else
    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
    
  peer = peer_lock (peer); /* bgp peer list reference */
  listnode_add_sort (bgp->peer, peer);

  active = peer_active (peer);

  if (afi && safi)
    peer->afc[afi][safi] = 1;

  /* Last read and reset time set */
  peer->readtime = peer->resettime = bgp_clock ();

  /* Default TTL set. */
  peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1);

  /* Make peer's address string. */
  sockunion2str (su, buf, SU_ADDRSTRLEN);
  peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);

  /* Set up peer's events and timers. */
  if (! active && peer_active (peer))
    bgp_timer_set (peer);

  return peer;
}

/* Make accept BGP peer.  Called from bgp_accept (). */
struct peer *
peer_create_accept (struct bgp *bgp)
{
  struct peer *peer;

  peer = peer_new (bgp);
  
  peer = peer_lock (peer); /* bgp peer list reference */
  listnode_add_sort (bgp->peer, peer);

  return peer;
}

/* Change peer's AS number.  */
static void
peer_as_change (struct peer *peer, as_t as)
{
  int type;

  /* Stop peer. */
  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
    }
  type = peer_sort (peer);
  peer->as = as;

  if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION)
      && ! bgp_confederation_peers_check (peer->bgp, as)
      && peer->bgp->as != as)
    peer->local_as = peer->bgp->confed_id;
  else
    peer->local_as = peer->bgp->as;

  /* Advertisement-interval reset */
  if (peer_sort (peer) == BGP_PEER_IBGP)
    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
  else
    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;

  /* TTL reset */
  if (peer_sort (peer) == BGP_PEER_IBGP)
    peer->ttl = 255;
  else if (type == BGP_PEER_IBGP)
    peer->ttl = 1;

  /* reflector-client reset */
  if (peer_sort (peer) != BGP_PEER_IBGP)
    {
      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
		  PEER_FLAG_REFLECTOR_CLIENT);
      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST],
		  PEER_FLAG_REFLECTOR_CLIENT);
      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MPLS_VPN],
		  PEER_FLAG_REFLECTOR_CLIENT);
      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
		  PEER_FLAG_REFLECTOR_CLIENT);
      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MULTICAST],
		  PEER_FLAG_REFLECTOR_CLIENT);
    }

  /* local-as reset */
  if (peer_sort (peer) != BGP_PEER_EBGP)
    {
      peer->change_local_as = 0;
      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
    }
}

/* If peer does not exist, create new one.  If peer already exists,
   set AS number to the peer.  */
int
peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as,
		afi_t afi, safi_t safi)
{
  struct peer *peer;
  as_t local_as;

  peer = peer_lookup (bgp, su);

  if (peer)
    {
      /* When this peer is a member of peer-group.  */
      if (peer->group)
	{
	  if (peer->group->conf->as)
	    {
	      /* Return peer group's AS number.  */
	      *as = peer->group->conf->as;
	      return BGP_ERR_PEER_GROUP_MEMBER;
	    }
	  if (peer_sort (peer->group->conf) == BGP_PEER_IBGP)
	    {
	      if (bgp->as != *as)
		{
		  *as = peer->as;
		  return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
		}
	    }
	  else
	    {
	      if (bgp->as == *as)
		{
		  *as = peer->as;
		  return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
		}
	    }
	}

      /* Existing peer's AS number change. */
      if (peer->as != *as)
	peer_as_change (peer, *as);
    }
  else
    {

      /* If the peer is not part of our confederation, and its not an
	 iBGP peer then spoof the source AS */
      if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)
	  && ! bgp_confederation_peers_check (bgp, *as) 
	  && bgp->as != *as)
	local_as = bgp->confed_id;
      else
	local_as = bgp->as;
      
      /* If this is IPv4 unicast configuration and "no bgp default
         ipv4-unicast" is specified. */

      if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)
	  && afi == AFI_IP && safi == SAFI_UNICAST)
	peer = peer_create (su, bgp, local_as, *as, 0, 0); 
      else
	peer = peer_create (su, bgp, local_as, *as, afi, safi); 
    }

  return 0;
}

/* Activate the peer or peer group for specified AFI and SAFI.  */
int
peer_activate (struct peer *peer, afi_t afi, safi_t safi)
{
  int active;

  if (peer->afc[afi][safi])
    return 0;

  /* Activate the address family configuration. */
  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    peer->afc[afi][safi] = 1;
  else
    {
      active = peer_active (peer);

      peer->afc[afi][safi] = 1;

      if (! active && peer_active (peer))
	bgp_timer_set (peer);
      else
	{
	  if (peer->status == Established)
	    {
	      if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV))
		{
		  peer->afc_adv[afi][safi] = 1;
		  bgp_capability_send (peer, afi, safi,
				       CAPABILITY_CODE_MP,
				       CAPABILITY_ACTION_SET);
		  if (peer->afc_recv[afi][safi])
		    {
		      peer->afc_nego[afi][safi] = 1;
		      bgp_announce_route (peer, afi, safi);
		    }
		}
	      else
               {
                 peer->last_reset = PEER_DOWN_AF_ACTIVATE;
                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
               }
	    }
	}
    }
  return 0;
}

int
peer_deactivate (struct peer *peer, afi_t afi, safi_t safi)
{
  struct peer_group *group;
  struct peer *peer1;
  struct listnode *node, *nnode;

  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      group = peer->group;

      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
	{
	  if (peer1->af_group[afi][safi])
	    return BGP_ERR_PEER_GROUP_MEMBER_EXISTS;
	}
    }
  else
    {
      if (peer->af_group[afi][safi])
	return BGP_ERR_PEER_BELONGS_TO_GROUP;
    }

  if (! peer->afc[afi][safi])
    return 0;

  /* De-activate the address family configuration. */
  peer->afc[afi][safi] = 0;
  peer_af_flag_reset (peer, afi, safi);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {  
      if (peer->status == Established)
	{
	  if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV))
	    {
	      peer->afc_adv[afi][safi] = 0;
	      peer->afc_nego[afi][safi] = 0;

	      if (peer_active_nego (peer))
		{
		  bgp_capability_send (peer, afi, safi,
				       CAPABILITY_CODE_MP,
				       CAPABILITY_ACTION_UNSET);
		  bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL);
		  peer->pcount[afi][safi] = 0;
		}
	      else
               {
                 peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
               }
	    }
	  else
           {
             peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
             bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
           }
	}
    }
  return 0;
}

static void
peer_nsf_stop (struct peer *peer)
{
  afi_t afi;
  safi_t safi;

  UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
  UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);

  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
    for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++)
      peer->nsf[afi][safi] = 0;

  if (peer->t_gr_restart)
    {
      BGP_TIMER_OFF (peer->t_gr_restart);
      if (BGP_DEBUG (events, EVENTS))
	zlog_debug ("%s graceful restart timer stopped", peer->host);
    }
  if (peer->t_gr_stale)
    {
      BGP_TIMER_OFF (peer->t_gr_stale);
      if (BGP_DEBUG (events, EVENTS))
	zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
    }
  bgp_clear_route_all (peer);
}

/* Delete peer from confguration.
 *
 * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
 * it to "cool off" and refcounts to hit 0, at which state it is freed.
 *
 * This function /should/ take care to be idempotent, to guard against
 * it being called multiple times through stray events that come in
 * that happen to result in this function being called again.  That
 * said, getting here for a "Deleted" peer is a bug in the neighbour
 * FSM.
 */
int
peer_delete (struct peer *peer)
{
  int i;
  afi_t afi;
  safi_t safi;
  struct bgp *bgp;
  struct bgp_filter *filter;
  struct listnode *pn;

  assert (peer->status != Deleted);
  
  bgp = peer->bgp;

  if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
    peer_nsf_stop (peer);

  /* If this peer belongs to peer group, clear up the
     relationship.  */
  if (peer->group)
    {
      if ((pn = listnode_lookup (peer->group->peer, peer)))
        {
          peer = peer_unlock (peer); /* group->peer list reference */
          list_delete_node (peer->group->peer, pn);
        }
      peer->group = NULL;
    }
  
  /* Withdraw all information from routing table.  We can not use
   * BGP_EVENT_ADD (peer, BGP_Stop) at here.  Because the event is
   * executed after peer structure is deleted.
   */
  peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
  bgp_stop (peer);
  bgp_fsm_change_status (peer, Deleted);

  /* Password configuration */
  if (peer->password)
    {
      XFREE (MTYPE_PEER_PASSWORD, peer->password);
      peer->password = NULL;

      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
	bgp_md5_set (peer);
    }
  
  bgp_timer_set (peer); /* stops all timers for Deleted */
  
  /* Delete from all peer list. */
  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
      && (pn = listnode_lookup (bgp->peer, peer)))
    {
      peer_unlock (peer); /* bgp peer list reference */
      list_delete_node (bgp->peer, pn);
    }
      
  if (peer_rsclient_active (peer)
      && (pn = listnode_lookup (bgp->rsclient, peer)))
    {
      peer_unlock (peer); /* rsclient list reference */
      list_delete_node (bgp->rsclient, pn);

      /* Clear our own rsclient ribs. */
      for (afi = AFI_IP; afi < AFI_MAX; afi++)
        for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
          if (CHECK_FLAG(peer->af_flags[afi][safi],
                         PEER_FLAG_RSERVER_CLIENT))
            bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
    }

  /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not
      member of a peer_group. */
  for (afi = AFI_IP; afi < AFI_MAX; afi++)
    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
      if (peer->rib[afi][safi] && ! peer->af_group[afi][safi])
        bgp_table_finish (&peer->rib[afi][safi]);

  /* Buffers.  */
  if (peer->ibuf)
    stream_free (peer->ibuf);
  if (peer->obuf)
    stream_fifo_free (peer->obuf);
  if (peer->work)
    stream_free (peer->work);
  peer->obuf = NULL;
  peer->work = peer->ibuf = NULL;

  /* Local and remote addresses. */
  if (peer->su_local)
    sockunion_free (peer->su_local);
  if (peer->su_remote)
    sockunion_free (peer->su_remote);
  peer->su_local = peer->su_remote = NULL;
  
  /* Free filter related memory.  */
  for (afi = AFI_IP; afi < AFI_MAX; afi++)
    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
      {
	filter = &peer->filter[afi][safi];

	for (i = FILTER_IN; i < FILTER_MAX; i++)
	  {
	    if (filter->dlist[i].name)
	      free (filter->dlist[i].name);
	    if (filter->plist[i].name)
	      free (filter->plist[i].name);
	    if (filter->aslist[i].name)
	      free (filter->aslist[i].name);
            
            filter->dlist[i].name = NULL;
            filter->plist[i].name = NULL;
            filter->aslist[i].name = NULL;
          }
        for (i = RMAP_IN; i < RMAP_MAX; i++)
          {
	    if (filter->map[i].name)
	      free (filter->map[i].name);
            filter->map[i].name = NULL;
	  }

	if (filter->usmap.name)
	  free (filter->usmap.name);

	if (peer->default_rmap[afi][safi].name)
	  free (peer->default_rmap[afi][safi].name);
        
        filter->usmap.name = NULL;
        peer->default_rmap[afi][safi].name = NULL;
      }
  
  peer_unlock (peer); /* initial reference */

  return 0;
}

static int
peer_group_cmp (struct peer_group *g1, struct peer_group *g2)
{
  return strcmp (g1->name, g2->name);
}

/* If peer is configured at least one address family return 1. */
static int
peer_group_active (struct peer *peer)
{
  if (peer->af_group[AFI_IP][SAFI_UNICAST]
      || peer->af_group[AFI_IP][SAFI_MULTICAST]
      || peer->af_group[AFI_IP][SAFI_MPLS_VPN]
      || peer->af_group[AFI_IP6][SAFI_UNICAST]
      || peer->af_group[AFI_IP6][SAFI_MULTICAST])
    return 1;
  return 0;
}

/* Peer group cofiguration. */
static struct peer_group *
peer_group_new (void)
{
  return (struct peer_group *) XCALLOC (MTYPE_PEER_GROUP,
					sizeof (struct peer_group));
}

static void
peer_group_free (struct peer_group *group)
{
  XFREE (MTYPE_PEER_GROUP, group);
}

struct peer_group *
peer_group_lookup (struct bgp *bgp, const char *name)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
    {
      if (strcmp (group->name, name) == 0)
	return group;
    }
  return NULL;
}

struct peer_group *
peer_group_get (struct bgp *bgp, const char *name)
{
  struct peer_group *group;

  group = peer_group_lookup (bgp, name);
  if (group)
    return group;

  group = peer_group_new ();
  group->bgp = bgp;
  group->name = strdup (name);
  group->peer = list_new ();
  group->conf = peer_new (bgp);
  if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
    group->conf->afc[AFI_IP][SAFI_UNICAST] = 1;
  group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name);
  group->conf->group = group;
  group->conf->as = 0; 
  group->conf->ttl = 1;
  group->conf->gtsm_hops = 0;
  group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
  UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER);
  UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT);
  group->conf->keepalive = 0;
  group->conf->holdtime = 0;
  group->conf->connect = 0;
  SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP);
  listnode_add_sort (bgp->group, group);

  return 0;
}

static void 
peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
			     afi_t afi, safi_t safi)
{
  int in = FILTER_IN;
  int out = FILTER_OUT;
  struct peer *conf;
  struct bgp_filter *pfilter;
  struct bgp_filter *gfilter;

  conf = group->conf;
  pfilter = &peer->filter[afi][safi];
  gfilter = &conf->filter[afi][safi];

  /* remote-as */
  if (conf->as)
    peer->as = conf->as;

  /* remote-as */
  if (conf->change_local_as)
    peer->change_local_as = conf->change_local_as;

  /* TTL */
  peer->ttl = conf->ttl;

  /* GTSM hops */
  peer->gtsm_hops = conf->gtsm_hops;

  /* Weight */
  peer->weight = conf->weight;

  /* peer flags apply */
  peer->flags = conf->flags;
  /* peer af_flags apply */
  peer->af_flags[afi][safi] = conf->af_flags[afi][safi];
  /* peer config apply */
  peer->config = conf->config;

  /* peer timers apply */
  peer->holdtime = conf->holdtime;
  peer->keepalive = conf->keepalive;
  peer->connect = conf->connect;
  if (CHECK_FLAG (conf->config, PEER_CONFIG_CONNECT))
    peer->v_connect = conf->connect;
  else
    peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;

  /* advertisement-interval reset */
  if (peer_sort (peer) == BGP_PEER_IBGP)
    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
  else
    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;

  /* password apply */
  if (peer->password)
    XFREE (MTYPE_PEER_PASSWORD, peer->password);

  if (conf->password)
    peer->password =  XSTRDUP (MTYPE_PEER_PASSWORD, conf->password);
  else
    peer->password = NULL;

  bgp_md5_set (peer);

  /* maximum-prefix */
  peer->pmax[afi][safi] = conf->pmax[afi][safi];
  peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi];
  peer->pmax_restart[afi][safi] = conf->pmax_restart[afi][safi];

  /* allowas-in */
  peer->allowas_in[afi][safi] = conf->allowas_in[afi][safi];

  /* route-server-client */
  if (CHECK_FLAG(conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
    {
      /* Make peer's RIB point to group's RIB. */
      peer->rib[afi][safi] = group->conf->rib[afi][safi];

      /* Import policy. */
      if (pfilter->map[RMAP_IMPORT].name)
        free (pfilter->map[RMAP_IMPORT].name);
      if (gfilter->map[RMAP_IMPORT].name)
        {
          pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
          pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
        }
      else
        {
          pfilter->map[RMAP_IMPORT].name = NULL;
          pfilter->map[RMAP_IMPORT].map = NULL;
        }

      /* Export policy. */
      if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
        {
          pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
          pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
        }
    }

  /* default-originate route-map */
  if (conf->default_rmap[afi][safi].name)
    {
      if (peer->default_rmap[afi][safi].name)
	free (peer->default_rmap[afi][safi].name);
      peer->default_rmap[afi][safi].name = strdup (conf->default_rmap[afi][safi].name);
      peer->default_rmap[afi][safi].map = conf->default_rmap[afi][safi].map;
    }

  /* update-source apply */
  if (conf->update_source)
    {
      if (peer->update_source)
	sockunion_free (peer->update_source);
      if (peer->update_if)
	{
	  XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
	  peer->update_if = NULL;
	}
      peer->update_source = sockunion_dup (conf->update_source);
    }
  else if (conf->update_if)
    {
      if (peer->update_if)
	XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
      if (peer->update_source)
	{
	  sockunion_free (peer->update_source);
	  peer->update_source = NULL;
	}
      peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, conf->update_if);
    }

  /* inbound filter apply */
  if (gfilter->dlist[in].name && ! pfilter->dlist[in].name)
    {
      if (pfilter->dlist[in].name)
	free (pfilter->dlist[in].name);
      pfilter->dlist[in].name = strdup (gfilter->dlist[in].name);
      pfilter->dlist[in].alist = gfilter->dlist[in].alist;
    }
  if (gfilter->plist[in].name && ! pfilter->plist[in].name)
    {
      if (pfilter->plist[in].name)
	free (pfilter->plist[in].name);
      pfilter->plist[in].name = strdup (gfilter->plist[in].name);
      pfilter->plist[in].plist = gfilter->plist[in].plist;
    }
  if (gfilter->aslist[in].name && ! pfilter->aslist[in].name)
    {
      if (pfilter->aslist[in].name)
	free (pfilter->aslist[in].name);
      pfilter->aslist[in].name = strdup (gfilter->aslist[in].name);
      pfilter->aslist[in].aslist = gfilter->aslist[in].aslist;
    }
  if (gfilter->map[RMAP_IN].name && ! pfilter->map[RMAP_IN].name)
    {
      if (pfilter->map[RMAP_IN].name)
        free (pfilter->map[RMAP_IN].name);
      pfilter->map[RMAP_IN].name = strdup (gfilter->map[RMAP_IN].name);
      pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map;
    }

  /* outbound filter apply */
  if (gfilter->dlist[out].name)
    {
      if (pfilter->dlist[out].name)
	free (pfilter->dlist[out].name);
      pfilter->dlist[out].name = strdup (gfilter->dlist[out].name);
      pfilter->dlist[out].alist = gfilter->dlist[out].alist;
    }
  else
    {
      if (pfilter->dlist[out].name)
	free (pfilter->dlist[out].name);
      pfilter->dlist[out].name = NULL;
      pfilter->dlist[out].alist = NULL;
    }
  if (gfilter->plist[out].name)
    {
      if (pfilter->plist[out].name)
	free (pfilter->plist[out].name);
      pfilter->plist[out].name = strdup (gfilter->plist[out].name);
      pfilter->plist[out].plist = gfilter->plist[out].plist;
    }
  else
    {
      if (pfilter->plist[out].name)
	free (pfilter->plist[out].name);
      pfilter->plist[out].name = NULL;
      pfilter->plist[out].plist = NULL;
    }
  if (gfilter->aslist[out].name)
    {
      if (pfilter->aslist[out].name)
	free (pfilter->aslist[out].name);
      pfilter->aslist[out].name = strdup (gfilter->aslist[out].name);
      pfilter->aslist[out].aslist = gfilter->aslist[out].aslist;
    }
  else
    {
      if (pfilter->aslist[out].name)
	free (pfilter->aslist[out].name);
      pfilter->aslist[out].name = NULL;
      pfilter->aslist[out].aslist = NULL;
    }
  if (gfilter->map[RMAP_OUT].name)
    {
      if (pfilter->map[RMAP_OUT].name)
        free (pfilter->map[RMAP_OUT].name);
      pfilter->map[RMAP_OUT].name = strdup (gfilter->map[RMAP_OUT].name);
      pfilter->map[RMAP_OUT].map = gfilter->map[RMAP_OUT].map;
    }
  else
    {
      if (pfilter->map[RMAP_OUT].name)
        free (pfilter->map[RMAP_OUT].name);
      pfilter->map[RMAP_OUT].name = NULL;
      pfilter->map[RMAP_OUT].map = NULL;
    }

 /* RS-client's import/export route-maps. */
  if (gfilter->map[RMAP_IMPORT].name)
    {
      if (pfilter->map[RMAP_IMPORT].name)
        free (pfilter->map[RMAP_IMPORT].name);
      pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
      pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
    }
  else
    {
      if (pfilter->map[RMAP_IMPORT].name)
        free (pfilter->map[RMAP_IMPORT].name);
      pfilter->map[RMAP_IMPORT].name = NULL;
      pfilter->map[RMAP_IMPORT].map = NULL;
    }
  if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
    {
      if (pfilter->map[RMAP_EXPORT].name)
        free (pfilter->map[RMAP_EXPORT].name);
      pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
      pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
    }

  if (gfilter->usmap.name)
    {
      if (pfilter->usmap.name)
	free (pfilter->usmap.name);
      pfilter->usmap.name = strdup (gfilter->usmap.name);
      pfilter->usmap.map = gfilter->usmap.map;
    }
  else
    {
      if (pfilter->usmap.name)
	free (pfilter->usmap.name);
      pfilter->usmap.name = NULL;
      pfilter->usmap.map = NULL;
    }
} 

/* Peer group's remote AS configuration.  */
int
peer_group_remote_as (struct bgp *bgp, const char *group_name, as_t *as)
{
  struct peer_group *group;
  struct peer *peer;
  struct listnode *node, *nnode;

  group = peer_group_lookup (bgp, group_name);
  if (! group)
    return -1;

  if (group->conf->as == *as)
    return 0;

  /* When we setup peer-group AS number all peer group member's AS
     number must be updated to same number.  */
  peer_as_change (group->conf, *as);

  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (peer->as != *as)
	peer_as_change (peer, *as);
    }

  return 0;
}

int
peer_group_delete (struct peer_group *group)
{
  struct bgp *bgp;
  struct peer *peer;
  struct listnode *node, *nnode;

  bgp = group->bgp;

  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      peer->group = NULL;
      peer_delete (peer);
    }
  list_delete (group->peer);

  free (group->name);
  group->name = NULL;

  group->conf->group = NULL;
  peer_delete (group->conf);

  /* Delete from all peer_group list. */
  listnode_delete (bgp->group, group);

  peer_group_free (group);

  return 0;
}

int
peer_group_remote_as_delete (struct peer_group *group)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  if (! group->conf->as)
    return 0;

  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      peer->group = NULL;
      peer_delete (peer);
    }
  list_delete_all_node (group->peer);

  group->conf->as = 0;

  return 0;
}

/* Bind specified peer to peer group.  */
int
peer_group_bind (struct bgp *bgp, union sockunion *su,
		 struct peer_group *group, afi_t afi, safi_t safi, as_t *as)
{
  struct peer *peer;
  int first_member = 0;

  /* Check peer group's address family.  */
  if (! group->conf->afc[afi][safi])
    return BGP_ERR_PEER_GROUP_AF_UNCONFIGURED;

  /* Lookup the peer.  */
  peer = peer_lookup (bgp, su);

  /* Create a new peer. */
  if (! peer)
    {
      if (! group->conf->as)
	return BGP_ERR_PEER_GROUP_NO_REMOTE_AS;

      peer = peer_create (su, bgp, bgp->as, group->conf->as, afi, safi);
      peer->group = group;
      peer->af_group[afi][safi] = 1;

      peer = peer_lock (peer); /* group->peer list reference */
      listnode_add (group->peer, peer);
      peer_group2peer_config_copy (group, peer, afi, safi);

      return 0;
    }

  /* When the peer already belongs to peer group, check the consistency.  */
  if (peer->af_group[afi][safi])
    {
      if (strcmp (peer->group->name, group->name) != 0)
	return BGP_ERR_PEER_GROUP_CANT_CHANGE;

      return 0;
    }

  /* Check current peer group configuration.  */
  if (peer_group_active (peer)
      && strcmp (peer->group->name, group->name) != 0)
    return BGP_ERR_PEER_GROUP_MISMATCH;

  if (! group->conf->as)
    {
      if (peer_sort (group->conf) != BGP_PEER_INTERNAL
	  && peer_sort (group->conf) != peer_sort (peer))
	{
	  if (as)
	    *as = peer->as;
	  return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
	}

      if (peer_sort (group->conf) == BGP_PEER_INTERNAL)
	first_member = 1;
    }

  peer->af_group[afi][safi] = 1;
  peer->afc[afi][safi] = 1;
  if (! peer->group)
    {
      peer->group = group;
      
      peer = peer_lock (peer); /* group->peer list reference */
      listnode_add (group->peer, peer);
    }
  else
    assert (group && peer->group == group);

  if (first_member)
    {
      /* Advertisement-interval reset */
      if (peer_sort (group->conf) == BGP_PEER_IBGP)
	group->conf->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
      else
	group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;

      /* ebgp-multihop reset */
      if (peer_sort (group->conf) == BGP_PEER_IBGP)
	group->conf->ttl = 255;

      /* local-as reset */
      if (peer_sort (group->conf) != BGP_PEER_EBGP)
	{
	  group->conf->change_local_as = 0;
	  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
	}
    }

  if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
    {
      struct listnode *pn;
      
      /* If it's not configured as RSERVER_CLIENT in any other address
          family, without being member of a peer_group, remove it from
          list bgp->rsclient.*/
      if (! peer_rsclient_active (peer)
          && (pn = listnode_lookup (bgp->rsclient, peer)))
        {
          peer_unlock (peer); /* peer rsclient reference */
          list_delete_node (bgp->rsclient, pn);

          /* Clear our own rsclient rib for this afi/safi. */
          bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
        }

      bgp_table_finish (&peer->rib[afi][safi]);

      /* Import policy. */
      if (peer->filter[afi][safi].map[RMAP_IMPORT].name)
        {
          free (peer->filter[afi][safi].map[RMAP_IMPORT].name);
          peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL;
          peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL;
        }

      /* Export policy. */
      if (! CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
              && peer->filter[afi][safi].map[RMAP_EXPORT].name)
        {
          free (peer->filter[afi][safi].map[RMAP_EXPORT].name);
          peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL;
          peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL;
        }
    }

  peer_group2peer_config_copy (group, peer, afi, safi);

  if (peer->status == Established)
    {
      peer->last_reset = PEER_DOWN_RMAP_BIND;
      bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                      BGP_NOTIFY_CEASE_CONFIG_CHANGE);
    }
  else
    BGP_EVENT_ADD (peer, BGP_Stop);

  return 0;
}

int
peer_group_unbind (struct bgp *bgp, struct peer *peer,
		   struct peer_group *group, afi_t afi, safi_t safi)
{
  if (! peer->af_group[afi][safi])
      return 0;

  if (group != peer->group)
    return BGP_ERR_PEER_GROUP_MISMATCH;

  peer->af_group[afi][safi] = 0;
  peer->afc[afi][safi] = 0;
  peer_af_flag_reset (peer, afi, safi);

  if (peer->rib[afi][safi])
    peer->rib[afi][safi] = NULL;

  if (! peer_group_active (peer))
    {
      assert (listnode_lookup (group->peer, peer));
      peer_unlock (peer); /* peer group list reference */
      listnode_delete (group->peer, peer);
      peer->group = NULL;
      if (group->conf->as)
	{
	  peer_delete (peer);
	  return 0;
	}
      peer_global_config_reset (peer);
    }

  if (peer->status == Established)
    {
      peer->last_reset = PEER_DOWN_RMAP_UNBIND;
      bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                      BGP_NOTIFY_CEASE_CONFIG_CHANGE);
    }
  else
    BGP_EVENT_ADD (peer, BGP_Stop);

  return 0;
}

/* BGP instance creation by `router bgp' commands. */
static struct bgp *
bgp_create (as_t *as, const char *name)
{
  struct bgp *bgp;
  afi_t afi;
  safi_t safi;

  if ( (bgp = XCALLOC (MTYPE_BGP, sizeof (struct bgp))) == NULL)
    return NULL;
  
  bgp_lock (bgp);
  bgp->peer_self = peer_new (bgp);
  bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement");

  bgp->peer = list_new ();
  bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp;

  bgp->group = list_new ();
  bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp;

  bgp->rsclient = list_new ();
  bgp->rsclient->cmp = (int (*)(void*, void*)) peer_cmp;

  for (afi = AFI_IP; afi < AFI_MAX; afi++)
    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
      {
	bgp->route[afi][safi] = bgp_table_init (afi, safi);
	bgp->aggregate[afi][safi] = bgp_table_init (afi, safi);
	bgp->rib[afi][safi] = bgp_table_init (afi, safi);
	bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
	bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
      }

  bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
  bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
  bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
  bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
  bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;

  bgp->as = *as;

  if (name)
    bgp->name = strdup (name);

  return bgp;
}

/* Return first entry of BGP. */
struct bgp *
bgp_get_default (void)
{
  if (bm->bgp->head)
    return (listgetdata (listhead (bm->bgp)));
  return NULL;
}

/* Lookup BGP entry. */
struct bgp *
bgp_lookup (as_t as, const char *name)
{
  struct bgp *bgp;
  struct listnode *node, *nnode;

  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
    if (bgp->as == as
	&& ((bgp->name == NULL && name == NULL) 
	    || (bgp->name && name && strcmp (bgp->name, name) == 0)))
      return bgp;
  return NULL;
}

/* Lookup BGP structure by view name. */
struct bgp *
bgp_lookup_by_name (const char *name)
{
  struct bgp *bgp;
  struct listnode *node, *nnode;

  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
    if ((bgp->name == NULL && name == NULL)
	|| (bgp->name && name && strcmp (bgp->name, name) == 0))
      return bgp;
  return NULL;
}

/* Called from VTY commands. */
int
bgp_get (struct bgp **bgp_val, as_t *as, const char *name)
{
  struct bgp *bgp;

  /* Multiple instance check. */
  if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
    {
      if (name)
	bgp = bgp_lookup_by_name (name);
      else
	bgp = bgp_get_default ();

      /* Already exists. */
      if (bgp)
	{
          if (bgp->as != *as)
	    {
	      *as = bgp->as;
	      return BGP_ERR_INSTANCE_MISMATCH;
	    }
	  *bgp_val = bgp;
	  return 0;
	}
    }
  else
    {
      /* BGP instance name can not be specified for single instance.  */
      if (name)
	return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET;

      /* Get default BGP structure if exists. */
      bgp = bgp_get_default ();

      if (bgp)
	{
	  if (bgp->as != *as)
	    {
	      *as = bgp->as;
	      return BGP_ERR_AS_MISMATCH;
	    }
	  *bgp_val = bgp;
	  return 0;
	}
    }

  bgp = bgp_create (as, name);
  bgp_router_id_set(bgp, &router_id_kroute);
  *bgp_val = bgp;

  /* Create BGP server socket, if first instance.  */
  if (list_isempty(bm->bgp))
    {
      if (bgp_socket (bm->port, bm->address) < 0)
	return BGP_ERR_INVALID_VALUE;
    }

  listnode_add (bm->bgp, bgp);

  return 0;
}

/* Delete BGP instance. */
int
bgp_delete (struct bgp *bgp)
{
  struct peer *peer;
  struct peer_group *group;
  struct listnode *node;
  struct listnode *next;
  afi_t afi;
  int i;

  /* Delete static route. */
  bgp_static_delete (bgp);

  /* Unset redistribution. */
  for (afi = AFI_IP; afi < AFI_MAX; afi++)
    for (i = 0; i < KROUTE_ROUTE_MAX; i++) 
      if (i != KROUTE_ROUTE_BGP)
	bgp_redistribute_unset (bgp, afi, i);

  for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer))
    peer_delete (peer);

  for (ALL_LIST_ELEMENTS (bgp->group, node, next, group))
    peer_group_delete (group);

  assert (listcount (bgp->rsclient) == 0);

  if (bgp->peer_self) {
    peer_delete(bgp->peer_self);
    bgp->peer_self = NULL;
  }
  
  /* Remove visibility via the master list - there may however still be
   * routes to be processed still referencing the struct bgp.
   */
  listnode_delete (bm->bgp, bgp);
  if (list_isempty(bm->bgp))
    bgp_close ();

  bgp_unlock(bgp);  /* initial reference */
  
  return 0;
}

static void bgp_free (struct bgp *);

void
bgp_lock (struct bgp *bgp)
{
  ++bgp->lock;
}

void
bgp_unlock(struct bgp *bgp)
{
  assert(bgp->lock > 0);
  if (--bgp->lock == 0)
    bgp_free (bgp);
}

static void
bgp_free (struct bgp *bgp)
{
  afi_t afi;
  safi_t safi;

  list_delete (bgp->group);
  list_delete (bgp->peer);
  list_delete (bgp->rsclient);

  if (bgp->name)
    free (bgp->name);
  
  for (afi = AFI_IP; afi < AFI_MAX; afi++)
    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
      {
	if (bgp->route[afi][safi])
          bgp_table_finish (&bgp->route[afi][safi]);
	if (bgp->aggregate[afi][safi])
          bgp_table_finish (&bgp->aggregate[afi][safi]) ;
	if (bgp->rib[afi][safi])
          bgp_table_finish (&bgp->rib[afi][safi]);
      }
  XFREE (MTYPE_BGP, bgp);
}

struct peer *
peer_lookup (struct bgp *bgp, union sockunion *su)
{
  struct peer *peer;
  struct listnode *node, *nnode;

  if (bgp != NULL)
    {
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
        if (sockunion_same (&peer->su, su)
            && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
          return peer;
    }
  else if (bm->bgp != NULL)
    {
      struct listnode *bgpnode, *nbgpnode;
  
      for (ALL_LIST_ELEMENTS (bm->bgp, bgpnode, nbgpnode, bgp))
        for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
          if (sockunion_same (&peer->su, su)
              && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
            return peer;
    }
  return NULL;
}

struct peer *
peer_lookup_with_open (union sockunion *su, as_t remote_as,
		       struct in_addr *remote_id, int *as)
{
  struct peer *peer;
  struct listnode *node;
  struct listnode *bgpnode;
  struct bgp *bgp;

  if (! bm->bgp)
    return NULL;

  for (ALL_LIST_ELEMENTS_RO (bm->bgp, bgpnode, bgp))
    {
      for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
        {
          if (sockunion_same (&peer->su, su)
              && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
            {
              if (peer->as == remote_as
                  && peer->remote_id.s_addr == remote_id->s_addr)
                return peer;
              if (peer->as == remote_as)
                *as = 1;
            }
        }

      for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
        {
          if (sockunion_same (&peer->su, su)
              &&  ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
            {
              if (peer->as == remote_as
                  && peer->remote_id.s_addr == 0)
                return peer;
              if (peer->as == remote_as)
                *as = 1;
            }
        }
    }
  return NULL;
}

/* If peer is configured at least one address family return 1. */
int
peer_active (struct peer *peer)
{
  if (peer->afc[AFI_IP][SAFI_UNICAST]
      || peer->afc[AFI_IP][SAFI_MULTICAST]
      || peer->afc[AFI_IP][SAFI_MPLS_VPN]
      || peer->afc[AFI_IP6][SAFI_UNICAST]
      || peer->afc[AFI_IP6][SAFI_MULTICAST])
    return 1;
  return 0;
}

/* If peer is negotiated at least one address family return 1. */
int
peer_active_nego (struct peer *peer)
{
  if (peer->afc_nego[AFI_IP][SAFI_UNICAST]
      || peer->afc_nego[AFI_IP][SAFI_MULTICAST]
      || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
      || peer->afc_nego[AFI_IP6][SAFI_UNICAST]
      || peer->afc_nego[AFI_IP6][SAFI_MULTICAST])
    return 1;
  return 0;
}

/* peer_flag_change_type. */
enum peer_change_type
{
  peer_change_none,
  peer_change_reset,
  peer_change_reset_in,
  peer_change_reset_out,
};

static void
peer_change_action (struct peer *peer, afi_t afi, safi_t safi,
		    enum peer_change_type type)
{
  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return;

  if (type == peer_change_reset)
    bgp_notify_send (peer, BGP_NOTIFY_CEASE,
		     BGP_NOTIFY_CEASE_CONFIG_CHANGE);
  else if (type == peer_change_reset_in)
    {
      if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV)
	  || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
	bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
      else
	bgp_notify_send (peer, BGP_NOTIFY_CEASE,
			 BGP_NOTIFY_CEASE_CONFIG_CHANGE);
    }
  else if (type == peer_change_reset_out)
    bgp_announce_route (peer, afi, safi);
}

struct peer_flag_action
{
  /* Peer's flag.  */
  u_int32_t flag;

  /* This flag can be set for peer-group member.  */
  u_char not_for_member;

  /* Action when the flag is changed.  */
  enum peer_change_type type;

  /* Peer down cause */
  u_char peer_down;
};

static const struct peer_flag_action peer_flag_action_list[] =
  {
    { PEER_FLAG_PASSIVE,                  0, peer_change_reset },
    { PEER_FLAG_SHUTDOWN,                 0, peer_change_reset },
    { PEER_FLAG_DONT_CAPABILITY,          0, peer_change_none },
    { PEER_FLAG_OVERRIDE_CAPABILITY,      0, peer_change_none },
    { PEER_FLAG_STRICT_CAP_MATCH,         0, peer_change_none },
    { PEER_FLAG_DYNAMIC_CAPABILITY,       0, peer_change_reset },
    { PEER_FLAG_DISABLE_CONNECTED_CHECK,  0, peer_change_reset },
    { 0, 0, 0 }
  };

static const struct peer_flag_action peer_af_flag_action_list[] =
  {
    { PEER_FLAG_NEXTHOP_SELF,             1, peer_change_reset_out },
    { PEER_FLAG_SEND_COMMUNITY,           1, peer_change_reset_out },
    { PEER_FLAG_SEND_EXT_COMMUNITY,       1, peer_change_reset_out },
    { PEER_FLAG_SOFT_RECONFIG,            0, peer_change_reset_in },
    { PEER_FLAG_REFLECTOR_CLIENT,         1, peer_change_reset },
    { PEER_FLAG_RSERVER_CLIENT,           1, peer_change_reset },
    { PEER_FLAG_AS_PATH_UNCHANGED,        1, peer_change_reset_out },
    { PEER_FLAG_NEXTHOP_UNCHANGED,        1, peer_change_reset_out },
    { PEER_FLAG_MED_UNCHANGED,            1, peer_change_reset_out },
    { PEER_FLAG_REMOVE_PRIVATE_AS,        1, peer_change_reset_out },
    { PEER_FLAG_ALLOWAS_IN,               0, peer_change_reset_in },
    { PEER_FLAG_ORF_PREFIX_SM,            1, peer_change_reset },
    { PEER_FLAG_ORF_PREFIX_RM,            1, peer_change_reset },
    { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED,  0, peer_change_reset_out },
    { 0, 0, 0 }
  };

/* Proper action set. */
static int
peer_flag_action_set (const struct peer_flag_action *action_list, int size,
		      struct peer_flag_action *action, u_int32_t flag)
{
  int i;
  int found = 0;
  int reset_in = 0;
  int reset_out = 0;
  const struct peer_flag_action *match = NULL;

  /* Check peer's frag action.  */
  for (i = 0; i < size; i++)
    {
      match = &action_list[i];

      if (match->flag == 0)
	break;

      if (match->flag & flag)
	{
	  found = 1;

	  if (match->type == peer_change_reset_in)
	    reset_in = 1;
	  if (match->type == peer_change_reset_out)
	    reset_out = 1;
	  if (match->type == peer_change_reset)
	    {
	      reset_in = 1;
	      reset_out = 1;
	    }
	  if (match->not_for_member)
	    action->not_for_member = 1;
	}
    }

  /* Set peer clear type.  */
  if (reset_in && reset_out)
    action->type = peer_change_reset;
  else if (reset_in)
    action->type = peer_change_reset_in;
  else if (reset_out)
    action->type = peer_change_reset_out;
  else
    action->type = peer_change_none;

  return found;
}

static void
peer_flag_modify_action (struct peer *peer, u_int32_t flag)
{
  if (flag == PEER_FLAG_SHUTDOWN)
    {
      if (CHECK_FLAG (peer->flags, flag))
	{
	  if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
	    peer_nsf_stop (peer);

	  UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
	  if (peer->t_pmax_restart)
	    {
	      BGP_TIMER_OFF (peer->t_pmax_restart);
              if (BGP_DEBUG (events, EVENTS))
		zlog_debug ("%s Maximum-prefix restart timer canceled",
			    peer->host);
	    }

      if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
	peer_nsf_stop (peer);

	  if (peer->status == Established)
	    bgp_notify_send (peer, BGP_NOTIFY_CEASE,
			     BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
	  else
	    BGP_EVENT_ADD (peer, BGP_Stop);
	}
      else
	{
	  peer->v_start = BGP_INIT_START_TIMER;
	  BGP_EVENT_ADD (peer, BGP_Stop);
	}
    }
  else if (peer->status == Established)
    {
      if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
	peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
      else if (flag == PEER_FLAG_PASSIVE)
	peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
      else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)
	peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE;

      bgp_notify_send (peer, BGP_NOTIFY_CEASE,
		       BGP_NOTIFY_CEASE_CONFIG_CHANGE);
    }
  else
    BGP_EVENT_ADD (peer, BGP_Stop);
}

/* Change specified peer flag. */
static int
peer_flag_modify (struct peer *peer, u_int32_t flag, int set)
{
  int found;
  int size;
  struct peer_group *group;
  struct listnode *node, *nnode;
  struct peer_flag_action action;

  memset (&action, 0, sizeof (struct peer_flag_action));
  size = sizeof peer_flag_action_list / sizeof (struct peer_flag_action);

  found = peer_flag_action_set (peer_flag_action_list, size, &action, flag);

  /* No flag action is found.  */
  if (! found)
    return BGP_ERR_INVALID_FLAG;    

  /* Not for peer-group member.  */
  if (action.not_for_member && peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  /* When unset the peer-group member's flag we have to check
     peer-group configuration.  */
  if (! set && peer_group_active (peer))
    if (CHECK_FLAG (peer->group->conf->flags, flag))
      {
	if (flag == PEER_FLAG_SHUTDOWN)
	  return BGP_ERR_PEER_GROUP_SHUTDOWN;
	else
	  return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;
      }

  /* Flag conflict check.  */
  if (set
      && CHECK_FLAG (peer->flags | flag, PEER_FLAG_STRICT_CAP_MATCH)
      && CHECK_FLAG (peer->flags | flag, PEER_FLAG_OVERRIDE_CAPABILITY))
    return BGP_ERR_PEER_FLAG_CONFLICT;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (set && CHECK_FLAG (peer->flags, flag) == flag)
	return 0;
      if (! set && ! CHECK_FLAG (peer->flags, flag))
	return 0;
    }

  if (set)
    SET_FLAG (peer->flags, flag);
  else
    UNSET_FLAG (peer->flags, flag);
 
  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (action.type == peer_change_reset)
	peer_flag_modify_action (peer, flag);

      return 0;
    }

  /* peer-group member updates. */
  group = peer->group;

  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (set && CHECK_FLAG (peer->flags, flag) == flag)
	continue;

      if (! set && ! CHECK_FLAG (peer->flags, flag))
	continue;

      if (set)
	SET_FLAG (peer->flags, flag);
      else
	UNSET_FLAG (peer->flags, flag);

      if (action.type == peer_change_reset)
	peer_flag_modify_action (peer, flag);
    }
  return 0;
}

int
peer_flag_set (struct peer *peer, u_int32_t flag)
{
  return peer_flag_modify (peer, flag, 1);
}

int
peer_flag_unset (struct peer *peer, u_int32_t flag)
{
  return peer_flag_modify (peer, flag, 0);
}

static int
peer_is_group_member (struct peer *peer, afi_t afi, safi_t safi)
{
  if (peer->af_group[afi][safi])
    return 1;
  return 0;
}

static int
peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag,
		     int set)
{
  int found;
  int size;
  struct listnode *node, *nnode;
  struct peer_group *group;
  struct peer_flag_action action;

  memset (&action, 0, sizeof (struct peer_flag_action));
  size = sizeof peer_af_flag_action_list / sizeof (struct peer_flag_action);
  
  found = peer_flag_action_set (peer_af_flag_action_list, size, &action, flag);
  
  /* No flag action is found.  */
  if (! found)
    return BGP_ERR_INVALID_FLAG;    

  /* Adress family must be activated.  */
  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  /* Not for peer-group member.  */
  if (action.not_for_member && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

 /* Spcecial check for reflector client.  */
  if (flag & PEER_FLAG_REFLECTOR_CLIENT
      && peer_sort (peer) != BGP_PEER_IBGP)
    return BGP_ERR_NOT_INTERNAL_PEER;

  /* Spcecial check for remove-private-AS.  */
  if (flag & PEER_FLAG_REMOVE_PRIVATE_AS
      && peer_sort (peer) == BGP_PEER_IBGP)
    return BGP_ERR_REMOVE_PRIVATE_AS;

  /* When unset the peer-group member's flag we have to check
     peer-group configuration.  */
  if (! set && peer->af_group[afi][safi])
    if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], flag))
      return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;

  /* When current flag configuration is same as requested one.  */
  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
	return 0;
      if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
	return 0;
    }

  if (set)
    SET_FLAG (peer->af_flags[afi][safi], flag);
  else
    UNSET_FLAG (peer->af_flags[afi][safi], flag);

  /* Execute action when peer is established.  */
  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
      && peer->status == Established)
    {
      if (! set && flag == PEER_FLAG_SOFT_RECONFIG)
	bgp_clear_adj_in (peer, afi, safi);
      else
       {
         if (flag == PEER_FLAG_REFLECTOR_CLIENT)
           peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
         else if (flag == PEER_FLAG_RSERVER_CLIENT)
           peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
         else if (flag == PEER_FLAG_ORF_PREFIX_SM)
           peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
         else if (flag == PEER_FLAG_ORF_PREFIX_RM)
           peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;

         peer_change_action (peer, afi, safi, action.type);
       }

    }

  /* Peer group member updates.  */
  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      group = peer->group;
      
      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
	{
	  if (! peer->af_group[afi][safi])
	    continue;

	  if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
	    continue;

	  if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
	    continue;

	  if (set)
	    SET_FLAG (peer->af_flags[afi][safi], flag);
	  else
	    UNSET_FLAG (peer->af_flags[afi][safi], flag);

	  if (peer->status == Established)
	    {
	      if (! set && flag == PEER_FLAG_SOFT_RECONFIG)
		bgp_clear_adj_in (peer, afi, safi);
	      else
               {
                 if (flag == PEER_FLAG_REFLECTOR_CLIENT)
                   peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
                 else if (flag == PEER_FLAG_RSERVER_CLIENT)
                   peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
                 else if (flag == PEER_FLAG_ORF_PREFIX_SM)
                   peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
                 else if (flag == PEER_FLAG_ORF_PREFIX_RM)
                   peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;

                 peer_change_action (peer, afi, safi, action.type);
               }
	    }
	}
    }
  return 0;
}

int
peer_af_flag_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
{
  return peer_af_flag_modify (peer, afi, safi, flag, 1);
}

int
peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
{
  return peer_af_flag_modify (peer, afi, safi, flag, 0);
}

/* EBGP multihop configuration. */
int
peer_ebgp_multihop_set (struct peer *peer, int ttl)
{
  struct peer_group *group;
  struct listnode *node, *nnode;
  struct peer *peer1;

  if (peer_sort (peer) == BGP_PEER_IBGP)
    return 0;

  /* see comment in peer_ttl_security_hops_set() */
  if (ttl != MAXTTL)
    {
      if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
        {
          group = peer->group;
          if (group->conf->gtsm_hops != 0)
            return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;

          for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
            {
              if (peer_sort (peer1) == BGP_PEER_IBGP)
                continue;

              if (peer1->gtsm_hops != 0)
                return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
            }
        }
      else
        {
          if (peer->gtsm_hops != 0)
            return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
        }
    }

  peer->ttl = ttl;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
	sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
    }
  else
    {
      group = peer->group;
      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
	{
	  if (peer_sort (peer) == BGP_PEER_IBGP)
	    continue;

	  peer->ttl = group->conf->ttl;

	  if (peer->fd >= 0)
	    sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
	}
    }
  return 0;
}

int
peer_ebgp_multihop_unset (struct peer *peer)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (peer_sort (peer) == BGP_PEER_IBGP)
    return 0;

  if (peer->gtsm_hops != 0 && peer->ttl != MAXTTL)
      return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;

  if (peer_group_active (peer))
    peer->ttl = peer->group->conf->ttl;
  else
    peer->ttl = 1;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
	sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
    }
  else
    {
      group = peer->group;
      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
	{
	  if (peer_sort (peer) == BGP_PEER_IBGP)
	    continue;

	  peer->ttl = 1;
	  
	  if (peer->fd >= 0)
	    sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
	}
    }
  return 0;
}

/* Neighbor description. */
int
peer_description_set (struct peer *peer, char *desc)
{
  if (peer->desc)
    XFREE (MTYPE_PEER_DESC, peer->desc);

  peer->desc = XSTRDUP (MTYPE_PEER_DESC, desc);

  return 0;
}

int
peer_description_unset (struct peer *peer)
{
  if (peer->desc)
    XFREE (MTYPE_PEER_DESC, peer->desc);

  peer->desc = NULL;

  return 0;
}

/* Neighbor update-source. */
int
peer_update_source_if_set (struct peer *peer, const char *ifname)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (peer->update_if)
    {
      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
	  && strcmp (peer->update_if, ifname) == 0)
	return 0;

      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
      peer->update_if = NULL;
    }

  if (peer->update_source)
    {
      sockunion_free (peer->update_source);
      peer->update_source = NULL;
    }

  peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
      return 0;
    }

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (peer->update_if)
	{
	  if (strcmp (peer->update_if, ifname) == 0)
	    continue;

	  XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
	  peer->update_if = NULL;
	}

      if (peer->update_source)
	{
	  sockunion_free (peer->update_source);
	  peer->update_source = NULL;
	}

      peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname);

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
    }
  return 0;
}

int
peer_update_source_addr_set (struct peer *peer, union sockunion *su)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (peer->update_source)
    {
      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
	  && sockunion_cmp (peer->update_source, su) == 0)
	return 0;
      sockunion_free (peer->update_source);
      peer->update_source = NULL;
    }

  if (peer->update_if)
    {
      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
      peer->update_if = NULL;
    }

  peer->update_source = sockunion_dup (su);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
      return 0;
    }

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (peer->update_source)
	{
	  if (sockunion_cmp (peer->update_source, su) == 0)
	    continue;
	  sockunion_free (peer->update_source);
	  peer->update_source = NULL;
	}

      if (peer->update_if)
	{
	  XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
	  peer->update_if = NULL;
	}

      peer->update_source = sockunion_dup (su);

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
    }
  return 0;
}

int
peer_update_source_unset (struct peer *peer)
{
  union sockunion *su;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
      && ! peer->update_source
      && ! peer->update_if)
    return 0;

  if (peer->update_source)
    {
      sockunion_free (peer->update_source);
      peer->update_source = NULL;
    }
  if (peer->update_if)
    {
      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
      peer->update_if = NULL;
    }

  if (peer_group_active (peer))
    {
      group = peer->group;

      if (group->conf->update_source)
	{
	  su = sockunion_dup (group->conf->update_source);
	  peer->update_source = su;
	}
      else if (group->conf->update_if)
	peer->update_if = 
	  XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, group->conf->update_if);
    }

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
      return 0;
    }

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (! peer->update_source && ! peer->update_if)
	continue;

      if (peer->update_source)
	{
	  sockunion_free (peer->update_source);
	  peer->update_source = NULL;
	}

      if (peer->update_if)
	{
	  XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
	  peer->update_if = NULL;
	}

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
	BGP_EVENT_ADD (peer, BGP_Stop);
    }
  return 0;
}

int
peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi,
			    const char *rmap)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  /* Adress family must be activated.  */
  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  /* Default originate can't be used for peer group memeber.  */
  if (peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)
      || (rmap && ! peer->default_rmap[afi][safi].name)
      || (rmap && strcmp (rmap, peer->default_rmap[afi][safi].name) != 0))
    { 
      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);

      if (rmap)
	{
	  if (peer->default_rmap[afi][safi].name)
	    free (peer->default_rmap[afi][safi].name);
	  peer->default_rmap[afi][safi].name = strdup (rmap);
	  peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap);
	}
    }

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established && peer->afc_nego[afi][safi])
	bgp_default_originate (peer, afi, safi, 0);
      return 0;
    }

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);

      if (rmap)
	{
	  if (peer->default_rmap[afi][safi].name)
	    free (peer->default_rmap[afi][safi].name);
	  peer->default_rmap[afi][safi].name = strdup (rmap);
	  peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap);
	}

      if (peer->status == Established && peer->afc_nego[afi][safi])
	bgp_default_originate (peer, afi, safi, 0);
    }
  return 0;
}

int
peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  /* Adress family must be activated.  */
  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  /* Default originate can't be used for peer group memeber.  */
  if (peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
    { 
      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);

      if (peer->default_rmap[afi][safi].name)
	free (peer->default_rmap[afi][safi].name);
      peer->default_rmap[afi][safi].name = NULL;
      peer->default_rmap[afi][safi].map = NULL;
    }

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established && peer->afc_nego[afi][safi])
	bgp_default_originate (peer, afi, safi, 1);
      return 0;
    }

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);

      if (peer->default_rmap[afi][safi].name)
	free (peer->default_rmap[afi][safi].name);
      peer->default_rmap[afi][safi].name = NULL;
      peer->default_rmap[afi][safi].map = NULL;

      if (peer->status == Established && peer->afc_nego[afi][safi])
	bgp_default_originate (peer, afi, safi, 1);
    }
  return 0;
}

int
peer_port_set (struct peer *peer, u_int16_t port)
{
  peer->port = port;
  return 0;
}

int
peer_port_unset (struct peer *peer)
{
  peer->port = BGP_PORT_DEFAULT;
  return 0;
}

/* neighbor weight. */
int
peer_weight_set (struct peer *peer, u_int16_t weight)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  SET_FLAG (peer->config, PEER_CONFIG_WEIGHT);
  peer->weight = weight;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      peer->weight = group->conf->weight;
    }
  return 0;
}

int
peer_weight_unset (struct peer *peer)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  /* Set default weight. */
  if (peer_group_active (peer))
    peer->weight = peer->group->conf->weight;
  else
    peer->weight = 0;

  UNSET_FLAG (peer->config, PEER_CONFIG_WEIGHT);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      peer->weight = 0;
    }
  return 0;
}

int
peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  /* Not for peer group memeber.  */
  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  /* keepalive value check.  */
  if (keepalive > 65535)
    return BGP_ERR_INVALID_VALUE;

  /* Holdtime value check.  */
  if (holdtime > 65535)
    return BGP_ERR_INVALID_VALUE;

  /* Holdtime value must be either 0 or greater than 3.  */
  if (holdtime < 3 && holdtime != 0)
    return BGP_ERR_INVALID_VALUE;

  /* Set value to the configuration. */
  SET_FLAG (peer->config, PEER_CONFIG_TIMER);
  peer->holdtime = holdtime;
  peer->keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      SET_FLAG (peer->config, PEER_CONFIG_TIMER);
      peer->holdtime = group->conf->holdtime;
      peer->keepalive = group->conf->keepalive;
    }
  return 0;
}

int
peer_timers_unset (struct peer *peer)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  /* Clear configuration. */
  UNSET_FLAG (peer->config, PEER_CONFIG_TIMER);
  peer->keepalive = 0;
  peer->holdtime = 0;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  /* peer-group member updates. */
  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      UNSET_FLAG (peer->config, PEER_CONFIG_TIMER);
      peer->holdtime = 0;
      peer->keepalive = 0;
    }

  return 0;
}

int
peer_timers_connect_set (struct peer *peer, u_int32_t connect)
{
  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  if (connect > 65535)
    return BGP_ERR_INVALID_VALUE;

  /* Set value to the configuration. */
  SET_FLAG (peer->config, PEER_CONFIG_CONNECT);
  peer->connect = connect;

  /* Set value to timer setting. */
  peer->v_connect = connect;

  return 0;
}

int
peer_timers_connect_unset (struct peer *peer)
{
  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  /* Clear configuration. */
  UNSET_FLAG (peer->config, PEER_CONFIG_CONNECT);
  peer->connect = 0;

  /* Set timer setting to default value. */
  peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;

  return 0;
}

int
peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv)
{
  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  if (routeadv > 600)
    return BGP_ERR_INVALID_VALUE;

  SET_FLAG (peer->config, PEER_CONFIG_ROUTEADV);
  peer->routeadv = routeadv;
  peer->v_routeadv = routeadv;

  return 0;
}

int
peer_advertise_interval_unset (struct peer *peer)
{
  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  UNSET_FLAG (peer->config, PEER_CONFIG_ROUTEADV);
  peer->routeadv = 0;

  if (peer_sort (peer) == BGP_PEER_IBGP)
    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
  else
    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
  
  return 0;
}

/* neighbor interface */
int
peer_interface_set (struct peer *peer, const char *str)
{
  if (peer->ifname)
    free (peer->ifname);
  peer->ifname = strdup (str);

  return 0;
}

int
peer_interface_unset (struct peer *peer)
{
  if (peer->ifname)
    free (peer->ifname);
  peer->ifname = NULL;

  return 0;
}

/* Allow-as in.  */
int
peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (allow_num < 1 || allow_num > 10)
    return BGP_ERR_INVALID_VALUE;

  if (peer->allowas_in[afi][safi] != allow_num)
    {
      peer->allowas_in[afi][safi] = allow_num;
      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
      peer_change_action (peer, afi, safi, peer_change_reset_in);
    }

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (peer->allowas_in[afi][safi] != allow_num)
	{
	  peer->allowas_in[afi][safi] = allow_num;
	  SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
	  peer_change_action (peer, afi, safi, peer_change_reset_in);
	}
	  
    }
  return 0;
}

int
peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
    {
      peer->allowas_in[afi][safi] = 0;
      peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
    }

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
	{
	  peer->allowas_in[afi][safi] = 0;
	  peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
	}
    }
  return 0;
}

int
peer_local_as_set (struct peer *peer, as_t as, int no_prepend)
{
  struct bgp *bgp = peer->bgp;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (peer_sort (peer) != BGP_PEER_EBGP
      && peer_sort (peer) != BGP_PEER_INTERNAL)
    return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP;

  if (bgp->as == as)
    return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS;

  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  if (peer->change_local_as == as &&
      ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && no_prepend)
       || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend)))
    return 0;

  peer->change_local_as = as;
  if (no_prepend)
    SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
  else
    UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
        BGP_EVENT_ADD (peer, BGP_Stop);

      return 0;
    }

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      peer->change_local_as = as;
      if (no_prepend)
	SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
      else
	UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
        BGP_EVENT_ADD (peer, BGP_Stop);
    }

  return 0;
}

int
peer_local_as_unset (struct peer *peer)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (peer_group_active (peer))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  if (! peer->change_local_as)
    return 0;

  peer->change_local_as = 0;
  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
        BGP_EVENT_ADD (peer, BGP_Stop);

      return 0;
    }

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      peer->change_local_as = 0;
      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);

      if (peer->status == Established)
       {
         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       }
      else
        BGP_EVENT_ADD (peer, BGP_Stop);
    }
  return 0;
}

/* Set password for authenticating with the peer. */
int
peer_password_set (struct peer *peer, const char *password)
{
  struct listnode *nn, *nnode;
  int len = password ? strlen(password) : 0;
  int ret = BGP_SUCCESS;

  if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN))
    return BGP_ERR_INVALID_VALUE;

  if (peer->password && strcmp (peer->password, password) == 0
      && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  if (peer->password)
    XFREE (MTYPE_PEER_PASSWORD, peer->password);
  
  peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, password);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->status == Established)
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
      else
        BGP_EVENT_ADD (peer, BGP_Stop);
        
      return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED;
    }

  for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
    {
      if (peer->password && strcmp (peer->password, password) == 0)
	continue;
      
      if (peer->password)
        XFREE (MTYPE_PEER_PASSWORD, peer->password);
      
      peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);

      if (peer->status == Established)
        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
      else
        BGP_EVENT_ADD (peer, BGP_Stop);
      
      if (bgp_md5_set (peer) < 0)
        ret = BGP_ERR_TCPSIG_FAILED;
    }

  return ret;
}

int
peer_password_unset (struct peer *peer)
{
  struct listnode *nn, *nnode;

  if (!peer->password
      && !CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  if (!CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer_group_active (peer)
	  && peer->group->conf->password
	  && strcmp (peer->group->conf->password, peer->password) == 0)
	return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;

      if (peer->status == Established)
        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
      else
        BGP_EVENT_ADD (peer, BGP_Stop);

      if (peer->password)
        XFREE (MTYPE_PEER_PASSWORD, peer->password);
      
      peer->password = NULL;
      
      bgp_md5_set (peer);

      return 0;
    }

  XFREE (MTYPE_PEER_PASSWORD, peer->password);
  peer->password = NULL;

  for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
    {
      if (!peer->password)
	continue;

      if (peer->status == Established)
        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
      else
        BGP_EVENT_ADD (peer, BGP_Stop);
      
      XFREE (MTYPE_PEER_PASSWORD, peer->password);
      peer->password = NULL;

      bgp_md5_set (peer);
    }

  return 0;
}

/* Set distribute list to the peer. */
int
peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, 
		     const char *name)
{
  struct bgp_filter *filter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != FILTER_IN && direct != FILTER_OUT)
    return BGP_ERR_INVALID_VALUE;

  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  if (filter->plist[direct].name)
    return BGP_ERR_PEER_FILTER_CONFLICT;

  if (filter->dlist[direct].name)
    free (filter->dlist[direct].name);
  filter->dlist[direct].name = strdup (name);
  filter->dlist[direct].alist = access_list_lookup (afi, name);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->dlist[direct].name)
	free (filter->dlist[direct].name);
      filter->dlist[direct].name = strdup (name);
      filter->dlist[direct].alist = access_list_lookup (afi, name);
    }

  return 0;
}

int
peer_distribute_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
{
  struct bgp_filter *filter;
  struct bgp_filter *gfilter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != FILTER_IN && direct != FILTER_OUT)
    return BGP_ERR_INVALID_VALUE;

  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  /* apply peer-group filter */
  if (peer->af_group[afi][safi])
    {
      gfilter = &peer->group->conf->filter[afi][safi];

      if (gfilter->dlist[direct].name)
	{
	  if (filter->dlist[direct].name)
	    free (filter->dlist[direct].name);
	  filter->dlist[direct].name = strdup (gfilter->dlist[direct].name);
	  filter->dlist[direct].alist = gfilter->dlist[direct].alist;
	  return 0;
	}
    }

  if (filter->dlist[direct].name)
    free (filter->dlist[direct].name);
  filter->dlist[direct].name = NULL;
  filter->dlist[direct].alist = NULL;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

    group = peer->group;
    for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
      {
	filter = &peer->filter[afi][safi];

	if (! peer->af_group[afi][safi])
	  continue;

	if (filter->dlist[direct].name)
	  free (filter->dlist[direct].name);
	filter->dlist[direct].name = NULL;
	filter->dlist[direct].alist = NULL;
      }

  return 0;
}

/* Update distribute list. */
static void
peer_distribute_update (struct access_list *access)
{
  afi_t afi;
  safi_t safi;
  int direct;
  struct listnode *mnode, *mnnode;
  struct listnode *node, *nnode;
  struct bgp *bgp;
  struct peer *peer;
  struct peer_group *group;
  struct bgp_filter *filter;

  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
    {
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
	{
	  for (afi = AFI_IP; afi < AFI_MAX; afi++)
	    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
	      {
		filter = &peer->filter[afi][safi];

		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
		  {
		    if (filter->dlist[direct].name)
		      filter->dlist[direct].alist = 
			access_list_lookup (afi, filter->dlist[direct].name);
		    else
		      filter->dlist[direct].alist = NULL;
		  }
	      }
	}
      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
	{
	  for (afi = AFI_IP; afi < AFI_MAX; afi++)
	    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
	      {
		filter = &group->conf->filter[afi][safi];

		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
		  {
		    if (filter->dlist[direct].name)
		      filter->dlist[direct].alist = 
			access_list_lookup (afi, filter->dlist[direct].name);
		    else
		      filter->dlist[direct].alist = NULL;
		  }
	      }
	}
    }
}

/* Set prefix list to the peer. */
int
peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct, 
		      const char *name)
{
  struct bgp_filter *filter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != FILTER_IN && direct != FILTER_OUT)
    return BGP_ERR_INVALID_VALUE;

  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  if (filter->dlist[direct].name)
    return BGP_ERR_PEER_FILTER_CONFLICT;

  if (filter->plist[direct].name)
    free (filter->plist[direct].name);
  filter->plist[direct].name = strdup (name);
  filter->plist[direct].plist = prefix_list_lookup (afi, name);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->plist[direct].name)
	free (filter->plist[direct].name);
      filter->plist[direct].name = strdup (name);
      filter->plist[direct].plist = prefix_list_lookup (afi, name);
    }
  return 0;
}

int
peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
{
  struct bgp_filter *filter;
  struct bgp_filter *gfilter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != FILTER_IN && direct != FILTER_OUT)
    return BGP_ERR_INVALID_VALUE;

  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  /* apply peer-group filter */
  if (peer->af_group[afi][safi])
    {
      gfilter = &peer->group->conf->filter[afi][safi];

      if (gfilter->plist[direct].name)
	{
	  if (filter->plist[direct].name)
	    free (filter->plist[direct].name);
	  filter->plist[direct].name = strdup (gfilter->plist[direct].name);
	  filter->plist[direct].plist = gfilter->plist[direct].plist;
	  return 0;
	}
    }

  if (filter->plist[direct].name)
    free (filter->plist[direct].name);
  filter->plist[direct].name = NULL;
  filter->plist[direct].plist = NULL;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->plist[direct].name)
	free (filter->plist[direct].name);
      filter->plist[direct].name = NULL;
      filter->plist[direct].plist = NULL;
    }

  return 0;
}

/* Update prefix-list list. */
static void
peer_prefix_list_update (struct prefix_list *plist)
{
  struct listnode *mnode, *mnnode;
  struct listnode *node, *nnode;
  struct bgp *bgp;
  struct peer *peer;
  struct peer_group *group;
  struct bgp_filter *filter;
  afi_t afi;
  safi_t safi;
  int direct;

  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
    {
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
	{
	  for (afi = AFI_IP; afi < AFI_MAX; afi++)
	    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
	      {
		filter = &peer->filter[afi][safi];

		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
		  {
		    if (filter->plist[direct].name)
		      filter->plist[direct].plist = 
			prefix_list_lookup (afi, filter->plist[direct].name);
		    else
		      filter->plist[direct].plist = NULL;
		  }
	      }
	}
      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
	{
	  for (afi = AFI_IP; afi < AFI_MAX; afi++)
	    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
	      {
		filter = &group->conf->filter[afi][safi];

		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
		  {
		    if (filter->plist[direct].name)
		      filter->plist[direct].plist = 
			prefix_list_lookup (afi, filter->plist[direct].name);
		    else
		      filter->plist[direct].plist = NULL;
		  }
	      }
	}
    }
}

int
peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
		 const char *name)
{
  struct bgp_filter *filter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != FILTER_IN && direct != FILTER_OUT)
    return BGP_ERR_INVALID_VALUE;

  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  if (filter->aslist[direct].name)
    free (filter->aslist[direct].name);
  filter->aslist[direct].name = strdup (name);
  filter->aslist[direct].aslist = as_list_lookup (name);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->aslist[direct].name)
	free (filter->aslist[direct].name);
      filter->aslist[direct].name = strdup (name);
      filter->aslist[direct].aslist = as_list_lookup (name);
    }
  return 0;
}

int
peer_aslist_unset (struct peer *peer,afi_t afi, safi_t safi, int direct)
{
  struct bgp_filter *filter;
  struct bgp_filter *gfilter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != FILTER_IN && direct != FILTER_OUT)
    return BGP_ERR_INVALID_VALUE;

  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  /* apply peer-group filter */
  if (peer->af_group[afi][safi])
    {
      gfilter = &peer->group->conf->filter[afi][safi];

      if (gfilter->aslist[direct].name)
	{
	  if (filter->aslist[direct].name)
	    free (filter->aslist[direct].name);
	  filter->aslist[direct].name = strdup (gfilter->aslist[direct].name);
	  filter->aslist[direct].aslist = gfilter->aslist[direct].aslist;
	  return 0;
	}
    }

  if (filter->aslist[direct].name)
    free (filter->aslist[direct].name);
  filter->aslist[direct].name = NULL;
  filter->aslist[direct].aslist = NULL;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->aslist[direct].name)
	free (filter->aslist[direct].name);
      filter->aslist[direct].name = NULL;
      filter->aslist[direct].aslist = NULL;
    }

  return 0;
}

static void
peer_aslist_update (void)
{
  afi_t afi;
  safi_t safi;
  int direct;
  struct listnode *mnode, *mnnode;
  struct listnode *node, *nnode;
  struct bgp *bgp;
  struct peer *peer;
  struct peer_group *group;
  struct bgp_filter *filter;

  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
    {
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
	{
	  for (afi = AFI_IP; afi < AFI_MAX; afi++)
	    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
	      {
		filter = &peer->filter[afi][safi];

		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
		  {
		    if (filter->aslist[direct].name)
		      filter->aslist[direct].aslist = 
			as_list_lookup (filter->aslist[direct].name);
		    else
		      filter->aslist[direct].aslist = NULL;
		  }
	      }
	}
      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
	{
	  for (afi = AFI_IP; afi < AFI_MAX; afi++)
	    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
	      {
		filter = &group->conf->filter[afi][safi];

		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
		  {
		    if (filter->aslist[direct].name)
		      filter->aslist[direct].aslist = 
			as_list_lookup (filter->aslist[direct].name);
		    else
		      filter->aslist[direct].aslist = NULL;
		  }
	      }
	}
    }
}

/* Set route-map to the peer. */
int
peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct, 
		    const char *name)
{
  struct bgp_filter *filter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != RMAP_IN && direct != RMAP_OUT &&
      direct != RMAP_IMPORT && direct != RMAP_EXPORT)
    return BGP_ERR_INVALID_VALUE;

  if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
      && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  if (filter->map[direct].name)
    free (filter->map[direct].name);
  
  filter->map[direct].name = strdup (name);
  filter->map[direct].map = route_map_lookup_by_name (name);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->map[direct].name)
	free (filter->map[direct].name);
      filter->map[direct].name = strdup (name);
      filter->map[direct].map = route_map_lookup_by_name (name);
    }
  return 0;
}

/* Unset route-map from the peer. */
int
peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
{
  struct bgp_filter *filter;
  struct bgp_filter *gfilter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (direct != RMAP_IN && direct != RMAP_OUT &&
      direct != RMAP_IMPORT && direct != RMAP_EXPORT)
    return BGP_ERR_INVALID_VALUE;

  if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
      && peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  /* apply peer-group filter */
  if (peer->af_group[afi][safi])
    {
      gfilter = &peer->group->conf->filter[afi][safi];

      if (gfilter->map[direct].name)
	{
	  if (filter->map[direct].name)
	    free (filter->map[direct].name);
	  filter->map[direct].name = strdup (gfilter->map[direct].name);
	  filter->map[direct].map = gfilter->map[direct].map;
	  return 0;
	}
    }

  if (filter->map[direct].name)
    free (filter->map[direct].name);
  filter->map[direct].name = NULL;
  filter->map[direct].map = NULL;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->map[direct].name)
	free (filter->map[direct].name);
      filter->map[direct].name = NULL;
      filter->map[direct].map = NULL;
    }
  return 0;
}

/* Set unsuppress-map to the peer. */
int
peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi, 
                         const char *name)
{
  struct bgp_filter *filter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  if (peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
      
  filter = &peer->filter[afi][safi];

  if (filter->usmap.name)
    free (filter->usmap.name);
  
  filter->usmap.name = strdup (name);
  filter->usmap.map = route_map_lookup_by_name (name);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->usmap.name)
	free (filter->usmap.name);
      filter->usmap.name = strdup (name);
      filter->usmap.map = route_map_lookup_by_name (name);
    }
  return 0;
}

/* Unset route-map from the peer. */
int
peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi)
{
  struct bgp_filter *filter;
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;
  
  if (peer_is_group_member (peer, afi, safi))
    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;

  filter = &peer->filter[afi][safi];

  if (filter->usmap.name)
    free (filter->usmap.name);
  filter->usmap.name = NULL;
  filter->usmap.map = NULL;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      filter = &peer->filter[afi][safi];

      if (! peer->af_group[afi][safi])
	continue;

      if (filter->usmap.name)
	free (filter->usmap.name);
      filter->usmap.name = NULL;
      filter->usmap.map = NULL;
    }
  return 0;
}

int
peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi,
			 u_int32_t max, u_char threshold,
			 int warning, u_int16_t restart)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
  peer->pmax[afi][safi] = max;
  peer->pmax_threshold[afi][safi] = threshold;
  peer->pmax_restart[afi][safi] = restart;
  if (warning)
    SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
  else
    UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (! peer->af_group[afi][safi])
	continue;

      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
      peer->pmax[afi][safi] = max;
      peer->pmax_threshold[afi][safi] = threshold;
      peer->pmax_restart[afi][safi] = restart;
      if (warning)
	SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
      else
	UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
    }
  return 0;
}

int
peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
{
  struct peer_group *group;
  struct listnode *node, *nnode;

  if (! peer->afc[afi][safi])
    return BGP_ERR_PEER_INACTIVE;

  /* apply peer-group config */
  if (peer->af_group[afi][safi])
    {
      if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi],
	  PEER_FLAG_MAX_PREFIX))
	SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
      else
	UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);

      if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi],
	  PEER_FLAG_MAX_PREFIX_WARNING))
	SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
      else
	UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);

      peer->pmax[afi][safi] = peer->group->conf->pmax[afi][safi];
      peer->pmax_threshold[afi][safi] = peer->group->conf->pmax_threshold[afi][safi];
      peer->pmax_restart[afi][safi] = peer->group->conf->pmax_restart[afi][safi];
      return 0;
    }

  UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
  UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
  peer->pmax[afi][safi] = 0;
  peer->pmax_threshold[afi][safi] = 0;
  peer->pmax_restart[afi][safi] = 0;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    return 0;

  group = peer->group;
  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
    {
      if (! peer->af_group[afi][safi])
	continue;

      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
      peer->pmax[afi][safi] = 0;
      peer->pmax_threshold[afi][safi] = 0;
      peer->pmax_restart[afi][safi] = 0;
    }
  return 0;
}

/* Set # of hops between us and BGP peer. */
int
peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
{
  struct peer_group *group;
  struct listnode *node, *nnode;
  struct peer *peer1;
  int ret;

  zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host);

  if (peer_sort (peer) == BGP_PEER_IBGP)
    return BGP_ERR_NO_IBGP_WITH_TTLHACK;

  /* We cannot configure ttl-security hops when ebgp-multihop is already
     set.  For non peer-groups, the check is simple.  For peer-groups, it's
     slightly messy, because we need to check both the peer-group structure
     and all peer-group members for any trace of ebgp-multihop configuration
     before actually applying the ttl-security rules.  Cisco really made a
     mess of this configuration parameter, and OpenBGPD got it right.
  */
  
  if (peer->gtsm_hops == 0) {
    if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
      {
        group = peer->group;
        if (group->conf->ttl != 1)
          return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;

        for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
          {
            if (peer_sort (peer1) == BGP_PEER_IBGP)
              continue;

            if (peer1->ttl != 1)
              return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
          }
      }
    else
      {
        if (peer->ttl != 1)
          return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
      }
    /* specify MAXTTL on outgoing packets */
    ret = peer_ebgp_multihop_set (peer, MAXTTL);
    if (ret != 0)
      return ret;
  }
  
  peer->gtsm_hops = gtsm_hops;

  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
	sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops);
    }
  else
    {
      group = peer->group;
      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
	{
	  if (peer_sort (peer) == BGP_PEER_IBGP)
	    continue;

	  peer->gtsm_hops = group->conf->gtsm_hops;

	  /* Change setting of existing peer
	   *   established then change value (may break connectivity)
	   *   not established yet (teardown session and restart)
	   *   no session then do nothing (will get handled by next connection)
	   */
	  if (peer->status == Established)
	    {
	      if (peer->fd >= 0 && peer->gtsm_hops != 0)
		sockopt_minttl (peer->su.sa.sa_family, peer->fd,
				MAXTTL + 1 - peer->gtsm_hops);
	    }
	  else if (peer->status < Established)
	    {
	      if (BGP_DEBUG (events, EVENTS))
		zlog_debug ("%s Min-ttl changed", peer->host);
	      BGP_EVENT_ADD (peer, BGP_Stop);
	    }
	}
    }

  return 0;
}

int
peer_ttl_security_hops_unset (struct peer *peer)
{
  struct peer_group *group;
  struct listnode *node, *nnode;
  struct peer *opeer;

  zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host);

  if (peer_sort (peer) == BGP_PEER_IBGP)
      return 0;

  /* if a peer-group member, then reset to peer-group default rather than 0 */
  if (peer_group_active (peer))
    peer->gtsm_hops = peer->group->conf->gtsm_hops;
  else
    peer->gtsm_hops = 0;

  opeer = peer;
  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
    {
      if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
	sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
    }
  else
    {
      group = peer->group;
      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
	{
	  if (peer_sort (peer) == BGP_PEER_IBGP)
	    continue;

	  peer->gtsm_hops = 0;
	  
	  if (peer->fd >= 0)
	    sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
	}
    }

  return peer_ebgp_multihop_unset (opeer);
}

int
peer_clear (struct peer *peer)
{
  if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
    {
      if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
	{
	  UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
	  if (peer->t_pmax_restart)
	    {
	      BGP_TIMER_OFF (peer->t_pmax_restart);
	      if (BGP_DEBUG (events, EVENTS))
		zlog_debug ("%s Maximum-prefix restart timer canceled",
			    peer->host);
	    }
	  BGP_EVENT_ADD (peer, BGP_Start);
	  return 0;
	}

      peer->v_start = BGP_INIT_START_TIMER;
      if (peer->status == Established)
	bgp_notify_send (peer, BGP_NOTIFY_CEASE,
			 BGP_NOTIFY_CEASE_ADMIN_RESET);
      else
        BGP_EVENT_ADD (peer, BGP_Stop);
    }
  return 0;
}

int
peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi,
		 enum bgp_clear_type stype)
{
  if (peer->status != Established)
    return 0;

  if (! peer->afc[afi][safi])
    return BGP_ERR_AF_UNCONFIGURED;

  if (stype == BGP_CLEAR_SOFT_RSCLIENT)
    {
      if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
        return 0;
      bgp_check_local_routes_rsclient (peer, afi, safi);
      bgp_soft_reconfig_rsclient (peer, afi, safi);
    }

  if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH)
    bgp_announce_route (peer, afi, safi);

  if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX)
    {
      if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
	  && (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)
	      || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
	{
	  struct bgp_filter *filter = &peer->filter[afi][safi];
	  u_char prefix_type;

	  if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
	    prefix_type = ORF_TYPE_PREFIX;
	  else
	    prefix_type = ORF_TYPE_PREFIX_OLD;

	  if (filter->plist[FILTER_IN].plist)
	    {
	      if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND))
		bgp_route_refresh_send (peer, afi, safi,
					prefix_type, REFRESH_DEFER, 1);
	      bgp_route_refresh_send (peer, afi, safi, prefix_type,
				      REFRESH_IMMEDIATE, 0);
	    }
	  else
	    {
	      if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND))
		bgp_route_refresh_send (peer, afi, safi,
					prefix_type, REFRESH_IMMEDIATE, 1);
	      else
		bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
	    }
	  return 0;
	}
    }

  if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH
      || stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX)
    {
      /* If neighbor has soft reconfiguration inbound flag.
	 Use Adj-RIB-In database. */
      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
	bgp_soft_reconfig_in (peer, afi, safi);
      else
	{
	  /* If neighbor has route refresh capability, send route refresh
	     message to the peer. */
	  if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV)
	      || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
	    bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
	  else
	    return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED;
	}
    }
  return 0;
}

/* Display peer uptime.*/
/* XXX: why does this function return char * when it takes buffer? */
char *
peer_uptime (time_t uptime2, char *buf, size_t len)
{
  time_t uptime1;
  struct tm *tm;

  /* Check buffer length. */
  if (len < BGP_UPTIME_LEN)
    {
      zlog_warn ("peer_uptime (): buffer shortage %lu", (u_long)len);
      /* XXX: should return status instead of buf... */
      snprintf (buf, len, "<error> "); 
      return buf;
    }

  /* If there is no connection has been done before print `never'. */
  if (uptime2 == 0)
    {
      snprintf (buf, len, "never   ");
      return buf;
    }

  /* Get current time. */
  uptime1 = bgp_clock ();
  uptime1 -= uptime2;
  tm = gmtime (&uptime1);

  /* Making formatted timer strings. */
#define ONE_DAY_SECOND 60*60*24
#define ONE_WEEK_SECOND 60*60*24*7

  if (uptime1 < ONE_DAY_SECOND)
    snprintf (buf, len, "%02d:%02d:%02d", 
	      tm->tm_hour, tm->tm_min, tm->tm_sec);
  else if (uptime1 < ONE_WEEK_SECOND)
    snprintf (buf, len, "%dd%02dh%02dm", 
	      tm->tm_yday, tm->tm_hour, tm->tm_min);
  else
    snprintf (buf, len, "%02dw%dd%02dh", 
	      tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
  return buf;
}

static void
bgp_config_write_filter (struct vty *vty, struct peer *peer,
			 afi_t afi, safi_t safi)
{
  struct bgp_filter *filter;
  struct bgp_filter *gfilter = NULL;
  char *addr;
  int in = FILTER_IN;
  int out = FILTER_OUT;

  addr = peer->host;
  filter = &peer->filter[afi][safi];
  if (peer->af_group[afi][safi])
    gfilter = &peer->group->conf->filter[afi][safi];

  /* distribute-list. */
  if (filter->dlist[in].name)
    if (! gfilter || ! gfilter->dlist[in].name
	|| strcmp (filter->dlist[in].name, gfilter->dlist[in].name) != 0)
    vty_out (vty, " neighbor %s distribute-list %s in%s", addr, 
	     filter->dlist[in].name, VTY_NEWLINE);
  if (filter->dlist[out].name && ! gfilter)
    vty_out (vty, " neighbor %s distribute-list %s out%s", addr, 
	     filter->dlist[out].name, VTY_NEWLINE);

  /* prefix-list. */
  if (filter->plist[in].name)
    if (! gfilter || ! gfilter->plist[in].name
	|| strcmp (filter->plist[in].name, gfilter->plist[in].name) != 0)
    vty_out (vty, " neighbor %s prefix-list %s in%s", addr, 
	     filter->plist[in].name, VTY_NEWLINE);
  if (filter->plist[out].name && ! gfilter)
    vty_out (vty, " neighbor %s prefix-list %s out%s", addr, 
	     filter->plist[out].name, VTY_NEWLINE);

  /* route-map. */
  if (filter->map[RMAP_IN].name)
    if (! gfilter || ! gfilter->map[RMAP_IN].name
       || strcmp (filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0)
      vty_out (vty, " neighbor %s route-map %s in%s", addr, 
              filter->map[RMAP_IN].name, VTY_NEWLINE);
  if (filter->map[RMAP_OUT].name && ! gfilter)
    vty_out (vty, " neighbor %s route-map %s out%s", addr, 
            filter->map[RMAP_OUT].name, VTY_NEWLINE);
  if (filter->map[RMAP_IMPORT].name && ! gfilter)
    vty_out (vty, " neighbor %s route-map %s import%s", addr,
        filter->map[RMAP_IMPORT].name, VTY_NEWLINE);
  if (filter->map[RMAP_EXPORT].name)
    if (! gfilter || ! gfilter->map[RMAP_EXPORT].name
    || strcmp (filter->map[RMAP_EXPORT].name,
                    gfilter->map[RMAP_EXPORT].name) != 0)
    vty_out (vty, " neighbor %s route-map %s export%s", addr,
        filter->map[RMAP_EXPORT].name, VTY_NEWLINE);

  /* unsuppress-map */
  if (filter->usmap.name && ! gfilter)
    vty_out (vty, " neighbor %s unsuppress-map %s%s", addr,
	     filter->usmap.name, VTY_NEWLINE);

  /* filter-list. */
  if (filter->aslist[in].name)
    if (! gfilter || ! gfilter->aslist[in].name
	|| strcmp (filter->aslist[in].name, gfilter->aslist[in].name) != 0)
      vty_out (vty, " neighbor %s filter-list %s in%s", addr, 
	       filter->aslist[in].name, VTY_NEWLINE);
  if (filter->aslist[out].name && ! gfilter)
    vty_out (vty, " neighbor %s filter-list %s out%s", addr, 
	     filter->aslist[out].name, VTY_NEWLINE);
}

/* BGP peer configuration display function. */
static void
bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
		       struct peer *peer, afi_t afi, safi_t safi)
{
  struct peer *g_peer = NULL;
  char buf[SU_ADDRSTRLEN];
  char *addr;

  addr = peer->host;
  if (peer_group_active (peer))
    g_peer = peer->group->conf;

  /************************************
   ****** Global to the neighbor ******
   ************************************/
  if (afi == AFI_IP && safi == SAFI_UNICAST)
    {
      /* remote-as. */
      if (! peer_group_active (peer))
	{
	  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
	    vty_out (vty, " neighbor %s peer-group%s", addr,
		     VTY_NEWLINE);
	  if (peer->as)
	    vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as,
		     VTY_NEWLINE);
	}
      else
	{
	  if (! g_peer->as)
	    vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as,
		     VTY_NEWLINE);
	  if (peer->af_group[AFI_IP][SAFI_UNICAST])
	    vty_out (vty, " neighbor %s peer-group %s%s", addr,
		     peer->group->name, VTY_NEWLINE);
	}

      /* local-as. */
      if (peer->change_local_as)
	if (! peer_group_active (peer))
	  vty_out (vty, " neighbor %s local-as %u%s%s", addr,
		   peer->change_local_as,
		   CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ?
		   " no-prepend" : "", VTY_NEWLINE);

      /* Description. */
      if (peer->desc)
	vty_out (vty, " neighbor %s description %s%s", addr, peer->desc,
		 VTY_NEWLINE);

      /* Shutdown. */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
        if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN))
	  vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE);

      /* Password. */
      if (peer->password)
	if (!peer_group_active (peer)
	    || ! g_peer->password
	    || strcmp (peer->password, g_peer->password) != 0)
	  vty_out (vty, " neighbor %s password %s%s", addr, peer->password,
		   VTY_NEWLINE);

      /* BGP port. */
      if (peer->port != BGP_PORT_DEFAULT)
	vty_out (vty, " neighbor %s port %d%s", addr, peer->port,
		 VTY_NEWLINE);

      /* Local interface name. */
      if (peer->ifname)
	vty_out (vty, " neighbor %s interface %s%s", addr, peer->ifname,
		 VTY_NEWLINE);
  
      /* Passive. */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
        if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE))
	  vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE);

      /* EBGP multihop.  */
      if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1 &&
                   !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL))
        if (! peer_group_active (peer) ||
	    g_peer->ttl != peer->ttl)
	  vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl,
		   VTY_NEWLINE);

     /* ttl-security hops */
      if (peer_sort (peer) != BGP_PEER_IBGP && peer->gtsm_hops != 0)
        if (! peer_group_active (peer) || g_peer->gtsm_hops != peer->gtsm_hops)
          vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
                   peer->gtsm_hops, VTY_NEWLINE);

      /* disable-connected-check.  */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
	if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
	  vty_out (vty, " neighbor %s disable-connected-check%s", addr, VTY_NEWLINE);

      /* Update-source. */
      if (peer->update_if)
	if (! peer_group_active (peer) || ! g_peer->update_if
	    || strcmp (g_peer->update_if, peer->update_if) != 0)
	  vty_out (vty, " neighbor %s update-source %s%s", addr,
		   peer->update_if, VTY_NEWLINE);
      if (peer->update_source)
	if (! peer_group_active (peer) || ! g_peer->update_source
	    || sockunion_cmp (g_peer->update_source,
			      peer->update_source) != 0)
	  vty_out (vty, " neighbor %s update-source %s%s", addr,
		   sockunion2str (peer->update_source, buf, SU_ADDRSTRLEN),
		   VTY_NEWLINE);

      /* advertisement-interval */
      if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV))
	vty_out (vty, " neighbor %s advertisement-interval %d%s",
		 addr, peer->v_routeadv, VTY_NEWLINE); 

      /* timers. */
      if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)
	  && ! peer_group_active (peer))
	  vty_out (vty, " neighbor %s timers %d %d%s", addr, 
	  peer->keepalive, peer->holdtime, VTY_NEWLINE);

      if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT))
	  vty_out (vty, " neighbor %s timers connect %d%s", addr, 
	  peer->connect, VTY_NEWLINE);

      /* Default weight. */
      if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT))
        if (! peer_group_active (peer) ||
	    g_peer->weight != peer->weight)
	  vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight,
		   VTY_NEWLINE);

      /* Dynamic capability.  */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
        if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
	vty_out (vty, " neighbor %s capability dynamic%s", addr,
	     VTY_NEWLINE);

      /* dont capability negotiation. */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
        if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DONT_CAPABILITY))
	vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr,
		 VTY_NEWLINE);

      /* override capability negotiation. */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
        if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
	vty_out (vty, " neighbor %s override-capability%s", addr,
		 VTY_NEWLINE);

      /* strict capability negotiation. */
      if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
        if (! peer_group_active (peer) ||
	    ! CHECK_FLAG (g_peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
	vty_out (vty, " neighbor %s strict-capability-match%s", addr,
	     VTY_NEWLINE);

      if (! peer_group_active (peer))
	{
	  if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
	    {
	      if (peer->afc[AFI_IP][SAFI_UNICAST])
		vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE);
	    }
          else
	    {
	      if (! peer->afc[AFI_IP][SAFI_UNICAST])
		vty_out (vty, " no neighbor %s activate%s", addr, VTY_NEWLINE);
	    }
	}
    }


  /************************************
   ****** Per AF to the neighbor ******
   ************************************/

  if (! (afi == AFI_IP && safi == SAFI_UNICAST))
    {
      if (peer->af_group[afi][safi])
	vty_out (vty, " neighbor %s peer-group %s%s", addr,
		 peer->group->name, VTY_NEWLINE);
      else
	vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE);
    }

  /* ORF capability.  */
  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
    if (! peer->af_group[afi][safi])
    {
      vty_out (vty, " neighbor %s capability orf prefix-list", addr);

      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
	  && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
	vty_out (vty, " both");
      else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM))
	vty_out (vty, " send");
      else
	vty_out (vty, " receive");
      vty_out (vty, "%s", VTY_NEWLINE);
    }

  /* Route reflector client. */
  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)
      && ! peer->af_group[afi][safi])
    vty_out (vty, " neighbor %s route-reflector-client%s", addr, 
	     VTY_NEWLINE);

  /* Nexthop self. */
  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)
      && ! peer->af_group[afi][safi])
    vty_out (vty, " neighbor %s next-hop-self%s", addr, VTY_NEWLINE);

  /* Remove private AS. */
  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)
      && ! peer->af_group[afi][safi])
    vty_out (vty, " neighbor %s remove-private-AS%s",
	     addr, VTY_NEWLINE);

  /* send-community print. */
  if (! peer->af_group[afi][safi])
    {
      if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
	{
	  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)
	      && peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
	    vty_out (vty, " neighbor %s send-community both%s", addr, VTY_NEWLINE);
	  else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))	
	    vty_out (vty, " neighbor %s send-community extended%s",
		     addr, VTY_NEWLINE);
	  else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY))
	    vty_out (vty, " neighbor %s send-community%s", addr, VTY_NEWLINE);
	}
      else
	{
	  if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)
	      && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
	    vty_out (vty, " no neighbor %s send-community both%s",
		     addr, VTY_NEWLINE);
	  else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
	    vty_out (vty, " no neighbor %s send-community extended%s",
		     addr, VTY_NEWLINE);
	  else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY))
	    vty_out (vty, " no neighbor %s send-community%s",
		     addr, VTY_NEWLINE);
	}
    }

  /* Default information */
  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE)
      && ! peer->af_group[afi][safi])
    {
      vty_out (vty, " neighbor %s default-originate", addr);
      if (peer->default_rmap[afi][safi].name)
	vty_out (vty, " route-map %s", peer->default_rmap[afi][safi].name);
      vty_out (vty, "%s", VTY_NEWLINE);
    }

  /* Soft reconfiguration inbound. */
  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
    if (! peer->af_group[afi][safi] ||
	! CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
    vty_out (vty, " neighbor %s soft-reconfiguration inbound%s", addr,
	     VTY_NEWLINE);

  /* maximum-prefix. */
  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
    if (! peer->af_group[afi][safi]
	|| g_peer->pmax[afi][safi] != peer->pmax[afi][safi]
        || g_peer->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi]
	|| CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)
	   != CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
      {
	vty_out (vty, " neighbor %s maximum-prefix %ld", addr, peer->pmax[afi][safi]);
	if (peer->pmax_threshold[afi][safi] != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
	  vty_out (vty, " %d", peer->pmax_threshold[afi][safi]);
	if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
	  vty_out (vty, " warning-only");
	if (peer->pmax_restart[afi][safi])
	  vty_out (vty, " restart %d", peer->pmax_restart[afi][safi]);
	vty_out (vty, "%s", VTY_NEWLINE);
      }

  /* Route server client. */
  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
      && ! peer->af_group[afi][safi])
    vty_out (vty, " neighbor %s route-server-client%s", addr, VTY_NEWLINE);

  /* Nexthop-local unchanged. */
  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)
      && ! peer->af_group[afi][safi])
    vty_out (vty, " neighbor %s nexthop-local unchanged%s", addr, VTY_NEWLINE);

  /* Allow AS in.  */
  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_ALLOWAS_IN))
    if (! peer_group_active (peer)
	|| ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN)
	|| peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi])
      {
	if (peer->allowas_in[afi][safi] == 3)
	  vty_out (vty, " neighbor %s allowas-in%s", addr, VTY_NEWLINE);
	else
	  vty_out (vty, " neighbor %s allowas-in %d%s", addr,
		   peer->allowas_in[afi][safi], VTY_NEWLINE);
      }

  /* Filter. */
  bgp_config_write_filter (vty, peer, afi, safi);

  /* atribute-unchanged. */
  if ((CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
      && ! peer->af_group[afi][safi])
    {
      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
          && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
          && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
	vty_out (vty, " neighbor %s attribute-unchanged%s", addr, VTY_NEWLINE);
      else
	vty_out (vty, " neighbor %s attribute-unchanged%s%s%s%s", addr, 
	     (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)) ?
	     " as-path" : "",
	     (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) ?
	     " next-hop" : "",
	     (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) ?
	     " med" : "", VTY_NEWLINE);
    }
}

/* Display "address-family" configuration header. */
void
bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi,
				int *write)
{
  if (*write)
    return;

  if (afi == AFI_IP && safi == SAFI_UNICAST)
    return;

  vty_out (vty, "!%s address-family ", VTY_NEWLINE);

  if (afi == AFI_IP)
    {
      if (safi == SAFI_MULTICAST)
	vty_out (vty, "ipv4 multicast");
      else if (safi == SAFI_MPLS_VPN)
	vty_out (vty, "vpnv4 unicast");
    }
  else if (afi == AFI_IP6)
    {
      vty_out (vty, "ipv6");
      
      if (safi == SAFI_MULTICAST)
        vty_out (vty, " multicast");
    }

  vty_out (vty, "%s", VTY_NEWLINE);

  *write = 1;
}

/* Address family based peer configuration display.  */
static int
bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi,
			 safi_t safi)
{
  int write = 0;
  struct peer *peer;
  struct peer_group *group;
  struct listnode *node, *nnode;

  bgp_config_write_network (vty, bgp, afi, safi, &write);

  bgp_config_write_redistribute (vty, bgp, afi, safi, &write);

  for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
    {
      if (group->conf->afc[afi][safi])
	{
	  bgp_config_write_family_header (vty, afi, safi, &write);
	  bgp_config_write_peer (vty, bgp, group->conf, afi, safi);
	}
    }
  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      if (peer->afc[afi][safi])
	{
	  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
	    {
	      bgp_config_write_family_header (vty, afi, safi, &write);
	      bgp_config_write_peer (vty, bgp, peer, afi, safi);
	    }
	}
    }

  bgp_config_write_maxpaths (vty, bgp, afi, safi, &write);

  if (write)
    vty_out (vty, " exit-address-family%s", VTY_NEWLINE);

  return write;
}

int
bgp_config_write (struct vty *vty)
{
  int write = 0;
  struct bgp *bgp;
  struct peer_group *group;
  struct peer *peer;
  struct listnode *node, *nnode;
  struct listnode *mnode, *mnnode;

  /* BGP Multiple instance. */
  if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
    {    
      vty_out (vty, "bgp multiple-instance%s", VTY_NEWLINE);
      write++;
    }

  /* BGP Config type. */
  if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
    {    
      vty_out (vty, "bgp config-type cisco%s", VTY_NEWLINE);
      write++;
    }

  /* BGP configuration. */
  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
    {
      if (write)
	vty_out (vty, "!%s", VTY_NEWLINE);

      /* Router bgp ASN */
      vty_out (vty, "router bgp %u", bgp->as);

      if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
	{
	  if (bgp->name)
	    vty_out (vty, " view %s", bgp->name);
	}
      vty_out (vty, "%s", VTY_NEWLINE);

      /* No Synchronization */
      if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
	vty_out (vty, " no synchronization%s", VTY_NEWLINE);

      /* BGP fast-external-failover. */
      if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
	vty_out (vty, " no bgp fast-external-failover%s", VTY_NEWLINE); 

      /* BGP router ID. */
      if (CHECK_FLAG (bgp->config, BGP_CONFIG_ROUTER_ID))
	vty_out (vty, " bgp router-id %s%s", inet_ntoa (bgp->router_id), 
		 VTY_NEWLINE);

      /* BGP log-neighbor-changes. */
      if (bgp_flag_check (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
	vty_out (vty, " bgp log-neighbor-changes%s", VTY_NEWLINE);

      /* BGP configuration. */
      if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED))
	vty_out (vty, " bgp always-compare-med%s", VTY_NEWLINE);

      /* BGP default ipv4-unicast. */
      if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
	vty_out (vty, " no bgp default ipv4-unicast%s", VTY_NEWLINE);

      /* BGP default local-preference. */
      if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
	vty_out (vty, " bgp default local-preference %d%s",
		 bgp->default_local_pref, VTY_NEWLINE);

      /* BGP client-to-client reflection. */
      if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
	vty_out (vty, " no bgp client-to-client reflection%s", VTY_NEWLINE);
      
      /* BGP cluster ID. */
      if (CHECK_FLAG (bgp->config, BGP_CONFIG_CLUSTER_ID))
	vty_out (vty, " bgp cluster-id %s%s", inet_ntoa (bgp->cluster_id),
		 VTY_NEWLINE);

      /* Confederation identifier*/
      if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
       vty_out (vty, " bgp confederation identifier %i%s", bgp->confed_id,
                VTY_NEWLINE);

      /* Confederation peer */
      if (bgp->confed_peers_cnt > 0)
	{
	  int i;

	  vty_out (vty, " bgp confederation peers");

         for (i = 0; i < bgp->confed_peers_cnt; i++)
           vty_out(vty, " %u", bgp->confed_peers[i]);

          vty_out (vty, "%s", VTY_NEWLINE);
	}

      /* BGP enforce-first-as. */
      if (bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS))
	vty_out (vty, " bgp enforce-first-as%s", VTY_NEWLINE);

      /* BGP deterministic-med. */
      if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
	vty_out (vty, " bgp deterministic-med%s", VTY_NEWLINE);

      /* BGP graceful-restart. */
      if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
	vty_out (vty, " bgp graceful-restart stalepath-time %d%s",
		 bgp->stalepath_time, VTY_NEWLINE);
      if (bgp_flag_check (bgp, BGP_FLAG_GRACEFUL_RESTART))
       vty_out (vty, " bgp graceful-restart%s", VTY_NEWLINE);

      /* BGP bestpath method. */
      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE))
	vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE);
      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
	vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE);
      if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID))
	vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE);
      if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)
	  || bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST))
	{
	  vty_out (vty, " bgp bestpath med");
	  if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED))
	    vty_out (vty, " confed");
	  if (bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST))
	    vty_out (vty, " missing-as-worst");
	  vty_out (vty, "%s", VTY_NEWLINE);
	}

      /* BGP network import check. */
      if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
	vty_out (vty, " bgp network import-check%s", VTY_NEWLINE);

      /* BGP scan interval. */
      bgp_config_write_scan_time (vty);

      /* BGP flag dampening. */
      if (CHECK_FLAG (bgp->af_flags[AFI_IP][SAFI_UNICAST],
	  BGP_CONFIG_DAMPENING))
	bgp_config_write_damp (vty);

      /* BGP static route configuration. */
      bgp_config_write_network (vty, bgp, AFI_IP, SAFI_UNICAST, &write);

      /* BGP redistribute configuration. */
      bgp_config_write_redistribute (vty, bgp, AFI_IP, SAFI_UNICAST, &write);

      /* BGP timers configuration. */
      if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE
	  && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
	vty_out (vty, " timers bgp %d %d%s", bgp->default_keepalive, 
		 bgp->default_holdtime, VTY_NEWLINE);

      /* peer-group */
      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
	{
	  bgp_config_write_peer (vty, bgp, group->conf, AFI_IP, SAFI_UNICAST);
	}

      /* Normal neighbor configuration. */
      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
	{
	  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
	    bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST);
	}

      /* maximum-paths */
      bgp_config_write_maxpaths (vty, bgp, AFI_IP, SAFI_UNICAST, &write);

      /* Distance configuration. */
      bgp_config_write_distance (vty, bgp);
      
      /* No auto-summary */
      if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
	vty_out (vty, " no auto-summary%s", VTY_NEWLINE);

      /* IPv4 multicast configuration.  */
      write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MULTICAST);

      /* IPv4 VPN configuration.  */
      write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MPLS_VPN);

      /* IPv6 unicast configuration.  */
      write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_UNICAST);

      /* IPv6 multicast configuration.  */
      write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MULTICAST);

      write++;
    }
  return write;
}

void
bgp_master_init (void)
{
  memset (&bgp_master, 0, sizeof (struct bgp_master));

  bm = &bgp_master;
  bm->bgp = list_new ();
  bm->listen_sockets = list_new ();
  bm->port = BGP_PORT_DEFAULT;
  bm->master = thread_master_create ();
  bm->start_time = bgp_clock ();
}


void
bgp_init (void)
{
  /* BGP VTY commands installation.  */
  bgp_vty_init ();

  /* Init kroute. */
  bgp_kroute_init ();

  /* BGP inits. */
  bgp_attr_init ();
  bgp_debug_init ();
  bgp_dump_init ();
  bgp_route_init ();
  bgp_route_map_init ();
  bgp_scan_init ();
  bgp_mplsvpn_init ();

  /* Access list initialize. */
  access_list_init ();
  access_list_add_hook (peer_distribute_update);
  access_list_delete_hook (peer_distribute_update);

  /* Filter list initialize. */
  bgp_filter_init ();
  as_list_add_hook (peer_aslist_update);
  as_list_delete_hook (peer_aslist_update);

  /* Prefix list initialize.*/
  prefix_list_init ();
  prefix_list_add_hook (peer_prefix_list_update);
  prefix_list_delete_hook (peer_prefix_list_update);

  /* Community list initialize. */
  bgp_clist = community_list_init ();

#ifdef HAVE_SNMP
  bgp_snmp_init ();
#endif /* HAVE_SNMP */
}

void
bgp_terminate (void)
{
  struct bgp *bgp;
  struct peer *peer;
  struct listnode *node, *nnode;
  struct listnode *mnode, *mnnode;

  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
    for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
      if (peer->status == Established)
          bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                           BGP_NOTIFY_CEASE_PEER_UNCONFIG);
  
  bgp_cleanup_routes ();
  
  if (bm->process_main_queue)
    {
      work_queue_free (bm->process_main_queue);
      bm->process_main_queue = NULL;
    }
  if (bm->process_rsclient_queue)
    {
      work_queue_free (bm->process_rsclient_queue);
      bm->process_rsclient_queue = NULL;
    }
}
