[quagga-dev 13579] [PATCH 09/22] BGP: Fix FSM to handle active/passive connections better

Donald Sharp sharpd at cumulusnetworks.com
Tue Nov 10 01:21:49 GMT 2015


From: Dinesh G Dutt <ddutt at cumulusnetworks.com>

The existing code didn't work well when dual connections resulted between
peers during session bringup. This patch fixes that.

Signed-off-by: Dinesh G Dutt <ddutt at cumulusnetworks.com>
---
 bgpd/bgp_advertise.c  |    1 +
 bgpd/bgp_aspath.c     |    1 +
 bgpd/bgp_clist.c      |    1 +
 bgpd/bgp_damp.c       |    1 +
 bgpd/bgp_ecommunity.c |    1 +
 bgpd/bgp_filter.c     |    1 +
 bgpd/bgp_fsm.c        |  253 ++++++++++++++++++++++++++-----
 bgpd/bgp_fsm.h        |    1 +
 bgpd/bgp_main.c       |   13 +-
 bgpd/bgp_mplsvpn.c    |    1 +
 bgpd/bgp_network.c    |  103 +++++++++----
 bgpd/bgp_network.h    |    2 +-
 bgpd/bgp_open.c       |    1 -
 bgpd/bgp_packet.c     |  255 +++++++++-----------------------
 bgpd/bgp_regex.c      |    1 +
 bgpd/bgp_route.c      |    2 +-
 bgpd/bgp_table.c      |    1 +
 bgpd/bgp_vty.c        |   28 +++-
 bgpd/bgpd.c           |  392 +++++++++++++++++++++++++++++++++++--------------
 bgpd/bgpd.h           |   10 +-
 vtysh/vtysh.c         |    1 +
 21 files changed, 689 insertions(+), 381 deletions(-)

diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c
index be1b1f3..afcbfde 100644
--- a/bgpd/bgp_advertise.c
+++ b/bgpd/bgp_advertise.c
@@ -25,6 +25,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "prefix.h"
 #include "hash.h"
 #include "thread.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_table.h"
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 0aec3ef..7bca6b6 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "log.h"
 #include "stream.h"
 #include "jhash.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_aspath.h"
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index db2cfa4..b8af86e 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -23,6 +23,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "command.h"
 #include "prefix.h"
 #include "memory.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_community.h"
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
index 05c30ff..4d4df8c 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -26,6 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "command.h"
 #include "log.h"
 #include "thread.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_damp.h"
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 04957d4..69706de 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "memory.h"
 #include "prefix.h"
 #include "command.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_ecommunity.h"
diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c
index fd8ece6..66ab5f0 100644
--- a/bgpd/bgp_filter.c
+++ b/bgpd/bgp_filter.c
@@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "log.h"
 #include "memory.h"
 #include "buffer.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_aspath.h"
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 002b094..a112185 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -70,6 +70,104 @@ bgp_start_jitter (int time)
   return ((random () % (time + 1)) - (time / 2));
 }
 
+static void
+peer_xfer_stats (struct peer *peer_dst, struct peer *peer_src)
+{
+  /* Copy stats over. These are only the pre-established state stats */
+  peer_dst->open_in += peer_src->open_in;
+  peer_dst->open_out += peer_src->open_out;
+  peer_dst->keepalive_in += peer_src->keepalive_in;
+  peer_dst->keepalive_out += peer_src->keepalive_out;
+  peer_dst->notify_in += peer_src->notify_in;
+  peer_dst->notify_out += peer_src->notify_out;
+  peer_dst->dynamic_cap_in += peer_src->dynamic_cap_in;
+  peer_dst->dynamic_cap_out += peer_src->dynamic_cap_out;
+}
+
+static struct peer *
+peer_xfer_conn(struct peer *from_peer)
+{
+  struct peer *peer;
+  afi_t afi;
+  safi_t safi;
+  int fd;
+  int status, pstatus;
+
+  assert(from_peer != NULL);
+
+  peer = from_peer->doppelganger;
+
+  if (!peer || !CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+    return from_peer;
+
+  BGP_WRITE_OFF(peer->t_write);
+  BGP_READ_OFF(peer->t_read);
+  BGP_WRITE_OFF(from_peer->t_write);
+  BGP_READ_OFF(from_peer->t_read);
+
+  fd = peer->fd;
+  peer->fd = from_peer->fd;
+  from_peer->fd = fd;
+  stream_reset(peer->ibuf);
+  stream_fifo_clean(peer->obuf);
+  stream_fifo_clean(from_peer->obuf);
+
+  peer->v_holdtime = from_peer->v_holdtime;
+  peer->v_keepalive = from_peer->v_keepalive;
+  peer->v_asorig = from_peer->v_asorig;
+  peer->routeadv = from_peer->routeadv;
+  peer->v_routeadv = from_peer->v_routeadv;
+  peer->v_gr_restart = from_peer->v_gr_restart;
+  peer->cap = from_peer->cap;
+  status = peer->status;
+  pstatus = peer->ostatus;
+  peer->status = from_peer->status;
+  peer->ostatus = from_peer->ostatus;
+  from_peer->status = status;
+  from_peer->ostatus = pstatus;
+  peer->remote_id = from_peer->remote_id;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+	peer->af_flags[afi][safi] = from_peer->af_flags[afi][safi];
+	peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
+	peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];
+	peer->afc_nego[afi][safi] = from_peer->afc_nego[afi][safi];
+	peer->afc_adv[afi][safi] = from_peer->afc_adv[afi][safi];
+	peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi];
+      }
+
+  if (bgp_getsockname(peer) < 0)
+    {
+      zlog_err ("%%bgp_getsockname() failed for %s peer %s fd %d (from_peer fd %d)",
+                (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER) ? "accept" : ""),
+                peer->host, peer->fd, from_peer->fd);
+      bgp_stop(peer);
+      bgp_stop(from_peer);
+      return NULL;
+    }
+  if (from_peer->status > Active)
+    {
+      if (bgp_getsockname(from_peer) < 0)
+        {
+          zlog_err ("%%bgp_getsockname() failed for %s from_peer %s fd %d (peer fd %d)",
+             (CHECK_FLAG (from_peer->sflags, PEER_STATUS_ACCEPT_PEER) ? "accept" : ""),
+             from_peer->host, from_peer->fd, peer->fd);
+          bgp_stop(from_peer);
+          from_peer = NULL;
+        }
+    }
+
+  BGP_READ_ON(peer->t_read, bgp_read, peer->fd);
+  BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd);
+
+  if (from_peer)
+    peer_xfer_stats(peer, from_peer);
+
+  return(peer);
+}
+
 /* Check if suppress start/restart of sessions to peer. */
 #define BGP_PEER_START_SUPPRESSED(P) \
   (CHECK_FLAG ((P)->flags, PEER_FLAG_SHUTDOWN) \
@@ -212,6 +310,7 @@ bgp_timer_set (struct peer *peer)
       BGP_TIMER_OFF (peer->t_keepalive);
       BGP_TIMER_OFF (peer->t_asorig);
       BGP_TIMER_OFF (peer->t_routeadv);
+      break;
     }
 }
 
@@ -240,6 +339,7 @@ static int
 bgp_connect_timer (struct thread *thread)
 {
   struct peer *peer;
+  int ret;
 
   peer = THREAD_ARG (thread);
   peer->t_connect = NULL;
@@ -248,10 +348,19 @@ bgp_connect_timer (struct thread *thread)
     zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)",
 	  peer->host);
 
-  THREAD_VAL (thread) = ConnectRetry_timer_expired;
-  bgp_event (thread); /* bgp_event unlocks peer */
+  if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    {
+      bgp_stop(peer);
+      ret = -1;
+    }
+  else
+    {
+      THREAD_VAL (thread) = ConnectRetry_timer_expired;
+      bgp_event (thread); /* bgp_event unlocks peer */
+      ret = 0;
+    }
 
-  return 0;
+  return ret;
 }
 
 /* BGP holdtime timer. */
