diff options
45 files changed, 8972 insertions, 2443 deletions
@@ -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; |