return ((rand () % (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) \
BGP_TIMER_OFF (peer->t_keepalive);
BGP_TIMER_OFF (peer->t_asorig);
BGP_TIMER_OFF (peer->t_routeadv);
+ break;
}
}
bgp_connect_timer (struct thread *thread)
{
struct peer *peer;
+ int ret;
peer = THREAD_ARG (thread);
peer->t_connect = NULL;
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. */
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..
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))
bgp_clearing_completed (struct peer *peer)
{
int rc = bgp_stop(peer);
- BGP_EVENT_FLUSH (peer);
+
+ if (rc >= 0)
+ BGP_EVENT_FLUSH (peer);
return rc;
}
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)
/* 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 (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 (orf_name);
+ }
}
/* Reset keepalive and holdtime */
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. */
if (peer->v_start >= (60 * 2))
peer->v_start = (60 * 2);
- bgp_stop (peer);
-
- return 0;
+ return(bgp_stop (peer));
}
/* 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));
}
static int
bgp_connect_success (struct peer *peer)
{
+ int ret = 0;
+
if (peer->fd < 0)
{
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))
{
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;
}
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
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
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))
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. */
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));
/* 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;
}
/* 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);
/* 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);
#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"
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))
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)
+ {
+ struct bgp *bgp = peer1->bgp;
+
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug("[Event] Closing incoming conn for %s (0x%x) 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;
}
}
/* After TCP connection is established. Get local address and port. */
-void
+int
bgp_getsockname (struct peer *peer)
{
if (peer->su_local)
}
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;
}
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);
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 *);
peer->host);
if (error != error_data)
-
bgp_notify_send_with_data (peer,
BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNSUP_CAPBL,
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;
}
}
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);
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
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
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;
+ }
}
}
}
u_int16_t send_holdtime;
as_t remote_as;
as_t as4 = 0;
- struct peer *realpeer;
+ struct peer *active_peer = NULL;
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);
{
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;
}
}
}
- /* 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))
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)
(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))
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;
}
zlog_debug ("%s Maximum-prefix restart timer expired, restore peering",
peer->host);
- peer_clear (peer);
+ peer_clear (peer, NULL);
return 0;
}
union sockunion su;
struct peer_group *group;
struct peer *peer;
+ struct peer *other;
ret = str2sockunion (argv[0], &su);
if (ret < 0)
{
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;
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);
}
if (stype == BGP_CLEAR_SOFT_NONE)
- ret = peer_clear (peer);
+ ret = peer_clear (peer, NULL);
else
ret = peer_clear_soft (peer, afi, safi, stype);
{
if (stype == BGP_CLEAR_SOFT_NONE)
{
- ret = peer_clear (peer);
+ ret = peer_clear (peer, NULL);
continue;
}
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);
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);
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 (delimit)
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
switch (type)
{
case show_all:
/* 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)
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
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset_safe(peer, &nnode);
}
}
}
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset_safe(peer, &nnode);
}
}
return 0;
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset_safe(peer, &nnode);
}
}
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset_safe(peer, &nnode);
}
}
}
return peer;
}
-
+
/* Allocate new peer object, implicitely locked. */
static struct peer *
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++)
peer->work = stream_new (BGP_MAX_PACKET_SIZE);
peer->scratch = stream_new (BGP_MAX_PACKET_SIZE);
+
bgp_sync_init (peer);
/* Get service port number. */
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)
{
/* 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);
struct peer *peer;
peer = peer_new (bgp);
-
+
peer = peer_lock (peer); /* bgp peer list reference */
listnode_add_sort (bgp->peer, peer);
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
}
type = peer_sort (peer);
peer->as = as;
&& 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);
+ peer = peer_create (su, bgp, local_as, *as, afi, safi);
}
return 0;
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)
*/
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 */
}
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); /* 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
{
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);
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)
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);
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;
}
}
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))
{
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
return 0;
}
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;
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);
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
return 0;
}
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, node, next, peer))
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) {
{
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)
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)
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);
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
{
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
}
/* Change specified peer flag. */
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
return 0;
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
}
return 0;
}
{
XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
peer->update_if = NULL;
+
}
peer->update_source = sockunion_dup (su);
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
return 0;
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
}
return 0;
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
return 0;
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
}
return 0;
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
+ bgp_session_reset(peer);
return 0;
}
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(peer);
}
return 0;
}
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;
}
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;
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);
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;
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
{
{
if (BGP_DEBUG (events, EVENTS))
zlog_debug ("%s Min-ttl changed", peer->host);
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_session_reset(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
{
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))
{
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;
}
{
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);
/* 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);
}
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)
*/
struct stream *scratch;
+ /* the doppelganger peer structure, due to dual TCP conn setup */
+ struct peer *doppelganger;
+
/* Status of the peer. */
int status;
int ostatus;
#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];
extern struct peer *peer_lookup (struct bgp *, union sockunion *);
extern struct peer_group *peer_group_lookup (struct bgp *, const char *);
extern struct peer_group *peer_group_get (struct bgp *, const char *);
-extern struct peer *peer_lookup_with_open (union sockunion *, as_t, struct in_addr *,
- int *);
extern struct peer *peer_lock (struct peer *);
extern struct peer *peer_unlock (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 *);
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);