@@ -668,6 +777,7 @@ bgp_update_delay_process_status_change(struct peer *peer)
 void
 bgp_fsm_change_status (struct peer *peer, int status)
 {
+
   bgp_dump_state (peer, peer->status, status);
 
   /* Transition into Clearing or Deleted must /always/ clear all routes.. 
@@ -696,6 +806,9 @@ bgp_fsm_change_status (struct peer *peer, int status)
   peer->ostatus = peer->status;
   peer->status = status;
 
+  if (status == Established)
+    UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+
   /* If update-delay processing is applicable, do the necessary. */
   if (bgp_update_delay_configured(peer->bgp) &&
       bgp_update_delay_applicable(peer->bgp))
@@ -713,7 +826,9 @@ static int
 bgp_clearing_completed (struct peer *peer)
 {
   int rc = bgp_stop(peer);
-  BGP_EVENT_FLUSH (peer);
+
+  if (rc >= 0)
+    BGP_EVENT_FLUSH (peer);
 
   return rc;
 }
@@ -726,6 +841,7 @@ bgp_stop (struct peer *peer)
   afi_t afi;
   safi_t safi;
   char orf_name[BUFSIZ];
+  int ret = 0;
 
   /* Can't do this in Clearing; events are used for state transitions */
   if (peer->status != Clearing)
@@ -832,9 +948,11 @@ bgp_stop (struct peer *peer)
 	/* Received ORF prefix-filter */
 	peer->orf_plist[afi][safi] = NULL;
 
-        /* ORF received prefix-filter pnt */
-        sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
-        prefix_bgp_orf_remove_all (afi, orf_name);
+	if ((peer->status == OpenConfirm) || (peer->status == Established))  {
+	  /* ORF received prefix-filter pnt */
+	  sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
+	  prefix_bgp_orf_remove_all (afi, orf_name);
+	}
       }
 
   /* Reset keepalive and holdtime */
@@ -862,7 +980,14 @@ bgp_stop (struct peer *peer)
   peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
 #endif /* 0 */
 
-  return 0;
+  if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE) &&
+      !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE)))
+    {
+      peer_delete(peer);
+      ret = -1;
+    }
+
+  return ret;
 }
 
 /* BGP peer is stoped by the error. */
@@ -876,9 +1001,7 @@ bgp_stop_with_error (struct peer *peer)
   if (peer->v_start >= (60 * 2))
     peer->v_start = (60 * 2);
 
-  bgp_stop (peer);
-
-  return 0;
+  return(bgp_stop (peer));
 }
 
 
@@ -889,21 +1012,10 @@ bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code)
   /* Send notify to remote peer */
   bgp_notify_send (peer, code, sub_code);
 
-  /* Sweep if it is temporary peer. */
-  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
-    {
-      zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host);
-      peer_delete (peer);
-      return -1;
-    }
-
   /* Clear start timer value to default. */
   peer->v_start = BGP_INIT_START_TIMER;
 
-  /* bgp_stop needs to be invoked while in Established state */
-  bgp_stop(peer);
-
-  return 0;
+  return(bgp_stop(peer));
 }
 
 
@@ -916,12 +1028,19 @@ bgp_connect_success (struct peer *peer)
     {
       zlog_err ("bgp_connect_success peer's fd is negative value %d",
 		peer->fd);
+      bgp_stop(peer);
       return -1;
     }
-  BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
 
-  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
-    bgp_getsockname (peer);
+  if (bgp_getsockname (peer) < 0)
+    {
+      zlog_err ("%s: bgp_getsockname(): failed for peer %s", __FUNCTION__,
+		peer->host);
+      bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); /* internal error */
+      return -1;
+    }
+
+  BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
 
   if (BGP_DEBUG (normal, NORMAL))
     {
@@ -934,8 +1053,7 @@ bgp_connect_success (struct peer *peer)
 	zlog_debug ("%s passive open", peer->host);
     }
 
-  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
-    bgp_open_send (peer);
+  bgp_open_send (peer);
 
   return 0;
 }
@@ -944,8 +1062,7 @@ bgp_connect_success (struct peer *peer)
 static int
 bgp_connect_fail (struct peer *peer)
 {
-  bgp_stop (peer);
-  return 0;
+  return (bgp_stop (peer));
 }
 
 /* This function is the first starting point of all BGP connection. It
@@ -1030,9 +1147,14 @@ bgp_start (struct peer *peer)
 static int
 bgp_reconnect (struct peer *peer)
 {
-  bgp_stop (peer);
-  bgp_start (peer);
-  return 0;
+  int ret = 0;
+
+  if (bgp_stop (peer) > 0)
+    bgp_start (peer);
+  else
+    ret = -1;
+
+  return ret;
 }
 
 static int
@@ -1093,6 +1215,19 @@ bgp_establish (struct peer *peer)
   afi_t afi;
   safi_t safi;
   int nsf_af_count = 0;
+  int ret = 0;
+  struct peer *other;
+
+  other = peer->doppelganger;
+  peer = peer_xfer_conn(peer);
+  if (!peer)
+    {
+      zlog_err ("%%Neighbor failed in xfer_conn");
+      return -1;
+    }
+
+  if (other == peer)
+    ret = 1; /* bgp_establish specific code when xfer_conn happens. */
 
   /* Reset capability open status flag. */
   if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
@@ -1196,7 +1331,19 @@ bgp_establish (struct peer *peer)
   if (!bgp_update_delay_active(peer->bgp))
     BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 0);
 
-  return 0;
+  if (peer->doppelganger && (peer->doppelganger->status != Deleted))
+    {
+      if (BGP_DEBUG (events, EVENTS))
+	zlog_debug("[Event] Deleting stub connection for peer %s", peer->host);
+
+      if (peer->doppelganger->status > Active)
+	bgp_notify_send (peer->doppelganger, BGP_NOTIFY_CEASE,
+			 BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+      else
+	peer_delete(peer->doppelganger);
+    }
+
+  return ret;
 }
 
 /* Keepalive packet is received. */
