summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Hall <GMCH@hestia.halldom.com>2010-01-04 15:09:29 +0000
committerChris Hall <GMCH@hestia.halldom.com>2010-01-04 15:09:29 +0000
commit6746ef8cd683a5e0afa57a5fc90a28f533be68be (patch)
treed7608f3253abe75208db6c789752dd91840e1fcc
parent152acaa5615afcb6d5a06aaed74d2fdd4b5a1233 (diff)
downloadquagga-6746ef8cd683a5e0afa57a5fc90a28f533be68be.tar.bz2
quagga-6746ef8cd683a5e0afa57a5fc90a28f533be68be.tar.xz
Initial commit for bgp_engine branch -- seeding new files...
On branch bgp_engine modified: .gitignore modified: bgpd/Makefile.am new file: bgpd/bgp.h new file: bgpd/bgp_common.c new file: bgpd/bgp_common.h new file: bgpd/bgp_connection.c new file: bgpd/bgp_connection.h modified: bgpd/bgp_debug.c new file: bgpd/bgp_engine.c new file: bgpd/bgp_engine.h modified: bgpd/bgp_fsm.c modified: bgpd/bgp_fsm.h modified: bgpd/bgp_network.c modified: bgpd/bgp_network.h new file: bgpd/bgp_notification.c new file: bgpd/bgp_notification.h modified: bgpd/bgp_open.c new file: bgpd/bgp_open_state.c new file: bgpd/bgp_open_state.h modified: bgpd/bgp_packet.c new file: bgpd/bgp_peer.c new file: bgpd/bgp_peer.h modified: bgpd/bgp_route.c new file: bgpd/bgp_session.c new file: bgpd/bgp_session.h modified: bgpd/bgp_vty.c modified: bgpd/bgp_zebra.c modified: bgpd/bgpd.c modified: bgpd/bgpd.h modified: lib/Makefile.am modified: lib/memtypes.c modified: lib/mqueue.c modified: lib/mqueue.h new file: lib/qafi_safi.h modified: lib/qpselect.c modified: lib/qpselect.h modified: lib/qpthreads.c modified: lib/qpthreads.h modified: lib/sockopt.c modified: lib/sockunion.c modified: lib/sockunion.h modified: lib/stream.c modified: lib/stream.h modified: lib/symtab.h modified: lib/zebra.h
-rw-r--r--.gitignore6
-rw-r--r--bgpd/Makefile.am8
-rw-r--r--bgpd/bgp.h1054
-rw-r--r--bgpd/bgp_common.c230
-rw-r--r--bgpd/bgp_common.h269
-rw-r--r--bgpd/bgp_connection.c767
-rw-r--r--bgpd/bgp_connection.h213
-rw-r--r--bgpd/bgp_debug.c67
-rw-r--r--bgpd/bgp_engine.c192
-rw-r--r--bgpd/bgp_engine.h50
-rw-r--r--bgpd/bgp_fsm.c2870
-rw-r--r--bgpd/bgp_fsm.h105
-rw-r--r--bgpd/bgp_network.c1198
-rw-r--r--bgpd/bgp_network.h52
-rw-r--r--bgpd/bgp_notification.c149
-rw-r--r--bgpd/bgp_notification.h286
-rw-r--r--bgpd/bgp_open.c149
-rw-r--r--bgpd/bgp_open_state.c138
-rw-r--r--bgpd/bgp_open_state.h91
-rw-r--r--bgpd/bgp_packet.c3
-rw-r--r--bgpd/bgp_peer.c762
-rw-r--r--bgpd/bgp_peer.h471
-rw-r--r--bgpd/bgp_route.c3
-rw-r--r--bgpd/bgp_session.c189
-rw-r--r--bgpd/bgp_session.h163
-rw-r--r--bgpd/bgp_vty.c1
-rw-r--r--bgpd/bgp_zebra.c81
-rw-r--r--bgpd/bgpd.c3
-rw-r--r--bgpd/bgpd.h420
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/memtypes.c3
-rw-r--r--lib/mqueue.c103
-rw-r--r--lib/mqueue.h55
-rw-r--r--lib/qafi_safi.h151
-rw-r--r--lib/qpselect.c2
-rw-r--r--lib/qpselect.h30
-rw-r--r--lib/qpthreads.c50
-rw-r--r--lib/qpthreads.h4
-rw-r--r--lib/sockopt.c181
-rw-r--r--lib/sockunion.c378
-rw-r--r--lib/sockunion.h23
-rw-r--r--lib/stream.c373
-rw-r--r--lib/stream.h21
-rw-r--r--lib/symtab.h16
-rw-r--r--lib/zebra.h33
45 files changed, 8972 insertions, 2443 deletions
diff --git a/.gitignore b/.gitignore
index 25a842b5..79aebd73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,4 +34,8 @@ build
.rebase-*
*~
*.loT
-
+.cdtconfigure.Build (GNU)
+.cproject
+.project
+.settings/
+m4/*.m4
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index 1b17d386..f1968c9c 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -15,14 +15,18 @@ libbgp_a_SOURCES = \
bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \
bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \
bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \
- bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c
+ bgp_peer.c bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c \
+ bgp_engine.c bgp_session.c bgp_connection.c \
+ bgp_common.c bgp_notification.c
noinst_HEADERS = \
bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \
bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \
bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \
bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
- bgp_advertise.h bgp_snmp.h bgp_vty.h
+ bgp_peer.h bgp_advertise.h bgp_snmp.h bgp_vty.h \
+ bgp_engine.h bgp_session.h bgp_connection.h \
+ bgp_common.h bgp_notification.h bgp.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
diff --git a/bgpd/bgp.h b/bgpd/bgp.h
new file mode 100644
index 00000000..7f0447bc
--- /dev/null
+++ b/bgpd/bgp.h
@@ -0,0 +1,1054 @@
+/* BGP Protocol and FSM definitions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_H
+#define _QUAGGA_BGP_H
+
+/*==============================================================================
+ * This is a set of definitions for the BGP Protocol and the BGP FSM.
+ *
+ * These are independent of Quagga.
+ */
+
+/*==============================================================================
+ * BGP Finite State Machine (FSM)
+ */
+
+
+#endif /* _QUAGGA_BGP_H */
+
+#define _GMCH_BGP_H "19-Dec-2009"
+
+#ifdef _GMCH_BGP_H
+
+#include "zassert.h"
+#include <stdint.h>
+
+/*##############################################################################
+ * BGP RFC's we know about -- as of 16-Oct-2009
+ *
+ * RFC5566 BGP IPSec Tunnel Encapsulation Attribute
+ * RFC5543 BGP Traffic Engineering Attribute
+ * RFC5512 BGP Encapsulation SAFI and the BGP Tunnel Encapsulation Attribute
+ * RFC5492 Capabilities Advertisement -- obsoletes RFC3392
+ * RFC5292 Address-Prefix Outbound Route Filter
+ * RFC5291 Outbound Route Filtering
+ * RFC5195 BGP-Based Auto-Discovery for Layer-1 VPNs
+ * RFC5123 Considerations in Validating the Path in BGP
+ * RFC5065 AS Confederations -- obsoletes RFC3065
+ * RFC5004 Avoid BGP Best Path Transitions from One External to Another
+ * RFC4893 4-octet AS number
+ * RFC4797 Use of PE-PE GRE or IP in BGP/MPLS VPNs
+ * RFC4781 Graceful Restart with MPLS
+ * RFC4761 Virtual Private LAN Service (VPLS) Using BGP for
+ * Auto-Discovery and Signaling -- updated by RFC5642
+ * RFC4760 Multiprotocol Extensions -- obsoletes RFC2858
+ * RFC4724 Graceful Restart
+ * RFC4684 Constrained Route Distribution for BGP/MPLS IP VPNs
+ * -- updates RFC4364
+ * RFC4659 BGP-MPLS IP VPN of IPv6 VPN
+ * RFC4577 OSPF as Provider/Customer Edge Protocol for BGP?MPLS IP VPNs
+ * -- updates RFC4364
+ * RFC4576 Using an LSA Options Bit to Prevent Looping in BGP/MPLS IP VPNs
+ * RFC4486 Sub-codes for BGP Cease Notification Message
+ * RFC4456 Route Reflection -- obsoletes RFC2796 & RFC1966
+ * RFC4451 BGP MED Considerations
+ * RFC4384 BGP Communities for Data Collection
+ * RFC4382 MPLS/BGP Layer 3 VPN MIB
+ * RFC4381 Analysis of the Security of BGP/MPLS IP VPNs
+ * RFC4365 Applicability Statement for BGP/MPLS IP VPNs
+ * RFC4364 BGP/MPLS IP VPN -- obsoletes RFC2547
+ * -- updated by RFC4577 & RFC4684
+ * RFC4360 BGP Extended Communities
+ * RFC4278 Standards Maturity Variance Regarding TCP MD5 Signature Option
+ * (RFC2385) and the BGP-4 Specification
+ * RFC4277 Experience with the BGP-4 Protocol
+ * RFC4276 BGP-4 Implementation Report
+ * RFC4275 BGP-4 MIB Implementation Survey
+ * RFC4274 BGP Protocol Analysis
+ * RFC4373 Definitions of Managed Objects for BGP-4
+ * -- obsoletes RFC2369 & RFC1657
+ * RFC4272 BGP Security Vulnerabilities Analysis
+ * RFC4271 BGP-4 -- obsoletes RFC1771
+ * RFC4264 BGP Wedgies
+ * RFC4098 Terminology for Benchmarking BGP Device Convergence in the
+ * Control Plane
+ * RFC3822 Configuring BGP to BLock Denial-of-Service Attack
+ * RFC3765 NOPEER Community
+ * RFC3392 ...see RFC5492 -- obsoletes RFC2842
+ * RFC3345 BGP Persistent Route Oscillation Condition
+ * RFC3107 Carrying Label Information in BGP
+ * RFC3065 ...see RFC5065 -- obsoletes RFC1965
+ * RFC2918 Route Refresh Capability
+ * RFC2858 ...see RFC4760 -- obsoletes RFC2283
+ * RFC2842 ...see RFC3392
+ * RFC2796 ...see RFC4456
+ * RFC2547 ...see RFC4364
+ * RFC2545 Use of BGP MP Extensions for IPv6
+ * RFC2439 Route Flap Dampening
+ * RFC2385 Protection of BGP Sessions via TCP MD5 Option
+ * RFC2283 ...see RFC2858
+ * RFC2042 Registering New BGP Attribute Types
+ * RFC1997 BGP Communities Attribute
+ * RFC1966 ...see RFC4456
+ * RFC1965 ...see RFC3065
+ * RFC1774 BGP-4 Protocol Analysis
+ * RFC1773 Experience with the BGP-4 Protocol
+ * RFC1772 Application of BGP in the Internet
+ * RFC1771 ...see RFC4271
+ *
+ * Plus: IANA bgp-parameters.txt of 6-Oct-2009
+ * and as-numbers.txt of 2-Sep-2009
+ * and capability-codes.txt of 4-Aug-2009
+ * and bgp-extended-communities of 11-Dec-2009
+ */
+
+/*==============================================================================
+ * These are used in the definitions below.
+ */
+
+typedef uint32_t U32 ;
+typedef uint16_t U16 ;
+typedef uint8_t U8 ;
+typedef U8 UB ;
+typedef U8 UBX[] ;
+
+#define VALUE(X) enum { X }
+
+/*==============================================================================
+ * AS Numbers and types there of
+ */
+typedef uint32_t asn_t ; /* general ASN */
+
+typedef uint16_t as2_t ; /* specifically 2 Octet ASN */
+typedef uint32_t as4_t ; /* specifically 4 Octet ASN -- RFC4893 */
+
+enum asn_type
+{
+ AS2 = 2,
+ AS4 = 4
+} ;
+
+typedef enum asn_type asn_type_t ;
+
+/* Other stuff.... */
+
+typedef uint32_t bgp_id_t ; /* actually an IPv4 IP Address */
+
+VALUE(BGP_MAX_NEXT_HOP_L = 32) ; /* maximum expected Next Hop address length */
+
+/*==============================================================================
+ * BGP Message Structure
+ */
+
+VALUE(BGP_MAX_MSG_L = 4096) ; /* RFC4271 hard limit on message length */
+
+/* Message Header Format ----------------------------------------------------*/
+
+typedef UB BGP_MH_MARKER_T[16] ; /* marker -- 16-octets of all 1's */
+typedef U16 BGP_MH_LEN_T ; /* length of message inc. header: octets */
+typedef U8 BGP_MH_TYPE_T ; /* BGP message type */
+typedef UBX BGP_MH_BODY_T ; /* rest is body of message */
+
+VALUE(BGP_MH_HEAD_L = /* message header length */
+ sizeof(BGP_MH_MARKER_T)
+ + sizeof(BGP_MH_LEN_T)
+ + sizeof(BGP_MH_TYPE_T) ) ;
+CONFIRM(BGP_MH_HEAD_L == 19) ; /* well known value ! */
+
+#define BGP_MAX_BODY_L (BGP_MAX_MSG_L - BGP_MH_HEAD_L)
+
+enum /* order of entries in Message Header */
+{
+ BGP_MH_MARKER,
+ BGP_MH_LEN,
+ BGP_MH_TYPE,
+ BGP_MH_BODY
+} ;
+
+/* Message Type Numbers ------------------------------------------------------*/
+
+enum BGP_MT
+{
+ BGP_MT_MIN = 1, /* min known message type */
+
+ BGP_MT_OPEN = 1,
+ BGP_MT_UPDATE = 2,
+ BGP_MT_NOTIFICATION = 3,
+ BGP_MT_KEEPALIVE = 4,
+ BGP_MT_ROUTE_REFRESH = 5, /* RFC2918 */
+
+ BGP_MT_MAX = 5, /* max known message type */
+} ;
+
+/* Open Message (type = BGP_MT_OPEN) -------------------------------------------
+ *
+ * The following follows the Message Header (19 octets)
+ */
+typedef U8 BGP_OPM_VERSION_T ;
+typedef U16 BGP_OPM_MY_AS_T ; /* AS4 ASN in AS4 Capability Option */
+typedef U16 BGP_OPM_H_TIME_T ; /* Hold Time in seconds: 0 or >= 3 */
+typedef U32 BGP_OPM_IDENT_T ; /* IPv4 address -- generally */
+typedef U8 BGP_OPM_O_LEN_T ; /* length of options part: octets */
+typedef UBX BGP_OPM_OPTS_T ; /* variable options ! */
+
+enum /* order of the fields */
+{
+ BGP_OPM_VERSION,
+ BGP_OPM_MY_AS,
+ BGP_OPM_H_TIME,
+ BGP_OPM_IDENT,
+ BGP_OPM_O_LEN,
+ BGP_OPM_OPTS
+} ;
+
+VALUE(BGP_OPM_MIN_L = /* minimum OPEN message length */
+ BGP_MH_HEAD_L
+ + sizeof(BGP_OPM_VERSION_T)
+ + sizeof(BGP_OPM_MY_AS_T)
+ + sizeof(BGP_OPM_H_TIME_T)
+ + sizeof(BGP_OPM_IDENT_T)
+ + sizeof(BGP_OPM_O_LEN_T) ) ;
+CONFIRM(BGP_OPM_MIN_L == 29) ; /* well known value ! */
+
+/* Open Message Optional Parameters, each appears as..........................*/
+
+typedef U8 BGP_OPM_P_TYPE_T ; /* optional parameter type */
+typedef U8 BGP_OPM_P_LEN_T ; /* length of parameter: octets */
+typedef UBX BGP_OPM_P_VALUE_T ; /* variable -- depending on type */
+
+VALUE(BGP_OPM_P_MIN_L = sizeof(BGP_OPM_P_TYPE_T) + sizeof(BGP_OPM_P_LEN_T)) ;
+ /* min len of an OPM */
+enum /* order */
+{
+ BGP_OPM_P_TYPE,
+ BGP_OPM_P_LEN,
+ BGP_OPM_P_VALUE
+} ;
+
+/* Optional Parameter Types...................................................*/
+
+enum BGP_OPT
+{
+ BGP_OPT_MIN = 1,
+
+ BGP_OPT_AUTH = 1, /* Authentication (deprecated: RFC5492) */
+ BGP_OPT_CAPS = 2, /* Capabilities */
+
+ BGP_OPT_MAX = 2,
+} ;
+
+/* Capability Announcements ----------------------------------------------------
+ *
+ * Each announcement is wrapped in a BGP_OPT_CAPS option parameter.
+ */
+typedef U8 BGP_CAP_CODE_T ; /* Capability Code -- see below */
+typedef U8 BGP_CAP_LEN_T ; /* length of capability value: octets */
+typedef UBX BGP_CAP_VALUE_T ; /* variable -- depending on code */
+
+VALUE(BGP_CAP_MIN_L = sizeof(BGP_CAP_CODE_T) + sizeof(BGP_CAP_LEN_T)) ;
+ /* min len of a capability announcement */
+enum /* order */
+{
+ BGP_CAP_CODE,
+ BGP_CAP_LEN,
+ BGP_CAP_VALUE
+} ;
+
+/* Capability Types...........................................................*/
+
+enum BGP_CAN {
+ BGP_CAN_MIN = 1,
+
+ BGP_CAN_MP_EXT = 1, /* Multiprotocol Extensions RFC4760 */
+ BGP_CAN_R_REFRESH = 2, /* Route Refresh RFC2918 */
+ BGP_CAN_ORF = 3, /* Outbound Route Filtering RFC5291 */
+ BGP_CAN_M_ROUTES = 4, /* Multiple routes to a dest. RFC3107 */
+ BGP_CAN_E_NEXT_HOP = 5, /* Extended Next Hop Encoding RFC5549 */
+ BGP_CAN_G_RESTART = 64, /* Graceful Restart RFC4724 */
+ BGP_CAN_AS4 = 65, /* Supports 4-octet AS number RFC4893 */
+
+ /* 66, Deprecated 6-Apr-2003 */
+ BGP_CAN_DYNAMIC_CAP= 67, /* Dynamic Capability [Chen] */
+ BGP_CAN_MULTI_SESS = 68, /* Multisession Capability [Appanna] */
+ BGP_CAN_ADD_PATH = 69, /* ADD-PATH [draft-idr] */
+
+ BGP_CAN_MAX = 69, /* but mind the gap(s) ! */
+} ;
+
+/* Update Message (type = BGP_MT_UPDATE) ---------------------------------------
+ *
+ * Note that the NLRI here are implicitly IPv4/SAFI_UNICAST.
+ *
+ * For IPv6 NLRI, see MP_REACH_NLRI and MP_UNREACH_NLRI Attributes
+ * (also MULTICAST).
+ *
+ * The following follows the Message Header (19 octets)
+ */
+typedef U16 BGP_UPM_W_LEN_T ; /* length of Withdrawn Routes section */
+typedef UBX BGP_UPM_W_NLRI_T ; /* variable ! */
+typedef U16 BGP_UPM_A_LEN_T ; /* length of Attributes section */
+typedef UBX BGP_UPM_ATTR_T ; /* variable ! */
+typedef UBX BGP_UPM_NLRI_T ; /* variable -- to end of message ! */
+ /* NB: Attributes len. == 0 <=> no NLRI */
+
+VALUE(BGP_UPM_MIN_L = /* minimum UPDATE message length */
+ BGP_MH_HEAD_L
+ + sizeof(BGP_UPM_W_LEN_T)
+ + sizeof(BGP_UPM_A_LEN_T) ) ;
+CONFIRM(BGP_UPM_MIN_L == 23) ; /* well known value ! */
+
+enum /* order */
+{
+ BGP_UPM_W_LEN,
+ BGP_UPM_W_NLRI,
+ BGP_UPM_A_LEN,
+ BGP_UPM_ATTR,
+ BGP_UPM_NLRI
+} ;
+
+/* Prefix format..............................................................*/
+
+typedef U8 BGP_PREF_LEN_T ; /* Prefix Length in bits */
+typedef UBX BGP_PREF_VAL_T ; /* variable ! (bits + 7) / 8 */
+
+/* Attributes format..........................................................*/
+
+typedef U8 BGP_ATTR_FLAGS_T ; /* see below for flags values */
+typedef U8 BGP_ATTR_TYPE_T ; /* see below for attribute types */
+typedef U8 BGP_ATTR_LEN_T ; /* minimum */
+typedef U16 BGP_ATTR_ELEN_T ; /* if BGP_ATF_EXTENDED is set */
+typedef UBX BGP_ATTR_VAL_T ; /* variable value */
+
+VALUE(BGP_ATTR_MIN_L = /* min len of attribute */
+ sizeof(BGP_ATTR_FLAGS_T)
+ + sizeof(BGP_ATTR_TYPE_T)
+ + sizeof(BGP_ATTR_LEN_T) ) ;
+
+enum /* order */
+{
+ BGP_ATTR_FLAGS,
+ BGP_ATTR_TYPE,
+ BGP_ATTR_LEN,
+ BGP_ATTR_ELEN = BGP_ATTR_LEN,
+ BGP_ATTR_VAL
+} ;
+
+/* Attribute Flags Byte values................................................*/
+
+enum
+{
+ BGP_ATF_OPTIONAL = 0x80, /* otherwise is Well Known */
+ BGP_ATF_TRANSITIVE = 0x40, /* MUST be set if Well Known */
+ BGP_ATF_PARTIAL = 0x20, /* MUST not be set if Well Known */
+ BGP_ATF_EXTENDED = 0x10, /* 2 octet Attribute Length */
+} ;
+
+/* Attribute Type Byte values -- see below for specifics of each..............*/
+
+enum BGP_ATT
+{
+ BGP_ATT_UNDEFINED = 0, /* IANA do not define meaning for this */
+ BGP_ATT_MIN = 1,
+
+ BGP_ATT_ORIGIN = 1,
+ BGP_ATT_AS_PATH = 2, /* AS2 or AS4, depending on context */
+ BGP_ATT_NEXT_HOP = 3, /* implicitly IPv4 */
+ BGP_ATT_MEDS = 4, /* MULTI_EXIT_DISC */
+ BGP_ATT_L_PREF = 5, /* LOCAL_PREF */
+ BGP_ATT_A_AGGREGATE = 6, /* ATOMIC_AGGREGATE */
+ BGP_ATT_AGGREGATOR = 7,
+
+ BGP_ATT_COMMUNITY = 8, /* RFC1997 */
+ BGP_ATT_ORIG_ID = 9, /* ORIGINATOR_ID : Route Refl. RFC4456 */
+ BGP_ATT_CLUSTER_LIST = 10, /* CLUSTER_LIST : Route Refl. RFC4456 */
+
+ BGP_ATT_DPA = 11, /* [Chen] */
+ BGP_ATT_ADVERTISER = 12, /* RFC1863 -- historic per RFC4223 */
+ BGP_ATT_RCID_PATH = 13, /* RFC1863 -- historic per RFC4223 */
+
+ BGP_ATT_MP_REACH = 14, /* MP_REACH_NRLI MP Ext. RFC4760 */
+ BGP_ATT_MP_UNREACH = 15, /* MP_UNREACH_NRLI MP Ext. RFC4760 */
+
+ BGP_ATT_EXT_COMMS = 16, /* EXTENDED_COMMUNITIES RFC4360 */
+ BGP_ATT_AS4_PATH = 17, /* AS4 stuff RFC4893 */
+ BGP_ATT_AS4_AGGR = 18, /* AS4 stuff (AS4_AGGREGATOR) RFC4893 */
+
+ BGP_ATT_SSA = 19, /* SAFI Specific Attribute [Nalawade] */
+ BGP_ATT_CONNECTOR = 20, /* Connector Attribute [Nalawade] */
+ BGP_ATT_AS_PATHLIMIT = 21, /* Expires 27-Oct-2007 [idr] */
+ BGP_ATT_PMSI_TUNNEL = 22, /* [l3vpn-2547bis] */
+ BGP_ATT_TUNNEL_ENCAP = 23, /* Tunnel Encapsulation Attrib RFC5512 */
+ BGP_ATT_TRAFFIC_ENG = 24, /* Traffic Engineering RFC5543 */
+ BGP_ATT_IPV6_EXT_COMMS = 25, /* IPv6 Ext. Community [l3vpn-v6] */
+
+ BGP_ATT_MAX = 25, /* last attribute known to us */
+
+ BGP_ATT_RESERVED = 255, /* reserved by IANA */
+} ;
+
+/* Notification Message (type = BGP_MT_NOTIFICATION) -------------------------*/
+
+typedef U8 BGP_NOM_CODE_T ; /* see below for Error Code values */
+typedef U8 BGP_NOM_SUBCODE_T ; /* see below for Error Subcode value */
+typedef UBX BGP_NOM_DATA_T ; /* variable -- to end of message ! */
+
+VALUE(BGP_NOM_MIN_L = /* minimum NOTIFICATION length */
+ BGP_MH_HEAD_L
+ + sizeof(BGP_NOM_CODE_T)
+ + sizeof(BGP_NOM_SUBCODE_T) ) ;
+CONFIRM(BGP_NOM_MIN_L == 21) ; /* well known value ! */
+
+enum /* order */
+{
+ BGP_NOM_CODE,
+ BGP_NOM_SUBCODE,
+ BGP_NOM_DATA,
+} ;
+
+/* Notification Message Error Codes...........................................*/
+enum BGP_NOMC
+{
+ BGP_NOMC_UNDEF = 0, /* Nothing defined for this code */
+
+ BGP_NOMC_HEADER = 1, /* Message Header Error */
+ BGP_NOMC_OPEN = 2, /* Open Message Error */
+ BGP_NOMC_UPDATE = 3, /* Update Message Error */
+ BGP_NOMC_HOLD_EXP = 4, /* Hold timer expired */
+ BGP_NOMC_FSM = 5, /* Finite State Machine Error */
+ BGP_NOMC_CEASE = 6, /* Cease RFC4486 */
+
+ BGP_NOMC_MAX = 6 /* max known error code */
+} ;
+
+/* Notification Message Error Subcodes........................................*/
+
+enum BGP_NOMS
+{
+ BGP_NOMS_UNSPECIFIC = 0 /* If nothing else applies */
+};
+
+enum BGP_NOMS_HEADER /* BGP_NOMC_HEADER subcodes */
+{
+ BGP_NOMS_H_NOT_SYNC = 1, /* Connection Not Synchronised */
+ /* (Marker field not all = 1,'s !) */
+ BGP_NOMS_H_BAD_LEN = 2, /* Bad Message Length */
+ /* DATA: the length that failed */
+ BGP_NOMS_H_BAD_TYPE = 3, /* Bad Message Type */
+ /* DATA: the message type objected to */
+
+ BGP_NOMS_H_MAX = 3, /* max known subcode */
+} ;
+
+enum BGP_NOMS_OPEN /* BGP_NOMC_OPEN subcodes */
+{
+ BGP_NOMS_O_VERSION = 1, /* Unsupported Version Number */
+ /* DATA: largest supported version */
+ BGP_NOMS_O_BAD_AS = 2, /* Bad Peer AS */
+ BGP_NOMS_O_BAD_ID = 3, /* Bad BGP Identifier */
+ BGP_NOMS_O_OPTION = 4, /* Unsupported Optional Parameter */
+ BGP_NOMS_O_AUTH = 5, /* Authentication Failure (depr.) */
+ BGP_NOMS_O_H_TIME = 6, /* Unacceptable Hold Time */
+
+ BGP_NOMS_O_CAPABILITY = 7, /* Unsupported Capability RFC5492 */
+ /* DATA: the unsupported capabilities */
+
+ BGP_NOMS_O_MAX = 7, /* max known subcode */
+} ;
+
+enum BGP_NOMS_UPDATE /* BGP_NOMC_UPDATE subcodes */
+{
+ BGP_NOMS_U_A_LIST = 1, /* Malformed Attribute List */
+ /* (Attribute repeated) */
+ BGP_NOMS_U_UNKNOWN = 2, /* Unrecognised Well-known Attrib */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_MISSING = 3, /* Missing Well-known Attrib. */
+ /* DATA: type of missing attribute(s?) */
+ BGP_NOMS_U_A_FLAGS = 4, /* Attribute Flags Error */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_A_LENGTH = 5, /* Attribute Length Error */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_ORIGIN = 6, /* Invalid Origin Attribute */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_AS_LOOP = 7, /* AS Routeing Loop (deprecated) */
+ BGP_NOMS_U_NEXT_HOP = 8, /* Invalid NEXT_HOP Attrib. */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_OPTIONAL = 9, /* Optional Attribute Error */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_NETWORK = 10, /* Invalid Network Field */
+ /* (badly formed NLRI) */
+ BGP_NOMS_U_AS_PATH = 11, /* Malformed AS Path */
+
+ BGP_NOMS_U_MAX = 11, /* max known subcode */
+} ;
+
+enum BGP_NOMS_HOLD_EXP /* BGP_NOMC_HOLD_EXP subcodes */
+{
+ BGP_NOMS_HE_MAX = 0 /* max known subcode */
+} ;
+
+enum BGP_NOMC_FSM /* BGP_NOMC_FSM subcodes */
+{
+ BGP_NOMS_F_MAX = 0 /* max known subcode */
+} ;
+
+enum BGP_NOMS_CEASE /* BGP_NOMC_CEASE subcodes RFC4486 */
+{
+ BGP_NOMS_C_MAX_PREF = 1, /* Max Number of Prefixes Reached MUST */
+ /* DATA: MAY be: AFI/SAFI/Upper-Bound */
+ BGP_NOMS_C_SHUTDOWN = 2, /* Administrative Shutdown SHOULD */
+ BGP_NOMS_C_DECONFIG = 3, /* Peer De-configured SHOULD */
+ BGP_NOMS_C_RESET = 4, /* Administrative Reset SHOULD */
+ BGP_NOMS_C_REJECTED = 5, /* Connection Rejected SHOULD */
+ BGP_NOMS_C_CONFIG = 6, /* Other Configuration Change SHOULD */
+ BGP_NOMS_C_COLLISION = 7, /* Connection Collision Res. SHOULD */
+ BGP_NOMS_C_RESOURCES = 8, /* Out of Resources MAY */
+
+ BGP_NOMS_C_MAX = 8 /* max known subcode */
+} ;
+
+/* Keepalive Message (type = BGP_MT_KEEPALIVE) -------------------------------*/
+
+VALUE(BGP_KAM_L = BGP_MH_HEAD_L) ; /* Keepalive message is entirely empty */
+
+/* Route Refresh Message (type = BGP_MT_ROUTE_REFRESH) -------------------------
+ *
+ * If it's just header + 4 bytes, it's an RFC2918 Route Refresh request.
+ * If it's longer that that, it's an RFC5291 ORF
+ */
+typedef U16 BGP_RRM_AFI_T ; /* Address Family Identifier */
+typedef U8 BGP_RRM_RES_T ; /* reserved = 0 */
+typedef U8 BGP_RRM_SAFI_T ; /* Subsequent Address Family Identifier */
+
+VALUE(BGP_RRM_L = BGP_MH_HEAD_L /* Route Refresh length */
+ + sizeof(BGP_RRM_AFI_T)
+ + sizeof(BGP_RRM_RES_T)
+ + sizeof(BGP_RRM_SAFI_T) ) ;
+CONFIRM(BGP_RRM_L == 23) ; /* well known value ! */
+
+typedef U8 BGP_RRM_ORF_WHEN_T ; /* when to refresh -- see RFC5291 */
+typedef UBX BGP_RRM_ORFS_T ; /* variable... see below */
+
+enum /* order */
+{
+ BGP_RRM_AFI,
+ BGP_RRM_RES,
+ BGP_RRM_SAFI,
+ BGP_RRM_ORF_WHEN, /* start of ORF message */
+ BGP_RRM_ORFS, /* start of ORF collections */
+} ;
+
+/* ORFS come in collections...................................................*/
+
+typedef U8 BGP_ORF_TYPE_T ; /* ORF Type */
+typedef U16 BGP_ORF_LEN_T ; /* length of ORF entries: octets */
+typedef UBX BGP_ORF_ENTRIES_T ; /* variable: ORF entry collection(s) */
+
+VALUE(BGP_ORF_MIN_L = sizeof(BGP_ORF_TYPE_T) + sizeof(BGP_ORF_LEN_T)) ;
+ /* min len of ORF */
+
+enum /* order */
+{
+ BGP_ORF_TYPE,
+ BGP_ORF_LEN,
+ BGP_ORF_ENTRIES,
+} ;
+
+/* Shape of ORF Entries depends on the ORF Type, but start with a common part */
+
+typedef U8 BGP_ORF_E_ACTION_T ; /* see below for action/match bits */
+typedef UBX BGP_ORF_E_REST_T ; /* rest depends on ORF Type */
+
+/* Known BGP_ORF_TYPE values..................................................*/
+
+enum BGP_ORF {
+ BGP_ORF_T_MIN = 64,
+
+ BGP_ORF_T_PREFIX = 64, /* Address Prefix ORF RFC5292 */
+
+ BGP_ORF_T_MAX = 64
+} ;
+
+/* Known BGP_ORF_E_ACTION bits................................................*/
+
+enum {
+ BGP_ORF_EA_MASK = 0xC0, /* mask to extract Action */
+
+ BGP_ORF_EA_ADD = 0x00, /* Action: add ORF */
+ BGP_ORF_EA_REMOVE = 0x40, /* Action: remove ORF */
+ BGP_ORF_EA_REM_ALL = 0x80, /* Action: remove all ORF */
+
+ BGP_ORF_EA_DENY = 0x20, /* deny -- otherwise permit */
+} ;
+
+/* Address Prefix ORF (BGP_ORF_T_PREFIX) type specific entry part.............*/
+
+typedef U32 BGP_ORF_E_P_SEQ_T ; /* Sequence number */
+typedef U8 BGP_ORF_E_P_MIN_T ; /* Minlen */
+typedef U8 BGP_ORF_E_P_MAX_T ; /* Maxlen */
+typedef U8 BGP_ORF_E_P_LEN_T ; /* Prefix Length */
+typedef UBX BGP_ORF_E_P_PFIX_T ; /* Prefix -- variable ! */
+
+/*==============================================================================
+ * Capability Values
+ */
+
+/* Multiprotocol Extensions -- BGP_CAN_MP_EXT -- RFC4760 -----------------------
+ *
+ * When more than one AFI/SAFI supported, will include more than one Capability
+ * Announcement -- that is, does *not* pack more than one of these into an
+ * announcement.
+ */
+typedef U16 BGP_CAP_MPE_AFI_T ; /* Address Family Identifier */
+typedef U8 BGP_CAP_MPE_RES_T ; /* Reserved: 0 */
+typedef U8 BGP_CAP_MPE_SAFI_T ; /* Subsequent Address Family */
+
+VALUE(BGP_CAP_MPE_L = 4) ; /* Fixed length == 4 ! */
+
+/* Route Refresh -- BGP_CAN_R_REFRESH -- RFC2918 -----------------------------*/
+
+VALUE(BGP_CAP_RRF_L = 0) ; /* no value part */
+
+/* Outbound Route Filtering -- BGP_CAN_ORF -- RFC5291 ------------------------*/
+
+typedef U16 BGP_CAP_ORF_AFI_T ; /* Address Family Identifier */
+typedef U8 BGP_CAP_ORF_RES_T ; /* Reserved: 0 */
+typedef U8 BGP_CAP_ORF_SAFI_T ; /* Subsequent Address Family */
+typedef U8 BGP_CAP_ORF_COUNT_T ; /* number of ORF Types supported */
+typedef UBX BGP_CAP_ORF_CAN_T ; /* variable -- 2 byte entries as below */
+
+VALUE(BGP_CAP_ORF_MIN_L = sizeof(BGP_CAP_ORF_AFI_T)
+ + sizeof(BGP_CAP_ORF_RES_T)
+ + sizeof(BGP_CAP_ORF_SAFI_T)
+ + sizeof(BGP_CAP_ORF_COUNT_T) ) ;
+
+VALUE(BGP_CAP_ORF_COUNT_O = sizeof(BGP_CAP_ORF_AFI_T)
+ + sizeof(BGP_CAP_ORF_RES_T)
+ + sizeof(BGP_CAP_ORF_SAFI_T) ) ;
+
+enum /* order */
+{
+ BGP_CAP_ORF_AFI,
+ BGP_CAP_ORF_RES,
+ BGP_CAP_ORF_SAFI,
+ BGP_CAP_ORF_COUNT,
+ BGP_CAP_ORF_CAN
+} ;
+
+/* Entries saying what ORF Types can be supported and how.....................*/
+
+typedef U8 BGP_CAP_ORFE_TYPE_T ; /* the ORF Type supported */
+typedef U8 BGP_CAP_ORFE_CAN_T ; /* what can do with ORF Type */
+
+VALUE(BGP_CAP_ORFE_L = sizeof(BGP_CAP_ORFE_TYPE_T)
+ + sizeof(BGP_CAP_ORFE_CAN_T)) ;
+ /* length of ORF capability Type entry */
+
+/* Values for the BGP_CAP_ORFE_CAN field */
+
+enum
+{
+ BGP_CAP_ORFE_CAN_RECV = 1, /* willing to receive ) can be... */
+ BGP_CAP_ORFE_CAN_SEND = 2 /* would like to send ) ... combined */
+} ;
+
+/* Multiple Routes to a Destination (Labels) -- BGP_CAN_M_ROUTES -- RFC3107 ----
+ *
+ * Requires Multiprotocol Extensions, really.
+ */
+
+VALUE(BGP_CAP_MRD_L = 0) ; /* no value part */
+
+/* Graceful Restart -- BGP_CAN_G_RESTART -- RFC4724 --------------------------*/
+
+typedef U16 BGP_CAP_GR_TIME_T ; /* See below for bit field masks */
+typedef UBX BGP_CAP_GR_CAN_T ; /* variable -- 4 byte entries follow */
+
+VALUE(BGP_CAP_GR_MIN_L = sizeof(BGP_CAP_GR_TIME_T)) ;
+
+/* The BGP_CAP_GR_TIME field is a bit-field...................................*/
+
+enum
+{
+ BGP_CAP_GR_T_R_FLAG = 0x8000, /* Restart State flag */
+ BGP_CAP_GR_T_MASK = 0x0FFF /* Restart Time in seconds */
+} ;
+
+/* Entries for what we can gracefully restart for.............................*/
+
+typedef U16 BGP_CAP_GRE_AFI_T ; /* Address Family Identifier */
+typedef U8 BGP_CAP_GRE_SAFI_T ; /* Subsequent Address Family */
+typedef U8 BGP_CAP_GRE_FLAGS_T ; /* Flags qualifying capability */
+
+VALUE(BGP_CAP_GRE_L = /* length of Graceful Restart entry */
+ sizeof(BGP_CAP_GRE_AFI_T)
+ + sizeof(BGP_CAP_GRE_SAFI_T)
+ + sizeof(BGP_CAP_GRE_FLAGS_T) ) ;
+
+/* The BGP_CAP_GRE_FLAGS field is also a bit-field */
+
+enum
+{
+ BGP_CAP_GRE_F_FORW = 0x80 /* Forwarding State preserved */
+} ;
+
+/* 4-Octet AS Numbers -- BGP_CAN_AS4 -- RFC4893 ------------------------------*/
+
+typedef U32 BGP_CAP_AS4_MY_AS_T ; /* can do AS4, and this is me AS4-wise */
+
+VALUE(BGP_CAP_AS4_L = sizeof(BGP_CAP_AS4_MY_AS_T)) ;
+
+/*==============================================================================
+ * Attributes -- form of the Path Attributes in an UPDATE Message
+ *
+ * NB: RFC4271 states that attribute of given type may appear at most ONCE in
+ * a given UPDATE Message (Section 5, p24).
+ */
+enum
+{
+ BGP_AT_IS_OPTIONAL = 0x80,
+ BGP_AT_IS_TRANSITIVE = 0x40,
+
+ BGP_AT_IS_MANDATORY = 0x08, /* otherwise discretionary */
+ BGP_AT_IS_WELL_KNOWN = 0x04, /* => TRANSITIVE */
+
+ BGP_AT_IS_IGP_ONLY = 0x01 /* IBGP sessions & intra-confederation */
+} ;
+
+typedef U8 BGP_AT_IS_T ; /* Attribute type fits in a byte */
+
+/* ORIGIN Attribute -- BGP_ATT_ORIGIN ----------------------------------------*/
+
+VALUE(BGP_ATT_ORIGIN_IS = BGP_AT_IS_WELL_KNOWN | BGP_AT_IS_MANDATORY) ;
+
+typedef U8 BGP_ATT_ORIGIN_T ; /* one byte of data ! */
+
+VALUE(BGP_ATT_ORIGIN_L = sizeof(BGP_ATT_ORIGIN_T)) ;
+
+enum BGP_ATT_ORG {
+ BGP_ATT_ORG_MIN = 0,
+
+ BGP_ATT_ORG_IGP = 0, /* NLRI is interior to originating AS */
+ BGP_ATT_ORG_EGP = 1, /* NLRI is learned vi EGP */
+ BGP_ATT_ORG_INCOMP = 2, /* INCOMPLETE -- NLRI learned somehow */
+
+ BGP_ATT_ORG_MAX = 2,
+} ;
+
+/* AS_PATH Attribute -- BGP_ATT_AS_PATH ----------------------------------------
+ *
+ * Attribute is held as a collection of Path Segment, each one comprising:
+ * (path segment type, path segment count, AS numbers)
+ *
+ * The AS numbers will be 2-octets unless both parties are AS4 speakers (RFC4893).
+ */
+VALUE(BGP_ATT_AS_PATH_IS = BGP_AT_IS_WELL_KNOWN | BGP_AT_IS_MANDATORY) ;
+
+typedef U8 BGP_ATT_ASPS_TYPE_T ; /* type of AS Path Segment */
+typedef U8 BGP_ATT_ASPS_COUNT_T ; /* count of ASes in AS Path Segment */
+
+VALUE(BGP_ATT_ASPS_MIN_L = sizeof(BGP_ATT_ASPS_TYPE_T)
+ + sizeof(BGP_ATT_ASPS_COUNT_T)) ;
+
+enum /* order */
+{
+ BGP_ATT_ASPS_TYPE,
+ BGP_ATT_ASPS_COUNT
+} ;
+
+typedef U16 BGP_ATT_ASPS_AS2_T ; /* obvious, really */
+typedef U32 BGP_ATT_ASPS_AS4_T ;
+
+/* AS Path Segment Types......................................................*/
+
+enum BGP_AS_SEG {
+ BGP_AS_SET = 1,
+ BGP_AS_SEQUENCE = 2,
+ BGP_AS_CONFED_SEQUENCE = 3, /* RFC5065 */
+ BGP_AS_CONFED_SET = 4, /* RFC5065 */
+} ;
+
+/* Special value AS numbers...................................................*/
+
+#define AS4(h, l) (((h) << 16) + (l))
+
+enum BGP_ASN {
+ BGP_ASN_NULL = 0, /* Reserved */
+
+ BGP_ASN_RES1_S = 64496, /* Start of Reservation 1 (0xFBF0) */
+
+ BGP_AS2_DOC_S = 64496, /* Start of Docm. & Samples AS2 (0xFBF0) */
+ BGP_AS2_DOC_E = 64511, /* End (0xFBFF) */
+
+ BGP_ASN_RES1_E = 64511, /* End of Reservation 1 (0xFBFF) */
+
+ BGP_ASN_PRIV_S = 64512, /* Start of Private Use ASN (0xFC00) */
+ BGP_ASN_PRIV_E = 65534, /* End (0xFFFE) */
+
+ BGP_ASN_RES2 = 65535, /* Start of Reservation 2 (0xFFFF) */
+
+ BGP_AS4_DOC_S = AS4(1,0), /* Start of Docm. & Samples AS4 (65536) */
+ BGP_AS4_DOC_E = AS4(1,15), /* End (65531) */
+
+ BGP_ASN_RES2_E = AS4(1,65535), /* End of Reservation 2 (131071) */
+
+ BGP_ASN4_KNOWN_S = AS4(2,0), /* Start of known AS4 space (131072) */
+ BGP_ASN4_KNOWN_E = AS4(6,65535), /* End (394239) */
+
+ BGP_ASN_UNKNOWN_S = AS4(7,0), /* Start of uncharted AS4 space (394240) */
+ /* (as of 12-Mar-2009) */
+
+ BGP_ASN_RES3 = 0xFFFFFFFF, /* Reserved 3 */
+
+ BGP_ASN_TRANS = 23456, /* place-holder for AS4 ASN (0x5BA0) */
+} ;
+
+/* NEXT_HOP Attribute -- BGP_ATT_NEXT_HOP --------------------------------------
+ *
+ * Implicitly an IPv4 address -- see MP_REACH_NLRI for IPv6 NEXT_HOP
+ */
+
+VALUE(BGP_ATT_NEXT_HOP_IS = BGP_AT_IS_WELL_KNOWN | BGP_AT_IS_MANDATORY) ;
+
+typedef U32 BGP_ATT_NEXT_HOP_T ; /* 4 bytes of IPv4 */
+
+VALUE(BGP_ATT_NEXT_HOP_L = sizeof(BGP_ATT_NEXT_HOP_T)) ;
+
+/* MULTI_EXIT_DISC Attribute -- BGP_ATT_MEDS ---------------------------------*/
+
+VALUE(BGP_ATT_MEDS_IS = BGP_AT_IS_OPTIONAL) ; /* non-transitive */
+
+typedef U32 BGP_ATT_MEDS_T ; /* 4 bytes of "metric" */
+
+VALUE(BGP_ATT_MEDS_L = sizeof(BGP_ATT_MEDS_T)) ;
+
+/* LOCAL_PREF Attribute -- BGP_ATT_L_PREF ------------------------------------*/
+
+VALUE(BGP_ATT_L_PREF_IS = BGP_AT_IS_WELL_KNOWN | BGP_AT_IS_MANDATORY
+ | BGP_AT_IS_IGP_ONLY) ;
+
+typedef U32 BGP_ATT_L_PREF_T ; /* 4 bytes of "metric" */
+
+VALUE(BGP_ATT_L_PREF_L = sizeof(BGP_ATT_L_PREF_T)) ;
+
+/* ATOMIC_AGGREGATE -- BGP_ATT_A_AGGREGATE -----------------------------------*/
+
+VALUE(BGP_ATT_A_AGGREGATE_IS = BGP_AT_IS_WELL_KNOWN) ;
+ /* discretionary -- SHOULD pass on */
+
+VALUE(BGP_ATT_A_AGGREGATE_L = 0) ; /* no data */
+
+/* AGGREGATOR -- BGP_ATT_AGGREGATOR --------------------------------------------
+ *
+ * The AS numbers will be 2-octets unless both parties are AS4 speakers (RFC4893).
+ */
+VALUE(BGP_ATT_AGGREGATOR_IS = BGP_AT_IS_OPTIONAL | BGP_AT_IS_TRANSITIVE) ;
+
+typedef U16 BGP_ATT_AGR_AS_AS2_T ; /* Aggregator AS2 } when not both AS4 */
+typedef U32 BGP_ATT_AGR_AS_AS4_T ; /* Aggregator AS4 } when both AS4 */
+typedef U32 BGP_ATT_AGR_ID_T ; /* Aggregator ID */
+
+VALUE(BGP_ATT_AGR_AS2_L = sizeof(BGP_ATT_AGR_AS_AS2_T)
+ + sizeof(BGP_ATT_AGR_ID_T)) ;
+VALUE(BGP_ATT_AGR_AS4_L = sizeof(BGP_ATT_AGR_AS_AS4_T)
+ + sizeof(BGP_ATT_AGR_ID_T)) ;
+
+/* COMMUNITIES -- BGP_ATT_COMMUNITY -- RFC1997 ---------------------------------
+ *
+ * MS half of a Community is expected to be AS number of "owner" of same, so
+ * this is implicitly AS2 -- see Extended Communities
+ */
+
+VALUE(BGP_ATT_COMMUNITY_IS = BGP_AT_IS_OPTIONAL | BGP_AT_IS_TRANSITIVE) ;
+
+typedef U32 BGP_ATT_COM_VALUE_T ; /* A list of these */
+
+enum
+{
+ BGP_ATT_COM_MS_RES1 = 0x0000, /* 0x0000_0000..0x0000_FFFF are reserved */
+ BGP_ATT_COM_MS_RES2 = 0xFFFF /* 0xFFFF_0000..0xFFFF_FFFF are reserved */
+} ;
+
+enum BGP_COMM {
+ BGP_ATT_COM_RES1_S = 0x00000000,
+
+ BGP_ATT_COM_INTERNET = 0x00000000,
+
+ BGP_ATT_COM_RES1_E = 0x0000FFFF,
+
+ BGP_ATT_COM_RES2_S = 0xFFFF0000,
+
+ BGP_ATT_COM_NO_EXPORT = 0xFFFFFF01,
+ BGP_ATT_COM_NO_ADVERTISE = 0xFFFFFF02,
+ BGP_ATT_COM_NO_EXPORT_SUBCONFED = 0xFFFFFF03,
+
+ BGP_ATT_COM_RES2_E = 0xFFFFFFFF,
+} ;
+
+/* ORIGINATOR_ID -- BGP_ATT_ORIG_ID -- RFC4456: BGP Route Reflection ---------*/
+
+VALUE(BGP_ATT_ORIG_ID_IS = BGP_AT_IS_OPTIONAL) ;
+
+typedef U32 BGP_ATT_ORIG_ID_T ; /* BGP Id of router in the local AS */
+
+VALUE(BGP_ATT_ORIG_ID_L = sizeof(BGP_ATT_ORIG_ID_T)) ;
+
+/* CLUSTER_LIST -- BGP_ATT_CLUSTER_LIST -- RFC4456: BGP Route Reflection -----*/
+
+VALUE(BGP_ATT_CLUSTER_LIST_IS = BGP_AT_IS_OPTIONAL) ;
+
+typedef U32 BGP_ATT_CLUSTER_ID_T ; /* attribute is list of these CLUSTER_IDs */
+
+/* MP_REACH_NLRI ---- BGP_ATT_MP_REACH ---- RFC4760: Multiprotocol Extensions --
+ *
+ * Attribute augments the reachable NLRI in the body of the UPDATE Message
+ *
+ * NB: there can be at most one MP_REACH_NLRI attribute in a given UPDATE
+ * Message.
+ *
+ * This attribute carries NLRI for a single AFI/SAFI.
+ *
+ * The main part of the UPDATE message carries NLRI for
+ * AFI_IPv4/SAFI_UNICAST (implicitly). RFC4760 does not prohibit NLRI in
+ * the main part and further NLRI for AFI_IPv4/SAFI_UNICAST in
+ * MP_REACH_NLRI -- however, it does seem an odd thing to do...
+ *
+ * So: UPDATE message carries NLRI for IPv4 plus one other AFI/SAFI -- at most.
+ *
+ * The Next Hop address is notionally related to AFI/SAFI, but the given length
+ * of Next Hop is paramount.
+ */
+
+VALUE(BGP_ATT_MP_REACH_IS = BGP_AT_IS_OPTIONAL) ;
+
+typedef U16 BGP_ATT_MPR_AFI_T ; /* Address Family */
+typedef U8 BGP_ATT_MPR_SAFI_T ; /* Subsequent Address Family */
+typedef U8 BGP_ATT_MPR_NH_LEN_T ; /* length of Next Hop Address: octets */
+typedef UBX BGP_ATT_MPR_NH_T ; /* Next Hop Address -- variable */
+typedef U8 BGP_ATT_MPR_RES_T ; /* reserved -- MUST be 0 */
+typedef UBX BGP_ATT_MPR_NLRI_T ; /* NLRIs now reachable -- variable */
+ /* Encoded as BGP Prefix (BGP_PREF_...) */
+ /* Semantics depend on AFI/SAFI pair */
+
+VALUE(BGP_ATT_MPR_MIN_L /* min len of attribute */
+ = sizeof(BGP_ATT_MPR_AFI_T)
+ + sizeof(BGP_ATT_MPR_SAFI_T)
+ + sizeof(BGP_ATT_MPR_NH_LEN_T)
+ + sizeof(BGP_ATT_MPR_RES_T) ) ;
+
+enum /* order */
+{
+ BGP_ATT_MPR_AFI,
+ BGP_ATT_MPR_SAFI,
+ BGP_ATT_MPR_NH_LEN,
+ BGP_ATT_MPR_NH,
+ BGP_ATT_MPR_RES,
+ BGP_ATT_MPR_NLRI
+} ;
+
+/* MP_UNREACH_NLRI -- BGP_ATT_MP_UNREACH -- RFC4760: Multiprotocol Extensions --
+ *
+ * NB: there can be at most one MP_UNREACH_NLRI attribute in a given UPDATE
+ * Message.
+ *
+ * This attribute carries withdrawn NLRI for a single AFI/SAFI.
+ *
+ * The main part of the UPDATE message carries withdrawn NLRI for
+ * AFI_IPv4/SAFI_UNICAST (implicitly).
+ *
+ * So: UPDATE message carries withdrawn NLRI for IPv4 plus one other
+ * AFI/SAFI -- at most (which may be different to the MP_REACH_NLRI).
+ */
+
+VALUE(BGP_ATT_MP_UNREACH_IS = BGP_AT_IS_OPTIONAL) ;
+
+typedef U16 BGP_ATT_MPU_AFI_T ; /* Address Family */
+typedef U8 BGP_ATT_MPU_SAFI_T ; /* Subsequent Address Family */
+typedef UBX BGP_ATT_MPU_NLRI_T ; /* NLRIs now unreachable -- variable */
+ /* Encoded as BGP Prefix (BGP_PREF_...) */
+ /* Semantics depend on AFI/SAFI pair */
+
+VALUE(BGP_ATT_MPU_MIN_L = sizeof(BGP_ATT_MPU_AFI_T)
+ + sizeof(BGP_ATT_MPU_SAFI_T) ) ;
+
+enum /* order */
+{
+ BGP_ATT_MPU_AFI,
+ BGP_ATT_MPU_SAFI,
+ BGP_ATT_MPU_NLRI
+} ;
+
+/* EXTENDED_COMMUNITIES -- BGP_ATT_EXT_COMMS -- RFC4360 ----------------------*/
+
+#define BGP_ATT_EXT_COMMS_IS (BGP_AT_IS_OPTIONAL | BGP_AT_IS_TRANSITIVE)
+
+typedef U8 BGP_ATT_EXC_TYPE_T ; /* first byte is Type (high) */
+typedef U8 BGP_ATT_EXC_STYPE_T ; /* second byte is Subtype (low) -- opt. */
+typedef U16 BGP_ATT_EXC_ETYPE_T ; /* or two byte Extended Type */
+
+typedef UB BGP_ATT_EXC_T[8] ; /* each Extended Community is 8 octets */
+
+VALUE(BGP_ATT_EXC_L = sizeof(BGP_ATT_EXC_T)) ;
+CONFIRM(BGP_ATT_EXC_L == 8) ;
+
+enum
+{
+ /* Shape of the Type octet RFC4360 */
+ BGP_EXCT_IANA = 0x80, /* IANA Authority */
+ BGP_EXCT_NON_TRANS = 0x40, /* 0 => Transitive */
+ BGP_EXCT_MASK = 0x3F, /* the Type */
+
+ /* The following are the MS part of an extended type RFC4360 */
+ BGP_EXCT_AS2 = 0, /* AS Specific Ext. Community */
+ BGP_EXCT_IPV4 = 1, /* IPv4 Specific Ext. Community */
+ BGP_EXCT_OPAQUE = 3, /* Opaque Ext. Community */
+
+ /* The following are the LS part of an extended type RFC4360 */
+ BGP_EXCS_R_TARGET = 2, /* Route Target */
+ BGP_EXCS_R_ORIGIN = 3, /* Route Origin */
+
+ /* The following are regular (no subtype) types [Knoll] */
+ BGP_EXCT_QOS_MARK = 4, /* QoS marking */
+ BGP_EXCT_COS_CAP = 5, /* CoS Capability */
+} ;
+
+/* AS4_PATH -------- BGP_ATT_AS4_PATH -- RFC4893 -------------------------------
+ *
+ * Sent by AS4 speaker to non-AS4 speaker.
+ * NOT sent by AS4 speaker to AS4 speaker.
+ * Passed on by non-AS4 speakers.
+ *
+ * Takes the same form as an AS_PATH for an AS4 speaker -- ie 4 byte ASN
+ */
+
+VALUE(BGP_ATT_AS4_PATH_IS = BGP_AT_IS_OPTIONAL | BGP_AT_IS_TRANSITIVE) ;
+
+/* AS4_AGGREGATOR -- BGP_ATT_AS4_AGGR -- RFC4893 -------------------------------
+ *
+ * Sent by AS4 speaker to non-AS4 speaker.
+ * NOT sent by AS4 speaker to AS4 speaker.
+ * Passed on by non-AS4 speakers.
+ *
+ * Takes the same form as an AS_AGGREGATOR for an AS4 speaker -- ie 4 byte ASN
+ */
+VALUE(BGP_ATT_AS4_AGGR_IS = BGP_AT_IS_OPTIONAL | BGP_AT_IS_TRANSITIVE) ;
+
+#endif /* _GMCH_BGP_H */
diff --git a/bgpd/bgp_common.c b/bgpd/bgp_common.c
new file mode 100644
index 00000000..15ecdf3b
--- /dev/null
+++ b/bgpd/bgp_common.c
@@ -0,0 +1,230 @@
+/* BGP Common -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "bgpd/bgp_common.h"
+#include "lib/zassert.h"
+
+/*==============================================================================
+ * Conversion qafx_bit_t -> qafx_num_t.
+ *
+ * If no bits are set, returns qafx_num_undef.
+ *
+ * If more than one bit is set, returns the lowest number qafx.
+ *
+ * NB: this is not built for speed.
+ *
+ * NB: it is a mistake to convert a value > qafx_bits_max (FATAL unless NDEBUG)
+ */
+extern qafx_num_t
+qafx_num(qafx_bit_t bit)
+{
+ qafx_num_t num ;
+ dassert(bit <= qafx_bits_max) ;
+
+ if (bit == 0)
+ return qafx_num_undef ;
+
+ num = 0 ;
+
+ while ((bit & 0xF) == 0)
+ {
+ num += 4 ;
+ bit >>= 4 ;
+ }
+
+ while ((bit & 1) == 0)
+ {
+ num += 1;
+ bit >>= 1 ;
+ } ;
+
+ return num ;
+} ;
+
+/*==============================================================================
+ * Conversion tables for qafx_num => qAFI and qSAFI
+ * and qafx_num => iAFI and iSAFI
+ */
+
+const qAFI_t qAFI_map[] =
+ {
+ [qafx_ipv4_unicast] = qAFI_IPV4,
+ [qafx_ipv4_multicast] = qAFI_IPV4,
+ [qafx_ipv4_mpls_vpn] = qAFI_IPV4,
+ [qafx_ipv6_unicast] = qAFI_IPV6,
+ [qafx_ipv6_multicast] = qAFI_IPV6,
+ [qafx_ipv6_mpls_vpn] = qAFI_IPV6,
+ [qafx_num_other] = qAFI_undef
+ } ;
+
+const qSAFI_t qSAFI_map[] =
+ {
+ [qafx_ipv4_unicast] = qSAFI_Unicast,
+ [qafx_ipv4_multicast] = qSAFI_Multicast,
+ [qafx_ipv4_mpls_vpn] = qSAFI_MPLS_VPN,
+ [qafx_ipv6_unicast] = qSAFI_Unicast,
+ [qafx_ipv6_multicast] = qSAFI_Multicast,
+ [qafx_ipv6_mpls_vpn] = qSAFI_MPLS_VPN,
+ [qafx_num_other] = qSAFI_undef
+ } ;
+
+const iAFI_t iAFI_map[] =
+ {
+ [qafx_ipv4_unicast] = iAFI_IPV4,
+ [qafx_ipv4_multicast] = iAFI_IPV4,
+ [qafx_ipv4_mpls_vpn] = iAFI_IPV4,
+ [qafx_ipv6_unicast] = iAFI_IPV6,
+ [qafx_ipv6_multicast] = iAFI_IPV6,
+ [qafx_ipv6_mpls_vpn] = iAFI_IPV6,
+ [qafx_num_other] = iAFI_Reserved
+ } ;
+
+const iSAFI_t iSAFI_map[] =
+ {
+ [qafx_ipv4_unicast] = iSAFI_Unicast,
+ [qafx_ipv4_multicast] = iSAFI_Multicast,
+ [qafx_ipv4_mpls_vpn] = iSAFI_MPLS_VPN,
+ [qafx_ipv6_unicast] = iSAFI_Unicast,
+ [qafx_ipv6_multicast] = iSAFI_Multicast,
+ [qafx_ipv6_mpls_vpn] = iSAFI_MPLS_VPN,
+ [qafx_num_other] = iSAFI_Reserved,
+ } ;
+
+/*==============================================================================
+ * Convert iAFI/iSAFI => qafx_num_t
+ * and qAFI/qSAFI => qafx_num_t
+ */
+
+/* iAFI/iSAFI = qafx_num_t unknowns => qafx_num_other
+ * reserved => qafx_num_undef
+ */
+extern qafx_num_t
+qafx_num_from_iAFI_iSAFI(iAFI_t afi, iSAFI_t safi)
+{
+ switch (afi)
+ {
+ case iAFI_Reserved:
+ return qafx_num_undef ; /* no matter what the iSAFI is */
+
+ case iAFI_IP:
+ switch(safi)
+ {
+ case iSAFI_Reserved:
+ return qafx_num_undef ; /* no matter what the iAFI is */
+ case iSAFI_Unicast:
+ return qafx_ipv4_unicast ;
+ case iSAFI_Multicast:
+ return qafx_ipv4_multicast ;
+ case iSAFI_MPLS_VPN:
+ return qafx_ipv4_mpls_vpn ;
+ default:
+ break ;
+ } ;
+ break ;
+
+ case iAFI_IP6:
+ switch(safi)
+ {
+ case iSAFI_Reserved:
+ return qafx_num_undef ; /* no matter what the iAFI is */
+ case iSAFI_Unicast:
+ return qafx_ipv6_unicast ;
+ case iSAFI_Multicast:
+ return qafx_ipv6_multicast ;
+ case iSAFI_MPLS_VPN:
+ return qafx_ipv6_mpls_vpn ;
+ default:
+ break ;
+ } ;
+ break ;
+
+ default:
+ switch(safi)
+ {
+ case iSAFI_Reserved:
+ return qafx_num_undef ; /* no matter what the iAFI is */
+ default:
+ break ;
+ } ;
+ break ;
+ } ;
+
+ return qafx_num_other ;
+} ;
+
+/* qAFI/qSAFI = qafx_num_t
+ *
+ * NB: qAFI_undef with any qSAFI_xxx => qafx_num_undef
+ * qSAFI_undef with any qAFI_xxx => qafx_num_undef
+ * qSAFI_Unused qith any qAFI_xxx => qafx_num_undef
+ *
+ * NB: any unrecognised qAFI/qSAFI combinations => FATAL error
+ */
+extern qafx_num_t
+qafx_num_from_qAFI_qSAFI(qAFI_t afi, qSAFI_t safi)
+{
+ switch (afi)
+ {
+ case qAFI_undef:
+ if ((safi >= qSAFI_min) && (safi <= qSAFI_max))
+ return qafx_num_undef ; /* for all valid qSAFI */
+ break ;
+
+ case qAFI_IP:
+ switch(safi)
+ {
+ case qSAFI_undef:
+ case qSAFI_Unused:
+ return qafx_num_undef ;
+ case qSAFI_Unicast:
+ return qafx_ipv4_unicast ;
+ case qSAFI_Multicast:
+ return qafx_ipv4_multicast ;
+ case qSAFI_MPLS_VPN:
+ return qafx_ipv4_mpls_vpn ;
+ default:
+ break ;
+ } ;
+ break ;
+
+ case qAFI_IP6:
+ switch(safi)
+ {
+ case qSAFI_undef:
+ case qSAFI_Unused:
+ return qafx_num_undef ;
+ case qSAFI_Unicast:
+ return qafx_ipv6_unicast ;
+ case qSAFI_Multicast:
+ return qafx_ipv6_multicast ;
+ case qSAFI_MPLS_VPN:
+ return qafx_ipv6_mpls_vpn ;
+ default:
+ break ;
+ } ;
+ break ;
+
+ default:
+ break ;
+ } ;
+
+ zabort("invalid qAFI or qSAFI") ;
+} ;
diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h
new file mode 100644
index 00000000..fd73bc51
--- /dev/null
+++ b/bgpd/bgp_common.h
@@ -0,0 +1,269 @@
+/* BGP Common -- functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_COMMON_H
+#define _QUAGGA_BGP_COMMON_H
+
+#include <stdint.h>
+#include "qafi_safi.h"
+#include "zassert.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Here are a number of "incomplete" declarations, which allow a number of
+ * bgpd structures to refer to each other.
+ */
+
+struct bgp ;
+struct peer ;
+struct bgp_session ;
+struct bgp_connection ;
+struct bgp_open_state ;
+
+typedef struct bgp* bgp_instance ;
+typedef struct peer* bgp_peer ;
+typedef struct bgp_session* bgp_session ;
+typedef struct bgp_connection* bgp_connection ;
+typedef struct bgp_open_state* bgp_open_state ;
+
+/*==============================================================================
+ * Both session and connection require this.
+ */
+typedef enum bgp_connection_ordinal bgp_connection_ordinal_t ;
+enum bgp_connection_ordinal
+{
+ bgp_connection_primary = 0,
+ bgp_connection_secondary = 1,
+
+ bgp_connection_count = 2
+} ;
+
+/*==============================================================================
+ * Both session and connection require this.
+ */
+typedef enum bgp_stopped_causes bgp_stopped_cause_t ;
+enum bgp_stopped_causes
+{
+ bgp_stopped_min_cause = 0,
+
+ bgp_stopped_not = 0, /* not stopped (yet) */
+
+
+ bgp_stopped_admin = 1, /* Routeing Engine Stop */
+
+ bgp_stopped_notification = 2, /* Received NOTIFICATION */
+
+ bgp_stopped_collision = 3,
+
+ bgp_stopped_invalid = 4, /* some internal error */
+ bgp_stopped_unknown = 5, /* some unknown reason */
+
+ bgp_stopped_max_cause = 4
+} ;
+
+/*==============================================================================
+ * Other common types and ....
+ */
+
+/* AS Numbers */
+typedef uint32_t as_t ;
+typedef uint16_t as16_t ; /* we may still encounter 16 Bit asnums */
+
+/* BGP Identifier -- usually an IPv4 address ! */
+typedef uint32_t bgp_id_t ;
+
+/* Size of BGP packets or thing in such */
+typedef uint16_t bgp_size_t;
+
+/*==============================================================================
+ * AFI/SAFI encodings for bgpd
+ *
+ * This captures the AFI/SAFI combinations that bgpd supports.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * A qafx_num_t identifies a supported AFI/SAFI combination
+ */
+typedef enum qafx_num qafx_num_t ;
+
+enum qafx_num
+{
+ qafx_num_undef = -1, /* No defined AFI/SAFI */
+ qafx_num_min = 0, /* minimum valid qafx */
+
+ qafx_num_first = 0, /* first "real" qafx */
+
+ qafx_ipv4_unicast = 0, /* iAFI = 1, iSAFI = 1 */
+ qafx_ipv4_multicast = 1, /* iAFI = 1, iSAFI = 2 */
+ qafx_ipv4_mpls_vpn = 2, /* iAFI = 1, iSAFI = 128 */
+
+ qafx_ipv6_unicast = 3, /* iAFI = 2, iSAFI = 1 */
+ qafx_ipv6_multicast = 4, /* iAFI = 2, iSAFI = 2 */
+ qafx_ipv6_mpls_vpn = 5, /* iAFI = 2, iSAFI = 128 */
+
+ qafx_num_last = 5, /* last "real" qafx */
+
+ qafx_num_other = 6, /* place-holder: for unknown AFI/SAFI */
+
+ qafx_num_max = 6, /* maximum qafx */
+ qafx_count /* number of qafx */
+} ;
+
+/*------------------------------------------------------------------------------
+ * A qafx_set_t is a set of qafx_bit_t -- a bit-vector
+ */
+typedef enum qafx_bit qafx_bit_t ;
+typedef qafx_bit_t qafx_set_t ;
+
+enum qafx_bit
+{
+ qafx_set_empty = 0,
+
+ qafx_bits_min = (1 << qafx_num_min),
+
+ qafx_ipv4_unicast_bit = (1 << qafx_ipv4_unicast),
+ qafx_ipv4_multicast_bit = (1 << qafx_ipv4_multicast),
+ qafx_ipv4_mpls_vpn_bit = (1 << qafx_ipv4_mpls_vpn),
+
+ qafx_ipv6_unicast_bit = (1 << qafx_ipv6_unicast),
+ qafx_ipv6_multicast_bit = (1 << qafx_ipv6_multicast),
+ qafx_ipv6_mpls_vpn_bit = (1 << qafx_ipv6_mpls_vpn),
+
+ qafx_other_bit = (1 << qafx_num_other),
+
+ qafx_bits_max = (1 << qafx_count) - 1
+} ;
+
+/*------------------------------------------------------------------------------
+ * Conversions qafx_num <-> qafx_bit
+ *
+ * The conversion from qafx_bit -> qafx_num is not built for speed.
+ */
+
+/* Get qafx_bit_t for given qafx_num_t
+ *
+ * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG).
+ */
+Inline qafx_bit_t
+qafx_bit(qafx_num_t num)
+{
+ dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ;
+ return (1 << num) ;
+} ;
+
+/* Get qafx_num_t for the given qafx_bit_t.
+ */
+extern qafx_num_t
+qafx_num(qafx_bit_t bit) ;
+
+/*==============================================================================
+ * Conversions for qafx_num => qAFI and qSAFI
+ */
+
+/*------------------------------------------------------------------------------
+ * Convert qafx_num_t to qAFI_xxx
+ *
+ * Maps qafx_num_other to qAFI_undef.
+ *
+ * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG).
+ */
+
+extern const qAFI_t qAFI_map[] ;
+
+Inline qAFI_t
+get_qAFI(qafx_num_t num)
+{
+ dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ;
+
+ return qAFI_map[num] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert qafx_num_t to qSAFI_xxx
+ *
+ * Maps qafx_num_other to qSAFI_undef.
+ *
+ * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG).
+ */
+
+extern const qSAFI_t qSAFI_map[] ;
+
+Inline qSAFI_t
+get_qSAFI(qafx_num_t num)
+{
+ dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ;
+
+ return qSAFI_map[num] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert qafx_num_t to iAFI_xxx
+ *
+ * Maps qafx_num_other to iAFI_Reserved.
+ *
+ * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG).
+ */
+
+extern const iAFI_t iAFI_map[] ;
+
+Inline iAFI_t
+get_iAFI(qafx_num_t num)
+{
+ dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ;
+
+ return iAFI_map[num] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert qafx_num_t to iSAFI_xxx
+ *
+ * Maps qafx_num_other to iSAFI_Reserved.
+ *
+ * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG).
+ */
+
+extern const iSAFI_t iSAFI_map[] ;
+
+Inline iSAFI_t
+get_iSAFI(qafx_num_t num)
+{
+ dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ;
+
+ return iSAFI_map[num] ;
+} ;
+
+/*==============================================================================
+ * Conversions for qAFI/qSAFI => qafx_num_t
+ * and iAFI/iSAFI => qafx_num_t
+ */
+
+extern qafx_num_t
+qafx_num_from_iAFI_iSAFI(iAFI_t afi, iSAFI_t safi) ;
+
+extern qafx_num_t
+qafx_num_from_qAFI_qSAFI(qAFI_t afi, qSAFI_t safi) ;
+
+#endif /* _QUAGGA_BGP_COMMON_H */
+
diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c
new file mode 100644
index 00000000..28d584f0
--- /dev/null
+++ b/bgpd/bgp_connection.c
@@ -0,0 +1,767 @@
+/* BGP Connection Handling -- functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "bgpd/bgp.h"
+
+#include "bgpd/bgpd.h"
+
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_connection.h"
+#include "bgpd/bgp_notification.h"
+
+#include "lib/memory.h"
+#include "lib/mqueue.h"
+#include "lib/symtab.h"
+#include "lib/stream.h"
+
+/*==============================================================================
+ * BGP Connections.
+ *
+ * Each BGP Connection has its own:
+ *
+ * * BGP Finite State Machine (FSM)
+ * * socket and related qpselect file
+ * * input/output buffers and I/O management
+ * * timers to support the above
+ *
+ * Each BGP Session is associated with at most two BGP Connections. The second
+ * connection exists only if a connect and a listen connection is made while
+ * a session is starting up, and one will be dropped before either connection
+ * reaches Established state.
+ *
+ * The bgp_connection structure is private to the BGP Engine, and is accessed
+ * directly, without the need for any mutex.
+ *
+ * Each connection is closely tied to its parent bgp_session. The bgp_session
+ * is shared between the Routeing Engine and the BGP Engine, and therefore
+ * access is subject to the bgp_session's mutex.
+ *
+ */
+
+/*==============================================================================
+ * The connection queue.
+ *
+ * When the connection's write buffer empties, the connection is placed on the
+ * connection queue.
+ *
+ * The connection queue is processed as the highest priority action in the
+ * BGP Engine, at which point as many of the items on the connection's
+ * pending queue as possible will be processed.
+ *
+ * The connection_queue is managed as a circular list of connections. The
+ * connection_queue variable points at the next to be processed.
+ *
+ */
+
+static bgp_connection bgp_connection_queue ;
+
+/*==============================================================================
+ * Managing bgp_connection stuctures.
+ */
+static const char* bgp_connection_tags[] =
+ {
+ [bgp_connection_primary] = "(primary)",
+ [bgp_connection_secondary] = "(secondary)",
+ } ;
+
+static void
+bgp_connection_init_host(bgp_connection connection, const char* tag) ;
+
+static void
+bgp_write_buffer_init_new(bgp_wbuffer wb, size_t size) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise connection structure -- allocate if required.
+ *
+ *
+ *
+ * NB: acquires and releases the session mutex.
+ */
+extern bgp_connection
+bgp_connection_init_new(bgp_connection connection, bgp_session session,
+ bgp_connection_ordinal_t ordinal)
+{
+ assert( (ordinal == bgp_connection_primary)
+ || (ordinal == bgp_connection_secondary) ) ;
+ assert(session->connections[ordinal] == NULL) ;
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ if (connection == NULL)
+ connection = XCALLOC(MTYPE_BGP_CONNECTION, sizeof(struct bgp_connection)) ;
+ else
+ memset(connection, 0, sizeof(struct bgp_connection)) ;
+
+ /* Structure is zeroised, so the following are implictly initialised:
+ *
+ * * state bgp_fsm_Initial
+ * * next NULL -- not on the connection queue
+ * * prev NULL -- not on the connection queue
+ * * post bgp_fsm_null_event
+ * * fsm_active not active
+ * * stopped bgp_stopped_not
+ * * notification NULL -- none received or sent
+ * * err no error, so far
+ * * su_local no address, yet
+ * * su_remote no address, yet
+ * * hold_timer_interval none -- set when connection is opened
+ * * keepalive_timer_interval none -- set when connection is opened
+ * * read_pending nothing pending
+ * * read_header not reading header
+ * * notification_pending nothing pending
+ * * wbuff_full not full
+ * * wbuff all pointers NULL -- empty buffer
+ */
+
+ confirm(bgp_fsm_Initial == 0) ;
+ confirm(bgp_fsm_null_event == 0) ;
+ confirm(bgp_stopped_not == 0) ;
+
+ /* Link back to session, point at its mutex and point session here */
+ connection->session = session ;
+ connection->p_mutex = &session->mutex ;
+
+ connection->ordinal = ordinal ;
+ connection->accepted = (ordinal == bgp_connection_secondary) ;
+
+ session->connections[ordinal] = connection ;
+
+ /* qps_file structure */
+ qps_file_init_new(&connection->qf, NULL) ;
+
+ /* Initialise all the timers */
+ qtimer_init_new(&connection->hold_timer, p_bgp_engine->pile,
+ NULL, connection) ;
+ qtimer_init_new(&connection->keepalive_timer, p_bgp_engine->pile,
+ NULL, connection) ;
+
+ /* Copy log destination and make host name + (primary)/(secondary) */
+ /* Makes complete copies so that connection may continue to run, even */
+ /* after the session has stopped, and may have been destroyed. */
+ connection->log = session->log ;
+ bgp_connection_init_host(connection, bgp_connection_tags[ordinal]) ;
+
+ /* Need two empty "stream" buffers */
+ connection->ibuf = stream_new(BGP_MAX_MSG_L) ;
+ connection->obuf = stream_new(BGP_MAX_MSG_L) ;
+
+ /* Ensure mqueue_local_queue is empty. */
+ mqueue_local_init_new(&connection->pending_queue) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return connection ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the host field for the connection to session->host + given tag.
+ *
+ * NB: requires the session to be LOCKED.
+ */
+static void
+bgp_connection_init_host(bgp_connection connection, const char* tag)
+{
+ const char* host = connection->session->host ;
+
+ connection->host = XMALLOC(MTYPE_BGP_PEER_HOST, strlen(host)
+ + strlen(tag) + 1) ;
+ strcpy(connection->host, host) ;
+ strcat(connection->host, tag) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free connection.
+ *
+ * Connection must be Stopping -- no longer attached to a session.
+ *
+ * The FSM will have
+ *
+ *
+ */
+extern bgp_connection
+bgp_connection_free(bgp_connection connection)
+{
+
+ return connection ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Full if not enough room for a maximum size BGP message.
+ */
+static inline int
+bgp_write_buffer_full(bgp_wbuffer wb)
+{
+ return ((wb->limit - wb->p_in) < BGP_MAX_MSG_L) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty if in and out pointers are equal (but may need to be reset !)
+ */
+static inline int
+bgp_write_buffer_empty(bgp_wbuffer wb)
+{
+ return (wb->p_out == wb->p_in) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate new write buffer and initialise pointers
+ *
+ * NB: assumes structure has been zeroised by the initialisation of the
+ * enclosing connection.
+ */
+static void
+bgp_write_buffer_init_new(bgp_wbuffer wb, size_t size)
+{
+ assert(wb->base == NULL) ;
+
+ wb->base = XMALLOC(MTYPE_STREAM_DATA, size) ;
+ wb->limit = wb->base + size ;
+
+ wb->p_in = wb->p_out = wb->base ;
+ wb->full = bgp_write_buffer_full(wb) ;
+
+ assert(!wb->full) ;
+} ;
+
+/*==============================================================================
+ * Connection queue management.
+ *
+ * Connections appear on this queue when their write buffer becomes empty, or
+ * they are finally stopped.
+ */
+
+/*------------------------------------------------------------------------------
+ * Add connection to connection queue -- if not already on it
+ */
+extern void
+bgp_connection_queue_add(bgp_connection connection)
+{
+ if (connection->next == NULL)
+ {
+ if (bgp_connection_queue == NULL)
+ {
+ /* adding to empty queue */
+ bgp_connection_queue = connection ;
+ connection->next = connection ;
+ connection->prev = connection ;
+ }
+ else
+ {
+ /* add behind the current entry */
+ connection->next = bgp_connection_queue ;
+ connection->prev = bgp_connection_queue->prev ;
+
+ connection->next->prev = connection ;
+ connection->prev->next = connection ;
+ } ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete connection from connection queue -- if on it
+ */
+extern void
+bgp_connection_queue_del(bgp_connection connection)
+{
+ if (connection->next != NULL)
+ {
+ if (connection == connection->next)
+ {
+ /* deleting the only item on the queue */
+ assert((connection == connection->prev)
+ && (connection == bgp_connection_queue)) ;
+ bgp_connection_queue = NULL ;
+ }
+ else
+ {
+ if (connection == bgp_connection_queue)
+ bgp_connection_queue = connection->next ;
+
+ connection->next->prev = connection->prev ;
+ connection->prev->next = connection->next ;
+ } ;
+
+ connection->next = connection->prev = NULL ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process the connection queue until it becomes empty.
+ *
+ * Process each item until its pending queue becomes empty, or its write
+ * buffer becomes full, or it is stopped.
+ *
+ */
+extern void
+bgp_connection_queue_process(void)
+{
+ mqueue_block mqb ;
+
+ while (bgp_connection_queue != NULL)
+ {
+ /* select the first in the queue, and step to the next */
+ bgp_connection connection = bgp_connection_queue ;
+ bgp_connection_queue = connection->next ;
+
+ /* Reap the connection if it is now stopped. */
+ if (connection->state == bgp_fsm_Stopping)
+ {
+ bgp_connection_reset(connection) ;
+ } ;
+
+ /* ..... */
+
+
+ /* ..... */
+
+
+ } ;
+
+
+} ;
+
+/*==============================================================================
+ * Opening and closing Connections
+ */
+
+/*------------------------------------------------------------------------------
+ * Open connection.
+ *
+ * Expects connection to either be newly created or recently closed.
+ *
+ * Sets:
+ *
+ * * if accept() clears the session accept flag
+ * * sets the qfile and fd ready for use
+ * * clears err and stopped
+ * * discards any open_state and notification
+ * * copies hold_timer_interval and keep_alive_timer_interval from session
+ *
+ * Expects:
+ *
+ * * links to/from session to be set up (including ordinal)
+ * * timers to be initialised and unset
+ * * log and host to be set up
+ * * buffers to exist and all buffering to be set empty
+ * * pending queue to be empty
+ *
+ * Does not touch:
+ *
+ * * state of the connection
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern void
+bgp_connection_open(bgp_connection connection, int fd)
+{
+ bgp_session session = connection->session ;
+
+ /* If this is the secondary connection, do not accept any more. */
+ if (connection->ordinal == bgp_connection_secondary)
+ session->accept = 0 ;
+
+ /* Set the file going */
+ qps_add_file(p_bgp_engine->selection, &connection->qf, fd, connection) ;
+
+ /* Clear sundry state is clear */
+ connection->post = bgp_fsm_null_event ; /* no post event event */
+ connection->err = 0 ; /* so far, so good */
+ connection->stopped = bgp_stopped_not ; /* up and running */
+
+ /* These accept NULL arguments */
+ connection->open_recv = bgp_open_state_free(connection->open_recv) ;
+ connection->notification = bgp_notify_free(connection->notification) ;
+
+ /* Copy the original hold_timer_interval and keepalive_timer_interval
+ * Assume these have sensible initial values.
+ *
+ * These may be changed during the exchange of BGP OPEN messages.
+ */
+ connection->hold_timer_interval = session->hold_timer_interval ;
+ connection->keepalive_timer_interval = session->keepalive_timer_interval ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close connection.
+ *
+ * * if there is an fd, close it
+ * * if qfile is active, remove it
+ * * forget any addresses
+ * * unset any timers
+ * * reset all buffering to empty
+ * * empties the pending queue -- destroying all messages
+ *
+ * The following remain:
+ *
+ * * state of the connection
+ * * links to and from the session
+ * * the timers remain initialised (but unset)
+ * * the buffers remain (but reset)
+ * * logging and host string
+ * * any open_state that has been received
+ * * any notification sent/received
+ * * the stopped cause (if any)
+ *
+ * Once closed, the only further possible actions are:
+ *
+ * * bgp_connection_open() -- to retry connection
+ *
+ * * bgp_connection_free() -- to finally discard
+ *
+ * * bgp_connection_close() -- can do this again
+ *
+ */
+extern void
+bgp_connection_close(bgp_connection connection)
+{
+ int fd ;
+
+ /* close the qfile and any associate file descriptor */
+ qps_remove_file(&connection->qf) ;
+ fd = qps_file_unset_fd(&connection->qf) ;
+ if (fd != fd_undef)
+ shutdown(fd, SHUT_RDWR) ;
+
+ /* forget any addresses */
+ sockunion_clear(&connection->su_local) ;
+ sockunion_clear(&connection->su_remote) ;
+
+ /* Unset all the timers */
+ qtimer_unset(&connection->hold_timer) ;
+ qtimer_unset(&connection->keepalive_timer) ;
+
+ /* Reset all buffering empty. */
+ stream_reset(connection->ibuf) ;
+ stream_reset(connection->obuf) ;
+
+ connection->read_pending = 0 ;
+ connection->read_header = 0 ;
+ connection->notification_pending = 0 ;
+
+ connection->wbuff.p_in = connection->wbuff.base ;
+ connection->wbuff.p_out = connection->wbuff.base ;
+ connection->wbuff.full = 0 ;
+
+ /* Empty out the pending queue */
+ mqueue_local_reset_keep(&connection->pending_queue) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close connection for reading and purge the write buffers.
+ *
+ * This is done when the connection is about to be fully closed, but need to
+ * send a NOTIFICATION message before finally closing.
+ *
+ * * if there is an fd, shutdown(, SHUT_RD) and disable the qfile for reading
+ * * reset all read buffering to empty
+ * * discard all output except any partially written message
+ * * empty the pending queue
+ *
+ * Can do this because the write buffer contains only complete BGP messages.
+ *
+ * This ensures the write buffer is not full, so NOTIFICATION message can
+ * be written (at least as far as the write buffer).
+ *
+ * Everything else is left untouched.
+ */
+extern void
+bgp_connection_part_close(bgp_connection connection)
+{
+ bgp_wbuffer wb = &connection->wbuff ;
+ int fd ;
+ uint8_t* p ;
+ bgp_size_t mlen ;
+
+ /* close the qfile and any associate file descriptor */
+ fd = qps_file_fd(&connection->qf) ;
+ if (fd != fd_undef)
+ {
+ shutdown(fd, SHUT_RD) ;
+ qps_disable_modes(&connection->qf, qps_read_mbit) ;
+ } ;
+
+ /* Reset all input buffering. */
+ stream_reset(connection->ibuf) ;
+
+ connection->read_pending = 0 ;
+ connection->read_header = 0 ;
+
+ /* Reset obuf and purge wbuff. */
+ stream_reset(connection->obuf) ;
+
+ connection->notification_pending = 0 ;
+
+ if (wb->p_in != wb->p_out) /* will be equal if buffer is empty */
+ {
+ mlen = 0 ;
+ p = wb->base ;
+ do /* Advance p until p + mlen > wb->p_out */
+ {
+ p += mlen ;
+ mlen = bgp_msg_get_mlen(p) ;
+ } while ((p + mlen) <= wb->p_out) ;
+
+ if (p == wb->p_out)
+ mlen = 0 ; /* wb->p_out points at start of message */
+ else
+ memcpy(wb->base, p, mlen) ;
+
+ wb->p_out = wb->base + (wb->p_out - p) ;
+ wb->p_in = wb->base + mlen ;
+ }
+ else
+ wb->p_in = wb->p_out = wb->base ;
+
+ wb->full = bgp_write_buffer_full(wb) ;
+ assert(!wb->full) ;
+
+ /* Empty out the pending queue */
+ mqueue_local_reset_keep(&connection->pending_queue) ;
+} ;
+
+/*==============================================================================
+ * Writing to BGP connection.
+ *
+ * All writing is done by preparing a BGP message in the "obuf" buffer,
+ * and then calling bgp_connection_write().
+ *
+ * If possible, that is written away immediately. If not, then no further
+ * messages may be prepared until the buffer has been cleared.
+ *
+ * Write the contents of the "work" buffer.
+ *
+ * Returns true <=> able to write the entire buffer without blocking.
+ */
+
+static int bgp_connection_write_direct(bgp_connection connection) ;
+static void bgp_connection_write_action(qps_file qf, void* file_info) ;
+
+/*------------------------------------------------------------------------------
+ * Write the contents of the obuf -- MUST not be here if wbuff is full !
+ *
+ * Returns: 1 => all written -- obuf and wbuff are empty
+ * 0 => written -- obuf now empty
+ * -1 => failed -- error event generated
+ */
+extern int
+bgp_connection_write(bgp_connection connection)
+{
+ bgp_wbuffer wb = &connection->wbuff ;
+
+ if (bgp_write_buffer_empty(wb))
+ {
+ /* write buffer is empty -- attempt to write directly */
+ return bgp_connection_write_direct(connection) ;
+ } ;
+
+ /* Transfer the obuf contents to the staging buffer. */
+ wb->p_in = stream_transfer(wb->p_in, connection->obuf, wb->limit) ;
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The write buffer is empty -- so try to write obuf directly.
+ *
+ * If cannot empty the obuf directly to the TCP buffers, transfer it to to the
+ * write buffer, and enable the qpselect action.
+ * (This is where the write buffer is allocated, if it hasn't yet been.)
+ *
+ * Either way, the obuf is cleared and can be reused (unless failed).
+ *
+ * Returns: 1 => written obuf to TCP buffer -- all buffers empty
+ * 0 => written obuf to wbuff -- obuf empty
+ * -1 => failed -- stopping, dead
+ */
+enum { bgp_wbuff_size = BGP_MAX_MSG_L * 10 } ;
+
+static int
+bgp_connection_write_direct(bgp_connection connection)
+{
+ int ret ;
+
+ ret = stream_flush_try(connection->obuf, qps_file_fd(&connection->qf)) ;
+
+ if (ret == 0)
+ return 1 ; /* Done: wbuff and obuf are empty */
+
+ else if (ret > 0)
+ {
+ bgp_wbuffer wb = &connection->wbuff ;
+
+ /* Partial write -- set up buffering, if required. */
+ if (wb->base == NULL)
+ bgp_write_buffer_init_new(wb, bgp_wbuff_size) ;
+
+ /* Transfer *entire* message to staging buffer */
+ wb->p_in = stream_transfer(wb->base, connection->obuf, wb->limit) ;
+
+ wb->p_out = wb->p_in - ret ; /* output from here */
+
+ /* Must now be enabled to write */
+ qps_enable_mode(&connection->qf, qps_write_mnum,
+ bgp_connection_write_action) ;
+
+ return 0 ; /* Done: obuf is empty, but wbuff is not */
+ } ;
+
+ /* write failed -- signal error and return failed */
+ bgp_fsm_io_error(connection, errno) ;
+
+ return -1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write Action for bgp connection.
+ *
+ * Empty the write buffer if we can.
+ *
+ * If empties that, empty the obuf if there is anything pending, and....
+ *
+ * If empty out everything, disable write mode.
+ *
+ * If encounter an error, generate TCP_fatal_error event.
+ */
+static void
+bgp_connection_write_action(qps_file qf, void* file_info)
+{
+ bgp_connection connection = file_info ;
+ bgp_wbuffer wb = &connection->wbuff ;
+ int have ;
+ int ret ;
+
+ /* Try to empty the write buffer. */
+ have = wb->p_out - wb->p_in ;
+ while (have != 0)
+ {
+ ret = write(qps_file_fd(qf), wb->p_out, have) ;
+ if (ret > 0)
+ {
+ wb->p_out += ret ;
+ have -= ret ;
+ }
+ else if (ret < 0)
+ {
+ ret = errno ;
+ if (ret == EINTR)
+ continue ;
+
+ if ((ret != EAGAIN) && (ret != EWOULDBLOCK))
+ bgp_fsm_io_error(connection, errno) ;
+
+ return ;
+ } ;
+ } ;
+
+ /* Buffer is empty -- reset it and disable write mode */
+ wb->p_out = wb->p_in = wb->base ;
+ wb->full = 0 ;
+
+ qps_disable_modes(&connection->qf, qps_write_mbit) ;
+
+ /* If waiting to send NOTIFICATION, just did it. */
+ /* Otherwise: is writable again -- so add to connection_queue */
+ if (connection->notification_pending)
+ bgp_fsm_event(connection, bgp_fsm_Sent_NOTIFICATION_message) ;
+ else
+ bgp_connection_queue_add(connection) ;
+} ;
+
+/*==============================================================================
+ * Read Action for bgp connection.
+ *
+ * Don't directly read -- all reading is done in response to the socket
+ * becoming readable.
+ *
+ * Reads one BGP message into the ibuf and dispatches it.
+ *
+ * Performs the checks on the BGP message header:
+ *
+ * * Marker is all '1's
+ * * Length is <= BGP_MAX_MSG_L
+ * * Type is OPEN/UPDATE/NOTIFICATION/KEEPALIVE
+ *
+ */
+static void
+bgp_connection_read_action(qps_file qf, void* file_info)
+{
+ bgp_connection connection = file_info ;
+ int want ;
+ int ret ;
+
+ /* If nothing pending for partial packet, start reading new one. */
+
+ want = connection->read_pending ;
+ if (want == 0)
+ {
+ want = BGP_MH_HEAD_L ;
+ stream_reset(connection->ibuf) ;
+ connection->read_header = 1 ;
+ } ;
+
+ /* Loop to read entire BGP message into ibuf.
+ *
+ * On error or "EOF", raises suitable FSM events and returns.
+ *
+ * If cannot read entire message, sets new pending count and returns.
+ *
+ * Exits loop iff completes a BGP message.
+ */
+ while (1)
+ {
+ ret = stream_read_unblock(connection->ibuf, qps_file_fd(&connection->qf),
+ want) ;
+ if (ret >= 0)
+ {
+ want -= ret ;
+ if (want != 0)
+ {
+ connection->read_pending = want ;
+ return ; /* must wait for the rest */
+ } ;
+
+ if (!connection->read_header)
+ break ; /* got complete message */
+
+ connection->read_header = 0 ; /* got complete header */
+
+ want = bgp_msg_check_header(connection->ibuf) ;
+ /* returns balance of message */
+ if (want < 0)
+ return ; /* failed in header check */
+ }
+ else
+ {
+ bgp_fsm_io_error(connection, (ret == -1) ? errno : 0) ;
+ return ;
+ } ;
+ } ;
+
+ /* Deal with the BGP message. MUST remove from ibuf before returns ! */
+ bgp_msg_dispatch(connection) ;
+
+ /* Ready to read another message */
+ connection->read_pending = 0 ;
+} ;
+
diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h
new file mode 100644
index 00000000..037f9a77
--- /dev/null
+++ b/bgpd/bgp_connection.h
@@ -0,0 +1,213 @@
+/* BGP Connection Handling -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_CONNECTION_H
+#define _QUAGGA_BGP_CONNECTION_H
+
+#include "lib/mqueue.h"
+#include "lib/qpthreads.h"
+#include "lib/qtimers.h"
+#include "lib/qpselect.h"
+
+#include "lib/sockunion.h"
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_notification.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * The BGP Finite State Machine: states and events
+ *
+ */
+
+typedef enum bgp_fsm_states bgp_fsm_state_t ;
+enum bgp_fsm_states
+{
+ bgp_fsm_first_state = 0,
+
+ bgp_fsm_Initial = 0, /* extra: connection initialised */
+
+ bgp_fsm_Idle = 1, /* waiting for Idle Hold time */
+ bgp_fsm_Connect = 2, /* waiting for connect (may be listening) */
+ bgp_fsm_Active = 3, /* listening only */
+ bgp_fsm_OpenSent = 4, /* sent Open -- awaits Open */
+ bgp_fsm_OpenConfirm = 5, /* sent & received Open -- awaits keepalive */
+ bgp_fsm_Established = 6, /* running connection */
+
+ bgp_fsm_Stopping = 7, /* extra: connection shutting down */
+
+ bgp_fsm_last_state = 7,
+} ;
+
+typedef enum bgp_fsm_events bgp_fsm_event_t ;
+enum bgp_fsm_events
+{
+ bgp_fsm_null_event = 0,
+
+ bgp_fsm_BGP_Start = 1,
+ bgp_fsm_BGP_Stop = 2,
+ bgp_fsm_TCP_connection_open = 3,
+ bgp_fsm_TCP_connection_closed = 4,
+ bgp_fsm_TCP_connection_open_failed = 5,
+ bgp_fsm_TCP_fatal_error = 6,
+ bgp_fsm_ConnectRetry_timer_expired = 7,
+ bgp_fsm_Hold_Timer_expired = 8,
+ bgp_fsm_KeepAlive_timer_expired = 9,
+ bgp_fsm_Receive_OPEN_message = 10,
+ bgp_fsm_Receive_KEEPALIVE_message = 11,
+ bgp_fsm_Receive_UPDATE_message = 12,
+ bgp_fsm_Receive_NOTIFICATION_message = 13,
+ bgp_fsm_Sent_NOTIFICATION_message = 14,
+
+ bgp_fsm_last_event = 15,
+} ;
+
+/*==============================================================================
+ * BGP Connection Structure
+ *
+ * The BGP Connection is the main data structure for the BGP Engine.
+ *
+ * When a session terminates, or a connection is shut it may have a short
+ * independent life, if a NOTIFICATION message is pending.
+ *
+ */
+
+typedef struct bgp_wbuffer* bgp_wbuffer ;
+struct bgp_wbuffer
+{
+ int full ; /* not enough room for max length BGP message */
+
+ uint8_t* p_out ;
+ uint8_t* p_in ;
+
+ uint8_t* base ;
+ uint8_t* limit ;
+} ;
+
+/* TODO: management of the pending_queue... */
+/* need something at the Engine level to drain the pending queue */
+/* when/while obuf is not full.... */
+/* need to be able to activate and deactivate that */
+
+struct bgp_connection
+{
+ bgp_session session ; /* session connection belongs to */
+ /* NULL if connection stopping */
+ qpt_mutex p_mutex ; /* session mutex* */
+ /* NULL if connection stopping */
+
+ bgp_connection_ordinal_t ordinal ; /* primary/secondary connection */
+ int accepted ; /* came via accept() */
+
+ bgp_fsm_state_t state ; /* FSM state of connection */
+
+ bgp_connection next ; /* for the connection queue */
+ bgp_connection prev ; /* NULL <=> not on the queue */
+
+ int fsm_active ; /* active in fsm count */
+ bgp_fsm_event_t post ; /* event raised within FSM */
+
+ bgp_stopped_cause_t stopped ; /* why stopped */
+ bgp_notify notification ; /* if any sent/received */
+
+ bgp_open_state open_recv ; /* the open received. */
+
+ struct qps_file qf ; /* qpselect file structure */
+ int err ; /* error number -- if any */
+
+ union sockunion su_local ; /* address of the near end */
+ union sockunion su_remote ; /* address of the far end */
+
+ char* host ; /* peer "name" + Connect/Listen */
+ struct zlog* log ; /* where to log to */
+
+ unsigned hold_timer_interval ; /* subject to negotiation */
+ unsigned keepalive_timer_interval ; /* subject to negotiation */
+
+ struct qtimer hold_timer ;
+ struct qtimer keepalive_timer ;
+
+ struct stream* ibuf ; /* a single input "stream" */
+ unsigned read_pending ; /* how much input waiting for */
+ int read_header ; /* reading message header */
+
+ struct stream* obuf ; /* a single output "stream" */
+
+ int notification_pending ; /* waiting to write NOTIFICATION */
+
+ struct mqueue_local_queue
+ pending_queue ; /* pending write messages */
+
+ struct bgp_wbuffer wbuff ; /* write buffer */
+} ;
+
+/*==============================================================================
+ *
+ */
+
+extern bgp_connection
+bgp_connection_init_new(bgp_connection connection, bgp_session session,
+ bgp_connection_ordinal_t ordinal) ;
+extern bgp_connection
+bgp_connection_reset(bgp_connection connection, int free_structure) ;
+
+
+extern void
+bgp_connection_open(bgp_connection connection, int fd) ;
+
+extern void
+bgp_connection_close(bgp_connection connection) ;
+
+extern void
+bgp_connection_read_close(bgp_connection connection) ;
+
+extern int
+bgp_connection_write(bgp_connection connection) ;
+
+
+/*==============================================================================
+ * Access functions via bgp_connection for bgp_session attributes.
+ *
+ *
+ *
+ */
+
+Inline void
+BGP_CONNECTION_SESSION_LOCK(bgp_connection connection)
+{
+ if (connection->p_mutex != NULL)
+ qpt_mutex_lock(connection->p_mutex) ;
+} ;
+
+Inline void
+BGP_CONNECTION_SESSION_UNLOCK(bgp_connection connection)
+{
+ if (connection->p_mutex != NULL)
+ qpt_mutex_unlock(connection->p_mutex) ;
+} ;
+
+
+#endif /* QUAGGA_BGP_CONNECTION_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index c2077a59..9267eebd 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -29,13 +29,20 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "log.h"
#include "sockunion.h"
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_connection.h"
+
#include "bgpd/bgpd.h"
+#include "bgpd/bgp_peer.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_community.h"
+
unsigned long conf_bgp_debug_as4;
unsigned long conf_bgp_debug_fsm;
unsigned long conf_bgp_debug_events;
@@ -57,18 +64,18 @@ unsigned long term_bgp_debug_normal;
unsigned long term_bgp_debug_zebra;
/* messages for BGP-4 status */
-const struct message bgp_status_msg[] =
-{
- { Idle, "Idle" },
- { Connect, "Connect" },
- { Active, "Active" },
- { OpenSent, "OpenSent" },
- { OpenConfirm, "OpenConfirm" },
- { Established, "Established" },
- { Clearing, "Clearing" },
- { Deleted, "Deleted" },
+const struct message bgp_status_msg[] =
+{
+ { bgp_fsm_Initial, "Initial" },
+ { bgp_fsm_Idle, "Idle" },
+ { bgp_fsm_Connect, "Connect" },
+ { bgp_fsm_Active, "Active" },
+ { bgp_fsm_OpenSent, "OpenSent" },
+ { bgp_fsm_OpenConfirm, "OpenConfirm" },
+ { bgp_fsm_Established, "Established" },
+ { bgp_fsm_Stopping, "Stopping" },
};
-const int bgp_status_msg_max = BGP_STATUS_MAX;
+const int bgp_status_msg_max = bgp_fsm_last_state + 1 ;
/* BGP message type string. */
const char *bgp_type_str[] =
@@ -83,7 +90,7 @@ const char *bgp_type_str[] =
};
/* message for BGP-4 Notify */
-static const struct message bgp_notify_msg[] =
+static const struct message bgp_notify_msg[] =
{
{ BGP_NOTIFY_HEADER_ERR, "Message Header Error"},
{ BGP_NOTIFY_OPEN_ERR, "OPEN Message Error"},
@@ -95,7 +102,7 @@ static const struct message bgp_notify_msg[] =
};
static const int bgp_notify_msg_max = BGP_NOTIFY_MAX;
-static const struct message bgp_notify_head_msg[] =
+static const struct message bgp_notify_head_msg[] =
{
{ BGP_NOTIFY_HEADER_NOT_SYNC, "/Connection Not Synchronized"},
{ BGP_NOTIFY_HEADER_BAD_MESLEN, "/Bad Message Length"},
@@ -103,19 +110,19 @@ static const struct message bgp_notify_head_msg[] =
};
static const int bgp_notify_head_msg_max = BGP_NOTIFY_HEADER_MAX;
-static const struct message bgp_notify_open_msg[] =
+static const struct message bgp_notify_open_msg[] =
{
{ BGP_NOTIFY_OPEN_UNSUP_VERSION, "/Unsupported Version Number" },
{ BGP_NOTIFY_OPEN_BAD_PEER_AS, "/Bad Peer AS"},
{ BGP_NOTIFY_OPEN_BAD_BGP_IDENT, "/Bad BGP Identifier"},
{ BGP_NOTIFY_OPEN_UNSUP_PARAM, "/Unsupported Optional Parameter"},
{ BGP_NOTIFY_OPEN_AUTH_FAILURE, "/Authentication Failure"},
- { BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"},
+ { BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"},
{ BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"},
};
static const int bgp_notify_open_msg_max = BGP_NOTIFY_OPEN_MAX;
-static const struct message bgp_notify_update_msg[] =
+static const struct message bgp_notify_update_msg[] =
{
{ BGP_NOTIFY_UPDATE_MAL_ATTR, "/Malformed Attribute List"},
{ BGP_NOTIFY_UPDATE_UNREC_ATTR, "/Unrecognized Well-known Attribute"},
@@ -144,7 +151,7 @@ static const struct message bgp_notify_cease_msg[] =
};
static const int bgp_notify_cease_msg_max = BGP_NOTIFY_CEASE_MAX;
-static const struct message bgp_notify_capability_msg[] =
+static const struct message bgp_notify_capability_msg[] =
{
{ BGP_NOTIFY_CAPABILITY_INVALID_ACTION, "/Invalid Action Value" },
{ BGP_NOTIFY_CAPABILITY_INVALID_LENGTH, "/Invalid Capability Length"},
@@ -176,15 +183,15 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
char addrbuf[BUFSIZ];
/* Add MP case. */
- if (attr->extra->mp_nexthop_len == 16
+ if (attr->extra->mp_nexthop_len == 16
|| attr->extra->mp_nexthop_len == 32)
snprintf (buf + strlen (buf), size - strlen (buf), ", mp_nexthop %s",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
addrbuf, BUFSIZ));
if (attr->extra->mp_nexthop_len == 32)
snprintf (buf + strlen (buf), size - strlen (buf), "(%s)",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+ inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
addrbuf, BUFSIZ));
}
#endif /* HAVE_IPV6 */
@@ -193,11 +200,11 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
snprintf (buf + strlen (buf), size - strlen (buf), ", localpref %d",
attr->local_pref);
- if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
snprintf (buf + strlen (buf), size - strlen (buf), ", metric %d",
attr->med);
- if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
snprintf (buf + strlen (buf), size - strlen (buf), ", community %s",
community_str (attr->community));
@@ -223,7 +230,7 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
inet_ntoa (attr->extra->cluster->list[i]));
}
- if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
snprintf (buf + strlen (buf), size - strlen (buf), ", path %s",
aspath_print (attr->aspath));
@@ -235,14 +242,14 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
/* dump notify packet */
void
-bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
+bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
const char *direct)
{
const char *subcode_str;
subcode_str = "";
- switch (bgp_notify->code)
+ switch (bgp_notify->code)
{
case BGP_NOTIFY_HEADER_ERR:
subcode_str = LOOKUP (bgp_notify_head_msg, bgp_notify->subcode);
@@ -282,14 +289,14 @@ bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
subcode_str, bgp_notify->length,
bgp_notify->data ? bgp_notify->data : "");
}
-
+
/* Debug option setting interface. */
unsigned long bgp_debug_option = 0;
-int
+int
debug (unsigned int option)
{
- return bgp_debug_option & option;
+ return bgp_debug_option & option;
}
DEFUN (debug_bgp_as4,
@@ -585,7 +592,7 @@ DEFUN (debug_bgp_update_direct,
DEBUG_ON (update, UPDATE_IN);
}
else
- {
+ {
DEBUG_OFF (update, UPDATE_IN);
DEBUG_ON (update, UPDATE_OUT);
}
@@ -738,7 +745,7 @@ DEFUN (no_debug_bgp_all,
TERM_DEBUG_OFF (filter, FILTER);
TERM_DEBUG_OFF (zebra, ZEBRA);
vty_out (vty, "All possible debugging has been turned off%s", VTY_NEWLINE);
-
+
return CMD_SUCCESS;
}
diff --git a/bgpd/bgp_engine.c b/bgpd/bgp_engine.c
new file mode 100644
index 00000000..31919b27
--- /dev/null
+++ b/bgpd/bgp_engine.c
@@ -0,0 +1,192 @@
+/* BGP Engine pThread -- functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "bgpd/bgp_engine.h"
+
+#include "lib/memory.h"
+#include "lib/symtab.h"
+
+/*==============================================================================
+ * The BGP Engine pThread contains:
+ *
+ * * the BGP Finite State Machine (FSM) for BGP Sessions
+ * * the encoding and decoding of BGP protocol messages
+ * * all related socket handling and I/O
+ * * all related timers
+ *
+ * The BGP Engine communicates with the BGP Routeing pthread(s) via two Message
+ * Queues.
+ *
+ * There are also shared pools of data:
+ *
+ * * the BGP session structures, which ...
+ * * the Attributes Store
+ * * the Prefixes Store
+ * ...
+ *
+ * Other pthread-safe facilities used:
+ *
+ * * logging
+ * * privilege raising/lowering
+ * ...
+ *
+ * At the heart of the BGP Engine pthread is a select for the socket I/O, a
+ * Message Queue reader and a timer handler. The Message Queue uses SIGUSR2 to
+ * kick the pthread into action if it is stopped on the select.
+ *
+ */
+
+/*==============================================================================
+ * The qpnexus for the BGP Engine.
+ *
+ *
+ */
+
+qpn_nexus p_bgp_engine ;
+
+static struct qpn_nexus bgp_engine ;
+
+
+/*==============================================================================
+ * Start the BGP Engine Thread.
+ *
+ * Initialise the Engine Thread qpnexus
+ *
+ */
+
+static void* bgp_engine_loop(void* arg) ;
+
+extern void
+bgp_engine_start(void)
+{
+ p_bgp_engine = qpn_init_new(&bgp_engine) ;
+
+ p_bgp_engine->start = bgp_engine_loop ;
+
+ p_bgp_engine->thread_id = qpt_thread_self() ;
+
+ p_bgp_engine->selection = qps_selection_init_new(NULL) ;
+ p_bgp_engine->pile = qtimer_pile_init_new(NULL) ;
+ p_bgp_engine->queue = mqueue_init_new(NULL, mqt_signal_broadcast) ;
+ p_bgp_engine->mts = mqueue_thread_signal_init(NULL,
+ p_bgp_engine->thread_id, SIGMQUEUE) ;
+
+ qpn_exec(p_bgp_engine) ;
+} ;
+
+/*==============================================================================
+ * The BGP Engine Thread main loop
+ *
+ * Processes:
+ *
+ * 1) connections with pending work -- local queue.
+ *
+ * When a connection fills its output buffers, any further messages
+ * requiring output are placed on the connection's pending queue.
+ *
+ * When the output buffers empty sufficiently, some of those messages
+ * can (finally) be processed.
+ *
+ * So this is done first.
+ *
+ * [This is also where stopped connections are finally reaped.]
+ *
+ * 2) messages coming from the Routeing Engine -- mqueue_queue.
+ *
+ * These will mostly be BGP UPDATE messages, which will either be
+ * processed into the relevant connection's output buffers, or end up
+ * on its pending queue.
+ *
+ * There is a flow control mechanism to prevent the Routeing Engine from
+ * flooding the BGP Engine with UPDATE messages.
+ *
+ * Other messages start/stop sessions and so on.
+ *
+ * 3) I/O -- qpselect
+ *
+ * This deals with all active sockets for read/write/connect/accept.
+ *
+ * Each time a socket is readable, one message is read and dispatched to
+ * the Routeing Engine (or otherwise dealt with by the FSM).
+ *
+ * Each time a socket is writable, as much as possible is written to it
+ * from the connection's write buffer. When the write buffer is drained,
+ * the connection goes back onto the local queue.
+ *
+ * 4) Timers -- qtimers
+ *
+ * Which generate FSM events.
+ *
+ *
+ */
+
+
+
+
+
+
+/*==============================================================================
+ * BGP Session Handling
+ */
+
+
+/* BGP Engine Action: enable given session
+ *
+ * arg0 -- bgp_session to be enabled
+ *
+ *
+ */
+
+extern void
+bgp_session_enable_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_session session ;
+ bgp_connection connection ;
+
+ /* Construct bgp_connection for the new session. */
+
+ session = mqb_get_arg0(mqb) ;
+ connection = bgp_connection_init_new(NULL, session, 0) ;
+
+ /* */
+
+} ;
+
+
+/*==============================================================================
+ * The qpnexus for the BGP Engine.
+ *
+ *
+ */
+
+
+
+/*==============================================================================
+ * The write queue for the BGP Engine
+ *
+ * Each connection has a single message buffer. When that has yet to be
+ *
+ *
+ */
+
+
diff --git a/bgpd/bgp_engine.h b/bgpd/bgp_engine.h
new file mode 100644
index 00000000..9245d21a
--- /dev/null
+++ b/bgpd/bgp_engine.h
@@ -0,0 +1,50 @@
+/* BGP Engine pThread -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_ENGINE_H
+#define _QUAGGA_BGP_ENGINE_H
+
+#include "bgpd/bgp_common.h"
+
+#include "lib/mqueue.h"
+#include "lib/qpthreads.h"
+#include "lib/qtimers.h"
+#include "lib/qpselect.h"
+#include "lib/qpnexus.h"
+
+#include "lib/sockunion.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+
+
+
+extern qpn_nexus p_bgp_engine ;
+
+extern void
+bgp_engine_start(void) ;
+
+
+
+
+#endif /* QUAGGA_BGP_ENGINE_H */
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index c815f9a1..f5755cfa 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -1,1123 +1,2007 @@
-/* BGP-4 Finite State Machine
- From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
- Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
-
-This file is part of GNU Zebra.
-
-GNU Zebra is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
-
-GNU Zebra is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Zebra; see the file COPYING. If not, write to the Free
-Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+/* BGP-4 Finite State Machine
+ * From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
+ * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+ *
+ * Recast for pthreaded bgpd: Copyright (C) Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
#include <zebra.h>
+#include "bgpd/bgp.h"
-#include "linklist.h"
-#include "prefix.h"
-#include "vty.h"
-#include "sockunion.h"
-#include "thread.h"
#include "log.h"
-#include "stream.h"
-#include "memory.h"
-#include "plist.h"
-#include "bgpd/bgpd.h"
-#include "bgpd/bgp_attr.h"
-#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_connection.h"
+#include "bgpd/bgp_notification.h"
#include "bgpd/bgp_fsm.h"
+
+#include "lib/qtimers.h"
+
+#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_network.h"
-#include "bgpd/bgp_route.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_open.h"
#ifdef HAVE_SNMP
#include "bgpd/bgp_snmp.h"
#endif /* HAVE_SNMP */
-
-/* BGP FSM (finite state machine) has three types of functions. Type
- one is thread functions. Type two is event functions. Type three
- is FSM functions. Timer functions are set by bgp_timer_set
- function. */
-
-/* BGP event function. */
-int bgp_event (struct thread *);
-
-/* BGP thread functions. */
-static int bgp_start_timer (struct thread *);
-static int bgp_connect_timer (struct thread *);
-static int bgp_holdtime_timer (struct thread *);
-static int bgp_keepalive_timer (struct thread *);
-
-/* BGP FSM functions. */
-static int bgp_start (struct peer *);
-
-/* BGP start timer jitter. */
-static int
-bgp_start_jitter (int time)
-{
- return ((rand () % (time + 1)) - (time / 2));
-}
-
-/* Check if suppress start/restart of sessions to peer. */
-#define BGP_PEER_START_SUPPRESSED(P) \
- (CHECK_FLAG ((P)->flags, PEER_FLAG_SHUTDOWN) \
- || CHECK_FLAG ((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW))
-/* Hook function called after bgp event is occered. And vty's
- neighbor command invoke this function after making neighbor
- structure. */
-void
-bgp_timer_set (struct peer *peer)
-{
- int jitter = 0;
-
- switch (peer->status)
- {
- case Idle:
- /* First entry point of peer's finite state machine. In Idle
- status start timer is on unless peer is shutdown or peer is
- inactive. All other timer must be turned off */
- if (BGP_PEER_START_SUPPRESSED (peer) || ! peer_active (peer))
- {
- BGP_TIMER_OFF (peer->t_start);
- }
- else
- {
- jitter = bgp_start_jitter (peer->v_start);
- BGP_TIMER_ON (peer->t_start, bgp_start_timer,
- peer->v_start + jitter);
- }
- BGP_TIMER_OFF (peer->t_connect);
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
- break;
-
- case Connect:
- /* After start timer is expired, the peer moves to Connnect
- status. Make sure start timer is off and connect timer is
- on. */
- BGP_TIMER_OFF (peer->t_start);
- BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect);
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
- break;
-
- case Active:
- /* Active is waiting connection from remote peer. And if
- connect timer is expired, change status to Connect. */
- BGP_TIMER_OFF (peer->t_start);
- /* If peer is passive mode, do not set connect timer. */
- if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)
- || CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
- {
- BGP_TIMER_OFF (peer->t_connect);
- }
- else
- {
- BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect);
- }
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
- break;
+/*==============================================================================
+ * The BGP Finite State Machine
+ *
+ * The state machine is represented as a table, indexed by [state, event],
+ * giving an action to be performed to deal with the event and the state that
+ * will advance to (or stay at).
+ *
+ * In some cases the action routine may override the the default new state.
+ *
+ * When a new state is entered, bgp_fsm_state_change() is called to complete
+ * the transition (in particular to set/unset timers).
+ *
+ * The fsm action functions are called with the session locked.
+ *
+ *------------------------------------------------------------------------------
+ * FSM "events"
+ *
+ * These are raised when:
+ *
+ * * the BGP Engine receives instructions from the Routeing Engine
+ *
+ * * some I/O operations complete
+ *
+ * * timers go off
+ *
+ * and the mechanism is to call bgp_fsm_event().
+ *
+ * Note that the event is dealt with *immediately* -- there is no queueing of
+ * events. The problem with queueing events is that the state of the connection
+ * is "out of date" until its event queue is emptied, so decisions made in the
+ * meantime may be wrong.
+ *
+ * The downside is that need to deal with the possibility of events being
+ * raised while the FSM is in some indeterminate state while processing an
+ * event for the current connection. This requires a little care.
+ *
+ * Timers, messages, and qpselect actions all occur outside the FSM. So there
+ * is no problem there.
+ *
+ * However, the FSM does some I/O operations -- notably write() and connect().
+ * These may complete immediately, and may need to trigger a new event. To
+ * handle this, the connection can set a "post" event, to be processed at the
+ * tail end of the current event processing.
+ *
+ * Note that there is only one level of "post" event. The FSM only ever issues
+ * one I/O operation per event. (It's a RULE.)
+ *
+ *------------------------------------------------------------------------------
+ * Primary and Secondary Connections
+ *
+ * To support BGP's "symmetrical" open strategy, this code allows for two
+ * connections to be made for a session -- one connect() and one accept().
+ * If two connections are made, only one will reach Established state.
+ *
+ * Up to Established state, the primary connection will be the out-bound
+ * connect() connection (if allowed) and the secondary will be the in-bound
+ * accept() connection (if allowed).
+ *
+ * The session->accept flag is set iff the secondary connection is prepared to
+ * accept a connection. The flag is cleared as soon as a connection is
+ * accepted.
+ *
+ * When a session is enabled, the allowed connections are initialised and
+ * a BGP_Start event issued for each one.
+ *
+ *------------------------------------------------------------------------------
+ * Error Handling.
+ *
+ * I/O and other operations may fail. When they do one of four events may
+ * be generated:
+ *
+ * a. BGP_Stop
+ *
+ * The functions bgp_fsm_stop_connection() and bgp_fsm_stop_session()
+ * set the cause of stop and generate a BGP_Stop event for one or both
+ * connections. The session is stopped if no connections remain.
+ *
+ * (The FSM itself uses bgp_fsm_set_stopping() before moving to
+ * Stopping state.)
+ *
+ * b. TCP_connection_closed ("soft" error)
+ *
+ * A read or write operation finds that the connection has been closed.
+ *
+ * This is raised when a read operation returns 0 bytes.
+ *
+ * Is also raised when read or write see the errors:
+ *
+ * ECONNRESET, ENETDOWN, ENETUNREACH, EPIPE or ETIMEDOUT
+ *
+ * Other errors are reported as TCP_fatal_error.
+ *
+ * The function bgp_fsm_io_error() is used by read and write operations to
+ * signal an error -- it decides which event to generate. (Error == 0 is
+ * used to signal a read operation that has returned 0.)
+ *
+ * c. TCP_connection_open_failed ("soft" error)
+ *
+ * A connect() operation has failed:
+ *
+ * ECONNREFUSED, ECONNRESET, EHOSTUNREACH or ETIMEDOUT
+ *
+ * Other errors are reported as TCP_fatal_error.
+ *
+ * The function bgp_fsm_connect_completed() decides what event to generate.
+ * (It will generate TCP_connection_open if there is no error.)
+ *
+ * All errors that accept() may raise are fatal.
+ *
+ * d. TCP_fatal_error ("hard" error)
+ *
+ * Raised by unexpected errors in connect/accept/read/write
+ *
+ * The function bgp_fsm_io_fatal_error() will generate a TCP_fatal_error.
+ */
+
+
+/*==============================================================================
+ * Functions to enable, stop, signal I/O events to, etc. the FSM
+ */
- case OpenSent:
- /* OpenSent status. */
- BGP_TIMER_OFF (peer->t_start);
- BGP_TIMER_OFF (peer->t_connect);
- if (peer->v_holdtime != 0)
- {
- BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
- peer->v_holdtime);
- }
- else
- {
- BGP_TIMER_OFF (peer->t_holdtime);
- }
- BGP_TIMER_OFF (peer->t_keepalive);
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
- break;
+static void
+bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification, int both) ;
- case OpenConfirm:
- /* OpenConfirm status. */
- BGP_TIMER_OFF (peer->t_start);
- BGP_TIMER_OFF (peer->t_connect);
-
- /* If the negotiated Hold Time value is zero, then the Hold Time
- timer and KeepAlive timers are not started. */
- if (peer->v_holdtime == 0)
- {
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- }
- else
- {
- BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
- peer->v_holdtime);
- BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer,
- peer->v_keepalive);
- }
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
- break;
+/*------------------------------------------------------------------------------
+ * Enable the given connection -- which must be newly initialised.
+ *
+ * This is the first step in the FSM, and the connection advances to Idle.
+ */
- case Established:
- /* In Established status start and connect timer is turned
- off. */
- BGP_TIMER_OFF (peer->t_start);
- BGP_TIMER_OFF (peer->t_connect);
-
- /* Same as OpenConfirm, if holdtime is zero then both holdtime
- and keepalive must be turned off. */
- if (peer->v_holdtime == 0)
- {
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- }
- else
- {
- BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
- peer->v_holdtime);
- BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer,
- peer->v_keepalive);
- }
- BGP_TIMER_OFF (peer->t_asorig);
- break;
- case Deleted:
- BGP_TIMER_OFF (peer->t_gr_restart);
- BGP_TIMER_OFF (peer->t_gr_stale);
- BGP_TIMER_OFF (peer->t_pmax_restart);
- case Clearing:
- BGP_TIMER_OFF (peer->t_start);
- BGP_TIMER_OFF (peer->t_connect);
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
+extern void
+bgp_fsm_enable_connection(bgp_connection connection)
+{
+ assert(connection->state == bgp_fsm_Initial) ;
+ bgp_fsm_event(connection, bgp_fsm_BGP_Start) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Bring given connection to a stop.
+ *
+ * If is the only connection for the session, then the session is stopped.
+ *
+ * Is given the reasons for the stop. This function looks after releasing the
+ * notification once it is finished with it.
+ *
+ * Records the reasons for the stop, and then generates a BGP_Stop event.
+ *
+ * This may be used to stop:
+ *
+ * * as requested by Routeing Engine. The notification, if any, should be a
+ * Cease.
+ *
+ * * because a problem with a BGP packet has arisen. The notification, if
+ * any, will describe the problem.
+ *
+ * Note that I/O problems are signalled by bgp_fsm_io_error().
+ *
+ * NB: may NOT be used within the FSM.
+ *
+ * NB: locks and unlocks the session.
+ */
+extern void
+bgp_fsm_stop_connection(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification)
+{
+ bgp_fsm_set_stopping(connection, cause, notification, 0) ;
+ bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Bring given connection to a stop, and the other connection (if any), and
+ * then the session.
+ *
+ * See bgp_fsm_stop_connection, above.
+ */
+extern void
+bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification)
+{
+ bgp_fsm_set_stopping(connection, cause, notification, 1) ;
+ bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal a fatal I/O error on the given connection.
+ *
+ * Error to be reported as "TCP_fatal_error".
+ */
+extern void
+bgp_fsm_io_fatal_error(bgp_connection connection, int err)
+{
+ connection->err = err ;
+
+ plog_err (connection->log, "%s [Error] bgp IO error: %s",
+ connection->host, safe_strerror(err)) ;
+
+ bgp_fsm_event(connection, bgp_fsm_TCP_fatal_error) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal an I/O error on the given connection.
+ *
+ * This is used by read/write operations -- so not until the TCP connection
+ * is up (which implies OpenSent state or later).
+ *
+ * It is assumed that the read/write code deals with EAGAIN/EWOULDBLOCK/EINTR
+ * itself -- so only real errors are reported here.
+ *
+ * A read operation that returns zero is reported here as err == 0.
+ *
+ * The following are reported as "TCP_connection_closed":
+ *
+ * 0, ECONNRESET, ENETDOWN, ENETUNREACH, EPIPE or ETIMEDOUT
+ *
+ * All other errors are reported as "TCP_fatal_error".
+ */
+extern void
+bgp_fsm_io_error(bgp_connection connection, int err)
+{
+ connection->err = err ;
+
+ if ( (err == 0)
+ || (err == ECONNRESET)
+ || (err == ENETDOWN)
+ || (err == ENETUNREACH)
+ || (err == EPIPE)
+ || (err == ETIMEDOUT) )
+ {
+ if (BGP_DEBUG(events, EVENTS))
+ if (err == 0)
+ plog_debug(connection->log,
+ "%s [Event] BGP connection closed fd %d",
+ connection->host, qps_file_fd(&connection->qf)) ;
+ else
+ plog_debug(connection->log,
+ "%s [Event] BGP connection closed fd %d (%s)",
+ connection->host, qps_file_fd(&connection->qf),
+ safe_strerror(err)) ;
+
+ bgp_fsm_event(connection, bgp_fsm_TCP_connection_closed) ;
}
-}
-
-/* BGP start timer. This function set BGP_Start event to thread value
- and process event. */
-static int
-bgp_start_timer (struct thread *thread)
+ else
+ bgp_fsm_io_fatal_error(connection, err) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal completion of a connect() or an accept() for the given connection.
+ *
+ * This is used by the connect() and accept() qpselect actions. It is also
+ * used if a connect() attempt fails immediately.
+ *
+ * If err == 0, then all is well: generate TCP_connection_open event.
+ *
+ * If err is one of:
+ *
+ * ECONNREFUSED, ECONNRESET, EHOSTUNREACH or ETIMEDOUT
+ *
+ * generate TCP_connection_open_failed event. (accept() does not return any of
+ * these errors.)
+ *
+ * Other errors are reported as TCP_fatal_error.
+ */
+extern void
+bgp_fsm_connect_completed(bgp_connection connection, int err)
{
- struct peer *peer;
-
- peer = THREAD_ARG (thread);
- peer->t_start = NULL;
-
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (start timer expire).", peer->host);
-
- THREAD_VAL (thread) = BGP_Start;
- bgp_event (thread); /* bgp_event unlocks peer */
-
- return 0;
-}
+ connection->err = err ;
+
+ if (err == 0)
+ bgp_fsm_event(connection, bgp_fsm_TCP_connection_open) ;
+ else if ( (err == ECONNREFUSED)
+ || (err == ECONNRESET)
+ || (err == EHOSTUNREACH)
+ || (err == ETIMEDOUT) )
+ bgp_fsm_event(connection, bgp_fsm_TCP_connection_open_failed) ;
+ else
+ bgp_fsm_io_fatal_error(connection, err) ;
+} ;
+
+/*==============================================================================
+ * For debug...
+ */
+#define BGP_FSM_DEBUG(connection, message) \
+ if (BGP_DEBUG (fsm, FSM)) \
+ plog_debug (connection->log, "%s [FSM] " message, connection->host)
+
+/*==============================================================================
+ * The FSM table and the finite state machine actions.
+ */
+
+typedef bgp_fsm_state_t
+bgp_fsm_action(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event) ;
+
+struct bgp_fsm {
+ bgp_fsm_action* action ;
+ bgp_fsm_state_t next_state ;
+} ;
+
+static bgp_fsm_action bgp_fsm_null ;
+static bgp_fsm_action bgp_fsm_invalid ;
+static bgp_fsm_action bgp_fsm_ignore ;
+static bgp_fsm_action bgp_fsm_enter ;
+static bgp_fsm_action bgp_fsm_start ;
+static bgp_fsm_action bgp_fsm_restart ;
+static bgp_fsm_action bgp_fsm_stop ;
+static bgp_fsm_action bgp_fsm_open ;
+static bgp_fsm_action bgp_fsm_failed ;
+static bgp_fsm_action bgp_fsm_fatal ;
+static bgp_fsm_action bgp_fsm_retry ;
+static bgp_fsm_action bgp_fsm_error ;
+static bgp_fsm_action bgp_fsm_expire ;
+static bgp_fsm_action bgp_fsm_opened ;
+static bgp_fsm_action bgp_fsm_establish ;
+static bgp_fsm_action bgp_fsm_closed ;
+static bgp_fsm_action bgp_fsm_kal_send ;
+static bgp_fsm_action bgp_fsm_kal_recv ;
+static bgp_fsm_action bgp_fsm_update ;
+static bgp_fsm_action bgp_fsm_notified ;
+static bgp_fsm_action bgp_fsm_done ;
-/* BGP connect retry timer. */
-static int
-bgp_connect_timer (struct thread *thread)
+static void
+bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
+
+/*------------------------------------------------------------------------------
+ * Finite State Machine events
+ *
+ * 0. null_event
+ *
+ * Do nothing. As quietly as possible.
+ *
+ * Never generated, so should not be seen !
+ *
+ * 1. BGP_Start
+ *
+ * a. in Initial state (-> Idle)
+ *
+ * raised immediately after creating the connection.
+ *
+ * b. in Idle state
+ *
+ * raised on expiry of IdleHoldTime.
+ *
+ * primary connection: proceed to Connect
+ *
+ * secondary connection: proceed to Accept
+ *
+ * Cannot happen at any other time.
+ *
+ * 2. BGP_Stop
+ *
+ * a. in all states:
+ *
+ * -- from Routeing Engine -- at any time.
+ *
+ * -- internally in the event of collision resolution.
+ *
+ * -- internally, in the event of some protocol error -- once
+ * connection is up and packets are being transferred.
+ *
+ * The reason for stopping is set in the connection before the event is
+ * generated.
+ *
+ * 3. TCP_connection_open
+ *
+ * a. primary connection: in Connect state (-> OpenSent)
+ *
+ * raised when a connect() connection succeeds
+ *
+ * b. secondary connection: in Active state (-> OpenSent)
+ *
+ * raised when an accept() connection is accepted.
+ *
+ * Cannot happen at any other time.
+ *
+ * 4. TCP_connection_closed
+ *
+ * Raised by "EOF" on read or by EPIPE and some other errors.
+ *
+ * a. in OpenSent and OpenConfirm states
+ *
+ * This may be because the the other end has detected a collision.
+ * It may be because the other end is being vexatious.
+ *
+ * Fall back to Idle.
+ *
+ * b. and Established state
+ *
+ * Stop the session.
+ *
+ * NB: any errors generated when the OPEN message is sent (on exit from
+ * Connect or Active states) are not delivered until has entered
+ * OpenSent state.
+ *
+ * Cannot happen at any other time.
+ *
+ * 5. TCP_connection_open_failed ("soft" error)
+ *
+ * a. in Connect State
+ *
+ * raised if connect() fails eg: ECONNREFUSED or ETIMEDOUT
+ *
+ * Cannot happen at any other time. In particular, any errors during an
+ * accept() are reported as TCP_fatal_error.
+ *
+ * 6. TCP_fatal_error ("hard" error)
+ *
+ * a. in all states other than Initial
+ *
+ * raised by unexpected errors in connect/accept/read/write
+ *
+ * Stops the connection and disables the type of connection. So,
+ * for the remains of this session, will not attempt to <<<<<<<<<<<<<<<<
+ *
+ * 7. ConnectRetry_timer_expired
+ *
+ * a. in either Connect or Active states ONLY.
+ *
+ * Time to give up current connection attempt(s), and start trying
+ * to connect all over again.
+ *
+ * Cannot happen at any other time.
+ *
+ * 8. Hold_Timer_expired
+ *
+ * a. in OpenSent state
+ *
+ * Time to give up waiting for an OPEN (or NOTIFICATION) from the
+ * other end.
+ *
+ * Fall back to Idle.
+ *
+ * For this state the RFC recommends a "large" value for the hold
+ * time -- and suggests 4 minutes.
+ *
+ * b. in OpenConfirm state
+ *
+ * Time to give up waiting for a KEEPALIVE to confirm the connection.
+ *
+ * Fall back to Idle.
+ *
+ * In this state the hold time used is that negotiated in the OPEN
+ * messages that have been exchanged.
+ *
+ * c. in Established state
+ *
+ * The session has failed. Stop.
+ *
+ * In this state the hold time used is that negotiated in the OPEN
+ * messages that have been exchanged.
+ *
+ * d. in Stopping state
+ *
+ * Time to give up trying to send NOTIFICATION and terminate the
+ * connection.
+ *
+ * Cannot happen at any other time.
+ *
+ * 9. KeepAlive_timer_expired
+ *
+ * a. in OpenConfirm and Established states
+ *
+ * Time to send a KEEPALIVE message.
+ *
+ * In these states the keepalive time used is that which follows
+ * from the hold time negotiated in the OPEN messages that have been
+ * exchanged.
+ *
+ * Cannot happen at any other time.
+ *
+ * 10. Receive_OPEN_message
+ *
+ * Generated by read action.
+ *
+ * a. in OpenSent state -- the expected response
+ *
+ * Proceed (via collision resolution) to OpenConfirm or Stopping.
+ *
+ * b. in OpenConfirm state -- FSM error
+ *
+ * Send NOTIFICATION. Fall back to Idle.
+ *
+ * c. in Established state -- FSM error
+ *
+ * Terminate session.
+ *
+ * Cannot happen at any other time (connection not up).
+ *
+ * 11. Receive_KEEPALIVE_message
+ *
+ * Generated by read action.
+ *
+ * a. in OpenSent state -- FSM error
+ *
+ * Fall
+ *
+ * b. in OpenConfirm state -- the expected response
+ *
+ * c. in Established state -- expected
+ *
+ * Cannot happen at any other time (connection not up).
+ *
+ * 12. Receive_UPDATE_message
+ *
+ * Generated by read action.
+ *
+ * a. in OpenSent and OpenConfirm states -- FSM error
+ *
+ * b. in Established state -- expected
+ *
+ * Cannot happen at any other time (connection not up).
+ *
+ * 13. Receive_NOTIFICATION_message
+ *
+ * Generated by read action.
+ *
+ * a. in OpenSent, OpenConfirm and Established states -- give up
+ * on the session.
+ *
+ * Cannot happen at any other time (connection not up).
+ *
+ * 14. Sent_NOTIFICATION_message
+ *
+ * Generated by write action when completed sending the message.
+ *
+ * a. in Stopping state -- the desired outcome
+ *
+ * Terminate the connection.
+ *
+ * Cannot happen at any other time.
+ */
+
+/*------------------------------------------------------------------------------
+ * Finite State Machine structure
+ */
+
+static const struct bgp_fsm
+bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
{
- struct peer *peer;
-
- peer = THREAD_ARG (thread);
- peer->t_connect = NULL;
-
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)",
- peer->host);
+ {
+ /* bgp_fsm_Initial: initialised in this state...............................
+ *
+ * Expect only a BGP_Start event, which arms the IdleHoldTimer and advances
+ * to the Idle state.
+ *
+ * Could (just) get a bgp_fsm_Stop if other connection stops immediately !
+ *
+ * A connection should be in this state for a brief period between being
+ * initialised and set going.
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_Initial}, /* null event */
+ {bgp_fsm_enter, bgp_fsm_Idle}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_Idle: waiting for IdleHoldTimer..................................
+ *
+ * When a session is enabled both its connections start in this state.
+ * (Noting that an accept() only session starts only the secondary
+ * connection and a connect() only session starts only the primary.)
+ *
+ * While in this state is waiting for the IdleHoldTimer to expire. This
+ * timer becomes longer if the peer misbehaves.
+ *
+ * If a connection stops at OpenState or OpenConfirm, may loop back through
+ * Idle, with an increased IdleHoldTimer.
+ *
+ * In Idle state the connection is dormant. (While the secondary is Idle,
+ * no connections will be accepted.)
+ *
+ * If the peer keeps making or accepting TCP connections, and then dropping
+ * them, then the IdleHoldTimer will grow to slow down the rate of vexatious
+ * connections.
+ *
+ * When a connection falls back to Idle it will have been closed.
+ *
+ * The expected events are:
+ *
+ * * BGP_Start -- generated by IdleHoldTimer expired
+ *
+ * For primary connection:
+ *
+ * Causes a connect() to be attempted.
+ *
+ * * Connect state -- if connect() OK, or failed "soft"
+ *
+ * * Stopping state -- if connect() failed "hard"
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * For secondary connection:
+ *
+ * Enables session->accept, and goes to "Active" state.
+ *
+ * Note that bgp_fsm_start() decides on the appropriate next state.
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_Idle}, /* null event */
+ {bgp_fsm_start, bgp_fsm_Connect}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_Connect: waiting for connect (and listen)........................
+ *
+ * Only the primary connection can be in this state.
+ *
+ * While in this state is waiting for connection to succeed or fail, or for
+ * the ConnectRetryTimer to expire.
+ *
+ * The expected events are:
+ *
+ * * TCP_connection_open
+ *
+ * Send BGP OPEN message, arm the HoldTimer ("large" value) and advance
+ * to OpenSent.
+ *
+ * * TCP_connection_open_fail ("soft" error)
+ *
+ * Shut down the connection. Stay in Connect state.
+ *
+ * The ConnectRetryTimer is left running.
+ *
+ * * TCP_fatal_error ("hard" error)
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * ConnectRetry_timer_expired
+ *
+ * Shut down the connection. Retry opening a connection. Stay in
+ * Connect state. Refresh the ConnectRetryTimer.
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_Connect}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_open, bgp_fsm_OpenSent}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */
+ {bgp_fsm_failed, bgp_fsm_Connect}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_retry, bgp_fsm_Connect}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_Active: waiting for listen (only)................................
+ *
+ * Only the secondary connection can be in this state.
+ *
+ * While in this state is waiting for an incoming connection to succeed or
+ * for the ConnectRetryTimer to expire.
+ *
+ * The expected events are:
+ *
+ * * TCP_connection_open
+ *
+ * Send BGP OPEN message, arm the HoldTimer ("large" value) and advance
+ * to OpenSent.
+ *
+ * * TCP_fatal_error
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * ConnectRetry_timer_expired
+ *
+ * Stay in Active state. Refresh the ConnectRetryTimer.
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_Active}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_open, bgp_fsm_OpenSent}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_retry, bgp_fsm_Active}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_OpenSent: waiting for Open from the other end....................
+ *
+ * Both primary and secondary connections can be in this state.
+ *
+ * While in this state is waiting for a BGP OPEN to arrive or for the
+ * HoldTimer ("large" value) to expire.
+ *
+ * The expected events are:
+ *
+ * * Receive_OPEN_message
+ *
+ * This means has received a satisfactory BGP OPEN from the other end,
+ * so the session is very nearly up.
+ *
+ * If there is another connection, and it is in OpenConfirm state,
+ * then must now choose between the two -- terminating one or the
+ * other with a "Connection Collision Resolution" NOTIFICATION message.
+ *
+ * If proceeding, send a BGP KEEPALIVE message (effectively ACK), arm
+ * HoldTimer and KeepliveTimer (as per negotiated values) and advance
+ * to OpenConfirm state.
+ *
+ * * Receive_UPDATE_message
+ *
+ * FSM error -- bring connection to a dead stop.
+ *
+ * * Receive_KEEPALIVE_message
+ *
+ * FSM error -- bring connection to a dead stop.
+ *
+ * * Receive_NOTIFICATION_message
+ *
+ * Bring connection to a dead stop.
+ *
+ * * TCP_connection_closed
+ *
+ * Close connection,
+ *
+ * * TCP_fatal_error
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * Hold_Timer_expired
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_OpenSent}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
+ {bgp_fsm_restart, bgp_fsm_Idle}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_restart, bgp_fsm_Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_opened, bgp_fsm_OpenConfirm}, /* Receive_OPEN_message */
+ {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_notified, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_OpenConfirm: Opens sent and received, waiting for KeepAlive......
+ *
+ * Only one of the two connections can reach this state.
+ *
+ * While in this state is waiting for a BGP KEEPALIVE to arrive or for the
+ * HoldTimer to expire, or for the KeepaliveTimer to prompt sending of
+ * another KEEPALIVE message.
+ *
+ * The expected events are:
+ *
+ * * Receive_KEEPALIVE_message
+ *
+ * This means that the other end is acknowledging the OPEN, and the
+ * session is now Established.
+ *
+ * If there is another connection, now is the time to kill it off.
+ *
+ * This connection becomes the primary and only connection.
+ *
+ * Arm HoldTimer and KeepliveTimer (as per negotiated values) and
+ * advance to Established state.
+ *
+ * Pass a session established message to the Routeing Engine, complete
+ * with the bgp_open_state for the successful connection.
+ *
+ * * Receive_OPEN_message
+ *
+ * FSM error -- bring connection to a dead stop.
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * Receive_UPDATE_message
+ *
+ * FSM error -- bring connection to a dead stop.
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * Receive_NOTIFICATION_message
+ *
+ * Bring connection to a dead stop.
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * TCP_connection_closed
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * TCP_fatal_error
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * KeepAlive_Timer_expired
+ *
+ * Send KEEPALIVE message and recharge KeepaliveTimer.
+ *
+ * * Hold_Timer_expired
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_OpenConfirm}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
+ {bgp_fsm_restart, bgp_fsm_Idle}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_restart, bgp_fsm_Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_kal_send, bgp_fsm_OpenConfirm}, /* KeepAlive_timer_expired */
+ {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_establish, bgp_fsm_Established}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_notified, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_Established: session is up and running...........................
+ *
+ * Only the primary connection exists in this state.
+ *
+ * While in this state is waiting for a BGP UPDATE (or KEEPALIVE) messages
+ * to arrive or for the HoldTimer to expire, or for the KeepaliveTimer to
+ * prompt sending of another KEEPALIVE message.
+ *
+ * The expected events are:
+ *
+ * * Receive_OPEN_message
+ *
+ * FSM error -- bring connection and session to a dead stop.
+ *
+ * * Receive_UPDATE_message
+ *
+ * Restart the HoldTimer.
+ *
+ * * Receive_KEEPALIVE_message
+ *
+ * Restart the HoldTimer.
+ *
+ * * Receive_NOTIFICATION_message
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * TCP_connection_closed
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * TCP_fatal_error
+ *
+ * Bring connection and session to a dead stop.
+ *
+ * * KeepAlive_Timer_expired
+ *
+ * Send KEEPALIVE message and recharge KeepaliveTimer.
+ *
+ * * Hold_Timer_expired
+ *
+ * If primary, promote the secondary. If no secondary...
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_Established}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
+ {bgp_fsm_closed, bgp_fsm_Stopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_expire, bgp_fsm_Stopping}, /* Hold_Timer_expired */
+ {bgp_fsm_kal_send, bgp_fsm_Established}, /* KeepAlive_timer_expired */
+ {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_kal_recv, bgp_fsm_Established}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_update, bgp_fsm_Established}, /* Receive_UPDATE_message */
+ {bgp_fsm_notified, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_Stopping: waiting (briefly) to send Notification.................
+ *
+ * Before a connection is sent to Stopping state the reasons for stopping
+ * are set. (See bgp_fsm_set_stopping.)
+ *
+ * There are two flavours of stop:
+ *
+ * * stop-idle
+ *
+ * Close the connection, then fall back to Idle state.
+ *
+ * * stop-dead
+ *
+ * Close and terminate the connection (cut loose from session).
+ *
+ * If this is the only connection, stop the session.
+ *
+ * The complication is the possible need to send a NOTIFICATION message
+ * before closing the connection.
+ *
+ * Once a connection has reached Established state, the TCP write buffers
+ * may be full, so it may not be possible immediately to send the
+ * NOTIFICATION. Note that stopping from Established state is always
+ * stop-dead.
+ *
+ * In other states there should be plenty of room in the TCP write buffers.
+ *
+ * On entry to Stopping:
+ *
+ * 1) if this is stop-dead -- unlink self from session.
+ *
+ * NB: this clears the pointer from session to connection.
+ *
+ * ....
+ *
+ * 2) if there is a NOTIFICATION message (notification_pending):
+ *
+ * * close the connection for reading and purge read buffers
+ * * purge the write buffering and any pending writes
+ * * stop all timers
+ * * send the NOTIFICATION
+ *
+ * if the NOTIFICATION immediately clears the buffers (or fails),
+ * clear the notification_pending flag.
+ *
+ * 3) if the notification_pending flag is still set:
+ *
+ * * for stop-idle set a short time-out (5 seconds)
+ * * for stop-dead set a longer time-out (30 seconds)
+ *
+ * stays in Stopping state, waiting for NOTIFICATION to be sent, or
+ * to fail, or for the timeout.
+ *
+ * (Should not really need the time-out for stop-idle, but seems
+ * neater than crash closing the connection.)
+ *
+ * While in Stopping state, any further event will clear the
+ * notification-pending flag.
+ *
+ * When the notification-pending flag is not set:
+ *
+ * * close the connection
+ * * purge all buffering
+ * * stop all timers
+ *
+ * * for stop-idle: proceed to Idle state
+
+
+
+
+
+ * In this state the connection is no longer associated with a session.
+ *
+ * This state exists only to allow the TCP output buffer to drain
+ * sufficiently to allow the tail end of one BGP message to be sent,
+ * followed by a NOTIFICATION message.
+ *
+ * When entering this state, if there is no NOTIFICATION to send, then
+ * will terminate the session.
+ *
+ * While in this state is waiting for the NOTIFICATION message to have been
+ * sent, or for the HoldTimer to expire (does not wait indefinitely).
+ *
+ * The expected events are:
+ *
+ * * Sent NOTIFICATION message
+ * * Hold_Timer_expired
+ * * TCP_fatal_error
+ * * TCP_connection_closed
+ *
+ * Clear NOTIFICATION pending, so connection will then be terminated.
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_Stopping}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
+ {bgp_fsm_done, bgp_fsm_Stopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_done, bgp_fsm_Stopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_done, bgp_fsm_Stopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_done, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
+ },
+} ;
- THREAD_VAL (thread) = ConnectRetry_timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
+static const char *bgp_event_str[] =
+{
+ "NULL",
+ "BGP_Start",
+ "BGP_Stop",
+ "TCP_connection_open",
+ "TCP_connection_closed",
+ "TCP_connection_open_failed",
+ "TCP_fatal_error",
+ "ConnectRetry_timer_expired",
+ "Hold_Timer_expired",
+ "KeepAlive_timer_expired",
+ "Receive_OPEN_message",
+ "Receive_KEEPALIVE_message",
+ "Receive_UPDATE_message",
+ "Receive_NOTIFICATION_message",
+ "Sent_NOTIFICATION_message",
+} ;
+
+/*------------------------------------------------------------------------------
+ * Deal with the given event for the given connection.
+ */
+extern void
+bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
+{
+ bgp_fsm_state_t next_state ;
+ const struct bgp_fsm* fsm ;
- return 0;
-}
+ dassert( (event >= bgp_fsm_null_event)
+ && (event <= bgp_fsm_last_event)) ;
+ dassert( (connection->state >= bgp_fsm_first_state)
+ && (connection->state >= bgp_fsm_last_state) ) ;
-/* BGP holdtime timer. */
-static int
-bgp_holdtime_timer (struct thread *thread)
-{
- struct peer *peer;
+ /* Watch out for recursing through the FSM for this connection. */
+ ++connection->fsm_active ;
- peer = THREAD_ARG (thread);
- peer->t_holdtime = NULL;
+ if (connection->fsm_active == 2)
+ {
+ connection->post = event ;
+ return ;
+ } ;
+
+ /* Lock the session for the convenience of the event handlers.
+ *
+ * NB: if the current state is Stopping, then connection is no longer
+ * attached to session -- so connection->session is NULL -- BEWARE !
+ *
+ * The session lock does nothing if no session is attached.
+ */
+ BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (holdtime timer expire)",
- peer->host);
+ do
+ {
+ assert(connection->fsm_active == 1) ;
- THREAD_VAL (thread) = Hold_Timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
+ fsm = &bgp_fsm[connection->state][event] ;
+ next_state = fsm->next_state ;
- return 0;
-}
+ /* Call function. */
+ next_state = fsm->action(connection, next_state, event) ;
-/* BGP keepalive fire ! */
-static int
-bgp_keepalive_timer (struct thread *thread)
-{
- struct peer *peer;
+ dassert( (next_state >= bgp_fsm_first_state)
+ && (next_state <= bgp_fsm_last_state) ) ;
- peer = THREAD_ARG (thread);
- peer->t_keepalive = NULL;
+ /* If state is changed. */
+ if (next_state != connection->state)
+ {
+ bgp_fsm_state_t prev_state = connection->state ;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (keepalive timer expire)",
- peer->host);
+ /* Complete the state change */
+ bgp_fsm_state_change(connection, next_state) ;
- THREAD_VAL (thread) = KeepAlive_timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
+ /* Log state change as required. */
+ if (BGP_DEBUG(fsm, FSM))
+ plog_debug(connection->log,
+ "%s [FSM] %s (%s->%s)",
+ connection->host,
+ bgp_event_str[event],
+ LOOKUP (bgp_status_msg, prev_state),
+ LOOKUP (bgp_status_msg, next_state)) ;
- return 0;
-}
+ if (BGP_DEBUG(normal, NORMAL))
+ zlog_debug ("%s on %s went from %s to %s",
+ connection->host,
+ bgp_event_str[event],
+ LOOKUP (bgp_status_msg, prev_state),
+ LOOKUP (bgp_status_msg, next_state));
+ } ;
-static int
-bgp_routeadv_timer (struct thread *thread)
-{
- struct peer *peer;
+ /* Pick up post event -- if any */
+ event = connection->post ;
+ connection->post = bgp_fsm_null_event ;
- peer = THREAD_ARG (thread);
- peer->t_routeadv = NULL;
+ } while (--connection->fsm_active != 0) ;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (routeadv timer expire)",
- peer->host);
+ BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- peer->synctime = time (NULL);
+ /* Connections which are Stopping are no longer linked to a session. */
+ /* There is no way out of Stopping state
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ if (connection->state == bgp_fsm_Stopping)
+ {
+ /* Sever link with session -- after mutex unlock the first time */
- BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer,
- peer->v_routeadv);
+ session->connections[connection->ordinal] = NULL ;
- return 0;
-}
+ connection->session = NULL ;
+ connection->p_mutex = NULL ;
+ } ;
+} ;
-/* Reset bgp update timer */
-static void
-bgp_uptime_reset (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * Null action -- do nothing at all.
+ */
+static bgp_fsm_state_t
+bgp_fsm_null(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
{
- peer->uptime = time (NULL);
-}
-
-/* BGP Peer Down Cause */
-const char *peer_down_str[] =
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Invalid event -- cannot occur in current state.
+ *
+ * Brings down the session -- next state is bgp_fsm_stopping.
+ *
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_state_t
+bgp_fsm_invalid(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
{
- "",
- "Router ID changed",
- "Remote AS changed",
- "Local AS change",
- "Cluster ID changed",
- "Confederation identifier changed",
- "Confederation peer changed",
- "RR client config change",
- "RS client config change",
- "Update source change",
- "Address family activated",
- "Admin. shutdown",
- "User reset",
- "BGP Notification received",
- "BGP Notification send",
- "Peer closed the session",
- "Neighbor deleted",
- "Peer-group add member",
- "Peer-group delete member",
- "Capability changed",
- "Passive config change",
- "Multihop config change",
- "NSF peer closed the session"
-};
-
-static int
-bgp_graceful_restart_timer_expire (struct thread *thread)
+ if (BGP_DEBUG (fsm, FSM)) \
+ plog_debug (connection->log, "%s [FSM] invalid event %d in state %d",
+ connection->host, event, connection->state) ;
+
+ bgp_fsm_set_stopping(connection, bgp_stopped_invalid,
+ bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 1) ;
+
+ return bgp_fsm_Stopping ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * This is empty event -- should not really happen...
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_state_t
+bgp_fsm_ignore(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
{
- struct peer *peer;
- afi_t afi;
- safi_t safi;
-
- peer = THREAD_ARG (thread);
- peer->t_gr_restart = NULL;
-
- /* NSF delete stale route */
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
- if (peer->nsf[afi][safi])
- bgp_clear_stale_route (peer, afi, safi);
-
- UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
- BGP_TIMER_OFF (peer->t_gr_stale);
-
- if (BGP_DEBUG (events, EVENTS))
+ BGP_FSM_DEBUG(connection, "bgp_ignore called") ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Entry point to FSM.
+ *
+ * This is the first thing to happen to the FSM, and takes it from Initial
+ * state to Idle, with Idle Hold Timer running.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_state_t
+bgp_fsm_enter(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start up BGP Connection
+ *
+ * This is used:
+ *
+ * * to change from Idle to Connect or Active -- when the IdleHoldTimer
+ * expires.
+ *
+ * * to loop back to Connect or Active -- when the ConnectRetryTimer expires.
+ *
+ * The state entered depends on whether this is the primary or secondary
+ * connection.
+ *
+ * If this is the primary connection, then kicks a connect() into life,
+ * before the state change. Note that if that fails, then post an event to
+ * be processed as soon as completes the state transition.
+ *
+ * If this is the secondary connection, enables the session for accept().
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_state_t
+bgp_fsm_start(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ if (connection->ordinal == bgp_connection_primary)
{
- zlog_debug ("%s graceful restart timer expired", peer->host);
- zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+ next_state = bgp_fsm_Connect ;
+ connection->post = bgp_open_connect(connection) ;
}
-
- bgp_timer_set (peer);
-
- return 0;
-}
-
-static int
-bgp_graceful_stale_timer_expire (struct thread *thread)
+ else
+ {
+ next_state = bgp_fsm_Active ;
+ connection->session->accept = 1 ;
+ } ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Restart BGP Connection
+ *
+ * This is used when a TCP connection has come up, but has stopped -- for some
+ * reason (such as the connection simply closed) which suggests that the other
+ * end might still be prepared to make a connection.
+ *
+ * Extends the IdleHoldTimer for the session (up to a maximum of 120 secs) and
+ * changes to Idle state.
+ *
+ * Note that this works equally for the primary and the secondary connection.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_state_t
+bgp_fsm_restart(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
{
- struct peer *peer;
- afi_t afi;
- safi_t safi;
+ unsigned* p_interval = &connection->session->idle_hold_timer_interval ;
+
+ *p_interval *= 2 ;
+
+ if (*p_interval < 4)
+ *p_interval = 4 ;
+ else if (*p_interval > 120)
+ *p_interval = 120 ;
+
+ bgp_connection_close(connection) ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Stop BGP Connection
+ *
+ * The reason should already have been set: bgp_fsm_set_stopping().
+ * But, if not, set unknown reason.
+ *
+ * If no notification to be sent, close the connection now.
+ * If notification to be sent, try to send it now.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_state_t
+bgp_fsm_stop(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ if (connection->stopped == bgp_stopped_not)
+ bgp_fsm_set_stopping(connection, bgp_stopped_unknown, NULL, 0) ;
- peer = THREAD_ARG (thread);
- peer->t_gr_stale = NULL;
+ /* */
+ if (connection->notification_pending)
+ {
+ bgp_msg_write_notification(connection) ;
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s graceful restart stalepath timer expired", peer->host);
- /* NSF delete stale route */
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
- if (peer->nsf[afi][safi])
- bgp_clear_stale_route (peer, afi, safi);
+ nothing pending
+ }
+ * * notification_pending nothing pending
+ * * notification_written not written
+)
- return 0;
-}
-
-/* Called after event occured, this function change status and reset
- read/write and timer thread. */
-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..
- * (and must do so before actually changing into Deleted..
- */
- if (status >= Clearing)
- bgp_clear_route_all (peer);
-
- /* Preserve old status and change into new status. */
- peer->ostatus = peer->status;
- peer->status = status;
-
- if (BGP_DEBUG (normal, NORMAL))
- zlog_debug ("%s went from %s to %s",
- peer->host,
- LOOKUP (bgp_status_msg, peer->ostatus),
- LOOKUP (bgp_status_msg, peer->status));
-}
-
-/* Flush the event queue and ensure the peer is shut down */
-static int
-bgp_clearing_completed (struct peer *peer)
-{
- int rc = bgp_stop(peer);
- BGP_EVENT_FLUSH (peer);
+ /* If are still waiting for the
+ if (connection->notification_pending || connection->notification_written)
+ bgp_connection_read_close(connection) ;
+ else
+ bgp_connection_close(connection) ;
- return rc;
+ return next_state ;
}
-/* Administrative BGP peer stop event. */
-/* May be called multiple times for the same peer */
-int
-bgp_stop (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * TCP connection open.
+ *
+ * Send BGP Open Message to peer.
+ */
+static bgp_fsm_state_t
+bgp_fsm_open(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
{
- afi_t afi;
- safi_t safi;
- char orf_name[BUFSIZ];
+ char buf1[BUFSIZ];
- /* Can't do this in Clearing; events are used for state transitions */
- if (peer->status != Clearing)
+ if (connection->fd < 0)
{
- /* Delete all existing events of the peer */
- BGP_EVENT_FLUSH (peer);
+ zlog_err ("bgp_connect_success peer's fd is negative value %d",
+ connection->fd);
+ return -1;
}
- /* Increment Dropped count. */
- if (peer->status == Established)
+ bgp_getsockname(connection) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
{
- peer->dropped++;
-
- /* bgp log-neighbor-changes of neighbor Down */
- if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
- zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host,
- peer_down_str [(int) peer->last_reset]);
-
- /* graceful restart */
- if (peer->t_gr_stale)
- {
- BGP_TIMER_OFF (peer->t_gr_stale);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
- }
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
- {
- if (BGP_DEBUG (events, EVENTS))
- {
- zlog_debug ("%s graceful restart timer started for %d sec",
- peer->host, peer->v_gr_restart);
- zlog_debug ("%s graceful restart stalepath timer started for %d sec",
- peer->host, peer->bgp->stalepath_time);
- }
- BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire,
- peer->v_gr_restart);
- BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire,
- peer->bgp->stalepath_time);
- }
+ if (! connection->listenerCHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ zlog_debug ("%s open active, local address %s", peer->host,
+ sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN));
else
- {
- UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+ zlog_debug ("%s passive open", peer->host);
+ }
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
- peer->nsf[afi][safi] = 0;
- }
+ bgp_send_open(connection) ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Connect retry timer is expired when in Connect or Active states.
+ *
+ * For primary connection:
+ *
+ * * close the existing connection (may already be closed if failed)
+ * * start the connect() attempt again
+ *
+ * For secondary connection:
+ *
+ * * close the existing connection (easy, 'cos never opened !)
+ * * continue waiting to accept()
+ *
+ */
+static bgp_fsm_state_t
+bgp_fsm_retry(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ bgp_connection_close(connection) ;
+ return bgp_fsm_start(connection, next_state, event) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Error:
+ *
+ *
+ */
+static bgp_fsm_state_t
+bgp_fsm_error(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ /* Double start timer. */
+ peer->v_start *= 2;
- /* set last reset time */
- peer->resettime = time (NULL);
- /* Reset uptime. */
- bgp_uptime_reset (peer);
+ /* Overflow check. */
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
-#ifdef HAVE_SNMP
- bgpTrapBackwardTransition (peer);
-#endif /* HAVE_SNMP */
+ return bgp_stop(peer, next_state);
+} ;
- /* Reset uptime. */
- bgp_uptime_reset (peer);
+/*------------------------------------------------------------------------------
+ * Hold timer expire. This is error of BGP connection. So cut the
+ * peer and change to Idle status.
+ */
+static bgp_fsm_state_t
+bgp_fsm_expire(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ BGP_FSM_DEBUG(connection, "Hold timer expire") ;
- /* Reset peer synctime */
- peer->synctime = 0;
- }
+ /* Send notify to remote peer. */
+ bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0);
- /* Stop read and write threads when exists. */
- BGP_READ_OFF (peer->t_read);
- BGP_WRITE_OFF (peer->t_write);
-
- /* Stop all timers. */
- BGP_TIMER_OFF (peer->t_start);
- BGP_TIMER_OFF (peer->t_connect);
- BGP_TIMER_OFF (peer->t_holdtime);
- BGP_TIMER_OFF (peer->t_keepalive);
- BGP_TIMER_OFF (peer->t_asorig);
- BGP_TIMER_OFF (peer->t_routeadv);
-
- /* Stream reset. */
- peer->packet_size = 0;
-
- /* Clear input and output buffer. */
- if (peer->ibuf)
- stream_reset (peer->ibuf);
- if (peer->work)
- stream_reset (peer->work);
- if (peer->obuf)
- stream_fifo_clean (peer->obuf);
-
- /* Close of file descriptor. */
- if (peer->fd >= 0)
+ /* Sweep if it is temporary peer. */
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
{
- close (peer->fd);
- peer->fd = -1;
+ zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host);
+ peer_delete (peer);
+ return -1;
}
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
- {
- /* Reset all negotiated variables */
- peer->afc_nego[afi][safi] = 0;
- peer->afc_adv[afi][safi] = 0;
- peer->afc_recv[afi][safi] = 0;
-
- /* peer address family capability flags*/
- peer->af_cap[afi][safi] = 0;
+ /* bgp_stop needs to be invoked while in Established state */
+ return bgp_stop(peer, next_state) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Received an acceptable Open Message
+ *
+ * The next state is OpenConfirm.
+ *
+ * However: this is where we do Collision Resolution.
+ *
+ * If the sibling connection has reached OpenConfirm before this one, then now
+ * this one either closes its sibling, or itself.
+ *
+ * As soon as a connection reaches Established, it immediately kills off any
+ * sibling -- so the farthest two connections can get is to OpenSent.
+ *
+ * The connection that is closed should send a Cease/Collision Resolution
+ * NOTIFICATION. The other end should do likewise.
+ *
+ * The connection that is closed is not stopped. It falls
+ *
+ * Immediately respond with a keepalive......
+ *
+ */
+static bgp_fsm_state_t
+bgp_fsm_opened(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ bgp_send_keepalive(connection) ;
- /* peer address family status flags*/
- peer->af_sflags[afi][safi] = 0;
+ return next_state ;
+}
- /* Received ORF prefix-filter */
- peer->orf_plist[afi][safi] = NULL;
+/*------------------------------------------------------------------------------
+ * Status goes to Established.
+ *
+ * TODO: do we need to send a KEEPALIVE on entry to established ?
+ *
+ *
+ * On transition
+ */
+static bgp_fsm_state_t
+bgp_fsm_establish(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ bgp_session session = connection->session ;
+ bgp_connection sibling = bgp_fsm_get_sibling(connection) ;
- /* ORF received prefix-filter pnt */
- sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
- prefix_bgp_orf_remove_all (orf_name);
- }
+ assert(session != NULL) ;
- /* Reset keepalive and holdtime */
- if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
- {
- peer->v_keepalive = peer->keepalive;
- peer->v_holdtime = peer->holdtime;
- }
- else
+ /* The first thing to do is to kill off any sibling and establish
+ * self as the primary connection.
+ */
+ if (sibling != NULL)
{
- peer->v_keepalive = peer->bgp->default_keepalive;
- peer->v_holdtime = peer->bgp->default_holdtime;
- }
-
- peer->update_time = 0;
-
- /* Until we are sure that there is no problem about prefix count
- this should be commented out.*/
-#if 0
- /* Reset prefix count */
- peer->pcount[AFI_IP][SAFI_UNICAST] = 0;
- peer->pcount[AFI_IP][SAFI_MULTICAST] = 0;
- peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0;
- peer->pcount[AFI_IP6][SAFI_UNICAST] = 0;
- peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
-#endif /* 0 */
+ bgp_fsm_stop_connection(sibling, bgp_stopped_collision,
+ bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_C_COLLISION, 0)) ;
+
+ if (connection->ordinal != bgp_connection_primary)
+ {
+ connection->ordinal = bgp_connection_primary ;
+ session->connections[bgp_connection_primary] = connection ;
+ session->connections[bgp_connection_secondary] = NULL ;
+ } ;
+ } ;
+
+ /* Whatever else happens, will no longer accept connections. */
+ /* TODO: now would be a good time to withdraw the password from listener ? */
+ session->accept = 0 ;
+
+ /* Set the session state -- tell the Routeing Engine the news */
+ bgp_session_set_state(session, bgp_session_Established) ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Keepalive send to peer.
+ */
+static bgp_fsm_state_t
+bgp_fsm_kal_send(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ bgp_keepalive_send(connection) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Keepalive packet is received.
+ */
+static bgp_fsm_state_t
+bgp_fsm_kal_recv(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ /* peer count update */
+ peer->keepalive_in++;
- return 0;
+ bgp_hold_timer_recharge(connection) ;
+ return next_state ;
}
-/* BGP peer is stoped by the error. */
-static int
-bgp_stop_with_error (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * Update packet is received.
+ */
+static bgp_fsm_state_t
+bgp_fsm_update(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
{
- /* Double start timer. */
- peer->v_start *= 2;
+ bgp_hold_timer_recharge(connection) ;
+ return next_state ;
+}
- /* Overflow check. */
- if (peer->v_start >= (60 * 2))
- peer->v_start = (60 * 2);
+/*------------------------------------------------------------------------------
+ * We're done with this connection.
+ *
+ * May have been waiting to get a NOTIFICATION message away, and that has
+ * either succeeded or timed out.
+ *
+ * Shut the socket and tear down the connection.
+ */
+static bgp_fsm_state_t
+bgp_fsm_done(bgp_connection connection, bgp_fsm_state_t next_state,
+ bgp_fsm_event_t event)
+{
+ bgp_tear_down(connection);
+ return next_state ;
+}
- bgp_stop (peer);
+/*==============================================================================
+ * The FSM stopping management.
+ *
+ * There are many ways in which a connection may be required to stop.
+ *
+ * The stopping of one connection may mean that the entire session should be
+ * stopped -- either because there is only one connection (eg when in
+ * Established state), or because this is an administrative stop, or because
+ * there has been a serious error, or because a notification received, or ...
+ *
+ *
+ *
+ */
- return 0;
-}
-/* TCP connection open. Next we send open message to remote peer. And
- add read thread for reading open message. */
-static int
-bgp_connect_success (struct peer *peer)
+static void
+bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification, int both)
{
- char buf1[BUFSIZ];
+ bgp_session session ;
+ bgp_connection sibling ;
- if (peer->fd < 0)
- {
- zlog_err ("bgp_connect_success peer's fd is negative value %d",
- peer->fd);
- return -1;
- }
- BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+ /* If not connected to session, then already stopping and can do no more. */
+ session = connection->session ;
+ if (session == NULL)
+ return ;
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_getsockname (peer);
+ BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- if (BGP_DEBUG (normal, NORMAL))
- {
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- zlog_debug ("%s open active, local address %s", peer->host,
- sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN));
- else
- zlog_debug ("%s passive open", peer->host);
- }
+ /* Get "sibling" and set stopping same as us, if required. */
+ /* Won't be there if one-time sibling is already stopping/stopped. */
+ if (both && ((sibling = bgp_fsm_get_sibling(connection)) != NULL))
+ bgp_fsm_stop_connection(sibling, cause, bgp_notify_dup(notification)) ;
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_open_send (peer);
+ /* If have been passed a notification, then that should be sent to */
+ /* the other end, and reported with the session stop cause. */
- return 0;
-}
+ connection->notification_pending = (notification != NULL) ;
-/* TCP connect fail */
-static int
-bgp_connect_fail (struct peer *peer)
-{
- bgp_stop (peer);
- return 0;
-}
+ if (connection->notification_pending)
+ bgp_notify_set(&connection->notification, notification) ;
-/* This function is the first starting point of all BGP connection. It
- try to connect to remote peer with non-blocking IO. */
-int
-bgp_start (struct peer *peer)
-{
- int status;
+ /* Set the session stopping cause and copy the notification there */
- if (BGP_PEER_START_SUPPRESSED (peer))
- {
- if (BGP_DEBUG (fsm, FSM))
- plog_err (peer->log, "%s [FSM] Trying to start suppressed peer"
- " - this is never supposed to happen!", peer->host);
- return -1;
- }
+ session->stopped = cause ;
+ bgp_notify_set_dup(&session->notification, connection->notification) ;
+
+ BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+} ;
- /* Scrub some information that might be left over from a previous,
- * session
- */
- /* Connection information. */
- if (peer->su_local)
- {
- sockunion_free (peer->su_local);
- peer->su_local = NULL;
- }
- if (peer->su_remote)
- {
- sockunion_free (peer->su_remote);
- peer->su_remote = NULL;
- }
- /* Clear remote router-id. */
- peer->remote_id.s_addr = 0;
- /* Clear peer capability flag. */
- peer->cap = 0;
-
- /* If the peer is passive mode, force to move to Active mode. */
- if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
- {
- BGP_EVENT_ADD (peer, TCP_connection_open_failed);
- return 0;
- }
- status = bgp_connect (peer);
+static bgp_connection
+bgp_fsm_get_sibling(bgp_connection connection)
+{
+ bgp_session session = connection->session ;
+
+ if (session == NULL)
+ return NULL ; /* no sibling if no session */
+
+ confirm(bgp_connection_primary == (bgp_connection_secondary ^ 1)) ;
+ confirm(bgp_connection_secondary == (bgp_connection_primary ^ 1)) ;
+
+ return session->connections[connection->ordinal ^ 1] ;
+} ;
+
+
+
+
+/*==============================================================================
+ * The BGP connections timers handling.
+ *
+ * The FSM has four timers:
+ *
+ * * IdleHoldTimer -- uses connection.hold_timer with jitter
+ *
+ * This runs while in Idle state, and is a period in which no connections
+ * are started, and none will be accepted.
+ *
+ * The purpose of this timer is to slow down re-making connections with
+ * peers who are flapping or otherwise proving a nuisance.
+ *
+ * This is a one shot timer, which generates a bgp_fsm_BGP_Start event.
+ *
+ * * ConnectRetryTimer -- uses connection.hold_timer with jitter
+ *
+ * This runs while in Connect or Active state, and is the period for which
+ * the connection is prepared to wait between attempts to connect.
+ *
+ * When trying to make a connect connection:
+ *
+ * The FSM will be in Connect state.
+ *
+ * If listen connections are enabled, will be listening.
+ *
+ * If nothing happens before the ConnectRetryTimer expires, then
+ * the connection attempt will be abandoned, and another started.
+ *
+ * If the connection attempt fails, moves to Active state -- with the
+ * timer still running.
+ *
+ * If nothing further happens before the ConnectRetryTimer expires,
+ * another connect will be started and the FSM returns to Connect state.
+ *
+ * When only listening is enabled:
+ *
+ * The FSM will be in Active state (!).
+ *
+ * If nothing happens before the ConnectRetryTimer expires, then the
+ * FSM will loop round back into Active state.
+ *
+ * This timer is recharged each time it goes off, and generates a
+ * bgp_fsm_ConnectRetry_timer_expired event.
+ *
+ * * HoldTimer -- uses connection.hold_timer *without* jitter
+ *
+ * This timer is used in OpenSent state, and limits the time will wait for
+ * an Open to appear from the other end. RFC4271 calls for this to be a
+ * "large value" -- suggesting 240 seconds.
+ *
+ * This timer is also used in OpenConfirm and Established states, and limits
+ * the time the connection will be held if hear nothing from the other end.
+ * In these states the timer is set to the negotiated HoldTime. If this is
+ * zero, then the HoldTime is infinite.
+ *
+ * This is a one shot timer, which generates a bgp_fsm_Hold_Timer_expired
+ * event.
+ *
+ * * KeepaliveTimer -- uses connection.keepalive_timer with jitter.
+ *
+ * This timer is used in OpenConfirm and Established states only.
+ *
+ * The default KeepalineTimer is 1/3 the HoldTimer, and is set from the
+ * negotiated HoldTime. If that is zero, then the KeepaliveTime is also
+ * infinite, and no KEEPALIVE messages will be sent (other than the "ack"
+ * of the OPEN message).
+ *
+ * This timer is recharged each time it goes off, and generates a
+ * bgp_fsm_KeepAlive_timer_expired event.
+ */
+
+/* Forward reference */
+static inline void
+bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
+ int jitter, qtimer_action* action) ;
+
+/* Forward reference the action functions */
+static qtimer_action bgp_idle_hold_timer_action ;
+static qtimer_action bgp_connect_retry_timer_action ;
+static qtimer_action bgp_hold_timer_action ;
+static qtimer_action bgp_keepalive_timer_action ;
+
+/*==============================================================================
+ * Completion of State Change
+ *
+ * This performs fixed changes associated with the entry to each state from
+ * *another* state.
+ *
+ * Set and unset all the connection timers as required by the new state of
+ * the connection.
+ *
+ *
+ *
+ * NB: requires the session to be LOCKED.
+ */
+static void
+bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state)
+{
+ bgp_session session = connection->session ;
- switch (status)
+ switch (new_state)
{
- case connect_error:
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Connect error", peer->host);
- BGP_EVENT_ADD (peer, TCP_connection_open_failed);
- break;
- case connect_success:
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Connect immediately success",
- peer->host);
- BGP_EVENT_ADD (peer, TCP_connection_open);
- break;
- case connect_in_progress:
- /* To check nonblocking connect, we wait until socket is
- readable or writable. */
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Non blocking connect waiting result",
- peer->host);
- if (peer->fd < 0)
- {
- zlog_err ("bgp_start peer's fd is negative value %d",
- peer->fd);
- return -1;
- }
- BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ /* Base state of connection's finite state machine -- when a session has
+ * been enabled. Falls back to Idle in the event of various errors.
+ *
+ * In Idle state the IdleHoldTimer is running, at the end of which the
+ * BGP Engine will try to connect.
+ *
+ * Note that don't allow a zero (ie infinite) IdleHoldTimer -- by forcing
+ * a 1 second minimum time. As a side effect we make the time odd just
+ * before we jitter it.
+ *
+ * In Idle state refuses connections.
+ */
+ case bgp_fsm_Idle:
+ bgp_timer_set(connection, &connection->hold_timer,
+ (session->idle_hold_timer_interval | 1), 1,
+ bgp_idle_hold_timer_action) ;
+ qtimer_unset(&connection->keepalive_timer) ;
+
break;
- }
- return 0;
-}
-/* Connect retry timer is expired when the peer status is Connect. */
-static int
-bgp_reconnect (struct peer *peer)
-{
- bgp_stop (peer);
- bgp_start (peer);
- return 0;
-}
+ /* In Connect state the BGP Engine is attempting to make a connection
+ * with the peer and may be listening for a connection.
+ *
+ * In Active state the BGP Engine is only listening (!).
+ *
+ * In both cases, waits for the connect_hold_timer_interval.
+ *
+ * The ConnectRetryTimer automatically recharges, because will loop back
+ * round into the same state.
+ */
+ case bgp_fsm_Connect:
+ case bgp_fsm_Active:
+ bgp_timer_set(connection, &connection->hold_timer,
+ session->connect_retry_timer_interval, 1,
+ bgp_connect_retry_timer_action) ;
+ qtimer_unset(&connection->keepalive_timer) ;
+ break;
-static int
-bgp_fsm_open (struct peer *peer)
-{
- /* Send keepalive and make keepalive timer */
- bgp_keepalive_send (peer);
+ /* In OpenSent state is waiting for an OPEN from the other end, before
+ * proceeding to OpenConfirm state.
+ *
+ * Prepared to wait for quite a long time for this.
+ *
+ * Note that session->accept is left as it is. If have reached OpenSent
+ * on:
+ *
+ * * a connect() connection, then session->accept will be true and will
+ * still accept in-bound connections.
+ *
+ * * an accept() connection, then session->accept will be false.
+ */
+ case bgp_fsm_OpenSent:
+ bgp_timer_set(connection, &connection->hold_timer,
+ session->open_hold_timer_interval, 0,
+ bgp_hold_timer_action) ;
+ qtimer_unset(&connection->keepalive_timer) ;
+ break;
- /* Reset holdtimer value. */
- BGP_TIMER_OFF (peer->t_holdtime);
+ /* In OpenConfirm state is waiting for an "ack" before proceeding to
+ * Established. Session->accept is left as it is. If have reached
+ * OpenConfirm on:
+ *
+ * * a connect() connection, then session->accept may still be true and
+ * will still accept in-bound connections. (Collision detection may
+ * have discarded an accept() connection already.)
+ *
+ * * an accept() connection, then session->accept will be false.
+ *
+ * There is only one way into Established, and that is from OpenConfirm.
+ * OpenConfirm starts the KeepaliveTimer. It would be wrong to reset the
+ * timer on entry to Established.
+ *
+ * In both cases have just received a message, so can restart the HoldTimer.
+ *
+ * Both use the negotiated Hold Time and Keepalive Time. May send further
+ * KEEPALIVE messages in OpenConfirm.
+ *
+ * If the negotiated Hold Time value is zero, then the Keepalive Time
+ * value will also be zero, and this will unset both timers.
+ */
+ case bgp_fsm_OpenConfirm:
+ bgp_timer_set(connection, &connection->keepalive_timer,
+ connection->keepalive_timer_interval, 1,
+ bgp_keepalive_timer_action) ;
+ case bgp_fsm_Established:
+ bgp_timer_set(connection, &connection->hold_timer,
+ connection->hold_timer_interval, 0,
+ bgp_hold_timer_action) ;
+ break;
- return 0;
-}
+ /* The connection is coming to an dead stop.
+ *
+ */
-/* Keepalive send to peer. */
-static int
-bgp_fsm_keepalive_expire (struct peer *peer)
-{
- bgp_keepalive_send (peer);
- return 0;
-}
+ case bgp_fsm_Stopping:
+ if (connection->notification_pending)
+ bgp_timer_set(connection, &connection->hold_timer,
+ 60, 1, bgp_hold_timer_action) ;
+ else
+ qtimer_unset(&connection->hold_timer) ;
-/* Hold timer expire. This is error of BGP connection. So cut the
- peer and change to Idle status. */
-static int
-bgp_fsm_holdtime_expire (struct peer *peer)
-{
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG, "%s [FSM] Hold timer expire", peer->host);
+ qtimer_unset(&connection->keepalive_timer) ;
- /* Send notify to remote peer. */
- bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0);
+ break ;
- /* 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;
- }
+ default:
+ zabort("Unknown bgp_fsm_state") ;
+ } ;
- /* bgp_stop needs to be invoked while in Established state */
- bgp_stop(peer);
+ /* Finally: set the new state */
+ connection->state = new_state ;
+} ;
- return 0;
-}
+/*==============================================================================
+ * Timer set and Timer Action Functions
+ */
-/* Status goes to Established. Send keepalive packet then make first
- update information. */
-static int
-bgp_establish (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * Start or reset given qtimer with given interval, in seconds.
+ *
+ * If the interval is zero, unset the timer.
+ */
+static inline void
+bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
+ int jitter, qtimer_action* action)
{
- struct bgp_notify *notify;
- afi_t afi;
- safi_t safi;
- int nsf_af_count = 0;
-
- /* Reset capability open status flag. */
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
- SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
-
- /* Clear last notification data. */
- notify = &peer->notify;
- if (notify->data)
- XFREE (MTYPE_TMP, notify->data);
- memset (notify, 0, sizeof (struct bgp_notify));
-
- /* Clear start timer value to default. */
- peer->v_start = BGP_INIT_START_TIMER;
-
- /* Increment established count. */
- peer->established++;
- bgp_fsm_change_status (peer, Established);
-
- /* bgp log-neighbor-changes of neighbor Up */
- if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
- zlog_info ("%%ADJCHANGE: neighbor %s Up", peer->host);
-
- /* graceful restart */
- UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
- {
- if (peer->afc_nego[afi][safi]
- && CHECK_FLAG (peer->cap, PEER_CAP_RESTART_ADV)
- && CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV))
- {
- if (peer->nsf[afi][safi]
- && ! CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV))
- bgp_clear_stale_route (peer, afi, safi);
-
- peer->nsf[afi][safi] = 1;
- nsf_af_count++;
- }
- else
- {
- if (peer->nsf[afi][safi])
- bgp_clear_stale_route (peer, afi, safi);
- peer->nsf[afi][safi] = 0;
- }
- }
-
- if (nsf_af_count)
- SET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+ if (secs == 0)
+ qtimer_unset(timer) ;
else
{
- UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
- if (peer->t_gr_stale)
- {
- BGP_TIMER_OFF (peer->t_gr_stale);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
- }
- }
-
- if (peer->t_gr_restart)
- {
- BGP_TIMER_OFF (peer->t_gr_restart);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s graceful restart timer stopped", peer->host);
- }
+ if (jitter)
+ secs -= ((rand() % ((int)secs + 1)) / 4) ;
+ qtimer_set_interval(timer, QTIME(secs), action) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP start timer action => bgp_fsm_BGP_Start event
+ *
+ * The timer is automatically unset, which is fine.
+ */
+static void
+bgp_idle_hold_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when)
+{
+ bgp_connection connection = timer_info ;
-#ifdef HAVE_SNMP
- bgpTrapEstablished (peer);
-#endif /* HAVE_SNMP */
+ BGP_FSM_DEBUG(connection, "Timer (start timer expire)") ;
- /* Reset uptime, send keepalive, send current table. */
- bgp_uptime_reset (peer);
-
- /* Send route-refresh when ORF is enabled */
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
- if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV))
- {
- if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
- bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX,
- REFRESH_IMMEDIATE, 0);
- else if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
- bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX_OLD,
- REFRESH_IMMEDIATE, 0);
- }
-
- if (peer->v_keepalive)
- bgp_keepalive_send (peer);
-
- /* First update is deferred until ORF or ROUTE-REFRESH is received */
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
- if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV))
- if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
- || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV))
- SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH);
-
- bgp_announce_route_all (peer);
-
- BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1);
-
- return 0;
-}
+ bgp_fsm_event(connection, bgp_fsm_BGP_Start) ;
+} ;
-/* Keepalive packet is received. */
-static int
-bgp_fsm_keepalive (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * BGP connect retry timer => bgp_fsm_ConnectRetry_timer_expired event
+ *
+ * The timer is recharged here, applying a new "jitter", but that may be
+ * overridden by the bgp_event() handling.
+ */
+static void
+bgp_connect_retry_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when)
{
- /* peer count update */
- peer->keepalive_in++;
+ bgp_connection connection = timer_info ;
- BGP_TIMER_OFF (peer->t_holdtime);
- return 0;
-}
+ BGP_FSM_DEBUG(connection, "Timer (connect timer expire)") ;
-/* Update packet is received. */
-static int
-bgp_fsm_update (struct peer *peer)
-{
- BGP_TIMER_OFF (peer->t_holdtime);
- return 0;
-}
+ bgp_timer_set(connection, &connection->hold_timer,
+ connection->session->connect_retry_timer_interval, 1, NULL) ;
-/* This is empty event. */
-static int
-bgp_ignore (struct peer *peer)
-{
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host);
- return 0;
-}
-
-/* Finite State Machine structure */
-static const struct {
- int (*func) (struct peer *);
- int next_state;
-} FSM [BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] =
-{
- {
- /* Idle state: In Idle state, all events other than BGP_Start is
- ignored. With BGP_Start event, finite state machine calls
- bgp_start(). */
- {bgp_start, Connect}, /* BGP_Start */
- {bgp_stop, Idle}, /* BGP_Stop */
- {bgp_stop, Idle}, /* TCP_connection_open */
- {bgp_stop, Idle}, /* TCP_connection_closed */
- {bgp_ignore, Idle}, /* TCP_connection_open_failed */
- {bgp_stop, Idle}, /* TCP_fatal_error */
- {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
- {bgp_ignore, Idle}, /* Hold_Timer_expired */
- {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
- {bgp_ignore, Idle}, /* Receive_OPEN_message */
- {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
- {bgp_ignore, Idle}, /* Receive_UPDATE_message */
- {bgp_ignore, Idle}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Idle}, /* Clearing_Completed */
- },
- {
- /* Connect */
- {bgp_ignore, Connect}, /* BGP_Start */
- {bgp_stop, Idle}, /* BGP_Stop */
- {bgp_connect_success, OpenSent}, /* TCP_connection_open */
- {bgp_stop, Idle}, /* TCP_connection_closed */
- {bgp_connect_fail, Active}, /* TCP_connection_open_failed */
- {bgp_connect_fail, Idle}, /* TCP_fatal_error */
- {bgp_reconnect, Connect}, /* ConnectRetry_timer_expired */
- {bgp_ignore, Idle}, /* Hold_Timer_expired */
- {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
- {bgp_ignore, Idle}, /* Receive_OPEN_message */
- {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
- {bgp_ignore, Idle}, /* Receive_UPDATE_message */
- {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Idle}, /* Clearing_Completed */
- },
- {
- /* Active, */
- {bgp_ignore, Active}, /* BGP_Start */
- {bgp_stop, Idle}, /* BGP_Stop */
- {bgp_connect_success, OpenSent}, /* TCP_connection_open */
- {bgp_stop, Idle}, /* TCP_connection_closed */
- {bgp_ignore, Active}, /* TCP_connection_open_failed */
- {bgp_ignore, Idle}, /* TCP_fatal_error */
- {bgp_start, Connect}, /* ConnectRetry_timer_expired */
- {bgp_ignore, Idle}, /* Hold_Timer_expired */
- {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
- {bgp_ignore, Idle}, /* Receive_OPEN_message */
- {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
- {bgp_ignore, Idle}, /* Receive_UPDATE_message */
- {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Idle}, /* Clearing_Completed */
- },
- {
- /* OpenSent, */
- {bgp_ignore, OpenSent}, /* BGP_Start */
- {bgp_stop, Idle}, /* BGP_Stop */
- {bgp_stop, Active}, /* TCP_connection_open */
- {bgp_stop, Active}, /* TCP_connection_closed */
- {bgp_stop, Active}, /* TCP_connection_open_failed */
- {bgp_stop, Active}, /* TCP_fatal_error */
- {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
- {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
- {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
- {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */
- {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
- {bgp_ignore, Idle}, /* Receive_UPDATE_message */
- {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Idle}, /* Clearing_Completed */
- },
- {
- /* OpenConfirm, */
- {bgp_ignore, OpenConfirm}, /* BGP_Start */
- {bgp_stop, Idle}, /* BGP_Stop */
- {bgp_stop, Idle}, /* TCP_connection_open */
- {bgp_stop, Idle}, /* TCP_connection_closed */
- {bgp_stop, Idle}, /* TCP_connection_open_failed */
- {bgp_stop, Idle}, /* TCP_fatal_error */
- {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
- {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
- {bgp_ignore, OpenConfirm}, /* KeepAlive_timer_expired */
- {bgp_ignore, Idle}, /* Receive_OPEN_message */
- {bgp_establish, Established}, /* Receive_KEEPALIVE_message */
- {bgp_ignore, Idle}, /* Receive_UPDATE_message */
- {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Idle}, /* Clearing_Completed */
- },
- {
- /* Established, */
- {bgp_ignore, Established}, /* BGP_Start */
- {bgp_stop, Clearing}, /* BGP_Stop */
- {bgp_stop, Clearing}, /* TCP_connection_open */
- {bgp_stop, Clearing}, /* TCP_connection_closed */
- {bgp_stop, Clearing}, /* TCP_connection_open_failed */
- {bgp_stop, Clearing}, /* TCP_fatal_error */
- {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */
- {bgp_fsm_holdtime_expire, Clearing}, /* Hold_Timer_expired */
- {bgp_fsm_keepalive_expire, Established}, /* KeepAlive_timer_expired */
- {bgp_stop, Clearing}, /* Receive_OPEN_message */
- {bgp_fsm_keepalive, Established}, /* Receive_KEEPALIVE_message */
- {bgp_fsm_update, Established}, /* Receive_UPDATE_message */
- {bgp_stop_with_error, Clearing}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Idle}, /* Clearing_Completed */
- },
- {
- /* Clearing, */
- {bgp_ignore, Clearing}, /* BGP_Start */
- {bgp_stop, Clearing}, /* BGP_Stop */
- {bgp_stop, Clearing}, /* TCP_connection_open */
- {bgp_stop, Clearing}, /* TCP_connection_closed */
- {bgp_stop, Clearing}, /* TCP_connection_open_failed */
- {bgp_stop, Clearing}, /* TCP_fatal_error */
- {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */
- {bgp_stop, Clearing}, /* Hold_Timer_expired */
- {bgp_stop, Clearing}, /* KeepAlive_timer_expired */
- {bgp_stop, Clearing}, /* Receive_OPEN_message */
- {bgp_stop, Clearing}, /* Receive_KEEPALIVE_message */
- {bgp_stop, Clearing}, /* Receive_UPDATE_message */
- {bgp_stop, Clearing}, /* Receive_NOTIFICATION_message */
- {bgp_clearing_completed, Idle}, /* Clearing_Completed */
- },
- {
- /* Deleted, */
- {bgp_ignore, Deleted}, /* BGP_Start */
- {bgp_ignore, Deleted}, /* BGP_Stop */
- {bgp_ignore, Deleted}, /* TCP_connection_open */
- {bgp_ignore, Deleted}, /* TCP_connection_closed */
- {bgp_ignore, Deleted}, /* TCP_connection_open_failed */
- {bgp_ignore, Deleted}, /* TCP_fatal_error */
- {bgp_ignore, Deleted}, /* ConnectRetry_timer_expired */
- {bgp_ignore, Deleted}, /* Hold_Timer_expired */
- {bgp_ignore, Deleted}, /* KeepAlive_timer_expired */
- {bgp_ignore, Deleted}, /* Receive_OPEN_message */
- {bgp_ignore, Deleted}, /* Receive_KEEPALIVE_message */
- {bgp_ignore, Deleted}, /* Receive_UPDATE_message */
- {bgp_ignore, Deleted}, /* Receive_NOTIFICATION_message */
- {bgp_ignore, Deleted}, /* Clearing_Completed */
- },
-};
+ bgp_fsm_event(connection, bgp_fsm_ConnectRetry_timer_expired) ;
+} ;
-static const char *bgp_event_str[] =
+/*------------------------------------------------------------------------------
+ * BGP hold timer => bgp_fsm_Hold_Timer_expired event
+ *
+ * The timer is automatically unset, which is fine.
+ */
+static void
+bgp_hold_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when)
{
- NULL,
- "BGP_Start",
- "BGP_Stop",
- "TCP_connection_open",
- "TCP_connection_closed",
- "TCP_connection_open_failed",
- "TCP_fatal_error",
- "ConnectRetry_timer_expired",
- "Hold_Timer_expired",
- "KeepAlive_timer_expired",
- "Receive_OPEN_message",
- "Receive_KEEPALIVE_message",
- "Receive_UPDATE_message",
- "Receive_NOTIFICATION_message",
- "Clearing_Completed",
-};
+ bgp_connection connection = timer_info ;
-/* Execute event process. */
-int
-bgp_event (struct thread *thread)
-{
- int ret = 0;
- int event;
- int next;
- struct peer *peer;
+ BGP_FSM_DEBUG(connection, "Timer (holdtime timer expire)") ;
- peer = THREAD_ARG (thread);
- event = THREAD_VAL (thread);
+ bgp_fsm_event(connection, bgp_fsm_Hold_Timer_expired) ;
+} ;
- /* Logging this event. */
- next = FSM [peer->status -1][event - 1].next_state;
+/*------------------------------------------------------------------------------
+ * BGP keepalive fire => bgp_fsm_KeepAlive_timer_expired
+ *
+ * The timer is recharged here, applying a new "jitter", but that may be
+ * overridden by the bgp_event() handling.
+ */
+static void
+bgp_keepalive_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when)
+{
+ bgp_connection connection = timer_info ;
- if (BGP_DEBUG (fsm, FSM) && peer->status != next)
- 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));
+ BGP_FSM_DEBUG(connection, "Timer (keepalive timer expire)") ;
- /* Call function. */
- if (FSM [peer->status -1][event - 1].func)
- ret = (*(FSM [peer->status - 1][event - 1].func))(peer);
+ bgp_timer_set(connection, &connection->keepalive_timer,
+ connection->session->keepalive_timer_interval, 1, NULL) ;
- /* When function do not want proceed next job return -1. */
- if (ret >= 0)
- {
- /* If status is changed. */
- if (next != peer->status)
- bgp_fsm_change_status (peer, next);
-
- /* Make sure timer is set. */
- bgp_timer_set (peer);
- }
-
- return ret;
-}
+ bgp_fsm_event(connection, bgp_fsm_KeepAlive_timer_expired) ;
+} ;
+
+/*============================================================================*/
+/* BGP Peer Down Cause */
+const char *peer_down_str[] =
+{
+ "",
+ "Router ID changed",
+ "Remote AS changed",
+ "Local AS change",
+ "Cluster ID changed",
+ "Confederation identifier changed",
+ "Confederation peer changed",
+ "RR client config change",
+ "RS client config change",
+ "Update source change",
+ "Address family activated",
+ "Admin. shutdown",
+ "User reset",
+ "BGP Notification received",
+ "BGP Notification send",
+ "Peer closed the session",
+ "Neighbor deleted",
+ "Peer-group add member",
+ "Peer-group delete member",
+ "Capability changed",
+ "Passive config change",
+ "Multihop config change",
+ "NSF peer closed the session"
+};
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
index a749f8ea..1d530ee9 100644
--- a/bgpd/bgp_fsm.h
+++ b/bgpd/bgp_fsm.h
@@ -1,4 +1,4 @@
-/* BGP-4 Finite State Machine
+/* BGP-4 Finite State Machine
From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
Copyright (C) 1998 Kunihiro Ishiguro
@@ -22,60 +22,89 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_FSM_H
#define _QUAGGA_BGP_FSM_H
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_connection.h"
+
+/* Prototypes. */
+
+
+extern void
+bgp_fsm_enable_connection(bgp_connection connection) ;
+
+extern void
+bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) ;
+
+extern void
+bgp_fsm_io_error(bgp_connection connection, int err) ;
+
+extern void
+bgp_fsm_fd_closed(bgp_connection connection) ;
+
+extern void
+bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification) ;
+extern void
+bgp_fsm_stop_connection(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification) ;
+
+
+//extern int bgp_event (struct thread *);
+//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);
+//extern const char *peer_down_str[];
+
+/*==============================================================================
+ * Legacy -- to be removed....
+ */
+
/* Macro for BGP read, write and timer thread. */
-#define BGP_READ_ON(T,F,V) \
- do { \
- if (!(T) && (peer->status != Deleted)) \
- THREAD_READ_ON(master,T,F,peer,V); \
+#define BGP_READ_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->status != Deleted)) \
+ THREAD_READ_ON(master,T,F,peer,V); \
} while (0)
-#define BGP_READ_OFF(T) \
- do { \
- if (T) \
- THREAD_READ_OFF(T); \
+#define BGP_READ_OFF(T) \
+ do { \
+ if (T) \
+ THREAD_READ_OFF(T); \
} while (0)
-#define BGP_WRITE_ON(T,F,V) \
- do { \
- if (!(T) && (peer->status != Deleted)) \
+#define BGP_WRITE_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->status != Deleted)) \
THREAD_WRITE_ON(master,(T),(F),peer,(V)); \
} while (0)
-
-#define BGP_WRITE_OFF(T) \
- do { \
- if (T) \
- THREAD_WRITE_OFF(T); \
+
+#define BGP_WRITE_OFF(T) \
+ do { \
+ if (T) \
+ THREAD_WRITE_OFF(T); \
} while (0)
-#define BGP_TIMER_ON(T,F,V) \
- do { \
- if (!(T) && (peer->status != Deleted)) \
+#define BGP_TIMER_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->status != Deleted)) \
THREAD_TIMER_ON(master,(T),(F),peer,(V)); \
} while (0)
-#define BGP_TIMER_OFF(T) \
- do { \
- if (T) \
- THREAD_TIMER_OFF(T); \
+#define BGP_TIMER_OFF(T) \
+ do { \
+ if (T) \
+ THREAD_TIMER_OFF(T); \
} while (0)
-#define BGP_EVENT_ADD(P,E) \
- do { \
- if ((P)->status != Deleted) \
+#define BGP_EVENT_ADD(P,E) \
+ do { \
+ if ((P)->status != Deleted) \
thread_add_event (master, bgp_event, (P), (E)); \
} while (0)
-#define BGP_EVENT_FLUSH(P) \
- do { \
- assert (peer); \
- thread_cancel_event (master, (P)); \
+#define BGP_EVENT_FLUSH(P) \
+ do { \
+ assert (peer); \
+ thread_cancel_event (master, (P)); \
} while (0)
-/* Prototypes. */
-extern int bgp_event (struct thread *);
-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);
-extern const char *peer_down_str[];
-
#endif /* _QUAGGA_BGP_FSM_H */
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 9e3427d2..d857c856 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -1,210 +1,676 @@
/* BGP network related fucntions
- Copyright (C) 1999 Kunihiro Ishiguro
-
-This file is part of GNU Zebra.
-
-GNU Zebra is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
-
-GNU Zebra is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Zebra; see the file COPYING. If not, write to the Free
-Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
#include <zebra.h>
-#include "thread.h"
#include "sockunion.h"
#include "sockopt.h"
#include "memory.h"
#include "log.h"
#include "if.h"
#include "prefix.h"
-#include "command.h"
+//#include "command.h"
#include "privs.h"
-#include "linklist.h"
-#include "bgpd/bgpd.h"
-#include "bgpd/bgp_fsm.h"
-#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_connection.h"
+#include "bgpd/bgp_fsm.h"
+#include "qpselect.h"
+
extern struct zebra_privs_t bgpd_privs;
+/*==============================================================================
+ * This is the socket connect/listen/accept/close stuff for the BGP Engine.
+ *
+ * NB: this code is for use in the BGP Engine *only*.
+ */
+
+/* Forward references. */
+static void
+bgp_connect_action(qps_file qf, void* file_info) ;
+
+static void
+bgp_accept_action(qps_file qf, void* file_info) ;
+
+static int
+bgp_getsockname(int fd, union sockunion* su_local, union sockunion* su_remote) ;
+
+static int
+bgp_socket_set_common_options(int fd, union sockunion* su, int ttl,
+ const char* password) ;
+/*==============================================================================
+ * Open and close the listeners.
+ *
+ * When the BGP Engine is started it is passed the address and port to listen
+ * to. By default the address is NULL, which maps to INADDR_ANY and
+ * (if supported) IN6ADDR_ANY_INIT.
+ *
+ * When the BGP Engine is stopped the listening ports are closed.
+ *
+ * NB: once the listeners are opened they are active in the BGP Engine Nexus,
+ * and will be fielding attempts to connect.
+ *
+ * The BGP listeners are kept here. Keep lists of IPv4 and IPv6 listeners for
+ * the convenience of setting MD5 passwords.
+ */
+
+typedef struct bgp_listener* bgp_listener ;
+
+static bgp_listener bgp_listeners[] =
+ {
+ [AF_INET] = NULL,
+ [AF_INET6] = NULL
+ } ;
+
+CONFIRM((AF_INET < 20) && (AF_INET6 < 20)) ;
+
/* BGP listening socket. */
struct bgp_listener
{
- int fd;
- union sockunion su;
- struct thread *thread;
-};
-
-/*
- * Set MD5 key for the socket, for the given IPv4 peer address.
- * If the password is NULL or zero-length, the option will be disabled.
+ bgp_listener next ;
+ struct qps_file qf ;
+ union sockunion su ;
+} ;
+
+/* Forward reference */
+static int bgp_init_listener(int sock, struct sockaddr *sa, socklen_t salen) ;
+
+/*------------------------------------------------------------------------------
+ * Open Listeners.
+ *
+ * Using given address and port, get all possible addresses and set up a
+ * listener on each one.
+ *
+ * NB: only listens on AF_INET and (if HAVE_IPV6) AF_INET6.
+ *
+ * Returns: 0 => OK
+ * -1 => failed -- no listeners set up
+ *
*/
-static int
-bgp_md5_set_socket (int socket, union sockunion *su, const char *password)
+extern int
+bgp_open_listeners(unsigned short port, const char *address)
{
- int ret = -1;
- int en = ENOSYS;
-
- assert (socket >= 0);
-
-#if HAVE_DECL_TCP_MD5SIG
- ret = sockopt_tcp_signature (socket, su, password);
- en = errno;
-#endif /* HAVE_TCP_MD5SIG */
-
- if (ret < 0)
- zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s",
- socket, safe_strerror (en));
+#if defined (HAVE_IPV6) && ! defined (NRL) /*----------------------------*/
- return ret;
-}
+ /* IPv6 supported version of BGP server socket setup. */
-/* Helper for bgp_connect */
-static int
-bgp_md5_set_connect (int socket, union sockunion *su, const char *password)
-{
- int ret = -1;
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ int ret, count;
+ char port_str[16];
-#if HAVE_DECL_TCP_MD5SIG
- if ( bgpd_privs.change (ZPRIVS_RAISE) )
+ static const struct addrinfo req = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_PASSIVE,
+ .ai_socktype = SOCK_STREAM,
+ } ;
+
+ snprintf (port_str, sizeof(port_str), "%d", port);
+ port_str[sizeof (port_str) - 1] = '\0';
+
+ ret = getaddrinfo (address, port_str, &req, &ainfo_save);
+ if (ret != 0)
{
- zlog_err ("%s: could not raise privs", __func__);
- return ret;
+ zlog_err ("getaddrinfo: %s", gai_strerror (ret));
+ return -1;
}
-
- ret = bgp_md5_set_socket (socket, su, password);
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("%s: could not lower privs", __func__);
-#endif /* HAVE_TCP_MD5SIG */
-
- return ret;
-}
+ count = 0;
+ for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next)
+ {
+ int sock;
-int
-bgp_md5_set (struct peer *peer)
-{
- struct listnode *node;
- int ret = 0;
- struct bgp_listener *listener;
+ if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
+ continue;
- if ( bgpd_privs.change (ZPRIVS_RAISE) )
+ sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
+ if (sock < 0)
+ {
+ zlog_err ("socket: %s", safe_strerror (errno));
+ continue;
+ }
+
+ ret = bgp_init_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen);
+
+ if (ret == 0)
+ ++count;
+ else
+ close(sock);
+ }
+ freeaddrinfo (ainfo_save);
+
+ if (count == 0)
{
- zlog_err ("%s: could not raise privs", __func__);
+ zlog_err ("%s: no usable addresses", __func__);
return -1;
}
-
- /* Just set the password on the listen socket(s). Outbound connections
- * are taken care of in bgp_connect() below.
- */
- for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
- if (listener->su.sa.sa_family == peer->su.sa.sa_family)
- {
- ret = bgp_md5_set_socket (listener->fd, &peer->su, peer->password);
- break;
- }
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("%s: could not lower privs", __func__);
-
- return ret;
+ return 0;
}
-
-/* Accept bgp connection. */
-static int
-bgp_accept (struct thread *thread)
-{
- int bgp_sock;
- int accept_sock;
- union sockunion su;
- struct bgp_listener *listener = THREAD_ARG(thread);
- struct peer *peer;
- struct peer *peer1;
- char buf[SU_ADDRSTRLEN];
-
- /* Register accept thread. */
- accept_sock = THREAD_FD (thread);
- if (accept_sock < 0)
+#else /*----------------------------------------------------*/
+
+ /* Traditional IPv4 only version. */
+
+ int sock;
+ int socklen;
+ struct sockaddr_in sin;
+ int ret, en;
+
+ memset (&sin, 0, sizeof (struct sockaddr_in));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (port);
+ socklen = sizeof (struct sockaddr_in);
+
+ if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1))
{
- zlog_err ("accept_sock is nevative value %d", accept_sock);
- return -1;
+ zlog_err("bgp_socket: could not parse ip address %s: %s",
+ address, safe_strerror (errno));
+ return ret;
}
- listener->thread = thread_add_read (master, bgp_accept, listener, accept_sock);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin.sin_len = socklen;
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- /* Accept client connection. */
- bgp_sock = sockunion_accept (accept_sock, &su);
- if (bgp_sock < 0)
+ sock = socket (AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
{
- zlog_err ("[Error] BGP socket accept failed (%s)", safe_strerror (errno));
- return -1;
+ zlog_err ("socket: %s", safe_strerror (errno));
+ return sock;
}
- 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)
+ ret = bgp_init_listener (sock, (struct sockaddr *) &sin, socklen);
+ if (ret < 0)
{
- 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));
- }
- close (bgp_sock);
- return -1;
+ close (sock);
+ return ret;
}
+ return sock;
+}
+#endif /* HAVE_IPV6 && !NRL --------------------------------------------------*/
+
+/*------------------------------------------------------------------------------
+ * Close Listeners.
+ *
+ * Empty the listener lists, close files, remove from the selection.
+ *
+ */
+static void bgp_reset_listeners(bgp_listener* p_listener) ;
+
+extern void
+bgp_close_listeners(void)
+{
+ bgp_reset_listeners(&bgp_listeners[AF_INET]) ;
+ bgp_reset_listeners(&bgp_listeners[AF_INET6]) ;
+} ;
- /* In case of peer is EBGP, we should set TTL for this connection. */
- if (peer_sort (peer1) == BGP_PEER_EBGP)
- sockopt_ttl (peer1->su.sa.sa_family, bgp_sock, peer1->ttl);
+static void
+bgp_reset_listeners(bgp_listener* p_listener)
+{
+ bgp_listener listener ;
+ bgp_listener next ;
+
+ next = *p_listener ;
+ *p_listener = NULL ;
+
+ while (next != NULL)
+ {
+ listener = next ;
+ next = listener->next ;
+
+ close(qps_file_fd(&listener->qf)) ;
+ qps_remove_file(&listener->qf) ;
+
+ XFREE(MTYPE_BGP_LISTENER, listener) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise Listener Socket
+ *
+ * Sets up socket with the usual options. Binds to given address and listens.
+ *
+ * If all that is successful, creates bgp_listener, sets up qpselect file, adds
+ * to the BGP Engine selection and enables it for reading.
+ *
+ * Listener read events are handled by bgp_accept_action().
+ *
+ * Returns: 0 : OK
+ * != 0 : error number (from errno or otherwise)
+ */
+static int
+bgp_init_listener(int sock, struct sockaddr *sa, socklen_t salen)
+{
+ bgp_listener listener ;
+ int ret ;
- /* Make dummy peer until read Open packet. */
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("[Event] Make dummy peer structure until read Open packet");
+ ret = bgp_socket_set_common_options(sock, (union sockunion*)sa, 0, NULL) ;
+ if (ret != 0)
+ return ret ;
+#ifdef IPV6_V6ONLY
+ /* Want only IPV6 on ipv6 socket (not mapped addresses) */
+ if (sa->sa_family == AF_INET6)
{
- char buf[SU_ADDRSTRLEN + 1];
-
- 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;
-
- /* Make peer's address string. */
- sockunion2str (&su, buf, SU_ADDRSTRLEN);
- peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
+ int on = 1;
+ /* TODO: trap errors when setting IPPROTO_IPV6, IPV6_V6ONLY ?? */
+ setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)) ;
}
+#endif
- BGP_EVENT_ADD (peer, TCP_connection_open);
+ if (bgpd_privs.change(ZPRIVS_RAISE))
+ {
+ ret = errno ;
+ zlog_err("%s: could not raise privs", __func__);
- return 0;
-}
+ return ret ;
+ } ;
+
+ ret = bind(sock, sa, salen) ;
+ if (ret < 0)
+ {
+ ret = errno ;
+ zlog_err ("bind: %s", safe_strerror(ret));
+ } ;
+
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ {
+ if (ret == 0)
+ ret = errno ;
+ zlog_err("%s: could not lower privs", __func__) ;
+ } ;
+
+ if (ret != 0)
+ return ret ;
+
+ ret = listen (sock, 3);
+ if (ret < 0)
+ {
+ ret = errno ;
+ zlog_err ("listen: %s", safe_strerror(ret)) ;
+ return ret;
+ }
+
+ /* Having successfully opened the listener, record it so that can be found
+ * again, add it to the BGP Engine Nexus file selection and enable it for
+ * reading.
+ */
+
+ listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(struct bgp_listener)) ;
+
+ qps_file_init_new(&listener->qf, NULL) ;
+ qps_add_file(p_bgp_engine->selection, &listener->qf, sock, listener) ;
+ qps_enable_mode(&listener->qf, qps_read_mnum, bgp_accept_action) ;
+
+ memcpy(&listener->su, sa, salen) ;
+
+ listener->next = bgp_listeners[sa->sa_family] ;
+ bgp_listeners[sa->sa_family] = listener ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepare to accept() connection
+ *
+ * Sets session->accept true -- so accept() action will accept the connection.
+ *
+ *
+ *
+ * NB: requires the session mutex LOCKED.
+ */
+extern void
+bgp_open_accept(bgp_connection connection)
+{
+
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept bgp connection -- this is the read action for qpselect.
+ *
+ * Accepts the connection, then checks to see whether the source is a configured
+ * peer, and if it is, whether currently accepting connections from that peer.
+ *
+ * If connection passes those tests, sets up the new listener connection for
+ * the session (including qpselect file), and kicks the FSM for that into life
+ * by generating a bgp_fsm_TCP_connection_open event. At this point the qfile
+ * is not enabled in any mode and no timers are running.
+ *
+ * NB: uses bgp_session_lookup() to find the session, so will lock and unlock
+ * its mutex.
+ *
+ * NB: locks and unlocks the session mutex.
+ *
+ * NB: does not set up connection unless all parts of the accept process
+ * succeed.
+ *
+ * Events and Errors:
+ *
+ * * if the accept() fails, log (err) the error and continue.
+ *
+ * Error is no associated with any connection or session.
+ *
+ * * if the connection is not acceptable, because:
+ *
+ * (a) peer is not configured
+ * (b) session not currently accepting connections (for whatever reason)
+ *
+ * log (debug) the event and continue.
+ *
+ * -- could Cease/Connection Rejected in most cases
+ * -- could Cease/Connection Collision Resolution in those cases
+ *
+ * * if the connection is acceptable, but fails in getting the remote/local
+ * addresses or in setting options
+ *
+ * report error on primary connection and generate bgp_fsm_TCP_fatal_error
+ * event.
+ *
+ * * if all goes well, generate bgp_fsm_TCP_connection_open either for the
+ * new (secondary) connection or for the primary.
+ *
+ * Sets connection->err to the error (if any).
+ */
+static void
+bgp_accept_action(qps_file qf, void* file_info)
+{
+ bgp_connection connection ;
+ bgp_session session ;
+ union sockunion su_remote ;
+ union sockunion su_local ;
+ int exists ;
+ int fd ;
+ int ret ;
+ char buf[SU_ADDRSTRLEN] ;
+
+ /* Accept client connection. */
+ fd = sockunion_accept(qps_file_fd(qf), &su_remote) ;
+ if (fd < 0)
+ {
+ if (fd == -1)
+ zlog_err("[Error] BGP socket accept failed (%s)",
+ safe_strerror(errno)) ;
+ return ; /* have no connection to report this to */
+ } ;
+
+ if (BGP_DEBUG(events, EVENTS))
+ zlog_debug("[Event] BGP connection from host %s",
+ inet_sutop(&su_remote, buf)) ;
+
+ /* See if we are ready to accept connections from the connecting party */
+ session = bgp_session_lookup(&su_remote, &exists) ;
+ if (bgp_session_is_accepting(session))
+ {
+ if (BGP_DEBUG(events, EVENTS))
+ zlog_debug(exists
+ ? "[Event] BGP accept IP address %s is not accepting"
+ : "[Event] BGP accept IP address %s is not configured",
+ inet_sutop(&su_remote, buf)) ;
+ close(fd) ;
+ return ; /* quietly reject connection */
+ /* RFC recommends sending a NOTIFICATION... */
+ } ;
+
+ /* Will accept the connection.
+ *
+ * Now need the session locked, 'cos are about to start a new connection.
+ *
+ * This is running in the BGP Engine thread, so cannot in any case be
+ * foxed by the other connection making changes.
+ *
+ * The session is active, so the Peering Engine will not make any changes
+ * except under the mutex, and will not destroy the session.
+ */
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* Set the common socket options.
+ * Does not set password -- that is inherited from the listener.
+ *
+ * If all is well, set up the listener connection, and set it ready
+ * to go. Set session not to accept further inbound connections.
+ *
+ * Kicks the FSM with bgp_fsm_TCP_connection_open.
+ */
+
+ ret = bgp_getsockname(fd, &su_local, &su_remote) ;
+ if (ret != 0)
+ ret = bgp_socket_set_common_options(fd, &su_remote, session->ttl, NULL) ;
+
+ connection = session->connections[bgp_connection_secondary] ;
+
+ if (ret == 0)
+ {
+ bgp_connection_open(connection, fd) ;
+
+ memcpy(&connection->su_local, &su_local, sizeof(union sockunion)) ;
+ memcpy(&connection->su_remote, &su_remote, sizeof(union sockunion)) ;
+ }
+ else
+ close(fd) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* Now kick the FSM in an appropriate fashion */
+ bgp_fsm_connect_completed(connection, ret) ;
+} ;
+
+/*==============================================================================
+ * Open BGP Connection -- connect() to the other end
+ */
+
+static int bgp_bind(bgp_connection connection) ;
+static int bgp_update_source (bgp_connection connection) ;
+static bgp_fsm_event_t bgp_sex_connect_error(int err) ;
+
+/*------------------------------------------------------------------------------
+ * Open BGP Connection -- connect() to the other end
+ *
+ * Creates a *non-blocking* socket.
+ *
+ * If fails immediately, generate suitable FSM event -- setting connection->err.
+ *
+ * Success (immediate or otherwise) and delayed failure are dealt with in the
+ * qpselect action -- bgp_connect_action() -- below.
+ *
+ * NB: requires the session mutex LOCKED.
+ */
+extern void
+bgp_open_connect(bgp_connection connection)
+{
+ unsigned int ifindex = 0 ;
+ int fd ;
+ int ret ;
+ union sockunion* p_su = &connection->session->su_peer ;
+
+ /* Make socket for the connect connection. */
+ fd = sockunion_socket(p_su) ;
+ if (fd < 0)
+ return errno ; /* give up immediately if cannot create socket */
+
+ /* Set the common options. */
+ ret = bgp_socket_set_common_options(fd, p_su, connection->session->ttl,
+ connection->session->password) ;
+
+ /* Bind socket. */
+ if (ret == 0)
+ ret = bgp_bind(connection) ;
+
+ /* Update source bind. */
+ if (ret == 0)
+ ret = bgp_update_source(connection) ;
+
+#if 0 /* TODO: worry about peer->ifname and sessions ! */
+#ifdef HAVE_IPV6
+ if (peer->ifname)
+ ifindex = if_nametoindex(peer->ifname);
+#endif /* HAVE_IPV6 */
+#endif
+
+ if (BGP_DEBUG(events, EVENTS))
+ plog_debug(connection->log, "%s [Event] Connect start to %s fd %d",
+ connection->host, connection->host, fd);
+
+ /* Connect to the remote peer. */
+ if (ret == 0)
+ ret = sockunion_connect(fd, p_su, connection->session->port, ifindex) ;
+ /* does not report EINPROGRESS as an error. */
+
+ /* If not OK now, close the fd and signal the error */
+
+ if (ret != 0)
+ {
+ close(fd) ;
+
+ bgp_fsm_connect_completed(connection, ret) ;
+
+ return ;
+ } ;
+
+ /* Set connection waiting for connection to complete.
+ *
+ * The file is then enabled for both read and write:
+ *
+ * if succeeds: will become writable (may also be readable if data turns
+ * up immediately).
+ * if fails: will become readable (may also become writable)
+ *
+ * Generally, expect it to be a while before the fd becomes readable or
+ * writable. But for local connections this may happen immediately. But,
+ * in any case, this will be handled by the qpselect action.
+ */
+
+ bgp_connection_open(connection, fd) ;
+
+ qps_enable_mode(&connection->qf, qps_read_mnum, bgp_connect_action) ;
+ qps_enable_mode(&connection->qf, qps_write_mnum, bgp_connect_action) ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Complete non-blocking bgp connect() -- this is the read and write action for
+ * qpselect.
+ *
+ * If the connection succeeds, expect the socket to become writable. May also
+ * become readable if data arrives immediately.
+ *
+ * If the connection fails, expect the socket to become readable. May also
+ * become writable.
+ *
+ * Either way, use getsockopt() to extract any error condition.
+ *
+ * NB: does not require the session mutex.
+ *
+ * Events and Errors:
+ *
+ * * if has succeeded, generate a bgp_fsm_TCP_connection_open event.
+ *
+ * At this point the qfile is not enabled in any mode..
+ *
+ * * if has failed, generate:
+ *
+ * * bgp_fsm_TCP_connection_open_failed event
+ *
+ * for "soft" errors.
+ *
+ * * bgp_fsm_TCP_fatal_error event
+ *
+ * for "hard" errors.
+ *
+ * Sets connection->err to the error (if any).
+ */
+static void
+bgp_connect_action(qps_file qf, void* file_info)
+{
+ bgp_connection connection ;
+ int ret, err ;
+ socklen_t len = sizeof(err) ;
+
+ connection = file_info ;
+
+ /* See if connection successful or not. */
+ /* If successful, set the connection->su_local and ->su_remote */
+
+ ret = getsockopt(qps_file_fd(qf), SOL_SOCKET, SO_ERROR, &err, &len) ;
+ if (ret != 0)
+ ret = errno ;
+ else if (len != sizeof(err))
+ zabort("getsockopt returned unexpected length") ;
+ else if (err != 0)
+ ret = err ;
+ else
+ ret = bgp_getsockname(qps_file_fd(qf), &connection->su_local,
+ &connection->su_remote) ;
+
+ /* In any case, disable both read and write for this file. */
+ qps_disable_modes(qf, qps_write_mbit | qps_read_mbit) ;
+
+ /* Now kick the FSM in an appropriate fashion */
+ bgp_fsm_connect_completed(connection, ret) ;
+} ;
+
+/*==============================================================================
+ * Get local and remote address and port for connection.
+ */
+static int
+bgp_getsockname(int fd, union sockunion* su_local, union sockunion* su_remote)
+{
+ int ret_l, ret_r ;
+
+ ret_l = sockunion_getsockname(fd, su_local) ;
+ ret_r = sockunion_getpeername(fd, su_remote) ;
+
+#if 0 /* TODO: restore setting of peer->nexthop */
+ bgp_nexthop_set(peer->su_local, peer->su_remote, &peer->nexthop, peer);
+#endif
+
+ return (ret_l != 0) ? ret_l : ret_r ;
+} ;
-/* BGP socket bind. */
+/*==============================================================================
+ * Specific binding of outbound connections to interfaces...
+ *
+ */
+
+static struct in_addr* bgp_update_address (struct interface *ifp) ;
+static int bgp_bind_address (int sock, struct in_addr *addr) ;
+
+/*------------------------------------------------------------------------------
+ * BGP socket bind.
+ *
+ * If there is a specific interface to bind an outbound connection to, that
+ * is done here.
+ *
+ *
+ * Returns: 0 : OK (so far so good)
+ * != 0 : error number (from errno or otherwise)
+ */
static int
-bgp_bind (struct peer *peer)
+bgp_bind(bgp_connection connection)
{
+#if 0 /* TODO: restore binding to specific interfaces */
#ifdef SO_BINDTODEVICE
int ret;
struct ifreq ifreq;
@@ -215,10 +681,10 @@ bgp_bind (struct peer *peer)
strncpy ((char *)&ifreq.ifr_name, peer->ifname, sizeof (ifreq.ifr_name));
if ( bgpd_privs.change (ZPRIVS_RAISE) )
- zlog_err ("bgp_bind: could not raise privs");
-
- ret = setsockopt (peer->fd, SOL_SOCKET, SO_BINDTODEVICE,
- &ifreq, sizeof (ifreq));
+ zlog_err ("bgp_bind: could not raise privs");
+
+ ret = setsockopt (peer->fd, SOL_SOCKET, SO_BINDTODEVICE,
+ &ifreq, sizeof (ifreq));
if (bgpd_privs.change (ZPRIVS_LOWER) )
zlog_err ("bgp_bind: could not lower privs");
@@ -229,12 +695,54 @@ bgp_bind (struct peer *peer)
return ret;
}
#endif /* SO_BINDTODEVICE */
+#endif
return 0;
-}
+} ;
+/*------------------------------------------------------------------------------
+ * Update source selection.
+ *
+ * Returns: 0 : OK (so far so good)
+ * != 0 : error number (from errno or otherwise)
+ */
+static int
+bgp_update_source (bgp_connection connection)
+{
+#if 0 /* TODO: restore update-source handling */
+ struct interface *ifp;
+ struct in_addr *addr;
+
+ /* Source is specified with interface name. */
+ if (peer->update_if)
+ {
+ ifp = if_lookup_by_name (peer->update_if);
+ if (! ifp)
+ return;
+
+ addr = bgp_update_address (ifp);
+ if (! addr)
+ return;
+
+ bgp_bind_address (peer->fd, addr);
+ }
+
+ /* Source is specified with IP address. */
+ if (peer->update_source)
+ sockunion_bind (peer->fd, peer->update_source, 0, peer->update_source);
+#else
+ return 0 ;
+#endif
+} ;
+
+/*------------------------------------------------------------------------------
+ * Bind given socket to given address... ???
+ *
+ */
static int
bgp_bind_address (int sock, struct in_addr *addr)
{
+#if 0 /* TODO: restore update-source handling */
+
int ret;
struct sockaddr_in local;
@@ -247,20 +755,28 @@ bgp_bind_address (int sock, struct in_addr *addr)
if ( bgpd_privs.change (ZPRIVS_RAISE) )
zlog_err ("bgp_bind_address: could not raise privs");
-
+
ret = bind (sock, (struct sockaddr *)&local, sizeof (struct sockaddr_in));
if (ret < 0)
;
-
+
if (bgpd_privs.change (ZPRIVS_LOWER) )
zlog_err ("bgp_bind_address: could not lower privs");
-
+
+#endif
return 0;
-}
+} ;
+/*------------------------------------------------------------------------------
+ * Update address.... ???
+ *
+ * Returns: address of IPv4 prefix
+ * or: NULL
+ */
static struct in_addr *
bgp_update_address (struct interface *ifp)
{
+#if 0 /* TODO: restore update-source handling */
struct prefix_ipv4 *p;
struct connected *connected;
struct listnode *node;
@@ -270,266 +786,158 @@ bgp_update_address (struct interface *ifp)
p = (struct prefix_ipv4 *) connected->address;
if (p->family == AF_INET)
- return &p->prefix;
+ return &p->prefix;
}
+#endif
return NULL;
}
-/* Update source selection. */
-static void
-bgp_update_source (struct peer *peer)
-{
- struct interface *ifp;
- struct in_addr *addr;
-
- /* Source is specified with interface name. */
- if (peer->update_if)
- {
- ifp = if_lookup_by_name (peer->update_if);
- if (! ifp)
- return;
-
- addr = bgp_update_address (ifp);
- if (! addr)
- return;
-
- bgp_bind_address (peer->fd, addr);
- }
-
- /* Source is specified with IP address. */
- if (peer->update_source)
- sockunion_bind (peer->fd, peer->update_source, 0, peer->update_source);
-}
+/*==============================================================================
+ * BGP Socket Option handling
+ */
-/* BGP try to connect to the peer. */
-int
-bgp_connect (struct peer *peer)
+static int
+bgp_md5_set_socket(int fd, union sockunion *su, const char *password) ;
+
+/*------------------------------------------------------------------------------
+ * Common socket options:
+ *
+ * * non-blocking -- at all times
+ * * reuseaddr
+ * * reuseport
+ * * set TTL if given ttl != 0
+ * * set password if given password != NULL
+ * * for IPv4, set TOS if required
+ *
+ * These options are set on all sockets: connect/listen/accept
+ *
+ * Returns: 0 => OK
+ * != 0 == errno -- not that we really expect any errors here
+ */
+static int
+bgp_socket_set_common_options(int fd, union sockunion* su, int ttl,
+ const char* password)
{
- unsigned int ifindex = 0;
-
- /* Make socket for the peer. */
- peer->fd = sockunion_socket (&peer->su);
- if (peer->fd < 0)
- return -1;
-
- /* If we can get socket for the peer, adjest TTL and make connection. */
- if (peer_sort (peer) == BGP_PEER_EBGP)
- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
+ int ret ;
+ int val ;
+
+ /* Make socket non-blocking */
+ val = fcntl(fd, F_GETFL, 0) ;
+ if (val != -1) /* Don't really expect it to be -1 (see POSIX) */
+ val = fcntl(fd, F_SETFL, val | O_NONBLOCK) ;
+ if (val == -1)
+ return errno ;
+
+ /* Reuse addr and port */
+ if (sockopt_reuseaddr(fd) < 0)
+ return errno ;
+ if (sockopt_reuseport(fd) < 0)
+ return errno ;
+
+ /* Adjust ttl if required */
+ if (ttl != 0)
+ if ((ret = sockopt_ttl(sockunion_family(su), fd, ttl)) != 0)
+ return ret ;
+
+ /* Set the TCP MD5 "password", if required. */
+ if (password != NULL)
+ if ((ret = bgp_md5_set_socket(fd, su, password)) != 0)
+ return ret ;
- sockopt_reuseaddr (peer->fd);
- sockopt_reuseport (peer->fd);
-
#ifdef IPTOS_PREC_INTERNETCONTROL
- if (sockunion_family (&peer->su) == AF_INET)
- setsockopt_ipv4_tos (peer->fd, IPTOS_PREC_INTERNETCONTROL);
+ /* set IPPROTO_IP/IP_TOS -- if is AF_INET */
+ if (sockunion_family(su) == AF_INET)
+ if (setsockopt_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL) < 0)
+ return errno ;
#endif
- if (peer->password)
- bgp_md5_set_connect (peer->fd, &peer->su, peer->password);
-
- /* Bind socket. */
- bgp_bind (peer);
-
- /* Update source bind. */
- bgp_update_source (peer);
-
-#ifdef HAVE_IPV6
- if (peer->ifname)
- ifindex = if_nametoindex (peer->ifname);
-#endif /* HAVE_IPV6 */
-
- if (BGP_DEBUG (events, EVENTS))
- plog_debug (peer->log, "%s [Event] Connect start to %s fd %d",
- peer->host, peer->host, peer->fd);
-
- /* Connect to the remote peer. */
- return sockunion_connect (peer->fd, &peer->su, htons (peer->port), ifindex);
-}
-
-/* After TCP connection is established. Get local address and port. */
-void
-bgp_getsockname (struct peer *peer)
-{
- if (peer->su_local)
- {
- sockunion_free (peer->su_local);
- peer->su_local = NULL;
- }
-
- if (peer->su_remote)
- {
- sockunion_free (peer->su_remote);
- peer->su_remote = NULL;
- }
-
- peer->su_local = sockunion_getsockname (peer->fd);
- peer->su_remote = sockunion_getpeername (peer->fd);
-
- bgp_nexthop_set (peer->su_local, peer->su_remote, &peer->nexthop, peer);
-}
-
+ return 0 ;
+} ;
+/*------------------------------------------------------------------------------
+ * Set (or clear) MD5 key for the socket, for the given IPv4 peer address.
+ *
+ * If the password is NULL or zero-length, the option will be disabled.
+ *
+ * Returns: 0 => OK
+ * otherwise: errno
+ *
+ * NB: if MD5 is not supported, returns ENOSYS error (but it should not come
+ * to this !).
+ *
+ * NB: has to change up privileges, which can fail (if things are badly set up)
+ */
static int
-bgp_listener (int sock, struct sockaddr *sa, socklen_t salen)
+bgp_md5_set_socket(int fd, union sockunion *su, const char *password)
{
- struct bgp_listener *listener;
- int ret, en;
+ int ret ;
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
+ assert(fd >= 0) ;
-#ifdef IPTOS_PREC_INTERNETCONTROL
- if (sa->sa_family == AF_INET)
- setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL);
-#endif
-
-#ifdef IPV6_V6ONLY
- /* Want only IPV6 on ipv6 socket (not mapped addresses) */
- if (sa->sa_family == AF_INET6) {
- int on = 1;
- setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY,
- (void *) &on, sizeof (on));
- }
-#endif
-
- if (bgpd_privs.change (ZPRIVS_RAISE) )
- zlog_err ("bgp_socket: could not raise privs");
-
- ret = bind (sock, sa, salen);
- en = errno;
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("bgp_bind_address: could not lower privs");
-
- if (ret < 0)
- {
- zlog_err ("bind: %s", safe_strerror (en));
- return ret;
- }
-
- ret = listen (sock, 3);
- if (ret < 0)
+ if (bgpd_privs.change(ZPRIVS_RAISE))
{
- zlog_err ("listen: %s", safe_strerror (errno));
- return ret;
- }
-
- listener = XMALLOC (MTYPE_BGP_LISTENER, sizeof(*listener));
- listener->fd = sock;
- memcpy(&listener->su, sa, salen);
- listener->thread = thread_add_read (master, bgp_accept, listener, sock);
- listnode_add (bm->listen_sockets, listener);
-
- return 0;
-}
+ ret = errno ;
+ zlog_err("%s: could not raise privs", __func__);
-/* IPv6 supported version of BGP server socket setup. */
-#if defined (HAVE_IPV6) && ! defined (NRL)
-int
-bgp_socket (unsigned short port, const char *address)
-{
- struct addrinfo *ainfo;
- struct addrinfo *ainfo_save;
- static const struct addrinfo req = {
- .ai_family = AF_UNSPEC,
- .ai_flags = AI_PASSIVE,
- .ai_socktype = SOCK_STREAM,
- };
- int ret, count;
- char port_str[BUFSIZ];
+ return ret ;
+ } ;
- snprintf (port_str, sizeof(port_str), "%d", port);
- port_str[sizeof (port_str) - 1] = '\0';
+ ret = sockopt_tcp_signature(fd, su, password) ;
- ret = getaddrinfo (address, port_str, &req, &ainfo_save);
- if (ret != 0)
- {
- zlog_err ("getaddrinfo: %s", gai_strerror (ret));
- return -1;
- }
+ if (ret != 0) /* TODO: error already logged as zlog_err() */
+ zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s",
+ fd, safe_strerror(ret));
- count = 0;
- for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next)
+ if (bgpd_privs.change(ZPRIVS_LOWER))
{
- int sock;
-
- if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
- continue;
-
- sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
- if (sock < 0)
- {
- zlog_err ("socket: %s", safe_strerror (errno));
- continue;
- }
-
- ret = bgp_listener (sock, ainfo->ai_addr, ainfo->ai_addrlen);
if (ret == 0)
- ++count;
- else
- close(sock);
- }
- freeaddrinfo (ainfo_save);
- if (count == 0)
- {
- zlog_err ("%s: no usable addresses", __func__);
- return -1;
- }
+ ret = errno ;
+ zlog_err("%s: could not lower privs", __func__) ;
+ } ;
- return 0;
-}
-#else
-/* Traditional IPv4 only version. */
-int
-bgp_socket (unsigned short port, const char *address)
+ return ret;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set MD5 password for given peer in the listener(s) for the peer's address
+ * family.
+ *
+ * NB: requires the session mutex LOCKED.
+ *
+ * This allows system to accept MD5 "signed" incoming connections from the
+ * given address.
+ *
+ * Returns: 0 => OK
+ * otherwise: errno -- the first error encountered.
+ *
+ * NB: peer address must be AF_INET or (if supported) AF_INET6
+ *
+ * NB: if there are no listeners in the required
+ */
+extern int
+bgp_md5_set_listeners(bgp_connection connection)
{
- int sock;
- int socklen;
- struct sockaddr_in sin;
- int ret, en;
+ bgp_listener listener ;
+ int ret ;
- sock = socket (AF_INET, SOCK_STREAM, 0);
- if (sock < 0)
- {
- zlog_err ("socket: %s", safe_strerror (errno));
- return sock;
- }
+ union sockunion* su = &connection->session->su_peer ;
- memset (&sin, 0, sizeof (struct sockaddr_in));
- sin.sin_family = AF_INET;
- sin.sin_port = htons (port);
- socklen = sizeof (struct sockaddr_in);
+#ifdef HAVE_IPV6
+ assert((su->sa.sa_family == AF_INET) || (su->sa.sa_family == AF_INET6)) ;
+#else
+ assert(su->sa.sa_family == AF_INET) ;
+#endif
- if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1))
- {
- zlog_err("bgp_socket: could not parse ip address %s: %s",
- address, safe_strerror (errno));
- return ret;
- }
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sin.sin_len = socklen;
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+ listener = bgp_listeners[su->sa.sa_family] ;
- ret = bgp_listener (sock, (struct sockaddr *) &sin, socklen);
- if (ret < 0)
+ while (listener != NULL)
{
- close (sock);
- return ret;
- }
- return sock;
-}
-#endif /* HAVE_IPV6 && !NRL */
+ ret = bgp_md5_set_socket(qps_file_fd(&listener->qf), su,
+ connection->session->password) ;
+ if (ret != 0)
+ return ret ;
+ } ;
-void
-bgp_close (void)
-{
- struct listnode *node, *next;
- struct bgp_listener *listener;
+ return 0 ;
+} ;
- for (ALL_LIST_ELEMENTS (bm->listen_sockets, node, next, listener))
- {
- thread_cancel (listener->thread);
- close (listener->fd);
- listnode_delete (bm->listen_sockets, listener);
- XFREE (MTYPE_BGP_LISTENER, listener);
- }
-}
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
index 5bf2e5ff..d9966d7d 100644
--- a/bgpd/bgp_network.h
+++ b/bgpd/bgp_network.h
@@ -1,31 +1,39 @@
/* BGP network related header
- Copyright (C) 1999 Kunihiro Ishiguro
+ * Copyright (c) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
-This file is part of GNU Zebra.
-
-GNU Zebra is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
+#ifndef _QUAGGA_BGP_NETWORK_H
+#define _QUAGGA_BGP_NETWORK_H
-GNU Zebra is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
+#include "bgpd/bgp_connection.h"
-You should have received a copy of the GNU General Public License
-along with GNU Zebra; see the file COPYING. If not, write to the Free
-Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+extern int
+bgp_open_listeners(unsigned short port, const char *address) ;
-#ifndef _QUAGGA_BGP_NETWORK_H
-#define _QUAGGA_BGP_NETWORK_H
+extern void
+bgp_close_listeners(void) ;
-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 void
+bgp_open_connect(bgp_connection connection) ;
-extern int bgp_md5_set (struct peer *);
+extern int
+bgp_md5_set_listeners(bgp_connection connection) ;
#endif /* _QUAGGA_BGP_NETWORK_H */
diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c
new file mode 100644
index 00000000..49165626
--- /dev/null
+++ b/bgpd/bgp_notification.c
@@ -0,0 +1,149 @@
+/* BGP Notification state handling
+ * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+ *
+ * Recast for pthreaded bgpd: Copyright (C) Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "lib/memory.h"
+#include "bgpd/bgp_notification.h"
+
+/*==============================================================================
+ * A bgp_notify structure encapsulates the contents of a BGP NOTIFICATION
+ * message.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Calculate size for bgp_notify of given length
+ *
+ * Rounds up to multiple of 32, such that is always at least 16 bytes available.
+ */
+static inline bgp_size_t
+bgp_notify_size(bgp_size_t length)
+{
+ return sizeof(struct bgp_notify) + (((length + 32 + 16 - 1) / 32) * 32) ;
+} ;
+
+/*==============================================================================
+ * Create/Destroy bgp_notify
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate and initialise new notification
+ *
+ * Can specify an expected amount of data.
+ */
+extern bgp_notify
+bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ bgp_size_t expect)
+{
+ bgp_notify notification ;
+ bgp_size_t size = bgp_notify_size(expect) ;
+
+ notification = XCALLOC(MTYPE_BGP_NOTIFY, size) ;
+
+ notification->code = code ;
+ notification->subcode = subcode ;
+ notification->size = size ;
+ notification->length = 0 ;
+
+ return notification ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Duplicate existing notification (if any)
+ */
+extern bgp_notify
+bgp_notify_dup(bgp_notify notification)
+{
+ bgp_notify duplicate ;
+ bgp_size_t size ;
+
+ if (notification == NULL)
+ return NULL ;
+
+ size = bgp_notify_size(notification->length) ;
+ duplicate = XMALLOC(MTYPE_BGP_NOTIFY, size) ;
+
+ memcpy((void*)duplicate, (void*)notification, size) ;
+ duplicate->size = size ;
+
+ return duplicate ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set notification (if any)
+ */
+extern void
+bgp_notify_set(bgp_notify* p_dst, bgp_notify src)
+{
+ bgp_notify_free(*p_dst) ;
+ *p_dst = src ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set notification (if any)
+ */
+extern void
+bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src)
+{
+ bgp_notify_set(p_dst, bgp_notify_dup(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free notification structure
+ *
+ * Does nothing if there is no structure.
+ *
+ * Returns: NULL
+ */
+extern bgp_notify
+bgp_notify_free(bgp_notify notification)
+{
+ if (notification != NULL)
+ XFREE(MTYPE_BGP_NOTIFY, notification) ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Append data to given notification
+ *
+ * NB: returns possibly NEW ADDRESS of the notification.
+ */
+extern bgp_notify
+bgp_notify_append_data(bgp_notify notification, void* data, bgp_size_t len)
+{
+ bgp_size_t new_length = notification->length + len ;
+
+ if ((sizeof(struct bgp_notify) + new_length) > notification->size)
+ {
+ bgp_size_t size = bgp_notify_size(new_length) ;
+ notification = XREALLOC(MTYPE_BGP_NOTIFY, notification, size) ;
+ memset((void*)notification + notification->size, 0,
+ size - notification->size) ;
+ notification->size = size ;
+ } ;
+
+ memcpy((void*)(notification->data) + notification->length, data, len) ;
+
+ notification->length = new_length ;
+} ;
diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h
new file mode 100644
index 00000000..80a6cffd
--- /dev/null
+++ b/bgpd/bgp_notification.h
@@ -0,0 +1,286 @@
+/* BGP Notification state handling -- header
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * Recast for pthreaded bgpd: Copyright (C) Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_NOTIFY_H
+#define _QUAGGA_BGP_NOTIFY_H
+
+#include "bgpd/bgp_common.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * BGP NOTIFICATION message codes.
+ */
+typedef unsigned char bgp_nom_code_t ;
+typedef unsigned char bgp_nom_subcode_t ;
+
+#ifndef _GMCH_BGP_H
+
+/* Notification Message Error Codes...........................................*/
+enum BGP_NOMC
+{
+ BGP_NOMC_UNDEF = 0, /* Nothing defined for this code */
+
+ BGP_NOMC_HEADER = 1, /* Message Header Error */
+ BGP_NOMC_OPEN = 2, /* Open Message Error */
+ BGP_NOMC_UPDATE = 3, /* Update Message Error */
+ BGP_NOMC_HOLD_EXP = 4, /* Hold timer expired */
+ BGP_NOMC_FSM = 5, /* Finite State Machine Error */
+ BGP_NOMC_CEASE = 6, /* Cease RFC4486 */
+
+ BGP_NOMC_MAX = 6 /* max known error code */
+} ;
+
+/* Notification Message Error Subcodes........................................*/
+
+enum BGP_NOMS
+{
+ BGP_NOMS_UNSPECIFIC = 0 /* If nothing else applies */
+};
+
+enum BGP_NOMS_HEADER /* BGP_NOMC_HEADER subcodes */
+{
+ BGP_NOMS_H_NOT_SYNC = 1, /* Connection Not Synchronised */
+ /* (Marker field not all = 1,'s !) */
+ BGP_NOMS_H_BAD_LEN = 2, /* Bad Message Length */
+ /* DATA: the length that failed */
+ BGP_NOMS_H_BAD_TYPE = 3, /* Bad Message Type */
+ /* DATA: the message type objected to */
+
+ BGP_NOMS_H_MAX = 3, /* max known subcode */
+} ;
+
+enum BGP_NOMS_OPEN /* BGP_NOMC_OPEN subcodes */
+{
+ BGP_NOMS_O_VERSION = 1, /* Unsupported Version Number */
+ /* DATA: largest supported version */
+ BGP_NOMS_O_BAD_AS = 2, /* Bad Peer AS */
+ BGP_NOMS_O_BAD_ID = 3, /* Bad BGP Identifier */
+ BGP_NOMS_O_OPTION = 4, /* Unsupported Optional Parameter */
+ BGP_NOMS_O_AUTH = 5, /* Authentication Failure (depr.) */
+ BGP_NOMS_O_H_TIME = 6, /* Unacceptable Hold Time */
+
+ BGP_NOMS_O_CAPABILITY = 7, /* Unsupported Capability RFC5492 */
+ /* DATA: the unsupported capabilities */
+
+ BGP_NOMS_O_MAX = 7, /* max known subcode */
+} ;
+
+enum BGP_NOMS_UPDATE /* BGP_NOMC_UPDATE subcodes */
+{
+ BGP_NOMS_U_A_LIST = 1, /* Malformed Attribute List */
+ /* (Attribute repeated) */
+ BGP_NOMS_U_UNKNOWN = 2, /* Unrecognised Well-known Attrib */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_MISSING = 3, /* Missing Well-known Attrib. */
+ /* DATA: type of missing attribute(s?) */
+ BGP_NOMS_U_A_FLAGS = 4, /* Attribute Flags Error */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_A_LENGTH = 5, /* Attribute Length Error */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_ORIGIN = 6, /* Invalid Origin Attribute */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_AS_LOOP = 7, /* AS Routeing Loop (deprecated) */
+ BGP_NOMS_U_NEXT_HOP = 8, /* Invalid NEXT_HOP Attrib. */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_OPTIONAL = 9, /* Optional Attribute Error */
+ /* DATA: erroneous attribute */
+ BGP_NOMS_U_NETWORK = 10, /* Invalid Network Field */
+ /* (badly formed NLRI) */
+ BGP_NOMS_U_AS_PATH = 11, /* Malformed AS Path */
+
+ BGP_NOMS_U_MAX = 11, /* max known subcode */
+} ;
+
+enum BGP_NOMS_HOLD_EXP /* BGP_NOMC_HOLD_EXP subcodes */
+{
+ BGP_NOMS_HE_MAX = 0 /* max known subcode */
+} ;
+
+enum BGP_NOMC_FSM /* BGP_NOMC_FSM subcodes */
+{
+ BGP_NOMS_F_MAX = 0 /* max known subcode */
+} ;
+
+enum BGP_NOMS_CEASE /* BGP_NOMC_CEASE subcodes RFC4486 */
+{
+ BGP_NOMS_C_MAX_PREF = 1, /* Max Number of Prefixes Reached MUST */
+ /* DATA: MAY be: AFI/SAFI/Upper-Bound */
+ BGP_NOMS_C_SHUTDOWN = 2, /* Administrative Shutdown SHOULD */
+ BGP_NOMS_C_DECONFIG = 3, /* Peer De-configured SHOULD */
+ BGP_NOMS_C_RESET = 4, /* Administrative Reset SHOULD */
+ BGP_NOMS_C_REJECTED = 5, /* Connection Rejected SHOULD */
+ BGP_NOMS_C_CONFIG = 6, /* Other Configuration Change SHOULD */
+ BGP_NOMS_C_COLLISION = 7, /* Connection Collision Res. SHOULD */
+ BGP_NOMS_C_RESOURCES = 8, /* Out of Resources MAY */
+
+ BGP_NOMS_C_MAX = 8 /* max known subcode */
+} ;
+
+#endif
+
+/*==============================================================================
+ *
+ */
+typedef struct bgp_notify* bgp_notify ;
+
+struct bgp_notify
+{
+ bgp_nom_code_t code ;
+ bgp_nom_subcode_t subcode ;
+
+ bgp_size_t length ;
+ bgp_size_t size ;
+ char data[] ;
+} ;
+
+/*==============================================================================
+ * "Legacy" definitions
+ */
+
+/* BGP notify message codes. */
+#define BGP_NOTIFY_HEADER_ERR 1
+#define BGP_NOTIFY_OPEN_ERR 2
+#define BGP_NOTIFY_UPDATE_ERR 3
+#define BGP_NOTIFY_HOLD_ERR 4
+#define BGP_NOTIFY_FSM_ERR 5
+#define BGP_NOTIFY_CEASE 6
+#define BGP_NOTIFY_CAPABILITY_ERR 7
+#define BGP_NOTIFY_MAX 8
+
+/* BGP_NOTIFY_HEADER_ERR sub codes. */
+#define BGP_NOTIFY_HEADER_NOT_SYNC 1
+#define BGP_NOTIFY_HEADER_BAD_MESLEN 2
+#define BGP_NOTIFY_HEADER_BAD_MESTYPE 3
+#define BGP_NOTIFY_HEADER_MAX 4
+
+/* BGP_NOTIFY_OPEN_ERR sub codes. */
+#define BGP_NOTIFY_OPEN_UNSUP_VERSION 1
+#define BGP_NOTIFY_OPEN_BAD_PEER_AS 2
+#define BGP_NOTIFY_OPEN_BAD_BGP_IDENT 3
+#define BGP_NOTIFY_OPEN_UNSUP_PARAM 4
+#define BGP_NOTIFY_OPEN_AUTH_FAILURE 5
+#define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME 6
+#define BGP_NOTIFY_OPEN_UNSUP_CAPBL 7
+#define BGP_NOTIFY_OPEN_MAX 8
+
+/* BGP_NOTIFY_UPDATE_ERR sub codes. */
+#define BGP_NOTIFY_UPDATE_MAL_ATTR 1
+#define BGP_NOTIFY_UPDATE_UNREC_ATTR 2
+#define BGP_NOTIFY_UPDATE_MISS_ATTR 3
+#define BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR 4
+#define BGP_NOTIFY_UPDATE_ATTR_LENG_ERR 5
+#define BGP_NOTIFY_UPDATE_INVAL_ORIGIN 6
+#define BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP 7
+#define BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP 8
+#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
+#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
+#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11
+#define BGP_NOTIFY_UPDATE_MAX 12
+
+/* BGP_NOTIFY_CEASE sub codes (draft-ietf-idr-cease-subcode-05). */
+#define BGP_NOTIFY_CEASE_MAX_PREFIX 1
+#define BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN 2
+#define BGP_NOTIFY_CEASE_PEER_UNCONFIG 3
+#define BGP_NOTIFY_CEASE_ADMIN_RESET 4
+#define BGP_NOTIFY_CEASE_CONNECT_REJECT 5
+#define BGP_NOTIFY_CEASE_CONFIG_CHANGE 6
+#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION 7
+#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE 8
+#define BGP_NOTIFY_CEASE_MAX 9
+
+/* BGP_NOTIFY_CAPABILITY_ERR sub codes (draft-ietf-idr-dynamic-cap-02). */
+#define BGP_NOTIFY_CAPABILITY_INVALID_ACTION 1
+#define BGP_NOTIFY_CAPABILITY_INVALID_LENGTH 2
+#define BGP_NOTIFY_CAPABILITY_MALFORMED_CODE 3
+#define BGP_NOTIFY_CAPABILITY_MAX 4
+
+/*==============================================================================
+ *
+ */
+
+extern bgp_notify
+bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ bgp_size_t size) ;
+extern bgp_notify
+bgp_notify_free(bgp_notify notification) ;
+
+extern bgp_notify
+bgp_notify_dup(bgp_notify notification) ;
+
+extern void
+bgp_notify_set(bgp_notify* p_dst, bgp_notify src) ;
+
+extern void
+bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src) ;
+
+/*==============================================================================
+ * Access Functions -- mostly inline
+ *
+ * Note that the various get functions return undefined/unspecific/empty if
+ * given a NULL bgp_notify.
+ */
+
+Inline void
+bgp_notify_set_code(bgp_notify notification, bgp_nom_code_t code)
+{
+ notification->code = code ;
+} ;
+
+Inline void
+bgp_notify_set_subcode(bgp_notify notification, bgp_nom_subcode_t subcode)
+{
+ notification->subcode = subcode ;
+} ;
+
+extern bgp_notify
+bgp_notify_append_data(bgp_notify notification, void* data, bgp_size_t len) ;
+
+
+Inline bgp_nom_code_t
+bgp_notify_get_code(bgp_notify notification)
+{
+ return (notification != NULL) ? notification->code : BGP_NOMC_UNDEF ;
+} ;
+
+Inline bgp_nom_subcode_t
+bgp_notify_get_subcode(bgp_notify notification)
+{
+ return (notification != NULL) ? notification->subcode : BGP_NOMS_UNSPECIFIC ;
+} ;
+
+Inline bgp_size_t
+bgp_notify_get_length(bgp_notify notification)
+{
+ return (notification != NULL) ? notification->length : 0 ;
+} ;
+
+Inline void*
+bgp_notify_get_data(bgp_notify notification)
+{
+ return (notification != NULL) ? notification->data : NULL ;
+} ;
+
+#endif /* _QUAGGA_BGP_NOTIFY_H */
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 37595817..573371ee 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -29,6 +29,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "memory.h"
#include "bgpd/bgpd.h"
+
+#include "bgpd/bgp_peer.h"
+
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_fsm.h"
@@ -57,12 +60,12 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
pnt = peer->notify.data;
end = pnt + peer->notify.length;
-
+
while (pnt < end)
{
if (pnt + sizeof (struct capability_mp_data) + 2 > end)
return;
-
+
hdr = (struct capability_header *)pnt;
if (pnt + hdr->length + 2 > end)
return;
@@ -109,14 +112,14 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
vty_out (vty, " Capability error: vendor specific capability code %d",
hdr->code);
else
- vty_out (vty, " Capability error: unknown capability code %d",
+ vty_out (vty, " Capability error: unknown capability code %d",
hdr->code);
pnt += hdr->length + 2;
}
}
-static void
+static void
bgp_capability_mp_data (struct stream *s, struct capability_mp_data *mpc)
{
mpc->afi = stream_getw (s);
@@ -134,7 +137,7 @@ bgp_afi_safi_valid_indices (afi_t afi, safi_t *safi)
zlog_warn ("Invalid afi/safi combination (%u/%u)", afi, *safi);
return 0;
}
-
+
switch (afi)
{
case AFI_IP:
@@ -154,7 +157,7 @@ bgp_afi_safi_valid_indices (afi_t afi, safi_t *safi)
}
}
zlog_debug ("unknown afi/safi (%u/%u)", afi, *safi);
-
+
return 0;
}
@@ -164,22 +167,22 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
{
struct capability_mp_data mpc;
struct stream *s = BGP_INPUT (peer);
-
+
bgp_capability_mp_data (s, &mpc);
-
+
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u",
peer->host, mpc.afi, mpc.safi);
-
+
if (!bgp_afi_safi_valid_indices (mpc.afi, &mpc.safi))
return -1;
-
+
/* Now safi remapped, and afi/safi are valid array indices */
peer->afc_recv[mpc.afi][mpc.safi] = 1;
-
+
if (peer->afc[mpc.afi][mpc.safi])
peer->afc_nego[mpc.afi][mpc.safi] = 1;
- else
+ else
return -1;
return 0;
@@ -221,7 +224,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
u_char type;
u_char mode;
u_int16_t sm_cap = 0; /* capability send-mode receive */
- u_int16_t rm_cap = 0; /* capability receive-mode receive */
+ u_int16_t rm_cap = 0; /* capability receive-mode receive */
int i;
/* ORF Entry header */
@@ -229,7 +232,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
entry.num = stream_getc (s);
afi = entry.mpc.afi;
safi = entry.mpc.safi;
-
+
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s ORF Cap entry for afi/safi: %u/%u",
peer->host, entry.mpc.afi, entry.mpc.safi);
@@ -242,7 +245,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
peer->host, entry.mpc.afi, entry.mpc.safi);
return 0;
}
-
+
/* validate number field */
if (sizeof (struct capability_orf_entry) + (entry.num * 2) > hdr->length)
{
@@ -257,7 +260,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
{
type = stream_getc(s);
mode = stream_getc(s);
-
+
/* ORF Mode error check */
switch (mode)
{
@@ -297,7 +300,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
bgp_capability_orf_not_support (peer, afi, safi, type, mode);
continue;
}
-
+
/* AFI vs SAFI */
if (!((afi == AFI_IP && safi == SAFI_UNICAST)
|| (afi == AFI_IP && safi == SAFI_MULTICAST)
@@ -306,7 +309,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
bgp_capability_orf_not_support (peer, afi, safi, type, mode);
continue;
}
-
+
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s OPEN has %s ORF capability"
" as %s for afi/safi: %d/%d",
@@ -352,9 +355,9 @@ bgp_capability_orf (struct peer *peer, struct capability_header *hdr)
{
struct stream *s = BGP_INPUT (peer);
size_t end = stream_get_getp (s) + hdr->length;
-
+
assert (stream_get_getp(s) + sizeof(struct capability_orf_entry) <= end);
-
+
/* We must have at least one ORF entry, as the caller has already done
* minimum length validation for the capability code - for ORF there must
* at least one ORF entry (header and unknown number of pairs of bytes).
@@ -363,9 +366,9 @@ bgp_capability_orf (struct peer *peer, struct capability_header *hdr)
{
if (bgp_capability_orf_entry (peer, hdr) == -1)
return -1;
- }
+ }
while (stream_get_getp(s) + sizeof(struct capability_orf_entry) < end);
-
+
return 0;
}
@@ -397,7 +400,7 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
afi_t afi = stream_getw (s);
safi_t safi = stream_getc (s);
u_char flag = stream_getc (s);
-
+
if (!bgp_afi_safi_valid_indices (afi, &safi))
{
if (BGP_DEBUG (normal, NORMAL))
@@ -424,7 +427,7 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV);
if (CHECK_FLAG (flag, RESTART_F_BIT))
SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV);
-
+
}
}
return 0;
@@ -434,12 +437,12 @@ static as_t
bgp_capability_as4 (struct peer *peer, struct capability_header *hdr)
{
as_t as4 = stream_getl (BGP_INPUT(peer));
-
+
if (BGP_DEBUG (as4, AS4))
zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u",
peer->host, as4);
SET_FLAG (peer->cap, PEER_CAP_AS4_RCV);
-
+
return as4;
}
@@ -457,7 +460,7 @@ static const struct message capcode_str[] =
static const int capcode_str_max = sizeof(capcode_str)/sizeof(capcode_str[0]);
/* Minimum sizes for length field of each cap (so not inc. the header) */
-static const size_t cap_minsizes[] =
+static const size_t cap_minsizes[] =
{
[CAPABILITY_CODE_MP] = sizeof (struct capability_mp_data),
[CAPABILITY_CODE_REFRESH] = CAPABILITY_CODE_REFRESH_LEN,
@@ -478,15 +481,15 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
int ret;
struct stream *s = BGP_INPUT (peer);
size_t end = stream_get_getp (s) + length;
-
+
assert (STREAM_READABLE (s) >= length);
-
+
while (stream_get_getp (s) < end)
{
size_t start;
u_char *sp = stream_pnt (s);
struct capability_header caphdr;
-
+
/* We need at least capability code and capability length. */
if (stream_get_getp(s) + 2 > end)
{
@@ -494,11 +497,11 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
-
+
caphdr.code = stream_getc (s);
caphdr.length = stream_getc (s);
start = stream_get_getp (s);
-
+
/* Capability length check sanity check. */
if (start + caphdr.length > end)
{
@@ -506,13 +509,13 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
-
+
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s OPEN has %s capability (%u), length %u",
peer->host,
LOOKUP (capcode_str, caphdr.code),
caphdr.code, caphdr.length);
-
+
/* Length sanity check, type-specific, for known capabilities */
switch (caphdr.code)
{
@@ -529,9 +532,9 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
{
zlog_info ("%s %s Capability length error: got %u,"
" expected at least %u",
- peer->host,
+ peer->host,
LOOKUP (capcode_str, caphdr.code),
- caphdr.length,
+ caphdr.length,
(unsigned) cap_minsizes[caphdr.code]);
bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
return -1;
@@ -540,7 +543,7 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
default:
break;
}
-
+
switch (caphdr.code)
{
case CAPABILITY_CODE_MP:
@@ -590,7 +593,7 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
*/
if (!bgp_capability_as4 (peer, &caphdr))
return -1;
- break;
+ break;
default:
if (caphdr.code > 128)
{
@@ -622,9 +625,9 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
static int
bgp_auth_parse (struct peer *peer, size_t length)
{
- bgp_notify_send (peer,
- BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_AUTH_FAILURE);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_AUTH_FAILURE);
return -1;
}
@@ -650,7 +653,7 @@ peek_for_as4_capability (struct peer *peer, u_char length)
size_t orig_getp = stream_get_getp (s);
size_t end = orig_getp + length;
as_t as4 = 0;
-
+
/* The full capability parser will better flag the error.. */
if (STREAM_READABLE(s) < length)
return 0;
@@ -662,40 +665,40 @@ peek_for_as4_capability (struct peer *peer, u_char length)
/* the error cases we DONT handle, we ONLY try to read as4 out of
* correctly formatted options.
*/
- while (stream_get_getp(s) < end)
+ while (stream_get_getp(s) < end)
{
u_char opt_type;
u_char opt_length;
-
+
/* Check the length. */
if (stream_get_getp (s) + 2 > end)
goto end;
-
+
/* Fetch option type and length. */
opt_type = stream_getc (s);
opt_length = stream_getc (s);
-
+
/* Option length check. */
if (stream_get_getp (s) + opt_length > end)
goto end;
-
+
if (opt_type == BGP_OPEN_OPT_CAP)
{
unsigned long capd_start = stream_get_getp (s);
unsigned long capd_end = capd_start + opt_length;
-
+
assert (capd_end <= end);
-
+
while (stream_get_getp (s) < capd_end)
{
struct capability_header hdr;
-
+
if (stream_get_getp (s) + 2 > capd_end)
goto end;
-
+
hdr.code = stream_getc (s);
hdr.length = stream_getc (s);
-
+
if ((stream_get_getp(s) + hdr.length) > capd_end)
goto end;
@@ -703,11 +706,11 @@ peek_for_as4_capability (struct peer *peer, u_char length)
{
if (hdr.length != CAPABILITY_CODE_AS4_LEN)
goto end;
-
+
if (BGP_DEBUG (as4, AS4))
zlog_info ("[AS4] found AS4 capability, about to parse");
as4 = bgp_capability_as4 (peer, &hdr);
-
+
goto end;
}
stream_forward_getp (s, hdr.length);
@@ -736,12 +739,12 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u",
peer->host, length);
-
+
while (stream_get_getp(s) < end)
{
u_char opt_type;
u_char opt_length;
-
+
/* Must have at least an OPEN option header */
if (STREAM_READABLE(s) < 2)
{
@@ -753,7 +756,7 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
/* Fetch option type and length. */
opt_type = stream_getc (s);
opt_length = stream_getc (s);
-
+
/* Option length check. */
if (STREAM_READABLE (s) < opt_length)
{
@@ -768,7 +771,7 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" :
opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown",
opt_length);
-
+
switch (opt_type)
{
case BGP_OPEN_OPT_AUTH:
@@ -779,9 +782,9 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
*capability = 1;
break;
default:
- bgp_notify_send (peer,
- BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_UNSUP_PARAM);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_PARAM);
ret = -1;
break;
}
@@ -801,9 +804,9 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
/* If Unsupported Capability exists. */
if (error != error_data)
{
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
error_data, error - error_data);
return -1;
}
@@ -812,8 +815,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
peer. */
if (! strict_capability_same (peer))
{
- bgp_notify_send (peer,
- BGP_NOTIFY_OPEN_ERR,
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNSUP_CAPBL);
return -1;
}
@@ -823,7 +826,7 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
error. */
if (*capability && ! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
{
- if (! peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ if (! peer->afc_nego[AFI_IP][SAFI_UNICAST]
&& ! peer->afc_nego[AFI_IP][SAFI_MULTICAST]
&& ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
&& ! peer->afc_nego[AFI_IP6][SAFI_UNICAST]
@@ -833,13 +836,13 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
if (error != error_data)
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
error_data, error - error_data);
else
- bgp_notify_send (peer,
- BGP_NOTIFY_OPEN_ERR,
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNSUP_CAPBL);
return -1;
}
@@ -929,7 +932,7 @@ bgp_open_capability (struct stream *s, struct peer *peer)
stream_putc (s, 0);
/* Do not send capability. */
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN)
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN)
|| CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
return;
diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c
new file mode 100644
index 00000000..76c4b4bd
--- /dev/null
+++ b/bgpd/bgp_open_state.c
@@ -0,0 +1,138 @@
+/* BGP Open State -- functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_peer.h"
+
+#include "lib/memory.h"
+
+
+#include "bgpd/bgpd.h"
+
+/*==============================================================================
+ * BGP Open State.
+ *
+ * This structure encapsulates all the information that may be sent/received
+ * in a BGP OPEN Message.
+ *
+ */
+
+/* Initialise new bgp_open_state structure -- allocate if required.
+ *
+ */
+extern bgp_open_state
+bgp_open_state_init_new(bgp_open_state state)
+{
+ if (state == NULL)
+ state = XCALLOC(MTYPE_BGP_OPEN_STATE, sizeof(struct bgp_open_state)) ;
+ else
+ memset(state, 0, sizeof(struct bgp_open_state)) ;
+
+ return state ;
+} ;
+
+extern bgp_open_state
+bgp_open_state_free(bgp_open_state state)
+{
+ if (state != NULL)
+ XFREE(MTYPE_BGP_OPEN_STATE, state) ;
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Construct new bgp_open_state for the given peer -- allocate if required.
+ *
+ * Initialises the structure according to the current peer state.
+ */
+
+extern bgp_session
+bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer)
+{
+ safi_t safi ;
+ afi_t afi ;
+ qafx_num_t qafx ;
+
+ state = bgp_open_state_init_new(state) ; /* allocate if req. Zeroise. */
+
+ /* Choose the appropriate ASN */
+ if (peer->change_local_as)
+ state->my_as = peer->change_local_as ;
+ else
+ state->my_as = peer->local_as ;
+
+ /* Choose the appropriate hold time */
+ if (peer->config & PEER_CONFIG_TIMER)
+ state->holdtime = peer->holdtime ;
+ else
+ state->holdtime = peer->bgp->default_holdtime ;
+
+ /* Set our bgpd_id */
+ state->bgp_id = peer->local_id ;
+
+ /* Announce self as AS4 speaker if required */
+ state->can_as4 = ((peer->cap & PEER_CAP_AS4_ADV) != 0) ;
+
+ /* Fill in the supported AFI/SAFI */
+
+ for (afi = qAFI_MIN ; afi <= qAFI_MAX ; ++afi)
+ for (safi = qSAFI_MIN ; safi <= qSAFI_MAX ; ++safi)
+ if (peer->afc[afi][safi])
+ state->can_mp_ext |= quafi_bit(qafx_num_from_qAFI_qSAFI(afi, safi)) ;
+
+ /* Route refresh. */
+ state->can_r_refresh = (peer->cap & PEER_CAP_REFRESH_ADV)
+ ? (bgp_cap_form_old | bgp_cap_form_new)
+ : bgp_cap_form_none ;
+
+ /* ORF capability. */
+ for (afi = qAFI_MIN ; afi <= qAFI_MAX ; ++afi)
+ for (safi = qSAFI_MIN ; safi <= qSAFI_MAX ; ++safi)
+ {
+ if (peer->af_flags[afi][safi] & PEER_FLAG_ORF_PREFIX_SM)
+ state->can_orf_prefix_send |=
+ quafi_bit(qafx_num_from_qAFI_qSAFI(afi, safi)) ;
+ if (peer->af_flags[afi][safi] & PEER_FLAG_ORF_PREFIX_RM)
+ state->can_orf_prefix_recv |=
+ quafi_bit(qafx_num_from_qAFI_qSAFI(afi, safi)) ;
+ } ;
+
+ state->can_orf_prefix = (state->can_orf_prefix_send |
+ state->can_orf_prefix_recv)
+ ? (bgp_cap_form_old | bgp_cap_form_new)
+ : bgp_cap_form_none ;
+
+ /* Graceful restart capability */
+ /* TODO: check that support graceful restart for all supported AFI/SAFI */
+ if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART))
+ {
+ state->can_g_restart = state->can_mp_ext ;
+ state->restart_time = peer->bgp->restart_time ;
+ }
+ else
+ {
+ state->can_g_restart = 0 ;
+ state->restart_time = 0 ;
+ } ;
+
+ /* TODO: check not restarting and not preserving forwarding state (?) */
+ state->can_nsf = 0 ;
+ state->restarting = 0 ;
+} ;
diff --git a/bgpd/bgp_open_state.h b/bgpd/bgp_open_state.h
new file mode 100644
index 00000000..8dc4fb3c
--- /dev/null
+++ b/bgpd/bgp_open_state.h
@@ -0,0 +1,91 @@
+/* BGP Open State -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_OPEN_STATE_H
+#define _QUAGGA_BGP_OPEN_STATE_H
+
+#include "bgpd/bgp_common.h"
+
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Some BGP Capabilities have old and new forms. Wish to control whether to
+ * send both old and/or new forms, and to track what form(s) received the
+ * capability in.
+ */
+typedef enum bgp_cap_form bgp_cap_form_t ;
+
+enum bgp_cap_form
+{
+ bgp_cap_form_none = 0,
+ bgp_cap_form_old = 1,
+ bgp_cap_form_new = 2
+} ;
+
+/*==============================================================================
+ * BGP Open State.
+ *
+ * This structure encapsulates all the information that may be sent/received
+ * in a BGP OPEN Message.
+ *
+ */
+
+struct bgp_open_state
+{
+ as_t my_as ; /* generic ASN */
+ unsigned holdtime ; /* in seconds */
+ bgp_id_t bgp_id ; /* eg an IPv4 address as integer */
+
+ int can_capability ; /* false => don't send capabilities */
+
+ int can_as4 ; /* true/false */
+
+ qafx_set_t can_mp_ext ; /* will accept, may send these */
+
+ bgp_cap_form_t can_r_refresh ; /* none/old/new */
+ bgp_cap_form_t can_orf_prefix ; /* none/old/new */
+
+ qafx_set_t can_orf_prefix_send ; /* wish to send ORF Prefix-List */
+ qafx_set_t can_orf_prefix_recv ; /* will accept ORF Prefix-List */
+
+ qafx_set_t can_g_restart ; /* will gracefully restart these */
+ qafx_set_t can_nsf ; /* will preserve forwarding state */
+
+ int restarting ; /* Restart State flag */
+ int restart_time ; /* Restart Time in seconds */
+
+} ;
+
+/*==============================================================================
+ *
+ */
+
+extern bgp_open_state
+bgp_open_state_init_new(bgp_open_state state) ;
+
+extern bgp_open_state
+bgp_open_state_free(bgp_open_state state) ;
+
+
+#endif /* QUAGGA_BGP_OPEN_STATE_H */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index cbf58e56..cfafbdf9 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -32,6 +32,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "plist.h"
#include "bgpd/bgpd.h"
+
+#include "bgpd/bgp_peer.h"
+
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_attr.h"
diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c
new file mode 100644
index 00000000..03439928
--- /dev/null
+++ b/bgpd/bgp_peer.c
@@ -0,0 +1,762 @@
+/* BGP Peer Handling
+ * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+ *
+ * Recast for pthreaded bgpd: Copyright (C) Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "thread.h"
+#include "log.h"
+#include "stream.h"
+#include "memory.h"
+#include "plist.h"
+#include "mqueue.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_peer.h"
+
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_open.h"
+
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_session.h"
+
+#ifdef HAVE_SNMP
+#include "bgpd/bgp_snmp.h"
+#endif /* HAVE_SNMP */
+
+/*==============================================================================
+ * This is the high level management of BGP Peers and peering conversations.
+ *
+ * The BGP Engine looks after the opening, running and closing of BGP sessions.
+ *
+ * Here we look after...
+ *
+ * * the peer state and the effects of changes in that state
+ * *
+ * * timers for advertisements, graceful restart, ...
+ *
+ * The naming of peers/sessions and bgp_session_index
+ * --------------------------------------------------
+ *
+ * Each peer/session is known by its IP address (IPv4 or IPv6) -- see the
+ * "neighbor" commands.
+ *
+ * No matter how many bgp instances there may be, only one peer/session can
+ * exist with a given IP address.
+ *
+ * The bgp_peer_index maps IP addresses to the peer, and hence to the session
+ * (if any exists and is active).
+ *
+ */
+
+
+/*==============================================================================
+ * BGP Peer Index
+ *
+ * When peers are created, they are registered in the bgp_peer_index. When
+ * they are destroyed, they are removed. This is done by the Routeing Engine.
+ *
+ * The peer index is used by the Routeing Engine to lookup peers.
+ *
+ * The BGP Engine needs to lookup sessions when a listening socket accepts a
+ * connection -- first, to decide whether to continue with the connection, and
+ * second, to tie the connection to the right session. It uses the peer index
+ * to do this.
+ *
+ * A mutex is used to coordinate access to the index.
+ *
+ * NB: the bgp_peer points to its bgp_session (if any). The session pointer
+ * MUST only be changed while holding the index mutex.
+ *
+ * See bgp_peer_set_session().
+ *
+ * NB: to avoid deadlock, MUST NOT do anything using the index while holding
+ * any session mutex.
+ *
+ * But may acquire a session mutex while doing things to the index.
+ */
+
+static struct symbol_table bgp_peer_index ;
+static qpt_mutex_t bgp_peer_index_mutex ;
+
+inline static void BGP_PEER_INDEX_LOCK(void)
+{
+ qpt_mutex_lock(&bgp_peer_index_mutex) ;
+} ;
+
+inline static void BGP_PEER_INDEX_UNLOCK(void)
+{
+ qpt_mutex_unlock(&bgp_peer_index_mutex) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise the bgp_peer_index.
+ *
+ * This must be done before any peers are configured !
+ */
+extern void
+bgp_peer_index_init(void* parent)
+{
+ symbol_table_init_new(
+ &bgp_peer_index,
+ parent,
+ 10, /* start ready for a few sessions */
+ 200, /* allow to be quite dense */
+ sockunion_symbol_hash, /* "name" is an IP Address */
+ NULL) ; /* no value change call-back */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise the bgp_peer_index_mutex.
+ *
+ * This must be done before the BGP Engine is started.
+ */
+extern void
+bgp_peer_index_mutex_init(void* parent)
+{
+ qpt_mutex_init(&bgp_peer_index_mutex, qpt_mutex_recursive) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Lookup a peer -- do nothing if does not exist
+ *
+ * For use by the Routeing Engine.
+ *
+ * Returns the bgp_peer -- NULL if not found.
+ */
+extern bgp_peer
+bgp_peer_index_seek(union sockunion* su)
+{
+ bgp_peer peer ;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ peer = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return peer ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Register a peer in the peer index.
+ *
+ * For use by the Routeing Engine.
+ *
+ * NB: it is a FATAL error to register a peer for an address which is already
+ * registered.
+ */
+extern void
+bgp_peer_index_register(bgp_peer peer, union sockunion* su)
+{
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ peer = symbol_set_value(symbol_find(&bgp_peer_index, su), peer) ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ passert(peer != NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Lookup a session -- do nothing if does not exist
+ *
+ * For use by the BGP Engine.
+ *
+ * Returns the bgp_session, or NULL if not found OR not active
+ * Sets *p_found <=> found.
+ *
+ * NB: the BGP Engine may not access the bgp_session structure if it is not
+ * in either of the active states (Enabled or Established).
+ *
+ * So this function does not return the bgp_session unless it is active.
+ *
+ * For callers who care, the p_found return indicates whether the session
+ * exists, or not.
+ */
+extern bgp_session
+bgp_session_index_seek(union sockunion* su, int* p_found)
+{
+ bgp_session session ;
+ bgp_peer peer ;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ peer = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+ *p_found = (peer != NULL) ;
+ if (*p_found && bgp_session_is_active(peer->session))
+ /* NULL peer->session is not active */
+ session = peer->session ;
+ else
+ session = NULL ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return session ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set peer's session pointer.
+ *
+ * For use by the Routeing Engine. Locks the bgp_peer_index mutex so that the
+ * BGP Engine is not fooled when it looks up the session.
+ *
+ * Returns the old session pointer value.
+ *
+ * NB: it is a FATAL error to change the pointer if the current session is
+ * "active".
+ *
+ */
+
+extern bgp_session
+bgp_session_index_seek(bgp_peer peer, bgp_session new_session)
+{
+ bgp_session old_session ;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ old_session = peer->session ;
+ peer->session = new_session ;
+
+ passert(!bgp_session_is_active(old_session)) ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return old_session ;
+} ;
+
+
+
+/*==============================================================================
+ * Deal with change in session state -- mqueue_action function.
+ *
+ * Receives notifications from the BGP Engine when a BGP Session state changes,
+ * which may be:
+ *
+ * Enabled -> Established -- connection up and Opens exchanged
+ * Enabled -> Stopped -- stopped trying to connect (for some reason)
+ * Established -> Stopped -- for some reason
+ *
+ * -- arg0 = session
+ * arg1 = new state
+ *
+ */
+void
+bgp_peer_new_session_state(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_session session = mqb_get_arg0(mqb) ;
+ bgp_session_state_t state = mqb_get_arg1_i(mqb) ;
+ bgp_peer peer = session->peer ;
+
+ if (flag == mqb_action)
+ {
+ bgp_session_lock(session) ;
+
+
+
+ switch(new_state)
+ {
+ /* If now Enabled, then the BGP Engine is attempting to connect */
+ /* (may be waiting for the Idle Hold Time to expire. */
+ case bgp_session_Enabled:
+ break ;
+
+ /* If now Established, then the BGP Engine has exchanged BGP Open */
+ /* messages, and received the KeepAlive that acknowledges our Open. */
+ case bgp_session_Established:
+ break ;
+
+ /* If now Stopped, then for some reason the BGP Engine has either */
+ /* stopped trying to connect, or the session has been stopped. */
+ case bgp_session_Stopped:
+ break ;
+
+ default:
+ } ;
+
+ bgp_session_unlock(session) ;
+ } ;
+
+ mqb_free(mqb) ;
+} ;
+
+/* BGP Session has been Established.
+ *
+ * NB: holding the session structure mutex.
+ *
+ * Send keepalive packet then make first update information.
+ */
+static int
+bgp_session_has_established(bgp_peer peer)
+{
+ struct bgp_notify *notify;
+ afi_t afi;
+ safi_t safi;
+ int nsf_af_count = 0;
+
+ /* Reset capability open status flag. */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
+ SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* Clear last notification data. */
+ notify = &peer->notify;
+ if (notify->data)
+ XFREE (MTYPE_TMP, notify->data);
+ memset (notify, 0, sizeof (struct bgp_notify));
+
+ /* Clear start timer value to default. */
+ peer->v_start = BGP_INIT_START_TIMER;
+
+ /* Increment established count. */
+ peer->established++;
+
+ /* bgp log-neighbor-changes of neighbor Up */
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+ zlog_info ("%%ADJCHANGE: neighbor %s Up", peer->host);
+
+ /* graceful restart */
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
+ {
+ if (peer->afc_nego[afi][safi]
+ && CHECK_FLAG (peer->cap, PEER_CAP_RESTART_ADV)
+ && CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV))
+ {
+ if (peer->nsf[afi][safi]
+ && ! CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV))
+ bgp_clear_stale_route (peer, afi, safi);
+
+ peer->nsf[afi][safi] = 1;
+ nsf_af_count++;
+ }
+ else
+ {
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route (peer, afi, safi);
+ peer->nsf[afi][safi] = 0;
+ }
+ }
+
+ if (nsf_af_count)
+ SET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+ else
+ {
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+ if (peer->t_gr_stale)
+ {
+ BGP_TIMER_OFF (peer->t_gr_stale);
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+ }
+ }
+
+ if (peer->t_gr_restart)
+ {
+ BGP_TIMER_OFF (peer->t_gr_restart);
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s graceful restart timer stopped", peer->host);
+ }
+
+#ifdef HAVE_SNMP
+ bgpTrapEstablished (peer);
+#endif /* HAVE_SNMP */
+
+ /* Reset uptime, send keepalive, send current table. */
+ bgp_uptime_reset (peer);
+
+ /* Send route-refresh when ORF is enabled */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV))
+ {
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
+ bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX,
+ REFRESH_IMMEDIATE, 0);
+ else if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+ bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX_OLD,
+ REFRESH_IMMEDIATE, 0);
+ }
+
+ if (peer->v_keepalive)
+ bgp_keepalive_send (peer);
+
+ /* First update is deferred until ORF or ROUTE-REFRESH is received */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV))
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV))
+ SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH);
+
+ bgp_announce_route_all (peer);
+
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1);
+
+ return 0;
+}
+
+/* Administrative BGP peer stop event. */
+/* May be called multiple times for the same peer */
+static int
+bgp_session_has_stopped(bgp_peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+ char orf_name[BUFSIZ];
+
+ /* Can't do this in Clearing; events are used for state transitions */
+ if (peer->status != Clearing)
+ {
+ /* Delete all existing events of the peer */
+ BGP_EVENT_FLUSH (peer);
+ }
+
+ /* Increment Dropped count. */
+ if (peer->status == Established)
+ {
+ peer->dropped++;
+
+ /* bgp log-neighbor-changes of neighbor Down */
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+ zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host,
+ peer_down_str [(int) peer->last_reset]);
+
+ /* graceful restart */
+ if (peer->t_gr_stale)
+ {
+ BGP_TIMER_OFF (peer->t_gr_stale);
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+ }
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+ {
+ if (BGP_DEBUG (events, EVENTS))
+ {
+ zlog_debug ("%s graceful restart timer started for %d sec",
+ peer->host, peer->v_gr_restart);
+ zlog_debug ("%s graceful restart stalepath timer started for %d sec",
+ peer->host, peer->bgp->stalepath_time);
+ }
+ BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire,
+ peer->v_gr_restart);
+ BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire,
+ peer->bgp->stalepath_time);
+ }
+ else
+ {
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
+ peer->nsf[afi][safi] = 0;
+ }
+
+ /* set last reset time */
+ peer->resettime = time (NULL);
+ /* Reset uptime. */
+ bgp_uptime_reset (peer);
+
+#ifdef HAVE_SNMP
+ bgpTrapBackwardTransition (peer);
+#endif /* HAVE_SNMP */
+
+ /* Reset uptime. */
+ bgp_uptime_reset (peer);
+
+ /* Reset peer synctime */
+ peer->synctime = 0;
+ }
+
+ /* Stop read and write threads when exists. */
+ BGP_READ_OFF (peer->t_read);
+ BGP_WRITE_OFF (peer->t_write);
+
+ /* Stop all timers. */
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ {
+ /* Reset all negotiated variables */
+ peer->afc_nego[afi][safi] = 0;
+ peer->afc_adv[afi][safi] = 0;
+ peer->afc_recv[afi][safi] = 0;
+
+ /* peer address family capability flags*/
+ peer->af_cap[afi][safi] = 0;
+
+ /* peer address family status flags*/
+ peer->af_sflags[afi][safi] = 0;
+
+ /* 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);
+ }
+
+ /* Reset keepalive and holdtime */
+ if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+ {
+ peer->v_keepalive = peer->keepalive;
+ peer->v_holdtime = peer->holdtime;
+ }
+ else
+ {
+ peer->v_keepalive = peer->bgp->default_keepalive;
+ peer->v_holdtime = peer->bgp->default_holdtime;
+ }
+
+ peer->update_time = 0;
+
+ /* Until we are sure that there is no problem about prefix count
+ this should be commented out.*/
+#if 0
+ /* Reset prefix count */
+ peer->pcount[AFI_IP][SAFI_UNICAST] = 0;
+ peer->pcount[AFI_IP][SAFI_MULTICAST] = 0;
+ peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0;
+ peer->pcount[AFI_IP6][SAFI_UNICAST] = 0;
+ peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
+#endif /* 0 */
+
+ return 0;
+}
+
+
+
+/* Stop all timers for the given peer
+ */
+static void
+bgp_peer_timers_stop(bgp_peer peer)
+{
+ BGP_TIMER_OFF(peer->t_asorig) ;
+ BGP_TIMER_OFF(peer->t_routeadv) ;
+
+ BGP_TIMER_OFF (peer->t_gr_restart);
+ BGP_TIMER_OFF (peer->t_gr_stale);
+ BGP_TIMER_OFF (peer->t_pmax_restart);
+} ;
+
+
+
+
+
+
+void
+bgp_timer_set (struct peer *peer)
+{
+ int jitter = 0;
+
+ switch (peer->status)
+ {
+ case Idle:
+ /* First entry point of peer's finite state machine. In Idle
+ status start timer is on unless peer is shutdown or peer is
+ inactive. All other timer must be turned off */
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case Connect:
+ /* After start timer is expired, the peer moves to Connnect
+ status. Make sure start timer is off and connect timer is
+ on. */
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case Active:
+ /* Active is waiting connection from remote peer. And if
+ connect timer is expired, change status to Connect. */
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case OpenSent:
+ /* OpenSent status. */
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case OpenConfirm:
+ /* OpenConfirm status. */
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case Established:
+ /* In Established status start and connect timer is turned
+ off. */
+ BGP_TIMER_OFF (peer->t_asorig);
+ break;
+ case Deleted:
+ BGP_TIMER_OFF (peer->t_gr_restart);
+ BGP_TIMER_OFF (peer->t_gr_stale);
+ BGP_TIMER_OFF (peer->t_pmax_restart);
+ case Clearing:
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ }
+}
+
+
+static int
+bgp_routeadv_timer (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_routeadv = NULL;
+
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG,
+ "%s [FSM] Timer (routeadv timer expire)",
+ peer->host);
+
+ peer->synctime = time (NULL);
+
+ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer,
+ peer->v_routeadv);
+
+ return 0;
+}
+
+/* Reset bgp update timer */
+static void
+bgp_uptime_reset (struct peer *peer)
+{
+ peer->uptime = time (NULL);
+}
+
+/* BGP Peer Down Cause */
+const char *peer_down_str[] =
+{
+ "",
+ "Router ID changed",
+ "Remote AS changed",
+ "Local AS change",
+ "Cluster ID changed",
+ "Confederation identifier changed",
+ "Confederation peer changed",
+ "RR client config change",
+ "RS client config change",
+ "Update source change",
+ "Address family activated",
+ "Admin. shutdown",
+ "User reset",
+ "BGP Notification received",
+ "BGP Notification send",
+ "Peer closed the session",
+ "Neighbor deleted",
+ "Peer-group add member",
+ "Peer-group delete member",
+ "Capability changed",
+ "Passive config change",
+ "Multihop config change",
+ "NSF peer closed the session"
+};
+
+static int
+bgp_graceful_restart_timer_expire (struct thread *thread)
+{
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ peer = THREAD_ARG (thread);
+ peer->t_gr_restart = NULL;
+
+ /* NSF delete stale route */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route (peer, afi, safi);
+
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+ BGP_TIMER_OFF (peer->t_gr_stale);
+
+ if (BGP_DEBUG (events, EVENTS))
+ {
+ zlog_debug ("%s graceful restart timer expired", peer->host);
+ zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+ }
+
+ bgp_timer_set (peer);
+
+ return 0;
+}
+
+static int
+bgp_graceful_stale_timer_expire (struct thread *thread)
+{
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ peer = THREAD_ARG (thread);
+ peer->t_gr_stale = NULL;
+
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s graceful restart stalepath timer expired", peer->host);
+
+ /* NSF delete stale route */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route (peer, afi, safi);
+
+ return 0;
+}
+
+
+/* BGP peer is stoped by the error. */
+static int
+bgp_stop_with_error (struct peer *peer)
+{
+ /* Double start timer. */
+ peer->v_start *= 2;
+
+ /* Overflow check. */
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
+
+ bgp_stop (peer);
+
+ return 0;
+}
+
diff --git a/bgpd/bgp_peer.h b/bgpd/bgp_peer.h
new file mode 100644
index 00000000..e0de8930
--- /dev/null
+++ b/bgpd/bgp_peer.h
@@ -0,0 +1,471 @@
+/* BGP Peer -- header
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_PEER_H
+#define _QUAGGA_BGP_PEER_H
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_notification.h"
+
+#include "lib/plist.h"
+
+/*==============================================================================
+ *
+ */
+
+enum bgp_route_map_types
+{
+ RMAP_IN = 0,
+ RMAP_OUT = 1,
+ RMAP_IMPORT = 2,
+ RMAP_EXPORT = 3,
+ RMAP_MAX = 4,
+} ;
+
+/*==============================================================================
+ *
+ */
+
+/* Next hop self address. */
+struct bgp_nexthop
+{
+ struct interface* ifp;
+ struct in_addr v4;
+#ifdef HAVE_IPV6
+ struct in6_addr v6_global;
+ struct in6_addr v6_local;
+#endif /* HAVE_IPV6 */
+};
+
+/* BGP filter structure. */
+struct bgp_filter
+{
+ /* Distribute-list. */
+ struct
+ {
+ char *name;
+ struct access_list *alist;
+ } dlist[FILTER_MAX];
+
+ /* Prefix-list. */
+ struct
+ {
+#if 1
+ prefix_list_ref ref ;
+#else
+ char *name;
+ struct prefix_list *plist;
+#endif
+ } plist[FILTER_MAX];
+
+ /* Filter-list. */
+ struct
+ {
+ char *name;
+ struct as_list *aslist;
+ } aslist[FILTER_MAX];
+
+ /* Route-map. */
+ struct
+ {
+ char *name;
+ struct route_map *map;
+ } map[RMAP_MAX];
+
+ /* Unsuppress-map. */
+ struct
+ {
+ char *name;
+ struct route_map *map;
+ } usmap;
+};
+
+/*==============================================================================
+ * struct peer -- the BGP neighbor structure.
+ *
+ *
+ *
+ */
+
+struct peer
+{
+ /* BGP structure. */
+ struct bgp *bgp;
+
+ /* reference count, primarily to allow bgp_process'ing of route_node's
+ * to be done after a struct peer is deleted.
+ *
+ * named 'lock' for hysterical reasons within Quagga.
+ */
+ int lock;
+
+ /* BGP peer group. */
+ struct peer_group *group;
+ u_char af_group[AFI_MAX][SAFI_MAX];
+
+ /* Peer's remote AS number. */
+ as_t as;
+
+ /* Peer's local AS number. */
+ as_t local_as;
+
+ /* Peer's Change local AS number. */
+ as_t change_local_as;
+
+ /* Remote router ID. */
+ struct in_addr remote_id;
+
+ /* Local router ID. */
+ struct in_addr local_id;
+
+ /* Peer specific RIB when configured as route-server-client. */
+ struct bgp_table *rib[AFI_MAX][SAFI_MAX];
+
+ /* Status of the peer. */
+ int status;
+ int ostatus;
+
+ /* Peer index, used for dumping TABLE_DUMP_V2 format */
+ uint16_t table_dump_index;
+
+ /* Peer information */
+ bgp_session session ; /* Current session */
+
+ int ttl; /* TTL of TCP connection to the peer. */
+ char *desc; /* Description of the peer. */
+ unsigned short port; /* Destination port for peer */
+ char *host; /* Printable address of the peer. */
+ union sockunion su; /* Sockunion address of the peer. */
+ time_t uptime; /* Last Up/Down time */
+ time_t readtime; /* Last read time */
+ time_t resettime; /* Last reset time */
+
+ unsigned int ifindex; /* ifindex of the BGP connection. */
+ char *ifname; /* bind interface name. */
+ char *update_if;
+ union sockunion *update_source;
+ struct zlog *log;
+
+ union sockunion *su_local; /* Sockunion of local address. */
+ union sockunion *su_remote; /* Sockunion of remote address. */
+ int shared_network; /* Is this peer shared same network. */
+ struct bgp_nexthop nexthop; /* Nexthop */
+
+ /* Peer address family configuration. */
+ u_char afc[AFI_MAX][SAFI_MAX];
+ u_char afc_nego[AFI_MAX][SAFI_MAX];
+ u_char afc_adv[AFI_MAX][SAFI_MAX];
+ u_char afc_recv[AFI_MAX][SAFI_MAX];
+
+ /* Capability flags (reset in bgp_stop) */
+ u_int16_t cap;
+#define PEER_CAP_REFRESH_ADV (1 << 0) /* refresh advertised */
+#define PEER_CAP_REFRESH_OLD_RCV (1 << 1) /* refresh old received */
+#define PEER_CAP_REFRESH_NEW_RCV (1 << 2) /* refresh rfc received */
+#define PEER_CAP_DYNAMIC_ADV (1 << 3) /* dynamic advertised */
+#define PEER_CAP_DYNAMIC_RCV (1 << 4) /* dynamic received */
+#define PEER_CAP_RESTART_ADV (1 << 5) /* restart advertised */
+#define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */
+#define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */
+#define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */
+
+ /* Capability flags (reset in bgp_stop) */
+ u_int16_t af_cap[AFI_MAX][SAFI_MAX];
+#define PEER_CAP_ORF_PREFIX_SM_ADV (1 << 0) /* send-mode advertised */
+#define PEER_CAP_ORF_PREFIX_RM_ADV (1 << 1) /* receive-mode advertised */
+#define PEER_CAP_ORF_PREFIX_SM_RCV (1 << 2) /* send-mode received */
+#define PEER_CAP_ORF_PREFIX_RM_RCV (1 << 3) /* receive-mode received */
+#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1 << 4) /* send-mode received */
+#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1 << 5) /* receive-mode received */
+#define PEER_CAP_RESTART_AF_RCV (1 << 6) /* graceful restart afi/safi received */
+#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1 << 7) /* graceful restart afi/safi F-bit received */
+
+ /* Global configuration flags. */
+ u_int32_t flags;
+#define PEER_FLAG_PASSIVE (1 << 0) /* passive mode */
+#define PEER_FLAG_SHUTDOWN (1 << 1) /* shutdown */
+#define PEER_FLAG_DONT_CAPABILITY (1 << 2) /* dont-capability */
+#define PEER_FLAG_OVERRIDE_CAPABILITY (1 << 3) /* override-capability */
+#define PEER_FLAG_STRICT_CAP_MATCH (1 << 4) /* strict-match */
+#define PEER_FLAG_DYNAMIC_CAPABILITY (1 << 5) /* dynamic capability */
+#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */
+#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */
+
+ /* NSF mode (graceful restart) */
+ u_char nsf[AFI_MAX][SAFI_MAX];
+
+ /* Per AF configuration flags. */
+ u_int32_t af_flags[AFI_MAX][SAFI_MAX];
+#define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */
+#define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */
+#define PEER_FLAG_NEXTHOP_SELF (1 << 2) /* next-hop-self */
+#define PEER_FLAG_REFLECTOR_CLIENT (1 << 3) /* reflector-client */
+#define PEER_FLAG_RSERVER_CLIENT (1 << 4) /* route-server-client */
+#define PEER_FLAG_SOFT_RECONFIG (1 << 5) /* soft-reconfiguration */
+#define PEER_FLAG_AS_PATH_UNCHANGED (1 << 6) /* transparent-as */
+#define PEER_FLAG_NEXTHOP_UNCHANGED (1 << 7) /* transparent-next-hop */
+#define PEER_FLAG_MED_UNCHANGED (1 << 8) /* transparent-next-hop */
+#define PEER_FLAG_DEFAULT_ORIGINATE (1 << 9) /* default-originate */
+#define PEER_FLAG_REMOVE_PRIVATE_AS (1 << 10) /* remove-private-as */
+#define PEER_FLAG_ALLOWAS_IN (1 << 11) /* set allowas-in */
+#define PEER_FLAG_ORF_PREFIX_SM (1 << 12) /* orf capability send-mode */
+#define PEER_FLAG_ORF_PREFIX_RM (1 << 13) /* orf capability receive-mode */
+#define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */
+#define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */
+#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */
+
+ /* MD5 password */
+ char *password;
+
+ /* default-originate route-map. */
+ struct
+ {
+ char *name;
+ struct route_map *map;
+ } default_rmap[AFI_MAX][SAFI_MAX];
+
+ /* Peer status flags. */
+ u_int16_t sflags;
+#define PEER_STATUS_ACCEPT_PEER (1 << 0) /* accept peer */
+#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */
+#define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */
+#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */
+#define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */
+#define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */
+#define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */
+
+ /* Peer status af flags (reset in bgp_stop) */
+ u_int16_t af_sflags[AFI_MAX][SAFI_MAX];
+#define PEER_STATUS_ORF_PREFIX_SEND (1 << 0) /* prefix-list send peer */
+#define PEER_STATUS_ORF_WAIT_REFRESH (1 << 1) /* wait refresh received peer */
+#define PEER_STATUS_DEFAULT_ORIGINATE (1 << 2) /* default-originate peer */
+#define PEER_STATUS_PREFIX_THRESHOLD (1 << 3) /* exceed prefix-threshold */
+#define PEER_STATUS_PREFIX_LIMIT (1 << 4) /* exceed prefix-limit */
+#define PEER_STATUS_EOR_SEND (1 << 5) /* end-of-rib send to peer */
+#define PEER_STATUS_EOR_RECEIVED (1 << 6) /* end-of-rib received from peer */
+
+ /* Default attribute value for the peer. */
+ u_int32_t config;
+#define PEER_CONFIG_WEIGHT (1 << 0) /* Default weight. */
+#define PEER_CONFIG_TIMER (1 << 1) /* keepalive & holdtime */
+#define PEER_CONFIG_CONNECT (1 << 2) /* connect */
+#define PEER_CONFIG_ROUTEADV (1 << 3) /* route advertise */
+ u_int32_t weight;
+ u_int32_t holdtime;
+ u_int32_t keepalive;
+ u_int32_t connect;
+ u_int32_t routeadv;
+
+ /* Timer values. */
+ u_int32_t v_start;
+ u_int32_t v_connect;
+ u_int32_t v_holdtime;
+ u_int32_t v_keepalive;
+ u_int32_t v_asorig;
+ u_int32_t v_routeadv;
+ u_int32_t v_pmax_restart;
+ u_int32_t v_gr_restart;
+
+ /* Threads. */
+ struct thread *t_read;
+ struct thread *t_write;
+ struct thread *t_asorig;
+ struct thread *t_routeadv;
+ struct thread *t_pmax_restart;
+ struct thread *t_gr_restart;
+ struct thread *t_gr_stale;
+
+ /* workqueues */
+ struct work_queue *clear_node_queue;
+
+ /* Statistics field */
+ u_int32_t open_in; /* Open message input count */
+ u_int32_t open_out; /* Open message output count */
+ u_int32_t update_in; /* Update message input count */
+ u_int32_t update_out; /* Update message ouput count */
+ time_t update_time; /* Update message received time. */
+ u_int32_t keepalive_in; /* Keepalive input count */
+ u_int32_t keepalive_out; /* Keepalive output count */
+ u_int32_t notify_in; /* Notify input count */
+ u_int32_t notify_out; /* Notify output count */
+ u_int32_t refresh_in; /* Route Refresh input count */
+ u_int32_t refresh_out; /* Route Refresh output count */
+ u_int32_t dynamic_cap_in; /* Dynamic Capability input count. */
+ u_int32_t dynamic_cap_out; /* Dynamic Capability output count. */
+
+ /* BGP state count */
+ u_int32_t established; /* Established */
+ u_int32_t dropped; /* Dropped */
+
+ /* Syncronization list and time. */
+ struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX];
+ time_t synctime;
+
+ /* Send prefix count. */
+ unsigned long scount[AFI_MAX][SAFI_MAX];
+
+ /* Announcement attribute hash. */
+ struct hash *hash[AFI_MAX][SAFI_MAX];
+
+ /* Notify data. */
+ struct bgp_notify notify;
+
+ /* Filter structure. */
+ struct bgp_filter filter[AFI_MAX][SAFI_MAX];
+
+ /* ORF Prefix-list */
+ struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX];
+
+ /* Prefix count. */
+ unsigned long pcount[AFI_MAX][SAFI_MAX];
+
+ /* Max prefix count. */
+ unsigned long pmax[AFI_MAX][SAFI_MAX];
+ u_char pmax_threshold[AFI_MAX][SAFI_MAX];
+ u_int16_t pmax_restart[AFI_MAX][SAFI_MAX];
+#define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75
+
+ /* allowas-in. */
+ char allowas_in[AFI_MAX][SAFI_MAX];
+
+ /* peer reset cause */
+ char last_reset;
+#define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */
+#define PEER_DOWN_REMOTE_AS_CHANGE 2 /* neighbor remote-as command */
+#define PEER_DOWN_LOCAL_AS_CHANGE 3 /* neighbor local-as command */
+#define PEER_DOWN_CLID_CHANGE 4 /* bgp cluster-id command */
+#define PEER_DOWN_CONFED_ID_CHANGE 5 /* bgp confederation identifier command */
+#define PEER_DOWN_CONFED_PEER_CHANGE 6 /* bgp confederation peer command */
+#define PEER_DOWN_RR_CLIENT_CHANGE 7 /* neighbor route-reflector-client command */
+#define PEER_DOWN_RS_CLIENT_CHANGE 8 /* neighbor route-server-client command */
+#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9 /* neighbor update-source command */
+#define PEER_DOWN_AF_ACTIVATE 10 /* neighbor activate command */
+#define PEER_DOWN_USER_SHUTDOWN 11 /* neighbor shutdown command */
+#define PEER_DOWN_USER_RESET 12 /* clear ip bgp command */
+#define PEER_DOWN_NOTIFY_RECEIVED 13 /* notification received */
+#define PEER_DOWN_NOTIFY_SEND 14 /* notification send */
+#define PEER_DOWN_CLOSE_SESSION 15 /* tcp session close */
+#define PEER_DOWN_NEIGHBOR_DELETE 16 /* neghbor delete */
+#define PEER_DOWN_RMAP_BIND 17 /* neghbor peer-group command */
+#define PEER_DOWN_RMAP_UNBIND 18 /* no neighbor peer-group command */
+#define PEER_DOWN_CAPABILITY_CHANGE 19 /* neighbor capability command */
+#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */
+#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */
+#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */
+
+ /* The kind of route-map Flags.*/
+ u_char rmap_type;
+#define PEER_RMAP_TYPE_IN (1 << 0) /* neighbor route-map in */
+#define PEER_RMAP_TYPE_OUT (1 << 1) /* neighbor route-map out */
+#define PEER_RMAP_TYPE_NETWORK (1 << 2) /* network route-map */
+#define PEER_RMAP_TYPE_REDISTRIBUTE (1 << 3) /* redistribute route-map */
+#define PEER_RMAP_TYPE_DEFAULT (1 << 4) /* default-originate route-map */
+#define PEER_RMAP_TYPE_NOSET (1 << 5) /* not allow to set commands */
+#define PEER_RMAP_TYPE_IMPORT (1 << 6) /* neighbor route-map import */
+#define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */
+} ;
+
+
+
+
+
+
+
+
+
+/* Macro for BGP read, write and timer thread. */
+#define BGP_READ_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->status != Deleted)) \
+ THREAD_READ_ON(master,T,F,peer,V); \
+ } while (0)
+
+#define BGP_READ_OFF(T) \
+ do { \
+ if (T) \
+ THREAD_READ_OFF(T); \
+ } while (0)
+
+#define BGP_WRITE_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->status != Deleted)) \
+ THREAD_WRITE_ON(master,(T),(F),peer,(V)); \
+ } while (0)
+
+#define BGP_WRITE_OFF(T) \
+ do { \
+ if (T) \
+ THREAD_WRITE_OFF(T); \
+ } while (0)
+
+#define BGP_TIMER_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->status != Deleted)) \
+ THREAD_TIMER_ON(master,(T),(F),peer,(V)); \
+ } while (0)
+
+#define BGP_TIMER_OFF(T) \
+ do { \
+ if (T) \
+ THREAD_TIMER_OFF(T); \
+ } while (0)
+
+#define BGP_EVENT_ADD(P,E) \
+ do { \
+ if ((P)->status != Deleted) \
+ thread_add_event (master, bgp_event, (P), (E)); \
+ } while (0)
+
+#define BGP_EVENT_FLUSH(P) \
+ do { \
+ assert (peer); \
+ thread_cancel_event (master, (P)); \
+ } while (0)
+
+/* Prototypes. */
+extern int bgp_event (struct thread *);
+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);
+extern const char *peer_down_str[];
+
+
+/*==============================================================================
+ *
+ */
+
+extern void
+bgp_peer_index_init(void* parent) ;
+
+extern void
+bgp_peer_index_mutex_init(void* parent) ;
+
+extern bgp_peer
+bgp_peer_index_seek(union sockunion* su) ;
+
+extern void
+bgp_peer_index_register(bgp_peer peer, union sockunion* su) ;
+
+extern bgp_session
+bgp_session_index_seek(union sockunion* su, int* p_found) ;
+
+#endif /* _QUAGGA_BGP_PEER_H */
+
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 206cf702..b3e498cb 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -36,6 +36,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "workqueue.h"
#include "bgpd/bgpd.h"
+
+#include "bgpd/bgp_peer.h"
+
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c
new file mode 100644
index 00000000..4d19308f
--- /dev/null
+++ b/bgpd/bgp_session.c
@@ -0,0 +1,189 @@
+/* BGP Session -- functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_peer.h"
+
+#include "lib/memory.h"
+
+/*==============================================================================
+ * BGP Session.
+ *
+ * Every bgp_peer has (at most) one bgp_session associated with it.
+ *
+ * A session is shared by the Routeing Engine and the BGP Engine -- so there
+ * is a mutex to coordinate access.
+ *
+ * NB: to avoid deadlock, do NOT attempt to lookup a peer or session while
+ * a session mutex !
+ *
+ * A session is created when the bgp peer is first enabled, and may be destroyed
+ * when the peer is disabled, or once the session has stopped.
+ *
+ * A session may be in one of four states:
+ *
+ * * bgp_session_Idle -- not doing anything
+ * * bgp_session_Enabled -- the BGP Engine is trying to connect
+ * * bgp_session_Established -- the BGP Engine is exchanging updates etc
+ * * bgp_session_Stopped -- a session has come to a dead stop
+ *
+ * NB: in Idle and Stopped states the BGP Engine has no interest in the session.
+ * These are known as the "inactive" states.
+ *
+ * NB: in Enabled and Established states the Routeing Engine it may be too late
+ * to change items in the session ! These are known as the "active" states.
+ *
+ * NB: once a session is enabled the BGP_Engine looks after the state, up to
+ * and including setting the Stopped state.
+ *
+ * The BGP Engine's primary interest is in its (private) bgp_connection
+ * structure(s), which (while a session is Enabled or Established) are pointed
+ * to by their associated session.
+ *
+ */
+
+/*==============================================================================
+ * BGP Session handling.
+ *
+ */
+
+/* Initialise new session structure -- allocate if required.
+ *
+ */
+extern bgp_session
+bgp_session_init_new(bgp_session session)
+{
+ if (session == NULL)
+ session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ;
+ else
+ memset(session, 0, sizeof(struct bgp_session)) ;
+
+ qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ;
+
+
+
+} ;
+
+/* Look up session
+ *
+ */
+extern bgp_session
+bgp_session_lookup(union sockunion* su, int* exists) ;
+
+
+
+
+/* Enable session for given peer -- allocate session if required.
+ *
+ * Sets up the session given the current state of the peer. If the state
+ * changes, then....
+ *
+ *
+ */
+
+extern bgp_session
+bgp_session_enable(bgp_session session, bgp_peer peer)
+{
+ if (session == NULL)
+ session = bgp_session_init_new(session) ;
+
+ /* Tie back to peer and set state of session. */
+
+ assert((session->peer == NULL) || (session->peer == peer)) ;
+ assert((session->state != bgp_session_Enabled) &&
+ (session->state != bgp_session_Established)) ;
+
+ session->peer = peer ;
+ session->state = bgp_session_Enabled ;
+
+ /* Initialise what we need to make and run connections */
+
+ session->connect = (peer->flags & PEER_FLAG_PASSIVE) != 0 ;
+ session->listen = 1 ;
+ session->accept = 0 ;
+
+ session->ttl = peer->ttl ;
+ session->port = peer->port ;
+
+//session->su = peer->su ;
+
+ session->log = peer->log ;
+ session->host = peer->host ;
+
+ session->idle_hold_timer_interval = peer->v_start ;
+ session->connect_retry_timer_interval = peer->v_connect ;
+ /* TODO: proper value for open_hold_timer_interval */
+ session->open_hold_timer_interval = peer->v_connect ;
+ session->hold_timer_interval = peer->v_holdtime ;
+ session->keepalive_timer_interval = peer->v_keepalive ;
+
+ /* Initialise the BGP Open negotiating position */
+
+ session->router_id = peer->local_id ;
+
+ /* Now pass the session to the BGP Engine, which will set about */
+ /* making and running a connection to the peer. */
+
+
+
+} ;
+
+
+/*==============================================================================
+ * Session data access functions.
+ *
+ *
+ */
+
+extern int
+bgp_session_is_active(bgp_session session)
+{
+ int ret ;
+
+ if (session == NULL)
+ return 0 ; /* NULL session is implicitly not active */
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ ret = ( (session->state == bgp_session_Enabled)
+ || (session->state == bgp_session_Established) ) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return ret ;
+} ;
+
+extern int
+bgp_session_is_accepting(bgp_session session)
+{
+ int ret ;
+
+ if (session == NULL)
+ return 0 ; /* NULL session is implicitly not accepting */
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ ret = session->accept ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return ret ;
+} ;
diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h
new file mode 100644
index 00000000..3942ae7d
--- /dev/null
+++ b/bgpd/bgp_session.h
@@ -0,0 +1,163 @@
+/* BGP Session -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_SESSION_H
+#define _QUAGGA_BGP_SESSION_H
+
+#include <zebra.h>
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_connection.h"
+#include "bgpd/bgp_notification.h"
+
+#include "lib/qtimers.h"
+#include "lib/qpthreads.h"
+#include "lib/sockunion.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+
+/*==============================================================================
+ * BGP Session data structure.
+ *
+ * The bgp_session structure encapsulates a BGP session from the perspective
+ * of the Routeing Engine, and that is shared with the BGP Engine.
+ *
+ * The session may have up to two BGP connections associated with it, managed
+ * by the BGP Engine.
+ *
+ * The session includes the "negotiating position" for the BGP Open exchange,
+ * which is managed by the BGP Engine. Changes to that negotiating position
+ * may require any existing session to be terminated.
+ *
+ * NB: the session structure is shared by the Routeing Engine and the BGP
+ * Engine, so there is a mutex to coordinate access.
+ *
+ * The information in this shared structure is only required every now and
+ * then, so the overhead of a mutex operation for every access is not an
+ * issue.
+ *
+ * NB: the connections associated with a BGP session are private to the BGP
+ * Engine when sessions are disabled or have failed, there will be no
+ * connections.
+ *
+ */
+
+typedef enum bgp_session_states bgp_session_state_t ;
+enum bgp_session_states
+{
+ bgp_session_min_state = 0,
+
+ bgp_session_Idle = 0,
+
+ bgp_session_Enabled = 1, /* attempting to connect */
+ bgp_session_Established = 2,
+
+ bgp_session_Stopped = 3, /* for whatever reason */
+
+ bgp_session_max_state = 3
+} ;
+
+struct bgp_session
+{
+ bgp_peer peer ; /* peer whose session this is */
+
+ qpt_mutex_t mutex ; /* for access to the rest */
+
+ bgp_session_state_t state ; /* as above */
+
+ bgp_stopped_cause_t stopped ; /* why stopped */
+ bgp_notify notification ; /* if any sent/received */
+
+ bgp_open_state open_send ; /* how to open the session */
+ bgp_open_state open_recv ; /* how session was opened */
+
+ int connect ; /* initiate connections */
+ int listen ; /* listen for connections */
+
+ int accept ; /* accept connections */
+
+ int ttl ; /* TTL to set, if not zero */
+ unsigned short port ; /* destination port for peer */
+ union sockunion su_peer ; /* Sockunion address of the peer */
+
+ struct in_addr router_id ;
+
+ struct zlog* log ; /* where to log to */
+ char* host ; /* copy of printable peer's addr */
+
+ char* password ; /* copy of MD5 password */
+
+ unsigned idle_hold_timer_interval ; /* in seconds */
+ unsigned connect_retry_timer_interval ;
+ unsigned open_hold_timer_interval ;
+ unsigned hold_timer_interval ; /* subject to negotiation */
+ unsigned keepalive_timer_interval ; /* subject to negotiation */
+
+ /* NB: these values are private to the BGP Engine, which accesses them */
+ /* *without* worrying about the mutex. */
+ /* */
+ /* The BGP Engine uses these while the session is Enabled or */
+ /* Established -- so the session must not be freed in these states. */
+
+ bgp_connection connections[bgp_connection_count] ;
+} ;
+
+/*==============================================================================
+ * Session mutex lock/unlock
+ */
+
+inline static void BGP_SESSION_LOCK(bgp_session session)
+{
+ qpt_mutex_lock(&session->mutex) ;
+} ;
+
+inline static void BGP_SESSION_UNLOCK(bgp_session session)
+{
+ qpt_mutex_unlock(&session->mutex) ;
+} ;
+
+/*==============================================================================
+ *
+ */
+
+extern bgp_session
+bgp_session_init_new(bgp_session session) ;
+
+extern bgp_session
+bgp_session_lookup(union sockunion* su, int* exists) ;
+
+/*==============================================================================
+ * Session data access functions.
+ *
+ *
+ */
+
+extern int
+bgp_session_is_active(bgp_session session) ;
+
+extern int
+bgp_session_is_accepting(bgp_session session) ;
+
+#endif /* QUAGGA_BGP_SESSION_H */
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index bdf3190c..fd7697ca 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -32,6 +32,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "hash.h"
#include "bgpd/bgpd.h"
+#include "bgpd/bgp_peer.h"
#include "bgpd/bgp_advertise.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h"
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index f3baeee0..ed4f9e87 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -31,13 +31,14 @@ Boston, MA 02111-1307, USA. */
#include "thread.h"
#include "bgpd/bgpd.h"
+#include "bgpd/bgp_peer.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_nexthop.h"
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_fsm.h"
#include "bgpd/bgp_debug.h"
-
+
/* All information about zebra. */
struct zclient *zclient = NULL;
struct in_addr router_id_zebra;
@@ -376,11 +377,11 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length)
}
bgp_redistribute_delete ((struct prefix *) &p, api.type);
}
-
+
return 0;
}
#endif /* HAVE_IPV6 */
-
+
struct interface *
if_lookup_by_ipv4 (struct in_addr *addr)
{
@@ -389,8 +390,8 @@ if_lookup_by_ipv4 (struct in_addr *addr)
struct interface *ifp;
struct connected *connected;
struct prefix_ipv4 p;
- struct prefix *cp;
-
+ struct prefix *cp;
+
p.family = AF_INET;
p.prefix = *addr;
p.prefixlen = IPV4_MAX_BITLEN;
@@ -400,7 +401,7 @@ if_lookup_by_ipv4 (struct in_addr *addr)
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
{
cp = connected->address;
-
+
if (cp->family == AF_INET)
if (prefix_match (cp, (struct prefix *)&p))
return ifp;
@@ -416,14 +417,14 @@ if_lookup_by_ipv4_exact (struct in_addr *addr)
struct listnode *cnode;
struct interface *ifp;
struct connected *connected;
- struct prefix *cp;
-
+ struct prefix *cp;
+
for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp))
{
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
{
cp = connected->address;
-
+
if (cp->family == AF_INET)
if (IPV4_ADDR_SAME (&cp->u.prefix4, addr))
return ifp;
@@ -441,8 +442,8 @@ if_lookup_by_ipv6 (struct in6_addr *addr)
struct interface *ifp;
struct connected *connected;
struct prefix_ipv6 p;
- struct prefix *cp;
-
+ struct prefix *cp;
+
p.family = AF_INET6;
p.prefix = *addr;
p.prefixlen = IPV6_MAX_BITLEN;
@@ -452,7 +453,7 @@ if_lookup_by_ipv6 (struct in6_addr *addr)
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
{
cp = connected->address;
-
+
if (cp->family == AF_INET6)
if (prefix_match (cp, (struct prefix *)&p))
return ifp;
@@ -468,14 +469,14 @@ if_lookup_by_ipv6_exact (struct in6_addr *addr)
struct listnode *cnode;
struct interface *ifp;
struct connected *connected;
- struct prefix *cp;
+ struct prefix *cp;
for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp))
{
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
{
cp = connected->address;
-
+
if (cp->family == AF_INET6)
if (IPV6_ADDR_SAME (&cp->u.prefix6, addr))
return ifp;
@@ -489,12 +490,12 @@ if_get_ipv6_global (struct interface *ifp, struct in6_addr *addr)
{
struct listnode *cnode;
struct connected *connected;
- struct prefix *cp;
-
+ struct prefix *cp;
+
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
{
cp = connected->address;
-
+
if (cp->family == AF_INET6)
if (! IN6_IS_ADDR_LINKLOCAL (&cp->u.prefix6))
{
@@ -510,12 +511,12 @@ if_get_ipv6_local (struct interface *ifp, struct in6_addr *addr)
{
struct listnode *cnode;
struct connected *connected;
- struct prefix *cp;
-
+ struct prefix *cp;
+
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
{
cp = connected->address;
-
+
if (cp->family == AF_INET6)
if (IN6_IS_ADDR_LINKLOCAL (&cp->u.prefix6))
{
@@ -528,7 +529,7 @@ if_get_ipv6_local (struct interface *ifp, struct in6_addr *addr)
#endif /* HAVE_IPV6 */
int
-bgp_nexthop_set (union sockunion *local, union sockunion *remote,
+bgp_nexthop_set (union sockunion *local, union sockunion *remote,
struct bgp_nexthop *nexthop, struct peer *peer)
{
int ret = 0;
@@ -592,7 +593,7 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote,
/* Global address*/
if (! IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr))
{
- memcpy (&nexthop->v6_global, &local->sin6.sin6_addr,
+ memcpy (&nexthop->v6_global, &local->sin6.sin6_addr,
IPV6_MAX_BYTELEN);
/* If directory connected set link-local address. */
@@ -608,10 +609,10 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote,
/* If there is no global address. Set link-local address as
global. I know this break RFC specification... */
if (!ret)
- memcpy (&nexthop->v6_global, &local->sin6.sin6_addr,
+ memcpy (&nexthop->v6_global, &local->sin6.sin6_addr,
IPV6_MAX_BYTELEN);
else
- memcpy (&nexthop->v6_local, &local->sin6.sin6_addr,
+ memcpy (&nexthop->v6_local, &local->sin6.sin6_addr,
IPV6_MAX_BYTELEN);
}
}
@@ -700,7 +701,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp)
api.metric);
}
- zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient,
+ zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient,
(struct prefix_ipv4 *) p, &api);
}
#ifdef HAVE_IPV6
@@ -713,13 +714,13 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp)
ifindex = 0;
nexthop = NULL;
-
+
assert (info->attr->extra);
-
+
/* Only global address nexthop exists. */
if (info->attr->extra->mp_nexthop_len == 16)
nexthop = &info->attr->extra->mp_nexthop_global;
-
+
/* If both global and link-local address present. */
if (info->attr->extra->mp_nexthop_len == 32)
{
@@ -768,7 +769,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp)
api.metric);
}
- zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient,
+ zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient,
(struct prefix_ipv6 *) p, &api);
}
#endif /* HAVE_IPV6 */
@@ -826,7 +827,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info)
api.metric);
}
- zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient,
+ zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient,
(struct prefix_ipv4 *) p, &api);
}
#ifdef HAVE_IPV6
@@ -836,9 +837,9 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info)
struct zapi_ipv6 api;
unsigned int ifindex;
struct in6_addr *nexthop;
-
+
assert (info->attr->extra);
-
+
ifindex = 0;
nexthop = NULL;
@@ -883,12 +884,12 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info)
api.metric);
}
- zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient,
+ zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient,
(struct prefix_ipv6 *) p, &api);
}
#endif /* HAVE_IPV6 */
}
-
+
/* Other routes redistribution into BGP. */
int
bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type)
@@ -908,7 +909,7 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type)
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Zebra send: redistribute add %s", zebra_route_string(type));
-
+
/* Send distribute add message to zebra. */
zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type);
@@ -917,7 +918,7 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type)
/* Redistribute with route-map specification. */
int
-bgp_redistribute_rmap_set (struct bgp *bgp, afi_t afi, int type,
+bgp_redistribute_rmap_set (struct bgp *bgp, afi_t afi, int type,
const char *name)
{
if (bgp->rmap[afi][type].name
@@ -969,8 +970,8 @@ bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type)
return CMD_WARNING;
zclient->redist[type] = 0;
- if (bgp->redist[AFI_IP][type] == 0
- && bgp->redist[AFI_IP6][type] == 0
+ if (bgp->redist[AFI_IP][type] == 0
+ && bgp->redist[AFI_IP6][type] == 0
&& zclient->sock >= 0)
{
/* Send distribute delete message to zebra. */
@@ -979,7 +980,7 @@ bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type)
zebra_route_string(type));
zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type);
}
-
+
/* Withdraw redistributed routes from current BGP's routing table. */
bgp_redistribute_withdraw (bgp, afi, type);
@@ -1014,7 +1015,7 @@ bgp_redistribute_metric_unset (struct bgp *bgp, afi_t afi, int type)
return 1;
}
-
+
void
bgp_zclient_reset (void)
{
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index f811eae0..adb8264e 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -37,6 +37,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "workqueue.h"
#include "bgpd/bgpd.h"
+
+#include "bgpd/bgp_peer.h"
+
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 7fb29c55..92fd3883 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -21,16 +21,14 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGPD_H
#define _QUAGGA_BGPD_H
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_notification.h"
+
#include "plist.h"
/* For union sockunion. */
#include "sockunion.h"
-/* Typedef BGP specific types. */
-typedef u_int32_t as_t;
-typedef u_int16_t as16_t; /* we may still encounter 16 Bit asnums */
-typedef u_int16_t bgp_size_t;
-
/* BGP master for system wide configurations and variables. */
struct bgp_master
{
@@ -182,26 +180,6 @@ struct peer_group
struct peer *conf;
};
-/* BGP Notify message format. */
-struct bgp_notify
-{
- u_char code;
- u_char subcode;
- char *data;
- bgp_size_t length;
-};
-
-/* Next hop self address. */
-struct bgp_nexthop
-{
- struct interface *ifp;
- struct in_addr v4;
-#ifdef HAVE_IPV6
- struct in6_addr v6_global;
- struct in6_addr v6_local;
-#endif /* HAVE_IPV6 */
-};
-
/* BGP router distinguisher value. */
#define BGP_RD_SIZE 8
@@ -210,344 +188,9 @@ struct bgp_rd
u_char val[BGP_RD_SIZE];
};
-#define RMAP_IN 0
-#define RMAP_OUT 1
-#define RMAP_IMPORT 2
-#define RMAP_EXPORT 3
-#define RMAP_MAX 4
-
-/* BGP filter structure. */
-struct bgp_filter
-{
- /* Distribute-list. */
- struct
- {
- char *name;
- struct access_list *alist;
- } dlist[FILTER_MAX];
-
- /* Prefix-list. */
- struct
- {
-#if 1
- prefix_list_ref ref ;
-#else
- char *name;
- struct prefix_list *plist;
-#endif
- } plist[FILTER_MAX];
-
- /* Filter-list. */
- struct
- {
- char *name;
- struct as_list *aslist;
- } aslist[FILTER_MAX];
-
- /* Route-map. */
- struct
- {
- char *name;
- struct route_map *map;
- } map[RMAP_MAX];
-
- /* Unsuppress-map. */
- struct
- {
- char *name;
- struct route_map *map;
- } usmap;
-};
-
-/* BGP neighbor structure. */
-struct peer
-{
- /* BGP structure. */
- struct bgp *bgp;
-
- /* reference count, primarily to allow bgp_process'ing of route_node's
- * to be done after a struct peer is deleted.
- *
- * named 'lock' for hysterical reasons within Quagga.
- */
- int lock;
-
- /* BGP peer group. */
- struct peer_group *group;
- u_char af_group[AFI_MAX][SAFI_MAX];
-
- /* Peer's remote AS number. */
- as_t as;
-
- /* Peer's local AS number. */
- as_t local_as;
- /* Peer's Change local AS number. */
- as_t change_local_as;
- /* Remote router ID. */
- struct in_addr remote_id;
- /* Local router ID. */
- struct in_addr local_id;
-
- /* Peer specific RIB when configured as route-server-client. */
- struct bgp_table *rib[AFI_MAX][SAFI_MAX];
-
- /* Packet receive and send buffer. */
- struct stream *ibuf;
- struct stream_fifo *obuf;
- struct stream *work;
-
- /* Status of the peer. */
- int status;
- int ostatus;
-
- /* Peer index, used for dumping TABLE_DUMP_V2 format */
- uint16_t table_dump_index;
-
- /* Peer information */
- int fd; /* File descriptor */
- int ttl; /* TTL of TCP connection to the peer. */
- char *desc; /* Description of the peer. */
- unsigned short port; /* Destination port for peer */
- char *host; /* Printable address of the peer. */
- union sockunion su; /* Sockunion address of the peer. */
- time_t uptime; /* Last Up/Down time */
- time_t readtime; /* Last read time */
- time_t resettime; /* Last reset time */
-
- unsigned int ifindex; /* ifindex of the BGP connection. */
- char *ifname; /* bind interface name. */
- char *update_if;
- union sockunion *update_source;
- struct zlog *log;
-
- union sockunion *su_local; /* Sockunion of local address. */
- union sockunion *su_remote; /* Sockunion of remote address. */
- int shared_network; /* Is this peer shared same network. */
- struct bgp_nexthop nexthop; /* Nexthop */
-
- /* Peer address family configuration. */
- u_char afc[AFI_MAX][SAFI_MAX];
- u_char afc_nego[AFI_MAX][SAFI_MAX];
- u_char afc_adv[AFI_MAX][SAFI_MAX];
- u_char afc_recv[AFI_MAX][SAFI_MAX];
-
- /* Capability flags (reset in bgp_stop) */
- u_int16_t cap;
-#define PEER_CAP_REFRESH_ADV (1 << 0) /* refresh advertised */
-#define PEER_CAP_REFRESH_OLD_RCV (1 << 1) /* refresh old received */
-#define PEER_CAP_REFRESH_NEW_RCV (1 << 2) /* refresh rfc received */
-#define PEER_CAP_DYNAMIC_ADV (1 << 3) /* dynamic advertised */
-#define PEER_CAP_DYNAMIC_RCV (1 << 4) /* dynamic received */
-#define PEER_CAP_RESTART_ADV (1 << 5) /* restart advertised */
-#define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */
-#define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */
-#define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */
-
- /* Capability flags (reset in bgp_stop) */
- u_int16_t af_cap[AFI_MAX][SAFI_MAX];
-#define PEER_CAP_ORF_PREFIX_SM_ADV (1 << 0) /* send-mode advertised */
-#define PEER_CAP_ORF_PREFIX_RM_ADV (1 << 1) /* receive-mode advertised */
-#define PEER_CAP_ORF_PREFIX_SM_RCV (1 << 2) /* send-mode received */
-#define PEER_CAP_ORF_PREFIX_RM_RCV (1 << 3) /* receive-mode received */
-#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1 << 4) /* send-mode received */
-#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1 << 5) /* receive-mode received */
-#define PEER_CAP_RESTART_AF_RCV (1 << 6) /* graceful restart afi/safi received */
-#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1 << 7) /* graceful restart afi/safi F-bit received */
-
- /* Global configuration flags. */
- u_int32_t flags;
-#define PEER_FLAG_PASSIVE (1 << 0) /* passive mode */
-#define PEER_FLAG_SHUTDOWN (1 << 1) /* shutdown */
-#define PEER_FLAG_DONT_CAPABILITY (1 << 2) /* dont-capability */
-#define PEER_FLAG_OVERRIDE_CAPABILITY (1 << 3) /* override-capability */
-#define PEER_FLAG_STRICT_CAP_MATCH (1 << 4) /* strict-match */
-#define PEER_FLAG_DYNAMIC_CAPABILITY (1 << 5) /* dynamic capability */
-#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */
-#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */
-
- /* NSF mode (graceful restart) */
- u_char nsf[AFI_MAX][SAFI_MAX];
-
- /* Per AF configuration flags. */
- u_int32_t af_flags[AFI_MAX][SAFI_MAX];
-#define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */
-#define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */
-#define PEER_FLAG_NEXTHOP_SELF (1 << 2) /* next-hop-self */
-#define PEER_FLAG_REFLECTOR_CLIENT (1 << 3) /* reflector-client */
-#define PEER_FLAG_RSERVER_CLIENT (1 << 4) /* route-server-client */
-#define PEER_FLAG_SOFT_RECONFIG (1 << 5) /* soft-reconfiguration */
-#define PEER_FLAG_AS_PATH_UNCHANGED (1 << 6) /* transparent-as */
-#define PEER_FLAG_NEXTHOP_UNCHANGED (1 << 7) /* transparent-next-hop */
-#define PEER_FLAG_MED_UNCHANGED (1 << 8) /* transparent-next-hop */
-#define PEER_FLAG_DEFAULT_ORIGINATE (1 << 9) /* default-originate */
-#define PEER_FLAG_REMOVE_PRIVATE_AS (1 << 10) /* remove-private-as */
-#define PEER_FLAG_ALLOWAS_IN (1 << 11) /* set allowas-in */
-#define PEER_FLAG_ORF_PREFIX_SM (1 << 12) /* orf capability send-mode */
-#define PEER_FLAG_ORF_PREFIX_RM (1 << 13) /* orf capability receive-mode */
-#define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */
-#define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */
-#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */
-
- /* MD5 password */
- char *password;
-
- /* default-originate route-map. */
- struct
- {
- char *name;
- struct route_map *map;
- } default_rmap[AFI_MAX][SAFI_MAX];
-
- /* Peer status flags. */
- u_int16_t sflags;
-#define PEER_STATUS_ACCEPT_PEER (1 << 0) /* accept peer */
-#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */
-#define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */
-#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */
-#define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */
-#define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */
-#define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */
-
- /* Peer status af flags (reset in bgp_stop) */
- u_int16_t af_sflags[AFI_MAX][SAFI_MAX];
-#define PEER_STATUS_ORF_PREFIX_SEND (1 << 0) /* prefix-list send peer */
-#define PEER_STATUS_ORF_WAIT_REFRESH (1 << 1) /* wait refresh received peer */
-#define PEER_STATUS_DEFAULT_ORIGINATE (1 << 2) /* default-originate peer */
-#define PEER_STATUS_PREFIX_THRESHOLD (1 << 3) /* exceed prefix-threshold */
-#define PEER_STATUS_PREFIX_LIMIT (1 << 4) /* exceed prefix-limit */
-#define PEER_STATUS_EOR_SEND (1 << 5) /* end-of-rib send to peer */
-#define PEER_STATUS_EOR_RECEIVED (1 << 6) /* end-of-rib received from peer */
-
- /* Default attribute value for the peer. */
- u_int32_t config;
-#define PEER_CONFIG_WEIGHT (1 << 0) /* Default weight. */
-#define PEER_CONFIG_TIMER (1 << 1) /* keepalive & holdtime */
-#define PEER_CONFIG_CONNECT (1 << 2) /* connect */
-#define PEER_CONFIG_ROUTEADV (1 << 3) /* route advertise */
- u_int32_t weight;
- u_int32_t holdtime;
- u_int32_t keepalive;
- u_int32_t connect;
- u_int32_t routeadv;
-
- /* Timer values. */
- u_int32_t v_start;
- u_int32_t v_connect;
- u_int32_t v_holdtime;
- u_int32_t v_keepalive;
- u_int32_t v_asorig;
- u_int32_t v_routeadv;
- u_int32_t v_pmax_restart;
- u_int32_t v_gr_restart;
-
- /* Threads. */
- struct thread *t_read;
- struct thread *t_write;
- struct thread *t_start;
- struct thread *t_connect;
- struct thread *t_holdtime;
- struct thread *t_keepalive;
- struct thread *t_asorig;
- struct thread *t_routeadv;
- struct thread *t_pmax_restart;
- struct thread *t_gr_restart;
- struct thread *t_gr_stale;
-
- /* workqueues */
- struct work_queue *clear_node_queue;
-
- /* Statistics field */
- u_int32_t open_in; /* Open message input count */
- u_int32_t open_out; /* Open message output count */
- u_int32_t update_in; /* Update message input count */
- u_int32_t update_out; /* Update message ouput count */
- time_t update_time; /* Update message received time. */
- u_int32_t keepalive_in; /* Keepalive input count */
- u_int32_t keepalive_out; /* Keepalive output count */
- u_int32_t notify_in; /* Notify input count */
- u_int32_t notify_out; /* Notify output count */
- u_int32_t refresh_in; /* Route Refresh input count */
- u_int32_t refresh_out; /* Route Refresh output count */
- u_int32_t dynamic_cap_in; /* Dynamic Capability input count. */
- u_int32_t dynamic_cap_out; /* Dynamic Capability output count. */
-
- /* BGP state count */
- u_int32_t established; /* Established */
- u_int32_t dropped; /* Dropped */
-
- /* Syncronization list and time. */
- struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX];
- time_t synctime;
-
- /* Send prefix count. */
- unsigned long scount[AFI_MAX][SAFI_MAX];
-
- /* Announcement attribute hash. */
- struct hash *hash[AFI_MAX][SAFI_MAX];
-
- /* Notify data. */
- struct bgp_notify notify;
-
- /* Whole packet size to be read. */
- unsigned long packet_size;
-
- /* Filter structure. */
- struct bgp_filter filter[AFI_MAX][SAFI_MAX];
-
- /* ORF Prefix-list */
- struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX];
-
- /* Prefix count. */
- unsigned long pcount[AFI_MAX][SAFI_MAX];
-
- /* Max prefix count. */
- unsigned long pmax[AFI_MAX][SAFI_MAX];
- u_char pmax_threshold[AFI_MAX][SAFI_MAX];
- u_int16_t pmax_restart[AFI_MAX][SAFI_MAX];
-#define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75
-
- /* allowas-in. */
- char allowas_in[AFI_MAX][SAFI_MAX];
-
- /* peer reset cause */
- char last_reset;
-#define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */
-#define PEER_DOWN_REMOTE_AS_CHANGE 2 /* neighbor remote-as command */
-#define PEER_DOWN_LOCAL_AS_CHANGE 3 /* neighbor local-as command */
-#define PEER_DOWN_CLID_CHANGE 4 /* bgp cluster-id command */
-#define PEER_DOWN_CONFED_ID_CHANGE 5 /* bgp confederation identifier command */
-#define PEER_DOWN_CONFED_PEER_CHANGE 6 /* bgp confederation peer command */
-#define PEER_DOWN_RR_CLIENT_CHANGE 7 /* neighbor route-reflector-client command */
-#define PEER_DOWN_RS_CLIENT_CHANGE 8 /* neighbor route-server-client command */
-#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9 /* neighbor update-source command */
-#define PEER_DOWN_AF_ACTIVATE 10 /* neighbor activate command */
-#define PEER_DOWN_USER_SHUTDOWN 11 /* neighbor shutdown command */
-#define PEER_DOWN_USER_RESET 12 /* clear ip bgp command */
-#define PEER_DOWN_NOTIFY_RECEIVED 13 /* notification received */
-#define PEER_DOWN_NOTIFY_SEND 14 /* notification send */
-#define PEER_DOWN_CLOSE_SESSION 15 /* tcp session close */
-#define PEER_DOWN_NEIGHBOR_DELETE 16 /* neghbor delete */
-#define PEER_DOWN_RMAP_BIND 17 /* neghbor peer-group command */
-#define PEER_DOWN_RMAP_UNBIND 18 /* no neighbor peer-group command */
-#define PEER_DOWN_CAPABILITY_CHANGE 19 /* neighbor capability command */
-#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */
-#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */
-#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */
-
- /* The kind of route-map Flags.*/
- u_char rmap_type;
-#define PEER_RMAP_TYPE_IN (1 << 0) /* neighbor route-map in */
-#define PEER_RMAP_TYPE_OUT (1 << 1) /* neighbor route-map out */
-#define PEER_RMAP_TYPE_NETWORK (1 << 2) /* network route-map */
-#define PEER_RMAP_TYPE_REDISTRIBUTE (1 << 3) /* redistribute route-map */
-#define PEER_RMAP_TYPE_DEFAULT (1 << 4) /* default-originate route-map */
-#define PEER_RMAP_TYPE_NOSET (1 << 5) /* not allow to set commands */
-#define PEER_RMAP_TYPE_IMPORT (1 << 6) /* neighbor route-map import */
-#define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */
-};
#define PEER_PASSWORD_MINLEN (1)
#define PEER_PASSWORD_MAXLEN (80)
@@ -627,63 +270,6 @@ struct bgp_nlri
#define BGP_ORIGIN_EGP 1
#define BGP_ORIGIN_INCOMPLETE 2
-/* BGP notify message codes. */
-#define BGP_NOTIFY_HEADER_ERR 1
-#define BGP_NOTIFY_OPEN_ERR 2
-#define BGP_NOTIFY_UPDATE_ERR 3
-#define BGP_NOTIFY_HOLD_ERR 4
-#define BGP_NOTIFY_FSM_ERR 5
-#define BGP_NOTIFY_CEASE 6
-#define BGP_NOTIFY_CAPABILITY_ERR 7
-#define BGP_NOTIFY_MAX 8
-
-/* BGP_NOTIFY_HEADER_ERR sub codes. */
-#define BGP_NOTIFY_HEADER_NOT_SYNC 1
-#define BGP_NOTIFY_HEADER_BAD_MESLEN 2
-#define BGP_NOTIFY_HEADER_BAD_MESTYPE 3
-#define BGP_NOTIFY_HEADER_MAX 4
-
-/* BGP_NOTIFY_OPEN_ERR sub codes. */
-#define BGP_NOTIFY_OPEN_UNSUP_VERSION 1
-#define BGP_NOTIFY_OPEN_BAD_PEER_AS 2
-#define BGP_NOTIFY_OPEN_BAD_BGP_IDENT 3
-#define BGP_NOTIFY_OPEN_UNSUP_PARAM 4
-#define BGP_NOTIFY_OPEN_AUTH_FAILURE 5
-#define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME 6
-#define BGP_NOTIFY_OPEN_UNSUP_CAPBL 7
-#define BGP_NOTIFY_OPEN_MAX 8
-
-/* BGP_NOTIFY_UPDATE_ERR sub codes. */
-#define BGP_NOTIFY_UPDATE_MAL_ATTR 1
-#define BGP_NOTIFY_UPDATE_UNREC_ATTR 2
-#define BGP_NOTIFY_UPDATE_MISS_ATTR 3
-#define BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR 4
-#define BGP_NOTIFY_UPDATE_ATTR_LENG_ERR 5
-#define BGP_NOTIFY_UPDATE_INVAL_ORIGIN 6
-#define BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP 7
-#define BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP 8
-#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
-#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
-#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11
-#define BGP_NOTIFY_UPDATE_MAX 12
-
-/* BGP_NOTIFY_CEASE sub codes (draft-ietf-idr-cease-subcode-05). */
-#define BGP_NOTIFY_CEASE_MAX_PREFIX 1
-#define BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN 2
-#define BGP_NOTIFY_CEASE_PEER_UNCONFIG 3
-#define BGP_NOTIFY_CEASE_ADMIN_RESET 4
-#define BGP_NOTIFY_CEASE_CONNECT_REJECT 5
-#define BGP_NOTIFY_CEASE_CONFIG_CHANGE 6
-#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION 7
-#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE 8
-#define BGP_NOTIFY_CEASE_MAX 9
-
-/* BGP_NOTIFY_CAPABILITY_ERR sub codes (draft-ietf-idr-dynamic-cap-02). */
-#define BGP_NOTIFY_CAPABILITY_INVALID_ACTION 1
-#define BGP_NOTIFY_CAPABILITY_INVALID_LENGTH 2
-#define BGP_NOTIFY_CAPABILITY_MALFORMED_CODE 3
-#define BGP_NOTIFY_CAPABILITY_MAX 4
-
/* BGP finite state machine status. */
#define Idle 1
#define Connect 2
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 8f518f05..d6d3022a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -31,7 +31,7 @@ pkginclude_HEADERS = \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
workqueue.h route_types.h symtab.h heap.h \
qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h qpnexus.h \
- command_queue.h qlib_init.h
+ command_queue.h qlib_init.h qafi_safi.h
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 81acb205..c55a2cb7 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -114,6 +114,9 @@ struct memory_list memory_list_bgp[] =
{ MTYPE_PEER_GROUP, "Peer group" },
{ MTYPE_PEER_DESC, "Peer description" },
{ MTYPE_PEER_PASSWORD, "Peer password string" },
+ { MTYPE_BGP_SESSION, "BGP session" },
+ { MTYPE_BGP_CONNECTION, "BGP connection" },
+ { MTYPE_BGP_NOTIFY, "BGP notification" },
{ MTYPE_ATTR, "BGP attribute" },
{ MTYPE_ATTR_EXTRA, "BGP extra attributes" },
{ MTYPE_AS_PATH, "BGP aspath" },
diff --git a/lib/mqueue.c b/lib/mqueue.c
index 5afe902a..1e5e8be5 100644
--- a/lib/mqueue.c
+++ b/lib/mqueue.c
@@ -85,6 +85,13 @@
* For specific revoke, arg0 is assumed to identify the messages to be
* revoked.
*
+ *==============================================================================
+ * Local Queues
+ *
+ * A local queue may be used within a thread to requeue messages for later
+ * processing.
+ *
+ * Local queues are very simple FIFO queues.
*/
/*==============================================================================
@@ -93,7 +100,8 @@
* TODO: how to shut down a message queue... for reset/exit ?
*/
-/* Initialise new Message Queue, if required (mq == NULL) allocating it.
+/*------------------------------------------------------------------------------
+ * Initialise new Message Queue, if required (mq == NULL) allocating it.
*
* For mqt_cond_xxx type queues, sets the default timeout interval and the
* initial timeout time to now + that interval.
@@ -143,7 +151,59 @@ mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
return mq ;
} ;
-/* Set new timeout interval (or unset by setting <= 0)
+/*------------------------------------------------------------------------------
+ * Initialise new Local Message Queue, if required (lmq == NULL) allocating it.
+ *
+ * Returns address of Local Message Queue
+ */
+extern mqueue_local_queue
+mqueue_local_init_new(mqueue_local_queue lmq)
+{
+ if (lmq == NULL)
+ lmq = XCALLOC(MTYPE_MQUEUE_QUEUE, sizeof(struct mqueue_local_queue)) ;
+ else
+ memset(lmq, 0, sizeof(struct mqueue_local_queue)) ;
+
+ /* Zeroising the structure is enough to initialise:
+ *
+ * * head -- NULL
+ * * tail -- NULL
+ */
+
+ return lmq ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset Local Message Queue, and if required free it.
+ *
+ * Dequeues entries and dispatches them "mqb_revoke", to empty the queue.
+ *
+ * See: mqueue_local_reset_keep(lmq)
+ * mqueue_local_reset_free(lmq)
+ *
+ * Returns address of Local Message Queue
+ */
+extern mqueue_local_queue
+mqueue_local_reset(mqueue_local_queue lmq, int free_structure)
+{
+ mqueue_block mqb ;
+
+ while ((mqb = lmq->head) != NULL)
+ {
+ lmq->head = mqb->next ;
+ mqueue_dispatch_destroy(mqb) ;
+ } ;
+
+ if (free_structure)
+ XFREE(MTYPE_MQUEUE_QUEUE, lmq) ; /* sets lmq = NULL */
+ else
+ memset(lmq, 0, sizeof(struct mqueue_local_queue)) ;
+
+ return lmq ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set new timeout interval (or unset by setting <= 0)
*
* Sets the next timeout to be the time now + new interval (or never).
*
@@ -260,7 +320,8 @@ mqueue_block_new_lot(void)
static void mqueue_kick_signal(mqueue_queue mq, unsigned n) ;
static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
-/* Enqueue message.
+/*------------------------------------------------------------------------------
+ * Enqueue message.
*
* If priority != 0, will enqueue after any previously enqueued priority
* messages.
@@ -283,7 +344,6 @@ static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
* NB: this works perfectly well if !qpthreads enabled. Of course, there can
* never be any waiters... so no kicking is ever done.
*/
-
void
mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
{
@@ -353,7 +413,8 @@ mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
qpt_mutex_unlock(&mq->mutex) ;
} ;
-/* Dequeue message.
+/*------------------------------------------------------------------------------
+ * Dequeue message.
*
* If the queue is empty and wait != 0 (and qpthreads_enabled), will wait for a
* message. In which case for:
@@ -378,7 +439,6 @@ mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
*
* Returns a message block if one is available. (And not otherwise.)
*/
-
mqueue_block
mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
{
@@ -471,7 +531,8 @@ done:
return mqb ;
} ;
-/* No longer waiting for a signal -- does nothing if !qpthreads_enabled.
+/*------------------------------------------------------------------------------
+ * No longer waiting for a signal -- does nothing if !qpthreads_enabled.
*
* Returns true <=> signal has been kicked
*
@@ -507,6 +568,34 @@ mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig)
return kicked ;
} ;
+/*------------------------------------------------------------------------------
+ * Enqueue message on local queue
+ */
+extern void
+mqueue_local_enqueue(mqueue_local_queue lmq, mqueue_block mqb)
+{
+ if (lmq->head == NULL)
+ lmq->head = mqb ;
+ else
+ lmq->tail->next = mqb ;
+ lmq->tail = mqb ;
+ mqb->next = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dequeue message from local queue -- returns NULL if empty
+ */
+extern mqueue_block
+mqueue_local_dequeue(mqueue_local_queue lmq)
+{
+ mqueue_block mqb = lmq->head ;
+
+ if (mqb != NULL)
+ lmq->head = mqb->next ;
+
+ return mqb ;
+} ;
+
/*==============================================================================
* Message queue signal handling
*/
diff --git a/lib/mqueue.h b/lib/mqueue.h
index 7d649efe..fb28eea1 100644
--- a/lib/mqueue.h
+++ b/lib/mqueue.h
@@ -50,7 +50,7 @@ typedef union
enum mqb_flag
{
- mqb_revoke = 0,
+ mqb_destroy = 0,
mqb_action = 1
} ;
@@ -122,37 +122,60 @@ struct mqueue_queue
} kick ;
} ;
+typedef struct mqueue_local_queue* mqueue_local_queue ;
+
+struct mqueue_local_queue
+{
+ mqueue_block head ; /* NULL => list is empty */
+ mqueue_block tail ; /* last message (if not empty) */
+} ;
+
/*==============================================================================
* Functions
*/
-void
+extern void
mqueue_initialise(void) ;
-mqueue_queue
+extern mqueue_queue
mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type) ;
-void
+extern mqueue_local_queue
+mqueue_local_init_new(mqueue_local_queue lmq) ;
+
+extern mqueue_local_queue
+mqueue_local_reset(mqueue_local_queue lmq, int free_structure) ;
+
+#define mqueue_local_reset_keep(lmq) mqueue_local_reset(lmq, 0)
+#define mqueue_local_reset_free(lmq) mqueue_local_reset(lmq, 1)
+
+extern void
mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
-mqueue_thread_signal
+extern mqueue_thread_signal
mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
int signum) ;
-mqueue_block
+extern mqueue_block
mqb_init_new(mqueue_block mqb, mqueue_action action, void* arg0) ;
-void
+extern void
mqb_free(mqueue_block mqb) ;
-void
+extern void
mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority) ;
-mqueue_block
+extern mqueue_block
mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
-int
+extern int
mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig) ;
+extern void
+mqueue_local_enqueue(mqueue_local_queue lmq, mqueue_block mqb) ;
+
+extern mqueue_block
+mqueue_local_dequeue(mqueue_local_queue lmq) ;
+
/*==============================================================================
* Access functions for mqueue_block fields -- mqb_set_xxx/mqb_get_xxx
*
@@ -217,6 +240,18 @@ mqb_dispatch(mqueue_block mqb, mqb_flag_t flag)
mqb->action(mqb, flag) ;
} ;
+Inline void
+mqb_dispatch_action(mqueue_block mqb)
+{
+ mqb->action(mqb, mqb_action) ;
+} ;
+
+Inline void
+mqb_dispatch_destroy(mqueue_block mqb)
+{
+ mqb->action(mqb, mqb_destroy) ;
+} ;
+
Inline void*
mqb_get_arg0(mqueue_block mqb)
{
diff --git a/lib/qafi_safi.h b/lib/qafi_safi.h
new file mode 100644
index 00000000..bf0f0360
--- /dev/null
+++ b/lib/qafi_safi.h
@@ -0,0 +1,151 @@
+/* Quagga AFI/SAFI
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Kunihiro Ishiguro
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_AFI_SAFI_H
+#define _QUAGGA_AFI_SAFI_H
+
+#include <stdint.h>
+#include "lib/zassert.h"
+
+/*==============================================================================
+ * Generic AFI and SAFI types.
+ */
+typedef uint16_t afi_t;
+typedef uint8_t safi_t;
+
+/*==============================================================================
+ * iAFI and iSAFI
+ *
+ * These are the standard IANA registered AFI and SAFI values that Quagga is
+ * at all interested in.
+ */
+
+typedef enum iAFI iAFI_t ;
+
+enum iAFI
+{
+ iAFI_Reserved = 0, /* No meaning defined by IANA */
+
+ iAFI_IP = 1, /* IP (IP version 4) */
+ iAFI_IP6 = 2, /* IP6 (IP version 6) */
+
+ iAFI_IPV4 = iAFI_IP, /* locally AKA */
+ iAFI_IPV6 = iAFI_IP6 /* locally AKA */
+} ;
+
+
+typedef enum iSAFI iSAFI_t ;
+
+enum iSAFI
+{
+ iSAFI_Reserved = 0, /* No meaning defined by IANA */
+
+ iSAFI_Unicast = 1, /* unicast forwarding */
+ iSAFI_Multicast = 2, /* multicast forwarding */
+
+ iSAFI_Unused = 3, /* also Reserved by IANA */
+
+ iSAFI_MPLS_VPN = 128 /* MPLS-labeled VPN address */
+} ;
+
+/*==============================================================================
+ * qAFI and qSAFI
+ *
+ * These are the AFI and SAFI values that Quagga uses internally.
+ *
+ * They are almost the same as the IANA numbers, but different where that
+ * is required to produce a dense set.
+ */
+
+typedef enum qAFI qAFI_t ;
+
+enum qAFI
+{
+ qAFI_min = 0, /* minimum valid qAFI */
+ qAFI_undef = 0, /* undefined AFI */
+
+ qAFI_first = 1, /* first real qAFI */
+
+ qAFI_IP = 1,
+ qAFI_IP6 = 2,
+
+ qAFI_last = 2, /* last real qAFI */
+
+ qAFI_max = 2, /* maximum valid qAFI */
+ qAFI_count, /* number of distinct qAFI */
+
+ qAFI_IPV4 = qAFI_IP,
+ qAFI_IPV6 = qAFI_IP6
+} ;
+
+typedef enum qSAFI qSAFI_t ;
+
+enum qSAFI
+{
+ qSAFI_min = 1, /* minimum valid qSAFI */
+ qSAFI_undef = 0, /* undefined SAFI */
+
+ qSAFI_first = 1, /* first real qSAFI */
+
+ qSAFI_Unicast = 1,
+ qSAFI_Multicast = 2,
+ qSAFI_Unused = 3,
+ qSAFI_MPLS_VPN = 4,
+
+ qSAFI_last = 4, /* last real qSAFI */
+
+ qSAFI_max = 4, /* maximum valid qSAFI */
+ qSAFI_count /* number of distinct qSAFI */
+} ;
+
+/*==============================================================================
+ * Quagga AFI/SAFI values -- original macro definitions
+ */
+
+/* Address family numbers from RFC1700. */
+#define AFI_IP 1
+#define AFI_IP6 2
+#define AFI_MAX 3
+
+CONFIRM( (AFI_IP == qAFI_IP)
+ && (AFI_IP == iAFI_IP) ) ;
+CONFIRM( (AFI_IP6 == qAFI_IP6)
+ && (AFI_IP6 == iAFI_IP6) ) ;
+CONFIRM(AFI_MAX == qAFI_count) ;
+
+/* Subsequent Address Family Identifier. */
+#define SAFI_UNICAST 1
+#define SAFI_MULTICAST 2
+#define SAFI_UNICAST_MULTICAST 3
+#define SAFI_MPLS_VPN 4
+#define SAFI_MAX 5
+
+CONFIRM( (SAFI_UNICAST == qSAFI_Unicast)
+ && (SAFI_UNICAST == iSAFI_Unicast) ) ;
+CONFIRM( (SAFI_MULTICAST == qSAFI_Multicast)
+ && (SAFI_MULTICAST == iSAFI_Multicast) ) ;
+CONFIRM( (SAFI_UNICAST_MULTICAST == qSAFI_Unused)
+ && (SAFI_UNICAST_MULTICAST == iSAFI_Unused) ) ;
+CONFIRM(SAFI_MPLS_VPN == qSAFI_MPLS_VPN) ;
+CONFIRM(SAFI_MAX == qSAFI_count) ;
+
+#endif /* _QUAGGA_AFI_SAFI_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
index fb02f0fa..a281a61d 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -457,6 +457,8 @@ qps_file_init_new(qps_file qf, qps_file template)
* actions[] -- all set to NULL
*/
+ qf->fd = fd_undef ; /* no fd set yet */
+
if (template != NULL)
memcpy(qf->actions, template->actions, sizeof(qf->actions)) ;
diff --git a/lib/qpselect.h b/lib/qpselect.h
index 1e67d174..aec56c38 100644
--- a/lib/qpselect.h
+++ b/lib/qpselect.h
@@ -67,6 +67,9 @@ enum qps_mbits /* "mode" bits: error/read/write */
typedef enum qps_mbits qps_mbit_t ;
+/* "fd_undef" -- used when fd is undefined */
+enum { fd_undef = -1 } ;
+
/* Forward references */
typedef struct qps_selection* qps_selection ;
typedef struct qps_file* qps_file ;
@@ -205,4 +208,31 @@ qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
void
qps_disable_modes(qps_file qf, qps_mbit_t mbits) ;
+Inline void*
+qps_file_info(qps_file qf)
+{
+ return qf->file_info ;
+} ;
+
+Inline int
+qps_file_fd(qps_file qf)
+{
+ return qf->fd ;
+} ;
+
+Inline int
+qps_file_unset_fd(qps_file qf)
+{
+ int fd = qf->fd ;
+ qf->fd = fd_undef ;
+
+ return fd ;
+} ;
+
+Inline void
+qps_set_file_info(qps_file qf, void* info)
+{
+ qf->file_info = info ;
+} ;
+
#endif /* _ZEBRA_QPSELECT_H */
diff --git a/lib/qpthreads.c b/lib/qpthreads.c
index ba6e20be..e7b39f6d 100644
--- a/lib/qpthreads.c
+++ b/lib/qpthreads.c
@@ -24,6 +24,7 @@
#include "config.h"
#include <signal.h>
+#include <string.h>
#include "qpthreads.h"
#include "memory.h"
@@ -56,14 +57,34 @@
* A big Global Switch -- qpthreads_enabled -- is used to control whether the
* system is pthreaded or not.
*
+ * The initial state is qpthreads_enabled == false (0).
+ *
+ * The function qpt_set_qpthreads_enabled() should be called when the
+ * application has decided whether to use qpthreads or not. (But does not have
+ * to call this if it is happy to proceed in the default -- disabled -- state.)
+ *
* If this is never set, then the system runs without pthreads, and all the
* mutex and condition variable functions are NOPs. This allows, for example,
* mutex operations to be placed where they are needed for thread-safety,
* without affecting the code when running without pthreads.
*
- * Before the first thread is created and before any mutexes or condition
- * variables are initialised, the qpthreads_enabled MUST be set. And it MUST
- * not be changed again !
+ * There are a very few operations which require qpthreads_enabled:
+ *
+ * * qpt_thread_attr_init
+ * * qpt_thread_create
+ *
+ * A few operations "freeze" the state of qpthreads_enabled. Any call of these
+ * before qpthreads are enabled, causes the state to be frozen, disabled. This
+ * means that any later attempt to enable qpthreads will be refused. These
+ * operations are:
+ *
+ * * qpt_mutex_init_new
+ * * qpt_cond_init_new
+ *
+ * This allows the application to decide as late as possible (but no later)
+ * whether to enable pthreads. If a mutex or a condition variable has been
+ * initialised before the application gets around to enabling qpthreads, that
+ * will be trapped when qpthreads is finally enabled.
*
* Pthread Requirements
* ====================
@@ -231,25 +252,27 @@ enum qpthreads_enabled_state
qpt_state_set_enabled = 3,
} ;
-static enum qpthreads_enabled_state qpthreads_enabled_state = qpt_state_unset ;
+static enum qpthreads_enabled_state qpthreads_enabled_state = qpt_state_unset ;
int qpthreads_enabled_flag = 0 ;
/* Function to set qpthreads_enabled, one way or the other.
*
+ * Returns: true <=> successful set the required state.
+ * false <=> it is too late to enable qpthreads :-(
+ *
* NB: can repeatedly set to the same state, but not change state once set.
*/
-void
+extern int
qpt_set_qpthreads_enabled(int how)
{
-
switch (qpthreads_enabled_state)
{
case qpt_state_unset:
break ;
case qpt_state_set_frozen:
if (how != 0)
- zabort("Too late to enable qpthreads") ;
+ return 0 ;
break ;
case qpt_state_set_disabled:
if (how != 0)
@@ -266,6 +289,7 @@ qpt_set_qpthreads_enabled(int how)
qpthreads_enabled_flag = (how != 0) ;
qpthreads_enabled_state = (how != 0) ? qpt_state_set_enabled
: qpt_state_set_disabled ;
+ return 1 ;
} ;
/* Get state of qpthreads_enabled, and freeze if not yet explictly set.
@@ -490,7 +514,11 @@ qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts)
int err ;
if (!qpthreads_enabled_freeze)
- return mx ;
+ {
+ if (mx != NULL)
+ memset(mx, 0x0F, sizeof(qpt_mutex_t)) ;
+ return mx ;
+ } ;
if (mx == NULL)
mx = XMALLOC(MTYPE_QPT_MUTEX, sizeof(qpt_mutex_t)) ;
@@ -592,7 +620,11 @@ qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts)
int err ;
if (!qpthreads_enabled_freeze)
- return cv ;
+ {
+ if (cv != NULL)
+ memset(cv, 0x0F, sizeof(qpt_cond_t)) ;
+ return cv ;
+ } ;
if (cv == NULL)
cv = XMALLOC(MTYPE_QPT_COND, sizeof(qpt_cond_t)) ;
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
index 2fbbea9a..aaf8e5dd 100644
--- a/lib/qpthreads.h
+++ b/lib/qpthreads.h
@@ -67,7 +67,7 @@
*
* Early in the morning a decision may be made to enable qpthreads -- that must
* be done before any threads are created (or will zabort) and before any
- * mutexes and condition variables are initialised.
+ * mutexes and condition variables are initialised (or it will be too late).
*
* Use: qpthreads_enabled -- to test for the enabled-ness
* qpthreads_enabled_freeze -- to test and freeze unset if not yet enabled
@@ -131,7 +131,7 @@ qpt_thread_join(qpt_thread_t thread_id) ;
*/
private int qpthreads_enabled_flag ; /* DO NOT WRITE TO THIS PLEASE */
-private void
+private int
qpt_set_qpthreads_enabled(int how) ; /* qpthreads_enabled := how */
private int
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 55c6226b..1f84aaca 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -16,7 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -28,7 +28,7 @@ int
setsockopt_so_recvbuf (int sock, int size)
{
int ret;
-
+
if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
&size, sizeof (int))) < 0)
zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
@@ -42,7 +42,7 @@ setsockopt_so_sendbuf (const int sock, int size)
{
int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF,
(char *)&size, sizeof (int));
-
+
if (ret < 0)
zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
sock, size, safe_strerror (errno));
@@ -71,8 +71,8 @@ getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
{
struct cmsghdr *cmsg;
void *ptr = NULL;
-
- for (cmsg = ZCMSG_FIRSTHDR(msgh);
+
+ for (cmsg = ZCMSG_FIRSTHDR(msgh);
cmsg != NULL;
cmsg = CMSG_NXTHDR(msgh, cmsg))
if (cmsg->cmsg_level == level && cmsg->cmsg_type)
@@ -87,7 +87,7 @@ int
setsockopt_ipv6_pktinfo (int sock, int val)
{
int ret;
-
+
#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
if (ret < 0)
@@ -162,7 +162,7 @@ int
setsockopt_ipv6_multicast_loop (int sock, int val)
{
int ret;
-
+
ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
sizeof (val));
if (ret < 0)
@@ -174,9 +174,9 @@ static int
getsockopt_ipv6_ifindex (struct msghdr *msgh)
{
struct in6_pktinfo *pktinfo;
-
+
pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
-
+
return pktinfo->ipi6_ifindex;
}
#endif /* HAVE_IPV6 */
@@ -204,8 +204,8 @@ getsockopt_ipv6_ifindex (struct msghdr *msgh)
* allow leaves, or implicitly leave all groups joined to down interfaces.
*/
int
-setsockopt_multicast_ipv4(int sock,
- int optname,
+setsockopt_multicast_ipv4(int sock,
+ int optname,
struct in_addr if_addr /* required */,
unsigned int mcast_addr,
unsigned int ifindex /* optional: if non-zero, may be
@@ -216,7 +216,7 @@ setsockopt_multicast_ipv4(int sock,
/* This is better because it uses ifindex directly */
struct ip_mreqn mreqn;
int ret;
-
+
switch (optname)
{
case IP_MULTICAST_IF:
@@ -226,12 +226,12 @@ setsockopt_multicast_ipv4(int sock,
if (mcast_addr)
mreqn.imr_multiaddr.s_addr = mcast_addr;
-
+
if (ifindex)
mreqn.imr_ifindex = ifindex;
else
mreqn.imr_address = if_addr;
-
+
ret = setsockopt(sock, IPPROTO_IP, optname,
(void *)&mreqn, sizeof(mreqn));
if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
@@ -264,7 +264,7 @@ setsockopt_multicast_ipv4(int sock,
/* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
/* Add your favourite OS here! */
-#else /* #if OS_TYPE */
+#else /* #if OS_TYPE */
/* standard BSD API */
struct in_addr m;
@@ -281,7 +281,7 @@ setsockopt_multicast_ipv4(int sock,
switch (optname)
{
case IP_MULTICAST_IF:
- return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
+ return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
break;
case IP_ADD_MEMBERSHIP:
@@ -289,7 +289,7 @@ setsockopt_multicast_ipv4(int sock,
memset (&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = mcast_addr;
mreq.imr_interface = m;
-
+
ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
{
@@ -308,7 +308,7 @@ setsockopt_multicast_ipv4(int sock,
}
return ret;
break;
-
+
default:
/* Can out and give an understandable error */
errno = EINVAL;
@@ -359,7 +359,7 @@ int
setsockopt_ifindex (int af, int sock, int val)
{
int ret = -1;
-
+
switch (af)
{
case AF_INET:
@@ -375,7 +375,7 @@ setsockopt_ifindex (int af, int sock, int val)
}
return ret;
}
-
+
/*
* Requires: msgh is not NULL and points to a valid struct msghdr, which
* may or may not have control data about the incoming interface.
@@ -392,12 +392,12 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh)
#if defined(IP_PKTINFO)
/* Linux pktinfo based ifindex retrieval */
struct in_pktinfo *pktinfo;
-
- pktinfo =
+
+ pktinfo =
(struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
/* XXX Can pktinfo be NULL? Clean up post 0.98. */
ifindex = pktinfo->ipi_ifindex;
-
+
#elif defined(IP_RECVIF)
/* retrieval based on IP_RECVIF */
@@ -412,7 +412,7 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh)
#ifndef SUNOS_5
/* BSD */
- sdl =
+ sdl =
(struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
if (sdl != NULL)
ifindex = sdl->sdl_index;
@@ -424,7 +424,7 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh)
* enable it fails with errno=99, and the struct msghdr has
* controllen 0.
*/
- ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
+ ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
if (ifindex_p != NULL)
ifindex = *ifindex_p;
else
@@ -442,7 +442,7 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh)
#warning "Some daemons may fail to operate correctly!"
ifindex = 0;
-#endif /* IP_PKTINFO */
+#endif /* IP_PKTINFO */
return ifindex;
}
@@ -452,7 +452,7 @@ int
getsockopt_ifindex (int af, struct msghdr *msgh)
{
int ifindex = 0;
-
+
switch (af)
{
case AF_INET:
@@ -473,7 +473,7 @@ getsockopt_ifindex (int af, struct msghdr *msgh)
void
sockopt_iphdrincl_swab_htosys (struct ip *iph)
{
- /* BSD and derived take iph in network order, except for
+ /* BSD and derived take iph in network order, except for
* ip_len and ip_off
*/
#ifndef HAVE_IP_HDRINCL_BSD_ORDER
@@ -495,9 +495,19 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph)
iph->ip_id = ntohs(iph->ip_id);
}
+/*==============================================================================
+ * Set TCP MD5 signature socket option.
+ *
+ * Returns: 0 => OK
+ * errno => failed.
+ *
+ * NB: returns ENOSYS if TCP MD5 is not supported
+ */
int
sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
{
+ int ret ;
+
#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
/* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
* version of the Quagga patch (based on work by Rick Payne, and Bruce
@@ -513,50 +523,49 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
void *key; /* MD5 Key */
} cmd;
struct in_addr *addr = &su->sin.sin_addr;
-
+
cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL);
cmd.address = addr->s_addr;
- cmd.keylen = (password != NULL ? strlen (password) : 0);
- cmd.key = password;
-
- return setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd);
-
+ cmd.keylen = (password != NULL ? strlen (password) : 0);
+ cmd.key = password;
+
+ ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ;
+
+ return (ret >= 0) ? 0 : errno ;
+
#elif HAVE_DECL_TCP_MD5SIG
- int ret;
#ifndef GNU_LINUX
/*
* XXX Need to do PF_KEY operation here to add/remove an SA entry,
* and add/remove an SP entry for this peer's packet flows also.
*/
- int md5sig = password && *password ? 1 : 0;
+ int md5sig = password && *password ? 1 : 0;
#else
- int keylen = password ? strlen (password) : 0;
- struct tcp_md5sig md5sig;
- union sockunion *su2, *susock;
-
+ int keylen = password ? strlen (password) : 0 ;
+ struct tcp_md5sig md5sig ;
+ union sockunion *su2 ;
+ union sockunion susock ;
+
/* Figure out whether the socket and the sockunion are the same family..
* adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
*/
- if (!(susock = sockunion_getsockname (sock)))
- return -1;
-
- if (susock->sa.sa_family == su->sa.sa_family)
- su2 = su;
+ if ((ret = sockunion_getsockname(sock, &susock)) != 0)
+ return ret ;
+
+ if (susock.sa.sa_family == su->sa.sa_family)
+ su2 = su ;
else
{
/* oops.. */
- su2 = susock;
-
+ su2 = &susock ;
+
if (su2->sa.sa_family == AF_INET)
- {
- sockunion_free (susock);
- return 0;
- }
-
+ return 0 ; /* TODO: find out what this is doing ?? */
+
#ifdef HAVE_IPV6
/* If this does not work, then all users of this sockopt will need to
* differentiate between IPv4 and IPv6, and keep seperate sockets for
- * each.
+ * each.
*
* Sadly, it doesn't seem to work at present. It's unknown whether
* this is a bug or not.
@@ -572,26 +581,74 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
}
#endif
}
-
+
memset (&md5sig, 0, sizeof (md5sig));
memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2));
md5sig.tcpm_keylen = keylen;
if (keylen)
memcpy (md5sig.tcpm_key, password, keylen);
- sockunion_free (susock);
+
#endif /* GNU_LINUX */
- if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0)
+
+ ret = setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig) ;
+ if (ret < 0)
{
+ ret = errno ;
/* ENOENT is harmless. It is returned when we clear a password for which
one was not previously set. */
- if (ENOENT == errno)
- ret = 0;
+ if (ret == ENOENT)
+ ret = 0 ;
else
zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
- sock, safe_strerror(errno));
+ sock, safe_strerror(ret));
}
- return ret;
+ else
+ ret = 0 ; /* no error */
+
#else /* HAVE_TCP_MD5SIG */
- return -2;
+
+ ret = ENOSYS ; /* TCP MD5 is not supported */
+
#endif /* !HAVE_TCP_MD5SIG */
-}
+
+ return ret;
+} ;
+
+/*==============================================================================
+ * Set TTL for socket (only used in bgpd)
+ *
+ * Returns: 0 : OK (so far so good)
+ * != 0 : error number (from errno or otherwise)
+ */
+
+int
+sockopt_ttl (int family, int sock, int ttl)
+{
+ char* msg ;
+ int ret ;
+
+ ret = 0 ;
+
+#ifdef IP_TTL
+ if (family == AF_INET)
+ {
+ ret = setsockopt (sock, IPPROTO_IP, IP_TTL,(void*)&ttl, sizeof(int));
+ msg = "can't set sockopt IP_TTL %d to socket %d" ;
+ }
+#endif /* IP_TTL */
+#ifdef HAVE_IPV6
+ if (family == AF_INET6)
+ {
+ ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (void*)&ttl, sizeof(int));
+ msg = "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d" ;
+ }
+#endif /* HAVE_IPV6 */
+
+ ret = (ret < 0) ? errno : 0 ;
+
+ if (ret != 0)
+ zlog (NULL, LOG_WARNING, msg, ttl, sock) ;
+
+ return ret ;
+} ;
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 6a40f332..c33a5713 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -16,7 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -28,6 +28,8 @@
#include "str.h"
#include "log.h"
+#include "symtab.h"
+
#ifndef HAVE_INET_ATON
int
inet_aton (const char *cp, struct in_addr *inaddr)
@@ -95,13 +97,13 @@ inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
{
unsigned char *p = (unsigned char *) addrptr;
- if (family == AF_INET)
+ if (family == AF_INET)
{
char temp[INET_ADDRSTRLEN];
snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
- if (strlen(temp) >= len)
+ if (strlen(temp) >= len)
{
errno = ENOSPC;
return NULL;
@@ -231,49 +233,57 @@ static void
sockunion_normalise_mapped (union sockunion *su)
{
struct sockaddr_in sin;
-
+
#ifdef HAVE_IPV6
- if (su->sa.sa_family == AF_INET6
+ if (su->sa.sa_family == AF_INET6
&& IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
{
memset (&sin, 0, sizeof (struct sockaddr_in));
sin.sin_family = AF_INET;
- sin.sin_port = su->sin6.sin6_port;
+ sin.sin_port = su->sin6.sin6_port;
memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
+ memset (su, 0, sizeof(union sockunion)) ;
memcpy (su, &sin, sizeof (struct sockaddr_in));
}
#endif /* HAVE_IPV6 */
}
-/* Return socket of sockunion. */
-int
-sockunion_socket (union sockunion *su)
-{
- int sock;
-
- sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
- if (sock < 0)
- {
- zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror (errno));
- return -1;
- }
-
- return sock;
-}
-
-/* Return accepted new socket file descriptor. */
+/*------------------------------------------------------------------------------
+ * Return accepted new socket file descriptor.
+ *
+ * The following errors should be ignored:
+ *
+ * EAGAIN, EWOULDBLOCK or ECONNABORTED -- connection aborted before got
+ * around to it (or not ready, anyway).
+ *
+ * EINTR -- the usual suspect.
+ *
+ * Returns: >= 0 -- OK, this is the fd (socket)
+ * -1 -- error -- not one of the above
+ * -2 -- error -- one of the above
+ */
int
sockunion_accept (int sock, union sockunion *su)
{
socklen_t len;
- int client_sock;
+ int ret ;
- len = sizeof (union sockunion);
- client_sock = accept (sock, (struct sockaddr *) su, &len);
-
- sockunion_normalise_mapped (su);
- return client_sock;
-}
+ len = sizeof(union sockunion);
+ memset(su, 0, len) ;
+ ret = accept(sock, (struct sockaddr *)su, &len) ;
+
+ if (client_sock >= 0)
+ {
+ sockunion_normalise_mapped(su);
+ return ret ; /* OK -- got socket */
+ } ;
+
+ ret = errno ;
+ return ( (ret == EAGAIN)
+ || (ret == EWOULDBLOCK)
+ || (ret == ECONNABORTED)
+ || (ret == EINTR) ) ? -2 : -1 ;
+} ;
/* Return sizeof union sockunion. */
static int
@@ -302,7 +312,7 @@ sockunion_log (union sockunion *su)
{
static char buf[SU_ADDRSTRLEN];
- switch (su->sa.sa_family)
+ switch (su->sa.sa_family)
{
case AF_INET:
snprintf (buf, SU_ADDRSTRLEN, "%s", inet_ntoa (su->sin.sin_addr));
@@ -320,35 +330,59 @@ sockunion_log (union sockunion *su)
return (XSTRDUP (MTYPE_TMP, buf));
}
-/* sockunion_connect returns
- -1 : error occured
- 0 : connect success
- 1 : connect is in progress */
-enum connect_result
-sockunion_connect (int fd, union sockunion *peersu, unsigned short port,
- unsigned int ifindex)
+/*==============================================================================
+ * Return socket of sockunion. (only used in bgpd)
+ *
+ * Returns: -1 : failed -- see errno
+ * otherwise : socket
+ */
+int
+sockunion_socket (union sockunion *su)
{
- int ret;
- int val;
- union sockunion su;
+ int sockfd ;
- memcpy (&su, peersu, sizeof (union sockunion));
+ sockfd = socket(su->sa.sa_family, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ {
+ zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror(errno)) ;
+ return -1;
+ }
+
+ return sockfd ;
+}
+
+/*==============================================================================
+ * Initiate a connection (only used in bgpd)
+ *
+ * Reports EINPROGRESS as success.
+ *
+ * Returns: 0 : OK (so far so good)
+ * != 0 : error number (from errno or otherwise)
+ */
+extern int
+sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
+ unsigned int ifindex)
+{
+ union sockunion su ;
+ int ret ;
+
+ memcpy(&su, peer_su, sizeof(union sockunion)) ;
switch (su.sa.sa_family)
{
case AF_INET:
- su.sin.sin_port = port;
+ su.sin.sin_port = htons(port) ;
break;
#ifdef HAVE_IPV6
case AF_INET6:
- su.sin6.sin6_port = port;
+ su.sin6.sin6_port = htons(port) ;
#ifdef KAME
if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
{
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
/* su.sin6.sin6_scope_id = ifindex; */
#ifdef MUSICA
- su.sin6.sin6_scope_id = ifindex;
+ su.sin6.sin6_scope_id = ifindex;
#endif
#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
#ifndef MUSICA
@@ -358,37 +392,18 @@ sockunion_connect (int fd, union sockunion *peersu, unsigned short port,
#endif /* KAME */
break;
#endif /* HAVE_IPV6 */
- }
-
- /* Make socket non-block. */
- val = fcntl (fd, F_GETFL, 0);
- fcntl (fd, F_SETFL, val|O_NONBLOCK);
-
- /* Call connect function. */
- ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));
-
- /* Immediate success */
- if (ret == 0)
- {
- fcntl (fd, F_SETFL, val);
- return connect_success;
}
- /* If connect is in progress then return 1 else it's real error. */
- if (ret < 0)
- {
- if (errno != EINPROGRESS)
- {
- zlog_info ("can't connect to %s fd %d : %s",
- sockunion_log (&su), fd, safe_strerror (errno));
- return connect_error;
- }
- }
+ ret = connect(fd, (struct sockaddr *)&su, sockunion_sizeof(&su)) ;
- fcntl (fd, F_SETFL, val);
+ if ((ret == 0) || ((ret = errno) != EINPROGRESS))
+ return 0 ; /* instant success or EINPROGRESS as expected */
- return connect_in_progress;
-}
+ zlog_info("can't connect to %s fd %d : %s",
+ sockunion_log (&su), fd, safe_strerror(ret)) ;
+
+ return ret ;
+} ;
/* Make socket from sockunion union. */
int
@@ -409,7 +424,7 @@ sockunion_stream_socket (union sockunion *su)
/* Bind socket to specified address. */
int
-sockunion_bind (int sock, union sockunion *su, unsigned short port,
+sockunion_bind (int sock, union sockunion *su, unsigned short port,
union sockunion *su_addr)
{
int size = 0;
@@ -443,7 +458,7 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port,
}
}
#endif /* HAVE_IPV6 */
-
+
ret = bind (sock, (struct sockaddr *)su, size);
if (ret < 0)
@@ -458,7 +473,7 @@ sockopt_reuseaddr (int sock)
int ret;
int on = 1;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
+ ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
(void *) &on, sizeof (on));
if (ret < 0)
{
@@ -475,7 +490,7 @@ sockopt_reuseport (int sock)
int ret;
int on = 1;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
+ ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
(void *) &on, sizeof (on));
if (ret < 0)
{
@@ -492,41 +507,6 @@ sockopt_reuseport (int sock)
}
#endif /* 0 */
-int
-sockopt_ttl (int family, int sock, int ttl)
-{
- int ret;
-
-#ifdef IP_TTL
- if (family == AF_INET)
- {
- ret = setsockopt (sock, IPPROTO_IP, IP_TTL,
- (void *) &ttl, sizeof (int));
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock);
- return -1;
- }
- return 0;
- }
-#endif /* IP_TTL */
-#ifdef HAVE_IPV6
- if (family == AF_INET6)
- {
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
- (void *) &ttl, sizeof (int));
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
- ttl, sock);
- return -1;
- }
- return 0;
- }
-#endif /* HAVE_IPV6 */
- return 0;
-}
-
/* If same family and same prefix return 1. */
int
sockunion_same (union sockunion *su1, union sockunion *su2)
@@ -555,12 +535,19 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
return 0;
}
-/* After TCP connection is established. Get local address and port. */
-union sockunion *
-sockunion_getsockname (int fd)
+/*------------------------------------------------------------------------------
+ * After TCP connection is established. Get local or remote address and port.
+ *
+ * Returns: 0 => OK
+ * != 0 => failed, value = errno
+ *
+ * NB: returns EAFNOSUPPORT if don't recognise the address family.
+ */
+static int
+sockunion_get_name(int fd, union sockunion* su, int local)
{
int ret;
- socklen_t len;
+
union
{
struct sockaddr sa;
@@ -569,82 +556,74 @@ sockunion_getsockname (int fd)
struct sockaddr_in6 sin6;
#endif /* HAVE_IPV6 */
char tmp_buffer[128];
- } name;
- union sockunion *su;
+ } name ;
+
+ socklen_t len = sizeof(name) ;
- memset (&name, 0, sizeof name);
- len = sizeof name;
+ memset(&name, 0, len);
+ memset(su, 0, sizeof(union sockunion)) ;
+
+ if (local)
+ ret = getsockname(fd, (struct sockaddr *)&name, &len) ;
+ else
+ ret = getpeername(fd, (struct sockaddr *)&name, &len) ;
- ret = getsockname (fd, (struct sockaddr *)&name, &len);
if (ret < 0)
{
- zlog_warn ("Can't get local address and port by getsockname: %s",
- safe_strerror (errno));
- return NULL;
+ ret = errno ;
+ zlog_warn ("Can't get %s address and port: %s",
+ local ? "local" : "remote", safe_strerror(ret)) ;
+ return ret ;
}
- if (name.sa.sa_family == AF_INET)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in));
- return su;
- }
+ switch (name.sa.sa_family)
+ {
+ case AF_INET:
+ memcpy(su, &name, sizeof (struct sockaddr_in)) ;
+ break ;
+
#ifdef HAVE_IPV6
- if (name.sa.sa_family == AF_INET6)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in6));
- sockunion_normalise_mapped (su);
- return su;
- }
+ case AF_INET6:
+ memcpy(su, &name, sizeof (struct sockaddr_in6)) ;
+ sockunion_normalise_mapped(su) ;
+ break ;
#endif /* HAVE_IPV6 */
- return NULL;
-}
-/* After TCP connection is established. Get remote address and port. */
-union sockunion *
-sockunion_getpeername (int fd)
+ default:
+ ret = EAFNOSUPPORT ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * After TCP connection is established. Get local address and port.
+ *
+ * Returns: 0 => OK
+ * != 0 => failed, value = errno
+ *
+ * NB: returns EAFNOSUPPORT if don't recognise the socket's address family.
+ */
+int
+sockunion_getsockname(int fd, union sockunion* su_local)
{
- int ret;
- socklen_t len;
- union
- {
- struct sockaddr sa;
- struct sockaddr_in sin;
-#ifdef HAVE_IPV6
- struct sockaddr_in6 sin6;
-#endif /* HAVE_IPV6 */
- char tmp_buffer[128];
- } name;
- union sockunion *su;
+ return sockunion_get_name(fd, su_local, 1) ;
+} ;
- memset (&name, 0, sizeof name);
- len = sizeof name;
- ret = getpeername (fd, (struct sockaddr *)&name, &len);
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s",
- safe_strerror (errno));
- return NULL;
- }
+/*------------------------------------------------------------------------------
+ * After TCP connection is established. Get remote address and port.
+ *
+ * Returns: 0 => OK
+ * != 0 => failed, value = errno
+ *
+ * NB: returns EAFNOSUPPORT if don't recognise the socket's address family.
+ */
+int
+sockunion_getpeername (int fd, union sockunion* su_remote)
+{
+ return sockunion_get_name(fd, su_remote, 0) ;
+} ;
- if (name.sa.sa_family == AF_INET)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in));
- return su;
- }
-#ifdef HAVE_IPV6
- if (name.sa.sa_family == AF_INET6)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in6));
- sockunion_normalise_mapped (su);
- return su;
- }
-#endif /* HAVE_IPV6 */
- return NULL;
-}
/* Print sockunion structure */
static void __attribute__ ((unused))
@@ -653,7 +632,7 @@ sockunion_print (union sockunion *su)
if (su == NULL)
return;
- switch (su->sa.sa_family)
+ switch (su->sa.sa_family)
{
case AF_INET:
printf ("%s\n", inet_ntoa (su->sin.sin_addr));
@@ -744,3 +723,42 @@ sockunion_free (union sockunion *su)
{
XFREE (MTYPE_SOCKUNION, su);
}
+
+/*==============================================================================
+ * Clear a given sockunion -- ie zeroise it
+ */
+extern void
+sockunion_clear(union sockunion* su)
+{
+ memset(su, 0, sizeof(union sockunion)) ;
+} ;
+
+/*==============================================================================
+ * Symbol Table Hash function -- for symbols whose name is an address.
+ */
+
+extern void
+sockunion_symbol_hash(symbol_hash p_hash, const void* name)
+{
+ const union sockunion* su = name ;
+
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ confirm(sizeof(p_hash->hash) == sizeof(su->sin.sin_addr.s_addr)) ;
+ p_hash->hash = su->sin.sin_addr.s_addr ;
+ p_hash->name = (const void*)&su->sin.sin_addr.s_addr ;
+ p_hash->name_len = sizeof(su->sin.sin_addr.s_addr) ;
+ p_hash->name_copy_len = sizeof(su->sin.sin_addr.s_addr) ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ symbol_hash_bytes(p_hash, (const void*)&su->sin6.sin6_addr,
+ sizeof(su->sin6.sin6_addr)) ;
+ break ;
+#endif /* HAVE_IPV6 */
+ default:
+ zabort("Unknown address family") ;
+ } ;
+} ;
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 96b9a0d4..2009da2f 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -17,12 +17,14 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#ifndef _ZEBRA_SOCKUNION_H
#define _ZEBRA_SOCKUNION_H
+#include "symtab.h"
+
#if 0
union sockunion {
struct sockinet {
@@ -38,7 +40,7 @@ union sockunion {
#define su_port su_si.si_port
#endif /* 0 */
-union sockunion
+union sockunion
{
struct sockaddr sa;
struct sockaddr_in sin;
@@ -99,21 +101,21 @@ extern int sockunion_accept (int sock, union sockunion *);
extern int sockunion_stream_socket (union sockunion *);
extern int sockopt_reuseaddr (int);
extern int sockopt_reuseport (int);
-extern int sockunion_bind (int sock, union sockunion *,
+extern int sockunion_bind (int sock, union sockunion *,
unsigned short, union sockunion *);
extern int sockopt_ttl (int family, int sock, int ttl);
extern int sockunion_socket (union sockunion *su);
extern const char *inet_sutop (union sockunion *su, char *str);
-extern enum connect_result sockunion_connect (int fd, union sockunion *su,
- unsigned short port,
- unsigned int);
-extern union sockunion *sockunion_getsockname (int);
-extern union sockunion *sockunion_getpeername (int);
+extern int sockunion_connect (int fd, union sockunion *su, unsigned short port,
+ unsigned int) ;
+extern int sockunion_getsockname (int, union sockunion*);
+extern int sockunion_getpeername (int, union sockunion*);
extern union sockunion *sockunion_dup (union sockunion *);
extern void sockunion_free (union sockunion *);
+extern void sockunion_clear(union sockunion*);
#ifndef HAVE_INET_NTOP
-extern const char * inet_ntop (int family, const void *addrptr,
+extern const char * inet_ntop (int family, const void *addrptr,
char *strptr, size_t len);
#endif /* HAVE_INET_NTOP */
@@ -125,4 +127,7 @@ extern int inet_pton (int family, const char *strptr, void *addrptr);
extern int inet_aton (const char *cp, struct in_addr *inaddr);
#endif
+extern void
+sockunion_symbol_hash(symbol_hash p_hash, const void* name) ;
+
#endif /* _ZEBRA_SOCKUNION_H */
diff --git a/lib/stream.c b/lib/stream.c
index 983330ff..36bbba1e 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <stddef.h>
@@ -29,7 +29,7 @@
#include "prefix.h"
#include "log.h"
-/* Tests whether a position is valid */
+/* Tests whether a position is valid */
#define GETP_VALID(S,G) \
((G) <= (S)->endp)
#define PUT_AT_VALID(S,G) GETP_VALID(S,G)
@@ -92,24 +92,24 @@ stream_new (size_t size)
struct stream *s;
assert (size > 0);
-
+
if (size == 0)
{
zlog_warn ("stream_new(): called with 0 size!");
return NULL;
}
-
+
s = XCALLOC (MTYPE_STREAM, sizeof (struct stream));
if (s == NULL)
return s;
-
+
if ( (s->data = XMALLOC (MTYPE_STREAM_DATA, size)) == NULL)
{
XFREE (MTYPE_STREAM, s);
return NULL;
}
-
+
s->size = size;
return s;
}
@@ -120,7 +120,7 @@ stream_free (struct stream *s)
{
if (!s)
return;
-
+
XFREE (MTYPE_STREAM_DATA, s->data);
XFREE (MTYPE_STREAM, s);
}
@@ -129,15 +129,15 @@ struct stream *
stream_copy (struct stream *new, struct stream *src)
{
STREAM_VERIFY_SANE (src);
-
+
assert (new != NULL);
assert (STREAM_SIZE(new) >= src->endp);
new->endp = src->endp;
new->getp = src->getp;
-
+
memcpy (new->data, src->data, src->endp);
-
+
return new;
}
@@ -154,30 +154,52 @@ stream_dup (struct stream *s)
return (stream_copy (new, s));
}
+struct stream *
+stream_dup_pending (struct stream *s)
+{
+ struct stream *new;
+ int new_endp ;
+
+ STREAM_VERIFY_SANE (s);
+
+ new_endp = s->endp - s->getp ;
+ if ( (new = stream_new(new_endp)) == NULL)
+ return NULL;
+
+ assert (STREAM_SIZE(new) >= new_endp);
+
+ new->endp = new_endp ;
+ new->getp = 0 ;
+
+ memcpy (new->data, s->data + s->getp, new_endp) ;
+
+ return new ;
+}
+
size_t
stream_resize (struct stream *s, size_t newsize)
{
u_char *newdata;
STREAM_VERIFY_SANE (s);
-
+
newdata = XREALLOC (MTYPE_STREAM_DATA, s->data, newsize);
-
+
if (newdata == NULL)
return s->size;
-
+
s->data = newdata;
s->size = newsize;
-
+
if (s->endp > s->size)
s->endp = s->size;
if (s->getp > s->endp)
s->getp = s->endp;
-
+
STREAM_VERIFY_SANE (s);
-
+
return s->size;
}
-
+
size_t
stream_get_getp (struct stream *s)
{
@@ -204,7 +226,7 @@ void
stream_set_getp (struct stream *s, size_t pos)
{
STREAM_VERIFY_SANE(s);
-
+
if (!GETP_VALID (s, pos))
{
STREAM_BOUND_WARN (s, "set getp");
@@ -219,13 +241,13 @@ void
stream_forward_getp (struct stream *s, size_t size)
{
STREAM_VERIFY_SANE(s);
-
+
if (!GETP_VALID (s, s->getp + size))
{
STREAM_BOUND_WARN (s, "seek getp");
return;
}
-
+
s->getp += size;
}
@@ -233,28 +255,28 @@ void
stream_forward_endp (struct stream *s, size_t size)
{
STREAM_VERIFY_SANE(s);
-
+
if (!ENDP_VALID (s, s->endp + size))
{
STREAM_BOUND_WARN (s, "seek endp");
return;
}
-
+
s->endp += size;
}
-
+
/* Copy from stream to destination. */
void
stream_get (void *dst, struct stream *s, size_t size)
{
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_READABLE(s) < size)
{
STREAM_BOUND_WARN (s, "get");
return;
}
-
+
memcpy (dst, s->data + s->getp, size);
s->getp += size;
}
@@ -264,7 +286,7 @@ u_char
stream_getc (struct stream *s)
{
u_char c;
-
+
STREAM_VERIFY_SANE (s);
if (STREAM_READABLE(s) < sizeof (u_char))
@@ -273,7 +295,7 @@ stream_getc (struct stream *s)
return 0;
}
c = s->data[s->getp++];
-
+
return c;
}
@@ -284,15 +306,15 @@ stream_getc_from (struct stream *s, size_t from)
u_char c;
STREAM_VERIFY_SANE(s);
-
+
if (!GETP_VALID (s, from + sizeof (u_char)))
{
STREAM_BOUND_WARN (s, "get char");
return 0;
}
-
+
c = s->data[from];
-
+
return c;
}
@@ -309,10 +331,10 @@ stream_getw (struct stream *s)
STREAM_BOUND_WARN (s, "get ");
return 0;
}
-
+
w = s->data[s->getp++] << 8;
w |= s->data[s->getp++];
-
+
return w;
}
@@ -323,16 +345,16 @@ stream_getw_from (struct stream *s, size_t from)
u_int16_t w;
STREAM_VERIFY_SANE(s);
-
+
if (!GETP_VALID (s, from + sizeof (u_int16_t)))
{
STREAM_BOUND_WARN (s, "get ");
return 0;
}
-
+
w = s->data[from++] << 8;
w |= s->data[from];
-
+
return w;
}
@@ -343,18 +365,18 @@ stream_getl_from (struct stream *s, size_t from)
u_int32_t l;
STREAM_VERIFY_SANE(s);
-
+
if (!GETP_VALID (s, from + sizeof (u_int32_t)))
{
STREAM_BOUND_WARN (s, "get long");
return 0;
}
-
+
l = s->data[from++] << 24;
l |= s->data[from++] << 16;
l |= s->data[from++] << 8;
l |= s->data[from];
-
+
return l;
}
@@ -364,18 +386,18 @@ stream_getl (struct stream *s)
u_int32_t l;
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_READABLE (s) < sizeof (u_int32_t))
{
STREAM_BOUND_WARN (s, "get long");
return 0;
}
-
+
l = s->data[s->getp++] << 24;
l |= s->data[s->getp++] << 16;
l |= s->data[s->getp++] << 8;
l |= s->data[s->getp++];
-
+
return l;
}
@@ -386,22 +408,22 @@ stream_getq_from (struct stream *s, size_t from)
uint64_t q;
STREAM_VERIFY_SANE(s);
-
+
if (!GETP_VALID (s, from + sizeof (uint64_t)))
{
STREAM_BOUND_WARN (s, "get quad");
return 0;
}
-
+
q = ((uint64_t) s->data[from++]) << 56;
q |= ((uint64_t) s->data[from++]) << 48;
q |= ((uint64_t) s->data[from++]) << 40;
- q |= ((uint64_t) s->data[from++]) << 32;
+ q |= ((uint64_t) s->data[from++]) << 32;
q |= ((uint64_t) s->data[from++]) << 24;
q |= ((uint64_t) s->data[from++]) << 16;
q |= ((uint64_t) s->data[from++]) << 8;
q |= ((uint64_t) s->data[from++]);
-
+
return q;
}
@@ -411,22 +433,22 @@ stream_getq (struct stream *s)
uint64_t q;
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_READABLE (s) < sizeof (uint64_t))
{
STREAM_BOUND_WARN (s, "get quad");
return 0;
}
-
+
q = ((uint64_t) s->data[s->getp++]) << 56;
q |= ((uint64_t) s->data[s->getp++]) << 48;
q |= ((uint64_t) s->data[s->getp++]) << 40;
- q |= ((uint64_t) s->data[s->getp++]) << 32;
+ q |= ((uint64_t) s->data[s->getp++]) << 32;
q |= ((uint64_t) s->data[s->getp++]) << 24;
q |= ((uint64_t) s->data[s->getp++]) << 16;
q |= ((uint64_t) s->data[s->getp++]) << 8;
q |= ((uint64_t) s->data[s->getp++]);
-
+
return q;
}
@@ -437,19 +459,19 @@ stream_get_ipv4 (struct stream *s)
u_int32_t l;
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_READABLE (s) < sizeof(u_int32_t))
{
STREAM_BOUND_WARN (s, "get ipv4");
return 0;
}
-
+
memcpy (&l, s->data + s->getp, sizeof(u_int32_t));
s->getp += sizeof(u_int32_t);
return l;
}
-
+
/* Copy to source to stream.
*
* XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap
@@ -463,15 +485,15 @@ stream_put (struct stream *s, const void *src, size_t size)
/* XXX: CHECK_SIZE has strange semantics. It should be deprecated */
CHECK_SIZE(s, size);
-
+
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < size)
{
STREAM_BOUND_WARN (s, "put");
return;
}
-
+
if (src)
memcpy (s->data + s->endp, src, size);
else
@@ -485,13 +507,13 @@ int
stream_putc (struct stream *s, u_char c)
{
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < sizeof(u_char))
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
s->data[s->endp++] = c;
return sizeof (u_char);
}
@@ -507,7 +529,7 @@ stream_putw (struct stream *s, u_int16_t w)
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
s->data[s->endp++] = (u_char)(w >> 8);
s->data[s->endp++] = (u_char) w;
@@ -525,7 +547,7 @@ stream_putl (struct stream *s, u_int32_t l)
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
s->data[s->endp++] = (u_char)(l >> 24);
s->data[s->endp++] = (u_char)(l >> 16);
s->data[s->endp++] = (u_char)(l >> 8);
@@ -545,7 +567,7 @@ stream_putq (struct stream *s, uint64_t q)
STREAM_BOUND_WARN (s, "put quad");
return 0;
}
-
+
s->data[s->endp++] = (u_char)(q >> 56);
s->data[s->endp++] = (u_char)(q >> 48);
s->data[s->endp++] = (u_char)(q >> 40);
@@ -562,15 +584,15 @@ int
stream_putc_at (struct stream *s, size_t putp, u_char c)
{
STREAM_VERIFY_SANE(s);
-
+
if (!PUT_AT_VALID (s, putp + sizeof (u_char)))
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
s->data[putp] = c;
-
+
return 1;
}
@@ -578,16 +600,16 @@ int
stream_putw_at (struct stream *s, size_t putp, u_int16_t w)
{
STREAM_VERIFY_SANE(s);
-
+
if (!PUT_AT_VALID (s, putp + sizeof (u_int16_t)))
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
s->data[putp] = (u_char)(w >> 8);
s->data[putp + 1] = (u_char) w;
-
+
return 2;
}
@@ -595,7 +617,7 @@ int
stream_putl_at (struct stream *s, size_t putp, u_int32_t l)
{
STREAM_VERIFY_SANE(s);
-
+
if (!PUT_AT_VALID (s, putp + sizeof (u_int32_t)))
{
STREAM_BOUND_WARN (s, "put");
@@ -605,7 +627,7 @@ stream_putl_at (struct stream *s, size_t putp, u_int32_t l)
s->data[putp + 1] = (u_char)(l >> 16);
s->data[putp + 2] = (u_char)(l >> 8);
s->data[putp + 3] = (u_char)l;
-
+
return 4;
}
@@ -613,7 +635,7 @@ int
stream_putq_at (struct stream *s, size_t putp, uint64_t q)
{
STREAM_VERIFY_SANE(s);
-
+
if (!PUT_AT_VALID (s, putp + sizeof (uint64_t)))
{
STREAM_BOUND_WARN (s, "put");
@@ -627,7 +649,7 @@ stream_putq_at (struct stream *s, size_t putp, uint64_t q)
s->data[putp + 5] = (u_char)(q >> 16);
s->data[putp + 6] = (u_char)(q >> 8);
s->data[putp + 7] = (u_char)q;
-
+
return 8;
}
@@ -636,7 +658,7 @@ int
stream_put_ipv4 (struct stream *s, u_int32_t l)
{
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < sizeof (u_int32_t))
{
STREAM_BOUND_WARN (s, "put");
@@ -653,7 +675,7 @@ int
stream_put_in_addr (struct stream *s, struct in_addr *addr)
{
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < sizeof (u_int32_t))
{
STREAM_BOUND_WARN (s, "put");
@@ -671,24 +693,24 @@ int
stream_put_prefix (struct stream *s, struct prefix *p)
{
size_t psize;
-
+
STREAM_VERIFY_SANE(s);
-
+
psize = PSIZE (p->prefixlen);
-
+
if (STREAM_WRITEABLE (s) < psize)
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
stream_putc (s, p->prefixlen);
memcpy (s->data + s->endp, &p->u.prefix, psize);
s->endp += psize;
-
+
return psize;
}
-
+
/* Read size from fd. */
int
stream_read (struct stream *s, int fd, size_t size)
@@ -696,18 +718,18 @@ stream_read (struct stream *s, int fd, size_t size)
int nbytes;
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < size)
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
nbytes = readn (fd, s->data + s->endp, size);
if (nbytes > 0)
s->endp += nbytes;
-
+
return nbytes;
}
@@ -717,15 +739,15 @@ stream_read_unblock (struct stream *s, int fd, size_t size)
{
int nbytes;
int val;
-
+
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < size)
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
val = fcntl (fd, F_GETFL, 0);
fcntl (fd, F_SETFL, val|O_NONBLOCK);
nbytes = read (fd, s->data + s->endp, size);
@@ -733,17 +755,67 @@ stream_read_unblock (struct stream *s, int fd, size_t size)
if (nbytes > 0)
s->endp += nbytes;
-
+
return nbytes;
}
+/*------------------------------------------------------------------------------
+ * Read up to size bytes into stream -- assuming non-blocking socket.
+ *
+ * Loops internally if gets EINTR -- so if does not read everything asked for,
+ * that must be because the read would otherwise block.
+ *
+ * Returns: 0..size -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met
+ *
+ * NB: if asks for zero bytes, will return 0 or error (if any).
+ */
+int
+stream_read_nonblock(struct stream *s, int fd, size_t size)
+{
+ int ret ;
+ int want = size ;
+
+ STREAM_VERIFY_SANE(s);
+
+ if (STREAM_WRITEABLE (s) < size)
+ {
+ STREAM_BOUND_WARN (s, "put");
+ return 0;
+ }
+
+ do
+ {
+ ret = read(fd, s->data + s->endp, want);
+
+ if (ret > 0)
+ {
+ s->endp += ret ;
+ want -= ret ;
+ }
+ else if (ret == 0)
+ return (want == 0) ? 0 : -2 ;
+ else
+ {
+ int err = errno ;
+ if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ break ;
+ if (err != EINTR)
+ return -1 ;
+ } ;
+ } while (want > 0) ;
+
+ return size - want ;
+}
+
ssize_t
stream_read_try(struct stream *s, int fd, size_t size)
{
ssize_t nbytes;
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE(s) < size)
{
STREAM_BOUND_WARN (s, "put");
@@ -767,14 +839,14 @@ stream_read_try(struct stream *s, int fd, size_t size)
/* Read up to size bytes into the stream from the fd, using recvmsgfrom
* whose arguments match the remaining arguments to this function
*/
-ssize_t
+ssize_t
stream_recvfrom (struct stream *s, int fd, size_t size, int flags,
- struct sockaddr *from, socklen_t *fromlen)
+ struct sockaddr *from, socklen_t *fromlen)
{
ssize_t nbytes;
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE(s) < size)
{
STREAM_BOUND_WARN (s, "put");
@@ -783,7 +855,7 @@ stream_recvfrom (struct stream *s, int fd, size_t size, int flags,
return -1;
}
- if ((nbytes = recvfrom (fd, s->data + s->endp, size,
+ if ((nbytes = recvfrom (fd, s->data + s->endp, size,
flags, from, fromlen)) >= 0)
{
s->endp += nbytes;
@@ -802,15 +874,15 @@ stream_recvfrom (struct stream *s, int fd, size_t size, int flags,
* Stream need not be empty.
*/
ssize_t
-stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags,
+stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags,
size_t size)
{
int nbytes;
struct iovec *iov;
-
+
STREAM_VERIFY_SANE(s);
- assert (msgh->msg_iovlen > 0);
-
+ assert (msgh->msg_iovlen > 0);
+
if (STREAM_WRITEABLE (s) < size)
{
STREAM_BOUND_WARN (s, "put");
@@ -818,19 +890,19 @@ stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags,
to hold the desired data! */
return -1;
}
-
+
iov = &(msgh->msg_iov[0]);
iov->iov_base = (s->data + s->endp);
iov->iov_len = size;
-
+
nbytes = recvmsg (fd, msgh, flags);
-
+
if (nbytes > 0)
s->endp += nbytes;
-
+
return nbytes;
}
-
+
/* Write data to buffer. */
size_t
stream_write (struct stream *s, const void *ptr, size_t size)
@@ -839,20 +911,20 @@ stream_write (struct stream *s, const void *ptr, size_t size)
CHECK_SIZE(s, size);
STREAM_VERIFY_SANE(s);
-
+
if (STREAM_WRITEABLE (s) < size)
{
STREAM_BOUND_WARN (s, "put");
return 0;
}
-
+
memcpy (s->data + s->endp, ptr, size);
s->endp += size;
return size;
}
-/* Return current read pointer.
+/* Return current read pointer.
* DEPRECATED!
* Use stream_get_pnt_to if you must, but decoding streams properly
* is preferred
@@ -882,26 +954,97 @@ stream_reset (struct stream *s)
s->getp = s->endp = 0;
}
+/* Number of bytes pending to be written */
+int
+stream_pending(struct stream* s)
+{
+ STREAM_VERIFY_SANE(s);
+
+ return s->endp - s->getp ;
+}
+
/* Write stream contens to the file discriptor. */
int
-stream_flush (struct stream *s, int fd)
+stream_flush (struct stream* s, int fd)
{
int nbytes;
-
+
STREAM_VERIFY_SANE(s);
-
+
nbytes = write (fd, s->data + s->getp, s->endp - s->getp);
-
+
return nbytes;
}
-
+
+/*------------------------------------------------------------------------------
+ * Try to write stream contents to the file descriptor -- assuming non-blocking.
+ *
+ * Loops if gets EINTR.
+ *
+ * If writes everything, resets the stream.
+ *
+ * If does not write everything, then would block.
+ *
+ * Returns: >= 0 number of bytes left to write
+ * -1 => some error (not including EINTR, EAGAIN or EWOULDBLOCK)
+ */
+int
+stream_flush_try(struct stream* s, int fd)
+{
+ int have ;
+ int ret ;
+
+ STREAM_VERIFY_SANE(s);
+
+ while ((have = (s->endp - s->getp)) != 0)
+ {
+ ret = write(fd, s->data + s->getp, have) ;
+ if (ret > 0)
+ s->getp += ret ;
+ else if (ret < 0)
+ {
+ ret = errno ;
+ if ((ret == EAGAIN) || (ret == EWOULDBLOCK))
+ return have ;
+ if (ret != EINTR)
+ return -1 ;
+ } ;
+ } ;
+
+ s->getp = s->endp = 0;
+
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Transfer contents of stream to given buffer and reset stream.
+ *
+ * Transfers *entire* stream buffer.
+ *
+ * Returns pointer to next byte in given buffer
+ */
+void*
+stream_transfer(void* p, struct stream* s, void* limit)
+{
+ size_t have = s->endp ;
+
+ STREAM_VERIFY_SANE(s);
+ assert((p + have) <= limit) ;
+
+ memcpy(p, s->data, have) ;
+
+ s->getp = s->endp = 0;
+
+ return p + have ;
+} ;
+
/* Stream first in first out queue. */
struct stream_fifo *
stream_fifo_new (void)
{
struct stream_fifo *new;
-
+
new = XCALLOC (MTYPE_STREAM_FIFO, sizeof (struct stream_fifo));
return new;
}
@@ -914,7 +1057,7 @@ stream_fifo_push (struct stream_fifo *fifo, struct stream *s)
fifo->tail->next = s;
else
fifo->head = s;
-
+
fifo->tail = s;
fifo->count++;
@@ -925,20 +1068,20 @@ struct stream *
stream_fifo_pop (struct stream_fifo *fifo)
{
struct stream *s;
-
- s = fifo->head;
+
+ s = fifo->head;
if (s)
- {
+ {
fifo->head = s->next;
if (fifo->head == NULL)
fifo->tail = NULL;
- }
- fifo->count--;
+ fifo->count--;
+ }
- return s;
+ return s;
}
/* Return first fifo entry. */
diff --git a/lib/stream.h b/lib/stream.h
index 3e4ba7b4..4c8a4fa9 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#ifndef _ZEBRA_STREAM_H
@@ -78,7 +78,7 @@
* The stream is empty from endp to size. Without adjusting getp, there are
* still endp-getp bytes of valid data to be read from the stream.
*
- * Methods are provided to get and put to/from the stream, as well as
+ * Methods are provided to get and put to/from the stream, as well as
* retrieve the values of the 3 markers and manipulate the getp marker.
*
* Note:
@@ -98,7 +98,7 @@ struct stream
/* Remainder is ***private*** to stream
* direct access is frowned upon!
- * Use the appropriate functions/macros
+ * Use the appropriate functions/macros
*/
size_t getp; /* next get position */
size_t endp; /* last valid data position */
@@ -127,7 +127,7 @@ struct stream_fifo
#define STREAM_DATA(S) ((S)->data)
#define STREAM_REMAIN(S) STREAM_WRITEABLE((S))
-/* Stream prototypes.
+/* Stream prototypes.
* For stream_{put,get}S, the S suffix mean:
*
* c: character (unsigned byte)
@@ -139,6 +139,7 @@ extern struct stream *stream_new (size_t);
extern void stream_free (struct stream *);
extern struct stream * stream_copy (struct stream *, struct stream *src);
extern struct stream *stream_dup (struct stream *);
+extern struct stream* stream_dup_pending(struct stream*) ;
extern size_t stream_resize (struct stream *, size_t);
extern size_t stream_get_getp (struct stream *);
extern size_t stream_get_endp (struct stream *);
@@ -177,7 +178,7 @@ extern u_int32_t stream_get_ipv4 (struct stream *);
#undef stream_read
#undef stream_write
-/* Deprecated: assumes blocking I/O. Will be removed.
+/* Deprecated: assumes blocking I/O. Will be removed.
Use stream_read_try instead. */
extern int stream_read (struct stream *, int, size_t);
@@ -185,6 +186,8 @@ extern int stream_read (struct stream *, int, size_t);
Will be removed. Use stream_read_try instead. */
extern int stream_read_unblock (struct stream *, int, size_t);
+extern int stream_read_nonblock (struct stream *s, int fd, size_t size) ;
+
/* Read up to size bytes into the stream.
Return code:
>0: number of bytes read
@@ -197,8 +200,8 @@ extern ssize_t stream_read_try(struct stream *s, int fd, size_t size);
extern ssize_t stream_recvmsg (struct stream *s, int fd, struct msghdr *,
int flags, size_t size);
-extern ssize_t stream_recvfrom (struct stream *s, int fd, size_t len,
- int flags, struct sockaddr *from,
+extern ssize_t stream_recvfrom (struct stream *s, int fd, size_t len,
+ int flags, struct sockaddr *from,
socklen_t *fromlen);
extern size_t stream_write (struct stream *, const void *, size_t);
@@ -207,6 +210,10 @@ extern void stream_reset (struct stream *);
extern int stream_flush (struct stream *, int);
extern int stream_empty (struct stream *); /* is the stream empty? */
+extern int stream_pending(struct stream* s) ;
+extern int stream_flush_try(struct stream* s, int fd) ;
+extern void* stream_transfer(void* p, struct stream* s, void* limit) ;
+
/* deprecated */
extern u_char *stream_pnt (struct stream *);
diff --git a/lib/symtab.h b/lib/symtab.h
index 7b4e002c..a8a6e622 100644
--- a/lib/symtab.h
+++ b/lib/symtab.h
@@ -23,6 +23,8 @@
#define _ZEBRA_SYMTAB_H
#include "vector.h"
+#include <stddef.h>
+#include <stdint.h>
/* Macro in case there are particular compiler issues. */
#ifndef Inline
@@ -94,9 +96,9 @@ struct symbol
symbol_ref ref_list ; /* list of symbol_ref references */
unsigned ref_count ; /* count of simple references */
- u_int32_t hash ; /* used in lookup and when extending bases. */
+ uint32_t hash ; /* used in lookup and when extending bases. */
- u_int16_t name_len ; /* see: symbol_get_name_len(sym) */
+ uint16_t name_len ; /* see: symbol_get_name_len(sym) */
char name[] ; /* see: symbol_get_name(sym) */
} ;
@@ -127,10 +129,10 @@ struct symbol_ref
/* Result of a hash function for a symbol name. */
struct symbol_hash
{
- u_int32_t hash ; /* the hash value ! */
- const void* name ; /* symbol name as byte vector */
- u_int16_t name_len ; /* length in chars for comparison purposes */
- u_int16_t name_copy_len ; /* number of chars to copy to store name. */
+ uint32_t hash ; /* the hash value ! */
+ const void* name ; /* symbol name as byte vector */
+ uint16_t name_len ; /* length in chars for comparison purposes */
+ uint16_t name_copy_len ; /* number of chars to copy to store name. */
} ;
/* Symbol Walk Iterator */
@@ -265,7 +267,7 @@ sym_ref_name(symbol_ref ref)
{
return symbol_get_name(sym_ref_symbol(ref)) ;
}
-Inline u_int16_t
+Inline uint16_t
sym_ref_name_len(symbol_ref ref)
{
return symbol_get_name_len(sym_ref_symbol(ref)) ;
diff --git a/lib/zebra.h b/lib/zebra.h
index 2dc84514..65aad161 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -115,7 +115,7 @@ typedef int socklen_t;
#ifdef __va_copy
#define va_copy(DST,SRC) __va_copy(DST,SRC)
#else
-/* Now we are desperate; this should work on many typical platforms.
+/* Now we are desperate; this should work on many typical platforms.
But this is slightly dangerous, because the standard does not require
va_copy to be a macro. */
#define va_copy(DST,SRC) memcpy(&(DST), &(SRC), sizeof(va_list))
@@ -260,7 +260,7 @@ typedef int socklen_t;
#endif /* BSDI_NRL */
/* Local includes: */
-#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL))
+#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL))
#define __attribute__(x)
#endif /* !__GNUC__ || VTYSH_EXTRACT_PL */
@@ -285,7 +285,7 @@ typedef int socklen_t;
-/*
+/*
* RFC 3542 defines several macros for using struct cmsghdr.
* Here, we define those that are not present
*/
@@ -330,7 +330,7 @@ struct in_pktinfo
};
#endif
-/*
+/*
* OSPF Fragmentation / fragmented writes
*
* ospfd can support writing fragmented packets, for cases where
@@ -350,13 +350,13 @@ struct in_pktinfo
#define WANT_OSPF_WRITE_FRAGMENT
#endif
-/*
+/*
* IP_HDRINCL / struct ip byte order
*
* Linux: network byte order
* *BSD: network, except for length and offset. (cf Stevens)
* SunOS: nominally as per BSD. but bug: network order on LE.
- * OpenBSD: network byte order, apart from older versions which are as per
+ * OpenBSD: network byte order, apart from older versions which are as per
* *BSD
*/
#if defined(__NetBSD__) || defined(__FreeBSD__) \
@@ -385,7 +385,7 @@ struct in_pktinfo
/* MAX / MIN are not commonly defined, but useful */
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#endif
+#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
@@ -451,7 +451,7 @@ struct in_pktinfo
extern const char *zebra_route_string(unsigned int route_type);
/* Map a route type to a char. For example, ZEBRA_ROUTE_RIPNG -> 'R'. */
extern char zebra_route_char(unsigned int route_type);
-/* Map a zserv command type to the same string,
+/* Map a zserv command type to the same string,
* e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */
/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/
extern int proto_name2num(const char *s);
@@ -496,17 +496,8 @@ extern const char *zserv_command_string (unsigned int command);
#define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */
#endif
-/* Address family numbers from RFC1700. */
-#define AFI_IP 1
-#define AFI_IP6 2
-#define AFI_MAX 3
-
-/* Subsequent Address Family Identifier. */
-#define SAFI_UNICAST 1
-#define SAFI_MULTICAST 2
-#define SAFI_UNICAST_MULTICAST 3
-#define SAFI_MPLS_VPN 4
-#define SAFI_MAX 5
+/* AFI/SAFI types and numbers. */
+#include "qafi_safi.h"
/* Filter direction. */
#define FILTER_IN 0
@@ -530,10 +521,6 @@ extern const char *zserv_command_string (unsigned int command);
#define SET_FLAG(V,F) (V) |= (F)
#define UNSET_FLAG(V,F) (V) &= ~(F)
-/* AFI and SAFI type. */
-typedef u_int16_t afi_t;
-typedef u_int8_t safi_t;
-
/* Zebra types. Used in Zserv message header. */
typedef u_int16_t zebra_size_t;
typedef u_int16_t zebra_command_t;