@@ -1398,19 +1545,34 @@ static const char *bgp_event_str[] =
 int
 bgp_event (struct thread *thread)
 {
-  int ret = 0;
   int event;
-  int next;
   struct peer *peer;
+  int ret;
 
   peer = THREAD_ARG (thread);
   event = THREAD_VAL (thread);
 
+  ret = bgp_event_update(peer, event);
+
+  return (ret);
+}
+
+int
+bgp_event_update (struct peer *peer, int event)
+{
+  int next;
+  int ret = 0;
+  struct peer *other;
+  int passive_conn = 0;
+
+  other = peer->doppelganger;
+  passive_conn = (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) ? 1 : 0;
+
   /* Logging this event. */
   next = FSM [peer->status -1][event - 1].next_state;
 
   if (BGP_DEBUG (fsm, FSM) && peer->status != next)
-    plog_debug (peer->log, "%s [FSM] %s (%s->%s)", peer->host, 
+    plog_debug (peer->log, "%s [FSM] %s (%s->%s)", peer->host,
 	       bgp_event_str[event],
 	       LOOKUP (bgp_status_msg, peer->status),
 	       LOOKUP (bgp_status_msg, next));
@@ -1422,13 +1584,28 @@ bgp_event (struct thread *thread)
   /* When function do not want proceed next job return -1. */
   if (ret >= 0)
     {
+      if (ret == 1 && next == Established)
+        {
+          /* The case when doppelganger swap accurred in bgp_establish.
+             Update the peer pointer accordingly */
+          peer = other;
+        }
+
       /* If status is changed. */
       if (next != peer->status)
         bgp_fsm_change_status (peer, next);
       
       /* Make sure timer is set. */
       bgp_timer_set (peer);
+
+    }
+  else if (!passive_conn && peer->bgp)
+    {
+      /* If we got a return value of -1, that means there was an error, restart
+       * the FSM. If the peer structure was deleted
+       */
+      bgp_fsm_change_status(peer, Idle);
+      bgp_timer_set(peer);
     }
-  
   return ret;
 }
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
index eecd131..a4c5a61 100644
--- a/bgpd/bgp_fsm.h
+++ b/bgpd/bgp_fsm.h
@@ -75,6 +75,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 /* Prototypes. */
 extern int bgp_event (struct thread *);
+extern int bgp_event_update (struct peer *, int event);
 extern int bgp_stop (struct peer *peer);
 extern void bgp_timer_set (struct peer *);
 extern void bgp_fsm_change_status (struct peer *peer, int status);
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 13e0dea..677d1b8 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -37,6 +37,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "plist.h"
 #include "stream.h"
 #include "vrf.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
@@ -50,6 +51,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_filter.h"
 #include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_network.h"
 
 /* bgpd options, we use GNU getopt library. */
 static const struct option longopts[] = 
@@ -225,7 +227,6 @@ bgp_exit (int status)
 {
   struct bgp *bgp;
   struct listnode *node, *nnode;
-  int *socket;
   struct interface *ifp;
   extern struct zclient *zclient;
   extern struct zclient *zlookup;
@@ -233,19 +234,13 @@ bgp_exit (int status)
   /* it only makes sense for this to be called on a clean exit */
   assert (status == 0);
 
+  bgp_close();
+
   /* reverse bgp_master_init */
   for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
     bgp_delete (bgp);
   list_free (bm->bgp);
 
-  /* reverse bgp_master_init */
-  for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, socket))
-    {
-      if (close ((int)(long)socket) == -1)
-        zlog_err ("close (%d): %s", (int)(long)socket, safe_strerror (errno));
-    }
-  list_delete (bm->listen_sockets);
-
   /* reverse bgp_zebra_init/if_init */
   if (retain_mode)
     if_add_hook (IF_DELETE_HOOK, NULL);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 8a1ed70..fdc0f7e 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -25,6 +25,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "log.h"
 #include "memory.h"
 #include "stream.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_table.h"
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 7059e8a..d2cb885 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -33,6 +33,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "network.h"
 
 #include "bgpd/bgpd.h"
+#include "bgpd/bgp_open.h"
 #include "bgpd/bgp_fsm.h"
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_debug.h"
@@ -149,7 +150,7 @@ static void
 bgp_set_socket_ttl (struct peer *peer, int bgp_sock)
 {
   char buf[INET_ADDRSTRLEN];
-  int ret;
+  int ret = 0;
 
   /* In case of peer is EBGP, we should set TTL for this connection.  */
   if (!peer->gtsm_hops && (peer_sort (peer) == BGP_PEER_EBGP))
@@ -223,48 +224,87 @@ bgp_accept (struct thread *thread)
 
   if (BGP_DEBUG (events, EVENTS))
     zlog_debug ("[Event] BGP connection from host %s", inet_sutop (&su, buf));
-  
+
   /* Check remote IP address */
   peer1 = peer_lookup (NULL, &su);
-  if (! peer1 || peer1->status == Idle)
+  if (! peer1)
     {
       if (BGP_DEBUG (events, EVENTS))
 	{
-	  if (! peer1)
-	    zlog_debug ("[Event] BGP connection IP address %s is not configured",
-		       inet_sutop (&su, buf));
-	  else
-	    zlog_debug ("[Event] BGP connection IP address %s is Idle state",
-		       inet_sutop (&su, buf));
+	  zlog_debug ("[Event] BGP connection IP address %s is not configured",
+		      inet_sutop (&su, buf));
 	}
       close (bgp_sock);
       return -1;
     }
 
+  if (CHECK_FLAG(peer1->flags, PEER_FLAG_SHUTDOWN))
+    {
+      zlog_debug ("[Event] connection from %s rejected due to admin shutdown",
+		  inet_sutop (&su, buf));
+      close (bgp_sock);
+      return -1;
+    }
+
+  /*
+   * Do not accept incoming connections in Clearing state. This can result
+   * in incorect state transitions - e.g., the connection goes back to
+   * Established and then the Clearing_Completed event is generated. Also,
+   * block incoming connection in Deleted state.
+   */
+  if (peer1->status == Clearing || peer1->status == Deleted)
+    {
+      if (BGP_DEBUG (events, EVENTS))
+        zlog_debug("[Event] Closing incoming conn for %s (%p) state %d",
+                   peer1->host, peer1, peer1->status);
+      close (bgp_sock);
+      return -1;
+    }
+
+  if (peer1->doppelganger)
+    {
+      /* We have an existing connection. Kill the existing one and run
+	 with this one.
+      */
+      if (BGP_DEBUG (events, EVENTS))
+	zlog_debug ("[Event] New active connection from peer %s, Killing"
+		    " previous active connection", peer1->host);
+      peer_delete(peer1->doppelganger);
+    }
+
   bgp_set_socket_ttl (peer1, bgp_sock);
 
-  /* Make dummy peer until read Open packet. */
-  if (BGP_DEBUG (events, EVENTS))
-    zlog_debug ("[Event] Make dummy peer structure until read Open packet");
+  peer = peer_create (&su, peer1->bgp, peer1->local_as,
+		      peer1->as, 0, 0);
 
-  {
-    char buf[SU_ADDRSTRLEN];
+  peer_xfer_config(peer, peer1);
+  UNSET_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE);
 
-    peer = peer_create_accept (peer1->bgp);
-    SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER);
-    peer->su = su;
-    peer->fd = bgp_sock;
-    peer->status = Active;
-    peer->local_id = peer1->local_id;
-    peer->v_holdtime = peer1->v_holdtime;
-    peer->v_keepalive = peer1->v_keepalive;
+  peer->doppelganger = peer1;
+  peer1->doppelganger = peer;
+  peer->fd = bgp_sock;
+  bgp_fsm_change_status(peer, Active);
+  BGP_TIMER_OFF(peer->t_start);	/* created in peer_create() */
 
-    /* Make peer's address string. */
-    sockunion2str (&su, buf, SU_ADDRSTRLEN);
-    peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
-  }
+  SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER);
 
-  BGP_EVENT_ADD (peer, TCP_connection_open);
+  /* Make dummy peer until read Open packet. */
+  if (peer1->status == Established &&
+      CHECK_FLAG (peer1->sflags, PEER_STATUS_NSF_MODE))
+    {
+      /* If we have an existing established connection with graceful restart
+       * capability announced with one or more address families, then drop
+       * existing established connection and move state to connect.
+       */
+      peer1->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+      SET_FLAG (peer1->sflags, PEER_STATUS_NSF_WAIT);
+      bgp_event_update(peer1, TCP_connection_closed);
+    }
+
+  if (peer_active (peer))
+    {
+      BGP_EVENT_ADD (peer, TCP_connection_open);
+    }
 
   return 0;
 }
@@ -414,7 +454,7 @@ bgp_connect (struct peer *peer)
 }
 
 /* After TCP connection is established.  Get local address and port. */
-void
+int
 bgp_getsockname (struct peer *peer)
 {
   if (peer->su_local)
@@ -430,9 +470,13 @@ bgp_getsockname (struct peer *peer)
     }
 
   peer->su_local = sockunion_getsockname (peer->fd);
+  if (!peer->su_local) return -1;
   peer->su_remote = sockunion_getpeername (peer->fd);
+  if (!peer->su_remote) return -1;
 
   bgp_nexthop_set (peer->su_local, peer->su_remote, &peer->nexthop, peer);
+
+  return 0;
 }
 
 
@@ -595,6 +639,9 @@ bgp_close (void)
   struct listnode *node, *next;
   struct bgp_listener *listener;
 
+  if (bm->listen_sockets == NULL)
+    return;
+
   for (ALL_LIST_ELEMENTS (bm->listen_sockets, node, next, listener))
     {
       thread_cancel (listener->thread);
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
index 1276843..403393e 100644
--- a/bgpd/bgp_network.h
+++ b/bgpd/bgp_network.h
@@ -26,7 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 extern int bgp_socket (unsigned short, const char *);
 extern void bgp_close (void);
 extern int bgp_connect (struct peer *);
-extern void bgp_getsockname (struct peer *);
+extern int bgp_getsockname (struct peer *);
 
 extern int bgp_md5_set (struct peer *);
 
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index b9d6e93..3d50910 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -815,7 +815,6 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *mp_capability)
 		    peer->host);
 
 	  if (error != error_data)
-
 	    bgp_notify_send_with_data (peer, 
 				       BGP_NOTIFY_OPEN_ERR, 
 				       BGP_NOTIFY_OPEN_UNSUP_CAPBL, 
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index dd51615..87c9fc5 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -831,6 +831,8 @@ bgp_write_notify (struct peer *peer)
   if (peer->v_start >= (60 * 2))
     peer->v_start = (60 * 2);
 
+  /* Handle Graceful Restart case where the state changes to
+     Connect instead of Idle */
   BGP_EVENT_ADD (peer, BGP_Stop);
 
   return 0;
@@ -1009,8 +1011,8 @@ bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code,
       }
     }
   else
-     zlog_info ("Notification sent to neighbor %s: configuration change",
-                peer->host);
+     zlog_info ("Notification sent to neighbor %s:%d: configuration change",
+                peer->host, peer->fd);
 
   /* Call immediately. */
   BGP_WRITE_OFF (peer->t_write);
@@ -1169,13 +1171,7 @@ static int
 bgp_collision_detect (struct peer *new, struct in_addr remote_id)
 {
   struct peer *peer;
-  struct listnode *node, *nnode;
-  struct bgp *bgp;
 
-  bgp = bgp_get_default ();
-  if (! bgp)
-    return 0;
-  
   /* Upon receipt of an OPEN message, the local system must examine
      all of its connections that are in the OpenConfirm state.  A BGP
      speaker may also examine connections in an OpenSent state if it
@@ -1185,31 +1181,42 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id)
      OPEN message, then the local system performs the following
      collision resolution procedure: */
 
-  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+  if ((peer = new->doppelganger) != NULL)
     {
-      /* Under OpenConfirm status, local peer structure already hold
-         remote router ID. */
-
-      if (peer != new
-	  && (peer->status == OpenConfirm || peer->status == OpenSent)
-	  && sockunion_same (&peer->su, &new->su))
+      /* Do not accept the new connection in Established or Clearing states.
+       * Note that a peer GR is handled by closing the existing connection
+       * upon receipt of new one.
+       */
+      if (peer->status == Established || peer->status == Clearing)
+        {
+          bgp_notify_send (new, BGP_NOTIFY_CEASE,
+                           BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+          return (-1);
+        }
+      else if ((peer->status == OpenConfirm) || (peer->status == OpenSent))
 	{
 	  /* 1. The BGP Identifier of the local system is compared to
 	     the BGP Identifier of the remote system (as specified in
 	     the OPEN message). */
 
 	  if (ntohl (peer->local_id.s_addr) < ntohl (remote_id.s_addr))
-	    {
-	      /* 2. If the value of the local BGP Identifier is less
-		 than the remote one, the local system closes BGP
-		 connection that already exists (the one that is
-		 already in the OpenConfirm state), and accepts BGP
-		 connection initiated by the remote system. */
-
-	      if (peer->fd >= 0)
-		bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
-	      return 1;
-	    }
+	    if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+	      {
+		/* 2. If the value of the local BGP Identifier is less
+		   than the remote one, the local system closes BGP
+		   connection that already exists (the one that is
+		   already in the OpenConfirm state), and accepts BGP
+		   connection initiated by the remote system. */
+		bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+				 BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+		return 1;
+	      }
+	    else
+	      {
+		bgp_notify_send (new, BGP_NOTIFY_CEASE,
+			         BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+		return -1;
+	      }
 	  else
 	    {
 	      /* 3. Otherwise, the local system closes newly created
@@ -1217,11 +1224,18 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id)
 		 received OPEN message), and continues to use the
 		 existing one (the one that is already in the
 		 OpenConfirm state). */
-
-	      if (new->fd >= 0)
-		bgp_notify_send (new, BGP_NOTIFY_CEASE, 
-			         BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
-	      return -1;
+	      if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+		{
+		  bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+				   BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+		  return 1;
+		}
+	      else
+		{
+		  bgp_notify_send (new, BGP_NOTIFY_CEASE,
+				   BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+		  return -1;
+		}
 	    }
 	}
     }
@@ -1238,14 +1252,11 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
   u_int16_t send_holdtime;
   as_t remote_as;
   as_t as4 = 0;
-  struct peer *realpeer;
   struct in_addr remote_id;
   int mp_capability;
   u_int8_t notify_data_remote_as[2];
   u_int8_t notify_data_remote_id[4];
 
-  realpeer = NULL;
-  
   /* Parse open packet. */
   version = stream_getc (peer->ibuf);
   memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2);
@@ -1294,8 +1305,8 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
         {
           zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed",
                     peer->host);
-          bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
-                 BGP_NOTIFY_OPEN_BAD_PEER_AS);
+	  bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+			   BGP_NOTIFY_OPEN_BAD_PEER_AS);
           return -1;
         }
       
@@ -1325,148 +1336,6 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
 	}
     }
 
-  /* Lookup peer from Open packet. */
-  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
-    {
-      int as = 0;
-
-      realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as);
-
-      if (! realpeer)
-	{
-	  /* Peer's source IP address is check in bgp_accept(), so this
-	     must be AS number mismatch or remote-id configuration
-	     mismatch. */
-	  if (as)
-	    {
-	      if (BGP_DEBUG (normal, NORMAL))
-		zlog_debug ("%s bad OPEN, wrong router identifier %s",
-			    peer->host, inet_ntoa (remote_id));
-	      bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, 
-					 BGP_NOTIFY_OPEN_BAD_BGP_IDENT,
-					 notify_data_remote_id, 4);
-	    }
-	  else
-	    {
-	      if (BGP_DEBUG (normal, NORMAL))
-		zlog_debug ("%s bad OPEN, remote AS is %u, expected %u",
-			    peer->host, remote_as, peer->as);
-	      bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR,
-					 BGP_NOTIFY_OPEN_BAD_PEER_AS,
-					 notify_data_remote_as, 2);
-	    }
-	  return -1;
-	}
-    }
-
-  /* When collision is detected and this peer is closed.  Retrun
-     immidiately. */
-  ret = bgp_collision_detect (peer, remote_id);
-  if (ret < 0)
-    return ret;
-
-  /* Hack part. */
-  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
-    {
-	if (realpeer->status == Established
-	    && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE))
-	{
-	  realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
-	  SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT);
-	}
-	else if (ret == 0 && realpeer->status != Active
-	         && realpeer->status != OpenSent
-		 && realpeer->status != OpenConfirm
-		 && realpeer->status != Connect)
- 	{
- 	  /* XXX: This is an awful problem.. 
- 	   *
- 	   * According to the RFC we should just let this connection (of the
- 	   * accepted 'peer') continue on to Established if the other
- 	   * connection (the 'realpeer' one) is in state Connect, and deal
- 	   * with the more larval FSM as/when it gets far enough to receive
- 	   * an Open. We don't do that though, we instead close the (more
- 	   * developed) accepted connection.
- 	   *
- 	   * This means there's a race, which if hit, can loop:
- 	   *
- 	   *       FSM for A                        FSM for B
- 	   *  realpeer     accept-peer       realpeer     accept-peer
- 	   *
- 	   *  Connect                        Connect
- 	   *               Active
- 	   *               OpenSent          OpenSent
- 	   *               <arrive here,
- 	   *               Notify, delete>   
- 	   *                                 Idle         Active
- 	   *   OpenSent                                   OpenSent
- 	   *                                              <arrive here,
- 	   *                                              Notify, delete>
- 	   *   Idle
- 	   *   <wait>                        <wait>
- 	   *   Connect                       Connect
- 	   *
-           *
- 	   * If both sides are Quagga, they're almost certain to wait for
- 	   * the same amount of time of course (which doesn't preclude other
- 	   * implementations also waiting for same time). The race is
- 	   * exacerbated by high-latency (in bgpd and/or the network).
- 	   *
- 	   * The reason we do this is because our FSM is tied to our peer
- 	   * structure, which carries our configuration information, etc. 
- 	   * I.e. we can't let the accepted-peer FSM continue on as it is,
- 	   * cause it's not associated with any actual peer configuration -
- 	   * it's just a dummy.
- 	   *
- 	   * It's possible we could hack-fix this by just bgp_stop'ing the
- 	   * realpeer and continueing on with the 'transfer FSM' below. 
- 	   * Ideally, we need to seperate FSMs from struct peer.
- 	   *
- 	   * Setting one side to passive avoids the race, as a workaround.
- 	   */
- 	  if (BGP_DEBUG (events, EVENTS))
-	    zlog_debug ("%s peer status is %s close connection",
-			realpeer->host, LOOKUP (bgp_status_msg,
-			realpeer->status));
-	  bgp_notify_send (peer, BGP_NOTIFY_CEASE,
-			   BGP_NOTIFY_CEASE_CONNECT_REJECT);
-
- 	  return -1;
- 	}
-
-      if (BGP_DEBUG (events, EVENTS))
-	zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)",
-		   peer->host, 
-		   LOOKUP (bgp_status_msg, realpeer->status));
-
-      bgp_stop (realpeer);
-      
-      /* Transfer file descriptor. */
-      realpeer->fd = peer->fd;
-      peer->fd = -1;
-
-      /* Transfer input buffer. */
-      stream_free (realpeer->ibuf);
-      realpeer->ibuf = peer->ibuf;
-      realpeer->packet_size = peer->packet_size;
-      peer->ibuf = NULL;
-
-      /* Transfer status. */
-      realpeer->status = peer->status;
-      bgp_stop (peer);
-      
-      /* peer pointer change. Open packet send to neighbor. */
-      peer = realpeer;
-      bgp_open_send (peer);
-      if (peer->fd < 0)
-	{
-	  zlog_err ("bgp_open_receive peer's fd is negative value %d",
-		    peer->fd);
-	  return -1;
-	}
-      BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
-    }
-
   /* remote router-id check. */
   if (remote_id.s_addr == 0
       || IPV4_CLASS_DE (ntohl (remote_id.s_addr))
@@ -1578,10 +1447,27 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
       peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST];
     }
 
+  /* When collision is detected and this peer is closed.  Retrun
+     immidiately. */
+  ret = bgp_collision_detect (peer, remote_id);
+  if (ret < 0)
+    return ret;
+
   /* Get sockname. */
-  bgp_getsockname (peer);
+  if ((ret = bgp_getsockname (peer)) < 0)
+    {
+      zlog_err("%s: bgp_getsockname() failed for peer: %s", __FUNCTION__,
+	       peer->host);
+      return (ret);
+    }
 
-  BGP_EVENT_ADD (peer, Receive_OPEN_message);
+  if ((ret = bgp_event_update(peer, Receive_OPEN_message)) < 0)
+    {
+      zlog_err("%s: BGP event update failed for peer: %s", __FUNCTION__,
+	       peer->host);
+      /* DD: bgp send notify and reset state */
+      return (ret);
+    }
 
   peer->packet_size = 0;
   if (peer->ibuf)
@@ -1613,7 +1499,8 @@ bgp_check_update_delay(struct bgp *bgp)
           (bgp->v_establish_wait == bgp->v_update_delay))
         for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
           {
-            if (!CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)
+            if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)
+                && !CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)
                 && !peer->update_delay_over)
               {
                 if (BGP_DEBUG (normal, NORMAL))
@@ -2755,11 +2642,5 @@ bgp_read (struct thread *thread)
     stream_reset (peer->ibuf);
 
  done:
-  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
-    {
-      if (BGP_DEBUG (events, EVENTS))
-	zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host);
-      peer_delete (peer);
-    }
   return 0;
 }
diff --git a/bgpd/bgp_regex.c b/bgpd/bgp_regex.c
index 9b65f7c..3cdfe18 100644
--- a/bgpd/bgp_regex.c
+++ b/bgpd/bgp_regex.c
@@ -23,6 +23,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "log.h"
 #include "command.h"
 #include "memory.h"
+#include "linklist.h"
 
 #include "bgpd.h"
 #include "bgp_aspath.h"
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index f72c72e..fa89b6c 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1817,7 +1817,7 @@ bgp_maximum_prefix_restart_timer (struct thread *thread)
     zlog_debug ("%s Maximum-prefix restart timer expired, restore peering",
 		peer->host);
 
-  peer_clear (peer);
+  peer_clear (peer, NULL);
 
   return 0;
 }
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index 7a6c675..e0bd4de 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "memory.h"
 #include "sockunion.h"
 #include "vty.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_table.h"
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 6e272eb..40f97ea 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -1669,6 +1669,7 @@ DEFUN (no_neighbor,
   union sockunion su;
   struct peer_group *group;
   struct peer *peer;
+  struct peer *other;
 
   ret = str2sockunion (argv[0], &su);
   if (ret < 0)
@@ -1686,7 +1687,12 @@ DEFUN (no_neighbor,
     {
       peer = peer_lookup (vty->index, &su);
       if (peer)
-        peer_delete (peer);
+	{
+	  other = peer->doppelganger;
+	  peer_delete (peer);
+	  if (other && other->status != Deleted)
+	    peer_delete(other);
+	}
     }
 
   return CMD_SUCCESS;
@@ -4486,12 +4492,16 @@ bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, safi_t safi,
   struct listnode *node, *nnode;
 
   /* Clear all neighbors. */
+  /*
+   * Pass along pointer to next node to peer_clear() when walking all nodes
+   * on the BGP instance as that may get freed if it is a doppelganger
+   */
   if (sort == clear_all)
     {
       for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
 	{
 	  if (stype == BGP_CLEAR_SOFT_NONE)
-	    ret = peer_clear (peer);
+	    ret = peer_clear (peer, &nnode);
 	  else
 	    ret = peer_clear_soft (peer, afi, safi, stype);
 
@@ -4527,7 +4537,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, safi_t safi,
 	}
 
       if (stype == BGP_CLEAR_SOFT_NONE)
-	ret = peer_clear (peer);
+	ret = peer_clear (peer, NULL);
       else
 	ret = peer_clear_soft (peer, afi, safi, stype);
 
@@ -4553,7 +4563,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, safi_t safi,
 	{
 	  if (stype == BGP_CLEAR_SOFT_NONE)
 	    {
-	      ret = peer_clear (peer);
+	      ret = peer_clear (peer, NULL);
 	      continue;
 	    }
 
@@ -4576,7 +4586,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, safi_t safi,
 	    continue;
 
 	  if (stype == BGP_CLEAR_SOFT_NONE)
-	    ret = peer_clear (peer);
+	    ret = peer_clear (peer, &nnode);
 	  else
 	    ret = peer_clear_soft (peer, afi, safi, stype);
 
@@ -4600,7 +4610,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, safi_t safi,
 
 	  find = 1;
 	  if (stype == BGP_CLEAR_SOFT_NONE)
-	    ret = peer_clear (peer);
+	    ret = peer_clear (peer, &nnode);
 	  else
 	    ret = peer_clear_soft (peer, afi, safi, stype);
 
@@ -7103,6 +7113,9 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
   
   for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
     {
+      if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+	continue;
+
       if (peer->afc[afi][safi])
 	{
           if (!count)
@@ -8136,6 +8149,9 @@ bgp_show_neighbor (struct vty *vty, struct bgp *bgp,
 
   for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
     {
+      if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+	continue;
+
       switch (type)
 	{
 	case show_all:
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index fd43044..181ae46 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -73,6 +73,44 @@ struct bgp_master *bm;
 /* BGP community-list.  */
 struct community_list_handler *bgp_clist;
 
+
+static inline void
+bgp_session_reset(struct peer *peer)
+{
+  if (peer->doppelganger && (peer->doppelganger->status != Deleted)
+      && !(CHECK_FLAG(peer->doppelganger->flags, PEER_FLAG_CONFIG_NODE)))
+    peer_delete(peer->doppelganger);
+
+  BGP_EVENT_ADD (peer, BGP_Stop);
+}
+
+/*
+ * During session reset, we may delete the doppelganger peer, which would
+ * be the next node to the current node. If the session reset was invoked
+ * during walk of peer list, we would end up accessing the freed next
+ * node. This function moves the next node along.
+ */
+static inline void
+bgp_session_reset_safe(struct peer *peer, struct listnode **nnode)
+{
+  struct listnode *n;
+  struct peer     *npeer;
+
+  n = (nnode) ? *nnode : NULL;
+  npeer = (n) ? listgetdata(n) : NULL;
+
+  if (peer->doppelganger && (peer->doppelganger->status != Deleted)
+      && !(CHECK_FLAG(peer->doppelganger->flags, PEER_FLAG_CONFIG_NODE)))
+    {
+      if (peer->doppelganger == npeer)
+        /* nnode and *nnode are confirmed to be non-NULL here */
+        *nnode = (*nnode)->next;
+      peer_delete(peer->doppelganger);
+    }
+
+  BGP_EVENT_ADD (peer, BGP_Stop);
+}
+
 /* BGP global flag manipulation.  */
 int
 bgp_option_set (int flag)
@@ -308,9 +346,8 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as)
                  bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                               BGP_NOTIFY_CEASE_CONFIG_CHANGE);
                }
-
 	      else
-		BGP_EVENT_ADD (peer, BGP_Stop);
+		bgp_session_reset_safe(peer, &nnode);
 	    }
 	}
       else
@@ -329,7 +366,7 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as)
                               BGP_NOTIFY_CEASE_CONFIG_CHANGE);
                }
 	      else
-		BGP_EVENT_ADD (peer, BGP_Stop);
+		bgp_session_reset_safe(peer, &nnode);
 	    }
 	}
     }
@@ -359,7 +396,7 @@ bgp_confederation_id_unset (struct bgp *bgp)
            }
 
 	  else
-	    BGP_EVENT_ADD (peer, BGP_Stop);
+	    bgp_session_reset_safe(peer, &nnode);
 	}
     }
   return 0;
@@ -422,7 +459,7 @@ bgp_confederation_peers_add (struct bgp *bgp, as_t as)
                                   BGP_NOTIFY_CEASE_CONFIG_CHANGE);
                }
 	      else
-	        BGP_EVENT_ADD (peer, BGP_Stop);
+		bgp_session_reset_safe(peer, &nnode);
 	    }
 	}
     }
@@ -478,7 +515,7 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as)
                                   BGP_NOTIFY_CEASE_CONFIG_CHANGE);
                }
 	      else
-		BGP_EVENT_ADD (peer, BGP_Stop);
+		bgp_session_reset_safe(peer, &nnode);
 	    }
 	}
     }
@@ -780,7 +817,7 @@ peer_unlock_with_caller (const char *name, struct peer *peer)
 
   return peer;
 }
-  
+
 /* Allocate new peer object, implicitely locked.  */
 static struct peer *
 peer_new (struct bgp *bgp)
@@ -805,11 +842,11 @@ peer_new (struct bgp *bgp)
   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);
+  peer->weight = 0;
+  peer->password = NULL;
 
   /* Set default flags.  */
   for (afi = AFI_IP; afi < AFI_MAX; afi++)
@@ -830,6 +867,7 @@ peer_new (struct bgp *bgp)
   peer->work = stream_new (BGP_MAX_PACKET_SIZE);
   peer->scratch = stream_new (BGP_MAX_PACKET_SIZE);
 
+
   bgp_sync_init (peer);
 
   /* Get service port number.  */
@@ -839,8 +877,96 @@ peer_new (struct bgp *bgp)
   return peer;
 }
 
+/*
+ * This function is invoked when a duplicate peer structure associated with
+ * a neighbor is being deleted. If this about-to-be-deleted structure is
+ * the one with all the config, then we have to copy over the info.
+ */
+void
+peer_xfer_config (struct peer *peer_dst, struct peer *peer_src)
+{
+  afi_t afi;
+  safi_t safi;
+
+  assert(peer_src);
+  assert(peer_dst);
+
+  /* The following function is used by both peer group config copy to
+   * individual peer and when we transfer config
+   */
+  if (peer_src->change_local_as)
+    peer_dst->change_local_as = peer_src->change_local_as;
+
+  /* peer flags apply */
+  peer_dst->flags = peer_src->flags;
+  peer_dst->cap = peer_src->cap;
+  peer_dst->config = peer_src->config;
+
+  peer_dst->local_as = peer_src->local_as;
+  peer_dst->ifindex = peer_src->ifindex;
+  peer_dst->port = peer_src->port;
+  peer_sort(peer_dst);
+  peer_dst->rmap_type = peer_src->rmap_type;
+
+  /* Timers */
+  peer_dst->holdtime = peer_src->holdtime;
+  peer_dst->keepalive = peer_src->keepalive;
+  peer_dst->connect = peer_src->connect;
+  peer_dst->v_holdtime = peer_src->v_holdtime;
+  peer_dst->v_keepalive = peer_src->v_keepalive;
+  peer_dst->v_asorig = peer_src->v_asorig;
+  peer_dst->routeadv = peer_src->routeadv;
+  peer_dst->v_routeadv = peer_src->v_routeadv;
+
+  /* password apply */
+  if (peer_src->password && !peer_dst->password)
+    peer_dst->password =  XSTRDUP (MTYPE_PEER_PASSWORD, peer_src->password);
+
+  bgp_md5_set (peer_dst);
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+	peer_dst->afc[afi][safi] = peer_src->afc[afi][safi];
+	peer_dst->af_flags[afi][safi] = peer_src->af_flags[afi][safi];
+	peer_dst->allowas_in[afi][safi] = peer_src->allowas_in[afi][safi];
+    }
+
+  /* update-source apply */
+  if (peer_src->update_source)
+    {
+      if (peer_dst->update_source)
+	sockunion_free (peer_dst->update_source);
+      if (peer_dst->update_if)
+	{
+	  XFREE (MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
+	  peer_dst->update_if = NULL;
+	}
+      peer_dst->update_source = sockunion_dup (peer_src->update_source);
+    }
+  else if (peer_src->update_if)
+    {
+      if (peer_dst->update_if)
+	XFREE (MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
+      if (peer_dst->update_source)
+	{
+	  sockunion_free (peer_dst->update_source);
+	  peer_dst->update_source = NULL;
+	}
+      peer_dst->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, peer_src->update_if);
+    }
+
+  if (peer_src->ifname)
+    {
+      if (peer_dst->ifname)
+	free(peer_dst->ifname);
+
+      peer_dst->ifname = strdup(peer_src->ifname);
+    }
+}
+
 /* Create new BGP peer.  */
-static struct peer *
+struct peer *
 peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
 	     as_t remote_as, afi_t afi, safi_t safi)
 {
@@ -874,6 +1000,8 @@ peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
   /* Default TTL set. */
   peer->ttl = (peer->sort == BGP_PEER_IBGP) ? 255 : 1;
 
+  SET_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE);
+
   /* Make peer's address string. */
   sockunion2str (su, buf, SU_ADDRSTRLEN);
   peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
@@ -892,7 +1020,7 @@ 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);
 
@@ -916,7 +1044,7 @@ peer_as_change (struct peer *peer, as_t as)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
     }
   type = peer_sort (peer);
   peer->as = as;
@@ -1034,7 +1162,7 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as,
 	  && afi == AFI_IP && safi == SAFI_UNICAST)
 	peer_create (su, bgp, local_as, *as, 0, 0); 
       else
-	peer_create (su, bgp, local_as, *as, afi, safi); 
+	peer = peer_create (su, bgp, local_as, *as, afi, safi);
     }
 
   return 0;
@@ -1203,12 +1331,14 @@ peer_delete (struct peer *peer)
   struct listnode *pn;
 
   assert (peer->status != Deleted);
-  
+
   bgp = peer->bgp;
 
   if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
     peer_nsf_stop (peer);
 
+  SET_FLAG(peer->flags, PEER_FLAG_DELETE);
+
   /* If this peer belongs to peer group, clear up the
      relationship.  */
   if (peer->group)
@@ -1227,6 +1357,13 @@ peer_delete (struct peer *peer)
    */
   peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
   bgp_stop (peer);
+  UNSET_FLAG(peer->flags, PEER_FLAG_DELETE);
+
+  if (peer->doppelganger)
+    peer->doppelganger->doppelganger = NULL;
+  peer->doppelganger = NULL;
+
+  UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
   bgp_fsm_change_status (peer, Deleted);
 
   /* Password configuration */
@@ -1240,7 +1377,7 @@ peer_delete (struct peer *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)))
@@ -1255,12 +1392,15 @@ peer_delete (struct peer *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);
+      if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+	{
+	  /* 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
@@ -1701,13 +1841,21 @@ peer_group_delete (struct peer_group *group)
 {
   struct bgp *bgp;
   struct peer *peer;
+  struct peer *other;
   struct listnode *node, *nnode;
 
   bgp = group->bgp;
 
   for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
     {
+      other = peer->doppelganger;
+      peer->group = NULL;
       peer_delete (peer);
+      if (other && other->status != Deleted)
+	{
+	  other->group = NULL;
+	  peer_delete(other);
+	}
     }
   list_delete (group->peer);
 
@@ -1728,7 +1876,7 @@ peer_group_delete (struct peer_group *group)
 int
 peer_group_remote_as_delete (struct peer_group *group)
 {
-  struct peer *peer;
+  struct peer *peer, *other;
   struct listnode *node, *nnode;
 
   if (! group->conf->as)
@@ -1736,7 +1884,16 @@ peer_group_remote_as_delete (struct peer_group *group)
 
   for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
     {
+      other = peer->doppelganger;
+
+      peer->group = NULL;
       peer_delete (peer);
+
+      if (other && other->status != Deleted)
+	{
+	  other->group = NULL;
+	  peer_delete(other);
+	}
     }
   list_delete_all_node (group->peer);
 
@@ -1773,6 +1930,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
       peer = peer_lock (peer); /* group->peer list reference */
       listnode_add (group->peer, peer);
       peer_group2peer_config_copy (group, peer, afi, safi);
+      SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
 
       return 0;
     }
@@ -1879,6 +2037,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
     }
 
   peer_group2peer_config_copy (group, peer, afi, safi);
+  SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
 
   if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
     {
@@ -1887,7 +2046,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
                       BGP_NOTIFY_CEASE_CONFIG_CHANGE);
     }
   else
-    BGP_EVENT_ADD (peer, BGP_Stop);
+    bgp_session_reset(peer);
 
   return 0;
 }
@@ -1896,6 +2055,8 @@ int
 peer_group_unbind (struct bgp *bgp, struct peer *peer,
 		   struct peer_group *group, afi_t afi, safi_t safi)
 {
+  struct peer *other;
+
   if (! peer->af_group[afi][safi])
       return 0;
 
@@ -1915,9 +2076,20 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer,
       peer_unlock (peer); /* peer group list reference */
       listnode_delete (group->peer, peer);
       peer->group = NULL;
+      other = peer->doppelganger;
       if (group->conf->as)
 	{
 	  peer_delete (peer);
+	  if (other && other->status != Deleted)
+	    {
+	      if (other->group)
+		{
+		  peer_unlock(other);
+		  listnode_delete(group->peer, other);
+		}
+	      other->group = NULL;
+	      peer_delete(other);
+	    }
 	  return 0;
 	}
       peer_global_config_reset (peer);
@@ -1930,7 +2102,7 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer,
                       BGP_NOTIFY_CEASE_CONFIG_CHANGE);
     }
   else
-    BGP_EVENT_ADD (peer, BGP_Stop);
+    bgp_session_reset(peer);
 
   return 0;
 }
@@ -2125,17 +2297,6 @@ bgp_delete (struct bgp *bgp)
       if (i != ZEBRA_ROUTE_BGP)
 	bgp_redistribute_unset (bgp, afi, i);
 
-  for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer))
-    {
-      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
-	{
-	  /* Send notify to remote peer. */
-	  bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
-	}
-
-      peer_delete (peer);
-    }
-
   for (ALL_LIST_ELEMENTS (bgp->group, node, next, group))
     {
       for (ALL_LIST_ELEMENTS (group->peer, pnode, pnext, peer))
@@ -2149,6 +2310,17 @@ bgp_delete (struct bgp *bgp)
       peer_group_delete (group);
     }
 
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+	{
+	  /* Send notify to remote peer. */
+	  bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+	}
+
+      peer_delete (peer);
+    }
+
   assert (listcount (bgp->rsclient) == 0);
 
   if (bgp->peer_self) {
@@ -2220,7 +2392,7 @@ peer_lookup (struct bgp *bgp, union sockunion *su)
     {
       for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
         if (sockunion_same (&peer->su, su)
-            && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+            && (CHECK_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE)))
           return peer;
     }
   else if (bm->bgp != NULL)
@@ -2230,55 +2402,12 @@ peer_lookup (struct bgp *bgp, union sockunion *su)
       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))
+              && (CHECK_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE)))
             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)
@@ -2325,16 +2454,31 @@ peer_change_action (struct peer *peer, afi_t afi, safi_t safi,
     return;
 
   if (type == peer_change_reset)
-    bgp_notify_send (peer, BGP_NOTIFY_CEASE,
-		     BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+    {
+      /* If we're resetting session, we've to delete both peer struct */
+      if ((peer->doppelganger) && (peer->doppelganger->status != Deleted)
+	  && (!CHECK_FLAG(peer->doppelganger->flags,
+			  PEER_FLAG_CONFIG_NODE)))
+	peer_delete(peer->doppelganger);
+
+      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);
+	{
+	  if ((peer->doppelganger) && (peer->doppelganger->status != Deleted)
+	      && (!CHECK_FLAG(peer->doppelganger->flags,
+			      PEER_FLAG_CONFIG_NODE)))
+	    peer_delete(peer->doppelganger);
+
+	  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);
@@ -2456,14 +2600,14 @@ peer_flag_modify_action (struct peer *peer, u_int32_t flag)
 			    peer->host);
 	    }
 
-      if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
-	peer_nsf_stop (peer);
+          if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+            peer_nsf_stop (peer);
 
 	  if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
 	    bgp_notify_send (peer, BGP_NOTIFY_CEASE,
 			     BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
 	  else
-	    BGP_EVENT_ADD (peer, BGP_Stop);
+            bgp_session_reset(peer);
 	}
       else
 	{
@@ -2484,7 +2628,7 @@ peer_flag_modify_action (struct peer *peer, u_int32_t flag)
 		       BGP_NOTIFY_CEASE_CONFIG_CHANGE);
     }
   else
-    BGP_EVENT_ADD (peer, BGP_Stop);
+    bgp_session_reset(peer);
 }
 
 /* Change specified peer flag. */
@@ -2881,7 +3025,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
       return 0;
     }
 
@@ -2913,7 +3057,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
     }
   return 0;
 }
@@ -2937,6 +3081,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su)
     {
       XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
       peer->update_if = NULL;
+
     }
 
   peer->update_source = sockunion_dup (su);
@@ -2950,7 +3095,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
       return 0;
     }
 
@@ -2981,7 +3126,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
     }
   return 0;
 }
@@ -3032,7 +3177,7 @@ peer_update_source_unset (struct peer *peer)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
       return 0;
     }
 
@@ -3062,7 +3207,7 @@ peer_update_source_unset (struct peer *peer)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-	BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
     }
   return 0;
 }
@@ -3563,8 +3708,7 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
-
+	bgp_session_reset(peer);
       return 0;
     }
 
@@ -3639,7 +3783,7 @@ peer_local_as_unset (struct peer *peer)
                           BGP_NOTIFY_CEASE_CONFIG_CHANGE);
        }
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset(peer);
     }
   return 0;
 }
@@ -3669,7 +3813,7 @@ peer_password_set (struct peer *peer, const char *password)
       if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
+        bgp_session_reset(peer);
         
       return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED;
     }
@@ -3687,7 +3831,7 @@ peer_password_set (struct peer *peer, const char *password)
       if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
+        bgp_session_reset(peer);
       
       if (bgp_md5_set (peer) < 0)
         ret = BGP_ERR_TCPSIG_FAILED;
@@ -3715,7 +3859,7 @@ peer_password_unset (struct peer *peer)
       if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
+        bgp_session_reset(peer);
 
       if (peer->password)
         XFREE (MTYPE_PEER_PASSWORD, peer->password);
@@ -3738,7 +3882,7 @@ peer_password_unset (struct peer *peer)
       if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
+        bgp_session_reset(peer);
       
       XFREE (MTYPE_PEER_PASSWORD, peer->password);
       peer->password = NULL;
@@ -4571,7 +4715,11 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
   if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
     {
       if (peer->fd >= 0)
-	sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops);
+        sockopt_minttl (peer->su.sa.sa_family, peer->fd,
+                        MAXTTL + 1 - gtsm_hops);
+      if (peer->status != Established && peer->doppelganger)
+        sockopt_minttl (peer->su.sa.sa_family, peer->doppelganger->fd,
+                        MAXTTL + 1 - gtsm_hops);
     }
   else
     {
@@ -4595,7 +4743,7 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
 	    {
 	      if (BGP_DEBUG (events, EVENTS))
 		zlog_debug ("%s Min-ttl changed", peer->host);
-	      BGP_EVENT_ADD (peer, BGP_Stop);
+              bgp_session_reset(peer);
 	    }
 	}
     }
@@ -4623,6 +4771,9 @@ peer_ttl_security_hops_unset (struct peer *peer)
     {
       if (peer->fd >= 0)
 	sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
+
+      if (peer->status != Established && peer->doppelganger)
+        sockopt_minttl (peer->su.sa.sa_family, peer->doppelganger->fd, 0);
     }
   else
     {
@@ -4633,14 +4784,24 @@ peer_ttl_security_hops_unset (struct peer *peer)
 	  
 	  if (peer->fd >= 0)
 	    sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
+
+          if (peer->status != Established && peer->doppelganger)
+            sockopt_minttl (peer->su.sa.sa_family, peer->doppelganger->fd, 0);
 	}
     }
 
   return peer_ebgp_multihop_unset (opeer);
 }
 
+/*
+ * If peer clear is invoked in a loop for all peers on the BGP instance,
+ * it may end up freeing the doppelganger, and if this was the next node
+ * to the current node, we would end up accessing the freed next node.
+ * Pass along additional parameter which can be updated if next node
+ * is freed; only required when walking the peer list on BGP instance.
+ */
 int
-peer_clear (struct peer *peer)
+peer_clear (struct peer *peer, struct listnode **nnode)
 {
   if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
     {
@@ -4663,7 +4824,7 @@ peer_clear (struct peer *peer)
 	bgp_notify_send (peer, BGP_NOTIFY_CEASE,
 			 BGP_NOTIFY_CEASE_ADMIN_RESET);
       else
-        BGP_EVENT_ADD (peer, BGP_Stop);
+	bgp_session_reset_safe(peer, nnode);
     }
   return 0;
 }
@@ -5268,7 +5429,7 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi,
     {
       if (peer->afc[afi][safi])
 	{
-	  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+	  if (CHECK_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE))
 	    {
 	      bgp_config_write_family_header (vty, afi, safi, &write);
 	      bgp_config_write_peer (vty, bgp, peer, afi, safi);
@@ -5457,7 +5618,7 @@ bgp_config_write (struct vty *vty)
       /* Normal neighbor configuration. */
       for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
 	{
-	  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+	  if (CHECK_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE))
 	    bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST);
 	}
 
@@ -5553,12 +5714,25 @@ bgp_terminate (void)
   struct listnode *node, *nnode;
   struct listnode *mnode, *mnnode;
 
+  /* Close the listener sockets first as this prevents peers from attempting
+   * to reconnect on receiving the peer unconfig message. In the presence
+   * of a large number of peers this will ensure that no peer is left with
+   * a dangling connection
+   */
+  /* reverse bgp_master_init */
+  bgp_close();
+  if (bm->listen_sockets)
+    list_free(bm->listen_sockets);
+  bm->listen_sockets = NULL;
+
   for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
     for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
-      if (peer->status == Established)
+      if (peer->status == Established ||
+          peer->status == OpenSent ||
+          peer->status == OpenConfirm)
           bgp_notify_send (peer, BGP_NOTIFY_CEASE,
                            BGP_NOTIFY_CEASE_PEER_UNCONFIG);
-  
+
   bgp_cleanup_routes ();
   
   if (bm->process_main_queue)
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 6f4d6cd..e0d6e97 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -347,6 +347,9 @@ struct peer
    */
   struct stream *scratch;
 
+  /* the doppelganger peer structure, due to dual TCP conn setup */
+  struct peer *doppelganger;
+
   /* Status of the peer. */
   int status;
   int ostatus;
@@ -420,6 +423,8 @@ struct peer
 #define PEER_FLAG_DISABLE_CONNECTED_CHECK   (1 << 6) /* disable-connected-check */
 #define PEER_FLAG_LOCAL_AS_NO_PREPEND       (1 << 7) /* local-as no-prepend */
 #define PEER_FLAG_LOCAL_AS_REPLACE_AS       (1 << 8) /* local-as no-prepend replace-as */
+#define PEER_FLAG_DELETE		    (1 << 9) /* mark the peer for deleting */
+#define PEER_FLAG_CONFIG_NODE		    (1 << 10) /* the node to update configs on */
 
   /* NSF mode (graceful restart) */
   u_char nsf[AFI_MAX][SAFI_MAX];
@@ -895,7 +900,10 @@ extern struct peer *peer_unlock_with_caller(const char *, struct peer *);
 extern bgp_peer_sort_t peer_sort (struct peer *peer);
 extern int peer_active (struct peer *);
 extern int peer_active_nego (struct peer *);
+extern struct peer *peer_create(union sockunion *su, struct bgp *bgp, as_t local_as,
+			        as_t remote_as, afi_t afi, safi_t safi);
 extern struct peer *peer_create_accept (struct bgp *);
+extern void peer_xfer_config (struct peer *dst, struct peer *src);
 extern char *peer_uptime (time_t, char *, size_t);
 extern int bgp_config_write (struct vty *);
 extern void bgp_config_write_family_header (struct vty *, afi_t, safi_t, int *);
@@ -1021,7 +1029,7 @@ extern int peer_unsuppress_map_unset (struct peer *, afi_t, safi_t);
 extern int peer_maximum_prefix_set (struct peer *, afi_t, safi_t, u_int32_t, u_char, int, u_int16_t);
 extern int peer_maximum_prefix_unset (struct peer *, afi_t, safi_t);
 
-extern int peer_clear (struct peer *);
+extern int peer_clear (struct peer *, struct listnode **);
 extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type);
 
 extern int peer_ttl_security_hops_set (struct peer *, int);
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 1fe3f82..a969f1c 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -34,6 +34,7 @@
 #include "memory.h"
 #include "vtysh/vtysh.h"
 #include "log.h"
+#include "linklist.h"
 #include "bgpd/bgp_vty.h"
 #include "vrf.h"
 
-- 
1.7.10.4





More information about the Quagga-dev mailing list