summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--HACKING.pending4
-rw-r--r--Makefile.am2
-rw-r--r--bgpd/Makefile.am29
-rw-r--r--bgpd/bgp.h1129
-rw-r--r--bgpd/bgp_advertise.c222
-rw-r--r--bgpd/bgp_advertise.h138
-rw-r--r--bgpd/bgp_aspath.c509
-rw-r--r--bgpd/bgp_aspath.h31
-rw-r--r--bgpd/bgp_attr.c968
-rw-r--r--bgpd/bgp_attr.h58
-rw-r--r--bgpd/bgp_clist.c223
-rw-r--r--bgpd/bgp_clist.h50
-rw-r--r--bgpd/bgp_common.c284
-rw-r--r--bgpd/bgp_common.h494
-rw-r--r--bgpd/bgp_community.c7
-rw-r--r--bgpd/bgp_community.h1
-rw-r--r--bgpd/bgp_connection.c1108
-rw-r--r--bgpd/bgp_connection.h372
-rw-r--r--bgpd/bgp_damp.c130
-rw-r--r--bgpd/bgp_debug.c308
-rw-r--r--bgpd/bgp_debug.h5
-rw-r--r--bgpd/bgp_dump.c64
-rw-r--r--bgpd/bgp_ecommunity.c2
-rw-r--r--bgpd/bgp_engine.c144
-rw-r--r--bgpd/bgp_engine.h144
-rw-r--r--bgpd/bgp_fsm.c3448
-rw-r--r--bgpd/bgp_fsm.h134
-rw-r--r--bgpd/bgp_main.c605
-rw-r--r--bgpd/bgp_mplsvpn.c42
-rw-r--r--bgpd/bgp_msg_read.c1768
-rw-r--r--bgpd/bgp_msg_read.h40
-rw-r--r--bgpd/bgp_msg_write.c871
-rw-r--r--bgpd/bgp_msg_write.h62
-rw-r--r--bgpd/bgp_network.c1442
-rw-r--r--bgpd/bgp_network.h49
-rw-r--r--bgpd/bgp_nexthop.c117
-rw-r--r--bgpd/bgp_notification.c320
-rw-r--r--bgpd/bgp_notification.h225
-rw-r--r--bgpd/bgp_open.c195
-rw-r--r--bgpd/bgp_open.h24
-rw-r--r--bgpd/bgp_open_state.c457
-rw-r--r--bgpd/bgp_open_state.h161
-rw-r--r--bgpd/bgp_packet.c918
-rw-r--r--bgpd/bgp_packet.h28
-rw-r--r--bgpd/bgp_peer.c1941
-rw-r--r--bgpd/bgp_peer.h495
-rw-r--r--bgpd/bgp_peer_index.c437
-rw-r--r--bgpd/bgp_peer_index.h88
-rw-r--r--bgpd/bgp_route.c3262
-rw-r--r--bgpd/bgp_route.h107
-rw-r--r--bgpd/bgp_route_refresh.c198
-rw-r--r--bgpd/bgp_route_refresh.h132
-rw-r--r--bgpd/bgp_routemap.c435
-rw-r--r--bgpd/bgp_session.c1069
-rw-r--r--bgpd/bgp_session.h378
-rw-r--r--bgpd/bgp_snmp.c48
-rw-r--r--bgpd/bgp_table.c35
-rw-r--r--bgpd/bgp_table.h10
-rw-r--r--bgpd/bgp_vty.c923
-rw-r--r--bgpd/bgp_zebra.c99
-rw-r--r--bgpd/bgpd.c2690
-rw-r--r--bgpd/bgpd.h557
-rw-r--r--configure-quagga6
-rwxr-xr-xconfigure.ac3
-rw-r--r--isisd/isis_dlpi.c6
-rw-r--r--isisd/isis_flags.c12
-rw-r--r--isisd/isis_lsp.c2
-rw-r--r--isisd/isis_misc.c6
-rw-r--r--isisd/isis_misc.h2
-rw-r--r--isisd/isis_routemap.c21
-rw-r--r--isisd/isis_zebra.c2
-rw-r--r--isisd/isisd.c4
-rw-r--r--isisd/topology/spgrid.c28
-rw-r--r--lib/Makefile.am14
-rw-r--r--lib/buffer.c51
-rw-r--r--lib/buffer.h17
-rw-r--r--lib/command.c2940
-rw-r--r--lib/command.h258
-rw-r--r--lib/command_execute.h79
-rw-r--r--lib/command_queue.c154
-rw-r--r--lib/command_queue.h31
-rw-r--r--lib/confirm.h28
-rw-r--r--lib/daemon.c6
-rw-r--r--lib/distribute.c53
-rw-r--r--lib/errno_names.c383
-rw-r--r--lib/errno_names.h31
-rw-r--r--lib/filter.c26
-rw-r--r--lib/getopt.c1016
-rw-r--r--lib/getopt.h66
-rw-r--r--lib/getopt1.c158
-rw-r--r--lib/heap.c517
-rw-r--r--lib/heap.h160
-rw-r--r--lib/if.c56
-rw-r--r--lib/if_rmap.c41
-rw-r--r--lib/keychain.c50
-rw-r--r--lib/keystroke.c1204
-rw-r--r--lib/keystroke.h194
-rw-r--r--lib/list_util.c80
-rw-r--r--lib/list_util.h729
-rw-r--r--lib/log.c844
-rw-r--r--lib/log.h107
-rw-r--r--lib/mem_tracker.c590
-rw-r--r--lib/memory.c583
-rw-r--r--lib/memory.h67
-rw-r--r--lib/memtypes.awk2
-rw-r--r--lib/memtypes.c52
-rw-r--r--lib/miyagi.h40
-rw-r--r--lib/mqueue.c1319
-rw-r--r--lib/mqueue.h393
-rw-r--r--lib/network.c167
-rw-r--r--lib/network.h6
-rw-r--r--lib/node_type.h81
-rw-r--r--lib/pid_output.c98
-rw-r--r--lib/plist.c2447
-rw-r--r--lib/plist.h48
-rw-r--r--lib/prefix.c241
-rw-r--r--lib/prefix.h86
-rw-r--r--lib/privs.c400
-rw-r--r--lib/privs.h4
-rw-r--r--lib/pthread_safe.c516
-rw-r--r--lib/pthread_safe.h46
-rw-r--r--lib/qafi_safi.h172
-rw-r--r--lib/qfstring.c1204
-rw-r--r--lib/qfstring.h163
-rw-r--r--lib/qiovec.c261
-rw-r--r--lib/qiovec.h99
-rw-r--r--lib/qlib_init.c98
-rw-r--r--lib/qlib_init.h40
-rw-r--r--lib/qpnexus.c327
-rw-r--r--lib/qpnexus.h158
-rw-r--r--lib/qpselect.c1414
-rw-r--r--lib/qpselect.h241
-rw-r--r--lib/qpthreads.c775
-rw-r--r--lib/qpthreads.h443
-rw-r--r--lib/qstring.c511
-rw-r--r--lib/qstring.h468
-rw-r--r--lib/qtime.c205
-rw-r--r--lib/qtime.h333
-rw-r--r--lib/qtimers.c450
-rw-r--r--lib/qtimers.h174
-rw-r--r--lib/routemap.c206
-rw-r--r--lib/routemap.h27
-rw-r--r--lib/sigevent.c109
-rw-r--r--lib/sigevent.h9
-rw-r--r--lib/smux.c115
-rw-r--r--lib/sockopt.c1222
-rw-r--r--lib/sockopt.h50
-rw-r--r--lib/sockunion.c1289
-rw-r--r--lib/sockunion.h103
-rw-r--r--lib/stream.c365
-rw-r--r--lib/stream.h24
-rw-r--r--lib/symtab.c1186
-rw-r--r--lib/symtab.h320
-rw-r--r--lib/thread.c779
-rw-r--r--lib/thread.h24
-rw-r--r--lib/uty.h239
-rw-r--r--lib/vector.c1289
-rw-r--r--lib/vector.h348
-rw-r--r--lib/vio_fifo.c1165
-rw-r--r--lib/vio_fifo.h205
-rw-r--r--lib/vio_lines.c380
-rw-r--r--lib/vio_lines.h91
-rw-r--r--lib/vty.c3732
-rw-r--r--lib/vty.h287
-rw-r--r--lib/vty_cli.c2705
-rw-r--r--lib/vty_cli.h49
-rw-r--r--lib/vty_io.c2742
-rw-r--r--lib/vty_io.h310
-rw-r--r--lib/workqueue.c295
-rw-r--r--lib/workqueue.h114
-rw-r--r--lib/zassert.h36
-rw-r--r--lib/zclient.c332
-rw-r--r--lib/zclient.h16
-rw-r--r--lib/zebra.h65
-rw-r--r--ospf6d/ospf6_abr.c8
-rw-r--r--ospf6d/ospf6_asbr.c6
-rw-r--r--ospf6d/ospf6_asbr.h11
-rw-r--r--ospf6d/ospf6_intra.c10
-rw-r--r--ospf6d/ospf6_main.c23
-rw-r--r--ospf6d/ospf6_network.c2
-rw-r--r--ospf6d/ospf6_route.c10
-rw-r--r--ospf6d/ospf6_route.h24
-rw-r--r--ospf6d/ospf6_snmp.c11
-rw-r--r--ospf6d/ospf6_spf.c18
-rw-r--r--ospf6d/ospf6_top.c30
-rw-r--r--ospf6d/ospf6d.c19
-rw-r--r--ospfd/ospf_abr.c7
-rw-r--r--ospfd/ospf_apiserver.c70
-rw-r--r--ospfd/ospf_apiserver.h4
-rw-r--r--ospfd/ospf_ase.c2
-rw-r--r--ospfd/ospf_network.c2
-rw-r--r--ospfd/ospf_opaque.c48
-rw-r--r--ospfd/ospf_opaque.h2
-rw-r--r--ospfd/ospf_packet.c9
-rw-r--r--ospfd/ospf_route.c6
-rw-r--r--ospfd/ospf_spf.c2
-rw-r--r--ospfd/ospf_te.c6
-rw-r--r--ospfd/ospf_vty.c6
-rw-r--r--ospfd/ospf_zebra.c13
-rw-r--r--ospfd/ospfd.c6
-rw-r--r--ripd/rip_interface.c2
-rw-r--r--ripd/rip_snmp.c2
-rw-r--r--ripd/rip_zebra.c1
-rw-r--r--ripd/ripd.c412
-rw-r--r--ripd/ripd.h13
-rw-r--r--ripngd/ripng_interface.c2
-rw-r--r--ripngd/ripngd.c185
-rw-r--r--ripngd/ripngd.h9
-rw-r--r--tests/Makefile.am13
-rw-r--r--tests/aspath_test.c269
-rw-r--r--tests/bgp_capability_test.c145
-rw-r--r--tests/bgp_mp_attr_test.c139
-rw-r--r--tests/ecommunity_test.c27
-rw-r--r--tests/heavy-thread.c26
-rw-r--r--tests/heavy-wq.c54
-rw-r--r--tests/heavy.c26
-rw-r--r--tests/main.c34
-rw-r--r--tests/test-checksum.c101
-rw-r--r--tests/test-list_util.c2112
-rw-r--r--tests/test-privs.c28
-rw-r--r--tests/test-sig.c10
-rw-r--r--tests/test-stream.c10
-rw-r--r--tests/test-symtab.c344
-rw-r--r--tests/test-vector.c1377
-rw-r--r--vtysh/vtysh.c141
-rw-r--r--vtysh/vtysh_config.c14
-rw-r--r--watchquagga/watchquagga.c18
-rw-r--r--zebra/Makefile.am3
-rw-r--r--zebra/if_ioctl.c3
-rw-r--r--zebra/if_ioctl_solaris.c5
-rw-r--r--zebra/if_method.h39
-rw-r--r--zebra/if_netlink.c5
-rw-r--r--zebra/if_proc.c32
-rw-r--r--zebra/if_sysctl.c13
-rw-r--r--zebra/interface.h19
-rw-r--r--zebra/ioctl.c46
-rw-r--r--zebra/ioctl.h10
-rw-r--r--zebra/ioctl_solaris.c5
-rw-r--r--zebra/ipforward.h11
-rw-r--r--zebra/ipforward_aix.c1
-rw-r--r--zebra/ipforward_ews.c1
-rw-r--r--zebra/irdp_main.c4
-rw-r--r--zebra/main.c2
-rw-r--r--zebra/misc_null.c19
-rw-r--r--zebra/mtu_kvm.c7
-rw-r--r--zebra/router-id.c8
-rw-r--r--zebra/rt.h9
-rw-r--r--zebra/rt_netlink.c44
-rw-r--r--zebra/rtread.h38
-rw-r--r--zebra/rtread_getmsg.c2
-rw-r--r--zebra/rtread_netlink.c9
-rw-r--r--zebra/rtread_sysctl.c2
-rw-r--r--zebra/test_main.c1
-rw-r--r--zebra/zebra_rib.c183
-rw-r--r--zebra/zserv.c4
-rw-r--r--zebra/zserv.h2
257 files changed, 64163 insertions, 16809 deletions
diff --git a/.gitignore b/.gitignore
index 516ed611..2f5d6b86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,5 +35,10 @@ build
.rebase-*
*~
*.loT
+.cdtconfigure.Build (GNU)
+.cproject
+.project
+.settings/
m4/*.m4
+commit.log
diff --git a/HACKING.pending b/HACKING.pending
index 5e0defd8..80c8cb45 100644
--- a/HACKING.pending
+++ b/HACKING.pending
@@ -28,7 +28,11 @@ the list have been stored.
Tom Henderson of Boeing has created a repository to work on
multi-topology routing support for OSPF. Work on this repository
+<<<<<<< HEAD
+takes place on the branch mtr, which has a branch poing of 0.99.17
+=======
takes place on the branch mtr, which has a branch point of 0.99.17
+>>>>>>> 538cb284864c17de66152a5236db4cd80e3e7639
* posted patches
diff --git a/Makefile.am b/Makefile.am
index 007758f2..5362623d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@
SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ \
@ISISD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
- redhat @SOLARIS@
+ redhat tests @SOLARIS@
DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d \
isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index 1b17d386..ef5e735d 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -11,18 +11,27 @@ noinst_LIBRARIES = libbgp.a
sbin_PROGRAMS = bgpd
libbgp_a_SOURCES = \
- bgpd.c bgp_fsm.c bgp_aspath.c bgp_community.c bgp_attr.c \
- 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_advertise.c bgp_aspath.c bgp_attr.c \
+ bgp_clist.c bgp_common.c bgp_community.c bgp_connection.c \
+ bgp_damp.c bgpd.c bgp_debug.c bgp_dump.c \
+ bgp_ecommunity.c bgp_engine.c bgp_filter.c bgp_fsm.c \
+ bgp_main.c bgp_mplsvpn.c bgp_msg_read.c bgp_msg_write.c \
+ bgp_network.c bgp_nexthop.c bgp_notification.c bgp_open.c \
+ bgp_open_state.c bgp_packet.c bgp_peer.c bgp_peer_index.c \
+ bgp_regex.c bgp_route.c bgp_routemap.c bgp_route_refresh.c \
+ bgp_session.c bgp_snmp.c bgp_table.c bgp_vty.c \
+ bgp_zebra.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_advertise.h bgp_aspath.h bgp_attr.h bgp_clist.h \
+ bgp_common.h bgp_community.h bgp_connection.h bgp_damp.h \
+ bgp_debug.h bgpd.h bgp_dump.h bgp_ecommunity.h \
+ bgp_engine.h bgp_filter.h bgp_fsm.h bgp.h \
+ bgp_mplsvpn.h bgp_msg_read.h bgp_msg_write.h bgp_network.h \
+ bgp_nexthop.h bgp_notification.h bgp_open.h bgp_open_state.h \
+ bgp_packet.h bgp_peer.h bgp_peer_index.h bgp_regex.h \
+ bgp_route.h bgp_route_refresh.h bgp_session.h bgp_snmp.h \
+ bgp_table.h bgp_vty.h bgp_zebra.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..6019b33c
--- /dev/null
+++ b/bgpd/bgp.h
@@ -0,0 +1,1129 @@
+/* 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.
+ */
+
+#define _GMCH_BGP_H "19-Dec-2009"
+
+#include <stdint.h>
+#include "confirm.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
+ * RFC5082 The Generalized TTL Security Mechanism (GTSM)
+ * -- obsoletes RFC3682
+ * 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
+ * RFC3882 Configuring BGP to Block Denial-of-Service Attack
+ * RFC3765 NOPEER Community
+ * RFC3682 ...see RFC5082
+ * 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 */
+
+typedef enum asn_type asn_type_t ;
+enum asn_type
+{
+ AS2 = 2,
+ AS4 = 4
+} ;
+
+/* Other stuff.... */
+
+typedef uint32_t bgp_id_t ; /* actually an IPv4 IP Address */
+
+typedef bgp_id_t bgp_id_ht ; /* in host order */
+typedef bgp_id_t bgp_id_nt ; /* in network order */
+
+/* Size of BGP packets or thing in such */
+typedef uint16_t bgp_size_t;
+
+VALUE(BGP_NEXT_HOP_MAX_L = 32) ; /* maximum expected Next Hop address length */
+
+/*==============================================================================
+ * BGP Message Structure
+ */
+
+VALUE(BGP_MSG_MAX_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_MARKER_L = sizeof(BGP_MH_MARKER_T)) ;
+VALUE(BGP_MH_HEAD_L = /* message header length */
+ BGP_MH_MARKER_L
+ + sizeof(BGP_MH_LEN_T)
+ + sizeof(BGP_MH_TYPE_T) ) ;
+CONFIRM(BGP_MH_HEAD_L == 19) ; /* well known value ! */
+
+VALUE(BGP_MSG_BODY_MAX_L = BGP_MSG_MAX_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_CAPABILITY = 6, /* draft-ietf-idr-dynamic-cap-10 */
+
+ BGP_MT_MAX = 6, /* max known message type */
+
+ BGP_MT_ROUTE_REFRESH_pre = 128 /* pre RFC2918 (Sep-2000) */
+} ;
+
+/* 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 = /* min len of an OPM Optional Param */
+ sizeof(BGP_OPM_P_TYPE_T)
+ + sizeof(BGP_OPM_P_LEN_T)) ;
+VALUE(BGP_OPM_P_MAX_L = 255); /* max len of an OPM Optional Param */
+
+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 */
+VALUE(BGP_CAP_MAX_L = 255) ; /* max len of a capability announcement */
+CONFIRM(sizeof(BGP_CAP_LEN_T) == 1) ;
+
+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 */
+
+ BGP_CAN_DYNAMIC_CAP_old = 66, /* Dynamic Capability (draft 02) [Chen]
+ 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) ! */
+
+ BGP_CAN_R_REFRESH_pre = 128, /* pre-RFC value */
+ BGP_CAN_ORF_pre = 130, /* pre-RFC value */
+} ;
+
+/* 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_DYN_CAP = 7, /* Dynamic Capability
+ draft-ietf-idr-dynamic-cap-10.txt */
+
+ 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 */
+} ;
+
+enum BGP_DYN_CAP /* BGP_NOMC_DYN_CAP subcodes
+ draft-ietf-idr-dynamic-cap-10.txt */
+{
+ BGP_NOMS_D_UNKN_SEQ = 1, /* Unknown Sequence Number MUST */
+ BGP_NOMS_D_INV_LEN = 2, /* Invalid Capability Length MUST */
+ BGP_NOMS_D_MALFORM = 3, /* Malformed Capability Length MUST */
+ BGP_NOMS_D_UNSUP = 4, /* Unsupported Capability MUST */
+
+ BGP_NOMS_D_MAX = 4 /* 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_MIN_L = /* Route Refresh length */
+ BGP_MH_HEAD_L
+ + sizeof(BGP_RRM_AFI_T)
+ + sizeof(BGP_RRM_RES_T)
+ + sizeof(BGP_RRM_SAFI_T) ) ;
+CONFIRM(BGP_RRM_MIN_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 -- *one* or more */
+} ;
+
+enum /* values for the BGP_RRM_ORF_WHEN byte */
+{
+ BGP_ORF_WTR_IMMEDIATE = 1, /* when-to-refresh == immediately */
+ BGP_ORF_WTR_DEFER = 2 /* when-to-refresh == defer */
+} ;
+
+/* ORFS come in collections -- one or more....................................*/
+
+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 */
+
+VALUE(BGP_ORF_E_COM_L = sizeof(BGP_ORF_E_ACTION_T)) ;
+
+/* 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,
+
+ BGP_ORF_T_PREFIX_pre = 128 /* pre RFC value */
+} ;
+
+/* Known BGP_ORF_E_ACTION bits................................................*/
+
+enum {
+ BGP_ORF_EA_MASK = 0x3 << 6, /* mask to extract Action */
+
+ BGP_ORF_EA_ADD = 0 << 6, /* Action: ADD */
+ BGP_ORF_EA_REMOVE = 1 << 6, /* Action: REMOVE */
+ BGP_ORF_EA_RM_ALL = 2 << 6, /* Action: REMOVE-ALL */
+
+ BGP_ORF_EA_PERMIT = 0 << 5, /* Match: PERMIT */
+ BGP_ORF_EA_DENY = 1 << 5, /* Match: DENY */
+} ;
+
+/* 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 ! */
+
+VALUE(BGP_ORF_E_P_MIN_L = BGP_ORF_E_COM_L
+ + sizeof(BGP_ORF_E_P_SEQ_T)
+ + sizeof(BGP_ORF_E_P_MIN_T)
+ + sizeof(BGP_ORF_E_P_MAX_T)
+ + sizeof(BGP_ORF_E_P_LEN_T) ) ;
+
+/* Dynamic Capability Message (type = BGP_MT_CAPABILITY) -----------------------
+ *
+ * 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
+ */
+
+/*==============================================================================
+ * 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 --------------------------
+ *
+ * The capability value is *one* or more of the following entries:
+ */
+typedef U16 BGP_CAP_ORFE_AFI_T ; /* Address Family Identifier */
+typedef U8 BGP_CAP_ORFE_RES_T ; /* Reserved: 0 */
+typedef U8 BGP_CAP_ORFE_SAFI_T ; /* Subsequent Address Family */
+typedef U8 BGP_CAP_ORFE_COUNT_T ; /* number of ORF Types supported */
+typedef UBX BGP_CAP_ORFE_TYPES_T ; /* variable -- 2 byte entries as below */
+
+VALUE(BGP_CAP_ORFE_MIN_L = sizeof(BGP_CAP_ORFE_AFI_T)
+ + sizeof(BGP_CAP_ORFE_RES_T)
+ + sizeof(BGP_CAP_ORFE_SAFI_T)
+ + sizeof(BGP_CAP_ORFE_COUNT_T) ) ;
+
+enum /* order */
+{
+ BGP_CAP_ORFE_AFI,
+ BGP_CAP_ORFE_RES,
+ BGP_CAP_ORFE_SAFI,
+ BGP_CAP_ORFE_COUNT,
+ BGP_CAP_ORFE_TYPES
+} ;
+
+/* Entries saying what ORF Types can be supported and how.......................
+ *
+ * There are BGP_CAP_ORF_COUNT of these.
+ * A zero count is pointless, but not explicitly forbidden in RFC5291 !
+ */
+typedef U8 BGP_CAP_ORFT_TYPE_T ; /* the ORF Type supported */
+typedef U8 BGP_CAP_ORFT_MODE_T ; /* what can do with ORF Type */
+
+VALUE(BGP_CAP_ORFT_L = sizeof(BGP_CAP_ORFT_TYPE_T)
+ + sizeof(BGP_CAP_ORFT_MODE_T)) ;
+ /* length of ORF capability Type entry */
+enum /* order */
+{
+ BGP_CAP_ORFT_TYPE,
+ BGP_CAP_ORFT_MODE,
+} ;
+
+/* Values for the BGP_CAP_ORFT_TYPE field */
+enum
+{
+ BGP_CAP_ORFT_T_PFIX = 64, /* Address Prefix ORF */
+ BGP_CAP_ORFT_T_PFIX_pre = 128, /* Address Prefix ORF, pre-RFC */
+} ;
+
+/* Values for the BGP_CAP_ORFT_MODE field */
+enum
+{
+ BGP_CAP_ORFT_M_RECV = 1, /* willing to receive */
+ BGP_CAP_ORFT_M_SEND = 2, /* would like to send */
+ BGP_CAP_ORFT_M_BOTH = 3 /* may be 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 */
+ /* may be none at all */
+
+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)) ;
+
+/* Dynamic Capability -- BGP_CAN_DYNAMIC_CAP -- draft-10----------------------*/
+/* draft-ietf-idr-dynamic-cap-10 15-Jan-2010 */
+
+VALUE(BGP_CAP_DYN_L = 0) ;
+
+/*==============================================================================
+ * 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_AS2_RES2 = 65535, /* Last AS2 value is reserved (0xFFFF) */
+ BGP_AS2_MAX = 65535, /* Last of the Mohicans (0xFFFF) */
+
+ BGP_ASN_RES2_S = 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_AS4_KNOWN_S = AS4(2,0), /* Start of known AS4 space (131072) */
+ BGP_AS4_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_advertise.c b/bgpd/bgp_advertise.c
index 666218fa..c03546d7 100644
--- a/bgpd/bgp_advertise.c
+++ b/bgpd/bgp_advertise.c
@@ -35,7 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_fsm.h"
#include "bgpd/bgp_mplsvpn.h"
-
+
/* BGP advertise attribute is used for pack same attribute update into
one packet. To do that we maintain attribute hash in struct
peer. */
@@ -79,14 +79,14 @@ baa_hash_cmp (const void *p1, const void *p2)
return attrhash_cmp (baa1->attr, baa2->attr);
}
-
+
/* BGP update and withdraw information is stored in BGP advertise
structure. This structure is referred from BGP adjacency
information. */
static struct bgp_advertise *
bgp_advertise_new (void)
{
- return (struct bgp_advertise *)
+ return (struct bgp_advertise *)
XCALLOC (MTYPE_BGP_ADVERTISE, sizeof (struct bgp_advertise));
}
@@ -102,9 +102,9 @@ static void
bgp_advertise_add (struct bgp_advertise_attr *baa,
struct bgp_advertise *adv)
{
- adv->next = baa->adv;
+ adv->adv_next = baa->adv;
if (baa->adv)
- baa->adv->prev = adv;
+ baa->adv->adv_prev = adv;
baa->adv = adv;
}
@@ -112,12 +112,12 @@ static void
bgp_advertise_delete (struct bgp_advertise_attr *baa,
struct bgp_advertise *adv)
{
- if (adv->next)
- adv->next->prev = adv->prev;
- if (adv->prev)
- adv->prev->next = adv->next;
+ if (adv->adv_next)
+ adv->adv_next->adv_prev = adv->adv_prev;
+ if (adv->adv_prev)
+ adv->adv_prev->adv_next = adv->adv_next;
else
- baa->adv = adv->next;
+ baa->adv = adv->adv_next;
}
static struct bgp_advertise_attr *
@@ -151,14 +151,8 @@ bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa)
baa_free (baa);
}
}
-
+
/* BGP adjacency keeps minimal advertisement information. */
-static void
-bgp_adj_out_free (struct bgp_adj_out *adj)
-{
- peer_unlock (adj->peer); /* adj_out peer reference */
- XFREE (MTYPE_BGP_ADJ_OUT, adj);
-}
int
bgp_adj_out_lookup (struct peer *peer, struct prefix *p,
@@ -166,14 +160,14 @@ bgp_adj_out_lookup (struct peer *peer, struct prefix *p,
{
struct bgp_adj_out *adj;
- for (adj = rn->adj_out; adj; adj = adj->next)
+ for (adj = rn->adj_out; adj; adj = adj->adj_next)
if (adj->peer == peer)
break;
if (! adj)
return 0;
- return (adj->adv
+ return (adj->adv
? (adj->adv->baa ? 1 : 0)
: (adj->attr ? 1 : 0));
}
@@ -203,7 +197,7 @@ bgp_advertise_clean (struct peer *peer, struct bgp_adj_out *adj,
}
/* Unlink myself from advertisement FIFO. */
- FIFO_DEL (adv);
+ bgp_advertise_fifo_del(adv);
/* Free memory. */
bgp_advertise_free (adj->adv);
@@ -217,43 +211,57 @@ bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p,
struct attr *attr, afi_t afi, safi_t safi,
struct bgp_info *binfo)
{
- struct bgp_adj_out *adj = NULL;
+ struct bgp_adj_out* adj = NULL;
+ struct bgp_adj_out** adj_out_head ;
struct bgp_advertise *adv;
if (DISABLE_BGP_ANNOUNCE)
return;
+ assert(rn != NULL) ;
+ assert((afi == rn->table->afi) && (safi == rn->table->safi)) ;
+
/* Look for adjacency information. */
- if (rn)
- {
- for (adj = rn->adj_out; adj; adj = adj->next)
- if (adj->peer == peer)
+ for (adj = rn->adj_out; adj; adj = adj->adj_next)
+ if (adj->peer == peer)
break;
- }
- if (! adj)
+ if (adj == NULL)
{
adj = XCALLOC (MTYPE_BGP_ADJ_OUT, sizeof (struct bgp_adj_out));
- adj->peer = peer_lock (peer); /* adj_out peer reference */
-
- if (rn)
- {
- BGP_ADJ_OUT_ADD (rn, adj);
- bgp_lock_node (rn);
- }
- }
+
+ /* Add to list of adj_out stuff for the peer */
+ adj->peer = bgp_peer_lock (peer);
+
+ adj_out_head = &(peer->adj_out_head[afi][safi]) ;
+
+ adj->route_next = *adj_out_head ;
+ adj->route_prev = NULL ;
+ if (*adj_out_head != NULL)
+ (*adj_out_head)->route_prev = adj ;
+ *adj_out_head = adj ;
+
+ /* Add to list of adj out stuff for the bgp_node */
+ adj->rn = bgp_lock_node (rn);
+
+ adj->adj_next = rn->adj_out ;
+ adj->adj_prev = NULL ;
+ if (rn->adj_out != NULL)
+ rn->adj_out->adj_prev = adj ;
+ rn->adj_out = adj ;
+ } ;
if (adj->adv)
bgp_advertise_clean (peer, adj, afi, safi);
-
+
adj->adv = bgp_advertise_new ();
adv = adj->adv;
adv->rn = rn;
-
+
assert (adv->binfo == NULL);
adv->binfo = bgp_info_lock (binfo); /* bgp_info adj_out reference */
-
+
if (attr)
adv->baa = bgp_advertise_intern (peer->hash[afi][safi], attr);
else
@@ -263,11 +271,11 @@ bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p,
/* Add new advertisement to advertisement attribute list. */
bgp_advertise_add (adv->baa, adv);
- FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo);
+ bgp_advertise_fifo_add(&peer->sync[afi][safi]->update, adv);
}
void
-bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p,
+bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p,
afi_t afi, safi_t safi)
{
struct bgp_adj_out *adj;
@@ -277,63 +285,80 @@ bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p,
return;
/* Lookup existing adjacency, if it is not there return immediately. */
- for (adj = rn->adj_out; adj; adj = adj->next)
+ for (adj = rn->adj_out; adj; adj = adj->adj_next)
if (adj->peer == peer)
break;
- if (! adj)
+ if (adj == NULL)
return;
- /* Clearn up previous advertisement. */
+ assert(rn == adj->rn) ;
+
+ /* Clear up previous advertisement. */
if (adj->adv)
bgp_advertise_clean (peer, adj, afi, safi);
if (adj->attr)
{
- /* We need advertisement structure. */
+ /* We need advertisement structure. */
adj->adv = bgp_advertise_new ();
adv = adj->adv;
adv->rn = rn;
adv->adj = adj;
- /* Add to synchronization entry for withdraw announcement. */
- FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo);
+ /* Add to synchronization entry for withdraw announcement */
+ bgp_advertise_fifo_add(&peer->sync[afi][safi]->withdraw, adv);
/* Schedule packet write. */
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ bgp_write(peer, NULL) ;
}
else
- {
- /* Remove myself from adjacency. */
- BGP_ADJ_OUT_DEL (rn, adj);
-
- /* Free allocated information. */
- bgp_adj_out_free (adj);
-
- bgp_unlock_node (rn);
- }
+ bgp_adj_out_remove(rn, adj, peer, afi, safi) ;
}
void
-bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj,
+bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj,
struct peer *peer, afi_t afi, safi_t safi)
{
+ assert((rn == adj->rn) && (peer == adj->peer)) ;
+
if (adj->attr)
bgp_attr_unintern (&adj->attr);
if (adj->adv)
bgp_advertise_clean (peer, adj, afi, safi);
- BGP_ADJ_OUT_DEL (rn, adj);
- bgp_adj_out_free (adj);
+ /* Unhook from peer */
+ if (adj->route_next != NULL)
+ adj->route_next->route_prev = adj->route_prev ;
+ if (adj->route_prev != NULL)
+ adj->route_prev->route_next = adj->route_next ;
+ else
+ peer->adj_out_head[afi][safi] = adj->route_next ;
+
+ bgp_peer_unlock (peer);
+
+ /* Unhook from bgp_node */
+ if (adj->adj_next)
+ adj->adj_next->adj_prev = adj->adj_prev;
+ if (adj->adj_prev)
+ adj->adj_prev->adj_next = adj->adj_next;
+ else
+ rn->adj_out = adj->adj_next;
+
+ bgp_unlock_node (rn);
+
+ /* now can release memory. */
+ XFREE (MTYPE_BGP_ADJ_OUT, adj);
}
-
+
void
bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr)
{
struct bgp_adj_in *adj;
+ struct bgp_adj_in** adj_in_head ;
- for (adj = rn->adj_in; adj; adj = adj->next)
+ for (adj = rn->adj_in; adj; adj = adj->adj_next)
{
if (adj->peer == peer)
{
@@ -345,19 +370,69 @@ bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr)
return;
}
}
+
+ /* Need to create a brand new bgp_adj_in */
+
adj = XCALLOC (MTYPE_BGP_ADJ_IN, sizeof (struct bgp_adj_in));
- adj->peer = peer_lock (peer); /* adj_in peer reference */
+
+ /* Set the interned attributes */
adj->attr = bgp_attr_intern (attr);
- BGP_ADJ_IN_ADD (rn, adj);
- bgp_lock_node (rn);
+
+ /* Add to list of adj in stuff for the peer */
+ adj->peer = bgp_peer_lock (peer);
+
+ adj_in_head = &(peer->adj_in_head[rn->table->afi][rn->table->safi]) ;
+
+ adj->route_next = *adj_in_head ;
+ adj->route_prev = NULL ;
+ if (*adj_in_head != NULL)
+ (*adj_in_head)->route_prev = adj ;
+ *adj_in_head = adj ;
+
+ /* Add to list of adj in stuff for the bgp_node */
+ adj->rn = bgp_lock_node (rn);
+
+ adj->adj_next = rn->adj_in ;
+ adj->adj_prev = NULL ;
+ if (rn->adj_in != NULL)
+ rn->adj_in->adj_prev = adj ;
+ rn->adj_in = adj ;
}
void
bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai)
{
+ bgp_peer peer = bai->peer ;
+ struct bgp_adj_in** adj_in_head ;
+
+ adj_in_head = &(peer->adj_in_head[rn->table->afi][rn->table->safi]) ;
+
+ assert(rn == bai->rn) ;
+
+ /* Done with this copy of attributes */
bgp_attr_unintern (&bai->attr);
- BGP_ADJ_IN_DEL (rn, bai);
- peer_unlock (bai->peer); /* adj_in peer reference */
+
+ /* Unhook from peer */
+ if (bai->route_next != NULL)
+ bai->route_next->route_prev = bai->route_prev ;
+ if (bai->route_prev != NULL)
+ bai->route_prev->route_next = bai->route_next ;
+ else
+ *adj_in_head = bai->route_next ;
+
+ bgp_peer_unlock (peer);
+
+ /* Unhook from bgp_node */
+ if (bai->adj_next)
+ bai->adj_next->adj_prev = bai->adj_prev;
+ if (bai->adj_prev)
+ bai->adj_prev->adj_next = bai->adj_next;
+ else
+ rn->adj_in = bai->adj_next;
+
+ bgp_unlock_node (rn);
+
+ /* now can release memory. */
XFREE (MTYPE_BGP_ADJ_IN, bai);
}
@@ -366,7 +441,7 @@ bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer)
{
struct bgp_adj_in *adj;
- for (adj = rn->adj_in; adj; adj = adj->next)
+ for (adj = rn->adj_in; adj; adj = adj->adj_next)
if (adj->peer == peer)
break;
@@ -374,9 +449,8 @@ bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer)
return;
bgp_adj_in_remove (rn, adj);
- bgp_unlock_node (rn);
}
-
+
void
bgp_sync_init (struct peer *peer)
{
@@ -387,11 +461,11 @@ bgp_sync_init (struct peer *peer)
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
- sync = XCALLOC (MTYPE_BGP_SYNCHRONISE,
+ sync = XCALLOC (MTYPE_BGP_SYNCHRONISE,
sizeof (struct bgp_synchronize));
- FIFO_INIT (&sync->update);
- FIFO_INIT (&sync->withdraw);
- FIFO_INIT (&sync->withdraw_low);
+ bgp_advertise_fifo_init(&sync->update);
+ bgp_advertise_fifo_init(&sync->withdraw);
+ bgp_advertise_fifo_init(&sync->withdraw_low);
peer->sync[afi][safi] = sync;
peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp);
}
@@ -409,7 +483,7 @@ bgp_sync_delete (struct peer *peer)
if (peer->sync[afi][safi])
XFREE (MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]);
peer->sync[afi][safi] = NULL;
-
+
if (peer->hash[afi][safi])
hash_free (peer->hash[afi][safi]);
peer->hash[afi][safi] = NULL;
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
index 4ebde907..ca92238a 100644
--- a/bgpd/bgp_advertise.h
+++ b/bgpd/bgp_advertise.h
@@ -21,11 +21,26 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ADVERTISE_H
#define _QUAGGA_BGP_ADVERTISE_H
+#ifndef Inline
+#define Inline static inline
+#endif
+
/* BGP advertise FIFO. */
+typedef struct bgp_advertise* bgp_advertise ;
+
+typedef struct bgp_advertise_fifo_base* bgp_advertise_fifo_base ;
+
struct bgp_advertise_fifo
{
- struct bgp_advertise *next;
- struct bgp_advertise *prev;
+ bgp_advertise_fifo_base base ;
+ bgp_advertise next;
+ bgp_advertise prev;
+};
+
+struct bgp_advertise_fifo_base
+{
+ bgp_advertise head;
+ bgp_advertise tail;
};
/* BGP advertise attribute. */
@@ -47,8 +62,8 @@ struct bgp_advertise
struct bgp_advertise_fifo fifo;
/* Link list for same attribute advertise. */
- struct bgp_advertise *next;
- struct bgp_advertise *prev;
+ bgp_advertise adv_next;
+ bgp_advertise adv_prev;
/* Prefix information. */
struct bgp_node *rn;
@@ -66,60 +81,129 @@ struct bgp_advertise
/* BGP adjacency out. */
struct bgp_adj_out
{
- /* Lined list pointer. */
- struct bgp_adj_out *next;
- struct bgp_adj_out *prev;
+ /* Linked list pointer. */
+ struct bgp_node* rn ;
+ struct bgp_adj_out *adj_next;
+ struct bgp_adj_out *adj_prev;
- /* Advertised peer. */
+ /* Advertised peer. */
struct peer *peer;
+ struct bgp_adj_out* route_next ;
+ struct bgp_adj_out* route_prev ;
- /* Advertised attribute. */
+ /* Advertised attribute. */
struct attr *attr;
- /* Advertisement information. */
+ /* Advertisement information. */
struct bgp_advertise *adv;
};
/* BGP adjacency in. */
struct bgp_adj_in
{
- /* Linked list pointer. */
- struct bgp_adj_in *next;
- struct bgp_adj_in *prev;
+ /* Linked list pointer. */
+ struct bgp_node* rn ;
+ struct bgp_adj_in *adj_next;
+ struct bgp_adj_in *adj_prev;
- /* Received peer. */
+ /* Received peer. */
struct peer *peer;
+ struct bgp_adj_in* route_next ;
+ struct bgp_adj_in* route_prev ;
- /* Received attribute. */
+ /* Received attribute. */
struct attr *attr;
};
/* BGP advertisement list. */
struct bgp_synchronize
{
- struct bgp_advertise_fifo update;
- struct bgp_advertise_fifo withdraw;
- struct bgp_advertise_fifo withdraw_low;
+ struct bgp_advertise_fifo_base update;
+ struct bgp_advertise_fifo_base withdraw;
+ struct bgp_advertise_fifo_base withdraw_low;
};
+/* bgp_advertise_fifo handling
+ *
+ * Rules: base->head == NULL => empty
+ * base->tail -- only valid if base->head != NULL
+ *
+ * adv->fifo.base == NULL => not on fifo
+ *
+ * adv->fifo.next == NULL => last (if fifo.base != NULL)
+ * adv->fifo.prev == NULL => first (if fifo.base != NULL)
+ */
+Inline void
+bgp_advertise_fifo_init(bgp_advertise_fifo_base base)
+{
+ base->head = NULL ;
+} ;
+
+Inline bgp_advertise
+bgp_advertise_fifo_head(bgp_advertise_fifo_base base)
+{
+ return base->head ;
+} ;
+
+Inline void
+bgp_advertise_fifo_add(bgp_advertise_fifo_base base, bgp_advertise adv)
+{
+ adv->fifo.next = NULL ;
+ adv->fifo.base = base ;
+
+ if (base->head == NULL)
+ {
+ adv->fifo.prev = NULL ;
+ base->head = adv ;
+ }
+ else
+ {
+ adv->fifo.prev = base->tail ;
+ base->tail->fifo.next = adv ;
+ } ;
+
+ base->tail = adv ;
+} ;
+
+Inline void
+bgp_advertise_fifo_del(bgp_advertise adv)
+{
+ bgp_advertise_fifo_base base = adv->fifo.base ;
+
+ if (base != NULL)
+ {
+ if (adv->fifo.next == NULL)
+ base->tail = adv->fifo.prev ;
+ else
+ adv->fifo.next->fifo.prev = adv->fifo.prev ;
+
+ if (adv->fifo.prev == NULL)
+ base->head = adv->fifo.next ;
+ else
+ adv->fifo.prev->fifo.next = adv->fifo.next ;
+
+ adv->fifo.base = NULL ;
+ } ;
+ } ;
+
/* BGP adjacency linked list. */
#define BGP_INFO_ADD(N,A,TYPE) \
do { \
- (A)->prev = NULL; \
- (A)->next = (N)->TYPE; \
+ (A)->adj_prev = NULL; \
+ (A)->adj_next = (N)->TYPE; \
if ((N)->TYPE) \
- (N)->TYPE->prev = (A); \
+ (N)->TYPE->adj_prev = (A); \
(N)->TYPE = (A); \
} while (0)
#define BGP_INFO_DEL(N,A,TYPE) \
do { \
- if ((A)->next) \
- (A)->next->prev = (A)->prev; \
- if ((A)->prev) \
- (A)->prev->next = (A)->next; \
+ if ((A)->adj_next) \
+ (A)->adj_next->adj_prev = (A)->adj_prev; \
+ if ((A)->adj_prev) \
+ (A)->adj_prev->adj_next = (A)->adj_next; \
else \
- (N)->TYPE = (A)->next; \
+ (N)->TYPE = (A)->adj_next; \
} while (0)
#define BGP_ADJ_IN_ADD(N,A) BGP_INFO_ADD(N,A,adj_in)
@@ -132,7 +216,7 @@ extern void bgp_adj_out_set (struct bgp_node *, struct peer *, struct prefix *,
struct attr *, afi_t, safi_t, struct bgp_info *);
extern void bgp_adj_out_unset (struct bgp_node *, struct peer *, struct prefix *,
afi_t, safi_t);
-extern void bgp_adj_out_remove (struct bgp_node *, struct bgp_adj_out *,
+extern void bgp_adj_out_remove (struct bgp_node *, struct bgp_adj_out *,
struct peer *, afi_t, safi_t);
extern int bgp_adj_out_lookup (struct peer *, struct prefix *, afi_t, safi_t,
struct bgp_node *);
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index ba4f5b48..dc6ed166 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -34,9 +34,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_attr.h"
-
+
/* Attr. Flags and Attr. Type Code. */
-#define AS_HEADER_SIZE 2
+#define AS_HEADER_SIZE 2
/* Now FOUR octets are used for AS value. */
#define AS_VALUE_SIZE sizeof (as_t)
@@ -44,7 +44,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#define AS16_VALUE_SIZE sizeof (as16_t)
/* Maximum protocol segment length value */
-#define AS_SEGMENT_MAX 255
+#define AS_SEGMENT_MAX 255
+#define AS_SEGMENT_MIN 1
/* The following length and size macros relate specifically to Quagga's
* internal representation of AS-Segments, not per se to the on-wire
@@ -76,7 +77,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \
&& ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) )
-/* As segment header - the on-wire representation
+/* As segment header - the on-wire representation
* NOT the internal representation!
*/
struct assegment_header
@@ -90,7 +91,7 @@ static struct hash *ashash;
/* Stream for SNMP. See aspath_snmp_pathseg */
static struct stream *snmp_stream;
-
+
static inline as_t *
assegment_data_new (int num)
{
@@ -112,15 +113,15 @@ static struct assegment *
assegment_new (u_char type, u_short length)
{
struct assegment *new;
-
+
new = XCALLOC (MTYPE_AS_SEG, sizeof (struct assegment));
-
+
if (length)
new->as = assegment_data_new (length);
-
+
new->length = length;
new->type = type;
-
+
return new;
}
@@ -129,12 +130,12 @@ assegment_free (struct assegment *seg)
{
if (!seg)
return;
-
+
if (seg->as)
XFREE (MTYPE_AS_SEG_DATA, seg->as);
memset (seg, 0xfe, sizeof(struct assegment));
XFREE (MTYPE_AS_SEG, seg);
-
+
return;
}
@@ -143,7 +144,7 @@ static void
assegment_free_all (struct assegment *seg)
{
struct assegment *prev;
-
+
while (seg)
{
prev = seg;
@@ -157,10 +158,10 @@ static struct assegment *
assegment_dup (struct assegment *seg)
{
struct assegment *new;
-
+
new = assegment_new (seg->type, seg->length);
memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) );
-
+
return new;
}
@@ -170,7 +171,7 @@ assegment_dup_all (struct assegment *seg)
{
struct assegment *new = NULL;
struct assegment *head = NULL;
-
+
while (seg)
{
if (head)
@@ -180,7 +181,7 @@ assegment_dup_all (struct assegment *seg)
}
else
head = new = assegment_dup (seg);
-
+
seg = seg->next;
}
return head;
@@ -191,24 +192,24 @@ static struct assegment *
assegment_prepend_asns (struct assegment *seg, as_t asnum, int num)
{
as_t *newas;
-
+
if (!num)
return seg;
-
+
if (num >= AS_SEGMENT_MAX)
return seg; /* we don't do huge prepends */
-
+
newas = assegment_data_new (seg->length + num);
-
+
if (newas)
{
int i;
for (i = 0; i < num; i++)
newas[i] = asnum;
-
+
memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1));
XFREE (MTYPE_AS_SEG_DATA, seg->as);
- seg->as = newas;
+ seg->as = newas;
seg->length += num;
return seg;
}
@@ -222,7 +223,7 @@ static struct assegment *
assegment_append_asns (struct assegment *seg, as_t *asnos, int num)
{
as_t *newas;
-
+
newas = XREALLOC (MTYPE_AS_SEG_DATA, seg->as,
ASSEGMENT_DATA_SIZE (seg->length + num, 1));
@@ -243,8 +244,8 @@ int_cmp (const void *p1, const void *p2)
{
const as_t *as1 = p1;
const as_t *as2 = p2;
-
- return (*as1 == *as2)
+
+ return (*as1 == *as2)
? 0 : ( (*as1 > *as2) ? 1 : -1);
}
@@ -259,14 +260,14 @@ assegment_normalise (struct assegment *head)
{
struct assegment *seg = head, *pin;
struct assegment *tmp;
-
+
if (!head)
return head;
-
+
while (seg)
{
pin = seg;
-
+
/* Sort values SET segments, for determinism in paths to aid
* creation of hash values / path comparisons
* and because it helps other lesser implementations ;)
@@ -275,15 +276,15 @@ assegment_normalise (struct assegment *head)
{
int tail = 0;
int i;
-
+
qsort (seg->as, seg->length, sizeof(as_t), int_cmp);
-
+
/* weed out dupes */
for (i=1; i < seg->length; i++)
{
if (seg->as[tail] == seg->as[i])
continue;
-
+
tail++;
if (tail < i)
seg->as[tail] = seg->as[i];
@@ -297,28 +298,28 @@ assegment_normalise (struct assegment *head)
* are packable/mergeable. Append all following packable segments
* to the segment we have pinned and remove these appended
* segments.
- */
+ */
while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next))
{
tmp = pin->next;
seg = pin->next;
-
+
/* append the next sequence to the pinned sequence */
pin = assegment_append_asns (pin, seg->as, seg->length);
-
+
/* bypass the next sequence */
pin->next = seg->next;
-
+
/* get rid of the now referenceless segment */
assegment_free (tmp);
-
+
}
seg = pin->next;
}
return head;
}
-
+
static struct aspath *
aspath_new (void)
{
@@ -344,7 +345,7 @@ aspath_unintern (struct aspath **aspath)
{
struct aspath *ret;
struct aspath *asp = *aspath;
-
+
if (asp->refcnt)
asp->refcnt--;
@@ -415,14 +416,14 @@ aspath_count_confeds (struct aspath *aspath)
{
int count = 0;
struct assegment *seg = aspath->segments;
-
+
while (seg)
{
if (seg->type == AS_CONFED_SEQUENCE)
count += seg->length;
else if (seg->type == AS_CONFED_SET)
count++;
-
+
seg = seg->next;
}
return count;
@@ -433,14 +434,14 @@ aspath_count_hops (struct aspath *aspath)
{
int count = 0;
struct assegment *seg = aspath->segments;
-
+
while (seg)
{
if (seg->type == AS_SEQUENCE)
count += seg->length;
else if (seg->type == AS_SET)
count++;
-
+
seg = seg->next;
}
return count;
@@ -457,7 +458,7 @@ aspath_size (struct aspath *aspath)
{
int size = 0;
struct assegment *seg = aspath->segments;
-
+
while (seg)
{
size += ASSEGMENT_SIZE(seg->length, 1);
@@ -473,7 +474,7 @@ aspath_highest (struct aspath *aspath)
struct assegment *seg = aspath->segments;
as_t highest = 0;
unsigned int i;
-
+
while (seg)
{
for (i = 0; i < seg->length; i++)
@@ -492,7 +493,7 @@ aspath_has_as4 (struct aspath *aspath)
{
struct assegment *seg = aspath->segments;
unsigned int i;
-
+
while (seg)
{
for (i = 0; i < seg->length; i++)
@@ -519,9 +520,9 @@ aspath_make_str_count (struct aspath *as)
str_buf[0] = '\0';
return str_buf;
}
-
+
seg = as->segments;
-
+
/* ASN takes 5 to 10 chars plus seperator, see below.
* If there is one differing segment type, we need an additional
* 2 chars for segment delimiters, and the final '\0'.
@@ -540,7 +541,7 @@ aspath_make_str_count (struct aspath *as)
{
int i;
char seperator;
-
+
/* Check AS type validity. Set seperator for segment */
switch (seg->type)
{
@@ -556,7 +557,7 @@ aspath_make_str_count (struct aspath *as)
XFREE (MTYPE_AS_STR, str_buf);
return NULL;
}
-
+
/* We might need to increase str_buf, particularly if path has
* differing segments types, our initial guesstimate above will
* have been wrong. Need 10 chars for ASN, a seperator each and
@@ -574,32 +575,32 @@ aspath_make_str_count (struct aspath *as)
}
#undef ASN_STR_LEN
#undef SEGMENT_STR_LEN
-
+
if (seg->type != AS_SEQUENCE)
- len += snprintf (str_buf + len, str_size - len,
- "%c",
+ len += snprintf (str_buf + len, str_size - len,
+ "%c",
aspath_delimiter_char (seg->type, AS_SEG_START));
-
+
/* write out the ASNs, with their seperators, bar the last one*/
for (i = 0; i < seg->length; i++)
{
len += snprintf (str_buf + len, str_size - len, "%u", seg->as[i]);
-
+
if (i < (seg->length - 1))
len += snprintf (str_buf + len, str_size - len, "%c", seperator);
}
-
+
if (seg->type != AS_SEQUENCE)
- len += snprintf (str_buf + len, str_size - len, "%c",
+ len += snprintf (str_buf + len, str_size - len, "%c",
aspath_delimiter_char (seg->type, AS_SEG_END));
if (seg->next)
len += snprintf (str_buf + len, str_size - len, " ");
-
+
seg = seg->next;
}
-
+
assert (len < str_size);
-
+
str_buf[len] = '\0';
return str_buf;
@@ -618,7 +619,7 @@ struct aspath *
aspath_intern (struct aspath *aspath)
{
struct aspath *find;
-
+
/* Assert this AS path structure is not interned. */
assert (aspath->refcnt == 0);
@@ -662,7 +663,7 @@ aspath_hash_alloc (void *arg)
/* New aspath structure is needed. */
aspath = aspath_dup (arg);
-
+
/* Malformed AS path value. */
if (! aspath->str)
{
@@ -673,151 +674,162 @@ aspath_hash_alloc (void *arg)
return aspath;
}
-/* parse as-segment byte stream in struct assegment */
-static int
-assegments_parse (struct stream *s, size_t length,
- struct assegment **result, int use32bit)
+/* parse *not-empty* as-segment byte stream in struct assegment
+ *
+ * Requires stream to be positioned immediately after the length field of the
+ * atttribute red-tape, and for the length != 0.
+ *
+ * Returns NULL if the AS_PATH or AS4_PATH is not valid.
+ */
+static struct assegment *
+assegments_parse (struct stream *s, size_t length, int use32bit, int as4_path)
{
struct assegment_header segh;
struct assegment *seg, *prev = NULL, *head = NULL;
- size_t bytes = 0;
-
- /* empty aspath (ie iBGP or somesuch) */
- if (length == 0)
- return 0;
-
+
+ assert (length > 0); /* does not expect empty AS_PATH or AS4_PATH */
+
if (BGP_DEBUG (as4, AS4_SEGMENT))
zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
(unsigned long) length);
- /* basic checks */
- if ((STREAM_READABLE(s) < length)
- || (STREAM_READABLE(s) < AS_HEADER_SIZE)
- || (length % AS16_VALUE_SIZE ))
- return -1;
-
- while (bytes < length)
+
+ /* double check that length does not exceed stream */
+ if (STREAM_READABLE(s) < length)
+ return NULL;
+
+ /* deal with each segment in turn */
+ while (length > 0)
{
int i;
size_t seg_size;
-
- if ((length - bytes) <= AS_HEADER_SIZE)
- {
- if (head)
- assegment_free_all (head);
- return -1;
- }
-
- /* softly softly, get the header first on its own */
- segh.type = stream_getc (s);
- segh.length = stream_getc (s);
-
- seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
- if (BGP_DEBUG (as4, AS4_SEGMENT))
- zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
- segh.type, segh.length);
-
- /* check it.. */
- if ( ((bytes + seg_size) > length)
- /* 1771bis 4.3b: seg length contains one or more */
- || (segh.length == 0)
- /* Paranoia in case someone changes type of segment length.
- * Shift both values by 0x10 to make the comparison operate
- * on more, than 8 bits (otherwise it's a warning, bug #564).
- */
- || ((sizeof segh.length > 1)
- && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX)))
+ /* softly softly, get the header first on its own */
+ if (length >= AS_HEADER_SIZE)
{
- if (head)
- assegment_free_all (head);
- return -1;
+ segh.type = stream_getc (s);
+ segh.length = stream_getc (s);
+ confirm((sizeof(segh.length) == 1) && (AS_SEGMENT_MIN == 1)
+ && (AS_SEGMENT_MAX == 255)) ;
+ /* 1..255 is valid */
+
+ seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
+ /* includes the segment type and length red tape */
+
+ if (BGP_DEBUG (as4, AS4_SEGMENT))
+ zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
+ segh.type, segh.length);
+
+ /* Check that the segment type is valid */
+ switch (segh.type)
+ {
+ case AS_SEQUENCE:
+ case AS_SET:
+ break ;
+
+ case AS_CONFED_SEQUENCE:
+ case AS_CONFED_SET:
+ if (!as4_path)
+ break ;
+ /* RFC4893 3: "invalid for the AS4_PATH attribute" */
+ /* fall through */
+
+ default: /* reject unknown or invalid AS_PATH segment types */
+ seg_size = 0 ;
+ } ;
}
-
- switch (segh.type)
+ else
+ seg_size = 0 ;
+
+ /* Stop now if segment is not valid (discarding anything collected to date)
+ *
+ * RFC4271 4.3, Path Attributes, b) AS_PATH:
+ *
+ * "path segment value field contains one or more AS numbers"
+ */
+ if ((seg_size == 0) || (seg_size > length)
+ || (segh.length < AS_SEGMENT_MIN))
{
- case AS_SEQUENCE:
- case AS_SET:
- case AS_CONFED_SEQUENCE:
- case AS_CONFED_SET:
- break;
- default:
- if (head)
- assegment_free_all (head);
- return -1;
- }
-
+ assegment_free_all (head);
+ return NULL;
+ } ;
+
+ length -= seg_size ;
+
/* now its safe to trust lengths */
seg = assegment_new (segh.type, segh.length);
-
+
if (head)
prev->next = seg;
else /* it's the first segment */
head = prev = seg;
-
+
for (i = 0; i < segh.length; i++)
seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s);
- bytes += seg_size;
-
if (BGP_DEBUG (as4, AS4_SEGMENT))
- zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu",
- (unsigned long) bytes);
-
+ zlog_debug ("[AS4SEG] Parse aspath segment: length left: %lu",
+ (unsigned long) length);
+
prev = seg;
}
-
- *result = assegment_normalise (head);
- return 0;
+
+ return assegment_normalise (head);
}
-/* AS path parse function. pnt is a pointer to byte stream and length
- is length of byte stream. If there is same AS path in the the AS
- path hash then return it else make new AS path structure.
-
- On error NULL is returned.
+/* AS path parse function -- parses AS_PATH and AS4_PATH attributes
+ *
+ * Requires: s -- stream, currently positioned before first segment
+ * of AS_PATH or AS4_PATH (ie after attribute header)
+ * length -- length of the value of the AS_PATH or AS4_PATH
+ * use32bit -- true <=> 4Byte ASN, otherwise 2Byte ASN
+ * as4_path -- true <=> AS4_PATH, otherwise AS_PATH
+ *
+ * Returns: if valid: address of struct aspath in the hash of known aspaths,
+ * with reference count incremented.
+ * else: NULL
+ *
+ * NB: empty AS path (length == 0) is valid. The returned struct aspath will
+ * have segments == NULL and str == zero length string (unique).
*/
struct aspath *
-aspath_parse (struct stream *s, size_t length, int use32bit)
+aspath_parse (struct stream *s, size_t length, bool use32bit, bool as4_path)
{
struct aspath as;
struct aspath *find;
- /* If length is odd it's malformed AS path. */
- /* Nit-picking: if (use32bit == 0) it is malformed if odd,
- * otherwise its malformed when length is larger than 2 and (length-2)
- * is not dividable by 4.
- * But... this time we're lazy
- */
- if (length % AS16_VALUE_SIZE )
- return NULL;
-
+ /* Parse each segment and construct normalised list of struct assegment */
memset (&as, 0, sizeof (struct aspath));
- if (assegments_parse (s, length, &as.segments, use32bit) < 0)
- return NULL;
-
- /* If already same aspath exist then return it. */
+ if (length != 0)
+ {
+ as.segments = assegments_parse (s, length, use32bit, as4_path);
+
+ if (as.segments == NULL)
+ return NULL ; /* Invalid AS_PATH or AS4_PATH */
+ } ;
+
+ /* If already same aspath exist then return it. */
find = hash_get (ashash, &as, aspath_hash_alloc);
-
+
+ assert(find) ; /* valid aspath, so must find or create */
+
/* aspath_hash_alloc dupes segments too. that probably could be
* optimised out.
*/
assegment_free_all (as.segments);
if (as.str)
XFREE (MTYPE_AS_STR, as.str);
-
- if (! find)
- return NULL;
+
find->refcnt++;
return find;
-}
+} ;
static inline void
assegment_data_put (struct stream *s, as_t *as, int num, int use32bit)
{
int i;
assert (num <= AS_SEGMENT_MAX);
-
+
for (i = 0; i < num; i++)
if ( use32bit )
stream_putl (s, as[i]);
@@ -847,10 +859,10 @@ aspath_put (struct stream *s, struct aspath *as, int use32bit )
{
struct assegment *seg = as->segments;
size_t bytes = 0;
-
+
if (!seg || seg->length == 0)
return 0;
-
+
if (seg)
{
/*
@@ -867,7 +879,7 @@ aspath_put (struct stream *s, struct aspath *as, int use32bit )
int written = 0;
int asns_packed = 0;
size_t lenp;
-
+
/* Overlength segments have to be split up */
while ( (seg->length - written) > AS_SEGMENT_MAX)
{
@@ -876,12 +888,12 @@ aspath_put (struct stream *s, struct aspath *as, int use32bit )
written += AS_SEGMENT_MAX;
bytes += ASSEGMENT_SIZE (written, use32bit);
}
-
+
/* write the final segment, probably is also the first */
lenp = assegment_header_put (s, seg->type, seg->length - written);
- assegment_data_put (s, (seg->as + written), seg->length - written,
+ assegment_data_put (s, (seg->as + written), seg->length - written,
use32bit);
-
+
/* Sequence-type segments can be 'packed' together
* Case of a segment which was overlength and split up
* will be missed here, but that doesn't matter.
@@ -896,18 +908,18 @@ aspath_put (struct stream *s, struct aspath *as, int use32bit )
* the segment list twice every time we write out
* aspath's.
*/
-
+
/* Next segment's data can fit in this one */
assegment_data_put (s, next->as, next->length, use32bit);
-
+
/* update the length of the segment header */
stream_putc_at (s, lenp, seg->length - written + next->length);
asns_packed += next->length;
-
+
next = next->next;
}
-
- bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed,
+
+ bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed,
use32bit);
seg = next;
}
@@ -928,18 +940,18 @@ aspath_snmp_pathseg (struct aspath *as, size_t *varlen)
snmp_stream = stream_new (SNMP_PATHSEG_MAX);
else
stream_reset (snmp_stream);
-
+
if (!as)
{
*varlen = 0;
return NULL;
}
aspath_put (snmp_stream, as, 0); /* use 16 bit for now here */
-
+
*varlen = stream_get_endp (snmp_stream);
return stream_pnt(snmp_stream);
}
-
+
#define min(A,B) ((A) < (B) ? (A) : (B))
static struct assegment *
@@ -971,13 +983,13 @@ aspath_aggregate_as_set_add (struct aspath *aspath, struct assegment *asset,
for (i = 0; i < asset->length; i++)
if (asset->as[i] == as)
return asset;
-
+
asset->length++;
- asset->as = XREALLOC (MTYPE_AS_SEG_DATA, asset->as,
+ asset->as = XREALLOC (MTYPE_AS_SEG_DATA, asset->as,
asset->length * AS_VALUE_SIZE);
asset->as[asset->length - 1] = as;
}
-
+
return asset;
}
@@ -1003,7 +1015,7 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2)
/* First of all check common leading sequence. */
while (seg1 && seg2)
- {
+ {
/* Check segment type. */
if (seg1->type != seg2->type)
break;
@@ -1018,7 +1030,7 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2)
if (match)
{
struct assegment *seg = assegment_new (seg1->type, 0);
-
+
seg = assegment_append_asns (seg, seg1->as, match);
if (! aspath)
@@ -1028,14 +1040,14 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2)
}
else
prevseg->next = seg;
-
+
prevseg = seg;
}
- if (match != minlen || match != seg1->length
+ if (match != minlen || match != seg1->length
|| seg1->length != seg2->length)
break;
-
+
seg1 = seg1->next;
seg2 = seg2->next;
}
@@ -1049,7 +1061,7 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2)
{
for (i = from; i < seg1->length; i++)
asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]);
-
+
from = 0;
seg1 = seg1->next;
}
@@ -1063,7 +1075,7 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2)
from = 0;
seg2 = seg2->next;
}
-
+
assegment_normalise (aspath->segments);
aspath_str_update (aspath);
return aspath;
@@ -1071,13 +1083,13 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2)
/* When a BGP router receives an UPDATE with an MP_REACH_NLRI
attribute, check the leftmost AS number in the AS_PATH attribute is
- or not the peer's AS number. */
+ or not the peer's AS number. */
int
aspath_firstas_check (struct aspath *aspath, as_t asno)
{
if ( (aspath == NULL) || (aspath->segments == NULL) )
return 0;
-
+
if (aspath->segments
&& (aspath->segments->type == AS_SEQUENCE)
&& (aspath->segments->as[0] == asno ))
@@ -1095,17 +1107,17 @@ aspath_loop_check (struct aspath *aspath, as_t asno)
if ( (aspath == NULL) || (aspath->segments == NULL) )
return 0;
-
+
seg = aspath->segments;
-
+
while (seg)
{
int i;
-
+
for (i = 0; i < seg->length; i++)
if (seg->as[i] == asno)
count++;
-
+
seg = seg->next;
}
return count;
@@ -1116,16 +1128,16 @@ int
aspath_private_as_check (struct aspath *aspath)
{
struct assegment *seg;
-
+
if ( !(aspath && aspath->segments) )
return 0;
-
+
seg = aspath->segments;
while (seg)
{
int i;
-
+
for (i = 0; i < seg->length; i++)
{
if ( (seg->as[i] < BGP_PRIVATE_AS_MIN)
@@ -1183,11 +1195,11 @@ aspath_merge (struct aspath *as1, struct aspath *as2)
return NULL;
last = new = assegment_dup_all (as1->segments);
-
+
/* find the last valid segment */
while (last && last->next)
last = last->next;
-
+
last->next = as2->segments;
as2->segments = new;
aspath_str_update (as2);
@@ -1203,10 +1215,10 @@ aspath_prepend (struct aspath *as1, struct aspath *as2)
if (! as1 || ! as2)
return NULL;
-
+
seg1 = as1->segments;
seg2 = as2->segments;
-
+
/* If as2 is empty, only need to dupe as1's chain onto as2 */
if (seg2 == NULL)
{
@@ -1214,11 +1226,11 @@ aspath_prepend (struct aspath *as1, struct aspath *as2)
aspath_str_update (as2);
return as2;
}
-
+
/* If as1 is empty AS, no prepending to do. */
if (seg1 == NULL)
return as2;
-
+
/* find the tail as1's segment chain. */
while (seg1 && seg1->next)
seg1 = seg1->next;
@@ -1233,35 +1245,35 @@ aspath_prepend (struct aspath *as1, struct aspath *as2)
if (seg1->type == AS_SEQUENCE)
{
- /* We have two chains of segments, as1->segments and seg2,
+ /* We have two chains of segments, as1->segments and seg2,
* and we have to attach them together, merging the attaching
* segments together into one.
- *
+ *
* 1. dupe as1->segments onto head of as2
* 2. merge seg2's asns onto last segment of this new chain
* 3. attach chain after seg2
*/
-
+
/* dupe as1 onto as2's head */
seg1 = as2->segments = assegment_dup_all (as1->segments);
-
+
/* refind the tail of as2, reusing seg1 */
while (seg1 && seg1->next)
seg1 = seg1->next;
-
+
/* merge the old head, seg2, into tail, seg1 */
seg1 = assegment_append_asns (seg1, seg2->as, seg2->length);
-
+
/* bypass the merged seg2, and attach any chain after it to
* chain descending from as2's head
*/
seg1->next = seg2->next;
-
+
/* seg2 is now referenceless and useless*/
assegment_free (seg2);
-
+
/* we've now prepended as1's segment chain to as2, merging
- * the inbetween AS_SEQUENCE of seg2 in the process
+ * the inbetween AS_SEQUENCE of seg2 in the process
*/
aspath_str_update (as2);
return as2;
@@ -1272,7 +1284,7 @@ aspath_prepend (struct aspath *as1, struct aspath *as2)
return aspath_merge (as1, as2);
}
/* XXX: Ermmm, what if as1 has multiple segments?? */
-
+
/* Not reached */
}
@@ -1362,7 +1374,7 @@ aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type)
{
aspath->segments = assegment_new (type, 1);
aspath->segments->as[0] = asno;
-
+
if (assegment)
assegment_free (assegment);
@@ -1371,16 +1383,16 @@ aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type)
if (assegment->type == type)
aspath->segments = assegment_prepend_asns (aspath->segments, asno, 1);
- else
+ else
{
/* create new segment
- * push it onto head of aspath's segment chain
+ * push it onto head of aspath's segment chain
*/
struct assegment *newsegment;
-
+
newsegment = assegment_new (type, 1);
newsegment->as[0] = asno;
-
+
newsegment->next = assegment;
aspath->segments = newsegment;
}
@@ -1422,7 +1434,7 @@ aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2)
if (!(seg1 && seg2
&& (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE)))
return 0;
-
+
if (seg1->as[0] == seg2->as[0])
return 1;
@@ -1441,16 +1453,16 @@ aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
struct assegment *seg, *newseg, *prevseg = NULL;
struct aspath *newpath = NULL, *mergedpath;
int hops, cpasns = 0;
-
+
if (!aspath)
return NULL;
-
+
seg = aspath->segments;
-
+
/* CONFEDs should get reconciled too.. */
hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath))
- aspath_count_hops (as4path);
-
+
if (hops < 0)
{
if (BGP_DEBUG (as4, AS4))
@@ -1461,10 +1473,10 @@ aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
*/
hops = aspath_count_hops (aspath);
}
-
+
if (!hops)
return aspath_dup (as4path);
-
+
if ( BGP_DEBUG(as4, AS4))
zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now",
aspath->str, as4path->str);
@@ -1497,9 +1509,9 @@ aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
cpasns = MIN(seg->length, hops);
hops -= seg->length;
}
-
+
assert (cpasns <= seg->length);
-
+
newseg = assegment_new (seg->type, 0);
newseg = assegment_append_asns (newseg, seg->as, cpasns);
@@ -1514,7 +1526,7 @@ aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
prevseg = newseg;
seg = seg->next;
}
-
+
/* We may be able to join some segments here, and we must
* do this because... we want normalised aspaths in out hash
* and we do not want to stumble in aspath_put.
@@ -1523,11 +1535,11 @@ aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
aspath_free (newpath);
mergedpath->segments = assegment_normalise (mergedpath->segments);
aspath_str_update (mergedpath);
-
+
if ( BGP_DEBUG(as4, AS4))
zlog_debug ("[AS4] result of synthesizing is %s",
mergedpath->str);
-
+
return mergedpath;
}
@@ -1539,14 +1551,14 @@ aspath_cmp_left_confed (const struct aspath *aspath1, const struct aspath *aspat
{
if (! (aspath1 && aspath2) )
return 0;
-
+
if ( !(aspath1->segments && aspath2->segments) )
return 0;
-
+
if ( (aspath1->segments->type != AS_CONFED_SEQUENCE)
|| (aspath2->segments->type != AS_CONFED_SEQUENCE) )
return 0;
-
+
if (aspath1->segments->as[0] == aspath2->segments->as[0])
return 1;
@@ -1564,18 +1576,18 @@ aspath_delete_confed_seq (struct aspath *aspath)
return aspath;
seg = aspath->segments;
-
- /* "if the first path segment of the AS_PATH is
+
+ /* "if the first path segment of the AS_PATH is
* of type AS_CONFED_SEQUENCE,"
*/
if (aspath->segments->type != AS_CONFED_SEQUENCE)
return aspath;
- /* "... that segment and any immediately following segments
- * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed
+ /* "... that segment and any immediately following segments
+ * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed
* from the AS_PATH attribute,"
*/
- while (seg &&
+ while (seg &&
(seg->type == AS_CONFED_SEQUENCE || seg->type == AS_CONFED_SET))
{
aspath->segments = seg->next;
@@ -1602,7 +1614,7 @@ aspath_as_add (struct aspath *as, as_t asno)
if (!seg)
return;
-
+
/* Last segment search procedure. */
while (seg->next)
seg = seg->next;
@@ -1627,10 +1639,13 @@ aspath_segment_add (struct aspath *as, int type)
as->segments = new;
}
+/*------------------------------------------------------------------------------
+ * Construct an interned empty AS Path
+ */
struct aspath *
aspath_empty (void)
{
- return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */
+ return aspath_parse (NULL, 0, true, false); /* 32Bit ;-) not AS4_PATH */
}
struct aspath *
@@ -1647,9 +1662,9 @@ unsigned long
aspath_count (void)
{
return ashash->count;
-}
-
-/*
+}
+
+/*
Theoretically, one as path can have:
One BGP packet size should be less than 4096.
@@ -1714,15 +1729,15 @@ aspath_gettoken (const char *buf, enum as_token *token, u_long *asno)
}
/* Check actual AS value. */
- if (isdigit ((int) *p))
+ if (isdigit ((int) *p))
{
as_t asval;
-
+
*token = as_token_asval;
asval = (*p - '0');
p++;
-
- while (isdigit ((int) *p))
+
+ while (isdigit ((int) *p))
{
asval *= 10;
asval += (*p - '0');
@@ -1731,7 +1746,7 @@ aspath_gettoken (const char *buf, enum as_token *token, u_long *asno)
*asno = asval;
return p;
}
-
+
/* There is no match then return unknown token. */
*token = as_token_unknown;
return p++;
@@ -1802,7 +1817,7 @@ aspath_str2aspath (const char *str)
return aspath;
}
-
+
/* Make hash value by raw aspath data. */
unsigned int
aspath_key_make (void *p)
@@ -1812,7 +1827,7 @@ aspath_key_make (void *p)
if (!aspath->str)
aspath_str_update (aspath);
-
+
key = jhash (aspath->str, strlen(aspath->str), 2334325);
return key;
@@ -1824,14 +1839,14 @@ aspath_cmp (const void *arg1, const void *arg2)
{
const struct assegment *seg1 = ((const struct aspath *)arg1)->segments;
const struct assegment *seg2 = ((const struct aspath *)arg2)->segments;
-
+
while (seg1 || seg2)
{
int i;
if ((!seg1 && seg2) || (seg1 && !seg2))
return 0;
if (seg1->type != seg2->type)
- return 0;
+ return 0;
if (seg1->length != seg2->length)
return 0;
for (i = 0; i < seg1->length; i++)
@@ -1855,11 +1870,11 @@ aspath_finish (void)
{
hash_free (ashash);
ashash = NULL;
-
+
if (snmp_stream)
stream_free (snmp_stream);
}
-
+
/* return and as path value */
const char *
aspath_print (struct aspath *as)
@@ -1896,7 +1911,7 @@ aspath_show_all_iterator (struct hash_backet *backet, struct vty *vty)
void
aspath_print_all_vty (struct vty *vty)
{
- hash_iterate (ashash,
+ hash_iterate (ashash,
(void (*) (struct hash_backet *, void *))
aspath_show_all_iterator,
vty);
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index d55f9ce6..74eb775c 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -21,6 +21,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ASPATH_H
#define _QUAGGA_BGP_ASPATH_H
+#include <stdbool.h>
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
/* AS path segment type. */
#define AS_SET 1
#define AS_SEQUENCE 2
@@ -47,14 +54,14 @@ struct assegment
};
/* AS path may be include some AsSegments. */
-struct aspath
+struct aspath
{
/* Reference count to this aspath. */
unsigned long refcnt;
/* segment data */
struct assegment *segments;
-
+
/* String expression of AS path. This string is used by vty output
and AS path regular expression match. */
char *str;
@@ -65,7 +72,7 @@ struct aspath
/* Prototypes. */
extern void aspath_init (void);
extern void aspath_finish (void);
-extern struct aspath *aspath_parse (struct stream *, size_t, int);
+extern struct aspath *aspath_parse (struct stream *, size_t, bool, bool);
extern struct aspath *aspath_dup (struct aspath *);
extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *);
extern struct aspath *aspath_prepend (struct aspath *, struct aspath *);
@@ -103,4 +110,22 @@ extern unsigned int aspath_has_as4 (struct aspath *);
/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */
extern u_char *aspath_snmp_pathseg (struct aspath *, size_t *);
+Inline as_t
+aspath_origin(struct aspath* asp)
+{
+ struct assegment* seg ;
+
+ if ((asp == NULL) || (asp->segments == NULL))
+ return 0 ;
+
+ seg = asp->segments ;
+ while (seg->next != NULL)
+ seg = seg->next ;
+
+ if ((seg->length == 0) || (seg->type != AS_SEQUENCE))
+ return 0 ;
+
+ return seg->as[seg->length - 1] ;
+} ;
+
#endif /* _QUAGGA_BGP_ASPATH_H */
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index d43c104f..c5a710c8 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -38,32 +38,32 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_ecommunity.h"
-
+
/* Attribute strings for logging. */
-static const struct message attr_str [] =
-{
- { BGP_ATTR_ORIGIN, "ORIGIN" },
- { BGP_ATTR_AS_PATH, "AS_PATH" },
- { BGP_ATTR_NEXT_HOP, "NEXT_HOP" },
- { BGP_ATTR_MULTI_EXIT_DISC, "MULTI_EXIT_DISC" },
- { BGP_ATTR_LOCAL_PREF, "LOCAL_PREF" },
- { BGP_ATTR_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" },
- { BGP_ATTR_AGGREGATOR, "AGGREGATOR" },
- { BGP_ATTR_COMMUNITIES, "COMMUNITY" },
+static const struct message attr_str [] =
+{
+ { BGP_ATTR_ORIGIN, "ORIGIN" },
+ { BGP_ATTR_AS_PATH, "AS_PATH" },
+ { BGP_ATTR_NEXT_HOP, "NEXT_HOP" },
+ { BGP_ATTR_MULTI_EXIT_DISC, "MULTI_EXIT_DISC" },
+ { BGP_ATTR_LOCAL_PREF, "LOCAL_PREF" },
+ { BGP_ATTR_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" },
+ { BGP_ATTR_AGGREGATOR, "AGGREGATOR" },
+ { BGP_ATTR_COMMUNITIES, "COMMUNITY" },
{ BGP_ATTR_ORIGINATOR_ID, "ORIGINATOR_ID" },
- { BGP_ATTR_CLUSTER_LIST, "CLUSTERLIST" },
+ { BGP_ATTR_CLUSTER_LIST, "CLUSTERLIST" },
{ BGP_ATTR_DPA, "DPA" },
{ BGP_ATTR_ADVERTISER, "ADVERTISER"} ,
{ BGP_ATTR_RCID_PATH, "RCID_PATH" },
{ BGP_ATTR_MP_REACH_NLRI, "MP_REACH_NLRI" },
{ BGP_ATTR_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" },
{ BGP_ATTR_EXT_COMMUNITIES, "EXT_COMMUNITIES" },
- { BGP_ATTR_AS4_PATH, "AS4_PATH" },
- { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" },
+ { BGP_ATTR_AS4_PATH, "AS4_PATH" },
+ { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" },
{ BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT" },
};
static const int attr_str_max = sizeof(attr_str)/sizeof(attr_str[0]);
-
+
static struct hash *cluster_hash;
static void *
@@ -107,7 +107,7 @@ int
cluster_loop_check (struct cluster_list *cluster, struct in_addr originator)
{
int i;
-
+
for (i = 0; i < cluster->length / 4; i++)
if (cluster->list[i].s_addr == originator.s_addr)
return 1;
@@ -118,7 +118,6 @@ static unsigned int
cluster_hash_key_make (void *p)
{
const struct cluster_list *cluster = p;
-
return jhash(cluster->list, cluster->length, 0);
}
@@ -156,7 +155,7 @@ cluster_dup (struct cluster_list *cluster)
}
else
new->list = NULL;
-
+
return new;
}
#endif
@@ -199,7 +198,7 @@ cluster_finish (void)
hash_free (cluster_hash);
cluster_hash = NULL;
}
-
+
/* Unknown transit attribute. */
static struct hash *transit_hash;
@@ -250,8 +249,7 @@ transit_unintern (struct transit *transit)
static unsigned int
transit_hash_key_make (void *p)
{
- const struct transit * transit = p;
-
+ const struct transit * transit = p ;
return jhash(transit->val, transit->length, 0);
}
@@ -277,7 +275,7 @@ transit_finish (void)
hash_free (transit_hash);
transit_hash = NULL;
}
-
+
/* Attribute hash routines. */
static struct hash *attrhash;
@@ -337,18 +335,13 @@ attrhash_key_make (void *p)
{
const struct attr * attr = (struct attr *) p;
uint32_t key = 0;
-#define MIX(val) key = jhash_1word(val, key)
+#define MIX(val) key = jhash_1word(val, key)
MIX(attr->origin);
MIX(attr->nexthop.s_addr);
MIX(attr->med);
MIX(attr->local_pref);
- key += attr->origin;
- key += attr->nexthop.s_addr;
- key += attr->med;
- key += attr->local_pref;
-
if (attr->extra)
{
MIX(attr->extra->aggregator_as);
@@ -356,12 +349,12 @@ attrhash_key_make (void *p)
MIX(attr->extra->weight);
MIX(attr->extra->mp_nexthop_global_in.s_addr);
}
-
+
if (attr->aspath)
MIX(aspath_key_make (attr->aspath));
if (attr->community)
MIX(community_hash_make (attr->community));
-
+
if (attr->extra)
{
if (attr->extra->ecommunity)
@@ -397,7 +390,7 @@ attrhash_cmp (const void *p1, const void *p2)
{
const struct attr_extra *ae1 = attr1->extra;
const struct attr_extra *ae2 = attr2->extra;
-
+
if (ae1 && ae2
&& ae1->aggregator_as == ae2->aggregator_as
&& ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr
@@ -439,14 +432,14 @@ attr_show_all_iterator (struct hash_backet *backet, struct vty *vty)
{
struct attr *attr = backet->data;
- vty_out (vty, "attr[%ld] nexthop %s%s", attr->refcnt,
- inet_ntoa (attr->nexthop), VTY_NEWLINE);
+ vty_out (vty, "attr[%ld] nexthop %s%s", attr->refcnt,
+ safe_inet_ntoa (attr->nexthop), VTY_NEWLINE);
}
void
attr_show_all (struct vty *vty)
{
- hash_iterate (attrhash,
+ hash_iterate (attrhash,
(void (*)(struct hash_backet *, void *))
attr_show_all_iterator,
vty);
@@ -469,7 +462,38 @@ bgp_attr_hash_alloc (void *p)
return attr;
}
-/* Internet argument attribute. */
+/*------------------------------------------------------------------------------
+ * Internet argument attribute.
+ *
+ * 1. "internalise" and increase reference count for each of:
+ *
+ * attr->aspath
+ * attr->community
+ * attr->extra->ecommunity
+ * attr->extra->cluster
+ * attr->extra->transit
+ *
+ * Noting that the reference count for each of these is zero if they have not
+ * yet been internalised.
+ *
+ * Each of these pointers is updated to point at either a new entry in the
+ * relevant attribute store, or at the existing entry.
+ *
+ * 2. "internalise" the complete attribute object and increase its reference
+ * count.
+ *
+ * If the attribute collection is new, then this function returns a pointer
+ * to a brand new attribute object, complete with a copy of the attr->extra.
+ *
+ * If the attribute collection is not new, then this function returns a
+ * pointer to the stored attribute object.
+ *
+ * In any event, the pointer returned != pointer passed in.
+ *
+ * NB: the incoming attr reference count is ignored.
+ *
+ * Note that the original attr object remains with its own attr->extra object.
+ */
struct attr *
bgp_attr_intern (struct attr *attr)
{
@@ -493,14 +517,14 @@ bgp_attr_intern (struct attr *attr)
if (attr->extra)
{
struct attr_extra *attre = attr->extra;
-
+
if (attre->ecommunity)
{
if (! attre->ecommunity->refcnt)
attre->ecommunity = ecommunity_intern (attre->ecommunity);
else
attre->ecommunity->refcnt++;
-
+
}
if (attre->cluster)
{
@@ -517,21 +541,23 @@ bgp_attr_intern (struct attr *attr)
attre->transit->refcnt++;
}
}
-
+
find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc);
find->refcnt++;
-
+
return find;
}
-
-/* Make network statement's attribute. */
+/* Make network statement's attribute.
+ *
+ * All elements are interned, but not the attribute set itself.
+ */
struct attr *
bgp_attr_default_set (struct attr *attr, u_char origin)
{
memset (attr, 0, sizeof (struct attr));
bgp_attr_extra_get (attr);
-
+
attr->origin = origin;
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
attr->aspath = aspath_empty ();
@@ -553,15 +579,15 @@ bgp_attr_default_intern (u_char origin)
struct attr attr;
struct attr *new;
struct attr_extra *attre;
-
+
memset (&attr, 0, sizeof (struct attr));
attre = bgp_attr_extra_get (&attr);
-
+
bgp_attr_default_set(&attr, origin);
new = bgp_attr_intern (&attr);
bgp_attr_extra_free (&attr);
-
+
aspath_unintern (&new->aspath);
return new;
}
@@ -577,7 +603,7 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
memset (&attr, 0, sizeof (struct attr));
attre = bgp_attr_extra_get (&attr);
-
+
/* Origin attribute. */
attr.origin = origin;
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
@@ -613,154 +639,183 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
new = bgp_attr_intern (&attr);
bgp_attr_extra_free (&attr);
-
+
aspath_unintern (&new->aspath);
return new;
}
/* Unintern just the sub-components of the attr, but not the attr */
-void
-bgp_attr_unintern_sub (struct attr *attr)
+extern void
+bgp_attr_unintern_sub (struct attr *attr, bool free_extra)
{
/* aspath refcount shoud be decrement. */
if (attr->aspath)
aspath_unintern (&attr->aspath);
UNSET_FLAG(attr->flag, BGP_ATTR_AS_PATH);
-
+
if (attr->community)
community_unintern (&attr->community);
UNSET_FLAG(attr->flag, BGP_ATTR_COMMUNITIES);
-
+
if (attr->extra)
{
if (attr->extra->ecommunity)
ecommunity_unintern (&attr->extra->ecommunity);
UNSET_FLAG(attr->flag, BGP_ATTR_EXT_COMMUNITIES);
-
+
if (attr->extra->cluster)
cluster_unintern (attr->extra->cluster);
UNSET_FLAG(attr->flag, BGP_ATTR_CLUSTER_LIST);
-
+
if (attr->extra->transit)
transit_unintern (attr->extra->transit);
- }
-}
-/* Free bgp attribute and aspath. */
+ if (free_extra)
+ bgp_attr_extra_free (attr) ;
+ }
+}
+
+/*------------------------------------------------------------------------------
+ * Free bgp attribute and aspath.
+ *
+ * For all the elements of the given attributes:
+ *
+ * if the reference count != 0, decrement it
+ * if the reference count is now zero, remove from hash table and discard
+ * stored value.
+ *
+ * For the attribute object itself:
+ *
+ * decrement the reference count
+ * if the reference count is now zero, remove from hash table and discard
+ * stored value.
+ *
+ * So... do NOT do this to a set of attributes whose elements have not been
+ * interned !
+ *
+ * Can do this to an attribute object which has not been interned, because its
+ * reference count SHOULD be zero.
+ */
void
bgp_attr_unintern (struct attr **attr)
{
- struct attr *ret;
- struct attr tmp;
-
- /* Decrement attribute reference. */
- (*attr)->refcnt--;
-
+ struct attr tmp ;
+ struct attr_extra tmp_extra ;
+
+ /* Take copy of attributes so that can unintern sub-objects */
tmp = *(*attr);
-
+
if ((*attr)->extra)
{
- tmp.extra = bgp_attr_extra_new ();
+ tmp.extra = &tmp_extra ;
memcpy (tmp.extra, (*attr)->extra, sizeof (struct attr_extra));
}
-
- /* If reference becomes zero then free attribute object. */
- if ((*attr)->refcnt == 0)
- {
- ret = hash_release (attrhash, *attr);
- assert (ret != NULL);
- bgp_attr_extra_free (*attr);
- XFREE (MTYPE_ATTR, *attr);
- *attr = NULL;
- }
-
- bgp_attr_unintern_sub (&tmp);
-}
+ /* If reference becomes zero then free attribute object. */
+ if ((*attr)->refcnt != 0)
+ {
+ --(*attr)->refcnt ;
+ if ((*attr)->refcnt == 0)
+ {
+ struct attr *ret;
+ ret = hash_release (attrhash, *attr);
+ assert (ret != NULL);
+ bgp_attr_extra_free (*attr);
+ XFREE (MTYPE_ATTR, *attr); /* sets *attr = NULL */
+ } ;
+ } ;
+
+ /* Now the sub-objects */
+ bgp_attr_unintern_sub (&tmp, false) ; /* false => don't free extra */
+}
+
+/*------------------------------------------------------------------------------
+ * Release any element whose reference count is zero.
+ *
+ * This is used where attributes have been replaced, but not internalised, and
+ * which are no longer of interest -- typically where a route-map returns DENY.
+ */
void
bgp_attr_flush (struct attr *attr)
{
- if (attr->aspath && ! attr->aspath->refcnt)
+ if (attr->aspath && (attr->aspath->refcnt == 0))
aspath_free (attr->aspath);
- if (attr->community && ! attr->community->refcnt)
+ if (attr->community && (attr->community->refcnt == 0))
community_free (attr->community);
if (attr->extra)
{
struct attr_extra *attre = attr->extra;
-
- if (attre->ecommunity && ! attre->ecommunity->refcnt)
+ if (attre->ecommunity && (attre->ecommunity->refcnt == 0))
ecommunity_free (&attre->ecommunity);
- if (attre->cluster && ! attre->cluster->refcnt)
+ if (attre->cluster && (attre->cluster->refcnt == 0))
cluster_free (attre->cluster);
- if (attre->transit && ! attre->transit->refcnt)
+ if (attre->transit && (attre->transit->refcnt == 0))
transit_free (attre->transit);
}
}
-/* Implement draft-scudder-idr-optional-transitive behaviour and
+/* Implement draft-ietf-idr-optional-transitive behaviour and
* avoid resetting sessions for malformed attributes which are
* are partial/optional and hence where the error likely was not
* introduced by the sending neighbour.
*/
static bgp_attr_parse_ret_t
-bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
- u_char subcode, u_char *startp, bgp_size_t length)
+bgp_attr_malformed (struct peer *peer, u_char attr_type, u_char flag,
+ u_char subcode, u_char *startp, bgp_size_t length)
{
/* Only relax error handling for eBGP peers */
- if (peer_sort (peer) != BGP_PEER_EBGP)
+ if (peer_sort (peer) == BGP_PEER_EBGP)
{
- bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode,
- startp, length);
- return BGP_ATTR_PARSE_ERROR;
+ switch (attr_type)
+ {
+ /* where an optional attribute is inconsequential, e.g. it does not
+ * affect route selection, and can be safely ignored then any such
+ * attributes which are malformed should just be ignored and the
+ * route processed as normal.
+ */
+ case BGP_ATTR_AS4_AGGREGATOR:
+ case BGP_ATTR_AGGREGATOR:
+ case BGP_ATTR_ATOMIC_AGGREGATE:
+ return BGP_ATTR_PARSE_PROCEED;
- }
-
- switch (type) {
- /* where an optional attribute is inconsequential, e.g. it does not affect
- * route selection, and can be safely ignored then any such attributes
- * which are malformed should just be ignored and the route processed as
- * normal.
- */
- case BGP_ATTR_AS4_AGGREGATOR:
- case BGP_ATTR_AGGREGATOR:
- case BGP_ATTR_ATOMIC_AGGREGATE:
- return BGP_ATTR_PARSE_PROCEED;
-
- /* Core attributes, particularly ones which may influence route
- * selection should always cause session resets
- */
- case BGP_ATTR_ORIGIN:
- case BGP_ATTR_AS_PATH:
- case BGP_ATTR_NEXT_HOP:
- case BGP_ATTR_MULTI_EXIT_DISC:
- case BGP_ATTR_LOCAL_PREF:
- case BGP_ATTR_COMMUNITIES:
- case BGP_ATTR_ORIGINATOR_ID:
- case BGP_ATTR_CLUSTER_LIST:
- case BGP_ATTR_MP_REACH_NLRI:
- case BGP_ATTR_MP_UNREACH_NLRI:
- case BGP_ATTR_EXT_COMMUNITIES:
- bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode,
- startp, length);
- return BGP_ATTR_PARSE_ERROR;
- }
-
- /* Partial optional attributes that are malformed should not cause
- * the whole session to be reset. Instead treat it as a withdrawal
- * of the routes, if possible.
- */
- if (CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS)
- && CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
- && CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL))
- return BGP_ATTR_PARSE_WITHDRAW;
-
- /* default to reset */
+ /* Core attributes, particularly ones which may influence route
+ * selection should always cause session resets
+ */
+ case BGP_ATTR_ORIGIN:
+ case BGP_ATTR_AS_PATH:
+ case BGP_ATTR_NEXT_HOP:
+ case BGP_ATTR_MULTI_EXIT_DISC:
+ case BGP_ATTR_LOCAL_PREF:
+ case BGP_ATTR_COMMUNITIES:
+ case BGP_ATTR_ORIGINATOR_ID:
+ case BGP_ATTR_CLUSTER_LIST:
+ case BGP_ATTR_MP_REACH_NLRI:
+ case BGP_ATTR_MP_UNREACH_NLRI:
+ case BGP_ATTR_EXT_COMMUNITIES:
+ break ;
+
+ /* Partial optional attributes that are malformed should not cause
+ * the whole session to be reset. Instead treat it as a withdrawal
+ * of the routes, if possible.
+ */
+ default:
+ if (CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS) &&
+ CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL) &&
+ CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL))
+ return BGP_ATTR_PARSE_WITHDRAW;
+ break ;
+ } ;
+ } ;
+
+ /* default to reset */
+ bgp_peer_down_error_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode,
+ startp, length) ;
return BGP_ATTR_PARSE_ERROR;
-}
+} ;
/* Get origin attribute of the update message. */
static bgp_attr_parse_ret_t
-bgp_attr_origin (struct peer *peer, bgp_size_t length,
+bgp_attr_origin (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag, u_char *startp)
{
bgp_size_t total;
@@ -775,11 +830,11 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
attribute (type, length and value). */
if (flag != BGP_ATTR_FLAG_TRANS)
{
- zlog (peer->log, LOG_ERR,
+ zlog (peer->log, LOG_ERR,
"Origin attribute flag isn't transitive %d", flag);
return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
}
/* If any recognized attribute has Attribute Length that conflicts
@@ -792,8 +847,8 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d",
length);
return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ startp, total);
}
/* Fetch origin attribute. */
@@ -809,60 +864,87 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d",
attr->origin);
return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
- BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
- startp, total);
+ BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
+ startp, total);
}
/* Set oring attribute flag. */
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
- return 0;
-}
-
-/* Parse AS path information. This function is wrapper of
- aspath_parse. */
-static int
-bgp_attr_aspath (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+ return BGP_ATTR_PARSE_PROCEED ;
+}
+
+/*------------------------------------------------------------------------------
+ * Parse AS path information. This function is wrapper of aspath_parse.
+ *
+ * Parses AS_PATH or AS4_PATH.
+ *
+ * Returns: if valid: BGP_ATTR_PARSE_PROCEED
+ * and sets *p_asp = address of struct aspath in the hash of
+ * known aspaths, with reference count incremented.
+ *
+ * else: whatever bgp_attr_malformed() decides.
+ *
+ * NB: empty AS path (length == 0) is valid. The returned struct aspath will
+ * have segments == NULL and str == zero length string (unique).
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_aspath (struct peer *peer, struct aspath** p_asp, bgp_size_t length,
+ struct attr *attr, u_char flag, u_char *startp,
+ u_char attr_type)
{
- bgp_size_t total;
+ u_char require ;
+ bool as4_path = (attr_type == BGP_ATTR_AS4_PATH) ;
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+ /* Check the attribute flags */
+ require = as4_path ? BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+ : BGP_ATTR_FLAG_TRANS ;
- /* Flag check. */
- if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
- || ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
+ if ((flag & (BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS)) != require)
{
- zlog (peer->log, LOG_ERR,
- "As-Path attribute flag isn't transitive %d", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+ bgp_size_t total;
- /*
- * peer with AS4 => will get 4Byte ASnums
- * otherwise, will get 16 Bit
- */
- attr->aspath = aspath_parse (peer->ibuf, length,
- CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV));
+ *p_asp = NULL ;
+
+ if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS))
+ zlog (peer->log, LOG_ERR, "%s attribute flag isn't transitive 0x%02X",
+ as4_path ? "AS4_PATH" : "AS_PATH", flag) ;
+
+ if ((flag & BGP_ATTR_FLAG_OPTIONAL) != (require & BGP_ATTR_FLAG_OPTIONAL))
+ zlog (peer->log, LOG_ERR, "%s attribute flag must %sbe optional 0x%02X",
+ as4_path ? "AS4_PATH" : "AS_PATH",
+ (flag & BGP_ATTR_FLAG_OPTIONAL) ? "not " : "", flag) ;
+
+ total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ return bgp_attr_malformed (peer, attr_type, flag,
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
+ } ;
- /* In case of IBGP, length will be zero. */
- if (! attr->aspath)
+ /* Parse the AS_PATH/AS4_PATH body.
+ *
+ * For AS_PATH peer with AS4 => 4Byte ASN otherwise 2Byte ASN
+ * AS4_PATH 4Byte ASN
+ */
+ *p_asp = aspath_parse (peer->ibuf, length,
+ PEER_CAP_AS4_USE(peer) || as4_path, as4_path) ;
+ if (*p_asp == NULL)
{
- zlog (peer->log, LOG_ERR,
- "Malformed AS path from %s, length is %d",
- peer->host, length);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
- }
+ zlog (peer->log, LOG_ERR, "Malformed %s from %s, length is %d",
+ as4_path ? "AS4_PATH" : "AS4_PATH", peer->host, length);
- /* Set aspath attribute flag. */
- attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+ return bgp_attr_malformed (peer, attr_type, flag,
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ NULL, 0);
+ } ;
+
+ /* Success !
+ */
+ attr->flag |= ATTR_FLAG_BIT (attr_type) ;
return BGP_ATTR_PARSE_PROCEED;
-}
+} ;
static bgp_attr_parse_ret_t
bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
@@ -876,30 +958,33 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
*/
struct bgp *bgp = peer->bgp;
struct aspath *aspath;
+ bgp_peer_sort_t sort = peer_sort(peer) ;
bgp = peer->bgp;
-
+
/* Confederation sanity check. */
- if ((peer_sort (peer) == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) ||
- (peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath)))
+ if ( ((sort == BGP_PEER_CONFED) && ! aspath_left_confed_check (attr->aspath))
+ || ((sort == BGP_PEER_EBGP) && aspath_confed_check (attr->aspath)) )
{
zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host);
+
return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ NULL, 0);
}
/* First AS check for EBGP. */
if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS))
{
- if (peer_sort (peer) == BGP_PEER_EBGP
+ if (sort == BGP_PEER_EBGP
&& ! aspath_firstas_check (attr->aspath, peer->as))
{
zlog (peer->log, LOG_ERR,
"%s incorrect first AS (must be %u)", peer->host, peer->as);
+
return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ NULL, 0);
}
}
@@ -916,51 +1001,9 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
return BGP_ATTR_PARSE_PROCEED;
}
-/* Parse AS4 path information. This function is another wrapper of
- aspath_parse. */
-static int
-bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp,
- struct aspath **as4_path)
-{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
- /* Flag check. */
- if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
- || !CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
- {
- zlog (peer->log, LOG_ERR,
- "As4-Path attribute flag isn't optional/transitive %d", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
-
- *as4_path = aspath_parse (peer->ibuf, length, 1);
-
- /* In case of IBGP, length will be zero. */
- if (!*as4_path)
- {
- zlog (peer->log, LOG_ERR,
- "Malformed AS4 path from %s, length is %d",
- peer->host, length);
- return bgp_attr_malformed (peer, BGP_ATTR_AS4_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
- }
-
- /* Set aspath attribute flag. */
- if (as4_path)
- attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH);
-
- return BGP_ATTR_PARSE_PROCEED;
-}
-
/* Nexthop attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
+bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag, u_char *startp)
{
bgp_size_t total;
@@ -971,11 +1014,12 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
|| ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
{
- zlog (peer->log, LOG_ERR,
+ zlog (peer->log, LOG_ERR,
"Origin attribute flag isn't transitive %d", flag);
+
return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
}
/* Check nexthop attribute length. */
@@ -985,8 +1029,8 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
length);
return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ startp, total);
}
attr->nexthop.s_addr = stream_get_ipv4 (peer->ibuf);
@@ -997,7 +1041,7 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
/* MED atrribute. */
static bgp_attr_parse_ret_t
-bgp_attr_med (struct peer *peer, bgp_size_t length,
+bgp_attr_med (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag, u_char *startp)
{
bgp_size_t total;
@@ -1007,12 +1051,12 @@ bgp_attr_med (struct peer *peer, bgp_size_t length,
/* Length check. */
if (length != 4)
{
- zlog (peer->log, LOG_ERR,
+ zlog (peer->log, LOG_ERR,
"MED attribute length isn't four [%d]", length);
return bgp_attr_malformed (peer, BGP_ATTR_MULTI_EXIT_DISC, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ startp, total);
}
attr->med = stream_getl (peer->ibuf);
@@ -1024,7 +1068,7 @@ bgp_attr_med (struct peer *peer, bgp_size_t length,
/* Local preference attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
+bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag)
{
/* If it is contained in an UPDATE message that is received from an
@@ -1036,9 +1080,9 @@ bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
return BGP_ATTR_PARSE_PROCEED;
}
- if (length == 4)
+ if (length == 4)
attr->local_pref = stream_getl (peer->ibuf);
- else
+ else
attr->local_pref = 0;
/* Set atomic aggregate flag. */
@@ -1049,7 +1093,7 @@ bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
/* Atomic aggregate. */
static int
-bgp_attr_atomic (struct peer *peer, bgp_size_t length,
+bgp_attr_atomic (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag)
{
if (length != 0)
@@ -1057,8 +1101,8 @@ bgp_attr_atomic (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR, "Bad atomic aggregate length %d", length);
return bgp_attr_malformed (peer, BGP_ATTR_ATOMIC_AGGREGATE, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ NULL, 0);
}
/* Set atomic aggregate flag. */
@@ -1074,21 +1118,22 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
{
int wantedlen = 6;
struct attr_extra *attre = bgp_attr_extra_get (attr);
-
+
/* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */
- if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
+ if ( PEER_CAP_AS4_USE(peer) )
wantedlen = 8;
-
+
if (length != wantedlen)
{
- zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen, length);
+ zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen,
+ length);
return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ NULL, 0);
}
-
- if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) )
+
+ if ( PEER_CAP_AS4_USE(peer) )
attre->aggregator_as = stream_getl (peer->ibuf);
else
attre->aggregator_as = stream_getw (peer->ibuf);
@@ -1103,7 +1148,7 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
/* New Aggregator attribute */
static bgp_attr_parse_ret_t
bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag,
+ struct attr *attr, u_char flag,
as_t *as4_aggregator_as,
struct in_addr *as4_aggregator_addr)
{
@@ -1111,8 +1156,8 @@ bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
{
zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length);
return bgp_attr_malformed (peer, BGP_ATTR_AS4_AGGREGATOR, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ NULL, 0);
}
*as4_aggregator_as = stream_getl (peer->ibuf);
as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf);
@@ -1132,8 +1177,8 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
int ignore_as4_path = 0;
struct aspath *newpath;
struct attr_extra *attre = attr->extra;
-
- if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
+
+ if ( PEER_CAP_AS4_USE(peer) )
{
/* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR
* if given.
@@ -1145,15 +1190,15 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH)))
zlog_debug ("[AS4] %s %s AS4_PATH",
peer->host, "AS4 capable peer, yet it sent");
-
+
if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR)))
zlog_debug ("[AS4] %s %s AS4_AGGREGATOR",
peer->host, "AS4 capable peer, yet it sent");
}
-
+
return BGP_ATTR_PARSE_PROCEED;
}
-
+
if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH))
&& !(attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))))
{
@@ -1164,12 +1209,13 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
* But... yeah, paranoia
* Take this as a "malformed attribute"
*/
- zlog (peer->log, LOG_ERR,
+ zlog (peer->log, LOG_ERR,
"%s BGP not AS4 capable peer sent AS4_PATH but"
" no AS_PATH, cant do anything here", peer->host);
+
return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_ATTR,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_MAL_ATTR,
+ NULL, 0);
}
/* We have a asn16 peer. First, look for AS4_AGGREGATOR
@@ -1180,11 +1226,11 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) )
{
assert (attre);
-
+
/* received both.
* if the as_number in aggregator is not AS_TRANS,
* then AS4_AGGREGATOR and AS4_PATH shall be ignored
- * and the Aggregator shall be taken as
+ * and the Aggregator shall be taken as
* info on the aggregating node, and the AS_PATH
* shall be taken as the AS_PATH
* otherwise
@@ -1197,7 +1243,7 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
{
/* ignore */
if ( BGP_DEBUG(as4, AS4))
- zlog_debug ("[AS4] %s BGP not AS4 capable peer"
+ zlog_debug ("[AS4] %s BGP not AS4 capable peer"
" send AGGREGATOR != AS_TRANS and"
" AS4_AGGREGATOR, so ignore"
" AS4_AGGREGATOR and AS4_PATH", peer->host);
@@ -1220,8 +1266,10 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
if ( BGP_DEBUG(as4, AS4))
zlog_debug ("[AS4] %s BGP not AS4 capable peer send"
" AS4_AGGREGATOR but no AGGREGATOR, will take"
- " it as if AGGREGATOR with AS_TRANS had been there", peer->host);
- (attre = bgp_attr_extra_get (attr))->aggregator_as = as4_aggregator;
+ " it as if AGGREGATOR with AS_TRANS had been there",
+ peer->host);
+ attre = bgp_attr_extra_get (attr) ;
+ attre->aggregator_as = as4_aggregator;
/* sweep it under the carpet and simulate a "good" AGGREGATOR */
attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR));
}
@@ -1239,29 +1287,29 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
/* Community attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_community (struct peer *peer, bgp_size_t length,
+bgp_attr_community (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag, u_char *startp)
{
bgp_size_t total
= length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
+
if (length == 0)
{
attr->community = NULL;
return BGP_ATTR_PARSE_PROCEED;
}
-
+
attr->community =
community_parse ((u_int32_t *)stream_pnt (peer->ibuf), length);
-
+
/* XXX: fix community_parse to use stream API and remove this */
stream_forward_getp (peer->ibuf, length);
if (!attr->community)
return bgp_attr_malformed (peer, BGP_ATTR_COMMUNITIES, flag,
- BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
- startp, total);
-
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ startp, total);
+
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
return BGP_ATTR_PARSE_PROCEED;
@@ -1269,7 +1317,7 @@ bgp_attr_community (struct peer *peer, bgp_size_t length,
/* Originator ID attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
+bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag)
{
if (length != 4)
@@ -1277,11 +1325,11 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length);
return bgp_attr_malformed (peer, BGP_ATTR_ORIGINATOR_ID, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ NULL, 0);
}
- (bgp_attr_extra_get (attr))->originator_id.s_addr
+ (bgp_attr_extra_get (attr))->originator_id.s_addr
= stream_get_ipv4 (peer->ibuf);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
@@ -1291,7 +1339,7 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
/* Cluster list attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
+bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag)
{
/* Check length. */
@@ -1300,13 +1348,13 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length);
return bgp_attr_malformed (peer, BGP_ATTR_CLUSTER_LIST, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- NULL, 0);
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ NULL, 0);
}
- (bgp_attr_extra_get (attr))->cluster
+ (bgp_attr_extra_get (attr))->cluster
= cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length);
-
+
/* XXX: Fix cluster_parse to use stream API and then remove this */
stream_forward_getp (peer->ibuf, length);
@@ -1316,7 +1364,7 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
}
/* Multiprotocol reachability information parse. */
-int
+extern bgp_attr_parse_ret_t
bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
struct bgp_nlri *mp_update)
{
@@ -1327,35 +1375,35 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
int ret;
struct stream *s;
struct attr_extra *attre = bgp_attr_extra_get(attr);
-
+
/* Set end of packet. */
s = BGP_INPUT(peer);
start = stream_get_getp(s);
-
+
/* safe to read statically sized header? */
#define BGP_MP_REACH_MIN_SIZE 5
#define LEN_LEFT (length - (stream_get_getp(s) - start))
if ((length > STREAM_READABLE(s)) || (length < BGP_MP_REACH_MIN_SIZE))
{
- zlog_info ("%s: %s sent invalid length, %lu",
+ zlog_info ("%s: %s sent invalid length, %lu",
__func__, peer->host, (unsigned long)length);
return BGP_ATTR_PARSE_ERROR;
}
-
+
/* Load AFI, SAFI. */
afi = stream_getw (s);
safi = stream_getc (s);
/* Get nexthop length. */
attre->mp_nexthop_len = stream_getc (s);
-
+
if (LEN_LEFT < attre->mp_nexthop_len)
{
- zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute",
+ zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute",
__func__, peer->host, attre->mp_nexthop_len);
return BGP_ATTR_PARSE_ERROR;
}
-
+
/* Nexthop length check. */
switch (attre->mp_nexthop_len)
{
@@ -1388,7 +1436,9 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
char buf2[INET6_ADDRSTRLEN];
if (BGP_DEBUG (update, UPDATE_IN))
- zlog_debug ("%s got two nexthop %s %s but second one is not a link-local nexthop", peer->host,
+ zlog_debug ("%s got two nexthop %s %s "
+ "but second one is not a link-local nexthop",
+ peer->host,
inet_ntop (AF_INET6, &attre->mp_nexthop_global,
buf1, INET6_ADDRSTRLEN),
inet_ntop (AF_INET6, &attre->mp_nexthop_local,
@@ -1399,7 +1449,7 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
break;
#endif /* HAVE_IPV6 */
default:
- zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d",
+ zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d",
__func__, peer->host, attre->mp_nexthop_len);
return BGP_ATTR_PARSE_ERROR;
}
@@ -1410,14 +1460,14 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
__func__, peer->host);
return BGP_ATTR_PARSE_ERROR;
}
-
+
{
- u_char val;
+ u_char val;
if ((val = stream_getc (s)))
zlog_warn ("%s sent non-zero value, %u, for defunct SNPA-length field",
peer->host, val);
}
-
+
/* must have nrli_len, what is left of the attribute */
nlri_len = LEN_LEFT;
if ((!nlri_len) || (nlri_len > STREAM_READABLE(s)))
@@ -1426,11 +1476,11 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
__func__, peer->host);
return BGP_ATTR_PARSE_ERROR;
}
-
+
if (safi != BGP_SAFI_VPNV4)
{
ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), nlri_len);
- if (ret < 0)
+ if (ret < 0)
{
zlog_info ("%s: (%s) NLRI doesn't pass sanity check",
__func__, peer->host);
@@ -1450,8 +1500,8 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
}
/* Multiprotocol unreachable parse */
-int
-bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length,
+extern bgp_attr_parse_ret_t
+bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length,
struct bgp_nlri *mp_withdraw)
{
struct stream *s;
@@ -1461,14 +1511,14 @@ bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length,
int ret;
s = peer->ibuf;
-
+
#define BGP_MP_UNREACH_MIN_SIZE 3
if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE))
return BGP_ATTR_PARSE_ERROR;
-
+
afi = stream_getw (s);
safi = stream_getc (s);
-
+
withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE;
if (safi != BGP_SAFI_VPNV4)
@@ -1490,16 +1540,17 @@ bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length,
/* Extended Community attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
+bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
struct attr *attr, u_char flag, u_char *startp)
{
bgp_size_t total
= length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
+
if (length == 0)
{
if (attr->extra)
attr->extra->ecommunity = NULL;
+
/* Empty extcomm doesn't seem to be invalid per se */
return BGP_ATTR_PARSE_PROCEED;
}
@@ -1508,12 +1559,12 @@ bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
/* XXX: fix ecommunity_parse to use stream API */
stream_forward_getp (peer->ibuf, length);
-
+
if (!attr->extra->ecommunity)
- return bgp_attr_malformed (peer, BGP_ATTR_EXT_COMMUNITIES,
- flag, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
- startp, total);
-
+ return bgp_attr_malformed (peer, BGP_ATTR_EXT_COMMUNITIES, flag,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ startp, total);
+
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
return BGP_ATTR_PARSE_PROCEED;
@@ -1531,9 +1582,9 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
peer->host, type, length);
-
+
if (BGP_DEBUG (events, EVENTS))
- zlog (peer->log, LOG_DEBUG,
+ zlog (peer->log, LOG_DEBUG,
"Unknown attribute type %d length %d is received", type, length);
/* Forward read pointer of input stream. */
@@ -1549,8 +1600,8 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
{
return bgp_attr_malformed (peer, type, flag,
- BGP_NOTIFY_UPDATE_UNREC_ATTR,
- startp, total);
+ BGP_NOTIFY_UPDATE_UNREC_ATTR,
+ startp, total);
}
/* Unrecognized non-transitive optional attributes must be quietly
@@ -1571,7 +1622,7 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
transit = attre->transit;
if (transit->val)
- transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val,
+ transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val,
transit->length + total);
else
transit->val = XMALLOC (MTYPE_TRANSIT_VAL, total);
@@ -1582,8 +1633,25 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
return BGP_ATTR_PARSE_PROCEED;
}
-/* Read attribute of update packet. This function is called from
- bgp_update() in bgpd.c. */
+/*------------------------------------------------------------------------------
+ * Read attribute of update packet.
+ *
+ * This function is called from bgp_update() in bgpd.c.
+ *
+ * NB: expects the structures pointed to by:
+ *
+ * attr
+ * mp_update
+ * mp_withdraw
+ *
+ * to be zeroised on entry to this function.
+ *
+ * Any elements in attr or attr->extra will be internalised as they are set.
+ * (So their reference counts will *not* be zero.)
+ *
+ * However, the attr object itself is NOT internalised.
+ * (So its reference count will be zero.)
+ */
bgp_attr_parse_ret_t
bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw)
@@ -1606,7 +1674,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
/* End pointer of BGP attribute. */
endp = BGP_INPUT_PNT (peer) + size;
-
+
/* Get attributes to the end of attribute length. */
while (BGP_INPUT_PNT (peer) < endp)
{
@@ -1614,14 +1682,15 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
if (endp - BGP_INPUT_PNT (peer) < BGP_ATTR_MIN_LEN)
{
/* XXX warning: long int format, int arg (arg 5) */
- zlog (peer->log, LOG_WARNING,
+ zlog (peer->log, LOG_WARNING,
"%s error BGP attribute length %lu is smaller than min len",
peer->host,
(unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ if (as4_path != NULL)
+ aspath_unintern (&as4_path);
return BGP_ATTR_PARSE_ERROR;
}
@@ -1634,42 +1703,44 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN)
&& ((endp - startp) < (BGP_ATTR_MIN_LEN + 1)))
{
- zlog (peer->log, LOG_WARNING,
+ zlog (peer->log, LOG_WARNING,
"%s Extended length set, but just %lu bytes of attr header",
peer->host,
(unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ if (as4_path != NULL)
+ aspath_unintern (&as4_path);
return BGP_ATTR_PARSE_ERROR;
}
- /* Check extended attribue length bit. */
+ /* Check extended attribute length bit. */
if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN))
length = stream_getw (BGP_INPUT (peer));
else
length = stream_getc (BGP_INPUT (peer));
-
- /* If any attribute appears more than once in the UPDATE
- message, then the Error Subcode is set to Malformed Attribute
- List. */
+ /* If any attribute appears more than once in the UPDATE
+ * message, then the Error Subcode is set to Malformed Attribute
+ * List.
+ */
if (CHECK_BITMAP (seen, type))
{
zlog (peer->log, LOG_WARNING,
"%s error BGP attribute type %d appears twice in a message",
peer->host, type);
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MAL_ATTR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ if (as4_path != NULL)
+ aspath_unintern (&as4_path);
return BGP_ATTR_PARSE_ERROR;
}
/* Set type to bitmap to check duplicate attribute. `type' is
- unsigned char so it never overflow bitmap range. */
-
+ * unsigned char so it never overflow bitmap range.
+ */
SET_BITMAP (seen, type);
/* Overflow check. */
@@ -1677,11 +1748,15 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
if (attr_endp > endp)
{
- zlog (peer->log, LOG_WARNING,
- "%s BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp);
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ zlog (peer->log, LOG_WARNING,
+ "%s BGP type %d length %d is too large, "
+ "attribute total length is %d. attr_endp is %p. endp is %p",
+ peer->host, type, length, size, attr_endp, endp);
+
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ if (as4_path != NULL)
+ aspath_unintern (&as4_path);
return BGP_ATTR_PARSE_ERROR;
}
@@ -1692,12 +1767,14 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
ret = bgp_attr_origin (peer, length, attr, flag, startp);
break;
case BGP_ATTR_AS_PATH:
- ret = bgp_attr_aspath (peer, length, attr, flag, startp);
- break;
+ ret = bgp_attr_aspath (peer, &attr->aspath, length, attr, flag,
+ startp, BGP_ATTR_AS_PATH);
+ break;
case BGP_ATTR_AS4_PATH:
- ret = bgp_attr_as4_path (peer, length, attr, flag, startp, &as4_path);
+ ret = bgp_attr_aspath (peer, &as4_path, length, attr, flag,
+ startp, BGP_ATTR_AS4_PATH);
break;
- case BGP_ATTR_NEXT_HOP:
+ case BGP_ATTR_NEXT_HOP:
ret = bgp_attr_nexthop (peer, length, attr, flag, startp);
break;
case BGP_ATTR_MULTI_EXIT_DISC:
@@ -1714,7 +1791,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
break;
case BGP_ATTR_AS4_AGGREGATOR:
ret = bgp_attr_as4_aggregator (peer, length, attr, flag,
- &as4_aggregator,
+ &as4_aggregator,
&as4_aggregator_addr);
break;
case BGP_ATTR_COMMUNITIES:
@@ -1739,101 +1816,109 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
ret = bgp_attr_unknown (peer, attr, flag, type, length, startp);
break;
}
-
+
/* If hard error occured immediately return to the caller. */
if (ret == BGP_ATTR_PARSE_ERROR)
{
zlog (peer->log, LOG_WARNING,
- "%s: Attribute %s, parse error",
- peer->host,
+ "%s: Attribute %s, parse error",
+ peer->host,
LOOKUP (attr_str, type));
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MAL_ATTR);
- if (as4_path)
+
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ if (as4_path != NULL)
aspath_unintern (&as4_path);
return ret;
}
+
if (ret == BGP_ATTR_PARSE_WITHDRAW)
{
-
zlog (peer->log, LOG_WARNING,
"%s: Attribute %s, parse error - treating as withdrawal",
peer->host,
LOOKUP (attr_str, type));
- if (as4_path)
+
+ if (as4_path != NULL)
aspath_unintern (&as4_path);
return ret;
}
-
+
/* Check the fetched length. */
if (BGP_INPUT_PNT (peer) != attr_endp)
{
- zlog (peer->log, LOG_WARNING,
- "%s: BGP attribute %s, fetch error",
+ zlog (peer->log, LOG_WARNING,
+ "%s: BGP attribute %s, fetch error",
peer->host, LOOKUP (attr_str, type));
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
- if (as4_path)
+
+ if (type == BGP_ATTR_AS_PATH)
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s: is %sAS4",
+ peer->host,
+ PEER_CAP_AS4_USE(peer) ? "" : "NOT ") ;
+ } ;
+
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ if (as4_path != NULL)
aspath_unintern (&as4_path);
- return BGP_ATTR_PARSE_ERROR;
+ return BGP_ATTR_PARSE_ERROR;
}
}
/* Check final read pointer is same as end pointer. */
if (BGP_INPUT_PNT (peer) != endp)
{
- zlog (peer->log, LOG_WARNING,
+ zlog (peer->log, LOG_WARNING,
"%s BGP attribute %s, length mismatch",
peer->host, LOOKUP (attr_str, type));
- bgp_notify_send (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
- if (as4_path)
+
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ if (as4_path != NULL)
aspath_unintern (&as4_path);
return BGP_ATTR_PARSE_ERROR;
}
- /*
+ /*
* At this place we can see whether we got AS4_PATH and/or
* AS4_AGGREGATOR from a 16Bit peer and act accordingly.
* We can not do this before we've read all attributes because
* the as4 handling does not say whether AS4_PATH has to be sent
* after AS_PATH or not - and when AS4_AGGREGATOR will be send
* in relationship to AGGREGATOR.
+ *
* So, to be defensive, we are not relying on any order and read
* all attributes first, including these 32bit ones, and now,
* afterwards, we look what and if something is to be done for as4.
*/
- if (bgp_attr_munge_as4_attrs (peer, attr, flag, as4_path,
- as4_aggregator, &as4_aggregator_addr))
- {
- if (as4_path)
- aspath_unintern (&as4_path);
- return BGP_ATTR_PARSE_ERROR;
- }
+ ret = bgp_attr_munge_as4_attrs (peer, attr, flag, as4_path,
+ as4_aggregator, &as4_aggregator_addr) ;
/* At this stage, we have done all fiddling with as4, and the
* resulting info is in attr->aggregator resp. attr->aspath
* so we can chuck as4_aggregator and as4_path alltogether in
* order to save memory
- */
- if (as4_path)
- {
- aspath_unintern (&as4_path); /* unintern - it is in the hash */
- /* The flag that we got this is still there, but that does not
- * do any trouble
- */
- }
- /*
+ *
* The "rest" of the code does nothing with as4_aggregator.
* there is no memory attached specifically which is not part
* of the attr.
* so ignoring just means do nothing.
*/
- /*
- * Finally do the checks on the aspath we did not do yet
+ if (as4_path != NULL)
+ {
+ /* The flag that we got this is still there, but that does not
+ * do any trouble
+ */
+ aspath_unintern (&as4_path);
+ as4_path = NULL ;
+ } ;
+
+ if (ret != BGP_ATTR_PARSE_PROCEED)
+ return ret;
+
+ /* Finally do the checks on the aspath we did not do yet
* because we waited for a potentially synthesized aspath.
*/
if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
@@ -1855,7 +1940,7 @@ int
bgp_attr_check (struct peer *peer, struct attr *attr)
{
u_char type = 0;
-
+
if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
type = BGP_ATTR_ORIGIN;
@@ -1871,18 +1956,18 @@ bgp_attr_check (struct peer *peer, struct attr *attr)
if (type)
{
- zlog (peer->log, LOG_WARNING,
+ zlog (peer->log, LOG_WARNING,
"%s Missing well-known attribute %d.",
peer->host, type);
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MISS_ATTR,
- &type, 1);
+
+ bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MISS_ATTR,
+ &type, 1);
return BGP_ATTR_PARSE_ERROR;
}
return BGP_ATTR_PARSE_PROCEED;
}
-
+
int stream_put_prefix (struct stream *, struct prefix *);
/* Make attribute packet. */
@@ -1897,7 +1982,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
struct aspath *aspath;
int send_as4_path = 0;
int send_as4_aggregator = 0;
- int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0;
+ int use32bit = PEER_CAP_AS4_USE(peer) ;
+ bgp_peer_sort_t sort ;
if (! bgp)
bgp = bgp_get_default ();
@@ -1914,11 +2000,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
/* AS path attribute. */
/* If remote-peer is EBGP */
- if (peer_sort (peer) == BGP_PEER_EBGP
+ sort = peer_sort(peer) ;
+
+ if (sort == BGP_PEER_EBGP
&& (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
|| attr->aspath->segments == NULL)
&& (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)))
- {
+ {
aspath = aspath_dup (attr->aspath);
if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
@@ -1935,7 +2023,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
aspath = aspath_add_seq (aspath, peer->change_local_as);
}
}
- else if (peer_sort (peer) == BGP_PEER_CONFED)
+ else if (sort == BGP_PEER_CONFED)
{
/* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */
aspath = aspath_dup (attr->aspath);
@@ -1958,13 +2046,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
aspath_sizep = stream_get_endp (s);
stream_putw (s, 0);
stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, use32bit));
-
- /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs
+
+ /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs
* in the path
*/
if (!use32bit && aspath_has_as4 (aspath))
send_as4_path = 1; /* we'll do this later, at the correct place */
-
+
/* Nexthop attribute. */
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP)
{
@@ -1992,8 +2080,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
}
/* Local preference. */
- if (peer_sort (peer) == BGP_PEER_IBGP ||
- peer_sort (peer) == BGP_PEER_CONFED)
+ if ((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED))
{
stream_putc (s, BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_LOCAL_PREF);
@@ -2013,11 +2100,11 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
{
assert (attr->extra);
-
+
/* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_AGGREGATOR);
-
+
if (use32bit)
{
/* AS4 capable peer */
@@ -2028,12 +2115,12 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
{
/* 2-byte AS peer */
stream_putc (s, 6);
-
+
/* Is ASN representable in 2-bytes? Or must AS_TRANS be used? */
if ( attr->extra->aggregator_as > 65535 )
{
stream_putw (s, BGP_AS_TRANS);
-
+
/* we have to send AS4_AGGREGATOR, too.
* we'll do that later in order to send attributes in ascending
* order.
@@ -2047,18 +2134,19 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
}
/* Community attribute. */
- if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
&& (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
{
if (attr->community->size * 4 > 255)
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc (s, BGP_ATTR_COMMUNITIES);
stream_putw (s, attr->community->size * 4);
}
else
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_COMMUNITIES);
stream_putc (s, attr->community->size * 4);
}
@@ -2066,9 +2154,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
}
/* Route Reflector. */
- if (peer_sort (peer) == BGP_PEER_IBGP
- && from
- && peer_sort (from) == BGP_PEER_IBGP)
+ if ( (sort == BGP_PEER_IBGP) && (from != NULL)
+ && (peer_sort (from) == BGP_PEER_IBGP) )
{
/* Originator ID. */
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
@@ -2077,13 +2164,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
stream_put_in_addr (s, &attr->extra->originator_id);
- else
+ else
stream_put_in_addr (s, &from->remote_id);
/* Cluster list. */
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
stream_putc (s, BGP_ATTR_CLUSTER_LIST);
-
+
if (attr->extra && attr->extra->cluster)
{
stream_putc (s, attr->extra->cluster->length + 4);
@@ -2092,7 +2179,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_put_in_addr (s, &bgp->cluster_id);
else
stream_put_in_addr (s, &bgp->router_id);
- stream_put (s, attr->extra->cluster->list,
+ stream_put (s, attr->extra->cluster->list,
attr->extra->cluster->length);
}
else
@@ -2112,9 +2199,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
{
unsigned long sizep;
struct attr_extra *attre = attr->extra;
-
+
assert (attr->extra);
-
+
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
sizep = stream_get_endp (s);
@@ -2131,7 +2218,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_put (s, &attre->mp_nexthop_global, 16);
stream_put (s, &attre->mp_nexthop_local, 16);
}
-
+
/* SNPA */
stream_putc (s, 0);
@@ -2197,25 +2284,25 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
}
/* Extended Communities attribute. */
- if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
&& (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
{
struct attr_extra *attre = attr->extra;
-
+
assert (attre);
-
- if (peer_sort (peer) == BGP_PEER_IBGP
- || peer_sort (peer) == BGP_PEER_CONFED)
+
+ if ((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED))
{
if (attre->ecommunity->size * 8 > 255)
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
stream_putw (s, attre->ecommunity->size * 8);
}
else
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
stream_putc (s, attre->ecommunity->size * 8);
}
@@ -2243,13 +2330,14 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
{
if (ecom_tr_size * 8 > 255)
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
stream_putw (s, ecom_tr_size * 8);
}
else
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
stream_putc (s, ecom_tr_size * 8);
}
@@ -2279,12 +2367,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
* Hm, I wonder... confederation things *should* only be at
* the beginning of an aspath, right? Then we should use
* aspath_delete_confed_seq for this, because it is already
- * there! (JK)
+ * there! (JK)
* Folks, talk to me: what is reasonable here!?
*/
aspath = aspath_delete_confed_seq (aspath);
- stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_FLAG_TRANS |BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc (s, BGP_ATTR_AS4_PATH);
aspath_sizep = stream_get_endp (s);
stream_putw (s, 0);
@@ -2294,7 +2383,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
if (aspath != attr->aspath)
aspath_free (aspath);
- if ( send_as4_aggregator )
+ if ( send_as4_aggregator )
{
assert (attr->extra);
@@ -2308,7 +2397,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_putl (s, attr->extra->aggregator_as);
stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
}
-
+
/* Unknown transit attribute. */
if (attr->extra && attr->extra->transit)
stream_put (s, attr->extra->transit->val, attr->extra->transit->length);
@@ -2388,7 +2477,7 @@ bgp_attr_finish (void)
/* Make attribute packet. */
void
-bgp_dump_routes_attr (struct stream *s, struct attr *attr,
+bgp_dump_routes_attr (struct stream *s, struct attr *attr,
struct prefix *prefix)
{
unsigned long cp;
@@ -2409,12 +2498,12 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
stream_putc (s, attr->origin);
aspath = attr->aspath;
-
+
stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
stream_putc (s, BGP_ATTR_AS_PATH);
aspath_lenp = stream_get_endp (s);
stream_putw (s, 0);
-
+
stream_putw_at (s, aspath_lenp, aspath_put (s, aspath, 1));
/* Nexthop attribute. */
@@ -2461,7 +2550,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
{
assert (attr->extra);
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_AGGREGATOR);
stream_putc (s, 8);
stream_putl (s, attr->extra->aggregator_as);
@@ -2473,7 +2562,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
{
if (attr->community->size * 4 > 255)
{
- stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc (s, BGP_ATTR_COMMUNITIES);
stream_putw (s, attr->community->size * 4);
}
@@ -2493,7 +2583,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
{
int sizep;
struct attr_extra *attre = attr->extra;
-
+
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
stream_putc(s, BGP_ATTR_MP_REACH_NLRI);
sizep = stream_get_endp (s);
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index c0112514..8cca316d 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -1,4 +1,4 @@
-/* BGP attributes.
+/* BGP attributes.
Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
This file is part of GNU Zebra.
@@ -21,6 +21,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ATTR_H
#define _QUAGGA_BGP_ATTR_H
+#include <stdbool.h>
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgpd.h"
+
/* Simple bit mapping. */
#define BITMAP_NBBY 8
@@ -61,28 +66,28 @@ struct attr_extra
/* Extended Communities attribute. */
struct ecommunity *ecommunity;
-
+
/* Route-Reflector Cluster attribute */
struct cluster_list *cluster;
-
+
/* Unknown transitive attribute. */
struct transit *transit;
struct in_addr mp_nexthop_global_in;
struct in_addr mp_nexthop_local_in;
-
+
/* Aggregator Router ID attribute */
struct in_addr aggregator_addr;
-
+
/* Route Reflector Originator attribute */
struct in_addr originator_id;
-
+
/* Local weight, not actually an attribute */
u_int32_t weight;
-
+
/* Aggregator ASN */
as_t aggregator_as;
-
+
/* MP Nexthop length */
u_char mp_nexthop_len;
};
@@ -94,22 +99,22 @@ struct attr
struct aspath *aspath;
/* Community structure */
- struct community *community;
-
+ struct community *community;
+
/* Lazily allocated pointer to extra attributes */
struct attr_extra *extra;
-
+
/* Reference count of this attribute. */
unsigned long refcnt;
/* Flag of attribute is set or not. */
u_int32_t flag;
-
+
/* Apart from in6_addr, the remaining static attributes */
struct in_addr nexthop;
u_int32_t med;
u_int32_t local_pref;
-
+
/* Path origin attribute */
u_char origin;
};
@@ -133,9 +138,9 @@ struct transit
#define ATTR_FLAG_BIT(X) (1 << ((X) - 1))
typedef enum {
- BGP_ATTR_PARSE_PROCEED = 0,
- BGP_ATTR_PARSE_ERROR = -1,
- BGP_ATTR_PARSE_WITHDRAW = -2,
+ BGP_ATTR_PARSE_PROCEED = 0,
+ BGP_ATTR_PARSE_ERROR = -1,
+ BGP_ATTR_PARSE_WITHDRAW = -2,
} bgp_attr_parse_ret_t;
/* Prototypes. */
@@ -149,20 +154,20 @@ extern struct attr_extra *bgp_attr_extra_get (struct attr *);
extern void bgp_attr_extra_free (struct attr *);
extern void bgp_attr_dup (struct attr *, struct attr *);
extern struct attr *bgp_attr_intern (struct attr *attr);
-extern void bgp_attr_unintern_sub (struct attr *);
+extern void bgp_attr_unintern_sub (struct attr *attr, bool free_extra) ;
extern void bgp_attr_unintern (struct attr **);
extern void bgp_attr_flush (struct attr *);
extern struct attr *bgp_attr_default_set (struct attr *attr, u_char);
extern struct attr *bgp_attr_default_intern (u_char);
extern struct attr *bgp_attr_aggregate_intern (struct bgp *, u_char,
- struct aspath *,
+ struct aspath *,
struct community *, int as_set);
-extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *,
- struct stream *, struct attr *,
- struct prefix *, afi_t, safi_t,
+extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *,
+ struct stream *, struct attr *,
+ struct prefix *, afi_t, safi_t,
struct peer *, struct prefix_rd *, u_char *);
-extern bgp_size_t bgp_packet_withdraw (struct peer *peer, struct stream *s,
- struct prefix *p, afi_t, safi_t,
+extern bgp_size_t bgp_packet_withdraw (struct peer *peer, struct stream *s,
+ struct prefix *p, afi_t, safi_t,
struct prefix_rd *, u_char *);
extern void bgp_dump_routes_attr (struct stream *, struct attr *,
struct prefix *);
@@ -180,8 +185,9 @@ extern void cluster_unintern (struct cluster_list *);
void transit_unintern (struct transit *);
/* Exported for unit-test purposes only */
-extern int bgp_mp_reach_parse (struct peer *, bgp_size_t, struct attr *,
- struct bgp_nlri *);
-extern int bgp_mp_unreach_parse (struct peer *, bgp_size_t, struct bgp_nlri *);
+extern bgp_attr_parse_ret_t bgp_mp_reach_parse (struct peer *,
+ bgp_size_t, struct attr *, struct bgp_nlri *);
+extern bgp_attr_parse_ret_t bgp_mp_unreach_parse (struct peer *, bgp_size_t,
+ struct bgp_nlri *);
#endif /* _QUAGGA_BGP_ATTR_H */
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 6c9976e3..9c57cb65 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -30,10 +30,27 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_regex.h"
#include "bgpd/bgp_clist.h"
-
+
+
+/* Community-list handler.
+ *
+ * Contains a set of community and extcommunity filters.
+ *
+ * NB: have separate name-spaces for community-list and extcommunity-list.
+*/
+struct community_list_handler
+{
+ /* Community-list. */
+ struct symbol_table community_list ;
+
+ /* Exteded community-list. */
+ struct symbol_table extcommunity_list ;
+};
+
+
/* Lookup master structure for community-list or
extcommunity-list. */
-struct community_list_master *
+struct symbol_table*
community_list_master_lookup (struct community_list_handler *ch, int master)
{
if (ch)
@@ -95,127 +112,22 @@ community_list_new (void)
static void
community_list_free (struct community_list *list)
{
- if (list->name)
- XFREE (MTYPE_COMMUNITY_LIST_NAME, list->name);
XFREE (MTYPE_COMMUNITY_LIST, list);
}
-
-static struct community_list *
-community_list_insert (struct community_list_handler *ch,
- const char *name, int master)
-{
- size_t i;
- long number;
- struct community_list *new;
- struct community_list *point;
- struct community_list_list *list;
- struct community_list_master *cm;
-
- /* Lookup community-list master. */
- cm = community_list_master_lookup (ch, master);
- if (!cm)
- return NULL;
-
- /* Allocate new community_list and copy given name. */
- new = community_list_new ();
- new->name = XSTRDUP (MTYPE_COMMUNITY_LIST_NAME, name);
-
- /* If name is made by all digit character. We treat it as
- number. */
- for (number = 0, i = 0; i < strlen (name); i++)
- {
- if (isdigit ((int) name[i]))
- number = (number * 10) + (name[i] - '0');
- else
- break;
- }
-
- /* In case of name is all digit character */
- if (i == strlen (name))
- {
- new->sort = COMMUNITY_LIST_NUMBER;
-
- /* Set access_list to number list. */
- list = &cm->num;
-
- for (point = list->head; point; point = point->next)
- if (atol (point->name) >= number)
- break;
- }
- else
- {
- new->sort = COMMUNITY_LIST_STRING;
-
- /* Set access_list to string list. */
- list = &cm->str;
-
- /* Set point to insertion point. */
- for (point = list->head; point; point = point->next)
- if (strcmp (point->name, name) >= 0)
- break;
- }
-
- /* Link to upper list. */
- new->parent = list;
-
- /* In case of this is the first element of master. */
- if (list->head == NULL)
- {
- list->head = list->tail = new;
- return new;
- }
-
- /* In case of insertion is made at the tail of access_list. */
- if (point == NULL)
- {
- new->prev = list->tail;
- list->tail->next = new;
- list->tail = new;
- return new;
- }
-
- /* In case of insertion is made at the head of access_list. */
- if (point == list->head)
- {
- new->next = list->head;
- list->head->prev = new;
- list->head = new;
- return new;
- }
-
- /* Insertion is made at middle of the access_list. */
- new->next = point;
- new->prev = point->prev;
-
- if (point->prev)
- point->prev->next = new;
- point->prev = new;
-
- return new;
-}
-
struct community_list *
community_list_lookup (struct community_list_handler *ch,
const char *name, int master)
{
- struct community_list *list;
- struct community_list_master *cm;
+ struct symbol_table* table;
if (!name)
return NULL;
- cm = community_list_master_lookup (ch, master);
- if (!cm)
+ table = community_list_master_lookup (ch, master);
+ if (!table)
return NULL;
- for (list = cm->num.head; list; list = list->next)
- if (strcmp (list->name, name) == 0)
- return list;
- for (list = cm->str.head; list; list = list->next)
- if (strcmp (list->name, name) == 0)
- return list;
-
- return NULL;
+ return symbol_get_value(symbol_seek(table, name)) ;
}
static struct community_list *
@@ -223,37 +135,51 @@ community_list_get (struct community_list_handler *ch,
const char *name, int master)
{
struct community_list *list;
+ struct symbol_table* table;
+ struct symbol* sym ;
- list = community_list_lookup (ch, name, master);
+ if (!name)
+ return NULL;
+
+ table = community_list_master_lookup (ch, master);
+ if (!table)
+ return NULL;
+
+ sym = symbol_find(table, name) ;
+ list = symbol_get_value(sym) ;
if (!list)
- list = community_list_insert (ch, name, master);
+ {
+ /* Allocate new community_list and tie symbol and list together. */
+ list = community_list_new ();
+
+ symbol_set_value(sym, list) ;
+ list->sym = symbol_inc_ref(sym) ;
+ }
+
return list;
}
static void
community_list_delete (struct community_list *list)
{
- struct community_list_list *clist;
struct community_entry *entry, *next;
+ /* Easy if the list is not defined ! */
+ if (list == NULL)
+ return ;
+
+ /* Free body of list */
for (entry = list->head; entry; entry = next)
{
next = entry->next;
community_entry_free (entry);
}
- clist = list->parent;
-
- if (list->next)
- list->next->prev = list->prev;
- else
- clist->tail = list->prev;
-
- if (list->prev)
- list->prev->next = list->next;
- else
- clist->head = list->next;
+ /* Kill value in related symbol and drop reference. */
+ symbol_unset_value(list->sym) ;
+ list->sym = symbol_dec_ref(list->sym) ;
+ /* Free community list structure */
community_list_free (list);
}
@@ -262,7 +188,7 @@ community_list_empty_p (struct community_list *list)
{
return (list->head == NULL && list->tail == NULL) ? 1 : 0;
}
-
+
/* Add community-list entry to the list. */
static void
community_list_entry_add (struct community_list *list,
@@ -329,7 +255,7 @@ community_list_entry_lookup (struct community_list *list, const void *arg,
}
return NULL;
}
-
+
/* Internal function to perform regular expression match for community
attribute. */
static int
@@ -514,19 +440,19 @@ community_list_match_delete (struct community *com,
{
if (entry->any)
{
- if (entry->direct == COMMUNITY_PERMIT)
+ if (entry->direct == COMMUNITY_PERMIT)
{
/* This is a tricky part. Currently only
* route_set_community_delete() uses this function. In the
* function com->size is zero, it free the community
- * structure.
+ * structure.
*/
com->size = 0;
}
return com;
}
- if ((entry->style == COMMUNITY_LIST_STANDARD)
+ if ((entry->style == COMMUNITY_LIST_STANDARD)
&& (community_include (entry->u.com, COMMUNITY_INTERNET)
|| community_match (com, entry->u.com) ))
{
@@ -590,7 +516,7 @@ community_list_dup_check (struct community_list *list,
}
return 0;
}
-
+
/* Set community-list. */
int
community_list_set (struct community_list_handler *ch,
@@ -653,7 +579,7 @@ community_list_set (struct community_list_handler *ch,
community-list entry belongs to the specified name. */
int
community_list_unset (struct community_list_handler *ch,
- const char *name, const char *str,
+ const char *name, const char *str,
int direct, int style)
{
struct community_entry *entry = NULL;
@@ -702,7 +628,7 @@ community_list_unset (struct community_list_handler *ch,
/* Set extcommunity-list. */
int
extcommunity_list_set (struct community_list_handler *ch,
- const char *name, const char *str,
+ const char *name, const char *str,
int direct, int style)
{
struct community_entry *entry = NULL;
@@ -772,7 +698,7 @@ extcommunity_list_set (struct community_list_handler *ch,
extcommunity-list entry belongs to the specified name. */
int
extcommunity_list_unset (struct community_list_handler *ch,
- const char *name, const char *str,
+ const char *name, const char *str,
int direct, int style)
{
struct community_entry *entry = NULL;
@@ -819,33 +745,34 @@ extcommunity_list_unset (struct community_list_handler *ch,
}
/* Initializa community-list. Return community-list handler. */
-struct community_list_handler *
+struct community_list_handler*
community_list_init (void)
{
struct community_list_handler *ch;
ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER,
sizeof (struct community_list_handler));
+
+ symbol_table_init_new(&ch->community_list, &ch, 20, 200, NULL, NULL) ;
+ symbol_table_init_new(&ch->extcommunity_list, &ch, 20, 200, NULL, NULL) ;
+
return ch;
}
-/* Terminate community-list. */
+/* Terminate community-list.
+ *
+ * Any references to community lists must be released by the owners of those
+ * references.
+ */
void
community_list_terminate (struct community_list_handler *ch)
{
- struct community_list_master *cm;
- struct community_list *list;
+ struct community_list *list ;
- cm = &ch->community_list;
- while ((list = cm->num.head) != NULL)
- community_list_delete (list);
- while ((list = cm->str.head) != NULL)
- community_list_delete (list);
+ while ((list = symbol_table_ream_keep(&ch->community_list)))
+ community_list_delete(list) ;
- cm = &ch->extcommunity_list;
- while ((list = cm->num.head) != NULL)
- community_list_delete (list);
- while ((list = cm->str.head) != NULL)
- community_list_delete (list);
+ while ((list = symbol_table_ream_keep(&ch->extcommunity_list)))
+ community_list_delete(list) ;
XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);
}
diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h
index 5dcb3b4c..1d9b9189 100644
--- a/bgpd/bgp_clist.h
+++ b/bgpd/bgp_clist.h
@@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_CLIST_H
#define _QUAGGA_BGP_CLIST_H
+#include "symtab.h"
+
/* Master Community-list. */
#define COMMUNITY_LIST_MASTER 0
#define EXTCOMMUNITY_LIST_MASTER 1
@@ -30,8 +32,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#define COMMUNITY_PERMIT 1
/* Number and string based community-list name. */
-#define COMMUNITY_LIST_STRING 0
-#define COMMUNITY_LIST_NUMBER 1
+//#define COMMUNITY_LIST_STRING 0
+//#define COMMUNITY_LIST_NUMBER 1
/* Community-list entry types. */
#define COMMUNITY_LIST_STANDARD 0 /* Standard community-list. */
@@ -42,18 +44,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
/* Community-list. */
struct community_list
{
- /* Name of the community-list. */
- char *name;
+ /* Pointer to symbol entry of the community-list. */
+ struct symbol* sym ;
/* String or number. */
- int sort;
-
- /* Link to upper list. */
- struct community_list_list *parent;
-
- /* Linked list for other community-list. */
- struct community_list *next;
- struct community_list *prev;
+//int sort;
/* Community-list entry in this community-list. */
struct community_entry *head;
@@ -89,30 +84,11 @@ struct community_entry
regex_t *reg;
};
-/* Linked list of community-list. */
-struct community_list_list
-{
- struct community_list *head;
- struct community_list *tail;
-};
-
-/* Master structure of community-list and extcommunity-list. */
-struct community_list_master
-{
- struct community_list_list num;
- struct community_list_list str;
-};
-
-/* Community-list handler. community_list_init() returns this
- structure as handler. */
-struct community_list_handler
-{
- /* Community-list. */
- struct community_list_master community_list;
-
- /* Exteded community-list. */
- struct community_list_master extcommunity_list;
-};
+/* Community-list handler.
+ * community_list_init() returns this structure as handler -- contains a
+ * distinct set of community-list and extcommunity-list filters.
+ */
+struct community_list_handler ;
/* Error code of community-list. */
#define COMMUNITY_LIST_ERR_CANT_FIND_LIST -1
@@ -140,7 +116,7 @@ extern int extcommunity_list_unset (struct community_list_handler *ch,
const char *name, const char *str,
int direct, int style);
-extern struct community_list_master *
+extern struct symbol_table*
community_list_master_lookup (struct community_list_handler *, int);
extern struct community_list *
diff --git a/bgpd/bgp_common.c b/bgpd/bgp_common.c
new file mode 100644
index 00000000..cfee5ca2
--- /dev/null
+++ b/bgpd/bgp_common.c
@@ -0,0 +1,284 @@
+/* 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
+ * and qafx_num => pAF
+ */
+
+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,
+ } ;
+
+const sa_family_t sa_family_map[] =
+ {
+ [qafx_ipv4_unicast] = AF_INET,
+ [qafx_ipv4_multicast] = AF_INET,
+ [qafx_ipv4_mpls_vpn] = AF_INET,
+ [qafx_ipv6_unicast] = AF_INET6,
+ [qafx_ipv6_multicast] = AF_INET6,
+ [qafx_ipv6_mpls_vpn] = AF_INET6,
+ [qafx_num_other] = AF_UNSPEC,
+ } ;
+
+/*==============================================================================
+ * Convert iAFI/iSAFI => qafx_num_t -- tolerates unknown/reserved
+ * and qAFI/qSAFI => qafx_num_t -- tolerates undef, but not unknown
+ */
+
+/*------------------------------------------------------------------------------
+ * 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") ;
+} ;
+
+/*==============================================================================
+ * Convert iAFI/iSAFI => qafx_bit_t -- tolerates unknown/reserved
+ * and qAFI/qSAFI => qafx_bit_t -- tolerates undef, but not unknown
+ */
+
+/*------------------------------------------------------------------------------
+ * iAFI/iSAFI => qafx_bit_t unknowns => 0
+ * reserved => 0
+ */
+extern qafx_bit_t
+qafx_bit_from_iAFI_iSAFI(iAFI_t afi, iSAFI_t safi)
+{
+ qafx_num_t qn = qafx_num_from_iAFI_iSAFI(afi, safi) ;
+
+ if ((qn != qafx_num_undef) && (qn != qafx_num_other))
+ return qafx_bit(qn) ;
+ else
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * qAFI/qSAFI => qafx_bit_t
+ *
+ * NB: qAFI_undef with any qSAFI_xxx => 0
+ * qSAFI_undef with any qAFI_xxx => 0
+ * qSAFI_Unused qith any qAFI_xxx => 0
+ *
+ * NB: any unrecognised qAFI/qSAFI combinations => FATAL error
+ */
+extern qafx_bit_t
+qafx_bit_from_qAFI_qSAFI(qAFI_t afi, qSAFI_t safi)
+{
+ qafx_num_t qn = qafx_num_from_qAFI_qSAFI(afi, safi) ;
+
+ if ((qn != qafx_num_undef) && (qn != qafx_num_other))
+ return qafx_bit(qn) ;
+ else
+ return 0 ;
+} ;
diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h
new file mode 100644
index 00000000..a1f4df42
--- /dev/null
+++ b/bgpd/bgp_common.h
@@ -0,0 +1,494 @@
+/* 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 <stdbool.h>
+#include <sys/socket.h>
+
+#include "bgpd/bgp.h"
+#include "qafi_safi.h"
+#include "lib/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 ;
+
+/*==============================================================================
+ * Some BGP capabilities and messages have RFC and pre-RFC forms.
+ *
+ * Sometimes see both, or send RFC and/or pre-RFC forms, or track what form(s)
+ * are being used.
+ */
+typedef enum bgp_form bgp_form_t ;
+
+enum bgp_form
+{
+ bgp_form_none = 0,
+ bgp_form_pre = 1,
+ bgp_form_rfc = 2,
+ bgp_form_both = 3 /* _rfc and _pre are bits ! */
+} ;
+
+/*==============================================================================
+ * Both session and connection require these
+ */
+
+typedef enum bgp_connection_ord bgp_connection_ord_t ;
+enum bgp_connection_ord
+{
+ bgp_connection_primary = 0,
+ bgp_connection_secondary = 1,
+
+ bgp_connection_count = 2
+} ;
+
+typedef enum bgp_session_states bgp_session_state_t ;
+enum bgp_session_states
+{
+ bgp_session_min_state = 0,
+
+ bgp_session_sIdle = 0, /* session contents "unset" */
+
+ bgp_session_sEnabled = 1, /* attempting to connect */
+ bgp_session_sEstablished = 2,
+
+ bgp_session_sLimping = 3, /* disable message sent */
+ bgp_session_sDisabled = 4, /* disable message acknowledged */
+
+ bgp_session_max_state = 4
+} ;
+
+typedef enum bgp_session_events bgp_session_event_t ;
+enum bgp_session_events
+{
+ bgp_session_min_event = 0,
+ bgp_session_null_event = 0,
+
+ bgp_session_eEstablished, /* session state -> sEstablished */
+ bgp_session_eDisabled, /* disabled by Routeing Engine */
+
+ bgp_session_eStart, /* enter sConnect/sAccept from sIdle */
+ bgp_session_eRetry, /* loop round in sConnect/sAccept */
+
+ bgp_session_eOpen_reject, /* had to reject an OPEN message */
+ bgp_session_eInvalid_msg, /* BGP message invalid */
+ bgp_session_eFSM_error, /* unexpected BGP message received */
+ bgp_session_eNOM_recv, /* NOTIFICATION message received */
+
+ bgp_session_eExpired, /* HoldTime expired */
+ bgp_session_eTCP_dropped, /* TCP connection dropped */
+
+ bgp_session_eTCP_failed, /* TCP connection failed to come up */
+ bgp_session_eTCP_error, /* some socket level error */
+
+ bgp_session_eInvalid, /* invalid internal event */
+
+ bgp_session_max_event = bgp_session_eInvalid,
+
+ /* These are used by the FSM, but are not reported to the Routeing Engine */
+
+ bgp_session_eCollision, /* given way to sibling */
+ bgp_session_eDiscard, /* discarded by sibling */
+} ;
+
+typedef enum bgp_peer_states bgp_peer_state_t ;
+enum bgp_peer_states
+{
+ bgp_peer_min_state = 0,
+
+ bgp_peer_pIdle = 1, /* session not yet established */
+ bgp_peer_pEstablished = 2, /* session established */
+ bgp_peer_pClearing = 3, /* Clearing routes */
+ bgp_peer_pDeleting = 4, /* Deleting, lingers until lock count == 0 */
+
+ bgp_peer_max_state = 4
+} ;
+
+/*==============================================================================
+ * Other common types and ....
+ */
+
+/* AS Numbers */
+typedef uint32_t as_t ;
+typedef uint16_t as16_t ; /* we may still encounter 16 Bit asnums */
+
+
+/*==============================================================================
+ * 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, /* all first..last are "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 */
+} ;
+
+CONFIRM(qafx_num_other > qafx_num_last) ;
+CONFIRM(qafx_num_other == qafx_num_max) ;
+
+/*------------------------------------------------------------------------------
+ * 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_bits_min = 0,
+
+ qafx_set_empty = 0,
+
+ qafx_first_bit = (1 << qafx_num_first),
+ /* first..last are all "real" qafx */
+
+ 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_last_bit = (1 << qafx_num_last),
+
+ qafx_other_bit = (1 << qafx_num_other),
+
+ qafx_bits_max = (1 << qafx_count) - 1,
+
+ qafx_known_bits = (1 << (qafx_num_last + 1)) - 1
+} ;
+
+CONFIRM(qafx_known_bits == ( qafx_ipv4_unicast_bit
+ | qafx_ipv4_multicast_bit
+ | qafx_ipv4_mpls_vpn_bit
+ | qafx_ipv6_unicast_bit
+ | qafx_ipv6_multicast_bit
+ | qafx_ipv6_mpls_vpn_bit )) ;
+
+/*------------------------------------------------------------------------------
+ * 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, qSAFI, iAFI, iSAFI and pAF
+ */
+
+/*------------------------------------------------------------------------------
+ * 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] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert qafx_num_t to AF_xxx (pAF_t)
+ *
+ * Maps qafx_num_other to iSAFI_Reserved.
+ *
+ * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG).
+ */
+
+extern const sa_family_t sa_family_map[] ;
+
+Inline sa_family_t
+get_sa_family(qafx_num_t num)
+{
+ dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ;
+
+ return sa_family_map[num] ;
+} ;
+
+/*==============================================================================
+ * Conversions for iAFI/iSAFI => qafx_num_t
+ * and qAFI/qSAFI => qafx_num_t
+ *
+ * and iAFI/iSAFI => qafx_bit_t
+ * and qAFI/qSAFI => qafx_bit_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) ;
+
+extern qafx_bit_t
+qafx_bit_from_iAFI_iSAFI(iAFI_t afi, iSAFI_t safi) ;
+
+extern qafx_bit_t
+qafx_bit_from_qAFI_qSAFI(qAFI_t afi, qSAFI_t safi) ;
+
+/*==============================================================================
+ *
+ */
+
+
+
+/*==============================================================================
+ * Buffer sucking
+ *
+ *
+ */
+
+typedef uint8_t* ptr_t ;
+
+typedef struct sucker sucker_t ;
+typedef struct sucker* sucker ;
+struct sucker
+{
+ ptr_t start ; /* current known start */
+ ptr_t ptr ; /* current read pointer */
+ ptr_t end ; /* current known end */
+} ;
+
+Inline void
+suck_init(sucker sr, void* start, unsigned length)
+{
+ sr->start = (ptr_t)start ;
+ sr->ptr = (ptr_t)start ;
+ sr->end = (ptr_t)start + length ;
+}
+
+Inline int
+suck_left(sucker sr)
+{
+ return sr->end - sr->ptr ;
+} ;
+
+Inline int
+suck_total(sucker sr)
+{
+ return sr->end - sr->start ;
+} ;
+
+Inline ptr_t
+suck_start(sucker sr)
+{
+ return sr->start ;
+} ;
+
+Inline ptr_t
+suck_step(sucker sr, unsigned length)
+{
+ ptr_t ptr = sr->ptr ;
+ sr->ptr += length ;
+ dassert(sr->ptr <= sr->end) ;
+ return ptr ;
+} ;
+
+Inline void
+suck_push(sucker sr, unsigned length, sucker sv)
+{
+ *sv = *sr ;
+ sr->start = sr->ptr ;
+ sr->end = sr->ptr + length ;
+ dassert(sr->end <= sv->end) ;
+} ;
+
+Inline void
+suck_pop(sucker sr, sucker sv)
+{
+ dassert((sr->ptr <= sr->end) && (sr->end <= sv->end)) ;
+ sr->start = sv->start ;
+ sr->ptr = sr->end ;
+ sr->end = sv->end ;
+} ;
+
+Inline void
+suck_pop_exact(sucker sr, sucker sv)
+{
+ dassert(sr->ptr == sr->end) ;
+ sr->start = sv->start ;
+ sr->end = sv->end ;
+} ;
+
+Inline void
+suck_x(sucker sr)
+{
+ ++sr->ptr ;
+ dassert(sr->ptr <= sr->end) ;
+} ;
+
+Inline void
+suck_nx(sucker sr, unsigned n)
+{
+ sr->ptr += n ;
+ dassert(sr->ptr <= sr->end) ;
+} ;
+
+Inline uint8_t
+suck_b(sucker sr)
+{
+ dassert(sr->ptr < sr->end) ;
+ return *sr->ptr++ ;
+} ;
+
+Inline uint16_t
+suck_w(sucker sr)
+{
+ uint16_t w ;
+ dassert((sr->ptr + 1) < sr->end) ;
+ w = *sr->ptr++ ;
+ return (w << 8) + *sr->ptr++ ;
+} ;
+
+Inline uint32_t
+suck_l(sucker sr)
+{
+ uint32_t l ;
+ dassert((sr->ptr + 3) < sr->end) ;
+ l = *sr->ptr++ ;
+ l = (l << 8) + *sr->ptr++ ;
+ l = (l << 8) + *sr->ptr++ ;
+ return (l << 8) + *sr->ptr++ ;
+} ;
+
+
+#endif /* _QUAGGA_BGP_COMMON_H */
+
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
index 2ba45f6e..9cbf5f4a 100644
--- a/bgpd/bgp_community.c
+++ b/bgpd/bgp_community.c
@@ -292,6 +292,13 @@ community_com2str (struct community *com)
return str;
}
+/* Find an 'intern'ed community structure */
+struct community *
+community_lookup (struct community *com)
+{
+ return (struct community *) hash_lookup (comhash, com);
+}
+
/* Intern communities attribute. */
struct community *
community_intern (struct community *com)
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
index 9e483770..5643e285 100644
--- a/bgpd/bgp_community.h
+++ b/bgpd/bgp_community.h
@@ -70,5 +70,6 @@ extern int community_include (struct community *, u_int32_t);
extern void community_del_val (struct community *, u_int32_t *);
extern unsigned long community_count (void);
extern struct hash *community_hash (void);
+extern struct community *community_lookup (struct community *);
#endif /* _QUAGGA_BGP_COMMUNITY_H */
diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c
new file mode 100644
index 00000000..d7997dba
--- /dev/null
+++ b/bgpd/bgp_connection.c
@@ -0,0 +1,1108 @@
+/* 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_connection.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_notification.h"
+#include "bgpd/bgp_msg_read.h"
+
+#include "lib/memory.h"
+#include "lib/mqueue.h"
+#include "lib/symtab.h"
+#include "lib/stream.h"
+#include "lib/sockunion.h"
+#include "lib/list_util.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, a primary
+ * and a secondary. The primary starts as the connect() connection, and the
+ * secondary as the acccept(). One will be dropped before either connection
+ * reaches sEstablished state, and the remaining connection becomes the primary.
+ *
+ * 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 and the connection's pending queue.
+ *
+ * When it is no longer possible to write the the connection's write buffer,
+ * any mqueue messages that cannot be dealt with are queued on the connection's
+ * pending queue. So when the BGP Engine's mqueue is processed, the messages
+ * are either dealt with, or queued in the relevant connection.
+ *
+ * When the connection's write buffer empties, the connection is placed on the
+ * BGP Engine's 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 each 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 ; /* BGP Engine connection queue */
+
+static bgp_connection bgp_connection_list ; /* list of known connections */
+
+enum { CUT_LOOSE_LOCK_COUNT = 1000 } ;
+
+/*==============================================================================
+ * 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_free(bgp_wbuffer wb) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise connection structure -- allocate if required.
+ *
+ * Copies information required by the connection from the parent session.
+ *
+ * NB: requires the session LOCKED
+ */
+extern bgp_connection
+bgp_connection_init_new(bgp_connection connection, bgp_session session,
+ bgp_connection_ord_t ordinal)
+{
+ assert( (ordinal == bgp_connection_primary)
+ || (ordinal == bgp_connection_secondary) ) ;
+ assert(session->connections[ordinal] == NULL) ;
+
+ 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
+ * * comatose not comatose
+ * * half_open not half open
+ * * next NULL -- not on the connection queue
+ * * prev NULL -- not on the connection queue
+ * * follow_on bgp_fsm_null_event
+ * * exception bgp_session_null_event
+ * * fsm_active not active
+ * * notification NULL -- none received or sent
+ * * err no error, so far
+ * * cap_suppress do not suppress capabilities
+ * * gtsm false -- no minttl set, yet
+ * * su_local NULL -- no address, yet
+ * * su_remote NULL -- no address, yet
+ * * hold_timer_interval none -- set when connection is opened
+ * * keepalive_timer_interval none -- set when connection is opened
+ * * as4 not AS4 conversation
+ * * route_refresh_pre not pre-RFC ROUTE-REFRESH
+ * * orf_prefix_pre not pre-RFC ORF by prefix
+ * * read_pending nothing pending
+ * * read_header not reading header
+ * * msg_type none -- set when reading message
+ * * msg_size none -- set when reading message
+ * * notification_pending nothing pending
+ * * wbuff all pointers NULL -- empty but not writable
+ */
+ confirm(bgp_fsm_sInitial == 0) ;
+ confirm(bgp_fsm_null_event == 0) ;
+ confirm(bgp_session_null_event == 0) ;
+
+ /* Put on the connections that exist list */
+ sdl_push(bgp_connection_list, connection, exist) ;
+
+ /* Link back to session, point at its mutex and point session here */
+ connection->session = session ;
+ connection->p_mutex = &session->mutex ;
+ connection->lock_count = 0 ; /* no question about it */
+
+ connection->ordinal = ordinal ;
+ connection->accepted = (ordinal == bgp_connection_secondary) ;
+
+ session->connections[ordinal] = connection ;
+
+ /* qps_file structure */
+ connection->qf = qps_file_init_new(NULL, NULL) ;
+
+ /* Initialise all the timers */
+ connection->hold_timer = qtimer_init_new(NULL, bgp_nexus->pile,
+ NULL, connection) ;
+ connection->keepalive_timer = qtimer_init_new(NULL, bgp_nexus->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_MSG_MAX_L) ;
+ connection->obuf = stream_new(BGP_MSG_MAX_L) ;
+
+ /* Ensure mqueue_local_queue is empty. */
+ mqueue_local_init_new(&connection->pending_queue) ;
+
+ return connection ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Cut connection free from session.
+ *
+ * NB: will release any lock on the session. The bumps the lock count up
+ * so that will neither lock nor unlock the session again.
+ *
+ * It's only necessary to bump the count by 1, because all locks must be
+ * exactly balanced by unlocks. However, adding a big number makes this
+ * stand out.
+ */
+extern void
+BGP_CONNECTION_SESSION_CUT_LOOSE(bgp_connection connection)
+{
+ if (connection->session != NULL)
+ {
+ if (connection->lock_count != 0)
+ qpt_mutex_unlock(connection->p_mutex) ;
+
+ connection->lock_count += CUT_LOOSE_LOCK_COUNT ;
+
+ connection->session->connections[connection->ordinal] = NULL ;
+ connection->session = NULL ;
+ connection->p_mutex = NULL ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 ;
+
+ if (connection->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, connection->host) ;
+ connection->host = XMALLOC(MTYPE_BGP_PEER_HOST, strlen(host)
+ + strlen(tag) + 1) ;
+ strcpy(connection->host, host) ;
+ strcat(connection->host, tag) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get sibling (if any) for given connection.
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern bgp_connection
+bgp_connection_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] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make given connection the primary.
+ *
+ * Expects the given connection to be the only remaining connection.
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern void
+bgp_connection_make_primary(bgp_connection connection)
+{
+ bgp_session session = connection->session ;
+
+ /* Deal with the connection ordinal. */
+ if (connection->ordinal != bgp_connection_primary)
+ {
+ assert(session->connections[bgp_connection_primary] == NULL) ;
+ session->connections[connection->ordinal] = NULL ;
+ connection->ordinal = bgp_connection_primary ;
+ session->connections[connection->ordinal] = connection ;
+ } ;
+
+ assert(session->connections[bgp_connection_secondary] == NULL) ;
+
+ /* Move the open_state to the session.
+ * Copy the state of the cap_suppress flag
+ * Change the connection host to drop the primary/secondary distinction.
+ * Copy the negotiated hold_timer_interval and keepalive_timer_interval
+ * Copy the su_local and su_remote
+ */
+ bgp_open_state_set_mov(&session->open_recv, &connection->open_recv) ;
+
+ session->cap_suppress = connection->cap_suppress ;
+
+ if (connection->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, connection->host) ;
+ bgp_connection_init_host(connection, "") ;
+
+ session->hold_timer_interval = connection->hold_timer_interval ;
+ session->keepalive_timer_interval = connection->keepalive_timer_interval ;
+
+ session->as4 = connection->as4 ;
+ session->route_refresh_pre = connection->route_refresh
+ == bgp_form_pre ;
+ session->orf_prefix_pre = connection->orf_prefix
+ == bgp_form_pre ;
+
+ sockunion_set_dup(&session->su_local, connection->su_local) ;
+ sockunion_set_dup(&session->su_remote, connection->su_remote) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Exit connection
+ *
+ * Make sure the connection is closed, then queue it to be reaped.
+ *
+ * When BGP Engine gets round to it, will free the structure. This avoids
+ * freeing the connection structure somewhere inside the FSM, and having to
+ * cope with the possibility of having dangling references to it.
+ *
+ * In fact, the connection may be set to be reaped before the FSM has cut it
+ * loose from the session -- so the connection may still be active inside the
+ * FSM when this is called.
+ */
+extern void
+bgp_connection_exit(bgp_connection connection)
+{
+ bgp_connection_close(connection, false) ; /* false => not keep timers */
+
+ assert(connection->state == bgp_fsm_sStopping) ;
+
+ bgp_connection_queue_add(connection) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free connection.
+ *
+ * Connection must be Stopping -- no longer attached to a session.
+ *
+ * This is done in the BGP Engine connection queue handling -- so that the
+ * structure is reaped once there is no chance of any dangling pointers to it.
+ */
+static void
+bgp_connection_free(bgp_connection connection)
+{
+ assert( (connection->state == bgp_fsm_sStopping)
+ && (connection->session == NULL)
+ && ( (connection->lock_count == 0) ||
+ (connection->lock_count == CUT_LOOSE_LOCK_COUNT) ) ) ;
+
+ /* Make sure is closed, so no active file, no timers, pending queue is empty,
+ * not on the connection queue, etc.
+ */
+ bgp_connection_close(connection, false) ; /* false => not keep timers */
+
+ /* Free any components which still exist */
+ connection->qf = qps_file_free(connection->qf) ;
+ connection->hold_timer = qtimer_free(connection->hold_timer) ;
+ connection->keepalive_timer = qtimer_free(connection->hold_timer) ;
+
+ bgp_notify_unset(&connection->notification) ;
+ bgp_open_state_unset(&connection->open_recv) ;
+ sockunion_unset(&connection->su_local) ;
+ sockunion_unset(&connection->su_remote) ;
+ if (connection->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, connection->host) ;
+ stream_free(connection->ibuf) ;
+ stream_free(connection->obuf) ;
+ bgp_write_buffer_free(&connection->wbuff) ;
+
+ /* Free the body */
+ sdl_del(bgp_connection_list, connection, exist) ;
+
+ XFREE(MTYPE_BGP_CONNECTION, connection) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminate all known connections.
+ *
+ * TODO: for bringing the BGP Engine to a dead halt.
+ *
+ * Problem: can it be assumed that all sessions have been closed ?
+ *
+ * if not... how are all the connections to be pursuaded to adopt
+ * an appropriate posture ?
+ */
+
+
+/*------------------------------------------------------------------------------
+ * If required, allocate new write buffer.
+ * Initialise pointers empty and writable.
+ *
+ * NB: structure was zeroised the enclosing connection was initialised.
+ * Buffer may have been allocated since then.
+ */
+static void
+bgp_write_buffer_init(bgp_wbuffer wb, size_t size)
+{
+ if (wb->base == NULL)
+ {
+ wb->base = XMALLOC(MTYPE_STREAM_DATA, size) ;
+ wb->limit = wb->base + size ;
+ } ;
+
+ bgp_write_buffer_reset(wb) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free any write buffer
+ */
+static void
+bgp_write_buffer_free(bgp_wbuffer wb)
+{
+ if (wb->base != NULL)
+ XFREE(MTYPE_STREAM_DATA, wb->base) ; /* sets wb->base = NULL */
+
+ wb->p_in = wb->p_out = wb->limit = wb->base;
+} ;
+
+/*==============================================================================
+ * 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 connection in turn, dealing with one item on each one's pending
+ * queue. Dealing with the item will either remove it from the connection's
+ * pending queue (success) or remove connection from the pending queue.
+ *
+ * This is also where connections come to die.
+ *
+ * Returns: 0 => nothing to do
+ * 1 => dealt with one or more queued bits of work
+ */
+extern int
+bgp_connection_queue_process(void)
+{
+ mqueue_block mqb ;
+
+ if (bgp_connection_queue == NULL)
+ return 0 ;
+
+ 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_sStopping)
+ {
+ bgp_connection_free(connection) ; /* removes from connection queue */
+ continue ;
+ } ;
+
+ /* Process next item on connection's pending queue */
+ mqb = mqueue_local_dequeue(&connection->pending_queue) ;
+ if (mqb != NULL)
+ /* The action will either remove the mqb from the pending queue,
+ * or remove the connection from the connection queue.
+ */
+ {
+ bgp_session session = mqb_get_arg0(mqb) ;
+ assert( (session == connection->session)
+ && (connection
+ == session->connections[bgp_connection_primary]) ) ;
+ mqb_dispatch_action(mqb) ;
+ } ;
+
+ /* If head is unchanged, then no more to do now. */
+ if (mqb == mqueue_local_head(&connection->pending_queue))
+ bgp_connection_queue_del(connection) ;
+ } ;
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add given message block to the given connection's pending queue
+ *
+ * If mqb is not already pending, add it at the tail and mark it pending.
+ *
+ * If is already pending, then is being put back onto the queue, so put it
+ * at the head.
+ *
+ * In any case, remove the connection from the BGP Engine connection queue (if
+ * there) -- there is nothing more to be done for the connection for the time
+ * being.
+ */
+extern void
+bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb,
+ bgp_connection* is_pending)
+{
+ if (*is_pending == NULL)
+ {
+ mqueue_local_enqueue(&connection->pending_queue, mqb) ;
+ *is_pending = connection ;
+ }
+ else
+ {
+ dassert(*is_pending == connection) ;
+ mqueue_local_enqueue_head(&connection->pending_queue, mqb) ;
+ } ;
+
+ bgp_connection_queue_del(connection) ;
+} ;
+
+/*==============================================================================
+ * Opening and closing Connections
+ */
+
+/*------------------------------------------------------------------------------
+ * Open connection.
+ *
+ * Expects connection to either be newly created or recently closed.
+ *
+ * For connect() connections this is done at connect() time, so before any
+ * connection comes up.
+ *
+ * For accept() connections this is done at accept() time, so when the
+ * connection comes up.
+ *
+ * The file is disabled in all modes.
+ *
+ * To complete the process must bgp_connection_start(), which resets the write
+ * buffer (allocating if required), and ensures that all is ready to read/write.
+ *
+ * Resets:
+ *
+ * * closes any file that may be lingering (should never be)
+ * * reset all stream buffers to empty (should already be)
+ * * set write buffer unwritable
+ * * clears half_open
+ *
+ * Sets:
+ *
+ * * if secondary connection, turn off accept()
+ * * sets the qfile and sock_fd ready for use -- disabled in all modes
+ * * clears err -- must be OK so far
+ * * discards any open_state
+ * * 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
+ * * log and host to be set up
+ * * stream buffers to exist
+ *
+ * Does not touch:
+ *
+ * * state of the connection (including exception and follow-on event)
+ * * capabilities suppress flag
+ * * timers -- FSM looks after those
+ *
+ * NB: nothing can be written until bgp_connection_start() has been called.
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern void
+bgp_connection_open(bgp_connection connection, int sock_fd)
+{
+ bgp_session session = connection->session ;
+
+ /* Make sure that there is no file and that buffers are clear, etc.
+ * If this is the secondary connection, do not accept any more.
+ * The FSM deals with the timers.
+ */
+ bgp_connection_close(connection, true) ; /* true => keep timers */
+
+ /* Set the file going */
+ qps_add_file(bgp_nexus->selection, connection->qf, sock_fd, connection) ;
+
+ connection->err = 0 ; /* so far, so good */
+
+ bgp_open_state_unset(&connection->open_recv) ;
+
+ /* 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start connection which has just come up -- connect() or accept()
+ *
+ * Copy the local and remote addresses (IPv4 mapped IPv6 addresses appear as
+ * IPv4 addresses).
+ *
+ * Make sure now have a write buffer, and set it empty and writable.
+ */
+extern void
+bgp_connection_start(bgp_connection connection, union sockunion* su_local,
+ union sockunion* su_remote)
+{
+ sockunion_set_dup(&connection->su_local, su_local) ;
+ sockunion_set_dup(&connection->su_remote, su_remote) ;
+
+ bgp_write_buffer_init(&connection->wbuff, bgp_wbuff_size) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Stop connection buffering -- may leave write buffer to be emptied.
+ *
+ * * reset stream buffers
+ * * empty out any pending queue
+ * * remove from the BGP Engine connection queue, if there
+ * * no notification pending (yet)
+ *
+ * If required:
+ *
+ * * set write buffer unwritable and empty
+ *
+ * NB: requires the session to be LOCKED.
+ */
+static void
+bgp_connection_stop(bgp_connection connection, bool stop_writer)
+{
+ /* Reset all stream buffering empty. */
+ stream_reset(connection->ibuf) ;
+ stream_reset(connection->obuf) ;
+
+ connection->read_pending = 0 ;
+ connection->read_header = 0 ;
+ connection->notification_pending = 0 ;
+
+ /* Empty out the pending queue and remove from connection queue */
+ mqueue_local_reset_keep(&connection->pending_queue) ;
+ bgp_connection_queue_del(connection) ;
+
+ /* If required: set write buffer *unwritable* (and empty). */
+ if (stop_writer)
+ bgp_write_buffer_unwritable(&connection->wbuff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Enable connection/session for accept()
+ *
+ * NB: requires the session to be LOCKED
+ */
+extern void
+bgp_connection_enable_accept(bgp_connection connection)
+{
+ bgp_session session = connection->session ;
+
+ assert(connection->ordinal == bgp_connection_secondary) ;
+ assert(session != NULL) ;
+ assert(session->active) ;
+
+ session->accept = true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Disable connection for accept() -- assuming still have session !
+ *
+ * NB: requires the session to be LOCKED
+ */
+extern void
+bgp_connection_disable_accept(bgp_connection connection)
+{
+ if (connection->session != NULL)
+ connection->session->accept = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if there is a connection which is ready to accept()
+ *
+ * Note that if there *is* a connection, then the session is active, and is not
+ * subject to the whim of the Routing Engine -- in particular it cannot be
+ * deleted !
+ *
+ * NB: this is *only* used in the BGP Engine. The session->active and
+ * session->accept flags are private variables, only set by the BGP Engine.
+ *
+ * NB: this is called under the Peer Index Mutex. The Routing Engine never
+ * deletes sessions while it holds the Peer Index Mutex, nor when the
+ * session->active is true.
+ *
+ * Only returns a connection if session->active -- so safe.
+ */
+extern bgp_connection
+bgp_connection_query_accept(bgp_session session)
+{
+ bgp_connection connection ;
+
+ if ((session != NULL) && session->active && session->accept)
+ {
+ connection = session->connections[bgp_connection_secondary] ;
+ assert(connection != NULL) ;
+ }
+ else
+ {
+ connection = NULL ;
+ } ;
+
+ return connection ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close connection.
+ *
+ * * if there is an sock_fd, close it
+ * * if qfile is active, remove it
+ * * forget any addresses
+ * * reset all stream buffers to empty
+ * * reset write buffer to unwritable
+ * * empties the pending queue -- destroying all messages
+ *
+ * * for secondary connection: disable accept
+ * * clear half_open
+ *
+ * * if required: unset all timers
+ *
+ * The following remain:
+ *
+ * * state of the connection
+ * * links to and from the session
+ * * the timers remain initialised -- and remain on or are unset
+ * * the buffers remain (but reset)
+ * * logging and host string
+ * * any open_state that has been received
+ * * any notification sent/received
+ * * the exception state and any error
+ *
+ * Once closed, the only further possible actions are:
+ *
+ * * bgp_connection_open() -- to retry connection
+ *
+ * * bgp_connection_free() -- to finally discard
+ *
+ * * bgp_connection_full_close() -- can do this again
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern void
+bgp_connection_close(bgp_connection connection, bool keep_timers)
+{
+ int sock_fd ;
+
+ /* Close connection's file, if any. */
+ qps_remove_file(connection->qf) ;
+
+ sock_fd = qps_file_unset_fd(connection->qf) ;
+ if (sock_fd != fd_undef)
+ close(sock_fd) ;
+
+ /* If required, unset the timers. */
+ if (!keep_timers)
+ {
+ qtimer_unset(connection->hold_timer) ;
+ qtimer_unset(connection->keepalive_timer) ;
+ } ;
+
+ /* If this is the secondary connection, do not accept any more. */
+ if (connection->ordinal == bgp_connection_secondary)
+ bgp_connection_disable_accept(connection) ;
+
+ connection->half_open = 0 ;
+
+ /* forget any addresses */
+ sockunion_unset(&connection->su_local) ;
+ sockunion_unset(&connection->su_remote) ;
+
+ /* Stop all buffering activity, including write buffer. */
+ bgp_connection_stop(connection, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 sock_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.
+ *
+ * Returns: 1 => OK, ready to send NOTIFICATION now
+ * 0 => no file descriptor => no chance of sending NOTIFICATION
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern bool
+bgp_connection_part_close(bgp_connection connection)
+{
+ bgp_wbuffer wb = &connection->wbuff ;
+ int sock_fd ;
+ uint8_t* p ;
+ bgp_size_t mlen ;
+
+ /* Check that have a usable file descriptor */
+ sock_fd = qps_file_fd(connection->qf) ;
+
+ if (sock_fd == fd_undef)
+ return false ;
+
+ /* Shutdown the read side of this connection */
+ shutdown(sock_fd, SHUT_RD) ;
+ qps_disable_modes(connection->qf, qps_read_mbit) ;
+
+ /* Stop all buffering activity, except for write buffer. */
+ bgp_connection_stop(connection, false) ;
+
+ /* Purge wbuff of all but current partly written message (if any) */
+ if (wb->p_in != wb->p_out) /* will be equal if buffer is empty */
+ {
+ passert(wb->p_out < wb->p_in) ;
+ mlen = 0 ;
+ p = wb->base ;
+ do /* Advance p until p + mlen > wb->p_out */
+ {
+ p += mlen ;
+ mlen = bgp_msg_get_mlen(p, wb->p_in) ; /* checks pointers */
+ } 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
+ bgp_write_buffer_reset(wb) ;
+
+ /* OK -- part closed, ready to send NOTIFICATION */
+ return true ;
+} ;
+
+/*==============================================================================
+ * Writing to BGP connection -- once TCP connection has come up.
+ *
+ * Nothing is written directly -- all writing is qpselect driven.
+ *
+ * All writing is done by preparing a BGP message in a stream buffer,
+ * and then calling bgp_connection_write(). The contents of the stream buffer
+ * are transferred to the connection's write buffer.
+ *
+ * Returns true <=> able to write the entire buffer without blocking.
+ */
+
+static void bgp_connection_write_action(qps_file qf, void* file_info) ;
+
+/*------------------------------------------------------------------------------
+ * Write the contents of the given stream
+ *
+ * Writes everything or FATAL error.
+ *
+ * Returns: 1 => written to wbuff -- stream reset, empty
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ */
+extern int
+bgp_connection_write(bgp_connection connection, struct stream* s)
+{
+ bgp_wbuffer wb = &connection->wbuff ;
+
+ /* FATAL error if cannot write everything. */
+ if (bgp_write_buffer_cannot(wb, stream_pending(s)))
+ zabort("Write buffer does not have enough room") ;
+
+ /* If buffer is empty, enable write mode */
+ if (bgp_write_buffer_empty(wb))
+ qps_enable_mode(connection->qf, qps_write_mnum,
+ bgp_connection_write_action) ;
+
+ /* Transfer the obuf contents to the write buffer. */
+ wb->p_in = stream_transfer(wb->p_in, s, wb->limit) ;
+
+ return 1 ; /* written as far as the write buffer */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write Action for bgp connection.
+ *
+ * Empty the write buffer if we can.
+ *
+ * If empties that, disable write mode, then:
+ *
+ * -- if notification is pending, generate a notification sent event
+ *
+ * -- otherwise: place connection on the connection queue, so can start to
+ * flush out anything on the connection's pending queue.
+ *
+ * If encounter an error, generate TCP_fatal_error event, forcing buffer
+ * empty but unwritable.
+ */
+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 = bgp_write_buffer_has(wb) ;
+ 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_write_buffer_unwritable(wb) ;
+ bgp_fsm_io_error(connection, errno) ;
+ } ;
+ return ;
+ } ;
+ } ;
+
+ /* Buffer is empty -- reset it and disable write mode */
+ bgp_write_buffer_reset(wb) ;
+
+ 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_notification_sent(connection) ;
+ else
+ bgp_connection_queue_add(connection) ;
+} ;
+
+/*==============================================================================
+ * Reading from BGP connection -- once the TCP connection has come up.
+ *
+ * Nothing is read directly -- all reading is qpselect driven.
+ *
+ * Sets the qfile readable -- and leaves it there for the duration.
+ *
+ * TODO: implement some read flow control ??
+ */
+
+static void
+bgp_connection_read_action(qps_file qf, void* file_info) ;
+
+/*------------------------------------------------------------------------------
+ * Enable reading on the given connection.
+ */
+extern void
+bgp_connection_read_enable(bgp_connection connection)
+{
+ qps_enable_mode(connection->qf, qps_read_mnum, bgp_connection_read_action) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Read Action for BGP connection
+ *
+ * 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_MSG_MAX_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_nonblock(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) ;
+ /* returns balance of message */
+ if (want == 0)
+ break ;
+ }
+ else
+ {
+ bgp_fsm_io_error(connection, (ret == -1) ? errno : 0) ;
+ return ;
+ } ;
+ } ;
+
+ /* Deal with the BGP message. MUST remove from ibuf before returns !
+ *
+ * NB: if the session pointer is NULL, that means the connection has been
+ * cut from the session, so no point dealing with the message.
+ *
+ * NB: if something goes wrong while processing the message,
+ *
+ * NB: size passed is the size of the *body* of the message.
+ */
+ if (connection->session != NULL) /* don't bother if session gone ! */
+ {
+ BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ connection->msg_func(connection, connection->msg_body_size) ;
+
+ BGP_CONNECTION_SESSION_UNLOCK(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..ead222f9
--- /dev/null
+++ b/bgpd/bgp_connection.h
@@ -0,0 +1,372 @@
+/* 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 <stdbool.h>
+
+#include "lib/mqueue.h"
+#include "lib/qpthreads.h"
+#include "lib/qtimers.h"
+#include "lib/qpselect.h"
+
+#include "lib/sockunion.h"
+#include "lib/stream.h"
+
+//#include "bgpd/bgp.h"
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_notification.h"
+#include "bgpd/bgp_msg_read.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_sInitial = 0, /* extra: connection initialised */
+
+ bgp_fsm_sIdle = 1, /* waiting for Idle Hold time */
+ bgp_fsm_sConnect = 2, /* waiting for connect (may be listening) */
+ bgp_fsm_sActive = 3, /* listening only */
+ bgp_fsm_sOpenSent = 4, /* sent Open -- awaits Open */
+ bgp_fsm_sOpenConfirm = 5, /* sent & received Open -- awaits keepalive */
+ bgp_fsm_sEstablished = 6, /* running connection */
+
+ bgp_fsm_sStopping = 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_eBGP_Start = 1,
+ bgp_fsm_eBGP_Stop = 2,
+ bgp_fsm_eTCP_connection_open = 3,
+ bgp_fsm_eTCP_connection_closed = 4,
+ bgp_fsm_eTCP_connection_open_failed = 5,
+ bgp_fsm_eTCP_fatal_error = 6,
+ bgp_fsm_eConnectRetry_timer_expired = 7,
+ bgp_fsm_eHold_Timer_expired = 8,
+ bgp_fsm_eKeepAlive_timer_expired = 9,
+ bgp_fsm_eReceive_OPEN_message = 10,
+ bgp_fsm_eReceive_KEEPALIVE_message = 11,
+ bgp_fsm_eReceive_UPDATE_message = 12,
+ bgp_fsm_eReceive_NOTIFICATION_message = 13,
+ bgp_fsm_eSent_NOTIFICATION_message = 14,
+
+ bgp_fsm_last_event = 15,
+} ;
+
+/*==============================================================================
+ * BGP Connection Structures
+ *
+ *------------------------------------------------------------------------------
+ * Write buffer for connection.
+ *
+ * NB: when connection is initialised all the pointers are set NULL.
+ *
+ * The buffer is not allocated until the TCP connection comes up.
+ *
+ * NB: p_out == p_in => buffer is empty
+ *
+ * BUT: p_out == limit => buffer is not writable.
+ *
+ * When connection is first initialised all pointers are NULL, so the
+ * buffer is "empty but not writable".
+ *
+ * When connection is opened, closed or fails, buffer is set into this
+ * "empty but not writable" state.
+ */
+typedef struct bgp_wbuffer* bgp_wbuffer ;
+struct bgp_wbuffer
+{
+ uint8_t* p_out ;
+ uint8_t* p_in ;
+
+ uint8_t* base ;
+ uint8_t* limit ;
+} ;
+
+/* Buffer is allocated for a number of maximum size BGP messages. */
+enum { bgp_wbuff_size = BGP_MSG_MAX_L * 10 } ;
+
+/*==============================================================================
+ * 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.
+ */
+struct bgp_connection
+{
+ struct dl_list_pair(bgp_connection) exist ;
+ /* list of existing connections */
+
+ bgp_session session ; /* session connection belongs to */
+ /* NULL if connection stopping */
+ qpt_mutex p_mutex ; /* session mutex* */
+ /* (avoids incomplete type issue) */
+ unsigned lock_count ; /* session mutex lock count */
+
+ bgp_connection_ord_t ordinal ; /* primary/secondary connection */
+ bool accepted ; /* came via accept() */
+
+ bgp_fsm_state_t state ; /* FSM state of connection */
+ bool comatose ; /* Idle and no timer set */
+ bool half_open ; /* Idle but accepted connection */
+
+ bgp_connection next ; /* for the connection queue */
+ bgp_connection prev ; /* NULL <=> not on the queue */
+
+ int fsm_active ; /* active in FSM counter */
+ bgp_fsm_event_t follow_on ; /* event raised within FSM */
+
+ bgp_session_event_t exception; /* exception posted here */
+ bgp_notify notification ; /* if any sent/received */
+ int err ; /* erno, if any */
+
+ bool cap_suppress ; /* capability send suppress
+ always set false when connection
+ initialised. Set if get
+ NOTIFICATION that other end does
+ not do capabilities. Copied to
+ session when established. */
+
+ bgp_open_state open_recv ; /* the open received. */
+
+ qps_file qf ; /* qpselect file structure */
+ bool gtsm ; /* minttl has been set */
+
+ 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 */
+
+ bool as4 ; /* subject to negotiation */
+ bgp_form_t route_refresh ; /* subject to negotiation */
+ bgp_form_t orf_prefix ; /* subject to negotiation */
+
+ qtimer hold_timer ;
+ qtimer keepalive_timer ;
+
+ struct stream* ibuf ; /* a single input "stream" */
+ unsigned read_pending ; /* how much input waiting for */
+
+ bool read_header ; /* reading message header */
+ uint8_t msg_type ; /* copy of message type */
+ bgp_size_t msg_body_size ; /* size of message *body* */
+ bgp_msg_handler* msg_func ; /* function to handle message */
+
+ 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 */
+} ;
+
+/*==============================================================================
+ * The functions
+ */
+
+extern bgp_connection bgp_connection_init_new(bgp_connection connection,
+ bgp_session session, bgp_connection_ord_t ordinal) ;
+extern void bgp_connection_open(bgp_connection connection, int sock_fd) ;
+extern void bgp_connection_start(bgp_connection connection, sockunion su_local,
+ sockunion su_remote) ;
+extern void bgp_connection_enable_accept(bgp_connection connection) ;
+extern void bgp_connection_disable_accept(bgp_connection connection) ;
+extern bgp_connection bgp_connection_query_accept(bgp_session session) ;
+extern bgp_connection bgp_connection_get_sibling(bgp_connection connection) ;
+extern void bgp_connection_make_primary(bgp_connection connection) ;
+extern void bgp_connection_close(bgp_connection connection, bool keep_timers) ;
+extern bool bgp_connection_part_close(bgp_connection connection) ;
+extern void bgp_connection_exit(bgp_connection connection) ;
+extern void bgp_connection_read_enable(bgp_connection connection) ;
+extern int bgp_connection_write(bgp_connection connection, struct stream* s) ;
+extern void bgp_connection_queue_add(bgp_connection connection) ;
+extern void bgp_connection_queue_del(bgp_connection connection) ;
+extern int bgp_connection_queue_process(void) ;
+
+Inline bool
+bgp_connection_no_pending(bgp_connection connection, bgp_connection* is_pending)
+{
+ return ( (mqueue_local_head(&connection->pending_queue) == NULL)
+ || (*is_pending != NULL) ) ;
+} ;
+
+extern void bgp_connection_add_pending(bgp_connection connection,
+ mqueue_block mqb, bgp_connection* is_pending) ;
+
+/*------------------------------------------------------------------------------
+ * Set buffer *unwritable* (buffer appears full, but nothing pending).
+ */
+Inline void
+bgp_write_buffer_unwritable(bgp_wbuffer wb)
+{
+ wb->p_in = wb->p_out = wb->limit ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If allocated: set buffer empty
+ * If unallocated: buffer remains *unwritable*
+ */
+Inline void
+bgp_write_buffer_reset(bgp_wbuffer wb)
+{
+ wb->p_in = wb->p_out = wb->base ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if do NOT have enough room for what want to write PLUS 1.
+ *
+ * NB: there is never any room in an unallocated buffer.
+ */
+Inline bool
+bgp_write_buffer_cannot(bgp_wbuffer wb, size_t want)
+{
+ return ((size_t)(wb->limit - wb->p_in) <= want) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Full if NOT enough room for a maximum size BGP message + 1
+ *
+ * NB: there is never any room in an unallocated buffer.
+ */
+enum { bgp_write_buffer_full_threshold = BGP_MSG_MAX_L + 1 } ;
+
+Inline bool
+bgp_write_buffer_cannot_max(bgp_wbuffer wb)
+{
+ return bgp_write_buffer_cannot(wb, BGP_MSG_MAX_L) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if buffer has anything in it.
+ *
+ * If empty, ensures that the buffer has been allocated, and sets the pointers
+ * to the start of the buffer -- so all set to go.
+ */
+Inline bool
+bgp_write_buffer_empty(bgp_wbuffer wb)
+{
+ if (wb->p_out < wb->p_in)
+ return false ; /* not empty => has buffer */
+
+ dassert(wb->p_out == wb->p_in) ;
+
+ passert(wb->base != NULL) ; /* must have buffer */
+
+ bgp_write_buffer_reset(wb) ; /* pointers to start of buffer */
+
+ return true ; /* empty and all ready to go */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return how much the write buffer still has to write.
+ *
+ * NB: if returns 0, may not yet have been allocated.
+ *
+ * > 0 => allocated.
+ */
+Inline int
+bgp_write_buffer_has(bgp_wbuffer wb)
+{
+ dassert(wb->p_out <= wb->p_in) ;
+ return (wb->p_in - wb->p_out) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * As above, for connection
+ */
+Inline bool
+bgp_connection_write_cannot_max(bgp_connection connection)
+{
+ return bgp_write_buffer_cannot_max(&connection->wbuff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * As above, for connection
+ */
+Inline bool
+bgp_connection_write_empty(bgp_connection connection)
+{
+ return bgp_write_buffer_empty(&connection->wbuff) ;
+} ;
+
+/*==============================================================================
+ * Locking the session associated with the connection.
+ *
+ * This is slightly complicated by the fact that when the connection is in
+ * sStopping, it is no longer attached to the session.
+ *
+ * To facilitate that, the connection maintains its own "recursive" count, so
+ * that when the connection is cut loose from the session, the session's mutex
+ * can be released.
+ *
+ * Further -- when the connection is cut loose, a big number is added to the
+ * count, so when the session is "unlocked" nothing will happen !
+ *
+ * Also -- this mechanism means that the session lock can be called even after
+ * the connection has been cut loose, without requiring any other tests.
+ */
+
+Inline void
+BGP_CONNECTION_SESSION_LOCK(bgp_connection connection)
+{
+ if (connection->lock_count++ == 0)
+ qpt_mutex_lock(connection->p_mutex) ;
+} ;
+
+Inline void
+BGP_CONNECTION_SESSION_UNLOCK(bgp_connection connection)
+{
+ if (--connection->lock_count == 0)
+ qpt_mutex_unlock(connection->p_mutex) ;
+} ;
+
+extern void
+BGP_CONNECTION_SESSION_CUT_LOOSE(bgp_connection connection) ;
+
+#endif /* QUAGGA_BGP_CONNECTION_H */
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
index a5138833..79d75a3f 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -31,7 +31,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_damp.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_route.h"
-#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_advertise.h"
/* Global variable to access damping configuration */
@@ -40,9 +40,25 @@ static struct bgp_damp_config *damp = &bgp_damp_cfg;
/* Utility macro to add and delete BGP dampening information to no
used list. */
-#define BGP_DAMP_LIST_ADD(N,A) BGP_INFO_ADD(N,A,no_reuse_list)
-#define BGP_DAMP_LIST_DEL(N,A) BGP_INFO_DEL(N,A,no_reuse_list)
-
+#define BGP_DAMP_LIST_ADD(N,A) \
+ do { \
+ (A)->prev = NULL; \
+ (A)->next = (N)->no_reuse_list; \
+ if ((N)->no_reuse_list) \
+ (N)->no_reuse_list->prev = (A); \
+ (N)->no_reuse_list = (A); \
+ } while (0)
+
+#define BGP_DAMP_LIST_DEL(N,A) \
+ do { \
+ if ((A)->next) \
+ (A)->next->prev = (A)->prev; \
+ if ((A)->prev) \
+ (A)->prev->next = (A)->next; \
+ else \
+ (N)->no_reuse_list = (A)->next; \
+ } while (0)
+
/* Calculate reuse list index by penalty value. */
static int
bgp_reuse_index (int penalty)
@@ -51,17 +67,17 @@ bgp_reuse_index (int penalty)
int index;
i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor);
-
+
if ( i >= damp->reuse_index_size )
i = damp->reuse_index_size - 1;
index = damp->reuse_index[i] - damp->reuse_index[0];
- return (damp->reuse_offset + index) % damp->reuse_list_size;
+ return (damp->reuse_offset + index) % damp->reuse_list_size;
}
/* Add BGP dampening information to reuse list. */
-static void
+static void
bgp_reuse_list_add (struct bgp_damp_info *bdi)
{
int index;
@@ -85,10 +101,10 @@ bgp_reuse_list_delete (struct bgp_damp_info *bdi)
bdi->prev->next = bdi->next;
else
damp->reuse_list[bdi->index] = bdi->next;
-}
-
+}
+
/* Return decayed penalty value. */
-int
+int
bgp_damp_decay (time_t tdiff, int penalty)
{
unsigned int i;
@@ -96,8 +112,8 @@ bgp_damp_decay (time_t tdiff, int penalty)
i = (int) ((double) tdiff / DELTA_T);
if (i == 0)
- return penalty;
-
+ return penalty;
+
if (i >= damp->decay_array_size)
return 0;
@@ -112,7 +128,7 @@ bgp_reuse_timer (struct thread *t)
struct bgp_damp_info *bdi;
struct bgp_damp_info *next;
time_t t_now, t_diff;
-
+
damp->t_reuse = NULL;
damp->t_reuse =
thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
@@ -132,14 +148,14 @@ bgp_reuse_timer (struct thread *t)
for (; bdi; bdi = next)
{
struct bgp *bgp = bdi->binfo->peer->bgp;
-
+
next = bdi->next;
/* Set t-diff = t-now - t-updated. */
t_diff = t_now - bdi->t_updated;
/* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */
- bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
+ bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
/* Set t-updated = t-now. */
bdi->t_updated = t_now;
@@ -155,7 +171,7 @@ bgp_reuse_timer (struct thread *t)
{
bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_HISTORY);
bgp_aggregate_increment (bgp, &bdi->rn->p, bdi->binfo,
- bdi->afi, bdi->safi);
+ bdi->afi, bdi->safi);
bgp_process (bgp, bdi->rn, bdi->afi, bdi->safi);
}
@@ -180,13 +196,13 @@ bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
time_t t_now;
struct bgp_damp_info *bdi = NULL;
double last_penalty = 0;
-
+
t_now = bgp_clock ();
/* Processing Unreachable Messages. */
if (binfo->extra)
bdi = binfo->extra->damp_info;
-
+
if (bdi == NULL)
{
/* If there is no previous stability history. */
@@ -214,8 +230,8 @@ bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
last_penalty = bdi->penalty;
/* 1. Set t-diff = t-now - t-updated. */
- bdi->penalty =
- (bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty)
+ bdi->penalty =
+ (bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty)
+ (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY));
if (bdi->penalty > damp->ceiling)
@@ -223,9 +239,9 @@ bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
bdi->flap++;
}
-
+
assert ((rn == bdi->rn) && (binfo == bdi->binfo));
-
+
bdi->lastrecord = BGP_RECORD_WITHDRAW;
bdi->t_updated = t_now;
@@ -235,13 +251,13 @@ bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
/* Remove the route from a reuse list if it is on one. */
if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED))
{
- /* If decay rate isn't equal to 0, reinsert brn. */
+ /* If decay rate isn't equal to 0, reinsert brn. */
if (bdi->penalty != last_penalty)
{
bgp_reuse_list_delete (bdi);
- bgp_reuse_list_add (bdi);
+ bgp_reuse_list_add (bdi);
}
- return BGP_DAMP_SUPPRESSED;
+ return BGP_DAMP_SUPPRESSED;
}
/* If not suppressed before, do annonunce this withdraw and
@@ -258,7 +274,7 @@ bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
}
int
-bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn,
+bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn,
afi_t afi, safi_t safi)
{
time_t t_now;
@@ -287,28 +303,28 @@ bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn,
status = BGP_DAMP_USED;
}
else
- status = BGP_DAMP_SUPPRESSED;
+ status = BGP_DAMP_SUPPRESSED;
if (bdi->penalty > damp->reuse_limit / 2.0)
bdi->t_updated = t_now;
else
bgp_damp_info_free (bdi, 0);
-
+
return status;
}
/* Remove dampening information and history route. */
-int
+int
bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
{
time_t t_now, t_diff;
struct bgp_damp_info *bdi;
-
+
assert (binfo->extra && binfo->extra->damp_info);
-
+
t_now = bgp_clock ();
bdi = binfo->extra->damp_info;
-
+
if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
{
t_diff = t_now - bdi->suppress_time;
@@ -321,7 +337,7 @@ bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
bdi->penalty = damp->reuse_limit;
bdi->suppress_time = 0;
bdi->t_updated = t_now;
-
+
/* Need to announce UPDATE once this binfo is usable again. */
if (bdi->lastrecord == BGP_RECORD_UPDATE)
return 1;
@@ -336,13 +352,13 @@ bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
if (bdi->penalty <= damp->reuse_limit / 2.0)
{
- /* release the bdi, bdi->binfo. */
+ /* release the bdi, bdi->binfo. */
bgp_damp_info_free (bdi, 1);
return 0;
- }
+ }
else
bdi->t_updated = t_now;
- }
+ }
return 0;
}
@@ -366,7 +382,7 @@ bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw)
if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
bgp_info_delete (bdi->rn, binfo);
-
+
XFREE (MTYPE_BGP_DAMP_INFO, bdi);
}
@@ -376,7 +392,7 @@ bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
double reuse_max_ratio;
unsigned int i;
double j;
-
+
damp->suppress_value = sup;
damp->half_life = hlife;
damp->reuse_limit = reuse;
@@ -385,11 +401,11 @@ bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
/* Initialize params per bgp_damp_config. */
damp->reuse_index_size = REUSE_ARRAY_SIZE;
- damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life)));
+ damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life)));
/* Decay-array computations */
damp->decay_array_size = ceil ((double) damp->max_suppress_time / DELTA_T);
- damp->decay_array = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
+ damp->decay_array = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
sizeof(double) * (damp->decay_array_size));
damp->decay_array[0] = 1.0;
damp->decay_array[1] = exp ((1.0/((double)damp->half_life/DELTA_T)) * log(0.5));
@@ -397,15 +413,15 @@ bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
/* Calculate decay values for all possible times */
for (i = 2; i < damp->decay_array_size; i++)
damp->decay_array[i] = damp->decay_array[i-1] * damp->decay_array[1];
-
+
/* Reuse-list computations */
i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1;
if (i > REUSE_LIST_SIZE || i == 0)
i = REUSE_LIST_SIZE;
- damp->reuse_list_size = i;
+ damp->reuse_list_size = i;
- damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
- damp->reuse_list_size
+ damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
+ damp->reuse_list_size
* sizeof (struct bgp_reuse_node *));
/* Reuse-array computations */
@@ -421,7 +437,7 @@ bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
for (i = 0; i < damp->reuse_index_size; i++)
{
- damp->reuse_index[i] =
+ damp->reuse_index[i] =
(int)(((double)damp->half_life / DELTA_REUSE)
* log10 (1.0 / (damp->reuse_limit * ( 1.0 + ((double)i/damp->scale_factor)))) / log10(0.5));
}
@@ -446,7 +462,7 @@ bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
/* Register reuse timer. */
if (! damp->t_reuse)
- damp->t_reuse =
+ damp->t_reuse =
thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
return 0;
@@ -545,14 +561,14 @@ bgp_get_reuse_time (unsigned int penalty, char *buf, size_t len)
if (penalty > damp->reuse_limit)
{
- reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1]))));
+ reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1]))));
if (reuse_time > damp->max_suppress_time)
reuse_time = damp->max_suppress_time;
tm = gmtime (&reuse_time);
}
- else
+ else
reuse_time = 0;
/* Making formatted timer strings. */
@@ -561,20 +577,20 @@ bgp_get_reuse_time (unsigned int penalty, char *buf, size_t len)
if (reuse_time == 0)
snprintf (buf, len, "00:00:00");
else if (reuse_time < ONE_DAY_SECOND)
- snprintf (buf, len, "%02d:%02d:%02d",
+ snprintf (buf, len, "%02d:%02d:%02d",
tm->tm_hour, tm->tm_min, tm->tm_sec);
else if (reuse_time < ONE_WEEK_SECOND)
- snprintf (buf, len, "%dd%02dh%02dm",
+ snprintf (buf, len, "%dd%02dh%02dm",
tm->tm_yday, tm->tm_hour, tm->tm_min);
else
- snprintf (buf, len, "%02dw%dd%02dh",
- tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+ snprintf (buf, len, "%02dw%dd%02dh",
+ tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
return buf;
}
-
+
void
-bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)
+bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)
{
struct bgp_damp_info *bdi;
time_t t_now, t_diff;
@@ -583,7 +599,7 @@ bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)
if (!binfo->extra)
return;
-
+
/* BGP dampening information. */
bdi = binfo->extra->damp_info;
@@ -616,10 +632,10 @@ bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo,
struct bgp_damp_info *bdi;
time_t t_now, t_diff;
int penalty;
-
+
if (!binfo->extra)
return NULL;
-
+
/* BGP dampening information. */
bdi = binfo->extra->damp_info;
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 26b35dfc..eda146dd 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -19,6 +19,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <zebra.h>
+#include <stdbool.h>
#include <lib/version.h>
#include "prefix.h"
@@ -28,14 +29,22 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "str.h"
#include "log.h"
#include "sockunion.h"
+#include "memory.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 +66,27 @@ 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_sInitial, "Initial" },
+ { bgp_fsm_sIdle, "Idle" },
+ { bgp_fsm_sConnect, "Connect" },
+ { bgp_fsm_sActive, "Active" },
+ { bgp_fsm_sOpenSent, "OpenSent" },
+ { bgp_fsm_sOpenConfirm, "OpenConfirm" },
+ { bgp_fsm_sEstablished, "Established" },
+ { bgp_fsm_sStopping, "Stopping" },
};
-const int bgp_status_msg_max = BGP_STATUS_MAX;
+const int bgp_status_msg_max = bgp_fsm_last_state + 1 ;
+
+const struct message bgp_peer_status_msg[] =
+{
+ { bgp_peer_pIdle, "Idle" },
+ { bgp_peer_pEstablished, "Established" },
+ { bgp_peer_pClearing, "Clearing" },
+ { bgp_peer_pDeleting, "Deleting" },
+};
+const int bgp_peer_status_msg_max = bgp_peer_max_state + 1 ;
/* BGP message type string. */
const char *bgp_type_str[] =
@@ -83,72 +101,73 @@ const char *bgp_type_str[] =
};
/* message for BGP-4 Notify */
-static const struct message bgp_notify_msg[] =
-{
- { BGP_NOTIFY_HEADER_ERR, "Message Header Error"},
- { BGP_NOTIFY_OPEN_ERR, "OPEN Message Error"},
- { BGP_NOTIFY_UPDATE_ERR, "UPDATE Message Error"},
- { BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired"},
- { BGP_NOTIFY_FSM_ERR, "Finite State Machine Error"},
- { BGP_NOTIFY_CEASE, "Cease"},
- { BGP_NOTIFY_CAPABILITY_ERR, "CAPABILITY Message Error"},
+static const struct message bgp_notify_msg[] =
+{
+ { BGP_NOTIFY_HEADER_ERR, "Message Header Error" },
+ { BGP_NOTIFY_OPEN_ERR, "OPEN Message Error" },
+ { BGP_NOTIFY_UPDATE_ERR, "UPDATE Message Error" },
+ { BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired" },
+ { BGP_NOTIFY_FSM_ERR, "Finite State Machine Error" },
+ { BGP_NOTIFY_CEASE, "Cease" },
+ { BGP_NOTIFY_CAPABILITY_ERR, "CAPABILITY Message Error" },
};
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"},
- { BGP_NOTIFY_HEADER_BAD_MESTYPE, "/Bad Message Type"}
+ { BGP_NOTIFY_HEADER_NOT_SYNC, "/Connection Not Synchronized" },
+ { BGP_NOTIFY_HEADER_BAD_MESLEN, "/Bad Message Length" },
+ { BGP_NOTIFY_HEADER_BAD_MESTYPE, "/Bad Message Type" }
};
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_UNSUP_CAPBL, "/Unsupported Capability"},
+ { 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_UNSUP_CAPBL, "/Unsupported Capability" },
};
static const int bgp_notify_open_msg_max = BGP_NOTIFY_OPEN_MAX;
-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"},
- { BGP_NOTIFY_UPDATE_MISS_ATTR, "/Missing Well-known Attribute"},
- { BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error"},
- { BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error"},
- { BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute"},
- { BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP, "/AS Routing Loop"},
- { BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute"},
- { BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
- { BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
- { BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
+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" },
+ { BGP_NOTIFY_UPDATE_MISS_ATTR, "/Missing Well-known Attribute" },
+ { BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error" },
+ { BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error" },
+ { BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute" },
+ { BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP, "/AS Routing Loop" },
+ { BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute" },
+ { BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error" },
+ { BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field" },
+ { BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH" },
};
static const int bgp_notify_update_msg_max = BGP_NOTIFY_UPDATE_MAX;
static const struct message bgp_notify_cease_msg[] =
{
- { BGP_NOTIFY_CEASE_MAX_PREFIX, "/Maximum Number of Prefixes Reached"},
- { BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, "/Administratively Shutdown"},
- { BGP_NOTIFY_CEASE_PEER_UNCONFIG, "/Peer Unconfigured"},
- { BGP_NOTIFY_CEASE_ADMIN_RESET, "/Administratively Reset"},
- { BGP_NOTIFY_CEASE_CONNECT_REJECT, "/Connection Rejected"},
- { BGP_NOTIFY_CEASE_CONFIG_CHANGE, "/Other Configuration Change"},
- { BGP_NOTIFY_CEASE_COLLISION_RESOLUTION, "/Connection collision resolution"},
- { BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resource"},
+ { BGP_NOTIFY_CEASE_MAX_PREFIX, "/Maximum Number of Prefixes Reached" },
+ { BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, "/Administratively Shutdown" },
+ { BGP_NOTIFY_CEASE_PEER_UNCONFIG, "/Peer Unconfigured" },
+ { BGP_NOTIFY_CEASE_ADMIN_RESET, "/Administratively Reset" },
+ { BGP_NOTIFY_CEASE_CONNECT_REJECT, "/Connection Rejected" },
+ { BGP_NOTIFY_CEASE_CONFIG_CHANGE, "/Other Configuration Change" },
+ { BGP_NOTIFY_CEASE_COLLISION_RESOLUTION, "/Connection collision resolution" },
+ { BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resource" },
};
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"},
- { BGP_NOTIFY_CAPABILITY_MALFORMED_CODE, "/Malformed Capability Value"},
+ { BGP_NOTIFY_CAPABILITY_INVALID_ACTION, "/Invalid Action Value" },
+ { BGP_NOTIFY_CAPABILITY_INVALID_LENGTH, "/Invalid Capability Length" },
+ { BGP_NOTIFY_CAPABILITY_MALFORMED_CODE, "/Malformed Capability Value" },
+ { 4, "/Unsupported Capability" }
};
static const int bgp_notify_capability_msg_max = BGP_NOTIFY_CAPABILITY_MAX;
@@ -164,7 +183,7 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
return 0;
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)))
- snprintf (buf, size, "nexthop %s", inet_ntoa (attr->nexthop));
+ snprintf (buf, size, "nexthop %s", safe_inet_ntoa (attr->nexthop));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
snprintf (buf + strlen (buf), size - strlen (buf), ", origin %s",
@@ -176,15 +195,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 +212,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));
@@ -207,11 +226,11 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)))
snprintf (buf + strlen (buf), size - strlen (buf), ", aggregated by %u %s",
attr->extra->aggregator_as,
- inet_ntoa (attr->extra->aggregator_addr));
+ safe_inet_ntoa (attr->extra->aggregator_addr));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)))
snprintf (buf + strlen (buf), size - strlen (buf), ", originator %s",
- inet_ntoa (attr->extra->originator_id));
+ safe_inet_ntoa (attr->extra->originator_id));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)))
{
@@ -220,10 +239,10 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
snprintf (buf + strlen (buf), size - strlen (buf), ", clusterlist");
for (i = 0; i < attr->extra->cluster->length / 4; i++)
snprintf (buf + strlen (buf), size - strlen (buf), " %s",
- inet_ntoa (attr->extra->cluster->list[i]));
+ safe_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,61 +254,110 @@ 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,
- const char *direct)
-{
- const char *subcode_str;
+bgp_notify_print(struct peer *peer, bgp_notify notification)
+{
+ const char* subcode_str ;
+ const char* code_str ;
+ const char* hex_form ;
+ bool log_neighbor_changes ;
+ int length ;
+ char* alloc ;
+ int subcode ;
+
+ /* See if we need to do any of this */
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+ log_neighbor_changes = true ;
+ else if (BGP_DEBUG (normal, NORMAL))
+ log_neighbor_changes = false ;
+ else
+ return ; /* quit if nothing to do */
+
+ /* Sort out string forms of code and subcode */
+ code_str = LOOKUP (bgp_notify_msg, notification->code) ;
- subcode_str = "";
+ subcode = notification->subcode ;
+ subcode_str = "/Unspecific";
- switch (bgp_notify->code)
+ switch (notification->code)
{
case BGP_NOTIFY_HEADER_ERR:
- subcode_str = LOOKUP (bgp_notify_head_msg, bgp_notify->subcode);
+ if (subcode != 0)
+ subcode_str = LOOKUP (bgp_notify_head_msg, subcode);
break;
case BGP_NOTIFY_OPEN_ERR:
- subcode_str = LOOKUP (bgp_notify_open_msg, bgp_notify->subcode);
+ if (subcode != 0)
+ subcode_str = LOOKUP (bgp_notify_open_msg, subcode);
break;
case BGP_NOTIFY_UPDATE_ERR:
- subcode_str = LOOKUP (bgp_notify_update_msg, bgp_notify->subcode);
+ if (subcode != 0)
+ subcode_str = LOOKUP (bgp_notify_update_msg, subcode);
break;
case BGP_NOTIFY_HOLD_ERR:
- subcode_str = "";
- break;
case BGP_NOTIFY_FSM_ERR:
- subcode_str = "";
+ if (subcode != 0)
+ subcode_str = "/*unknown*" ;
+ else
+ subcode_str = "";
break;
case BGP_NOTIFY_CEASE:
- subcode_str = LOOKUP (bgp_notify_cease_msg, bgp_notify->subcode);
+ if (subcode != 0)
+ subcode_str = LOOKUP (bgp_notify_cease_msg, subcode);
break;
case BGP_NOTIFY_CAPABILITY_ERR:
- subcode_str = LOOKUP (bgp_notify_capability_msg, bgp_notify->subcode);
+ if (subcode != 0)
+ subcode_str = LOOKUP (bgp_notify_capability_msg, subcode);
break;
}
- if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
- zlog_info ("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s",
- strcmp (direct, "received") == 0 ? "received from" : "sent to",
- peer->host, bgp_notify->code, bgp_notify->subcode,
- LOOKUP (bgp_notify_msg, bgp_notify->code),
- subcode_str, bgp_notify->length,
- bgp_notify->data ? bgp_notify->data : "");
- else if (BGP_DEBUG (normal, NORMAL))
- plog_debug (peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s",
- peer ? peer->host : "",
- direct, bgp_notify->code, bgp_notify->subcode,
- LOOKUP (bgp_notify_msg, bgp_notify->code),
- subcode_str, bgp_notify->length,
- bgp_notify->data ? bgp_notify->data : "");
-}
-
+ /* Construct hex_form of data, if required. */
+ length = bgp_notify_get_length(notification) ;
+ if (length != 0)
+ {
+ const char* form ;
+ uint8_t* p = bgp_notify_get_data(notification) ;
+ uint8_t* e = p + length ;
+ char* q ;
+
+ hex_form = alloc = q = XMALLOC(MTYPE_TMP, (length * 3) + 1) ;
+
+ form = "%02x" ;
+ while (p < e)
+ {
+ int n = snprintf (q, 4, form, *p++) ;
+ q += n ;
+ form = " %02x" ;
+ } ;
+ }
+ else
+ {
+ hex_form = "" ;
+ alloc = NULL ;
+ } ;
+
+ /* Output the required logging */
+ if (log_neighbor_changes)
+ zlog_info("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s",
+ notification->received ? "received from" : "sent to", peer->host,
+ notification->code, notification->subcode,
+ code_str, subcode_str, length, hex_form) ;
+ else
+ plog_debug(peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s",
+ peer->host, notification->received ? "received" : "sending",
+ notification->code, notification->subcode,
+ code_str, subcode_str, length, hex_form) ;
+
+ /* Release the space allocated to the hex form of the data, if any */
+ if (alloc != NULL)
+ XFREE(MTYPE_TMP, alloc) ;
+} ;
+
/* 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,
@@ -299,7 +367,7 @@ DEFUN (debug_bgp_as4,
BGP_STR
"BGP AS4 actions\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (as4, AS4);
else
{
@@ -317,7 +385,7 @@ DEFUN (no_debug_bgp_as4,
BGP_STR
"BGP AS4 actions\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (as4, AS4);
else
{
@@ -342,7 +410,7 @@ DEFUN (debug_bgp_as4_segment,
"BGP AS4 actions\n"
"BGP AS4 aspath segment handling\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (as4, AS4_SEGMENT);
else
{
@@ -361,7 +429,7 @@ DEFUN (no_debug_bgp_as4_segment,
"BGP AS4 actions\n"
"BGP AS4 aspath segment handling\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (as4, AS4_SEGMENT);
else
{
@@ -386,7 +454,7 @@ DEFUN (debug_bgp_fsm,
BGP_STR
"BGP Finite State Machine\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (fsm, FSM);
else
{
@@ -404,7 +472,7 @@ DEFUN (no_debug_bgp_fsm,
BGP_STR
"Finite State Machine\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (fsm, FSM);
else
{
@@ -428,7 +496,7 @@ DEFUN (debug_bgp_events,
BGP_STR
"BGP events\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (events, EVENTS);
else
{
@@ -446,7 +514,7 @@ DEFUN (no_debug_bgp_events,
BGP_STR
"BGP events\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (events, EVENTS);
else
{
@@ -470,7 +538,7 @@ DEFUN (debug_bgp_filter,
BGP_STR
"BGP filters\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (filter, FILTER);
else
{
@@ -488,7 +556,7 @@ DEFUN (no_debug_bgp_filter,
BGP_STR
"BGP filters\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (filter, FILTER);
else
{
@@ -512,7 +580,7 @@ DEFUN (debug_bgp_keepalive,
BGP_STR
"BGP keepalives\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (keepalive, KEEPALIVE);
else
{
@@ -530,7 +598,7 @@ DEFUN (no_debug_bgp_keepalive,
BGP_STR
"BGP keepalives\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (keepalive, KEEPALIVE);
else
{
@@ -554,7 +622,7 @@ DEFUN (debug_bgp_update,
BGP_STR
"BGP updates\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
{
DEBUG_ON (update, UPDATE_IN);
DEBUG_ON (update, UPDATE_OUT);
@@ -577,7 +645,7 @@ DEFUN (debug_bgp_update_direct,
"Inbound updates\n"
"Outbound updates\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
{
if (strncmp ("i", argv[0], 1) == 0)
{
@@ -585,7 +653,7 @@ DEFUN (debug_bgp_update_direct,
DEBUG_ON (update, UPDATE_IN);
}
else
- {
+ {
DEBUG_OFF (update, UPDATE_IN);
DEBUG_ON (update, UPDATE_OUT);
}
@@ -616,7 +684,7 @@ DEFUN (no_debug_bgp_update,
BGP_STR
"BGP updates\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
{
DEBUG_OFF (update, UPDATE_IN);
DEBUG_OFF (update, UPDATE_OUT);
@@ -643,7 +711,7 @@ DEFUN (debug_bgp_normal,
DEBUG_STR
BGP_STR)
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (normal, NORMAL);
else
{
@@ -660,7 +728,7 @@ DEFUN (no_debug_bgp_normal,
DEBUG_STR
BGP_STR)
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (normal, NORMAL);
else
{
@@ -683,7 +751,7 @@ DEFUN (debug_bgp_zebra,
BGP_STR
"BGP Zebra messages\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_ON (zebra, ZEBRA);
else
{
@@ -701,7 +769,7 @@ DEFUN (no_debug_bgp_zebra,
BGP_STR
"BGP Zebra messages\n")
{
- if (vty->node == CONFIG_NODE)
+ if (vty_get_node(vty) == CONFIG_NODE)
DEBUG_OFF (zebra, ZEBRA);
else
{
@@ -737,7 +805,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_debug.h b/bgpd/bgp_debug.h
index ce8547b0..2b9ca268 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -22,6 +22,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#define _QUAGGA_BGP_DEBUG_H
#include "bgp_attr.h"
+#include "stdbool.h"
/* sort of packet direction */
#define DUMP_ON 1
@@ -120,9 +121,11 @@ extern unsigned long term_bgp_debug_zebra;
extern const char *bgp_type_str[];
extern int bgp_dump_attr (struct peer *, struct attr *, char *, size_t);
-extern void bgp_notify_print (struct peer *, struct bgp_notify *, const char *);
+extern void bgp_notify_print (struct peer* peer, bgp_notify notification);
extern const struct message bgp_status_msg[];
extern const int bgp_status_msg_max;
+extern const struct message bgp_peer_status_msg[];
+extern const int bgp_peer_status_msg_max;
#endif /* _QUAGGA_BGP_DEBUG_H */
diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
index edb725a9..d76b5699 100644
--- a/bgpd/bgp_dump.c
+++ b/bgpd/bgp_dump.c
@@ -33,7 +33,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_dump.h"
-
+
enum bgp_dump_type
{
BGP_DUMP_ALL,
@@ -89,28 +89,28 @@ struct bgp_dump bgp_dump_routes;
/* Dump whole BGP table is very heavy process. */
struct thread *t_bgp_dump_routes;
-
+
/* Some define for BGP packet dump. */
static FILE *
bgp_dump_open_file (struct bgp_dump *bgp_dump)
{
int ret;
time_t clock;
- struct tm *tm;
+ struct tm tm;
char fullpath[MAXPATHLEN];
char realpath[MAXPATHLEN];
mode_t oldumask;
time (&clock);
- tm = localtime (&clock);
+ localtime_r(&clock, &tm);
if (bgp_dump->filename[0] != DIRECTORY_SEP)
{
sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
- ret = strftime (realpath, MAXPATHLEN, fullpath, tm);
+ ret = strftime (realpath, MAXPATHLEN, fullpath, &tm);
}
else
- ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm);
+ ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, &tm);
if (ret == 0)
{
@@ -127,11 +127,11 @@ bgp_dump_open_file (struct bgp_dump *bgp_dump)
if (bgp_dump->fp == NULL)
{
- zlog_warn ("bgp_dump_open_file: %s: %s", realpath, strerror (errno));
+ zlog_warn("bgp_dump_open_file: %s: %s", realpath, errtoa(errno, 0).str);
umask(oldumask);
return NULL;
}
- umask(oldumask);
+ umask(oldumask);
return bgp_dump->fp;
}
@@ -141,7 +141,7 @@ bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
{
int secs_into_day;
time_t t;
- struct tm *tm;
+ struct tm tm;
if (interval > 0)
{
@@ -152,11 +152,11 @@ bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
* intervals, dump every interval seconds starting from midnight
*/
(void) time(&t);
- tm = localtime(&t);
- secs_into_day = tm->tm_sec + 60*tm->tm_min + 60*60*tm->tm_hour;
+ localtime_r(&t, &tm);
+ secs_into_day = tm.tm_sec + 60*tm.tm_min + 60*60*tm.tm_hour;
interval = interval - secs_into_day % interval; /* always > 0 */
}
- bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func,
+ bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func,
bgp_dump, interval);
}
else
@@ -179,7 +179,7 @@ bgp_dump_header (struct stream *obuf, int type, int subtype)
time (&now);
/* Put dump packet header. */
- stream_putl (obuf, now);
+ stream_putl (obuf, now);
stream_putw (obuf, type);
stream_putw (obuf, subtype);
@@ -348,7 +348,7 @@ bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
/* Entry count, note that this is overwritten later */
stream_putw(obuf, 0);
- for (info = rn->info; info; info = info->next)
+ for (info = rn->info; info; info = info->info_next)
{
entry_count++;
@@ -356,11 +356,7 @@ bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
stream_putw(obuf, info->peer->table_dump_index);
/* Originated */
-#ifdef HAVE_CLOCK_MONOTONIC
- stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime));
-#else
- stream_putl (obuf, info->uptime);
-#endif /* HAVE_CLOCK_MONOTONIC */
+ stream_putl (obuf, bgp_wall_clock(info->uptime));
/* Dump attribute. */
/* Skip prefix & AFI/SAFI for MP_NLRI */
@@ -504,7 +500,7 @@ bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
/* Dump header and common part. */
if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
- {
+ {
bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4);
}
else
@@ -515,7 +511,7 @@ bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
/* Packet contents. */
stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
-
+
/* Set length. */
bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
@@ -535,7 +531,7 @@ bgp_dump_packet (struct peer *peer, int type, struct stream *packet)
if (type == BGP_MSG_UPDATE)
bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
}
-
+
static unsigned int
bgp_dump_parse_time (const char *str)
{
@@ -589,10 +585,10 @@ bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump,
const char *interval_str)
{
unsigned int interval;
-
+
if (interval_str)
{
-
+
/* Check interval string. */
interval = bgp_dump_parse_time (interval_str);
if (interval == 0)
@@ -614,13 +610,13 @@ bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump,
if (bgp_dump->interval_str)
free (bgp_dump->interval_str);
bgp_dump->interval_str = strdup (interval_str);
-
+
}
else
{
interval = 0;
}
-
+
/* Create interval thread. */
bgp_dump_interval_add (bgp_dump, interval);
@@ -669,7 +665,7 @@ bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
free (bgp_dump->interval_str);
bgp_dump->interval_str = NULL;
}
-
+
return CMD_SUCCESS;
}
@@ -816,36 +812,36 @@ config_write_bgp_dump (struct vty *vty)
if (bgp_dump_all.filename)
{
if (bgp_dump_all.interval_str)
- vty_out (vty, "dump bgp all %s %s%s",
+ vty_out (vty, "dump bgp all %s %s%s",
bgp_dump_all.filename, bgp_dump_all.interval_str,
VTY_NEWLINE);
else
- vty_out (vty, "dump bgp all %s%s",
+ vty_out (vty, "dump bgp all %s%s",
bgp_dump_all.filename, VTY_NEWLINE);
}
if (bgp_dump_updates.filename)
{
if (bgp_dump_updates.interval_str)
- vty_out (vty, "dump bgp updates %s %s%s",
+ vty_out (vty, "dump bgp updates %s %s%s",
bgp_dump_updates.filename, bgp_dump_updates.interval_str,
VTY_NEWLINE);
else
- vty_out (vty, "dump bgp updates %s%s",
+ vty_out (vty, "dump bgp updates %s%s",
bgp_dump_updates.filename, VTY_NEWLINE);
}
if (bgp_dump_routes.filename)
{
if (bgp_dump_routes.interval_str)
- vty_out (vty, "dump bgp routes-mrt %s %s%s",
+ vty_out (vty, "dump bgp routes-mrt %s %s%s",
bgp_dump_routes.filename, bgp_dump_routes.interval_str,
VTY_NEWLINE);
else
- vty_out (vty, "dump bgp routes-mrt %s%s",
+ vty_out (vty, "dump bgp routes-mrt %s%s",
bgp_dump_routes.filename, VTY_NEWLINE);
}
return 0;
}
-
+
/* Initialize BGP packet dump functionality. */
void
bgp_dump_init (void)
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 8d91c746..c107b031 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -709,7 +709,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format)
eip.val |= (*pnt++);
len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
- inet_ntoa (eip.ip), eip.val);
+ safe_inet_ntoa (eip.ip), eip.val);
str_pnt += len;
first = 0;
}
diff --git a/bgpd/bgp_engine.c b/bgpd/bgp_engine.c
new file mode 100644
index 00000000..b1207ebc
--- /dev/null
+++ b/bgpd/bgp_engine.c
@@ -0,0 +1,144 @@
+/* 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.
+ *
+ *
+ */
+
+/*==============================================================================
+ * Start the BGP Engine Thread.
+ *
+ * Initialise the Engine Thread qpnexus
+ *
+ */
+
+/* BGP Engine side of bgp_engine_start() must call bgp_open_listeners()
+ * for which it needs the port and address from command line.
+ *
+ * Implemented in bgp_main.c
+ */
+
+/*==============================================================================
+ * Stop the BGP Engine Thread.
+ *
+ */
+
+/* BGP Engine side of bgp_engine_stop() must call bgp_close_listeners()
+ *
+ * Implemented in bgp_main.c
+ */
+
+/*==============================================================================
+ * 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.
+ *
+ * Implemented in qpnexus.c
+ *
+ */
+
+
+/*==============================================================================
+ * 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..ceec1b2f
--- /dev/null
+++ b/bgpd/bgp_engine.h
@@ -0,0 +1,144 @@
+/* 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/bgpd.h"
+
+#include "lib/mqueue.h"
+#include "lib/qpnexus.h"
+#include "lib/log.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+enum { qdebug =
+#ifdef QDEBUG
+ 1
+#else
+ 0
+#endif
+};
+
+/*==============================================================================
+ *
+ */
+
+struct queue_stats
+{
+ unsigned count ;
+ unsigned long total ;
+ unsigned max ;
+ unsigned recent ;
+
+ unsigned xon ;
+ unsigned event ;
+ unsigned update ;
+} ;
+
+static struct queue_stats bgp_engine_queue_stats ;
+static struct queue_stats routing_engine_queue_stats ;
+
+Inline void
+bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats)
+{
+ unsigned long average ;
+ unsigned av_i ;
+ unsigned av_f ;
+ unsigned my_count ;
+ mqueue_block mqb ;
+
+ ++stats->count ;
+
+ qpt_mutex_lock(&mq->mutex) ;
+
+ if (mq->count > stats->max)
+ stats->max = mq->count ;
+ if (mq->count > stats->recent)
+ stats->recent = mq->count ;
+
+ stats->total += mq->count ;
+
+ if (stats->count < 1000)
+ {
+ qpt_mutex_unlock(&mq->mutex) ;
+ return ;
+ } ;
+
+ my_count = 0 ;
+
+ mqb = mq->head ;
+ while (mqb != NULL)
+ {
+ ++my_count ;
+ mqb = mqb->next ;
+ } ;
+
+ assert(my_count == mq->count) ;
+
+ qpt_mutex_unlock(&mq->mutex) ;
+
+ average = stats->total * 1000 ;
+ average = (average / stats->count) + 5 ;
+ av_i = average / 1000 ;
+ av_f = (average % 1000) / 10 ;
+
+ zlog_debug("%s queue: max=%u recent: max=%u av=%d.%.2d (%u) [x=%u e=%u u=%u]",
+ name, stats->max, stats->recent, av_i, av_f, stats->count,
+ stats->xon, stats->event, stats->update) ;
+
+ stats->recent = 0 ;
+ stats->count = 0 ;
+ stats->total = 0 ;
+
+ stats->event = 0 ;
+ stats->update = 0 ;
+ stats->xon = 0 ;
+} ;
+
+/* Send given message to the BGP Engine -- priority/ordinary
+ */
+Inline void
+bgp_to_bgp_engine(mqueue_block mqb, enum mqb_rank priority)
+{
+ mqueue_enqueue(bgp_nexus->queue, mqb, priority) ;
+ if (qdebug)
+ bgp_queue_logging("BGP Engine", bgp_nexus->queue, &bgp_engine_queue_stats) ;
+} ;
+
+/*==============================================================================
+ *
+ */
+
+/* Send given message to the Routing Engine -- priority/ordinary
+ */
+Inline void
+bgp_to_routing_engine(mqueue_block mqb, enum mqb_rank priority)
+{
+ mqueue_enqueue(routing_nexus->queue, mqb, priority) ;
+ if (qdebug)
+ bgp_queue_logging("Routing Engine", routing_nexus->queue,
+ &routing_engine_queue_stats) ;
+} ;
+
+#endif /* QUAGGA_BGP_ENGINE_H */
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 487ebddb..a03f1b2c 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -1,1111 +1,2541 @@
-/* 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 "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_session.h"
+#include "bgpd/bgp_connection.h"
+#include "bgpd/bgp_notification.h"
#include "bgpd/bgp_fsm.h"
-#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_msg_write.h"
+#include "bgpd/bgp_msg_read.h"
+
+#include "lib/qtimers.h"
+#include "lib/sockunion.h"
+
+#include "bgpd/bgp_debug.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)
+/*==============================================================================
+ * The BGP Finite State Machine
+ *
+ * The working of BGP is described in the RFC (4271) in terms of a finite
+ * state machine.
+ *
+ * This code follows the older Quagga code, which appears to be based on the
+ * earlier RFC 1771.
+ *
+ * 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.
+ *
+ * In general the FSM manages connections, but there is some interaction with
+ * the session. In particular, exceptions are expressed as session_eXXX
+ * values -- which are passed to the Routing Engine as session events. The
+ * handling of FSM events is depends mostly on the FSM state, but any
+ * exception influences that too.
+ *
+ * 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.
+ *
+ * TODO: restore NSF
+ * TODO: track incoming connections while established
+ *
+ * To do this need to accept connection while established, then wait for
+ * OPEN. While waiting, the established connection may drop, and
+ * so the session restart, but with an incoming connection already
+ * in place.
+ *
+ * When OPEN arrives, for NSF should close any established connection
+ * and restart with the new one.
+ *
+ * Otherwise, if CollisionDetectEstablishedState option is set, should
+ * go through collision detection !
+ *
+ *------------------------------------------------------------------------------
+ * FSM "events" and Session "exceptions".
+ *
+ * These are raised when:
+ *
+ * * the BGP Engine receives instructions from the Routeing Engine
+ *
+ * * some I/O operations complete
+ *
+ * * some I/O operations fail
+ *
+ * * timers go off
+ *
+ * FSM events are raised by calling bgp_fsm_event(). A number of events are
+ * associated with session exceptions -- the exception is posted in the
+ * connection and then a suitable FSM event is raised.
+ *
+ * The most general FSM event is fsm_eBGP_Stop -- which MUST have a posted
+ * exception to tell it why the stop has been been raised.
+ *
+ * However, nothing external calls bgp_fsm_event() directly -- functions
+ * defined here will raise the appropriate event.
+ *
+ * Note that the event is dealt with *immediately* -- there is no queueing of
+ * events. This avoids the problem of the state of the connection being
+ * "out of date" until its event queue is emptied, and the problem of the state
+ * changing between the raising of an event and dealing with same.
+ *
+ * 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 "follow on" event, to be processed at
+ * immediately after the current event and any state change that makes.
+ *
+ * Also, some things within the FSM are most consistently dealt with by
+ * raising follow on events.
+ *
+ * Note that there is only one level of "follow on" event. The FSM never
+ * issues more than one I/O operation per event or more than one follow on
+ * event, and never both at the same time. (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 sOpenConfirm state and
+ * hence sEstablished.
+ *
+ * When a session is enabled, the allowed connections are initialised and
+ * a BGP_Start event issued for each one.
+ *
+ * The listeners (in bgp_network) will only accept connections from addresses
+ * known to be peer addresses, and then only when the FSM is ready for them.
+ * The accept field in the peer_index entry (see bgp_peer_index) is maintained
+ * by this code -- the accept field (when set) points to the secondary
+ * connection of the session.
+ *
+ * Up to sEstablished 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). In sEstablished state, the primary
+ * connection is the one that won the race -- any other connection has been
+ * snuffed out.
+ *
+ * As per the RFC, collision detection/resolution is performed when an OPEN
+ * message is received -- that is, as the connection attempts to advance to
+ * sOpenConfirm state. At that point, if the sibling is in sOpenConfirm state,
+ * then one of the two connections is closed (and will go sIdle once the
+ * NOTIFICATION has been sent).
+ *
+ * See below for a discussion of the fall back to sIdle -- the losing connection
+ * will remain comatose until the winner either reaches sEstablished (when the
+ * loser is snuffed out) or the winner falls back to sIdle (when the
+ * IdleHoldTimer for the loser is set, and it will wake up in due course).
+ *
+ * NB: the RFC talks of matching source/destination and destination/source
+ * addresses of connections in order to detect collisions. This code
+ * uses only the far end address to detect collisions. It does so
+ * implicitly because the in-bound connection is matched with the out-
+ * bound one using the peer's known IP address -- effectively its name.
+ *
+ * [It is not deemed relevant if the local addresses for the in- and out-
+ * bound connections are different.]
+ *
+ * The RFC further says "the local system MUST examine all of its
+ * connections that are in OpenConfirm state" ... "If, among these
+ * connections, there is a connection to a remote BGP speaker whose BGP
+ * identifier equals the one in the OPEN message, and this connection
+ * collides with [it]" ... then must resolve the collision.
+ *
+ * This code almost does this, but:
+ *
+ * * there can only be one connection that collides (i.e. only one other
+ * which has the same remote end address), and that is the sibling
+ * connection.
+ *
+ * So there's not a lot of "examining" to be done.
+ *
+ * * the RFC seems to accept that there could be two distinct connections
+ * with the same remote end address, but *different* BGP Identifiers.
+ *
+ * As far as Quagga is concerned, that is impossible. The remote end
+ * IP address is the name of the peering session, and there cannot
+ * be two peering sessions with the same name. It follows that Quagga
+ * requires that the "My AS" and the "BGP Identifier" entries in the
+ * OPEN messages from a given remote end IP address MUST MATCH !
+ *
+ *------------------------------------------------------------------------------
+ * The FSM proceeds in three basic phases:
+ *
+ * 1) attempting to establish a TCP connection: sIdle/sActive/sConnect
+ *
+ * In this phase there is no connection for the other end to close !
+ *
+ * sIdle is a "stutter step" which becomes longer each time the FSM falls
+ * back to sIdle, which it does if the process fails in sOpenSent or
+ * sOpenConfirm.
+ *
+ * Cannot fail in sIdle !
+ *
+ * In sActive/sConnect any failure causes the FSM to stop trying to
+ * connect. It does nothing further until the end of the ConnectRetryTimer
+ * interval -- at which point it will try again, re-charging the timer.
+ * (That is usually 120 seconds (less jitter) -- so in the worst case, it
+ * will try to do something impossible every 90-120 seconds.)
+ *
+ * A connection may fall back to sIdle from sOpenSent/sOpenConfirm (see
+ * below). While one connection is sOpenSent or sOpenConfirm don't really
+ * want to start another TCP connection in competition. So, on entry
+ * to sIdle:
+ *
+ * * if a sibling exists and is in sOpenSent or sOpenConfirm:
+ *
+ * - do not change the IdleHoldTimer interval.
+ * - unset the IdleHoldTimer.
+ * - set self "comatose".
+ *
+ * * otherwise:
+ *
+ * - increase the IdleHoldTimer interval.
+ * - set the IdleHoldTimer (with jitter).
+ *
+ * and if a sibling exists and is comatose:
+ *
+ * - set *its* IdleHoldTimer (with jitter).
+ * - clear *its* comatose flag.
+ *
+ * The effect is that if both connections make it to sOpenSent, then only
+ * when *both* fall back to sIdle will the FSM try to make any new TCP
+ * connections.
+ *
+ * The IdleHoldTimer increases up to 120 seconds. In the worst case, the
+ * far end repeatedly makes outgoing connection attempts, and immediately
+ * drops them. In which case, the IdleHoldTimer grows, and the disruption
+ * reduces to once every 90-120 seconds !
+ *
+ * 2) attempting to establish a BGP session: sOpenSent/sOpenConfirm
+ *
+ * If something goes wrong, or the other end closes the connection (with
+ * or without notification) the FSM will loop back to sIdle state. Also,
+ * when collision resolution closes one connection it too loops back to
+ * sIdle (see above).
+ *
+ * Both connections may reach sOpenSent. Only one at once can reach
+ * sOpenConfirm -- collision resolution sees to that.
+ *
+ * Note that while a NOTIFICATION is being sent the connection stays
+ * in sOpenSent/sOpenConfirm state.
+ *
+ * 3) BGP session established
+ *
+ * If something goes wrong, or the other end closes the connection
+ * (with or without notification) will stop the session.
+ *
+ * Only three things bring the FSM to a dead stop, and stop the session:
+ *
+ * 1) the Routeing Engine disabling the session.
+ *
+ * 2) invalid events -- which are assumed to be bugs, really.
+ *
+ * 3) anything that stops the session while in sEstablished state.
+ *
+ * This means that the FSM will plough on trying to establish connections with
+ * configured peers, even in circumstances when the likelihood of success
+ * appears slim to vanishing. However, the Routeing Engine and the operator
+ * are responsible for the decision to start and to stop trying to connect.
+ *
+ *------------------------------------------------------------------------------
+ * Exception handling.
+ *
+ * The basic mechanism is:
+ *
+ * * exceptions may the "thrown" -- which posts a given exception in the
+ * connection then kicks the FSM with a given fsm_eXxxxx event.
+ *
+ * Information posted is:
+ *
+ * sesssion_eXxxxx -- what the exception is
+ * notification -- any NOTIFICATION message
+ * err -- any I/O or other error
+ *
+ * on exit from the FSM this information is passed to the Routing Engine.
+ *
+ * Can throw exceptions within the FSM, as discussed above.
+ *
+ * * within the FSM exceptions are "caught".
+ *
+ * Which deals with the exception as thrown, depending on the next state.
+ *
+ * See the various exception functions below for what exceptions are posted and
+ * what fsm_eXxxx events are generated.
+ *
+ * The following fsm events require an exception:
+ *
+ * bgp_fsm_eBGP_Stop -- bgp_fsm_exception()
+ * bgp_fsm_eTCP_connection_closed -- bgp_fsm_io_error()
+ * bgp_fsm_eTCP_connection_open_failed -- bgp_fsm_connect_completed()
+ * bgp_fsm_eTCP_fatal_error -- bgp_fsm_io_fatal_error()
+ * bgp_fsm_eReceive_NOTIFICATION_message -- bgp_fsm_notification_exception()
+ *
+ *------------------------------------------------------------------------------
+ * FSM errors
+ *
+ * Invalid events: if the FSM receives an event that cannot be raised in the
+ * current state, it will terminate the session, sending an FSM Error
+ * NOTIFICATION (if a TCP connection is up). See bgp_fsm_invalid().
+ *
+ * If the FSM receives a message type that is not expected in the current,
+ * state, it will close the connection (if sOpenSent or sOpenConfirm) or stop
+ * the session (if sEstablished), also sending an FSM Error NOTIFICATION.
+ * See bgp_fsm_error().
+ *
+ *------------------------------------------------------------------------------
+ * Sending NOTIFICATION message
+ *
+ * In sOpenSent, sOpenConfirm and sEstablished states may send a NOTIFICATION
+ * message.
+ *
+ * The procedure for sending a NOTIFICATION is:
+ *
+ * -- close the connection for reading and clear read buffers.
+ *
+ * This ensures that no further read I/O can occur and no related events.
+ *
+ * Note that anything sent from the other end is acknowledged, but
+ * quietly discarded.
+ *
+ * -- purge the write buffer of all output except any partly sent message.
+ *
+ * This ensures there is room in the write buffer at the very least.
+ *
+ * For sOpenSent and sOpenConfirm states there should be zero chance of
+ * there being anything to purge.
+ *
+ * -- purge any pending write messages for the connection (for sEstablished).
+ *
+ * -- set notification_pending = 1 (write pending)
+ *
+ * -- write the NOTIFICATION message.
+ *
+ * For sEstablished, the message will at the very least be written to the
+ * write buffer. For sOpenSent and sOpenConfirm expect it to go directly
+ * to the TCP buffer.
+ *
+ * -- stop the KeepaliveTimer
+ *
+ * -- set HoldTimer to a waiting to clear buffer time -- say 20 secs.
+ *
+ * Don't expect to need to wait at all in sOpenSent/sOpenConfirm states.
+ *
+ * -- when the NOTIFICATION message clears the write buffer, that will
+ * generate a Sent_NOTIFICATION_message event.
+ *
+ * After sending the NOTIFICATION, sOpenSent & sOpenConfirm stay in their
+ * respective states. sEstablished goes to sStopping State.
+ *
+ * When the Sent_NOTIFICATION_message event occurs, set the HoldTimer to
+ * a "courtesy" time of 5 seconds. Remain in the current state.
+ *
+ * During the "courtesy" time the socket will continue to acknowledge, but
+ * discard input. In the case of Collision Resolution this gives a little time
+ * for the other end to send its NOTIFICATION message. In all cases, it gives
+ * a little time before the connection is slammed shut.
+ *
+ * When the HoldTimer expires close the connection completely (whether or not
+ * the NOTIFICATION has cleared the write buffer).
+ *
+ *------------------------------------------------------------------------------
+ * Communication with the Routeing Engine
+ *
+ * The FSM sends bgp_session_event messages to the Routeing Engine which keep
+ * it up to date with the progress and state of the FSM.
+ *
+ * In particular, these event messages tell the Routeing Engine when the
+ * session enters and leaves sEstablished, when the session stops for any
+ * reason and when the session has been disabled -- which are what really
+ * matters to it !
+ */
+
+static void
+bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) ;
+
+/*==============================================================================
+ * Recharge the HoldTimer
+ *
+ * Defined here for the convenience of bgp_fsm_pre_update(), which is called
+ * once for every incoming update.
+ *
+ * NB: no jitter.
+ *
+ * NB: do nothing if connection->hold_timer_interval == 0
+ *
+ * NB: if connection->hold_timer_interval != 0, timer MUST be set
+ */
+static inline void
+bgp_hold_timer_recharge(bgp_connection connection)
{
- int jitter = 0;
+ if (connection->hold_timer_interval != 0)
+ qtimer_set_interval(connection->hold_timer,
+ QTIME(connection->hold_timer_interval), NULL) ;
+} ;
+
+/*==============================================================================
+ * Enable the given session -- which must be newly initialised.
+ *
+ * This is the first step in the FSM, and the connection advances to Idle.
+ *
+ * Returns in something of a hurry if not enabled for connect() or for accept().
+ *
+ * NB: requires the session LOCKED
+ */
+extern void
+bgp_fsm_enable_session(bgp_session session)
+{
+ bgp_connection connection ;
- switch (peer->status)
+ /* Proceed instantly to a dead stop if neither connect nor listen ! */
+ if (!(session->connect || session->listen))
{
- 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;
+ bgp_session_event(session, bgp_session_eInvalid, NULL, 0, 0, 1) ;
+ return ;
+ } ;
+
+ /* Primary connection -- if connect allowed
+ *
+ * NB: the start event for the primary connection is guaranteed to succeed,
+ * and nothing further will happen until the initial IdleHoldTimer
+ * expires -- always has a small, non-zero time.
+ *
+ * This ensures that the secondary connection can be started before
+ * there's any change of the session being torn down !!
+ */
+ if (session->connect)
+ {
+ connection = bgp_connection_init_new(NULL, session,
+ bgp_connection_primary) ;
+ bgp_fsm_event(connection, bgp_fsm_eBGP_Start) ;
+ } ;
- 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;
+ /* Secondary connection -- if listen allowed
+ */
+ if (session->listen)
+ {
+ connection = bgp_connection_init_new(NULL, session,
+ bgp_connection_secondary) ;
+ bgp_fsm_event(connection, bgp_fsm_eBGP_Start) ;
+ } ;
+} ;
+
+ /*=============================================================================
+ * Signalling events and throwing exceptions.
+ *
+ */
+static void
+bgp_fsm_throw(bgp_connection connection, bgp_session_event_t exception,
+ bgp_notify notification, int err, bgp_fsm_event_t event) ;
+
+static bgp_fsm_state_t
+bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) ;
+
+/*------------------------------------------------------------------------------
+ * Signal that valid OPEN message has been received and processed into the
+ * connection->open_recv.
+ */
+extern void
+bgp_fsm_open_received(bgp_connection connection)
+{
+ bgp_fsm_event(connection, bgp_fsm_eReceive_OPEN_message) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal that valid KEEPALIVE message has been received.
+ */
+extern void
+bgp_fsm_keepalive_received(bgp_connection connection)
+{
+ bgp_fsm_event(connection, bgp_fsm_eReceive_KEEPALIVE_message);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal that have received a message that is some form of "update".
+ *
+ * If is sEstablished: re-charge the HoldTimer.
+ *
+ * otherwise: raise bfp_fsm_eUpdate event, which will most likely
+ * throw an error.
+ *
+ * Avoids going through the full FSM process for update events (of which there
+ * may be many) in the simple case -- where only need to re-charge HoldTimer.
+ *
+ * Deals, via the FSM, with unexpected "update" events -- for example an
+ * UPDATE (or ROUTE-REFRESH) before reaching sEstablished !
+ */
+extern int
+bgp_fsm_pre_update(bgp_connection connection)
+{
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ bgp_hold_timer_recharge(connection) ;
+ return 0 ;
+ } ;
+
+ bgp_fsm_event(connection, bgp_fsm_eReceive_UPDATE_message) ;
+ return -1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal that notification message has cleared buffers into TCP.
+ */
+extern void
+bgp_fsm_notification_sent(bgp_connection connection)
+{
+ bgp_fsm_event(connection, bgp_fsm_eSent_NOTIFICATION_message) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Ultimate exception -- disable the session
+ *
+ * Does nothing if neither connection exists (which implies the session has
+ * already been disabled, or never got off the ground).
+ *
+ * NB: takes responsibility for the notification structure.
+ *
+ * NB: requires the session LOCKED
+ */
+extern void
+bgp_fsm_disable_session(bgp_session session, bgp_notify notification)
+{
+ bgp_connection connection ;
- 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;
+ connection = session->connections[bgp_connection_primary] ;
+ if (connection == NULL)
+ connection = session->connections[bgp_connection_secondary] ;
- 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);
+ if (connection != NULL)
+ bgp_fsm_exception(connection, bgp_session_eDisabled, notification) ;
+ else
+ {
+ /* Acknowledge the disable -- session is stopped. */
+ bgp_session_event(session, bgp_session_eDisabled, NULL, 0, 0, 1) ;
+
+ bgp_notify_free(notification) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Throw a general exception -- not I/O related.
+ *
+ * Note that I/O problems are signalled by bgp_fsm_io_error().
+ *
+ * NB: can throw an exception for other connections while in the FSM.
+ *
+ * Can throw an exception for the current connection while in the FSM, the
+ * fsm_active/follow_on mechanism looks after this.
+ */
+extern void
+bgp_fsm_exception(bgp_connection connection, bgp_session_event_t exception,
+ bgp_notify notification)
+{
+ bgp_fsm_throw(connection, exception, notification, 0, bgp_fsm_eBGP_Stop) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Raise a discard exception for sibling.
+ *
+ * A connection will discard any sibling if:
+ *
+ * * the session is being disabled (by the Routing Engine)
+ *
+ * * an invalid event is bringing down the session
+ *
+ * * it has reached Established state, and is snuffing out the sibling.
+ *
+ * NB: requires the session LOCKED
+ */
+static void
+bgp_fsm_discard_sibling(bgp_connection sibling, bgp_notify notification)
+{
+ bgp_fsm_throw(sibling, bgp_session_eDiscard,
+ notification, 0, bgp_fsm_eBGP_Stop) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Raise a NOTIFICATION received exception
+ */
+extern void
+bgp_fsm_notification_exception(bgp_connection connection,
+ bgp_notify notification)
+{
+ bgp_fsm_throw(connection, bgp_session_eNOM_recv, notification, 0,
+ bgp_fsm_eReceive_NOTIFICATION_message) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Raise a "fatal I/O error" exception on the given connection.
+ *
+ * Error to be reported as "TCP_fatal_error".
+ */
+extern void
+bgp_fsm_io_fatal_error(bgp_connection connection, int err)
+{
+ plog_err (connection->log, "%s [Error] bgp IO error: %s",
+ connection->host, errtoa(err, 0).str) ;
+
+ assert(err != EFAULT) ;
+
+ bgp_fsm_throw(connection, bgp_session_eTCP_error, NULL, err,
+ bgp_fsm_eTCP_fatal_error) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Raise an "I/O error" exception 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)
+{
+ 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),
+ errtoa(err, 0).str) ;
+ } ;
+
+ bgp_fsm_throw(connection, bgp_session_eTCP_dropped, NULL, err,
+ bgp_fsm_eTCP_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: start the connection (can now write to it)
+ * and 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.
+ *
+ * NB: in any case on entry to this function the file is *disabled* in all
+ * modes.
+ */
+extern void
+bgp_fsm_connect_completed(bgp_connection connection, int err,
+ union sockunion* su_local,
+ union sockunion* su_remote)
{
- 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;
-}
-
-/* BGP connect retry timer. */
-static int
-bgp_connect_timer (struct thread *thread)
+ if (err == 0)
+ {
+ bgp_connection_start(connection, su_local, su_remote) ;
+ bgp_fsm_event(connection, bgp_fsm_eTCP_connection_open) ;
+ }
+ else if ( (err == ECONNREFUSED)
+ || (err == ECONNRESET)
+ || (err == EHOSTUNREACH)
+ || (err == ETIMEDOUT) )
+ bgp_fsm_throw(connection, bgp_session_eTCP_failed, NULL, err,
+ bgp_fsm_eTCP_connection_open_failed) ;
+ else
+ bgp_fsm_io_fatal_error(connection, err) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Post the given exception and raise the given event.
+ *
+ * NB: takes responsibility for the notification structure.
+ */
+static void
+bgp_fsm_throw(bgp_connection connection, bgp_session_event_t exception,
+ bgp_notify notification, int err, bgp_fsm_event_t event)
{
- 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);
-
- THREAD_VAL (thread) = ConnectRetry_timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
-
- return 0;
-}
-
-/* BGP holdtime timer. */
-static int
-bgp_holdtime_timer (struct thread *thread)
+ connection->exception = exception ;
+ bgp_notify_set(&connection->notification, notification) ;
+ connection->err = err ;
+
+ bgp_fsm_event(connection, event) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Post the given exception and raise a follow-on fsm_eBGP_Stop event
+ *
+ * This is for use *within* the FSM.
+ *
+ * Returns the current connection state, so the follow-on fsm_eBGP_Stop uses
+ * action from the current state.
+ *
+ * In most cases the effect is to translate one event into a suitable general
+ * exception -- to be handled same like any other, in the current state.
+ *
+ * NB: takes responsibility for the notification structure.
+ */
+static bgp_fsm_state_t
+bgp_fsm_throw_stop(bgp_connection connection, bgp_session_event_t exception,
+ bgp_notify notification)
{
- struct peer *peer;
+ bgp_fsm_throw(connection, exception, notification, 0, bgp_fsm_eBGP_Stop) ;
+ return connection->state ;
+} ;
- peer = THREAD_ARG (thread);
- peer->t_holdtime = NULL;
+/*==============================================================================
+ * For debug...
+ */
+#define BGP_FSM_DEBUG(connection, message) \
+ if (BGP_DEBUG (fsm, FSM)) \
+ plog_debug (connection->log, "%s [FSM] " message, connection->host)
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (holdtime timer expire)",
- peer->host);
+/*==============================================================================
+ * The FSM table
+ */
- THREAD_VAL (thread) = Hold_Timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
+#define bgp_fsm_action(FUNCNAME) \
+ bgp_fsm_state_t FUNCNAME(bgp_connection connection, \
+ bgp_fsm_state_t next_state, bgp_fsm_event_t event)
- return 0;
-}
+typedef bgp_fsm_action(bgp_fsm_action_func) ;
-/* BGP keepalive fire ! */
-static int
-bgp_keepalive_timer (struct thread *thread)
+struct bgp_fsm
{
- struct peer *peer;
-
- peer = THREAD_ARG (thread);
- peer->t_keepalive = NULL;
+ bgp_fsm_action_func* action ;
+ bgp_fsm_state_t next_state ;
+} ;
+
+static bgp_fsm_action(bgp_fsm_null) ;
+static bgp_fsm_action(bgp_fsm_enter) ;
+static bgp_fsm_action(bgp_fsm_stop) ;
+static bgp_fsm_action(bgp_fsm_invalid) ;
+static bgp_fsm_action(bgp_fsm_start) ;
+static bgp_fsm_action(bgp_fsm_half_open) ;
+static bgp_fsm_action(bgp_fsm_connect) ;
+static bgp_fsm_action(bgp_fsm_accept) ;
+static bgp_fsm_action(bgp_fsm_send_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_closed) ;
+static bgp_fsm_action(bgp_fsm_expire) ;
+static bgp_fsm_action(bgp_fsm_recv_open) ;
+static bgp_fsm_action(bgp_fsm_error) ;
+static bgp_fsm_action(bgp_fsm_recv_nom) ;
+static bgp_fsm_action(bgp_fsm_sent_nom) ;
+static bgp_fsm_action(bgp_fsm_send_kal) ;
+static bgp_fsm_action(bgp_fsm_establish) ;
+static bgp_fsm_action(bgp_fsm_recv_kal) ;
+static bgp_fsm_action(bgp_fsm_update) ;
+static bgp_fsm_action(bgp_fsm_exit) ;
+
+/*------------------------------------------------------------------------------
+ * 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 sInitial state (-> sIdle)
+ *
+ * raised immediately after creating the connection.
+ *
+ * b. in sIdle state
+ *
+ * raised on expiry of IdleHoldTime.
+ *
+ * primary connection: proceed to sConnect
+ *
+ * secondary connection: proceed to sAccept
+ *
+ * c. in sConnect
+ *
+ * raised on expiry of ConnectRetryTime.
+ *
+ * Start a new connect() attempt.
+ *
+ * d. in sActive
+ *
+ * raised on expiry of ConnectRetryTime.
+ *
+ * Start a new accept() attempt.
+ *
+ * 2. BGP_Stop -- process the posted exception (invalid if none !)
+ *
+ * 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.
+ *
+ * -- in the event of any general exception.
+ *
+ * 3. TCP_connection_open
+ *
+ * a. primary connection: in sConnect state (-> OpenSent)
+ *
+ * raised when a connect() connection succeeds
+ *
+ * b. secondary connection: in sIdle state (stay sIdle)
+ *
+ * raised when an accept() connection is accepted.
+ *
+ * The connection is not refused, but it is ignored until the
+ * IdleHoldTimer expires.
+ *
+ * c. secondary connection: in sActive state (-> OpenSent)
+ *
+ * raised when an accept() connection is accepted.
+ *
+ * Cannot happen at any other time.
+ *
+ * 4. TCP_connection_closed -- process the posted exception (MUST be there)
+ *
+ * Raised by "EOF" on read or by EPIPE and some other errors.
+ *
+ * a. in sOpenSent and sOpenConfirm 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 sIdle.
+ *
+ * b. and sEstablished state
+ *
+ * Stop the session.
+ *
+ * NB: any errors generated when the OPEN message is sent (on exit from
+ * sConnect or sActive states) are not delivered until has entered
+ * sOpenSent state.
+ *
+ * Cannot happen at any other time.
+ *
+ * 5. TCP_connection_open_failed ("soft" error)
+ * -- process the posted exception (MUST be there)
+ *
+ * a. in sConnect or sActive states
+ *
+ * Close the connection. For sActive state, disable accept.
+ *
+ * Stay in sConnect/sActive (until ConnectRetryTimer expires).
+ *
+ * Cannot happen at any other time.
+ *
+ * 6. TCP_fatal_error ("hard" error)
+ * -- process the posted exception (MUST be there)
+ *
+ * a. in sConnect or sActive states
+ *
+ * Close the connection. For sActive state, disable accept.
+ *
+ * Stay in sConnect/sActive (until ConnectRetryTimer expires).
+ *
+ * b. in sOpenSent/sOpenConfirm states
+ *
+ * Close the connection.
+ *
+ * Fall back to sIdle.
+ *
+ * c. in sEstablished state
+ *
+ * Close the the session -- go to sStopping state.
+ *
+ * d. in sStopping state.
+ *
+ * Terminate the connection.
+ *
+ * Cannot happen at any other time (ie sIdle).
+ *
+ * 7. ConnectRetry_timer_expired
+ *
+ * a. in either sConnect or sActive 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 sOpenSent state
+ *
+ * Time to give up waiting for an OPEN (or NOTIFICATION) from the
+ * other end. For this wait the RFC recommends a "large" value for
+ * the hold time -- and suggests 4 minutes. Send NOTIFICATION.
+ *
+ * Or, if the connection has sent a notification message that
+ * process is now complete.
+ *
+ * Close the connection. Fall back to sIdle.
+ *
+ * b. in sOpenConfirm state
+ *
+ * Time to give up waiting for a KEEPALIVE to confirm the connection.
+ * For this wait the hold time used is that negotiated in the OPEN
+ * messages that have been exchanged. Send NOTIFICATION.
+ *
+ * Or, if the connection has sent a notification message that
+ * process is now complete.
+ *
+ * Close the connection. Fall back to sIdle.
+ *
+ * c. in sEstablished state
+ *
+ * In this state the hold time used is that negotiated in the OPEN
+ * messages that have been exchanged.
+ *
+ * The session has failed. Send NOTIFICATION.
+ *
+ * Close the session -- go to sStopping state.
+ *
+ * d. in sStopping 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 sOpenConfirm and sEstablished 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 sOpenSent state -- the expected response
+ *
+ * Proceed (via collision resolution) to sOpenConfirm or sIdle.
+ *
+ * b. in sOpenConfirm state -- FSM error
+ *
+ * Send NOTIFICATION. Fall back to sIdle.
+ *
+ * c. in Established state -- FSM error
+ *
+ * Send NOTIFICATION. Close session.
+ *
+ * Cannot happen at any other time (connection not up or read closed).
+ *
+ * 11. Receive_KEEPALIVE_message
+ *
+ * Generated by read action.
+ *
+ * a. in sOpenSent state -- FSM error
+ *
+ * Send NOTIFICATION. Fall back to Idle.
+ *
+ * b. in sOpenConfirm state -- the expected response
+ *
+ * c. in sEstablished state -- expected
+ *
+ * Cannot happen at any other time (connection not up or read closed).
+ *
+ * 12. Receive_UPDATE_message
+ *
+ * Generated by read action.
+ *
+ * a. in sOpenSent and sOpenConfirm states -- FSM error
+ *
+ * Send NOTIFICATION. Fall back to sIdle.
+ *
+ * b. in sEstablished state -- expected
+ *
+ * Cannot happen at any other time (connection not up or read closed).
+ *
+ * 13. Receive_NOTIFICATION_message
+ * -- process the posted exception (MUST be there)
+ *
+ * Generated by read action.
+ *
+ * a. in sOpenSent and sOpenConfirm close the connection and fall back
+ * to sIdle.
+ *
+ * b. in sEstablished state -- close the session.
+ *
+ * Cannot happen at any other time (connection not up or read closed).
+ *
+ * 14. Sent_NOTIFICATION_message
+ *
+ * Generated by write action when completed sending the message.
+ *
+ * a. in sOpenSent, sOpenConfirm, and sStopping states
+ *
+ * Set the "courtesy" hold time.
+ *
+ * Cannot happen at any other time.
+ */
+
+/*------------------------------------------------------------------------------
+ * Finite State Machine Table
+ */
+
+static const struct bgp_fsm
+bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
+{
+ {
+ /* bgp_fsm_sInitial: initialised in this state..............................
+ *
+ * Expect only a eBGP_Start event, which arms the IdleHoldTimer and
+ * advances to the sIdle state.
+ *
+ * Could (just) get a bgp_fsm_eStop 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_sInitial}, /* null event */
+ {bgp_fsm_enter, bgp_fsm_sIdle}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sInitial}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sIdle: 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 sOpenState or sOpenConfirm, may loop back
+ * through sIdle, with an increased IdleHoldTimer.
+ *
+ * In sIdle state the connection is dormant. (While the secondary is sIdle,
+ * 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 sIdle it will have been closed.
+ *
+ * The expected events are:
+ *
+ * * eBGP_Start -- generated by IdleHoldTimer expired
+ *
+ * * eBGP_Stop -- for whatever reason
+ *
+ * * eTCP_connection_open -- generated if connection is accepted
+ *
+ * This set the connection "half_open", but stays in sIdle until
+ * the IdleHoldTimer expires. This avoids rejecting inbound
+ * connections, but also enforces the IdleHoldTimer if the other end
+ * is being vexatious.
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sIdle}, /* null event */
+ {bgp_fsm_start, bgp_fsm_sConnect}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sIdle}, /* BGP_Stop */
+ {bgp_fsm_half_open, bgp_fsm_sIdle}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sConnect: 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:
+ *
+ * * eBGP_Start -- follow-on event from ConnectRetryTimer expired
+ *
+ * * TCP_connection_open -- good news !
+ *
+ * * TCP_connection_open_fail ("soft" error)
+ *
+ * * TCP_fatal_error ("hard" error)
+ *
+ * * ConnectRetryTimer expired
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sConnect}, /* null event */
+ {bgp_fsm_connect, bgp_fsm_sConnect}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sConnect}, /* BGP_Stop */
+ {bgp_fsm_send_open, bgp_fsm_sOpenSent}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_closed */
+ {bgp_fsm_failed, bgp_fsm_sConnect}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_sConnect}, /* TCP_fatal_error */
+ {bgp_fsm_retry, bgp_fsm_sConnect}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sActive: 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:
+ *
+ * * eBGP_Start -- follow-on event from ConnectRetryTimer expired
+ *
+ * * TCP_connection_open -- good news
+ *
+ * * TCP_connection_open_fail ("soft" error)
+ *
+ * * TCP_fatal_error
+ *
+ * * ConnectRetry_timer_expired
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sActive}, /* null event */
+ {bgp_fsm_accept, bgp_fsm_sActive}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sActive}, /* BGP_Stop */
+ {bgp_fsm_send_open, bgp_fsm_sOpenSent}, /* TCP_connection_open */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_closed */
+ {bgp_fsm_failed, bgp_fsm_sActive}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_sActive}, /* TCP_fatal_error */
+ {bgp_fsm_retry, bgp_fsm_sActive}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sOpenSent: 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 -- good news
+ *
+ * * Receive_UPDATE_message -- FSM error !
+ *
+ * * Receive_KEEPALIVE_message -- FSM error !
+ *
+ * * Receive_NOTIFICATION_message -- bad news
+ *
+ * * TCP_connection_closed
+ *
+ * * TCP_fatal_error
+ *
+ * * Hold_Timer_expired
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sOpenSent}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sIdle}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open */
+ {bgp_fsm_closed, bgp_fsm_sIdle}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_sIdle}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_expire, bgp_fsm_sIdle}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_recv_open, bgp_fsm_sOpenConfirm},/* Receive_OPEN_message */
+ {bgp_fsm_error, bgp_fsm_sOpenSent}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_error, bgp_fsm_sOpenSent}, /* Receive_UPDATE_message */
+ {bgp_fsm_recv_nom, bgp_fsm_sIdle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_sent_nom, bgp_fsm_sOpenSent}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sOpenConfirm: 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 -- effectively 'ACK' for OPEN.
+ *
+ * * Receive_OPEN_message -- FSM error
+ *
+ * * Receive_UPDATE_message -- FSM error
+ *
+ * * Receive_NOTIFICATION_message -- bad news
+ *
+ * * TCP_connection_closed
+ *
+ * * TCP_fatal_error
+ *
+ * * KeepAlive_Timer_expired -- keep trying
+ *
+ * * Hold_Timer_expired
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sOpenConfirm},/* null event */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sIdle}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open */
+ {bgp_fsm_closed, bgp_fsm_sIdle}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_sIdle}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_expire, bgp_fsm_sIdle}, /* Hold_Timer_expired */
+ {bgp_fsm_send_kal, bgp_fsm_sOpenConfirm},/* KeepAlive_timer_expired */
+ {bgp_fsm_error, bgp_fsm_sOpenConfirm},/* Receive_OPEN_message */
+ {bgp_fsm_establish, bgp_fsm_sEstablished},/* Receive_KEEPALIVE_message */
+ {bgp_fsm_error, bgp_fsm_sOpenConfirm},/* Receive_UPDATE_message */
+ {bgp_fsm_recv_nom, bgp_fsm_sIdle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_sent_nom, bgp_fsm_sOpenConfirm},/* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sEstablished: 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
+ *
+ * * Receive_UPDATE_message -- the real business
+ *
+ * In fact this is dealt with elsewhere, to avoid going through the
+ * full FSM process -- see above.
+ *
+ * * Receive_KEEPALIVE_message
+ *
+ * * Receive_NOTIFICATION_message
+ *
+ * * TCP_connection_closed
+ *
+ * * TCP_fatal_error
+ *
+ * * KeepAlive_Timer_expired
+ *
+ * * Hold_Timer_expired
+ *
+ * * BGP_Stop -- for whatever reason
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sEstablished},/* null event */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* BGP_Start */
+ {bgp_fsm_stop, bgp_fsm_sStopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open */
+ {bgp_fsm_closed, bgp_fsm_sStopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_sStopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_expire, bgp_fsm_sStopping}, /* Hold_Timer_expired */
+ {bgp_fsm_send_kal, bgp_fsm_sEstablished},/* KeepAlive_timer_expired */
+ {bgp_fsm_error, bgp_fsm_sStopping}, /* Receive_OPEN_message */
+ {bgp_fsm_recv_kal, bgp_fsm_sEstablished},/* Receive_KEEPALIVE_message */
+ {bgp_fsm_update, bgp_fsm_sEstablished},/* Receive_UPDATE_message */
+ {bgp_fsm_recv_nom, bgp_fsm_sStopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Sent NOTIFICATION message */
+ },
+ {
+ /* bgp_fsm_sStopping: waiting (briefly) to send Notification................
+ *
+ * Before a connection is sent to sStopping state it may have sent a
+ * NOTIFICATION message. If so, it will stay in sStopping state until
+ * that process completes -- which will be signalled by the HoldTimer
+ * expiring, or some sort of I/O failure.
+ *
+ * Other exceptions and any invalid fsm events will raise an fsm_eBGP_Stop.
+ *
+ * On exit from the FSM a connection that has just entered sStopping state
+ * will be unlinked from the session.
+ *
+ * The only way out of sStopping is bgp_fsm_exit(), which terminates the
+ * connection which will later be reaped.
+ *
+ * The expected events are:
+ *
+ * * BGP_Stop -- exit
+ *
+ * * Sent NOTIFICATION message -- set "courtesy" timer
+ *
+ * * Hold_Timer_expired -- exit
+ *
+ * * TCP_fatal_error -- exit
+ *
+ * * TCP_connection_closed -- exit
+ *
+ * All other events (other than null) are invalid (should not happen).
+ */
+ {bgp_fsm_null, bgp_fsm_sStopping}, /* null event */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* BGP_Start */
+ {bgp_fsm_exit, bgp_fsm_sStopping}, /* BGP_Stop */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open */
+ {bgp_fsm_exit, bgp_fsm_sStopping}, /* TCP_connection_closed */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* TCP_connection_open_failed */
+ {bgp_fsm_exit, bgp_fsm_sStopping}, /* TCP_fatal_error */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_exit, bgp_fsm_sStopping}, /* Hold_Timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* KeepAlive_timer_expired */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_OPEN_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_UPDATE_message */
+ {bgp_fsm_invalid, bgp_fsm_sStopping}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_sent_nom, bgp_fsm_sStopping}, /* Sent NOTIFICATION message */
+ },
+} ;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (keepalive timer expire)",
- peer->host);
+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",
+} ;
- THREAD_VAL (thread) = KeepAlive_timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
+/*==============================================================================
+ * Signal FSM event.
+ */
- return 0;
-}
+static void
+bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
-static int
-bgp_routeadv_timer (struct thread *thread)
+/*------------------------------------------------------------------------------
+ * Signal event to FSM for the given connection.
+ */
+static void
+bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
{
- struct peer *peer;
-
- peer = THREAD_ARG (thread);
- peer->t_routeadv = NULL;
+ bgp_fsm_state_t next_state ;
+ const struct bgp_fsm* fsm ;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (routeadv timer expire)",
- peer->host);
+ dassert( (event >= bgp_fsm_null_event)
+ && (event <= bgp_fsm_last_event)) ;
+ dassert( (connection->state >= bgp_fsm_first_state)
+ && (connection->state <= bgp_fsm_last_state) ) ;
- peer->synctime = bgp_clock ();
+ /* Watch out for recursing through the FSM for this connection. */
+ ++connection->fsm_active ;
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
-
- BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer,
- peer->v_routeadv);
+ if (connection->fsm_active == 2)
+ {
+ connection->follow_on = event ;
+ return ;
+ } ;
+
+ /* Lock the session for the convenience of the event handlers.
+ *
+ * NB: if the current state is sStopping, then connection is no longer
+ * attached to session -- so connection->session is NULL -- BEWARE !
+ *
+ * The session lock does nothing if no session is attached.
+ */
- return 0;
-}
+ BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-/* BGP Peer Down Cause */
-const char *peer_down_str[] =
+ do
+ {
+ assert(connection->fsm_active == 1) ;
+
+ fsm = &bgp_fsm[connection->state][event] ;
+ next_state = fsm->next_state ;
+
+ /* Call function. */
+ next_state = fsm->action(connection, next_state, event) ;
+
+ dassert( (next_state >= bgp_fsm_first_state)
+ && (next_state <= bgp_fsm_last_state) ) ;
+
+ /* If state is changed. */
+ if (next_state != connection->state)
+ {
+ bgp_fsm_state_t prev_state = connection->state ;
+
+ /* Complete the state change */
+ bgp_fsm_state_change(connection, next_state) ;
+
+ /* 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)) ;
+
+ 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));
+ } ;
+
+ /* Pick up follow_on event -- if any */
+ event = connection->follow_on ;
+ connection->follow_on = bgp_fsm_null_event ;
+
+ } while (--connection->fsm_active != 0) ;
+
+ /* If required, post session event. */
+ if (connection->exception != bgp_session_null_event)
+ {
+ int stopped = (connection->state == bgp_fsm_sStopping) ;
+ int has_session = (connection->session != NULL) ;
+
+ /* Some exceptions are not reported to the Routeing Engine
+ *
+ * In particular: eDiscard and eCollision -- so the only time the
+ * connection->state will be Stopping is when the session is being
+ * stopped. (eDiscard and eCollision go quietly to Stopping !)
+ */
+ if ((connection->exception <= bgp_session_max_event) && has_session)
+ bgp_session_event(connection->session, connection->exception,
+ bgp_notify_take(&connection->notification),
+ connection->err,
+ connection->ordinal,
+ stopped) ;
+
+ /* Tidy up -- notification already cleared */
+ connection->exception = bgp_session_null_event ;
+ connection->err = 0 ;
+ bgp_notify_unset(&connection->notification) ; /* if any */
+
+ if (stopped && has_session)
+ BGP_CONNECTION_SESSION_CUT_LOOSE(connection) ;
+ } ;
+
+ BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+} ;
+
+/*==============================================================================
+ * The BGP FSM Action Functions
+ */
+
+static void
+bgp_hold_timer_set(bgp_connection connection, unsigned secs) ;
+
+/*------------------------------------------------------------------------------
+ * Null action -- do nothing at all.
+ */
+static bgp_fsm_action(bgp_fsm_null)
{
- "",
- "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)
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Entry point to FSM.
+ *
+ * This is the first thing to happen to the FSM, and takes it from sInitial
+ * state to sIdle, with IdleHoldTimer running.
+ *
+ * NB: the IdleHoldTimer is always a finite time. So the start up event for
+ * the primary connection *cannot* fail.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_action(bgp_fsm_enter)
{
- 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))
+ if (connection->ordinal == bgp_connection_secondary)
{
- 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)
+ bgp_prepare_to_accept(connection) ;
+ bgp_connection_enable_accept(connection) ;
+ } ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Stop BGP Connection -- general exception event.
+ *
+ * An exception should have been posted, treat as invalid if not.
+ *
+ * For some exceptions the next state is forced to be sStopping.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_stop)
{
- 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;
-}
-
-/* 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)
+ if (connection->exception == bgp_session_null_event)
+ return bgp_fsm_invalid(connection, bgp_fsm_sStopping, event) ;
+
+ if ( (connection->exception == bgp_session_eDisabled)
+ || (connection->exception == bgp_session_eDiscard)
+ || (connection->exception == bgp_session_eInvalid) )
+ next_state = bgp_fsm_sStopping ;
+
+ return bgp_fsm_catch(connection, next_state) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Invalid event -- cannot occur in current state.
+ *
+ * Throws a general bgp_session_eInvalid exception -- staying in the current
+ * state. The follow-on eBGP_Stop event bounces the entire session into
+ * sStopped.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_invalid)
{
- 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)
+ if (BGP_DEBUG(fsm, FSM)) \
+ plog_debug(connection->log, "%s [FSM] invalid event %d in state %d",
+ connection->host, event, connection->state) ;
+
+ return bgp_fsm_throw_stop(connection, bgp_session_eInvalid,
+ bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Half open a BGP Connection
+ *
+ * Used during sIdle when a connection is made before the IdleHoldTimer
+ * expires.
+ *
+ * Expected only for the secondary connection.
+ *
+ * Sets the connection half_open, and remains in sIdle.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_half_open)
{
- int rc = bgp_stop(peer);
- BGP_EVENT_FLUSH (peer);
-
- return rc;
-}
-
-/* Administrative BGP peer stop event. */
-/* May be called multiple times for the same peer */
-int
-bgp_stop (struct peer *peer)
+ assert( (connection->ordinal == bgp_connection_secondary)
+ && !connection->half_open ) ;
+
+ connection->half_open = 1 ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start up BGP Connection
+ *
+ * Used on exit from sIdle to sConnect or sActive -- when the IdleHoldTimer
+ * expires.
+ *
+ * If not half open:
+ *
+ * Enters either sConnect or sActive, depending on primary/secondary.
+ *
+ * Throws a session_eStart exception so the Routing Engine gets to see this,
+ * and a follow-on fsm_eBGP_Start event to kick the connect() or accept()
+ * into life.
+ *
+ * If is half open:
+ *
+ * Must be secondary. Enters sActive.
+ *
+ * Throws a session_eStart exception so the Routing Engine gets to see this,
+ * and a follow-on bgp_fsm_eTCP_connection_open event to kick sActive into
+ * processing the already open connection.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_start)
{
- afi_t afi;
- safi_t safi;
- char orf_name[BUFSIZ];
+ bgp_fsm_event_t fsm_event ;
- /* Can't do this in Clearing; events are used for state transitions */
- if (peer->status != Clearing)
+ if (!connection->half_open)
+ /* Straightforward -- set eBGP_Start follow-on event */
+ fsm_event = bgp_fsm_eBGP_Start ;
+ else
{
- /* Delete all existing events of the peer */
- BGP_EVENT_FLUSH (peer);
- }
-
- /* Increment Dropped count. */
- if (peer->status == Established)
+ /* Is half open -- so set a eTCP_connection_open follow-on event, then
+ * change to sActive where the event will be collected.
+ */
+ assert(connection->ordinal == bgp_connection_secondary) ;
+
+ connection->half_open = 0 ;
+
+ fsm_event = bgp_fsm_eTCP_connection_open ;
+ } ;
+
+ bgp_fsm_throw(connection, bgp_session_eStart, NULL, 0, fsm_event) ;
+
+ return (connection->ordinal == bgp_connection_primary) ? bgp_fsm_sConnect
+ : bgp_fsm_sActive ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start up BGP connect()
+ *
+ * * to change from sIdle to sConnect -- when the IdleHoldTimer
+ *
+ * * to loop back to sConnect -- when the ConnectRetryTimer expires.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_connect)
+{
+ assert((connection->ordinal == bgp_connection_primary)
+ && (next_state = bgp_fsm_sConnect)) ;
+ bgp_open_connect(connection) ;
+
+ return next_state ;
+} ;
+/*------------------------------------------------------------------------------
+ * Start up BGP accept()
+ *
+ * * to change from sIdle to sActive -- when the IdleHoldTimer expires.
+ *
+ * * to loop back to sActive -- when the ConnectRetryTimer expires.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_accept)
+{
+ assert((connection->ordinal == bgp_connection_secondary)
+ && (next_state = bgp_fsm_sActive)) ;
+ bgp_connection_enable_accept(connection) ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * TCP connection open has come up -- connect() or accept()
+ *
+ * Send BGP Open Message to peer.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_send_open)
+{
+ 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);
- }
- 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 = peer->uptime = bgp_clock ();
-
-#ifdef HAVE_SNMP
- bgpTrapBackwardTransition (peer);
-#endif /* HAVE_SNMP */
+ const char* how ;
- /* 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_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)
+ if (connection->ordinal == bgp_connection_primary)
+ how = "connect" ;
+ else
+ how = "accept" ;
+
+ zlog_debug("%s open %s(), local address %s",
+ sutoa(connection->su_remote).str,
+ how,
+ sutoa(connection->su_local).str) ;
+ } ;
+
+ bgp_connection_read_enable(connection) ;
+
+ bgp_msg_send_open(connection, connection->session->open_send) ;
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * TCP connection has failed to come up -- sConnect/sActive states.
+ *
+ * This is in response to TCP_connection_open_failed, which has posted the
+ * exception -- so now need to deal with it.
+ *
+ * Close the connection -- if secondary connection, disable accept.
+ *
+ * Will stay in sConnect/sActive states.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_failed)
+{
+ return bgp_fsm_catch(connection, next_state) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Fatal I/O error -- any state (other than sIdle).
+ *
+ * Close the connection (if any) -- if secondary connection, disable accept.
+ *
+ * This is in response to TCP_fatal_error, which has posted the
+ * exception -- so now need to deal with it.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_fatal)
+{
+ return bgp_fsm_catch(connection, next_state) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * ConnectRetryTimer expired -- sConnect/sActive states.
+ *
+ * If the connection failed, the connection will have been closed. For the
+ * secondary connection accept() will have been disabled.
+ *
+ * In any case, close the connection (but leave timers running) and then
+ * throw a session_eRetry event and an fsm_eBGP_Start follow-on.
+ *
+ * NB: the connection remains in the current state, and the retry timer will
+ * still be running, because it automatically recharges.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_retry)
+{
+ bgp_connection_close(connection, true) ; /* true => keep timers, the
+ * FSM handles them. */
+
+ bgp_fsm_throw(connection, bgp_session_eRetry, NULL, 0,
+ bgp_fsm_eBGP_Start) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * TCP connection has closed -- sOpenSent/sOpenConfirm/sEstablished states
+ *
+ * This is in response to TCP_connection_closed, which has posted the
+ * exception -- so now need to deal with it.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_closed)
+{
+ return bgp_fsm_catch(connection, next_state) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Hold timer expire -- sOpenSent/sOpenConfirm/sEstablished/sStopping
+ *
+ * This means either: have finished sending NOTIFICATION (end of "courtesy"
+ * wait time)
+ *
+ * or: can wait no longer for something from the other end.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_expire)
+{
+ /* The process of sending a NOTIFICATION comes to an end here. */
+ if (connection->notification_pending)
{
- close (peer->fd);
- peer->fd = -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;
-
- /* peer address family status flags*/
- peer->af_sflags[afi][safi] = 0;
-
- /* Received ORF prefix-filter */
- peer->orf_plist[afi][safi] = NULL;
+ bgp_connection_close(connection, true) ; /* true => keep timers, the
+ * FSM handles them. */
+ return next_state ;
+ } ;
+
+ /* Otherwise: treat as a general exception. */
+ return bgp_fsm_throw_stop(connection, bgp_session_eExpired,
+ bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Received an acceptable OPEN Message
+ *
+ * The next state is expected to be sOpenConfirm.
+ *
+ * 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 sEstablished, it immediately kills off any
+ * sibling -- so the farthest two connections can get is to sOpenSent.
+ *
+ * The connection that is closed should send a Cease/Collision Resolution
+ * NOTIFICATION. The other end should do likewise.
+ *
+ * The connection that is closed will fall back to sIdle -- so that if the
+ * connection that wins the race to sOpenConfirm fails there, then both will be
+ * back in sIdle state.
+ *
+ * If makes it past Collision Resolution, respond with a KEEPALIVE (to "ack"
+ * the OPEN message).
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_recv_open)
+{
+ bgp_session session = connection->session ;
+ bgp_connection sibling = bgp_connection_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
+ /* If there is a sibling, and it is in sOpenConfirm state, then now must do
+ * collision resolution.
+ */
+ if ((sibling != NULL) && (sibling->state == bgp_fsm_sOpenConfirm))
{
- 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;
+ bgp_connection loser ;
+
+ /* Before choosing a winner, check that both have the same BGP Id.
+ *
+ * The two connections are to an address which is configured for the
+ * given peer, and both have given the right ASN. It would be
+ * astonishing (but also disturbing) to find that they had different
+ * BGP Ids !!!
+ */
+ if (connection->open_recv->bgp_id != sibling->open_recv->bgp_id)
+ {
+ bgp_fsm_exception( sibling, bgp_session_eOpen_reject,
+ bgp_msg_noms_o_bad_id(NULL, sibling->open_recv->bgp_id)) ;
+
+ return bgp_fsm_throw_stop(connection, bgp_session_eOpen_reject,
+ bgp_msg_noms_o_bad_id(NULL, connection->open_recv->bgp_id)) ;
+ } ;
+
+ /* NB: bgp_id in open_state is in *network* order */
+ loser = (ntohl(session->open_send->bgp_id) <
+ ntohl(sibling->open_recv->bgp_id))
+ ? connection
+ : sibling ;
+
+ /* Throw exception */
+ bgp_fsm_exception(loser, bgp_session_eCollision,
+ bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_C_COLLISION)) ;
+
+ /* If self is the loser, exit now to process the eBGP_Stop */
+ if (loser == connection)
+ return connection->state ; /* sBGP_Stop deals with state */
+ } ;
+
+ /* All is well: send a KEEPALIVE message to acknowledge the OPEN */
+ bgp_msg_send_keepalive(connection, 1) ;
+
+ /* Transition to OpenConfirm state */
+ return next_state ;
}
-/* BGP peer is stoped by the error. */
-static int
-bgp_stop_with_error (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * FSM error -- received wrong type of message !
+ *
+ * For example, an OPEN message while in sEstablished state.
+ *
+ * For use in: sOpenSent, sOpenConfirm and sEstablished states.
+ *
+ * Sends NOTIFICATION.
+ *
+ * Next state will be sIdle, except if is sEstablished, when will be
+ * sStopping.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_error)
{
- /* 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;
-}
-
-/* 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)
+ return bgp_fsm_throw_stop(connection, bgp_session_eFSM_error,
+ bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Receive NOTIFICATION from far end -- sOpenSent/sOpenConfirm/sEstablished
+ *
+ * This is in response to Receive_NOTIFICATION_message, which has posted the
+ * exception -- so now need to deal with it.
+ *
+ * Next state will be sIdle, except if is sEstablished, when will be sStopping.
+ *
+ * Per RFC 5492: if get a NOTIFICATION: "Open/Unsupported Optional Parameter"
+ * then suppress sending of capabilities.
+ *
+ * If didn't send capabilities the last time, doesn't make any
+ * difference if suppress them from now on !
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_recv_nom)
{
- char buf1[BUFSIZ];
-
- if (peer->fd < 0)
+ if (connection->state != bgp_fsm_sEstablished)
{
- 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 ( (connection->notification->code == BGP_NOMC_OPEN)
+ && (connection->notification->subcode == BGP_NOMS_O_OPTION) )
+ {
+ if (!connection->cap_suppress)
+ BGP_FSM_DEBUG(connection, "Suppressing Capabilities") ;
+
+ connection->cap_suppress = true ;
+ } ;
+ } ;
+
+ return bgp_fsm_catch(connection, next_state) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pending NOTIFICATION has cleared write buffers
+ * -- sOpenSent/sOpenConfirm/sStopping
+ *
+ * Set the "courtesy" HoldTimer. Expect to stay in current state.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_sent_nom)
+{
+ bgp_hold_timer_set(connection, 5) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send Keepalive to peer.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_send_kal)
+{
+ bgp_msg_send_keepalive(connection, 0) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Session Established !
+ *
+ * If there is another connection, that is now snuffed out and this connection
+ * becomes the primary.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_establish)
+{
+ bgp_session session = connection->session ;
+ bgp_connection sibling = bgp_connection_get_sibling(connection) ;
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_getsockname (peer);
+ assert(session != NULL) ;
- 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);
- }
+ /* The first thing to do is to snuff out any sibling */
+ if (sibling != NULL)
+ bgp_fsm_discard_sibling(sibling,
+ bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_C_COLLISION)) ;
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_open_send (peer);
+ /* Establish self as primary and copy state up to session */
+ bgp_connection_make_primary(connection) ;
- return 0;
-}
+ /* Signal exciting session event */
+ bgp_session_event(session, bgp_session_eEstablished, NULL, 0, 0, 0) ;
-/* TCP connect fail */
-static int
-bgp_connect_fail (struct peer *peer)
+ /* TODO: now would be a good time to withdraw the password from listener ? */
+
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Keepalive packet is received -- sOpenConfirm/sEstablished
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_recv_kal)
{
- bgp_stop (peer);
- return 0;
+ bgp_hold_timer_recharge(connection) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Update packet is received.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_update)
+{
+ bgp_hold_timer_recharge(connection) ;
+ return next_state ;
}
-/* 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)
+/*------------------------------------------------------------------------------
+ * Connection exit
+ *
+ * Ring down the curtain. Connection structure will be freed by the BGP Engine.
+ *
+ * NB: requires the session LOCKED
+ */
+static bgp_fsm_action(bgp_fsm_exit)
+{
+ assert(connection->state == bgp_fsm_sStopping) ;
+
+ bgp_connection_exit(connection) ;
+
+ return bgp_fsm_sStopping ;
+} ;
+
+/*==============================================================================
+ * Catching FSM Exceptions.
+ *
+ * Uses the posted information and the expected next_state to deal with some
+ * exception. Proceeds:
+ *
+ * 1a) if have notification & not eNOM_recv & is in a suitable state
+ *
+ * Suitable states are sOpenSent/sOpenConfirm/sEstablished.
+ *
+ * Send NOTIFICATION -- see notes above on the process.
+ *
+ * For sOpenSent/sOpenConfirm, override the next_state to stay where it is
+ * until NOTIFICATION process completes.
+ *
+ * For sEstablished, the next_state will be sStopping.
+ *
+ * 1b) otherwise: close the connection file.
+ *
+ * If the next_state is sStopping, there is nothing else to do, so
+ * raise an eBGP_Stop event, so that the connection exits.
+ *
+ * 2) if next state is sStopping, and exception is not session_eDiscard
+ *
+ * This means we bring down the session, so discard any sibling.
+ *
+ * The sibling will send any notification, and proceed immediately to
+ * sStopping.
+ *
+ * (The sibling will be session_eDiscard -- so no deadly embrace here.)
+ *
+ * So: proceeds to the given next_state unless has started notification process,
+ * and next_state was not sStopping.
+ *
+ * Issues follow-on events:
+ *
+ * * bgp_fsm_eSent_NOTIFICATION_message if notification clears to TCP buffers
+ * immediately.
+ *
+ * * bgp_fsm_eTCP_fatal_error (or other such) if fails trying to send
+ * notification.
+ *
+ * * bgp_fsm_eBGP_Stop if next_state is sStopping and no notification to
+ * send.
+ *
+ * The state machine takes care of the rest:
+ *
+ * * complete entry to new state
+ *
+ * * send message to Routeing Engine
+ *
+ * * cutting the connection loose if ends up sStopping.
+ *
+ * NB: requires the session LOCKED -- connection-wise
+ */
+static bgp_fsm_state_t
+bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state)
{
- int status;
+ bgp_notify send_notification ;
- 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;
- }
+ assert(connection->exception != bgp_session_null_event) ;
- /* Scrub some information that might be left over from a previous,
- * session
+ /* Have a notification to send iff not just received one, and is in a
+ * suitable state to send one at all.
*/
- /* Connection information. */
- if (peer->su_local)
+ if (connection->exception == bgp_session_eNOM_recv)
+ send_notification = NULL ;
+ else
{
- sockunion_free (peer->su_local);
- peer->su_local = NULL;
- }
+ if ( (connection->state != bgp_fsm_sOpenSent)
+ && (connection->state != bgp_fsm_sOpenConfirm)
+ && (connection->state != bgp_fsm_sEstablished) )
+ bgp_notify_unset(&connection->notification) ;
+
+ send_notification = connection->notification ;
+ } ;
+
+ /* If there is a NOTIFICATION to send, send it if possible.
+ * Otherwise, close the connection but leave the timers.
+ *
+ * The state transition stuff looks after timers. In particular an error
+ * in Connect/Active states leaves the ConnectRetryTimer running.
+ *
+ * However, in any event, no longer require any Keepalive.
+ */
+ qtimer_unset(connection->keepalive_timer) ;
- if (peer->su_remote)
+ if ((send_notification != NULL) && bgp_connection_part_close(connection))
{
- sockunion_free (peer->su_remote);
- peer->su_remote = NULL;
+ /* If not changing to stopping, we hold in the current state until
+ * the NOTIFICATION process is complete.
+ */
+ if (next_state != bgp_fsm_sStopping)
+ next_state = connection->state ;
+
+ /* Write the message */
+ bgp_msg_write_notification(connection, send_notification) ;
+
+ /* notification is sitting in the write buffer
+ *
+ * notification_pending is set, so write action will raise the required
+ * event in due course.
+ *
+ * Set the HoldTimer to something suitable. Don't really expect this
+ * to happen in anything except sEstablished state -- but copes. (Is
+ * ready to wait 20 seconds in sStopping state and 5 otherwise.)
+ */
+ bgp_hold_timer_set(connection,
+ (next_state == bgp_fsm_sStopping) ? 20 : 5) ;
}
+ else
+ {
+ bgp_connection_close(connection, true) ; /* true => keep timers, the
+ * FSM handles them. */
+ if (next_state == bgp_fsm_sStopping) /* can exit if sStopping */
+ bgp_fsm_event(connection, bgp_fsm_eBGP_Stop) ;
+ } ;
+
+ /* If sStopping and not eDiscard, do in any sibling */
+ if ( (next_state == bgp_fsm_sStopping)
+ && (connection->exception != bgp_session_eDiscard) )
+ {
+ bgp_connection sibling ;
+
+ sibling = bgp_connection_get_sibling(connection) ; /* ... if any */
+
+ if (sibling != NULL)
+ bgp_fsm_discard_sibling(sibling, bgp_notify_dup(send_notification)) ;
+ } ;
+
+ /* Return the (possibly adjusted) next_state */
+ return next_state ;
+} ;
+
+/*==============================================================================
+ * 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_eBGP_Start event.
+ *
+ * * ConnectRetryTimer -- uses connection.hold_timer with jitter
+ *
+ * This runs while in sConnect or sActive 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 primary connection (if any) will be in sConnect state.
+ *
+ * The secondary connection (if any) will be in sActive state.
+ *
+ * If nothing happens before the ConnectRetryTimer expires, then
+ * the connection attempt will be abandoned, and another started.
+ *
+ * If the connection attempt fails, closes any connect() socket, but then
+ * waits for the ConnectRetryTimer to expire.
+ *
+ * The ConnectRetryTimer recharges itself (with new jitter each time). It
+ * generates a bgp_fsm_eConnectRetry_timer_expired event.
+ *
+ * * HoldTimer -- uses connection.hold_timer *without* jitter
+ *
+ * This timer is used in sOpenSent 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 sOpenConfirm and sEstablished 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_eHold_Timer_expired
+ * event.
+ *
+ * * KeepaliveTimer -- uses connection.keepalive_timer with jitter.
+ *
+ * This timer is used in sOpenConfirm and sEstablished 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
+ * zero and treated as 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_eKeepAlive_timer_expired event.
+ */
+
+/* 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 ;
+
+/*==============================================================================
+ * Timer set functions -- general and HoldTimer specific.
+ */
+enum
+{
+ no_jitter = 0,
+ with_jitter = 1,
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start or reset given qtimer with given interval, in seconds.
+ *
+ * If the interval is zero, unset the timer.
+ */
+static void
+bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
+ int jitter, qtimer_action* action)
+{
+ if (secs == 0)
+ qtimer_unset(timer) ;
+ else
+ {
+ secs *= 40 ; /* a bit of resolution for jitter */
+ if (jitter != no_jitter)
+ secs -= ((rand() % ((int)secs + 1)) / 4) ;
+ qtimer_set_interval(timer, QTIME(secs) / 40, action) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set HoldTimer with given time (without jitter) so will generate a
+ * Hold_Timer_expired event.
+ *
+ * Setting 0 will unset the HoldTimer.
+ */
+static void
+bgp_hold_timer_set(bgp_connection connection, unsigned secs)
+{
+ bgp_timer_set(connection, connection->hold_timer, secs, no_jitter,
+ bgp_hold_timer_action) ;
+} ;
+
+/*==============================================================================
+ * Completion of State Change
+ *
+ * This performs fixed changes associated with the entry to each state from
+ * *another* state.
+ *
+ * connection->state == current (soon to be old) state
+ *
+ * Set and unset all the connection timers as required by the new state of
+ * the connection -- which may depend on the current state.
+ *
+ * NB: requires the session LOCKED
+ */
+static void
+bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state)
+{
+ bgp_connection sibling ;
+ unsigned interval ;
+ bgp_session session = connection->session ;
- /* 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))
+ switch (new_state)
{
- BGP_EVENT_ADD (peer, TCP_connection_open_failed);
- return 0;
- }
+ /* 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 sIdle state:
+ *
+ * either: the IdleHoldTimer is running, at the end of which the
+ * BGP Engine will try to connect.
+ *
+ * or: the connection is comatose, in which case will stay that way
+ * until sibling connection also falls back to Idle (from
+ * OpenSent/OpenConfirm.
+ *
+ * When entering sIdle from anything other than Initial state, and not
+ * falling into a coma, extend the IdleHoldTimer.
+ *
+ * In sIdle state doesn't refuse connections (unless comatose), but won't
+ * act on them until the IdleHoldTimer expires.
+ */
+ case bgp_fsm_sIdle:
+ interval = session->idle_hold_timer_interval ;
+ sibling = bgp_connection_get_sibling(connection) ;
+
+ if (connection->state == bgp_fsm_sInitial)
+ interval = (interval > 0) ? interval : 1 ; /* may not be zero */
+ else
+ {
+ if ( (sibling != NULL)
+ && ( (sibling->state == bgp_fsm_sOpenSent)
+ || (sibling->state == bgp_fsm_sOpenConfirm) ) )
+ {
+ interval = 0 ; /* unset the HoldTimer */
+ connection->comatose = 1 ; /* so now comatose */
+ }
+ else
+ {
+ /* increase the IdleHoldTimer interval */
+ interval *= 2 ;
- status = bgp_connect (peer);
+ if (interval < 4) /* enforce this minimum */
+ interval = 4 ;
+ else if (interval > 120)
+ interval = 120 ;
- switch (status)
- {
- 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);
- break;
- }
- return 0;
-}
+ session->idle_hold_timer_interval = interval ;
-/* 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;
-}
+ if (connection->ordinal == bgp_connection_secondary)
+ bgp_connection_enable_accept(connection) ;
-static int
-bgp_fsm_open (struct peer *peer)
-{
- /* Send keepalive and make keepalive timer */
- bgp_keepalive_send (peer);
+ /* if sibling is comatose, set time for it to come round */
- /* Reset holdtimer value. */
- BGP_TIMER_OFF (peer->t_holdtime);
+ if ((sibling != NULL) && (sibling->comatose))
+ {
+ sibling->comatose = 0 ; /* no longer comatose */
- return 0;
-}
+ if (sibling->ordinal == bgp_connection_secondary)
+ bgp_connection_enable_accept(sibling) ;
-/* Keepalive send to peer. */
-static int
-bgp_fsm_keepalive_expire (struct peer *peer)
-{
- bgp_keepalive_send (peer);
- return 0;
-}
+ bgp_timer_set(sibling, sibling->hold_timer, interval,
+ with_jitter, bgp_idle_hold_timer_action) ;
+ } ;
+ } ;
+ } ;
-/* 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);
+ bgp_timer_set(connection, connection->hold_timer, interval,
+ with_jitter, bgp_idle_hold_timer_action) ;
- /* Send notify to remote peer. */
- bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0);
+ qtimer_unset(connection->keepalive_timer) ;
- /* 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;
- }
+ break;
- /* bgp_stop needs to be invoked while in Established state */
- bgp_stop(peer);
+ /* In sConnect state the primary connection is attempting connect().
+ *
+ * In sActive state the secondary connection is prepared to accept().
+ *
+ * The ConnectRetryTimer automatically recharges, because will loop back
+ * round into the same state.
+ */
+ case bgp_fsm_sConnect:
+ case bgp_fsm_sActive:
+ bgp_timer_set(connection, connection->hold_timer,
+ session->connect_retry_timer_interval, with_jitter,
+ bgp_connect_retry_timer_action) ;
+ qtimer_unset(connection->keepalive_timer) ;
+ break;
- return 0;
-}
+ /* In sOpenSent state is waiting for an OPEN from the other end, before
+ * proceeding to sOpenConfirm state.
+ *
+ * Prepared to wait for quite a long time for this.
+ */
+ case bgp_fsm_sOpenSent:
+ bgp_hold_timer_set(connection, session->open_hold_timer_interval) ;
+ qtimer_unset(connection->keepalive_timer) ;
+ break;
-/* Status goes to Established. Send keepalive packet then make first
- update information. */
-static int
-bgp_establish (struct peer *peer)
+ /* In sOpenConfirm state is waiting for an "ack" before proceeding to
+ * sEstablished.
+ *
+ * There is only one way into sEstablished, and that is from sOpenConfirm.
+ * sOpenConfirm starts the KeepaliveTimer. It would be wrong to reset the
+ * timer on entry to sEstablished.
+ *
+ * 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 sOpenConfirm.
+ *
+ * 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_sOpenConfirm:
+ bgp_timer_set(connection, connection->keepalive_timer,
+ connection->keepalive_timer_interval,
+ with_jitter, bgp_keepalive_timer_action) ;
+ case bgp_fsm_sEstablished:
+ bgp_hold_timer_set(connection, connection->hold_timer_interval) ;
+ break;
+
+ /* The connection is coming to an dead stop.
+ *
+ * Leave the HoldTimer running -- may be waiting for NOTIFICATION to clear,
+ * or for the "courtesy" time to expire.
+ */
+ case bgp_fsm_sStopping:
+ qtimer_unset(connection->keepalive_timer) ;
+
+ break ;
+
+ default:
+ zabort("Unknown bgp_fsm_state") ;
+ } ;
+
+ /* Finally: set the new state */
+ connection->state = new_state ;
+} ;
+
+/*==============================================================================
+ * Timer Action Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * BGP start timer action => bgp_fsm_eBGP_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)
{
- 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);
- 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);
- }
- }
+ bgp_connection connection = timer_info ;
- 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);
- }
+ BGP_FSM_DEBUG(connection, "Timer (start timer expire)") ;
-#ifdef HAVE_SNMP
- bgpTrapEstablished (peer);
-#endif /* HAVE_SNMP */
-
- /* Reset uptime, send keepalive, send current table. */
- peer->uptime = bgp_clock ();
-
- /* 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_eBGP_Start) ;
+} ;
-/* Keepalive packet is received. */
-static int
-bgp_fsm_keepalive (struct peer *peer)
+/*------------------------------------------------------------------------------
+ * BGP connect retry timer => bgp_fsm_eConnectRetry_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, with_jitter, 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_eConnectRetry_timer_expired) ;
+} ;
-static const char *bgp_event_str[] =
+/*------------------------------------------------------------------------------
+ * BGP hold timer => bgp_fsm_eHold_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_eHold_Timer_expired) ;
+} ;
- /* Logging this event. */
- next = FSM [peer->status -1][event - 1].next_state;
+/*------------------------------------------------------------------------------
+ * BGP keepalive fire => bgp_fsm_eKeepAlive_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,
+ with_jitter, 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_eKeepAlive_timer_expired) ;
+} ;
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
index a749f8ea..077d45c3 100644
--- a/bgpd/bgp_fsm.h
+++ b/bgpd/bgp_fsm.h
@@ -1,81 +1,71 @@
-/* BGP-4 Finite State Machine
- From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
- Copyright (C) 1998 Kunihiro Ishiguro
+/* 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.
+ */
-This file is part of GNU Zebra.
+#ifndef _QUAGGA_BGP_FSM_H
+#define _QUAGGA_BGP_FSM_H
-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.
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_connection.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.
+/* Prototypes. */
-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 void
+bgp_fsm_enable_session(bgp_session session) ;
-#ifndef _QUAGGA_BGP_FSM_H
-#define _QUAGGA_BGP_FSM_H
+extern void
+bgp_fsm_disable_session(bgp_session session, bgp_notify notification) ;
-/* 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)
+extern int
+bgp_fsm_pre_update(bgp_connection connection) ;
+
+extern void
+bgp_fsm_open_received(bgp_connection connection) ;
+
+extern void
+bgp_fsm_keepalive_received(bgp_connection connection) ;
+
+extern void
+bgp_fsm_notification_sent(bgp_connection connection) ;
+
+extern void
+bgp_fsm_exception(bgp_connection connection, bgp_session_event_t except,
+ bgp_notify notification) ;
+
+extern void
+bgp_fsm_io_fatal_error(bgp_connection connection, int err) ;
+
+extern void
+bgp_fsm_io_error(bgp_connection connection, int err) ;
+
+extern void
+bgp_fsm_connect_completed(bgp_connection connection, int err,
+ union sockunion* su_local,
+ union sockunion* su_remote) ;
+
+extern void
+bgp_fsm_notification_exception(bgp_connection connection,
+ bgp_notify notification) ;
-/* 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_main.c b/bgpd/bgp_main.c
index 1a460c6b..60b66533 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -19,6 +19,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <zebra.h>
+#include <stdbool.h>
#include "vector.h"
#include "vty.h"
@@ -35,6 +36,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "routemap.h"
#include "filter.h"
#include "plist.h"
+#include "qpnexus.h"
+#include "qlib_init.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
@@ -47,9 +50,12 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_clist.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_zebra.h"
/* bgpd options, we use GNU getopt library. */
-static const struct option longopts[] =
+static const struct option longopts[] =
{
{ "daemon", no_argument, NULL, 'd'},
{ "config_file", required_argument, NULL, 'f'},
@@ -65,41 +71,27 @@ static const struct option longopts[] =
{ "version", no_argument, NULL, 'v'},
{ "dryrun", no_argument, NULL, 'C'},
{ "help", no_argument, NULL, 'h'},
+ { "threaded", no_argument, NULL, 't'},
+ { "ignore_warnings", no_argument, NULL, 'I'},
{ 0 }
};
+/* Configuration file and directory. */
+char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG;
-/* signal definitions */
-void sighup (void);
-void sigint (void);
-void sigusr1 (void);
+/* Route retain mode flag. */
+static bool retain_mode = false;
-static void bgp_exit (int);
+/* Have started terminating the program */
+static bool program_terminating = false ;
-static struct quagga_signal_t bgp_signals[] =
-{
- {
- .signal = SIGHUP,
- .handler = &sighup,
- },
- {
- .signal = SIGUSR1,
- .handler = &sigusr1,
- },
- {
- .signal = SIGINT,
- .handler = &sigint,
- },
- {
- .signal = SIGTERM,
- .handler = &sigint,
- },
-};
+/* whether to ignore warnings in configuration file */
+static bool config_ignore_warnings = false;
-/* Configuration file and directory. */
-char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG;
+/* whether configured to run with qpthreads */
+static bool config_threaded = false ;
-/* Route retain mode flag. */
-static int retain_mode = 0;
+/* whether configured to run as an AS2 speaker */
+static bool config_as2_speaker = false ;
/* Master of threads. */
struct thread_master *master;
@@ -107,17 +99,19 @@ struct thread_master *master;
/* Manually specified configuration file name. */
char *config_file = NULL;
+/* Have we done the second stage initialization? */
+static int done_2nd_stage_init = 0;
/* Process ID saved for use by init system */
static const char *pid_file = PATH_BGPD_PID;
/* VTY port number and address. */
-int vty_port = BGP_VTY_PORT;
+int vty_port = BGP_VTY_PORT;
char *vty_addr = NULL;
/* privileges */
-static zebra_capabilities_t _caps_p [] =
+static zebra_capabilities_t _caps_p [] =
{
- ZCAP_BIND,
+ ZCAP_BIND,
ZCAP_NET_RAW,
};
@@ -142,49 +136,108 @@ usage (char *progname, int status)
if (status != 0)
fprintf (stderr, "Try `%s --help' for more information.\n", progname);
else
- {
- printf ("Usage : %s [OPTION...]\n\n\
-Daemon which manages kernel routing table management and \
-redistribution between different routing protocols.\n\n\
--d, --daemon Runs in daemon mode\n\
--f, --config_file Set configuration file name\n\
--i, --pid_file Set process identifier file name\n\
--p, --bgp_port Set bgp protocol's port number\n\
--l, --listenon Listen on specified address (implies -n)\n\
--A, --vty_addr Set vty's bind address\n\
--P, --vty_port Set vty's port number\n\
--r, --retain When program terminates, retain added route by bgpd.\n\
--n, --no_kernel Do not install route to kernel.\n\
--u, --user User to run as\n\
--g, --group Group to run as\n\
--v, --version Print program version\n\
--C, --dryrun Check configuration for validity and exit\n\
--h, --help Display this help and exit\n\
-\n\
-Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+ {
+ printf (
+ "Usage : %s [OPTION...]\n"
+ "\n"
+ "Daemon which manages kernel routing table management and redistribution "
+ "between different routing protocols.\n"
+ "\n"
+ "-d, --daemon Runs in daemon mode\n"
+ "-f, --config_file Set configuration file name\n"
+ "-i, --pid_file Set process identifier file name\n"
+ "-p, --bgp_port Set bgp protocol's port number\n"
+ "-l, --listenon Listen on specified address (implies -n)\n"
+ "-A, --vty_addr Set vty's bind address\n"
+ "-P, --vty_port Set vty's port number\n"
+ "-r, --retain When program terminates, retain added route by bgpd.\n"
+ "-n, --no_kernel Do not install route to kernel.\n"
+ "-u, --user User to run as\n"
+ "-g, --group Group to run as\n"
+ "-v, --version Print program version\n"
+ "-C, --dryrun Check configuration for validity and exit\n"
+ "-h, --help Display this help and exit\n"
+ "-t, --threaded Use pthreads\n"
+ "-I, --ignore_warnings Ignore warnings while reading configuration file\n"
+ "-2, --as2 Do not advertise AS4 capability\n"
+ "\n"
+ "Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
}
exit (status);
}
-
-/* SIGHUP handler. */
-void
+/*==============================================================================
+ * Signal Handling.
+ *
+ * Actual signals are caught in lib/sigevent. When a signal is caught, a flag
+ * is set and the immediate signal handler returns.
+ *
+ * Those flags are polled in the qpnexus loop, and the Quagga level signal
+ * handler called -- in the main (CLI) thread.
+ */
+
+/* signal definitions */
+void sighup (void);
+void sigint (void);
+void sigusr1 (void);
+void sigusr2 (void);
+
+/* prototypes */
+static void bgp_exit (int);
+static void init_second_stage(int pthreads);
+static void bgp_in_thread_init(void);
+static void routing_start(void) ;
+static void routing_finish(void) ;
+static int routing_foreground(void);
+static int routing_background(void);
+static void sighup_action(mqueue_block mqb, mqb_flag_t flag);
+static void sighup_enqueue(void);
+static void sigterm_action(mqueue_block mqb, mqb_flag_t flag);
+static void sigterm_enqueue(void);
+
+static struct quagga_signal_t bgp_signals[] =
+{
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGUSR2,
+ .handler = &sigusr2,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+/*------------------------------------------------------------------------------
+ * SIGHUP handler.
+ *
+ * The vty level is reset, closing all terminals and vtysh servers, and
+ * closing all listeners.
+ *
+ * A message is sent to the Routeing Engine to restart.
+ *
+ * When the Routeing Engine has restarted, it will send a message to the CLI
+ * to restart the listeners.
+ */
+void
sighup (void)
{
zlog (NULL, LOG_INFO, "SIGHUP received");
- /* Terminate all thread. */
- bgp_terminate ();
- bgp_reset ();
- zlog_info ("bgpd restarting!");
-
- /* Reload config file. */
- vty_read_config (config_file, config_default);
-
- /* Create VTY's socket */
- vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH);
+ vty_reset_because("Reloading configuration");
+ sighup_enqueue(); /* tell the Routeing Engine */
- /* Try to return to normal operation. */
}
/* SIGINT handler. */
@@ -193,10 +246,9 @@ sigint (void)
{
zlog_notice ("Terminating on signal");
- if (! retain_mode)
- bgp_terminate ();
-
- bgp_exit (0);
+ /* tell the routing engine to send notifies to peers and wait
+ * for all sessions to be disabled */
+ sigterm_enqueue();
}
/* SIGUSR1 handler. */
@@ -206,19 +258,34 @@ sigusr1 (void)
zlog_rotate (NULL);
}
-/*
- Try to free up allocations we know about so that diagnostic tools such as
- valgrind are able to better illuminate leaks.
+/* SIGUSR2 handler. */
+void
+sigusr2 (void)
+{
+ /* Used to signal message queues */
+ if (qpthreads_enabled)
+ return;
+ else
+ exit(1);
+}
- Zebra route removal and protocol teardown are not meant to be done here.
- For example, "retain_mode" may be set.
-*/
+/*------------------------------------------------------------------------------
+ * Final exit code...
+ *
+ * ...try to free up allocations we know about so that diagnostic tools such as
+ * valgrind are able to better illuminate leaks.
+ *
+ * Zebra route removal and protocol teardown are not meant to be done here.
+ * For example, "retain_mode" may be set.
+ *
+ * Note that by the time reach here, only the main (CLI) thread is running,
+ *
+ */
static void
bgp_exit (int status)
{
struct bgp *bgp;
struct listnode *node, *nnode;
- int *socket;
struct interface *ifp;
extern struct zclient *zclient;
extern struct zclient *zlookup;
@@ -231,14 +298,6 @@ bgp_exit (int status)
bgp_delete (bgp);
list_free (bm->bgp);
- /* reverse bgp_master_init */
- for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, socket))
- {
- if (close ((int)(long)socket) == -1)
- zlog_err ("close (%d): %s", (int)(long)socket, safe_strerror (errno));
- }
- list_delete (bm->listen_sockets);
-
/* reverse bgp_zebra_init/if_init */
if (retain_mode)
if_add_hook (IF_DELETE_HOOK, NULL);
@@ -254,6 +313,9 @@ bgp_exit (int status)
}
list_free (iflist);
+ /* curtains */
+ zlog_notice ("Terminated");
+
/* reverse bgp_attr_init */
bgp_attr_finish ();
@@ -300,29 +362,145 @@ bgp_exit (int status)
if (zlog_default)
closezlog (zlog_default);
+ zlog_default = NULL ;
+
+ if (qpthreads_enabled)
+ {
+ qpn_reset_free(routing_nexus);
+ qpn_reset_free(bgp_nexus);
+ } ;
+ cli_nexus = qpn_reset_free(cli_nexus);
if (CONF_BGP_DEBUG (normal, NORMAL))
log_memstats_stderr ("bgpd");
- exit (status);
+ qexit (status);
+}
+
+/*------------------------------------------------------------------------------
+ * Second stage initialisation and qpthreads_enabled.
+ *
+ * Really want to do this before the configuration file is read. However,
+ * also want to allow qpthreads to be enabled by configuration file.
+ *
+ * So... configuration file reader has a mechanism to look for a given
+ * command as the *first* in the file and:
+ *
+ * 1. if it's there, invoke the command in the usual way
+ *
+ * 2. if it's not there, invoke the command but with a NULL set of arguments,
+ * which signals the "default" nature of the call.
+ *
+ * This mechanism is used so that the "threaded_cmd" is the time at which
+ * second stage initialisation is done. (But only once -- not on rereading
+ * the configuration file.)
+ */
+
+/* Threaded command. If present must be the first command in the
+ * configuration file. If not the first command it will log and abort.
+ */
+DEFUN_HID_CALL (threaded,
+ threaded_cmd,
+ "threaded",
+ "Use pthreads\n")
+{
+ if (argv != NULL)
+ config_threaded = 1 ; /* Explicit command => turn on threading */
+
+ if (!done_2nd_stage_init)
+ init_second_stage(config_threaded) ;
+ else
+ vty_out(vty, "pthreads are %s\n", qpthreads_enabled ? "enabled"
+ : "disabled") ;
+ return CMD_SUCCESS;
+}
+
+/* Enable or disables pthreads. Create the nexus(es). Perform
+ * any post nexus creation initialization. The nexus(es) need
+ * to be created as soon as we know the pthread state so that
+ * the message queues are available for the configuration data.
+ */
+static void
+init_second_stage(int pthreads)
+{
+ assert(!done_2nd_stage_init) ;
+
+ done_2nd_stage_init = 1;
+
+ qlib_init_second_stage(pthreads);
+ bgp_peer_index_mutex_init();
+
+ /* Make nexus for main thread, always needed */
+ cli_nexus = qpn_init_new(cli_nexus, 1); /* main thread */
+
+ /* if using pthreads create additional nexus */
+ if (qpthreads_enabled)
+ {
+ bgp_nexus = qpn_init_new(bgp_nexus, 0);
+ routing_nexus = qpn_init_new(routing_nexus, 0);
+ }
+ else
+ {
+ /* we all share the single nexus and single thread */
+ bgp_nexus = cli_nexus;
+ routing_nexus = cli_nexus;
+ }
+
+ /* Tell thread stuff to use this qtimer pile */
+ thread_set_qtimer_pile(routing_nexus->pile) ;
+
+ /* Nexus hooks.
+ * Beware if !qpthreads_enabled then there is only 1 nexus object
+ * with all nexus pointers being aliases for it.
+ */
+ qpn_add_hook_function(&routing_nexus->in_thread_init, routing_start) ;
+ qpn_add_hook_function(&bgp_nexus->in_thread_init, bgp_in_thread_init) ;
+
+ qpn_add_hook_function(&routing_nexus->in_thread_final, routing_finish) ;
+ qpn_add_hook_function(&bgp_nexus->in_thread_final, bgp_close_listeners) ;
+
+ qpn_add_hook_function(&routing_nexus->foreground, routing_foreground) ;
+ qpn_add_hook_function(&bgp_nexus->foreground, bgp_connection_queue_process) ;
+
+ qpn_add_hook_function(&routing_nexus->background, routing_background) ;
+
+ confirm(qpn_hooks_max >= 2) ;
+
+ /* vty and zclient can use either nexus or threads.
+ * For bgp client we always want nexus, regardless of pthreads.
+ */
+ vty_init_r(cli_nexus, routing_nexus);
+ zclient_init_r(routing_nexus);
+
+ /* Now we have our nexus we can init BGP. */
+ /* BGP related initialization. */
+ bgp_init ();
+
+ /* Sort CLI commands. */
+ sort_node ();
}
-
+
/* Main routine of bgpd. Treatment of argument and start bgp finite
state machine is handled at here. */
int
main (int argc, char **argv)
{
char *p;
- int opt;
- int daemon_mode = 0;
- int dryrun = 0;
+ int opt;
+ bool daemon_mode = false ;
+ bool dryrun = false ;
char *progname;
- struct thread thread;
- int tmp_port;
+ int tmp_port;
/* Set umask before anything for security */
umask (0027);
+#ifdef QDEBUG
+ fprintf(stderr, "%s\n", debug_banner);
+#endif
+
+ qlib_init_first_stage();
+
/* Preserve name of myself. */
progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
@@ -333,19 +511,19 @@ main (int argc, char **argv)
bgp_master_init ();
/* Command line argument treatment. */
- while (1)
+ while (1)
{
- opt = getopt_long (argc, argv, "df:i:hp:l:A:P:rnu:g:vC", longopts, 0);
-
+ opt = getopt_long (argc, argv, "df:i:hp:l:A:P:rnu:g:vCtI2", longopts, 0);
+
if (opt == EOF)
break;
- switch (opt)
+ switch (opt)
{
case 0:
break;
case 'd':
- daemon_mode = 1;
+ daemon_mode = true ;
break;
case 'f':
config_file = optarg;
@@ -366,17 +544,17 @@ main (int argc, char **argv)
case 'P':
/* Deal with atoi() returning 0 on failure, and bgpd not
listening on bgp port... */
- if (strcmp(optarg, "0") == 0)
+ if (strcmp(optarg, "0") == 0)
{
vty_port = 0;
break;
- }
+ }
vty_port = atoi (optarg);
if (vty_port <= 0 || vty_port > 0xffff)
vty_port = BGP_VTY_PORT;
break;
case 'r':
- retain_mode = 1;
+ retain_mode = true ;
break;
case 'l':
bm->address = optarg;
@@ -395,11 +573,20 @@ main (int argc, char **argv)
exit (0);
break;
case 'C':
- dryrun = 1;
+ dryrun = true ;
break;
case 'h':
usage (progname, 0);
break;
+ case 't':
+ config_threaded = true ;
+ break;
+ case 'I':
+ config_ignore_warnings = true ;
+ break ;
+ case '2':
+ config_as2_speaker = true ;
+ break ;
default:
usage (progname, 1);
break;
@@ -414,46 +601,218 @@ main (int argc, char **argv)
signal_init (master, Q_SIGC(bgp_signals), bgp_signals);
zprivs_init (&bgpd_privs);
cmd_init (1);
+ install_element (CONFIG_NODE, &threaded_cmd);
vty_init (master);
memory_init ();
- /* BGP related initialization. */
- bgp_init ();
+ /* Read config file.
+ *
+ * NB: second state initialisation is done in the threaded_cmd, which must
+ * either be the first command in the file, or is executed by default
+ * before the first command in the file.
+ *
+ * NB: if fails to open the configuration file, fails to read anything, or
+ * it is completely empty (or effectively so), then may still need to do
+ * second stage initialisation.
+ */
+ done_2nd_stage_init = 0 ;
- /* Sort CLI commands. */
- sort_node ();
+ vty_read_config_first_cmd_special(config_file, config_default,
+ &threaded_cmd, config_ignore_warnings) ;
- /* Parse config file. */
- vty_read_config (config_file, config_default);
+ if (!done_2nd_stage_init)
+ init_second_stage(config_threaded) ;
+
+ bm->as2_speaker = config_as2_speaker ;
/* Start execution only if not in dry-run mode */
- if(dryrun)
+ if (dryrun)
return(0);
-
- /* Turn into daemon if daemon_mode is set. */
+
+ /* only the calling thread survives in the child after a fork
+ * so ensure we haven't created any threads yet
+ */
+ assert(!qpthreads_thread_created);
+
+ /* Turn into daemon if daemon_mode is set. */
if (daemon_mode && daemon (0, 0) < 0)
{
- zlog_err("BGPd daemon failed: %s", strerror(errno));
+ zlog_err("BGPd daemon failed: %s", errtoa(errno, 0).str);
return (1);
}
-
- /* Process ID file creation. */
+ /* Process ID file creation. */
pid_output (pid_file);
- /* Make bgp vty socket. */
- vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH);
+ /* Ready to run VTY now. */
+ vty_start(vty_addr, vty_port, BGP_VTYSH_PATH);
/* Print banner. */
- zlog_notice ("BGPd %s starting: vty@%d, bgp@%s:%d", QUAGGA_VERSION,
- vty_port,
+#ifdef QDEBUG
+ zlog_notice("%s", debug_banner);
+#endif
+ zlog_notice ("BGPd %s%s starting: vty@%d, bgp@%s:%d",
+ QUAGGA_VERSION,
+ (qpthreads_enabled ? " pthreaded" : ""),
+ vty_port,
(bm->address ? bm->address : "<all>"),
- bm->port);
+ (int)bm->port);
+
+ /* Launch finite state machine(s) */
+ if (qpthreads_enabled)
+ {
+ void * thread_result = NULL;
+
+ qpn_exec(routing_nexus);
+ qpn_exec(bgp_nexus);
+ qpn_exec(cli_nexus); /* must be last to start - on main thread */
+
+ /* terminating, wait for all threads to finish */
+ thread_result = qpt_thread_join(routing_nexus->thread_id);
+ thread_result = qpt_thread_join(bgp_nexus->thread_id);
+ }
+ else
+ {
+ qpn_exec(cli_nexus); /* only nexus - on main thread */
+ }
+
+ /* Note that from this point forward is running in the main (CLI) thread
+ * and any other threads have been joined and their nexuses freed.
+ */
+ bgp_exit(0);
+}
+
+/* bgp_nexus in-thread initialization */
+static void
+bgp_in_thread_init(void)
+{
+ bgp_open_listeners(bm->address, bm->port);
+}
+
+/* routing_nexus in-thread initialization -- for gdb ! */
+
+static int routing_started = 0 ;
- /* Start finite state machine, here we go! */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
+static void
+routing_start(void)
+{
+ routing_started = 1 ;
+}
+
+static void
+routing_finish(void)
+{
+ routing_started = 0 ;
+}
+
+/* legacy threads in routing engine */
+static int
+routing_foreground(void)
+{
+ return thread_dispatch(master) ;
+}
+
+/* background threads in routing engine */
+static int
+routing_background(void)
+{
+ return thread_dispatch_background(master) ;
+}
+
+/*==============================================================================
+ * SIGHUP and SIGTERM
+ */
+
+/*------------------------------------------------------------------------------
+ * SIGHUP: message sent to Routeing engine and the action it then takes.
+ *
+ * TODO: should SIGHUP be a priority message (!)
+ */
+static void
+sighup_enqueue(void)
+{
+ mqueue_block mqb = mqb_init_new(NULL, sighup_action, NULL) ;
- /* Not reached. */
- return (0);
+ mqueue_enqueue(routing_nexus->queue, mqb, mqb_priority) ;
}
+
+/* dispatch a command from the message queue block */
+static void
+sighup_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ if ((flag == mqb_action) && !program_terminating)
+ {
+ zlog_info ("bgpd restarting!");
+
+ bgp_terminate (0, 0); /* send notifies */
+ bgp_reset ();
+
+ /* Reload config file. */
+ vty_read_config (config_file, config_default);
+
+ /* Create VTY's socket */
+ vty_restart(vty_addr, vty_port, BGP_VTYSH_PATH);
+
+ /* Try to return to normal operation. */
+ }
+
+ mqb_free(mqb);
+}
+
+/*------------------------------------------------------------------------------
+ * Foreground task to see if all peers have been deleted yet.
+ */
+static int
+program_terminate_if_all_peers_deleted(void)
+{
+ if (bm->peer_linger_count == 0)
+ {
+ /* ask remaining pthreads to die
+ *
+ * Note that qpn_terminate does nothing if it has been called once
+ * already.
+ */
+ if (qpthreads_enabled && routing_nexus != NULL)
+ qpn_terminate(routing_nexus);
+
+ if (qpthreads_enabled && bgp_nexus != NULL)
+ qpn_terminate(bgp_nexus);
+
+ if (cli_nexus != NULL)
+ qpn_terminate(cli_nexus) ;
+ } ;
+
+ return 0 ; /* nothing to do, really. */
+} ;
+
+/*------------------------------------------------------------------------------
+ * SIGTERM: message sent to Routeing engine and the action it then takes.
+ */
+static void
+sigterm_enqueue(void)
+{
+ mqueue_block mqb = mqb_init_new(NULL, sigterm_action, NULL) ;
+
+ mqueue_enqueue(routing_nexus->queue, mqb, mqb_priority) ;
+} ;
+
+/* dispatch a command from the message queue block */
+static void
+sigterm_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ if ((flag == mqb_action) && !program_terminating)
+ {
+ /* send notify to all peers, wait for all sessions to be disables
+ * then terminate all pthreads
+ */
+ program_terminating = true ;
+
+ bgp_terminate(1, retain_mode);
+
+ qpn_add_hook_function(&routing_nexus->foreground,
+ program_terminate_if_all_peers_deleted) ;
+ }
+
+ mqb_free(mqb);
+} ;
+
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 72ad089e..983a4867 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -36,7 +36,7 @@ static u_int16_t
decode_rd_type (u_char *pnt)
{
u_int16_t v;
-
+
v = ((u_int16_t) *pnt++ << 8);
v |= (u_int16_t) *pnt;
return v;
@@ -58,7 +58,7 @@ decode_rd_as (u_char *pnt, struct rd_as *rd_as)
{
rd_as->as = (u_int16_t) *pnt++ << 8;
rd_as->as |= (u_int16_t) *pnt++;
-
+
rd_as->val = ((u_int32_t) *pnt++ << 24);
rd_as->val |= ((u_int32_t) *pnt++ << 16);
rd_as->val |= ((u_int32_t) *pnt++ << 8);
@@ -70,13 +70,13 @@ decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
{
memcpy (&rd_ip->ip, pnt, 4);
pnt += 4;
-
+
rd_ip->val = ((u_int16_t) *pnt++ << 8);
rd_ip->val |= (u_int16_t) *pnt;
}
int
-bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr,
+bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr,
struct bgp_nlri *packet)
{
u_char *pnt;
@@ -92,9 +92,9 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr,
u_char *tagpnt;
/* Check peer status. */
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
return 0;
-
+
/* Make prefix_rd */
prd.family = AF_UNSPEC;
prd.prefixlen = 64;
@@ -146,10 +146,10 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr,
#if 0
if (type == RD_TYPE_AS)
zlog_info ("prefix %ld:%ld:%ld:%s/%d", label, rd_as.as, rd_as.val,
- inet_ntoa (p.u.prefix4), p.prefixlen);
+ safe_inet_ntoa (p.u.prefix4), p.prefixlen);
else if (type == RD_TYPE_IP)
- zlog_info ("prefix %ld:%s:%ld:%s/%d", label, inet_ntoa (rd_ip.ip),
- rd_ip.val, inet_ntoa (p.u.prefix4), p.prefixlen);
+ zlog_info ("prefix %ld:%s:%ld:%s/%d", label, safe_inet_ntoa (rd_ip.ip),
+ rd_ip.val, safe_inet_ntoa (p.u.prefix4), p.prefixlen);
#endif /* 0 */
if (pnt + psize > lim)
@@ -234,12 +234,12 @@ str2tag (const char *str, u_char *tag)
u_int32_t t;
l = strtoul (str, &endptr, 10);
-
+
if (*endptr == '\0' || l == ULONG_MAX || l > UINT32_MAX)
return 0;
t = (u_int32_t) l;
-
+
tag[0] = (u_char)(t >> 12);
tag[1] = (u_char)(t >> 4);
tag[2] = (u_char)(t << 4);
@@ -271,7 +271,7 @@ prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size)
else if (type == RD_TYPE_IP)
{
decode_rd_ip (pnt + 2, &rd_ip);
- snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+ snprintf (buf, size, "%s:%d", safe_inet_ntoa (rd_ip.ip), rd_ip.val);
return buf;
}
@@ -342,7 +342,7 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd)
if (header)
{
vty_out (vty, "BGP table version is 0, local router ID is %s%s",
- inet_ntoa (bgp->router_id), VTY_NEWLINE);
+ safe_inet_ntoa (bgp->router_id), VTY_NEWLINE);
vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s",
VTY_NEWLINE);
vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s",
@@ -373,7 +373,7 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd)
if (type == RD_TYPE_AS)
vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
else if (type == RD_TYPE_IP)
- vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+ vty_out (vty, "%s:%d", safe_inet_ntoa (rd_ip.ip), rd_ip.val);
vty_out (vty, "%s", VTY_NEWLINE);
rd_header = 0;
@@ -421,7 +421,7 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty
vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn))
{
if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
@@ -432,7 +432,7 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty
rd_header = 1;
for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
- for (ri = rm->info; ri; ri = ri->next)
+ for (ri = rm->info; ri; ri = ri->info_next)
{
if (type == bgp_show_type_neighbor)
{
@@ -448,7 +448,7 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty
else
{
vty_out (vty, "BGP table version is 0, local router ID is %s%s",
- inet_ntoa (bgp->router_id), VTY_NEWLINE);
+ safe_inet_ntoa (bgp->router_id), VTY_NEWLINE);
vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s",
VTY_NEWLINE);
vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s",
@@ -480,9 +480,9 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty
if (type == RD_TYPE_AS)
vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
else if (type == RD_TYPE_IP)
- vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
-
- vty_out (vty, "%s", VTY_NEWLINE);
+ vty_out (vty, "%s:%d", safe_inet_ntoa (rd_ip.ip), rd_ip.val);
+
+ vty_out (vty, "%s", VTY_NEWLINE);
rd_header = 0;
}
if (tags)
@@ -579,7 +579,7 @@ DEFUN (show_ip_bgp_vpnv4_all_neighbor_routes,
{
union sockunion *su;
struct peer *peer;
-
+
su = sockunion_str2su (argv[0]);
if (su == NULL)
{
diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c
new file mode 100644
index 00000000..ed49f535
--- /dev/null
+++ b/bgpd/bgp_msg_read.c
@@ -0,0 +1,1768 @@
+/* BGP Message Read -- 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 <time.h>
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_msg_read.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_route_refresh.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_vty.h"
+
+/*------------------------------------------------------------------------------
+ * Message handler functions.
+ */
+static void bgp_msg_unknown_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+static void bgp_msg_open_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+static void bgp_msg_update_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+static void bgp_msg_notify_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+static void bgp_msg_keepalive_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+static void bgp_msg_route_refresh_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+static void bgp_msg_capability_receive(bgp_connection connection,
+ bgp_size_t body_size) ;
+
+/*------------------------------------------------------------------------------
+ * Get BGP message length, given a pointer to the start of a message.
+ *
+ * Make sure things are kosher.
+ */
+extern bgp_size_t
+bgp_msg_get_mlen(uint8_t* p, uint8_t* limit)
+{
+ uint16_t mlen ;
+ passert((p + BGP_MH_HEAD_L) <= limit) ;
+
+ mlen = ((bgp_size_t)(*(p + BGP_MH_MARKER_L)) << 8)
+ + (*(p + BGP_MH_MARKER_L + 1)) ;
+
+ passert((p + mlen) <= limit) ;
+
+ return mlen ;
+} ;
+
+/*==============================================================================
+ * Header validation and sexing of messages
+ */
+enum
+{
+ qBGP_MT_unknown = 0,
+ qBGP_MT_OPEN,
+ qBGP_MT_UPDATE,
+ qBGP_MT_NOTIFICATION,
+ qBGP_MT_KEEPALIVE,
+ qBGP_MT_ROUTE_REFRESH,
+ qBGP_MT_CAPABILITY,
+ qBGP_MT_ROUTE_REFRESH_pre,
+
+ qBGP_MT_count,
+} ;
+ /* 0 1 2 3 */
+static const uint8_t bgp_header[] = { 0xFF, 0xFF, 0xFF, 0xFF, /* 4 */
+ 0xFF, 0xFF, 0xFF, 0xFF, /* 8 */
+ 0xFF, 0xFF, 0xFF, 0xFF, /* 12 */
+ 0xFF, 0xFF, 0xFF, 0xFF /* 16 */
+ } ;
+CONFIRM(sizeof(bgp_header) == BGP_MH_MARKER_L) ;
+
+/* Array to map real BGP message type to qBGP message type */
+static const uint8_t bgp_type_map[256] =
+{
+ [BGP_MT_OPEN] = qBGP_MT_OPEN,
+ [BGP_MT_UPDATE] = qBGP_MT_UPDATE,
+ [BGP_MT_NOTIFICATION] = qBGP_MT_NOTIFICATION,
+ [BGP_MT_KEEPALIVE] = qBGP_MT_KEEPALIVE,
+ [BGP_MT_ROUTE_REFRESH] = qBGP_MT_ROUTE_REFRESH,
+ [BGP_MT_CAPABILITY] = qBGP_MT_CAPABILITY,
+ [BGP_MT_ROUTE_REFRESH_pre] = qBGP_MT_ROUTE_REFRESH_pre
+} ;
+CONFIRM(qBGP_MT_unknown == 0) ;
+
+/* Array of minimum message length -- by qBGP_MT_xxx */
+static const bgp_size_t bgp_type_min_size[] =
+{
+ [qBGP_MT_unknown] = BGP_MSG_MAX_L + 1, /* invalid ! */
+
+ [qBGP_MT_OPEN] = BGP_OPM_MIN_L,
+ [qBGP_MT_OPEN] = BGP_OPM_MIN_L,
+ [qBGP_MT_UPDATE] = BGP_UPM_MIN_L,
+ [qBGP_MT_NOTIFICATION] = BGP_NOM_MIN_L,
+ [qBGP_MT_KEEPALIVE] = BGP_KAM_L,
+ [qBGP_MT_ROUTE_REFRESH] = BGP_RRM_MIN_L,
+ [qBGP_MT_CAPABILITY] = BGP_MH_HEAD_L, /* pro tem */
+ [qBGP_MT_ROUTE_REFRESH_pre] = BGP_RRM_MIN_L
+} ;
+
+/* Array of message handler functions -- by qBGP_MT_xxx */
+static bgp_msg_handler* const bgp_type_handler[] =
+{
+ [qBGP_MT_unknown] = bgp_msg_unknown_receive,
+
+ [qBGP_MT_OPEN] = bgp_msg_open_receive,
+ [qBGP_MT_UPDATE] = bgp_msg_update_receive,
+ [qBGP_MT_NOTIFICATION] = bgp_msg_notify_receive,
+ [qBGP_MT_KEEPALIVE] = bgp_msg_keepalive_receive,
+ [qBGP_MT_ROUTE_REFRESH] = bgp_msg_route_refresh_receive,
+ [qBGP_MT_CAPABILITY] = bgp_msg_capability_receive,
+ [qBGP_MT_ROUTE_REFRESH_pre] = bgp_msg_route_refresh_receive
+} ;
+
+/* Array of message type names by qBGP_MT_xxxx */
+static const char* bgp_type_name[] =
+{
+ [qBGP_MT_unknown] = "*unknown*",
+
+ [qBGP_MT_OPEN] = "OPEN",
+ [qBGP_MT_UPDATE] = "UPDATE",
+ [qBGP_MT_NOTIFICATION] = "NOTIFICATION",
+ [qBGP_MT_KEEPALIVE] = "KEEPALIVE",
+ [qBGP_MT_ROUTE_REFRESH] = "ROUTE-REFRESH",
+ [qBGP_MT_CAPABILITY] = "CAPABILITY",
+ [qBGP_MT_ROUTE_REFRESH_pre] = "ROUTE-REFRESH(pre)"
+} ;
+
+/*------------------------------------------------------------------------------
+ * The message size field is invalid per RFC
+ *
+ * Issue notification and kick FSM.
+ */
+static void
+bgp_msg_header_bad_len(bgp_connection connection, uint8_t type, bgp_size_t size)
+{
+ uint16_t notify_size = htons(size) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ plog_debug (connection->log,
+ "%s bad message length - %d for %s",
+ connection->host, size,
+ bgp_type_name[bgp_type_map[type]]) ;
+
+ bgp_fsm_exception(connection, bgp_session_eInvalid_msg,
+ bgp_notify_new_with_data(BGP_NOMC_HEADER, BGP_NOMS_H_BAD_LEN,
+ (void*)&notify_size, 2)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The message type is either unknown, or not enabled by capability exchange.
+ *
+ * Issue notification and kick FSM.
+ */
+static void
+bgp_msg_header_bad_type(bgp_connection connection, uint8_t type)
+{
+ if (BGP_DEBUG (normal, NORMAL))
+ {
+ if (bgp_type_map[type] == qBGP_MT_unknown)
+ plog_debug (connection->log, "%s unknown message type 0x%02x",
+ connection->host, type) ;
+ else
+ plog_err (connection->log, "%s [Error] BGP %s is not enabled",
+ connection->host, bgp_type_name[bgp_type_map[type]]) ;
+ } ;
+
+ bgp_fsm_exception(connection, bgp_session_eInvalid_msg,
+ bgp_notify_new_with_data(BGP_NOMC_HEADER, BGP_NOMS_H_BAD_TYPE,
+ (void*)&type, 1)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Validate header part of BGP message.
+ *
+ * Have just read the header part (BGP_MH_HEAD_L) into connection->ibuf, need
+ * to check it's valid, and find how much more to read to complete the
+ * message.
+ *
+ * Advances the stream getp past the header.
+ *
+ * Plants: msg_type -- the message type
+ * msg_body_size -- the size of the message *body*
+ * msg_func -- the function to process the message
+ *
+ * in the connection, ready for bgp_msg_dispatch().
+ *
+ * Returns: number of bytes still to read (ie msg_body_size) -- may be 0
+ *
+ * NB: deals with invalid header, unknown message type and less than minimum
+ * message length for the message type.
+ *
+ * These all raise the required events, and return with minimum size
+ * message and the function set to bgp_msg_unknown_receive.
+ *
+ * The effect is that the reader will not read any more, and will dispatch
+ * to bgp_msg_unknown_receive.
+ */
+int
+bgp_msg_check_header(bgp_connection connection)
+{
+ uint8_t type ;
+ bgp_size_t size ;
+ uint8_t qt ;
+ bgp_size_t min_size ;
+
+ /* Get size and type. */
+ stream_forward_getp (connection->ibuf, BGP_MH_MARKER_L);
+ size = stream_getw (connection->ibuf);
+ type = stream_getc (connection->ibuf);
+
+ if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0)
+ zlog_debug ("%s rcv message type %d, length (excl. header) %d",
+ connection->host, type, size - BGP_MH_HEAD_L);
+
+ /* Marker check */
+ /* TODO: why did old code only do this on OPEN and KEEPALIVE ? */
+ if (memcmp(connection->ibuf->data, bgp_header, BGP_MH_MARKER_L) == 0)
+ {
+ /* BGP type check and minimum/maximum message length checks. */
+
+ qt = bgp_type_map[type] ; /* qBGP_MT_unknown if not valid */
+ min_size = bgp_type_min_size[qt] ;/* > BGP_MSG_MAX_L if not valid */
+
+ if ((size < min_size) || (size > BGP_MSG_MAX_L))
+ {
+ if (qt == qBGP_MT_unknown)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ plog_debug (connection->log, "%s unknown message type 0x%02x",
+ connection->host, type);
+
+ bgp_fsm_exception(connection, bgp_session_eInvalid_msg,
+ bgp_notify_new_with_data(BGP_NOMC_HEADER, BGP_NOMS_H_BAD_TYPE,
+ (void*)&type, 1)) ;
+ }
+ else
+ bgp_msg_header_bad_len(connection, type, size) ;
+
+ size = BGP_MH_HEAD_L ; /* can stop reading, now */
+ }
+ }
+ else
+ {
+ bgp_fsm_exception(connection, bgp_session_eInvalid_msg,
+ bgp_notify_new(BGP_NOMC_HEADER, BGP_NOMS_H_NOT_SYNC)) ;
+ qt = qBGP_MT_unknown ; /* force unknown message */
+ size = BGP_MH_HEAD_L ; /* can stop reading, now */
+ } ;
+
+
+ connection->msg_type = type ;
+ connection->msg_body_size = size - BGP_MH_HEAD_L ;
+ connection->msg_func = bgp_type_handler[qt] ;
+
+ return connection->msg_body_size ;
+} ;
+
+/*==============================================================================
+ * Invalid message handler.
+ *
+ * Does nothing at all -- the error has already been deal with, this just
+ * allows unknown (and invalid) messages to be handled just like OK ones.
+ */
+static void bgp_msg_unknown_receive(bgp_connection connection, bgp_size_t body_size)
+{
+ return ;
+} ;
+
+/*==============================================================================
+ * BGP OPEN message
+ *
+ *
+ *
+ */
+
+static int
+bgp_msg_open_option_parse (bgp_connection connection, bgp_notify notification,
+ sucker sr) ;
+static int
+bgp_msg_capability_option_parse (bgp_connection connection,
+ bgp_notify notification, sucker sr) ;
+static int
+bgp_msg_open_error(bgp_notify notification, bgp_nom_subcode_t subcode) ;
+
+static int
+bgp_msg_open_invalid(bgp_notify notification) ;
+
+/*------------------------------------------------------------------------------
+ * Receive BGP open packet and parse it into the connection's open_recv
+ *
+ * NB: requires the session to be locked (connection-wise) and not NULL.
+ */
+static void
+bgp_msg_open_receive (bgp_connection connection, bgp_size_t body_size)
+{
+ int ret;
+ u_char version;
+ u_char optlen;
+ struct in_addr remote_id;
+ bgp_open_state open_recv;
+ struct stream* s ;
+ struct sucker ssr ;
+ unsigned holdtime ;
+
+ ++connection->session->stats.open_in ;
+
+ /* Start with an unspecific OPEN notification */
+ bgp_notify notification = bgp_notify_new(BGP_NOMC_OPEN,
+ BGP_NOMS_UNSPECIFIC) ;
+
+ /* To receive the parsed open message */
+ open_recv = connection->open_recv
+ = bgp_open_state_init_new(connection->open_recv) ;
+
+ /* Parse fixed part of the open packet */
+ s = connection->ibuf ;
+
+ version = stream_getc (s);
+ open_recv->my_as2 = stream_getw (s);
+ open_recv->holdtime = stream_getw (s);
+ open_recv->bgp_id = stream_get_ipv4 (s);
+
+ open_recv->my_as = open_recv->my_as2 ;
+
+ remote_id.s_addr = open_recv->bgp_id ;
+
+ /* Receive OPEN message log */
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u,"
+ " holdtime %d, id %s",
+ connection->host, version,
+ open_recv->my_as, open_recv->holdtime,
+ safe_inet_ntoa (remote_id));
+
+ /* Peer BGP version check. */
+ if (version != BGP_VERSION_4)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug("%s bad protocol version, remote requested %d, local max %d",
+ connection->host, version, BGP_VERSION_4);
+
+ bgp_msg_open_error(notification, BGP_NOMS_O_VERSION) ;
+ bgp_notify_append_w(notification, BGP_VERSION_4) ;
+
+ goto reject ;
+ }
+
+ /* Remote bgp_id may not be multicast, or the same as here */
+ if (IN_MULTICAST(ntohl(open_recv->bgp_id)) ||
+ (open_recv->bgp_id == connection->session->open_send->bgp_id))
+ {
+ zlog_debug ("%s rcv OPEN, multicast or our id %s",
+ connection->host, safe_inet_ntoa (remote_id)) ;
+ bgp_msg_noms_o_bad_id(notification, open_recv->bgp_id) ;
+ goto reject ;
+ } ;
+
+ /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST
+ calculate the value of the Hold Timer by using the smaller of its
+ configured Hold Time and the Hold Time received in the OPEN message.
+ The Hold Time MUST be either zero or at least three seconds. An
+ implementation may reject connections on the basis of the Hold Time.
+
+ See below where sets keepalive to hold / 3 !!
+ */
+ if (open_recv->holdtime < 3 && open_recv->holdtime != 0)
+ {
+ bgp_msg_open_error(notification, BGP_NOMS_O_H_TIME) ;
+ goto reject ;
+ } ;
+
+ /* Open option part parse */
+
+ optlen = stream_getc(s) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u",
+ connection->host, optlen) ;
+
+ if (optlen != stream_get_left(s))
+ {
+ zlog_err ("%s bad OPEN, message length %u but option length %u",
+ connection->host, (unsigned)stream_get_endp(s), optlen) ;
+ bgp_msg_open_invalid(notification) ;
+ goto reject ;
+ } ;
+
+ suck_init(&ssr, stream_pnt(s), optlen) ;
+
+ ret = bgp_msg_open_option_parse (connection, notification, &ssr) ;
+ if (ret < 0)
+ goto reject ;
+
+ /* Now worry about the AS number */
+
+ /* ASN == 0 is odd for AS2, error for AS4 */
+ if (open_recv->my_as == 0)
+ {
+ if (open_recv->can_as4)
+ {
+ zlog_err ("%s [AS4] bad OPEN, got AS4 capability, but AS4 set to 0",
+ connection->host) ;
+ bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ;
+ goto reject ;
+ }
+ else
+ {
+ if (BGP_DEBUG (as4, AS4))
+ zlog_debug ("%s [AS4] OPEN remote_as is 0 (not AS4 speaker)"
+ " odd, but proceeding.", connection->host) ;
+ } ;
+ } ;
+
+ /* ASN = BGP_AS_TRANS is odd for AS2, error for AS4 */
+ if (open_recv->my_as == BGP_ASN_TRANS)
+ {
+ if (open_recv->can_as4)
+ {
+ zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed",
+ connection->host);
+ bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ;
+ goto reject ;
+ }
+ else
+ {
+ if (BGP_DEBUG (as4, AS4))
+ zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS (not AS4 speaker)"
+ " odd, but proceeding.", connection->host) ;
+ } ;
+ } ;
+
+ /* Worry about my_as2 for AS4 speaker, if as2 != as4 */
+ if ((open_recv->can_as4) && (open_recv->my_as != open_recv->my_as2))
+ {
+ if (open_recv->my_as2 == BGP_ASN_TRANS)
+ {
+ if ((open_recv->my_as <= BGP_AS2_MAX) && BGP_DEBUG(as4, AS4))
+ zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS,"
+ " but AS4 (%u) fits in 2-bytes, very odd peer.",
+ connection->host, open_recv->my_as) ;
+ }
+ else
+ {
+ zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u"
+ " mismatch with 16bit 'myasn' %u in open",
+ connection->host, open_recv->my_as, open_recv->my_as2) ;
+
+ bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ;
+ goto reject ;
+ } ;
+ } ;
+
+ /* Finally -- require the AS to be the configured AS */
+ if (open_recv->my_as != connection->session->as_peer)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s bad OPEN, remote AS is %u, expected %u",
+ connection->host, open_recv->my_as,
+ connection->session->as_peer) ;
+
+ bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ;
+ if (open_recv->can_as4)
+ bgp_notify_append_l(notification, open_recv->my_as) ;
+ else
+ bgp_notify_append_w(notification, open_recv->my_as) ;
+
+ goto reject ;
+ }
+
+ /*............................................................................
+ * It's OK ! Update the connection and issue event.
+ */
+ bgp_notify_free(notification) ; /* No further use for this */
+
+ holdtime = connection->session->open_send->holdtime ;
+
+ if (holdtime > open_recv->holdtime)
+ holdtime = open_recv->holdtime ; /* use smaller of theirs & ours */
+ if (holdtime < 3)
+ holdtime = 0 ; /* no slip ups */
+
+ connection->hold_timer_interval = holdtime ;
+ connection->keepalive_timer_interval = holdtime / 3 ;
+
+ connection->as4 = open_recv->can_as4 ;
+ connection->route_refresh = open_recv->can_r_refresh ;
+ connection->orf_prefix = open_recv->can_orf_prefix ;
+
+ bgp_fsm_open_received(connection) ;
+
+ return ;
+
+ /*............................................................................
+ * Failed. Reject the OPEN with the required notification.
+ */
+reject:
+ bgp_fsm_exception(connection, bgp_session_eOpen_reject, notification);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set notification to BGP_NOMC_OPEN/BGP_NOMS_O_BAD_ID and set the data part
+ * to be the given bad id.
+ *
+ * Create notification if required.
+ */
+extern bgp_notify
+bgp_msg_noms_o_bad_id(bgp_notify notification, bgp_id_t id)
+{
+ notification = bgp_notify_reset(notification, BGP_NOMC_OPEN,
+ BGP_NOMS_O_BAD_ID) ;
+ bgp_notify_append_data(notification, &id, 4) ;
+
+ return notification ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset notification to BGP_NOMC_OPEN with the given subcode, and return -1.
+ */
+static int
+bgp_msg_open_error(bgp_notify notification, bgp_nom_subcode_t subcode)
+{
+ bgp_notify_reset(notification, BGP_NOMC_OPEN, subcode) ;
+ return -1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset notification to BGP_NOMC_OPEN/BGP_NOMS_UNSPECIFIC, and return -1.
+ */
+static int
+bgp_msg_open_invalid(bgp_notify notification)
+{
+ return bgp_msg_open_error(notification, BGP_NOMS_UNSPECIFIC) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add unsupported capability to notification.
+ *
+ * The sr points at the start of the capability value.
+ */
+static void
+bgp_msg_capability_unsupported(bgp_notify notification, sucker sr)
+{
+ ptr_t p_cap ;
+ int cap_len ;
+
+ if (notification->subcode != BGP_NOMS_O_CAPABILITY)
+ bgp_notify_reset(notification, BGP_NOMC_OPEN, BGP_NOMS_O_CAPABILITY) ;
+
+ cap_len = suck_total(sr) ;
+ p_cap = suck_start(sr) - BGP_CAP_MIN_L ;
+
+ assert(*(p_cap + 1) == cap_len) ;
+
+ bgp_notify_append_data(notification, p_cap, BGP_CAP_MIN_L + cap_len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Parse OPEN message options part.
+ *
+ * Expects the notification to be set up: BGP_NOMC_OPEN, BGP_NOMS_UNSPECIFIC
+ * with no data, yet.
+ *
+ * Returns: -1 => error -- see notification
+ * 0 => OK, no capabilities
+ * 1 => OK, at least one capability
+ */
+static int
+bgp_msg_open_option_parse (bgp_connection connection, bgp_notify notification,
+ sucker sr)
+{
+ int ret, capability ;
+ int left ;
+ bgp_session session = connection->session ;
+ bgp_open_state open_send = session->open_send ;
+ bgp_open_state open_recv = connection->open_recv ;
+
+ /* Prepare to read BGP OPEN message options */
+
+ ret = 0 ; /* OK so far */
+ capability = 0 ; /* No capability option, yet */
+
+ while ((left = suck_left(sr)) > 0)
+ {
+ struct sucker ssr ;
+ u_char opt_type ;
+ u_char opt_length ;
+
+ /* Fetch option type and length, if possible */
+ if ((left -= 2) > 0)
+ {
+ opt_type = suck_b(sr);
+ opt_length = suck_b(sr);
+ left -= opt_length ;
+ }
+ else
+ {
+ opt_type = 0 ; /* ensure initialised */
+ opt_length = 0 ;
+ }
+
+ /* Must not have exceeded available bytes */
+ if (left < 0)
+ {
+ zlog_info ("%s Option length error", connection->host);
+ return bgp_msg_open_invalid(notification) ;
+ }
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcvd OPEN w/ optional parameter type %u (%s) len %u",
+ connection->host, opt_type,
+ opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" :
+ opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown",
+ opt_length);
+
+ suck_push(sr, opt_length, &ssr) ;
+
+ switch (opt_type)
+ {
+ case BGP_OPT_AUTH:
+ return bgp_msg_open_error(notification, BGP_NOMS_O_AUTH) ;
+
+ case BGP_OPT_CAPS:
+ capability = 1 ; /* does => can */
+
+ ret = bgp_msg_capability_option_parse(connection, notification, sr) ;
+ if (ret < 0)
+ return bgp_msg_open_invalid(notification) ;
+
+ break;
+
+ default:
+ return bgp_msg_open_error(notification, BGP_NOMS_O_OPTION) ;
+ } ;
+
+ suck_pop(sr, &ssr) ;
+ } ;
+
+ /* All OPEN option is parsed.
+ *
+ * Do "strict" capability checks:
+ *
+ * 1) if there were any unknown capabilities, or any AFI/SAFI which are
+ * unknown or are not configured, then that is now an error.
+ *
+ * 2) the local AFI/SAFI must be the same as the remote AFI/SAFI.
+ *
+ * NB: cap_override and cap_strict are mutually exclusive
+ *
+ * TODO: what about graceful restart and no CAP-MP ??
+ */
+ if (session->cap_strict)
+ {
+ /* Treat any unsupported capability as an error. */
+ if (bgp_notify_get_subcode(notification) == BGP_NOMS_O_CAPABILITY)
+ return -1 ;
+
+ /* Check local AFI/SAFI set is same as the remote one. */
+ if (open_recv->can_mp_ext != open_send->can_mp_ext)
+ return bgp_msg_open_error(notification, BGP_NOMS_O_CAPABILITY) ;
+ } ;
+
+ /* If there were any capabilities, and not going to override AFI/SAFI,
+ * then check that there is at least one AFI/SAFI in common.
+ */
+ if (capability && ! session->cap_override)
+ {
+ if ((open_recv->can_mp_ext & open_send->can_mp_ext) == 0)
+ {
+ plog_err (connection->log, "%s [Error] No common capability",
+ connection->host) ;
+ if (bgp_notify_get_subcode(notification) != BGP_NOMS_O_CAPABILITY)
+ bgp_msg_open_error(notification, BGP_NOMS_O_CAPABILITY) ;
+
+ return -1 ;
+ }
+ } ;
+
+ return connection->open_recv->can_capability = capability ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * From IANA "Capability Codes (last updated 2009-08-04) Reference: [RFC5492]"
+ *
+ * Range Registration Procedures
+ * --------- --------------------------
+ * 1- 63 IETF Review
+ * 64-127 First Come First Served
+ * 128-255 Reserved for Private Use (IANA does not assign)
+ *
+ * 1 Multiprotocol Extensions for BGP-4 [RFC2858]
+ * 2 Route Refresh Capability for BGP-4 [RFC2918]
+ * 3 Outbound Route Filtering Capability [RFC5291]
+ * 4 Multiple routes to a destination capability [RFC3107]
+ * 5 Extended Next Hop Encoding [RFC5549]
+ * 64 Graceful Restart Capability [RFC4724]
+ * 65 Support for 4-octet AS number capability [RFC4893]
+ * 66 Deprecated (2003-03-06)
+ * 67 Support for Dynamic Capability (capability specific) [Chen]
+ * 68 Multisession BGP Capability [Appanna]
+ * 69 ADD-PATH Capability [draft-ietf-idr-add-paths]
+ *
+ * 66 is, in fact, for draft-ietf-idr-dynamic-cap-02 of the Support for
+ * Dynamic Capability (capability specific)
+ *
+ * Supported:
+ *
+ * 1 BGP_CAN_MP_EXT -- Multiprotocol Extensions
+ * 2 BGP_CAN_R_REFRESH -- Route Refresh
+ * 3 BGP_CAN_ORF -- Outbound Route Filtering
+ * 64 BGP_CAN_G_RESTART -- Graceful Restart
+ * 65 BGP_CAN_AS4 -- AS4
+ * 66 BGP_CAN_DYNAMIC_CAP_old -- Dynamic Capability (old form)
+ * 128 BGP_CAN_R_REFRESH_pre -- pre-RFC Route Refresh
+ * 130 BGP_CAN_ORF_pre -- pre-RFC Outbound Route Filtering
+ */
+
+CONFIRM(BGP_CAP_MPE_L == sizeof (struct capability_mp_data)) ;
+CONFIRM(BGP_CAP_RRF_L == CAPABILITY_CODE_REFRESH_LEN) ;
+CONFIRM(BGP_CAP_ORFE_MIN_L == sizeof (struct capability_orf_entry)) ;
+CONFIRM(BGP_CAP_GR_MIN_L == sizeof (struct capability_gr)) ;
+CONFIRM(BGP_CAP_AS4_L == CAPABILITY_CODE_AS4_LEN) ;
+CONFIRM(BGP_CAP_DYN_L == CAPABILITY_CODE_DYNAMIC_LEN) ;
+
+CONFIRM(BGP_CAN_MP_EXT == CAPABILITY_CODE_MP) ;
+CONFIRM(BGP_CAN_R_REFRESH == CAPABILITY_CODE_REFRESH) ;
+CONFIRM(BGP_CAN_ORF == CAPABILITY_CODE_ORF) ;
+CONFIRM(BGP_CAN_G_RESTART == CAPABILITY_CODE_RESTART) ;
+CONFIRM(BGP_CAN_AS4 == CAPABILITY_CODE_AS4) ;
+CONFIRM(BGP_CAN_DYNAMIC_CAP_old == CAPABILITY_CODE_DYNAMIC) ;
+CONFIRM(BGP_CAN_R_REFRESH_pre == CAPABILITY_CODE_REFRESH_OLD) ;
+CONFIRM(BGP_CAN_ORF_pre == CAPABILITY_CODE_ORF_OLD) ;
+
+/* TODO: clarify value for BGP_CAN_DYNAMIC_CAP !! */
+
+/* Minimum sizes for length field of each cap (so not inc. the header) */
+static const unsigned cap_minsizes[] =
+{
+ [BGP_CAN_MP_EXT] = BGP_CAP_MPE_L,
+ [BGP_CAN_R_REFRESH] = BGP_CAP_RRF_L,
+ [BGP_CAN_ORF] = BGP_CAP_ORFE_MIN_L,
+ [BGP_CAN_G_RESTART] = BGP_CAP_GR_MIN_L,
+ [BGP_CAN_AS4] = BGP_CAP_AS4_L,
+ [BGP_CAN_DYNAMIC_CAP_old] = BGP_CAP_DYN_L,
+ [BGP_CAN_DYNAMIC_CAP] = BGP_CAP_DYN_L,
+ [BGP_CAN_R_REFRESH_pre] = BGP_CAP_RRF_L,
+ [BGP_CAN_ORF_pre] = BGP_CAP_ORFE_MIN_L,
+} ;
+
+static const unsigned cap_maxsizes[] =
+{
+ [BGP_CAN_MP_EXT] = BGP_CAP_MPE_L,
+ [BGP_CAN_R_REFRESH] = BGP_CAP_RRF_L,
+ [BGP_CAN_ORF] = BGP_CAP_MAX_L, /* variable */
+ [BGP_CAN_G_RESTART] = BGP_CAP_MAX_L, /* variable */
+ [BGP_CAN_AS4] = BGP_CAP_AS4_L,
+ [BGP_CAN_DYNAMIC_CAP_old] = BGP_CAP_DYN_L,
+ [BGP_CAN_DYNAMIC_CAP] = BGP_CAP_DYN_L,
+ [BGP_CAN_R_REFRESH_pre] = BGP_CAP_RRF_L,
+ [BGP_CAN_ORF_pre] = BGP_CAP_MAX_L, /* variable */
+} ;
+
+/* Forward references for parsing individual capabilities, return -1 if the
+ * capability is malformed or contains invalid values.
+ */
+static int
+bgp_msg_capability_mp(bgp_connection connection, sucker sr) ;
+
+static int
+bgp_msg_capability_orf (bgp_connection connection, uint8_t cap_code, sucker sr);
+
+static int
+bgp_msg_capability_restart (bgp_connection connection, sucker sr) ;
+
+static int
+bgp_msg_capability_as4 (bgp_connection connection, sucker sr) ;
+
+/*------------------------------------------------------------------------------
+ * Set notification to malformed/invalid.
+ *
+ * Returns -1 !
+ */
+static int
+bgp_msg_capability_bad(bgp_notify notification)
+{
+ bgp_notify_reset(notification, BGP_NOMC_OPEN, BGP_NOMS_UNSPECIFIC) ;
+ return -1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Parse given capability option -- may contain multiple capabilities.
+ *
+ * Adjusts the open_recv open state according to the capabilities seen.
+ *
+ * Collects unsupported capabilities in the notification, where a
+ * BGP_NOMS_O_CAPABILITY message will be created. So, at the end of the
+ * process, can tell if there are any unsupported capabilities.
+ *
+ * The unsupported capabilities will be:
+ *
+ * * MP Extensions AFI/SAFI which are unknown or are not configured at
+ * this end.
+ *
+ * * any unknown capabilities < 128
+ *
+ * If an invalid or malformed capability is found, the notification is set
+ * BGP_NOMS_UNSPECIFIC -- see bgp_msg_capability_bad() above.
+ *
+ * Returns: 0 => OK (but may have collected some unsupported capabilities)
+ * -1 => invalid or malformed
+ */
+static int
+bgp_msg_capability_option_parse (bgp_connection connection,
+ bgp_notify notification, sucker sr)
+{
+ int ret, left ;
+ bgp_open_state open_recv = connection->open_recv ;
+
+ while ((left = suck_left(sr)) > 0)
+ {
+ struct sucker ssr ;
+ int cap_code ;
+ unsigned cap_length ;
+
+ /* We need at least capability code and capability length. */
+ if ((left -= 2) >= 0)
+ {
+ cap_code = suck_b(sr);
+ cap_length = suck_b(sr);
+ left -= cap_length ;
+ }
+
+ if (left < 0)
+ {
+ zlog_info ("%s Capability length error (< header)", connection->host);
+ return bgp_msg_capability_bad(notification) ;
+ } ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s OPEN has %s capability (%u), length %u",
+ connection->host,
+ LOOKUP (capcode_str, cap_code),
+ cap_code, cap_length) ;
+
+ /* Length sanity check, type-specific, for known capabilities */
+ switch (cap_code)
+ {
+ case BGP_CAN_MP_EXT:
+ case BGP_CAN_R_REFRESH:
+ case BGP_CAN_ORF:
+ case BGP_CAN_G_RESTART:
+ case BGP_CAN_AS4:
+ case BGP_CAN_DYNAMIC_CAP:
+ case BGP_CAN_R_REFRESH_pre:
+ case BGP_CAN_ORF_pre:
+ /* Check length. */
+ if ( (cap_length < cap_minsizes[cap_code]) ||
+ (cap_length > cap_maxsizes[cap_code]) )
+ {
+ const char* tag = "" ;
+ if (cap_minsizes[cap_code] != cap_maxsizes[cap_code])
+ tag = "at least " ;
+ zlog_info ("%s %s Capability length error: got %u,"
+ " expected %s%u",
+ connection->host,
+ LOOKUP (capcode_str, cap_code),
+ cap_length, tag,
+ (unsigned) cap_minsizes[cap_code]) ;
+ return bgp_msg_capability_bad(notification) ;
+ /* invalid: stop dead */
+ } ;
+ break ;
+ /* we deliberately ignore unknown codes, see below */
+ default:
+ break ;
+ } ;
+
+ /* By this point the capability length is exactly right for the
+ * fixed length capabilities, at least the minimum length for the rest.
+ * Also, then capability fits within the capability option.
+ */
+ suck_push(sr, cap_length, &ssr) ;
+
+ ret = 0 ;
+ switch (cap_code)
+ {
+ case BGP_CAN_MP_EXT:
+ /* Ignore capability when override-capability is set.
+ *
+ * NB: bgp_msg_capability_mp() returns > 0 if the AFI/SAFI is not
+ * recognised or is not one of the configured ones.
+ */
+ if (! connection->session->cap_override)
+ ret = bgp_msg_capability_mp(connection, sr);
+ break;
+
+ case BGP_CAN_R_REFRESH:
+ open_recv->can_r_refresh |= bgp_form_rfc ;
+ break ;
+
+ case BGP_CAN_R_REFRESH_pre:
+ open_recv->can_r_refresh |= bgp_form_pre;
+ break;
+
+ case BGP_CAN_ORF:
+ case BGP_CAN_ORF_pre:
+ ret = bgp_msg_capability_orf(connection, cap_code, sr) ;
+ break;
+
+ case BGP_CAN_G_RESTART:
+ ret = bgp_msg_capability_restart(connection, sr) ;
+ break;
+
+ case BGP_CAN_DYNAMIC_CAP_old:
+ open_recv->can_dynamic = 1;
+ break;
+
+ case BGP_CAN_AS4:
+ ret = bgp_msg_capability_as4(connection, sr) ;
+ break;
+
+ default:
+ if (cap_code >= 128)
+ {
+ /* We don't send Notification for unknown vendor specific
+ capabilities. It seems reasonable for now... */
+ zlog_warn ("%s Vendor specific capability %d",
+ connection->host, cap_code);
+ }
+ else
+ {
+ zlog_warn ("%s unrecognized capability code: %d - ignored",
+ connection->host, cap_code) ;
+ ret = 1 ; /* collect unsupported capability */
+ }
+
+ /* Add given unknown capability and its value */
+ bgp_open_state_unknown_add(open_recv, cap_code,
+ suck_start(sr), suck_total(sr)) ;
+ }
+
+ if (ret < 0)
+ return bgp_msg_capability_bad(notification) ;
+
+ if (ret > 0)
+ bgp_msg_capability_unsupported(notification, sr) ;
+
+ suck_pop(sr, &ssr) ;
+ }
+
+ return 0 ;
+} ;
+
+static qafx_bit_t
+bgp_msg_afi_safi(sucker sr, struct iAFI_SAFI* mp)
+{
+ mp->afi = suck_w(sr) ;
+ suck_x(sr) ;
+ mp->safi = suck_b(sr) ;
+
+ return qafx_bit_from_iAFI_iSAFI(mp->afi, mp->safi) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process value of Multiprotocol Extensions -- BGP_CAN_MP_EXT -- RFC2858
+ *
+ * Capability is: AFI -- word
+ * reserved -- byte
+ * SAFI -- byte
+ *
+ * This is a fixed length capability, so that's been dealt with.
+ *
+ * Treats: invalid AFI/SAFI combinations )
+ * unknown AFI/SAFI combinations ) unsupported capability
+ * unsupported AFI/SAFI combinations )
+ *
+ * Sets any known AFI/SAFI combination in open_recv->can_mp_ext.
+ *
+ * Returns: 0 => OK -- AFI/SAFI is in the open_sent set
+ * 1 => -- AFI/SAFI is known, but not in the open_sent set
+ * 2 => -- AFI/SAFI is not known, may be invalid
+ */
+static int
+bgp_msg_capability_mp(bgp_connection connection, sucker sr)
+{
+ struct iAFI_SAFI mp ;
+ qafx_bit_t qb ;
+ bgp_cap_afi_safi cap ;
+
+ qb = bgp_msg_afi_safi(sr, &mp) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u",
+ connection->host, mp.afi, mp.safi) ;
+
+ cap = bgp_open_state_afi_safi_add(connection->open_recv, mp.afi, mp.safi,
+ (qb != 0), BGP_CAN_MP_EXT) ;
+ if (qb == 0)
+ {
+ zlog_warn ("Unknown afi/safi combination (%u/%u)", mp.afi, mp.safi) ;
+ return 2 ;
+ } ;
+
+ /* Now can register the capability */
+ connection->open_recv->can_mp_ext |= qb;
+
+ /* Return: 0 => is in the open_send set
+ * 1 => is not
+ */
+ return ((connection->session->open_send->can_mp_ext & qb) != 0) ? 0 : 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process value of Outbound Route Filtering -- BGP_CAN_ORF -- RFC5291
+ *
+ * Must have at least one ORF Entry, but may have several and each is of
+ * variable length.
+ *
+ * Requirement for at least one entry has already been checked.
+ *
+ * Returns: 0 => OK
+ * -1 => malformed !
+ *
+ * NB: treats multiple ORF capabilities and multiple entries as additive.
+ * Does not track what AFI/SAFI and ORF types have been declared.
+ */
+
+static int bgp_msg_capability_orf_entry(bgp_connection connection,
+ uint8_t cap_code, sucker sr) ;
+
+static int
+bgp_msg_capability_orf(bgp_connection connection, uint8_t cap_code, sucker sr)
+{
+ while (suck_left(sr) > 0)
+ {
+ if (suck_left(sr) < BGP_CAP_ORFE_MIN_L)
+ {
+ zlog_info ("%s ORF Capability length error,"
+ " Cap length left %u",
+ connection->host, suck_left(sr)) ;
+ return -1;
+ } ;
+
+ if (bgp_msg_capability_orf_entry (connection, cap_code, sr) == -1)
+ return -1;
+ } ;
+
+ return 0;
+} ;
+
+/* Process ORF Entry:
+ *
+ * Entry is: AFI -- word
+ * reserved -- byte
+ * SAFI -- byte
+ * number -- byte
+ * type -- byte ) repeated "number" times
+ * send/recv -- byte )
+ *
+ * Returns: 0 => OK
+ * -1 => malformed
+ */
+static int
+bgp_msg_capability_orf_entry(bgp_connection connection, uint8_t cap_code,
+ sucker sr)
+{
+ iAFI_SAFI_t mp ;
+ int number ;
+ int length ;
+ sucker_t ssr ;
+ bgp_cap_afi_safi cap ;
+
+ bgp_open_state open_recv = connection->open_recv ;
+ qafx_bit_t qb ;
+
+ /* ORF Entry header */
+ qb = bgp_msg_afi_safi(sr, &mp) ;
+ number = suck_b(sr) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s ORF Cap entry for afi/safi: %u/%u %d types",
+ connection->host, mp.afi, mp.safi, number) ;
+
+ /* Check AFI and SAFI. */
+ if (qb == 0)
+ zlog_info ("%s Addr-family %d/%d not known."
+ " Ignoring the ORF capability",
+ connection->host, mp.afi, mp.safi) ;
+
+ /* Validate number field */
+ length = number * BGP_CAP_ORFT_L ;
+
+ if (suck_left(sr) < length)
+ {
+ zlog_info ("%s ORF Capability entry length error,"
+ " Cap length left %u, num %u",
+ connection->host, suck_left(sr), number) ;
+ return -1;
+ } ;
+
+ /* Process the supported ORF types */
+
+ suck_push(sr, length, &ssr) ;
+
+ while (number--)
+ {
+ qafx_bit_t qbs ;
+
+ uint8_t type = suck_b(sr) ;
+ uint8_t mode = suck_b(sr) ;
+
+ /* ORF Mode error check */
+ switch (mode)
+ {
+ case BGP_CAP_ORFT_M_RECV:
+ case BGP_CAP_ORFT_M_SEND:
+ case BGP_CAP_ORFT_M_BOTH:
+ break;
+ default:
+ zlog_info ("%s Invalid send/receive 'mode' value %d"
+ " in ORF capability",
+ connection->host, mode) ;
+ return -1 ;
+ } ;
+
+ /* ORF Type and afi/safi sexing */
+ qbs = 0 ;
+ switch (cap_code)
+ {
+ case BGP_CAN_ORF:
+ switch (type)
+ {
+ case BGP_CAP_ORFT_T_PFIX:
+ open_recv->can_orf_prefix |= bgp_form_rfc ;
+ qbs = qafx_ipv4_unicast_bit
+ | qafx_ipv4_multicast_bit
+ | qafx_ipv6_unicast_bit ;
+ break ;
+ default:
+ break ;
+ }
+ break;
+ case BGP_CAN_ORF_pre:
+ switch (type)
+ {
+ case BGP_CAP_ORFT_T_PFIX_pre:
+ open_recv->can_orf_prefix |= bgp_form_pre ;
+ qbs = qafx_ipv4_unicast_bit
+ | qafx_ipv4_multicast_bit
+ | qafx_ipv6_unicast_bit ;
+ break ;
+ default:
+ break ;
+ }
+ break ;
+ default:
+ break ;
+ } ;
+
+ cap = bgp_open_state_afi_safi_add(connection->open_recv, mp.afi, mp.safi,
+ (qb != 0), cap_code) ;
+ cap->caps.orf.known_orf_type = (qbs != 0) ;
+ cap->caps.orf.type = type ;
+ cap->caps.orf.send = ((mode & BGP_CAP_ORFT_M_SEND) != 0) ;
+ cap->caps.orf.recv = ((mode & BGP_CAP_ORFT_M_RECV) != 0) ;
+
+ if ((qbs & qb) == 0)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s Addr-family %d/%d has"
+ " ORF type/mode %d/%d not supported",
+ connection->host, mp.afi, mp.safi, type, mode) ;
+ continue ;
+ } ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s OPEN has %s ORF capability"
+ " as %s for afi/safi: %d/%d",
+ connection->host, LOOKUP (orf_type_str, type),
+ LOOKUP (orf_mode_str, mode),
+ mp.afi, mp.safi) ;
+
+ if (mode & BGP_CAP_ORFT_M_SEND)
+ open_recv->can_orf_prefix_send |= qb ;
+ if (mode & BGP_CAP_ORFT_M_RECV)
+ open_recv->can_orf_prefix_recv |= qb ;
+
+ confirm((BGP_CAP_ORFT_M_RECV & BGP_CAP_ORFT_M_SEND) == 0) ;
+
+ confirm((BGP_CAP_ORFT_M_SEND & BGP_CAP_ORFT_M_SEND) != 0) ;
+ confirm((BGP_CAP_ORFT_M_BOTH & BGP_CAP_ORFT_M_SEND) != 0) ;
+
+ confirm((BGP_CAP_ORFT_M_RECV & BGP_CAP_ORFT_M_RECV) != 0) ;
+ confirm((BGP_CAP_ORFT_M_BOTH & BGP_CAP_ORFT_M_RECV) != 0) ;
+ } ;
+
+ /* Should now be exactly at the end. */
+ suck_pop_exact(sr, &ssr) ;
+
+ return 0;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process value of Graceful Restart capability -- BGP_CAN_G_RESTART -- RFC2918
+ *
+ * Capability is: time -- word
+ * AFI -- word )
+ * SAFI -- byte ) repeated 0 or more times
+ * flag -- byte )
+ *
+ * This is a variable length capability, minimum size already checked.
+ *
+ * The sr covers from the start to the end of the capability value.
+ *
+ * Returns: 0 => OK
+ * -1 => malformed !
+ *
+ * TODO: RFC 4724 suggests "implicit" IPv4/Unicast if no CAP-MP
+ * TODO: RFC 4760 says MUST CAP-MP if propose to use CAP-MP !
+ *
+ */
+static int
+bgp_msg_capability_restart (bgp_connection connection, sucker sr)
+{
+ bgp_open_state open_recv = connection->open_recv;
+ u_int16_t restart_flag_time ;
+ int length ;
+ bgp_cap_afi_safi cap ;
+
+ length = suck_left(sr) ; /* total length of value, for reporting */
+
+ /* RFC4724: "If more than one instance of the Graceful Restart Capability
+ * is carried in the capability advertisement, the receiver of
+ * the advertisement MUST ignore all but the last instance..."
+ */
+
+ open_recv->can_g_restart = 1;
+
+ /* Deal with the restart time and restarted flag */
+ restart_flag_time = suck_w(sr) ;
+
+ open_recv->has_restarted = (restart_flag_time & BGP_CAP_GR_T_R_FLAG) != 0 ;
+ open_recv->restart_time = restart_flag_time & BGP_CAP_GR_T_MASK ;
+
+ open_recv->can_preserve = 0 ;
+ open_recv->has_preserved = 0 ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ {
+ zlog_debug ("%s OPEN has Graceful Restart capability", connection->host);
+ zlog_debug ("%s Peer has%srestarted. Restart Time : %d",
+ connection->host, open_recv->has_restarted ? " " : " not ",
+ open_recv->restart_time);
+ } ;
+
+ if ((suck_left(sr) % BGP_CAP_GRE_L) != 0)
+ {
+ zlog_info ("%s Graceful Restart Capability length error,"
+ " Cap length %u",
+ connection->host, length);
+ return -1;
+ } ;
+
+ while (suck_left(sr) > 0)
+ {
+ iAFI_SAFI_t mp ;
+ uint8_t flags ;
+ qafx_bit_t qb ;
+ int has_preserved ;
+
+ mp.afi = suck_w(sr) ;
+ mp.safi = suck_b(sr) ;
+ flags = suck_b(sr) ;
+
+ qb = qafx_bit_from_iAFI_iSAFI(mp.afi, mp.safi) ;
+ has_preserved = ((flags & BGP_CAP_GRE_F_FORW) != 0) ;
+
+ cap = bgp_open_state_afi_safi_add(connection->open_recv, mp.afi, mp.safi,
+ (qb != 0), BGP_CAN_G_RESTART) ;
+ cap->caps.gr.has_preserved = has_preserved ;
+
+ if (qb == 0)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported."
+ " Ignore the Graceful Restart capability",
+ connection->host, mp.afi, mp.safi);
+ }
+ else
+ {
+ /* Now can register the capability */
+
+ if (connection->session->open_send->can_mp_ext & qb)
+ {
+ open_recv->can_preserve |= qb ;
+ open_recv->has_preserved |= (has_preserved ? qb : 0) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s Address family %s is%spreserved",
+ connection->host,
+ afi_safi_print (mp.afi, mp.safi),
+ (has_preserved ? " " : " not ")) ;
+ }
+ else
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled."
+ " Ignore the Graceful Restart capability",
+ connection->host, mp.afi, mp.safi);
+ } ;
+ } ;
+ } ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process value of AS4 capability -- BGP_CAN_AS4 -- RFC4893
+ *
+ * Capability is: ASN -- long word (4 bytes)
+ *
+ * This is a fixed length capability, so that's been dealt with.
+ *
+ * Validation of ASN and cross-check against my_as2, done elsewhere.
+ *
+ * Returns: 0 => OK
+ */
+static int
+bgp_msg_capability_as4 (bgp_connection connection, sucker sr)
+{
+ bgp_open_state open_recv = connection->open_recv ;
+
+ open_recv->can_as4 = 1 ;
+ open_recv->my_as = suck_l(sr) ;
+
+ if (BGP_DEBUG (as4, AS4))
+ zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u",
+ connection->host, open_recv->my_as) ;
+
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * BGP UPDATE message
+ *
+ * NB: requires the session to be locked (connection-wise) and not NULL.
+ */
+static void
+bgp_msg_update_receive (bgp_connection connection, bgp_size_t body_size)
+{
+ /* Must be prepared to receive "update" like messages */
+ if (bgp_fsm_pre_update(connection) != 0)
+ {
+ plog_err(connection->log,
+ "%s [Error] Update message received while in %s State",
+ connection->host, LOOKUP(bgp_status_msg, connection->state)) ;
+ return ;
+ } ;
+
+ ++connection->session->stats.update_in ;
+ connection->session->stats.update_time = bgp_clock() ;
+
+ /* PRO TEM: pass raw update message across to Routing Engine */
+ /* TODO: decode update messages in the BGP Engine. */
+ bgp_session_update_recv(connection->session, connection->ibuf, body_size);
+}
+
+/*==============================================================================
+ * BGP KEEPALIVE message
+ *
+ * NB: requires the session to be locked (connection-wise) and not NULL.
+ */
+static void
+bgp_msg_keepalive_receive (bgp_connection connection, bgp_size_t body_size)
+{
+ ++connection->session->stats.keepalive_in ;
+
+ if (BGP_DEBUG (keepalive, KEEPALIVE))
+ zlog_debug ("%s KEEPALIVE rcvd", connection->host);
+
+ if (body_size == 0)
+ bgp_fsm_keepalive_received(connection) ;
+ else
+ bgp_msg_header_bad_len(connection, BGP_MT_KEEPALIVE, body_size) ;
+} ;
+
+/*==============================================================================
+ * BGP NOTIFICATION message
+ *
+ * NB: requires the session to be locked (connection-wise) and not NULL.
+ */
+static void
+bgp_msg_notify_receive (bgp_connection connection, bgp_size_t body_size)
+{
+ bgp_notify notification ;
+
+ bgp_nom_code_t code = stream_getc (connection->ibuf);
+ bgp_nom_subcode_t subcode = stream_getc (connection->ibuf);
+
+ ++connection->session->stats.notify_in ;
+
+ notification = bgp_notify_new_with_data(code, subcode,
+ stream_pnt(connection->ibuf), body_size - 2) ;
+ bgp_notify_set_received(notification) ;
+
+ bgp_notify_print(connection->session->peer, notification) ; /* Logging */
+
+ bgp_fsm_notification_exception(connection, notification) ;
+} ;
+
+/*==============================================================================
+ * BGP ROUTE-REFRESH message
+ *
+ * NB: requires the session to be locked (connection-wise) and not NULL.
+ */
+static int
+bgp_msg_orf_recv(bgp_connection connection, bgp_route_refresh rr,
+ qafx_bit_t qb, sucker sr) ;
+static int
+bgp_msg_orf_prefix_recv(orf_prefix orfpe, qafx_bit_t qb, sucker sr) ;
+
+/*------------------------------------------------------------------------------
+ * Process BGP ROUTE-REFRESH message
+ *
+ * This may contain ORF stuff !
+ */
+static void
+bgp_msg_route_refresh_receive(bgp_connection connection, bgp_size_t body_size)
+{
+ struct iAFI_SAFI mp ;
+ sucker_t ssr ;
+ qafx_bit_t qb ;
+ bgp_route_refresh rr ;
+ unsigned form ;
+ int ret ;
+
+ ++connection->session->stats.refresh_in ;
+
+ /* If peer does not have the capability, treat as bad message type */
+
+ switch (connection->msg_type)
+ {
+ case BGP_MT_ROUTE_REFRESH:
+ form = bgp_form_rfc ;
+ break ;
+ case BGP_MT_ROUTE_REFRESH_pre:
+ form = bgp_form_pre ;
+ break ;
+ default: /* should not happen, really */
+ form = 0 ;
+ break ;
+ } ;
+
+ if ((connection->route_refresh & form) == 0)
+ {
+ bgp_msg_header_bad_type(connection, connection->msg_type) ;
+ return ;
+ } ;
+
+ /* Must be prepared to receive "update" like messages */
+ if (connection->state != bgp_fsm_sEstablished)
+ {
+ plog_err(connection->log,
+ "%s [Error] Route-Refresh message received while in %s State",
+ connection->host, LOOKUP(bgp_status_msg, connection->state)) ;
+ return ;
+ } ;
+
+ /* Set about parsing the message */
+
+ dassert(stream_get_left(connection->ibuf) == body_size) ;
+ suck_init(&ssr, stream_pnt(connection->ibuf), body_size) ;
+
+ /* Start with AFI, reserved, SAFI */
+ qb = bgp_msg_afi_safi(&ssr, &mp) ;
+
+ qb &= qafx_ipv4_unicast_bit | qafx_ipv4_multicast_bit |
+ qafx_ipv6_unicast_bit | qafx_ipv6_multicast_bit |
+ qafx_ipv4_mpls_vpn_bit ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d%s",
+ connection->host, mp.afi, mp.safi,
+ (qb == 0) ? " -- unknown combination" : "") ;
+
+ rr = bgp_route_refresh_new(mp.afi, mp.safi, 0) ;
+
+ /* If there are any ORF entries, time to suck them up now. */
+ ret = 0 ;
+
+ if (suck_left(&ssr) != 0)
+ {
+ uint8_t when_to_refresh ;
+ bool defer = 0 ;
+
+ when_to_refresh = suck_b(&ssr) ;
+
+ switch (when_to_refresh)
+ {
+ case BGP_ORF_WTR_IMMEDIATE:
+ defer = 0 ;
+ break ;
+ case BGP_ORF_WTR_DEFER:
+ defer = 1 ;
+ break ;
+ default:
+ zlog_info ("%s ORF route refresh invalid 'when' value %d"
+ " (AFI/SAFI %d/%d)",
+ connection->host, when_to_refresh, rr->afi, rr->safi) ;
+ ret = -1 ;
+ break ;
+ } ;
+
+ if (ret >= 0)
+ {
+ bgp_route_refresh_set_orf_defer(rr, defer) ;
+
+ /* After the when to refresh, expect 1 or more ORFs */
+ do
+ {
+ ret = bgp_msg_orf_recv(connection, rr, qb, &ssr) ;
+ } while ((ret == 0) && (suck_left(&ssr) != 0)) ;
+ } ;
+ } ;
+
+ if (ret < 0)
+ {
+ bgp_fsm_exception(connection, bgp_session_eInvalid_msg,
+ bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_UNSPECIFIC)) ;
+ return ;
+ }
+
+ bgp_session_route_refresh_recv(connection->session, rr) ;
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process ORF Type and following ORF Entries.
+ *
+ * Expects there to be at least one ORF entry -- that is, the length of the
+ * ORF Entries may not be 0.
+ *
+ * Returns: 0 => OK
+ * -1 => invalid or malformed
+ */
+static int
+bgp_msg_orf_recv(bgp_connection connection, bgp_route_refresh rr,
+ qafx_bit_t qb, sucker sr)
+{
+ sucker_t ssr ;
+ int left ;
+ uint8_t orf_type ;
+ bgp_size_t orf_len ;
+ int unknown ;
+ int form ;
+ int ret ;
+
+ /* Suck up the ORF type and the length of the entries that follow */
+ left = suck_left(sr) - BGP_ORF_MIN_L ;
+ if (left >= 0)
+ {
+ orf_type = suck_b(sr) ;
+ orf_len = suck_w(sr) ;
+ }
+ else
+ {
+ orf_type = 0 ; /* ensure initialised */
+ orf_len = 0 ;
+ } ;
+
+ /* The length may not be zero and may not exceed what there is left */
+ if ((orf_len == 0) || (left < orf_len))
+ {
+ zlog_info ("%s ORF route refresh length error: %d when %d left"
+ " (AFI/SAFI %d/%d, type %d length %d)",
+ connection->host, orf_len, left,
+ rr->afi, rr->safi, orf_type, orf_len) ;
+ return -1 ;
+ } ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcvd ORF type %d length %d",
+ connection->host, orf_type, orf_len);
+
+ /* Sex the ORF type */
+ form = bgp_form_none ;
+ unknown = 1 ;
+ switch (orf_type)
+ {
+ case BGP_ORF_T_PREFIX:
+ if ((connection->orf_prefix & bgp_form_rfc) != 0)
+ {
+ form = bgp_form_rfc ;
+ unknown = (qb != 0) ;
+ } ;
+ break ;
+
+ case BGP_ORF_T_PREFIX_pre:
+ if ((connection->orf_prefix & bgp_form_pre) != 0)
+ {
+ orf_type = BGP_ORF_T_PREFIX ;
+ form = bgp_form_pre ;
+ unknown = (qb != 0) ;
+ } ;
+ break ;
+
+ default:
+ break ;
+ } ;
+
+ /* Suck up the ORF entries. NB: orf_len != 0 */
+ suck_push(sr, orf_len, &ssr) ;
+
+ if (unknown)
+ bgp_orf_add_unknown(rr, orf_type, orf_len, suck_step(sr, orf_len)) ;
+ else
+ {
+ do
+ {
+ bool remove_all = 0 ;
+ bool remove = 0 ;
+ bool deny = 0 ;
+ uint8_t common ;
+
+ common = suck_b(sr) ;
+ switch (common & BGP_ORF_EA_MASK)
+ {
+ case BGP_ORF_EA_ADD:
+ break ;
+ case BGP_ORF_EA_REMOVE:
+ remove = 1 ;
+ break ;
+ case BGP_ORF_EA_RM_ALL:
+ remove_all = 1 ;
+ break ;
+ default:
+ zlog_info ("%s ORF route refresh invalid common byte: %u"
+ " (AFI/SAFI %d/%d, type %d length %d)",
+ connection->host, common,
+ rr->afi, rr->safi, orf_type, orf_len) ;
+ return -1 ;
+ } ;
+
+ deny = ((common & BGP_ORF_EA_DENY) != 0) ;
+
+ ret = 0 ;
+ if (remove_all)
+ bgp_orf_add_remove_all(rr, orf_type, form) ;
+ else
+ {
+ bgp_orf_entry orfe ;
+ orfe = bgp_orf_add(rr, orf_type, form, remove, deny) ;
+
+ switch (orf_type)
+ {
+ case BGP_ORF_T_PREFIX:
+ ret = bgp_msg_orf_prefix_recv(&orfe->body.orf_prefix, qb,
+ sr) ;
+ break ;
+
+ default:
+ zabort("Lost track of ORF type") ;
+ break ;
+ } ;
+
+ if (ret < 0)
+ {
+ zlog_info ("%s ORF route refresh invalid Prefix ORF entry"
+ " (AFI/SAFI %d/%d, type %d length %d)",
+ connection->host,
+ rr->afi, rr->safi, orf_type, orf_len) ;
+ return -1 ;
+ } ;
+ } ;
+
+ } while (suck_left(sr) > 0) ;
+ } ;
+
+ suck_pop_exact(sr, &ssr) ;
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process ORF Prefix entry, from after the common byte.
+ *
+ * This is for entries which are *not* remove-all
+ *
+ * Returns: 0 => OK
+ * -1 => invalid or malformed
+ */
+static int
+bgp_msg_orf_prefix_recv(orf_prefix orfpe, qafx_bit_t qb, sucker sr)
+{
+ sa_family_t paf ;
+ int left ;
+
+ assert(qb != 0) ;
+ paf = get_sa_family(qafx_num(qb)) ;
+
+ /* Must have the minimum Prefix ORF entry, less the common byte, left */
+ left = suck_left(sr) - (BGP_ORF_E_P_MIN_L - BGP_ORF_E_COM_L) ;
+ if (left >= 0)
+ {
+ uint8_t plen ;
+ uint8_t blen ;
+
+ orfpe->seq = suck_l(sr) ;
+ orfpe->ge = suck_b(sr) ; /* aka min */
+ orfpe->le = suck_b(sr) ; /* aka max */
+ plen = suck_b(sr) ;
+
+ memset(&orfpe->p, 0, sizeof(struct prefix)) ;
+
+ blen = (plen + 7) / 8 ;
+ if ((left -= blen) >= 0)
+ {
+ orfpe->p.family = paf ;
+ orfpe->p.prefixlen = plen ;
+ if (blen != 0)
+ {
+ if (blen <= prefix_blen(&orfpe->p))
+ memcpy(&orfpe->p.u.prefix, suck_step(sr, blen), blen) ;
+ else
+ left = -1 ;
+ } ;
+ } ;
+ } ;
+
+ return left ;
+} ;
+
+/*==============================================================================
+ * BGP CAPABILITY message -- Dynamic Capabilities
+ *
+ * NB: requires the session to be locked (connection-wise) and not NULL.
+ */
+static void bgp_msg_capability_receive(bgp_connection connection,
+ bgp_size_t body_size)
+{
+ ++connection->session->stats.dynamic_cap_in ;
+
+ return ;
+} ;
diff --git a/bgpd/bgp_msg_read.h b/bgpd/bgp_msg_read.h
new file mode 100644
index 00000000..b8b6235e
--- /dev/null
+++ b/bgpd/bgp_msg_read.h
@@ -0,0 +1,40 @@
+/* BGP Message Read Handling -- header
+ * Copyright (C) 2010 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 BGP_MSG_READ_H_
+#define BGP_MSG_READ_H_
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_notification.h"
+
+extern bgp_size_t
+bgp_msg_get_mlen(uint8_t* p, uint8_t* limit) ;
+
+extern int
+bgp_msg_check_header(bgp_connection connection);
+
+typedef void bgp_msg_handler(bgp_connection connection, bgp_size_t size) ;
+
+extern bgp_notify
+bgp_msg_noms_o_bad_id(bgp_notify notification, bgp_id_t id) ;
+
+#endif /* BGP_MSG_READ_H_ */
diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c
new file mode 100644
index 00000000..21e48d69
--- /dev/null
+++ b/bgpd/bgp_msg_write.c
@@ -0,0 +1,871 @@
+/* BGP message writing -- in BGP Engine
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * Recast for pthreaded bgpd: 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 <stdbool.h>
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_msg_write.h"
+#include "bgpd/bgp_route_refresh.h"
+
+#include "thread.h"
+#include "stream.h"
+#include "network.h"
+#include "prefix.h"
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "sockunion.h" /* for inet_ntop () */
+#include "linklist.h"
+#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"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_vty.h"
+
+/*==============================================================================
+ * BGP Engine BGP Message encoding and sending.
+ *
+ *
+ */
+
+/*==============================================================================
+ * NOTIFICATION and KEEPALIVE
+ */
+
+/*------------------------------------------------------------------------------
+ * Make NOTIFICATION message and dispatch.
+ *
+ * NB: the write buffers MUST have been flushed -- so demand success !
+ *
+ * Returns: 1 => written to wbuff -- qpselect will write from there
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ *
+ * NB: requires the session LOCKED -- connection-wise
+ */
+extern int
+bgp_msg_write_notification(bgp_connection connection, bgp_notify notification)
+{
+ struct stream *s = connection->obuf ;
+ int length;
+
+ ++connection->session->stats.notify_out ;
+
+ assert(notification != NULL) ;
+
+ bgp_notify_print(connection->session->peer, notification) ;
+
+ /* Make NOTIFY message header */
+ bgp_packet_set_marker (s, BGP_MSG_NOTIFY);
+
+ /* Set notify code and subcode */
+ stream_putc(s, bgp_notify_get_code(notification)) ;
+ stream_putc(s, bgp_notify_get_subcode(notification)) ;
+
+ /* Copy the data portion, if any. */
+ length = bgp_notify_get_length(notification) ;
+ if (length != 0)
+ stream_write(s, bgp_notify_get_data(notification), length) ;
+
+ /* Set and get BGP packet length. */
+ length = bgp_packet_set_size(s);
+
+ /* Set flag so that write_action raises required event when buffer becomes
+ * empty.
+ */
+ connection->notification_pending = 1 ;
+
+ /* Finally -- write the obuf away */
+ return bgp_connection_write(connection, s) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make KEEPALIVE message and dispatch.
+ *
+ * Generally, does nothing if the write buffer is not empty -- a KEEPALIVE is
+ * redundant if there is stuff waiting to go !
+ *
+ * KEEPALIVE is sent in response to OPEN, and that MUST be sent, hence the
+ * 'must_send' option.
+ *
+ * Returns: 1 => written to wbuff -- qpselect will write from there
+ * 0 => nothing written -- no need, buffer not empty !
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ *
+ * NB: requires the session LOCKED -- connection-wise
+ */
+extern int
+bgp_msg_send_keepalive(bgp_connection connection, bool must_send)
+{
+ struct stream *s = connection->obuf ;
+ int length;
+
+ if (!must_send && !bgp_connection_write_empty(connection))
+ return 0 ;
+
+ ++connection->session->stats.keepalive_out ;
+
+ /* Make KEEPALIVE message -- comprises header only */
+ bgp_packet_set_marker(s, BGP_MSG_KEEPALIVE);
+ length = bgp_packet_set_size(s);
+
+ /* Dump packet if debug option is set. */
+ /* bgp_packet_dump (s); */
+
+ if (BGP_DEBUG (keepalive, KEEPALIVE))
+ zlog_debug ("%s sending KEEPALIVE", connection->host);
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s send message type %d, length (incl. header) %d",
+ connection->host, BGP_MSG_KEEPALIVE, length);
+
+ /* Finally -- write the obuf away */
+ return bgp_connection_write(connection, s) ;
+} ;
+
+/*==============================================================================
+ * OPEN message -- transform bgp_open_state into BGP message
+ */
+
+static void
+bgp_open_options(struct stream *s, bgp_open_state open_state,
+ bool cap_suppress) ;
+
+static void
+bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi,
+ u_char cap_code, u_char orf_type, u_char mode) ;
+
+/*------------------------------------------------------------------------------
+ * Make OPEN message and dispatch.
+ *
+ * OPEN is the first message to be sent. If the buffers are not empty,
+ * something is badly wrong !
+ *
+ * Returns: 1 => written to wbuff -- qpselect will write from there
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ *
+ * NB: requires the session LOCKED -- connection-wise
+ */
+extern int
+bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state)
+{
+ struct stream *s = connection->obuf ;
+ int length ;
+
+ ++connection->session->stats.open_out ;
+
+ /* Make OPEN message header */
+ bgp_packet_set_marker(s, BGP_MSG_OPEN) ;
+
+ /* Set OPEN message fixed part */
+ stream_putc(s, BGP_VERSION_4) ;
+ stream_putw(s, (open_state->my_as <= BGP_AS_MAX)
+ ? (u_int16_t) open_state->my_as : BGP_AS_TRANS) ;
+ stream_putw(s, open_state->holdtime) ;
+ stream_put_ipv4(s, open_state->bgp_id) ;
+
+ /* Set OPEN message options */
+ bgp_open_options(s, open_state, connection->cap_suppress) ;
+
+ /* Set BGP message length. */
+ length = bgp_packet_set_size(s) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ {
+ char buf[INET_ADDRSTRLEN] ;
+ const char* no_cap ;
+
+ if (!open_state->can_capability)
+ no_cap = " (sans capabilities)" ;
+ else if (connection->cap_suppress)
+ no_cap = " (capabilities suppressed)" ;
+ else
+ no_cap = "" ;
+
+ inet_ntop(AF_INET, &open_state->bgp_id, buf, INET_ADDRSTRLEN) ;
+
+ zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s%s",
+ connection->host, BGP_VERSION_4, open_state->my_as,
+ open_state->holdtime, buf, no_cap) ;
+
+ } ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s send message type %d, length (incl. header) %d",
+ connection->host, BGP_MSG_OPEN, length);
+
+ /* Dump packet if debug option is set. */
+ /* bgp_packet_dump (s); */
+
+ /* Finally -- write the obuf away */
+ return bgp_connection_write(connection, s) ;
+} ;
+
+enum
+{
+ have_ipv6 =
+#ifdef HAVE_IPV6
+ 1
+#else
+ 0
+#endif
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add options to given encoded OPEN message.
+ *
+ * Supports the status quo: only Capability Options.
+ *
+ * Creates an empty options part of there are no capabilities to set.
+ */
+static void
+bgp_open_options(struct stream *s, bgp_open_state open_state, bool cap_suppress)
+{
+ u_char len ;
+ unsigned long cp ;
+ qafx_num_t qafx ;
+
+ /* Remember current pointer for Opt Parm Len. */
+ cp = stream_get_endp (s);
+
+ /* Opt Parm Len. */
+ stream_putc(s, 0);
+
+ /* If do not send capability, quit now -- zero options. */
+ if (!open_state->can_capability || cap_suppress)
+ return;
+
+ /* TODO: RFC 5492 (2009): SHOULD send only one Capability Option !! */
+ /* RFC 3392 (2002): silent on the matter */
+ /* RFC 2842 (2000): silent on the matter */
+
+ /* Send capability for every AFI/SAFI supported. */
+ for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx)
+ {
+ if (open_state->can_mp_ext & qafx_bit(qafx))
+ {
+ iAFI_t afi = get_iAFI(qafx) ;
+ iSAFI_t safi = get_iSAFI(qafx) ;
+
+ if (!have_ipv6 && (afi == iAFI_IP6))
+ continue ;
+
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_MP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN);
+ stream_putw (s, afi);
+ stream_putc (s, 0);
+ stream_putc (s, safi);
+ } ;
+ } ;
+
+ /* Route refresh. */
+
+ if (open_state->can_r_refresh & bgp_form_pre)
+ {
+ stream_putc (s, BGP_OPEN_OPT_CAP) ;
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ;
+ stream_putc (s, CAPABILITY_CODE_REFRESH_OLD) ;
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ;
+ } ;
+
+ if (open_state->can_r_refresh & bgp_form_pre)
+ {
+ stream_putc (s, BGP_OPEN_OPT_CAP) ;
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ;
+ stream_putc (s, CAPABILITY_CODE_REFRESH) ;
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ;
+ } ;
+
+ /* AS4 */
+
+ if (open_state->can_as4)
+ {
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_AS4);
+ stream_putc (s, CAPABILITY_CODE_AS4_LEN);
+ stream_putl (s, open_state->my_as) ;
+ } ;
+
+ /* ORF Capabilities */
+
+ for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx)
+ {
+ u_char mode = 0 ;
+
+ if (open_state->can_orf_prefix_send & qafx_bit(qafx))
+ mode |= ORF_MODE_SEND ;
+ if (open_state->can_orf_prefix_recv & qafx_bit(qafx))
+ mode |= ORF_MODE_RECEIVE ;
+
+ confirm((ORF_MODE_SEND | ORF_MODE_RECEIVE) == ORF_MODE_BOTH) ;
+
+ if (mode != 0)
+ {
+ iAFI_t afi = get_iAFI(qafx) ;
+ iSAFI_t safi = get_iSAFI(qafx) ;
+
+ if (!have_ipv6 && (afi == iAFI_IP6))
+ continue ;
+
+ if (open_state->can_orf_prefix & bgp_form_pre)
+ bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF_OLD,
+ ORF_TYPE_PREFIX_OLD, mode) ;
+
+ if (open_state->can_orf_prefix & bgp_form_rfc)
+ bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF,
+ ORF_TYPE_PREFIX, mode) ;
+ } ;
+ } ;
+
+ /* Dynamic capability. */
+ if (open_state->can_dynamic)
+ {
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_DYNAMIC);
+ stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN);
+ } ;
+
+ /* Graceful restart capability */
+ if (open_state->can_g_restart)
+ {
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_RESTART);
+ stream_putc (s, CAPABILITY_CODE_RESTART_LEN);
+ stream_putw (s, open_state->restart_time);
+ } ;
+
+ /* TODO: restarting flag ?? */
+ /* TODO: graceful restart preserving forwarding state */
+
+ /* Total Opt Parm Len. */
+ len = stream_get_endp (s) - cp - 1;
+ stream_putc_at (s, cp, len);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add an ORF capability to the given encoded OPEN message.
+ *
+ * Supports the status quo: only prefix-list filtering !
+ */
+static void
+bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi,
+ u_char cap_code, u_char orf_type, u_char mode)
+{
+ u_char cap_len;
+ u_char orf_len;
+ unsigned long capp;
+ unsigned long orfp;
+ unsigned long numberp;
+
+ int number_of_orfs = 0;
+
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp (s); /* Set Capability Len Pointer */
+ stream_putc (s, 0); /* Capability Length */
+ stream_putc (s, cap_code); /* Capability Code */
+ orfp = stream_get_endp (s); /* Set ORF Len Pointer */
+ stream_putc (s, 0); /* ORF Length */
+
+ stream_putw (s, afi);
+ stream_putc (s, 0);
+ stream_putc (s, safi);
+
+ numberp = stream_get_endp (s); /* Set Number Pointer */
+ stream_putc (s, 0); /* Number of ORFs */
+
+ /* Address Prefix ORF */
+ stream_putc (s, orf_type) ; /* type of ORF */
+ stream_putc (s, mode) ;
+
+ number_of_orfs++;
+
+ /* Total Number of ORFs. */
+ stream_putc_at (s, numberp, number_of_orfs);
+
+ /* Total ORF Len. */
+ orf_len = stream_get_endp (s) - orfp - 1;
+ stream_putc_at (s, orfp, orf_len);
+
+ /* Total Capability Len. */
+ cap_len = stream_get_endp (s) - capp - 1;
+ stream_putc_at (s, capp, cap_len);
+} ;
+
+/*==============================================================================
+ * ROUTE-REFRESH -- transform bgp_prefix_orf_update into BGP message
+ */
+
+static int
+bgp_msg_orf_part(struct stream* s, bgp_connection connection,
+ bgp_route_refresh rr) ;
+static int
+bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown,
+ bgp_size_t left) ;
+static int
+bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) ;
+
+static int
+bgp_msg_orf_prefix(struct stream* s, uint8_t common,
+ orf_prefix orfpe, bgp_size_t left) ;
+
+/*------------------------------------------------------------------------------
+ * Make Route-Refresh message(s) and dispatch.
+ *
+ * May return before all required messages have been sent, if the write
+ * buffer is or becomes full. The "next" entry in the "bgp_prefix_orf_update"
+ * allows the process to be continued, later.
+ *
+ * If has to send more than one message, then all but the last will be set
+ * "defer". The last will be set as per the defer flag.
+ *
+ * Supports the status quo, only Address-Prefix ORF.
+ *
+ * Returns: 1 => written to wbuff -- qpselect will write from there
+ * 0 => nothing written -- insufficient space in wbuff
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ *
+ * NB: requires the session LOCKED -- connection-wise
+ */
+extern int
+bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr)
+{
+ struct stream *s = connection->obuf ;
+ uint8_t msg_type ;
+ bool done ;
+ bgp_size_t msg_len ;
+
+ ++connection->session->stats.refresh_out ;
+
+ msg_type = (connection->route_refresh == bgp_form_pre)
+ ? BGP_MT_ROUTE_REFRESH_pre
+ : BGP_MT_ROUTE_REFRESH ;
+ done = (bgp_orf_get_count(rr) == 0) ;
+
+ do
+ {
+ if (bgp_connection_write_cannot_max(connection))
+ return 0 ;
+
+ /* Construct BGP message header for new/old form ROUTE-REFRESH */
+ bgp_packet_set_marker(s, msg_type) ;
+
+ /* Encode Route Refresh message. */
+ stream_putw(s, rr->afi) ;
+ stream_putc(s, 0);
+ stream_putc(s, rr->safi);
+
+ /* Process as many (remaining) ORF entries as can into message */
+ if (!done)
+ done = bgp_msg_orf_part(s, connection, rr) ;
+
+ /* Set BGP message length & dispatch. */
+ msg_len = bgp_packet_set_size(s) ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d length %d",
+ connection->host, rr->afi, rr->safi, msg_len) ;
+
+ bgp_connection_write(connection, s) ;
+ } while (!done) ;
+
+ return done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the current ORF entry length (if any)
+ *
+ * Returns total length of BGP message.
+ */
+inline static bgp_size_t
+bgp_msg_set_orf_length(struct stream* s, unsigned long elenp)
+{
+ bgp_size_t length = stream_get_endp(s) ;
+
+ if (elenp != 0)
+ stream_putw_at(s, elenp, length - elenp - 2) ;
+
+ return length ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put ORF entries to the given stream until run out of entries or run out
+ * of room in the message.
+ *
+ * There MUST BE at least one ORF entry to go.
+ *
+ * Returns true <=> done all available entries.
+ */
+static int
+bgp_msg_orf_part(struct stream* s, bgp_connection connection,
+ bgp_route_refresh rr)
+{
+ bgp_orf_entry entry ;
+
+ uint8_t orf_type ;
+ uint8_t orf_type_sent ;
+
+ unsigned long whenp ; /* where the "when" byte is */
+ unsigned long elenp ; /* where the entries length is */
+
+ bgp_size_t left ;
+ bgp_size_t length ;
+ bool done ;
+ bool first ;
+
+ /* Heading for Prefix-Address ORF type section */
+ whenp = stream_get_endp(s) ; /* position of "when" */
+ stream_putc(s, rr->defer ? BGP_ORF_WTR_DEFER : BGP_ORF_WTR_IMMEDIATE) ;
+
+ /* Process ORF entries until run out of entries or space */
+
+ elenp = 0 ; /* no entries length, yet */
+ orf_type = 0 ; /* no ORF type, yet */
+
+ first = 1 ; /* next entry is first of its ORF type */
+
+ while (1)
+ {
+ entry = bgp_orf_get_entry(rr, rr->next) ;
+ done = (entry == NULL) ;
+
+ if (done)
+ break ;
+
+ /* How much space is there left -- give up if very little
+ *
+ * What is "very little" is arbitrary, BUT MUST cover the ORF Type
+ * byte and the Length of ORF entries word, AT LEAST.
+ * */
+ left = BGP_MSG_MAX_L - stream_get_endp(s) ;
+
+ if (left < 16)
+ break ; /* NB: done == false */
+
+ confirm(16 > BGP_ORF_MIN_L) ; /* Type & Length */
+
+ /* Start new collection of ORF entries, if required. */
+ if (first || (orf_type != entry->orf_type))
+ {
+ /* fill in length of previous ORF entries, if any */
+ bgp_msg_set_orf_length(s, elenp) ;
+
+ /* set type and dummy entries length. */
+ orf_type = entry->orf_type ;
+ orf_type_sent = entry->orf_type ;
+
+ if ((orf_type == BGP_ORF_T_PREFIX) &&
+ (connection->orf_prefix == bgp_form_pre))
+ orf_type_sent = BGP_ORF_T_PREFIX_pre ;
+
+ stream_putc(s, orf_type_sent) ; /* ORF entries type */
+
+ elenp = stream_get_endp(s) ; /* offset of the length */
+ stream_putw(s, 0) ; /* length of ORF entries */
+
+ first = 1 ; /* next ORF entry is first of collection */
+ } ;
+
+ /* Insert the entry, if will fit.
+ *
+ * sets done <=> fitted
+ */
+ if (entry->unknown)
+ done = bgp_msg_orf_unknown(s, &entry->body.orf_unknown, left) ;
+ else
+ {
+ if (entry->remove_all)
+ done = bgp_msg_orf_remove_all(s, left) ;
+ else
+ {
+ uint8_t common = (entry->remove ? BGP_ORF_EA_REMOVE
+ : BGP_ORF_EA_ADD)
+ | (entry->deny ? BGP_ORF_EA_DENY
+ : BGP_ORF_EA_PERMIT) ;
+ switch (entry->orf_type)
+ {
+ case BGP_ORF_T_PREFIX:
+ done = bgp_msg_orf_prefix(s, common, &entry->body.orf_prefix,
+ left) ;
+ break ;
+ default:
+ zabort("unknown ORF type") ;
+ break ;
+ } ;
+ } ;
+ } ;
+
+ /* exit loop now if not enough room for current ORF entry */
+ if (!done)
+ break ;
+
+ /* Done ORF entry. Step to the next. NB: done == true */
+ ++rr->next ;
+ first = 0 ; /* no longer first */
+ } ;
+
+ /* If not done, need to:
+ *
+ * a) force defer,
+ * b) undo ORF entries if none output of current type
+ *
+ */
+ if (!done)
+ {
+ stream_putc_at(s, whenp , BGP_ORF_WTR_DEFER) ;
+
+ if (first)
+ {
+ stream_set_endp(s, elenp - 1) ;
+ elenp = 0 ; /* no entries length to set */
+ } ;
+ } ;
+
+ /* fill in length of last ORF entries (if any) */
+ length = bgp_msg_set_orf_length(s, elenp) ;
+
+ /* Something has gone wrong if nothing has been output after the "when"
+ * byte. Two possibilities:
+ *
+ * a) have been called again after having reported "done" (so there are
+ * no more entries to deal with.
+ *
+ * b) have been asked to output an "unknown" ORF entry which is too long
+ * for a BGP message !!
+ */
+ if (length == (whenp + 1))
+ {
+ if (entry == NULL)
+ zabort("called bgp_msg_send_route_refresh() after said was done") ;
+
+ if (entry->unknown)
+ zlog_err("%s sending REFRESH_REQ with impossible length (%d) ORF",
+ connection->host, entry->body.orf_unknown.length) ;
+ else
+ zabort("failed to put even one ORF entry") ;
+
+ done = 1 ; /* force done */
+ } ;
+
+ return done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put given unknown ORF entry to stream -- verbatim -- if possible.
+ */
+static int
+bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown,
+ bgp_size_t left)
+{
+ if (left < orf_unknown->length)
+ return 0 ;
+
+ stream_write(s, orf_unknown->data, orf_unknown->length) ;
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put remove all ORF entry to stream -- if possible.
+ */
+static int
+bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left)
+{
+ if (left == 1) /* only one byte required ! */
+ return 0 ;
+
+ stream_putc(s, BGP_ORF_EA_RM_ALL) ;
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put given Address-Prefix ORF entry to stream -- if possible.
+ */
+static int
+bgp_msg_orf_prefix(struct stream* s, uint8_t common,
+ orf_prefix orfpe, bgp_size_t left)
+{
+ bgp_size_t blen = (orfpe->p.prefixlen + 7) / 8 ;
+
+ if (left < (BGP_ORF_E_P_MIN_L + blen))
+ return 0 ;
+
+ stream_putc(s, common) ;
+ stream_putl(s, orfpe->seq) ;
+ stream_putc(s, orfpe->ge) ; /* aka min */
+ stream_putc(s, orfpe->le) ; /* aka max */
+ stream_putc(s, orfpe->p.prefixlen) ;
+ stream_write(s, &orfpe->p.u.prefix, blen) ;
+
+ return 1 ;
+} ;
+
+/*==============================================================================
+ * UPDATE -- send an UPDATE message
+ *
+ * PRO TEM -- this is passed a raw BGP message in a stream buffer
+ */
+
+/*------------------------------------------------------------------------------
+ * Make UPDATE message and dispatch.
+ *
+ * Returns: 1 => written to wbuff -- qpselect will write from there
+ * 0 => nothing written -- insufficient space in wbuff
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ *
+ * NB: requires the session LOCKED -- connection-wise
+ */
+extern int
+bgp_msg_send_update(bgp_connection connection, struct stream* s)
+{
+ if (bgp_connection_write_cannot_max(connection))
+ return 0 ;
+
+ ++connection->session->stats.update_out ;
+
+ return bgp_connection_write(connection, s) ;
+} ;
+
+/*==============================================================================
+ * End-of-RIB -- send an End-of-RIB BGP message (see Graceful Restart)
+ */
+
+/*------------------------------------------------------------------------------
+ * Make End-of-RIB message and dispatch.
+ *
+ * Returns: 1 => written to wbuff -- qpselect will write from there
+ * 0 => nothing written -- insufficient space in wbuff
+ *
+ * NB: actual I/O occurs in the qpselect action function -- so this cannot
+ * fail !
+ */
+extern int
+bgp_msg_send_end_of_rib(bgp_connection connection, iAFI_t afi, iSAFI_t safi)
+{
+ struct stream *s = connection->obuf ;
+
+ if (bgp_connection_write_cannot_max(connection))
+ return 0 ;
+
+ /* Make UPDATE message header */
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE) ;
+
+ /* Minimum size UPDATE */
+ stream_putw(s, 0) ; /* no Withdrawn Routes */
+ stream_putw(s, 0) ; /* no Attributes => no NLRI */
+
+ /* If not IPv4/Unicast, need empty MP Unreachable attribute */
+ if ((afi != iAFI_IP) || (safi != iSAFI_Unicast))
+ {
+ bgp_size_t attrp = stream_get_endp(s) ;
+
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI);
+ stream_putc (s, 3);
+ stream_putw (s, afi);
+ stream_putc (s, safi);
+
+ stream_putw_at(s, attrp-2, stream_get_endp(s) - attrp) ;
+ }
+
+ bgp_packet_set_size(s);
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi),
+ connection->host) ;
+
+ /* Finally -- write the buffer away */
+ return bgp_connection_write(connection, s) ;
+} ;
+
+/*==============================================================================
+ * Utilities for creating BGP messages
+ */
+ /* 0 1 2 3 4 5 6 7 */
+static const char bgp_header[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 8 */
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 16 */
+ "\x00" ;
+CONFIRM(sizeof(bgp_header) == (BGP_MARKER_SIZE + 2)) ;
+
+/*------------------------------------------------------------------------------
+ * Insert BGP message standard header
+ *
+ * 16 bytes of 0xFF
+ * 2 bytes -- total length of message -- filled in later
+ * 1 byte -- the type of message as given
+ */
+extern int
+bgp_packet_set_marker(struct stream *s, uint8_t type)
+{
+ /* Fill in marker & dummy total length (to be filled in later on) */
+ stream_write(s, bgp_header, BGP_MARKER_SIZE + 2) ;
+
+ /* BGP packet type. */
+ stream_putc (s, type);
+
+ /* Return current stream size. */
+ return stream_get_endp (s);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set BGP packet header size entry and return same.
+ */
+extern int
+bgp_packet_set_size (struct stream *s)
+{
+ int cp;
+
+ /* Preserve current pointer. */
+ cp = stream_get_endp (s);
+ stream_putw_at (s, BGP_MARKER_SIZE, cp);
+
+ return cp;
+} ;
diff --git a/bgpd/bgp_msg_write.h b/bgpd/bgp_msg_write.h
new file mode 100644
index 00000000..77bfc1f2
--- /dev/null
+++ b/bgpd/bgp_msg_write.h
@@ -0,0 +1,62 @@
+/* BGP message writing -- in BGP Engine -- header
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * Recast for pthreaded bgpd: 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_MSG_WRITE_H
+#define _QUAGGA_BGP_MSG_WRITE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_connection.h"
+#include "bgpd/bgp_notification.h"
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_route_refresh.h"
+
+#include "lib/stream.h"
+
+extern int
+bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) ;
+
+extern int
+bgp_msg_send_keepalive(bgp_connection connection, bool must_send) ;
+
+extern int
+bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) ;
+
+extern int
+bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) ;
+
+extern int
+bgp_msg_send_update(bgp_connection connection, struct stream* s) ;
+
+extern int
+bgp_msg_send_end_of_rib(bgp_connection connection, iAFI_t afi, iSAFI_t safi);
+
+extern int
+bgp_packet_set_marker(struct stream *s, uint8_t type) ;
+
+extern int
+bgp_packet_set_size (struct stream *s) ;
+
+#endif /* _QUAGGA_BGP_MSG_WRITE_H */
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 570cc3b7..405fc62d 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -1,549 +1,1135 @@
/* 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 <stdbool.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 "privs.h"
-#include "linklist.h"
-#include "network.h"
+#include "qpselect.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_peer_index.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_get_names(int sock_fd, union sockunion* su_local,
+ union sockunion* su_remote) ;
+static int bgp_socket_set_common_options(int sock_fd, union sockunion* su,
+ bgp_connection connection) ;
+static int bgp_set_ttl(int sock_fd, bgp_connection connnection,
+ int ttl, bool gtsm) ;
+static int bgp_md5_set_listeners(union sockunion* su, 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_inet = NULL ;
+#ifdef HAVE_IPV6
+static bgp_listener bgp_listeners_inet6 = NULL ;
+#endif
+
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define BGP_USE_ADDRINFO 1
+#else
+# define BGP_USE_ADDRINFO 0
+#endif
+
/* 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.
- */
-static int
-bgp_md5_set_socket (int socket, union sockunion *su, const char *password)
+ bgp_listener next ;
+ struct qps_file qf ;
+ union sockunion su ;
+} ;
+
+/* Get pointer to list base for listeners in the given address family. */
+static inline
+bgp_listener* bgp_listeners(sa_family_t family)
{
- 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));
+ switch (family)
+ {
+ case AF_INET:
+ return &bgp_listeners_inet ;
- return ret;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ return &bgp_listeners_inet6 ;
+
+#endif
+ default:
+ zabort("invalid address family") ;
+ } ;
}
-/* Helper for bgp_connect */
-static int
-bgp_md5_set_connect (int socket, union sockunion *su, const char *password)
+/* Forward reference */
+static int bgp_open_listeners_addrinfo(const char* address,
+ unsigned short port) ;
+static int bgp_open_listeners_simple(const char* address, unsigned short port) ;
+static int bgp_open_listener(sockunion su, unsigned short port,
+ int sock_type, int sock_protocol) ;
+
+/*------------------------------------------------------------------------------
+ * Open Listeners.
+ *
+ * Using given address and port, get all possible addresses and set up a
+ * listener on each one.
+ *
+ * Accepts: address = NULL => any local address
+ * address = comma separated list of addresses
+ *
+ * NB: an empty address counts as "any local address", so:
+ *
+ * "80.177.246.130,80.177.246.131" -- will listen on those addresses.
+ *
+ * "80.177.246.130," -- will list on that address and
+ * any other local address.
+ *
+ * NB: only listens on AF_INET and AF_INET6 (if HAVE_IPV6).
+ *
+ * Returns: > 0 => OK -- number of listeners set up
+ * -1 => failed -- no listeners set up
+ */
+extern int
+bgp_open_listeners(const char* address, unsigned short port)
{
- int ret = -1;
+ int count ;
+ bool done_null ;
+ char* copy ;
+ char* next ;
+
+ if (address == NULL)
+ address = "" ;
-#if HAVE_DECL_TCP_MD5SIG
- if ( bgpd_privs.change (ZPRIVS_RAISE) )
+ copy = XSTRDUP(MTYPE_TMP, address) ;
+
+ done_null = false ;
+ next = copy ;
+ count = 0 ;
+ do
{
- zlog_err ("%s: could not raise privs", __func__);
- return ret;
- }
-
- ret = bgp_md5_set_socket (socket, su, password);
+ address = next ;
+ next = strchr(address, ',') ;
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("%s: could not lower privs", __func__);
-#endif /* HAVE_TCP_MD5SIG */
-
- return ret;
-}
+ if (next != NULL)
+ *next++ = '\0' ; /* replace ',' and step past */
-int
-bgp_md5_set (struct peer *peer)
-{
- struct listnode *node;
- int ret = 0;
- struct bgp_listener *listener;
+ if (*address == '\0')
+ {
+ if (done_null)
+ continue ; /* don't do "" more than once */
+ else
+ done_null = true ;
+ } ;
+
+ count += BGP_USE_ADDRINFO ? bgp_open_listeners_addrinfo(address, port)
+ : bgp_open_listeners_simple(address, port) ;
+ } while (next != NULL) ;
- if ( bgpd_privs.change (ZPRIVS_RAISE) )
+ XFREE(MTYPE_TMP, copy) ;
+
+ 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;
-}
-
-/* Accept bgp connection. */
+ return 0;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open listeners using getaddrinfo() to find the addresses.
+ *
+ * Note that this will accept names as well as numeric addresses.
+ *
+ * Returns: count of listeners opened successfully.
+ */
static int
-bgp_accept (struct thread *thread)
+bgp_open_listeners_addrinfo(const char* address, unsigned short port)
{
- 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)
- {
- zlog_err ("accept_sock is nevative value %d", accept_sock);
- return -1;
- }
- listener->thread = thread_add_read (master, bgp_accept, listener, accept_sock);
+#if BGP_USE_ADDRINFO
+
+# ifndef HAVE_IPV6
+# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
+# endif
- /* Accept client connection. */
- bgp_sock = sockunion_accept (accept_sock, &su);
- if (bgp_sock < 0)
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ int ret, count;
+ char port_str[16];
+
+ 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';
+
+ if (*address == '\0')
+ address = NULL ;
+
+ ret = getaddrinfo (address, port_str, &req, &ainfo_save);
+ if (ret != 0)
{
- zlog_err ("[Error] BGP socket accept failed (%s)", safe_strerror (errno));
- return -1;
+ zlog_err ("%s: getaddrinfo: %s", __func__, eaitoa(ret, errno, 0).str);
+ return 0 ;
}
- set_nonblocking (bgp_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)
+
+ count = 0;
+ for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next)
{
- 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;
+ union sockunion su ;
+ int err ;
+
+ if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
+ continue;
+
+ sockunion_new_sockaddr(&su, ainfo->ai_addr) ;
+ err = bgp_open_listener(&su, port,
+ ainfo->ai_socktype, ainfo->ai_protocol) ;
+ if (err == 0)
+ ++count;
}
+ freeaddrinfo (ainfo_save);
- /* 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);
- if (peer1->gtsm_hops)
- sockopt_minttl (peer1->su.sa.sa_family, bgp_sock, MAXTTL + 1 - peer1->gtsm_hops);
- }
+ return count ;
+
+#else
+ zabort("bgp_open_listeners_addrinfo not implemented") ;
+#endif /* BGP_USE_ADDRINFO */
+}
+/*------------------------------------------------------------------------------
+ * Open listener the old fashioned way.
+ *
+ * NB: if address is "" tries IPv4 and IPv6 (if supported).
+ *
+ * NB: if address is not NULL, must be a numeric IP address (which may be IPv6
+ * if that is supported).
+ *
+ * Returns: count of listeners opened successfully.
+ */
+static int
+bgp_open_listeners_simple(const char* address, unsigned short port)
+{
+ union sockunion su ;
+ int err ;
+ int count ;
- /* Make dummy peer until read Open packet. */
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("[Event] Make dummy peer structure until read Open packet");
+ /* If address is not null, must be a single, specific, numeric address */
+ if (*address != '\0')
+ {
+ int ret = str2sockunion (address, &su) ;
+ if (ret < 0)
+ {
+ zlog_err("bgp_socket: could not parse ip address %s: %s",
+ address, errtoa(errno, 0).str);
+ return 0 ;
+ }
- {
- char buf[SU_ADDRSTRLEN + 1];
+ err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ;
- 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;
+ return (err == 0) ? 1 : 0 ;
+ } ;
- /* Make peer's address string. */
- sockunion2str (&su, buf, SU_ADDRSTRLEN);
- peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
- }
+ /* Null address, try <any> for IPv4 and (if supported) IPv6 */
+ count = 0 ;
- BGP_EVENT_ADD (peer, TCP_connection_open);
+ sockunion_init_new(&su, AF_INET) ;
+ err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ;
+ if (err == 0)
+ ++count ;
- return 0;
-}
+#ifdef HAVE_IPV6
+ sockunion_init_new(&su, AF_INET6) ;
+ err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ;
+ if (err == 0)
+ ++count ;
+#endif
-/* BGP socket bind. */
+ return count ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open 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_bind (struct peer *peer)
+bgp_open_listener(sockunion su, unsigned short port,
+ int sock_type, int sock_protocol)
{
-#ifdef SO_BINDTODEVICE
- int ret;
- struct ifreq ifreq;
+ bgp_listener listener ;
+ int ret, err ;
+ int slen ;
+ int sock_fd ;
+
+ /* Construct socket and set the common options. */
+ sock_fd = sockunion_socket(su, sock_type, sock_protocol) ;
+ if (sock_fd < 0)
+ {
+ err = errno ;
+ zlog_err ("%s: could not open socket for family %d: %s", __func__,
+ sockunion_family(su), errtoa(err, 0).str) ;
+ return errno = err ;
+ }
- if (! peer->ifname)
- return 0;
+ err = bgp_socket_set_common_options(sock_fd, su, NULL) ;
- strncpy ((char *)&ifreq.ifr_name, peer->ifname, sizeof (ifreq.ifr_name));
+ /* Want only IPV6 on ipv6 socket (not mapped addresses)
+ *
+ * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
+ * attempt to bind to :: after binding to 0.0.0.0.
+ *
+ * Also, for all the apparent utility of IPv4-mapped addresses, the semantics
+ * are simpler if IPv6 sockets speak IPv6 and IPv4 sockets speak IPv4.
+ */
+#ifdef HAVE_IPV6
+ if ((err == 0) && (sockunion_family(su) == AF_INET6))
+ if (setsockopt_ipv6_v6only(sock_fd) < 0)
+ err = errno ;
+#endif
- 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));
+ /* Bind to port and address (if any) */
+ if (err == 0)
+ {
+ if (bgpd_privs.change(ZPRIVS_RAISE))
+ {
+ err = errno ;
+ zlog_err("%s: could not raise privs: %s", __func__,
+ errtoa(errno, 0).str) ;
+ } ;
+
+ slen = sockunion_set_port(su, port) ;
+
+ ret = bind(sock_fd, &su->sa, slen) ;
+ if (ret < 0)
+ {
+ err = errno ;
+ zlog_err ("%s: bind: %s", __func__, errtoa(err, 0).str);
+ } ;
+
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ {
+ if (err == 0)
+ err = errno ;
+ zlog_err("%s: could not lower privs: %s", __func__,
+ errtoa(errno, 0).str) ;
+ } ;
+ } ;
+
+ /* Last lap... listen() */
+ if (err == 0)
+ {
+ ret = listen (sock_fd, 43);
+ if (ret < 0)
+ {
+ err = errno ;
+ zlog_err ("%s: listen: %s", __func__, errtoa(err, 0).str) ;
+ }
+ } ;
+
+ if (err != 0)
+ {
+ close(sock_fd) ;
+ return err ;
+ } ;
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("bgp_bind: could not lower privs");
+ /* 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)) ;
- if (ret < 0)
- {
- zlog (peer->log, LOG_INFO, "bind to interface %s failed", peer->ifname);
- return ret;
- }
-#endif /* SO_BINDTODEVICE */
- return 0;
-}
+ qps_file_init_new(&listener->qf, NULL) ;
+ qps_add_file(bgp_nexus->selection, &listener->qf, sock_fd, listener) ;
+ qps_enable_mode(&listener->qf, qps_read_mnum, bgp_accept_action) ;
-static int
-bgp_bind_address (int sock, struct in_addr *addr)
+ sockunion_copy(&listener->su, su) ;
+
+ listener->next = *bgp_listeners(sockunion_family(su)) ;
+ *bgp_listeners(sockunion_family(su)) = listener ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
{
- int ret;
- struct sockaddr_in local;
-
- memset (&local, 0, sizeof (struct sockaddr_in));
- local.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- local.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- memcpy (&local.sin_addr, addr, sizeof (struct in_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");
-
- return 0;
-}
+ bgp_reset_listeners(bgp_listeners(AF_INET)) ;
+#ifdef HAVE_IPV6
+ bgp_reset_listeners(bgp_listeners(AF_INET6)) ;
+#endif
+} ;
-static struct in_addr *
-bgp_update_address (struct interface *ifp)
+static void
+bgp_reset_listeners(bgp_listener* p_listener)
{
- struct prefix_ipv4 *p;
- struct connected *connected;
- struct listnode *node;
+ bgp_listener listener ;
+ bgp_listener next ;
+
+ next = *p_listener ;
+ *p_listener = NULL ;
- for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+ while (next != NULL)
{
- p = (struct prefix_ipv4 *) connected->address;
+ listener = next ;
+ next = listener->next ;
+
+ close(qps_file_fd(&listener->qf)) ;
+ qps_remove_file(&listener->qf) ;
+
+ XFREE(MTYPE_BGP_LISTENER, listener) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepare to accept() connection
+ *
+ * If the session has a password, then this is where the listener(s) for the
+ * appropriate address family are told about the password.
+ *
+ * This is done shortly before the session is first enabled for accept().
+ *
+ * The effect is (probably) that the peer's attempts to connect with MD5 signed
+ * packets will simply have been ignored up to this point. From this point
+ * forward they will be accepted, but closed until accept is enabled.
+ *
+ * NB: requires the session mutex LOCKED.
+ */
+extern void
+bgp_prepare_to_accept(bgp_connection connection)
+{
+ int err ;
- if (p->family == AF_INET)
- return &p->prefix;
- }
- return NULL;
-}
+ if (connection->session->password != NULL)
+ {
+ err = bgp_md5_set_listeners(connection->session->su_peer,
+ connection->session->password) ;
+
+/* TODO: failure to set password in bgp_prepare_to_accept ? */
+ } ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * No longer prepared to accept() connection
+ *
+ * If the session has a password, then this is where it is withdrawn from the
+ * listener(s) for the appropriate address family.
+ *
+ * NB: requires the session mutex LOCKED.
+ */
+extern void
+bgp_not_prepared_to_accept(bgp_connection connection)
+{
+ int err ;
-/* Update source selection. */
+ if (connection->session->password != NULL)
+ {
+ err = bgp_md5_set_listeners(connection->session->su_peer, NULL) ;
+
+/* TODO: failure to clear password in bgp_not_prepared_to_accept ? */
+ } ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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_update_source (struct peer *peer)
+bgp_accept_action(qps_file qf, void* file_info)
{
- struct interface *ifp;
- struct in_addr *addr;
-
- /* Source is specified with interface name. */
- if (peer->update_if)
+ bgp_connection connection ;
+ union sockunion su_remote ;
+ union sockunion su_local ;
+ bool exists ;
+ int sock_fd ;
+ int err ;
+
+ /* Accept client connection.
+ *
+ * We arrange for an IPv4 listener *and* an IPv6 one (assuming have IPv6),
+ * and we arrange for AF_INET6 listener to be IPV6_V6ONLY. This means that
+ * should NOT get an IPv4 mapped address. However, should we get such an
+ * address, the su_remote will be set to the actual IPv4 address.
+ *
+ * This means: the address family of su_remote is the address family of the
+ * underlying connection, NOT NECESSARILY the socket -- should that matter.
+ */
+ sock_fd = sockunion_accept(qps_file_fd(qf), &su_remote) ;
+ if (sock_fd < 0)
{
- ifp = if_lookup_by_name (peer->update_if);
- if (! ifp)
- return;
+ err = errno ;
+ if (sock_fd == -1)
+ zlog_err("[Error] BGP socket accept failed (%s)", errtoa(err, 0).str) ;
+ return ; /* have no connection to report this to */
+ } ;
+
+ if (BGP_DEBUG(events, EVENTS))
+ zlog_debug("[Event] BGP connection from host %s", sutoa(&su_remote).str) ;
+
+ /* See if we are ready to accept connections from the connecting party */
+ connection = bgp_peer_index_seek_accept(&su_remote, &exists) ;
+ if (connection == NULL)
+ {
+ 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",
+ sutoa(&su_remote).str) ;
+ close(sock_fd) ;
+ return ; /* quietly reject connection */
+/* TODO: RFC recommends sending a NOTIFICATION when refusing accept() */
+ } ;
+
+ /* 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 Routing Engine will not make any changes
+ * except under the mutex, and will not destroy the session.
+ */
- addr = bgp_update_address (ifp);
- if (! addr)
- return;
+ BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- bgp_bind_address (peer->fd, addr);
- }
+ /* Set the common socket options.
+ * Does not set password -- that is inherited from the listener.
+ *
+ * At this point, su_remote is the value returned by sockunion_accept(), so
+ * if we have an AF_INET6 socket with an IPv4 mapped address, then su_remote
+ * is an AF_INET.
+ */
+ err = bgp_socket_set_common_options(sock_fd, &su_remote, connection) ;
- /* Source is specified with IP address. */
- if (peer->update_source)
- sockunion_bind (peer->fd, peer->update_source, 0, peer->update_source);
-}
+ /* Get the local and remote addresses -- noting that IPv6 mapped IPv4
+ * addresses are rendered as IPv4 addresses.
+ */
+ if (err == 0)
+ err = bgp_get_names(sock_fd, &su_local, &su_remote) ;
+
+ /* If all is well, set up the accept connection, and set it ready
+ * to go. Set session not to accept further inbound connections.
+ */
+ if (err == 0)
+ bgp_connection_open(connection, sock_fd) ;
+ else
+ close(sock_fd) ;
+
+ BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-/* BGP try to connect to the peer. */
-int
-bgp_connect (struct peer *peer)
+ /* Now kick the FSM in an appropriate fashion */
+ bgp_fsm_connect_completed(connection, err, &su_local, &su_remote) ;
+} ;
+
+/*==============================================================================
+ * Open BGP Connection -- connect() to the other end
+ */
+
+static int bgp_md5_set_socket(int sock_fd, sockunion su, const char *password) ;
+static int bgp_bind_ifname(bgp_connection connection, int sock_fd) ;
+static int bgp_bind_ifaddress(bgp_connection connection, int sock_fd) ;
+
+/*------------------------------------------------------------------------------
+ * 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;
-
- /* 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);
- if (peer->gtsm_hops)
- sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - peer->gtsm_hops);
- }
-
- 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);
-#endif
+ int sock_fd ;
+ int err ;
- if (peer->password)
- bgp_md5_set_connect (peer->fd, &peer->su, peer->password);
+ sockunion su = connection->session->su_peer ;
- /* Bind socket. */
- bgp_bind (peer);
+ err = 0 ;
- /* Update source bind. */
- bgp_update_source (peer);
+ /* Make socket for the connect connection. */
+ sock_fd = sockunion_socket(su, SOCK_STREAM, 0) ;
+ if (sock_fd < 0)
+ err = errno ;
-#ifdef HAVE_IPV6
- if (peer->ifname)
- ifindex = if_nametoindex (peer->ifname);
-#endif /* HAVE_IPV6 */
+ if (BGP_DEBUG(events, EVENTS))
+ plog_debug(connection->log, "%s [Event] Connect start to %s socket %d%s",
+ connection->host, connection->host, sock_fd,
+ (sock_fd < 0) ? " -- failed" : "" ) ;
- if (BGP_DEBUG (events, EVENTS))
- plog_debug (peer->log, "%s [Event] Connect start to %s fd %d",
- peer->host, peer->host, peer->fd);
+ /* Set the common options. */
+ if (err == 0)
+ err = bgp_socket_set_common_options(sock_fd, su, connection) ;
- /* Connect to the remote peer. */
- return sockunion_connect (peer->fd, &peer->su, htons (peer->port), ifindex);
-}
+ /* Set the TCP MD5 "password", if required. */
+ if (err== 0)
+ if (connection->session->password != NULL)
+ err = bgp_md5_set_socket(sock_fd, su, connection->session->password) ;
-/* After TCP connection is established. Get local address and port. */
-void
-bgp_getsockname (struct peer *peer)
-{
- if (peer->su_local)
+ /* Bind socket. */
+ if (err == 0)
+ err = bgp_bind_ifname(connection, sock_fd) ;
+
+ /* Update source bind. */
+ if (err == 0)
+ err = bgp_bind_ifaddress(connection, sock_fd) ;
+
+ /* Connect to the remote peer. */
+ if (err == 0)
{
- sockunion_free (peer->su_local);
- peer->su_local = NULL;
- }
+ int ret ;
+ ret = sockunion_connect(sock_fd, su, connection->session->port,
+ connection->session->ifindex) ;
+ /* does not report EINPROGRESS as an error. */
+ if (ret < 0)
+ err = errno ;
+ } ;
+
+ /* If not OK now, close the sock_fd and signal the error */
+
+ if (err != 0)
+ {
+ if (sock_fd >= 0)
+ close(sock_fd) ;
+
+ bgp_fsm_connect_completed(connection, err, NULL, NULL) ;
+
+ 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 sock_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.
+ */
- if (peer->su_remote)
+ bgp_connection_open(connection, sock_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.
+ *
+ * If becomes both readable and writable at the same time, then the first to
+ * arrive here will disable the file for both read and write, which will
+ * discard the other pending event -- so will not attempt to do this more than
+ * once.
+ *
+ * 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 ;
+ union sockunion su_remote ;
+ union sockunion su_local ;
+
+ connection = file_info ;
+
+ /* See if connection successful or not. */
+ /* If successful, set the connection->su_local and ->su_remote */
+
+ len = sizeof(err) ;
+ err = 0 ;
+ ret = getsockopt(qps_file_fd(qf), SOL_SOCKET, SO_ERROR, &err, &len) ;
+ if (ret != 0)
{
- sockunion_free (peer->su_remote);
- peer->su_remote = NULL;
+ err = errno ;
+ if (err == 0) /* cannot be and cannot continue */
+ zabort("Invalid return from getsockopt()") ;
}
+ else
+ {
+ if (len != sizeof(err))
+ zabort("getsockopt returned unexpected length") ;
+ } ;
- peer->su_local = sockunion_getsockname (peer->fd);
- peer->su_remote = sockunion_getpeername (peer->fd);
+ if (err == 0)
+ err = bgp_get_names(qps_file_fd(qf), &su_local, &su_remote) ;
- bgp_nexthop_set (peer->su_local, peer->su_remote, &peer->nexthop, peer);
-}
+ /* 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, err, &su_local, &su_remote) ;
+} ;
+/*==============================================================================
+ * Set the TTL for the given connection (if any), if there is an sock_fd.
+ */
+extern void
+bgp_set_new_ttl(bgp_connection connection, int ttl, bool gtsm)
+{
+ int sock_fd ;
+
+ if (connection == NULL)
+ return ;
+
+ sock_fd = qps_file_fd(connection->qf) ;
+ if (sock_fd < 0)
+ return ;
+
+ bgp_set_ttl(sock_fd, connection, ttl, gtsm) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP set minttl (GTSM) and/or ttl.
+ *
+ * A ttl of <= 0 is treated as "turn off" -- effectively MAXTTL, forcing gtsm
+ * *off*.
+ *
+ * If GTSM is not supported, then sets ttl.
+ *
+ * Returns: 0 : OK (so far so good)
+ * != 0 : error number (from errno or otherwise)
+ */
static int
-bgp_listener (int sock, struct sockaddr *sa, socklen_t salen)
+bgp_set_ttl(int sock_fd, bgp_connection connection, int ttl, bool gtsm)
{
- struct bgp_listener *listener;
- int ret, en;
+ int ret ;
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
+ if (gtsm && (ttl > 0))
+ {
+ ret = setsockopt_minttl(sock_fd, ttl) ;
+
+ if (ret >= 0)
+ {
+ ttl = MAXTTL ;
+ connection->gtsm = true ;
+ }
+ else if (errno != EOPNOTSUPP)
+ return errno ;
+ }
+ else if (connection->gtsm)
+ {
+ ret = setsockopt_minttl(sock_fd, 0) ; /* turn off */
-#ifdef IPTOS_PREC_INTERNETCONTROL
- if (sa->sa_family == AF_INET)
- setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL);
-#endif
+ if (ret < 0) /* must have turned it on, so should not fail */
+ return errno ;
-#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
+ connection->gtsm = false ;
+ } ;
- if (bgpd_privs.change (ZPRIVS_RAISE) )
- zlog_err ("bgp_socket: could not raise privs");
+ ret = setsockopt_ttl(sock_fd, ttl) ;
- ret = bind (sock, sa, salen);
- en = errno;
- if (bgpd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("bgp_bind_address: could not lower privs");
+ return (ret >= 0) ? 0 : errno ;
+} ;
- if (ret < 0)
- {
- zlog_err ("bind: %s", safe_strerror (en));
- return ret;
- }
+/*==============================================================================
+ * Get local and remote address and port for connection.
+ *
+ * Returns: 0 => OK
+ * != 0 : error number (from errno or otherwise)
+ */
+static int
+bgp_get_names(int sock_fd, union sockunion* su_local,
+ union sockunion* su_remote)
+{
+ int ret, err ;
- ret = listen (sock, 3);
+ err = 0 ;
+
+ ret = sockunion_getsockname(sock_fd, su_local) ;
if (ret < 0)
- {
- zlog_err ("listen: %s", safe_strerror (errno));
- return ret;
- }
+ err = errno ;
- 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);
+ ret = sockunion_getpeername(sock_fd, su_remote) ;
+ if ((ret < 0) && (err == 0))
+ err = errno ;
- return 0;
-}
+ return err ;
+} ;
+
+/*==============================================================================
+ * Specific binding of outbound connections to interfaces...
+ *
+ */
-/* IPv6 supported version of BGP server socket setup. */
-#if defined (HAVE_IPV6) && ! defined (NRL)
-int
-bgp_socket (unsigned short port, const char *address)
+/*------------------------------------------------------------------------------
+ * 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_ifname(bgp_connection connection, int sock_fd)
{
- 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];
+#ifdef SO_BINDTODEVICE
+ int ret, err ;
+ struct ifreq ifreq;
- snprintf (port_str, sizeof(port_str), "%d", port);
- port_str[sizeof (port_str) - 1] = '\0';
+ if (connection->session->ifname == NULL)
+ return 0;
- ret = getaddrinfo (address, port_str, &req, &ainfo_save);
- if (ret != 0)
+ strncpy ((char *)&ifreq.ifr_name, connection->session->ifname,
+ sizeof (ifreq.ifr_name)) ;
+
+ err = 0 ;
+ if (bgpd_privs.change (ZPRIVS_RAISE))
{
- zlog_err ("getaddrinfo: %s", gai_strerror (ret));
- return -1;
- }
+ err = errno ;
+ zlog_err ("bgp_bind: could not raise privs: %s", errtoa(errno, 0).str);
+ } ;
- count = 0;
- for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next)
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_BINDTODEVICE,
+ &ifreq, sizeof (ifreq)) ;
+ if (ret < 0)
+ err = errno ;
+
+ 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;
- }
-
- /* if we intend to implement ttl-security, this socket needs ttl=255 */
- sockopt_ttl (ainfo->ai_family, sock, MAXTTL);
-
- ret = bgp_listener (sock, ainfo->ai_addr, ainfo->ai_addrlen);
- if (ret == 0)
- ++count;
- else
- close(sock);
- }
- freeaddrinfo (ainfo_save);
- if (count == 0)
+ if (err == 0)
+ err = errno ;
+ zlog_err ("bgp_bind: could not lower privs: %s", errtoa(errno, 0).str);
+ } ;
+
+ if (err != 0)
{
- zlog_err ("%s: no usable addresses", __func__);
- return -1;
+ zlog (connection->log, LOG_INFO, "bind to interface %s failed (%s)",
+ connection->session->ifname, errtoa(err, 0).str) ;
+ return err ;
}
-
+#endif /* SO_BINDTODEVICE */
return 0;
-}
-#else
-/* Traditional IPv4 only version. */
-int
-bgp_socket (unsigned short port, const char *address)
+} ;
+
+/*------------------------------------------------------------------------------
+ * Update source selection -- if connection specifies an IP address.
+ *
+ * If required, tries to bind the given socket to the given address.
+ *
+ * Returns: 0 : OK (so far so good)
+ * != 0 : error number (from errno or otherwise)
+ */
+static int
+bgp_bind_ifaddress(bgp_connection connection, int sock_fd)
{
- int sock;
- int socklen;
- struct sockaddr_in sin;
- int ret, en;
-
- sock = socket (AF_INET, SOCK_STREAM, 0);
- if (sock < 0)
+ if (connection->session->ifaddress != NULL)
{
- zlog_err ("socket: %s", safe_strerror (errno));
- return sock;
- }
+ union sockunion su[1] ;
+ int ret ;
- /* if we intend to implement ttl-security, this socket needs ttl=255 */
- sockopt_ttl (AF_INET, sock, MAXTTL);
+ sockunion_new_sockaddr(su, &connection->session->ifaddress->sa) ;
+ ret = sockunion_bind (sock_fd, su, 0, false) ;
- memset (&sin, 0, sizeof (struct sockaddr_in));
- sin.sin_family = AF_INET;
- sin.sin_port = htons (port);
- socklen = sizeof (struct sockaddr_in);
+ if (ret < 0)
+ return errno ;
+ } ;
+ return 0 ;
+} ;
- if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1))
+/*==============================================================================
+ * BGP Socket Option handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Common socket options:
+ *
+ * * non-blocking -- at all times
+ * * reuseaddr
+ * * reuseport
+ * * set security ttl (GTSM) and/or ttl -- if connection given.
+ * * for IPv4, set TOS if required
+ *
+ * These options are set on all sockets: listen/connect/accept
+ * (except either form of ttl, which is not set on listen).
+ *
+ * Note that the family of the given sockunion is the *protocol*, not the
+ * *socket* family.
+ *
+ * Returns: 0 => OK
+ * != 0 == errno -- not that we really expect any errors here
+ */
+static int
+bgp_socket_set_common_options(int sock_fd, union sockunion* su,
+ bgp_connection connection)
+{
+ int val ;
+
+ /* Make socket non-blocking */
+ val = fcntl(sock_fd, F_GETFL, 0) ;
+ if (val != -1) /* POSIX says "return value is not negative" */
+ val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK) ;
+ if (val == -1)
+ return errno ;
+
+ /* Reuse addr and port */
+ if (setsockopt_reuseaddr(sock_fd) < 0)
+ return errno ;
+ if (setsockopt_reuseport(sock_fd) < 0)
+ return errno ;
+
+ /* Adjust ttl if required */
+ if (connection != NULL)
{
- 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 */
+ int err ;
+ err = bgp_set_ttl(sock_fd, connection, connection->session->ttl,
+ connection->session->gtsm) ;
+ if (err != 0)
+ return err ;
+ } ;
+
+#ifdef IPTOS_PREC_INTERNETCONTROL
+ /* set IPPROTO_IP/IP_TOS -- if is AF_INET
+ *
+ * We assume that if the socket is an AF_INET6 with an IPv4 mapped address,
+ * then can still set IP_PROTOCOL/IP_TOS.
+ */
+ if (sockunion_family(su) == AF_INET)
+ if (setsockopt_ipv4_tos(sock_fd, IPTOS_PREC_INTERNETCONTROL) < 0)
+ return errno ;
+#endif
+
+ 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 EOPNOTSUPP 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_md5_set_socket(int sock_fd, sockunion su, const char *password)
+{
+ int err, ret ;
- ret = bgp_listener (sock, (struct sockaddr *) &sin, socklen);
- if (ret < 0)
+ assert(sock_fd >= 0) ;
+
+ err = 0 ;
+
+ if (bgpd_privs.change(ZPRIVS_RAISE))
{
- close (sock);
- return ret;
- }
- return sock;
-}
-#endif /* HAVE_IPV6 && !NRL */
+ err = errno ;
+ zlog_err("%s: could not raise privs: %s", __func__, errtoa(errno, 0).str);
+ } ;
-void
-bgp_close (void)
+ ret = setsockopt_tcp_signature(sock_fd, su, password) ;
+
+ if (ret != 0)
+ err = errno ;
+
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ {
+ if (err == 0)
+ err = errno ;
+ zlog_err("%s: could not lower privs: %s", __func__, errtoa(errno, 0).str);
+ } ;
+ return err ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set (or clear) MD5 password for given peer in the listener(s) for the peer's
+ * address family.
+ *
+ * This allows system to accept MD5 "signed" incoming connections from the
+ * given address.
+ *
+ * NULL password clears the password for the given peer.
+ *
+ * Returns: 0 => OK
+ * otherwise: errno -- the first error encountered.
+ *
+ * NB: peer address must be AF_INET or (if supported) AF_INET6
+ *
+ * NB: does nothing and returns "OK" if there are no listeners in the
+ * address family -- wanting to set MD5 makes no difference to this !
+ */
+static int
+bgp_md5_set_listeners(union sockunion* su, const char* password)
{
- struct listnode *node, *next;
- struct bgp_listener *listener;
+ bgp_listener listener ;
+ int err ;
- for (ALL_LIST_ELEMENTS (bm->listen_sockets, node, next, listener))
+#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
+
+ listener = *bgp_listeners(su->sa.sa_family) ;
+
+ while (listener != NULL)
{
- thread_cancel (listener->thread);
- close (listener->fd);
- listnode_delete (bm->listen_sockets, listener);
- XFREE (MTYPE_BGP_LISTENER, listener);
- }
-}
+ err = bgp_md5_set_socket(qps_file_fd(&listener->qf), su, password) ;
+ if (err != 0)
+ return err ;
+ listener = listener->next ;
+ } ;
+
+ return 0 ;
+} ;
+
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
index 5bf2e5ff..d05fa3a0 100644
--- a/bgpd/bgp_network.h
+++ b/bgpd/bgp_network.h
@@ -1,31 +1,34 @@
/* BGP network related header
- 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) 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.
+ */
#ifndef _QUAGGA_BGP_NETWORK_H
#define _QUAGGA_BGP_NETWORK_H
-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 *);
+#include "bgpd/bgp_connection.h"
-extern int bgp_md5_set (struct peer *);
+extern int bgp_open_listeners(const char *address, unsigned short port) ;
+extern void bgp_close_listeners(void) ;
+extern void bgp_open_connect(bgp_connection connection) ;
+extern void bgp_prepare_to_accept(bgp_connection connection) ;
+extern void bgp_not_prepared_to_accept(bgp_connection connection) ;
+extern void bgp_set_new_ttl(bgp_connection connection, int ttl, bool gtsm) ;
#endif /* _QUAGGA_BGP_NETWORK_H */
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index 719cb966..6c21c247 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -43,7 +43,7 @@ struct bgp_nexthop_cache *zlookup_query (struct in_addr);
#ifdef HAVE_IPV6
struct bgp_nexthop_cache *zlookup_query_ipv6 (struct in6_addr *);
#endif /* HAVE_IPV6 */
-
+
/* Only one BGP scan thread are activated at the same time. */
static struct thread *bgp_scan_thread = NULL;
@@ -66,7 +66,7 @@ static struct bgp_table *bgp_connected_table[AFI_MAX];
/* BGP nexthop lookup query client. */
struct zclient *zlookup = NULL;
-
+
/* Add nexthop to the end of the list. */
static void
bnc_nexthop_add (struct bgp_nexthop_cache *bnc, struct nexthop *nexthop)
@@ -107,7 +107,7 @@ bnc_free (struct bgp_nexthop_cache *bnc)
bnc_nexthop_free (bnc);
XFREE (MTYPE_BGP_NEXTHOP_CACHE, bnc);
}
-
+
static int
bgp_nexthop_same (struct nexthop *next1, struct nexthop *next2)
{
@@ -234,7 +234,7 @@ bgp_nexthop_lookup_ipv6 (struct peer *peer, struct bgp_info *ri, int *changed,
/* Only check IPv6 global address only nexthop. */
attr = ri->attr;
- if (attr->extra->mp_nexthop_len != 16
+ if (attr->extra->mp_nexthop_len != 16
|| IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_global))
return 1;
@@ -277,7 +277,7 @@ bgp_nexthop_lookup_ipv6 (struct peer *peer, struct bgp_info *ri, int *changed,
if (bnc->metric != oldbnc->metric)
bnc->metricchanged = 1;
- bgp_unlock_node (oldrn);
+ bgp_unlock_node (oldrn);
}
}
}
@@ -438,7 +438,7 @@ bgp_scan (afi_t afi, safi_t safi)
/* Maximum prefix check */
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
continue;
if (peer->afc[afi][SAFI_UNICAST])
@@ -454,14 +454,15 @@ bgp_scan (afi_t afi, safi_t safi)
{
for (bi = rn->info; bi; bi = next)
{
- next = bi->next;
+ next = bi->info_next;
if (bi->type == ZEBRA_ROUTE_BGP && bi->sub_type == BGP_ROUTE_NORMAL)
{
changed = 0;
metricchanged = 0;
- if (peer_sort (bi->peer) == BGP_PEER_EBGP && bi->peer->ttl == 1)
+ if ((peer_sort (bi->peer) == BGP_PEER_EBGP)
+ && (bi->peer->ttl == 1))
valid = bgp_nexthop_check_ebgp (afi, bi->attr);
else
valid = bgp_nexthop_lookup (afi, bi->peer, bi,
@@ -534,7 +535,7 @@ bgp_scan_timer (struct thread *t)
return 0;
}
-
+
struct bgp_connected_ref
{
unsigned int refcnt;
@@ -561,7 +562,7 @@ bgp_connected_add (struct connected *ifc)
if (addr->family == AF_INET)
{
- PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc));
+ prefix_copy_ipv4(&p, CONNECTED_PREFIX(ifc));
apply_mask_ipv4 ((struct prefix_ipv4 *) &p);
if (prefix_ipv4_any ((struct prefix_ipv4 *) &p))
@@ -583,7 +584,7 @@ bgp_connected_add (struct connected *ifc)
#ifdef HAVE_IPV6
else if (addr->family == AF_INET6)
{
- PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc));
+ prefix_copy_ipv6(&p, CONNECTED_PREFIX(ifc));
apply_mask_ipv6 ((struct prefix_ipv6 *) &p);
if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6))
@@ -626,7 +627,7 @@ bgp_connected_delete (struct connected *ifc)
if (addr->family == AF_INET)
{
- PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc));
+ prefix_copy_ipv4(&p, CONNECTED_PREFIX(ifc));
apply_mask_ipv4 ((struct prefix_ipv4 *) &p);
if (prefix_ipv4_any ((struct prefix_ipv4 *) &p))
@@ -649,7 +650,7 @@ bgp_connected_delete (struct connected *ifc)
#ifdef HAVE_IPV6
else if (addr->family == AF_INET6)
{
- PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc));
+ prefix_copy_ipv6(&p, CONNECTED_PREFIX(ifc));
apply_mask_ipv6 ((struct prefix_ipv6 *) &p);
if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6))
@@ -690,14 +691,14 @@ bgp_nexthop_self (afi_t afi, struct attr *attr)
{
p = ifc->address;
- if (p && p->family == AF_INET
+ if (p && p->family == AF_INET
&& IPV4_ADDR_SAME (&p->u.prefix4, &attr->nexthop))
return 1;
}
}
return 0;
}
-
+
static struct bgp_nexthop_cache *
zlookup_read (void)
{
@@ -723,16 +724,16 @@ zlookup_read (void)
nbytes = stream_read (s, zlookup->sock, length - 2);
marker = stream_getc (s);
version = stream_getc (s);
-
+
if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER)
{
zlog_err("%s: socket %d version mismatch, marker %d, version %d",
__func__, zlookup->sock, marker, version);
return NULL;
}
-
+
command = stream_getw (s);
-
+
raddr.s_addr = stream_get_ipv4 (s);
metric = stream_getl (s);
nexthop_num = stream_getc (s);
@@ -784,9 +785,9 @@ zlookup_query (struct in_addr addr)
stream_reset (s);
zclient_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP);
stream_put_in_addr (s, &addr);
-
+
stream_putw_at (s, 0, stream_get_endp (s));
-
+
ret = writen (zlookup->sock, s->data, stream_get_endp (s));
if (ret < 0)
{
@@ -831,16 +832,16 @@ zlookup_read_ipv6 (void)
nbytes = stream_read (s, zlookup->sock, length - 2);
marker = stream_getc (s);
version = stream_getc (s);
-
+
if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER)
{
zlog_err("%s: socket %d version mismatch, marker %d, version %d",
__func__, zlookup->sock, marker, version);
return NULL;
}
-
+
command = stream_getw (s);
-
+
stream_get (&raddr, s, 16);
metric = stream_getl (s);
@@ -899,7 +900,7 @@ zlookup_query_ipv6 (struct in6_addr *addr)
zclient_create_header (s, ZEBRA_IPV6_NEXTHOP_LOOKUP);
stream_put (s, addr, 16);
stream_putw_at (s, 0, stream_get_endp (s));
-
+
ret = writen (zlookup->sock, s->data, stream_get_endp (s));
if (ret < 0)
{
@@ -947,12 +948,12 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric,
s = zlookup->obuf;
stream_reset (s);
zclient_create_header (s, ZEBRA_IPV4_IMPORT_LOOKUP);
-
+
stream_putc (s, p->prefixlen);
stream_put_in_addr (s, &p->u.prefix4);
-
+
stream_putw_at (s, 0, stream_get_endp (s));
-
+
/* Write the packet. */
ret = writen (zlookup->sock, s->data, stream_get_endp (s));
@@ -989,9 +990,9 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric,
__func__, zlookup->sock, marker, version);
return 0;
}
-
+
command = stream_getw (s);
-
+
addr.s_addr = stream_get_ipv4 (s);
metric = stream_getl (s);
nexthop_num = stream_getc (s);
@@ -1035,7 +1036,7 @@ bgp_import (struct thread *t)
afi_t afi;
safi_t safi;
- bgp_import_thread =
+ bgp_import_thread =
thread_add_timer (master, bgp_import, NULL, bgp_import_interval);
if (BGP_DEBUG (events, EVENTS))
@@ -1086,29 +1087,6 @@ bgp_import (struct thread *t)
return 0;
}
-/* Connect to zebra for nexthop lookup. */
-static int
-zlookup_connect (struct thread *t)
-{
- struct zclient *zlookup;
-
- zlookup = THREAD_ARG (t);
- zlookup->t_connect = NULL;
-
- if (zlookup->sock != -1)
- return 0;
-
-#ifdef HAVE_TCP_ZEBRA
- zlookup->sock = zclient_socket ();
-#else
- zlookup->sock = zclient_socket_un (ZEBRA_SERV_PATH);
-#endif /* HAVE_TCP_ZEBRA */
- if (zlookup->sock < 0)
- return -1;
-
- return 0;
-}
-
/* Check specified multiaccess next-hop. */
int
bgp_multiaccess_check_v4 (struct in_addr nexthop, char *peer)
@@ -1141,7 +1119,7 @@ bgp_multiaccess_check_v4 (struct in_addr nexthop, char *peer)
if (! rn1)
return 0;
bgp_unlock_node (rn1);
-
+
rn2 = bgp_node_match (bgp_connected_table[AFI_IP], &p2);
if (! rn2)
return 0;
@@ -1154,7 +1132,7 @@ bgp_multiaccess_check_v4 (struct in_addr nexthop, char *peer)
return 0;
}
-
+
DEFUN (bgp_scan_time,
bgp_scan_time_cmd,
"bgp scan-time <5-60>",
@@ -1167,7 +1145,7 @@ DEFUN (bgp_scan_time,
if (bgp_scan_thread)
{
thread_cancel (bgp_scan_thread);
- bgp_scan_thread =
+ bgp_scan_thread =
thread_add_timer (master, bgp_scan_timer, NULL, bgp_scan_interval);
}
@@ -1186,7 +1164,7 @@ DEFUN (no_bgp_scan_time,
if (bgp_scan_thread)
{
thread_cancel (bgp_scan_thread);
- bgp_scan_thread =
+ bgp_scan_thread =
thread_add_timer (master, bgp_scan_timer, NULL, bgp_scan_interval);
}
@@ -1224,17 +1202,17 @@ DEFUN (show_ip_bgp_scan,
{
if (bnc->valid)
vty_out (vty, " %s valid [IGP metric %d]%s",
- inet_ntoa (rn->p.u.prefix4), bnc->metric, VTY_NEWLINE);
+ safe_inet_ntoa (rn->p.u.prefix4), bnc->metric, VTY_NEWLINE);
else
vty_out (vty, " %s invalid%s",
- inet_ntoa (rn->p.u.prefix4), VTY_NEWLINE);
+ safe_inet_ntoa (rn->p.u.prefix4), VTY_NEWLINE);
}
#ifdef HAVE_IPV6
{
char buf[BUFSIZ];
- for (rn = bgp_table_top (bgp_nexthop_cache_table[AFI_IP6]);
- rn;
+ for (rn = bgp_table_top (bgp_nexthop_cache_table[AFI_IP6]);
+ rn;
rn = bgp_route_next (rn))
if ((bnc = rn->info) != NULL)
{
@@ -1251,19 +1229,19 @@ DEFUN (show_ip_bgp_scan,
#endif /* HAVE_IPV6 */
vty_out (vty, "BGP connected route:%s", VTY_NEWLINE);
- for (rn = bgp_table_top (bgp_connected_table[AFI_IP]);
- rn;
+ for (rn = bgp_table_top (bgp_connected_table[AFI_IP]);
+ rn;
rn = bgp_route_next (rn))
if (rn->info != NULL)
- vty_out (vty, " %s/%d%s", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+ vty_out (vty, " %s/%d%s", safe_inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
VTY_NEWLINE);
#ifdef HAVE_IPV6
{
char buf[BUFSIZ];
- for (rn = bgp_table_top (bgp_connected_table[AFI_IP6]);
- rn;
+ for (rn = bgp_table_top (bgp_connected_table[AFI_IP6]);
+ rn;
rn = bgp_route_next (rn))
if (rn->info != NULL)
vty_out (vty, " %s/%d%s",
@@ -1289,7 +1267,10 @@ bgp_scan_init (void)
{
zlookup = zclient_new ();
zlookup->sock = -1;
- zlookup->t_connect = thread_add_event (master, zlookup_connect, zlookup, 0);
+
+ /* enable zebra client and schedule connection */
+ zlookup->enable = 1 ;
+ zlookup_schedule(zlookup);
bgp_scan_interval = BGP_SCAN_INTERVAL_DEFAULT;
bgp_import_interval = BGP_IMPORT_INTERVAL_DEFAULT;
@@ -1308,7 +1289,7 @@ bgp_scan_init (void)
#endif /* HAVE_IPV6 */
/* Make BGP scan thread. */
- bgp_scan_thread = thread_add_timer (master, bgp_scan_timer,
+ bgp_scan_thread = thread_add_timer (master, bgp_scan_timer,
NULL, bgp_scan_interval);
/* Make BGP import there. */
bgp_import_thread = thread_add_timer (master, bgp_import, NULL, 0);
diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c
new file mode 100644
index 00000000..7dd68c63
--- /dev/null
+++ b/bgpd/bgp_notification.c
@@ -0,0 +1,320 @@
+/* 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 <string.h>
+#include <netinet/in.h>
+
+#include "lib/zassert.h"
+#include "lib/memory.h"
+
+#include "bgpd/bgp_notification.h"
+#include "bgpd/bgp_open_state.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 size)
+{
+ return (size == 0) ? 0 : ((size + 32 + 16 - 1) / 32) * 32 ;
+} ;
+
+/*==============================================================================
+ * Create/Destroy bgp_notify
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate and initialise new notification
+ *
+ * Can add data later if required.
+ *
+ * NB: returns a 'NOT received' notification.
+ */
+extern bgp_notify
+bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode)
+{
+ bgp_notify notification ;
+
+ notification = XCALLOC(MTYPE_BGP_NOTIFY, sizeof(struct bgp_notify)) ;
+
+ notification->code = code ;
+ notification->subcode = subcode ;
+
+ /* Implicitly:
+ *
+ * notification->received = false ;
+ * notification->size = 0
+ * notification->length = 0
+ * notification->data = NULL
+ */
+
+ return notification ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate and initialise new notification -- expecting some data.
+ *
+ * Can specify an expected amount of data -- may use more or less than this...
+ * ...but pre-allocates at least the expected amount.
+ *
+ * May expect 0.
+ *
+ * NB: returns a 'NOT received' notification.
+ */
+extern bgp_notify
+bgp_notify_new_expect(bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ bgp_size_t expect)
+{
+ bgp_notify notification = bgp_notify_new(code, subcode) ;
+
+ if (expect != 0)
+ {
+ notification->size = bgp_notify_size(expect) ;
+ notification->data = XCALLOC(MTYPE_TMP, notification->size) ;
+ } ;
+
+ return notification ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate and initialise new notification, complete with data
+ *
+ * Can specify an expected amount of data -- copes with len == 0 (and data may
+ * be NULL iff len == 0).
+ *
+ * NB: returns a 'NOT received' notification.
+ */
+extern bgp_notify
+bgp_notify_new_with_data(bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ const void* data, bgp_size_t len)
+{
+ bgp_notify notification ;
+
+ notification = bgp_notify_new_expect(code, subcode, len) ;
+ bgp_notify_append_data(notification, data, len) ;
+
+ return notification ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free notification structure
+ *
+ * Does nothing if there is no structure.
+ */
+extern void
+bgp_notify_free(bgp_notify notification)
+{
+ if (notification != NULL)
+ {
+ if (notification->data != NULL)
+ XFREE(MTYPE_TMP, notification->data) ;
+ XFREE(MTYPE_BGP_NOTIFY, notification) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Duplicate existing notification (if any)
+ */
+extern bgp_notify
+bgp_notify_dup(bgp_notify notification)
+{
+ bgp_notify duplicate ;
+
+ if (notification == NULL)
+ return NULL ;
+
+ duplicate = XMALLOC(MTYPE_BGP_NOTIFY, sizeof(struct bgp_notify)) ;
+ *duplicate = *notification ;
+
+ if (duplicate->length == 0)
+ {
+ duplicate->size = 0 ;
+ duplicate->data = NULL ;
+ }
+ else
+ {
+ duplicate->size = bgp_notify_size(duplicate->length) ;
+ duplicate->data = XCALLOC(MTYPE_TMP, duplicate->size) ;
+ memcpy(duplicate->data, notification->data, duplicate->length) ;
+ } ;
+
+ return duplicate ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unset pointer to notification and free any existing notification structure.
+ *
+ * Does nothing if there is no structure.
+ */
+extern void
+bgp_notify_unset(bgp_notify* p_notification)
+{
+ bgp_notify_free(*p_notification) ; /* free anything that's there */
+ *p_notification = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unset pointer to notification and return the pointer value.
+ *
+ * Returns NULL if there is no structure.
+ */
+extern bgp_notify
+bgp_notify_take(bgp_notify* p_notification)
+{
+ bgp_notify take = *p_notification ; /* take anything that's there */
+ *p_notification = NULL ;
+ return take ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to notification
+ *
+ * Frees any existing notification at the destination.
+ *
+ * NB: copies the source pointer -- so must be clear about responsibility
+ * for the notification structure.
+ */
+extern void
+bgp_notify_set(bgp_notify* p_dst, bgp_notify src)
+{
+ if (*p_dst != src) /* empty operation if already set ! */
+ {
+ bgp_notify_free(*p_dst) ;
+ *p_dst = src ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to notification to a *copy* of the source.
+ *
+ * Frees any existing notification at the destination unless points at src !
+ */
+extern void
+bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src)
+{
+ if (*p_dst != src)
+ bgp_notify_free(*p_dst) ; /* avoid freeing what we're duplicating */
+
+ *p_dst = bgp_notify_dup(src) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to notification and unset source pointer
+ *
+ * Frees any existing notification at the destination.
+ *
+ * NB: responsibility for the notification structure passes to the destination.
+ */
+extern void
+bgp_notify_set_mov(bgp_notify* p_dst, bgp_notify* p_src)
+{
+ bgp_notify_set(p_dst, *p_src) ;
+ *p_src = NULL ;
+} ;
+
+/*==============================================================================
+ * Set new Code and Subcode and discard and data accumulated so far.
+ *
+ * NB: does not change the received state of the notification.
+ */
+extern bgp_notify
+bgp_notify_reset(bgp_notify notification, bgp_nom_code_t code,
+ bgp_nom_subcode_t subcode)
+{
+ if (notification == NULL)
+ return bgp_notify_new(code, subcode) ;
+
+ notification->code = code ;
+ notification->subcode = subcode ;
+ notification->length = 0 ;
+
+ return notification ;
+} ;
+
+/*==============================================================================
+ * Append data to given notification
+ *
+ * Copes with zero length append (and data may be NULL if len == 0).
+ *
+ * NB: returns possibly NEW ADDRESS of the notification.
+ */
+extern void
+bgp_notify_append_data(bgp_notify notification, const void* data,
+ bgp_size_t len)
+{
+ bgp_size_t new_length = notification->length + len ; /* unsigned */
+
+ if (new_length > notification->size)
+ {
+ bgp_size_t size = bgp_notify_size(new_length) ;
+
+ if (notification->size == 0)
+ notification->data = XCALLOC(MTYPE_TMP, size) ;
+ else
+ notification->data = XREALLOC(MTYPE_TMP, notification->data, size) ;
+
+ memset(notification->data + new_length, 0, size - new_length) ;
+ notification->size = size ;
+ } ;
+
+ if (len > 0)
+ memcpy(notification->data + notification->length, data, len) ;
+
+ notification->length = new_length ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append one byte
+ */
+extern void
+bgp_notify_append_b(bgp_notify notification, uint8_t b)
+{
+ bgp_notify_append_data(notification, &b, 1) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append one word (uint16_t), in network byte order
+ */
+extern void
+bgp_notify_append_w(bgp_notify notification, uint16_t w)
+{
+ w = htons(w) ;
+ bgp_notify_append_data(notification, &w, 2) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append one long (uint32_t), in network byte order
+ */
+extern void
+bgp_notify_append_l(bgp_notify notification, uint32_t l)
+{
+ l = htonl(l) ;
+ bgp_notify_append_data(notification, &l, 4) ;
+} ;
diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h
new file mode 100644
index 00000000..20e96063
--- /dev/null
+++ b/bgpd/bgp_notification.h
@@ -0,0 +1,225 @@
+/* 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 <stddef.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 ;
+
+/*==============================================================================
+ * Structure for notification.
+ *
+ * Note that vast majority of notification handling concerns notifications that
+ * are *sent* to the far end. Occasionally a notification will be received.
+ */
+typedef struct bgp_notify* bgp_notify ;
+
+struct bgp_notify
+{
+ bgp_nom_code_t code ;
+ bgp_nom_subcode_t subcode ;
+
+ bool received ;
+
+ bgp_size_t length ;
+ bgp_size_t size ;
+ ptr_t 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
+
+/*==============================================================================
+ * Functions
+ */
+
+extern bgp_notify
+bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode) ;
+
+extern bgp_notify
+bgp_notify_new_expect(bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ bgp_size_t expect) ;
+extern bgp_notify
+bgp_notify_new_with_data(bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ const void* data, bgp_size_t len) ;
+extern void
+bgp_notify_free(bgp_notify notification) ;
+
+extern bgp_notify
+bgp_notify_dup(bgp_notify notification) ;
+
+extern void
+bgp_notify_unset(bgp_notify* p_notification) ;
+
+extern bgp_notify
+bgp_notify_take(bgp_notify* p_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) ;
+
+extern void
+bgp_notify_set_mov(bgp_notify* p_dst, bgp_notify* p_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 ;
+} ;
+
+Inline void
+bgp_notify_set_received(bgp_notify notification)
+{
+ notification->received = true ;
+} ;
+
+extern bgp_notify
+bgp_notify_reset(bgp_notify notification, bgp_nom_code_t code,
+ bgp_nom_subcode_t subcode) ;
+extern void
+bgp_notify_append_data(bgp_notify notification, const void* data,
+ bgp_size_t len) ;
+extern void
+bgp_notify_append_b(bgp_notify notification, uint8_t b) ;
+
+extern void
+bgp_notify_append_w(bgp_notify notification, uint16_t w) ;
+
+extern void
+bgp_notify_append_l(bgp_notify notification, uint32_t l) ;
+
+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 bool
+bgp_notify_get_received(bgp_notify notification)
+{
+ return (notification != NULL) ? notification->received : false ;
+} ;
+
+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..80b90683 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"
@@ -55,14 +58,18 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
struct capability_mp_data mpc;
struct capability_header *hdr;
- pnt = peer->notify.data;
- end = pnt + peer->notify.length;
-
+ if ((peer == NULL) || (peer->session == NULL)
+ || (peer->session->notification == NULL))
+ return;
+
+ pnt = (char*)peer->session->notification->data;
+ end = pnt + peer->session->notification->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 +116,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
+void
bgp_capability_mp_data (struct stream *s, struct capability_mp_data *mpc)
{
mpc->afi = stream_getw (s);
@@ -134,7 +141,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 +161,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 +171,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;
@@ -194,21 +201,21 @@ bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi,
peer->host, afi, safi, type, mode);
}
-static const struct message orf_type_str[] =
+const struct message orf_type_str[] =
{
{ ORF_TYPE_PREFIX, "Prefixlist" },
{ ORF_TYPE_PREFIX_OLD, "Prefixlist (old)" },
};
-static const int orf_type_str_max
+const int orf_type_str_max
= sizeof(orf_type_str)/sizeof(orf_type_str[0]);
-static const struct message orf_mode_str[] =
+const struct message orf_mode_str[] =
{
{ ORF_MODE_RECEIVE, "Receive" },
{ ORF_MODE_SEND, "Send" },
{ ORF_MODE_BOTH, "Both" },
};
-static const int orf_mode_str_max
+const int orf_mode_str_max
= sizeof(orf_mode_str)/sizeof(orf_mode_str[0]);
static int
@@ -221,7 +228,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 +236,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,14 +249,15 @@ 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)
{
zlog_info ("%s ORF Capability entry length error,"
" Cap length %u, num %u",
- peer->host, hdr->length, entry.num);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ peer->host, hdr->length, entry.num) ;
+ /* TODO: is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
@@ -257,7 +265,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 +305,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 +314,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 +360,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 +371,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 +405,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 +432,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,16 +442,16 @@ 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;
}
-static const struct message capcode_str[] =
+const struct message capcode_str[] =
{
{ CAPABILITY_CODE_MP, "MultiProtocol Extensions" },
{ CAPABILITY_CODE_REFRESH, "Route Refresh" },
@@ -454,10 +462,10 @@ static const struct message capcode_str[] =
{ CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)" },
{ CAPABILITY_CODE_ORF_OLD, "ORF (Old)" },
};
-static const int capcode_str_max = sizeof(capcode_str)/sizeof(capcode_str[0]);
+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[] =
+const size_t cap_minsizes[] =
{
[CAPABILITY_CODE_MP] = sizeof (struct capability_mp_data),
[CAPABILITY_CODE_REFRESH] = CAPABILITY_CODE_REFRESH_LEN,
@@ -478,41 +486,43 @@ 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)
{
zlog_info ("%s Capability length error (< header)", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (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)
{
zlog_info ("%s Capability length error (< length)", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (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,18 +539,19 @@ 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);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
/* we deliberately ignore unknown codes, see below */
default:
break;
}
-
+
switch (caphdr.code)
{
case CAPABILITY_CODE_MP:
@@ -590,7 +601,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 +633,8 @@ 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_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_AUTH_FAILURE);
return -1;
}
@@ -650,7 +660,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 +672,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 +713,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,29 +746,31 @@ 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)
{
zlog_info ("%s Option length error", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
/* Fetch option type and length. */
opt_type = stream_getc (s);
opt_length = stream_getc (s);
-
+
/* Option length check. */
if (STREAM_READABLE (s) < opt_length)
{
zlog_info ("%s Option length error", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
@@ -768,7 +780,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 +791,8 @@ 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_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_PARAM);
ret = -1;
break;
}
@@ -801,10 +812,10 @@ 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,
- error_data, error - error_data);
+ bgp_peer_down_error_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data, error - error_data);
return -1;
}
@@ -812,9 +823,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_OPEN_UNSUP_CAPBL);
+ bgp_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
return -1;
}
}
@@ -823,7 +833,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,14 +843,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,
- error_data, error - error_data);
+ bgp_peer_down_error_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_OPEN_UNSUP_CAPBL);
+ bgp_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
return -1;
}
}
@@ -929,8 +938,8 @@ 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)
- || CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
+ if (// ! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN) ||
+ CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
return;
/* IPv4 unicast. */
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 2b1382d8..dd294eae 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -21,6 +21,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_OPEN_H
#define _QUAGGA_BGP_OPEN_H
+#include "log.h"
+#include "stream.h"
+#include "vty.h"
+
/* Standard header for capability TLV */
struct capability_header
{
@@ -37,7 +41,7 @@ struct capability_mp_data
};
#pragma pack(1)
-struct capability_orf_entry
+struct capability_orf_entry
{
struct capability_mp_data mpc;
u_char num;
@@ -86,13 +90,13 @@ struct capability_gr
/* Cooperative Route Filtering Capability. */
/* ORF Type */
-#define ORF_TYPE_PREFIX 64
+#define ORF_TYPE_PREFIX 64
#define ORF_TYPE_PREFIX_OLD 128
/* ORF Mode */
-#define ORF_MODE_RECEIVE 1
-#define ORF_MODE_SEND 2
-#define ORF_MODE_BOTH 3
+#define ORF_MODE_RECEIVE 1
+#define ORF_MODE_SEND 2
+#define ORF_MODE_BOTH 3
/* Capability Message Action. */
#define CAPABILITY_ACTION_SET 0
@@ -102,10 +106,20 @@ struct capability_gr
#define RESTART_R_BIT 0x8000
#define RESTART_F_BIT 0x80
+extern const struct message capcode_str[];
+extern const int capcode_str_max;
+
+extern const struct message orf_type_str[];
+extern const int orf_type_str_max;
+extern const struct message orf_mode_str[];
+extern const int orf_mode_str_max;
+
extern int bgp_open_option_parse (struct peer *, u_char, int *);
extern void bgp_open_capability (struct stream *, struct peer *);
extern void bgp_capability_vty_out (struct vty *, struct peer *);
extern as_t peek_for_as4_capability (struct peer *, u_char);
+extern void bgp_capability_mp_data (struct stream *s,
+ struct capability_mp_data *mpc);
extern int bgp_afi_safi_valid_indices (afi_t, safi_t *);
#endif /* _QUAGGA_BGP_OPEN_H */
diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c
new file mode 100644
index 00000000..9ca9617e
--- /dev/null
+++ b/bgpd/bgp_open_state.c
@@ -0,0 +1,457 @@
+/* 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 "zebra.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_peer.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_open_state.h"
+
+#include "lib/memory.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)) ;
+
+ vector_init_new(&state->unknowns, 0) ;
+
+ return state ;
+}
+
+/*------------------------------------------------------------------------------
+ * Free bgp_open_state structure (if any)
+ *
+ * Returns NULL.
+ */
+extern bgp_open_state
+bgp_open_state_free(bgp_open_state state)
+{
+ bgp_cap_unknown unknown ;
+ bgp_cap_afi_safi afi_safi ;
+
+ if (state != NULL)
+ {
+ while ((unknown = vector_ream_keep(&state->unknowns)) != NULL)
+ XFREE(MTYPE_TMP, unknown) ;
+
+ while ((afi_safi = vector_ream_keep(&state->afi_safi)) != NULL)
+ XFREE(MTYPE_TMP, afi_safi) ;
+
+ XFREE(MTYPE_BGP_OPEN_STATE, state) ;
+ } ;
+
+ return NULL ;
+}
+
+/*------------------------------------------------------------------------------
+ * Unset pointer to open_state structure and free structure (if any).
+ */
+extern void
+bgp_open_state_unset(bgp_open_state* p_state)
+{
+ bgp_open_state_free(*p_state) ;
+ *p_state = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to open_state and unset source pointer
+ *
+ * Frees any existing open_state at the destination.
+ *
+ * NB: responsibility for the open_state structure passes to the destination.
+ */
+extern void
+bgp_open_state_set_mov(bgp_open_state* p_dst, bgp_open_state* p_src)
+{
+ bgp_open_state_free(*p_dst) ;
+ *p_dst = *p_src ;
+ *p_src = NULL ;
+} ;
+
+/*==============================================================================
+ * Construct new bgp_open_state for the given peer -- allocate if required.
+ *
+ * Initialises the structure according to the current peer state.
+ */
+
+bgp_open_state
+bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer)
+{
+ safi_t safi ;
+ afi_t afi ;
+
+ 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.s_addr ;
+
+ /* Whether to send capability or not */
+ state->can_capability = ! CHECK_FLAG(peer->flags, PEER_FLAG_DONT_CAPABILITY) ;
+
+ /* Announce self as AS4 speaker always */
+ if (!bm->as2_speaker)
+ SET_FLAG(peer->cap, PEER_CAP_AS4_ADV) ;
+ state->can_as4 = CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV) ? 1 : 0 ;
+
+ state->my_as2 = (state->my_as > BGP_AS2_MAX ) ? BGP_ASN_TRANS
+ : state->my_as ;
+
+ /* 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 |= qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)) ;
+
+ /* Route refresh -- always */
+ SET_FLAG(peer->cap, PEER_CAP_REFRESH_ADV) ;
+ state->can_r_refresh = CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_ADV)
+ ? (bgp_form_pre | bgp_form_rfc)
+ : bgp_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 |=
+ qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)) ;
+ if (peer->af_flags[afi][safi] & PEER_FLAG_ORF_PREFIX_RM)
+ state->can_orf_prefix_recv |=
+ qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)) ;
+ } ;
+
+ state->can_orf_prefix = (state->can_orf_prefix_send |
+ state->can_orf_prefix_recv)
+ ? (bgp_form_pre | bgp_form_rfc)
+ : bgp_form_none ;
+
+ /* Dynamic Capabilities TODO: check requirement */
+ state->can_dynamic = ( CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)
+ != 0 ) ;
+ if (state->can_dynamic)
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV) ;
+
+ /* Graceful restart capability */
+ if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART))
+ {
+ SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV) ;
+ state->can_g_restart = 1 ;
+ state->restart_time = peer->bgp->restart_time ;
+ }
+ else
+ {
+ state->can_g_restart = 0 ;
+ state->restart_time = 0 ;
+ } ;
+
+ /* TODO: check not has restarted and not preserving forwarding state (?) */
+ state->can_preserve = 0 ; /* cannot preserve forwarding */
+ state->has_preserved = 0 ; /* has not preserved forwarding */
+ state->has_restarted = 0 ; /* has not restarted */
+
+ return state;
+}
+
+/*==============================================================================
+ * Unknown capabilities handling.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Add given unknown capability and its value to the given open_state.
+ */
+extern void
+bgp_open_state_unknown_add(bgp_open_state state, uint8_t code,
+ void* value, bgp_size_t length)
+{
+ bgp_cap_unknown unknown ;
+
+ unknown = XCALLOC(MTYPE_TMP, sizeof(struct bgp_cap_unknown) + length) ;
+
+ unknown->code = code ;
+ unknown->length = length ;
+
+ if (length != 0)
+ memcpy(unknown->value, value, length) ;
+
+ vector_push_item(&state->unknowns, unknown) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get count of number of unknown capabilities in given open_state.
+ */
+extern int
+bgp_open_state_unknown_count(bgp_open_state state)
+{
+ return vector_end(&state->unknowns) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get n'th unknown capability -- if exists.
+ */
+extern bgp_cap_unknown
+bgp_open_state_unknown_cap(bgp_open_state state, unsigned index)
+{
+ return vector_get_item(&state->unknowns, index) ;
+} ;
+
+/*==============================================================================
+ * Generic afi/safi capabilities handling.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Add given afi/safi capability and its value to the given open_state.
+ */
+extern bgp_cap_afi_safi
+bgp_open_state_afi_safi_add(bgp_open_state state, iAFI_t afi, iSAFI_t safi,
+ bool known, uint8_t cap_code)
+{
+ bgp_cap_afi_safi afi_safi ;
+
+ afi_safi = XCALLOC(MTYPE_TMP, sizeof(struct bgp_cap_afi_safi)) ;
+
+ afi_safi->known_afi_safi = known ;
+ afi_safi->afi = afi ;
+ afi_safi->safi = safi ;
+ afi_safi->cap_code = cap_code ;
+
+ vector_push_item(&state->afi_safi, afi_safi) ;
+
+ return afi_safi ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get count of number of afi/safi capabilities in given open_state.
+ */
+extern int
+bgp_open_state_afi_safi_count(bgp_open_state state)
+{
+ return vector_end(&state->afi_safi) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get n'th afi_safi capability -- if exists.
+ */
+extern bgp_cap_afi_safi
+bgp_open_state_afi_safi_cap(bgp_open_state state, unsigned index)
+{
+ return vector_get_item(&state->afi_safi, index) ;
+} ;
+
+/*==============================================================================
+ *
+ */
+
+/* Received an open, update the peer's state
+ *
+ * Takes the peer->session->open_recv and fills in:
+ *
+ * peer->v_holdtime ) per negotiated values
+ * peer->v_keepalive )
+ *
+ * peer->remote_id.s_addr
+ *
+ * peer->cap ) updated per open_recv -- assumes all recv flags
+ * peer->af_cap ) have been cleared.
+ *
+ * peer->v_gr_restart set to value received (if any)
+ *
+ * peer->afc_recv set/cleared according to what is advertised
+ * BUT if !open_recv->can_capability or
+ * neighbor override-capability, then
+ * all flags are cleared.
+ *
+ * peer->afc_nego set/cleared according to what is advertised and
+ * what is activated.
+ * BUT if !open_recv->can_capability or
+ * neighbor override-capability, then
+ * set everything which has been activated.
+ *
+ *
+ *
+ * NB: for safety, best to have the session locked -- though won't, in fact,
+ * change any of this information after the session is established.
+ */
+void
+bgp_peer_open_state_receive(bgp_peer peer)
+{
+ bgp_session session = peer->session;
+ bgp_open_state open_recv = session->open_recv;
+ qAFI_t afi;
+ qSAFI_t safi;
+ qafx_bit_t qbs ;
+ int recv ;
+
+ /* Check neighbor as number. */
+ assert(open_recv->my_as == peer->as);
+
+ /* If had to suppress sending of capabilities, note that */
+ if (session->cap_suppress)
+ SET_FLAG (peer->cap, PEER_CAP_SUPPRESSED) ;
+
+ /* holdtime */
+ /* From the rfc: A reasonable maximum time between KEEPALIVE messages
+ would be one third of the Hold Time interval. KEEPALIVE messages
+ MUST NOT be sent more frequently than one per second. An
+ implementation MAY adjust the rate at which it sends KEEPALIVE
+ messages as a function of the Hold Time interval. */
+
+ /* The BGP Engine sets the session's HoldTimer and KeepaliveTimer intervals
+ * to the values negotiated when the OPEN messages were exchanged.
+ *
+ * Take copies of that information.
+ */
+ peer->v_holdtime = session->hold_timer_interval ;
+ peer->v_keepalive = session->keepalive_timer_interval ;
+
+ /* Set remote router-id */
+ peer->remote_id.s_addr = open_recv->bgp_id;
+
+ /* AS4 */
+ if (open_recv->can_as4)
+ SET_FLAG (peer->cap, PEER_CAP_AS4_RCV);
+
+ /* AFI/SAFI -- as received, or assumed or overridden */
+
+ if (!open_recv->can_capability || session->cap_override)
+ {
+ /* There were no capabilities, or are OVERRIDING AFI/SAFI, so force
+ * not having received any AFI/SAFI, but apply all known.
+ */
+ recv = 0 ;
+ qbs = qafx_known_bits ;
+ }
+ else
+ {
+ /* Use the AFI/SAFI received, if any. */
+ recv = 1 ;
+ qbs = open_recv->can_mp_ext ;
+ }
+
+ for (afi = qAFI_min ; afi <= qAFI_max ; ++afi)
+ for (safi = qSAFI_min ; safi <= qSAFI_max ; ++safi)
+ {
+ qafx_bit_t qb = qafx_bit_from_qAFI_qSAFI(afi, safi) ;
+ if (qb & qbs)
+ {
+ peer->afc_recv[afi][safi] = recv ;
+ peer->afc_nego[afi][safi] = peer->afc[afi][safi] ;
+ }
+ else
+ {
+ peer->afc_recv[afi][safi] = 0 ;
+ peer->afc_nego[afi][safi] = 0 ;
+ } ;
+ } ;
+
+ /* Route refresh. */
+ if (open_recv->can_r_refresh & bgp_form_pre)
+ SET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV);
+ else if (open_recv->can_r_refresh & bgp_form_rfc)
+ SET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV);
+
+ /* ORF */
+ for (afi = qAFI_min ; afi <= qAFI_max ; ++afi)
+ for (safi = qSAFI_min ; safi <= qSAFI_max ; ++safi)
+ {
+ qafx_bit_t qb = qafx_bit_from_qAFI_qSAFI(afi, safi);
+ if (qb & open_recv->can_orf_prefix_send)
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV);
+ if (qb & open_recv->can_orf_prefix_recv)
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV);
+ }
+
+ /* ORF prefix. */
+ if (open_recv->can_orf_prefix_send)
+ {
+ if (open_recv->can_orf_prefix & bgp_form_pre)
+ SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_SM_OLD_RCV);
+ else if (open_recv->can_orf_prefix & bgp_form_rfc)
+ SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_SM_RCV);
+ }
+ if (open_recv->can_orf_prefix_recv)
+ {
+ if (open_recv->can_orf_prefix & bgp_form_pre)
+ SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_RM_OLD_RCV);
+ else if (open_recv->can_orf_prefix & bgp_form_rfc)
+ SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_RM_RCV);
+ }
+
+ /* Dynamic Capabilities */
+ if (open_recv->can_dynamic)
+ SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV);
+
+ /* Graceful restart
+ *
+ * NB: appear not to care about open_recv->has_restarted !
+ */
+ if (open_recv->can_g_restart)
+ SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV) ;
+
+ for (afi = qAFI_min ; afi <= qAFI_max ; ++afi)
+ for (safi = qSAFI_min ; safi <= qSAFI_max ; ++safi)
+ {
+ qafx_bit_t qb = qafx_bit_from_qAFI_qSAFI(afi, safi);
+ if (peer->afc[afi][safi] && (qb & open_recv->can_preserve))
+ {
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV);
+ if (qb & open_recv->has_preserved)
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV);
+ }
+ }
+
+ peer->v_gr_restart = open_recv->restart_time;
+ /* TODO: should we do anything with this? */
+#if 0
+ int restarting ; /* Restart State flag */
+#endif
+}
diff --git a/bgpd/bgp_open_state.h b/bgpd/bgp_open_state.h
new file mode 100644
index 00000000..8c30712b
--- /dev/null
+++ b/bgpd/bgp_open_state.h
@@ -0,0 +1,161 @@
+/* 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 <stdint.h>
+
+#include "bgpd/bgp.h"
+#include "bgpd/bgp_common.h"
+#include "lib/vector.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * BGP Open State.
+ *
+ * This structure encapsulates all the information that may be sent/received
+ * in a BGP OPEN Message.
+ *
+ */
+
+typedef struct bgp_cap_unknown* bgp_cap_unknown ;
+struct bgp_cap_unknown /* to capture unknown capability */
+{
+ uint8_t code ;
+ bgp_size_t length ;
+ uint8_t value[] ;
+} ;
+
+typedef struct bgp_cap_mp* bgp_cap_mp ;
+struct bgp_cap_mp
+{
+} ;
+
+typedef struct bgp_cap_orf* bgp_cap_orf ;
+struct bgp_cap_orf
+{
+ bool known_orf_type ;
+
+ uint8_t type ;
+ bool send ;
+ bool recv ;
+} ;
+
+typedef struct bgp_cap_gr* bgp_cap_gr ;
+struct bgp_cap_gr
+{
+ bool has_preserved ;
+} ;
+
+typedef struct bgp_cap_afi_safi* bgp_cap_afi_safi ;
+struct bgp_cap_afi_safi
+{
+ bool known_afi_safi ;
+
+ iAFI_t afi ;
+ iSAFI_t safi ;
+
+ uint8_t cap_code ; /* eg BGP_CAN_MP_EXT */
+ union
+ {
+ struct bgp_cap_mp mp ;
+ struct bgp_cap_orf orf ;
+ struct bgp_cap_gr gr ;
+ } caps ;
+} ;
+
+struct bgp_open_state
+{
+ as_t my_as ; /* generic ASN */
+ unsigned holdtime ; /* in seconds */
+ bgp_id_t bgp_id ; /* an IPv4 address -- *network order* */
+
+ bool can_capability ; /* false => don't do capabilities */
+
+ bool can_as4 ;
+ as2_t my_as2 ; /* AS2 from OPEN message */
+
+ qafx_set_t can_mp_ext ; /* will accept, may send these */
+
+ bgp_form_t can_r_refresh ; /* none/old/new/both */
+ bgp_form_t can_orf_prefix ; /* none/old/new/both */
+
+ 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 */
+
+ bool can_dynamic ;
+
+ bool can_g_restart ; /* can do graceful restart */
+ qafx_set_t can_preserve ; /* can preserve forwarding for these */
+ qafx_set_t has_preserved ; /* has preserved forwarding for these */
+
+ bool has_restarted ; /* Restart State flag */
+ unsigned restart_time ; /* Restart Time in seconds */
+
+ struct vector unknowns ; /* list of bgp_cap_unknown */
+ struct vector afi_safi ; /* various afi/safi capabilities */
+} ;
+
+/*==============================================================================
+ *
+ */
+
+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) ;
+
+extern void
+bgp_open_state_unset(bgp_open_state* state) ;
+
+extern void
+bgp_open_state_set_mov(bgp_open_state* p_dst, bgp_open_state* p_src) ;
+
+extern void
+bgp_open_state_unknown_add(bgp_open_state state, uint8_t code,
+ void* value, bgp_size_t length) ;
+extern int
+bgp_open_state_unknown_count(bgp_open_state state) ;
+
+extern bgp_cap_unknown
+bgp_open_state_unknown_cap(bgp_open_state state, unsigned index) ;
+
+extern bgp_cap_afi_safi
+bgp_open_state_afi_safi_add(bgp_open_state state, iAFI_t afi, iSAFI_t safi,
+ bool known, uint8_t cap_code) ;
+extern int
+bgp_open_state_afi_safi_count(bgp_open_state state) ;
+
+extern bgp_cap_afi_safi
+bgp_open_state_afi_safi_cap(bgp_open_state state, unsigned index) ;
+
+extern bgp_open_state
+bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer);
+
+extern void
+bgp_peer_open_state_receive(bgp_peer peer);
+
+#endif /* QUAGGA_BGP_OPEN_STATE_H */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 1d9fcc97..b21cec4e 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -29,9 +29,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "memory.h"
#include "sockunion.h" /* for inet_ntop () */
#include "linklist.h"
-#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"
@@ -47,9 +49,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_advertise.h"
#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_route_refresh.h"
int stream_put_prefix (struct stream *, struct prefix *);
-
+
/* Set up BGP packet marker and packet type. */
static int
bgp_packet_set_marker (struct stream *s, u_char type)
@@ -84,21 +87,7 @@ bgp_packet_set_size (struct stream *s)
return cp;
}
-/* Add new packet to the peer. */
-static void
-bgp_packet_add (struct peer *peer, struct stream *s)
-{
- /* Add packet to the end of list. */
- stream_fifo_push (peer->obuf, s);
-}
-
-/* Free first packet. */
-static void
-bgp_packet_delete (struct peer *peer)
-{
- stream_free (stream_fifo_pop (peer->obuf));
-}
-
+#if 0
/* Check file descriptor whether connect is established. */
static void
bgp_connect_check (struct peer *peer)
@@ -121,7 +110,7 @@ bgp_connect_check (struct peer *peer)
zlog (peer->log, LOG_INFO, "can't get sockopt for nonblocking connect");
BGP_EVENT_ADD (peer, TCP_fatal_error);
return;
- }
+ }
/* When status is 0 then TCP connection is established. */
if (status == 0)
@@ -136,15 +125,22 @@ bgp_connect_check (struct peer *peer)
BGP_EVENT_ADD (peer, TCP_connection_open_failed);
}
}
+#endif
-/* Make BGP update packet. */
+/*------------------------------------------------------------------------------
+ * Construct an update from head of peer->sync[afi][safi]->update.
+ *
+ * Generates complete BGP message in the peer->work stream structure.
+ *
+ * Returns: peer->work -- if have something to be written.
+ * NULL -- otherwise
+ */
static struct stream *
bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
{
struct stream *s;
struct bgp_adj_out *adj;
struct bgp_advertise *adv;
- struct stream *packet;
struct bgp_node *rn = NULL;
struct bgp_info *binfo = NULL;
bgp_size_t total_attr_len = 0;
@@ -154,7 +150,7 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
s = peer->work;
stream_reset (s);
- adv = FIFO_HEAD (&peer->sync[afi][safi]->update);
+ adv = bgp_advertise_fifo_head(&peer->sync[afi][safi]->update);
while (adv)
{
@@ -174,7 +170,7 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
struct prefix_rd *prd = NULL;
u_char *tag = NULL;
struct peer *from = NULL;
-
+
if (rn->prn)
prd = (struct prefix_rd *) &rn->prn->p;
if (binfo)
@@ -183,21 +179,21 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
if (binfo->extra)
tag = binfo->extra->tag;
}
-
+
bgp_packet_set_marker (s, BGP_MSG_UPDATE);
- stream_putw (s, 0);
+ stream_putw (s, 0);
pos = stream_get_endp (s);
stream_putw (s, 0);
- total_attr_len = bgp_packet_attribute (NULL, peer, s,
+ total_attr_len = bgp_packet_attribute (NULL, peer, s,
adv->baa->attr,
- &rn->p, afi, safi,
+ &rn->p, afi, safi,
from, prd, tag);
stream_putw_at (s, pos, total_attr_len);
}
if (afi == AFI_IP && safi == SAFI_UNICAST)
stream_put_prefix (s, &rn->p);
-
+
if (BGP_DEBUG (update, UPDATE_OUT))
zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d",
peer->host,
@@ -217,24 +213,26 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
if (! (afi == AFI_IP && safi == SAFI_UNICAST))
break;
}
-
- if (! stream_empty (s))
- {
- bgp_packet_set_size (s);
- packet = stream_dup (s);
- bgp_packet_add (peer, packet);
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
- stream_reset (s);
- return packet;
- }
- return NULL;
+
+ if (stream_empty (s))
+ return NULL ;
+
+ bgp_packet_set_size (s) ;
+ return s ;
}
+/*------------------------------------------------------------------------------
+ * Construct an End-of-RIB update message for given AFI/SAFI.
+ *
+ * Generates complete BGP message in the peer->work stream structure.
+ *
+ * Returns: peer->work -- if have something to be written.
+ * NULL -- otherwise
+ */
static struct stream *
bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi)
{
struct stream *s;
- struct stream *packet;
if (DISABLE_BGP_ANNOUNCE)
return NULL;
@@ -242,7 +240,8 @@ bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi)
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host);
- s = stream_new (BGP_MAX_PACKET_SIZE);
+ s = peer->work;
+ stream_reset (s);
/* Make BGP update packet. */
bgp_packet_set_marker (s, BGP_MSG_UPDATE);
@@ -267,18 +266,21 @@ bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi)
}
bgp_packet_set_size (s);
- packet = stream_dup (s);
- bgp_packet_add (peer, packet);
- stream_free (s);
- return packet;
+ return s ;
}
-/* Make BGP withdraw packet. */
+/*------------------------------------------------------------------------------
+ * Construct a withdraw update from from head of peer->sync[afi][safi]->withdraw
+ *
+ * Generates complete BGP message in the peer->work stream structure.
+ *
+ * Returns: peer->work -- if have something to be written.
+ * NULL -- otherwise
+ */
static struct stream *
bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
{
struct stream *s;
- struct stream *packet;
struct bgp_adj_out *adj;
struct bgp_advertise *adv;
struct bgp_node *rn;
@@ -290,13 +292,14 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
s = peer->work;
stream_reset (s);
- while ((adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL)
+ while ((adv = bgp_advertise_fifo_head(&peer->sync[afi][safi]->withdraw))
+ != NULL)
{
assert (adv->rn);
adj = adv->adj;
rn = adv->rn;
- if (STREAM_REMAIN (s)
+ if (STREAM_REMAIN (s)
< (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen)))
break;
@@ -311,14 +314,14 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
else
{
struct prefix_rd *prd = NULL;
-
+
if (rn->prn)
prd = (struct prefix_rd *) &rn->prn->p;
pos = stream_get_endp (s);
stream_putw (s, 0);
total_attr_len
= bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL);
-
+
/* Set total path attribute length. */
stream_putw_at (s, pos, total_attr_len);
}
@@ -332,37 +335,38 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
peer->scount[afi][safi]--;
bgp_adj_out_remove (rn, adj, peer, afi, safi);
- bgp_unlock_node (rn);
if (! (afi == AFI_IP && safi == SAFI_UNICAST))
break;
}
- if (! stream_empty (s))
+ if (stream_empty (s))
+ return NULL ;
+
+ if (afi == AFI_IP && safi == SAFI_UNICAST)
{
- if (afi == AFI_IP && safi == SAFI_UNICAST)
- {
- unfeasible_len
+ unfeasible_len
= stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN;
- stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len);
- stream_putw (s, 0);
- }
- bgp_packet_set_size (s);
- packet = stream_dup (s);
- bgp_packet_add (peer, packet);
- stream_reset (s);
- return packet;
- }
+ stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len);
+ stream_putw (s, 0);
+ } ;
- return NULL;
+ bgp_packet_set_size (s);
+ return s ;
}
+/*------------------------------------------------------------------------------
+ * Construct an update for the default route, place it in the obuf queue
+ * and kick write.
+ *
+ * Uses peer->work stream structure, but copies result to new stream, which is
+ * pushed onto the obuf queue.
+ */
void
bgp_default_update_send (struct peer *peer, struct attr *attr,
afi_t afi, safi_t safi, struct peer *from)
{
struct stream *s;
- struct stream *packet;
struct prefix p;
unsigned long pos;
bgp_size_t total_attr_len;
@@ -375,7 +379,7 @@ bgp_default_update_send (struct peer *peer, struct attr *attr,
if (afi == AFI_IP)
str2prefix ("0.0.0.0/0", &p);
#ifdef HAVE_IPV6
- else
+ else
str2prefix ("::/0", &p);
#endif /* HAVE_IPV6 */
@@ -388,7 +392,8 @@ bgp_default_update_send (struct peer *peer, struct attr *attr,
p.prefixlen, attrstr);
}
- s = stream_new (BGP_MAX_PACKET_SIZE);
+ s = peer->work ;
+ stream_reset (s);
/* Make BGP update packet. */
bgp_packet_set_marker (s, BGP_MSG_UPDATE);
@@ -411,25 +416,26 @@ bgp_default_update_send (struct peer *peer, struct attr *attr,
/* Set size. */
bgp_packet_set_size (s);
- packet = stream_dup (s);
- stream_free (s);
-
/* Dump packet if debug option is set. */
#ifdef DEBUG
- /* bgp_packet_dump (packet); */
+ /* bgp_packet_dump (s); */
#endif /* DEBUG */
/* Add packet to the peer. */
- bgp_packet_add (peer, packet);
-
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ bgp_write(peer, s);
}
+/*------------------------------------------------------------------------------
+ * Construct a withdraw update for the default route, place it in the obuf
+ * queue and kick write.
+ *
+ * Uses peer->work stream structure, but copies result to new stream, which is
+ * pushed onto the obuf queue.
+ */
void
bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
{
struct stream *s;
- struct stream *packet;
struct prefix p;
unsigned long pos;
unsigned long cp;
@@ -443,7 +449,7 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
if (afi == AFI_IP)
str2prefix ("0.0.0.0/0", &p);
#ifdef HAVE_IPV6
- else
+ else
str2prefix ("::/0", &p);
#endif /* HAVE_IPV6 */
@@ -455,7 +461,8 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ),
p.prefixlen);
- s = stream_new (BGP_MAX_PACKET_SIZE);
+ s = peer->work ;
+ stream_reset (s);
/* Make BGP update packet. */
bgp_packet_set_marker (s, BGP_MSG_UPDATE);
@@ -489,16 +496,18 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
bgp_packet_set_size (s);
- packet = stream_dup (s);
- stream_free (s);
-
/* Add packet to the peer. */
- bgp_packet_add (peer, packet);
-
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ bgp_write(peer, s);
}
-/* Get next packet to be written. */
+/*------------------------------------------------------------------------------
+ * Get next update message to be written.
+ *
+ * Generates complete BGP message in the peer->work stream structure.
+ *
+ * Returns: peer->work -- if have something to be written.
+ * NULL -- otherwise
+ */
static struct stream *
bgp_write_packet (struct peer *peer)
{
@@ -507,14 +516,10 @@ bgp_write_packet (struct peer *peer)
struct stream *s = NULL;
struct bgp_advertise *adv;
- s = stream_fifo_head (peer->obuf);
- if (s)
- return s;
-
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
- adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw);
+ adv = bgp_advertise_fifo_head(&peer->sync[afi][safi]->withdraw);
if (adv)
{
s = bgp_withdraw_packet (peer, afi, safi);
@@ -522,11 +527,11 @@ bgp_write_packet (struct peer *peer)
return s;
}
}
-
+
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
- adv = FIFO_HEAD (&peer->sync[afi][safi]->update);
+ adv = bgp_advertise_fifo_head(&peer->sync[afi][safi]->update);
if (adv)
{
if (adv->binfo && adv->binfo->uptime < peer->synctime)
@@ -563,6 +568,7 @@ bgp_write_packet (struct peer *peer)
return NULL;
}
+#if 0
/* Is there partially written packet or updates we can send right
now. */
static int
@@ -588,118 +594,52 @@ bgp_write_proceed (struct peer *peer)
return 0;
}
+#endif
-/* Write packet to the peer. */
+/*------------------------------------------------------------------------------
+ * Write packets to the peer -- subject to the XON flow control.
+ *
+ * Takes an optional stream argument, if not NULL then must be peer->work,
+ * in which there is a message to be sent.
+ *
+ * Then processes the peer->sync structure to generate further updates.
+ *
+ * TODO: work out how bgp_routeadv_timer fits into this.
+ */
int
-bgp_write (struct thread *thread)
+bgp_write (bgp_peer peer, struct stream* s)
{
- struct peer *peer;
- u_char type;
- struct stream *s;
- int num;
- unsigned int count = 0;
-
- /* Yes first of all get peer pointer. */
- peer = THREAD_ARG (thread);
- peer->t_write = NULL;
-
- /* For non-blocking IO check. */
- if (peer->status == Connect)
- {
- bgp_connect_check (peer);
- return 0;
- }
-
- s = bgp_write_packet (peer);
- if (!s)
- return 0; /* nothing to send */
-
- sockopt_cork (peer->fd, 1);
+ if (s != NULL)
+ stream_fifo_push(peer->obuf, stream_dup(s)) ;
- /* Nonblocking write until TCP output buffer is full. */
- do
+ while (bgp_session_is_XON(peer))
{
- int writenum;
+ s = bgp_write_packet(peer); /* uses peer->work */
+ if (s == NULL)
+ break;
- /* Number of bytes to be sent. */
- writenum = stream_get_endp (s) - stream_get_getp (s);
+ stream_fifo_push (peer->obuf, stream_dup(s)) ;
- /* Call write() system call. */
- num = write (peer->fd, STREAM_PNT (s), writenum);
- if (num < 0)
- {
- /* write failed either retry needed or error */
- if (ERRNO_IO_RETRY(errno))
- break;
-
- BGP_EVENT_ADD (peer, TCP_fatal_error);
- return 0;
- }
+ /* Count down flow control, send fifo if hits BGP_XON_KICK */
+ if (bgp_session_dec_flow_count(peer))
+ bgp_session_update_send(peer->session, peer->obuf) ;
+ } ;
- if (num != writenum)
- {
- /* Partial write */
- stream_forward_getp (s, num);
- break;
- }
-
- /* Retrieve BGP packet type. */
- stream_set_getp (s, BGP_MARKER_SIZE + 2);
- type = stream_getc (s);
-
- switch (type)
- {
- case BGP_MSG_OPEN:
- peer->open_out++;
- break;
- case BGP_MSG_UPDATE:
- peer->update_out++;
- break;
- case BGP_MSG_NOTIFY:
- peer->notify_out++;
- /* Double start timer. */
- peer->v_start *= 2;
-
- /* Overflow check. */
- if (peer->v_start >= (60 * 2))
- peer->v_start = (60 * 2);
-
- /* Flush any existing events */
- BGP_EVENT_ADD (peer, BGP_Stop);
- return 0;
- case BGP_MSG_KEEPALIVE:
- peer->keepalive_out++;
- break;
- case BGP_MSG_ROUTE_REFRESH_NEW:
- case BGP_MSG_ROUTE_REFRESH_OLD:
- peer->refresh_out++;
- break;
- case BGP_MSG_CAPABILITY:
- peer->dynamic_cap_out++;
- break;
- }
+ /* In any case, send what's in the FIFO */
+ if (stream_fifo_head(peer->obuf) != NULL)
+ bgp_session_update_send(peer->session, peer->obuf) ;
- /* OK we send packet so delete it. */
- bgp_packet_delete (peer);
- }
- while (++count < BGP_WRITE_PACKET_MAX &&
- (s = bgp_write_packet (peer)) != NULL);
-
- if (bgp_write_proceed (peer))
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
- else
- sockopt_cork (peer->fd, 0);
-
return 0;
}
+#if 0
/* This is only for sending NOTIFICATION message to neighbor. */
static int
bgp_write_notify (struct peer *peer)
{
int ret, val;
u_char type;
- struct stream *s;
+ struct stream *s;
/* There should be at least one packet. */
s = stream_fifo_head (peer->obuf);
@@ -738,7 +678,9 @@ bgp_write_notify (struct peer *peer)
return 0;
}
+#endif
+#if 0
/* Make keepalive packet and send it to the peer. */
void
bgp_keepalive_send (struct peer *peer)
@@ -756,19 +698,20 @@ bgp_keepalive_send (struct peer *peer)
/* Dump packet if debug option is set. */
/* bgp_packet_dump (s); */
-
- if (BGP_DEBUG (keepalive, KEEPALIVE))
- zlog_debug ("%s sending KEEPALIVE", peer->host);
+
+ if (BGP_DEBUG (keepalive, KEEPALIVE))
+ zlog_debug ("%s sending KEEPALIVE", peer->host);
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s send message type %d, length (incl. header) %d",
peer->host, BGP_MSG_KEEPALIVE, length);
/* Add packet to the peer. */
bgp_packet_add (peer, s);
-
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ bgp_write(peer);
}
+#endif
+#if 0
/* Make open packet and send it to the peer. */
void
bgp_open_send (struct peer *peer)
@@ -785,9 +728,9 @@ bgp_open_send (struct peer *peer)
/* local-as Change */
if (peer->change_local_as)
- local_as = peer->change_local_as;
+ local_as = peer->change_local_as;
else
- local_as = peer->local_as;
+ local_as = peer->local_as;
s = stream_new (BGP_MAX_PACKET_SIZE);
@@ -796,7 +739,7 @@ bgp_open_send (struct peer *peer)
/* Set open packet values. */
stream_putc (s, BGP_VERSION_4); /* BGP version */
- stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as
+ stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as
: BGP_AS_TRANS);
stream_putw (s, send_holdtime); /* Hold Time */
stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */
@@ -808,9 +751,9 @@ bgp_open_send (struct peer *peer)
length = bgp_packet_set_size (s);
if (BGP_DEBUG (normal, NORMAL))
- zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s",
+ zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s",
peer->host, BGP_VERSION_4, local_as,
- send_holdtime, inet_ntoa (peer->local_id));
+ send_holdtime, safe_inet_ntoa (peer->local_id));
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s send message type %d, length (incl. header) %d",
@@ -821,105 +764,89 @@ bgp_open_send (struct peer *peer)
/* Add packet to the peer. */
bgp_packet_add (peer, s);
-
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ bgp_write(peer);
}
+#endif
+
+/* Send route refresh message to the peer. */
-/* Send BGP notify packet with data potion. */
void
-bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code,
- u_char *data, size_t datalen)
+bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
+ u_char orf_type, u_char when_to_refresh, int remove)
{
- struct stream *s;
- int length;
-
- /* Allocate new stream. */
- s = stream_new (BGP_MAX_PACKET_SIZE);
+ bgp_route_refresh rr = NULL;
+ struct bgp_filter *filter = NULL;
+ bgp_session session = peer->session;
+ bgp_orf_entry orfpe = NULL;
+ struct prefix_list *plist = NULL;
+ struct orf_prefix orfp;
+ vector_index i;
+ int orf_refresh = 0;
+ enum prefix_list_type pe_type;
- /* Make nitify packet. */
- bgp_packet_set_marker (s, BGP_MSG_NOTIFY);
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
- /* Set notify packet values. */
- stream_putc (s, code); /* BGP notify code */
- stream_putc (s, sub_code); /* BGP notify sub_code */
+ filter = &peer->filter[afi][safi];
- /* If notify data is present. */
- if (data)
- stream_write (s, data, datalen);
-
- /* Set BGP packet length. */
- length = bgp_packet_set_size (s);
-
- /* Add packet to the peer. */
- stream_fifo_clean (peer->obuf);
- bgp_packet_add (peer, s);
+ /* Adjust safi code. */
+ if (safi == SAFI_MPLS_VPN)
+ safi = BGP_SAFI_VPNV4;
- /* For debug */
- {
- struct bgp_notify bgp_notify;
- int first = 0;
- int i;
- char c[4];
+ rr = bgp_route_refresh_new(afi, safi, 1);
+ rr->defer = (when_to_refresh == REFRESH_DEFER);
- bgp_notify.code = code;
- bgp_notify.subcode = sub_code;
- bgp_notify.data = NULL;
- bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE;
-
- if (bgp_notify.length)
+ if (orf_type == ORF_TYPE_PREFIX
+ || orf_type == ORF_TYPE_PREFIX_OLD)
+ if (remove || filter->plist[FILTER_IN].ref)
{
- bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3);
- for (i = 0; i < bgp_notify.length; i++)
- if (first)
- {
- sprintf (c, " %02x", data[i]);
- strcat (bgp_notify.data, c);
- }
- else
- {
- first = 1;
- sprintf (c, "%02x", data[i]);
- strcpy (bgp_notify.data, c);
- }
+ orf_refresh = 1;
+ if (remove)
+ {
+ UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
+ bgp_orf_add_remove_all(rr, BGP_ORF_T_PREFIX, bgp_form_none);
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s)"
+ " for afi/safi: %d/%d",
+ peer->host, orf_type,
+ (when_to_refresh == REFRESH_DEFER)
+ ? "defer"
+ : "immediate",
+ afi, safi);
+ }
+ else
+ {
+ SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
+ plist = prefix_list_ref_plist(filter->plist[FILTER_IN].ref) ;
+ for (i = 0; prefix_bgp_orf_get(plist, i, &orfp, &pe_type); ++i)
+ {
+ orfpe = bgp_orf_add(rr, BGP_ORF_T_PREFIX, bgp_form_none, 0,
+ pe_type == PREFIX_DENY);
+ orfpe->body.orf_prefix = orfp;
+ }
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d)"
+ " (%s) for afi/safi: %d/%d",
+ peer->host, orf_type,
+ (when_to_refresh == REFRESH_DEFER)
+ ? "defer"
+ : "immediate",
+ afi, safi);
+ }
}
- bgp_notify_print (peer, &bgp_notify, "sending");
- if (bgp_notify.data)
- XFREE (MTYPE_TMP, bgp_notify.data);
- }
if (BGP_DEBUG (normal, NORMAL))
- zlog_debug ("%s send message type %d, length (incl. header) %d",
- peer->host, BGP_MSG_NOTIFY, length);
-
- /* peer reset cause */
- if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE)
{
- if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET)
- peer->last_reset = PEER_DOWN_USER_RESET;
- else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN)
- peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
- else
- peer->last_reset = PEER_DOWN_NOTIFY_SEND;
+ if (! orf_refresh)
+ zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d",
+ peer->host, afi, safi);
}
- /* Call imidiately. */
- BGP_WRITE_OFF (peer->t_write);
-
- bgp_write_notify (peer);
-}
+ bgp_session_route_refresh_send(session, rr);
-/* Send BGP notify packet. */
-void
-bgp_notify_send (struct peer *peer, u_char code, u_char sub_code)
-{
- bgp_notify_send_with_data (peer, code, sub_code, NULL, 0);
-}
+#if 0
+ /* old code */
-/* Send route refresh message to the peer. */
-void
-bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
- u_char orf_type, u_char when_to_refresh, int remove)
-{
struct stream *s;
struct stream *packet;
int length;
@@ -934,7 +861,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
/* Adjust safi code. */
if (safi == SAFI_MPLS_VPN)
safi = BGP_SAFI_VPNV4;
-
+
s = stream_new (BGP_MAX_PACKET_SIZE);
/* Make BGP update packet. */
@@ -947,15 +874,15 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
stream_putw (s, afi);
stream_putc (s, 0);
stream_putc (s, safi);
-
+
if (orf_type == ORF_TYPE_PREFIX
|| orf_type == ORF_TYPE_PREFIX_OLD)
- if (remove || filter->plist[FILTER_IN].plist)
+ if (remove || filter->plist[FILTER_IN].ref)
{
u_int16_t orf_len;
unsigned long orfp;
- orf_refresh = 1;
+ orf_refresh = 1;
stream_putc (s, when_to_refresh);
stream_putc (s, orf_type);
orfp = stream_get_endp (s);
@@ -966,7 +893,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
stream_putc (s, ORF_COMMON_PART_REMOVE_ALL);
if (BGP_DEBUG (normal, NORMAL))
- zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d",
+ zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d",
peer->host, orf_type,
(when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"),
afi, safi);
@@ -974,11 +901,11 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
else
{
SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
- prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].plist,
+ prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].ref,
ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT,
ORF_COMMON_PART_DENY);
if (BGP_DEBUG (normal, NORMAL))
- zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d",
+ zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d",
peer->host, orf_type,
(when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"),
afi, safi);
@@ -995,7 +922,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
if (BGP_DEBUG (normal, NORMAL))
{
if (! orf_refresh)
- zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d",
+ zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d",
peer->host, afi, safi);
zlog_debug ("%s send message type %d, length (incl. header) %d",
peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ?
@@ -1008,24 +935,27 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
/* Add packet to the peer. */
bgp_packet_add (peer, packet);
-
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ bgp_write(peer);
+#endif
}
/* Send capability message to the peer. */
+
+/* TODO: require BGP Engine support for Dynamic Capability messages. */
+
void
bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi,
int capability_code, int action)
{
struct stream *s;
- struct stream *packet;
int length;
/* Adjust safi code. */
if (safi == SAFI_MPLS_VPN)
safi = BGP_SAFI_VPNV4;
- s = stream_new (BGP_MAX_PACKET_SIZE);
+ s = peer->work;
+ stream_reset (s);
/* Make BGP update packet. */
bgp_packet_set_marker (s, BGP_MSG_CAPABILITY);
@@ -1049,20 +979,15 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi,
/* Set packet size. */
length = bgp_packet_set_size (s);
- /* Make real packet. */
- packet = stream_dup (s);
- stream_free (s);
-
- /* Add packet to the peer. */
- bgp_packet_add (peer, packet);
-
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s send message type %d, length (incl. header) %d",
- peer->host, BGP_MSG_CAPABILITY, length);
+ peer->host, BGP_MSG_CAPABILITY, length);
- BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ /* Add packet to the peer. */
+ bgp_write(peer, s);
}
-
+
+#if 0
/* RFC1771 6.8 Connection collision detection. */
static int
bgp_collision_detect (struct peer *new, struct in_addr remote_id)
@@ -1074,7 +999,7 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id)
bgp = bgp_get_default ();
if (! bgp)
return 0;
-
+
/* Upon receipt of an OPEN message, the local system must examine
all of its connections that are in the OpenConfirm state. A BGP
speaker may also examine connections in an OpenSent state if it
@@ -1118,7 +1043,7 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id)
OpenConfirm state). */
if (new->fd >= 0)
- bgp_notify_send (new, BGP_NOTIFY_CEASE,
+ bgp_notify_send (new, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
return -1;
}
@@ -1126,7 +1051,9 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id)
}
return 0;
}
+#endif
+#if 0
static int
bgp_open_receive (struct peer *peer, bgp_size_t size)
{
@@ -1144,7 +1071,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
u_int8_t notify_data_remote_id[4];
realpeer = NULL;
-
+
/* Parse open packet. */
version = stream_getc (peer->ibuf);
memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2);
@@ -1158,21 +1085,21 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u,"
" holdtime %d, id %s",
peer->host, version, remote_as, holdtime,
- inet_ntoa (remote_id));
-
+ safe_inet_ntoa (remote_id));
+
/* BEGIN to read the capability here, but dont do it yet */
capability = 0;
optlen = stream_getc (peer->ibuf);
-
+
if (optlen != 0)
{
/* We need the as4 capability value *right now* because
* if it is there, we have not got the remote_as yet, and without
* that we do not know which peer is connecting to us now.
- */
+ */
as4 = peek_for_as4_capability (peer, optlen);
}
-
+
/* Just in case we have a silly peer who sends AS4 capability set to 0 */
if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4)
{
@@ -1182,7 +1109,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
BGP_NOTIFY_OPEN_BAD_PEER_AS);
return -1;
}
-
+
if (remote_as == BGP_AS_TRANS)
{
/* Take the AS4 from the capability. We must have received the
@@ -1197,7 +1124,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
BGP_NOTIFY_OPEN_BAD_PEER_AS);
return -1;
}
-
+
if (!as4 && BGP_DEBUG (as4, AS4))
zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4."
" Odd, but proceeding.", peer->host);
@@ -1206,8 +1133,8 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
"in 2-bytes, very odd peer.", peer->host, as4);
if (as4)
remote_as = as4;
- }
- else
+ }
+ else
{
/* We may have a partner with AS4 who has an asno < BGP_AS_MAX */
/* If we have got the capability, peer->as4cap must match remote_as */
@@ -1240,8 +1167,8 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
{
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s bad OPEN, wrong router identifier %s",
- peer->host, inet_ntoa (remote_id));
- bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR,
+ peer->host, safe_inet_ntoa (remote_id));
+ bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_BAD_BGP_IDENT,
notify_data_remote_id, 4);
}
@@ -1278,7 +1205,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
&& realpeer->status != OpenConfirm
&& realpeer->status != Connect)
{
- /* XXX: This is an awful problem..
+ /* XXX: This is an awful problem..
*
* According to the RFC we should just let this connection (of the
* accepted 'peer') continue on to Established if the other
@@ -1296,7 +1223,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
* Active
* OpenSent OpenSent
* <arrive here,
- * Notify, delete>
+ * Notify, delete>
* Idle Active
* OpenSent OpenSent
* <arrive here,
@@ -1312,13 +1239,13 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
* exacerbated by high-latency (in bgpd and/or the network).
*
* The reason we do this is because our FSM is tied to our peer
- * structure, which carries our configuration information, etc.
+ * structure, which carries our configuration information, etc.
* I.e. we can't let the accepted-peer FSM continue on as it is,
* cause it's not associated with any actual peer configuration -
* it's just a dummy.
*
* It's possible we could hack-fix this by just bgp_stop'ing the
- * realpeer and continueing on with the 'transfer FSM' below.
+ * realpeer and continueing on with the 'transfer FSM' below.
* Ideally, we need to seperate FSMs from struct peer.
*
* Setting one side to passive avoids the race, as a workaround.
@@ -1335,11 +1262,11 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
if (BGP_DEBUG (events, EVENTS))
zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)",
- peer->host,
+ peer->host,
LOOKUP (bgp_status_msg, realpeer->status));
bgp_stop (realpeer);
-
+
/* Transfer file descriptor. */
realpeer->fd = peer->fd;
peer->fd = -1;
@@ -1353,7 +1280,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
/* Transfer status. */
realpeer->status = peer->status;
bgp_stop (peer);
-
+
/* peer pointer change. Open packet send to neighbor. */
peer = realpeer;
bgp_open_send (peer);
@@ -1373,9 +1300,9 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
{
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s bad OPEN, wrong router identifier %s",
- peer->host, inet_ntoa (remote_id));
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_OPEN_ERR,
+ peer->host, safe_inet_ntoa (remote_id));
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_BAD_BGP_IDENT,
notify_data_remote_id, 4);
return -1;
@@ -1391,8 +1318,8 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s bad protocol version, remote requested %d, local request %d",
peer->host, version, BGP_VERSION_4);
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_OPEN_ERR,
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNSUP_VERSION,
&maxver, 1);
return -1;
@@ -1404,8 +1331,8 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s bad OPEN, remote AS is %u, expected %u",
peer->host, remote_as, peer->as);
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_OPEN_ERR,
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_BAD_PEER_AS,
notify_data_remote_as, 2);
return -1;
@@ -1420,11 +1347,11 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
if (holdtime < 3 && holdtime != 0)
{
bgp_notify_send (peer,
- BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNACEP_HOLDTIME);
return -1;
}
-
+
/* From the rfc: A reasonable maximum time between KEEPALIVE messages
would be one third of the Hold Time interval. KEEPALIVE messages
MUST NOT be sent more frequently than one per second. An
@@ -1444,7 +1371,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
peer->v_keepalive = peer->v_holdtime / 3;
/* Open option part parse. */
- if (optlen != 0)
+ if (optlen != 0)
{
ret = bgp_open_option_parse (peer, optlen, &capability);
if (ret < 0)
@@ -1477,9 +1404,10 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
return 0;
}
+#endif
/* Parse BGP Update packet and make attribute object. */
-static int
+int
bgp_update_receive (struct peer *peer, bgp_size_t size)
{
int ret;
@@ -1494,13 +1422,14 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
struct bgp_nlri mp_update;
struct bgp_nlri mp_withdraw;
char attrstr[BUFSIZ] = "";
+ bgp_attr_parse_ret_t ap_ret ;
/* Status must be Established. */
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
{
zlog_err ("%s [FSM] Update packet received under status %s",
- peer->host, LOOKUP (bgp_status_msg, peer->status));
- bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0);
+ peer->host, LOOKUP (bgp_peer_status_msg, peer->state));
+ bgp_peer_down_error (peer, BGP_NOTIFY_FSM_ERR, 0);
return -1;
}
@@ -1523,8 +1452,8 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
zlog_err ("%s [Error] Update packet error"
" (packet length is short for unfeasible length)",
peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MAL_ATTR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
return -1;
}
@@ -1537,8 +1466,8 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
zlog_err ("%s [Error] Update packet error"
" (packet unfeasible length overflow %d)",
peer->host, withdraw_len);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MAL_ATTR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
return -1;
}
@@ -1558,15 +1487,15 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
withdraw.length = withdraw_len;
stream_forward_getp (s, withdraw_len);
}
-
+
/* Attribute total length check. */
if (stream_pnt (s) + 2 > end)
{
zlog_warn ("%s [Error] Packet Error"
" (update packet is short for attribute length)",
peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MAL_ATTR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
return -1;
}
@@ -1579,42 +1508,41 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
zlog_warn ("%s [Error] Packet Error"
" (update packet attribute length overflow %d)",
peer->host, attribute_len);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_MAL_ATTR);
+ bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
return -1;
}
-
+
/* Certain attribute parsing errors should not be considered bad enough
* to reset the session for, most particularly any partial/optional
* attributes that have 'tunneled' over speakers that don't understand
* them. Instead we withdraw only the prefix concerned.
- *
+ *
* Complicates the flow a little though..
*/
- bgp_attr_parse_ret_t attr_parse_ret = BGP_ATTR_PARSE_PROCEED;
+ ap_ret = BGP_ATTR_PARSE_PROCEED;
+
/* This define morphs the update case into a withdraw when lower levels
* have signalled an error condition where this is best.
*/
-#define NLRI_ATTR_ARG (attr_parse_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL)
+#define NLRI_ATTR_ARG (ap_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL)
/* Parse attribute when it exists. */
if (attribute_len)
{
- attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len,
- &mp_update, &mp_withdraw);
- if (attr_parse_ret == BGP_ATTR_PARSE_ERROR)
+ ap_ret = bgp_attr_parse (peer, &attr, attribute_len,
+ &mp_update, &mp_withdraw);
+ if (ap_ret == BGP_ATTR_PARSE_ERROR)
return -1;
}
-
+
/* Logging the attribute. */
- if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW
- || BGP_DEBUG (update, UPDATE_IN))
+ if ((ap_ret == BGP_ATTR_PARSE_WITHDRAW) || BGP_DEBUG (update, UPDATE_IN))
{
ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ);
- int lvl = (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW)
- ? LOG_ERR : LOG_DEBUG;
-
- if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW)
+ int lvl = (ap_ret == BGP_ATTR_PARSE_WITHDRAW) ? LOG_ERR : LOG_DEBUG ;
+
+ if (ap_ret == BGP_ATTR_PARSE_WITHDRAW)
zlog (peer->log, LOG_ERR,
"%s rcvd UPDATE with errors in attr(s)!! Withdrawing route.",
peer->host);
@@ -1623,7 +1551,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
zlog (peer->log, lvl, "%s rcvd UPDATE w/ attr: %s",
peer->host, attrstr);
}
-
+
/* Network Layer Reachability Information. */
update_len = end - stream_pnt (s);
@@ -1633,9 +1561,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), update_len);
if (ret < 0)
{
- bgp_attr_unintern_sub (&attr);
- if (attr.extra)
- bgp_attr_extra_free (&attr);
+ bgp_attr_unintern_sub (&attr, true) ; /* true => free extra */
return -1;
}
@@ -1661,9 +1587,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
ret = bgp_attr_check (peer, &attr);
if (ret < 0)
{
- bgp_attr_unintern_sub (&attr);
- if (attr.extra)
- bgp_attr_extra_free (&attr);
+ bgp_attr_unintern_sub (&attr, true) ; /* true => free extra */
return -1;
}
@@ -1671,12 +1595,12 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
}
if (mp_update.length
- && mp_update.afi == AFI_IP
+ && mp_update.afi == AFI_IP
&& mp_update.safi == SAFI_UNICAST)
bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update);
if (mp_withdraw.length
- && mp_withdraw.afi == AFI_IP
+ && mp_withdraw.afi == AFI_IP
&& mp_withdraw.safi == SAFI_UNICAST)
bgp_nlri_parse (peer, NULL, &mp_withdraw);
@@ -1691,19 +1615,20 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
bgp_clear_stale_route (peer, AFI_IP, SAFI_UNICAST);
if (BGP_DEBUG (normal, NORMAL))
- zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Unicast from %s",
+ zlog (peer->log, LOG_DEBUG,
+ "rcvd End-of-RIB for IPv4 Unicast from %s",
peer->host);
}
}
if (peer->afc[AFI_IP][SAFI_MULTICAST])
{
if (mp_update.length
- && mp_update.afi == AFI_IP
+ && mp_update.afi == AFI_IP
&& mp_update.safi == SAFI_MULTICAST)
bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update);
if (mp_withdraw.length
- && mp_withdraw.afi == AFI_IP
+ && mp_withdraw.afi == AFI_IP
&& mp_withdraw.safi == SAFI_MULTICAST)
bgp_nlri_parse (peer, NULL, &mp_withdraw);
@@ -1721,19 +1646,20 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
bgp_clear_stale_route (peer, AFI_IP, SAFI_MULTICAST);
if (BGP_DEBUG (normal, NORMAL))
- zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Multicast from %s",
+ zlog (peer->log, LOG_DEBUG,
+ "rcvd End-of-RIB for IPv4 Multicast from %s",
peer->host);
}
}
if (peer->afc[AFI_IP6][SAFI_UNICAST])
{
- if (mp_update.length
- && mp_update.afi == AFI_IP6
+ if (mp_update.length
+ && mp_update.afi == AFI_IP6
&& mp_update.safi == SAFI_UNICAST)
bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update);
- if (mp_withdraw.length
- && mp_withdraw.afi == AFI_IP6
+ if (mp_withdraw.length
+ && mp_withdraw.afi == AFI_IP6
&& mp_withdraw.safi == SAFI_UNICAST)
bgp_nlri_parse (peer, NULL, &mp_withdraw);
@@ -1743,26 +1669,28 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
&& mp_withdraw.length == 0)
{
/* End-of-RIB received */
- SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], PEER_STATUS_EOR_RECEIVED);
+ SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST],
+ PEER_STATUS_EOR_RECEIVED);
/* NSF delete stale route */
if (peer->nsf[AFI_IP6][SAFI_UNICAST])
bgp_clear_stale_route (peer, AFI_IP6, SAFI_UNICAST);
if (BGP_DEBUG (normal, NORMAL))
- zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Unicast from %s",
+ zlog (peer->log, LOG_DEBUG,
+ "rcvd End-of-RIB for IPv6 Unicast from %s",
peer->host);
}
}
if (peer->afc[AFI_IP6][SAFI_MULTICAST])
{
- if (mp_update.length
- && mp_update.afi == AFI_IP6
+ if (mp_update.length
+ && mp_update.afi == AFI_IP6
&& mp_update.safi == SAFI_MULTICAST)
bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update);
- if (mp_withdraw.length
- && mp_withdraw.afi == AFI_IP6
+ if (mp_withdraw.length
+ && mp_withdraw.afi == AFI_IP6
&& mp_withdraw.safi == SAFI_MULTICAST)
bgp_nlri_parse (peer, NULL, &mp_withdraw);
@@ -1778,19 +1706,20 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
bgp_clear_stale_route (peer, AFI_IP6, SAFI_MULTICAST);
if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Multicast from %s",
+ zlog (peer->log, LOG_DEBUG,
+ "rcvd End-of-RIB for IPv6 Multicast from %s",
peer->host);
}
}
if (peer->afc[AFI_IP][SAFI_MPLS_VPN])
{
- if (mp_update.length
- && mp_update.afi == AFI_IP
+ if (mp_update.length
+ && mp_update.afi == AFI_IP
&& mp_update.safi == BGP_SAFI_VPNV4)
bgp_nlri_parse_vpnv4 (peer, NLRI_ATTR_ARG, &mp_update);
- if (mp_withdraw.length
- && mp_withdraw.afi == AFI_IP
+ if (mp_withdraw.length
+ && mp_withdraw.afi == AFI_IP
&& mp_withdraw.safi == BGP_SAFI_VPNV4)
bgp_nlri_parse_vpnv4 (peer, NULL, &mp_withdraw);
@@ -1802,32 +1731,20 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
/* End-of-RIB received */
if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from %s",
+ zlog (peer->log, LOG_DEBUG,
+ "rcvd End-of-RIB for VPNv4 Unicast from %s",
peer->host);
}
}
/* Everything is done. We unintern temporary structures which
interned in bgp_attr_parse(). */
- bgp_attr_unintern_sub (&attr);
- if (attr.extra)
- bgp_attr_extra_free (&attr);
-
- /* If peering is stopped due to some reason, do not generate BGP
- event. */
- if (peer->status != Established)
- return 0;
-
- /* Increment packet counter. */
- peer->update_in++;
- peer->update_time = bgp_clock ();
-
- /* Generate BGP event. */
- BGP_EVENT_ADD (peer, Receive_UPDATE_message);
+ bgp_attr_unintern_sub (&attr, true) ; /* true => free extra */
return 0;
}
+#if 0
/* Notify message treatment function. */
static void
bgp_notify_receive (struct peer *peer, bgp_size_t size)
@@ -1901,17 +1818,95 @@ bgp_notify_receive (struct peer *peer, bgp_size_t size)
BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message);
}
+#endif
+#if 0
/* Keepalive treatment function -- get keepalive send keepalive */
static void
bgp_keepalive_receive (struct peer *peer, bgp_size_t size)
{
- if (BGP_DEBUG (keepalive, KEEPALIVE))
- zlog_debug ("%s KEEPALIVE rcvd", peer->host);
-
+ if (BGP_DEBUG (keepalive, KEEPALIVE))
+ zlog_debug ("%s KEEPALIVE rcvd", peer->host);
+
BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message);
}
+#endif
+
+/* Process incoming route refresh */
+void
+bgp_route_refresh_recv(bgp_peer peer, bgp_route_refresh rr)
+{
+ afi_t afi;
+ safi_t safi;
+ vector_index i;
+ char name[BUFSIZ];
+ int ret;
+
+ afi = rr->afi;
+ safi = rr->safi;
+
+ /* Adjust safi code. */
+ if (safi == BGP_SAFI_VPNV4)
+ safi = SAFI_MPLS_VPN;
+
+ /* ORF prefix-list name */
+ ret = snprintf (name, BUFSIZ, "%s.%d.%d", peer->host, afi, safi);
+ assert(ret < BUFSIZ);
+
+ if (rr->entries.end > 0)
+ {
+ for (i = 0; i < rr->entries.end; ++i)
+ {
+ bgp_orf_entry orfep = vector_slot(&rr->entries, i);
+
+ /* ignore unknown */
+ if (orfep->unknown)
+ continue;
+ if (orfep->orf_type == BGP_ORF_T_PREFIX)
+ {
+ if (orfep->remove_all)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcvd Remove-All pfxlist ORF request",
+ peer->host);
+ prefix_bgp_orf_remove_all (name);
+ break;
+ }
+
+ ret = prefix_bgp_orf_set (name, afi, &orfep->body.orf_prefix,
+ orfep->deny, orfep->remove);
+
+ if (ret != CMD_SUCCESS)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s Received misformatted prefixlist ORF."
+ "Remove All pfxlist", peer->host);
+ prefix_bgp_orf_remove_all (name);
+ break;
+ }
+
+ peer->orf_plist[afi][safi] =
+ prefix_list_lookup (AFI_ORF_PREFIX, name);
+ }
+ }
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s rcvd Refresh %s ORF request", peer->host,
+ rr->defer ? "Defer" : "Immediate");
+ if (rr->defer)
+ return;
+ }
+
+ /* First update is deferred until ORF or ROUTE-REFRESH is received */
+ if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH))
+ UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH);
+
+ /* Perform route refreshment to the peer */
+ bgp_announce_route (peer, afi, safi);
+}
+
+#if 0
/* Route refresh message is received. */
static void
bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
@@ -1926,24 +1921,23 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
{
plog_err (peer->log, "%s [Error] BGP route refresh is not enabled",
peer->host);
- bgp_notify_send (peer,
- BGP_NOTIFY_HEADER_ERR,
- BGP_NOTIFY_HEADER_BAD_MESTYPE);
+ bgp_peer_down_error (peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_BAD_MESTYPE);
return;
}
/* Status must be Established. */
- if (peer->status != Established)
+ if (peer->status != Established)
{
plog_err (peer->log,
"%s [Error] Route refresh packet received under status %s",
peer->host, LOOKUP (bgp_status_msg, peer->status));
- bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0);
+ bgp_peer_down_error (peer, BGP_NOTIFY_FSM_ERR, 0);
return;
}
s = peer->ibuf;
-
+
/* Parse packet. */
afi = stream_getw (s);
reserved = stream_getc (s);
@@ -1980,7 +1974,7 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5)
{
zlog_info ("%s ORF route refresh length error", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return;
}
@@ -1989,9 +1983,9 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
while ((stream_pnt (s) + 2) < end)
{
- orf_type = stream_getc (s);
+ orf_type = stream_getc (s);
orf_len = stream_getw (s);
-
+
/* orf_len in bounds? */
if ((stream_pnt (s) + orf_len) > end)
break; /* XXX: Notify instead?? */
@@ -2018,8 +2012,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
* and 7 bytes of ORF Address-filter entry from the stream
*/
if (orf_len < 7)
- break;
-
+ break;
+
/* ORF prefix-list name */
sprintf (name, "%s.%d.%d", peer->host, afi, safi);
@@ -2077,13 +2071,13 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d%s",
peer->host,
- (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"),
+ (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"),
(common & ORF_COMMON_PART_DENY ? "deny" : "permit"),
- orfp.seq,
+ orfp.seq,
inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, BUFSIZ),
orfp.p.prefixlen, orfp.ge, orfp.le,
ok ? "" : " MALFORMED");
-
+
if (ok)
ret = prefix_bgp_orf_set (name, afi, &orfp,
(common & ORF_COMMON_PART_DENY ? 0 : 1 ),
@@ -2117,6 +2111,7 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
/* Perform route refreshment to the peer */
bgp_announce_route (peer, afi, safi);
}
+#endif
static int
bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
@@ -2133,24 +2128,26 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
end = pnt + length;
while (pnt < end)
- {
+ {
/* We need at least action, capability code and capability length. */
if (pnt + 3 > end)
{
zlog_info ("%s Capability length error", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
action = *pnt;
hdr = (struct capability_header *)(pnt + 1);
-
+
/* Action value check. */
if (action != CAPABILITY_ACTION_SET
&& action != CAPABILITY_ACTION_UNSET)
{
zlog_info ("%s Capability Action Value error %d",
peer->host, action);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
@@ -2162,7 +2159,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
if ((pnt + hdr->length + 3) > end)
{
zlog_info ("%s Capability length error", peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ /* TODO: Is this the right notification ?? */
+ bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0);
return -1;
}
@@ -2178,7 +2176,7 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
/* Ignore capability when override-capability is set. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
continue;
-
+
if (!bgp_afi_safi_valid_indices (afi, &safi))
{
if (BGP_DEBUG (normal, NORMAL))
@@ -2186,15 +2184,15 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
"(%u/%u)", peer->host, afi, safi);
continue;
}
-
+
/* Address family check. */
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u",
peer->host,
- action == CAPABILITY_ACTION_SET
+ action == CAPABILITY_ACTION_SET
? "Advertising" : "Removing",
ntohs(mpc.afi) , mpc.safi);
-
+
if (action == CAPABILITY_ACTION_SET)
{
peer->afc_recv[afi][safi] = 1;
@@ -2208,11 +2206,20 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
{
peer->afc_recv[afi][safi] = 0;
peer->afc_nego[afi][safi] = 0;
+ bool completed ;
if (peer_active_nego (peer))
- bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL);
+ completed = bgp_clear_routes (peer, afi, safi, false);
else
+ {
+ completed = true ;
+ /* TODO: only used for unit tests. Test will need fixing */
+#if 0
BGP_EVENT_ADD (peer, BGP_Stop);
+#endif
+ } ;
+ /* if bgp_clear_routes does not complete. what do we do ? */
+ passert(completed) ;
}
}
else
@@ -2225,10 +2232,10 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
return 0;
}
-/* Dynamic Capability is received.
+/* Dynamic Capability is received.
*
* This is exported for unit-test purposes
- */
+ */ extern int bgp_capability_receive(struct peer*, bgp_size_t) ;
int
bgp_capability_receive (struct peer *peer, bgp_size_t size)
{
@@ -2245,25 +2252,26 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size)
{
plog_err (peer->log, "%s [Error] BGP dynamic capability is not enabled",
peer->host);
- bgp_notify_send (peer,
- BGP_NOTIFY_HEADER_ERR,
- BGP_NOTIFY_HEADER_BAD_MESTYPE);
+ bgp_peer_down_error (peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_BAD_MESTYPE);
return -1;
}
/* Status must be Established. */
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
{
plog_err (peer->log,
- "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status));
- bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0);
+ "%s [Error] Dynamic capability packet received under status %s",
+ peer->host, LOOKUP (bgp_status_msg, peer->state));
+ bgp_peer_down_error (peer, BGP_NOTIFY_FSM_ERR, 0);
return -1;
}
/* Parse packet. */
return bgp_capability_msg_parse (peer, pnt, size);
}
-
+
+#if 0
/* BGP read utility function. */
static int
bgp_read_packet (struct peer *peer)
@@ -2281,7 +2289,7 @@ bgp_read_packet (struct peer *peer)
nbytes = stream_read_try (peer->ibuf, peer->fd, readsize);
/* If read byte is smaller than zero then error occured. */
- if (nbytes < 0)
+ if (nbytes < 0)
{
/* Transient error should retry */
if (nbytes == -2)
@@ -2290,7 +2298,7 @@ bgp_read_packet (struct peer *peer)
plog_err (peer->log, "%s [Error] bgp_read_packet error: %s",
peer->host, safe_strerror (errno));
- if (peer->status == Established)
+ if (peer->status == Established)
{
if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE))
{
@@ -2303,16 +2311,16 @@ bgp_read_packet (struct peer *peer)
BGP_EVENT_ADD (peer, TCP_fatal_error);
return -1;
- }
+ }
/* When read byte is zero : clear bgp peer and return */
- if (nbytes == 0)
+ if (nbytes == 0)
{
if (BGP_DEBUG (events, EVENTS))
plog_debug (peer->log, "%s [Event] BGP connection closed fd %d",
peer->host, peer->fd);
- if (peer->status == Established)
+ if (peer->status == Established)
{
if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE))
{
@@ -2333,7 +2341,9 @@ bgp_read_packet (struct peer *peer)
return 0;
}
+#endif
+#if 0
/* Marker check. */
static int
bgp_marker_all_one (struct stream *s, int length)
@@ -2346,7 +2356,9 @@ bgp_marker_all_one (struct stream *s, int length)
return 1;
}
+#endif
+#if 0
/* Starting point of packet process function. */
int
bgp_read (struct thread *thread)
@@ -2386,7 +2398,7 @@ bgp_read (struct thread *thread)
ret = bgp_read_packet (peer);
/* Header read error or partial read packet. */
- if (ret < 0)
+ if (ret < 0)
goto done;
/* Get size and type. */
@@ -2403,15 +2415,14 @@ bgp_read (struct thread *thread)
if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE))
&& ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE))
{
- bgp_notify_send (peer,
- BGP_NOTIFY_HEADER_ERR,
- BGP_NOTIFY_HEADER_NOT_SYNC);
+ bgp_peer_down_error (peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_NOT_SYNC);
goto done;
}
/* BGP type check. */
- if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE
- && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE
+ if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE
+ && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE
&& type != BGP_MSG_ROUTE_REFRESH_NEW
&& type != BGP_MSG_ROUTE_REFRESH_OLD
&& type != BGP_MSG_CAPABILITY)
@@ -2440,7 +2451,7 @@ bgp_read (struct thread *thread)
if (BGP_DEBUG (normal, NORMAL))
plog_debug (peer->log,
"%s bad message length - %d for %s",
- peer->host, size,
+ peer->host, size,
type == 128 ? "ROUTE-REFRESH" :
bgp_type_str[(int) type]);
bgp_notify_send_with_data (peer,
@@ -2455,7 +2466,7 @@ bgp_read (struct thread *thread)
}
ret = bgp_read_packet (peer);
- if (ret < 0)
+ if (ret < 0)
goto done;
/* Get size and type again. */
@@ -2464,11 +2475,11 @@ bgp_read (struct thread *thread)
/* BGP packet dump function. */
bgp_dump_packet (peer, type, peer->ibuf);
-
+
size = (peer->packet_size - BGP_HEADER_SIZE);
/* Read rest of the packet and call each sort of packet routine */
- switch (type)
+ switch (type)
{
case BGP_MSG_OPEN:
peer->open_in++;
@@ -2506,7 +2517,8 @@ bgp_read (struct thread *thread)
{
if (BGP_DEBUG (events, EVENTS))
zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host);
- peer_delete (peer);
+ bgp_peer_delete (peer);
}
return 0;
}
+#endif
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index 8f0ebe31..6cf9a394 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -21,6 +21,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_PACKET_H
#define _QUAGGA_BGP_PACKET_H
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route_refresh.h"
+
#define BGP_NLRI_LENGTH 1U
#define BGP_TOTAL_ATTR_LEN 2U
#define BGP_UNFEASIBLE_LEN 2U
@@ -28,24 +31,21 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
/* When to refresh */
#define REFRESH_IMMEDIATE 1
-#define REFRESH_DEFER 2
+#define REFRESH_DEFER 2
/* ORF Common part flag */
-#define ORF_COMMON_PART_ADD 0x00
-#define ORF_COMMON_PART_REMOVE 0x80
-#define ORF_COMMON_PART_REMOVE_ALL 0xC0
-#define ORF_COMMON_PART_PERMIT 0x00
-#define ORF_COMMON_PART_DENY 0x20
+#define ORF_COMMON_PART_ADD 0x00
+/* TODO: BUG REPORT... ORF_COMMON_PART_REMOVE should be 0x40 ! */
+#define ORF_COMMON_PART_REMOVE 0x80
+/* TODO: BUG REPORT... ORF_COMMON_PART_REMOVE_ALL should be 0x80 ! */
+#define ORF_COMMON_PART_REMOVE_ALL 0xC0
+#define ORF_COMMON_PART_PERMIT 0x00
+#define ORF_COMMON_PART_DENY 0x20
/* Packet send and receive function prototypes. */
extern int bgp_read (struct thread *);
-extern int bgp_write (struct thread *);
+extern int bgp_write (bgp_peer peer, struct stream*);
-extern void bgp_keepalive_send (struct peer *);
-extern void bgp_open_send (struct peer *);
-extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t);
-extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t,
- u_int8_t *, size_t);
extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int);
extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int);
extern void bgp_default_update_send (struct peer *, struct attr *,
@@ -54,4 +54,8 @@ extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t);
extern int bgp_capability_receive (struct peer *, bgp_size_t);
+extern int bgp_update_receive (struct peer *peer, bgp_size_t size);
+
+extern void bgp_route_refresh_recv(bgp_peer peer, bgp_route_refresh rr);
+
#endif /* _QUAGGA_BGP_PACKET_H */
diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c
new file mode 100644
index 00000000..45d4ca0e
--- /dev/null
+++ b/bgpd/bgp_peer.c
@@ -0,0 +1,1941 @@
+/* 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 "bgpd/bgp_peer.h"
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_peer_index.h"
+#include "bgpd/bgpd.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_advertise.h"
+
+#include "linklist.h"
+#include "prefix.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "thread.h"
+#include "log.h"
+#include "stream.h"
+#include "memory.h"
+#include "plist.h"
+#include "mqueue.h"
+#include "workqueue.h"
+#include "if.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).
+ *
+ * [To support multi-instance BGP, might be nice to use the "update source"
+ * address as part of the name of a peer. But that is another story.]
+ *
+ *------------------------------------------------------------------------------
+ * The Peer State.
+ *
+ * The peer has a small number of states. Where there is a session, its state
+ * is a sub-state of the main peer state.
+ *
+ * The states of a peer are as follows:
+ *
+ * 1. bgp_peer_pIdle
+ *
+ * All peers start in this state.
+ *
+ * This is the case when:
+ *
+ * a. peer is administratively down/disabled/deactivated
+ *
+ * b. waiting for route flap or other such timer before reawakening.
+ *
+ * c. waiting for previous session to be closed -- in BGP Engine
+ *
+ * d. waiting for session to establish -- in BGP Engine
+ *
+ * The session states relate to the above as follows:
+ *
+ * sIdle, sDisabled or no session at all -- (a) or (b)
+ *
+ * sEnabled -- (d)
+ *
+ * sEstablished -- IMPOSSIBLE
+ *
+ * sLimping -- (c)
+ *
+ * Only when the peer is none of (a), (b) or (c) can a session be enabled
+ * and the session move to (d), sEnabled. So changes either in the state
+ * of the peer or in the state of the session have to check whether it
+ * is time to:
+ *
+ * 1. enable a new session
+ *
+ * 2. disable a session which has yet to reach sEstablished, but is
+ * now no longer correctly configured, or the like.
+ *
+ * 2. bgp_peer_pEstablished
+ *
+ * Reaches this state from pIdle when a session becomes established.
+ *
+ * This can only be the case when the session is sEstablished.
+ *
+ * If the session is stopped for any reason, issues a Disable request to
+ * the BGP Engine:
+ *
+ * peer -> pClearing
+ * session -> sLimping
+ *
+ * 3. bgp_peer_pClearing
+ *
+ * Reaches this state from pEstablished *only*, as above.
+ *
+ * In this state the session can only be:
+ *
+ * sLimping -- session disable has been sent to the BGP Engine.
+ * sDisabled -- session has been disabled by the BGP Engine
+ *
+ * Tidies up the peer, including clearing routes etc. Once the peer is
+ * completely tidy:
+ *
+ * peer -> pIdle
+ *
+ * On entry to pClearing the session will be sLimping, on exit it may be
+ * still sLimping, or have advanced to sDisabled.
+ *
+ * NB: while in pClearing the peer's routes and RIBs are being processed.
+ * All other parts of the peer may be modified... but mindful of the
+ * "background" task which is yet to complete.
+ *
+ * 4. bgp_peer_pDeleted
+ *
+ * This is an exotic state, reached only when a peer is being completely
+ * deleted.
+ *
+ * This state may be reached from any of the above.
+ *
+ * If there is an active session, it will be sLimping. When advances to
+ * sDisabled it will be deleted.
+ *
+ * The remaining tasks are to clear out routes, dismantle the peer
+ * structure and delete it. While that is happening, the peer is in this
+ * state.
+ */
+
+/* prototypes */
+
+static void bgp_session_has_established(bgp_session session);
+static void bgp_session_has_stopped(bgp_session session,
+ bgp_notify notification) ;
+static void bgp_session_has_disabled(bgp_session session);
+static void bgp_peer_stop (struct peer *peer, bool nsf) ;
+static void bgp_peer_reset_idle(struct peer *peer) ;
+static void bgp_peer_down_notify(bgp_peer peer, peer_down_t why_down,
+ bgp_notify notification) ;
+static void bgp_peer_shutdown(bgp_peer peer) ;
+static bgp_notify bgp_peer_map_peer_down(peer_down_t why_down) ;
+static peer_down_t bgp_peer_map_notification(bgp_notify notification) ;
+static void bgp_peer_free (struct peer *peer) ;
+static void bgp_peer_change_status (bgp_peer peer, bgp_peer_state_t new_state);
+static void bgp_peer_timers_set (struct peer *peer) ;
+static int bgp_routeadv_timer (struct thread *thread);
+static int bgp_graceful_restart_timer_expire (struct thread *thread);
+static void bgp_graceful_restart_timer_cancel (struct peer* peer) ;
+static int bgp_graceful_stale_timer_expire (struct thread *thread);
+static void bgp_graceful_stale_timer_cancel (struct peer* peer) ;
+
+
+/*==============================================================================
+ * Deal with change in session state -- mqueue_action function.
+ *
+ * Receives notifications from the BGP Engine a session event occurs.
+ *
+ * -- arg0 = session
+ * args = bgp_session_event_args
+ */
+void
+bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag)
+{
+
+ struct bgp_session_event_args * args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ if (flag == mqb_action)
+ {
+ /* Pull stuff into Routing Engine *private* fields in the session */
+
+ session->event = args->event ; /* last event */
+ session->err = args->err ; /* errno, if any */
+ session->ordinal = args->ordinal ; /* primary/secondary connection */
+
+ switch(args->event)
+ {
+ /* If now Established, then the BGP Engine has exchanged BGP Open
+ * messages, and received the KeepAlive that acknowledges our Open.
+ *
+ * Ignore this, however, if the session is sLimping -- which can
+ * happen when the session has been disabled, but it became established
+ * before the BGP Engine had seen the disable message.
+ */
+ case bgp_session_eEstablished:
+ assert(args->notification == NULL) ;
+
+ if (session->state == bgp_session_sLimping)
+ break ;
+
+ bgp_session_has_established(session);
+ break ;
+
+ /* If now Disabled, then the BGP Engine is acknowledging the a
+ * session disable, and the session is now disabled.
+ *
+ * If sent a notification with the disable request, then it is
+ * returned iff the notification was actually sent. Don't really
+ * care one way or the other.
+ *
+ * BEWARE: this may be the last thing that happens to the session
+ * and/or the related peer -- which may be deleted inside
+ * bgp_session_has_disabled().
+ */
+ case bgp_session_eDisabled:
+ bgp_session_has_disabled(session);
+ break ;
+
+ /* If now Stopped, then for some reason the BGP Engine has either
+ * stopped trying to connect, or the session has been stopped.
+ *
+ * Again we ignore this in sLimping.
+ */
+ default:
+ if (session->state == bgp_session_sLimping)
+ break ;
+
+ if (args->stopped)
+ bgp_session_has_stopped(session,
+ bgp_notify_take(&(args->notification))) ;
+ break ;
+ } ;
+ } ;
+
+ bgp_notify_free(args->notification) ; /* Discard any notification. */
+
+ mqb_free(mqb) ;
+}
+
+/*------------------------------------------------------------------------------
+ * BGP Session has been Established.
+ */
+static void
+bgp_session_has_established(bgp_session session)
+{
+ afi_t afi;
+ safi_t safi;
+ int nsf_af_count ;
+
+ bgp_peer peer = session->peer ;
+ assert(peer->session == session) ; /* Safety first */
+
+ /* Session state change -- Routing Engine private fields */
+ assert(session->state == bgp_session_sEnabled) ;
+
+ session->state = bgp_session_sEstablished ;
+ session->flow_control = BGP_XON_REFRESH; /* updates can be sent */
+
+ /* Peer state change.
+ *
+ * This stops all timers other than the Graceful Stale Timer.
+ */
+ bgp_peer_change_status (peer, bgp_peer_pEstablished);
+
+ /* Extracting information from shared fields. */
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ bgp_peer_open_state_receive(peer);
+
+ sockunion_set_dup(&peer->su_local, session->su_local) ;
+ sockunion_set_dup(&peer->su_remote, session->su_remote) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ /* Install next hop, as required. */
+ bgp_nexthop_set(peer->su_local, peer->su_remote, &peer->nexthop, peer) ;
+
+ /* Clear last notification data -- Routing Engine private field */
+ bgp_notify_unset(&(peer->session->notification));
+
+ /* Clear start timer value to default. */
+ /* TODO: figure out where to increase the IdleHoldTimer */
+ 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) ;
+
+ nsf_af_count = 0 ;
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
+ {
+ /* If the afi/safi has been negotiated, and have received Graceful
+ * Restart capability, and is Restarting, and will Gracefully Restart
+ * the afi/safi, then....
+ */
+ 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 have held onto routes for this afi/safi but forwarding has
+ * not been preserved, then clean out the stale routes.
+ *
+ * Set NSF for this address family for next time.
+ */
+ 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
+ {
+ /* Remove stale routes, if any for this afi/safi */
+ 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);
+ bgp_graceful_stale_timer_cancel(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);
+ }
+
+ /* 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);
+
+ /* Reset uptime, send current table. */
+ peer->uptime = bgp_clock ();
+
+ bgp_announce_route_all (peer);
+
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1);
+
+#ifdef HAVE_SNMP
+ bgpTrapEstablished (peer);
+#endif /* HAVE_SNMP */
+}
+
+/*------------------------------------------------------------------------------
+ * BGP Session has stopped of its own accord -> disable the peer & session.
+ *
+ * The BGP Engine has signalled that it has stopped the session. Response to
+ * that is to tell it to disable the session, and then wait in sLimping state
+ * until the BGP Engine completes the disable request and signals that.
+ *
+ * NB: takes responsibility for the notification.
+ *
+ * TODO: session stopped because we stopped it or because the other end did ?
+ * TODO: restore NSF !!
+ */
+static void
+bgp_session_has_stopped(bgp_session session, bgp_notify notification)
+{
+ peer_down_t why_down ;
+
+ bgp_peer peer = session->peer ;
+ assert(peer->session == session) ; /* Safety first */
+
+ assert(bgp_session_is_active(session)) ; /* "confused" if not */
+
+ if (session->state == bgp_session_sEstablished)
+ {
+ /* This code has been moved from where it was, in bgp_write */
+ /* TODO: not clear whether v_start handling is still correct */
+ peer->v_start *= 2;
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
+ } ;
+
+ if (notification == NULL)
+ why_down = PEER_DOWN_CLOSE_SESSION ;
+ else
+ {
+ if (notification->received)
+ why_down = PEER_DOWN_NOTIFY_RECEIVED ;
+ else
+ why_down = bgp_peer_map_notification(notification) ;
+ } ;
+
+ bgp_peer_down_notify(peer, why_down, notification) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Session is now disabled -> can now move peer on to next state.
+ *
+ * The BGP Engine has closed the session in response to a disable request, and
+ * no longer has an interest in it, and has signalled that.
+ */
+static void
+bgp_session_has_disabled(bgp_session session)
+{
+ bgp_peer peer = session->peer ;
+ assert(peer->session == session) ; /* Safety first */
+
+ assert(session->state == bgp_session_sLimping) ;
+
+ session->state = bgp_session_sDisabled ;
+
+ /* Immediately discard any other messages for this session. */
+ mqueue_revoke(routing_nexus->queue, session) ;
+
+ /* If the session is marked "delete_me", do that.
+ *
+ * Otherwise, Old session now gone, so re-enable peer if now possible.
+ */
+ if (session->delete_me)
+ bgp_session_delete(peer) ; /* NB: this may also delete the peer. */
+ else
+ bgp_peer_enable(peer);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Administrative BGP peer stop event -- stop pEstablished peer.
+ *
+ * MUST be pEstablished.
+ *
+ * Changes to pClearing and sets off to clear down all routes etc, subject to
+ * the required NSF.
+ *
+ * On exit the peer will be:
+ *
+ * - pIdle if the clearing of routes etc completed immediately.
+ *
+ * - pClearing if further work for clearing of routes has been scheduled
+ * for later.
+ *
+ * NB: Leaves any Max Prefix Timer running.
+ *
+ * Starts Graceful Restart and Stale Route timers iff NSF and at least one
+ * afi/safi is enabled for NSF.
+ */
+static void
+bgp_peer_stop (struct peer *peer, bool nsf)
+{
+ bool cleared ;
+
+ assert(peer->state == bgp_peer_pEstablished) ;
+
+ /* Change state to pClearing.
+ *
+ * Turns off all timers.
+ */
+ bgp_peer_change_status(peer, bgp_peer_pClearing) ;
+
+ peer->dropped++ ;
+ peer->resettime = bgp_clock () ;
+
+ /* 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]);
+
+ /* Clear out routes, with NSF if required.
+ *
+ * Sets PEER_STATUS_NSF_WAIT iff NSF and at least one afi/safi is enabled
+ * for NSF. Clears PEER_STATUS_NSF_WAIT otherwise.
+ */
+ cleared = bgp_clear_all_routes (peer, nsf) ;
+
+ /* graceful restart */
+ 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);
+ } ;
+
+ /* Reset uptime. */
+ peer->uptime = bgp_clock ();
+
+#ifdef HAVE_SNMP
+ bgpTrapBackwardTransition (peer);
+#endif /* HAVE_SNMP */
+
+ /* Reset peer synctime */
+ peer->synctime = 0;
+
+ /* If completed the clearing of routes, then can now go pIdle */
+ if (cleared)
+ bgp_peer_change_status(peer, bgp_peer_pIdle) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear out any stale routes, cancel any Graceful Restart timers.
+ *
+ * NB: may still be pClearing from when peer went down leaving these stale
+ * routes.
+ *
+ * NB: assumes clearing stale routes will complete immediately !
+ */
+static void
+bgp_peer_clear_all_stale_routes (struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ 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);
+
+ bgp_graceful_restart_timer_cancel(peer) ;
+ bgp_graceful_stale_timer_cancel(peer) ;
+
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+} ;
+
+/*------------------------------------------------------------------------------
+ * If waiting for NSF peer to come back, stop now.
+ *
+ * When a session stops -- see bgp_peer_stop(), above -- the peer is set
+ * PEER_STATUS_NSF_WAIT iff there are now stale routes in the table, waiting
+ * for peer to come back.
+ *
+ * This function terminates that wait and clears out any stale routes, and
+ * cancels any timers.
+ *
+ * Also clears down all NSF flags.
+ *
+ * If is PEER_STATUS_NSF_WAIT, MUST be pIdle or pClearing.
+ *
+ * NB: may still be pClearing from when peer went down leaving these stale
+ * routes.
+ *
+ * NB: assumes clearing stale routes will complete immediately !
+ */
+static void
+bgp_peer_nsf_stop (struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
+ {
+ assert( (peer->state == bgp_peer_pIdle)
+ || (peer->state == bgp_peer_pClearing) ) ;
+
+ bgp_peer_clear_all_stale_routes (peer) ;
+ } ;
+
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ peer->nsf[afi][safi] = 0;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the PEER_FLAG_SHUTDOWN flag and also:
+ *
+ * - turn off any NSF and related timers.
+ *
+ * - turn off any Max Prefix overflow and related timers.
+ */
+static void
+bgp_peer_shutdown(struct peer *peer)
+{
+ SET_FLAG (peer->flags, PEER_FLAG_SHUTDOWN) ;
+
+ bgp_maximum_prefix_cancel_timer(peer) ;
+
+ bgp_peer_nsf_stop (peer) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset state as peer goes to pIdle.
+ *
+ * This tidies things up, ready for session to be enabled again.
+ *
+ * NB: can be called any number of times... either to tidy up or to prepare
+ * for session to be enabled.
+ */
+static void
+bgp_peer_reset_idle(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+ char orf_name[BUFSIZ];
+
+ assert(peer->state == bgp_peer_pIdle) ;
+
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE) ;
+
+ peer->cap = 0 ;
+ 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->nsf[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;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate new peer object, implicitly locked.
+ */
+struct peer *
+bgp_peer_new (struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct servent *sp;
+
+ /* bgp argument is absolutely required */
+ assert (bgp);
+ bgp_lock (bgp);
+
+ /* Allocate new peer. */
+ peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer));
+
+ /* Set default value. */
+ peer->v_start = BGP_INIT_START_TIMER;
+ peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+ peer->v_asorig = BGP_DEFAULT_ASORIGINATE;
+ peer->state = bgp_peer_pIdle;
+ peer->ostate = bgp_peer_pIdle;
+ peer->weight = 0;
+ peer->password = NULL;
+ peer->bgp = bgp;
+
+ peer = bgp_peer_lock (peer); /* initial reference */
+
+ /* Set default flags. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ {
+ if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
+ {
+ SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
+ SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
+ }
+ peer->orf_plist[afi][safi] = NULL;
+ }
+ /* Create buffers. */
+ peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE);
+ peer->obuf = stream_fifo_new ();
+ peer->work = stream_new (BGP_MAX_PACKET_SIZE);
+
+ bgp_sync_init (peer);
+
+ /* Get service port number. */
+ sp = getservbyname ("bgp", "tcp");
+ peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);
+
+ return peer;
+}
+
+/*------------------------------------------------------------------------------
+ * Create new BGP peer -- if an AFI/SAFI is given, activate & enable for that.
+ *
+ * Peer starts in pIdle state.
+ *
+ * This is creating a PEER_STATUS_REAL_PEER, which is placed on the bgp->peer
+ * list.
+ */
+struct peer *
+bgp_peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
+ as_t remote_as, afi_t afi, safi_t safi)
+{
+ struct peer *peer;
+ bool enable = (afi != 0) && (safi != 0) ;
+
+ /* TODO: should bgp_peer_new() set state pNull ?? */
+ peer = bgp_peer_new (bgp); /* sets peer->state == pIdle */
+
+ peer->su = *su;
+ peer->local_as = local_as;
+ peer->as = remote_as;
+ peer->local_id = bgp->router_id;
+ peer->v_holdtime = bgp->default_holdtime;
+ peer->v_keepalive = bgp->default_keepalive;
+
+ if (peer_sort (peer) == BGP_PEER_IBGP)
+ {
+ peer->ttl = MAXTTL ;
+ peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+
+ }
+ else /* BGP_PEER_EBGP or BGP_PEER_CONFED */
+ {
+ peer->ttl = 1 ;
+ peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+ } ;
+ peer->gtsm = false ;
+
+ SET_FLAG(peer->sflags, PEER_STATUS_REAL_PEER) ;
+ peer = bgp_peer_lock (peer); /* bgp peer list reference */
+ listnode_add_sort (bgp->peer, peer);
+
+ /* If required, activate given AFI/SAFI -- eg "default ipv4-unicast" */
+ if (enable)
+ peer->afc[afi][safi] = 1;
+
+ /* Last read time set */
+ peer->readtime = bgp_clock ();
+
+ /* Last reset time set */
+ peer->resettime = bgp_clock ();
+
+ /* Make peer's address string. */
+ peer->host = sockunion_su2str (su, MTYPE_BGP_PEER_HOST) ;
+
+ /* session -- NB: *before* peer is registered, so before any possible
+ * lookup up by accept() in the BGP Engine
+ */
+ bgp_session_init_new(peer);
+
+ /* register -- NB: *after* peer->session set, so safe */
+ bgp_peer_index_register(peer, &peer->su);
+
+ /* Fire up any timers that should run during pIdle */
+ bgp_peer_timers_set (peer);
+
+ /* If require, enable now all is ready */
+ if (enable)
+ bgp_peer_enable(peer) ;
+
+ return peer;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete peer from configuration.
+ *
+ * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
+ * it to "cool off" and refcounts to hit 0, at which state it is freed.
+ *
+ */
+int
+bgp_peer_delete (struct peer *peer)
+{
+ int i;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ struct bgp_filter *filter;
+
+ bgp = peer->bgp ;
+
+ /* Once peer is pDeleting it should be impossible to find in order to
+ * bgp_peer_delete() it !
+ */
+ assert (peer->state != bgp_peer_pDeleting);
+
+ /* If the peer is active, then need to shut it down now. If there are any
+ * stale routes, flush them now.
+ *
+ * If the peer ends up in pClearing state, that implies that some background
+ * work is required to completely clear this peer. So increment the
+ * reference count to hold the peer in existence. That will be decremented
+ * again in bgp_peer_clearing_completed().
+ *
+ * If the peer ends up in sIdle state, that implies that any routes that
+ * needed clearing have been dealt with.
+ *
+ * There may be a session in existence. If so, it must either be sLimping or
+ * sDisabled.
+ *
+ * Changing to pDeleting state turns off all timers.
+ */
+ bgp_peer_down(peer, PEER_DOWN_NEIGHBOR_DELETE) ;
+
+ if (peer->state == bgp_peer_pClearing)
+ bgp_peer_lock (peer) ;
+ else
+ assert(peer->state == bgp_peer_pIdle) ;
+
+ bgp_peer_change_status (peer, bgp_peer_pDeleting);
+
+ /* Increment count of peers lingering in pDeleting
+ *
+ * The count is used while terminating bgpd -- keeps all the nexuses running
+ * until this count drops to zero.
+ */
+ bm->peer_linger_count++ ;
+
+ /* If is an rsclient in its own right, remove from rsclient list */
+ if (peer_rsclient_active (peer))
+ {
+ struct listnode *pn;
+ pn = listnode_lookup (bgp->rsclient, peer) ;
+
+ bgp_peer_unlock (peer); /* rsclient list reference */
+ list_delete_node (bgp->rsclient, pn);
+ } ;
+
+ /* If this peer belongs to peer group, clear up the relationship. */
+ if (peer->group != NULL)
+ {
+ struct listnode *pn;
+ pn = listnode_lookup (peer->group->peer, peer) ;
+
+ assert(pn != NULL) ;
+
+ bgp_peer_unlock (peer); /* group->peer list reference */
+ list_delete_node (peer->group->peer, pn);
+
+ peer->group = NULL;
+ } ;
+
+ /* Password configuration */
+ if (peer->password)
+ {
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = NULL;
+ }
+
+ /* Delete from bgp->peer list, if required. */
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_REAL_PEER))
+ {
+ struct listnode *pn;
+ pn = listnode_lookup (bgp->peer, peer) ;
+
+ assert(pn != NULL) ;
+
+ bgp_peer_unlock (peer); /* bgp peer list reference */
+ list_delete_node (bgp->peer, pn);
+ } ;
+
+ /* Discard rsclient ribs which are owned by group
+ * and cross-check rib pointer and PEER_FLAG_RSERVER_CLIENT.
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+ {
+ assert(peer->rib[afi][safi] != NULL) ;
+ if (peer->af_group[afi][safi])
+ {
+ peer->rib[afi][safi] = NULL ;
+ UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ;
+ } ;
+ }
+ else
+ assert(peer->rib[afi][safi] == NULL) ;
+
+ /* Can now clear any rsclient ribs. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ if (peer->rib[afi][safi] != NULL)
+ {
+ bgp_clear_rsclient_rib(peer, afi, safi) ;
+ UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ;
+ } ;
+
+ /* Have now finished with any rsclient ribs */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ bgp_table_finish (&peer->rib[afi][safi]) ;
+
+ /* Buffers. */
+ if (peer->ibuf)
+ stream_free (peer->ibuf);
+ if (peer->obuf)
+ stream_fifo_free (peer->obuf);
+ if (peer->work)
+ stream_free (peer->work);
+ peer->obuf = NULL;
+ peer->work = peer->ibuf = NULL;
+
+ /* Local and remote addresses. */
+ if (peer->su_local)
+ sockunion_free (peer->su_local);
+ if (peer->su_remote)
+ sockunion_free (peer->su_remote);
+ peer->su_local = peer->su_remote = NULL;
+
+ /* Free filter related memory. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ {
+ filter = &peer->filter[afi][safi];
+
+ for (i = FILTER_IN; i < FILTER_MAX; i++)
+ {
+ if (filter->dlist[i].name)
+ free (filter->dlist[i].name);
+ if (filter->aslist[i].name)
+ free (filter->aslist[i].name);
+ filter->dlist[i].name = NULL;
+ prefix_list_unset_ref(&filter->plist[i].ref) ;
+ filter->aslist[i].name = NULL;
+ }
+ for (i = RMAP_IN; i < RMAP_MAX; i++)
+ {
+ if (filter->map[i].name)
+ free (filter->map[i].name);
+ filter->map[i].name = NULL;
+ }
+
+ if (filter->usmap.name)
+ free (filter->usmap.name);
+
+ if (peer->default_rmap[afi][safi].name)
+ free (peer->default_rmap[afi][safi].name);
+
+ filter->usmap.name = NULL;
+ peer->default_rmap[afi][safi].name = NULL;
+ }
+
+ /* Unregister the peer.
+ *
+ * NB: the peer can no longer be looked up by its 'name'.
+ *
+ * In particular this means that the accept() logic in the BGP Engine
+ * will conclude that the session should not be accepting connections.
+ *
+ * NB: also (currently) releases the peer_id -- which may not be so clever ?
+ */
+ if (peer->index_entry != NULL)
+ bgp_peer_index_deregister(peer, &peer->su);
+
+ /* Tear down session, if any and if possible. */
+ bgp_session_delete(peer) ;
+
+ /* Finally: count down the initial reference, which will delete the peer
+ * iff everything else has finished with it.
+ */
+ bgp_peer_unlock (peer); /* initial reference */
+
+ return 0;
+} ;
+
+/*------------------------------------------------------------------------------
+ * increase reference count on a struct peer
+ */
+struct peer *
+bgp_peer_lock (struct peer *peer)
+{
+ assert (peer && (peer->lock >= 0));
+
+ peer->lock++;
+
+ return peer;
+}
+
+/*------------------------------------------------------------------------------
+ * decrease reference count on a struct peer
+ *
+ * If is last reference, the structure is freed and NULL returned
+ */
+struct peer *
+bgp_peer_unlock (struct peer *peer)
+{
+ assert (peer && (peer->lock > 0));
+
+ peer->lock--;
+
+ if (peer->lock == 0)
+ {
+#if 0
+ zlog_debug ("unlocked and freeing");
+ zlog_backtrace (LOG_DEBUG);
+#endif
+ bgp_peer_free (peer);
+ return NULL;
+ }
+
+#if 0
+ if (peer->lock == 1)
+ {
+ zlog_debug ("unlocked to 1");
+ zlog_backtrace (LOG_DEBUG);
+ }
+#endif
+
+ return peer;
+}
+
+/*------------------------------------------------------------------------------
+ * Dismantle and free peer data structure.
+ */
+static void
+bgp_peer_free (struct peer *peer)
+{
+ struct bgp* bgp ;
+
+ assert (peer->state == bgp_peer_pDeleting);
+ assert (peer->session == NULL) ; /* session holds a lock on peer */
+
+ bm->peer_linger_count-- ;
+
+ bgp = peer->bgp ;
+
+ if (peer->desc)
+ XFREE (MTYPE_PEER_DESC, peer->desc);
+
+ /* Free allocated host character. */
+ if (peer->host)
+ XFREE (MTYPE_BGP_PEER_HOST, peer->host);
+
+ /* Update source configuration. */
+ if (peer->update_source)
+ sockunion_free (peer->update_source);
+
+ if (peer->update_if)
+ XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+
+ bgp_sync_delete (peer);
+
+ memset (peer, 0, sizeof (struct peer));
+ peer->lock = -54321 ;
+ XFREE (MTYPE_BGP_PEER, peer);
+
+ bgp_unlock(bgp);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Enable Peer
+ *
+ * This means that something has changed, and session can be started, if no
+ * session has already started.
+ *
+ * So does nothing unless in pIdle, and expects the session state to be:
+ *
+ * - sIdle, sDisabled or no session at all -- session enabled if possible
+ *
+ * - sEnabled -- session already enabled, so do nothing
+ *
+ * - sEstablished -- IMPOSSIBLE
+ *
+ * - sLimping -- cannot, yet, start a new session -- that will happen in due
+ * course.
+ *
+ * This means that any change which requires the session to be taken down and
+ * restarted needs to call bgp_peer_disable().
+ *
+ * The other peer states:
+ *
+ * - pEstablished == sEstablished
+ *
+ * - pClearing -- cannot restart the peer yet, that will happen in due course.
+ *
+ * - pDeleted -- Enable peer makes no sense... asserts invalid.
+ *
+ * TODO: assert !pEstablished in bgp_peer_enable ?
+ */
+void
+bgp_peer_enable(bgp_peer peer)
+{
+ switch (peer->state)
+ {
+ case bgp_peer_pIdle:
+
+ /* Enable the session unless:
+ * 1) session is active -- ie sEnabled/sLimping (see above)
+ * 2) no address family is activated
+ * 3) the peer has been shutdown
+ * 4) is dealing with prefix overflow (waiting for timer)
+ */
+
+ if (bgp_session_is_active(peer->session))
+ assert(peer->session->state != bgp_session_sEstablished) ;
+ else
+ {
+ if (peer_active (peer)
+ && !CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)
+ && !CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+ {
+ /* enable the session */
+ bgp_peer_reset_idle(peer) ; /* tidy up */
+ bgp_session_enable(peer);
+ } ;
+ } ;
+ break ;
+
+ case bgp_peer_pEstablished:
+ break ;
+
+ case bgp_peer_pClearing:
+ break ;
+
+ case bgp_peer_pDeleting:
+ zabort("cannot enable a pDeleting peer") ;
+ break ;
+
+ default:
+ zabort("unknown peer->state") ;
+ break ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Down Peer -- bring down any existing session and restart if possible.
+ *
+ * The following "why_down" values are special:
+ *
+ * - PEER_DOWN_NSF_CLOSE_SESSION
+ *
+ * causes NSF to be turned on as the peer is stopped and routes are cleared.
+ *
+ * - PEER_DOWN_USER_SHUTDOWN
+ *
+ * causes the peer to be shutdown -- so won't restart.
+ *
+ * - PEER_DOWN_NEIGHBOR_DELETE
+ *
+ * prevents the peer from restarting.
+ *
+ * If there is an active session, then it must be disabled, sending the given
+ * notification, or one based on the reason for downing the peer.
+ *
+ * If there is no active session, any stale NSF routes will be cleared.
+ *
+ * So any session ends up as:
+ *
+ * sIdle -- wasn't active and still isn't
+ *
+ * sLimping -- was sEnabled or sEstablished, we now wait for BGP Engine
+ * to complete the disable action and signal when done.
+ *
+ * sDisabled -- has previously been disabled.
+ *
+ * The result depends on the initial peer state:
+ *
+ * 1. bgp_peer_pIdle
+ *
+ * Any session that was sEnabled will have been disabled -- and will now
+ * be sLimping.
+ *
+ * The peer will have automatically restarted, if possible.
+ *
+ * Noting that PEER_DOWN_USER_SHUTDOWN and PEER_DOWN_NEIGHBOR_DELETE both
+ * prevent any restart.
+ *
+ * 2. bgp_peer_pEstablished
+ *
+ * The session will have been disabled -- and will now be sLimping.
+ *
+ * See bgp_peer_stop() for the state of the peer.
+ *
+ * 3. bgp_peer_pClearing
+ *
+ * In this state the session can only be:
+ *
+ * sLimping -- session disable has been sent to the BGP Engine.
+ * sDisabled -- session has been disabled by the BGP Engine
+ *
+ * because peer must have been pEstablished immediately before.
+ *
+ * Do nothing -- will proceed to pIdle in due course.
+ *
+ * 4. bgp_peer_pDeleting
+ *
+ * In this state there may be no session at all, or the session can
+ * only be:
+ *
+ * sIdle -- session never got going
+ * sLimping -- session disable has been sent to the BGP Engine.
+ * sDisabled -- session has been disabled by the BGP Engine
+ *
+ * Do nothing -- peer will be deleted in due course.
+ */
+void
+bgp_peer_down(bgp_peer peer, peer_down_t why_down)
+{
+ bgp_peer_down_notify(peer, why_down, NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Down Peer for the given reason, with the given notification, if any.
+ *
+ * See bgp_peer_down() above.
+ *
+ * If the notification is NULL and need to send a notification, make one up from
+ * the given reason for downing the peer.
+ *
+ * NB: takes responsibility for the notification.
+ */
+static void
+bgp_peer_down_notify(bgp_peer peer, peer_down_t why_down,
+ bgp_notify notification)
+{
+ /* Deal with session (if any). */
+
+ if (bgp_session_is_active(peer->session))
+ {
+ if (notification == NULL)
+ notification = bgp_peer_map_peer_down(why_down) ;
+
+ bgp_notify_set(&peer->session->notification, notification) ;
+
+ bgp_session_disable(peer, bgp_notify_dup(notification)) ;
+ /* The copy of the notification will be discarded either by
+ * the BGP_Engine, if the notification is not sent, or when
+ * it is returned in the eDisabled message.
+ */
+ } ;
+
+ /* Now worry about the state of the peer */
+
+ if (why_down == PEER_DOWN_USER_SHUTDOWN)
+ bgp_peer_shutdown(peer) ;
+
+ if (why_down != PEER_DOWN_NULL)
+ peer->last_reset = why_down ;
+
+ switch (peer->state)
+ {
+ case bgp_peer_pIdle:
+ assert(!bgp_session_is_active(peer->session)
+ || (peer->session->state == bgp_session_sLimping)) ;
+
+ bgp_peer_nsf_stop (peer) ; /* flush stale routes, if any */
+
+ if (why_down != PEER_DOWN_NEIGHBOR_DELETE)
+ bgp_peer_enable(peer) ; /* Restart if possible. */
+
+ break ;
+
+ case bgp_peer_pEstablished:
+ assert(peer->session->state == bgp_session_sLimping) ;
+
+ bgp_peer_stop(peer, why_down == PEER_DOWN_NSF_CLOSE_SESSION) ;
+
+ break ;
+
+ case bgp_peer_pClearing:
+ assert( (peer->session->state == bgp_session_sLimping)
+ || (peer->session->state == bgp_session_sDisabled) ) ;
+
+ bgp_peer_nsf_stop (peer) ; /* flush stale routes, if any */
+
+ break ;
+
+ case bgp_peer_pDeleting:
+ assert( (peer->session == NULL)
+ || (peer->session->state == bgp_session_sIdle)
+ || (peer->session->state == bgp_session_sLimping)
+ || (peer->session->state == bgp_session_sDisabled) ) ;
+ break ;
+
+ default:
+ zabort("unknown peer->state") ;
+ break ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Notify the far end that an error has been detected, and close down the
+ * session.
+ *
+ * The session will have been established, so the IdleHoldTime will be extended.
+ *
+ * Because it knows no better, the session will be restarted.
+ */
+extern void
+bgp_peer_down_error(struct peer* peer,
+ bgp_nom_code_t code, bgp_nom_subcode_t subcode)
+{
+ bgp_peer_down_error_with_data (peer, code, subcode, NULL, 0);
+}
+
+/*------------------------------------------------------------------------------
+ * Notify the far end that an error has been detected, and close down the
+ * session.
+ *
+ * Same as above, except that this accepts a data part for the notification
+ * message -- but len may be 0 (and data may be null iff len == 0).
+ */
+extern void
+bgp_peer_down_error_with_data (struct peer* peer,
+ bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ const u_int8_t* data, size_t datalen)
+{
+ bgp_notify notification;
+ notification = bgp_notify_new_with_data(code, subcode, data, datalen);
+
+ bgp_peer_down_notify(peer, bgp_peer_map_notification(notification),
+ notification) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct notification based on the reason for bringing down the session
+ *
+ * Where the session is brought down by the other end, returns NULL.
+ */
+static bgp_notify
+bgp_peer_map_peer_down(peer_down_t why_down)
+{
+ bgp_nom_code_t code ;
+ bgp_nom_subcode_t subcode ;
+
+ assert((why_down >= PEER_DOWN_first) && (why_down < PEER_DOWN_count)) ;
+
+ code = BGP_NOMC_CEASE ; /* Default values */
+ subcode = BGP_NOMS_UNSPECIFIC ;
+
+ switch(why_down)
+ {
+ case PEER_DOWN_NULL:
+ return NULL ;
+
+ /* Session taken down at this end for some unspecified reason */
+
+ case PEER_DOWN_UNSPECIFIED:
+ break ;
+
+ /* Configuration changes that cause a session to be reset. */
+
+ case PEER_DOWN_CONFIG_CHANGE:
+ case PEER_DOWN_RID_CHANGE:
+ case PEER_DOWN_REMOTE_AS_CHANGE:
+ case PEER_DOWN_LOCAL_AS_CHANGE:
+ case PEER_DOWN_CLID_CHANGE:
+ case PEER_DOWN_CONFED_ID_CHANGE:
+ case PEER_DOWN_CONFED_PEER_CHANGE:
+ case PEER_DOWN_RR_CLIENT_CHANGE:
+ case PEER_DOWN_RS_CLIENT_CHANGE:
+ case PEER_DOWN_UPDATE_SOURCE_CHANGE:
+ case PEER_DOWN_AF_ACTIVATE:
+ case PEER_DOWN_RMAP_BIND:
+ case PEER_DOWN_RMAP_UNBIND:
+ case PEER_DOWN_DONT_CAPABILITY:
+ case PEER_DOWN_OVERRIDE_CAPABILITY:
+ case PEER_DOWN_STRICT_CAP_MATCH:
+ case PEER_DOWN_CAPABILITY_CHANGE:
+ case PEER_DOWN_PASSIVE_CHANGE:
+ case PEER_DOWN_MULTIHOP_CHANGE:
+ case PEER_DOWN_AF_DEACTIVATE:
+ case PEER_DOWN_PASSWORD_CHANGE:
+ case PEER_DOWN_ALLOWAS_IN_CHANGE:
+ subcode = BGP_NOMS_C_CONFIG ;
+ break ;
+
+ /* Other actions that cause a session to be reset */
+
+ case PEER_DOWN_USER_SHUTDOWN:
+ subcode = BGP_NOMS_C_SHUTDOWN ;
+ break ;
+
+ case PEER_DOWN_USER_RESET:
+ subcode = BGP_NOMS_C_RESET ;
+ break ;
+
+ case PEER_DOWN_NEIGHBOR_DELETE:
+ subcode = BGP_NOMS_C_DECONFIG ;
+ break ;
+
+ case PEER_DOWN_INTERFACE_DOWN:
+ return NULL ; /* nowhere to send a notification ! */
+
+ /* Errors and problems that cause a session to be reset */
+ /* */
+ /* SHOULD really have a notification constructed for these, but for */
+ /* completeness construct an "unspecified" for these. */
+
+ case PEER_DOWN_MAX_PREFIX:
+ subcode = BGP_NOMS_C_MAX_PREF ;
+ break ;
+
+ case PEER_DOWN_HEADER_ERROR:
+ code = BGP_NOMC_HEADER ;
+ break ;
+
+ case PEER_DOWN_OPEN_ERROR:
+ code = BGP_NOMC_OPEN ;
+ break ;
+
+ case PEER_DOWN_UPDATE_ERROR:
+ code = BGP_NOMC_UPDATE ;
+ break ;
+
+ case PEER_DOWN_HOLD_TIMER:
+ code = BGP_NOMC_HOLD_EXP ;
+ break ;
+
+ case PEER_DOWN_FSM_ERROR:
+ code = BGP_NOMC_FSM ;
+ break ;
+
+ case PEER_DOWN_DYN_CAP_ERROR:
+ code = BGP_NOMC_DYN_CAP ;
+ break ;
+
+ /* Things the far end can do to cause a session to be reset */
+
+ case PEER_DOWN_NOTIFY_RECEIVED:
+ return NULL ; /* should not get here ! */
+
+ case PEER_DOWN_CLOSE_SESSION:
+ case PEER_DOWN_NSF_CLOSE_SESSION:
+ return NULL ; /* nowhere to send a notification ! */
+
+ /* To keep the compiler happy. */
+ case PEER_DOWN_count:
+ break ; /* should have asserted already */
+ } ;
+
+ return bgp_notify_new(code, subcode) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct reason for bringing down the session based on the notification
+ */
+static peer_down_t
+bgp_peer_map_notification(bgp_notify notification)
+{
+ switch (bgp_notify_get_code(notification)) /* deals with NULL */
+ {
+ case BGP_NOMC_UNDEF:
+ break ;
+
+ case BGP_NOMC_HEADER:
+ return PEER_DOWN_HEADER_ERROR ;
+
+ case BGP_NOMC_OPEN:
+ return PEER_DOWN_OPEN_ERROR ;
+
+ case BGP_NOMC_UPDATE:
+ return PEER_DOWN_UPDATE_ERROR ;
+
+ case BGP_NOMC_HOLD_EXP:
+ return PEER_DOWN_HOLD_TIMER ;
+
+ case BGP_NOMC_FSM:
+ return PEER_DOWN_FSM_ERROR ;
+
+ case BGP_NOMC_CEASE:
+ switch (notification->subcode)
+ {
+ case BGP_NOMS_C_MAX_PREF:
+ return PEER_DOWN_MAX_PREFIX ;
+
+ case BGP_NOMS_C_SHUTDOWN:
+ return PEER_DOWN_USER_SHUTDOWN ;
+
+ case BGP_NOMS_C_DECONFIG:
+ return PEER_DOWN_NEIGHBOR_DELETE ;
+
+ case BGP_NOMS_C_RESET:
+ return PEER_DOWN_USER_RESET ;
+
+ case BGP_NOMS_C_REJECTED: /* should not get here */
+ return PEER_DOWN_NULL ;
+
+ case BGP_NOMS_C_CONFIG:
+ return PEER_DOWN_CONFIG_CHANGE ;
+
+ case BGP_NOMS_C_COLLISION: /* should not get here */
+ return PEER_DOWN_NULL ;
+
+ case BGP_NOMS_C_RESOURCES: /* not used */
+ return PEER_DOWN_NULL ;
+
+ default:
+ break ;
+ } ;
+ break ;
+
+ case BGP_NOMC_DYN_CAP:
+ return PEER_DOWN_DYN_CAP_ERROR ;
+
+ default:
+ break ;
+ } ;
+
+ return PEER_DOWN_UNSPECIFIED ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Background route clearing has completed.
+ *
+ * If there is any background work required to clear routes for a given peer,
+ * then when that completes, this is called to move the state of the peer
+ * forwards.
+ */
+extern void
+bgp_peer_clearing_completed(struct peer *peer)
+{
+ switch (peer->state)
+ {
+ case bgp_peer_pIdle:
+ case bgp_peer_pEstablished:
+ zabort("invalid peer->state") ;
+ break ;
+
+ case bgp_peer_pClearing:
+ bgp_peer_change_status (peer, bgp_peer_pIdle);
+ bgp_peer_enable(peer);
+ break ;
+
+ case bgp_peer_pDeleting:
+ bgp_peer_unlock (peer); /* route clearing "reference" */
+ break ;
+
+ default:
+ zabort("unknown peer->state") ;
+ break ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set new peer state.
+ *
+ * If state changes, do dump new state and log state change if required.
+ *
+ * In any case, set timers for the new state -- so if state hasn't changed,
+ * will restart those timers.
+ */
+static void
+bgp_peer_change_status (bgp_peer peer, bgp_peer_state_t new_state)
+{
+ if (peer->state != new_state)
+ {
+ bgp_dump_state (peer, peer->state, new_state);
+
+ /* Preserve old status and change into new status. */
+ peer->ostate = peer->state ;
+ peer->state = new_state ;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_debug ("%s went from %s to %s", peer->host,
+ LOOKUP (bgp_peer_status_msg, peer->ostate),
+ LOOKUP (bgp_peer_status_msg, peer->state)) ;
+
+ if (new_state == bgp_peer_pIdle)
+ bgp_peer_reset_idle(peer) ; /* tidy up */
+ } ;
+
+ bgp_peer_timers_set (peer);
+} ;
+
+/*==============================================================================
+ * Timer handling
+ */
+
+static void
+bgp_peer_timers_set (struct peer *peer)
+{
+ switch (peer->state)
+ {
+ case bgp_peer_pIdle:
+ /* On entry to pIdle the Graceful Restart Timers are left running:
+ *
+ * - if no connection is established within the Graceful Restart time,
+ * then things are no longer graceful, and the stale routes have to
+ * be thrown away.
+ *
+ * - if routes do not thereafter arrive quickly enough, then the
+ * Graceful Stale time kicks in and stale routes will be thrown away.
+ */
+ BGP_TIMER_OFF (peer->t_routeadv) ;
+ BGP_TIMER_OFF (peer->t_pmax_restart) ;
+ break;
+
+ case bgp_peer_pEstablished:
+ /* On entry to pEstablished only the the Graceful Stale Timer is left
+ * running.
+ *
+ * Any Graceful Restart Timer can be cancelled -- have established in
+ * time.
+ */
+ BGP_TIMER_OFF (peer->t_routeadv) ;
+ BGP_TIMER_OFF (peer->t_pmax_restart) ;
+
+ bgp_graceful_restart_timer_cancel(peer) ;
+ break;
+
+ case bgp_peer_pClearing:
+ /* On entry to pClearing, turn off all timers.
+ *
+ * The Graceful Restart timer should not be running in any case.
+ *
+ * If the session is brought down quickly enough, the Graceful Stale
+ * timer may be running.
+ */
+ BGP_TIMER_OFF (peer->t_routeadv);
+ BGP_TIMER_OFF (peer->t_pmax_restart);
+ BGP_TIMER_OFF (peer->t_gr_restart);
+
+ bgp_graceful_stale_timer_cancel(peer) ;
+ break ;
+
+ case bgp_peer_pDeleting:
+ /* On entry to pDeleting, turn off all timers.
+ */
+ BGP_TIMER_OFF (peer->t_routeadv);
+ BGP_TIMER_OFF (peer->t_pmax_restart);
+ BGP_TIMER_OFF (peer->t_gr_restart);
+ BGP_TIMER_OFF (peer->t_gr_stale);
+ break;
+
+ default:
+ assert(0);
+ } ;
+} ;
+
+static int
+bgp_routeadv_timer (struct thread *thread)
+{
+ struct peer *peer;
+ uint32_t jittered ;
+ uint32_t jitter ;
+
+ 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 = bgp_clock ();
+
+ bgp_write(peer, NULL);
+
+ /* Apply +/- 10% jitter to the route advertise timer.
+ *
+ * The time is in seconds, so for anything less than 10 seconds this forced
+ * to be +/- 1 second.
+ */
+ jittered = jitter = peer->v_routeadv ;
+ if (jitter < 10)
+ jitter = 10 ;
+ jittered = (jittered * 90) + (rand() % (jitter * 20)) ; /* jitter is +/-10% */
+ jittered = (jittered + 50) / 100 ;
+
+ /* TODO: move this to the Routeing Engine qtimer pile. */
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, jittered) ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * BGP Peer Down Causes mapped to strings
+ */
+const char *peer_down_str[] =
+{
+ [PEER_DOWN_NULL] = "",
+
+ [PEER_DOWN_UNSPECIFIED] = "Unspecified reason",
+
+ [PEER_DOWN_CONFIG_CHANGE] = "Unspecified config change",
+
+ [PEER_DOWN_RID_CHANGE] = "Router ID changed",
+ [PEER_DOWN_REMOTE_AS_CHANGE] = "Remote AS changed",
+ [PEER_DOWN_LOCAL_AS_CHANGE] = "Local AS change",
+ [PEER_DOWN_CLID_CHANGE] = "Cluster ID changed",
+ [PEER_DOWN_CONFED_ID_CHANGE] = "Confederation identifier changed",
+ [PEER_DOWN_CONFED_PEER_CHANGE] = "Confederation peer changed",
+ [PEER_DOWN_RR_CLIENT_CHANGE] = "RR client config change",
+ [PEER_DOWN_RS_CLIENT_CHANGE] = "RS client config change",
+ [PEER_DOWN_UPDATE_SOURCE_CHANGE] = "Update source change",
+ [PEER_DOWN_AF_ACTIVATE] = "Address family activated",
+ [PEER_DOWN_RMAP_BIND] = "Peer-group add member",
+ [PEER_DOWN_RMAP_UNBIND] = "Peer-group delete member",
+ [PEER_DOWN_DONT_CAPABILITY] = "dont-capability-negotiate changed",
+ [PEER_DOWN_OVERRIDE_CAPABILITY] = "override-capability changed",
+ [PEER_DOWN_STRICT_CAP_MATCH] = "strict-capability-match changed",
+ [PEER_DOWN_CAPABILITY_CHANGE] = "Capability changed",
+ [PEER_DOWN_PASSIVE_CHANGE] = "Passive config change",
+ [PEER_DOWN_MULTIHOP_CHANGE] = "Multihop config change",
+ [PEER_DOWN_AF_DEACTIVATE] = "Address family deactivated",
+ [PEER_DOWN_PASSWORD_CHANGE] = "MD5 Password changed",
+ [PEER_DOWN_ALLOWAS_IN_CHANGE] = "Allow AS in changed",
+
+ [PEER_DOWN_USER_SHUTDOWN] = "Admin. shutdown",
+ [PEER_DOWN_USER_RESET] = "User reset",
+ [PEER_DOWN_NEIGHBOR_DELETE] = "Neighbor deleted",
+
+ [PEER_DOWN_INTERFACE_DOWN] = "Interface down",
+
+ [PEER_DOWN_MAX_PREFIX] = "Max Prefix Limit exceeded",
+
+ [PEER_DOWN_HEADER_ERROR] = "Error in message header",
+ [PEER_DOWN_OPEN_ERROR] = "Error in BGP OPEN message",
+ [PEER_DOWN_UPDATE_ERROR] = "Error in BGP UPDATE message",
+ [PEER_DOWN_HOLD_TIMER] = "HoldTimer expired",
+ [PEER_DOWN_FSM_ERROR] = "Error in FSM sequence",
+ [PEER_DOWN_DYN_CAP_ERROR] = "Error in Dynamic Capability",
+
+ [PEER_DOWN_NOTIFY_RECEIVED] = "Notification received",
+ [PEER_DOWN_NSF_CLOSE_SESSION] = "NSF peer closed the session",
+ [PEER_DOWN_CLOSE_SESSION] = "Peer closed the session",
+} ;
+
+CONFIRM(sizeof(peer_down_str) == (PEER_DOWN_count * sizeof(const char*))) ;
+
+/*------------------------------------------------------------------------------
+ * Graceful Restart timer has expired.
+ *
+ * MUST be pIdle or pClearing -- transition to pEstablished cancels this timer.
+ *
+ * Clears out stale routes and stops the Graceful Restart Stale timer.
+ *
+ * Clears down PEER_STATUS_NSF_MODE & PEER_STATUS_NSF_WAIT.
+ */
+static int
+bgp_graceful_restart_timer_expire (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_gr_restart = NULL;
+
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s graceful restart timer expired", peer->host) ;
+
+ bgp_peer_nsf_stop (peer) ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Cancel any Graceful Restart timer
+ *
+ * NB: does NOT do anything about any stale routes or about any stale timer !
+ */
+static void
+bgp_graceful_restart_timer_cancel (struct peer* peer)
+{
+ 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);
+ }
+} ;
+
+/*------------------------------------------------------------------------------
+ * Graceful Restart Stale timer has expired.
+ *
+ * SHOULD be pEstablished, because otherwise the Graceful Restart timer should
+ * have gone off before this does, and cancelled this.
+ *
+ * To be safe, if not pEstablished, then MUST be pIdle or pClearing, so can do
+ * bgp_peer_nsf_stop (peer).
+ *
+ * Clears out stale routes and stops the Graceful Restart Stale timer.
+ *
+ * Clears down PEER_STATUS_NSF_MODE & PEER_STATUS_NSF_WAIT.
+ */
+static int
+bgp_graceful_stale_timer_expire (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_gr_stale = NULL;
+
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s graceful restart stalepath timer expired", peer->host);
+
+ if (peer->state == bgp_peer_pEstablished)
+ bgp_peer_clear_all_stale_routes(peer) ;
+ else
+ bgp_peer_nsf_stop(peer) ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Cancel any Graceful Restart Stale timer
+ *
+ * NB: does NOT do anything about any stale routes !
+ */
+static void
+bgp_graceful_stale_timer_cancel (struct peer* peer)
+{
+ 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 0
+/* BGP peer is stopped 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;
+}
+#endif
+
+
+/*==============================================================================
+ * For the given interface name, get a suitable address so can bind() before
+ * connect() so that we use the required interface.
+ *
+ * If has a choice, uses address that best matches the peer's address.
+ */
+extern sockunion
+bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, sa_family_t af)
+{
+ struct interface* ifp ;
+ struct connected* connected;
+ struct listnode* node;
+ struct prefix* best_prefix ;
+ struct prefix* peer_prefix ;
+ int best, this ;
+
+ if (ifname == NULL)
+ return NULL ;
+
+ ifp = if_lookup_by_name (peer->update_if) ;
+ if (ifp == NULL)
+ {
+ zlog_err("Peer %s interface %s is not known", peer->host, ifname) ;
+ return NULL ;
+ } ;
+
+ peer_prefix = sockunion2hostprefix(&peer->su) ;
+ best_prefix = NULL ;
+ best = -1 ;
+
+ for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+ {
+ if (connected->address->family != af)
+ continue ;
+
+ this = prefix_common_bits (connected->address, peer_prefix) ;
+ if (this > best)
+ {
+ best_prefix = connected->address ;
+ best = this ;
+ } ;
+ } ;
+
+ prefix_free(peer_prefix) ;
+
+ if (best_prefix != NULL)
+ return sockunion_new_prefix(NULL, best_prefix) ;
+
+ zlog_err("Peer %s interface %s has no suitable address", peer->host, ifname);
+
+ return NULL ;
+} ;
diff --git a/bgpd/bgp_peer.h b/bgpd/bgp_peer.h
new file mode 100644
index 00000000..89708661
--- /dev/null
+++ b/bgpd/bgp_peer.h
@@ -0,0 +1,495 @@
+/* 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 "bgpd/bgp_peer_index.h"
+
+#include "lib/plist.h"
+
+/*==============================================================================
+ *
+ */
+
+enum bgp_route_map_types
+{
+ RMAP_IN = 0,
+ RMAP_OUT = 1,
+ RMAP_IMPORT = 2,
+ RMAP_EXPORT = 3,
+ RMAP_RS_IN = 4,
+ RMAP_MAX = 5,
+} ;
+
+/*==============================================================================
+ *
+ */
+
+/* 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.
+ *
+ *
+ *
+ */
+
+enum PEER_DOWN {
+ PEER_DOWN_first = 0,
+
+ PEER_DOWN_NULL = 0, /* Not a PEER_DOWN */
+
+ /* Session taken down at this end for some unspecified reason */
+
+ PEER_DOWN_UNSPECIFIED,
+
+ /* Configuration changes that cause a session to be reset. */
+
+ PEER_DOWN_CONFIG_CHANGE, /* Unspecified config change */
+
+ PEER_DOWN_RID_CHANGE, /* 'bgp router-id' */
+ PEER_DOWN_REMOTE_AS_CHANGE, /* 'neighbor remote-as' */
+ PEER_DOWN_LOCAL_AS_CHANGE, /* 'neighbor local-as' */
+ PEER_DOWN_CLID_CHANGE, /* 'bgp cluster-id' */
+ PEER_DOWN_CONFED_ID_CHANGE, /* 'bgp confederation identifier' */
+ PEER_DOWN_CONFED_PEER_CHANGE, /* 'bgp confederation peer' */
+ PEER_DOWN_RR_CLIENT_CHANGE, /* 'neighbor route-reflector-client' */
+ PEER_DOWN_RS_CLIENT_CHANGE, /* 'neighbor route-server-client' */
+ PEER_DOWN_UPDATE_SOURCE_CHANGE, /* 'neighbor update-source' */
+ PEER_DOWN_AF_ACTIVATE, /* 'neighbor activate' */
+ PEER_DOWN_RMAP_BIND, /* 'neighbor peer-group' */
+ PEER_DOWN_RMAP_UNBIND, /* 'no neighbor peer-group' */
+ PEER_DOWN_DONT_CAPABILITY, /* 'neighbor dont-capability-negotiate' */
+ PEER_DOWN_OVERRIDE_CAPABILITY, /* 'neighbor override-capability' */
+ PEER_DOWN_STRICT_CAP_MATCH, /* 'neighbor strict-capability-match' */
+ PEER_DOWN_CAPABILITY_CHANGE, /* 'neighbor capability' */
+ PEER_DOWN_PASSIVE_CHANGE, /* 'neighbor passive' */
+ PEER_DOWN_MULTIHOP_CHANGE, /* 'neighbor multihop' */
+ PEER_DOWN_AF_DEACTIVATE, /* 'no neighbor activate' */
+ PEER_DOWN_PASSWORD_CHANGE, /* password changed */
+ PEER_DOWN_ALLOWAS_IN_CHANGE, /* allowas-in change */
+
+ /* Other actions that cause a session to be reset */
+
+ PEER_DOWN_USER_SHUTDOWN, /* 'neighbor shutdown' */
+ PEER_DOWN_USER_RESET, /* 'clear ip bgp' */
+ PEER_DOWN_NEIGHBOR_DELETE, /* neighbor delete */
+
+ PEER_DOWN_INTERFACE_DOWN, /* interface reported to be down */
+
+ /* Errors and problems that cause a session to be reset */
+
+ PEER_DOWN_MAX_PREFIX, /* max prefix limit exceeded */
+
+ PEER_DOWN_HEADER_ERROR, /* error in BGP Message header */
+ PEER_DOWN_OPEN_ERROR, /* error in BGP OPEN message */
+ PEER_DOWN_UPDATE_ERROR, /* error in BGP UPDATE message */
+ PEER_DOWN_HOLD_TIMER, /* HoldTimer expired */
+ PEER_DOWN_FSM_ERROR, /* error in FSM sequence */
+ PEER_DOWN_DYN_CAP_ERROR, /* error in Dynamic Capability */
+
+ /* Things the far end can do to cause a session to be reset */
+
+ PEER_DOWN_NOTIFY_RECEIVED, /* notification received */
+ PEER_DOWN_CLOSE_SESSION, /* tcp session close */
+ PEER_DOWN_NSF_CLOSE_SESSION, /* NSF tcp session close */
+
+ /* Number of down causes */
+ PEER_DOWN_count
+} ;
+
+typedef enum PEER_DOWN peer_down_t ;
+
+
+
+
+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];
+
+ /* Collection of routes originated by peer */
+ struct bgp_info* routes_head[AFI_MAX][SAFI_MAX] ;
+
+ /* Collection of adj_in routes */
+ struct bgp_adj_in* adj_in_head[AFI_MAX][SAFI_MAX] ;
+
+ /* Collection of adj_out routes */
+ struct bgp_adj_out* adj_out_head[AFI_MAX][SAFI_MAX] ;
+
+ /* Packet receive buffer. */
+ struct stream *ibuf;
+
+ struct stream_fifo *obuf;
+ struct stream *work;
+
+ /* Status of the peer. */
+ bgp_peer_state_t state; /* current state */
+ bgp_peer_state_t ostate; /* old state */
+
+ /* Peer index, used for dumping TABLE_DUMP_V2 format */
+ uint16_t table_dump_index;
+
+ /* Peer information */
+ bgp_peer_index_entry index_entry ;
+ bgp_session session ; /* Current session */
+
+ int ttl ; /* TTL of TCP connection to the peer. */
+ bool gtsm ; /* ttl set by neighbor xxx ttl_security */
+
+ 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 */
+
+#define PEER_CAP_SUPPRESSED (1 << 9) /* none sent */
+
+#define PEER_CAP_AS4_BOTH (PEER_CAP_AS4_ADV + PEER_CAP_AS4_RCV)
+#define PEER_CAP_AS4_USE(peer) \
+ (((peer)->cap & PEER_CAP_AS4_BOTH) == PEER_CAP_AS4_BOTH)
+
+ /* 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_REAL_PEER (1 << 0) /* not group conf or peer_self */
+#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */
+#define PEER_STATUS_GROUP (1 << 3) /* peer-group conf */
+#define PEER_STATUS_NSF_MODE (1 << 4) /* NSF aware peer */
+#define PEER_STATUS_NSF_WAIT (1 << 5) /* 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_asorig;
+ struct thread *t_routeadv;
+ struct thread *t_pmax_restart;
+ struct thread *t_gr_restart;
+ struct thread *t_gr_stale;
+
+ /* BGP state count */
+ u_int32_t established;
+ u_int32_t dropped;
+
+ /* Synchronization 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];
+
+ /* 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 */
+ peer_down_t last_reset;
+
+ bgp_notify notification ;
+
+ /* The kind of route-map Flags. */
+ u_int16_t 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_RMAP_TYPE_RS_IN (1 << 8) /* neighbor route-map rs-in */
+} ;
+
+
+#define BGP_TIMER_ON(T,F,V) \
+ do { \
+ if (!(T) && (peer->state != bgp_peer_pDeleting)) \
+ 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)->state != bgp_peer_pDeleting) \
+ 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);
+#if 0
+extern void bgp_timer_set (struct peer *);
+#endif
+extern void bgp_fsm_change_status (struct peer *peer, int status);
+extern const char *peer_down_str[];
+
+
+/*==============================================================================
+ *
+ */
+
+extern void bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag);
+extern void bgp_peer_enable(bgp_peer peer);
+extern void bgp_peer_down(bgp_peer peer, peer_down_t why_down) ;
+extern void bgp_peer_down_error(bgp_peer peer,
+ bgp_nom_code_t code, bgp_nom_subcode_t subcode) ;
+extern void bgp_peer_down_error_with_data (bgp_peer peer,
+ bgp_nom_code_t code, bgp_nom_subcode_t subcode,
+ const u_int8_t* data, size_t datalen) ;
+extern void bgp_peer_clearing_completed(bgp_peer peer) ;
+extern bgp_peer bgp_peer_new (struct bgp *bgp);
+extern bgp_peer bgp_peer_create (sockunion su, struct bgp *bgp, as_t local_as,
+ as_t remote_as, afi_t afi, safi_t safi);
+extern bgp_peer bgp_peer_lock (bgp_peer peer) ;
+extern bgp_peer bgp_peer_unlock (bgp_peer peer) ;
+extern int bgp_peer_delete (bgp_peer peer);
+extern sockunion bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname,
+ sa_family_t af) ;
+
+#endif /* _QUAGGA_BGP_PEER_H */
+
diff --git a/bgpd/bgp_peer_index.c b/bgpd/bgp_peer_index.c
new file mode 100644
index 00000000..518a22bc
--- /dev/null
+++ b/bgpd/bgp_peer_index.c
@@ -0,0 +1,437 @@
+/* BGP Peer Index -- 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 "lib/zassert.h"
+
+#include "bgpd/bgp_peer_index.h"
+#include "bgpd/bgp_peer.h"
+#include "bgpd/bgp_session.h"
+#include "bgpd/bgp_connection.h"
+
+#include "lib/symtab.h"
+#include "lib/vector.h"
+#include "lib/qpthreads.h"
+#include "lib/sockunion.h"
+#include "lib/memory.h"
+
+/*==============================================================================
+ * 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 Routing Engine.
+ *
+ * The Peer Index is used by the Routing Engine to lookup peers either by
+ * name (IP address) or by peer_id.
+ *
+ * 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. Only the Routing engine
+ * makes changes to the Peer Index, so it only needs to lock the mutex when it
+ * does make changes. The BGP Engine needs to lock the Peer Index whenever it
+ * accesses it.
+ *
+ * The BGP Engine needs the session associated with a given address if and only
+ * if the session is enabled for accept(), which implies that it is active and
+ * in the hands of the BGP Engine. To get to the session it needs to step
+ * via the peer->session pointer, having found the peer via the index. So,
+ * setting the peer->session pointer is done under the Peer Index Mutex.
+ */
+
+static struct symbol_table bgp_peer_index ; /* lookup by 'name' */
+static struct vector bgp_peer_id_index ; /* lookup by peer-id */
+
+static qpt_mutex bgp_peer_index_mutex = NULL ;
+
+CONFIRM(bgp_peer_id_null == 0) ;
+
+enum { bgp_peer_id_unit = 64 } ; /* allocate many at a time */
+
+typedef struct bgp_peer_id_table_chunk* bgp_peer_id_table_chunk ;
+struct bgp_peer_id_table_chunk
+{
+ bgp_peer_id_table_chunk next ;
+
+ struct bgp_peer_index_entry entries[bgp_peer_id_unit] ;
+} ;
+
+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) ;
+} ;
+
+static bgp_peer_id_t bgp_peer_id_count = 0 ;
+
+static bgp_peer_id_table_chunk bgp_peer_id_table = NULL ;
+
+static bgp_peer_index_entry bgp_peer_id_free_head = NULL ;
+static bgp_peer_index_entry bgp_peer_id_free_tail = NULL ;
+
+/* Forward references */
+static void bgp_peer_id_table_free_entry(bgp_peer_index_entry entry) ;
+static void bgp_peer_id_table_make_ids(void) ;
+
+/*------------------------------------------------------------------------------
+ * 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 */
+
+ vector_init_new(&bgp_peer_id_index, bgp_peer_id_unit) ;
+
+ /* Initialise table entirely empty */
+ bgp_peer_id_table = NULL ;
+ bgp_peer_id_free_head = NULL ;
+ bgp_peer_id_free_tail = NULL ;
+
+ bgp_peer_id_count = 0 ;
+
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise the bgp_peer_index_mutex.
+ *
+ * This must be done as soon as any qpthreads are enabled.
+ */
+extern void
+bgp_peer_index_mutex_init(void)
+{
+ bgp_peer_index_mutex = qpt_mutex_init_new(NULL, qpt_mutex_recursive) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset the peer index -- freeing all memory.
+ *
+ * The index can be used again without initialisation, and without further
+ * initialisation of the mutex.
+ *
+ * NB: all peers MUST have been deregistered already.
+ */
+extern void
+bgp_peer_index_reset(void)
+{
+ bgp_peer_index_entry entry ;
+ bgp_peer_id_table_chunk chunk ;
+
+ /* Ream out the peer id vector -- checking that all entries are empty */
+ while ((entry = vector_ream_keep(&bgp_peer_id_index)) != NULL)
+ passert((entry->peer == NULL) && (entry->next_free != entry)) ;
+
+ /* Discard body of symbol table -- must be empty ! */
+ symbol_table_reset_keep(&bgp_peer_index) ;
+
+ /* Discard the empty chunks of entries */
+ while (bgp_peer_id_table != NULL)
+ {
+ chunk = bgp_peer_id_table ;
+ bgp_peer_id_table = chunk->next ;
+ XFREE(MTYPE_BGP_PEER_ID_TABLE, chunk) ;
+ } ;
+
+ /* Set utterly empty */
+ bgp_peer_id_table = NULL ;
+ bgp_peer_id_free_head = NULL ;
+ bgp_peer_id_free_tail = NULL ;
+
+ bgp_peer_id_count = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the bgp_peer_index_mutex -- for shut down.
+ */
+extern void
+bgp_peer_index_mutex_free(void)
+{
+ qpt_mutex_destroy_free(bgp_peer_index_mutex) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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_entry entry ;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* First need an entry, which allocates a peer_id. May need to extend */
+ /* the bgp_peer_id_table -- so need to be locked for this. */
+
+ if (bgp_peer_id_free_head == NULL)
+ bgp_peer_id_table_make_ids() ;
+
+ entry = bgp_peer_id_free_head ;
+ bgp_peer_id_free_head = entry->next_free ;
+
+ assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ;
+
+ /* Initialise the entry -- the id is already set */
+ entry->peer = peer ;
+ entry->next_free = entry ;
+
+ peer->index_entry = entry;
+
+ /* Insert the new entry into the symbol table. */
+ entry = symbol_set_value(symbol_find(&bgp_peer_index, su), entry) ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ passert(entry == NULL) ; /* Must be new entry */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Deregister a peer from the peer index.
+ *
+ * For use by the Routeing Engine.
+ *
+ * NB: The peer MUST NOT be deregistered if any of the following apply:
+ *
+ * * there is an active session
+ *
+ * * the peer_id is still in use (anywhere at all)
+ *
+ * NB: it is a FATAL error to deregister a peer which is not registered.
+ */
+extern void
+bgp_peer_index_deregister(bgp_peer peer, union sockunion* su)
+{
+ bgp_peer_index_entry entry ;
+ symbol sym ;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ sym = symbol_seek(&bgp_peer_index, su) ;
+ passert(sym != NULL) ;
+
+ entry = symbol_delete(sym) ;
+
+ passert( (entry != NULL) && (entry->id != bgp_peer_id_null)
+ && (entry->peer == peer)
+ && (entry->next_free == entry) ) ;
+
+ bgp_peer_id_table_free_entry(entry) ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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_index_entry entry ;
+
+ /* Only the Routing Engine can add/delete entries -- so no lock required */
+
+ entry = bgp_peer_index_seek_entry(su) ;
+
+ return (entry != NULL) ? entry->peer : NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Lookup a peer's peer index entry -- do nothing if does not exist
+ *
+ * For use by the Routeing Engine.
+ *
+ * Returns the bgp_peer_index_entry -- NULL if not found.
+ */
+extern bgp_peer_index_entry
+bgp_peer_index_seek_entry(union sockunion* su)
+{
+ bgp_peer_index_entry entry ;
+
+ /* Only the Routing Engine can add/delete entries -- so no lock required */
+
+ entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+
+ if (entry != NULL)
+ assert((entry->peer != NULL) && (entry->next_free = entry)) ;
+
+ return entry ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set peer->session field.
+ *
+ * This is done under the Peer Index Mutex, so that the BGP Engine can step
+ * from the Peer Index entry, via the peer structure, to the session, which is
+ * also controlled by that Mutex, in safety.
+ */
+extern void
+bgp_peer_index_set_session(bgp_peer peer, bgp_session session)
+{
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ peer->session = session ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+} ;
+
+/*------------------------------------------------------------------------------
+ * Find whether given address is for a known peer, and if so whether it has
+ * an active session which is prepared to accept() a connection.
+ *
+ * For use by the BGP Engine.
+ *
+ * Returns: bgp_connection if: peer with given address is configured
+ * and: the session is prepared to accept()
+ *
+ * Note that the session cannot be deleted while it is in a prepared
+ * to accept state.
+ *
+ * or: NULL otherwise
+ *
+ * Sets *p_found <=> a peer with the given address is configured.
+ *
+ * NB: the BGP Engine sets/clears the pointer to the connection. The pointer
+ * is initialised NULL when the index entry is created.
+ */
+extern bgp_connection
+bgp_peer_index_seek_accept(union sockunion* su, bool* p_found)
+{
+ bgp_connection accept ;
+ bgp_peer_index_entry entry ;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+
+ if (entry != NULL)
+ {
+ *p_found = true ;
+ accept = bgp_connection_query_accept(entry->peer->session) ;
+ }
+ else
+ {
+ *p_found = false ;
+ accept = NULL ;
+ } ;
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return accept ;
+} ;
+
+/*==============================================================================
+ * Extending the bgp_peer_id_table and adding free entries to it.
+ *
+ * NB: when entry is free, the peer field is NULL.
+ * when entry is in use, the peer field is not NULL !
+ *
+ * when entry is free, the accept field is overloaded as pointer to next
+ * free entry.
+ */
+
+/*------------------------------------------------------------------------------
+ * Free the given peer index entry and release its peer_id.
+ */
+static void
+bgp_peer_id_table_free_entry(bgp_peer_index_entry entry)
+{
+ assert((entry != NULL) && (entry->id < bgp_peer_id_count)) ;
+ assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ;
+
+ if (bgp_peer_id_free_head == NULL)
+ bgp_peer_id_free_head = entry ;
+ else
+ bgp_peer_id_free_tail->next_free = entry ;
+
+ bgp_peer_id_free_tail = entry ;
+ entry->next_free = NULL ;
+
+ entry->peer = NULL ; /* only when free ! */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a new set of free bgp_peer_ids.
+ */
+static void
+bgp_peer_id_table_make_ids(void)
+{
+ bgp_peer_id_t id_new ;
+ bgp_peer_id_table_chunk chunk ;
+ bgp_peer_index_entry entry ;
+
+ chunk = XCALLOC(MTYPE_BGP_PEER_ID_TABLE,
+ sizeof(struct bgp_peer_id_table_chunk)) ;
+ chunk->next = bgp_peer_id_table ;
+ bgp_peer_id_table = chunk ;
+
+ entry = &chunk->entries[0] ;
+
+ /* Special case to avoid id == 0 being used. Is not set in vector. */
+ if (bgp_peer_id_count == 0)
+ {
+ confirm(bgp_peer_id_null == 0) ;
+
+ entry->id = 0 ; /* should never be used */
+ entry->peer = NULL ; /* invalid in use */
+ entry->next_free = NULL ; /* invalid in use */
+
+ ++entry ; /* step past id == 0 */
+ id_new = 1 ; /* avoid setting id == 0 free */
+ }
+ else
+ id_new = bgp_peer_id_count ;
+
+ bgp_peer_id_count += bgp_peer_id_unit ;
+
+ while (id_new < bgp_peer_id_count)
+ {
+ vector_set_item(&bgp_peer_id_index, id_new, entry) ;
+
+ entry->id = id_new ;
+ bgp_peer_id_table_free_entry(entry) ;
+
+ ++id_new ;
+ ++entry ;
+ } ;
+} ;
+
+
diff --git a/bgpd/bgp_peer_index.h b/bgpd/bgp_peer_index.h
new file mode 100644
index 00000000..2ce3fb16
--- /dev/null
+++ b/bgpd/bgp_peer_index.h
@@ -0,0 +1,88 @@
+/* BGP Peer Index -- 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_PEER_INDEX_H
+#define _QUAGGA_BGP_PEER_INDEX_H
+
+#include "bgpd/bgp_common.h"
+
+#include "lib/sockunion.h"
+
+/*==============================================================================
+ * The Peer Index maps:
+ *
+ * * IP address (name of peer)
+ * * peer_id (ordinal of peer)
+ *
+ * To the bgp_peer_index_entry.
+ */
+typedef struct bgp_peer_index_entry* bgp_peer_index_entry ;
+
+typedef unsigned bgp_peer_id_t ;
+
+struct bgp_peer_index_entry
+{
+ bgp_peer_index_entry next_free ; /* for list of free peer_id's */
+ /* points to self if entry is in use */
+
+ bgp_peer peer ; /* NULL if entry is not in use */
+
+ bgp_peer_id_t id ; /* maps IP address to peer_id */
+} ;
+
+enum { bgp_peer_id_null = 0 } ; /* no peer can have id == 0 */
+
+/*==============================================================================
+ *
+ */
+
+extern void
+bgp_peer_index_init(void* parent) ;
+
+extern void
+bgp_peer_index_mutex_init(void) ;
+
+extern void
+bgp_peer_index_reset(void) ;
+
+extern void
+bgp_peer_index_mutex_free(void) ;
+
+extern void
+bgp_peer_index_register(bgp_peer peer, union sockunion* su) ;
+
+extern void
+bgp_peer_index_deregister(bgp_peer peer, union sockunion* su) ;
+
+extern bgp_peer
+bgp_peer_index_seek(sockunion su) ;
+
+extern bgp_peer_index_entry
+bgp_peer_index_seek_entry(sockunion su) ;
+
+extern void
+bgp_peer_index_set_session(bgp_peer peer, bgp_session session) ;
+
+extern bgp_connection
+bgp_peer_index_seek_accept(sockunion su, bool* p_found) ;
+
+#endif /* _QUAGGA_BGP_PEER_INDEX_H */
+
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index aabd264a..bcf23db5 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"
@@ -58,18 +61,18 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
/* Extern from bgp_dump.c */
extern const char *bgp_origin_str[];
extern const char *bgp_origin_long_str[];
-
+
static struct bgp_node *
bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p,
struct prefix_rd *prd)
{
struct bgp_node *rn;
struct bgp_node *prn = NULL;
-
+
assert (table);
if (!table)
return NULL;
-
+
if (safi == SAFI_MPLS_VPN)
{
prn = bgp_node_get (table, (struct prefix *) prd);
@@ -88,7 +91,7 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix
return rn;
}
-
+
/* Allocate bgp_info_extra */
static struct bgp_info_extra *
bgp_info_extra_new (void)
@@ -105,11 +108,11 @@ bgp_info_extra_free (struct bgp_info_extra **extra)
{
if ((*extra)->damp_info)
bgp_damp_info_free ((*extra)->damp_info, 0);
-
+
(*extra)->damp_info = NULL;
-
+
XFREE (MTYPE_BGP_ROUTE_EXTRA, *extra);
-
+
*extra = NULL;
}
}
@@ -138,10 +141,8 @@ bgp_info_free (struct bgp_info *binfo)
{
if (binfo->attr)
bgp_attr_unintern (&binfo->attr);
-
- bgp_info_extra_free (&binfo->extra);
- peer_unlock (binfo->peer); /* bgp_info peer reference */
+ bgp_info_extra_free (&binfo->extra);
XFREE (MTYPE_BGP_ROUTE, binfo);
}
@@ -158,7 +159,7 @@ bgp_info_unlock (struct bgp_info *binfo)
{
assert (binfo && binfo->lock > 0);
binfo->lock--;
-
+
if (binfo->lock == 0)
{
#if 0
@@ -176,42 +177,67 @@ bgp_info_unlock (struct bgp_info *binfo)
zlog_backtrace (LOG_DEBUG);
}
#endif
-
+
return binfo;
}
void
bgp_info_add (struct bgp_node *rn, struct bgp_info *ri)
{
- struct bgp_info *top;
+ bgp_peer peer = ri->peer ;
+ struct bgp_info** routes_head ;
- top = rn->info;
-
- ri->next = rn->info;
- ri->prev = NULL;
- if (top)
- top->prev = ri;
+ /* add to list of routes for this bgp_node */
+ ri->rn = rn ;
+ ri->info_next = rn->info;
+ ri->info_prev = NULL;
+ if (rn->info != NULL)
+ ((struct bgp_info*)rn->info)->info_prev = ri;
rn->info = ri;
-
+
+ /* add to list of routes for this peer */
+ routes_head = &(peer->routes_head[rn->table->afi][rn->table->safi]) ;
+ ri->routes_next = *routes_head ;
+ ri->routes_prev = NULL ;
+ if (*routes_head != NULL)
+ (*routes_head)->routes_prev = ri ;
+ *routes_head = ri ;
+
bgp_info_lock (ri);
bgp_lock_node (rn);
- peer_lock (ri->peer); /* bgp_info peer reference */
+ bgp_peer_lock (peer); /* bgp_info peer reference */
}
-/* Do the actual removal of info from RIB, for use by bgp_process
+/* Do the actual removal of info from RIB, for use by bgp_process
completion callback *only* */
static void
bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri)
{
- if (ri->next)
- ri->next->prev = ri->prev;
- if (ri->prev)
- ri->prev->next = ri->next;
+ bgp_peer peer = ri->peer ;
+ struct bgp_info** routes_head ;
+
+ assert(ri->rn == rn) ;
+
+ /* remove from list of routes for the bgp_node */
+ if (ri->info_next)
+ ri->info_next->info_prev = ri->info_prev;
+ if (ri->info_prev)
+ ri->info_prev->info_next = ri->info_next;
else
- rn->info = ri->next;
-
- bgp_info_unlock (ri);
- bgp_unlock_node (rn);
+ rn->info = ri->info_next;
+
+ /* remove from list of routes for the peer */
+ routes_head = &(peer->routes_head[rn->table->afi][rn->table->safi]) ;
+ if (ri->routes_next != NULL)
+ ri->routes_next->routes_prev = ri->routes_prev ;
+ if (ri->routes_prev != NULL)
+ ri->routes_prev->routes_next = ri->routes_next ;
+ else
+ *routes_head = ri->routes_next ;
+
+ bgp_info_unlock (ri); /* fewer references to bgp_info */
+ bgp_unlock_node (rn); /* fewer references to bgp_node */
+ bgp_peer_unlock (peer); /* fewer references to peer */
}
void
@@ -233,7 +259,7 @@ bgp_info_restore (struct bgp_node *rn, struct bgp_info *ri)
SET_FLAG (ri->flags, BGP_INFO_VALID);
}
-/* Adjust pcount as required */
+/* Adjust pcount as required */
static void
bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri)
{
@@ -244,13 +270,13 @@ bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri)
if (rn->table->type != BGP_TABLE_MAIN
|| ri->peer == ri->peer->bgp->peer_self)
return;
-
+
if (BGP_INFO_HOLDDOWN (ri)
&& CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
{
-
+
UNSET_FLAG (ri->flags, BGP_INFO_COUNTED);
-
+
/* slight hack, but more robust against errors. */
if (ri->peer->pcount[rn->table->afi][rn->table->safi])
ri->peer->pcount[rn->table->afi][rn->table->safi]--;
@@ -260,9 +286,9 @@ bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri)
__func__, ri->peer->host);
zlog_backtrace (LOG_WARNING);
zlog_warn ("%s: Please report to Quagga bugzilla", __func__);
- }
+ }
}
- else if (!BGP_INFO_HOLDDOWN (ri)
+ else if (!BGP_INFO_HOLDDOWN (ri)
&& !CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
{
SET_FLAG (ri->flags, BGP_INFO_COUNTED);
@@ -278,11 +304,11 @@ void
bgp_info_set_flag (struct bgp_node *rn, struct bgp_info *ri, u_int32_t flag)
{
SET_FLAG (ri->flags, flag);
-
+
/* early bath if we know it's not a flag that changes useability state */
if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_UNUSEABLE))
return;
-
+
bgp_pcount_adjust (rn, ri);
}
@@ -290,11 +316,11 @@ void
bgp_info_unset_flag (struct bgp_node *rn, struct bgp_info *ri, u_int32_t flag)
{
UNSET_FLAG (ri->flags, flag);
-
+
/* early bath if we know it's not a flag that changes useability state */
if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_UNUSEABLE))
return;
-
+
bgp_pcount_adjust (rn, ri);
}
@@ -331,6 +357,7 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
int internal_as_route = 0;
int confed_as_route = 0;
int ret;
+ bgp_peer_sort_t new_sort, exist_sort ;
/* 0. Null check. */
if (new == NULL)
@@ -358,7 +385,7 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
exist_pref = exist->attr->local_pref;
else
exist_pref = bgp->default_local_pref;
-
+
if (new_pref > exist_pref)
return 1;
if (new_pref < exist_pref)
@@ -385,14 +412,14 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
{
int exist_hops = aspath_count_hops (exist->attr->aspath);
int exist_confeds = aspath_count_confeds (exist->attr->aspath);
-
+
if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
{
int aspath_hops;
-
+
aspath_hops = aspath_count_hops (new->attr->aspath);
aspath_hops += aspath_count_confeds (new->attr->aspath);
-
+
if ( aspath_hops < (exist_hops + exist_confeds))
return 1;
if ( aspath_hops > (exist_hops + exist_confeds))
@@ -401,7 +428,7 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
else
{
int newhops = aspath_count_hops (new->attr->aspath);
-
+
if (newhops < exist_hops)
return 1;
if (newhops > exist_hops)
@@ -422,7 +449,7 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
&& aspath_count_confeds (exist->attr->aspath) > 0
&& aspath_count_hops (new->attr->aspath) == 0
&& aspath_count_hops (exist->attr->aspath) == 0);
-
+
if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED)
|| (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)
&& confed_as_route)
@@ -439,18 +466,18 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
return 0;
}
- /* 7. Peer type check. */
- if (peer_sort (new->peer) == BGP_PEER_EBGP
- && peer_sort (exist->peer) == BGP_PEER_IBGP)
+ /* 7. Peer type check. CONFED and iBGP rank equal, "internal" (RFC5065)
+ */
+ new_sort = peer_sort(new->peer) ;
+ exist_sort = peer_sort(exist->peer) ;
+
+ if ((new_sort == BGP_PEER_EBGP) && (exist_sort == BGP_PEER_IBGP))
return 1;
- if (peer_sort (new->peer) == BGP_PEER_EBGP
- && peer_sort (exist->peer) == BGP_PEER_CONFED)
+ if ((new_sort == BGP_PEER_EBGP) && (exist_sort == BGP_PEER_CONFED))
return 1;
- if (peer_sort (new->peer) == BGP_PEER_IBGP
- && peer_sort (exist->peer) == BGP_PEER_EBGP)
+ if ((new_sort == BGP_PEER_IBGP) && (exist_sort == BGP_PEER_EBGP))
return 0;
- if (peer_sort (new->peer) == BGP_PEER_CONFED
- && peer_sort (exist->peer) == BGP_PEER_EBGP)
+ if ((new_sort == BGP_PEER_CONFED) && (exist_sort == BGP_PEER_EBGP))
return 0;
/* 8. IGP metric check. */
@@ -458,7 +485,7 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
{
uint32_t newm = (new->extra ? new->extra->igpmetric : 0);
uint32_t existm = (exist->extra ? exist->extra->igpmetric : 0);
-
+
if (newm < existm)
return 1;
if (newm > existm)
@@ -472,8 +499,8 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist)
newer path won't displace an older one, even if it was the
preferred route based on the additional decision criteria below. */
if (! bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID)
- && peer_sort (new->peer) == BGP_PEER_EBGP
- && peer_sort (exist->peer) == BGP_PEER_EBGP)
+ && (new_sort == BGP_PEER_EBGP)
+ && (exist_sort == BGP_PEER_EBGP) )
{
if (CHECK_FLAG (new->flags, BGP_INFO_SELECTED))
return 1;
@@ -532,31 +559,31 @@ bgp_input_filter (struct peer *peer, struct prefix *p, struct attr *attr,
#define FILTER_EXIST_WARN(F,f,filter) \
if (BGP_DEBUG (update, UPDATE_IN) \
- && !(F ## _IN (filter))) \
+ && !(F ## _IN_LIST (filter))) \
plog_warn (peer->log, "%s: Could not find configured input %s-list %s!", \
peer->host, #f, F ## _IN_NAME(filter));
-
+
if (DISTRIBUTE_IN_NAME (filter)) {
FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter);
-
- if (access_list_apply (DISTRIBUTE_IN (filter), p) == FILTER_DENY)
+
+ if (access_list_apply (DISTRIBUTE_IN_LIST (filter), p) == FILTER_DENY)
return FILTER_DENY;
}
if (PREFIX_LIST_IN_NAME (filter)) {
FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter);
-
- if (prefix_list_apply (PREFIX_LIST_IN (filter), p) == PREFIX_DENY)
+
+ if (prefix_list_apply (PREFIX_LIST_IN_LIST(filter), p) == PREFIX_DENY)
return FILTER_DENY;
}
-
+
if (FILTER_LIST_IN_NAME (filter)) {
FILTER_EXIST_WARN(FILTER_LIST, as, filter);
-
- if (as_list_apply (FILTER_LIST_IN (filter), attr->aspath)== AS_FILTER_DENY)
+
+ if (as_list_apply (FILTER_LIST_IN_LIST (filter), attr->aspath)== AS_FILTER_DENY)
return FILTER_DENY;
}
-
+
return FILTER_PERMIT;
#undef FILTER_EXIST_WARN
}
@@ -571,28 +598,29 @@ bgp_output_filter (struct peer *peer, struct prefix *p, struct attr *attr,
#define FILTER_EXIST_WARN(F,f,filter) \
if (BGP_DEBUG (update, UPDATE_OUT) \
- && !(F ## _OUT (filter))) \
+ && !(F ## _OUT_LIST (filter))) \
plog_warn (peer->log, "%s: Could not find configured output %s-list %s!", \
peer->host, #f, F ## _OUT_NAME(filter));
if (DISTRIBUTE_OUT_NAME (filter)) {
FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter);
-
- if (access_list_apply (DISTRIBUTE_OUT (filter), p) == FILTER_DENY)
+
+ if (access_list_apply (DISTRIBUTE_OUT_LIST (filter), p) == FILTER_DENY)
return FILTER_DENY;
}
- if (PREFIX_LIST_OUT_NAME (filter)) {
+ if (PREFIX_LIST_OUT_REF (filter)) {
FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter);
-
- if (prefix_list_apply (PREFIX_LIST_OUT (filter), p) == PREFIX_DENY)
+
+ if (prefix_list_apply (PREFIX_LIST_OUT_LIST (filter), p) == PREFIX_DENY)
return FILTER_DENY;
}
if (FILTER_LIST_OUT_NAME (filter)) {
FILTER_EXIST_WARN(FILTER_LIST, as, filter);
-
- if (as_list_apply (FILTER_LIST_OUT (filter), attr->aspath) == AS_FILTER_DENY)
+
+ if (as_list_apply (FILTER_LIST_OUT_LIST (filter), attr->aspath)
+ == AS_FILTER_DENY)
return FILTER_DENY;
}
@@ -606,18 +634,19 @@ bgp_community_filter (struct peer *peer, struct attr *attr)
{
if (attr->community)
{
+ bgp_peer_sort_t sort = peer_sort(peer) ;
+
/* NO_ADVERTISE check. */
if (community_include (attr->community, COMMUNITY_NO_ADVERTISE))
return 1;
/* NO_EXPORT check. */
- if (peer_sort (peer) == BGP_PEER_EBGP &&
+ if ((sort == BGP_PEER_EBGP) &&
community_include (attr->community, COMMUNITY_NO_EXPORT))
return 1;
/* NO_EXPORT_SUBCONFED check. */
- if (peer_sort (peer) == BGP_PEER_EBGP
- || peer_sort (peer) == BGP_PEER_CONFED)
+ if ((sort == BGP_PEER_EBGP) || (sort == BGP_PEER_CONFED))
if (community_include (attr->community, COMMUNITY_NO_EXPORT_SUBCONFED))
return 1;
}
@@ -636,139 +665,447 @@ bgp_cluster_filter (struct peer *peer, struct attr *attr)
cluster_id = peer->bgp->cluster_id;
else
cluster_id = peer->bgp->router_id;
-
+
if (cluster_loop_check (attr->extra->cluster, cluster_id))
return 1;
}
return 0;
}
-
-static int
+
+/*------------------------------------------------------------------------------
+ * Process given attributes against any in route-map.
+ *
+ * If the result is RMAP_PERMIT, then returns address of newly internalised
+ * version of the attributes.
+ *
+ * If the result is RMAP_DENY, then returns NULL.
+ *
+ * The structure pointed to by attr is untouched.
+ *
+ * NB: All the elements of the incoming attr must have been internalised.
+ *
+ * This is because a copy -- bgp_attr_dup() -- of those attributes is handed
+ * to the route-map. Any element of the attributes which is changed is
+ * overwritten by the route-map -- and if it has a 0 reference count, the
+ * element will be deleted. Unfortunately, that leaves a dangling reference
+ * in the original attr.
+ */
+static struct attr*
bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr,
afi_t afi, safi_t safi)
{
struct bgp_filter *filter;
- struct bgp_info info;
- route_map_result_t ret;
+ struct attr rmap_attr_s ;
+ struct attr* rmap_attr ;
+ struct attr* use_attr ;
- filter = &peer->filter[afi][safi];
+ rmap_attr = NULL ;
- /* Apply default weight value. */
+ /* Apply default weight value. */
if (peer->weight)
- (bgp_attr_extra_get (attr))->weight = peer->weight;
+ {
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup (rmap_attr, attr) ;
+
+ (bgp_attr_extra_get (rmap_attr))->weight = peer->weight;
+ } ;
/* Route map apply. */
+ filter = &peer->filter[afi][safi];
+
if (ROUTE_MAP_IN_NAME (filter))
{
- /* Duplicate current value to new strucutre for modification. */
- info.peer = peer;
- info.attr = attr;
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret;
- SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN);
+ if (rmap_attr == NULL)
+ {
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup (rmap_attr, attr) ;
+ } ;
- /* Apply BGP route map to the attribute. */
- ret = route_map_apply (ROUTE_MAP_IN (filter), p, RMAP_BGP, &info);
+ /* Duplicate current value to new structure for modification. */
+ info_s.peer = peer;
+ info_s.attr = rmap_attr;
+
+ SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN);
+
+ /* Apply BGP route map to the attribute. */
+ ret = route_map_apply (ROUTE_MAP_IN (filter), p, RMAP_BGP, &info_s);
peer->rmap_type = 0;
if (ret == RMAP_DENYMATCH)
- {
- /* Free newly generated AS path and community by route-map. */
- bgp_attr_flush (attr);
- return RMAP_DENY;
- }
+ {
+ /* Discard any new elements set by the route-map -- these will have
+ * reference counts == 0.
+ *
+ * Discard any "extra" part of the duplicated attributes.
+ */
+ bgp_attr_flush (rmap_attr);
+ bgp_attr_extra_free (rmap_attr);
+
+ return NULL ;
+ } ;
+ } ;
+
+ /* If the attributes may have changed, intern the result.
+ *
+ * Otherwise, intern the incoming stuff
+ */
+ if (rmap_attr != NULL)
+ {
+ use_attr = bgp_attr_intern(rmap_attr) ;
+
+ bgp_attr_extra_free (rmap_attr) ;
+ }
+ else
+ use_attr = bgp_attr_intern(attr) ;
+
+ return use_attr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Structure to capture route-server route.
+ *
+ * Main purpose of this is to do any required rs-in route-map once when
+ * updating a set of route server clients, and not at all if there are no
+ * route server clients for the given afi/safi.
+ */
+struct rs_route
+{
+ bool rs_in_applied ; /* whether rs-in applied yet */
+ bool rs_in_deny ; /* answer when it has been */
+
+ /* The orig_attr MUST have all elements interned, but may or may not be
+ * interned itself.
+ */
+ struct attr* orig_attr ; /* attributes before rs-in applied */
+
+ /* The rs_in_attr is interned when the pointer is set.
+ *
+ * The pointer is NULL if the rs-in has not been applied, and remains NULL
+ * if the answer is RMAP_DENY.
+ */
+ struct attr* rs_in_attr ; /* attributes after rs-in applied */
+
+ /* The other attributes of the route */
+
+ struct peer* peer ;
+
+ afi_t afi ;
+ safi_t safi ;
+
+ struct prefix* p ;
+
+ int type ;
+ int sub_type ;
+
+ struct prefix_rd* prd ;
+ u_char* tag ;
+};
+
+/*------------------------------------------------------------------------------
+ * Set up an rs_route object.
+ */
+static void
+bgp_rs_route_init(struct rs_route* rt, afi_t afi, safi_t safi,
+ struct attr* attr, struct peer* peer, struct prefix* p,
+ int type, int sub_type, struct prefix_rd* prd, u_char* tag)
+{
+ rt->rs_in_applied = false ;
+ rt->rs_in_deny = 0 ; /* invalid while !rs_in_applied */
+ rt->rs_in_attr = NULL ; /* nothing yet */
+
+ rt->orig_attr = attr ;
+
+ rt->peer = peer ;
+ rt->afi = afi ;
+ rt->safi = safi ;
+ rt->p = p ;
+ rt->type = type ;
+ rt->sub_type = sub_type ;
+ rt->prd = prd ;
+ rt->tag = tag ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset an rs_route object.
+ *
+ * Discards any rs_in_attr and clears the rs_in_applied flag.
+ *
+ * Leaves everything else -- so can be reused pretty much as is.
+ */
+static void
+bgp_rs_route_reset(struct rs_route* rt)
+{
+ if (rt->rs_in_attr != NULL)
+ {
+ bgp_attr_unintern(&rt->rs_in_attr) ;
+ rt->rs_in_attr = NULL ;
+ } ;
+
+ rt->rs_in_applied = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process rt->orig_attr against any rs-in route-map.
+ *
+ * If the result is RMAP_PERMIT, then rt->rs_in_attr will be set to a newly
+ * internalised version of the attributes.
+ *
+ * If the result is RMAP_DENY, then rt->rs_in_attr is left NULL.
+ *
+ * The structure pointed to by rt->orig_attr is untouched.
+ *
+ * NB: All the elements of the incoming rt->orig_attr must have been
+ * internalised.
+ *
+ * This is because a copy -- bgp_attr_dup() -- of those attributes is handed
+ * to the route-map. Any element of the attributes which is changed is
+ * overwritten by the route-map -- and if it has a 0 reference count, the
+ * element will be deleted. Unfortunately, that leaves a dangling reference
+ * in the original rt->orig_attr.
+ *
+ * NB: must NOT be called more than once for the same "rt", hence the
+ * "rs_in_applied" flag.
+ */
+static void
+bgp_rs_input_modifier (struct rs_route* rt)
+{
+ struct bgp_filter *filter;
+
+ assert(! rt->rs_in_applied && (rt->rs_in_attr == NULL)) ;
+
+ rt->rs_in_applied = true ;
+
+ /* Route map apply. */
+ filter = &rt->peer->filter[rt->afi][rt->safi];
+
+ if (ROUTE_MAP_RS_IN_NAME (filter))
+ {
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret ;
+ struct attr* rmap_attr ;
+ struct attr rmap_attr_s ;
+
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup(rmap_attr, rt->orig_attr) ;
+
+ /* Duplicate current value to new structure for modification. */
+ info_s.peer = rt->peer;
+ info_s.attr = rmap_attr ;
+
+ SET_FLAG (rt->peer->rmap_type, PEER_RMAP_TYPE_RS_IN);
+
+ /* Apply BGP route map to the attribute. */
+ ret = route_map_apply(ROUTE_MAP_RS_IN(filter), rt->p, RMAP_BGP, &info_s) ;
+
+ rt->peer->rmap_type = 0;
+
+ if (ret == RMAP_DENYMATCH)
+ {
+ /* Discard any new elements set by the route-map -- these will have
+ * reference counts == 0.
+ */
+ bgp_attr_flush (rmap_attr);
+
+ rt->rs_in_deny = true ; /* NB: rs_in_attr is NULL */
+ }
+ else
+ {
+ rt->rs_in_attr = bgp_attr_intern(rmap_attr) ;
+ rt->rs_in_deny = false ;
+ } ;
+
+ /* Discard any "extra" part of the duplicated attributes. */
+ bgp_attr_extra_free (rmap_attr);
}
- return RMAP_PERMIT;
-}
-
-static int
-bgp_export_modifier (struct peer *rsclient, struct peer *peer,
- struct prefix *p, struct attr *attr, afi_t afi, safi_t safi)
+ else
+ {
+ /* Simply intern the original */
+ rt->rs_in_attr = bgp_attr_intern(rt->orig_attr) ;
+ rt->rs_in_deny = false ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take the already interned client_attr, and if required apply the export
+ * route-map for the peer.
+ *
+ * If not DENY, returns interned client_attr, which may or may not have changed.
+ *
+ * If DENY, returns NULL and the client_attr will have been uninterned.
+ */
+static struct attr*
+bgp_export_modifier (struct peer *rsclient, struct rs_route* rt,
+ struct attr* client_attr)
{
struct bgp_filter *filter;
- struct bgp_info info;
- route_map_result_t ret;
- filter = &peer->filter[afi][safi];
+ /* Route map apply. */
+ filter = &rt->peer->filter[rt->afi][rt->safi];
- /* Route map apply. */
if (ROUTE_MAP_EXPORT_NAME (filter))
{
- /* Duplicate current value to new strucutre for modification. */
- info.peer = rsclient;
- info.attr = attr;
+ struct bgp_info info_s = { 0 } ;
+ struct attr rmap_attr_s ;
+ struct attr* rmap_attr ;
+ route_map_result_t ret;
+
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup (rmap_attr, client_attr) ;
+
+ /* Duplicate current value to new structure for modification. */
+ info_s.peer = rsclient;
+ info_s.attr = rmap_attr ;
SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_EXPORT);
- /* Apply BGP route map to the attribute. */
- ret = route_map_apply (ROUTE_MAP_EXPORT (filter), p, RMAP_BGP, &info);
+ /* Apply BGP route map to the attribute. */
+ ret = route_map_apply(ROUTE_MAP_EXPORT(filter), rt->p, RMAP_BGP, &info_s);
rsclient->rmap_type = 0;
if (ret == RMAP_DENYMATCH)
{
- /* Free newly generated AS path and community by route-map. */
- bgp_attr_flush (attr);
- return RMAP_DENY;
- }
- }
- return RMAP_PERMIT;
-}
+ /* Discard any new elements set by the route-map -- these will have
+ * reference counts == 0.
+ */
+ bgp_attr_flush (rmap_attr);
-static int
-bgp_import_modifier (struct peer *rsclient, struct peer *peer,
- struct prefix *p, struct attr *attr, afi_t afi, safi_t safi)
+ bgp_attr_unintern(&client_attr) ;
+
+ client_attr = NULL ;
+ }
+ else
+ {
+ /* Intern the result of the rmap and unintern the old version
+ *
+ * Done in this order so that any unchanged elements in rmap_attr
+ * gain a reference before they are released from the old interned
+ * attributes.
+ */
+ struct attr* old_attr ;
+
+ old_attr = client_attr ;
+ client_attr = bgp_attr_intern(rmap_attr) ;
+ bgp_attr_unintern(&old_attr) ;
+ } ;
+
+ /* Discard any "extra" part of the duplicated attributes. */
+ bgp_attr_extra_free (rmap_attr) ;
+ } ;
+
+ return client_attr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take the already interned client_attr, and if required apply the import
+ * route-map for the route server client.
+ *
+ * If not DENY, returns interned client_attr, which may or may not have changed.
+ *
+ * If DENY, returns NULL and the client_attr will have been uninterned.
+ */
+static struct attr*
+bgp_import_modifier (struct peer *rsclient, struct rs_route* rt,
+ struct attr* client_attr)
{
struct bgp_filter *filter;
- struct bgp_info info;
- route_map_result_t ret;
+ struct attr rmap_attr_s ;
+ struct attr* rmap_attr ;
- filter = &rsclient->filter[afi][safi];
+ rmap_attr = NULL ;
- /* Apply default weight value. */
- if (peer->weight)
- (bgp_attr_extra_get (attr))->weight = peer->weight;
+ /* Apply default weight value. */
+ if (rt->peer->weight)
+ {
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup (rmap_attr, client_attr) ;
+
+ (bgp_attr_extra_get (rmap_attr))->weight = rt->peer->weight;
+ } ;
+
+ /* Route map apply. */
+ filter = &rsclient->filter[rt->afi][rt->safi];
- /* Route map apply. */
if (ROUTE_MAP_IMPORT_NAME (filter))
{
- /* Duplicate current value to new strucutre for modification. */
- info.peer = peer;
- info.attr = attr;
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret ;
- SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT);
+ if (rmap_attr == NULL)
+ {
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup (rmap_attr, client_attr) ;
+ } ;
+
+ /* Duplicate current value to new structure for modification. */
+ /* TODO: should this be rt->peer or rsclient ?? */
+ info_s.peer = rt->peer;
+ info_s.attr = rmap_attr;
+
+ SET_FLAG (rt->peer->rmap_type, PEER_RMAP_TYPE_IMPORT);
/* Apply BGP route map to the attribute. */
- ret = route_map_apply (ROUTE_MAP_IMPORT (filter), p, RMAP_BGP, &info);
+ ret = route_map_apply(ROUTE_MAP_IMPORT(filter), rt->p, RMAP_BGP, &info_s);
- peer->rmap_type = 0;
+ rt->peer->rmap_type = 0;
if (ret == RMAP_DENYMATCH)
{
- /* Free newly generated AS path and community by route-map. */
- bgp_attr_flush (attr);
- return RMAP_DENY;
+ /* Discard any new elements set by the route-map -- these will have
+ * reference counts == 0.
+ *
+ * Discard any "extra" part of the duplicated attributes.
+ */
+ bgp_attr_flush (rmap_attr);
+ bgp_attr_extra_free (rmap_attr);
+
+ bgp_attr_unintern(&client_attr) ;
+
+ return NULL ;
}
}
- return RMAP_PERMIT;
-}
-
+
+ /* If the attributes may have changed, intern the new result and unintern the
+ * old version
+ *
+ * Done in this order so that any unchanged elements in rmap_attr gain
+ * an extra reference before they are released from the old interned
+ * attributes.
+ */
+ if (rmap_attr != NULL)
+ {
+ struct attr* old_attr ;
+
+ old_attr = client_attr ;
+ client_attr = bgp_attr_intern(rmap_attr) ;
+ bgp_attr_unintern(&old_attr) ;
+
+ bgp_attr_extra_free (rmap_attr) ;
+ } ;
+
+ return client_attr ;
+} ;
+
static int
bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
struct attr *attr, afi_t afi, safi_t safi)
{
- int ret;
char buf[SU_ADDRSTRLEN];
struct bgp_filter *filter;
struct peer *from;
struct bgp *bgp;
int transparent;
int reflect;
+ bgp_peer_sort_t sort, from_sort ;
from = ri->peer;
filter = &peer->filter[afi][safi];
bgp = peer->bgp;
-
+
if (DISABLE_BGP_ANNOUNCE)
return 0;
@@ -814,7 +1151,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
transparent = 0;
/* If community is not disabled check the no-export and local. */
- if (! transparent && bgp_community_filter (peer, ri->attr))
+ if (! transparent && bgp_community_filter (peer, ri->attr))
return 0;
/* If the attribute has originator-id and it is same as remote
@@ -823,7 +1160,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
{
if (IPV4_ADDR_SAME (&peer->remote_id, &ri->attr->extra->originator_id))
{
- if (BGP_DEBUG (filter, FILTER))
+ if (BGP_DEBUG (filter, FILTER))
zlog (peer->log, LOG_DEBUG,
"%s [Update:SEND] %s/%d originator-id is same as remote router-id",
peer->host,
@@ -832,7 +1169,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
return 0;
}
}
-
+
/* ORF prefix-list filter check */
if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)
&& (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
@@ -859,8 +1196,8 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
/* AS path loop check. */
if (aspath_loop_check (ri->attr->aspath, peer->as))
{
- if (BGP_DEBUG (filter, FILTER))
- zlog (peer->log, LOG_DEBUG,
+ if (BGP_DEBUG (filter, FILTER))
+ zlog (peer->log, LOG_DEBUG,
"%s [Update:SEND] suppress announcement to peer AS %u is AS path.",
peer->host, peer->as);
return 0;
@@ -872,17 +1209,20 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
{
if (aspath_loop_check(ri->attr->aspath, bgp->confed_id))
{
- if (BGP_DEBUG (filter, FILTER))
- zlog (peer->log, LOG_DEBUG,
+ if (BGP_DEBUG (filter, FILTER))
+ zlog (peer->log, LOG_DEBUG,
"%s [Update:SEND] suppress announcement to peer AS %u is AS path.",
peer->host,
bgp->confed_id);
return 0;
- }
+ }
}
/* Route-Reflect check. */
- if (peer_sort (from) == BGP_PEER_IBGP && peer_sort (peer) == BGP_PEER_IBGP)
+ from_sort = peer_sort (from) ;
+ sort = peer_sort (peer) ;
+
+ if ((from_sort == BGP_PEER_IBGP) && (sort == BGP_PEER_IBGP))
reflect = 1;
else
reflect = 0;
@@ -909,13 +1249,12 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
return 0;
}
}
-
+
/* For modify attribute, copy it to temporary structure. */
bgp_attr_dup (attr, ri->attr);
-
+
/* If local-preference is not set. */
- if ((peer_sort (peer) == BGP_PEER_IBGP
- || peer_sort (peer) == BGP_PEER_CONFED)
+ if (((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED))
&& (! (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))))
{
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
@@ -923,7 +1262,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
}
/* Remove MED if its an EBGP peer - will get overwritten by route-maps */
- if (peer_sort (peer) == BGP_PEER_EBGP
+ if ((sort == BGP_PEER_EBGP)
&& attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
{
if (ri->peer != bgp->peer_self && ! transparent
@@ -936,7 +1275,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
|| (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
&& ((p->family == AF_INET && attr->nexthop.s_addr)
#ifdef HAVE_IPV6
- || (p->family == AF_INET6 &&
+ || (p->family == AF_INET6 &&
! IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
#endif /* HAVE_IPV6 */
)))
@@ -946,10 +1285,10 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF)
|| (p->family == AF_INET && attr->nexthop.s_addr == 0)
#ifdef HAVE_IPV6
- || (p->family == AF_INET6 &&
+ || (p->family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
#endif /* HAVE_IPV6 */
- || (peer_sort (peer) == BGP_PEER_EBGP
+ || ((sort == BGP_PEER_EBGP)
&& bgp_multiaccess_check_v4 (attr->nexthop, peer->host) == 0))
{
/* Set IPv4 nexthop. */
@@ -966,7 +1305,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
if (p->family == AF_INET6)
{
/* IPv6 global nexthop must be included. */
- memcpy (&attr->extra->mp_nexthop_global, &peer->nexthop.v6_global,
+ memcpy (&attr->extra->mp_nexthop_global, &peer->nexthop.v6_global,
IPV6_MAX_BYTELEN);
attr->extra->mp_nexthop_len = 16;
}
@@ -976,8 +1315,8 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
#ifdef HAVE_IPV6
if (p->family == AF_INET6)
{
- /* Left nexthop_local unchanged if so configured. */
- if ( CHECK_FLAG (peer->af_flags[afi][safi],
+ /* Left nexthop_local unchanged if so configured. */
+ if ( CHECK_FLAG (peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) )
{
if ( IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_local) )
@@ -987,16 +1326,16 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
}
/* Default nexthop_local treatment for non-RS-Clients */
- else
+ else
{
/* Link-local address should not be transit to different peer. */
attr->extra->mp_nexthop_len = 16;
/* Set link-local address for shared network peer. */
- if (peer->shared_network
+ if (peer->shared_network
&& ! IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local))
{
- memcpy (&attr->extra->mp_nexthop_local, &peer->nexthop.v6_local,
+ memcpy (&attr->extra->mp_nexthop_local, &peer->nexthop.v6_local,
IPV6_MAX_BYTELEN);
attr->extra->mp_nexthop_len = 32;
}
@@ -1015,7 +1354,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
#endif /* HAVE_IPV6 */
/* If this is EBGP peer and remove-private-AS is set. */
- if (peer_sort (peer) == BGP_PEER_EBGP
+ if ((sort == BGP_PEER_EBGP)
&& peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)
&& aspath_private_as_check (attr->aspath))
attr->aspath = aspath_empty_get ();
@@ -1024,33 +1363,42 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
if (ROUTE_MAP_OUT_NAME (filter)
|| (ri->extra && ri->extra->suppress) )
{
- struct bgp_info info;
- struct attr dummy_attr = { 0 };
-
- info.peer = peer;
- info.attr = attr;
+ struct bgp_info info_s = { 0 } ;
+ struct attr dummy_attr_s ;
+ struct attr* dummy_attr ;
+ route_map_result_t ret;
+
+ info_s.peer = peer;
/* The route reflector is not allowed to modify the attributes
of the reflected IBGP routes. */
- if (peer_sort (from) == BGP_PEER_IBGP
- && peer_sort (peer) == BGP_PEER_IBGP)
+ if ((from_sort == BGP_PEER_IBGP) && (sort == BGP_PEER_IBGP))
{
- bgp_attr_dup (&dummy_attr, attr);
- info.attr = &dummy_attr;
+ dummy_attr = &dummy_attr_s ;
+ bgp_attr_dup (dummy_attr, attr);
+ info_s.attr = dummy_attr;
}
+ else
+ {
+ dummy_attr = NULL ;
+ info_s.attr = attr;
+ } ;
- SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT);
+ SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT);
if (ri->extra && ri->extra->suppress)
- ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info);
+ ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info_s);
else
- ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info);
+ ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info_s);
peer->rmap_type = 0;
-
- if (dummy_attr.extra)
- bgp_attr_extra_free (&dummy_attr);
-
+
+ if (dummy_attr != NULL)
+ {
+ bgp_attr_flush (dummy_attr) ;
+ bgp_attr_extra_free (dummy_attr) ;
+ } ;
+
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (attr);
@@ -1064,10 +1412,8 @@ static int
bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient,
struct prefix *p, struct attr *attr, afi_t afi, safi_t safi)
{
- int ret;
char buf[SU_ADDRSTRLEN];
struct bgp_filter *filter;
- struct bgp_info info;
struct peer *from;
struct bgp *bgp;
@@ -1103,7 +1449,7 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient,
peer's id. */
if (ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID))
{
- if (IPV4_ADDR_SAME (&rsclient->remote_id,
+ if (IPV4_ADDR_SAME (&rsclient->remote_id,
&ri->attr->extra->originator_id))
{
if (BGP_DEBUG (filter, FILTER))
@@ -1186,11 +1532,11 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient,
if (p->family == AF_INET6)
{
struct attr_extra *attre = attr->extra;
-
+
assert (attr->extra);
-
+
/* Left nexthop_local unchanged if so configured. */
- if ( CHECK_FLAG (rsclient->af_flags[afi][safi],
+ if ( CHECK_FLAG (rsclient->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) )
{
if ( IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local) )
@@ -1198,11 +1544,11 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient,
else
attre->mp_nexthop_len=16;
}
-
+
/* Default nexthop_local treatment for RS-Clients */
- else
- {
- /* Announcer and RS-Client are both in the same network */
+ else
+ {
+ /* Announcer and RS-Client are both in the same network */
if (rsclient->shared_network && from->shared_network &&
(rsclient->ifindex == from->ifindex))
{
@@ -1238,15 +1584,18 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient,
/* Route map & unsuppress-map apply. */
if (ROUTE_MAP_OUT_NAME (filter) || (ri->extra && ri->extra->suppress) )
{
- info.peer = rsclient;
- info.attr = attr;
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret;
+
+ info_s.peer = rsclient;
+ info_s.attr = attr;
SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_OUT);
if (ri->extra && ri->extra->suppress)
- ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info);
+ ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info_s);
else
- ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info);
+ ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info_s);
rsclient->rmap_type = 0;
@@ -1275,11 +1624,11 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair *
struct bgp_info *ri1;
struct bgp_info *ri2;
struct bgp_info *nextri = NULL;
-
+
/* bgp deterministic-med */
new_select = NULL;
if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
- for (ri1 = rn->info; ri1; ri1 = ri1->next)
+ for (ri1 = rn->info; ri1; ri1 = ri1->info_next)
{
if (CHECK_FLAG (ri1->flags, BGP_INFO_DMED_CHECK))
continue;
@@ -1287,8 +1636,8 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair *
continue;
new_select = ri1;
- if (ri1->next)
- for (ri2 = ri1->next; ri2; ri2 = ri2->next)
+ if (ri1->info_next)
+ for (ri2 = ri1->info_next; ri2; ri2 = ri2->info_next)
{
if (CHECK_FLAG (ri2->flags, BGP_INFO_DMED_CHECK))
continue;
@@ -1315,20 +1664,20 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair *
/* Check old selected route and new selected route. */
old_select = NULL;
new_select = NULL;
- for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1); ri = nextri)
+ for (ri = rn->info; (ri != NULL) && (nextri = ri->info_next, 1); ri = nextri)
{
if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED))
old_select = ri;
if (BGP_INFO_HOLDDOWN (ri))
{
- /* reap REMOVED routes, if needs be
+ /* reap REMOVED routes, if needs be
* selected route must stay for a while longer though
*/
if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)
&& (ri != old_select))
bgp_info_reap (rn, ri);
-
+
continue;
}
@@ -1344,7 +1693,7 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair *
if (bgp_info_cmp (bgp, ri, new_select))
new_select = ri;
}
-
+
result->old = old_select;
result->new = new_select;
@@ -1361,7 +1710,7 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected,
p = &rn->p;
/* Announce route to Established peer. */
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
return 0;
/* Address family configuration check. */
@@ -1384,43 +1733,68 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected,
bgp_adj_out_unset (rn, peer, p, afi, safi);
break;
case BGP_TABLE_RSCLIENT:
- /* Announcement to peer->conf. If the route is filtered,
+ /* Announcement to peer->conf. If the route is filtered,
withdraw it. */
- if (selected &&
+ if (selected &&
bgp_announce_check_rsclient (selected, peer, p, &attr, afi, safi))
bgp_adj_out_set (rn, peer, p, &attr, afi, safi, selected);
else
bgp_adj_out_unset (rn, peer, p, afi, safi);
break;
}
-
+
bgp_attr_extra_free (&attr);
-
+
return 0;
}
-struct bgp_process_queue
+struct bgp_process_queue
{
- struct bgp *bgp;
- struct bgp_node *rn;
- afi_t afi;
- safi_t safi;
+ struct bgp* bgp ;
+ struct bgp_node* head ;
+ struct bgp_node* tail ;
};
+WQ_ARGS_SIZE_OK(bgp_process_queue) ;
+
static wq_item_status
-bgp_process_rsclient (struct work_queue *wq, void *data)
+bgp_process_rsclient (struct work_queue *wq, work_queue_item item)
{
- struct bgp_process_queue *pq = data;
- struct bgp *bgp = pq->bgp;
- struct bgp_node *rn = pq->rn;
- afi_t afi = pq->afi;
- safi_t safi = pq->safi;
+ struct bgp_process_queue *pq = work_queue_item_args(item) ;
+ struct bgp *bgp = pq->bgp ;
+ struct bgp_node *rn ;
+ afi_t afi ;
+ safi_t safi ;
struct bgp_info *new_select;
struct bgp_info *old_select;
struct bgp_info_pair old_and_new;
struct listnode *node, *nnode;
- struct peer *rsclient = rn->table->owner;
-
+ struct bgp_table *table ;
+ struct peer *rsclient ;
+
+ assert(wq->spec.data == item) ;
+
+ /* Is there anything left on the queue ? */
+ rn = pq->head ;
+ if (rn == NULL)
+ return WQ_SUCCESS ;
+
+ /* hack off queue and prepare to process */
+
+ dassert((rn->on_wq != 0) && (rn->lock > 0)) ;
+
+ pq->head = rn->wq_next ;
+ rn->wq_next = NULL ; /* Keep tidy */
+ rn->on_wq = 0 ;
+
+ table = rn->table ;
+ rsclient = table->owner;
+ afi = table->afi;
+ safi = table->safi;
+
+ dassert(table->lock > 0) ;
+ dassert(rsclient->lock > 0) ;
+
/* Best path selection. */
bgp_best_selection (bgp, rn, &old_and_new);
new_select = old_and_new.new;
@@ -1462,41 +1836,70 @@ bgp_process_rsclient (struct work_queue *wq, void *data)
if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
bgp_info_reap (rn, old_select);
-
- UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
- return WQ_SUCCESS;
+
+ bgp_unlock_node (rn);
+ bgp_table_unlock (table); /* NB: *after* node, in case table is deleted */
+ bgp_unlock (bgp);
+
+ if (pq->head == NULL)
+ return WQ_SUCCESS ;
+ else
+ return WQ_REQUEUE ;
}
static wq_item_status
-bgp_process_main (struct work_queue *wq, void *data)
-{
- struct bgp_process_queue *pq = data;
- struct bgp *bgp = pq->bgp;
- struct bgp_node *rn = pq->rn;
- afi_t afi = pq->afi;
- safi_t safi = pq->safi;
- struct prefix *p = &rn->p;
+bgp_process_main (struct work_queue *wq, work_queue_item item)
+{
+ struct bgp_process_queue *pq = work_queue_item_args(item) ;
+ struct bgp *bgp = pq->bgp ;
+ struct bgp_node *rn ;
+ afi_t afi ;
+ safi_t safi ;
+ struct prefix *p ;
struct bgp_info *new_select;
struct bgp_info *old_select;
struct bgp_info_pair old_and_new;
struct listnode *node, *nnode;
+ struct bgp_table *table ;
struct peer *peer;
-
+
+ assert(wq->spec.data == item) ;
+
+ /* Is there anything left on the queue ? */
+ rn = pq->head ;
+ if (rn == NULL)
+ return WQ_SUCCESS ;
+
+ /* hack off queue and prepare to process */
+
+ dassert((rn->on_wq != 0) && (rn->lock > 0)) ;
+
+ pq->head = rn->wq_next ;
+ rn->wq_next = NULL ; /* Keep tidy */
+ rn->on_wq = 0 ;
+
+ table = rn->table ;
+ afi = table->afi;
+ safi = table->safi;
+
+ dassert(table->lock > 0) ;
+
+ p = &rn->p ;
+
/* Best path selection. */
bgp_best_selection (bgp, rn, &old_and_new);
old_select = old_and_new.old;
new_select = old_and_new.new;
/* Nothing to do. */
- if (old_select && old_select == new_select)
+ if (old_select && (old_select == new_select))
{
if (! CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED))
{
if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED))
bgp_zebra_announce (p, old_select, bgp);
-
- UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
- return WQ_SUCCESS;
+
+ goto finish ; /* was return ! */
}
}
@@ -1516,106 +1919,182 @@ bgp_process_main (struct work_queue *wq, void *data)
}
/* FIB update. */
- if (safi == SAFI_UNICAST && ! bgp->name &&
- ! bgp_option_check (BGP_OPT_NO_FIB))
+ if ((safi == SAFI_UNICAST) && (bgp->name == NULL) &&
+ ! bgp_option_check (BGP_OPT_NO_FIB))
{
- if (new_select
- && new_select->type == ZEBRA_ROUTE_BGP
- && new_select->sub_type == BGP_ROUTE_NORMAL)
+ if (new_select && (new_select->type == ZEBRA_ROUTE_BGP)
+ && (new_select->sub_type == BGP_ROUTE_NORMAL))
bgp_zebra_announce (p, new_select, bgp);
else
{
/* Withdraw the route from the kernel. */
- if (old_select
- && old_select->type == ZEBRA_ROUTE_BGP
- && old_select->sub_type == BGP_ROUTE_NORMAL)
+ if (old_select && (old_select->type == ZEBRA_ROUTE_BGP)
+ && (old_select->sub_type == BGP_ROUTE_NORMAL))
bgp_zebra_withdraw (p, old_select);
}
}
-
- /* Reap old select bgp_info, it it has been removed */
+
+ /* Reap old select bgp_info, it it has been removed */
if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
bgp_info_reap (rn, old_select);
-
- UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
- return WQ_SUCCESS;
-}
-static void
-bgp_processq_del (struct work_queue *wq, void *data)
-{
- struct bgp_process_queue *pq = data;
- struct bgp_table *table = pq->rn->table;
-
- bgp_unlock (pq->bgp);
- bgp_unlock_node (pq->rn);
- bgp_table_unlock (table);
- XFREE (MTYPE_BGP_PROCESS_QUEUE, pq);
+ /* Finish up */
+
+finish:
+ bgp_unlock_node (rn) ;
+ bgp_table_unlock (table) ; /* NB: *after* node, in case table is deleted */
+ bgp_unlock (bgp) ;
+
+ if (pq->head == NULL)
+ return WQ_SUCCESS ;
+ else
+ return WQ_REQUEUE ;
}
+/*------------------------------------------------------------------------------
+ * Delete item from work queue
+ */
static void
-bgp_process_queue_init (void)
+bgp_processq_del (struct work_queue *wq, work_queue_item item)
{
- bm->process_main_queue
- = work_queue_new (bm->master, "process_main_queue");
- bm->process_rsclient_queue
- = work_queue_new (bm->master, "process_rsclient_queue");
-
- if ( !(bm->process_main_queue && bm->process_rsclient_queue) )
+ struct bgp_process_queue *pq = work_queue_item_args(item) ;
+ struct bgp_node *rn ;
+
+ assert(wq->spec.data == item) ;
+
+ while ((rn = pq->head) != NULL)
{
- zlog_err ("%s: Failed to allocate work queue", __func__);
- exit (1);
- }
-
- bm->process_main_queue->spec.workfunc = &bgp_process_main;
- bm->process_main_queue->spec.del_item_data = &bgp_processq_del;
- bm->process_main_queue->spec.max_retries = 0;
- bm->process_main_queue->spec.hold = 50;
-
- memcpy (bm->process_rsclient_queue, bm->process_main_queue,
- sizeof (struct work_queue *));
- bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient;
-}
+ struct bgp_table *table ;
+
+ dassert((rn->on_wq != 0) && (rn->lock > 0)) ;
+
+ pq->head = rn->wq_next ;
+ rn->wq_next = NULL ; /* Keep tidy */
+ rn->on_wq = 0 ;
+
+ table = rn->table ;
+
+ dassert(table->lock > 0) ;
+
+ bgp_unlock_node (rn);
+ bgp_table_unlock (table); /* NB: *after* node, in case table is deleted */
+ bgp_unlock (pq->bgp);
+ } ;
+
+ wq->spec.data = NULL ;
+} ;
+/*------------------------------------------------------------------------------
+ * Create new work queue for given bgp instance and given type of table
+ */
+static work_queue
+bgp_process_queue_init (struct bgp* bgp, bgp_table_t type)
+{
+ work_queue wq ;
+ const char* name ;
+ wq_workfunc* workfunc ;
+ work_queue* p_wq ;
+
+ switch (type)
+ {
+ case BGP_TABLE_MAIN:
+ p_wq = &bgp->process_main_queue ;
+ name = "process_main_queue" ;
+ workfunc = &bgp_process_main ;
+ break ;
+ case BGP_TABLE_RSCLIENT:
+ p_wq = &bgp->process_rsclient_queue ;
+ name = "process_rsclient_queue" ;
+ workfunc = &bgp_process_rsclient ;
+ break ;
+ default:
+ zabort("invalid BGP table type") ;
+ } ;
+
+ wq = work_queue_new (bm->master, name) ;
+
+ wq->spec.data = NULL ;
+ wq->spec.errorfunc = NULL ;
+ wq->spec.workfunc = workfunc ;
+ wq->spec.del_item_data = &bgp_processq_del ;
+ wq->spec.completion_func = NULL ;
+ wq->spec.max_retries = 0 ;
+ wq->spec.hold = 50 ;
+
+ return *p_wq = wq ;
+}
+
+/*------------------------------------------------------------------------------
+ * Place given route node on appropriate work queue, so that best path
+ * selection etc. can take place later.
+ */
void
bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi)
{
- struct bgp_process_queue *pqnode;
-
- /* already scheduled for processing? */
- if (CHECK_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED))
- return;
-
- if ( (bm->process_main_queue == NULL) ||
- (bm->process_rsclient_queue == NULL) )
- bgp_process_queue_init ();
-
- pqnode = XCALLOC (MTYPE_BGP_PROCESS_QUEUE,
- sizeof (struct bgp_process_queue));
- if (!pqnode)
+ work_queue_item item ;
+ struct bgp_process_queue *pq ;
+ struct work_queue* wq ;
+
+ /* already scheduled for processing? */
+ if (rn->on_wq)
return;
- /* all unlocked in bgp_processq_del */
- bgp_table_lock (rn->table);
- pqnode->rn = bgp_lock_node (rn);
- pqnode->bgp = bgp;
- bgp_lock (bgp);
- pqnode->afi = afi;
- pqnode->safi = safi;
-
+ /* get the required work queue -- making it if necessary */
switch (rn->table->type)
{
case BGP_TABLE_MAIN:
- work_queue_add (bm->process_main_queue, pqnode);
+ wq = bgp->process_main_queue ;
+ if (wq == NULL)
+ wq = bgp_process_queue_init(bgp, BGP_TABLE_MAIN) ;
break;
case BGP_TABLE_RSCLIENT:
- work_queue_add (bm->process_rsclient_queue, pqnode);
+ wq = bgp->process_rsclient_queue ;
+ if (wq == NULL)
+ wq = bgp_process_queue_init(bgp, BGP_TABLE_RSCLIENT) ;
break;
+ default:
+ zabort("invalid rn->table->type") ;
+ }
+
+ /* get the work queue item -- making it if necessary */
+ item = wq->spec.data ;
+ if (item == NULL)
+ {
+ /* TODO: sort out assumption that item == args */
+ item = wq->spec.data = work_queue_item_add(wq) ;
+ pq = work_queue_item_args(item) ;
+
+ pq->bgp = bgp ;
+ pq->head = NULL ;
+ pq->tail = NULL ;
}
-
+ else
+ pq = work_queue_item_args(item) ;
+
+ /* all unlocked when processed or deleted */
+ bgp_lock (bgp);
+ bgp_table_lock (rn->table);
+ bgp_lock_node (rn);
+
+ /* add to the queue */
+ if (pq->head == NULL)
+ pq->head = rn ;
+ else
+ pq->tail->wq_next = rn ;
+
+ pq->tail = rn ;
+ rn->wq_next = NULL ;
+
+ rn->on_wq = 1 ;
+
return;
}
+/*============================================================================*/
+
+/*------------------------------------------------------------------------------
+ * Max Prefix Overflow timer expired -- turn off overflow status and enable.
+ */
static int
bgp_maximum_prefix_restart_timer (struct thread *thread)
{
@@ -1624,17 +2103,44 @@ bgp_maximum_prefix_restart_timer (struct thread *thread)
peer = THREAD_ARG (thread);
peer->t_pmax_restart = NULL;
+ assert(CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) ;
+
if (BGP_DEBUG (events, EVENTS))
zlog_debug ("%s Maximum-prefix restart timer expired, restore peering",
peer->host);
- peer_clear (peer);
+ UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+
+ bgp_peer_enable(peer);
return 0;
}
+/*------------------------------------------------------------------------------
+ * If there is an active max prefix restart timer, cancel it now.
+ *
+ * NB: clears PEER_STATUS_PREFIX_OVERFLOW, but does NOT enable the peer.
+ */
+void
+bgp_maximum_prefix_cancel_timer (struct peer *peer)
+{
+ if (peer->t_pmax_restart)
+ {
+ assert(CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) ;
+
+ BGP_TIMER_OFF (peer->t_pmax_restart);
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s Maximum-prefix restart timer cancelled", peer->host) ;
+ } ;
+
+ UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Number of prefixes has overflowed.
+ */
int
-bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi,
+bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi,
safi_t safi, int always)
{
if (!CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
@@ -1650,17 +2156,18 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi,
"%%MAXPFXEXCEED: No. of %s prefix received from %s %ld exceed, "
"limit %ld", afi_safi_print (afi, safi), peer->host,
peer->pcount[afi][safi], peer->pmax[afi][safi]);
+
SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT);
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
- return 0;
+ return 0;
{
u_int8_t ndata[7];
if (safi == SAFI_MPLS_VPN)
safi = BGP_SAFI_VPNV4;
-
+
ndata[0] = (afi >> 8);
ndata[1] = afi;
ndata[2] = safi;
@@ -1670,22 +2177,23 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi,
ndata[6] = (peer->pmax[afi][safi]);
SET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
- bgp_notify_send_with_data (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7);
+ /* Disable the peer, the timer routine will reenable. */
+ bgp_peer_down_error_with_data(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7);
}
/* restart timer start */
if (peer->pmax_restart[afi][safi])
- {
- peer->v_pmax_restart = peer->pmax_restart[afi][safi] * 60;
+ {
+ peer->v_pmax_restart = peer->pmax_restart[afi][safi] * 60;
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s Maximum-prefix restart timer started for %d secs",
- peer->host, peer->v_pmax_restart);
+ if (BGP_DEBUG (events, EVENTS))
+ zlog_debug ("%s Maximum-prefix restart timer started for %d secs",
+ peer->host, peer->v_pmax_restart);
- BGP_TIMER_ON (peer->t_pmax_restart, bgp_maximum_prefix_restart_timer,
- peer->v_pmax_restart);
- }
+ BGP_TIMER_ON (peer->t_pmax_restart, bgp_maximum_prefix_restart_timer,
+ peer->v_pmax_restart);
+ }
return 1;
}
@@ -1709,7 +2217,10 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi,
return 0;
}
-/* Unconditionally remove the route from the RIB, without taking
+/*============================================================================*/
+
+/*------------------------------------------------------------------------------
+ * Unconditionally remove the route from the RIB, without taking
* damping into consideration (eg, because the session went down)
*/
static void
@@ -1717,10 +2228,10 @@ bgp_rib_remove (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
afi_t afi, safi_t safi)
{
bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi);
-
+
if (!CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
bgp_info_delete (rn, ri); /* keep historical info */
-
+
bgp_process (peer->bgp, rn, afi, safi);
}
@@ -1730,104 +2241,119 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
{
int status = BGP_DAMP_NONE;
- /* apply dampening, if result is suppressed, we'll be retaining
+ /* apply dampening, if result is suppressed, we'll be retaining
* the bgp_info in the RIB for historical reference.
*/
if (CHECK_FLAG (peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
&& peer_sort (peer) == BGP_PEER_EBGP)
- if ( (status = bgp_damp_withdraw (ri, rn, afi, safi, 0))
+ if ( (status = bgp_damp_withdraw (ri, rn, afi, safi, 0))
== BGP_DAMP_SUPPRESSED)
{
bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi);
return;
}
-
+
bgp_rib_remove (rn, ri, peer, afi, safi);
}
+/*------------------------------------------------------------------------------
+ * Update the given RS Client's RIB with the given route from the given peer.
+ *
+ * The peer's rs-in route-map once for all the rsclients who are to receive
+ * the route.
+ *
+ * Then export and import route-maps for the peer and the rsclient respectively.
+ */
static void
-bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
- struct attr *attr, struct peer *peer, struct prefix *p, int type,
- int sub_type, struct prefix_rd *prd, u_char *tag)
+bgp_update_rsclient (struct peer *rsclient, struct rs_route* rt)
{
- struct bgp_node *rn;
+ struct attr* client_attr ;
struct bgp *bgp;
- struct attr new_attr = { 0 };
- struct attr *attr_new;
- struct attr *attr_new2;
+ struct bgp_node *rn;
struct bgp_info *ri;
- struct bgp_info *new;
const char *reason;
char buf[SU_ADDRSTRLEN];
- /* Do not insert announces from a rsclient into its own 'bgp_table'. */
- if (peer == rsclient)
+ /* Do not insert announces from a rsclient into its own 'bgp_table'. */
+ if (rt->peer == rsclient)
return;
- bgp = peer->bgp;
- rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, prd);
+ /* Apply rs_in policy. */
+ if (!rt->rs_in_applied)
+ bgp_rs_input_modifier(rt) ;
- /* Check previously received route. */
- for (ri = rn->info; ri; ri = ri->next)
- if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
+ client_attr = NULL ; /* no attributes, yet */
+
+ /* Find node for this route */
+ bgp = rt->peer->bgp;
+ rn = bgp_afi_node_get (rsclient->rib[rt->afi][rt->safi], rt->afi, rt->safi,
+ rt->p, rt->prd);
+
+ /* Check previously received route. */
+ for (ri = rn->info; ri; ri = ri->info_next)
+ if (ri->peer == rt->peer && ri->type == rt->type
+ && ri->sub_type == rt->sub_type)
break;
- /* AS path loop check. */
- if (aspath_loop_check (attr->aspath, rsclient->as) > peer->allowas_in[afi][safi])
+ /* If rs-in denies the route, stop now */
+ if (rt->rs_in_deny)
+ {
+ reason = "rs-in-policy;";
+ goto filtered;
+ } ;
+
+ /* AS path loop check. */
+ if (aspath_loop_check (rt->rs_in_attr->aspath, rsclient->as) >
+ rt->peer->allowas_in[rt->afi][rt->safi])
{
reason = "as-path contains our own AS;";
goto filtered;
}
- /* Route reflector originator ID check. */
- if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)
- && IPV4_ADDR_SAME (&rsclient->remote_id, &attr->extra->originator_id))
+ /* Route reflector originator ID check. */
+ if (rt->rs_in_attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)
+ && IPV4_ADDR_SAME (&rsclient->remote_id,
+ &rt->rs_in_attr->extra->originator_id))
{
reason = "originator is us;";
goto filtered;
}
-
- bgp_attr_dup (&new_attr, attr);
- /* Apply export policy. */
- if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) &&
- bgp_export_modifier (rsclient, peer, p, &new_attr, afi, safi) == RMAP_DENY)
+ /* Need own internalised version of the rs_in attributes */
+ client_attr = bgp_attr_intern(rt->rs_in_attr) ;
+
+ /* Apply export policy. */
+ if (CHECK_FLAG(rt->peer->af_flags[rt->afi][rt->safi],
+ PEER_FLAG_RSERVER_CLIENT))
{
- reason = "export-policy;";
- goto filtered;
- }
+ client_attr = bgp_export_modifier (rsclient, rt, client_attr) ;
+ if (client_attr == NULL)
+ {
+ reason = "export-policy;";
+ goto filtered;
+ } ;
+ } ;
- attr_new2 = bgp_attr_intern (&new_attr);
-
/* Apply import policy. */
- if (bgp_import_modifier (rsclient, peer, p, &new_attr, afi, safi) == RMAP_DENY)
+ client_attr = bgp_import_modifier (rsclient, rt, client_attr) ;
+ if (client_attr == NULL)
{
- bgp_attr_unintern (&attr_new2);
-
reason = "import-policy;";
goto filtered;
}
- attr_new = bgp_attr_intern (&new_attr);
- bgp_attr_unintern (&attr_new2);
-
/* IPv4 unicast next hop check. */
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ if (rt->afi == AFI_IP && rt->safi == SAFI_UNICAST)
{
/* Next hop must not be 0.0.0.0 nor Class E address. */
- if (new_attr.nexthop.s_addr == 0
- || ntohl (new_attr.nexthop.s_addr) >= 0xe0000000)
+ if (client_attr->nexthop.s_addr == 0
+ || ntohl (client_attr->nexthop.s_addr) >= 0xe0000000)
{
- bgp_attr_unintern (&attr_new);
-
reason = "martian next-hop;";
goto filtered;
}
}
-
- /* new_attr isn't passed to any functions after here */
- bgp_attr_extra_free (&new_attr);
-
+
/* If the update is implicit withdraw. */
if (ri)
{
@@ -1835,109 +2361,106 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
/* Same attribute comes in. */
if (!CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)
- && attrhash_cmp (ri->attr, attr_new))
+ && attrhash_cmp (ri->attr, client_attr))
{
bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG,
+ zlog (rt->peer->log, LOG_DEBUG,
"%s rcvd %s/%d for RS-client %s...duplicate ignored",
- peer->host,
- inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
- p->prefixlen, rsclient->host);
+ rt->peer->host,
+ inet_ntop(rt->p->family, &rt->p->u.prefix,
+ buf, SU_ADDRSTRLEN),
+ rt->p->prefixlen, rsclient->host);
- bgp_unlock_node (rn);
- bgp_attr_unintern (&attr_new);
+ /* Discard the duplicate interned attributes */
+ bgp_attr_unintern (&client_attr);
- return;
+ /* Unlock node -- locked in bgp_afi_node_get() */
+ bgp_unlock_node (rn);
+ return; /* FIN <<<<<<<<<<<<<<<<<<< */
}
/* Withdraw/Announce before we fully processed the withdraw */
if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
bgp_info_restore (rn, ri);
-
- /* Received Logging. */
- if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s",
- peer->host,
- inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
- p->prefixlen, rsclient->host);
- /* The attribute is changed. */
+ /* The attribute is changed. */
bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
- /* Update to new attribute. */
+ /* Discard the old attribute */
bgp_attr_unintern (&ri->attr);
- ri->attr = attr_new;
-
+ }
+ else
+ {
+ /* Make new BGP info. */
+ ri = bgp_info_new ();
+ ri->type = rt->type;
+ ri->sub_type = rt->sub_type;
+ ri->peer = rt->peer;
+ ri->uptime = bgp_clock ();
+#if 0 /* TODO: do we need this ?? */
/* Update MPLS tag. */
if (safi == SAFI_MPLS_VPN)
memcpy ((bgp_info_extra_get (ri))->tag, tag, 3);
+#endif
- bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+ /* Register new BGP information. */
+ bgp_info_add (rn, ri);
+ } ;
- /* Process change. */
- bgp_process (bgp, rn, afi, safi);
- bgp_unlock_node (rn);
+ /* Set the new attributes and update any MPLS tag.
+ *
+ * Any old attributes have been discarded.
+ *
+ * Note that we are here passing responsibility for the client_attr to the
+ * ri entry.
+ */
+ ri->attr = client_attr ;
- return;
- }
+ if (rt->safi == SAFI_MPLS_VPN)
+ memcpy ((bgp_info_extra_get (ri))->tag, rt->tag, 3);
- /* Received Logging. */
+ /* Received Logging. */
if (BGP_DEBUG (update, UPDATE_IN))
- {
- zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s",
- peer->host,
- inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
- p->prefixlen, rsclient->host);
- }
+ zlog (rt->peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s",
+ rt->peer->host,
+ inet_ntop(rt->p->family, &rt->p->u.prefix, buf, SU_ADDRSTRLEN),
+ rt->p->prefixlen, rsclient->host);
- /* Make new BGP info. */
- new = bgp_info_new ();
- new->type = type;
- new->sub_type = sub_type;
- new->peer = peer;
- new->attr = attr_new;
- new->uptime = bgp_clock ();
+ /* Process change. */
+ bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+ bgp_process (bgp, rn, rt->afi, rt->safi);
- /* Update MPLS tag. */
- if (safi == SAFI_MPLS_VPN)
- memcpy ((bgp_info_extra_get (new))->tag, tag, 3);
-
- bgp_info_set_flag (rn, new, BGP_INFO_VALID);
-
- /* Register new BGP information. */
- bgp_info_add (rn, new);
-
- /* route_node_get lock */
+ /* Unlock node -- locked in bgp_afi_node_get() */
bgp_unlock_node (rn);
-
- /* Process change. */
- bgp_process (bgp, rn, afi, safi);
-
- bgp_attr_extra_free (&new_attr);
-
- return;
- filtered:
+ return; /* FIN <<<<<<<<<<<<<<<<<<< */
+
+ /* Deal with route which has been filtered out.
+ *
+ * If there was a previous route, then remove it.
+ *
+ * If have an interned client attributes, then discard those.
+ */
+ filtered:
/* This BGP update is filtered. Log the reason then update BGP entry. */
if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG,
+ zlog (rt->peer->log, LOG_DEBUG,
"%s rcvd UPDATE about %s/%d -- DENIED for RS-client %s due to: %s",
- peer->host,
- inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
- p->prefixlen, rsclient->host, reason);
+ rt->peer->host,
+ inet_ntop (rt->p->family, &rt->p->u.prefix, buf, SU_ADDRSTRLEN),
+ rt->p->prefixlen, rsclient->host, reason);
if (ri)
- bgp_rib_remove (rn, ri, peer, afi, safi);
+ bgp_rib_remove (rn, ri, rt->peer, rt->afi, rt->safi);
+
+ if (client_attr != NULL)
+ bgp_attr_unintern (&client_attr);
bgp_unlock_node (rn);
-
- if (new_attr.extra)
- bgp_attr_extra_free (&new_attr);
-
return;
}
@@ -1956,7 +2479,7 @@ bgp_withdraw_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, prd);
/* Lookup withdrawn route. */
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
break;
@@ -1978,20 +2501,23 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
afi_t afi, safi_t safi, int type, int sub_type,
struct prefix_rd *prd, u_char *tag, int soft_reconfig)
{
- int ret;
int aspath_loop_count = 0;
struct bgp_node *rn;
struct bgp *bgp;
- struct attr new_attr = { 0 };
- struct attr *attr_new;
+ struct attr* use_attr ;
struct bgp_info *ri;
struct bgp_info *new;
const char *reason;
char buf[SU_ADDRSTRLEN];
+ bgp_peer_sort_t sort ;
+
+ use_attr = NULL ; /* nothing to use, yet */
bgp = peer->bgp;
rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
-
+
+ sort = peer_sort(peer) ;
+
/* When peer's soft reconfiguration enabled. Record input packet in
Adj-RIBs-In. */
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)
@@ -1999,7 +2525,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
bgp_adj_in_set (rn, peer, attr);
/* Check previously received route. */
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
break;
@@ -2009,7 +2535,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
if (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
aspath_loop_count = 1;
- if (aspath_loop_check (attr->aspath, peer->change_local_as) > aspath_loop_count)
+ if (aspath_loop_check (attr->aspath, peer->change_local_as) > aspath_loop_count)
{
reason = "as-path contains our own AS;";
goto filtered;
@@ -2049,21 +2575,20 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
}
/* Apply incoming route-map. */
- bgp_attr_dup (&new_attr, attr);
-
- if (bgp_input_modifier (peer, p, &new_attr, afi, safi) == RMAP_DENY)
+ use_attr = bgp_input_modifier(peer, p, attr, afi, safi) ;
+ if (use_attr == NULL)
{
reason = "route-map;";
goto filtered;
}
/* IPv4 unicast next hop check. */
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ if ((afi == AFI_IP) && (safi == SAFI_UNICAST))
{
/* If the peer is EBGP and nexthop is not on connected route,
discard it. */
- if (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl == 1
- && ! bgp_nexthop_check_ebgp (afi, &new_attr)
+ if ((sort == BGP_PEER_EBGP) && (peer->ttl == 1)
+ && ! bgp_nexthop_check_ebgp (afi, use_attr)
&& ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
{
reason = "non-connected next-hop;";
@@ -2072,33 +2597,31 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
/* Next hop must not be 0.0.0.0 nor Class E address. Next hop
must not be my own address. */
- if (bgp_nexthop_self (afi, &new_attr)
- || new_attr.nexthop.s_addr == 0
- || ntohl (new_attr.nexthop.s_addr) >= 0xe0000000)
+ if (bgp_nexthop_self (afi, use_attr)
+ || (use_attr->nexthop.s_addr == 0)
+ || (ntohl (use_attr->nexthop.s_addr) >= 0xe0000000))
{
reason = "martian next-hop;";
goto filtered;
}
}
- attr_new = bgp_attr_intern (&new_attr);
-
/* If the update is implicit withdraw. */
if (ri)
{
ri->uptime = bgp_clock ();
/* Same attribute comes in. */
- if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)
- && attrhash_cmp (ri->attr, attr_new))
+ if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)
+ && attrhash_cmp (ri->attr, use_attr))
{
bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
- && peer_sort (peer) == BGP_PEER_EBGP
+ && (sort == BGP_PEER_EBGP)
&& CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
{
- if (BGP_DEBUG (update, UPDATE_IN))
+ if (BGP_DEBUG (update, UPDATE_IN))
zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d",
peer->host,
inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
@@ -2112,7 +2635,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
}
else /* Duplicate - odd */
{
- if (BGP_DEBUG (update, UPDATE_IN))
+ if (BGP_DEBUG (update, UPDATE_IN))
zlog (peer->log, LOG_DEBUG,
"%s rcvd %s/%d...duplicate ignored",
peer->host,
@@ -2127,10 +2650,9 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
}
}
- bgp_unlock_node (rn);
- bgp_attr_unintern (&attr_new);
- bgp_attr_extra_free (&new_attr);
-
+ bgp_attr_unintern (&use_attr);
+
+ bgp_unlock_node (rn);
return 0;
}
@@ -2138,7 +2660,8 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
{
if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d, flapped quicker than processing",
+ zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d, "
+ "flapped quicker than processing",
peer->host,
inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
p->prefixlen);
@@ -2146,7 +2669,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
}
/* Received Logging. */
- if (BGP_DEBUG (update, UPDATE_IN))
+ if (BGP_DEBUG (update, UPDATE_IN))
zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d",
peer->host,
inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
@@ -2158,25 +2681,25 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
/* The attribute is changed. */
bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
-
+
/* implicit withdraw, decrement aggregate and pcount here.
* only if update is accepted, they'll increment below.
*/
bgp_aggregate_decrement (bgp, p, ri, afi, safi);
-
+
/* Update bgp route dampening information. */
if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
- && peer_sort (peer) == BGP_PEER_EBGP)
+ && (sort == BGP_PEER_EBGP))
{
/* This is implicit withdraw so we should update dampening
information. */
if (! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
- bgp_damp_withdraw (ri, rn, afi, safi, 1);
+ bgp_damp_withdraw (ri, rn, afi, safi, 1);
}
-
+
/* Update to new attribute. */
bgp_attr_unintern (&ri->attr);
- ri->attr = attr_new;
+ ri->attr = use_attr ;
/* Update MPLS tag. */
if (safi == SAFI_MPLS_VPN)
@@ -2184,24 +2707,24 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
/* Update bgp route dampening information. */
if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
- && peer_sort (peer) == BGP_PEER_EBGP)
+ && (sort == BGP_PEER_EBGP))
{
+ int ret ;
/* Now we do normal update dampening. */
ret = bgp_damp_update (ri, rn, afi, safi);
if (ret == BGP_DAMP_SUPPRESSED)
{
bgp_unlock_node (rn);
- bgp_attr_extra_free (&new_attr);
return 0;
}
}
/* Nexthop reachability check. */
if ((afi == AFI_IP || afi == AFI_IP6)
- && safi == SAFI_UNICAST
- && (peer_sort (peer) == BGP_PEER_IBGP
- || peer_sort (peer) == BGP_PEER_CONFED
- || (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1)
+ && safi == SAFI_UNICAST
+ && ( (sort == BGP_PEER_IBGP)
+ || (sort == BGP_PEER_CONFED)
+ || ((sort == BGP_PEER_EBGP) && (peer->ttl != 1))
|| CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)))
{
if (bgp_nexthop_lookup (afi, peer, ri, NULL, NULL))
@@ -2216,14 +2739,13 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
bgp_aggregate_increment (bgp, p, ri, afi, safi);
bgp_process (bgp, rn, afi, safi);
+
bgp_unlock_node (rn);
- bgp_attr_extra_free (&new_attr);
-
return 0;
}
/* Received Logging. */
- if (BGP_DEBUG (update, UPDATE_IN))
+ if (BGP_DEBUG (update, UPDATE_IN))
{
zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d",
peer->host,
@@ -2236,7 +2758,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
new->type = type;
new->sub_type = sub_type;
new->peer = peer;
- new->attr = attr_new;
+ new->attr = use_attr;
new->uptime = bgp_clock ();
/* Update MPLS tag. */
@@ -2246,9 +2768,9 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
/* Nexthop reachability check. */
if ((afi == AFI_IP || afi == AFI_IP6)
&& safi == SAFI_UNICAST
- && (peer_sort (peer) == BGP_PEER_IBGP
- || peer_sort (peer) == BGP_PEER_CONFED
- || (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1)
+ && ( (sort == BGP_PEER_IBGP)
+ || (sort == BGP_PEER_CONFED)
+ || ((sort == BGP_PEER_EBGP) && (peer->ttl != 1))
|| CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)))
{
if (bgp_nexthop_lookup (afi, peer, new, NULL, NULL))
@@ -2261,23 +2783,24 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
/* Increment prefix */
bgp_aggregate_increment (bgp, p, new, afi, safi);
-
+
/* Register new BGP information. */
bgp_info_add (rn, new);
-
- /* route_node_get lock */
- bgp_unlock_node (rn);
-
- bgp_attr_extra_free (&new_attr);
-
+
/* If maximum prefix count is configured and current prefix
- count exeed it. */
+ count exceeds it. */
if (bgp_maximum_prefix_overflow (peer, afi, safi, 0))
- return -1;
+ {
+ bgp_unlock_node (rn);
+ return -1;
+ } ;
/* Process change. */
bgp_process (bgp, rn, afi, safi);
+ /* route_node_get lock */
+ bgp_unlock_node (rn);
+
return 0;
/* This BGP update is filtered. Log the reason then update BGP
@@ -2290,13 +2813,13 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
p->prefixlen, reason);
- if (ri)
+ if (ri != NULL)
bgp_rib_remove (rn, ri, peer, afi, safi);
+ if (use_attr != NULL)
+ bgp_attr_unintern (&use_attr);
+
bgp_unlock_node (rn);
-
- bgp_attr_extra_free (&new_attr);
-
return 0;
}
@@ -2310,25 +2833,41 @@ bgp_update (struct peer *peer, struct prefix *p, struct attr *attr,
struct bgp *bgp;
int ret;
+ /* For all neighbors, update the main RIB */
ret = bgp_update_main (peer, p, attr, afi, safi, type, sub_type, prd, tag,
soft_reconfig);
+ /* Update all Route-Server Client RIBs */
bgp = peer->bgp;
- /* Process the update for each RS-client. */
- for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient))
+ if (bgp->rsclient != NULL)
{
- if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
- bgp_update_rsclient (rsclient, afi, safi, attr, peer, p, type,
- sub_type, prd, tag);
- }
+ struct rs_route rt_s ;
+ /* Prepare the rs_route object, ready to update all rs clients active
+ * in this afi/safi.
+ */
+ bgp_rs_route_init(&rt_s, afi, safi, attr, peer, p, type, sub_type,
+ prd, tag) ;
+
+ /* Process the update for each RS-client. */
+ for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient))
+ if (CHECK_FLAG(rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+ bgp_update_rsclient (rsclient, &rt_s) ;
+
+ /* Reset the rs_route object -- in particular discard any interned
+ * rs_in_attr which may have been created.
+ */
+ bgp_rs_route_reset(&rt_s) ;
+ } ;
+
+ /* Return result from bgp_update_main */
return ret;
}
int
-bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr,
- afi_t afi, safi_t safi, int type, int sub_type,
+bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr,
+ afi_t afi, safi_t safi, int type, int sub_type,
struct prefix_rd *prd, u_char *tag)
{
struct bgp *bgp;
@@ -2348,7 +2887,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr,
}
/* Logging. */
- if (BGP_DEBUG (update, UPDATE_IN))
+ if (BGP_DEBUG (update, UPDATE_IN))
zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE about %s/%d -- withdrawn",
peer->host,
inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
@@ -2364,7 +2903,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr,
bgp_adj_in_unset (rn, peer);
/* Lookup withdrawn route. */
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
break;
@@ -2372,7 +2911,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr,
if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
bgp_rib_withdraw (rn, ri, peer, afi, safi);
else if (BGP_DEBUG (update, UPDATE_IN))
- zlog (peer->log, LOG_DEBUG,
+ zlog (peer->log, LOG_DEBUG,
"%s Can't find the route %s/%d", peer->host,
inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
p->prefixlen);
@@ -2382,7 +2921,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr,
return 0;
}
-
+
void
bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
{
@@ -2390,16 +2929,14 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
struct attr attr = { 0 };
struct aspath *aspath = { 0 };
struct prefix p;
- struct bgp_info binfo;
struct peer *from;
- int ret = RMAP_DENYMATCH;
-
+
if (!(afi == AFI_IP || afi == AFI_IP6))
return;
-
+
bgp = peer->bgp;
from = bgp->peer_self;
-
+
bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
aspath = attr.aspath;
attr.local_pref = bgp->default_local_pref;
@@ -2412,23 +2949,23 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
{
struct attr_extra *ae;
attr.extra = NULL;
-
+
ae = bgp_attr_extra_get (&attr);
attr.extra = ae;
-
+
str2prefix ("::/0", &p);
/* IPv6 global nexthop must be included. */
- memcpy (&ae->mp_nexthop_global, &peer->nexthop.v6_global,
+ memcpy (&ae->mp_nexthop_global, &peer->nexthop.v6_global,
IPV6_MAX_BYTELEN);
ae->mp_nexthop_len = 16;
-
+
/* If the peer is on shared nextwork and we have link-local
nexthop set it. */
- if (peer->shared_network
+ if (peer->shared_network
&& !IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local))
{
- memcpy (&ae->mp_nexthop_local, &peer->nexthop.v6_local,
+ memcpy (&ae->mp_nexthop_local, &peer->nexthop.v6_local,
IPV6_MAX_BYTELEN);
ae->mp_nexthop_len = 32;
}
@@ -2437,13 +2974,16 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
if (peer->default_rmap[afi][safi].name)
{
- binfo.peer = bgp->peer_self;
- binfo.attr = &attr;
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret;
+
+ info_s.peer = bgp->peer_self ;
+ info_s.attr = &attr;
SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT);
ret = route_map_apply (peer->default_rmap[afi][safi].map, &p,
- RMAP_BGP, &binfo);
+ RMAP_BGP, &info_s);
bgp->peer_self->rmap_type = 0;
@@ -2465,11 +3005,11 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE);
bgp_default_update_send (peer, &attr, afi, safi, from);
}
-
+
bgp_attr_extra_free (&attr);
aspath_unintern (&aspath);
}
-
+
static void
bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
struct bgp_table *table, int rsclient)
@@ -2477,7 +3017,7 @@ bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
struct bgp_node *rn;
struct bgp_info *ri;
struct attr attr = { 0 };
-
+
if (! table)
table = (rsclient) ? peer->rib[afi][safi] : peer->bgp->rib[afi][safi];
@@ -2486,7 +3026,7 @@ bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
bgp_default_originate (peer, afi, safi, 0);
for (rn = bgp_table_top (table); rn; rn = bgp_route_next(rn))
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) && ri->peer != peer)
{
if ( (rsclient) ?
@@ -2495,7 +3035,7 @@ bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
bgp_adj_out_set (rn, peer, &rn->p, &attr, afi, safi, ri);
else
bgp_adj_out_unset (rn, peer, &rn->p, afi, safi);
-
+
bgp_attr_extra_free (&attr);
}
}
@@ -2506,7 +3046,7 @@ bgp_announce_route (struct peer *peer, afi_t afi, safi_t safi)
struct bgp_node *rn;
struct bgp_table *table;
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
return;
if (! peer->afc_nego[afi][safi])
@@ -2533,28 +3073,46 @@ bgp_announce_route_all (struct peer *peer)
{
afi_t afi;
safi_t safi;
-
+
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
bgp_announce_route (peer, afi, safi);
}
-
+
static void
bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi,
safi_t safi, struct bgp_table *table)
{
struct bgp_node *rn;
struct bgp_adj_in *ain;
+ struct rs_route rt_s ;
if (! table)
table = rsclient->bgp->rib[afi][safi];
+ /* Prepare the rs_route object, setting all the parts common to all routes
+ * which are about to announce to the rs client.
+ */
+ bgp_rs_route_init(&rt_s, afi, safi, NULL, NULL, NULL,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL) ;
+
+ /* Announce everything in the table. */
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- for (ain = rn->adj_in; ain; ain = ain->next)
+ for (ain = rn->adj_in; ain; ain = ain->adj_next)
{
- bgp_update_rsclient (rsclient, afi, safi, ain->attr, ain->peer,
- &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL);
- }
+ rt_s.orig_attr = ain->attr ;
+ rt_s.peer = ain->peer ;
+ rt_s.p = &rn->p ;
+
+ bgp_update_rsclient (rsclient, &rt_s) ;
+
+ /* Reset the rs_route object -- which discards any interned rs_in_attr
+ * which may have been created and clears the rs_in_applied flag.
+ *
+ * Leaves everything else !
+ */
+ bgp_rs_route_reset(&rt_s) ;
+ } ;
}
void
@@ -2562,7 +3120,7 @@ bgp_soft_reconfig_rsclient (struct peer *rsclient, afi_t afi, safi_t safi)
{
struct bgp_table *table;
struct bgp_node *rn;
-
+
if (safi != SAFI_MPLS_VPN)
bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, NULL);
@@ -2572,7 +3130,7 @@ bgp_soft_reconfig_rsclient (struct peer *rsclient, afi_t afi, safi_t safi)
if ((table = rn->info) != NULL)
bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, table);
}
-
+
static void
bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi,
struct bgp_table *table)
@@ -2585,7 +3143,7 @@ bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi,
table = peer->bgp->rib[afi][safi];
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- for (ain = rn->adj_in; ain; ain = ain->next)
+ for (ain = rn->adj_in; ain; ain = ain->adj_next)
{
if (ain->peer == peer)
{
@@ -2608,7 +3166,7 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi)
struct bgp_node *rn;
struct bgp_table *table;
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
return;
if (safi != SAFI_MPLS_VPN)
@@ -2619,7 +3177,332 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi)
if ((table = rn->info) != NULL)
bgp_soft_reconfig_table (peer, afi, safi, table);
}
-
+
+/*==============================================================================
+ * Clearing.
+ *
+ * There are two (quite different) forms of clearing:
+ *
+ * 1. Normal clearing -- mass withdraw of given peer's routes for all
+ * or individual AFI/SAFI.
+ *
+ * This is clears the routes *from* the given peer.
+ *
+ * Note that normal clearing deals with the main RIB and any RS Client
+ * RIBs that may also contain routes.
+ *
+ * 2. RS Client clearing -- dismantling of RS Client RIB for an AFI/SAFI.
+ *
+ * This clears out the routes *for* the given RS Client.
+ *
+ *------------------------------------------------------------------------------
+ * Normal clearing
+ *
+ * This is used in two ways:
+ *
+ * 1. when a peer falls out of Established state.
+ *
+ * See: bgp_clear_route_all().
+ *
+ * All the peer's routes in all AFI/SAFI are withdrawn, but may be subject
+ * to NSF.
+ *
+ * 2. when an individual AFI/SAFI is disabled.
+ *
+ * See: bgp_clear_route().
+ *
+ * [This appears to be for Dynamic Capabilities only.]
+ * TODO: discover whether NSF affects Dynamic Capability route clear.
+ *
+ * All the peer's routes in the AFI/SAFI are withdrawn. (NSF ??).
+ *
+ * Normal clearing affects:
+ *
+ * 1. the main RIB in all relevant AFI/SAFI.
+ *
+ * 2. all RS Client RIBs in all relevant AFI/SAFI
+ *
+ * Any routes (ie bgp_info objects) in the affected tables are either marked
+ * stale or are removed all together.
+ *
+ * Any adj_in (soft reconfig) and adj_out (announcement state) objects are
+ * removed.
+ *
+ * The peer's:
+ *
+ * struct bgp_info* routes_head[AFI_MAX][SAFI_MAX] ;
+ *
+ * This list threads through every use of all routes which belong to
+ * the peer, in all RIBs.
+ *
+ * struct bgp_adj_in* adj_in_head[AFI_MAX][SAFI_MAX] ;
+ *
+ * This list threads through every copy of all routes which belong to the
+ * peer and which have been preserved for soft reconfiguration, in all RIBs.
+ *
+ * struct bgp_adj_out* adj_out_head[AFI_MAX][SAFI_MAX] ;
+ *
+ * This list threads through every route which has been selected for the
+ * peer, in all RIBs.
+ *
+ * Are maintained for exactly this purpose.
+ *
+ * NB: this is now a linear process, because the lists identify the stuff to
+ * be processed.
+ *
+ * Not much work is required to remove a route -- the consequences are
+ * dealt with by the relevant processing work queue.
+ *
+ * In theory it would be better to break up the work. A peer who announces
+ * 500,000 prefixes has a fair amount to do here. A peer who announces
+ * 10,000 prefixes to 1,000 RS Clients has 10,000,000 routes to withdraw.
+ *
+ * Nevertheless, a really hard case looks like less than 10secs work...
+ * For the time being, the simplicity of living without a clearing work
+ * queue task is preferred -- and the
+ *
+ * [The old code walked the main RIB, and then every RS Client RIB, searching
+ * for bgp_node objects which had bgp_info from the given peer. It then issued
+ * a work queue task to do the actual change (which was probably more work than
+ * doing the change straight away).]
+ *
+ * [The MPLS VPN stuff has a two level RIB, which the above probably doesn't
+ * work for... more work required, here.]
+ *
+ * TODO: fix bgp_clear_route() and MPLS VPN !!
+ *
+ *------------------------------------------------------------------------------
+ * RS Client Clearing
+ *
+ * This is done when a given RS Client RIB is about to be dismantled.
+ *
+ * This walks the RS Client RIB and discards all bgp_info, adj_in and adj_out.
+ * (This is unconditional -- no NSF gets in the way.)
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Normal clearing of a a given peer's routes.
+ *
+ * The following lists are processed:
+ *
+ * * struct bgp_info* routes_head
+ *
+ * Walks this and clears each route.
+ *
+ * * struct bgp_adj_in* adj_in_head
+ * * struct bgp_adj_out* adj_out_head
+ *
+ * These two are simply emptied out.
+ *
+ * NB: in the latest scheme of things this is completed immediately...
+ *
+ * ...however, retain the ability for this to kick off background or other
+ * activity.
+ *
+ * Returns: true <=> clearing has completed
+ *
+ */
+extern bool
+bgp_clear_routes(struct peer *peer, afi_t afi, safi_t safi, bool nsf)
+{
+ struct bgp_info* ri ;
+ struct bgp_info* next_ri ;
+ struct bgp_adj_in* adj_in ;
+ struct bgp_adj_out* adj_out ;
+ struct bgp_adj_in** adj_in_head ;
+ struct bgp_adj_out** adj_out_head ;
+
+ next_ri = peer->routes_head[afi][safi] ;
+
+ /* If NSF requested and nsf configured for this afi/safi, do nsf and
+ * set flag to indicate that at least one afi/safi may have stale routes.
+ */
+ nsf = nsf && peer->nsf[afi][safi] ;
+ if (nsf)
+ SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) ;
+
+ /* TODO: fix bgp_clear_route_normal() so can clear an MPLS VPN table.... */
+ if (next_ri != NULL)
+ assert(safi != SAFI_MPLS_VPN) ;
+
+ while (next_ri != NULL)
+ {
+ /* The current bgp_info object may vanish, so bank the next */
+ ri = next_ri ;
+ next_ri = ri->routes_next ;
+
+ assert (peer == ri->peer) ;
+
+ if (nsf && ! CHECK_FLAG (ri->flags, BGP_INFO_STALE)
+ && ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
+ bgp_info_set_flag (ri->rn, ri, BGP_INFO_STALE);
+ else
+ bgp_rib_remove (ri->rn, ri, peer, afi, safi);
+ } ;
+
+ /* Empty out all adjacencies */
+ adj_in_head = &(peer->adj_in_head[afi][safi]) ;
+ while ((adj_in = *adj_in_head) != NULL)
+ {
+ assert(adj_in->route_prev == NULL) ;
+ bgp_adj_in_remove (adj_in->rn, adj_in) ;
+ assert(adj_in != *adj_in_head) ;
+ } ;
+
+ adj_out_head = &(peer->adj_out_head[afi][safi]) ;
+ while ((adj_out = *adj_out_head) != NULL)
+ {
+ assert(adj_out->route_prev == NULL) ;
+ bgp_adj_out_remove (adj_out->rn, adj_out, peer, afi, safi) ;
+ assert(adj_out != *adj_out_head) ;
+ } ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Normal clearing of given peer for all AFI/SAFI -- respecting NSF if required.
+ *
+ * NB: in the latest scheme of things this is completed immediately...
+ *
+ * ...however, retain the ability to run this in the background with the
+ * peer in bgp_peer_pClearing.
+ *
+ * Returns: true <=> all clearing completed
+ * so false => something running in the background.
+ */
+extern bool
+bgp_clear_all_routes (struct peer *peer, bool nsf)
+{
+ bool completed ;
+ afi_t afi;
+ safi_t safi;
+
+ assert(peer->state == bgp_peer_pClearing) ;
+
+ UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) ;
+
+ completed = true ;
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ if (!bgp_clear_routes(peer, afi, safi, nsf))
+ completed = false ;
+
+ return completed ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear Route Server RIB for given AFI/SAFI -- unconditionally
+ *
+ * This is used to dismantle a Route Server Client's RIB -- this is removing
+ * all the routes from all *other* Route Server Clients that have been placed
+ * in this Clients RIB.
+ *
+ * Walks all the nodes in the table and discards all routes, all adj_in and
+ * all adj_out.
+ *
+ * Does nothing if there is no RIB for that AFI/SAFI.
+ */
+extern void
+bgp_clear_rsclient_rib(struct peer* rsclient, afi_t afi, safi_t safi)
+{
+ struct bgp_node *rn ;
+ struct bgp_table* table ;
+
+ table = rsclient->rib[afi][safi] ;
+
+ if (table == NULL)
+ return ; /* Ignore unconfigured afi/safi or similar */
+
+ /* TODO: fix bgp_clear_rsclient_rib() so that will clear an MPLS VPN table. */
+ passert(table->safi != SAFI_MPLS_VPN) ;
+
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ {
+ struct bgp_info *ri;
+ struct bgp_info *next_ri ;
+ struct bgp_adj_in *ain;
+ struct bgp_adj_out *aout;
+
+ next_ri = rn->info ;
+ while(next_ri != NULL)
+ {
+ ri = next_ri ;
+ next_ri = ri->info_next ; /* bank this */
+
+ bgp_rib_remove (rn, ri, rsclient, table->afi, table->safi);
+ } ;
+
+ while ((ain = rn->adj_in) != NULL)
+ {
+ assert(ain->adj_prev == NULL) ;
+ bgp_adj_in_remove (rn, ain);
+ assert(ain != rn->adj_in) ;
+ } ;
+
+ while ((aout = rn->adj_out) != NULL)
+ {
+ assert(aout->adj_prev == NULL) ;
+ bgp_adj_out_remove (rn, aout, aout->peer, table->afi, table->safi) ;
+ assert(aout != rn->adj_out) ;
+ } ;
+ }
+ return ;
+}
+
+/*------------------------------------------------------------------------------
+ * Walk main RIB and remove any adj_in for given peer.
+ *
+ * TODO: walk peer->bgp_adj_in_head[afi][safi] -- but check which table ?
+ */
+void
+bgp_clear_adj_in (struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_table *table;
+ struct bgp_node *rn;
+ struct bgp_adj_in *ain;
+
+ table = peer->bgp->rib[afi][safi];
+
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ for (ain = rn->adj_in; ain ; ain = ain->adj_next)
+ if (ain->peer == peer)
+ {
+ bgp_adj_in_remove (rn, ain);
+ break;
+ }
+} ;
+
+/*------------------------------------------------------------------------------
+ * Walk main RIB and remove all stale routes for the given peer.
+ *
+ * NB: is required to complete immediately !
+ *
+ * TODO: walk peer->routes_head[afi][safi]
+ */
+void
+bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct bgp_table *table;
+
+ table = peer->bgp->rib[afi][safi];
+
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ {
+ for (ri = rn->info; ri; ri = ri->info_next)
+ if (ri->peer == peer)
+ {
+ if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
+ bgp_rib_remove (rn, ri, peer, afi, safi);
+ break;
+ }
+ }
+}
+
+#if 0
struct bgp_clear_node_queue
{
@@ -2627,19 +3510,21 @@ struct bgp_clear_node_queue
enum bgp_clear_route_type purpose;
};
+WQ_ARGS_SIZE_OK(bgp_clear_node_queue) ;
+
static wq_item_status
-bgp_clear_route_node (struct work_queue *wq, void *data)
+bgp_clear_route_node (struct work_queue *wq, work_queue_item item)
{
- struct bgp_clear_node_queue *cnq = data;
+ struct bgp_clear_node_queue *cnq = work_queue_item_args(item) ;
struct bgp_node *rn = cnq->rn;
struct peer *peer = wq->spec.data;
struct bgp_info *ri;
afi_t afi = rn->table->afi;
safi_t safi = rn->table->safi;
-
+
assert (rn && peer);
-
- for (ri = rn->info; ri; ri = ri->next)
+
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == peer || cnq->purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
{
/* graceful restart STALE flag set. */
@@ -2656,33 +3541,39 @@ bgp_clear_route_node (struct work_queue *wq, void *data)
}
static void
-bgp_clear_node_queue_del (struct work_queue *wq, void *data)
+bgp_clear_node_queue_del (struct work_queue *wq, work_queue_item item)
{
- struct bgp_clear_node_queue *cnq = data;
+ struct bgp_clear_node_queue *cnq = work_queue_item_args(item) ;
struct bgp_node *rn = cnq->rn;
struct bgp_table *table = rn->table;
-
- bgp_unlock_node (rn);
+
+ bgp_unlock_node (rn);
bgp_table_unlock (table);
- XFREE (MTYPE_BGP_CLEAR_NODE_QUEUE, cnq);
}
static void
bgp_clear_node_complete (struct work_queue *wq)
{
struct peer *peer = wq->spec.data;
-
- /* Tickle FSM to start moving again */
- BGP_EVENT_ADD (peer, Clearing_Completed);
- peer_unlock (peer); /* bgp_clear_route */
+ /* Flush the event queue and ensure the peer is shut down */
+ bgp_peer_stop(peer);
+ BGP_EVENT_FLUSH (peer);
+ if (peer->state == bgp_peer_pClearing)
+ {
+ peer_change_status (peer, bgp_peer_pIdle);
+ /* enable peer if required */
+ bgp_peer_enable(peer);
+ }
+
+ bgp_peer_unlock (peer); /* bgp_clear_route */
}
static void
bgp_clear_node_queue_init (struct peer *peer)
{
char wname[sizeof("clear xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")];
-
+
snprintf (wname, sizeof(wname), "clear %s", peer->host);
#undef CLEAR_QUEUE_NAME_LEN
@@ -2696,112 +3587,22 @@ bgp_clear_node_queue_init (struct peer *peer)
peer->clear_node_queue->spec.del_item_data = &bgp_clear_node_queue_del;
peer->clear_node_queue->spec.completion_func = &bgp_clear_node_complete;
peer->clear_node_queue->spec.max_retries = 0;
-
+
/* we only 'lock' this peer reference when the queue is actually active */
peer->clear_node_queue->spec.data = peer;
}
-static void
-bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi,
- struct bgp_table *table, struct peer *rsclient,
- enum bgp_clear_route_type purpose)
-{
- struct bgp_node *rn;
-
-
- if (! table)
- table = (rsclient) ? rsclient->rib[afi][safi] : peer->bgp->rib[afi][safi];
-
- /* If still no table => afi/safi isn't configured at all or smth. */
- if (! table)
- return;
-
- for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- {
- struct bgp_info *ri;
- struct bgp_adj_in *ain;
- struct bgp_adj_out *aout;
-
- if (rn->info == NULL)
- continue;
-
- /* XXX:TODO: This is suboptimal, every non-empty route_node is
- * queued for every clearing peer, regardless of whether it is
- * relevant to the peer at hand.
- *
- * Overview: There are 3 different indices which need to be
- * scrubbed, potentially, when a peer is removed:
- *
- * 1 peer's routes visible via the RIB (ie accepted routes)
- * 2 peer's routes visible by the (optional) peer's adj-in index
- * 3 other routes visible by the peer's adj-out index
- *
- * 3 there is no hurry in scrubbing, once the struct peer is
- * removed from bgp->peer, we could just GC such deleted peer's
- * adj-outs at our leisure.
- *
- * 1 and 2 must be 'scrubbed' in some way, at least made
- * invisible via RIB index before peer session is allowed to be
- * brought back up. So one needs to know when such a 'search' is
- * complete.
- *
- * Ideally:
- *
- * - there'd be a single global queue or a single RIB walker
- * - rather than tracking which route_nodes still need to be
- * examined on a peer basis, we'd track which peers still
- * aren't cleared
- *
- * Given that our per-peer prefix-counts now should be reliable,
- * this may actually be achievable. It doesn't seem to be a huge
- * problem at this time,
- */
- for (ri = rn->info; ri; ri = ri->next)
- if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
- {
- struct bgp_clear_node_queue *cnq;
-
- /* both unlocked in bgp_clear_node_queue_del */
- bgp_table_lock (rn->table);
- bgp_lock_node (rn);
- cnq = XCALLOC (MTYPE_BGP_CLEAR_NODE_QUEUE,
- sizeof (struct bgp_clear_node_queue));
- cnq->rn = rn;
- cnq->purpose = purpose;
- work_queue_add (peer->clear_node_queue, cnq);
- break;
- }
-
- for (ain = rn->adj_in; ain; ain = ain->next)
- if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
- {
- bgp_adj_in_remove (rn, ain);
- bgp_unlock_node (rn);
- break;
- }
- for (aout = rn->adj_out; aout; aout = aout->next)
- if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
- {
- bgp_adj_out_remove (rn, aout, peer, afi, safi);
- bgp_unlock_node (rn);
- break;
- }
- }
- return;
-}
-
void
-bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi,
- enum bgp_clear_route_type purpose)
+bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi)
{
- struct bgp_node *rn;
- struct bgp_table *table;
- struct peer *rsclient;
- struct listnode *node, *nnode;
+//struct bgp_node *rn;
+//struct bgp_table *table;
+//struct peer *rsclient;
+//struct listnode *node, *nnode;
+
+//if (peer->clear_node_queue == NULL)
+// bgp_clear_node_queue_init (peer);
- if (peer->clear_node_queue == NULL)
- bgp_clear_node_queue_init (peer);
-
/* bgp_fsm.c keeps sessions in state Clearing, not transitioning to
* Idle until it receives a Clearing_Completed event. This protects
* against peers which flap faster than we can we clear, which could
@@ -2814,37 +3615,45 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi,
* on the process_main queue. Fast-flapping could cause that queue
* to grow and grow.
*/
- if (!peer->clear_node_queue->thread)
- peer_lock (peer); /* bgp_clear_node_complete */
+//if (!peer->clear_node_queue->thread)
+ bgp_peer_lock (peer); /* bgp_clear_node_complete */
switch (purpose)
{
case BGP_CLEAR_ROUTE_NORMAL:
+ if (peer->routes_head[afi][safi] == NULL)
+ break ;
+
if (safi != SAFI_MPLS_VPN)
- bgp_clear_route_table (peer, afi, safi, NULL, NULL, purpose);
+ bgp_clear_route_normal(peer, afi, safi) ;
else
+/* TODO: how to deal with SAFI_MPLS_VPN in bgp_clear_route ?? */
+ passert(0) ;
+#if 0
for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
rn = bgp_route_next (rn))
if ((table = rn->info) != NULL)
bgp_clear_route_table (peer, afi, safi, table, NULL, purpose);
-
+#endif
+#if 0
for (ALL_LIST_ELEMENTS (peer->bgp->rsclient, node, nnode, rsclient))
if (CHECK_FLAG(rsclient->af_flags[afi][safi],
PEER_FLAG_RSERVER_CLIENT))
bgp_clear_route_table (peer, afi, safi, NULL, rsclient, purpose);
+#endif
break;
case BGP_CLEAR_ROUTE_MY_RSCLIENT:
- bgp_clear_route_table (peer, afi, safi, NULL, peer, purpose);
+ bgp_clear_route_table (peer, peer->rib[afi][safi]) ;
break;
default:
assert (0);
break;
}
-
+
/* If no routes were cleared, nothing was added to workqueue, the
- * completion function won't be run by workqueue code - call it here.
+ * completion function won't be run by workqueue code - call it here.
* XXX: Actually, this assumption doesn't hold, see
* bgp_clear_route_table(), we queue all non-empty nodes.
*
@@ -2862,62 +3671,17 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi,
* pre-Established, avoiding above list and table scans. Once we're
* sure it is safe..
*/
- if (!peer->clear_node_queue->thread)
- bgp_clear_node_complete (peer->clear_node_queue);
-}
-
-void
-bgp_clear_route_all (struct peer *peer)
-{
- afi_t afi;
- safi_t safi;
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL);
-}
+ /* The following was in bgp_clear_node_complete */
-void
-bgp_clear_adj_in (struct peer *peer, afi_t afi, safi_t safi)
-{
- struct bgp_table *table;
- struct bgp_node *rn;
- struct bgp_adj_in *ain;
-
- table = peer->bgp->rib[afi][safi];
-
- for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- for (ain = rn->adj_in; ain ; ain = ain->next)
- if (ain->peer == peer)
- {
- bgp_adj_in_remove (rn, ain);
- bgp_unlock_node (rn);
- break;
- }
+ bgp_peer_unlock (peer); /* bgp_clear_route */
}
+#endif
-void
-bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi)
-{
- struct bgp_node *rn;
- struct bgp_info *ri;
- struct bgp_table *table;
-
- table = peer->bgp->rib[afi][safi];
+/*============================================================================*/
- for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- {
- for (ri = rn->info; ri; ri = ri->next)
- if (ri->peer == peer)
- {
- if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
- bgp_rib_remove (rn, ri, peer, afi, safi);
- break;
- }
- }
-}
-
-/* Delete all kernel routes. */
+#if 0
+/* Delete all kernel routes. */
void
bgp_cleanup_routes (void)
{
@@ -2932,32 +3696,32 @@ bgp_cleanup_routes (void)
table = bgp->rib[AFI_IP][SAFI_UNICAST];
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)
- && ri->type == ZEBRA_ROUTE_BGP
+ && ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_NORMAL)
bgp_zebra_withdraw (&rn->p, ri);
table = bgp->rib[AFI_IP6][SAFI_UNICAST];
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)
- && ri->type == ZEBRA_ROUTE_BGP
+ && ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_NORMAL)
bgp_zebra_withdraw (&rn->p, ri);
}
}
+#endif
void
bgp_reset (void)
{
- vty_reset ();
bgp_zclient_reset ();
access_list_reset ();
prefix_list_reset ();
}
-
+
/* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr
value. */
int
@@ -2970,9 +3734,9 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet)
int ret;
/* Check peer status. */
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
return 0;
-
+
pnt = packet->nlri;
lim = pnt + packet->length;
@@ -2984,7 +3748,7 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet)
/* Fetch prefix length. */
p.prefixlen = *pnt++;
p.family = afi2family (packet->afi);
-
+
/* Already checked in nlri_sanity_check(). We do double check
here. */
if ((packet->afi == AFI_IP && p.prefixlen > 32)
@@ -3006,16 +3770,16 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet)
{
if (IN_CLASSD (ntohl (p.u.prefix4.s_addr)))
{
- /*
- * From draft-ietf-idr-bgp4-22, Section 6.3:
+ /*
+ * From draft-ietf-idr-bgp4-22, Section 6.3:
* If a BGP router receives an UPDATE message with a
* semantically incorrect NLRI field, in which a prefix is
* semantically incorrect (eg. an unexpected multicast IP
* address), it should ignore the prefix.
*/
- zlog (peer->log, LOG_ERR,
+ zlog (peer->log, LOG_ERR,
"IPv4 unicast NLRI is multicast address %s",
- inet_ntoa (p.u.prefix4));
+ safe_inet_ntoa (p.u.prefix4));
return -1;
}
@@ -3029,7 +3793,7 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet)
{
char buf[BUFSIZ];
- zlog (peer->log, LOG_WARNING,
+ zlog (peer->log, LOG_WARNING,
"IPv6 link-local NLRI received %s ignore this NLRI",
inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ));
@@ -3040,10 +3804,10 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet)
/* Normal process. */
if (attr)
- ret = bgp_update (peer, &p, attr, packet->afi, packet->safi,
+ ret = bgp_update (peer, &p, attr, packet->afi, packet->safi,
ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0);
else
- ret = bgp_withdraw (peer, &p, attr, packet->afi, packet->safi,
+ ret = bgp_withdraw (peer, &p, attr, packet->afi, packet->safi,
ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL);
/* Address family configuration mismatch or maximum-prefix count
@@ -3077,16 +3841,16 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt,
while (pnt < end)
{
prefixlen = *pnt++;
-
+
/* Prefix length check. */
if ((afi == AFI_IP && prefixlen > 32)
|| (afi == AFI_IP6 && prefixlen > 128))
{
- plog_err (peer->log,
+ plog_err (peer->log,
"%s [Error] Update packet error (wrong prefix length %d)",
peer->host, prefixlen);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_INVAL_NETWORK);
+ bgp_peer_down_error(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_NETWORK);
return -1;
}
@@ -3095,12 +3859,12 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt,
if (pnt + psize > end)
{
- plog_err (peer->log,
+ plog_err (peer->log,
"%s [Error] Update packet error"
" (prefix data overflow prefix size is %d)",
peer->host, psize);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_INVAL_NETWORK);
+ bgp_peer_down_error(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_NETWORK);
return -1;
}
@@ -3114,13 +3878,13 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt,
"%s [Error] Update packet error"
" (prefix length mismatch with total length)",
peer->host);
- bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_INVAL_NETWORK);
+ bgp_peer_down_error(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_NETWORK);
return -1;
}
return 0;
}
-
+
static struct bgp_static *
bgp_static_new (void)
{
@@ -3145,7 +3909,7 @@ bgp_static_withdraw_rsclient (struct bgp *bgp, struct peer *rsclient,
rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, NULL);
/* Check selected route and self inserted route. */
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == bgp->peer_self
&& ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_STATIC)
@@ -3169,13 +3933,11 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p,
{
struct bgp_node *rn;
struct bgp_info *ri;
- struct bgp_info *new;
- struct bgp_info info;
- struct attr *attr_new;
- struct attr attr = {0 };
- struct attr new_attr = { .extra = 0 };
+ struct attr static_attr_s ;
+ struct attr* static_attr ;
+ struct attr* client_attr ;
+ struct rs_route rt_s ;
struct bgp *bgp;
- int ret;
char buf[SU_ADDRSTRLEN];
bgp = rsclient->bgp;
@@ -3186,135 +3948,152 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p,
rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, NULL);
- bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+ /* Construct the static route attributes.
+ *
+ * Starts with an empty aspath, which is interned. No other elements are
+ * interned and the object itself is not interned.
+ */
+ static_attr = &static_attr_s ;
+ bgp_attr_default_set (static_attr, BGP_ORIGIN_IGP);
+
+ static_attr->nexthop = bgp_static->igpnexthop;
+ static_attr->med = bgp_static->igpmetric;
+ static_attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
- attr.nexthop = bgp_static->igpnexthop;
- attr.med = bgp_static->igpmetric;
- attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
-
if (bgp_static->atomic)
- attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
-
- /* Apply network route-map for export to this rsclient. */
+ static_attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+
+ /* Apply network route-map for export to this rsclient.
+ *
+ * Create interned attributes client_attr, either from route-map result, or
+ * from the static_attr.
+ */
if (bgp_static->rmap.name)
{
- struct attr attr_tmp = attr;
- info.peer = rsclient;
- info.attr = &attr_tmp;
-
+ struct bgp_info info_s = { 0 } ;
+ struct attr rmap_attr_s ;
+ struct attr* rmap_attr ;
+ route_map_result_t ret;
+
+ rmap_attr = &rmap_attr_s ;
+ bgp_attr_dup(rmap_attr, static_attr) ;
+
+ info_s.peer = rsclient ;
+ info_s.attr = rmap_attr ;
+
SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_EXPORT);
SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_NETWORK);
- ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info);
+ ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info_s);
rsclient->rmap_type = 0;
if (ret == RMAP_DENYMATCH)
{
- /* Free uninterned attribute. */
- bgp_attr_flush (&attr_tmp);
+ /* Free uninterned attribute. */
+ bgp_attr_flush (rmap_attr) ;
+ bgp_attr_extra_free (rmap_attr);
+
+ /* Unintern original. */
+ aspath_unintern (&static_attr->aspath);
+ bgp_attr_extra_free (static_attr);
- /* Unintern original. */
- aspath_unintern (&attr.aspath);
bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi);
- bgp_attr_extra_free (&attr);
-
+
return;
- }
- attr_new = bgp_attr_intern (&attr_tmp);
+ } ;
+
+ client_attr = bgp_attr_intern(rmap_attr) ;
+ bgp_attr_extra_free (rmap_attr) ;
}
else
- attr_new = bgp_attr_intern (&attr);
-
- bgp_attr_dup(&new_attr, attr_new);
-
+ client_attr = bgp_attr_intern (static_attr) ;
+
+ /* Have now finished with the static_attr */
+ aspath_unintern (&static_attr->aspath);
+ bgp_attr_extra_free (static_attr);
+
+ /* run the import route-map for the rsclient. */
+ bgp_rs_route_init(&rt_s, afi, safi, NULL, bgp->peer_self, p,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL) ;
+
SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK);
- if (bgp_import_modifier (rsclient, bgp->peer_self, p, &new_attr, afi, safi)
- == RMAP_DENY)
+ client_attr = bgp_import_modifier (rsclient, &rt_s, client_attr) ;
+
+ bgp->peer_self->rmap_type = 0;
+
+ if (client_attr == NULL)
{
/* This BGP update is filtered. Log the reason then update BGP entry. */
if (BGP_DEBUG (update, UPDATE_IN))
- zlog (rsclient->log, LOG_DEBUG,
- "Static UPDATE about %s/%d -- DENIED for RS-client %s due to: import-policy",
- inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
- p->prefixlen, rsclient->host);
+ zlog (rsclient->log, LOG_DEBUG,
+ "Static UPDATE about %s/%d -- DENIED for RS-client %s due to: "
+ "import-policy",
+ inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+ p->prefixlen, rsclient->host);
bgp->peer_self->rmap_type = 0;
- bgp_attr_unintern (&attr_new);
- aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
-
bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi);
-
+
return;
}
- bgp->peer_self->rmap_type = 0;
-
- bgp_attr_unintern (&attr_new);
- attr_new = bgp_attr_intern (&new_attr);
- bgp_attr_extra_free (&new_attr);
+ /* Apply the client_attr */
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_STATIC)
break;
if (ri)
{
- if (attrhash_cmp (ri->attr, attr_new) &&
- !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+ if (attrhash_cmp (ri->attr, client_attr) &&
+ !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
{
- bgp_unlock_node (rn);
- bgp_attr_unintern (&attr_new);
- aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
- return;
- }
+ /* No point duplicating */
+ bgp_attr_unintern (&client_attr);
+ }
else
{
- /* The attribute is changed. */
+ /* The attribute is changed. */
bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
- /* Rewrite BGP route information. */
+ /* Rewrite BGP route information. */
if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
bgp_info_restore(rn, ri);
+
bgp_attr_unintern (&ri->attr);
- ri->attr = attr_new;
+ ri->attr = client_attr ;
ri->uptime = bgp_clock ();
- /* Process change. */
+ /* Process change. */
bgp_process (bgp, rn, afi, safi);
- bgp_unlock_node (rn);
- aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
- return;
}
- }
-
- /* Make new BGP info. */
- new = bgp_info_new ();
- new->type = ZEBRA_ROUTE_BGP;
- new->sub_type = BGP_ROUTE_STATIC;
- new->peer = bgp->peer_self;
- SET_FLAG (new->flags, BGP_INFO_VALID);
- new->attr = attr_new;
- new->uptime = bgp_clock ();
- /* Register new BGP information. */
- bgp_info_add (rn, new);
-
- /* route_node_get lock */
- bgp_unlock_node (rn);
-
- /* Process change. */
+ bgp_unlock_node (rn);
+ return ;
+ } ;
+
+ /* Make new BGP info. */
+ ri = bgp_info_new ();
+ ri->type = rt_s.type ;
+ ri->sub_type = rt_s.sub_type ;
+ ri->peer = rt_s.peer ;
+ ri->attr = client_attr ;
+ ri->uptime = bgp_clock ();
+
+ SET_FLAG (ri->flags, BGP_INFO_VALID);
+
+ /* Register new BGP information. */
+ bgp_info_add (rn, ri);
+
+ /* Process change. */
bgp_process (bgp, rn, afi, safi);
- /* Unintern original. */
- aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
+ /* route_node_get lock */
+ bgp_unlock_node (rn);
}
static void
@@ -3324,10 +4103,8 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
struct bgp_node *rn;
struct bgp_info *ri;
struct bgp_info *new;
- struct bgp_info info;
struct attr attr = { 0 };
struct attr *attr_new;
- int ret;
assert (bgp_static);
if (!bgp_static)
@@ -3336,7 +4113,7 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, NULL);
bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
-
+
attr.nexthop = bgp_static->igpnexthop;
attr.med = bgp_static->igpmetric;
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
@@ -3348,17 +4125,20 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
if (bgp_static->rmap.name)
{
struct attr attr_tmp = attr;
- info.peer = bgp->peer_self;
- info.attr = &attr_tmp;
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret;
+
+ info_s.peer = bgp->peer_self;
+ info_s.attr = &attr_tmp;
SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK);
- ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info);
+ ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info_s);
bgp->peer_self->rmap_type = 0;
if (ret == RMAP_DENYMATCH)
- {
+ {
/* Free uninterned attribute. */
bgp_attr_flush (&attr_tmp);
@@ -3373,7 +4153,7 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
else
attr_new = bgp_attr_intern (&attr);
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_STATIC)
break;
@@ -3424,13 +4204,13 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
/* Aggregate address increment. */
bgp_aggregate_increment (bgp, p, new, afi, safi);
-
+
/* Register new BGP information. */
bgp_info_add (rn, new);
-
+
/* route_node_get lock */
bgp_unlock_node (rn);
-
+
/* Process change. */
bgp_process (bgp, rn, afi, safi);
@@ -3461,7 +4241,7 @@ bgp_static_update_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi,
{
struct bgp_node *rn;
struct bgp_info *new;
-
+
rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
/* Make new BGP info. */
@@ -3477,13 +4257,13 @@ bgp_static_update_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi,
/* Aggregate address increment. */
bgp_aggregate_increment (bgp, p, new, afi, safi);
-
+
/* Register new BGP information. */
bgp_info_add (rn, new);
/* route_node_get lock */
bgp_unlock_node (rn);
-
+
/* Process change. */
bgp_process (bgp, rn, afi, safi);
}
@@ -3498,8 +4278,8 @@ bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi,
rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, NULL);
/* Check selected route and self inserted route. */
- for (ri = rn->info; ri; ri = ri->next)
- if (ri->peer == bgp->peer_self
+ for (ri = rn->info; ri; ri = ri->info_next)
+ if (ri->peer == bgp->peer_self
&& ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_STATIC)
break;
@@ -3546,8 +4326,8 @@ bgp_static_withdraw_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi,
rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
/* Check selected route and self inserted route. */
- for (ri = rn->info; ri; ri = ri->next)
- if (ri->peer == bgp->peer_self
+ for (ri = rn->info; ri; ri = ri->info_next)
+ if (ri->peer == bgp->peer_self
&& ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_STATIC)
break;
@@ -3567,7 +4347,7 @@ bgp_static_withdraw_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi,
/* Configure static BGP network. When user don't run zebra, static
route should be installed as valid. */
static int
-bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str,
+bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str,
afi_t afi, safi_t safi, const char *rmap, int backdoor)
{
int ret;
@@ -3603,11 +4383,11 @@ bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str,
bgp_static = rn->info;
/* Check previous routes are installed into BGP. */
- if (bgp_static->valid && bgp_static->backdoor != backdoor)
+ if (bgp_static->valid && (bgp_static->backdoor != backdoor))
need_update = 1;
-
+
bgp_static->backdoor = backdoor;
-
+
if (rmap)
{
if (bgp_static->rmap.name)
@@ -3633,7 +4413,7 @@ bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str,
bgp_static->valid = 0;
bgp_static->igpmetric = 0;
bgp_static->igpnexthop.s_addr = 0;
-
+
if (rmap)
{
if (bgp_static->rmap.name)
@@ -3696,7 +4476,7 @@ bgp_static_unset (struct vty *vty, struct bgp *bgp, const char *ip_str,
}
bgp_static = rn->info;
-
+
/* Update BGP RIB. */
if (! bgp_static->backdoor)
bgp_static_withdraw (bgp, &p, afi, safi);
@@ -3726,7 +4506,7 @@ bgp_static_delete (struct bgp *bgp)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn))
if (rn->info != NULL)
- {
+ {
if (safi == SAFI_MPLS_VPN)
{
table = rn->info;
@@ -3823,7 +4603,7 @@ bgp_static_set_vpnv4 (struct vty *vty, const char *ip_str, const char *rd_str,
/* Configure static BGP network. */
int
-bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str,
+bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str,
const char *rd_str, const char *tag_str)
{
int ret;
@@ -3886,7 +4666,7 @@ bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str,
return CMD_SUCCESS;
}
-
+
DEFUN (bgp_network,
bgp_network_cmd,
"network A.B.C.D/M",
@@ -3930,7 +4710,7 @@ DEFUN (bgp_network_mask,
{
int ret;
char prefix_str[BUFSIZ];
-
+
ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
if (! ret)
{
@@ -3954,7 +4734,7 @@ DEFUN (bgp_network_mask_route_map,
{
int ret;
char prefix_str[BUFSIZ];
-
+
ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
if (! ret)
{
@@ -3977,7 +4757,7 @@ DEFUN (bgp_network_mask_backdoor,
{
int ret;
char prefix_str[BUFSIZ];
-
+
ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
if (! ret)
{
@@ -4059,7 +4839,7 @@ DEFUN (no_bgp_network,
"Specify a network to announce via BGP\n"
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
{
- return bgp_static_unset (vty, vty->index, argv[0], AFI_IP,
+ return bgp_static_unset (vty, vty->index, argv[0], AFI_IP,
bgp_node_safi (vty));
}
@@ -4099,7 +4879,7 @@ DEFUN (no_bgp_network_mask,
return CMD_WARNING;
}
- return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP,
+ return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP,
bgp_node_safi (vty));
}
@@ -4141,7 +4921,7 @@ DEFUN (no_bgp_network_mask_natural,
return CMD_WARNING;
}
- return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP,
+ return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP,
bgp_node_safi (vty));
}
@@ -4230,6 +5010,7 @@ ALIAS_DEPRECATED (bgp_network,
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (bgp_network_backdoor,
bgp_network_backdoor_ttl_cmd,
"network A.B.C.D/M backdoor pathlimit <0-255>",
@@ -4238,6 +5019,7 @@ ALIAS_DEPRECATED (bgp_network_backdoor,
"Specify a BGP backdoor route\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (bgp_network_mask,
bgp_network_mask_ttl_cmd,
"network A.B.C.D mask A.B.C.D pathlimit <0-255>",
@@ -4247,6 +5029,7 @@ ALIAS_DEPRECATED (bgp_network_mask,
"Network mask\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (bgp_network_mask_backdoor,
bgp_network_mask_backdoor_ttl_cmd,
"network A.B.C.D mask A.B.C.D backdoor pathlimit <0-255>",
@@ -4257,6 +5040,7 @@ ALIAS_DEPRECATED (bgp_network_mask_backdoor,
"Specify a BGP backdoor route\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (bgp_network_mask_natural,
bgp_network_mask_natural_ttl_cmd,
"network A.B.C.D pathlimit <0-255>",
@@ -4264,14 +5048,16 @@ ALIAS_DEPRECATED (bgp_network_mask_natural,
"Network number\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (bgp_network_mask_natural_backdoor,
bgp_network_mask_natural_backdoor_ttl_cmd,
- "network A.B.C.D backdoor pathlimit (1-255>",
+ "network A.B.C.D backdoor pathlimit <1-255>",
"Specify a network to announce via BGP\n"
"Network number\n"
"Specify a BGP backdoor route\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_bgp_network,
no_bgp_network_ttl_cmd,
"no network A.B.C.D/M pathlimit <0-255>",
@@ -4280,6 +5066,7 @@ ALIAS_DEPRECATED (no_bgp_network,
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_bgp_network,
no_bgp_network_backdoor_ttl_cmd,
"no network A.B.C.D/M backdoor pathlimit <0-255>",
@@ -4289,6 +5076,7 @@ ALIAS_DEPRECATED (no_bgp_network,
"Specify a BGP backdoor route\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_bgp_network,
no_bgp_network_mask_ttl_cmd,
"no network A.B.C.D mask A.B.C.D pathlimit <0-255>",
@@ -4299,6 +5087,7 @@ ALIAS_DEPRECATED (no_bgp_network,
"Network mask\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_bgp_network_mask,
no_bgp_network_mask_backdoor_ttl_cmd,
"no network A.B.C.D mask A.B.C.D backdoor pathlimit <0-255>",
@@ -4310,6 +5099,7 @@ ALIAS_DEPRECATED (no_bgp_network_mask,
"Specify a BGP backdoor route\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_bgp_network_mask_natural,
no_bgp_network_mask_natural_ttl_cmd,
"no network A.B.C.D pathlimit <0-255>",
@@ -4318,6 +5108,7 @@ ALIAS_DEPRECATED (no_bgp_network_mask_natural,
"Network number\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_bgp_network_mask_natural,
no_bgp_network_mask_natural_backdoor_ttl_cmd,
"no network A.B.C.D backdoor pathlimit <0-255>",
@@ -4327,6 +5118,7 @@ ALIAS_DEPRECATED (no_bgp_network_mask_natural,
"Specify a BGP backdoor route\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
#ifdef HAVE_IPV6
ALIAS_DEPRECATED (ipv6_bgp_network,
ipv6_bgp_network_ttl_cmd,
@@ -4335,6 +5127,7 @@ ALIAS_DEPRECATED (ipv6_bgp_network,
"IPv6 prefix <network>/<length>\n"
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
+
ALIAS_DEPRECATED (no_ipv6_bgp_network,
no_ipv6_bgp_network_ttl_cmd,
"no network X:X::X:X/M pathlimit <0-255>",
@@ -4344,8 +5137,8 @@ ALIAS_DEPRECATED (no_ipv6_bgp_network,
"AS-Path hopcount limit attribute\n"
"AS-Pathlimit TTL, in number of AS-Path hops\n")
#endif /* HAVE_IPV6 */
-
-/* Aggreagete address:
+
+/* Aggregate address:
advertise-map Set condition to advertise attribute
as-set Generate AS set path information
@@ -4383,11 +5176,11 @@ static void
bgp_aggregate_free (struct bgp_aggregate *aggregate)
{
XFREE (MTYPE_BGP_AGGREGATE, aggregate);
-}
+}
static void
bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew,
- afi_t afi, safi_t safi, struct bgp_info *del,
+ afi_t afi, safi_t safi, struct bgp_info *del,
struct bgp_aggregate *aggregate)
{
struct bgp_table *table;
@@ -4430,7 +5223,7 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew,
{
match = 0;
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
if (BGP_INFO_HOLDDOWN (ri))
continue;
@@ -4507,7 +5300,7 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew,
if (rinew)
{
aggregate->count++;
-
+
if (aggregate->summary_only)
(bgp_info_extra_get (rinew))->suppress++;
@@ -4598,7 +5391,7 @@ bgp_aggregate_increment (struct bgp *bgp, struct prefix *p,
}
void
-bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p,
+bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p,
struct bgp_info *del, afi_t afi, safi_t safi)
{
struct bgp_node *child;
@@ -4647,7 +5440,7 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi,
return;
if (afi == AFI_IP6 && p->prefixlen == IPV6_MAX_BITLEN)
return;
-
+
/* If routes exists below this node, generate aggregate routes. */
top = bgp_node_get (table, p);
for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top))
@@ -4655,7 +5448,7 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi,
{
match = 0;
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
if (BGP_INFO_HOLDDOWN (ri))
continue;
@@ -4702,7 +5495,7 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi,
aggregate->count++;
}
}
-
+
/* If this node is suppressed, process the change. */
if (match)
bgp_process (bgp, rn, afi, safi);
@@ -4724,14 +5517,14 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi,
bgp_info_add (rn, new);
bgp_unlock_node (rn);
-
+
/* Process change. */
bgp_process (bgp, rn, afi, safi);
}
}
void
-bgp_aggregate_delete (struct bgp *bgp, struct prefix *p, afi_t afi,
+bgp_aggregate_delete (struct bgp *bgp, struct prefix *p, afi_t afi,
safi_t safi, struct bgp_aggregate *aggregate)
{
struct bgp_table *table;
@@ -4754,7 +5547,7 @@ bgp_aggregate_delete (struct bgp *bgp, struct prefix *p, afi_t afi,
{
match = 0;
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
if (BGP_INFO_HOLDDOWN (ri))
continue;
@@ -4784,8 +5577,8 @@ bgp_aggregate_delete (struct bgp *bgp, struct prefix *p, afi_t afi,
/* Delete aggregate route from BGP table. */
rn = bgp_node_get (table, p);
- for (ri = rn->info; ri; ri = ri->next)
- if (ri->peer == bgp->peer_self
+ for (ri = rn->info; ri; ri = ri->info_next)
+ if (ri->peer == bgp->peer_self
&& ri->type == ZEBRA_ROUTE_BGP
&& ri->sub_type == BGP_ROUTE_AGGREGATE)
break;
@@ -4886,7 +5679,7 @@ bgp_aggregate_set (struct vty *vty, const char *prefix_str,
{
vty_out (vty, "Error deleting aggregate.%s", VTY_NEWLINE);
bgp_unlock_node (rn);
- return CMD_WARNING;
+ return CMD_WARNING;
}
}
@@ -5180,7 +5973,7 @@ DEFUN (ipv6_aggregate_address_summary_only,
"Aggregate prefix\n"
"Filter more specific routes from updates\n")
{
- return bgp_aggregate_set (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+ return bgp_aggregate_set (vty, argv[0], AFI_IP6, SAFI_UNICAST,
AGGREGATE_SUMMARY_ONLY, 0);
}
@@ -5241,7 +6034,7 @@ ALIAS (no_ipv6_aggregate_address_summary_only,
"Aggregate prefix\n"
"Filter more specific routes from updates\n")
#endif /* HAVE_IPV6 */
-
+
/* Redistribute route treatment. */
void
bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
@@ -5251,13 +6044,11 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
struct listnode *node, *nnode;
struct bgp_info *new;
struct bgp_info *bi;
- struct bgp_info info;
struct bgp_node *bn;
struct attr attr = { 0 };
struct attr attr_new = { 0 };
struct attr *new_attr;
afi_t afi;
- int ret;
/* Make default attribute. */
bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
@@ -5282,13 +6073,16 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
/* Apply route-map. */
if (bgp->rmap[afi][type].map)
{
- info.peer = bgp->peer_self;
- info.attr = &attr_new;
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret;
+
+ info_s.peer = bgp->peer_self;
+ info_s.attr = &attr_new;
SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE);
ret = route_map_apply (bgp->rmap[afi][type].map, p, RMAP_BGP,
- &info);
+ &info_s);
bgp->peer_self->rmap_type = 0;
@@ -5297,7 +6091,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
/* Free uninterned attribute. */
bgp_attr_flush (&attr_new);
bgp_attr_extra_free (&attr_new);
-
+
/* Unintern original. */
aspath_unintern (&attr.aspath);
bgp_attr_extra_free (&attr);
@@ -5306,17 +6100,17 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
}
}
- bn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST],
+ bn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST],
afi, SAFI_UNICAST, p, NULL);
-
+
new_attr = bgp_attr_intern (&attr_new);
bgp_attr_extra_free (&attr_new);
-
- for (bi = bn->info; bi; bi = bi->next)
+
+ for (bi = bn->info; bi; bi = bi->info_next)
if (bi->peer == bgp->peer_self
&& bi->sub_type == BGP_ROUTE_REDISTRIBUTE)
break;
-
+
if (bi)
{
if (attrhash_cmp (bi->attr, new_attr) &&
@@ -5332,7 +6126,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
{
/* The attribute is changed. */
bgp_info_set_flag (bn, bi, BGP_INFO_ATTR_CHANGED);
-
+
/* Rewrite BGP route information. */
if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED))
bgp_info_restore(bn, bi);
@@ -5341,7 +6135,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
bgp_attr_unintern (&bi->attr);
bi->attr = new_attr;
bi->uptime = bgp_clock ();
-
+
/* Process change. */
bgp_aggregate_increment (bgp, p, bi, afi, SAFI_UNICAST);
bgp_process (bgp, bn, afi, SAFI_UNICAST);
@@ -5349,7 +6143,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop,
aspath_unintern (&attr.aspath);
bgp_attr_extra_free (&attr);
return;
- }
+ }
}
new = bgp_info_new ();
@@ -5389,7 +6183,7 @@ bgp_redistribute_delete (struct prefix *p, u_char type)
{
rn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, p, NULL);
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == bgp->peer_self
&& ri->type == type)
break;
@@ -5417,7 +6211,7 @@ bgp_redistribute_withdraw (struct bgp *bgp, afi_t afi, int type)
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
{
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
if (ri->peer == bgp->peer_self
&& ri->type == type)
break;
@@ -5430,13 +6224,13 @@ bgp_redistribute_withdraw (struct bgp *bgp, afi_t afi, int type)
}
}
}
-
+
/* Static function to display route. */
static void
route_vty_out_route (struct prefix *p, struct vty *vty)
{
int len;
- u_int32_t destination;
+ u_int32_t destination;
char buf[BUFSIZ];
if (p->family == AF_INET)
@@ -5500,7 +6294,7 @@ route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo)
if ((binfo->peer->as) && (binfo->peer->as == binfo->peer->local_as))
vty_out (vty, "i");
else
- vty_out (vty, " ");
+ vty_out (vty, " ");
}
/* called from terminal list command */
@@ -5509,10 +6303,10 @@ route_vty_out (struct vty *vty, struct prefix *p,
struct bgp_info *binfo, int display, safi_t safi)
{
struct attr *attr;
-
- /* short status lead text */
+
+ /* short status lead text */
route_vty_short_status_out (vty, binfo);
-
+
/* print prefix and mask */
if (! display)
route_vty_out_route (p, vty);
@@ -5521,23 +6315,23 @@ route_vty_out (struct vty *vty, struct prefix *p,
/* Print attribute */
attr = binfo->attr;
- if (attr)
+ if (attr)
{
if (p->family == AF_INET)
{
if (safi == SAFI_MPLS_VPN)
vty_out (vty, "%-16s",
- inet_ntoa (attr->extra->mp_nexthop_global_in));
+ safe_inet_ntoa (attr->extra->mp_nexthop_global_in));
else
- vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+ vty_out (vty, "%-16s", safe_inet_ntoa (attr->nexthop));
}
-#ifdef HAVE_IPV6
+#ifdef HAVE_IPV6
else if (p->family == AF_INET6)
{
int len;
char buf[BUFSIZ];
- len = vty_out (vty, "%s",
+ len = vty_out (vty, "%s",
inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
buf, BUFSIZ));
len = 16 - len;
@@ -5559,7 +6353,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
vty_out (vty, " ");
vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0));
-
+
/* Print aspath */
if (attr->aspath)
aspath_print_vty (vty, "%s", attr->aspath, " ");
@@ -5568,7 +6362,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
vty_out (vty, "%s", bgp_origin_str[attr->origin]);
}
vty_out (vty, "%s", VTY_NEWLINE);
-}
+}
/* called from terminal list command */
void
@@ -5584,22 +6378,22 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p,
route_vty_out_route (p, vty);
/* Print attribute */
- if (attr)
+ if (attr)
{
if (p->family == AF_INET)
{
if (safi == SAFI_MPLS_VPN)
vty_out (vty, "%-16s",
- inet_ntoa (attr->extra->mp_nexthop_global_in));
+ safe_inet_ntoa (attr->extra->mp_nexthop_global_in));
else
- vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+ vty_out (vty, "%-16s", safe_inet_ntoa (attr->nexthop));
}
#ifdef HAVE_IPV6
else if (p->family == AF_INET6)
{
int len;
char buf[BUFSIZ];
-
+
assert (attr->extra);
len = vty_out (vty, "%s",
@@ -5622,9 +6416,9 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p,
vty_out (vty, "%7u", attr->local_pref);
else
vty_out (vty, " ");
-
+
vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0));
-
+
/* Print aspath */
if (attr->aspath)
aspath_print_vty (vty, "%s", attr->aspath, " ");
@@ -5634,7 +6428,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p,
}
vty_out (vty, "%s", VTY_NEWLINE);
-}
+}
void
route_vty_out_tag (struct vty *vty, struct prefix *p,
@@ -5642,13 +6436,13 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
{
struct attr *attr;
u_int32_t label = 0;
-
+
if (!binfo->extra)
return;
-
- /* short status lead text */
+
+ /* short status lead text */
route_vty_short_status_out (vty, binfo);
-
+
/* print prefix and mask */
if (! display)
route_vty_out_route (p, vty);
@@ -5657,24 +6451,24 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
/* Print attribute */
attr = binfo->attr;
- if (attr)
+ if (attr)
{
if (p->family == AF_INET)
{
if (safi == SAFI_MPLS_VPN)
vty_out (vty, "%-16s",
- inet_ntoa (attr->extra->mp_nexthop_global_in));
+ safe_inet_ntoa (attr->extra->mp_nexthop_global_in));
else
- vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+ vty_out (vty, "%-16s", safe_inet_ntoa (attr->nexthop));
}
-#ifdef HAVE_IPV6
+#ifdef HAVE_IPV6
else if (p->family == AF_INET6)
{
assert (attr->extra);
char buf[BUFSIZ];
char buf1[BUFSIZ];
if (attr->extra->mp_nexthop_len == 16)
- vty_out (vty, "%s",
+ vty_out (vty, "%s",
inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
buf, BUFSIZ));
else if (attr->extra->mp_nexthop_len == 32)
@@ -5683,7 +6477,7 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
buf, BUFSIZ),
inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
buf1, BUFSIZ));
-
+
}
#endif /* HAVE_IPV6 */
}
@@ -5693,7 +6487,7 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
vty_out (vty, "notag/%d", label);
vty_out (vty, "%s", VTY_NEWLINE);
-}
+}
/* dampening route */
static void
@@ -5704,9 +6498,9 @@ damp_route_vty_out (struct vty *vty, struct prefix *p,
int len;
char timebuf[BGP_UPTIME_LEN];
- /* short status lead text */
+ /* short status lead text */
route_vty_short_status_out (vty, binfo);
-
+
/* print prefix and mask */
if (! display)
route_vty_out_route (p, vty);
@@ -5745,15 +6539,15 @@ flap_route_vty_out (struct vty *vty, struct prefix *p,
struct bgp_damp_info *bdi;
char timebuf[BGP_UPTIME_LEN];
int len;
-
+
if (!binfo->extra)
return;
-
+
bdi = binfo->extra->damp_info;
/* short status lead text */
route_vty_short_status_out (vty, binfo);
-
+
/* print prefix and mask */
if (! display)
route_vty_out_route (p, vty);
@@ -5773,7 +6567,7 @@ flap_route_vty_out (struct vty *vty, struct prefix *p,
vty_out (vty, " ");
else
vty_out (vty, "%*s ", len, " ");
-
+
vty_out (vty, "%s ", peer_uptime (bdi->start_time,
timebuf, BGP_UPTIME_LEN));
@@ -5798,17 +6592,14 @@ flap_route_vty_out (struct vty *vty, struct prefix *p,
}
static void
-route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
+route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
struct bgp_info *binfo, afi_t afi, safi_t safi)
{
- char buf[INET6_ADDRSTRLEN];
- char buf1[BUFSIZ];
+ char buf[SU_ADDRSTRLEN];
struct attr *attr;
int sockunion_vty_out (struct vty *, union sockunion *);
-#ifdef HAVE_CLOCK_MONOTONIC
- time_t tbuf;
-#endif
-
+ time_t tbuf ;
+
attr = binfo->attr;
if (attr)
@@ -5828,25 +6619,27 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (CHECK_FLAG (binfo->flags, BGP_INFO_STALE))
vty_out (vty, ", (stale)");
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)))
- vty_out (vty, ", (aggregated by %u %s)",
+ vty_out (vty, ", (aggregated by %u %s)",
attr->extra->aggregator_as,
- inet_ntoa (attr->extra->aggregator_addr));
- if (CHECK_FLAG (binfo->peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+ safe_inet_ntoa (attr->extra->aggregator_addr));
+ if (CHECK_FLAG (binfo->peer->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT))
vty_out (vty, ", (Received from a RR-client)");
- if (CHECK_FLAG (binfo->peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+ if (CHECK_FLAG (binfo->peer->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT))
vty_out (vty, ", (Received from a RS-client)");
if (CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
vty_out (vty, ", (history entry)");
else if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
vty_out (vty, ", (suppressed due to dampening)");
vty_out (vty, "%s", VTY_NEWLINE);
-
+
/* Line2 display Next-hop, Neighbor, Router-id */
if (p->family == AF_INET)
{
vty_out (vty, " %s", safi == SAFI_MPLS_VPN ?
- inet_ntoa (attr->extra->mp_nexthop_global_in) :
- inet_ntoa (attr->nexthop));
+ safe_inet_ntoa (attr->extra->mp_nexthop_global_in) :
+ safe_inet_ntoa (attr->nexthop));
}
#ifdef HAVE_IPV6
else
@@ -5860,21 +6653,23 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (binfo->peer == bgp->peer_self)
{
- vty_out (vty, " from %s ",
+ vty_out (vty, " from %s ",
p->family == AF_INET ? "0.0.0.0" : "::");
- vty_out (vty, "(%s)", inet_ntoa(bgp->router_id));
+ vty_out (vty, "(%s)", safe_inet_ntoa(bgp->router_id));
}
else
{
if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID))
- vty_out (vty, " (inaccessible)");
+ vty_out (vty, " (inaccessible)");
else if (binfo->extra && binfo->extra->igpmetric)
vty_out (vty, " (metric %d)", binfo->extra->igpmetric);
- vty_out (vty, " from %s", sockunion2str (&binfo->peer->su, buf, SU_ADDRSTRLEN));
+ vty_out (vty, " from %s",
+ sockunion2str (&binfo->peer->su, buf, sizeof(buf)));
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
- vty_out (vty, " (%s)", inet_ntoa (attr->extra->originator_id));
+ vty_out (vty, " (%s)", safe_inet_ntoa (attr->extra->originator_id));
else
- vty_out (vty, " (%s)", inet_ntop (AF_INET, &binfo->peer->remote_id, buf1, BUFSIZ));
+ vty_out (vty, " (%s)",
+ inet_ntop (AF_INET, &binfo->peer->remote_id, buf, sizeof(buf)));
}
vty_out (vty, "%s", VTY_NEWLINE);
@@ -5884,17 +6679,18 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
{
vty_out (vty, " (%s)%s",
inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
- buf, INET6_ADDRSTRLEN),
+ buf, sizeof(buf)),
VTY_NEWLINE);
}
#endif /* HAVE_IPV6 */
- /* Line 3 display Origin, Med, Locpref, Weight, valid, Int/Ext/Local, Atomic, best */
+ /* Line 3 display:
+ * Origin, Med, Locpref, Weight, valid, Int/Ext/Local, Atomic, best */
vty_out (vty, " Origin %s", bgp_origin_long_str[attr->origin]);
-
+
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
vty_out (vty, ", metric %u", attr->med);
-
+
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
vty_out (vty, ", localpref %u", attr->local_pref);
else
@@ -5902,7 +6698,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (attr->extra && attr->extra->weight != 0)
vty_out (vty, ", weight %u", attr->extra->weight);
-
+
if (! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
vty_out (vty, ", valid");
@@ -5910,9 +6706,10 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
{
if (binfo->peer->as == binfo->peer->local_as)
vty_out (vty, ", internal");
- else
- vty_out (vty, ", %s",
- (bgp_confederation_peers_check(bgp, binfo->peer->as) ? "confed-external" : "external"));
+ else
+ vty_out (vty, ", %s",
+ (bgp_confederation_peers_check(bgp, binfo->peer->as)
+ ? "confed-external" : "external"));
}
else if (binfo->sub_type == BGP_ROUTE_AGGREGATE)
vty_out (vty, ", aggregated, local");
@@ -5923,56 +6720,52 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))
vty_out (vty, ", atomic-aggregate");
-
+
if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED))
vty_out (vty, ", best");
vty_out (vty, "%s", VTY_NEWLINE);
-
+
/* Line 4 display Community */
if (attr->community)
vty_out (vty, " Community: %s%s", attr->community->str,
VTY_NEWLINE);
-
+
/* Line 5 display Extended-community */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))
- vty_out (vty, " Extended Community: %s%s",
+ vty_out (vty, " Extended Community: %s%s",
attr->extra->ecommunity->str, VTY_NEWLINE);
-
+
/* Line 6 display Originator, Cluster-id */
if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) ||
(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)))
{
assert (attr->extra);
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
- vty_out (vty, " Originator: %s",
- inet_ntoa (attr->extra->originator_id));
+ vty_out (vty, " Originator: %s",
+ safe_inet_ntoa (attr->extra->originator_id));
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))
{
int i;
vty_out (vty, ", Cluster list: ");
for (i = 0; i < attr->extra->cluster->length / 4; i++)
- vty_out (vty, "%s ",
- inet_ntoa (attr->extra->cluster->list[i]));
+ vty_out (vty, "%s ",
+ safe_inet_ntoa (attr->extra->cluster->list[i]));
}
vty_out (vty, "%s", VTY_NEWLINE);
}
-
+
if (binfo->extra && binfo->extra->damp_info)
bgp_damp_info_vty (vty, binfo);
/* Line 7 display Uptime */
-#ifdef HAVE_CLOCK_MONOTONIC
- tbuf = time(NULL) - (bgp_clock() - binfo->uptime);
+ tbuf = bgp_wall_clock(binfo->uptime);
vty_out (vty, " Last update: %s", ctime(&tbuf));
-#else
- vty_out (vty, " Last update: %s", ctime(&binfo->uptime));
-#endif /* HAVE_CLOCK_MONOTONIC */
}
vty_out (vty, "%s", VTY_NEWLINE);
-}
-
+}
+
#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,%s r RIB-failure, S Stale, R Removed%s"
#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s"
#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path%s"
@@ -6022,12 +6815,12 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router
output_count = 0;
/* Start processing of routes. */
- for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
if (rn->info != NULL)
{
display = 0;
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
if (type == bgp_show_type_flap_statistics
|| type == bgp_show_type_flap_address
@@ -6049,7 +6842,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router
|| type == bgp_show_type_flap_regexp)
{
regex_t *regex = output_arg;
-
+
if (bgp_regexec (regex, ri->attr->aspath) == REG_NOMATCH)
continue;
}
@@ -6057,7 +6850,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router
|| type == bgp_show_type_flap_prefix_list)
{
struct prefix_list *plist = output_arg;
-
+
if (prefix_list_apply (plist, &rn->p) != PREFIX_PERMIT)
continue;
}
@@ -6073,18 +6866,19 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router
|| type == bgp_show_type_flap_route_map)
{
struct route_map *rmap = output_arg;
- struct bgp_info binfo;
- struct attr dummy_attr = { 0 };
- int ret;
+ struct attr dummy_attr = { 0 };
+ struct bgp_info info_s = { 0 } ;
+ route_map_result_t ret;
bgp_attr_dup (&dummy_attr, ri->attr);
- binfo.peer = ri->peer;
- binfo.attr = &dummy_attr;
+ info_s.peer = ri->peer;
+ info_s.attr = &dummy_attr;
+
+ /* TODO: check if routemap may be setting stuff */
+ ret = route_map_apply (rmap, &rn->p, RMAP_BGP, &info_s);
- ret = route_map_apply (rmap, &rn->p, RMAP_BGP, &binfo);
-
bgp_attr_extra_free (&dummy_attr);
-
+
if (ret == RMAP_DENYMATCH)
continue;
}
@@ -6175,7 +6969,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router
if (header)
{
- vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (*router_id), VTY_NEWLINE);
+ vty_out (vty, "BGP table version is 0, local router ID is %s%s", safe_inet_ntoa (*router_id), VTY_NEWLINE);
vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
if (type == bgp_show_type_dampend_paths
@@ -6264,8 +7058,8 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
struct prefix *p;
struct peer *peer;
struct listnode *node, *nnode;
- char buf1[INET6_ADDRSTRLEN];
- char buf2[INET6_ADDRSTRLEN];
+ char buf[SU_ADDRSTRLEN];
+ char buf_rd[RD_ADDRSTRLEN];
int count = 0;
int best = 0;
int suppress = 0;
@@ -6277,12 +7071,12 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
p = &rn->p;
vty_out (vty, "BGP routing table entry for %s%s%s/%d%s",
(safi == SAFI_MPLS_VPN ?
- prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""),
+ prefix_rd2str (prd, buf_rd, sizeof(buf_rd)) : ""),
safi == SAFI_MPLS_VPN ? ":" : "",
- inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN),
+ inet_ntop (p->family, &p->u.prefix, buf, sizeof(buf)),
p->prefixlen, VTY_NEWLINE);
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
count++;
if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED))
@@ -6327,8 +7121,9 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
if (bgp_adj_out_lookup (peer, p, afi, safi, rn))
{
if (! first)
- vty_out (vty, " Advertised to non peer-group peers:%s ", VTY_NEWLINE);
- vty_out (vty, " %s", sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN));
+ vty_out (vty, " Advertised to non peer-group peers:%s ",
+ VTY_NEWLINE);
+ vty_out (vty, " %s", sutoa(&peer->su).str);
first = 1;
}
}
@@ -6339,7 +7134,7 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
/* Display specified route of BGP table. */
static int
-bgp_show_route_in_table (struct vty *vty, struct bgp *bgp,
+bgp_show_route_in_table (struct vty *vty, struct bgp *bgp,
struct bgp_table *rib, const char *ip_str,
afi_t afi, safi_t safi, struct prefix_rd *prd,
int prefix_check)
@@ -6357,7 +7152,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp,
ret = str2prefix (ip_str, &match);
if (! ret)
{
- vty_out (vty, "address is malformed%s", VTY_NEWLINE);
+ vty_out (vty, "%% address is malformed%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -6376,23 +7171,22 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp,
if ((rm = bgp_node_match (table, &match)) != NULL)
{
- if (prefix_check && rm->p.prefixlen != match.prefixlen)
- {
- bgp_unlock_node (rm);
- continue;
- }
-
- for (ri = rm->info; ri; ri = ri->next)
+ if (prefix_check && rm->p.prefixlen == match.prefixlen)
{
- if (header)
+ for (ri = rm->info; ri; ri = ri->info_next)
{
- route_vty_out_detail_header (vty, bgp, rm, (struct prefix_rd *)&rn->p,
- AFI_IP, SAFI_MPLS_VPN);
-
- header = 0;
+ if (header)
+ {
+ route_vty_out_detail_header (vty, bgp, rm,
+ (struct prefix_rd *)&rn->p,
+ AFI_IP, SAFI_MPLS_VPN);
+
+ header = 0;
+ }
+ display++;
+ route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP,
+ SAFI_MPLS_VPN);
}
- display++;
- route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP, SAFI_MPLS_VPN);
}
bgp_unlock_node (rm);
@@ -6408,7 +7202,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp,
{
if (! prefix_check || rn->p.prefixlen == match.prefixlen)
{
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
if (header)
{
@@ -6460,8 +7254,8 @@ bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str,
return CMD_WARNING;
}
}
-
- return bgp_show_route_in_table (vty, bgp, bgp->rib[afi][safi], ip_str,
+
+ return bgp_show_route_in_table (vty, bgp, bgp->rib[afi][safi], ip_str,
afi, safi, prd, prefix_check);
}
@@ -6489,7 +7283,7 @@ DEFUN (show_ip_bgp_ipv4,
if (strncmp (argv[0], "m", 1) == 0)
return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, bgp_show_type_normal,
NULL);
-
+
return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL);
}
@@ -6853,7 +7647,7 @@ DEFUN (show_bgp_view,
vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
-
+
return bgp_show (vty, bgp, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL);
}
@@ -6861,11 +7655,11 @@ ALIAS (show_bgp_view,
show_bgp_view_ipv6_cmd,
"show bgp view WORD ipv6",
SHOW_STR
- BGP_STR
+ BGP_STR
"BGP view\n"
"View name\n"
"Address family\n")
-
+
DEFUN (show_bgp_view_route,
show_bgp_view_route_cmd,
"show bgp view WORD X:X::X:X",
@@ -6894,10 +7688,10 @@ DEFUN (show_bgp_view_prefix,
SHOW_STR
BGP_STR
"BGP view\n"
- "View name\n"
+ "View name\n"
"IPv6 prefix <network>/<length>\n")
{
- return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1);
+ return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1);
}
ALIAS (show_bgp_view_prefix,
@@ -6908,7 +7702,7 @@ ALIAS (show_bgp_view_prefix,
"BGP view\n"
"View name\n"
"Address family\n"
- "IPv6 prefix <network>/<length>\n")
+ "IPv6 prefix <network>/<length>\n")
/* old command */
DEFUN (show_ipv6_mbgp,
@@ -6946,10 +7740,10 @@ DEFUN (show_ipv6_mbgp_prefix,
return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1);
}
#endif
-
+
static int
-bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi,
+bgp_show_regexp (struct vty *vty, int argc, argv_t argv, afi_t afi,
safi_t safi, enum bgp_show_type type)
{
int i;
@@ -6958,7 +7752,7 @@ bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi,
int first;
regex_t *regex;
int rc;
-
+
first = 0;
b = buffer_new (1024);
for (i = 0; i < argc; i++)
@@ -6993,7 +7787,7 @@ bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi,
return rc;
}
-DEFUN (show_ip_bgp_regexp,
+DEFUN (show_ip_bgp_regexp,
show_ip_bgp_regexp_cmd,
"show ip bgp regexp .LINE",
SHOW_STR
@@ -7006,7 +7800,7 @@ DEFUN (show_ip_bgp_regexp,
bgp_show_type_regexp);
}
-DEFUN (show_ip_bgp_flap_regexp,
+DEFUN (show_ip_bgp_flap_regexp,
show_ip_bgp_flap_regexp_cmd,
"show ip bgp flap-statistics regexp .LINE",
SHOW_STR
@@ -7020,7 +7814,7 @@ DEFUN (show_ip_bgp_flap_regexp,
bgp_show_type_flap_regexp);
}
-DEFUN (show_ip_bgp_ipv4_regexp,
+DEFUN (show_ip_bgp_ipv4_regexp,
show_ip_bgp_ipv4_regexp_cmd,
"show ip bgp ipv4 (unicast|multicast) regexp .LINE",
SHOW_STR
@@ -7041,7 +7835,7 @@ DEFUN (show_ip_bgp_ipv4_regexp,
}
#ifdef HAVE_IPV6
-DEFUN (show_bgp_regexp,
+DEFUN (show_bgp_regexp,
show_bgp_regexp_cmd,
"show bgp regexp .LINE",
SHOW_STR
@@ -7053,7 +7847,7 @@ DEFUN (show_bgp_regexp,
bgp_show_type_regexp);
}
-ALIAS (show_bgp_regexp,
+ALIAS (show_bgp_regexp,
show_bgp_ipv6_regexp_cmd,
"show bgp ipv6 regexp .LINE",
SHOW_STR
@@ -7063,7 +7857,7 @@ ALIAS (show_bgp_regexp,
"A regular-expression to match the BGP AS paths\n")
/* old command */
-DEFUN (show_ipv6_bgp_regexp,
+DEFUN (show_ipv6_bgp_regexp,
show_ipv6_bgp_regexp_cmd,
"show ipv6 bgp regexp .LINE",
SHOW_STR
@@ -7077,7 +7871,7 @@ DEFUN (show_ipv6_bgp_regexp,
}
/* old command */
-DEFUN (show_ipv6_mbgp_regexp,
+DEFUN (show_ipv6_mbgp_regexp,
show_ipv6_mbgp_regexp_cmd,
"show ipv6 mbgp regexp .LINE",
SHOW_STR
@@ -7090,7 +7884,7 @@ DEFUN (show_ipv6_mbgp_regexp,
bgp_show_type_regexp);
}
#endif /* HAVE_IPV6 */
-
+
static int
bgp_show_prefix_list (struct vty *vty, const char *prefix_list_str, afi_t afi,
safi_t safi, enum bgp_show_type type)
@@ -7101,14 +7895,14 @@ bgp_show_prefix_list (struct vty *vty, const char *prefix_list_str, afi_t afi,
if (plist == NULL)
{
vty_out (vty, "%% %s is not a valid prefix-list name%s",
- prefix_list_str, VTY_NEWLINE);
+ prefix_list_str, VTY_NEWLINE);
return CMD_WARNING;
}
return bgp_show (vty, NULL, afi, safi, type, plist);
}
-DEFUN (show_ip_bgp_prefix_list,
+DEFUN (show_ip_bgp_prefix_list,
show_ip_bgp_prefix_list_cmd,
"show ip bgp prefix-list WORD",
SHOW_STR
@@ -7121,7 +7915,7 @@ DEFUN (show_ip_bgp_prefix_list,
bgp_show_type_prefix_list);
}
-DEFUN (show_ip_bgp_flap_prefix_list,
+DEFUN (show_ip_bgp_flap_prefix_list,
show_ip_bgp_flap_prefix_list_cmd,
"show ip bgp flap-statistics prefix-list WORD",
SHOW_STR
@@ -7135,7 +7929,7 @@ DEFUN (show_ip_bgp_flap_prefix_list,
bgp_show_type_flap_prefix_list);
}
-DEFUN (show_ip_bgp_ipv4_prefix_list,
+DEFUN (show_ip_bgp_ipv4_prefix_list,
show_ip_bgp_ipv4_prefix_list_cmd,
"show ip bgp ipv4 (unicast|multicast) prefix-list WORD",
SHOW_STR
@@ -7156,7 +7950,7 @@ DEFUN (show_ip_bgp_ipv4_prefix_list,
}
#ifdef HAVE_IPV6
-DEFUN (show_bgp_prefix_list,
+DEFUN (show_bgp_prefix_list,
show_bgp_prefix_list_cmd,
"show bgp prefix-list WORD",
SHOW_STR
@@ -7168,7 +7962,7 @@ DEFUN (show_bgp_prefix_list,
bgp_show_type_prefix_list);
}
-ALIAS (show_bgp_prefix_list,
+ALIAS (show_bgp_prefix_list,
show_bgp_ipv6_prefix_list_cmd,
"show bgp ipv6 prefix-list WORD",
SHOW_STR
@@ -7178,7 +7972,7 @@ ALIAS (show_bgp_prefix_list,
"IPv6 prefix-list name\n")
/* old command */
-DEFUN (show_ipv6_bgp_prefix_list,
+DEFUN (show_ipv6_bgp_prefix_list,
show_ipv6_bgp_prefix_list_cmd,
"show ipv6 bgp prefix-list WORD",
SHOW_STR
@@ -7192,7 +7986,7 @@ DEFUN (show_ipv6_bgp_prefix_list,
}
/* old command */
-DEFUN (show_ipv6_mbgp_prefix_list,
+DEFUN (show_ipv6_mbgp_prefix_list,
show_ipv6_mbgp_prefix_list_cmd,
"show ipv6 mbgp prefix-list WORD",
SHOW_STR
@@ -7205,7 +7999,7 @@ DEFUN (show_ipv6_mbgp_prefix_list,
bgp_show_type_prefix_list);
}
#endif /* HAVE_IPV6 */
-
+
static int
bgp_show_filter_list (struct vty *vty, const char *filter, afi_t afi,
safi_t safi, enum bgp_show_type type)
@@ -7215,14 +8009,14 @@ bgp_show_filter_list (struct vty *vty, const char *filter, afi_t afi,
as_list = as_list_lookup (filter);
if (as_list == NULL)
{
- vty_out (vty, "%% %s is not a valid AS-path access-list name%s", filter, VTY_NEWLINE);
+ vty_out (vty, "%% %s is not a valid AS-path access-list name%s", filter, VTY_NEWLINE);
return CMD_WARNING;
}
return bgp_show (vty, NULL, afi, safi, type, as_list);
}
-DEFUN (show_ip_bgp_filter_list,
+DEFUN (show_ip_bgp_filter_list,
show_ip_bgp_filter_list_cmd,
"show ip bgp filter-list WORD",
SHOW_STR
@@ -7235,7 +8029,7 @@ DEFUN (show_ip_bgp_filter_list,
bgp_show_type_filter_list);
}
-DEFUN (show_ip_bgp_flap_filter_list,
+DEFUN (show_ip_bgp_flap_filter_list,
show_ip_bgp_flap_filter_list_cmd,
"show ip bgp flap-statistics filter-list WORD",
SHOW_STR
@@ -7249,7 +8043,7 @@ DEFUN (show_ip_bgp_flap_filter_list,
bgp_show_type_flap_filter_list);
}
-DEFUN (show_ip_bgp_ipv4_filter_list,
+DEFUN (show_ip_bgp_ipv4_filter_list,
show_ip_bgp_ipv4_filter_list_cmd,
"show ip bgp ipv4 (unicast|multicast) filter-list WORD",
SHOW_STR
@@ -7264,13 +8058,13 @@ DEFUN (show_ip_bgp_ipv4_filter_list,
if (strncmp (argv[0], "m", 1) == 0)
return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_MULTICAST,
bgp_show_type_filter_list);
-
+
return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_UNICAST,
bgp_show_type_filter_list);
}
#ifdef HAVE_IPV6
-DEFUN (show_bgp_filter_list,
+DEFUN (show_bgp_filter_list,
show_bgp_filter_list_cmd,
"show bgp filter-list WORD",
SHOW_STR
@@ -7282,7 +8076,7 @@ DEFUN (show_bgp_filter_list,
bgp_show_type_filter_list);
}
-ALIAS (show_bgp_filter_list,
+ALIAS (show_bgp_filter_list,
show_bgp_ipv6_filter_list_cmd,
"show bgp ipv6 filter-list WORD",
SHOW_STR
@@ -7292,7 +8086,7 @@ ALIAS (show_bgp_filter_list,
"Regular expression access list name\n")
/* old command */
-DEFUN (show_ipv6_bgp_filter_list,
+DEFUN (show_ipv6_bgp_filter_list,
show_ipv6_bgp_filter_list_cmd,
"show ipv6 bgp filter-list WORD",
SHOW_STR
@@ -7306,7 +8100,7 @@ DEFUN (show_ipv6_bgp_filter_list,
}
/* old command */
-DEFUN (show_ipv6_mbgp_filter_list,
+DEFUN (show_ipv6_mbgp_filter_list,
show_ipv6_mbgp_filter_list_cmd,
"show ipv6 mbgp filter-list WORD",
SHOW_STR
@@ -7319,7 +8113,7 @@ DEFUN (show_ipv6_mbgp_filter_list,
bgp_show_type_filter_list);
}
#endif /* HAVE_IPV6 */
-
+
static int
bgp_show_route_map (struct vty *vty, const char *rmap_str, afi_t afi,
safi_t safi, enum bgp_show_type type)
@@ -7330,14 +8124,14 @@ bgp_show_route_map (struct vty *vty, const char *rmap_str, afi_t afi,
if (! rmap)
{
vty_out (vty, "%% %s is not a valid route-map name%s",
- rmap_str, VTY_NEWLINE);
+ rmap_str, VTY_NEWLINE);
return CMD_WARNING;
}
return bgp_show (vty, NULL, afi, safi, type, rmap);
}
-DEFUN (show_ip_bgp_route_map,
+DEFUN (show_ip_bgp_route_map,
show_ip_bgp_route_map_cmd,
"show ip bgp route-map WORD",
SHOW_STR
@@ -7350,7 +8144,7 @@ DEFUN (show_ip_bgp_route_map,
bgp_show_type_route_map);
}
-DEFUN (show_ip_bgp_flap_route_map,
+DEFUN (show_ip_bgp_flap_route_map,
show_ip_bgp_flap_route_map_cmd,
"show ip bgp flap-statistics route-map WORD",
SHOW_STR
@@ -7364,7 +8158,7 @@ DEFUN (show_ip_bgp_flap_route_map,
bgp_show_type_flap_route_map);
}
-DEFUN (show_ip_bgp_ipv4_route_map,
+DEFUN (show_ip_bgp_ipv4_route_map,
show_ip_bgp_ipv4_route_map_cmd,
"show ip bgp ipv4 (unicast|multicast) route-map WORD",
SHOW_STR
@@ -7384,7 +8178,7 @@ DEFUN (show_ip_bgp_ipv4_route_map,
bgp_show_type_route_map);
}
-DEFUN (show_bgp_route_map,
+DEFUN (show_bgp_route_map,
show_bgp_route_map_cmd,
"show bgp route-map WORD",
SHOW_STR
@@ -7396,7 +8190,7 @@ DEFUN (show_bgp_route_map,
bgp_show_type_route_map);
}
-ALIAS (show_bgp_route_map,
+ALIAS (show_bgp_route_map,
show_bgp_ipv6_route_map_cmd,
"show bgp ipv6 route-map WORD",
SHOW_STR
@@ -7404,7 +8198,7 @@ ALIAS (show_bgp_route_map,
"Address family\n"
"Display routes matching the route-map\n"
"A route-map to match on\n")
-
+
DEFUN (show_ip_bgp_cidr_only,
show_ip_bgp_cidr_only_cmd,
"show ip bgp cidr-only",
@@ -7448,7 +8242,7 @@ DEFUN (show_ip_bgp_ipv4_cidr_only,
return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
bgp_show_type_cidr_only, NULL);
}
-
+
DEFUN (show_ip_bgp_community_all,
show_ip_bgp_community_all_cmd,
"show ip bgp community",
@@ -7475,7 +8269,7 @@ DEFUN (show_ip_bgp_ipv4_community_all,
if (strncmp (argv[0], "m", 1) == 0)
return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
bgp_show_type_community_all, NULL);
-
+
return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
bgp_show_type_community_all, NULL);
}
@@ -7526,10 +8320,11 @@ DEFUN (show_ipv6_mbgp_community_all,
bgp_show_type_community_all, NULL);
}
#endif /* HAVE_IPV6 */
-
+
static int
-bgp_show_community (struct vty *vty, const char *view_name, int argc,
- const char **argv, int exact, afi_t afi, safi_t safi)
+bgp_show_community (struct vty *vty, const char *view_name,
+ int argc, argv_t argv,
+ int exact, afi_t afi, safi_t safi)
{
struct community *com;
struct buffer *b;
@@ -7543,19 +8338,19 @@ bgp_show_community (struct vty *vty, const char *view_name, int argc,
{
bgp = bgp_lookup_by_name (view_name);
if (bgp == NULL)
- {
- vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
- return CMD_WARNING;
- }
+ {
+ vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
}
else
{
bgp = bgp_get_default ();
if (bgp == NULL)
- {
- vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ {
+ vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
}
b = buffer_new (1024);
@@ -7565,11 +8360,12 @@ bgp_show_community (struct vty *vty, const char *view_name, int argc,
buffer_putc (b, ' ');
else
{
- if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0))
+ if ( (strcmp (argv[i], "unicast") == 0)
+ || (strcmp (argv[i], "multicast") == 0) )
continue;
first = 1;
}
-
+
buffer_putstr (b, argv[i]);
}
buffer_putc (b, '\0');
@@ -7620,7 +8416,7 @@ ALIAS (show_ip_bgp_community,
"Do not send outside local AS (well-known community)\n"
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
-
+
ALIAS (show_ip_bgp_community,
show_ip_bgp_community3_cmd,
"show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
@@ -7640,7 +8436,7 @@ ALIAS (show_ip_bgp_community,
"Do not send outside local AS (well-known community)\n"
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
-
+
ALIAS (show_ip_bgp_community,
show_ip_bgp_community4_cmd,
"show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
@@ -7680,10 +8476,11 @@ DEFUN (show_ip_bgp_ipv4_community,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
{
- if (strncmp (argv[0], "m", 1) == 0)
- return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST);
-
- return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+ safi_t safi ;
+
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST ;
+
+ return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, safi);
}
ALIAS (show_ip_bgp_ipv4_community,
@@ -7704,7 +8501,7 @@ ALIAS (show_ip_bgp_ipv4_community,
"Do not send outside local AS (well-known community)\n"
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
-
+
ALIAS (show_ip_bgp_ipv4_community,
show_ip_bgp_ipv4_community3_cmd,
"show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
@@ -7727,7 +8524,7 @@ ALIAS (show_ip_bgp_ipv4_community,
"Do not send outside local AS (well-known community)\n"
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
-
+
ALIAS (show_ip_bgp_ipv4_community,
show_ip_bgp_ipv4_community4_cmd,
"show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
@@ -8021,10 +8818,11 @@ DEFUN (show_ip_bgp_ipv4_community_exact,
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
{
- if (strncmp (argv[0], "m", 1) == 0)
- return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST);
-
- return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+ safi_t safi ;
+
+ safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST ;
+
+ return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, safi);
}
ALIAS (show_ip_bgp_ipv4_community_exact,
@@ -8070,7 +8868,7 @@ ALIAS (show_ip_bgp_ipv4_community_exact,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
-
+
ALIAS (show_ip_bgp_ipv4_community_exact,
show_ip_bgp_ipv4_community4_exact_cmd,
"show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
@@ -8156,7 +8954,7 @@ ALIAS (show_bgp_community,
"Do not send outside local AS (well-known community)\n"
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n")
-
+
ALIAS (show_bgp_community,
show_bgp_community3_cmd,
"show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
@@ -8447,7 +9245,7 @@ ALIAS (show_bgp_community_exact,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
-
+
ALIAS (show_bgp_community_exact,
show_bgp_ipv6_community4_exact_cmd,
"show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
@@ -8555,7 +9353,7 @@ ALIAS (show_ipv6_bgp_community_exact,
"Do not advertise to any peer (well-known community)\n"
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
-
+
/* old command */
DEFUN (show_ipv6_mbgp_community,
show_ipv6_mbgp_community_cmd,
@@ -8718,7 +9516,7 @@ ALIAS (show_ipv6_mbgp_community_exact,
"Do not export to next AS (well-known community)\n"
"Exact match of the communities")
#endif /* HAVE_IPV6 */
-
+
static int
bgp_show_community_list (struct vty *vty, const char *com, int exact,
afi_t afi, safi_t safi)
@@ -8766,7 +9564,7 @@ DEFUN (show_ip_bgp_ipv4_community_list,
{
if (strncmp (argv[0], "m", 1) == 0)
return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_MULTICAST);
-
+
return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_UNICAST);
}
@@ -8800,7 +9598,7 @@ DEFUN (show_ip_bgp_ipv4_community_list_exact,
{
if (strncmp (argv[0], "m", 1) == 0)
return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_MULTICAST);
-
+
return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_UNICAST);
}
@@ -8905,7 +9703,7 @@ DEFUN (show_ipv6_mbgp_community_list_exact,
return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_MULTICAST);
}
#endif /* HAVE_IPV6 */
-
+
static int
bgp_show_prefix_longer (struct vty *vty, const char *prefix, afi_t afi,
safi_t safi, enum bgp_show_type type)
@@ -9051,7 +9849,7 @@ DEFUN (show_ipv6_mbgp_prefix_longer,
#endif /* HAVE_IPV6 */
static struct peer *
-peer_lookup_in_view (struct vty *vty, const char *view_name,
+peer_lookup_in_view (struct vty *vty, const char *view_name,
const char *ip_str)
{
int ret;
@@ -9067,7 +9865,7 @@ peer_lookup_in_view (struct vty *vty, const char *view_name,
{
vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
return NULL;
- }
+ }
}
else
{
@@ -9079,7 +9877,7 @@ peer_lookup_in_view (struct vty *vty, const char *view_name,
}
}
- /* Get peer sockunion. */
+ /* Get peer sockunion. */
ret = str2sockunion (ip_str, &su);
if (ret < 0)
{
@@ -9094,10 +9892,10 @@ peer_lookup_in_view (struct vty *vty, const char *view_name,
vty_out (vty, "No such neighbor%s", VTY_NEWLINE);
return NULL;
}
-
+
return peer;
}
-
+
enum bgp_stats
{
BGP_STATS_MAXBITLEN = 0,
@@ -9149,7 +9947,7 @@ ravg_tally (unsigned long count, unsigned long oldavg, unsigned long newval)
unsigned long newtot = (count-1) * oldavg + (newval * TALLY_SIGFIG);
unsigned long res = (newtot * TALLY_SIGFIG) / count;
unsigned long ret = newtot / count;
-
+
if ((res % TALLY_SIGFIG) > (TALLY_SIGFIG/2))
return ret + 1;
else
@@ -9164,7 +9962,7 @@ bgp_table_stats_walker (struct thread *t)
struct bgp_node *top;
struct bgp_table_stats *ts = THREAD_ARG (t);
unsigned int space = 0;
-
+
if (!(top = bgp_table_top (ts->table)))
return 0;
@@ -9177,7 +9975,7 @@ bgp_table_stats_walker (struct thread *t)
space = IPV6_MAX_BITLEN;
break;
}
-
+
ts->counts[BGP_STATS_MAXBITLEN] = space;
for (rn = top; rn; rn = bgp_route_next (rn))
@@ -9185,13 +9983,13 @@ bgp_table_stats_walker (struct thread *t)
struct bgp_info *ri;
struct bgp_node *prn = rn->parent;
unsigned int rinum = 0;
-
+
if (rn == top)
continue;
-
+
if (!rn->info)
continue;
-
+
ts->counts[BGP_STATS_PREFIXES]++;
ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen;
@@ -9201,11 +9999,11 @@ bgp_table_stats_walker (struct thread *t)
ts->counts[BGP_STATS_AVGPLEN],
rn->p.prefixlen);
#endif
-
+
/* check if the prefix is included by any other announcements */
while (prn && !prn->info)
prn = prn->parent;
-
+
if (prn == NULL || prn == top)
{
ts->counts[BGP_STATS_UNAGGREGATEABLE]++;
@@ -9215,36 +10013,36 @@ bgp_table_stats_walker (struct thread *t)
}
else if (prn->info)
ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++;
-
- for (ri = rn->info; ri; ri = ri->next)
+
+ for (ri = rn->info; ri; ri = ri->info_next)
{
rinum++;
ts->counts[BGP_STATS_RIB]++;
-
+
if (ri->attr &&
(CHECK_FLAG (ri->attr->flag,
ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))))
ts->counts[BGP_STATS_AGGREGATES]++;
-
+
/* as-path stats */
if (ri->attr && ri->attr->aspath)
{
unsigned int hops = aspath_count_hops (ri->attr->aspath);
unsigned int size = aspath_size (ri->attr->aspath);
as_t highest = aspath_highest (ri->attr->aspath);
-
+
ts->counts[BGP_STATS_ASPATH_COUNT]++;
-
+
if (hops > ts->counts[BGP_STATS_ASPATH_MAXHOPS])
ts->counts[BGP_STATS_ASPATH_MAXHOPS] = hops;
-
+
if (size > ts->counts[BGP_STATS_ASPATH_MAXSIZE])
ts->counts[BGP_STATS_ASPATH_MAXSIZE] = size;
-
+
ts->counts[BGP_STATS_ASPATH_TOTHOPS] += hops;
ts->counts[BGP_STATS_ASPATH_TOTSIZE] += size;
#if 0
- ts->counts[BGP_STATS_ASPATH_AVGHOPS]
+ ts->counts[BGP_STATS_ASPATH_AVGHOPS]
= ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT],
ts->counts[BGP_STATS_ASPATH_AVGHOPS],
hops);
@@ -9266,25 +10064,25 @@ bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi)
{
struct bgp_table_stats ts;
unsigned int i;
-
+
if (!bgp->rib[afi][safi])
{
vty_out (vty, "%% No RIB exist for the AFI/SAFI%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
memset (&ts, 0, sizeof (ts));
ts.table = bgp->rib[afi][safi];
thread_execute (bm->master, bgp_table_stats_walker, &ts, 0);
vty_out (vty, "BGP %s RIB statistics%s%s",
afi_safi_print (afi, safi), VTY_NEWLINE, VTY_NEWLINE);
-
+
for (i = 0; i < BGP_STATS_MAX; i++)
{
if (!table_stats_strs[i])
continue;
-
+
switch (i)
{
#if 0
@@ -9301,7 +10099,7 @@ bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi)
vty_out (vty, "%-30s: ", table_stats_strs[i]);
vty_out (vty, "%12.2f",
ts.counts[i] ?
- (float)ts.counts[i] /
+ (float)ts.counts[i] /
(float)ts.counts[BGP_STATS_ASPATH_COUNT]
: 0);
break;
@@ -9309,7 +10107,7 @@ bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi)
vty_out (vty, "%-30s: ", table_stats_strs[i]);
vty_out (vty, "%12.2f",
ts.counts[i] ?
- (float)ts.counts[i] /
+ (float)ts.counts[i] /
(float)ts.counts[BGP_STATS_PREFIXES]
: 0);
break;
@@ -9319,27 +10117,27 @@ bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi)
if (ts.counts[BGP_STATS_MAXBITLEN] < 9)
break;
vty_out (vty, "%30s: ", "%% announced ");
- vty_out (vty, "%12.2f%s",
- 100 * (float)ts.counts[BGP_STATS_SPACE] /
+ vty_out (vty, "%12.2f%s",
+ 100 * (float)ts.counts[BGP_STATS_SPACE] /
(float)((uint64_t)1UL << ts.counts[BGP_STATS_MAXBITLEN]),
VTY_NEWLINE);
vty_out (vty, "%30s: ", "/8 equivalent ");
- vty_out (vty, "%12.2f%s",
- (float)ts.counts[BGP_STATS_SPACE] /
+ vty_out (vty, "%12.2f%s",
+ (float)ts.counts[BGP_STATS_SPACE] /
(float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 8)),
VTY_NEWLINE);
if (ts.counts[BGP_STATS_MAXBITLEN] < 25)
break;
vty_out (vty, "%30s: ", "/24 equivalent ");
- vty_out (vty, "%12.2f",
- (float)ts.counts[BGP_STATS_SPACE] /
+ vty_out (vty, "%12.2f",
+ (float)ts.counts[BGP_STATS_SPACE] /
(float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 24)));
break;
default:
vty_out (vty, "%-30s: ", table_stats_strs[i]);
vty_out (vty, "%12llu", ts.counts[i]);
}
-
+
vty_out (vty, "%s", VTY_NEWLINE);
}
return CMD_SUCCESS;
@@ -9352,7 +10150,7 @@ bgp_table_stats_vty (struct vty *vty, const char *name,
struct bgp *bgp;
afi_t afi;
safi_t safi;
-
+
if (name)
bgp = bgp_lookup_by_name (name);
else
@@ -9454,7 +10252,7 @@ ALIAS (show_bgp_statistics_view,
"Address family\n"
"Address Family modifier\n"
"BGP RIB advertisement statistics\n")
-
+
enum bgp_pcounts
{
PCOUNT_ADJ_IN = 0,
@@ -9496,25 +10294,25 @@ bgp_peer_count_walker (struct thread *t)
struct bgp_node *rn;
struct peer_pcounts *pc = THREAD_ARG (t);
const struct peer *peer = pc->peer;
-
+
for (rn = bgp_table_top (pc->table); rn; rn = bgp_route_next (rn))
{
struct bgp_adj_in *ain;
struct bgp_info *ri;
-
- for (ain = rn->adj_in; ain; ain = ain->next)
+
+ for (ain = rn->adj_in; ain; ain = ain->adj_next)
if (ain->peer == peer)
pc->count[PCOUNT_ADJ_IN]++;
- for (ri = rn->info; ri; ri = ri->next)
+ for (ri = rn->info; ri; ri = ri->info_next)
{
char buf[SU_ADDRSTRLEN];
-
+
if (ri->peer != peer)
continue;
-
+
pc->count[PCOUNT_ALL]++;
-
+
if (CHECK_FLAG (ri->flags, BGP_INFO_DAMPED))
pc->count[PCOUNT_DAMPED]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
@@ -9527,7 +10325,7 @@ bgp_peer_count_walker (struct thread *t)
pc->count[PCOUNT_VALID]++;
if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
pc->count[PCOUNT_PFCNT]++;
-
+
if (CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
{
pc->count[PCOUNT_COUNTED]++;
@@ -9561,28 +10359,28 @@ bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi)
{
struct peer_pcounts pcounts = { .peer = peer };
unsigned int i;
-
+
if (!peer || !peer->bgp || !peer->afc[afi][safi]
|| !peer->bgp->rib[afi][safi])
{
vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
memset (&pcounts, 0, sizeof(pcounts));
pcounts.peer = peer;
pcounts.table = peer->bgp->rib[afi][safi];
-
+
/* in-place call via thread subsystem so as to record execution time
* stats for the thread-walk (i.e. ensure this can't be blamed on
* on just vty_read()).
*/
thread_execute (bm->master, bgp_peer_count_walker, &pcounts, 0);
-
- vty_out (vty, "Prefix counts for %s, %s%s",
+
+ vty_out (vty, "Prefix counts for %s, %s%s",
peer->host, afi_safi_print (afi, safi), VTY_NEWLINE);
vty_out (vty, "PfxCt: %ld%s", peer->pcount[afi][safi], VTY_NEWLINE);
- vty_out (vty, "%sCounts from RIB table walk:%s%s",
+ vty_out (vty, "%sCounts from RIB table walk:%s%s",
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
for (i = 0; i < PCOUNT_MAX; i++)
@@ -9596,7 +10394,7 @@ bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi)
vty_out (vty, "Please report this bug, with the above command output%s",
VTY_NEWLINE);
}
-
+
return CMD_SUCCESS;
}
@@ -9613,10 +10411,10 @@ DEFUN (show_ip_bgp_neighbor_prefix_counts,
{
struct peer *peer;
- peer = peer_lookup_in_view (vty, NULL, argv[0]);
- if (! peer)
+ peer = peer_lookup_in_view (vty, NULL, argv[0]);
+ if (! peer)
return CMD_WARNING;
-
+
return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST);
}
@@ -9633,10 +10431,10 @@ DEFUN (show_bgp_ipv6_neighbor_prefix_counts,
{
struct peer *peer;
- peer = peer_lookup_in_view (vty, NULL, argv[0]);
- if (! peer)
+ peer = peer_lookup_in_view (vty, NULL, argv[0]);
+ if (! peer)
return CMD_WARNING;
-
+
return bgp_peer_counts (vty, peer, AFI_IP6, SAFI_UNICAST);
}
@@ -9685,7 +10483,7 @@ DEFUN (show_ip_bgp_vpnv4_neighbor_prefix_counts,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
return CMD_WARNING;
-
+
return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MPLS_VPN);
}
@@ -9711,11 +10509,11 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
table = bgp->rib[afi][safi];
output_count = 0;
-
+
if (! in && CHECK_FLAG (peer->af_sflags[afi][safi],
PEER_STATUS_DEFAULT_ORIGINATE))
{
- vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE);
+ vty_out (vty, "BGP table version is 0, local router ID is %s%s", safe_inet_ntoa (bgp->router_id), VTY_NEWLINE);
vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
@@ -9727,12 +10525,12 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
if (in)
{
- for (ain = rn->adj_in; ain; ain = ain->next)
+ for (ain = rn->adj_in; ain; ain = ain->adj_next)
if (ain->peer == peer)
{
if (header1)
{
- vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE);
+ vty_out (vty, "BGP table version is 0, local router ID is %s%s", safe_inet_ntoa (bgp->router_id), VTY_NEWLINE);
vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
header1 = 0;
@@ -9743,7 +10541,7 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
header2 = 0;
}
if (ain->attr)
- {
+ {
route_vty_out_tmp (vty, &rn->p, ain->attr, safi);
output_count++;
}
@@ -9751,12 +10549,12 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
}
else
{
- for (adj = rn->adj_out; adj; adj = adj->next)
+ for (adj = rn->adj_out; adj; adj = adj->adj_next)
if (adj->peer == peer)
{
if (header1)
{
- vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE);
+ vty_out (vty, "BGP table version is 0, local router ID is %s%s", safe_inet_ntoa (bgp->router_id), VTY_NEWLINE);
vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
header1 = 0;
@@ -9767,13 +10565,13 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
header2 = 0;
}
if (adj->attr)
- {
+ {
route_vty_out_tmp (vty, &rn->p, adj->attr, safi);
output_count++;
}
}
}
-
+
if (output_count != 0)
vty_out (vty, "%sTotal number of prefixes %ld%s",
VTY_NEWLINE, output_count, VTY_NEWLINE);
@@ -9781,7 +10579,7 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
static int
peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, int in)
-{
+{
if (! peer || ! peer->afc[afi][safi])
{
vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
@@ -9820,9 +10618,9 @@ DEFUN (show_ip_bgp_view_neighbor_advertised_route,
else
peer = peer_lookup_in_view (vty, NULL, argv[0]);
- if (! peer)
+ if (! peer)
return CMD_WARNING;
-
+
return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0);
}
@@ -9884,7 +10682,7 @@ DEFUN (show_bgp_view_neighbor_advertised_route,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
- return CMD_WARNING;
+ return CMD_WARNING;
return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0);
}
@@ -9949,7 +10747,7 @@ ALIAS (show_bgp_view_neighbor_advertised_route,
"Neighbor to display information about\n"
"Neighbor to display information about\n"
"Display the routes advertised to a BGP neighbor\n")
-
+
ALIAS (show_bgp_view_neighbor_advertised_route,
show_bgp_ipv6_neighbor_advertised_route_cmd,
"show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) advertised-routes",
@@ -9972,7 +10770,7 @@ ALIAS (show_bgp_view_neighbor_advertised_route,
"Neighbor to display information about\n"
"Neighbor to display information about\n"
"Display the routes advertised to a BGP neighbor\n")
-
+
/* old command */
DEFUN (ipv6_mbgp_neighbor_advertised_route,
ipv6_mbgp_neighbor_advertised_route_cmd,
@@ -9989,12 +10787,12 @@ DEFUN (ipv6_mbgp_neighbor_advertised_route,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
- return CMD_WARNING;
+ return CMD_WARNING;
return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 0);
}
#endif /* HAVE_IPV6 */
-
+
DEFUN (show_ip_bgp_view_neighbor_received_routes,
show_ip_bgp_view_neighbor_received_routes_cmd,
"show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes",
@@ -10051,7 +10849,7 @@ DEFUN (show_ip_bgp_ipv4_neighbor_received_routes,
peer = peer_lookup_in_view (vty, NULL, argv[1]);
if (! peer)
return CMD_WARNING;
-
+
if (strncmp (argv[0], "m", 1) == 0)
return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 1);
@@ -10182,7 +10980,7 @@ DEFUN (show_ip_bgp_ipv4_neighbor_received_prefix_filter,
prefix_bgp_show_prefix_list (vty, AFI_IP, name);
}
}
- else
+ else
{
sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_UNICAST);
count = prefix_bgp_show_prefix_list (NULL, AFI_IP, name);
@@ -10321,11 +11119,11 @@ DEFUN (show_bgp_view_neighbor_received_prefix_filter,
/* BGP structure lookup. */
bgp = bgp_lookup_by_name (argv[0]);
if (bgp == NULL)
- {
+ {
vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
-
+
su = sockunion_str2su (argv[1]);
if (su == NULL)
return CMD_WARNING;
@@ -10359,7 +11157,7 @@ ALIAS (show_bgp_view_neighbor_received_prefix_filter,
"Display information received from a BGP neighbor\n"
"Display the prefixlist filter\n")
#endif /* HAVE_IPV6 */
-
+
static int
bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi,
safi_t safi, enum bgp_show_type type)
@@ -10369,7 +11167,7 @@ bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi,
vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
return bgp_show (vty, peer->bgp, afi, safi, type, &peer->su);
}
@@ -10389,7 +11187,7 @@ DEFUN (show_ip_bgp_neighbor_routes,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
return CMD_WARNING;
-
+
return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
bgp_show_type_neighbor);
}
@@ -10410,7 +11208,7 @@ DEFUN (show_ip_bgp_neighbor_flap,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
return CMD_WARNING;
-
+
return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
bgp_show_type_flap_neighbor);
}
@@ -10431,7 +11229,7 @@ DEFUN (show_ip_bgp_neighbor_damp,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
return CMD_WARNING;
-
+
return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
bgp_show_type_damp_neighbor);
}
@@ -10455,7 +11253,7 @@ DEFUN (show_ip_bgp_ipv4_neighbor_routes,
peer = peer_lookup_in_view (vty, NULL, argv[1]);
if (! peer)
return CMD_WARNING;
-
+
if (strncmp (argv[0], "m", 1) == 0)
return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_MULTICAST,
bgp_show_type_neighbor);
@@ -10631,8 +11429,8 @@ DEFUN (show_ip_bgp_view_rsclient_route,
VTY_NEWLINE);
return CMD_WARNING;
}
-
- return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST],
+
+ return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST],
(argc == 3) ? argv[2] : argv[1],
AFI_IP, SAFI_UNICAST, NULL, 0);
}
@@ -10770,7 +11568,7 @@ DEFUN (show_ip_bgp_view_rsclient_prefix,
if (! peer)
return CMD_WARNING;
-
+
if (! peer->afc[AFI_IP][SAFI_UNICAST])
{
vty_out (vty, "%% Activate the neighbor for the address family first%s",
@@ -10785,8 +11583,8 @@ DEFUN (show_ip_bgp_view_rsclient_prefix,
VTY_NEWLINE);
return CMD_WARNING;
}
-
- return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST],
+
+ return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST],
(argc == 3) ? argv[2] : argv[1],
AFI_IP, SAFI_UNICAST, NULL, 1);
}
@@ -10901,7 +11699,7 @@ DEFUN (show_bgp_view_neighbor_routes,
peer = peer_lookup_in_view (vty, argv[0], argv[1]);
else
peer = peer_lookup_in_view (vty, NULL, argv[0]);
-
+
if (! peer)
return CMD_WARNING;
@@ -10999,7 +11797,7 @@ ALIAS (show_bgp_view_neighbor_flap,
"Neighbor to display information about\n"
"Neighbor to display information about\n"
"Display flap statistics of the routes learned from neighbor\n")
-
+
ALIAS (show_bgp_view_neighbor_routes,
show_bgp_neighbor_routes_cmd,
"show bgp neighbors (A.B.C.D|X:X::X:X) routes",
@@ -11051,7 +11849,7 @@ DEFUN (ipv6_mbgp_neighbor_routes,
peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
return CMD_WARNING;
-
+
return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_MULTICAST,
bgp_show_type_neighbor);
}
@@ -11511,7 +12309,7 @@ ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix,
"IP prefix <network>/<length>, e.g., 3ffe::/16\n")
#endif /* HAVE_IPV6 */
-
+
struct bgp_table *bgp_distance_table;
struct bgp_distance
@@ -11536,7 +12334,7 @@ bgp_distance_free (struct bgp_distance *bdistance)
}
static int
-bgp_distance_set (struct vty *vty, const char *distance_str,
+bgp_distance_set (struct vty *vty, const char *distance_str,
const char *ip_str, const char *access_list_str)
{
int ret;
@@ -11583,7 +12381,7 @@ bgp_distance_set (struct vty *vty, const char *distance_str,
}
static int
-bgp_distance_unset (struct vty *vty, const char *distance_str,
+bgp_distance_unset (struct vty *vty, const char *distance_str,
const char *ip_str, const char *access_list_str)
{
int ret;
@@ -11788,7 +12586,7 @@ DEFUN (no_bgp_distance_source_access_list,
bgp_distance_unset (vty, argv[0], argv[1], argv[2]);
return CMD_SUCCESS;
}
-
+
DEFUN (bgp_damp_set,
bgp_damp_set_cmd,
"bgp dampening <1-45> <1-20000> <1-20000> <1-255>",
@@ -11883,11 +12681,11 @@ DEFUN (show_ip_bgp_flap_statistics,
return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
bgp_show_type_flap_statistics, NULL);
}
-
+
/* Display specified route of BGP table. */
static int
-bgp_clear_damp_route (struct vty *vty, const char *view_name,
- const char *ip_str, afi_t afi, safi_t safi,
+bgp_clear_damp_route (struct vty *vty, const char *view_name,
+ const char *ip_str, afi_t afi, safi_t safi,
struct prefix_rd *prd, int prefix_check)
{
int ret;
@@ -11931,14 +12729,15 @@ bgp_clear_damp_route (struct vty *vty, const char *view_name,
if (safi == SAFI_MPLS_VPN)
{
- for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn))
+ for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn;
+ rn = bgp_route_next (rn))
{
if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
continue;
if ((table = rn->info) != NULL)
if ((rm = bgp_node_match (table, &match)) != NULL)
- {
+ {
if (! prefix_check || rm->p.prefixlen == match.prefixlen)
{
ri = rm->info;
@@ -11946,17 +12745,16 @@ bgp_clear_damp_route (struct vty *vty, const char *view_name,
{
if (ri->extra && ri->extra->damp_info)
{
- ri_temp = ri->next;
+ ri_temp = ri->info_next;
bgp_damp_info_free (ri->extra->damp_info, 1);
ri = ri_temp;
}
else
- ri = ri->next;
+ ri = ri->info_next;
}
}
-
bgp_unlock_node (rm);
- }
+ }
}
}
else
@@ -11970,15 +12768,14 @@ bgp_clear_damp_route (struct vty *vty, const char *view_name,
{
if (ri->extra && ri->extra->damp_info)
{
- ri_temp = ri->next;
+ ri_temp = ri->info_next;
bgp_damp_info_free (ri->extra->damp_info, 1);
ri = ri_temp;
}
else
- ri = ri->next;
+ ri = ri->info_next;
}
}
-
bgp_unlock_node (rn);
}
}
@@ -12047,7 +12844,7 @@ DEFUN (clear_ip_bgp_dampening_address_mask,
return bgp_clear_damp_route (vty, NULL, prefix_str, AFI_IP,
SAFI_UNICAST, NULL, 0);
}
-
+
static int
bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp,
afi_t afi, safi_t safi, int *write)
@@ -12061,11 +12858,11 @@ bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp,
u_int32_t label;
char buf[SU_ADDRSTRLEN];
char rdbuf[RD_ADDRSTRLEN];
-
+
/* Network configuration. */
for (prn = bgp_table_top (bgp->route[afi][safi]); prn; prn = bgp_route_next (prn))
if ((table = prn->info) != NULL)
- for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
if ((bgp_static = rn->info) != NULL)
{
p = &rn->p;
@@ -12079,7 +12876,7 @@ bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp,
label = decode_label (bgp_static->tag);
vty_out (vty, " network %s/%d rd %s tag %d",
- inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+ inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
p->prefixlen,
rdbuf, label);
vty_out (vty, "%s", VTY_NEWLINE);
@@ -12098,12 +12895,12 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp,
struct bgp_static *bgp_static;
struct bgp_aggregate *bgp_aggregate;
char buf[SU_ADDRSTRLEN];
-
+
if (afi == AFI_IP && safi == SAFI_MPLS_VPN)
return bgp_config_write_network_vpnv4 (vty, bgp, afi, safi, write);
/* Network configuration. */
- for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn))
+ for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn))
if ((bgp_static = rn->info) != NULL)
{
p = &rn->p;
@@ -12114,7 +12911,7 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp,
/* "network" configuration display. */
if (bgp_option_check (BGP_OPT_CONFIG_CISCO) && afi == AFI_IP)
{
- u_int32_t destination;
+ u_int32_t destination;
struct in_addr netmask;
destination = ntohl (p->u.prefix4.s_addr);
@@ -12130,18 +12927,18 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp,
/* Natural mask is not display. */
}
else
- vty_out (vty, " mask %s", inet_ntoa (netmask));
+ vty_out (vty, " mask %s", safe_inet_ntoa (netmask));
}
else
{
vty_out (vty, " network %s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+ inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
p->prefixlen);
}
if (bgp_static->rmap.name)
vty_out (vty, " route-map %s", bgp_static->rmap.name);
- else
+ else
{
if (bgp_static->backdoor)
vty_out (vty, " backdoor");
@@ -12166,7 +12963,7 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp,
masklen2ip (p->prefixlen, &netmask);
vty_out (vty, " aggregate-address %s %s",
inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
- inet_ntoa (netmask));
+ safe_inet_ntoa (netmask));
}
else
{
@@ -12177,7 +12974,7 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp,
if (bgp_aggregate->as_set)
vty_out (vty, " as-set");
-
+
if (bgp_aggregate->summary_only)
vty_out (vty, " summary-only");
@@ -12203,12 +13000,12 @@ bgp_config_write_distance (struct vty *vty, struct bgp *bgp)
vty_out (vty, " distance bgp %d %d %d%s",
bgp->distance_ebgp, bgp->distance_ibgp, bgp->distance_local,
VTY_NEWLINE);
-
+
for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn))
if ((bdistance = rn->info) != NULL)
{
vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance,
- inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+ safe_inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
bdistance->access_list ? bdistance->access_list : "",
VTY_NEWLINE);
}
@@ -12277,7 +13074,7 @@ bgp_route_init (void)
install_element (BGP_IPV4_NODE, &no_bgp_network_route_map_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_mask_route_map_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_route_map_cmd);
-
+
install_element (BGP_IPV4_NODE, &aggregate_address_cmd);
install_element (BGP_IPV4_NODE, &aggregate_address_mask_cmd);
install_element (BGP_IPV4_NODE, &aggregate_address_summary_only_cmd);
@@ -12312,6 +13109,7 @@ bgp_route_init (void)
install_element (BGP_IPV4M_NODE, &no_bgp_network_route_map_cmd);
install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_route_map_cmd);
install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_route_map_cmd);
+
install_element (BGP_IPV4M_NODE, &aggregate_address_cmd);
install_element (BGP_IPV4M_NODE, &aggregate_address_mask_cmd);
install_element (BGP_IPV4M_NODE, &aggregate_address_summary_only_cmd);
@@ -12423,7 +13221,7 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd);
install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_prefix_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd);
-
+
/* Restricted node: VIEW_NODE - (set of dangerous commands) */
install_element (RESTRICTED_NODE, &show_ip_bgp_route_cmd);
install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_route_cmd);
@@ -12666,16 +13464,16 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_bgp_view_neighbor_flap_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_flap_cmd);
install_element (VIEW_NODE, &show_bgp_view_neighbor_damp_cmd);
- install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd);
+ install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd);
install_element (VIEW_NODE, &show_bgp_view_rsclient_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd);
install_element (VIEW_NODE, &show_bgp_view_rsclient_route_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
install_element (VIEW_NODE, &show_bgp_view_rsclient_prefix_cmd);
install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
-
+
/* Restricted:
- * VIEW_NODE - (set of dangerous commands) - (commands dependent on prev)
+ * VIEW_NODE - (set of dangerous commands) - (commands dependent on prev)
*/
install_element (RESTRICTED_NODE, &show_bgp_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd);
@@ -12797,13 +13595,13 @@ bgp_route_init (void)
install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
install_element (ENABLE_NODE, &show_bgp_view_rsclient_prefix_cmd);
install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
-
+
/* Statistics */
install_element (ENABLE_NODE, &show_bgp_statistics_cmd);
install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd);
install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd);
install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd);
-
+
/* old command */
install_element (VIEW_NODE, &show_ipv6_bgp_cmd);
install_element (VIEW_NODE, &show_ipv6_bgp_route_cmd);
@@ -12841,7 +13639,7 @@ bgp_route_init (void)
install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_cmd);
install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_exact_cmd);
install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_longer_cmd);
-
+
/* old command */
install_element (ENABLE_NODE, &show_ipv6_bgp_cmd);
install_element (ENABLE_NODE, &show_ipv6_bgp_route_cmd);
@@ -12917,7 +13715,7 @@ bgp_route_init (void)
install_element (BGP_IPV4_NODE, &bgp_damp_set3_cmd);
install_element (BGP_IPV4_NODE, &bgp_damp_unset_cmd);
install_element (BGP_IPV4_NODE, &bgp_damp_unset2_cmd);
-
+
/* Deprecated AS-Pathlimit commands */
install_element (BGP_NODE, &bgp_network_ttl_cmd);
install_element (BGP_NODE, &bgp_network_mask_ttl_cmd);
@@ -12925,35 +13723,35 @@ bgp_route_init (void)
install_element (BGP_NODE, &bgp_network_backdoor_ttl_cmd);
install_element (BGP_NODE, &bgp_network_mask_backdoor_ttl_cmd);
install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd);
-
+
install_element (BGP_NODE, &no_bgp_network_ttl_cmd);
install_element (BGP_NODE, &no_bgp_network_mask_ttl_cmd);
install_element (BGP_NODE, &no_bgp_network_mask_natural_ttl_cmd);
install_element (BGP_NODE, &no_bgp_network_backdoor_ttl_cmd);
install_element (BGP_NODE, &no_bgp_network_mask_backdoor_ttl_cmd);
install_element (BGP_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd);
-
+
install_element (BGP_IPV4_NODE, &bgp_network_ttl_cmd);
install_element (BGP_IPV4_NODE, &bgp_network_mask_ttl_cmd);
install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_ttl_cmd);
install_element (BGP_IPV4_NODE, &bgp_network_backdoor_ttl_cmd);
install_element (BGP_IPV4_NODE, &bgp_network_mask_backdoor_ttl_cmd);
install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd);
-
+
install_element (BGP_IPV4_NODE, &no_bgp_network_ttl_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_mask_ttl_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_ttl_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_backdoor_ttl_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_mask_backdoor_ttl_cmd);
install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd);
-
+
install_element (BGP_IPV4M_NODE, &bgp_network_ttl_cmd);
install_element (BGP_IPV4M_NODE, &bgp_network_mask_ttl_cmd);
install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_ttl_cmd);
install_element (BGP_IPV4M_NODE, &bgp_network_backdoor_ttl_cmd);
install_element (BGP_IPV4M_NODE, &bgp_network_mask_backdoor_ttl_cmd);
install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd);
-
+
install_element (BGP_IPV4M_NODE, &no_bgp_network_ttl_cmd);
install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_ttl_cmd);
install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_ttl_cmd);
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 3e528596..c57cb205 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -23,7 +23,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgp_table.h"
-/* Ancillary information to struct bgp_info,
+/* Ancillary information to struct bgp_info,
* used for uncommonly used data (aggregation, MPLS, etc.)
* and lazily allocated to save memory.
*/
@@ -39,31 +39,34 @@ struct bgp_info_extra
u_int32_t igpmetric;
/* MPLS label. */
- u_char tag[3];
+ u_char tag[3];
};
struct bgp_info
{
- /* For linked list. */
- struct bgp_info *next;
- struct bgp_info *prev;
-
- /* Peer structure. */
+ /* For linked list. */
+ struct bgp_node* rn ;
+ struct bgp_info *info_next;
+ struct bgp_info *info_prev;
+
+ /* Peer structure. */
struct peer *peer;
+ struct bgp_info* routes_next ;
+ struct bgp_info* routes_prev ;
- /* Attribute structure. */
+ /* Attribute structure. */
struct attr *attr;
-
- /* Extra information */
+
+ /* Extra information */
struct bgp_info_extra *extra;
-
- /* Uptime. */
+
+ /* Uptime. */
time_t uptime;
- /* reference count */
+ /* reference count */
int lock;
-
- /* BGP information status. */
+
+ /* BGP information status. */
u_int16_t flags;
#define BGP_INFO_IGP_CHANGED (1 << 0)
#define BGP_INFO_DAMPED (1 << 1)
@@ -86,7 +89,7 @@ struct bgp_info
#define BGP_ROUTE_NORMAL 0
#define BGP_ROUTE_STATIC 1
#define BGP_ROUTE_AGGREGATE 2
-#define BGP_ROUTE_REDISTRIBUTE 3
+#define BGP_ROUTE_REDISTRIBUTE 3
};
/* BGP static route configuration. */
@@ -106,7 +109,7 @@ struct bgp_static
/* Atomic set reference count (ie cause of pathlimit) */
u_int32_t atomic;
-
+
/* BGP redistribute route-map. */
struct
{
@@ -130,32 +133,37 @@ struct bgp_static
|| CHECK_FLAG ((BI)->flags, BGP_INFO_UNUSEABLE))
#define DISTRIBUTE_IN_NAME(F) ((F)->dlist[FILTER_IN].name)
-#define DISTRIBUTE_IN(F) ((F)->dlist[FILTER_IN].alist)
+#define DISTRIBUTE_IN_LIST(F) ((F)->dlist[FILTER_IN].alist)
#define DISTRIBUTE_OUT_NAME(F) ((F)->dlist[FILTER_OUT].name)
-#define DISTRIBUTE_OUT(F) ((F)->dlist[FILTER_OUT].alist)
+#define DISTRIBUTE_OUT_LIST(F) ((F)->dlist[FILTER_OUT].alist)
-#define PREFIX_LIST_IN_NAME(F) ((F)->plist[FILTER_IN].name)
-#define PREFIX_LIST_IN(F) ((F)->plist[FILTER_IN].plist)
-#define PREFIX_LIST_OUT_NAME(F) ((F)->plist[FILTER_OUT].name)
-#define PREFIX_LIST_OUT(F) ((F)->plist[FILTER_OUT].plist)
+#define PREFIX_LIST_IN_REF(F) ((F)->plist[FILTER_IN].ref)
+#define PREFIX_LIST_IN_NAME(F) prefix_list_ref_name(PREFIX_LIST_IN_REF(F))
+#define PREFIX_LIST_IN_LIST(F) prefix_list_ref_plist(PREFIX_LIST_IN_REF(F))
+#define PREFIX_LIST_OUT_REF(F) ((F)->plist[FILTER_OUT].ref)
+#define PREFIX_LIST_OUT_NAME(F) prefix_list_ref_name(PREFIX_LIST_OUT_REF(F))
+#define PREFIX_LIST_OUT_LIST(F) prefix_list_ref_plist(PREFIX_LIST_OUT_REF(F))
#define FILTER_LIST_IN_NAME(F) ((F)->aslist[FILTER_IN].name)
-#define FILTER_LIST_IN(F) ((F)->aslist[FILTER_IN].aslist)
+#define FILTER_LIST_IN_LIST(F) ((F)->aslist[FILTER_IN].aslist)
#define FILTER_LIST_OUT_NAME(F) ((F)->aslist[FILTER_OUT].name)
-#define FILTER_LIST_OUT(F) ((F)->aslist[FILTER_OUT].aslist)
+#define FILTER_LIST_OUT_LIST(F) ((F)->aslist[FILTER_OUT].aslist)
+
+#define ROUTE_MAP_IN_NAME(F) ((F)->map[RMAP_IN].name)
+#define ROUTE_MAP_IN(F) ((F)->map[RMAP_IN].map)
+#define ROUTE_MAP_OUT_NAME(F) ((F)->map[RMAP_OUT].name)
+#define ROUTE_MAP_OUT(F) ((F)->map[RMAP_OUT].map)
-#define ROUTE_MAP_IN_NAME(F) ((F)->map[RMAP_IN].name)
-#define ROUTE_MAP_IN(F) ((F)->map[RMAP_IN].map)
-#define ROUTE_MAP_OUT_NAME(F) ((F)->map[RMAP_OUT].name)
-#define ROUTE_MAP_OUT(F) ((F)->map[RMAP_OUT].map)
+#define ROUTE_MAP_IMPORT_NAME(F) ((F)->map[RMAP_IMPORT].name)
+#define ROUTE_MAP_IMPORT(F) ((F)->map[RMAP_IMPORT].map)
+#define ROUTE_MAP_EXPORT_NAME(F) ((F)->map[RMAP_EXPORT].name)
+#define ROUTE_MAP_EXPORT(F) ((F)->map[RMAP_EXPORT].map)
-#define ROUTE_MAP_IMPORT_NAME(F) ((F)->map[RMAP_IMPORT].name)
-#define ROUTE_MAP_IMPORT(F) ((F)->map[RMAP_IMPORT].map)
-#define ROUTE_MAP_EXPORT_NAME(F) ((F)->map[RMAP_EXPORT].name)
-#define ROUTE_MAP_EXPORT(F) ((F)->map[RMAP_EXPORT].map)
+#define ROUTE_MAP_RS_IN_NAME(F) ((F)->map[RMAP_RS_IN].name)
+#define ROUTE_MAP_RS_IN(F) ((F)->map[RMAP_RS_IN].map)
-#define UNSUPPRESS_MAP_NAME(F) ((F)->usmap.name)
-#define UNSUPPRESS_MAP(F) ((F)->usmap.map)
+#define UNSUPPRESS_MAP_NAME(F) ((F)->usmap.name)
+#define UNSUPPRESS_MAP(F) ((F)->usmap.map)
enum bgp_clear_route_type
{
@@ -166,16 +174,19 @@ enum bgp_clear_route_type
/* Prototypes. */
extern void bgp_route_init (void);
extern void bgp_route_finish (void);
-extern void bgp_cleanup_routes (void);
+
extern void bgp_announce_route (struct peer *, afi_t, safi_t);
extern void bgp_announce_route_all (struct peer *);
extern void bgp_default_originate (struct peer *, afi_t, safi_t, int);
extern void bgp_soft_reconfig_in (struct peer *, afi_t, safi_t);
extern void bgp_soft_reconfig_rsclient (struct peer *, afi_t, safi_t);
-extern void bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi);
-extern void bgp_clear_route (struct peer *, afi_t, safi_t,
- enum bgp_clear_route_type);
-extern void bgp_clear_route_all (struct peer *);
+extern void bgp_check_local_routes_rsclient (struct peer *rsclient,
+ afi_t afi, safi_t safi);
+extern bool bgp_clear_routes(struct peer *peer, afi_t afi, safi_t safi,
+ bool nsf) ;
+extern void bgp_clear_rsclient_rib(struct peer* rsclient,
+ afi_t afi, safi_t safi) ;
+extern bool bgp_clear_all_routes (struct peer *, bool nsf);
extern void bgp_clear_adj_in (struct peer *, afi_t, safi_t);
extern void bgp_clear_stale_route (struct peer *, afi_t, safi_t);
@@ -191,25 +202,27 @@ extern int bgp_nlri_sanity_check (struct peer *, int, u_char *, bgp_size_t);
extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *);
extern int bgp_maximum_prefix_overflow (struct peer *, afi_t, safi_t, int);
+extern void bgp_maximum_prefix_cancel_timer (struct peer *peer) ;
-extern void bgp_redistribute_add (struct prefix *, struct in_addr *, u_int32_t, u_char);
+extern void bgp_redistribute_add (struct prefix *, struct in_addr *, u_int32_t,
+ u_char);
extern void bgp_redistribute_delete (struct prefix *, u_char);
extern void bgp_redistribute_withdraw (struct bgp *, afi_t, int);
extern void bgp_static_delete (struct bgp *);
-extern void bgp_static_update (struct bgp *, struct prefix *, struct bgp_static *,
- afi_t, safi_t);
+extern void bgp_static_update (struct bgp *, struct prefix *,
+ struct bgp_static *, afi_t, safi_t);
extern void bgp_static_withdraw (struct bgp *, struct prefix *, afi_t, safi_t);
-
-extern int bgp_static_set_vpnv4 (struct vty *vty, const char *,
+
+extern int bgp_static_set_vpnv4 (struct vty *vty, const char *,
const char *, const char *);
-extern int bgp_static_unset_vpnv4 (struct vty *, const char *,
+extern int bgp_static_unset_vpnv4 (struct vty *, const char *,
const char *, const char *);
/* this is primarily for MPLS-VPN */
extern int bgp_update (struct peer *, struct prefix *, struct attr *,
- afi_t, safi_t, int, int, struct prefix_rd *,
+ afi_t, safi_t, int, int, struct prefix_rd *,
u_char *, int);
extern int bgp_withdraw (struct peer *, struct prefix *, struct attr *,
afi_t, safi_t, int, int, struct prefix_rd *, u_char *);
diff --git a/bgpd/bgp_route_refresh.c b/bgpd/bgp_route_refresh.c
new file mode 100644
index 00000000..5c2b6e5c
--- /dev/null
+++ b/bgpd/bgp_route_refresh.c
@@ -0,0 +1,198 @@
+/* BGP ROUTE-REFRESH and ORF handling
+ * 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 <string.h>
+
+#include "lib/zassert.h"
+#include "lib/memory.h"
+#include "lib/vector.h"
+
+#include "bgpd/bgp_route_refresh.h"
+
+/*==============================================================================
+ * A bgp_route_refresh structure encapsulates the contents of a BGP
+ * ROUTE-REFRESH message, with or without ORF part.
+ *
+ * For incoming messages, expect a bgp_route_refresh structure to be the
+ * contents of a single ROUTE-REFRESH message.
+ *
+ * For outgoing messages, a bgp_route_refresh structure may contain a large
+ * number of ORF entries, and those are carved up into as many ROUTE-REFRESH
+ * messages as required.
+ */
+
+/*==============================================================================
+ * Create/Destroy bgp_route_refresh
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate and initialise new bgp_route_refresh
+ *
+ * Constructs complete simple ROUTE-REFRESH -- ORF stuff can then be added.
+ *
+ * Can specify an expected number of ORF entries (need not be actual number).
+ */
+extern bgp_route_refresh
+bgp_route_refresh_new(iAFI_t afi, iSAFI_t safi, unsigned count)
+{
+ bgp_route_refresh rr ;
+
+ rr = XCALLOC(MTYPE_BGP_ROUTE_REFRESH, sizeof(struct bgp_route_refresh)) ;
+
+ rr->afi = afi ;
+ rr->safi = safi ;
+
+ vector_init_new(&rr->entries, count) ;
+
+ /* rest of bgp_route_refresh zeroised -- not relevant when vector empty */
+
+ return rr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free bgp_route_refresh
+ */
+extern void
+bgp_route_refresh_free(bgp_route_refresh rr)
+{
+ bgp_orf_entry entry ;
+ while((entry = vector_ream_keep(&rr->entries)) != NULL)
+ XFREE(MTYPE_BGP_ORF_ENTRY, entry) ;
+
+ XFREE(MTYPE_BGP_ROUTE_REFRESH, rr) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the defer flag as required
+ */
+extern void
+bgp_route_refresh_set_orf_defer(bgp_route_refresh rr, bool defer)
+{
+ rr->defer = (defer != 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate new bgp_orf_entry -- for known or unknown type.
+ *
+ * Is for known type if unknown_size == 0 !
+ *
+ * Sets orf_type and unknown elements of the entry.
+ *
+ * NB: it is a FATAL error to set an unknown ORF type unless is explicitly
+ * unknown ! (In this context "pre-RFC" types are unknown.)
+ *
+ * Zeroises rest of structure -- in particular remove_all == false !
+ *
+ * Pushes entry onto the bgp_route_refresh list.
+ */
+static bgp_orf_entry
+bgp_orf_entry_new(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form,
+ size_t unknown_size)
+{
+ bgp_orf_entry orfe ;
+ size_t e_size ;
+
+ if (unknown_size == 0)
+ {
+ if (orf_type != BGP_ORF_T_PREFIX)
+ zabort("unknown ORF type") ;
+ e_size = 0 ;
+ }
+ else
+ {
+ if (unknown_size < bgp_orf_unknown_min_l)
+ e_size = 0 ;
+ else
+ e_size = unknown_size - bgp_orf_unknown_min_l ;
+ } ;
+
+ orfe = XCALLOC(MTYPE_BGP_ORF_ENTRY, sizeof(struct bgp_orf_entry) + e_size) ;
+
+ orfe->orf_type = orf_type ;
+ orfe->form = form ;
+ orfe->unknown = (unknown_size != 0) ;
+
+ vector_push_item(&rr->entries, orfe) ;
+
+ return orfe ;
+} ;
+
+/*==============================================================================
+ * Creating new ORF entries.
+ */
+
+/*------------------------------------------------------------------------------
+ * Add an entry for known type of ORF.
+ *
+ * Sets the common ORF entry part.
+ *
+ * Returns address of the ORF type specific structure, to be filled in.
+ *
+ * The orf_type presented must be a known BGP_ORF_T_xxx value, EXCLUDING any
+ * "pre-RFC" types. (Any use of pre-RFC values is looked after at the message
+ * level).
+ *
+ * NB: it is a FATAL error to set an unknown ORF type
+ */
+extern bgp_orf_entry
+bgp_orf_add(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form,
+ bool remove, bool deny)
+{
+ bgp_orf_entry orfe = bgp_orf_entry_new(rr, orf_type, form, 0) ;
+
+ orfe->remove = remove ;
+ orfe->deny = deny ;
+
+ return orfe ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add a remove_all entry.
+ *
+ * The orf_type presented must be a known BGP_ORF_T_xxx value, EXCLUDING any
+ * "pre-RFC" types. (Any use of pre-RFC values is looked after at the message
+ * level).
+ *
+ * NB: it is a FATAL error to set an unknown ORF type
+ */
+extern void
+bgp_orf_add_remove_all(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form)
+{
+ bgp_orf_entry orfe = bgp_orf_entry_new(rr, orf_type, form, 0) ;
+
+ orfe->remove_all = 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add an entry for an unknown type of ORF -- copy the entry verbatim.
+ *
+ * Provided so that can capture entire contents of incoming message.
+ */
+extern void
+bgp_orf_add_unknown(bgp_route_refresh rr, uint8_t orf_type, bgp_size_t length,
+ const void* entries)
+{
+ bgp_orf_entry orfe = bgp_orf_entry_new(rr, orf_type, bgp_form_none,
+ (length > 0) ? length : 1) ;
+
+ if (length != 0)
+ memcpy(&orfe->body.orf_unknown.data, entries, length) ;
+} ;
diff --git a/bgpd/bgp_route_refresh.h b/bgpd/bgp_route_refresh.h
new file mode 100644
index 00000000..b44ca9e0
--- /dev/null
+++ b/bgpd/bgp_route_refresh.h
@@ -0,0 +1,132 @@
+/* BGP ROUTE-REFRESH and ORF handling -- header
+ * 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_ROUTE_REFRESH_H
+#define _QUAGGA_BGP_ROUTE_REFRESH_H
+
+#include <stddef.h>
+#include "bgpd/bgp_common.h"
+
+#include "lib/prefix.h"
+#include "lib/plist.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Structures to hold ROUTE-REFRESH and ORF
+ */
+
+typedef struct orf_prefix* orf_prefix ; /* see lib/plist.h */
+
+typedef struct bgp_orf_unknown_entry* bgp_orf_unknown_entry ;
+struct bgp_orf_unknown_entry
+{
+ bgp_size_t length ;
+ uint8_t data[] ;
+} ;
+
+typedef struct bgp_orf_entry* bgp_orf_entry ;
+struct bgp_orf_entry
+{
+ uint8_t orf_type ; /* BGP_ORF_T_xxx -- _rfc version ! */
+ bgp_form_t form ; /* bgp_form_none/_rfc/_pre */
+
+ bool unknown ; /* ignore everything other than the */
+ /* unknown data part */
+
+ bool remove_all ; /* rest is ignored if this is set */
+
+ bool remove ; /* otherwise: add */
+ bool deny ; /* otherwise: permit */
+
+ union { /* must be last... */
+ struct orf_prefix orf_prefix ;
+ struct bgp_orf_unknown_entry orf_unknown ; /*... flexible array. */
+ } body ;
+} ;
+
+/* (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
+ * about first argument of offsetof().)
+ */
+typedef struct bgp_orf_entry bgp_orf_entry_t ;
+enum
+{
+ bgp_orf_unknown_min_l = sizeof(struct bgp_orf_entry)
+ - offsetof(bgp_orf_entry_t, body.orf_unknown.data)
+} ;
+
+typedef struct bgp_route_refresh* bgp_route_refresh ;
+struct bgp_route_refresh
+{
+ iAFI_t afi ; /* NB: Internet AFI/SAFI */
+ iSAFI_t safi ;
+
+ struct vector entries ; /* empty => simple ROUTE-REFRESH */
+
+ bool defer ; /* otherwise: immediate */
+
+ /* These support the output of ROUTE-REFRESH messages.
+ *
+ * These are zeroised when the bgp_route_refresh stucture is created.
+ */
+ unsigned next ; /* next entry to process */
+ uint8_t last_orf_type ; /* type of last ORF entry processed */
+} ;
+
+/*==============================================================================
+ * Prototypes
+ */
+
+extern bgp_route_refresh
+bgp_route_refresh_new(iAFI_t afi, iSAFI_t safi, unsigned count) ;
+
+extern void
+bgp_route_refresh_free(bgp_route_refresh rr) ;
+
+extern void
+bgp_route_refresh_set_orf_defer(bgp_route_refresh rr, bool defer) ;
+
+extern bgp_orf_entry
+bgp_orf_add(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form,
+ bool remove, bool deny) ;
+
+extern void
+bgp_orf_add_remove_all(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form);
+
+extern void
+bgp_orf_add_unknown(bgp_route_refresh rr, uint8_t orf_type, bgp_size_t length,
+ const void* entries) ;
+
+Inline unsigned
+bgp_orf_get_count(bgp_route_refresh rr)
+{
+ return vector_end(&rr->entries) ;
+} ;
+
+Inline bgp_orf_entry
+bgp_orf_get_entry(bgp_route_refresh rr, unsigned index)
+{
+ return vector_get_item(&rr->entries, index) ;
+} ;
+
+#endif /* _QUAGGA_BGP_ROUTE_REFRESH_H */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 178af603..33e2c9cd 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -99,8 +99,8 @@ o Local extention
set ipv6 next-hop local : Done
set as-path exclude : Done
-*/
-
+*/
+
/* 'match peer (A.B.C.D|X:X::X:X)' */
/* Compares the peer specified in the 'match peer' clause with the peer
@@ -137,12 +137,12 @@ route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type,
ret = RMAP_MATCH;
else
ret = RMAP_NOMATCH;
-
+
sockunion_free (su2);
return ret;
}
sockunion_free (su2);
-
+
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
if (sockunion_same (su, &peer->su))
@@ -202,7 +202,7 @@ struct route_map_rule_cmd route_match_peer_cmd =
/* Match function should return 1 if match is success else return
zero. */
static route_map_result_t
-route_match_ip_address (void *rule, struct prefix *prefix,
+route_match_ip_address (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct access_list *alist;
@@ -213,7 +213,7 @@ route_match_ip_address (void *rule, struct prefix *prefix,
alist = access_list_lookup (AFI_IP, (char *) rule);
if (alist == NULL)
return RMAP_NOMATCH;
-
+
return (access_list_apply (alist, prefix) == FILTER_DENY ?
RMAP_NOMATCH : RMAP_MATCH);
}
@@ -243,12 +243,12 @@ struct route_map_rule_cmd route_match_ip_address_cmd =
route_match_ip_address_compile,
route_match_ip_address_free
};
-
+
/* `match ip next-hop IP_ADDRESS' */
/* Match function return 1 if match is success else return zero. */
static route_map_result_t
-route_match_ip_next_hop (void *rule, struct prefix *prefix,
+route_match_ip_next_hop (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct access_list *alist;
@@ -295,12 +295,12 @@ struct route_map_rule_cmd route_match_ip_next_hop_cmd =
route_match_ip_next_hop_compile,
route_match_ip_next_hop_free
};
-
+
/* `match ip route-source ACCESS-LIST' */
/* Match function return 1 if match is success else return zero. */
static route_map_result_t
-route_match_ip_route_source (void *rule, struct prefix *prefix,
+route_match_ip_route_source (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct access_list *alist;
@@ -353,11 +353,11 @@ struct route_map_rule_cmd route_match_ip_route_source_cmd =
route_match_ip_route_source_compile,
route_match_ip_route_source_free
};
-
+
/* `match ip address prefix-list PREFIX_LIST' */
static route_map_result_t
-route_match_ip_address_prefix_list (void *rule, struct prefix *prefix,
+route_match_ip_address_prefix_list (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct prefix_list *plist;
@@ -367,7 +367,7 @@ route_match_ip_address_prefix_list (void *rule, struct prefix *prefix,
plist = prefix_list_lookup (AFI_IP, (char *) rule);
if (plist == NULL)
return RMAP_NOMATCH;
-
+
return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
RMAP_NOMATCH : RMAP_MATCH);
}
@@ -393,7 +393,7 @@ struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd =
route_match_ip_address_prefix_list_compile,
route_match_ip_address_prefix_list_free
};
-
+
/* `match ip next-hop prefix-list PREFIX_LIST' */
static route_map_result_t
@@ -440,7 +440,7 @@ struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd =
route_match_ip_next_hop_prefix_list_compile,
route_match_ip_next_hop_prefix_list_free
};
-
+
/* `match ip route-source prefix-list PREFIX_LIST' */
static route_map_result_t
@@ -493,12 +493,12 @@ struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd =
route_match_ip_route_source_prefix_list_compile,
route_match_ip_route_source_prefix_list_free
};
-
+
/* `match metric METRIC' */
/* Match function return 1 if match is success else return zero. */
static route_map_result_t
-route_match_metric (void *rule, struct prefix *prefix,
+route_match_metric (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
u_int32_t *med;
@@ -508,7 +508,7 @@ route_match_metric (void *rule, struct prefix *prefix,
{
med = rule;
bgp_info = object;
-
+
if (bgp_info->attr->med == *med)
return RMAP_MATCH;
else
@@ -528,12 +528,12 @@ route_match_metric_compile (const char *arg)
tmpval = strtoul (arg, &endptr, 10);
if (*endptr != '\0' || tmpval == ULONG_MAX || tmpval > UINT32_MAX)
return NULL;
-
+
med = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
-
+
if (!med)
return med;
-
+
*med = tmpval;
return med;
}
@@ -553,15 +553,15 @@ struct route_map_rule_cmd route_match_metric_cmd =
route_match_metric_compile,
route_match_metric_free
};
-
+
/* `match as-path ASPATH' */
/* Match function for as-path match. I assume given object is */
static route_map_result_t
-route_match_aspath (void *rule, struct prefix *prefix,
+route_match_aspath (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
-
+
struct as_list *as_list;
struct bgp_info *bgp_info;
@@ -570,9 +570,9 @@ route_match_aspath (void *rule, struct prefix *prefix,
as_list = as_list_lookup ((char *) rule);
if (as_list == NULL)
return RMAP_NOMATCH;
-
+
bgp_info = object;
-
+
/* Perform match. */
return ((as_list_apply (as_list, bgp_info->attr->aspath) == AS_FILTER_DENY) ? RMAP_NOMATCH : RMAP_MATCH);
}
@@ -594,14 +594,71 @@ route_match_aspath_free (void *rule)
}
/* Route map commands for aspath matching. */
-struct route_map_rule_cmd route_match_aspath_cmd =
+struct route_map_rule_cmd route_match_aspath_cmd =
{
"as-path",
route_match_aspath,
route_match_aspath_compile,
route_match_aspath_free
};
-
+
+/*------------------------------------------------------------------------------
+ * match as-origin ASN
+ */
+
+static route_map_result_t
+route_match_as_origin (void* p_asn, struct prefix *prefix,
+ route_map_object_t type, void *object)
+{
+ struct bgp_info *bgp_info;
+
+ if (type == RMAP_BGP)
+ {
+ bgp_info = object ;
+
+ return *((as_t*)p_asn) == aspath_origin(bgp_info->attr->aspath)
+ ? RMAP_MATCH : RMAP_NOMATCH ;
+ }
+ return RMAP_NOMATCH;
+}
+
+static void *
+route_match_as_origin_compile (const char *arg)
+{
+ as_t* p_asn ;
+ unsigned long tmp ;
+ char* ep ;
+
+ errno = 0 ;
+ ep = NULL ;
+
+ tmp = strtoul (arg, &ep, 10) ;
+ if ((errno != 0) || (*ep != '\0'))
+ return NULL ;
+
+ p_asn = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(as_t)) ;
+
+ *p_asn = tmp ;
+
+ return p_asn ;
+} ;
+
+static void
+route_match_as_origin_free (void* p_asn)
+{
+ XFREE (MTYPE_ROUTE_MAP_COMPILED, p_asn);
+}
+
+/* Route map commands for as-origin matching. */
+struct route_map_rule_cmd route_match_as_origin_cmd =
+{
+ "as-origin",
+ route_match_as_origin,
+ route_match_as_origin_compile,
+ route_match_as_origin_free
+} ;
+
+/*----------------------------------------------------------------------------*/
/* `match community COMMUNIY' */
struct rmap_community
{
@@ -611,14 +668,14 @@ struct rmap_community
/* Match function for community match. */
static route_map_result_t
-route_match_community (void *rule, struct prefix *prefix,
+route_match_community (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct community_list *list;
struct bgp_info *bgp_info;
struct rmap_community *rcom;
- if (type == RMAP_BGP)
+ if (type == RMAP_BGP)
{
bgp_info = object;
rcom = rule;
@@ -673,34 +730,34 @@ route_match_community_free (void *rule)
{
struct rmap_community *rcom = rule;
- XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+ XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name);
XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom);
}
/* Route map commands for community matching. */
-struct route_map_rule_cmd route_match_community_cmd =
+struct route_map_rule_cmd route_match_community_cmd =
{
"community",
route_match_community,
route_match_community_compile,
route_match_community_free
};
-
+
/* Match function for extcommunity match. */
static route_map_result_t
-route_match_ecommunity (void *rule, struct prefix *prefix,
+route_match_ecommunity (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct community_list *list;
struct bgp_info *bgp_info;
- if (type == RMAP_BGP)
+ if (type == RMAP_BGP)
{
bgp_info = object;
-
+
if (!bgp_info->attr->extra)
return RMAP_NOMATCH;
-
+
list = community_list_lookup (bgp_clist, (char *) rule,
EXTCOMMUNITY_LIST_MASTER);
if (! list)
@@ -727,20 +784,20 @@ route_match_ecommunity_free (void *rule)
}
/* Route map commands for community matching. */
-struct route_map_rule_cmd route_match_ecommunity_cmd =
+struct route_map_rule_cmd route_match_ecommunity_cmd =
{
"extcommunity",
route_match_ecommunity,
route_match_ecommunity_compile,
route_match_ecommunity_free
};
-
+
/* `match nlri` and `set nlri` are replaced by `address-family ipv4`
and `address-family vpnv4'. */
-
+
/* `match origin' */
static route_map_result_t
-route_match_origin (void *rule, struct prefix *prefix,
+route_match_origin (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
u_char *origin;
@@ -750,7 +807,7 @@ route_match_origin (void *rule, struct prefix *prefix,
{
origin = rule;
bgp_info = object;
-
+
if (bgp_info->attr->origin == *origin)
return RMAP_MATCH;
}
@@ -804,7 +861,9 @@ route_set_ip_nexthop (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct rmap_ip_nexthop_set *rins = rule;
+#if 0 /* see below */
struct in_addr peer_address;
+#endif
struct bgp_info *bgp_info;
struct peer *peer;
@@ -815,27 +874,35 @@ route_set_ip_nexthop (void *rule, struct prefix *prefix,
if (rins->peer_address)
{
- if ((CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) ||
- CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT))
- && peer->su_remote
+ if ( (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) ||
+ CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT) )
+ && peer->su_remote
&& sockunion_family (peer->su_remote) == AF_INET)
{
+#if 0 /* the following (a) appears redundant and (b) leaks memory */
inet_aton (sockunion_su2str (peer->su_remote), &peer_address);
bgp_info->attr->nexthop = peer_address;
+#else
+ bgp_info->attr->nexthop = peer->su_remote->sin.sin_addr ;
+#endif
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
}
else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT)
&& peer->su_local
&& sockunion_family (peer->su_local) == AF_INET)
{
+#if 0 /* the following (a) appears redundant and (b) leaks memory */
inet_aton (sockunion_su2str (peer->su_local), &peer_address);
bgp_info->attr->nexthop = peer_address;
+#else
+ bgp_info->attr->nexthop = peer->su_local->sin.sin_addr ;
+#endif
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
}
}
else
{
- /* Set next hop value. */
+ /* Set next hop value. */
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
bgp_info->attr->nexthop = *rins->address;
}
@@ -884,7 +951,7 @@ route_set_ip_nexthop_free (void *rule)
if (rins->address)
XFREE (MTYPE_ROUTE_MAP_COMPILED, rins->address);
-
+
XFREE (MTYPE_ROUTE_MAP_COMPILED, rins);
}
@@ -896,7 +963,7 @@ struct route_map_rule_cmd route_set_ip_nexthop_cmd =
route_set_ip_nexthop_compile,
route_set_ip_nexthop_free
};
-
+
/* `set local-preference LOCAL_PREF' */
/* Set local preference. */
@@ -912,8 +979,8 @@ route_set_local_pref (void *rule, struct prefix *prefix,
/* Fetch routemap's rule information. */
local_pref = rule;
bgp_info = object;
-
- /* Set local preference value. */
+
+ /* Set local preference value. */
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
bgp_info->attr->local_pref = *local_pref;
}
@@ -932,18 +999,18 @@ route_set_local_pref_compile (const char *arg)
/* Local preference value shoud be integer. */
if (! all_digit (arg))
return NULL;
-
+
tmp = strtoul (arg, &endptr, 10);
if (*endptr != '\0' || tmp == ULONG_MAX || tmp > UINT32_MAX)
return NULL;
-
- local_pref = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
-
+
+ local_pref = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+
if (!local_pref)
return local_pref;
-
+
*local_pref = tmp;
-
+
return local_pref;
}
@@ -955,14 +1022,14 @@ route_set_local_pref_free (void *rule)
}
/* Set local preference rule structure. */
-struct route_map_rule_cmd route_set_local_pref_cmd =
+struct route_map_rule_cmd route_set_local_pref_cmd =
{
"local-preference",
route_set_local_pref,
route_set_local_pref_compile,
route_set_local_pref_free,
};
-
+
/* `set weight WEIGHT' */
/* Set weight. */
@@ -978,8 +1045,8 @@ route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type,
/* Fetch routemap's rule information. */
weight = rule;
bgp_info = object;
-
- /* Set weight value. */
+
+ /* Set weight value. */
if (*weight)
(bgp_attr_extra_get (bgp_info->attr))->weight = *weight;
else if (bgp_info->attr->extra)
@@ -1005,14 +1072,14 @@ route_set_weight_compile (const char *arg)
tmp = strtoul (arg, &endptr, 10);
if (*endptr != '\0' || tmp == ULONG_MAX || tmp > UINT32_MAX)
return NULL;
-
+
weight = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
-
+
if (weight == NULL)
return weight;
-
- *weight = tmp;
-
+
+ *weight = tmp;
+
return weight;
}
@@ -1024,19 +1091,19 @@ route_set_weight_free (void *rule)
}
/* Set local preference rule structure. */
-struct route_map_rule_cmd route_set_weight_cmd =
+struct route_map_rule_cmd route_set_weight_cmd =
{
"weight",
route_set_weight,
route_set_weight_compile,
route_set_weight_free,
};
-
+
/* `set metric METRIC' */
/* Set metric to attribute. */
static route_map_result_t
-route_set_metric (void *rule, struct prefix *prefix,
+route_set_metric (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
char *metric;
@@ -1122,14 +1189,14 @@ route_set_metric_free (void *rule)
}
/* Set metric rule structure. */
-struct route_map_rule_cmd route_set_metric_cmd =
+struct route_map_rule_cmd route_set_metric_cmd =
{
"metric",
route_set_metric,
route_set_metric_compile,
route_set_metric_free,
};
-
+
/* `set as-path prepend ASPATH' */
/* For AS path prepend mechanism. */
@@ -1144,7 +1211,7 @@ route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t
{
aspath = rule;
binfo = object;
-
+
if (binfo->attr->aspath->refcnt)
new = aspath_dup (binfo->attr->aspath);
else
@@ -1178,14 +1245,14 @@ route_set_aspath_prepend_free (void *rule)
}
/* Set metric rule structure. */
-struct route_map_rule_cmd route_set_aspath_prepend_cmd =
+struct route_map_rule_cmd route_set_aspath_prepend_cmd =
{
"as-path prepend",
route_set_aspath_prepend,
route_set_aspath_prepend_compile,
route_set_aspath_prepend_free,
};
-
+
/* `set as-path exclude ASn' */
/* For ASN exclude mechanism.
@@ -1236,14 +1303,14 @@ route_set_aspath_exclude_free (void *rule)
}
/* Set ASn exlude rule structure. */
-struct route_map_rule_cmd route_set_aspath_exclude_cmd =
+struct route_map_rule_cmd route_set_aspath_exclude_cmd =
{
"as-path exclude",
route_set_aspath_exclude,
route_set_aspath_exclude_compile,
route_set_aspath_exclude_free,
};
-
+
/* `set community COMMUNITY' */
struct rmap_com_set
{
@@ -1263,7 +1330,7 @@ route_set_community (void *rule, struct prefix *prefix,
struct community *new = NULL;
struct community *old;
struct community *merge;
-
+
if (type == RMAP_BGP)
{
rcs = rule;
@@ -1283,8 +1350,8 @@ route_set_community (void *rule, struct prefix *prefix,
if (rcs->additive && old)
{
merge = community_merge (community_dup (old), rcs->com);
-
- /* HACK: if the old community is not intern'd,
+
+ /* HACK: if the old community is not intern'd,
* we should free it here, or all reference to it may be lost.
* Really need to cleanup attribute caching sometime.
*/
@@ -1295,7 +1362,7 @@ route_set_community (void *rule, struct prefix *prefix,
}
else
new = community_dup (rcs->com);
-
+
/* will be interned by caller if required */
attr->community = new;
@@ -1314,7 +1381,7 @@ route_set_community_compile (const char *arg)
char *sp;
int additive = 0;
int none = 0;
-
+
if (strcmp (arg, "none") == 0)
none = 1;
else
@@ -1336,12 +1403,12 @@ route_set_community_compile (const char *arg)
if (! com)
return NULL;
}
-
+
rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set));
rcs->com = com;
rcs->additive = additive;
rcs->none = none;
-
+
return rcs;
}
@@ -1357,14 +1424,14 @@ route_set_community_free (void *rule)
}
/* Set community rule structure. */
-struct route_map_rule_cmd route_set_community_cmd =
+struct route_map_rule_cmd route_set_community_cmd =
{
"community",
route_set_community,
route_set_community_compile,
route_set_community_free,
};
-
+
/* `set comm-list (<1-99>|<100-500>|WORD) delete' */
/* For community set mechanism. */
@@ -1453,12 +1520,12 @@ struct route_map_rule_cmd route_set_community_delete_cmd =
route_set_community_delete_compile,
route_set_community_delete_free,
};
-
+
/* `set extcommunity rt COMMUNITY' */
/* For community set mechanism. */
static route_map_result_t
-route_set_ecommunity_rt (void *rule, struct prefix *prefix,
+route_set_ecommunity_rt (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct ecommunity *ecom;
@@ -1470,10 +1537,10 @@ route_set_ecommunity_rt (void *rule, struct prefix *prefix,
{
ecom = rule;
bgp_info = object;
-
+
if (! ecom)
return RMAP_OKAY;
-
+
/* We assume additive for Extended Community. */
old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity;
@@ -1513,7 +1580,7 @@ route_set_ecommunity_rt_free (void *rule)
}
/* Set community rule structure. */
-struct route_map_rule_cmd route_set_ecommunity_rt_cmd =
+struct route_map_rule_cmd route_set_ecommunity_rt_cmd =
{
"extcommunity rt",
route_set_ecommunity_rt,
@@ -1525,7 +1592,7 @@ struct route_map_rule_cmd route_set_ecommunity_rt_cmd =
/* For community set mechanism. */
static route_map_result_t
-route_set_ecommunity_soo (void *rule, struct prefix *prefix,
+route_set_ecommunity_soo (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct ecommunity *ecom, *old_ecom, *new_ecom;
@@ -1535,7 +1602,7 @@ route_set_ecommunity_soo (void *rule, struct prefix *prefix,
{
ecom = rule;
bgp_info = object;
-
+
if (! ecom)
return RMAP_OKAY;
@@ -1578,14 +1645,14 @@ route_set_ecommunity_soo_free (void *rule)
}
/* Set community rule structure. */
-struct route_map_rule_cmd route_set_ecommunity_soo_cmd =
+struct route_map_rule_cmd route_set_ecommunity_soo_cmd =
{
"extcommunity soo",
route_set_ecommunity_soo,
route_set_ecommunity_soo_compile,
route_set_ecommunity_soo_free,
};
-
+
/* `set origin ORIGIN' */
/* For origin set. */
@@ -1599,7 +1666,7 @@ route_set_origin (void *rule, struct prefix *prefix, route_map_object_t type, vo
{
origin = rule;
bgp_info = object;
-
+
bgp_info->attr->origin = *origin;
}
@@ -1632,14 +1699,14 @@ route_set_origin_free (void *rule)
}
/* Set metric rule structure. */
-struct route_map_rule_cmd route_set_origin_cmd =
+struct route_map_rule_cmd route_set_origin_cmd =
{
"origin",
route_set_origin,
route_set_origin_compile,
route_set_origin_free,
};
-
+
/* `set atomic-aggregate' */
/* For atomic aggregate set. */
@@ -1673,14 +1740,14 @@ route_set_atomic_aggregate_free (void *rule)
}
/* Set atomic aggregate rule structure. */
-struct route_map_rule_cmd route_set_atomic_aggregate_cmd =
+struct route_map_rule_cmd route_set_atomic_aggregate_cmd =
{
"atomic-aggregate",
route_set_atomic_aggregate,
route_set_atomic_aggregate_compile,
route_set_atomic_aggregate_free,
};
-
+
/* `set aggregator as AS A.B.C.D' */
struct aggregator
{
@@ -1689,7 +1756,7 @@ struct aggregator
};
static route_map_result_t
-route_set_aggregator_as (void *rule, struct prefix *prefix,
+route_set_aggregator_as (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct bgp_info *bgp_info;
@@ -1701,7 +1768,7 @@ route_set_aggregator_as (void *rule, struct prefix *prefix,
bgp_info = object;
aggregator = rule;
ae = bgp_attr_extra_get (bgp_info->attr);
-
+
ae->aggregator_as = aggregator->as;
ae->aggregator_addr = aggregator->address;
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
@@ -1732,19 +1799,19 @@ route_set_aggregator_as_free (void *rule)
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
-struct route_map_rule_cmd route_set_aggregator_as_cmd =
+struct route_map_rule_cmd route_set_aggregator_as_cmd =
{
"aggregator as",
route_set_aggregator_as,
route_set_aggregator_as_compile,
route_set_aggregator_as_free,
};
-
+
#ifdef HAVE_IPV6
/* `match ipv6 address IP_ACCESS_LIST' */
static route_map_result_t
-route_match_ipv6_address (void *rule, struct prefix *prefix,
+route_match_ipv6_address (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct access_list *alist;
@@ -1754,7 +1821,7 @@ route_match_ipv6_address (void *rule, struct prefix *prefix,
alist = access_list_lookup (AFI_IP6, (char *) rule);
if (alist == NULL)
return RMAP_NOMATCH;
-
+
return (access_list_apply (alist, prefix) == FILTER_DENY ?
RMAP_NOMATCH : RMAP_MATCH);
}
@@ -1781,11 +1848,11 @@ struct route_map_rule_cmd route_match_ipv6_address_cmd =
route_match_ipv6_address_compile,
route_match_ipv6_address_free
};
-
+
/* `match ipv6 next-hop IP_ADDRESS' */
static route_map_result_t
-route_match_ipv6_next_hop (void *rule, struct prefix *prefix,
+route_match_ipv6_next_hop (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct in6_addr *addr;
@@ -1795,10 +1862,10 @@ route_match_ipv6_next_hop (void *rule, struct prefix *prefix,
{
addr = rule;
bgp_info = object;
-
+
if (!bgp_info->attr->extra)
return RMAP_NOMATCH;
-
+
if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, rule))
return RMAP_MATCH;
@@ -1843,11 +1910,11 @@ struct route_map_rule_cmd route_match_ipv6_next_hop_cmd =
route_match_ipv6_next_hop_compile,
route_match_ipv6_next_hop_free
};
-
+
/* `match ipv6 address prefix-list PREFIX_LIST' */
static route_map_result_t
-route_match_ipv6_address_prefix_list (void *rule, struct prefix *prefix,
+route_match_ipv6_address_prefix_list (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct prefix_list *plist;
@@ -1857,7 +1924,7 @@ route_match_ipv6_address_prefix_list (void *rule, struct prefix *prefix,
plist = prefix_list_lookup (AFI_IP6, (char *) rule);
if (plist == NULL)
return RMAP_NOMATCH;
-
+
return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
RMAP_NOMATCH : RMAP_MATCH);
}
@@ -1883,12 +1950,12 @@ struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd =
route_match_ipv6_address_prefix_list_compile,
route_match_ipv6_address_prefix_list_free
};
-
+
/* `set ipv6 nexthop global IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
static route_map_result_t
-route_set_ipv6_nexthop_global (void *rule, struct prefix *prefix,
+route_set_ipv6_nexthop_global (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct in6_addr *address;
@@ -1899,10 +1966,10 @@ route_set_ipv6_nexthop_global (void *rule, struct prefix *prefix,
/* Fetch routemap's rule information. */
address = rule;
bgp_info = object;
-
- /* Set next hop value. */
+
+ /* Set next hop value. */
(bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = *address;
-
+
/* Set nexthop length. */
if (bgp_info->attr->extra->mp_nexthop_len == 0)
bgp_info->attr->extra->mp_nexthop_len = 16;
@@ -1947,12 +2014,12 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_global_cmd =
route_set_ipv6_nexthop_global_compile,
route_set_ipv6_nexthop_global_free
};
-
+
/* `set ipv6 nexthop local IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
static route_map_result_t
-route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix,
+route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct in6_addr *address;
@@ -1963,10 +2030,10 @@ route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix,
/* Fetch routemap's rule information. */
address = rule;
bgp_info = object;
-
- /* Set next hop value. */
+
+ /* Set next hop value. */
(bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = *address;
-
+
/* Set nexthop length. */
if (bgp_info->attr->extra->mp_nexthop_len != 32)
bgp_info->attr->extra->mp_nexthop_len = 32;
@@ -2012,11 +2079,11 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd =
route_set_ipv6_nexthop_local_free
};
#endif /* HAVE_IPV6 */
-
+
/* `set vpnv4 nexthop A.B.C.D' */
static route_map_result_t
-route_set_vpnv4_nexthop (void *rule, struct prefix *prefix,
+route_set_vpnv4_nexthop (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct in_addr *address;
@@ -2027,8 +2094,8 @@ route_set_vpnv4_nexthop (void *rule, struct prefix *prefix,
/* Fetch routemap's rule information. */
address = rule;
bgp_info = object;
-
- /* Set next hop value. */
+
+ /* Set next hop value. */
(bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global_in = *address;
}
@@ -2068,7 +2135,7 @@ struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd =
route_set_vpnv4_nexthop_compile,
route_set_vpnv4_nexthop_free
};
-
+
/* `set originator-id' */
/* For origin set. */
@@ -2078,11 +2145,11 @@ route_set_originator_id (void *rule, struct prefix *prefix, route_map_object_t t
struct in_addr *address;
struct bgp_info *bgp_info;
- if (type == RMAP_BGP)
+ if (type == RMAP_BGP)
{
address = rule;
bgp_info = object;
-
+
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
(bgp_attr_extra_get (bgp_info->attr))->originator_id = *address;
}
@@ -2118,14 +2185,14 @@ route_set_originator_id_free (void *rule)
}
/* Set metric rule structure. */
-struct route_map_rule_cmd route_set_originator_id_cmd =
+struct route_map_rule_cmd route_set_originator_id_cmd =
{
"originator-id",
route_set_originator_id,
route_set_originator_id_compile,
route_set_originator_id_free,
};
-
+
/* Add bgp route map rule. */
static int
bgp_route_match_add (struct vty *vty, struct route_map_index *index,
@@ -2244,11 +2311,11 @@ bgp_route_map_update (const char *unused)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
filter = &peer->filter[afi][safi];
-
+
for (direct = RMAP_IN; direct < RMAP_MAX; direct++)
{
if (filter->map[direct].name)
- filter->map[direct].map =
+ filter->map[direct].map =
route_map_lookup_by_name (filter->map[direct].name);
else
filter->map[direct].map = NULL;
@@ -2266,11 +2333,11 @@ bgp_route_map_update (const char *unused)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
filter = &group->conf->filter[afi][safi];
-
+
for (direct = RMAP_IN; direct < RMAP_MAX; direct++)
{
if (filter->map[direct].name)
- filter->map[direct].map =
+ filter->map[direct].map =
route_map_lookup_by_name (filter->map[direct].name);
else
filter->map[direct].map = NULL;
@@ -2324,7 +2391,7 @@ bgp_route_map_update (const char *unused)
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
{
if (bgp->rmap[ZEBRA_FAMILY_IPV4][i].name)
- bgp->rmap[ZEBRA_FAMILY_IPV4][i].map =
+ bgp->rmap[ZEBRA_FAMILY_IPV4][i].map =
route_map_lookup_by_name (bgp->rmap[ZEBRA_FAMILY_IPV4][i].name);
#ifdef HAVE_IPV6
if (bgp->rmap[ZEBRA_FAMILY_IPV6][i].name)
@@ -2334,7 +2401,7 @@ bgp_route_map_update (const char *unused)
}
}
}
-
+
DEFUN (match_peer,
match_peer_cmd,
"match peer (A.B.C.D|X:X::X:X)",
@@ -2386,7 +2453,7 @@ ALIAS (no_match_peer,
"Match peer address\n"
"Static or Redistributed routes\n")
-DEFUN (match_ip_address,
+DEFUN (match_ip_address,
match_ip_address_cmd,
"match ip address (<1-199>|<1300-2699>|WORD)",
MATCH_STR
@@ -2399,7 +2466,7 @@ DEFUN (match_ip_address,
return bgp_route_match_add (vty, vty->index, "ip address", argv[0]);
}
-DEFUN (no_match_ip_address,
+DEFUN (no_match_ip_address,
no_match_ip_address_cmd,
"no match ip address",
NO_STR
@@ -2413,7 +2480,7 @@ DEFUN (no_match_ip_address,
return bgp_route_match_delete (vty, vty->index, "ip address", argv[0]);
}
-ALIAS (no_match_ip_address,
+ALIAS (no_match_ip_address,
no_match_ip_address_val_cmd,
"no match ip address (<1-199>|<1300-2699>|WORD)",
NO_STR
@@ -2424,7 +2491,7 @@ ALIAS (no_match_ip_address,
"IP access-list number (expanded range)\n"
"IP Access-list name\n")
-DEFUN (match_ip_next_hop,
+DEFUN (match_ip_next_hop,
match_ip_next_hop_cmd,
"match ip next-hop (<1-199>|<1300-2699>|WORD)",
MATCH_STR
@@ -2462,7 +2529,7 @@ ALIAS (no_match_ip_next_hop,
"IP access-list number (expanded range)\n"
"IP Access-list name\n")
-DEFUN (match_ip_route_source,
+DEFUN (match_ip_route_source,
match_ip_route_source_cmd,
"match ip route-source (<1-199>|<1300-2699>|WORD)",
MATCH_STR
@@ -2500,7 +2567,7 @@ ALIAS (no_match_ip_route_source,
"IP access-list number (expanded range)\n"
"IP standard access-list name\n")
-DEFUN (match_ip_address_prefix_list,
+DEFUN (match_ip_address_prefix_list,
match_ip_address_prefix_list_cmd,
"match ip address prefix-list WORD",
MATCH_STR
@@ -2537,7 +2604,7 @@ ALIAS (no_match_ip_address_prefix_list,
"Match entries of prefix-lists\n"
"IP prefix-list name\n")
-DEFUN (match_ip_next_hop_prefix_list,
+DEFUN (match_ip_next_hop_prefix_list,
match_ip_next_hop_prefix_list_cmd,
"match ip next-hop prefix-list WORD",
MATCH_STR
@@ -2574,7 +2641,7 @@ ALIAS (no_match_ip_next_hop_prefix_list,
"Match entries of prefix-lists\n"
"IP prefix-list name\n")
-DEFUN (match_ip_route_source_prefix_list,
+DEFUN (match_ip_route_source_prefix_list,
match_ip_route_source_prefix_list_cmd,
"match ip route-source prefix-list WORD",
MATCH_STR
@@ -2611,7 +2678,7 @@ ALIAS (no_match_ip_route_source_prefix_list,
"Match entries of prefix-lists\n"
"IP prefix-list name\n")
-DEFUN (match_metric,
+DEFUN (match_metric,
match_metric_cmd,
"match metric <0-4294967295>",
MATCH_STR
@@ -2642,7 +2709,7 @@ ALIAS (no_match_metric,
"Match metric of route\n"
"Metric value\n")
-DEFUN (match_community,
+DEFUN (match_community,
match_community_cmd,
"match community (<1-99>|<100-500>|WORD)",
MATCH_STR
@@ -2654,7 +2721,7 @@ DEFUN (match_community,
return bgp_route_match_add (vty, vty->index, "community", argv[0]);
}
-DEFUN (match_community_exact,
+DEFUN (match_community_exact,
match_community_exact_cmd,
"match community (<1-99>|<100-500>|WORD) exact-match",
MATCH_STR
@@ -2710,7 +2777,7 @@ ALIAS (no_match_community,
"Community-list name\n"
"Do exact matching of communities\n")
-DEFUN (match_ecommunity,
+DEFUN (match_ecommunity,
match_ecommunity_cmd,
"match extcommunity (<1-99>|<100-500>|WORD)",
MATCH_STR
@@ -2770,6 +2837,42 @@ ALIAS (no_match_aspath,
"Match BGP AS path list\n"
"AS path access-list name\n")
+/*------------------------------------------------------------------------------
+ * match as-origin 9999
+ * no match as-origin
+ * no match as-origin 9999
+ */
+
+DEFUN (match_as_origin,
+ match_as_origin_cmd,
+ "match as-origin " CMD_AS_RANGE,
+ MATCH_STR
+ "Match BGP AS path origin ASN\n"
+ "AS path origin ASN\n")
+{
+ return bgp_route_match_add (vty, vty->index, "as-origin", argv[0]);
+}
+
+DEFUN (no_match_as_origin,
+ no_match_as_origin_cmd,
+ "no match as-origin",
+ NO_STR
+ MATCH_STR
+ "Match BGP AS path origin ASN\n")
+{
+ return bgp_route_match_delete (vty, vty->index, "as-origin", NULL);
+}
+
+ALIAS (no_match_as_origin,
+ no_match_as_origin_val_cmd,
+ "no match as-origin " CMD_AS_RANGE,
+ NO_STR
+ MATCH_STR
+ "Match BGP AS path origin ASN\n"
+ "AS path origin ASN\n")
+
+/*----------------------------------------------------------------------------*/
+
DEFUN (match_origin,
match_origin_cmd,
"match origin (egp|igp|incomplete)",
@@ -2826,7 +2929,7 @@ DEFUN (set_ip_nexthop,
vty_out (vty, "%% Malformed Next-hop address%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
return bgp_route_set_add (vty, vty->index, "ip next-hop", argv[0]);
}
@@ -2964,7 +3067,7 @@ DEFUN (no_set_weight,
{
if (argc == 0)
return bgp_route_set_delete (vty, vty->index, "weight", NULL);
-
+
return bgp_route_set_delete (vty, vty->index, "weight", argv[0]);
}
@@ -3389,7 +3492,7 @@ DEFUN (set_aggregator_as,
char *argstr;
VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
-
+
ret = inet_aton (argv[1], &address);
if (ret == 0)
{
@@ -3424,7 +3527,7 @@ DEFUN (no_set_aggregator_as,
if (argv == 0)
return bgp_route_set_delete (vty, vty->index, "aggregator as", NULL);
-
+
VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
ret = inet_aton (argv[1], &address);
@@ -3456,9 +3559,9 @@ ALIAS (no_set_aggregator_as,
"AS number\n"
"IP address of aggregator\n")
-
+
#ifdef HAVE_IPV6
-DEFUN (match_ipv6_address,
+DEFUN (match_ipv6_address,
match_ipv6_address_cmd,
"match ipv6 address WORD",
MATCH_STR
@@ -3469,7 +3572,7 @@ DEFUN (match_ipv6_address,
return bgp_route_match_add (vty, vty->index, "ipv6 address", argv[0]);
}
-DEFUN (no_match_ipv6_address,
+DEFUN (no_match_ipv6_address,
no_match_ipv6_address_cmd,
"no match ipv6 address WORD",
NO_STR
@@ -3481,7 +3584,7 @@ DEFUN (no_match_ipv6_address,
return bgp_route_match_delete (vty, vty->index, "ipv6 address", argv[0]);
}
-DEFUN (match_ipv6_next_hop,
+DEFUN (match_ipv6_next_hop,
match_ipv6_next_hop_cmd,
"match ipv6 next-hop X:X::X:X",
MATCH_STR
@@ -3504,7 +3607,7 @@ DEFUN (no_match_ipv6_next_hop,
return bgp_route_match_delete (vty, vty->index, "ipv6 next-hop", argv[0]);
}
-DEFUN (match_ipv6_address_prefix_list,
+DEFUN (match_ipv6_address_prefix_list,
match_ipv6_address_prefix_list_cmd,
"match ipv6 address prefix-list WORD",
MATCH_STR
@@ -3589,7 +3692,7 @@ DEFUN (no_set_ipv6_nexthop_local,
{
if (argc == 0)
return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop local", NULL);
-
+
return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop local", argv[0]);
}
@@ -3657,7 +3760,7 @@ DEFUN (no_set_originator_id,
{
if (argc == 0)
return bgp_route_set_delete (vty, vty->index, "originator-id", NULL);
-
+
return bgp_route_set_delete (vty, vty->index, "originator-id", argv[0]);
}
@@ -3676,7 +3779,7 @@ DEFUN_DEPRECATED (set_pathlimit_ttl,
"BGP AS-Pathlimit attribute\n"
"Set AS-Path Hop-count TTL\n")
{
- return CMD_SUCCESS;
+ return CMD_SUCCESS ;
}
DEFUN_DEPRECATED (no_set_pathlimit_ttl,
@@ -3705,7 +3808,7 @@ DEFUN_DEPRECATED (match_pathlimit_as,
"BGP AS-Pathlimit attribute\n"
"Match Pathlimit AS number\n")
{
- return CMD_SUCCESS;
+ return CMD_SUCCESS ;
}
DEFUN_DEPRECATED (no_match_pathlimit_as,
@@ -3716,7 +3819,7 @@ DEFUN_DEPRECATED (no_match_pathlimit_as,
"BGP AS-Pathlimit attribute\n"
"Match Pathlimit AS number\n")
{
- return CMD_SUCCESS;
+ return CMD_SUCCESS ;
}
ALIAS (no_match_pathlimit_as,
@@ -3727,7 +3830,7 @@ ALIAS (no_match_pathlimit_as,
"BGP AS-Pathlimit attribute\n"
"Match Pathlimit ASN\n")
-
+
/* Initialization of route map. */
void
bgp_route_map_init (void)
@@ -3745,6 +3848,7 @@ bgp_route_map_init (void)
route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd);
route_map_install_match (&route_match_ip_route_source_prefix_list_cmd);
route_map_install_match (&route_match_aspath_cmd);
+ route_map_install_match (&route_match_as_origin_cmd);
route_map_install_match (&route_match_community_cmd);
route_map_install_match (&route_match_ecommunity_cmd);
route_map_install_match (&route_match_metric_cmd);
@@ -3794,6 +3898,9 @@ bgp_route_map_init (void)
install_element (RMAP_NODE, &match_aspath_cmd);
install_element (RMAP_NODE, &no_match_aspath_cmd);
install_element (RMAP_NODE, &no_match_aspath_val_cmd);
+ install_element (RMAP_NODE, &match_as_origin_cmd);
+ install_element (RMAP_NODE, &no_match_as_origin_cmd);
+ install_element (RMAP_NODE, &no_match_as_origin_val_cmd);
install_element (RMAP_NODE, &match_metric_cmd);
install_element (RMAP_NODE, &no_match_metric_cmd);
install_element (RMAP_NODE, &no_match_metric_val_cmd);
@@ -3864,7 +3971,7 @@ bgp_route_map_init (void)
route_map_install_match (&route_match_ipv6_address_prefix_list_cmd);
route_map_install_set (&route_set_ipv6_nexthop_global_cmd);
route_map_install_set (&route_set_ipv6_nexthop_local_cmd);
-
+
install_element (RMAP_NODE, &match_ipv6_address_cmd);
install_element (RMAP_NODE, &no_match_ipv6_address_cmd);
install_element (RMAP_NODE, &match_ipv6_next_hop_cmd);
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c
new file mode 100644
index 00000000..99077907
--- /dev/null
+++ b/bgpd/bgp_session.c
@@ -0,0 +1,1069 @@
+/* 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_common.h"
+#include "bgpd/bgp_peer.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_peer_index.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_route_refresh.h"
+#include "bgpd/bgp_msg_write.h"
+#include "bgpd/bgp_network.h"
+
+#include "bgpd/bgp_packet.h"
+#include "bgp_debug.h"
+
+#include "lib/memory.h"
+#include "lib/sockunion.h"
+#include "lib/log.h"
+#include "lib/mqueue.h"
+#include "lib/zassert.h"
+
+/* prototypes */
+static void bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) ;
+static void bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_update_send(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_end_of_rib_send(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_route_refresh_send(mqueue_block mqb,
+ mqb_flag_t flag);
+static void bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag) ;
+static void bgp_session_XON(bgp_session session);
+static void bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag);
+
+/*==============================================================================
+ * 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.
+ *
+ * A session is created some time before it is enabled, and may be destroyed
+ * once the session is disabled.
+ *
+ * A session may be in one of the states:
+ *
+ * * bgp_session_sIdle -- not doing anything
+ * * bgp_session_sEnabled -- the BGP Engine is trying to connect
+ * * bgp_session_sEstablished -- the BGP Engine is exchanging updates etc
+ * * bgp_session_sLimping -- in the process of being disabled
+ * * bgp_session_sDisabled -- completely disabled
+ *
+ * NB: in sIdle and sDisabled states the BGP Engine has no interest in the
+ * session. These are known as the "inactive" states.
+ *
+ * NB: in sEnabled, sEstablished and sLimping states the BGP Engine is running
+ * connection(s) for the session. These are known as the "active" states.
+ *
+ * While the session is active the Routeing Engine should not attempt to
+ * change any shared item in the session, except under the mutex. And
+ * even then it may make no sense !
+ *
+ * NB: a session reaches eDisabled when the Routing Engine has sent a disable
+ * request to the BGP Engine, AND an eDisabled event has come back.
+ *
+ * While the Routing Engine is waiting for the eDisabled event, the session
+ * is in sLimping state.
+ *
+ * The BGP Engine's primary interest is in its (private) bgp_connection
+ * structure(s), which (while a session is sEnabled, sEstablished or sLimping)
+ * are pointed to by their associated session.
+ */
+
+/*==============================================================================
+ * BGP Session handling.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate & initialise new session structure.
+ *
+ * Ties peer and session together. Sets session sIdle, initialises mutex.
+ *
+ * Unsets everything else -- mostly by zeroising it.
+ *
+ * NB: if not allocating, the existing session MUST be sIdle/sDisabled OR never
+ * been kissed.
+ *
+ * NB: peer MUST NOT have a session set up:
+ *
+ * (a) because if there was a session, there would have to be code here
+ * to worry about its state, and tearing it down etc.
+ *
+ * (b) so that do not have to worry about BGP Engine reaching the old
+ * session while it was being replaced or whatever.
+ */
+extern bgp_session
+bgp_session_init_new(bgp_peer peer)
+{
+ bgp_session session ;
+
+ assert(peer->session == NULL) ;
+
+ session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ;
+
+ qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ;
+
+ session->peer = peer ;
+ bgp_peer_lock(peer) ; /* Account for the session->peer pointer */
+
+ session->state = bgp_session_sIdle ;
+
+ /* Zeroising the structure has set:
+ *
+ * delete_me -- 0 -- false
+ *
+ * event -- bgp_session_null_event
+ * notification -- NULL -- none
+ * err -- 0 -- none
+ * ordinal -- 0 -- unset
+ *
+ * open_send -- NULL -- none
+ * open_recv -- NULL -- none
+ *
+ * connect -- unset, false
+ * listen -- unset, false
+ *
+ * cap_suppress -- unset, false
+ * cap_override -- unset, false
+ * cap_strict -- unset, false
+ *
+ * ttl -- unset
+ * port -- unset
+ * as_peer -- unset
+ * su_peer -- NULL -- none
+ *
+ * ifname -- NULL -- none
+ * ifindex -- 0 -- none
+ * ifaddress -- NULL -- none
+ *
+ * log -- NULL -- none
+ * host -- NULL -- none
+ * password -- NULL -- none
+ *
+ * idle_hold_timer_interval )
+ * connect_retry_timer_interval )
+ * open_hold_timer_interval ) unset
+ * hold_timer_interval )
+ * keepalive_timer_interval )
+ *
+ * as4 -- unset, false
+ * route_refresh_pre -- unset, false
+ *
+ * su_local -- NULL -- none
+ * su_remote -- NULL -- none
+ *
+ * connections[] -- NULL -- none
+ * active -- false, not yet active
+ * accept -- false, not yet ready to accept()
+ */
+ confirm(bgp_session_null_event == 0) ;
+
+ /* Once the session is fully initialised, can set peer->session pointer.
+ *
+ * NB: this is done last and under the Peer Index Mutex, so that the
+ * accept() code does not trip over.
+ */
+ bgp_peer_index_set_session(peer, session) ;
+
+ return session ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: delete session for given peer.
+ *
+ * This is for use when the peer itself is being deleted. (Peer MUST be in
+ * pDeleting state.)
+ *
+ * Does nothing if there is no session !
+ *
+ * If the session is active, simply sets delete_me flag, which will be honoured
+ * when the session goes dDisabled. Note, it is the callers responsibility
+ * to arrange for that to happen.
+ *
+ * If the session is not active, it is immediately freed.
+ *
+ * NB: if the session is freed, the peer may vanish at the same time !
+ */
+extern void
+bgp_session_delete(bgp_peer peer)
+{
+ bgp_session session = peer->session ;
+
+ if (session == NULL)
+ return ; /* easy if no session anyway */
+
+ assert(peer == session->peer) ;
+
+ /* If is active, set flag so that session is deleted when next it becomes
+ * sDisabled.
+ */
+ if (bgp_session_is_active(session))
+ {
+ session->delete_me = true ;
+ return ;
+ } ;
+
+ /*--------------------------------------------------------------------------*/
+ /* Proceed to free the session structure. */
+
+ /* Make sure that the BGP Engine has, in fact, let go of the session.
+ *
+ * The LOCK/UNLOCK makes sure that the BGP Engine has unlocked the session.
+ *
+ * Without this, the qpt_mutex_destroy() can fail horribly, if the BGP
+ * Engine sends the disable acknowledge before finally unlocking the session.
+ */
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ qpt_mutex_destroy(&session->mutex, 0) ;
+
+ /* Proceed to dismantle the session. */
+
+ bgp_notify_unset(&session->notification);
+ bgp_open_state_free(session->open_send);
+ bgp_open_state_free(session->open_recv);
+ if (session->ifname != NULL)
+ free(session->ifname) ;
+ sockunion_unset(&session->ifaddress) ;
+ sockunion_unset(&session->su_peer) ;
+ if (session->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, session->host);
+ if (session->password != NULL)
+ XFREE(MTYPE_PEER_PASSWORD, session->password);
+ sockunion_unset(&session->su_local) ;
+ sockunion_unset(&session->su_remote) ;
+
+ /* Drop the peer->session and session->peer pointers
+ *
+ * NB: the peer->session pointer is cleared under the Peer Index Mutex,
+ * so that the accept() code does not trip over.
+ *
+ * NB: at this point it is possible for the peer structure to suddenly
+ * vanish -- if peer has been deleted, and has been waiting for the
+ * session to go sDisabled.
+ */
+ bgp_peer_index_set_session(peer, NULL) ;
+
+ session->peer = NULL ;
+ bgp_peer_unlock(peer) ; /* NB: peer->session == NULL */
+
+ /* Zeroize to catch dangling references asap */
+ memset(session, 0, sizeof(struct bgp_session)) ;
+ XFREE(MTYPE_BGP_SESSION, session);
+} ;
+
+/*==============================================================================
+ * Routing Engine: enable session for given peer -- allocate if required.
+ *
+ * Sets up the session given the current state of the peer. If the state
+ * changes, then need to disable the session and re-enable it again with new
+ * parameters -- unless something more cunning is devised.
+ */
+extern void
+bgp_session_enable(bgp_peer peer)
+{
+ bgp_session session ;
+ mqueue_block mqb ;
+
+ assert(peer->state = bgp_peer_pIdle) ;
+
+ /* Set up session if required. Check session if already exists.
+ *
+ * Only the Routing Engine creates sessions, so it is safe to pick up the
+ * peer->session pointer and test it.
+ *
+ * If session exists, it MUST be inactive.
+ *
+ * Routing Engine does not require the mutex while the session is inactive.
+ */
+ session = peer->session ;
+
+ if (session == NULL)
+ session = bgp_session_init_new(peer) ;
+ else
+ {
+ assert(session->peer == peer) ;
+ assert(!bgp_session_is_active(session)) ;
+ } ;
+
+ /* Initialise what we need to make and run connections */
+ session->state = bgp_session_sIdle ;
+ session->delete_me = false ;
+ session->flow_control = 0 ;
+ session->event = bgp_session_null_event ;
+ bgp_notify_unset(&session->notification) ;
+ session->err = 0 ;
+ session->ordinal = 0 ;
+
+ session->open_send = bgp_peer_open_state_init_new(session->open_send, peer) ;
+ bgp_open_state_unset(&session->open_recv) ;
+
+ session->connect = (peer->flags & PEER_FLAG_PASSIVE) == 0 ;
+ session->listen = true ;
+
+ session->ttl = peer->ttl ;
+ session->gtsm = peer->gtsm ;
+ session->port = peer->port ;
+
+ if (session->ifname != NULL)
+ free(session->ifname) ;
+ session->ifindex = 0 ;
+
+ if (peer->ifname != NULL)
+ {
+ session->ifname = strdup(peer->ifname) ;
+ session->ifindex = if_nametoindex(peer->ifname) ;
+ } ;
+
+ sockunion_unset(&session->ifaddress) ;
+ if (peer->update_source != NULL)
+ session->ifaddress = sockunion_dup(peer->update_source) ;
+ else if (peer->update_if != NULL)
+ session->ifaddress = bgp_peer_get_ifaddress(peer, peer->update_if,
+ peer->su.sa.sa_family) ;
+ session->as_peer = peer->as ;
+ sockunion_set_dup(&session->su_peer, &peer->su) ;
+
+ session->log = peer->log ;
+
+ /* take copies of host and password */
+ if (session->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, session->host);
+ session->host = (peer->host != NULL)
+ ? XSTRDUP(MTYPE_BGP_PEER_HOST, peer->host)
+ : NULL;
+ if (session->password != NULL)
+ XFREE(MTYPE_PEER_PASSWORD, session->password);
+ session->password = (peer->password != NULL)
+ ? XSTRDUP(MTYPE_PEER_PASSWORD, peer->password)
+ : NULL;
+
+ 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 = 4 * 60;
+ session->hold_timer_interval = peer->v_holdtime ;
+ session->keepalive_timer_interval = peer->v_keepalive ;
+
+ session->as4 = false ;
+ session->route_refresh_pre = false ;
+ session->orf_prefix_pre = false ;
+
+ /* su_local set when session Established */
+ /* su_remote set when session Established */
+
+ /* TODO: check whether session stats should persist */
+ memset(&session->stats, 0, sizeof(struct bgp_session_stats)) ;
+
+ memset(&session->connections, 0,
+ sizeof(bgp_connection) * bgp_connection_count) ;
+
+ session->active = false ;
+ session->accept = false ;
+
+ /* Routeing Engine does the state change now. */
+
+ /* Now pass the session to the BGP Engine, which will set about */
+ /* making and running a connection to the peer. */
+
+ mqb = mqb_init_new(NULL, bgp_session_do_enable, session) ;
+
+ confirm(sizeof(struct bgp_session_enable_args) == 0) ;
+
+ session->state = bgp_session_sEnabled;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: session enable message action
+ */
+static void
+bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag)
+{
+ if (flag == mqb_action)
+ {
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ session->active = true ;
+ bgp_fsm_enable_session(session) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ } ;
+
+ mqb_free(mqb) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: disable session for given peer -- if enabled (!).
+ *
+ * Does nothing if the session is not sEnabled or sEstablished.
+ *
+ * Passes any bgp_notify to the BGP Engine, which will dispose of it in due
+ * course.
+ *
+ * If no bgp_notify provided, no notify will be sent.
+ *
+ * The BGP Engine will stop the session -- unless it is already stopped due to
+ * some event in the BGP Engine. In any case, the BGP Engine will respond with
+ * an eDisabled.
+ *
+ * NB: is taking responsibility for the notification, which is either freed
+ * here or passed to the BGP Engine.
+ */
+extern void
+bgp_session_disable(bgp_peer peer, bgp_notify notification)
+{
+ bgp_session session ;
+ mqueue_block mqb ;
+ struct bgp_session_disable_args* args ;
+
+ session = peer->session ;
+ assert((session != NULL) && (session->peer == peer)) ;
+
+ /* Do nothing if session is not active, or is already limping. */
+
+ if ( (session->state != bgp_session_sEnabled) &&
+ (session->state != bgp_session_sEstablished) )
+ {
+ bgp_notify_free(notification) ; /* discard any bgp_notify */
+ return ;
+ } ;
+
+ /* Can revoke whatever may be queued already. Will revoke again when the
+ * disable is acknowledged to finally clear the session out of the queue.
+ */
+ mqueue_revoke(routing_nexus->queue, session) ;
+
+ /* Now change to limping state */
+ session->state = bgp_session_sLimping;
+
+ /* Ask the BGP engine to disable the session.
+ *
+ * NB: the session may already be stopped when the BGP Engine sees this
+ * message:
+ *
+ * * the disable is being issued in response to a stopped event from
+ * the BGP Engine.
+ *
+ * * the session is stopped, but the message to the Routing Engine is
+ * still in its message queue.
+ *
+ * * the session is stopped while the disable message is in the
+ * BGP Engine queue.
+ *
+ * in any case, the BGP Engine responds with an eDisabled message to
+ * acknowledge the disable request -- and the session will then be
+ * disabled.
+ *
+ * NB: The BGP Engine will discard any outstanding work for the session.
+ *
+ * The Routing Engine should discard all further messages for this
+ * session up to the eDisabled, and must then discard any other
+ * messages for the session.
+ *
+ * NB: the Routing Engine MUST not issue any further messages until it sees
+ * the returned eDisabled event.
+ */
+ mqb = mqb_init_new(NULL, bgp_session_do_disable, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->notification = notification ;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_priority) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: session disable message action
+ *
+ * NB: either passes the notification to the FSM or frees it here.
+ */
+static void
+bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_session session = mqb_get_arg0(mqb) ;
+ struct bgp_session_disable_args* args = mqb_get_args(mqb) ;
+
+ if (flag == mqb_action)
+ {
+ /* Immediately discard any other messages for this session. */
+ mqueue_revoke(bgp_nexus->queue, session) ;
+
+ /* Get the FSM to send any notification and close connections */
+ bgp_fsm_disable_session(session, args->notification) ;
+ }
+ else
+ bgp_notify_free(args->notification) ;
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * BGP Engine: send session event signal to Routeing Engine
+ *
+ * NB: is passing responsibility for the notification to the Routing Engine.
+ */
+extern void
+bgp_session_event(bgp_session session, bgp_session_event_t event,
+ bgp_notify notification,
+ int err,
+ bgp_connection_ord_t ordinal,
+ bool stopped)
+{
+ struct bgp_session_event_args* args ;
+ mqueue_block mqb ;
+
+ if (stopped)
+ {
+ session->active = false ; /* ignore updates etc */
+ session->accept = false ; /* for completeness */
+ } ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_event, session) ;
+
+ args = mqb_get_args(mqb) ;
+
+ args->event = event ;
+ args->notification = notification ;
+ args->err = err ;
+ args->ordinal = ordinal ;
+ args->stopped = stopped,
+
+ ++routing_engine_queue_stats.event ;
+
+ bgp_to_routing_engine(mqb, stopped ? mqb_priority : mqb_ordinary) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: dispatch update(s) to peer -> BGP Engine
+ *
+ * PRO TEM -- this is being passed the pre-packaged BGP message(s).
+ *
+ * The BGP Engine takes care of discarding the stream block(s) once dealt with.
+ */
+extern void
+bgp_session_update_send(bgp_session session, struct stream_fifo* fifo)
+{
+ struct bgp_session_update_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_update_send, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->buf = stream_fifo_head(fifo) ;
+ args->is_pending = NULL ;
+ args->xon_kick = (session->flow_control == BGP_XON_KICK);
+
+ ++bgp_engine_queue_stats.update ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+
+ stream_fifo_reset(fifo) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: write given BGP update message(s) -- mqb action function.
+ *
+ * Each connection has a pending queue associated with it, onto which messages
+ * are put if the connection's write buffer is unable to absorb any further
+ * messages.
+ *
+ * This function is called both when the mqb is received from the Routing
+ * Engine, and when the BGP Engine is trying to empty the connection's pending
+ * queue.
+ *
+ * When the mqb is received from the Routing Engine, then:
+ *
+ * -- if the connection's pending queue is empty, try to send the message(s).
+ *
+ * When the mqb is from connection's pending queue, then:
+ *
+ * -- try to send the message(s).
+ *
+ * In any case, if cannot send all the message(s), add it (back) to the
+ * connection's pending queue.
+ *
+ * If the mqb has been dealt with, it is freed, along with the stream buffer.
+ * Also, update the flow control counter, and issue XON if required.
+ */
+static void
+bgp_session_do_update_send(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct bgp_session_update_args* args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ while (args->buf != NULL)
+ {
+ struct stream* buf ;
+
+ if ((flag == mqb_action) && session->active)
+ {
+ bgp_connection connection ;
+
+ connection = session->connections[bgp_connection_primary] ;
+ assert(connection != NULL) ;
+
+ /* If established, try and send. */
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ int ret ;
+ ret = bgp_connection_no_pending(connection, &args->is_pending) ;
+
+ if (ret != 0)
+ ret = bgp_msg_send_update(connection, args->buf) ;
+
+ if (ret == 0)
+ {
+ /* Either there is already a pending queue, or the message
+ * could not be sent (and has not failed) -- so add to the
+ * pending queue.
+ */
+ bgp_connection_add_pending(connection, mqb,
+ &args->is_pending) ;
+ return ; /* Quit now, with message intact. */
+ }
+ } ;
+ } ;
+
+ buf = args->buf ;
+ args->buf = buf->next ;
+
+ stream_free(buf) ;
+ } ;
+
+ /* If gets to here, then has dealt with all message(s). */
+ if ((flag == mqb_action) && (args->xon_kick))
+ bgp_session_XON(session) ;
+
+ mqb_free(mqb) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: are we in XON state ?
+ */
+extern int
+bgp_session_is_XON(bgp_peer peer)
+{
+ int result = 0;
+ bgp_session session = peer->session;
+
+ result = (session->flow_control > 0);
+
+ return result;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Count down flow control -- signal if reached XON point.
+ */
+extern int
+bgp_session_dec_flow_count(bgp_peer peer)
+{
+ bgp_session session = peer->session;
+
+ assert(session->flow_control > 0) ;
+ return (--session->flow_control == BGP_XON_KICK) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: dispatch Route Refresh to peer -> BGP Engine
+ *
+ * The BGP Engine takes care of discarding the bgp_route_refresh once it's been
+ * dealt with.
+ */
+extern void
+bgp_session_route_refresh_send(bgp_session session, bgp_route_refresh rr)
+{
+ struct bgp_session_route_refresh_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_route_refresh_send, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->rr = rr ;
+ args->is_pending = NULL ;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: write given BGP route refresh message -- mqb action function.
+ *
+ * The logic here is the same as for bgp_session_do_update_send -- except that
+ * there is no flow control (!).
+ */
+static void
+bgp_session_do_route_refresh_send(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct bgp_session_route_refresh_args* args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ if ((flag == mqb_action) && session->active)
+ {
+ bgp_connection connection = session->connections[bgp_connection_primary] ;
+ assert(connection != NULL) ;
+
+ /* If established, try and send. */
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ int ret = bgp_connection_no_pending(connection, &args->is_pending) ;
+
+ if (ret != 0)
+ ret = bgp_msg_send_route_refresh(connection, args->rr) ;
+
+ if (ret == 0)
+ {
+ /* Either there is already a pending queue, or the message
+ * could not be sent (and has not failed) -- so add to the
+ * pending queue.
+ */
+ bgp_connection_add_pending(connection, mqb, &args->is_pending) ;
+ return ; /* Quit now, with message intact. */
+ } ;
+ } ;
+ } ;
+
+ bgp_route_refresh_free(args->rr) ;
+ mqb_free(mqb) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: dispatch End-of-RIB to peer -> BGP Engine
+ */
+extern void
+bgp_session_end_of_rib_send(bgp_session session, qAFI_t afi, qSAFI_t safi)
+{
+ struct bgp_session_end_of_rib_args* args ;
+ mqueue_block mqb ;
+ qafx_num_t qafx ;
+
+ qafx = qafx_num_from_qAFI_qSAFI(afi, safi) ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_end_of_rib_send, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->afi = get_iAFI(qafx) ;
+ args->safi = get_iSAFI(qafx) ;
+ args->is_pending = NULL ;
+
+ ++bgp_engine_queue_stats.xon ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: write given BGP end-of-RIB message -- mqb action function.
+ *
+ * The logic here is the same as for bgp_session_do_update_send -- except that
+ * there is no flow control (!).
+ */
+static void
+bgp_session_do_end_of_rib_send(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct bgp_session_end_of_rib_args* args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ if ((flag == mqb_action) && session->active)
+ {
+ bgp_connection connection = session->connections[bgp_connection_primary] ;
+ assert(connection != NULL) ;
+
+ /* If established, try and send. */
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ int ret = bgp_connection_no_pending(connection, &args->is_pending) ;
+
+ if (ret != 0)
+ ret = bgp_msg_send_end_of_rib(connection, args->afi, args->safi) ;
+
+ if (ret == 0)
+ {
+ /* Either there is already a pending queue, or the message
+ * could not be sent (and has not failed) -- so add to the
+ * pending queue.
+ */
+ bgp_connection_add_pending(connection, mqb, &args->is_pending) ;
+
+ return ; /* Quit now, with message intact. */
+ } ;
+ } ;
+ } ;
+
+ mqb_free(mqb) ;
+} ;
+
+/*==============================================================================
+ * BGP Engine: forward incoming update -> Routing Engine
+ *
+ * PRO TEM -- this is being passed the raw BGP message.
+ *
+ * The Routing Engine takes care of discarding the stream block once it's been
+ * dealt with.
+ */
+extern void
+bgp_session_update_recv(bgp_session session, struct stream* buf, bgp_size_t size)
+{
+ struct bgp_session_update_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_update_recv, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->buf = stream_dup(buf) ;
+ args->size = size;
+ args->xon_kick = 0;
+
+ ++routing_engine_queue_stats.update ;
+
+ bgp_to_routing_engine(mqb, mqb_ordinary) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: process incoming update message -- mqb action function.
+ *
+ * Discard the update if the session is not sEstablished.
+ */
+static void
+bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_session session = mqb_get_arg0(mqb) ;
+ struct bgp_session_update_args* args = mqb_get_args(mqb) ;
+
+ if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) )
+ {
+ bgp_peer peer = session->peer;
+ stream_free(peer->ibuf);
+ peer->ibuf = args->buf;
+ bgp_update_receive (peer, args->size);
+ }
+ else
+ stream_free(args->buf) ;
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * BGP Engine: received Route Refresh to peer
+ *
+ * The Routing Engine takes care of discarding the bgp_route_refresh once
+ * it's been dealt with.
+ */
+extern void
+bgp_session_route_refresh_recv(bgp_session session, bgp_route_refresh rr)
+{
+ struct bgp_session_route_refresh_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_route_refresh_recv, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->rr = rr ;
+ args->is_pending = NULL ;
+
+ bgp_to_routing_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: receive given BGP route refresh message -- mqb action
+ * function.
+ */
+static void
+bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct bgp_session_route_refresh_args* args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) )
+ bgp_route_refresh_recv(session->peer, args->rr) ;
+
+ bgp_route_refresh_free(args->rr);
+ mqb_free(mqb);
+}
+
+/*==============================================================================
+ * BGP Engine: send XON message to Routing Engine
+ *
+ * Can be sent more packets now
+ */
+static void
+bgp_session_XON(bgp_session session)
+{
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_XON, session) ;
+
+ confirm(sizeof(struct bgp_session_XON_args) == 0) ;
+
+ ++routing_engine_queue_stats.xon ;
+
+ bgp_to_routing_engine(mqb, mqb_ordinary) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: process incoming XON message -- mqb action function.
+ */
+static void
+bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) )
+ {
+ int xoff = (session->flow_control <= 0);
+
+ session->flow_control = BGP_XON_REFRESH;
+ if (xoff)
+ bgp_write (session->peer, NULL) ;
+ }
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * Routing Engine: send set ttl message to BGP Engine, if session is active.
+ */
+void
+bgp_session_set_ttl(bgp_session session, int ttl, bool gtsm)
+{
+ mqueue_block mqb ;
+ struct bgp_session_ttl_args *args;
+
+ if (bgp_session_is_active(session))
+ {
+ mqb = mqb_init_new(NULL, bgp_session_do_set_ttl, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->ttl = ttl ;
+ args->gtsm = gtsm ;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+ } ;
+}
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: process set ttl message -- mqb action function.
+ */
+static void
+bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag)
+{
+
+ if (flag == mqb_action)
+ {
+ bgp_session session = mqb_get_arg0(mqb) ;
+ struct bgp_session_ttl_args *args = mqb_get_args(mqb) ;
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ session->ttl = args->ttl ;
+ session->gtsm = args->gtsm ;
+
+ bgp_set_new_ttl(session->connections[bgp_connection_primary],
+ session->ttl, session->gtsm) ;
+ bgp_set_new_ttl(session->connections[bgp_connection_secondary],
+ session->ttl, session->gtsm) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ }
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * Session data access functions.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: see if session exists and is active.
+ *
+ * If exists then performs a few checks, just to make sure things are straight.
+ *
+ * NB: accessing Routing Engine "private" variable -- no lock required.
+ *
+ * checks session->active, only when not active -- no lock required.
+ */
+extern bool
+bgp_session_is_active(bgp_session session)
+{
+ bool active ;
+
+ if (session == NULL)
+ active = false ;
+ else
+ {
+ switch (session->state)
+ {
+ case bgp_session_sIdle:
+ case bgp_session_sDisabled:
+ assert(!session->active) ;
+ active = false ;
+ break ;
+
+ case bgp_session_sEnabled:
+ case bgp_session_sEstablished:
+ case bgp_session_sLimping:
+ active = true ;
+ break ;
+
+ default:
+ zabort("invalid session->state") ;
+ } ;
+ } ;
+
+ return active ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get a copy of the session statistics, copied all at once so
+ * forms a consistent snapshot
+ */
+void
+bgp_session_get_stats(bgp_session session, struct bgp_session_stats *stats)
+{
+ if (session == NULL)
+ {
+ memset(stats, 0, sizeof(struct bgp_session_stats)) ;
+ return;
+ }
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ *stats = session->stats;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+}
diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h
new file mode 100644
index 00000000..24bb5cd6
--- /dev/null
+++ b/bgpd/bgp_session.h
@@ -0,0 +1,378 @@
+/* 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 <stdbool.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 "bgpd/bgp_route_refresh.h"
+#include "bgpd/bgp_peer_index.h"
+
+#include "lib/qtimers.h"
+#include "lib/qpthreads.h"
+#include "lib/sockunion.h"
+#include "lib/mqueue.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.
+ *
+ * For simplicity, the BGP Engine may lock the session associated with the
+ * connection it is dealing with.
+ *
+ * Parts of the session structure are private to the Routing Engine, and
+ * do not require the mutex for access.
+ *
+ * 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.
+ */
+
+/* Statistics */
+struct bgp_session_stats
+{
+ 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. */
+};
+
+struct bgp_session
+{
+ /* The following is set when the session is created, and not changed
+ * thereafter, so do not need to lock the session to access this.
+ */
+ bgp_peer peer ; /* peer whose session this is */
+
+ /* This is a *recursive* mutex */
+ qpt_mutex_t mutex ; /* for access to the rest */
+
+ /* While sIdle and sDisabled -- aka not "active" states:
+ *
+ * the session belongs to the Routing Engine.
+ *
+ * The BGP Engine will not touch a session in these states and the
+ * Routing Engine may do what it likes with it.
+ *
+ * While sEnabled, sEstablished and sLimping -- aka "active" states:
+ *
+ * the session belongs to the BGP Engine.
+ *
+ * A (very) few items in the session may be accessed by the Routing Engine,
+ * as noted below. (Subject to the mutex.)
+ *
+ * Only the Routing Engine creates and destroys sessions. The BGP Engine
+ * assumes that a session will not be destroyed while it is sEnabled,
+ * sEstablished or sStopping.
+ *
+ * These are private to the Routing Engine.
+ */
+ bgp_session_state_t state ;
+
+ int flow_control ; /* limits number of updates sent
+ by the Routing Engine */
+
+ bool delete_me ; /* when next goes sDisabled */
+
+ /* These are private to the Routing Engine, and are set each time a session
+ * event message is received from the BGP Engine.
+ */
+ bgp_session_event_t event ; /* last event */
+ bgp_notify notification ; /* if any sent/received */
+ int err ; /* errno, if any */
+ bgp_connection_ord_t ordinal ; /* primary/secondary connection */
+
+ /* The Routeing Engine sets open_send and clears open_recv before enabling
+ * the session, and may not change them while sEnabled/sEstablished.
+ *
+ * The as_expected is the AS configured for the far end -- which is what
+ * expect to see in the incoming OPEN.
+ *
+ * The BGP Engine sets open_recv signalling the session eEstablished, and
+ * will not touch it thereafter.
+ */
+ bgp_open_state open_send ; /* how to open the session */
+ bgp_open_state open_recv ; /* set when session Established */
+
+ /* The following are set by the Routeing Engine before a session is
+ * enabled, and not changed at any other time by either engine.
+ */
+ bool connect ; /* initiate connections */
+ bool listen ; /* listen for connections */
+
+ bool cap_suppress ; /* always set false when session is
+ enabled. Set to state of connection
+ when session is established */
+
+ bool cap_override ; /* assume other end can do all afi/safi
+ this end has active */
+ bool cap_strict ; /* must recognise all capabilities
+ received and have exact afi/safi
+ match */
+
+ int ttl ; /* TTL to set, if not zero */
+ bool gtsm ; /* ttl set by ttl-security */
+ unsigned short port ; /* destination port for peer */
+
+ /* TODO: ifindex and ifaddress should be rebound if the peer hears any
+ * bgp_session_eTCP_failed or bgp_session_eTCP_error -- in case interface
+ * state has changed, for the better.
+ */
+ char* ifname ; /* interface to bind to, if any */
+ unsigned ifindex ; /* and its index, if any */
+ union sockunion* ifaddress ; /* address to bind to, if any */
+
+ as_t as_peer ; /* ASN of the peer */
+ union sockunion* su_peer ; /* Sockunion address of the peer */
+
+ 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 ;
+
+ /* These are set by the Routeing Engine before a session is enabled,
+ * but are affected by the capabilities received in the OPEN message.
+ *
+ * When the session is established, the BGP Engine sets these.
+ */
+ unsigned hold_timer_interval ; /* subject to negotiation */
+ unsigned keepalive_timer_interval ; /* subject to negotiation */
+
+ bool as4 ; /* set by OPEN */
+ bool route_refresh_pre ; /* use pre-RFC version */
+ bool orf_prefix_pre ; /* use pre-RFC version */
+
+ /* These are cleared by the Routeing Engine before a session is enabled,
+ * and set by the BGP Engine when the session is established.
+ */
+ union sockunion* su_local ; /* set when session Established */
+ union sockunion* su_remote ; /* set when session Established */
+
+ /* Statistics */
+ struct bgp_session_stats stats;
+
+ /* These values are are private to the BGP Engine.
+ *
+ * They must be cleared before the session is enabled, but may not be
+ * touched by the Routeing Engine at any other time.
+ *
+ * Before stopping a session the BGP Engine unlinks any connections from
+ * the session, and sets the stopped flag.
+ *
+ * The active flag is set when one or more connections are activated, and
+ * cleared when either the BGP Engine stops the session or the Routing
+ * Engine disables it. When not "active" all messages other than disable
+ * and enable are ignored. This deals with the hiatus that exists between
+ * the BGP Engine signalling that it has stopped (because of some exception)
+ * and the Routing Engine acknowledging that (by disabling the session).
+ *
+ * The accept flag is set when the secondary connection is completely ready
+ * to accept connections. It is cleared otherwise, or when the active flag
+ * is cleared.
+ */
+ bgp_connection connections[bgp_connection_count] ;
+
+ bool active ;
+ bool accept ;
+} ;
+
+/*==============================================================================
+ * Mqueue messages related to sessions
+ *
+ * In all these messages arg0 is the session.
+ */
+
+struct bgp_session_enable_args /* to BGP Engine */
+{
+ /* no further arguments */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_enable_args) ;
+
+struct bgp_session_disable_args /* to BGP Engine */
+{
+ bgp_notify notification ; /* NOTIFICATION to send */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_enable_args) ;
+
+struct bgp_session_update_args /* to and from BGP Engine */
+{
+ struct stream* buf ;
+ bgp_size_t size ;
+ int xon_kick; /* send XON when processed this */
+
+ bgp_connection is_pending ; /* used inside the BGP Engine */
+ /* set NULL on message creation */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_update_args) ;
+
+struct bgp_session_route_refresh_args /* to and from BGP Engine */
+{
+ bgp_route_refresh rr ;
+
+ bgp_connection is_pending ; /* used inside the BGP Engine */
+ /* set NULL on message creation */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_route_refresh_args) ;
+
+struct bgp_session_end_of_rib_args /* to and from BGP Engine */
+{
+ iAFI_t afi ;
+ iSAFI_t safi ;
+
+ bgp_connection is_pending ; /* used inside the BGP Engine */
+ /* set NULL on message creation */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_end_of_rib_args) ;
+
+struct bgp_session_event_args /* to Routeing Engine */
+{
+ bgp_session_event_t event ; /* what just happened */
+ bgp_notify notification ; /* sent or received (if any) */
+ int err ; /* errno if any */
+ bgp_connection_ord_t ordinal ; /* primary/secondary connection */
+ int stopped ; /* session has stopped */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_event_args) ;
+
+struct bgp_session_XON_args /* to Routeing Engine */
+{
+ /* no further arguments */
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_XON_args) ;
+enum { BGP_XON_REFRESH = 40,
+ BGP_XON_KICK = 20,
+} ;
+
+struct bgp_session_ttl_args /* to bgp Engine */
+{
+ int ttl ;
+ bool gtsm ;
+} ;
+MQB_ARGS_SIZE_OK(bgp_session_ttl_args) ;
+
+/*==============================================================================
+ * 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) ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern bgp_session
+bgp_session_init_new(bgp_peer peer) ;
+
+extern void
+bgp_session_enable(bgp_peer peer) ;
+
+extern void
+bgp_session_disable(bgp_peer peer, bgp_notify notification) ;
+
+extern void
+bgp_session_delete(bgp_peer peer);
+
+extern void
+bgp_session_event(bgp_session session, bgp_session_event_t event,
+ bgp_notify notification,
+ int err,
+ bgp_connection_ord_t ordinal,
+ bool stopped) ;
+
+extern void
+bgp_session_update_send(bgp_session session, struct stream_fifo* fifo) ;
+
+extern void
+bgp_session_route_refresh_send(bgp_session session, bgp_route_refresh rr) ;
+
+extern void
+bgp_session_end_of_rib_send(bgp_session session, qAFI_t afi, qSAFI_t) ;
+
+extern void
+bgp_session_update_recv(bgp_session session, struct stream* buf,
+ bgp_size_t size) ;
+
+extern void
+bgp_session_route_refresh_recv(bgp_session session, bgp_route_refresh rr);
+
+extern int
+bgp_session_is_XON(bgp_peer peer);
+extern int
+bgp_session_dec_flow_count(bgp_peer peer) ;
+
+extern void
+bgp_session_set_ttl(bgp_session session, int ttl, bool gtsm);
+
+extern void
+bgp_session_get_stats(bgp_session session, struct bgp_session_stats *stats);
+
+/*==============================================================================
+ * Session data access functions.
+ */
+
+extern bool
+bgp_session_is_active(bgp_session session) ;
+
+#endif /* QUAGGA_BGP_SESSION_H */
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index 86cc0879..eaa41e6c 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -415,6 +415,7 @@ write_bgpPeerTable (int action, u_char *var_val,
printf ("val: %ld\n", intval);
+ /* TODO: wire up timer interval settings so that session sees them */
switch (v->magic)
{
case BGPPEERADMINSTATUS:
@@ -422,7 +423,8 @@ write_bgpPeerTable (int action, u_char *var_val,
#define BGP_PeerAdmin_start 2
/* When the peer is established, */
if (intval == BGP_PeerAdmin_stop)
- BGP_EVENT_ADD (peer, BGP_Stop);
+/* TODO: wire up SNMP BGPPEERADMINSTATUS BGP_PeerAdmin_stop ?? */
+/* BGP_EVENT_ADD (peer, BGP_Stop) */ ;
else if (intval == BGP_PeerAdmin_start)
; /* Do nothing. */
else
@@ -430,21 +432,21 @@ write_bgpPeerTable (int action, u_char *var_val,
break;
case BGPPEERCONNECTRETRYINTERVAL:
SET_FLAG (peer->config, PEER_CONFIG_CONNECT);
- peer->connect = intval;
+ peer->connect = intval;
peer->v_connect = intval;
break;
case BGPPEERHOLDTIMECONFIGURED:
SET_FLAG (peer->config, PEER_CONFIG_TIMER);
- peer->holdtime = intval;
+ peer->holdtime = intval;
peer->v_holdtime = intval;
break;
case BGPPEERKEEPALIVECONFIGURED:
SET_FLAG (peer->config, PEER_CONFIG_TIMER);
- peer->keepalive = intval;
+ peer->keepalive = intval;
peer->v_keepalive = intval;
break;
case BGPPEERMINASORIGINATIONINTERVAL:
- peer->v_asorig = intval;
+ peer->v_asorig = intval;
break;
case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
peer->v_routeadv = intval;
@@ -459,6 +461,7 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length,
{
static struct in_addr addr;
struct peer *peer;
+ struct bgp_session_stats stats;
*write_method = NULL;
memset (&addr, 0, sizeof (struct in_addr));
@@ -467,13 +470,15 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length,
if (! peer)
return NULL;
+ bgp_session_get_stats(peer->session, &stats);
+
switch (v->magic)
{
case BGPPEERIDENTIFIER:
return SNMP_IPADDRESS (peer->remote_id);
break;
case BGPPEERSTATE:
- return SNMP_INTEGER (peer->status);
+ return SNMP_INTEGER (peer->state); /* TODO: reconstruct old value */
break;
case BGPPEERADMINSTATUS:
*write_method = write_bgpPeerTable;
@@ -515,26 +520,29 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length,
return SNMP_INTEGER (peer->as);
break;
case BGPPEERINUPDATES:
- return SNMP_INTEGER (peer->update_in);
+ return SNMP_INTEGER (stats.update_in);
break;
case BGPPEEROUTUPDATES:
- return SNMP_INTEGER (peer->update_out);
+ return SNMP_INTEGER (stats.update_out);
break;
case BGPPEERINTOTALMESSAGES:
- return SNMP_INTEGER (peer->open_in + peer->update_in
- + peer->keepalive_in + peer->notify_in
- + peer->refresh_in + peer->dynamic_cap_in);
+ return SNMP_INTEGER (stats.open_in + stats.update_in
+ + stats.keepalive_in + stats.notify_in
+ + stats.refresh_in + stats.dynamic_cap_in);
break;
case BGPPEEROUTTOTALMESSAGES:
- return SNMP_INTEGER (peer->open_out + peer->update_out
- + peer->keepalive_out + peer->notify_out
- + peer->refresh_out + peer->dynamic_cap_out);
+ return SNMP_INTEGER (stats.open_out + stats.update_out
+ + stats.keepalive_out + stats.notify_out
+ + stats.refresh_out + stats.dynamic_cap_out);
break;
case BGPPEERLASTERROR:
{
static u_char lasterror[2];
- lasterror[0] = peer->notify.code;
- lasterror[1] = peer->notify.subcode;
+ bgp_notify notification = NULL ;
+ if (peer->session != NULL)
+ notification = peer->session->notification ;
+ lasterror[0] = (notification != NULL) ? notification->code : 0 ;
+ lasterror[1] = (notification != NULL) ? notification->subcode : 0 ;
*var_len = 2;
return (u_char *)&lasterror;
}
@@ -581,10 +589,10 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length,
return SNMP_INTEGER (peer->v_routeadv);
break;
case BGPPEERINUPDATEELAPSEDTIME:
- if (peer->update_time == 0)
+ if (stats.update_time == 0)
return SNMP_INTEGER (0);
else
- return SNMP_INTEGER (bgp_clock () - peer->update_time);
+ return SNMP_INTEGER (bgp_clock () - stats.update_time);
break;
default:
return NULL;
@@ -662,7 +670,7 @@ bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
{
bgp_unlock_node (rn);
- for (binfo = rn->info; binfo; binfo = binfo->next)
+ for (binfo = rn->info; binfo; binfo = binfo->info_next)
if (sockunion_same (&binfo->peer->su, &su))
return binfo;
}
@@ -715,7 +723,7 @@ bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
{
min = NULL;
- for (binfo = rn->info; binfo; binfo = binfo->next)
+ for (binfo = rn->info; binfo; binfo = binfo->info_next)
{
if (binfo->peer->su.sin.sin_family == AF_INET
&& ntohl (paddr.s_addr)
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index a249c23d..5d1ebf3d 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -30,7 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
static void bgp_node_delete (struct bgp_node *);
static void bgp_table_free (struct bgp_table *);
-
+
struct bgp_table *
bgp_table_init (afi_t afi, safi_t safi)
{
@@ -42,7 +42,7 @@ bgp_table_init (afi_t afi, safi_t safi)
rt->type = BGP_TABLE_MAIN;
rt->afi = afi;
rt->safi = safi;
-
+
return rt;
}
@@ -83,7 +83,7 @@ static struct bgp_node *
bgp_node_set (struct bgp_table *table, struct prefix *prefix)
{
struct bgp_node *node;
-
+
node = bgp_node_create ();
prefix_copy (&node->p, prefix);
@@ -96,6 +96,7 @@ bgp_node_set (struct bgp_table *table, struct prefix *prefix)
static void
bgp_node_free (struct bgp_node *node)
{
+ node->lock = -54321 ;
XFREE (MTYPE_BGP_NODE, node);
}
@@ -105,7 +106,7 @@ bgp_table_free (struct bgp_table *rt)
{
struct bgp_node *tmp_node;
struct bgp_node *node;
-
+
if (rt == NULL)
return;
@@ -128,11 +129,17 @@ bgp_table_free (struct bgp_table *rt)
continue;
}
+ assert( (node->info == NULL)
+ && (node->adj_out == NULL)
+ && (node->adj_in == NULL)
+ && (node->on_wq == 0) ) ;
+
tmp_node = node;
node = node->parent;
tmp_node->table->count--;
tmp_node->lock = 0; /* to cause assert if unlocked after this */
+
bgp_node_free (tmp_node);
if (node != NULL)
@@ -147,21 +154,22 @@ bgp_table_free (struct bgp_table *rt)
break;
}
}
-
+
assert (rt->count == 0);
if (rt->owner)
{
- peer_unlock (rt->owner);
+ bgp_peer_unlock (rt->owner);
rt->owner = NULL;
}
+ rt->lock = -54321 ;
XFREE (MTYPE_BGP_TABLE, rt);
return;
}
/* Utility mask array. */
-static u_char maskbit[] =
+static const u_char maskbit[] =
{
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
};
@@ -241,7 +249,7 @@ bgp_node_match (const struct bgp_table *table, struct prefix *p)
/* Walk down tree. If there is matched route then store it to
matched. */
- while (node && node->p.prefixlen <= p->prefixlen &&
+ while (node && node->p.prefixlen <= p->prefixlen &&
prefix_match (&node->p, p))
{
if (node->info)
@@ -292,7 +300,7 @@ bgp_node_lookup (const struct bgp_table *table, struct prefix *p)
node = table->top;
- while (node && node->p.prefixlen <= p->prefixlen &&
+ while (node && node->p.prefixlen <= p->prefixlen &&
prefix_match (&node->p, p))
{
if (node->p.prefixlen == p->prefixlen && node->info)
@@ -314,7 +322,7 @@ bgp_node_get (struct bgp_table *const table, struct prefix *p)
match = NULL;
node = table->top;
- while (node && node->p.prefixlen <= p->prefixlen &&
+ while (node && node->p.prefixlen <= p->prefixlen &&
prefix_match (&node->p, p))
{
if (node->p.prefixlen == p->prefixlen)
@@ -357,7 +365,7 @@ bgp_node_get (struct bgp_table *const table, struct prefix *p)
}
table->count++;
bgp_lock_node (new);
-
+
return new;
}
@@ -370,6 +378,7 @@ bgp_node_delete (struct bgp_node *node)
assert (node->lock == 0);
assert (node->info == NULL);
+ assert (node->on_wq == 0) ;
if (node->l_left && node->l_right)
return;
@@ -393,9 +402,9 @@ bgp_node_delete (struct bgp_node *node)
}
else
node->table->top = child;
-
+
node->table->count--;
-
+
bgp_node_free (node);
/* If parent node is stub then delete it also. */
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 53df0bc6..8cb1a9ed 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -30,18 +30,18 @@ typedef enum
struct bgp_table
{
bgp_table_t type;
-
+
/* afi/safi of this table */
afi_t afi;
safi_t safi;
-
+
int lock;
/* The owner of this 'bgp_table' structure. */
struct peer *owner;
struct bgp_node *top;
-
+
unsigned long count;
};
@@ -65,8 +65,8 @@ struct bgp_node
int lock;
- u_char flags;
-#define BGP_NODE_PROCESS_SCHEDULED (1 << 0)
+ struct bgp_node* wq_next ;
+ uint8_t on_wq ;
};
extern struct bgp_table *bgp_table_init (afi_t, safi_t);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index e7e7dba1..48d8ecbe 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"
@@ -48,6 +49,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_session.h"
extern struct in_addr router_id_zebra;
@@ -55,7 +57,9 @@ extern struct in_addr router_id_zebra;
afi_t
bgp_node_afi (struct vty *vty)
{
- if (vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE)
+ enum node_type node = vty_get_node(vty) ;
+
+ if (node == BGP_IPV6_NODE || node == BGP_IPV6M_NODE)
return AFI_IP6;
return AFI_IP;
}
@@ -65,9 +69,11 @@ bgp_node_afi (struct vty *vty)
safi_t
bgp_node_safi (struct vty *vty)
{
- if (vty->node == BGP_VPNV4_NODE)
+ enum node_type node = vty_get_node(vty) ;
+
+ if (node == BGP_VPNV4_NODE)
return SAFI_MPLS_VPN;
- if (vty->node == BGP_IPV4M_NODE || vty->node == BGP_IPV6M_NODE)
+ if (node == BGP_IPV4M_NODE || node == BGP_IPV6M_NODE)
return SAFI_MULTICAST;
return SAFI_UNICAST;
}
@@ -213,7 +219,9 @@ bgp_vty_return (struct vty *vty, int ret)
case BGP_ERR_TCPSIG_FAILED:
str = "Error while applying TCP-Sig to session(s)";
break;
- case BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK:
+ case BGP_ERR_PEER_EXISTS:
+ str = "Cannot have the same neighbor in different bgp views";
+ case BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM:
str = "ebgp-multihop and ttl-security cannot be configured together";
break;
case BGP_ERR_NO_IBGP_WITH_TTLHACK:
@@ -313,10 +321,10 @@ DEFUN_DEPRECATED (neighbor_version,
{
return CMD_SUCCESS;
}
-
+
/* "router bgp" commands. */
-DEFUN (router_bgp,
- router_bgp_cmd,
+DEFUN (router_bgp,
+ router_bgp_cmd,
"router bgp " CMD_AS_RANGE,
ROUTER_STR
BGP_STR
@@ -336,7 +344,7 @@ DEFUN (router_bgp,
switch (ret)
{
case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET:
- vty_out (vty, "Please specify 'bgp multiple-instance' first%s",
+ vty_out (vty, "Please specify 'bgp multiple-instance' first%s",
VTY_NEWLINE);
return CMD_WARNING;
case BGP_ERR_AS_MISMATCH:
@@ -349,7 +357,7 @@ DEFUN (router_bgp,
return CMD_WARNING;
}
- vty->node = BGP_NODE;
+ vty_set_node(vty, BGP_NODE) ;
vty->index = bgp;
return CMD_SUCCESS;
@@ -363,7 +371,7 @@ ALIAS (router_bgp,
AS_STR
"BGP view\n"
"view name\n")
-
+
/* "no router bgp" commands. */
DEFUN (no_router_bgp,
no_router_bgp_cmd,
@@ -404,7 +412,7 @@ ALIAS (no_router_bgp,
AS_STR
"BGP view\n"
"view name\n")
-
+
/* BGP router-id. */
DEFUN (bgp_router_id,
@@ -475,7 +483,7 @@ ALIAS (no_bgp_router_id,
BGP_STR
"Override configured router identifier\n"
"Manually configured router identifier\n")
-
+
/* BGP Cluster ID. */
DEFUN (bgp_cluster_id,
@@ -545,7 +553,7 @@ ALIAS (no_bgp_cluster_id,
BGP_STR
"Configure Route-Reflector Cluster-id\n"
"Route-Reflector Cluster-id in IP address format\n")
-
+
DEFUN (bgp_confederation_identifier,
bgp_confederation_identifier_cmd,
"bgp confederation identifier " CMD_AS_RANGE,
@@ -595,7 +603,7 @@ ALIAS (no_bgp_confederation_identifier,
"AS confederation parameters\n"
"AS number\n"
"Set routing domain confederation AS\n")
-
+
DEFUN (bgp_confederation_peers,
bgp_confederation_peers_cmd,
"bgp confederation peers ." CMD_AS_RANGE,
@@ -649,7 +657,7 @@ DEFUN (no_bgp_confederation_peers,
}
return CMD_SUCCESS;
}
-
+
/* BGP timers. */
DEFUN (bgp_timers,
@@ -705,7 +713,7 @@ ALIAS (no_bgp_timers,
"BGP timers\n"
"Keepalive interval\n"
"Holdtime\n")
-
+
DEFUN (bgp_client_to_client_reflection,
bgp_client_to_client_reflection_cmd,
"bgp client-to-client reflection",
@@ -762,7 +770,7 @@ DEFUN (no_bgp_always_compare_med,
bgp_flag_unset (bgp, BGP_FLAG_ALWAYS_COMPARE_MED);
return CMD_SUCCESS;
}
-
+
/* "bgp deterministic-med" configuration. */
DEFUN (bgp_deterministic_med,
bgp_deterministic_med_cmd,
@@ -893,7 +901,7 @@ DEFUN (no_bgp_fast_external_failover,
bgp_flag_set (bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER);
return CMD_SUCCESS;
}
-
+
/* "bgp enforce-first-as" configuration. */
DEFUN (bgp_enforce_first_as,
bgp_enforce_first_as_cmd,
@@ -921,7 +929,7 @@ DEFUN (no_bgp_enforce_first_as,
bgp_flag_unset (bgp, BGP_FLAG_ENFORCE_FIRST_AS);
return CMD_SUCCESS;
}
-
+
/* "bgp bestpath compare-routerid" configuration. */
DEFUN (bgp_bestpath_compare_router_id,
bgp_bestpath_compare_router_id_cmd,
@@ -951,7 +959,7 @@ DEFUN (no_bgp_bestpath_compare_router_id,
bgp_flag_unset (bgp, BGP_FLAG_COMPARE_ROUTER_ID);
return CMD_SUCCESS;
}
-
+
/* "bgp bestpath as-path ignore" configuration. */
DEFUN (bgp_bestpath_aspath_ignore,
bgp_bestpath_aspath_ignore_cmd,
@@ -983,7 +991,7 @@ DEFUN (no_bgp_bestpath_aspath_ignore,
bgp_flag_unset (bgp, BGP_FLAG_ASPATH_IGNORE);
return CMD_SUCCESS;
}
-
+
/* "bgp bestpath as-path confed" configuration. */
DEFUN (bgp_bestpath_aspath_confed,
bgp_bestpath_aspath_confed_cmd,
@@ -1015,7 +1023,7 @@ DEFUN (no_bgp_bestpath_aspath_confed,
bgp_flag_unset (bgp, BGP_FLAG_ASPATH_CONFED);
return CMD_SUCCESS;
}
-
+
/* "bgp log-neighbor-changes" configuration. */
DEFUN (bgp_log_neighbor_changes,
bgp_log_neighbor_changes_cmd,
@@ -1043,7 +1051,7 @@ DEFUN (no_bgp_log_neighbor_changes,
bgp_flag_unset (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
return CMD_SUCCESS;
}
-
+
/* "bgp bestpath med" configuration. */
DEFUN (bgp_bestpath_med,
bgp_bestpath_med_cmd,
@@ -1055,7 +1063,7 @@ DEFUN (bgp_bestpath_med,
"Treat missing MED as the least preferred one\n")
{
struct bgp *bgp;
-
+
bgp = vty->index;
if (strncmp (argv[0], "confed", 1) == 0)
@@ -1076,7 +1084,7 @@ DEFUN (bgp_bestpath_med2,
"Treat missing MED as the least preferred one\n")
{
struct bgp *bgp;
-
+
bgp = vty->index;
bgp_flag_set (bgp, BGP_FLAG_MED_CONFED);
bgp_flag_set (bgp, BGP_FLAG_MED_MISSING_AS_WORST);
@@ -1105,7 +1113,7 @@ DEFUN (no_bgp_bestpath_med,
struct bgp *bgp;
bgp = vty->index;
-
+
if (strncmp (argv[0], "confed", 1) == 0)
bgp_flag_unset (bgp, BGP_FLAG_MED_CONFED);
else
@@ -1125,7 +1133,7 @@ DEFUN (no_bgp_bestpath_med2,
"Treat missing MED as the least preferred one\n")
{
struct bgp *bgp;
-
+
bgp = vty->index;
bgp_flag_unset (bgp, BGP_FLAG_MED_CONFED);
bgp_flag_unset (bgp, BGP_FLAG_MED_MISSING_AS_WORST);
@@ -1141,7 +1149,7 @@ ALIAS (no_bgp_bestpath_med2,
"MED attribute\n"
"Treat missing MED as the least preferred one\n"
"Compare MED among confederation paths\n")
-
+
/* "no bgp default ipv4-unicast". */
DEFUN (no_bgp_default_ipv4_unicast,
no_bgp_default_ipv4_unicast_cmd,
@@ -1171,7 +1179,7 @@ DEFUN (bgp_default_ipv4_unicast,
bgp_flag_unset (bgp, BGP_FLAG_NO_DEFAULT_IPV4);
return CMD_SUCCESS;
}
-
+
/* "bgp import-check" configuration. */
DEFUN (bgp_network_import_check,
bgp_network_import_check_cmd,
@@ -1201,7 +1209,7 @@ DEFUN (no_bgp_network_import_check,
bgp_flag_unset (bgp, BGP_FLAG_IMPORT_CHECK);
return CMD_SUCCESS;
}
-
+
DEFUN (bgp_default_local_preference,
bgp_default_local_preference_cmd,
"bgp default local-preference <0-4294967295>",
@@ -1245,9 +1253,9 @@ ALIAS (no_bgp_default_local_preference,
"Configure BGP defaults\n"
"local preference (higher=more preferred)\n"
"Configure default local preference value\n")
-
+
static int
-peer_remote_as_vty (struct vty *vty, const char *peer_str,
+peer_remote_as_vty (struct vty *vty, const char *peer_str,
const char *as_str, afi_t afi, safi_t safi)
{
int ret;
@@ -1305,7 +1313,7 @@ DEFUN (neighbor_remote_as,
{
return peer_remote_as_vty (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST);
}
-
+
DEFUN (neighbor_peer_group,
neighbor_peer_group_cmd,
"neighbor WORD peer-group",
@@ -1353,7 +1361,7 @@ DEFUN (no_neighbor,
{
peer = peer_lookup (vty->index, &su);
if (peer)
- peer_delete (peer);
+ bgp_peer_delete (peer);
}
return CMD_SUCCESS;
@@ -1410,7 +1418,7 @@ DEFUN (no_neighbor_peer_group_remote_as,
}
return CMD_SUCCESS;
}
-
+
DEFUN (neighbor_local_as,
neighbor_local_as_cmd,
NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE,
@@ -1487,7 +1495,7 @@ ALIAS (no_neighbor_local_as,
"Specify a local-as number\n"
"AS number used as local AS\n"
"Do not prepend local-as to updates from ebgp peers\n")
-
+
DEFUN (neighbor_password,
neighbor_password_cmd,
NEIGHBOR_CMD2 "password LINE",
@@ -1525,7 +1533,7 @@ DEFUN (no_neighbor_password,
ret = peer_password_unset (peer);
return bgp_vty_return (vty, ret);
}
-
+
DEFUN (neighbor_activate,
neighbor_activate_cmd,
NEIGHBOR_CMD2 "activate",
@@ -1564,7 +1572,7 @@ DEFUN (no_neighbor_activate,
return bgp_vty_return (vty, ret);
}
-
+
DEFUN (neighbor_set_peer_group,
neighbor_set_peer_group_cmd,
NEIGHBOR_CMD "peer-group WORD",
@@ -1602,7 +1610,7 @@ DEFUN (neighbor_set_peer_group,
return CMD_WARNING;
}
- ret = peer_group_bind (bgp, &su, group, bgp_node_afi (vty),
+ ret = peer_group_bind (bgp, &su, group, bgp_node_afi (vty),
bgp_node_safi (vty), &as);
if (ret == BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT)
@@ -1646,9 +1654,9 @@ DEFUN (no_neighbor_set_peer_group,
return bgp_vty_return (vty, ret);
}
-
+
static int
-peer_flag_modify_vty (struct vty *vty, const char *ip_str,
+peer_flag_modify_vty (struct vty *vty, const char *ip_str,
u_int16_t flag, int set)
{
int ret;
@@ -1699,7 +1707,7 @@ DEFUN (no_neighbor_passive,
{
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_PASSIVE);
}
-
+
/* neighbor shutdown. */
DEFUN (neighbor_shutdown,
neighbor_shutdown_cmd,
@@ -1721,7 +1729,7 @@ DEFUN (no_neighbor_shutdown,
{
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_SHUTDOWN);
}
-
+
/* Deprecated neighbor capability route-refresh. */
DEFUN_DEPRECATED (neighbor_capability_route_refresh,
neighbor_capability_route_refresh_cmd,
@@ -1745,7 +1753,7 @@ DEFUN_DEPRECATED (no_neighbor_capability_route_refresh,
{
return CMD_SUCCESS;
}
-
+
/* neighbor capability dynamic. */
DEFUN (neighbor_capability_dynamic,
neighbor_capability_dynamic_cmd,
@@ -1769,7 +1777,7 @@ DEFUN (no_neighbor_capability_dynamic,
{
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DYNAMIC_CAPABILITY);
}
-
+
/* neighbor dont-capability-negotiate */
DEFUN (neighbor_dont_capability_negotiate,
neighbor_dont_capability_negotiate_cmd,
@@ -1791,10 +1799,10 @@ DEFUN (no_neighbor_dont_capability_negotiate,
{
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY);
}
-
+
static int
peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi,
- safi_t safi, u_int32_t flag, int set)
+ safi_t safi, u_int32_t flag, bool set)
{
int ret;
struct peer *peer;
@@ -1803,10 +1811,7 @@ peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi,
if (! peer)
return CMD_WARNING;
- if (set)
- ret = peer_af_flag_set (peer, afi, safi, flag);
- else
- ret = peer_af_flag_unset (peer, afi, safi, flag);
+ ret = peer_af_flag_modify(peer, afi, safi, flag, set);
return bgp_vty_return (vty, ret);
}
@@ -1815,16 +1820,16 @@ static int
peer_af_flag_set_vty (struct vty *vty, const char *peer_str, afi_t afi,
safi_t safi, u_int32_t flag)
{
- return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 1);
+ return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, true);
}
static int
peer_af_flag_unset_vty (struct vty *vty, const char *peer_str, afi_t afi,
safi_t safi, u_int32_t flag)
{
- return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 0);
+ return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, false);
}
-
+
/* neighbor capability orf prefix-list. */
DEFUN (neighbor_capability_orf_prefix,
neighbor_capability_orf_prefix_cmd,
@@ -1880,7 +1885,7 @@ DEFUN (no_neighbor_capability_orf_prefix,
return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty), flag);
}
-
+
/* neighbor next-hop-self. */
DEFUN (neighbor_nexthop_self,
neighbor_nexthop_self_cmd,
@@ -1904,7 +1909,7 @@ DEFUN (no_neighbor_nexthop_self,
return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty), PEER_FLAG_NEXTHOP_SELF);
}
-
+
/* neighbor remove-private-AS. */
DEFUN (neighbor_remove_private_as,
neighbor_remove_private_as_cmd,
@@ -1930,7 +1935,7 @@ DEFUN (no_neighbor_remove_private_as,
bgp_node_safi (vty),
PEER_FLAG_REMOVE_PRIVATE_AS);
}
-
+
/* neighbor send-community. */
DEFUN (neighbor_send_community,
neighbor_send_community_cmd,
@@ -1956,7 +1961,7 @@ DEFUN (no_neighbor_send_community,
bgp_node_safi (vty),
PEER_FLAG_SEND_COMMUNITY);
}
-
+
/* neighbor send-community extended. */
DEFUN (neighbor_send_community_type,
neighbor_send_community_type_cmd,
@@ -2008,7 +2013,7 @@ DEFUN (no_neighbor_send_community_type,
(PEER_FLAG_SEND_COMMUNITY |
PEER_FLAG_SEND_EXT_COMMUNITY));
}
-
+
/* neighbor soft-reconfig. */
DEFUN (neighbor_soft_reconfiguration,
neighbor_soft_reconfiguration_cmd,
@@ -2036,7 +2041,7 @@ DEFUN (no_neighbor_soft_reconfiguration,
bgp_node_afi (vty), bgp_node_safi (vty),
PEER_FLAG_SOFT_RECONFIG);
}
-
+
DEFUN (neighbor_route_reflector_client,
neighbor_route_reflector_client_cmd,
NEIGHBOR_CMD2 "route-reflector-client",
@@ -2068,9 +2073,9 @@ DEFUN (no_neighbor_route_reflector_client,
bgp_node_safi (vty),
PEER_FLAG_REFLECTOR_CLIENT);
}
-
+
static int
-peer_rsclient_set_vty (struct vty *vty, const char *peer_str,
+peer_rsclient_set_vty (struct vty *vty, const char *peer_str,
int afi, int safi)
{
int ret;
@@ -2080,7 +2085,7 @@ peer_rsclient_set_vty (struct vty *vty, const char *peer_str,
struct listnode *node, *nnode;
struct bgp_filter *pfilter;
struct bgp_filter *gfilter;
- int locked_and_added = 0;
+ bool was_active ;
bgp = vty->index;
@@ -2092,29 +2097,22 @@ peer_rsclient_set_vty (struct vty *vty, const char *peer_str,
if ( CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) )
return CMD_SUCCESS;
- if ( ! peer_rsclient_active (peer) )
- {
- peer = peer_lock (peer); /* rsclient peer list reference */
- listnode_add_sort (bgp->rsclient, peer);
- locked_and_added = 1;
- }
+ was_active = peer_rsclient_active(peer) ;
ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
if (ret < 0)
- {
- if (locked_and_added)
- {
- listnode_delete (bgp->rsclient, peer);
- peer_unlock (peer); /* rsclient peer list reference */
- }
+ return bgp_vty_return (vty, ret);
- return bgp_vty_return (vty, ret);
- }
+ if (!was_active)
+ {
+ bgp_peer_lock (peer); /* rsclient peer list reference */
+ listnode_add_sort (bgp->rsclient, peer);
+ } ;
peer->rib[afi][safi] = bgp_table_init (afi, safi);
peer->rib[afi][safi]->type = BGP_TABLE_RSCLIENT;
/* RIB peer reference. Released when table is free'd in bgp_table_free. */
- peer->rib[afi][safi]->owner = peer_lock (peer);
+ peer->rib[afi][safi]->owner = bgp_peer_lock (peer);
/* Check for existing 'network' and 'redistribute' routes. */
bgp_check_local_routes_rsclient (peer, afi, safi);
@@ -2131,7 +2129,7 @@ peer_rsclient_set_vty (struct vty *vty, const char *peer_str,
{
pfilter = &peer->filter[afi][safi];
- /* Members of a non-RS-Client group should not be RS-Clients, as that
+ /* Members of a non-RS-Client group should not be RS-Clients, as that
is checked when the become part of the peer-group */
ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
if (ret < 0)
@@ -2166,7 +2164,7 @@ peer_rsclient_set_vty (struct vty *vty, const char *peer_str,
}
static int
-peer_rsclient_unset_vty (struct vty *vty, const char *peer_str,
+peer_rsclient_unset_vty (struct vty *vty, const char *peer_str,
int afi, int safi)
{
int ret;
@@ -2181,10 +2179,19 @@ peer_rsclient_unset_vty (struct vty *vty, const char *peer_str,
if ( ! peer )
return CMD_WARNING;
+ assert(bgp == peer->bgp) ;
+
/* If it is not a RS-Client, don't do anything. */
if ( ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) )
return CMD_SUCCESS;
+ /* If this is a Peer Group, then need to undo the relevant rsclient state
+ * for all the group members.
+ *
+ * That means clearing the state flag and the pointer to the shared RIB.
+ *
+ * TODO: peer_af_flag_unset PEER_FLAG_RSERVER_CLIENT fails for group members ?
+ */
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
{
group = peer->group;
@@ -2201,22 +2208,65 @@ peer_rsclient_unset_vty (struct vty *vty, const char *peer_str,
peer = group->conf;
}
+ /* Unset the rsclient flag and remove from rsclient list if no longer a
+ * distinct rsclient.
+ *
+ * NB: this takes care of downing the peer, if required.
+ */
ret = peer_af_flag_unset (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
if (ret < 0)
return bgp_vty_return (vty, ret);
- if ( ! peer_rsclient_active (peer) )
+ /* Now tidy up the data structures. */
+ peer_rsclient_unset(peer, afi, safi, false) ;
+
+ return CMD_SUCCESS;
+}
+
+/* Have unset rsclient state for a peer that was a distinct rsclient.
+ *
+ * Tidy up the data structures.
+ *
+ * NB: does not down the peer or deal with other consequences.
+ */
+void
+peer_rsclient_unset(struct peer* peer, int afi, int safi, bool keep_export)
+{
+ assert(peer->rib[afi][safi] != NULL) ;
+
+ /* If the peer is no longer a distinct rsclient, remove from list of same. */
+ if (! peer_rsclient_active (peer))
{
- bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
- listnode_delete (bgp->rsclient, peer);
- peer_unlock (peer); /* peer bgp rsclient reference */
+ struct listnode *pn;
+ pn = listnode_lookup (peer->bgp->rsclient, peer) ;
+
+ assert(pn != NULL) ;
+
+ bgp_peer_unlock (peer); /* peer rsclient reference */
+ list_delete_node (peer->bgp->rsclient, pn);
}
- bgp_table_finish (&peer->rib[bgp_node_afi(vty)][bgp_node_safi(vty)]);
+ /* Discard the rsclient rib */
+ bgp_clear_rsclient_rib (peer, afi, safi);
+ bgp_table_finish (&peer->rib[afi][safi]);
- return CMD_SUCCESS;
+ /* Discard import policy unconditionally */
+ if (peer->filter[afi][safi].map[RMAP_IMPORT].name)
+ {
+ free (peer->filter[afi][safi].map[RMAP_IMPORT].name);
+ peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL;
+ peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL;
+ }
+
+ /* Discard export policy unless should be kept. */
+ if (peer->filter[afi][safi].map[RMAP_EXPORT].name && !keep_export)
+ {
+ free (peer->filter[afi][safi].map[RMAP_EXPORT].name);
+ peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL;
+ peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL;
+ }
}
-
+
/* neighbor route-server-client. */
DEFUN (neighbor_route_server_client,
neighbor_route_server_client_cmd,
@@ -2240,7 +2290,7 @@ DEFUN (no_neighbor_route_server_client,
return peer_rsclient_unset_vty (vty, argv[0], bgp_node_afi(vty),
bgp_node_safi(vty));
}
-
+
DEFUN (neighbor_nexthop_local_unchanged,
neighbor_nexthop_local_unchanged_cmd,
NEIGHBOR_CMD2 "nexthop-local unchanged",
@@ -2253,7 +2303,7 @@ DEFUN (neighbor_nexthop_local_unchanged,
bgp_node_safi (vty),
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED );
}
-
+
DEFUN (no_neighbor_nexthop_local_unchanged,
no_neighbor_nexthop_local_unchanged_cmd,
NO_NEIGHBOR_CMD2 "nexthop-local unchanged",
@@ -2267,7 +2317,7 @@ DEFUN (no_neighbor_nexthop_local_unchanged,
bgp_node_safi (vty),
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED );
}
-
+
DEFUN (neighbor_attr_unchanged,
neighbor_attr_unchanged_cmd,
NEIGHBOR_CMD2 "attribute-unchanged",
@@ -2432,7 +2482,7 @@ ALIAS (neighbor_attr_unchanged,
DEFUN (no_neighbor_attr_unchanged,
no_neighbor_attr_unchanged_cmd,
NO_NEIGHBOR_CMD2 "attribute-unchanged",
- NO_STR
+ NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"BGP attribute is propagated unchanged to this neighbor\n")
@@ -2624,10 +2674,10 @@ DEFUN_DEPRECATED (neighbor_transparent_nexthop,
bgp_node_safi (vty),
PEER_FLAG_NEXTHOP_UNCHANGED);
}
-
+
/* EBGP multihop configuration. */
static int
-peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str,
+peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str,
const char *ttl_str)
{
struct peer *peer;
@@ -2638,15 +2688,15 @@ peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str,
return CMD_WARNING;
if (! ttl_str)
- ttl = TTL_MAX;
+ ttl = MAXTTL;
else
- VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, 255);
+ VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, MAXTTL);
return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, ttl));
}
static int
-peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str)
+peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str)
{
struct peer *peer;
@@ -2698,7 +2748,7 @@ ALIAS (no_neighbor_ebgp_multihop,
NEIGHBOR_ADDR_STR2
"Allow EBGP neighbors not on directly connected networks\n"
"maximum hop count\n")
-
+
/* disable-connected-check */
DEFUN (neighbor_disable_connected_check,
neighbor_disable_connected_check_cmd,
@@ -2737,7 +2787,7 @@ ALIAS (no_neighbor_disable_connected_check,
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Enforce EBGP neighbors perform multihop\n")
-
+
DEFUN (neighbor_description,
neighbor_description_cmd,
NEIGHBOR_CMD2 "description .LINE",
@@ -2792,10 +2842,10 @@ ALIAS (no_neighbor_description,
NEIGHBOR_ADDR_STR2
"Neighbor specific description\n"
"Up to 80 characters describing this neighbor\n")
-
+
/* Neighbor update-source. */
static int
-peer_update_source_vty (struct vty *vty, const char *peer_str,
+peer_update_source_vty (struct vty *vty, const char *peer_str,
const char *source_str)
{
struct peer *peer;
@@ -2849,10 +2899,10 @@ DEFUN (no_neighbor_update_source,
{
return peer_update_source_vty (vty, argv[0], NULL);
}
-
+
static int
-peer_default_originate_set_vty (struct vty *vty, const char *peer_str,
- afi_t afi, safi_t safi,
+peer_default_originate_set_vty (struct vty *vty, const char *peer_str,
+ afi_t afi, safi_t safi,
const char *rmap, int set)
{
int ret;
@@ -2916,10 +2966,10 @@ ALIAS (no_neighbor_default_originate,
"Originate default route to this neighbor\n"
"Route-map to specify criteria to originate default\n"
"route-map name\n")
-
+
/* Set neighbor's BGP port. */
static int
-peer_port_vty (struct vty *vty, const char *ip_str, int afi,
+peer_port_vty (struct vty *vty, const char *ip_str, int afi,
const char *port_str)
{
struct peer *peer;
@@ -2931,7 +2981,7 @@ peer_port_vty (struct vty *vty, const char *ip_str, int afi,
return CMD_WARNING;
if (! port_str)
- {
+ {
sp = getservbyname ("bgp", "tcp");
port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);
}
@@ -2976,10 +3026,10 @@ ALIAS (no_neighbor_port,
NEIGHBOR_ADDR_STR
"Neighbor's BGP port\n"
"TCP port number\n")
-
+
/* neighbor weight. */
static int
-peer_weight_set_vty (struct vty *vty, const char *ip_str,
+peer_weight_set_vty (struct vty *vty, const char *ip_str,
const char *weight_str)
{
int ret;
@@ -3041,7 +3091,7 @@ ALIAS (no_neighbor_weight,
NEIGHBOR_ADDR_STR2
"Set default weight for routes from this neighbor\n"
"default weight\n")
-
+
/* Override capability negotiation. */
DEFUN (neighbor_override_capability,
neighbor_override_capability_cmd,
@@ -3063,7 +3113,7 @@ DEFUN (no_neighbor_override_capability,
{
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_OVERRIDE_CAPABILITY);
}
-
+
DEFUN (neighbor_strict_capability,
neighbor_strict_capability_cmd,
NEIGHBOR_CMD "strict-capability-match",
@@ -3084,9 +3134,9 @@ DEFUN (no_neighbor_strict_capability,
{
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_STRICT_CAP_MATCH);
}
-
+
static int
-peer_timers_set_vty (struct vty *vty, const char *ip_str,
+peer_timers_set_vty (struct vty *vty, const char *ip_str,
const char *keep_str, const char *hold_str)
{
int ret;
@@ -3105,7 +3155,7 @@ peer_timers_set_vty (struct vty *vty, const char *ip_str,
return bgp_vty_return (vty, ret);
}
-
+
static int
peer_timers_unset_vty (struct vty *vty, const char *ip_str)
{
@@ -3143,9 +3193,9 @@ DEFUN (no_neighbor_timers,
{
return peer_timers_unset_vty (vty, argv[0]);
}
-
+
static int
-peer_timers_connect_set_vty (struct vty *vty, const char *ip_str,
+peer_timers_connect_set_vty (struct vty *vty, const char *ip_str,
const char *time_str)
{
int ret;
@@ -3211,10 +3261,10 @@ ALIAS (no_neighbor_timers_connect,
"BGP per neighbor timers\n"
"BGP connect timer\n"
"Connect timer\n")
-
+
static int
-peer_advertise_interval_vty (struct vty *vty, const char *ip_str,
- const char *time_str, int set)
+peer_advertise_interval_vty (struct vty *vty, const char *ip_str,
+ const char *time_str, int set)
{
int ret;
struct peer *peer;
@@ -3265,7 +3315,7 @@ ALIAS (no_neighbor_advertise_interval,
NEIGHBOR_ADDR_STR
"Minimum interval between sending BGP routing updates\n"
"time in seconds\n")
-
+
/* neighbor interface */
static int
peer_interface_vty (struct vty *vty, const char *ip_str, const char *str)
@@ -3307,10 +3357,10 @@ DEFUN (no_neighbor_interface,
{
return peer_interface_vty (vty, argv[0], NULL);
}
-
+
/* Set distribute list to the peer. */
static int
-peer_distribute_set_vty (struct vty *vty, const char *ip_str,
+peer_distribute_set_vty (struct vty *vty, const char *ip_str,
afi_t afi, safi_t safi,
const char *name_str, const char *direct_str)
{
@@ -3388,11 +3438,11 @@ DEFUN (no_neighbor_distribute_list,
return peer_distribute_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty), argv[2]);
}
-
+
/* Set prefix list to the peer. */
static int
peer_prefix_list_set_vty (struct vty *vty, const char *ip_str, afi_t afi,
- safi_t safi, const char *name_str,
+ safi_t safi, const char *name_str,
const char *direct_str)
{
int ret;
@@ -3425,7 +3475,7 @@ peer_prefix_list_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
peer = peer_and_group_lookup_vty (vty, ip_str);
if (! peer)
return CMD_WARNING;
-
+
/* Check filter direction. */
if (strncmp (direct_str, "i", 1) == 0)
direct = FILTER_IN;
@@ -3465,9 +3515,9 @@ DEFUN (no_neighbor_prefix_list,
return peer_prefix_list_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty), argv[2]);
}
-
+
static int
-peer_aslist_set_vty (struct vty *vty, const char *ip_str,
+peer_aslist_set_vty (struct vty *vty, const char *ip_str,
afi_t afi, safi_t safi,
const char *name_str, const char *direct_str)
{
@@ -3491,7 +3541,7 @@ peer_aslist_set_vty (struct vty *vty, const char *ip_str,
}
static int
-peer_aslist_unset_vty (struct vty *vty, const char *ip_str,
+peer_aslist_unset_vty (struct vty *vty, const char *ip_str,
afi_t afi, safi_t safi,
const char *direct_str)
{
@@ -3542,10 +3592,10 @@ DEFUN (no_neighbor_filter_list,
return peer_aslist_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty), argv[2]);
}
-
+
/* Set route-map to the peer. */
static int
-peer_route_map_set_vty (struct vty *vty, const char *ip_str,
+peer_route_map_set_vty (struct vty *vty, const char *ip_str,
afi_t afi, safi_t safi,
const char *name_str, const char *direct_str)
{
@@ -3566,6 +3616,8 @@ peer_route_map_set_vty (struct vty *vty, const char *ip_str,
direct = RMAP_IMPORT;
else if (strncmp (direct_str, "e", 1) == 0)
direct = RMAP_EXPORT;
+ else if (strncmp (direct_str, "r", 1) == 0)
+ direct = RMAP_RS_IN;
ret = peer_route_map_set (peer, afi, safi, direct, name_str);
@@ -3593,6 +3645,8 @@ peer_route_map_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
direct = RMAP_IMPORT;
else if (strncmp (direct_str, "e", 1) == 0)
direct = RMAP_EXPORT;
+ else if (strncmp (direct_str, "r", 1) == 0)
+ direct = RMAP_RS_IN;
ret = peer_route_map_unset (peer, afi, safi, direct);
@@ -3601,12 +3655,13 @@ peer_route_map_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
DEFUN (neighbor_route_map,
neighbor_route_map_cmd,
- NEIGHBOR_CMD2 "route-map WORD (in|out|import|export)",
+ NEIGHBOR_CMD2 "route-map WORD (in|rs-in|out|import|export)",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Apply route map to neighbor\n"
"Name of route map\n"
"Apply map to incoming routes\n"
+ "Apply map to incoming Route-Server routes\n"
"Apply map to outbound routes\n"
"Apply map to routes going into a Route-Server client's table\n"
"Apply map to routes coming from a Route-Server client")
@@ -3617,13 +3672,14 @@ DEFUN (neighbor_route_map,
DEFUN (no_neighbor_route_map,
no_neighbor_route_map_cmd,
- NO_NEIGHBOR_CMD2 "route-map WORD (in|out|import|export)",
+ NO_NEIGHBOR_CMD2 "route-map WORD (in|rs-in|out|import|export)",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Apply route map to neighbor\n"
"Name of route map\n"
"Apply map to incoming routes\n"
+ "Apply map to incoming Route-Server routes\n"
"Apply map to outbound routes\n"
"Apply map to routes going into a Route-Server client's table\n"
"Apply map to routes coming from a Route-Server client")
@@ -3631,7 +3687,7 @@ DEFUN (no_neighbor_route_map,
return peer_route_map_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty), argv[2]);
}
-
+
/* Set unsuppress-map to the peer. */
static int
peer_unsuppress_map_set_vty (struct vty *vty, const char *ip_str, afi_t afi,
@@ -3690,10 +3746,10 @@ DEFUN (no_neighbor_unsuppress_map,
return peer_unsuppress_map_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty));
}
-
+
static int
peer_maximum_prefix_set_vty (struct vty *vty, const char *ip_str, afi_t afi,
- safi_t safi, const char *num_str,
+ safi_t safi, const char *num_str,
const char *threshold_str, int warning,
const char *restart_str)
{
@@ -3837,7 +3893,7 @@ DEFUN (no_neighbor_maximum_prefix,
return peer_maximum_prefix_unset_vty (vty, argv[0], bgp_node_afi (vty),
bgp_node_safi (vty));
}
-
+
ALIAS (no_neighbor_maximum_prefix,
no_neighbor_maximum_prefix_val_cmd,
NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295>",
@@ -3900,7 +3956,7 @@ ALIAS (no_neighbor_maximum_prefix,
"Threshold value (%) at which to generate a warning msg\n"
"Restart bgp connection after limit is exceeded\n"
"Restart interval in minutes")
-
+
/* "neighbor allowas-in" */
DEFUN (neighbor_allowas_in,
neighbor_allowas_in_cmd,
@@ -3955,7 +4011,8 @@ DEFUN (no_neighbor_allowas_in,
return bgp_vty_return (vty, ret);
}
-
+
+
DEFUN (neighbor_ttl_security,
neighbor_ttl_security_cmd,
NEIGHBOR_CMD2 "ttl-security hops <1-254>",
@@ -3969,7 +4026,7 @@ DEFUN (neighbor_ttl_security,
peer = peer_and_group_lookup_vty (vty, argv[0]);
if (! peer)
return CMD_WARNING;
-
+
VTY_GET_INTEGER_RANGE ("", gtsm_hops, argv[1], 1, 254);
return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, gtsm_hops));
@@ -3991,7 +4048,7 @@ DEFUN (no_neighbor_ttl_security,
return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer));
}
-
+
/* Address family configuration. */
DEFUN (address_family_ipv4,
address_family_ipv4_cmd,
@@ -3999,7 +4056,7 @@ DEFUN (address_family_ipv4,
"Enter Address Family command mode\n"
"Address family\n")
{
- vty->node = BGP_IPV4_NODE;
+ vty_set_node(vty, BGP_IPV4_NODE) ;
return CMD_SUCCESS;
}
@@ -4012,9 +4069,9 @@ DEFUN (address_family_ipv4_safi,
"Address Family modifier\n")
{
if (strncmp (argv[0], "m", 1) == 0)
- vty->node = BGP_IPV4M_NODE;
+ vty_set_node(vty, BGP_IPV4M_NODE) ;
else
- vty->node = BGP_IPV4_NODE;
+ vty_set_node(vty, BGP_IPV4_NODE) ;
return CMD_SUCCESS;
}
@@ -4025,7 +4082,7 @@ DEFUN (address_family_ipv6,
"Enter Address Family command mode\n"
"Address family\n")
{
- vty->node = BGP_IPV6_NODE;
+ vty_set_node(vty, BGP_IPV6_NODE) ;
return CMD_SUCCESS;
}
@@ -4038,9 +4095,9 @@ DEFUN (address_family_ipv6_safi,
"Address Family modifier\n")
{
if (strncmp (argv[0], "m", 1) == 0)
- vty->node = BGP_IPV6M_NODE;
+ vty_set_node(vty, BGP_IPV6M_NODE) ;
else
- vty->node = BGP_IPV6_NODE;
+ vty_set_node(vty, BGP_IPV6_NODE) ;
return CMD_SUCCESS;
}
@@ -4051,7 +4108,7 @@ DEFUN (address_family_vpnv4,
"Enter Address Family command mode\n"
"Address family\n")
{
- vty->node = BGP_VPNV4_NODE;
+ vty_set_node(vty, BGP_VPNV4_NODE) ;
return CMD_SUCCESS;
}
@@ -4067,24 +4124,26 @@ DEFUN (exit_address_family,
"exit-address-family",
"Exit from Address Family configuration mode\n")
{
- if (vty->node == BGP_IPV4_NODE
- || vty->node == BGP_IPV4M_NODE
- || vty->node == BGP_VPNV4_NODE
- || vty->node == BGP_IPV6_NODE
- || vty->node == BGP_IPV6M_NODE)
- vty->node = BGP_NODE;
+ enum node_type node = vty_get_node(vty) ;
+
+ if (node == BGP_IPV4_NODE
+ || node == BGP_IPV4M_NODE
+ || node == BGP_VPNV4_NODE
+ || node == BGP_IPV6_NODE
+ || node == BGP_IPV6M_NODE)
+ vty_set_node(vty, BGP_NODE);
return CMD_SUCCESS;
}
-
+
/* BGP clear sort. */
-enum clear_sort
+typedef enum
{
clear_all,
clear_peer,
clear_group,
clear_external,
clear_as
-};
+} clear_sort_t ;
static void
bgp_clear_vty_error (struct vty *vty, struct peer *peer, afi_t afi,
@@ -4110,7 +4169,7 @@ bgp_clear_vty_error (struct vty *vty, struct peer *peer, afi_t afi,
/* `clear ip bgp' functions. */
static int
bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
- enum clear_sort sort,enum bgp_clear_type stype, const char *arg)
+ clear_sort_t sort, bgp_clear_type_t stype, const char *arg)
{
int ret;
struct peer *peer;
@@ -4172,7 +4231,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
if (! group)
{
vty_out (vty, "%%BGP: No such peer-group %s%s", arg, VTY_NEWLINE);
- return CMD_WARNING;
+ return CMD_WARNING;
}
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
@@ -4198,7 +4257,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
{
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
- if (peer_sort (peer) == BGP_PEER_IBGP)
+ if (peer_sort (peer) == BGP_PEER_IBGP)
continue;
if (stype == BGP_CLEAR_SOFT_NONE)
@@ -4219,17 +4278,17 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
int find = 0;
VTY_GET_LONG ("AS", as_ul, arg);
-
+
if (!as_ul)
{
- vty_out (vty, "Invalid AS number%s", VTY_NEWLINE);
+ vty_out (vty, "Invalid AS number%s", VTY_NEWLINE);
return CMD_WARNING;
}
as = (as_t) as_ul;
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
- if (peer->as != as)
+ if (peer->as != as)
continue;
find = 1;
@@ -4252,8 +4311,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
static int
bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi,
- enum clear_sort sort, enum bgp_clear_type stype,
- const char *arg)
+ clear_sort_t sort, bgp_clear_type_t stype, const char *arg)
{
struct bgp *bgp;
@@ -4279,7 +4337,7 @@ bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi,
return bgp_clear (vty, bgp, afi, safi, sort, stype, arg);
}
-
+
DEFUN (clear_ip_bgp_all,
clear_ip_bgp_all_cmd,
"clear ip bgp *",
@@ -4289,7 +4347,7 @@ DEFUN (clear_ip_bgp_all,
"Clear all peers\n")
{
if (argc == 1)
- return bgp_clear_vty (vty, argv[0], 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL);
+ return bgp_clear_vty (vty, argv[0], 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL);
return bgp_clear_vty (vty, NULL, 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL);
}
@@ -4329,7 +4387,7 @@ ALIAS (clear_ip_bgp_all,
"Clear all peers\n")
DEFUN (clear_ip_bgp_peer,
- clear_ip_bgp_peer_cmd,
+ clear_ip_bgp_peer_cmd,
"clear ip bgp (A.B.C.D|X:X::X:X)",
CLEAR_STR
IP_STR
@@ -4341,7 +4399,7 @@ DEFUN (clear_ip_bgp_peer,
}
ALIAS (clear_ip_bgp_peer,
- clear_bgp_peer_cmd,
+ clear_bgp_peer_cmd,
"clear bgp (A.B.C.D|X:X::X:X)",
CLEAR_STR
BGP_STR
@@ -4349,7 +4407,7 @@ ALIAS (clear_ip_bgp_peer,
"BGP IPv6 neighbor to clear\n")
ALIAS (clear_ip_bgp_peer,
- clear_bgp_ipv6_peer_cmd,
+ clear_bgp_ipv6_peer_cmd,
"clear bgp ipv6 (A.B.C.D|X:X::X:X)",
CLEAR_STR
BGP_STR
@@ -4358,7 +4416,7 @@ ALIAS (clear_ip_bgp_peer,
"BGP IPv6 neighbor to clear\n")
DEFUN (clear_ip_bgp_peer_group,
- clear_ip_bgp_peer_group_cmd,
+ clear_ip_bgp_peer_group_cmd,
"clear ip bgp peer-group WORD",
CLEAR_STR
IP_STR
@@ -4370,7 +4428,7 @@ DEFUN (clear_ip_bgp_peer_group,
}
ALIAS (clear_ip_bgp_peer_group,
- clear_bgp_peer_group_cmd,
+ clear_bgp_peer_group_cmd,
"clear bgp peer-group WORD",
CLEAR_STR
BGP_STR
@@ -4378,7 +4436,7 @@ ALIAS (clear_ip_bgp_peer_group,
"BGP peer-group name\n")
ALIAS (clear_ip_bgp_peer_group,
- clear_bgp_ipv6_peer_group_cmd,
+ clear_bgp_ipv6_peer_group_cmd,
"clear bgp ipv6 peer-group WORD",
CLEAR_STR
BGP_STR
@@ -4398,14 +4456,14 @@ DEFUN (clear_ip_bgp_external,
}
ALIAS (clear_ip_bgp_external,
- clear_bgp_external_cmd,
+ clear_bgp_external_cmd,
"clear bgp external",
CLEAR_STR
BGP_STR
"Clear all external peers\n")
ALIAS (clear_ip_bgp_external,
- clear_bgp_ipv6_external_cmd,
+ clear_bgp_ipv6_external_cmd,
"clear bgp ipv6 external",
CLEAR_STR
BGP_STR
@@ -4421,7 +4479,7 @@ DEFUN (clear_ip_bgp_as,
"Clear peers with the AS number\n")
{
return bgp_clear_vty (vty, NULL, 0, 0, clear_as, BGP_CLEAR_SOFT_NONE, argv[0]);
-}
+}
ALIAS (clear_ip_bgp_as,
clear_bgp_as_cmd,
@@ -4437,7 +4495,7 @@ ALIAS (clear_ip_bgp_as,
BGP_STR
"Address family\n"
"Clear peers with the AS number\n")
-
+
/* Outbound soft-reconfiguration */
DEFUN (clear_ip_bgp_all_soft_out,
clear_ip_bgp_all_soft_out_cmd,
@@ -4743,7 +4801,7 @@ ALIAS (clear_bgp_peer_soft_out,
"Soft reconfig outbound update\n")
DEFUN (clear_ip_bgp_peer_group_soft_out,
- clear_ip_bgp_peer_group_soft_out_cmd,
+ clear_ip_bgp_peer_group_soft_out_cmd,
"clear ip bgp peer-group WORD soft out",
CLEAR_STR
IP_STR
@@ -4758,7 +4816,7 @@ DEFUN (clear_ip_bgp_peer_group_soft_out,
}
ALIAS (clear_ip_bgp_peer_group_soft_out,
- clear_ip_bgp_peer_group_out_cmd,
+ clear_ip_bgp_peer_group_out_cmd,
"clear ip bgp peer-group WORD out",
CLEAR_STR
IP_STR
@@ -4847,7 +4905,7 @@ ALIAS (clear_bgp_peer_group_soft_out,
"Soft reconfig outbound update\n")
DEFUN (clear_ip_bgp_external_soft_out,
- clear_ip_bgp_external_soft_out_cmd,
+ clear_ip_bgp_external_soft_out_cmd,
"clear ip bgp external soft out",
CLEAR_STR
IP_STR
@@ -4861,7 +4919,7 @@ DEFUN (clear_ip_bgp_external_soft_out,
}
ALIAS (clear_ip_bgp_external_soft_out,
- clear_ip_bgp_external_out_cmd,
+ clear_ip_bgp_external_out_cmd,
"clear ip bgp external out",
CLEAR_STR
IP_STR
@@ -5064,7 +5122,7 @@ ALIAS (clear_bgp_as_soft_out,
"Address family\n"
"Clear peers with the AS number\n"
"Soft reconfig outbound update\n")
-
+
/* Inbound soft-reconfiguration */
DEFUN (clear_ip_bgp_all_soft_in,
clear_ip_bgp_all_soft_in_cmd,
@@ -5361,7 +5419,7 @@ ALIAS (clear_ip_bgp_peer_soft_in,
BGP_STR
"BGP neighbor address to clear\n"
"Soft reconfig inbound update\n")
-
+
DEFUN (clear_ip_bgp_peer_in_prefix_filter,
clear_ip_bgp_peer_in_prefix_filter_cmd,
"clear ip bgp A.B.C.D in prefix-filter",
@@ -6026,7 +6084,7 @@ ALIAS (clear_bgp_as_in_prefix_filter,
"Clear peers with the AS number\n"
"Soft reconfig inbound update\n"
"Push out prefix-list ORF and do inbound soft reconfig\n")
-
+
/* Both soft-reconfiguration */
DEFUN (clear_ip_bgp_all_soft,
clear_ip_bgp_all_soft_cmd,
@@ -6125,7 +6183,7 @@ DEFUN (clear_bgp_all_soft,
if (argc == 1)
return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all,
BGP_CLEAR_SOFT_BOTH, argv[0]);
-
+
return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
BGP_CLEAR_SOFT_BOTH, argv[0]);
}
@@ -6400,7 +6458,7 @@ ALIAS (clear_bgp_as_soft,
"Address family\n"
"Clear peers with the AS number\n"
"Soft reconfig\n")
-
+
/* RS-client soft reconfiguration. */
#ifdef HAVE_IPV6
DEFUN (clear_bgp_all_rsclient,
@@ -6576,17 +6634,17 @@ DEFUN (show_bgp_views,
vty_out (vty, "Multiple BGP views are not defined%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
vty_out (vty, "Defined BGP views:%s", VTY_NEWLINE);
for (ALL_LIST_ELEMENTS_RO(inst, node, bgp))
- vty_out (vty, "\t%s (AS%u)%s",
+ vty_out (vty, "\t%s (AS%u)%s",
bgp->name ? bgp->name : "(null)",
bgp->as, VTY_NEWLINE);
-
+
return CMD_SUCCESS;
}
-DEFUN (show_bgp_memory,
+DEFUN (show_bgp_memory,
show_bgp_memory_cmd,
"show bgp memory",
SHOW_STR
@@ -6595,14 +6653,14 @@ DEFUN (show_bgp_memory,
{
char memstrbuf[MTYPE_MEMSTR_LEN];
unsigned long count;
-
+
/* RIB related usage stats */
count = mtype_stats_alloc (MTYPE_BGP_NODE);
vty_out (vty, "%ld RIB nodes, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct bgp_node)),
VTY_NEWLINE);
-
+
count = mtype_stats_alloc (MTYPE_BGP_ROUTE);
vty_out (vty, "%ld BGP routes, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
@@ -6613,13 +6671,13 @@ DEFUN (show_bgp_memory,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct bgp_info_extra)),
VTY_NEWLINE);
-
+
if ((count = mtype_stats_alloc (MTYPE_BGP_STATIC)))
vty_out (vty, "%ld Static routes, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct bgp_static)),
VTY_NEWLINE);
-
+
/* Adj-In/Out */
if ((count = mtype_stats_alloc (MTYPE_BGP_ADJ_IN)))
vty_out (vty, "%ld Adj-In entries, using %s of memory%s", count,
@@ -6631,7 +6689,7 @@ DEFUN (show_bgp_memory,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct bgp_adj_out)),
VTY_NEWLINE);
-
+
if ((count = mtype_stats_alloc (MTYPE_BGP_NEXTHOP_CACHE)))
vty_out (vty, "%ld Nexthop cache entries, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
@@ -6646,32 +6704,32 @@ DEFUN (show_bgp_memory,
/* Attributes */
count = attr_count();
- vty_out (vty, "%ld BGP attributes, using %s of memory%s", count,
- mtype_memstr (memstrbuf, sizeof (memstrbuf),
- count * sizeof(struct attr)),
+ vty_out (vty, "%ld BGP attributes, using %s of memory%s", count,
+ mtype_memstr (memstrbuf, sizeof (memstrbuf),
+ count * sizeof(struct attr)),
VTY_NEWLINE);
if ((count = mtype_stats_alloc (MTYPE_ATTR_EXTRA)))
- vty_out (vty, "%ld BGP extra attributes, using %s of memory%s", count,
- mtype_memstr (memstrbuf, sizeof (memstrbuf),
- count * sizeof(struct attr_extra)),
+ vty_out (vty, "%ld BGP extra attributes, using %s of memory%s", count,
+ mtype_memstr (memstrbuf, sizeof (memstrbuf),
+ count * sizeof(struct attr_extra)),
VTY_NEWLINE);
-
+
if ((count = attr_unknown_count()))
vty_out (vty, "%ld unknown attributes%s", count, VTY_NEWLINE);
-
+
/* AS_PATH attributes */
count = aspath_count ();
vty_out (vty, "%ld BGP AS-PATH entries, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct aspath)),
VTY_NEWLINE);
-
+
count = mtype_stats_alloc (MTYPE_AS_SEG);
vty_out (vty, "%ld BGP AS-PATH segments, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct assegment)),
VTY_NEWLINE);
-
+
/* Other attributes */
if ((count = community_count ()))
vty_out (vty, "%ld BGP community entries, using %s of memory%s", count,
@@ -6683,26 +6741,26 @@ DEFUN (show_bgp_memory,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct ecommunity)),
VTY_NEWLINE);
-
+
if ((count = mtype_stats_alloc (MTYPE_CLUSTER)))
vty_out (vty, "%ld Cluster lists, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct cluster_list)),
VTY_NEWLINE);
-
+
/* Peer related usage */
count = mtype_stats_alloc (MTYPE_BGP_PEER);
vty_out (vty, "%ld peers, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct peer)),
VTY_NEWLINE);
-
+
if ((count = mtype_stats_alloc (MTYPE_PEER_GROUP)))
vty_out (vty, "%ld peer groups, using %s of memory%s", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof (struct peer_group)),
VTY_NEWLINE);
-
+
/* Other */
if ((count = mtype_stats_alloc (MTYPE_HASH)))
vty_out (vty, "%ld hash tables, using %s of memory%s", count,
@@ -6731,30 +6789,33 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
unsigned int count = 0;
char timebuf[BGP_UPTIME_LEN];
int len;
+ struct bgp_session_stats stats;
/* Header string for each address family. */
static char header[] = "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd";
-
+
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
+ bgp_session_get_stats(peer->session, &stats);
+
if (peer->afc[afi][safi])
{
if (!count)
{
unsigned long ents;
char memstrbuf[MTYPE_MEMSTR_LEN];
-
+
/* Usage summary and header */
vty_out (vty,
"BGP router identifier %s, local AS number %u%s",
- inet_ntoa (bgp->router_id), bgp->as, VTY_NEWLINE);
+ safe_inet_ntoa (bgp->router_id), bgp->as, VTY_NEWLINE);
ents = bgp_table_count (bgp->rib[afi][safi]);
vty_out (vty, "RIB entries %ld, using %s of memory%s", ents,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
ents * sizeof (struct bgp_node)),
VTY_NEWLINE);
-
+
/* Peer related usage */
ents = listcount (bgp->peer);
vty_out (vty, "Peers %ld, using %s of memory%s",
@@ -6762,14 +6823,14 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
mtype_memstr (memstrbuf, sizeof (memstrbuf),
ents * sizeof (struct peer)),
VTY_NEWLINE);
-
+
if ((ents = listcount (bgp->rsclient)))
vty_out (vty, "RS-Client peers %ld, using %s of memory%s",
ents,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
ents * sizeof (struct peer)),
VTY_NEWLINE);
-
+
if ((ents = listcount (bgp->group)))
vty_out (vty, "Peer groups %ld, using %s of memory%s", ents,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
@@ -6781,11 +6842,11 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "%s%s", header, VTY_NEWLINE);
}
-
+
count++;
- len = vty_out (vty, "%s", peer->host);
- len = 16 - len;
+ vty_out (vty, "%s", peer->host);
+ len = 16 - strlen(peer->host);
if (len < 1)
vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " ");
else
@@ -6795,17 +6856,17 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
vty_out (vty, "%5u %7d %7d %8d %4d %4lu ",
peer->as,
- peer->open_in + peer->update_in + peer->keepalive_in
- + peer->notify_in + peer->refresh_in + peer->dynamic_cap_in,
- peer->open_out + peer->update_out + peer->keepalive_out
- + peer->notify_out + peer->refresh_out
- + peer->dynamic_cap_out,
+ stats.open_in + stats.update_in + stats.keepalive_in
+ + stats.notify_in + stats.refresh_in + stats.dynamic_cap_in,
+ stats.open_out + stats.update_out + stats.keepalive_out
+ + stats.notify_out + stats.refresh_out
+ + stats.dynamic_cap_out,
0, 0, (unsigned long) peer->obuf->count);
- vty_out (vty, "%8s",
+ vty_out (vty, "%8s",
peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN));
- if (peer->status == Established)
+ if (peer->state == bgp_peer_pEstablished)
{
vty_out (vty, " %8ld", peer->pcount[afi][safi]);
}
@@ -6816,7 +6877,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
else if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
vty_out (vty, " Idle (PfxCt)");
else
- vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, peer->status));
+ vty_out (vty, " %-11s", LOOKUP(bgp_peer_status_msg, peer->state));
}
vty_out (vty, "%s", VTY_NEWLINE);
@@ -6832,8 +6893,8 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
return CMD_SUCCESS;
}
-static int
-bgp_show_summary_vty (struct vty *vty, const char *name,
+static int
+bgp_show_summary_vty (struct vty *vty, const char *name,
afi_t afi, safi_t safi)
{
struct bgp *bgp;
@@ -6841,27 +6902,27 @@ bgp_show_summary_vty (struct vty *vty, const char *name,
if (name)
{
bgp = bgp_lookup_by_name (name);
-
+
if (! bgp)
{
- vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE);
+ vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE);
return CMD_WARNING;
}
bgp_show_summary (vty, bgp, afi, safi);
return CMD_SUCCESS;
}
-
+
bgp = bgp_get_default ();
if (bgp)
- bgp_show_summary (vty, bgp, afi, safi);
-
+ bgp_show_summary (vty, bgp, afi, safi);
+
return CMD_SUCCESS;
}
/* `show ip bgp summary' commands. */
-DEFUN (show_ip_bgp_summary,
+DEFUN (show_ip_bgp_summary,
show_ip_bgp_summary_cmd,
"show ip bgp summary",
SHOW_STR
@@ -6882,10 +6943,10 @@ DEFUN (show_ip_bgp_instance_summary,
"View name\n"
"Summary of BGP neighbor status\n")
{
- return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+ return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
}
-DEFUN (show_ip_bgp_ipv4_summary,
+DEFUN (show_ip_bgp_ipv4_summary,
show_ip_bgp_ipv4_summary_cmd,
"show ip bgp ipv4 (unicast|multicast) summary",
SHOW_STR
@@ -6981,7 +7042,7 @@ DEFUN (show_ip_bgp_vpnv4_rd_summary,
}
#ifdef HAVE_IPV6
-DEFUN (show_bgp_summary,
+DEFUN (show_bgp_summary,
show_bgp_summary_cmd,
"show bgp summary",
SHOW_STR
@@ -7003,7 +7064,7 @@ DEFUN (show_bgp_instance_summary,
return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
}
-ALIAS (show_bgp_summary,
+ALIAS (show_bgp_summary,
show_bgp_ipv6_summary_cmd,
"show bgp ipv6 summary",
SHOW_STR
@@ -7056,7 +7117,7 @@ DEFUN (show_bgp_instance_ipv6_safi_summary,
}
/* old command */
-DEFUN (show_ipv6_bgp_summary,
+DEFUN (show_ipv6_bgp_summary,
show_ipv6_bgp_summary_cmd,
"show ipv6 bgp summary",
SHOW_STR
@@ -7068,7 +7129,7 @@ DEFUN (show_ipv6_bgp_summary,
}
/* old command */
-DEFUN (show_ipv6_mbgp_summary,
+DEFUN (show_ipv6_mbgp_summary,
show_ipv6_mbgp_summary_cmd,
"show ipv6 mbgp summary",
SHOW_STR
@@ -7079,7 +7140,7 @@ DEFUN (show_ipv6_mbgp_summary,
return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
}
#endif /* HAVE_IPV6 */
-
+
const char *
afi_safi_print (afi_t afi, safi_t safi)
{
@@ -7230,7 +7291,7 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi)
vty_out (vty, "(both)%s", VTY_NEWLINE);
else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY))
vty_out (vty, "(extended)%s", VTY_NEWLINE);
- else
+ else
vty_out (vty, "(standard)%s", VTY_NEWLINE);
}
if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
@@ -7247,12 +7308,14 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi)
vty_out (vty, " default not sent%s", VTY_NEWLINE);
}
- if (filter->plist[FILTER_IN].name
+ if (filter->plist[FILTER_IN].ref
|| filter->dlist[FILTER_IN].name
|| filter->aslist[FILTER_IN].name
|| filter->map[RMAP_IN].name)
vty_out (vty, " Inbound path policy configured%s", VTY_NEWLINE);
- if (filter->plist[FILTER_OUT].name
+ if (filter->map[RMAP_RS_IN].name)
+ vty_out (vty, " RS-Inbound path policy configured%s", VTY_NEWLINE);
+ if (filter->plist[FILTER_OUT].ref
|| filter->dlist[FILTER_OUT].name
|| filter->aslist[FILTER_OUT].name
|| filter->map[RMAP_OUT].name
@@ -7264,15 +7327,15 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi)
vty_out (vty, " Export policy for this RS-client configured%s", VTY_NEWLINE);
/* prefix-list */
- if (filter->plist[FILTER_IN].name)
+ if (filter->plist[FILTER_IN].ref)
vty_out (vty, " Incoming update prefix filter list is %s%s%s",
- filter->plist[FILTER_IN].plist ? "*" : "",
- filter->plist[FILTER_IN].name,
+ prefix_list_ref_plist(filter->plist[FILTER_IN].ref) ? "*" : "",
+ prefix_list_ref_name(filter->plist[FILTER_IN].ref),
VTY_NEWLINE);
- if (filter->plist[FILTER_OUT].name)
+ if (filter->plist[FILTER_OUT].ref)
vty_out (vty, " Outgoing update prefix filter list is %s%s%s",
- filter->plist[FILTER_OUT].plist ? "*" : "",
- filter->plist[FILTER_OUT].name,
+ prefix_list_ref_plist(filter->plist[FILTER_OUT].ref) ? "*" : "",
+ prefix_list_ref_name(filter->plist[FILTER_OUT].ref),
VTY_NEWLINE);
/* distribute-list */
@@ -7305,6 +7368,11 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi)
filter->map[RMAP_IN].map ? "*" : "",
filter->map[RMAP_IN].name,
VTY_NEWLINE);
+ if (filter->map[RMAP_RS_IN].name)
+ vty_out (vty, " Route map for RS incoming advertisements is %s%s%s",
+ filter->map[RMAP_RS_IN].map ? "*" : "",
+ filter->map[RMAP_RS_IN].name,
+ VTY_NEWLINE);
if (filter->map[RMAP_OUT].name)
vty_out (vty, " Route map for outgoing advertisements is %s%s%s",
filter->map[RMAP_OUT].map ? "*" : "",
@@ -7350,10 +7418,13 @@ static void
bgp_show_peer (struct vty *vty, struct peer *p)
{
struct bgp *bgp;
- char buf1[BUFSIZ];
+ char buf[SU_ADDRSTRLEN];
char timebuf[BGP_UPTIME_LEN];
afi_t afi;
safi_t safi;
+ struct bgp_session_stats stats;
+
+ bgp_session_get_stats(p->session, &stats);
bgp = p->bgp;
@@ -7371,7 +7442,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
/* Description. */
if (p->desc)
vty_out (vty, " Description: %s%s", p->desc, VTY_NEWLINE);
-
+
/* Peer-group */
if (p->group)
vty_out (vty, " Member of peer-group %s for session parameters%s",
@@ -7383,30 +7454,33 @@ bgp_show_peer (struct vty *vty, struct peer *p)
/* BGP Version. */
vty_out (vty, " BGP version 4");
- vty_out (vty, ", remote router ID %s%s",
- inet_ntop (AF_INET, &p->remote_id, buf1, BUFSIZ),
+ vty_out (vty, ", remote router ID %s%s",
+ inet_ntop (AF_INET, &p->remote_id, buf, sizeof(buf)),
VTY_NEWLINE);
/* Confederation */
if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)
&& bgp_confederation_peers_check (bgp, p->as))
vty_out (vty, " Neighbor under common administration%s", VTY_NEWLINE);
-
+
/* Status. */
- vty_out (vty, " BGP state = %s",
- LOOKUP (bgp_status_msg, p->status));
- if (p->status == Established)
- vty_out (vty, ", up for %8s",
+ vty_out (vty, " BGP state = %s",
+ LOOKUP (bgp_peer_status_msg, p->state));
+ if (p->state == bgp_peer_pEstablished)
+ vty_out (vty, ", up for %8s",
peer_uptime (p->uptime, timebuf, BGP_UPTIME_LEN));
+ /* TODO: what is state "Active" now? sEnabled? */
+#if 0
else if (p->status == Active)
{
if (CHECK_FLAG (p->flags, PEER_FLAG_PASSIVE))
- vty_out (vty, " (passive)");
+ vty_out (vty, " (passive)");
else if (CHECK_FLAG (p->sflags, PEER_STATUS_NSF_WAIT))
- vty_out (vty, " (NSF passive)");
+ vty_out (vty, " (NSF passive)");
}
+#endif
vty_out (vty, "%s", VTY_NEWLINE);
-
+
/* read timer */
vty_out (vty, " Last read %s", peer_uptime (p->readtime, timebuf, BGP_UPTIME_LEN));
@@ -7421,7 +7495,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
}
/* Capability. */
- if (p->status == Established)
+ if (p->state == bgp_peer_pEstablished)
{
if (p->cap
|| p->afc_adv[AFI_IP][SAFI_UNICAST]
@@ -7489,12 +7563,12 @@ bgp_show_peer (struct vty *vty, struct peer *p)
if (p->afc_adv[afi][safi] || p->afc_recv[afi][safi])
{
vty_out (vty, " Address family %s:", afi_safi_print (afi, safi));
- if (p->afc_adv[afi][safi])
+ if (p->afc_adv[afi][safi])
vty_out (vty, " advertised");
if (p->afc_recv[afi][safi])
vty_out (vty, " %sreceived", p->afc_adv[afi][safi] ? "and " : "");
vty_out (vty, "%s", VTY_NEWLINE);
- }
+ }
/* Gracefull Restart */
if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV)
@@ -7513,7 +7587,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
int restart_af_count = 0;
vty_out (vty, " Remote Restart timer is %d seconds%s",
- p->v_gr_restart, VTY_NEWLINE);
+ p->v_gr_restart, VTY_NEWLINE);
vty_out (vty, " Address families by peer:%s ", VTY_NEWLINE);
for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
@@ -7543,7 +7617,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
int eor_receive_af_count = 0;
vty_out (vty, " Graceful restart informations:%s", VTY_NEWLINE);
- if (p->status == Established)
+ if (p->state == bgp_peer_pEstablished)
{
vty_out (vty, " End-of-RIB send: ");
for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
@@ -7571,7 +7645,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
if (p->t_gr_restart)
vty_out (vty, " The remaining time of restart timer is %ld%s",
thread_timer_remain_second (p->t_gr_restart), VTY_NEWLINE);
-
+
if (p->t_gr_stale)
vty_out (vty, " The remaining time of stalepath timer is %ld%s",
thread_timer_remain_second (p->t_gr_stale), VTY_NEWLINE);
@@ -7582,16 +7656,16 @@ bgp_show_peer (struct vty *vty, struct peer *p)
vty_out (vty, " Inq depth is 0%s", VTY_NEWLINE);
vty_out (vty, " Outq depth is %lu%s", (unsigned long) p->obuf->count, VTY_NEWLINE);
vty_out (vty, " Sent Rcvd%s", VTY_NEWLINE);
- vty_out (vty, " Opens: %10d %10d%s", p->open_out, p->open_in, VTY_NEWLINE);
- vty_out (vty, " Notifications: %10d %10d%s", p->notify_out, p->notify_in, VTY_NEWLINE);
- vty_out (vty, " Updates: %10d %10d%s", p->update_out, p->update_in, VTY_NEWLINE);
- vty_out (vty, " Keepalives: %10d %10d%s", p->keepalive_out, p->keepalive_in, VTY_NEWLINE);
- vty_out (vty, " Route Refresh: %10d %10d%s", p->refresh_out, p->refresh_in, VTY_NEWLINE);
- vty_out (vty, " Capability: %10d %10d%s", p->dynamic_cap_out, p->dynamic_cap_in, VTY_NEWLINE);
- vty_out (vty, " Total: %10d %10d%s", p->open_out + p->notify_out +
- p->update_out + p->keepalive_out + p->refresh_out + p->dynamic_cap_out,
- p->open_in + p->notify_in + p->update_in + p->keepalive_in + p->refresh_in +
- p->dynamic_cap_in, VTY_NEWLINE);
+ vty_out (vty, " Opens: %10d %10d%s", stats.open_out, stats.open_in, VTY_NEWLINE);
+ vty_out (vty, " Notifications: %10d %10d%s", stats.notify_out, stats.notify_in, VTY_NEWLINE);
+ vty_out (vty, " Updates: %10d %10d%s", stats.update_out, stats.update_in, VTY_NEWLINE);
+ vty_out (vty, " Keepalives: %10d %10d%s", stats.keepalive_out, stats.keepalive_in, VTY_NEWLINE);
+ vty_out (vty, " Route Refresh: %10d %10d%s", stats.refresh_out, stats.refresh_in, VTY_NEWLINE);
+ vty_out (vty, " Capability: %10d %10d%s", stats.dynamic_cap_out, stats.dynamic_cap_in, VTY_NEWLINE);
+ vty_out (vty, " Total: %10d %10d%s", stats.open_out + stats.notify_out +
+ stats.update_out + stats.keepalive_out + stats.refresh_out + stats.dynamic_cap_out,
+ stats.open_in + stats.notify_in + stats.update_in + stats.keepalive_in + stats.refresh_in +
+ stats.dynamic_cap_in, VTY_NEWLINE);
/* advertisement-interval */
vty_out (vty, " Minimum time between advertisement runs is %d seconds%s",
@@ -7604,8 +7678,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
if (p->update_if)
vty_out (vty, "%s", p->update_if);
else if (p->update_source)
- vty_out (vty, "%s",
- sockunion2str (p->update_source, buf1, SU_ADDRSTRLEN));
+ vty_out (vty, "%s", sutoa(p->update_source).str);
vty_out (vty, "%s", VTY_NEWLINE);
}
@@ -7649,9 +7722,10 @@ bgp_show_peer (struct vty *vty, struct peer *p)
/* EBGP Multihop and GTSM */
if (peer_sort (p) != BGP_PEER_IBGP)
{
- if (p->gtsm_hops > 0)
- vty_out (vty, " External BGP neighbor may be up to %d hops away.%s",
- p->gtsm_hops, VTY_NEWLINE);
+ if (p->gtsm)
+ vty_out (vty, " External BGP neighbor may be up to %d hops away"
+ " -- using GTSM.%s",
+ p->ttl, VTY_NEWLINE);
else if (p->ttl > 1)
vty_out (vty, " External BGP neighbor may be up to %d hops away.%s",
p->ttl, VTY_NEWLINE);
@@ -7661,16 +7735,16 @@ bgp_show_peer (struct vty *vty, struct peer *p)
if (p->su_local)
{
vty_out (vty, "Local host: %s, Local port: %d%s",
- sockunion2str (p->su_local, buf1, SU_ADDRSTRLEN),
+ sutoa(p->su_local).str,
ntohs (p->su_local->sin.sin_port),
VTY_NEWLINE);
}
-
+
/* Remote address. */
if (p->su_remote)
{
vty_out (vty, "Foreign host: %s, Foreign port: %d%s",
- sockunion2str (p->su_remote, buf1, SU_ADDRSTRLEN),
+ sutoa(p->su_remote).str,
ntohs (p->su_remote->sin.sin_port),
VTY_NEWLINE);
}
@@ -7678,15 +7752,15 @@ bgp_show_peer (struct vty *vty, struct peer *p)
/* Nexthop display. */
if (p->su_local)
{
- vty_out (vty, "Nexthop: %s%s",
- inet_ntop (AF_INET, &p->nexthop.v4, buf1, BUFSIZ),
+ vty_out (vty, "Nexthop: %s%s",
+ inet_ntop (AF_INET, &p->nexthop.v4, buf, sizeof(buf)),
VTY_NEWLINE);
#ifdef HAVE_IPV6
- vty_out (vty, "Nexthop global: %s%s",
- inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, BUFSIZ),
+ vty_out (vty, "Nexthop global: %s%s",
+ inet_ntop (AF_INET6, &p->nexthop.v6_global, buf, sizeof(buf)),
VTY_NEWLINE);
vty_out (vty, "Nexthop local: %s%s",
- inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, BUFSIZ),
+ inet_ntop (AF_INET6, &p->nexthop.v6_local, buf, sizeof(buf)),
VTY_NEWLINE);
vty_out (vty, "BGP connection: %s%s",
p->shared_network ? "shared network" : "non shared network",
@@ -7694,23 +7768,28 @@ bgp_show_peer (struct vty *vty, struct peer *p)
#endif /* HAVE_IPV6 */
}
- /* Timer information. */
+ /* TODO: Timer information. */
+#if 0
if (p->t_start)
vty_out (vty, "Next start timer due in %ld seconds%s",
thread_timer_remain_second (p->t_start), VTY_NEWLINE);
if (p->t_connect)
vty_out (vty, "Next connect timer due in %ld seconds%s",
thread_timer_remain_second (p->t_connect), VTY_NEWLINE);
-
- vty_out (vty, "Read thread: %s Write thread: %s%s",
+#endif
+
+#if 0
+ vty_out (vty, "Read thread: %s Write thread: %s%s",
p->t_read ? "on" : "off",
p->t_write ? "on" : "off",
VTY_NEWLINE);
+#endif
- if (p->notify.code == BGP_NOTIFY_OPEN_ERR
- && p->notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL)
+ if (p->session != NULL && p->session->notification != NULL
+ && p->session->notification->code == BGP_NOTIFY_OPEN_ERR
+ && p->session->notification->subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL)
bgp_capability_vty_out (vty, p);
-
+
vty_out (vty, "%s", VTY_NEWLINE);
}
@@ -7741,12 +7820,12 @@ bgp_show_neighbor (struct vty *vty, struct bgp *bgp,
if (type == show_peer && ! find)
vty_out (vty, "%% No such neighbor%s", VTY_NEWLINE);
-
+
return CMD_SUCCESS;
}
-static int
-bgp_show_neighbor_vty (struct vty *vty, const char *name,
+static int
+bgp_show_neighbor_vty (struct vty *vty, const char *name,
enum show_type type, const char *ip_str)
{
int ret;
@@ -7766,10 +7845,10 @@ bgp_show_neighbor_vty (struct vty *vty, const char *name,
if (name)
{
bgp = bgp_lookup_by_name (name);
-
+
if (! bgp)
{
- vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE);
+ vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -7981,11 +8060,11 @@ ALIAS (show_ip_bgp_instance_neighbors_peer,
"Detailed information on TCP and BGP neighbor connections\n"
"Neighbor to display information about\n"
"Neighbor to display information about\n")
-
+
/* Show BGP's AS paths internal data. There are both `show ip bgp
paths' and `show ip mbgp paths'. Those functions results are the
same.*/
-DEFUN (show_ip_bgp_paths,
+DEFUN (show_ip_bgp_paths,
show_ip_bgp_paths_cmd,
"show ip bgp paths",
SHOW_STR
@@ -7998,7 +8077,7 @@ DEFUN (show_ip_bgp_paths,
return CMD_SUCCESS;
}
-DEFUN (show_ip_bgp_ipv4_paths,
+DEFUN (show_ip_bgp_ipv4_paths,
show_ip_bgp_ipv4_paths_cmd,
"show ip bgp ipv4 (unicast|multicast) paths",
SHOW_STR
@@ -8014,7 +8093,7 @@ DEFUN (show_ip_bgp_ipv4_paths,
return CMD_SUCCESS;
}
-
+
#include "hash.h"
static void
@@ -8028,7 +8107,7 @@ community_show_all_iterator (struct hash_backet *backet, struct vty *vty)
}
/* Show BGP's community internal data. */
-DEFUN (show_ip_bgp_community_info,
+DEFUN (show_ip_bgp_community_info,
show_ip_bgp_community_info_cmd,
"show ip bgp community-info",
SHOW_STR
@@ -8038,7 +8117,7 @@ DEFUN (show_ip_bgp_community_info,
{
vty_out (vty, "Address Refcnt Community%s", VTY_NEWLINE);
- hash_iterate (community_hash (),
+ hash_iterate (community_hash (),
(void (*) (struct hash_backet *, void *))
community_show_all_iterator,
vty);
@@ -8046,7 +8125,7 @@ DEFUN (show_ip_bgp_community_info,
return CMD_SUCCESS;
}
-DEFUN (show_ip_bgp_attr_info,
+DEFUN (show_ip_bgp_attr_info,
show_ip_bgp_attr_info_cmd,
"show ip bgp attribute-info",
SHOW_STR
@@ -8057,7 +8136,7 @@ DEFUN (show_ip_bgp_attr_info,
attr_show_all (vty);
return CMD_SUCCESS;
}
-
+
static int
bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient,
afi_t afi, safi_t safi)
@@ -8080,8 +8159,8 @@ bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient,
return count;
}
- len = vty_out (vty, "%s", rsclient->host);
- len = 16 - len;
+ vty_out (vty, "%s", rsclient->host);
+ len = 16 - strlen(rsclient->host) ;
if (len < 1)
vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " ");
@@ -8119,7 +8198,7 @@ bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient,
else if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_PREFIX_OVERFLOW))
vty_out (vty, " Idle (PfxCt)");
else
- vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, rsclient->status));
+ vty_out (vty, " %-11s", LOOKUP(bgp_peer_status_msg, rsclient->state));
vty_out (vty, "%s", VTY_NEWLINE);
@@ -8127,7 +8206,7 @@ bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient,
}
static int
-bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp,
+bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp,
afi_t afi, safi_t safi)
{
struct peer *peer;
@@ -8146,7 +8225,7 @@ bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp,
{
vty_out (vty,
"Route Server's BGP router identifier %s%s",
- inet_ntoa (bgp->router_id), VTY_NEWLINE);
+ safe_inet_ntoa (bgp->router_id), VTY_NEWLINE);
vty_out (vty,
"Route Server's local AS number %u%s", bgp->as,
VTY_NEWLINE);
@@ -8170,7 +8249,7 @@ bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp,
}
static int
-bgp_show_rsclient_summary_vty (struct vty *vty, const char *name,
+bgp_show_rsclient_summary_vty (struct vty *vty, const char *name,
afi_t afi, safi_t safi)
{
struct bgp *bgp;
@@ -8378,7 +8457,7 @@ ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary,
"Summary of all Route Server Clients\n")
#endif /* HAVE IPV6 */
-
+
/* Redistribute VTY commands. */
/* Utility function to convert user input route type string to route
@@ -8669,7 +8748,7 @@ ALIAS (no_bgp_redistribute_ipv4_rmap_metric,
"Default metric\n"
"Route map reference\n"
"Pointer to route-map entries\n")
-
+
#ifdef HAVE_IPV6
DEFUN (bgp_redistribute_ipv6,
bgp_redistribute_ipv6_cmd,
@@ -8924,7 +9003,7 @@ ALIAS (no_bgp_redistribute_ipv6_rmap_metric,
"Route map reference\n"
"Pointer to route-map entries\n")
#endif /* HAVE_IPV6 */
-
+
int
bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi,
safi_t safi, int *write)
@@ -8957,7 +9036,7 @@ bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi,
}
return *write;
}
-
+
/* BGP node structure. */
static struct cmd_node bgp_node =
{
@@ -9000,7 +9079,7 @@ static struct cmd_node bgp_vpnv4_node =
"%s(config-router-af)# ",
1
};
-
+
static void community_list_vty (void);
void
@@ -9021,7 +9100,7 @@ bgp_vty_init (void)
install_default (BGP_IPV6_NODE);
install_default (BGP_IPV6M_NODE);
install_default (BGP_VPNV4_NODE);
-
+
/* "bgp multiple-instance" commands. */
install_element (CONFIG_NODE, &bgp_multiple_instance_cmd);
install_element (CONFIG_NODE, &no_bgp_multiple_instance_cmd);
@@ -9074,7 +9153,7 @@ bgp_vty_init (void)
/* "bgp always-compare-med" commands */
install_element (BGP_NODE, &bgp_always_compare_med_cmd);
install_element (BGP_NODE, &no_bgp_always_compare_med_cmd);
-
+
/* "bgp deterministic-med" commands */
install_element (BGP_NODE, &bgp_deterministic_med_cmd);
install_element (BGP_NODE, &no_bgp_deterministic_med_cmd);
@@ -9085,7 +9164,7 @@ bgp_vty_init (void)
install_element (BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd);
install_element (BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd);
install_element (BGP_NODE, &no_bgp_graceful_restart_stalepath_time_val_cmd);
-
+
/* "bgp fast-external-failover" commands */
install_element (BGP_NODE, &bgp_fast_external_failover_cmd);
install_element (BGP_NODE, &no_bgp_fast_external_failover_cmd);
@@ -9121,7 +9200,7 @@ bgp_vty_init (void)
/* "no bgp default ipv4-unicast" commands. */
install_element (BGP_NODE, &no_bgp_default_ipv4_unicast_cmd);
install_element (BGP_NODE, &bgp_default_ipv4_unicast_cmd);
-
+
/* "bgp network import-check" commands. */
install_element (BGP_NODE, &bgp_network_import_check_cmd);
install_element (BGP_NODE, &no_bgp_network_import_check_cmd);
@@ -9175,7 +9254,7 @@ bgp_vty_init (void)
install_element (BGP_IPV6_NODE, &neighbor_set_peer_group_cmd);
install_element (BGP_IPV6M_NODE, &neighbor_set_peer_group_cmd);
install_element (BGP_VPNV4_NODE, &neighbor_set_peer_group_cmd);
-
+
/* "no neighbor peer-group unset" commands. */
install_element (BGP_NODE, &no_neighbor_set_peer_group_cmd);
install_element (BGP_IPV4_NODE, &no_neighbor_set_peer_group_cmd);
@@ -9183,7 +9262,7 @@ bgp_vty_init (void)
install_element (BGP_IPV6_NODE, &no_neighbor_set_peer_group_cmd);
install_element (BGP_IPV6M_NODE, &no_neighbor_set_peer_group_cmd);
install_element (BGP_VPNV4_NODE, &no_neighbor_set_peer_group_cmd);
-
+
/* "neighbor softreconfiguration inbound" commands.*/
install_element (BGP_NODE, &neighbor_soft_reconfiguration_cmd);
install_element (BGP_NODE, &no_neighbor_soft_reconfiguration_cmd);
@@ -9605,7 +9684,7 @@ bgp_vty_init (void)
install_element (BGP_IPV6M_NODE, &neighbor_unsuppress_map_cmd);
install_element (BGP_IPV6M_NODE, &no_neighbor_unsuppress_map_cmd);
install_element (BGP_VPNV4_NODE, &neighbor_unsuppress_map_cmd);
- install_element (BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element (BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd);
/* "neighbor maximum-prefix" commands. */
install_element (BGP_NODE, &neighbor_maximum_prefix_cmd);
@@ -10112,16 +10191,16 @@ bgp_vty_init (void)
install_element (VIEW_NODE, &show_bgp_memory_cmd);
install_element (RESTRICTED_NODE, &show_bgp_memory_cmd);
install_element (ENABLE_NODE, &show_bgp_memory_cmd);
-
+
/* "show bgp views" commands. */
install_element (VIEW_NODE, &show_bgp_views_cmd);
install_element (RESTRICTED_NODE, &show_bgp_views_cmd);
install_element (ENABLE_NODE, &show_bgp_views_cmd);
-
+
/* Community-list. */
community_list_vty ();
}
-
+
#include "memory.h"
#include "bgp_regex.h"
#include "bgp_clist.h"
@@ -10167,7 +10246,7 @@ community_list_perror (struct vty *vty, int ret)
/* VTY interface for community_set() function. */
static int
-community_list_set_vty (struct vty *vty, int argc, const char **argv,
+community_list_set_vty (struct vty *vty, int argc, argv_t argv,
int style, int reject_all_digit_name)
{
int ret;
@@ -10220,7 +10299,7 @@ community_list_set_vty (struct vty *vty, int argc, const char **argv,
/* Communiyt-list entry delete. */
static int
-community_list_unset_vty (struct vty *vty, int argc, const char **argv,
+community_list_unset_vty (struct vty *vty, int argc, argv_t argv,
int style)
{
int ret;
@@ -10448,20 +10527,21 @@ community_list_show (struct vty *vty, struct community_list *list)
{
struct community_entry *entry;
+ const char* list_name = symbol_get_name(list->sym) ;
for (entry = list->head; entry; entry = entry->next)
{
if (entry == list->head)
{
- if (all_digit (list->name))
+ if (all_digit (list_name))
vty_out (vty, "Community %s list %s%s",
entry->style == COMMUNITY_LIST_STANDARD ?
"standard" : "(expanded) access",
- list->name, VTY_NEWLINE);
+ list_name, VTY_NEWLINE);
else
vty_out (vty, "Named Community %s list %s%s",
entry->style == COMMUNITY_LIST_STANDARD ?
"standard" : "expanded",
- list->name, VTY_NEWLINE);
+ list_name, VTY_NEWLINE);
}
if (entry->any)
vty_out (vty, " %s%s",
@@ -10482,18 +10562,26 @@ DEFUN (show_ip_community_list,
IP_STR
"List community-list\n")
{
+ struct symbol_table* table;
+ vector extract ;
+ vector_index i ;
+ struct symbol* sym ;
struct community_list *list;
- struct community_list_master *cm;
- cm = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER);
- if (! cm)
+ table = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER);
+ if (table == NULL)
return CMD_SUCCESS;
- for (list = cm->num.head; list; list = list->next)
- community_list_show (vty, list);
+ extract = symbol_table_extract(table, NULL, NULL, 0, symbol_mixed_name_cmp) ;
+
+ for (VECTOR_ITEMS(extract, sym, i))
+ {
+ list = symbol_get_value(sym) ;
+ if (list != NULL)
+ community_list_show (vty, list);
+ } ;
- for (list = cm->str.head; list; list = list->next)
- community_list_show (vty, list);
+ vector_free(extract) ; /* discard temporary vector */
return CMD_SUCCESS;
}
@@ -10520,9 +10608,9 @@ DEFUN (show_ip_community_list_arg,
return CMD_SUCCESS;
}
-
+
static int
-extcommunity_list_set_vty (struct vty *vty, int argc, const char **argv,
+extcommunity_list_set_vty (struct vty *vty, int argc, argv_t argv,
int style, int reject_all_digit_name)
{
int ret;
@@ -10570,8 +10658,7 @@ extcommunity_list_set_vty (struct vty *vty, int argc, const char **argv,
}
static int
-extcommunity_list_unset_vty (struct vty *vty, int argc, const char **argv,
- int style)
+extcommunity_list_unset_vty (struct vty *vty, int argc, argv_t argv, int style)
{
int ret;
int direct = 0;
@@ -10798,20 +10885,22 @@ extcommunity_list_show (struct vty *vty, struct community_list *list)
{
struct community_entry *entry;
+ const char* list_name = symbol_get_name(list->sym) ;
+
for (entry = list->head; entry; entry = entry->next)
{
if (entry == list->head)
{
- if (all_digit (list->name))
+ if (all_digit (list_name))
vty_out (vty, "Extended community %s list %s%s",
entry->style == EXTCOMMUNITY_LIST_STANDARD ?
"standard" : "(expanded) access",
- list->name, VTY_NEWLINE);
+ list_name, VTY_NEWLINE);
else
vty_out (vty, "Named extended community %s list %s%s",
entry->style == EXTCOMMUNITY_LIST_STANDARD ?
"standard" : "expanded",
- list->name, VTY_NEWLINE);
+ list_name, VTY_NEWLINE);
}
if (entry->any)
vty_out (vty, " %s%s",
@@ -10832,18 +10921,26 @@ DEFUN (show_ip_extcommunity_list,
IP_STR
"List extended-community list\n")
{
+ struct symbol_table* table;
+ vector extract ;
+ vector_index i ;
+ struct symbol* sym ;
struct community_list *list;
- struct community_list_master *cm;
- cm = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER);
- if (! cm)
+ table = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER);
+ if (table == NULL)
return CMD_SUCCESS;
- for (list = cm->num.head; list; list = list->next)
- extcommunity_list_show (vty, list);
+ extract = symbol_table_extract(table, NULL, NULL, 0, symbol_mixed_name_cmp) ;
+
+ for (VECTOR_ITEMS(extract, sym, i))
+ {
+ list = symbol_get_value(sym) ;
+ if (list != NULL)
+ extcommunity_list_show (vty, list);
+ } ;
- for (list = cm->str.head; list; list = list->next)
- extcommunity_list_show (vty, list);
+ vector_free(extract) ; /* discard temporary vector */
return CMD_SUCCESS;
}
@@ -10870,7 +10967,7 @@ DEFUN (show_ip_extcommunity_list_arg,
return CMD_SUCCESS;
}
-
+
/* Return configuration string of community-list entry. */
static const char *
community_list_config_str (struct community_entry *entry)
@@ -10889,60 +10986,80 @@ community_list_config_str (struct community_entry *entry)
return str;
}
-/* Display community-list and extcommunity-list configuration. */
+/* Put entire community-list or extcommunity-list. */
static int
-community_list_config_write (struct vty *vty)
+community_list_config_write_list(struct vty* vty, int what)
{
+ struct symbol_table* table;
struct community_list *list;
struct community_entry *entry;
- struct community_list_master *cm;
+ vector extract ;
+ vector_index i ;
+ struct symbol* sym ;
+
int write = 0;
- /* Community-list. */
- cm = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER);
-
- for (list = cm->num.head; list; list = list->next)
- for (entry = list->head; entry; entry = entry->next)
- {
- vty_out (vty, "ip community-list %s %s %s%s",
- list->name, community_direct_str (entry->direct),
- community_list_config_str (entry),
- VTY_NEWLINE);
- write++;
- }
- for (list = cm->str.head; list; list = list->next)
- for (entry = list->head; entry; entry = entry->next)
- {
- vty_out (vty, "ip community-list %s %s %s %s%s",
- entry->style == COMMUNITY_LIST_STANDARD
- ? "standard" : "expanded",
- list->name, community_direct_str (entry->direct),
- community_list_config_str (entry),
- VTY_NEWLINE);
- write++;
- }
+ table = community_list_master_lookup (bgp_clist, what);
+
+ extract = symbol_table_extract(table, NULL, NULL, 0, symbol_mixed_name_cmp) ;
+ for (VECTOR_ITEMS(extract, sym, i))
+ {
+ list = symbol_get_value(sym) ;
+
+ if (list == NULL)
+ continue ;
+
+ for (entry = list->head; entry; entry = entry->next)
+ {
+ const char* list_type = "" ;
+ const char* list_style = "" ;
+ const char* list_name = symbol_get_name(list->sym) ;
+
+ switch (entry->style)
+ {
+ case COMMUNITY_LIST_STANDARD:
+ list_type = "community-list" ;
+ list_style = "standard " ;
+ break ;
+ case COMMUNITY_LIST_EXPANDED:
+ list_type = "community-list" ;
+ list_style = "expanded " ;
+ break ;
+ case EXTCOMMUNITY_LIST_STANDARD:
+ list_type = "extcommunity-list" ;
+ list_style = "standard " ;
+ break ;
+ case EXTCOMMUNITY_LIST_EXPANDED:
+ list_type = "extcommunity-list" ;
+ list_style = "expanded " ;
+ break ;
+ } ;
+ if (all_digit(list_name))
+ list_style = "" ; /* squash style for all digit names */
+
+ vty_out (vty, "ip %s %s%s %s %s%s",
+ list_type, list_style, list_name,
+ community_direct_str (entry->direct),
+ community_list_config_str (entry),
+ VTY_NEWLINE);
+ write++;
+ }
+ }
+
+ vector_free(extract) ; /* discard temporary vector */
+
+ return write;
+}
+
+/* Display community-list and extcommunity-list configuration. */
+static int
+community_list_config_write (struct vty *vty)
+{
+ int write = 0;
+
+ write += community_list_config_write_list(vty, COMMUNITY_LIST_MASTER) ;
+ write += community_list_config_write_list(vty, EXTCOMMUNITY_LIST_MASTER);
- /* Extcommunity-list. */
- cm = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER);
-
- for (list = cm->num.head; list; list = list->next)
- for (entry = list->head; entry; entry = entry->next)
- {
- vty_out (vty, "ip extcommunity-list %s %s %s%s",
- list->name, community_direct_str (entry->direct),
- community_list_config_str (entry), VTY_NEWLINE);
- write++;
- }
- for (list = cm->str.head; list; list = list->next)
- for (entry = list->head; entry; entry = entry->next)
- {
- vty_out (vty, "ip extcommunity-list %s %s %s %s%s",
- entry->style == EXTCOMMUNITY_LIST_STANDARD
- ? "standard" : "expanded",
- list->name, community_direct_str (entry->direct),
- community_list_config_str (entry), VTY_NEWLINE);
- write++;
- }
return write;
}
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index f3baeee0..fd7cd39e 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;
@@ -166,7 +167,7 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length)
continue;
if (ifp == peer_if)
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_INTERFACE_DOWN);
}
}
}
@@ -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);
}
}
@@ -645,6 +646,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp)
int flags;
u_char distance;
struct peer *peer;
+ bgp_peer_sort_t sort ;
if (zclient->sock < 0)
return;
@@ -653,15 +655,16 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp)
return;
flags = 0;
- peer = info->peer;
+ peer = info->peer;
+ sort = peer_sort(peer) ;
- if (peer_sort (peer) == BGP_PEER_IBGP || peer_sort (peer) == BGP_PEER_CONFED)
+ if ((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED))
{
SET_FLAG (flags, ZEBRA_FLAG_IBGP);
SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
}
- if ((peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1)
+ if (((sort == BGP_PEER_EBGP) && (peer->ttl != 1))
|| CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
@@ -700,7 +703,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 +716,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 +771,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 */
@@ -779,6 +782,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info)
{
int flags;
struct peer *peer;
+ bgp_peer_sort_t sort ;
if (zclient->sock < 0)
return;
@@ -786,16 +790,17 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info)
if (! zclient->redist[ZEBRA_ROUTE_BGP])
return;
- peer = info->peer;
flags = 0;
+ peer = info->peer;
+ sort = peer_sort(peer) ;
- if (peer_sort (peer) == BGP_PEER_IBGP)
+ if (sort == BGP_PEER_IBGP)
{
SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG (flags, ZEBRA_FLAG_IBGP);
}
- if ((peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1)
+ if (((sort == BGP_PEER_EBGP) && (peer->ttl != 1))
|| CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
@@ -826,7 +831,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 +841,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 +888,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 +913,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 +922,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 +974,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 +984,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 +1019,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 ee0cc5da..ff601ba1 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.h"
+#include "bgpd/bgp_peer.h"
+
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
@@ -60,7 +63,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifdef HAVE_SNMP
#include "bgpd/bgp_snmp.h"
#endif /* HAVE_SNMP */
-
+
/* BGP process wide configuration. */
static struct bgp_master bgp_master;
@@ -69,9 +72,14 @@ extern struct in_addr router_id_zebra;
/* BGP process wide configuration pointer to export. */
struct bgp_master *bm;
+/* BGP process wide nexus. */
+qpn_nexus cli_nexus = NULL;
+qpn_nexus bgp_nexus = NULL;
+qpn_nexus routing_nexus = NULL;
+
/* BGP community-list. */
struct community_list_handler *bgp_clist;
-
+
/* BGP global flag manipulation. */
int
bgp_option_set (int flag)
@@ -113,7 +121,7 @@ bgp_option_check (int flag)
{
return CHECK_FLAG (bm->options, flag);
}
-
+
/* BGP flag manipulation. */
int
bgp_flag_set (struct bgp *bgp, int flag)
@@ -134,7 +142,7 @@ bgp_flag_check (struct bgp *bgp, int flag)
{
return CHECK_FLAG (bgp->flags, flag);
}
-
+
/* Internal function to set BGP structure configureation flag. */
static void
bgp_config_set (struct bgp *bgp, int config)
@@ -153,7 +161,7 @@ bgp_config_check (struct bgp *bgp, int config)
{
return CHECK_FLAG (bgp->config, config);
}
-
+
/* Set BGP router identifier. */
int
bgp_router_id_set (struct bgp *bgp, struct in_addr *id)
@@ -172,13 +180,7 @@ bgp_router_id_set (struct bgp *bgp, struct in_addr *id)
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
IPV4_ADDR_COPY (&peer->local_id, id);
-
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_RID_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
+ bgp_peer_down(peer, PEER_DOWN_RID_CHANGE) ;
}
return 0;
}
@@ -203,12 +205,7 @@ bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id)
if (peer_sort (peer) != BGP_PEER_IBGP)
continue;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CLID_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
+ bgp_peer_down(peer, PEER_DOWN_CLID_CHANGE) ;
}
return 0;
}
@@ -231,32 +228,16 @@ bgp_cluster_id_unset (struct bgp *bgp)
if (peer_sort (peer) != BGP_PEER_IBGP)
continue;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CLID_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
+ bgp_peer_down(peer, PEER_DOWN_CLID_CHANGE) ;
}
return 0;
}
-
-/* time_t value that is monotonicly increasing
- * and uneffected by adjustments to system clock
- */
-time_t bgp_clock (void)
-{
- struct timeval tv;
-
- quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv);
- return tv.tv_sec;
-}
/* BGP timer configuration. */
int
bgp_timers_set (struct bgp *bgp, u_int32_t keepalive, u_int32_t holdtime)
{
- bgp->default_keepalive = (keepalive < holdtime / 3
+ bgp->default_keepalive = (keepalive < holdtime / 3
? keepalive : holdtime / 3);
bgp->default_holdtime = holdtime;
@@ -267,11 +248,11 @@ int
bgp_timers_unset (struct bgp *bgp)
{
bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
- bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+ bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
return 0;
}
-
+
/* BGP confederation configuration. */
int
bgp_confederation_id_set (struct bgp *bgp, as_t as)
@@ -293,41 +274,28 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as)
were not doing confederation before, reset all EBGP sessions. */
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
+ bgp_peer_sort_t sort = peer_sort (peer) ;
/* We're looking for peers who's AS is not local or part of our
confederation. */
if (already_confed)
{
- if (peer_sort (peer) == BGP_PEER_EBGP)
+ if (sort == BGP_PEER_EBGP)
{
peer->local_as = as;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
-
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ;
}
}
else
{
- /* Not doign confederation before, so reset every non-local
+ /* Not doing confederation before, so reset every non-local
session */
- if (peer_sort (peer) != BGP_PEER_IBGP)
+ if (sort != BGP_PEER_IBGP)
{
/* Reset the local_as to be our EBGP one */
- if (peer_sort (peer) == BGP_PEER_EBGP)
+ if (sort == BGP_PEER_EBGP)
peer->local_as = as;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+
+ bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ;
}
}
}
@@ -342,22 +310,14 @@ bgp_confederation_id_unset (struct bgp *bgp)
bgp->confed_id = 0;
bgp_config_unset (bgp, BGP_CONFIG_CONFEDERATION);
-
+
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
{
/* We're looking for peers who's AS is not local */
if (peer_sort (peer) != BGP_PEER_IBGP)
{
peer->local_as = bgp->as;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
-
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ;
}
}
return 0;
@@ -375,7 +335,7 @@ bgp_confederation_peers_check (struct bgp *bgp, as_t as)
for (i = 0; i < bgp->confed_peers_cnt; i++)
if (bgp->confed_peers[i] == as)
return 1;
-
+
return 0;
}
@@ -396,11 +356,11 @@ bgp_confederation_peers_add (struct bgp *bgp, as_t as)
return -1;
if (bgp->confed_peers)
- bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST,
+ bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST,
bgp->confed_peers,
(bgp->confed_peers_cnt + 1) * sizeof (as_t));
else
- bgp->confed_peers = XMALLOC (MTYPE_BGP_CONFED_LIST,
+ bgp->confed_peers = XMALLOC (MTYPE_BGP_CONFED_LIST,
(bgp->confed_peers_cnt + 1) * sizeof (as_t));
bgp->confed_peers[bgp->confed_peers_cnt] = as;
@@ -413,14 +373,7 @@ bgp_confederation_peers_add (struct bgp *bgp, as_t as)
if (peer->as == as)
{
peer->local_as = bgp->as;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_CONFED_PEER_CHANGE) ;
}
}
}
@@ -469,21 +422,14 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as)
if (peer->as == as)
{
peer->local_as = bgp->confed_id;
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_CONFED_PEER_CHANGE) ;
}
}
}
return 0;
}
-
+
/* Local preference configuration. */
int
bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref)
@@ -506,7 +452,7 @@ bgp_default_local_preference_unset (struct bgp *bgp)
return 0;
}
-
+
/* If peer is RSERVER_CLIENT in at least one address family and is not member
of a peer_group for that family, return 1.
Used to check wether the peer is included in list bgp->rsclient. */
@@ -555,18 +501,14 @@ peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi)
free (filter->dlist[i].name);
filter->dlist[i].name = NULL;
}
- if (filter->plist[i].name)
- {
- free (filter->plist[i].name);
- filter->plist[i].name = NULL;
- }
+ prefix_list_unset_ref(&filter->plist[i].ref) ;
if (filter->aslist[i].name)
{
free (filter->aslist[i].name);
filter->aslist[i].name = NULL;
}
}
- for (i = RMAP_IN; i < RMAP_MAX; i++)
+ for (i = RMAP_IN; i < RMAP_MAX; i++)
{
if (filter->map[i].name)
{
@@ -619,7 +561,19 @@ peer_global_config_reset (struct peer *peer)
{
peer->weight = 0;
peer->change_local_as = 0;
- peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1);
+
+ if (peer_sort (peer) == BGP_PEER_IBGP)
+ {
+ peer->ttl = MAXTTL ;
+ peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+ }
+ else /* BGP_PEER_EBGP & BGP_PEER_CONFED */
+ {
+ peer->ttl = 1 ;
+ peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+ } ;
+ peer->gtsm = false ;
+
if (peer->update_source)
{
sockunion_free (peer->update_source);
@@ -631,11 +585,6 @@ peer_global_config_reset (struct peer *peer)
peer->update_if = NULL;
}
- if (peer_sort (peer) == BGP_PEER_IBGP)
- peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
- else
- peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
-
peer->flags = 0;
peer->config = 0;
peer->holdtime = 0;
@@ -644,7 +593,23 @@ peer_global_config_reset (struct peer *peer)
peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
}
-/* Check peer's AS number and determin is this peer IBGP or EBGP */
+/*------------------------------------------------------------------------------
+ * Check peer's AS number and determine is this peer IBGP or EBGP
+ *
+ * peer->as is the ASN heard from the peer at OPEN time (remote ASN)
+ *
+ * peer->local_as is the ASN sent to the peer at OPEN time
+ *
+ * Generally this will be the bgp->as, but for confederations
+ * the bgp->as is the member ASN, so for peers outside the
+ * confederation the local_as is set to the confed_id.
+ *
+ * Returns: BGP_PEER_EBGP
+ * BGP_PEER_IBGP -- including within a confederation member-AS
+ * BGP_PEER_CONFED -- between different confederation member-ASes
+ * BGP_PEER_INTERNAL -- this is group, and remote ASN is not set
+ * or is not group amd local ASN is not set
+ */
int
peer_sort (struct peer *peer)
{
@@ -652,35 +617,58 @@ peer_sort (struct peer *peer)
bgp = peer->bgp;
- /* Peer-group */
+ /* Peer-group
+ *
+ * If the group's ASN (remote ASN) is set, return iBGP or eBGP depending on
+ * the bgp ASN (local ASN).
+ *
+ * Otherwise: look at first peer in group (if any) and return iBGP or eBGP,
+ * depending on that peer's local and remote ASNs.
+ *
+ * Note that all peers in a group which does not specify an ASN,
+ * must all be of the same sort.
+ *
+ * Does not check for peer1->local_as being unset, because that
+ * is not realy possible... TODO ???
+ *
+ * Otherwise: return BGP_PEER_INTERNAL.
+ *
+ * NB: does not trouble itself about CONFEDERATION TODO ????
+ */
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
if (peer->as)
- return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP);
+ return (bgp->as == peer->as) ? BGP_PEER_IBGP : BGP_PEER_EBGP ;
else
{
struct peer *peer1;
peer1 = listnode_head (peer->group->peer);
if (peer1)
- return (peer1->local_as == peer1->as
- ? BGP_PEER_IBGP : BGP_PEER_EBGP);
- }
+ return (peer1->local_as == peer1->as) ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP ;
+ }
return BGP_PEER_INTERNAL;
}
- /* Normal peer */
+ /* Normal peer
+ *
+ * If the peer's local ASN is not set, return BGP_PEER_INTERNAL.
+ *
+ * Otherwise: if peer's remote and local ASN are the same, return iBGP unless
+ * it's the confed_id, in which case return eBGP TODO ????
+ *
+ * Otherwise: if this is a confederation peer, return BGP_PEER_CONFED.
+ *
+ * Otherwise: return eBGP
+ */
+ if (peer->local_as == 0) /* not really possible... TODO ??? */
+ return BGP_PEER_INTERNAL;
+
if (bgp && CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
{
- if (peer->local_as == 0)
- return BGP_PEER_INTERNAL;
-
if (peer->local_as == peer->as)
- {
- if (peer->local_as == bgp->confed_id)
- return BGP_PEER_EBGP;
- else
- return BGP_PEER_IBGP;
- }
+ return (peer->local_as != bgp->confed_id) ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP ;
if (bgp_confederation_peers_check (bgp, peer->as))
return BGP_PEER_CONFED;
@@ -689,204 +677,19 @@ peer_sort (struct peer *peer)
}
else
{
- return (peer->local_as == 0
- ? BGP_PEER_INTERNAL : peer->local_as == peer->as
- ? BGP_PEER_IBGP : BGP_PEER_EBGP);
+ return (peer->local_as == peer->as) ? BGP_PEER_IBGP : BGP_PEER_EBGP ;
}
}
-static inline void
-peer_free (struct peer *peer)
-{
- assert (peer->status == Deleted);
-
- bgp_unlock(peer->bgp);
-
- /* this /ought/ to have been done already through bgp_stop earlier,
- * but just to be sure..
- */
- bgp_timer_set (peer);
- BGP_READ_OFF (peer->t_read);
- BGP_WRITE_OFF (peer->t_write);
- BGP_EVENT_FLUSH (peer);
-
- if (peer->desc)
- XFREE (MTYPE_PEER_DESC, peer->desc);
-
- /* Free allocated host character. */
- if (peer->host)
- XFREE (MTYPE_BGP_PEER_HOST, peer->host);
-
- /* Update source configuration. */
- if (peer->update_source)
- sockunion_free (peer->update_source);
-
- if (peer->update_if)
- XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
-
- if (peer->clear_node_queue)
- work_queue_free (peer->clear_node_queue);
-
- bgp_sync_delete (peer);
- memset (peer, 0, sizeof (struct peer));
-
- XFREE (MTYPE_BGP_PEER, peer);
-}
-
-/* increase reference count on a struct peer */
-struct peer *
-peer_lock (struct peer *peer)
-{
- assert (peer && (peer->lock >= 0));
-
- peer->lock++;
-
- return peer;
-}
-
-/* decrease reference count on a struct peer
- * struct peer is freed and NULL returned if last reference
- */
-struct peer *
-peer_unlock (struct peer *peer)
-{
- assert (peer && (peer->lock > 0));
-
- peer->lock--;
-
- if (peer->lock == 0)
- {
-#if 0
- zlog_debug ("unlocked and freeing");
- zlog_backtrace (LOG_DEBUG);
-#endif
- peer_free (peer);
- return NULL;
- }
-
-#if 0
- if (peer->lock == 1)
- {
- zlog_debug ("unlocked to 1");
- zlog_backtrace (LOG_DEBUG);
- }
-#endif
-
- return peer;
-}
-
-/* Allocate new peer object, implicitely locked. */
-static struct peer *
-peer_new (struct bgp *bgp)
-{
- afi_t afi;
- safi_t safi;
- struct peer *peer;
- struct servent *sp;
-
- /* bgp argument is absolutely required */
- assert (bgp);
- if (!bgp)
- return NULL;
-
- /* Allocate new peer. */
- peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer));
-
- /* Set default value. */
- peer->fd = -1;
- peer->v_start = BGP_INIT_START_TIMER;
- peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
- peer->v_asorig = BGP_DEFAULT_ASORIGINATE;
- peer->status = Idle;
- peer->ostatus = Idle;
- peer->weight = 0;
- peer->password = NULL;
- peer->bgp = bgp;
- peer = peer_lock (peer); /* initial reference */
- bgp_lock (bgp);
-
- /* Set default flags. */
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- {
- if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
- {
- SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
- SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
- }
- peer->orf_plist[afi][safi] = NULL;
- }
- SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
-
- /* Create buffers. */
- peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE);
- peer->obuf = stream_fifo_new ();
- peer->work = stream_new (BGP_MAX_PACKET_SIZE);
-
- bgp_sync_init (peer);
-
- /* Get service port number. */
- sp = getservbyname ("bgp", "tcp");
- peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);
-
- return peer;
-}
-
-/* Create new BGP peer. */
-static struct peer *
-peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
- as_t remote_as, afi_t afi, safi_t safi)
-{
- int active;
- struct peer *peer;
- char buf[SU_ADDRSTRLEN];
-
- peer = peer_new (bgp);
- peer->su = *su;
- peer->local_as = local_as;
- peer->as = remote_as;
- peer->local_id = bgp->router_id;
- peer->v_holdtime = bgp->default_holdtime;
- peer->v_keepalive = bgp->default_keepalive;
- if (peer_sort (peer) == BGP_PEER_IBGP)
- peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
- else
- peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
-
- peer = peer_lock (peer); /* bgp peer list reference */
- listnode_add_sort (bgp->peer, peer);
-
- active = peer_active (peer);
-
- if (afi && safi)
- peer->afc[afi][safi] = 1;
-
- /* Last read and reset time set */
- peer->readtime = peer->resettime = bgp_clock ();
-
- /* Default TTL set. */
- peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1);
-
- /* Make peer's address string. */
- sockunion2str (su, buf, SU_ADDRSTRLEN);
- peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
-
- /* Set up peer's events and timers. */
- if (! active && peer_active (peer))
- bgp_timer_set (peer);
-
- return peer;
-}
-
/* Make accept BGP peer. Called from bgp_accept (). */
struct peer *
peer_create_accept (struct bgp *bgp)
{
struct peer *peer;
- peer = peer_new (bgp);
-
- peer = peer_lock (peer); /* bgp peer list reference */
+ peer = bgp_peer_new (bgp);
+
+ peer = bgp_peer_lock (peer); /* bgp peer list reference */
listnode_add_sort (bgp->peer, peer);
return peer;
@@ -896,22 +699,11 @@ peer_create_accept (struct bgp *bgp)
static void
peer_as_change (struct peer *peer, as_t as)
{
- int type;
+ bgp_peer_sort_t old_sort, new_sort ;
- /* Stop peer. */
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
- type = peer_sort (peer);
+ old_sort = peer_sort (peer);
peer->as = as;
+ new_sort = peer_sort (peer);
if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION)
&& ! bgp_confederation_peers_check (peer->bgp, as)
@@ -921,16 +713,33 @@ peer_as_change (struct peer *peer, as_t as)
peer->local_as = peer->bgp->as;
/* Advertisement-interval reset */
- if (peer_sort (peer) == BGP_PEER_IBGP)
+ if (new_sort == BGP_PEER_IBGP)
peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
else
peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
- /* TTL reset */
- if (peer_sort (peer) == BGP_PEER_IBGP)
- peer->ttl = 255;
- else if (type == BGP_PEER_IBGP)
- peer->ttl = 1;
+ /* TTL reset
+ *
+ * -------|----------- new -----------|
+ * old | iBGP | eBGP | Confed |
+ * -------|---------|--------|--------|
+ * iBGP | iBGP | eBGP | eBGP | -- sans GTSM
+ * -------|---------|--------|--------|
+ * eBGP | iBGP | stet | stet |
+ * -------|---------|--------|--------|
+ * Confed | iBGP | stet | stet |
+ * -------|---------|--------|--------|
+* */
+ if (new_sort == BGP_PEER_IBGP)
+ {
+ peer->ttl = MAXTTL ;
+ peer->gtsm = false ;
+ }
+ else if (old_sort == BGP_PEER_IBGP)
+ {
+ peer->ttl = 1;
+ peer->gtsm = false ; /* should already be the case ! */
+ } ;
/* reflector-client reset */
if (peer_sort (peer) != BGP_PEER_IBGP)
@@ -947,12 +756,16 @@ peer_as_change (struct peer *peer, as_t as)
PEER_FLAG_REFLECTOR_CLIENT);
}
- /* local-as reset */
+ /* local-as reset */
if (peer_sort (peer) != BGP_PEER_EBGP)
{
peer->change_local_as = 0;
UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
}
+
+ /* Down peer. */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ bgp_peer_down(peer, PEER_DOWN_REMOTE_AS_CHANGE) ;
}
/* If peer does not exist, create new one. If peer already exists,
@@ -1005,20 +818,26 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as,
/* If the peer is not part of our confederation, and its not an
iBGP peer then spoof the source AS */
if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)
- && ! bgp_confederation_peers_check (bgp, *as)
+ && ! bgp_confederation_peers_check (bgp, *as)
&& bgp->as != *as)
local_as = bgp->confed_id;
else
local_as = bgp->as;
-
- /* If this is IPv4 unicast configuration and "no bgp default
- ipv4-unicast" is specified. */
- if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)
- && afi == AFI_IP && safi == SAFI_UNICAST)
- peer = peer_create (su, bgp, local_as, *as, 0, 0);
+ /* Check that the neighbor IP is unique */
+ if (peer_lookup (NULL, su) != NULL)
+ return BGP_ERR_PEER_EXISTS ;
+
+ /* TODO: report bug... if is IPv4 unicast, may implicitly activate */
+
+ /* If this is IPv4 unicast configuration and "no bgp default
+ ipv4-unicast" is NOT specified.
+ */
+ if (!bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)
+ && (afi == AFI_IP) && (safi == SAFI_UNICAST))
+ peer = bgp_peer_create (su, bgp, local_as, *as, afi, safi);
else
- peer = peer_create (su, bgp, local_as, *as, afi, safi);
+ peer = bgp_peer_create (su, bgp, local_as, *as, 0, 0);
}
return 0;
@@ -1028,7 +847,7 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as,
int
peer_activate (struct peer *peer, afi_t afi, safi_t safi)
{
- int active;
+ bool was_active;
if (peer->afc[afi][safi])
return 0;
@@ -1038,14 +857,20 @@ peer_activate (struct peer *peer, afi_t afi, safi_t safi)
peer->afc[afi][safi] = 1;
else
{
- active = peer_active (peer);
-
+ was_active = peer_active (peer);
peer->afc[afi][safi] = 1;
- if (! active && peer_active (peer))
- bgp_timer_set (peer);
+ /* If wasn't active, can now enable since now is.
+ *
+ * Otherwise, to enable an extra AFI/SAFI need either to use Dynamic
+ * Capabilities or restart the session.
+ */
+ if (! was_active)
+ bgp_peer_enable (peer);
else
- {
+ /* TODO: Dynamic capability */
+#if 0
+ {
if (peer->status == Established)
{
if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV))
@@ -1061,13 +886,14 @@ peer_activate (struct peer *peer, afi_t afi, safi_t safi)
}
}
else
+#endif
{
- peer->last_reset = PEER_DOWN_AF_ACTIVATE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ bgp_peer_down(peer, PEER_DOWN_AF_ACTIVATE) ;
}
+#if 0
}
}
+#endif
}
return 0;
}
@@ -1075,17 +901,19 @@ peer_activate (struct peer *peer, afi_t afi, safi_t safi)
int
peer_deactivate (struct peer *peer, afi_t afi, safi_t safi)
{
- struct peer_group *group;
- struct peer *peer1;
- struct listnode *node, *nnode;
+ bool was_rsclient ;
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer* group_member ;
+
group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, group_member))
{
- if (peer1->af_group[afi][safi])
+ if (group_member->af_group[afi][safi])
return BGP_ERR_PEER_GROUP_MEMBER_EXISTS;
}
}
@@ -1098,220 +926,59 @@ peer_deactivate (struct peer *peer, afi_t afi, safi_t safi)
if (! peer->afc[afi][safi])
return 0;
- /* De-activate the address family configuration. */
+ /* If we arrive here, the peer is either:
+ *
+ * - a real peer which is not a group member for this afi/safi
+ *
+ * - a group which has no members for this afi/safi.
+ *
+ * In which case, if this is an rsclient, it is a distinct rsclient, and the
+ * rsclient RIB should be discarded.
+ */
+ was_rsclient = CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT) ;
+
+ /* De-activate the address family configuration. */
peer->afc[afi][safi] = 0;
peer_af_flag_reset (peer, afi, safi);
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
- if (peer->status == Established)
- {
- if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV))
- {
- peer->afc_adv[afi][safi] = 0;
- peer->afc_nego[afi][safi] = 0;
-
- if (peer_active_nego (peer))
- {
- bgp_capability_send (peer, afi, safi,
- CAPABILITY_CODE_MP,
- CAPABILITY_ACTION_UNSET);
- bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL);
- peer->pcount[afi][safi] = 0;
- }
- else
- {
- peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- }
- else
- {
- peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- }
- }
- return 0;
-}
+ /* Tidy up if was rsclient */
+ if (was_rsclient)
+ peer_rsclient_unset(peer, afi, safi, false) ;
-static void
-peer_nsf_stop (struct peer *peer)
-{
- afi_t afi;
- safi_t safi;
-
- UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
- UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
-
- for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
- for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++)
- peer->nsf[afi][safi] = 0;
-
- if (peer->t_gr_restart)
- {
- BGP_TIMER_OFF (peer->t_gr_restart);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s graceful restart timer stopped", peer->host);
- }
- if (peer->t_gr_stale)
+ /* Deal with knock on effect on real peer */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- BGP_TIMER_OFF (peer->t_gr_stale);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
- }
- bgp_clear_route_all (peer);
-}
-
-/* Delete peer from confguration.
- *
- * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
- * it to "cool off" and refcounts to hit 0, at which state it is freed.
- *
- * This function /should/ take care to be idempotent, to guard against
- * it being called multiple times through stray events that come in
- * that happen to result in this function being called again. That
- * said, getting here for a "Deleted" peer is a bug in the neighbour
- * FSM.
- */
-int
-peer_delete (struct peer *peer)
-{
- int i;
- afi_t afi;
- safi_t safi;
- struct bgp *bgp;
- struct bgp_filter *filter;
- struct listnode *pn;
-
- assert (peer->status != Deleted);
-
- bgp = peer->bgp;
-
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
- peer_nsf_stop (peer);
+ bool down = true ;
- /* If this peer belongs to peer group, clear up the
- relationship. */
- if (peer->group)
- {
- if ((pn = listnode_lookup (peer->group->peer, peer)))
+ if ( (peer->state == bgp_peer_pEstablished)
+ && CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV) )
{
- peer = peer_unlock (peer); /* group->peer list reference */
- list_delete_node (peer->group->peer, pn);
- }
- peer->group = NULL;
- }
-
- /* Withdraw all information from routing table. We can not use
- * BGP_EVENT_ADD (peer, BGP_Stop) at here. Because the event is
- * executed after peer structure is deleted.
- */
- peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
- bgp_stop (peer);
- bgp_fsm_change_status (peer, Deleted);
-
- /* Password configuration */
- if (peer->password)
- {
- XFREE (MTYPE_PEER_PASSWORD, peer->password);
- peer->password = NULL;
-
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- bgp_md5_set (peer);
- }
-
- bgp_timer_set (peer); /* stops all timers for Deleted */
-
- /* Delete from all peer list. */
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
- && (pn = listnode_lookup (bgp->peer, peer)))
- {
- peer_unlock (peer); /* bgp peer list reference */
- list_delete_node (bgp->peer, pn);
- }
-
- if (peer_rsclient_active (peer)
- && (pn = listnode_lookup (bgp->rsclient, peer)))
- {
- peer_unlock (peer); /* rsclient list reference */
- list_delete_node (bgp->rsclient, pn);
-
- /* Clear our own rsclient ribs. */
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- if (CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_RSERVER_CLIENT))
- bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
- }
-
- /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not
- member of a peer_group. */
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- if (peer->rib[afi][safi] && ! peer->af_group[afi][safi])
- bgp_table_finish (&peer->rib[afi][safi]);
-
- /* Buffers. */
- if (peer->ibuf)
- stream_free (peer->ibuf);
- if (peer->obuf)
- stream_fifo_free (peer->obuf);
- if (peer->work)
- stream_free (peer->work);
- peer->obuf = NULL;
- peer->work = peer->ibuf = NULL;
-
- /* Local and remote addresses. */
- if (peer->su_local)
- sockunion_free (peer->su_local);
- if (peer->su_remote)
- sockunion_free (peer->su_remote);
- peer->su_local = peer->su_remote = NULL;
-
- /* Free filter related memory. */
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- {
- filter = &peer->filter[afi][safi];
+ /* If can dynamically reconfigure can avoid restarting the session. */
+ peer->afc_adv[afi][safi] = 0;
+ peer->afc_nego[afi][safi] = 0;
- for (i = FILTER_IN; i < FILTER_MAX; i++)
- {
- if (filter->dlist[i].name)
- free (filter->dlist[i].name);
- if (filter->plist[i].name)
- free (filter->plist[i].name);
- if (filter->aslist[i].name)
- free (filter->aslist[i].name);
-
- filter->dlist[i].name = NULL;
- filter->plist[i].name = NULL;
- filter->aslist[i].name = NULL;
- }
- for (i = RMAP_IN; i < RMAP_MAX; i++)
- {
- if (filter->map[i].name)
- free (filter->map[i].name);
- filter->map[i].name = NULL;
- }
-
- if (filter->usmap.name)
- free (filter->usmap.name);
-
- if (peer->default_rmap[afi][safi].name)
- free (peer->default_rmap[afi][safi].name);
-
- filter->usmap.name = NULL;
- peer->default_rmap[afi][safi].name = NULL;
- }
-
- peer_unlock (peer); /* initial reference */
+ if (peer_active_nego (peer))
+ {
+ bool completed ;
+ bgp_capability_send (peer, afi, safi, CAPABILITY_CODE_MP,
+ CAPABILITY_ACTION_UNSET);
+ completed = bgp_clear_routes(peer, afi, safi, false) ;
+ /* If clearing routes does not complete, what do we do ? */
+ passert(completed) ;
+ peer->pcount[afi][safi] = 0;
+
+ down = false ; /* don't need to down the peer */
+ } ;
+ } ;
+
+ if (down)
+ bgp_peer_down(peer, PEER_DOWN_AF_DEACTIVATE) ;
+ } ;
return 0;
}
-
+
static int
peer_group_cmp (struct peer_group *g1, struct peer_group *g2)
{
@@ -1359,40 +1026,45 @@ peer_group_lookup (struct bgp *bgp, const char *name)
return NULL;
}
+/*------------------------------------------------------------------------------
+ * Get existing peer-group in given bgp instance, or make a new one.
+ *
+ * Returns: the peer-group (pre-existing or new)
+ */
struct peer_group *
peer_group_get (struct bgp *bgp, const char *name)
{
struct peer_group *group;
group = peer_group_lookup (bgp, name);
- if (group)
- return group;
-
- group = peer_group_new ();
- group->bgp = bgp;
- group->name = strdup (name);
- group->peer = list_new ();
- group->conf = peer_new (bgp);
- if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
- group->conf->afc[AFI_IP][SAFI_UNICAST] = 1;
- group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name);
- group->conf->group = group;
- group->conf->as = 0;
- group->conf->ttl = 1;
- group->conf->gtsm_hops = 0;
- group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
- UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER);
- UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT);
- group->conf->keepalive = 0;
- group->conf->holdtime = 0;
- group->conf->connect = 0;
- SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP);
- listnode_add_sort (bgp->group, group);
-
- return 0;
+ if (group == NULL)
+ {
+ group = peer_group_new ();
+ group->bgp = bgp;
+ group->name = strdup (name);
+ group->peer = list_new ();
+ group->conf = bgp_peer_new (bgp);
+ if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
+ group->conf->afc[AFI_IP][SAFI_UNICAST] = 1;
+ group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name);
+ group->conf->group = group;
+ group->conf->as = 0;
+ group->conf->ttl = 1;
+ group->conf->gtsm = false ;
+ group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+ UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER);
+ UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT);
+ group->conf->keepalive = 0;
+ group->conf->holdtime = 0;
+ group->conf->connect = 0;
+ SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP);
+ listnode_add_sort (bgp->group, group);
+ } ;
+
+ return group ;
}
-static void
+static void
peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
afi_t afi, safi_t safi)
{
@@ -1414,11 +1086,9 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
if (conf->change_local_as)
peer->change_local_as = conf->change_local_as;
- /* TTL */
- peer->ttl = conf->ttl;
-
- /* GTSM hops */
- peer->gtsm_hops = conf->gtsm_hops;
+ /* TTL & GTSM */
+ peer->ttl = conf->ttl;
+ peer->gtsm = conf->gtsm;
/* Weight */
peer->weight = conf->weight;
@@ -1454,8 +1124,6 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
else
peer->password = NULL;
- bgp_md5_set (peer);
-
/* maximum-prefix */
peer->pmax[afi][safi] = conf->pmax[afi][safi];
peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi];
@@ -1470,9 +1138,10 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
/* Make peer's RIB point to group's RIB. */
peer->rib[afi][safi] = group->conf->rib[afi][safi];
- /* Import policy. */
+ /* Import policy. */
if (pfilter->map[RMAP_IMPORT].name)
free (pfilter->map[RMAP_IMPORT].name);
+
if (gfilter->map[RMAP_IMPORT].name)
{
pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
@@ -1484,7 +1153,7 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
pfilter->map[RMAP_IMPORT].map = NULL;
}
- /* Export policy. */
+ /* Export policy. */
if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
{
pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
@@ -1533,13 +1202,10 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
pfilter->dlist[in].name = strdup (gfilter->dlist[in].name);
pfilter->dlist[in].alist = gfilter->dlist[in].alist;
}
- if (gfilter->plist[in].name && ! pfilter->plist[in].name)
- {
- if (pfilter->plist[in].name)
- free (pfilter->plist[in].name);
- pfilter->plist[in].name = strdup (gfilter->plist[in].name);
- pfilter->plist[in].plist = gfilter->plist[in].plist;
- }
+
+ if (! pfilter->plist[in].ref)
+ prefix_list_copy_ref(&pfilter->plist[in].ref, gfilter->plist[in].ref) ;
+
if (gfilter->aslist[in].name && ! pfilter->aslist[in].name)
{
if (pfilter->aslist[in].name)
@@ -1547,12 +1213,19 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
pfilter->aslist[in].name = strdup (gfilter->aslist[in].name);
pfilter->aslist[in].aslist = gfilter->aslist[in].aslist;
}
+
+ /* main In policy */
if (gfilter->map[RMAP_IN].name && ! pfilter->map[RMAP_IN].name)
{
- if (pfilter->map[RMAP_IN].name)
- free (pfilter->map[RMAP_IN].name);
pfilter->map[RMAP_IN].name = strdup (gfilter->map[RMAP_IN].name);
- pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map;
+ pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map;
+ }
+
+ /* Route-Server In policy */
+ if (gfilter->map[RMAP_RS_IN].name && ! pfilter->map[RMAP_RS_IN].name)
+ {
+ pfilter->map[RMAP_RS_IN].name = strdup (gfilter->map[RMAP_RS_IN].name);
+ pfilter->map[RMAP_RS_IN].map = gfilter->map[RMAP_RS_IN].map;
}
/* outbound filter apply */
@@ -1570,20 +1243,9 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
pfilter->dlist[out].name = NULL;
pfilter->dlist[out].alist = NULL;
}
- if (gfilter->plist[out].name)
- {
- if (pfilter->plist[out].name)
- free (pfilter->plist[out].name);
- pfilter->plist[out].name = strdup (gfilter->plist[out].name);
- pfilter->plist[out].plist = gfilter->plist[out].plist;
- }
- else
- {
- if (pfilter->plist[out].name)
- free (pfilter->plist[out].name);
- pfilter->plist[out].name = NULL;
- pfilter->plist[out].plist = NULL;
- }
+
+ prefix_list_copy_ref(&pfilter->plist[out].ref, gfilter->plist[out].ref) ;
+
if (gfilter->aslist[out].name)
{
if (pfilter->aslist[out].name)
@@ -1650,7 +1312,7 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
pfilter->usmap.name = NULL;
pfilter->usmap.map = NULL;
}
-}
+}
/* Peer group's remote AS configuration. */
int
@@ -1692,7 +1354,7 @@ peer_group_delete (struct peer_group *group)
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
{
peer->group = NULL;
- peer_delete (peer);
+ bgp_peer_delete (peer);
}
list_delete (group->peer);
@@ -1700,7 +1362,7 @@ peer_group_delete (struct peer_group *group)
group->name = NULL;
group->conf->group = NULL;
- peer_delete (group->conf);
+ bgp_peer_delete (group->conf);
/* Delete from all peer_group list. */
listnode_delete (bgp->group, group);
@@ -1722,7 +1384,7 @@ peer_group_remote_as_delete (struct peer_group *group)
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
{
peer->group = NULL;
- peer_delete (peer);
+ bgp_peer_delete (peer);
}
list_delete_all_node (group->peer);
@@ -1737,7 +1399,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
struct peer_group *group, afi_t afi, safi_t safi, as_t *as)
{
struct peer *peer;
- int first_member = 0;
+ bool first_member ;
/* Check peer group's address family. */
if (! group->conf->afc[afi][safi])
@@ -1746,63 +1408,98 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
/* Lookup the peer. */
peer = peer_lookup (bgp, su);
- /* Create a new peer. */
+ /* Create a new peer -- iff group specifies a remote-as. */
if (! peer)
{
if (! group->conf->as)
return BGP_ERR_PEER_GROUP_NO_REMOTE_AS;
- peer = peer_create (su, bgp, bgp->as, group->conf->as, afi, safi);
+ /* Check that the neighbor IP is unique */
+ if (peer_lookup (NULL, su) != NULL)
+ return BGP_ERR_PEER_EXISTS ;
+
+ peer = bgp_peer_create (su, bgp, bgp->as, group->conf->as, afi, safi);
peer->group = group;
peer->af_group[afi][safi] = 1;
- peer = peer_lock (peer); /* group->peer list reference */
+ peer = bgp_peer_lock (peer); /* group->peer list reference */
listnode_add (group->peer, peer);
peer_group2peer_config_copy (group, peer, afi, safi);
- return 0;
+ return 0 ; /* Done */
}
- /* When the peer already belongs to peer group, check the consistency. */
+ assert(bgp == peer->bgp) ;
+
+ /* When the peer already belongs to peer group, check the consistency.
+ *
+ * If already belong to a group for the current afi/safi, then must be the
+ * same group -- cannot change group association by this means.
+ */
if (peer->af_group[afi][safi])
{
if (strcmp (peer->group->name, group->name) != 0)
return BGP_ERR_PEER_GROUP_CANT_CHANGE;
- return 0;
+ return 0; /* Done */
}
- /* Check current peer group configuration. */
+ /* Check current peer group configuration.
+ *
+ * Can only belong to one group at a time. All afi/safi which are members of
+ * a group, must be members of the same group.
+ */
if (peer_group_active (peer)
&& strcmp (peer->group->name, group->name) != 0)
return BGP_ERR_PEER_GROUP_MISMATCH;
- if (! group->conf->as)
- {
- if (peer_sort (group->conf) != BGP_PEER_INTERNAL
- && peer_sort (group->conf) != peer_sort (peer))
- {
- if (as)
- *as = peer->as;
- return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
- }
+ /* If binding to this group would change the sort of peer, then cannot
+ * do it.
+ *
+ * The group itself may not carry a sort of peer ?? Or something ??
+ */
+ first_member = false ;
+ if (! group->conf->as) /* If group does NOT specify a remote AS ? */
+ {
if (peer_sort (group->conf) == BGP_PEER_INTERNAL)
- first_member = 1;
+ first_member = true ;
+ else
+ {
+ if (peer_sort (group->conf) != peer_sort (peer))
+ {
+ if (as)
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+ }
}
+ /* Can join the group.
+ *
+ * NB: if we get to here, then we know that this afi/safi was NOT a member
+ * of any group.
+ *
+ * Note that this appears to implicitly activate the afi/safi... but does
+ * not go through the rest of the activation process ???
+ *
+ * TODO: why does implicit activation of afi/safi not do more work ?
+ */
peer->af_group[afi][safi] = 1;
peer->afc[afi][safi] = 1;
+
+ /* If not already a member, add to list of members. */
if (! peer->group)
{
peer->group = group;
-
- peer = peer_lock (peer); /* group->peer list reference */
+
+ peer = bgp_peer_lock (peer); /* group->peer list reference */
listnode_add (group->peer, peer);
}
else
assert (group && peer->group == group);
+ /* Further magic stuff to do with peer sort. */
if (first_member)
{
/* Advertisement-interval reset */
@@ -1813,7 +1510,10 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
/* ebgp-multihop reset */
if (peer_sort (group->conf) == BGP_PEER_IBGP)
- group->conf->ttl = 255;
+ {
+ group->conf->ttl = MAXTTL;
+ group->conf->gtsm = false ;
+ } ;
/* local-as reset */
if (peer_sort (group->conf) != BGP_PEER_EBGP)
@@ -1823,53 +1523,39 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
}
}
+ /* Worry about rsclient state of the peer for this afi/safi.
+ *
+ * A peer cannot be an rsclient separately from its group.
+ *
+ * This peer was not previously a member of any group for this afi/safi.
+ *
+ * So if the peer is an rsclient for this afi/safi, it had better stop
+ * that now.
+ */
if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
{
- struct listnode *pn;
-
- /* If it's not configured as RSERVER_CLIENT in any other address
- family, without being member of a peer_group, remove it from
- list bgp->rsclient.*/
- if (! peer_rsclient_active (peer)
- && (pn = listnode_lookup (bgp->rsclient, peer)))
- {
- peer_unlock (peer); /* peer rsclient reference */
- list_delete_node (bgp->rsclient, pn);
-
- /* Clear our own rsclient rib for this afi/safi. */
- bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
- }
+ /* Now that we have set peer->af_group for this afi/safi, this peer
+ * may no longer have any distinct rsclient status, in which case it
+ * must be removes from list bgp->rsclient.
+ *
+ * Note that it must have had distinct rsclient status, because it is
+ * an rsclient in this afi/safi, and it was not a group member in this
+ * afi/safi.
+ *
+ * We discard any import and export route map, except if the group is
+ * an rsclient, when we keep the export route map.
+ */
+ UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ;
- bgp_table_finish (&peer->rib[afi][safi]);
-
- /* Import policy. */
- if (peer->filter[afi][safi].map[RMAP_IMPORT].name)
- {
- free (peer->filter[afi][safi].map[RMAP_IMPORT].name);
- peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL;
- peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL;
- }
-
- /* Export policy. */
- if (! CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
- && peer->filter[afi][safi].map[RMAP_EXPORT].name)
- {
- free (peer->filter[afi][safi].map[RMAP_EXPORT].name);
- peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL;
- peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL;
- }
+ peer_rsclient_unset(peer, afi, safi,
+ CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) ;
}
+ /* Now deal with the rest of the group configuration. */
peer_group2peer_config_copy (group, peer, afi, safi);
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_RMAP_BIND;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ /* And down the peer to push into new state. */
+ bgp_peer_down(peer, PEER_DOWN_RMAP_BIND) ;
return 0;
}
@@ -1879,44 +1565,45 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer,
struct peer_group *group, afi_t afi, safi_t safi)
{
if (! peer->af_group[afi][safi])
- return 0;
+ return 0; /* quit if not member of any group */
if (group != peer->group)
- return BGP_ERR_PEER_GROUP_MISMATCH;
+ return BGP_ERR_PEER_GROUP_MISMATCH; /* quit if not member of this group */
+ /* So is a member of this group for this afi/safi.
+ *
+ * This is an implied deactivation for this peer. That is taken care of in
+ * bgp_peer_down().
+ */
peer->af_group[afi][safi] = 0;
peer->afc[afi][safi] = 0;
peer_af_flag_reset (peer, afi, safi);
- if (peer->rib[afi][safi])
- peer->rib[afi][safi] = NULL;
+ /* Is member of group, so if it is an rsclient we were using its rsclient
+ * RIB, which must now forget.
+ */
+ peer->rib[afi][safi] = NULL;
+ /* If is now not a member of any group */
if (! peer_group_active (peer))
{
assert (listnode_lookup (group->peer, peer));
- peer_unlock (peer); /* peer group list reference */
+ bgp_peer_unlock (peer); /* peer group list reference */
listnode_delete (group->peer, peer);
peer->group = NULL;
if (group->conf->as)
{
- peer_delete (peer);
+ bgp_peer_delete (peer);
return 0;
}
peer_global_config_reset (peer);
}
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_RMAP_UNBIND;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_RMAP_UNBIND) ;
return 0;
}
-
+
/* BGP instance creation by `router bgp' commands. */
static struct bgp *
bgp_create (as_t *as, const char *name)
@@ -1927,9 +1614,9 @@ bgp_create (as_t *as, const char *name)
if ( (bgp = XCALLOC (MTYPE_BGP, sizeof (struct bgp))) == NULL)
return NULL;
-
+
bgp_lock (bgp);
- bgp->peer_self = peer_new (bgp);
+ bgp->peer_self = bgp_peer_new (bgp);
bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement");
bgp->peer = list_new ();
@@ -1981,7 +1668,7 @@ bgp_lookup (as_t as, const char *name)
for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
if (bgp->as == as
- && ((bgp->name == NULL && name == NULL)
+ && ((bgp->name == NULL && name == NULL)
|| (bgp->name && name && strcmp (bgp->name, name) == 0)))
return bgp;
return NULL;
@@ -2048,13 +1735,6 @@ bgp_get (struct bgp **bgp_val, as_t *as, const char *name)
}
}
- /* Create BGP server socket, if first instance. */
- if (list_isempty(bm->bgp))
- {
- if (bgp_socket (bm->port, bm->address) < 0)
- return BGP_ERR_INVALID_VALUE;
- }
-
bgp = bgp_create (as, name);
listnode_add (bm->bgp, bgp);
bgp_router_id_set(bgp, &router_id_zebra);
@@ -2079,12 +1759,12 @@ bgp_delete (struct bgp *bgp)
/* Unset redistribution. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
if (i != ZEBRA_ROUTE_BGP)
bgp_redistribute_unset (bgp, afi, i);
for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer))
- peer_delete (peer);
+ bgp_peer_delete (peer);
for (ALL_LIST_ELEMENTS (bgp->group, node, next, group))
peer_group_delete (group);
@@ -2092,19 +1772,17 @@ bgp_delete (struct bgp *bgp)
assert (listcount (bgp->rsclient) == 0);
if (bgp->peer_self) {
- peer_delete(bgp->peer_self);
+ bgp_peer_delete(bgp->peer_self);
bgp->peer_self = NULL;
}
-
+
/* Remove visibility via the master list - there may however still be
* routes to be processed still referencing the struct bgp.
*/
listnode_delete (bm->bgp, bgp);
- if (list_isempty(bm->bgp))
- bgp_close ();
bgp_unlock(bgp); /* initial reference */
-
+
return 0;
}
@@ -2136,7 +1814,7 @@ bgp_free (struct bgp *bgp)
if (bgp->name)
free (bgp->name);
-
+
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
@@ -2149,7 +1827,7 @@ bgp_free (struct bgp *bgp)
}
XFREE (MTYPE_BGP, bgp);
}
-
+
struct peer *
peer_lookup (struct bgp *bgp, union sockunion *su)
{
@@ -2159,23 +1837,22 @@ peer_lookup (struct bgp *bgp, union sockunion *su)
if (bgp != NULL)
{
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- if (sockunion_same (&peer->su, su)
- && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ if (sockunion_same (&peer->su, su))
return peer;
}
else if (bm->bgp != NULL)
{
struct listnode *bgpnode, *nbgpnode;
-
+
for (ALL_LIST_ELEMENTS (bm->bgp, bgpnode, nbgpnode, bgp))
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- if (sockunion_same (&peer->su, su)
- && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ if (sockunion_same (&peer->su, su))
return peer;
}
return NULL;
}
+#if 0
struct peer *
peer_lookup_with_open (union sockunion *su, as_t remote_as,
struct in_addr *remote_id, int *as)
@@ -2218,7 +1895,8 @@ peer_lookup_with_open (union sockunion *su, as_t remote_as,
}
return NULL;
}
-
+#endif
+
/* If peer is configured at least one address family return 1. */
int
peer_active (struct peer *peer)
@@ -2244,206 +1922,223 @@ peer_active_nego (struct peer *peer)
return 1;
return 0;
}
-
-/* peer_flag_change_type. */
+
+/*------------------------------------------------------------------------------
+ * Actions required after a change of state of a peer.
+ */
enum peer_change_type
{
- peer_change_none,
- peer_change_reset,
- peer_change_reset_in,
- peer_change_reset_out,
-};
+ peer_change_none, /* no further action */
+ peer_change_reset, /* drop any existing session and restart */
+ peer_change_reset_in, /* if possible, ask for route refresh,
+ otherwise drop and restart. */
+ peer_change_reset_out, /* reannounce everything */
+} ;
+typedef enum peer_change_type peer_change_type_t ;
+
+/* Perform action after change of state of a peer for the given afi/safi.
+ *
+ * If has to down the peer (drop any existing session and restart), then
+ * requires a peer_down_t to record why.
+ */
static void
peer_change_action (struct peer *peer, afi_t afi, safi_t safi,
- enum peer_change_type type)
+ enum peer_change_type type,
+ peer_down_t why_changed)
{
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
return;
- if (type == peer_change_reset)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- else if (type == peer_change_reset_in)
- {
- if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV)
- || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
- bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
- else
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else if (type == peer_change_reset_out)
- bgp_announce_route (peer, afi, safi);
-}
+ switch (type)
+ {
+ case peer_change_none:
+ break ;
+
+ case peer_change_reset:
+ bgp_peer_down(peer, why_changed) ;
+ break ;
+
+ case peer_change_reset_in:
+ if (peer->state == bgp_peer_pEstablished)
+ {
+ if ( CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV)
+ || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+ bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
+ else
+ bgp_peer_down(peer, why_changed);
+ } ;
+ break ;
+
+ case peer_change_reset_out:
+ bgp_announce_route (peer, afi, safi) ; /* Does nothing if !pEstablished */
+ break ;
+ default:
+ zabort("unknown peer_change_type") ;
+ break ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ *
+ *
+ */
struct peer_flag_action
{
- /* Peer's flag. */
+ /* Peer's flag. */
u_int32_t flag;
- /* This flag can be set for peer-group member. */
- u_char not_for_member;
+ /* This flag can be set for peer-group member. */
+ bool not_for_member;
- /* Action when the flag is changed. */
- enum peer_change_type type;
+ /* Action when the flag is changed. */
+ peer_change_type_t type;
- /* Peer down cause */
- u_char peer_down;
+ /* Peer down cause */
+ peer_down_t peer_down;
};
+/*------------------------------------------------------------------------------
+ * Table of actions for changing peer->flags
+ *
+ * NB: change actions are peer_change_none or peer_change_reset ONLY.
+ * (The peer->flags apply to all afi/safi.)
+ *
+ * NB: PEER_FLAG_LOCAL_AS_NO_PREPEND is dealt with elsewhere.
+ *
+ * NB: all flags are set/cleared individually.
+ */
static const struct peer_flag_action peer_flag_action_list[] =
{
- { PEER_FLAG_PASSIVE, 0, peer_change_reset },
- { PEER_FLAG_SHUTDOWN, 0, peer_change_reset },
- { PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none },
- { PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none },
- { PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none },
- { PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset },
- { PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset },
- { 0, 0, 0 }
+ { PEER_FLAG_PASSIVE,
+ false, peer_change_reset, PEER_DOWN_PASSIVE_CHANGE},
+ { PEER_FLAG_SHUTDOWN,
+ false, peer_change_reset, PEER_DOWN_USER_SHUTDOWN },
+ { PEER_FLAG_DONT_CAPABILITY,
+ false, peer_change_reset, PEER_DOWN_DONT_CAPABILITY },
+ { PEER_FLAG_OVERRIDE_CAPABILITY,
+ false, peer_change_reset, PEER_DOWN_OVERRIDE_CAPABILITY },
+ { PEER_FLAG_STRICT_CAP_MATCH,
+ false, peer_change_reset, PEER_DOWN_STRICT_CAP_MATCH },
+ { PEER_FLAG_DYNAMIC_CAPABILITY,
+ false, peer_change_reset, PEER_DOWN_CAPABILITY_CHANGE },
+ { PEER_FLAG_DISABLE_CONNECTED_CHECK,
+ false, peer_change_reset, PEER_DOWN_CONFIG_CHANGE },
+ { 0, false, peer_change_none, PEER_DOWN_NULL }
};
+/*------------------------------------------------------------------------------
+ * Table of actions for changing peer->af_flags
+ *
+ * NB: some flags may be set/cleared together, in any combination.
+ */
static const struct peer_flag_action peer_af_flag_action_list[] =
{
- { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out },
- { PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out },
- { PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out },
- { PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in },
- { PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset },
- { PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset },
- { PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out },
- { PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out },
- { PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out },
- { PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out },
- { PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in },
- { PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset },
- { PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset },
- { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out },
- { 0, 0, 0 }
+ { PEER_FLAG_NEXTHOP_SELF,
+ true, peer_change_reset_out, PEER_DOWN_NULL },
+ { PEER_FLAG_SEND_COMMUNITY
+ | PEER_FLAG_SEND_EXT_COMMUNITY,
+ true, peer_change_reset_out, PEER_DOWN_NULL },
+ { PEER_FLAG_SOFT_RECONFIG,
+ false, peer_change_reset_in, PEER_DOWN_CONFIG_CHANGE },
+ { PEER_FLAG_REFLECTOR_CLIENT,
+ true, peer_change_reset, PEER_DOWN_RR_CLIENT_CHANGE },
+ { PEER_FLAG_RSERVER_CLIENT,
+ true, peer_change_reset, PEER_DOWN_RS_CLIENT_CHANGE },
+ { PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_NEXTHOP_UNCHANGED
+ | PEER_FLAG_MED_UNCHANGED,
+ true, peer_change_reset_out, PEER_DOWN_NULL },
+ { PEER_FLAG_REMOVE_PRIVATE_AS,
+ true, peer_change_reset_out, PEER_DOWN_NULL },
+ { PEER_FLAG_ALLOWAS_IN,
+ false, peer_change_reset_in, PEER_DOWN_ALLOWAS_IN_CHANGE },
+ { PEER_FLAG_ORF_PREFIX_SM
+ | PEER_FLAG_ORF_PREFIX_RM,
+ true, peer_change_reset, PEER_DOWN_CONFIG_CHANGE },
+ { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED,
+ false, peer_change_reset_out, PEER_DOWN_NULL },
+ { 0, false, peer_change_none, PEER_DOWN_NULL }
};
-/* Proper action set. */
-static int
-peer_flag_action_set (const struct peer_flag_action *action_list, int size,
- struct peer_flag_action *action, u_int32_t flag)
+/*------------------------------------------------------------------------------
+ * Look up action for given flags.
+ *
+ * Returns: address of required peer_flag_action structure,
+ * or: NULL if no action found for the given combination of flags.
+ *
+ * This mechanism is generally used when setting/clearing one flag at a time.
+ *
+ * The table may contain entries with more than one flag. In these cases,
+ * any combination of those flags may be set/cleared together -- any flags
+ * which are not mentioned are not affected.
+ *
+ * The given flags value must either exactly match a single flag entry, or be
+ * a subset of a multiple flag entry. The table is searched in the order
+ * given, and proceeds to the end before stopping.
+ */
+static const struct peer_flag_action*
+peer_flag_action_set (const struct peer_flag_action* action, uint32_t flag)
{
- int i;
- int found = 0;
- int reset_in = 0;
- int reset_out = 0;
- const struct peer_flag_action *match = NULL;
-
- /* Check peer's frag action. */
- for (i = 0; i < size; i++)
+ while (action->flag != 0)
{
- match = &action_list[i];
-
- if (match->flag == 0)
- break;
-
- if (match->flag & flag)
- {
- found = 1;
-
- if (match->type == peer_change_reset_in)
- reset_in = 1;
- if (match->type == peer_change_reset_out)
- reset_out = 1;
- if (match->type == peer_change_reset)
- {
- reset_in = 1;
- reset_out = 1;
- }
- if (match->not_for_member)
- action->not_for_member = 1;
- }
+ if ((action->flag & flag) == flag)
+ return action ;
+ action++ ;
}
- /* Set peer clear type. */
- if (reset_in && reset_out)
- action->type = peer_change_reset;
- else if (reset_in)
- action->type = peer_change_reset_in;
- else if (reset_out)
- action->type = peer_change_reset_out;
- else
- action->type = peer_change_none;
-
- return found;
+ return NULL ;
}
+/*------------------------------------------------------------------------------
+ * Having set or cleared something in peer->flags, make any implied changes.
+ *
+ * NB: Clearing PEER_FLAG_SHUTDOWN is a special case
+ *
+ * NB: Setting PEER_FLAG_SHUTDOWN -> PEER_DOWN_USER_SHUTDOWN, which is a
+ * special case in bgp_peer_down.
+ */
static void
-peer_flag_modify_action (struct peer *peer, u_int32_t flag)
+peer_flag_modify_action (struct peer *peer,
+ const struct peer_flag_action* action, bool set)
{
- if (flag == PEER_FLAG_SHUTDOWN)
- {
- if (CHECK_FLAG (peer->flags, flag))
- {
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
- peer_nsf_stop (peer);
+ if (action->type == peer_change_none)
+ return ;
- UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
- if (peer->t_pmax_restart)
- {
- BGP_TIMER_OFF (peer->t_pmax_restart);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s Maximum-prefix restart timer canceled",
- peer->host);
- }
-
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
- peer_nsf_stop (peer);
+ assert(action->type == peer_change_reset) ;
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
- else
- {
- peer->v_start = BGP_INIT_START_TIMER;
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
- }
- else if (peer->status == Established)
+ if ((action->flag == PEER_FLAG_SHUTDOWN) && !set)
{
- if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
- peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
- else if (flag == PEER_FLAG_PASSIVE)
- peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
- else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)
- peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE;
+ peer->v_start = BGP_INIT_START_TIMER;
+ bgp_peer_enable(peer);
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-}
+ return ; /* Note */
+ } ;
+
+ bgp_peer_down(peer, action->peer_down) ;
+} ;
-/* Change specified peer flag. */
+/*------------------------------------------------------------------------------
+ * Change specified peer->flags flag.
+ *
+ * See: peer_flag_action_list above.
+ */
static int
-peer_flag_modify (struct peer *peer, u_int32_t flag, int set)
+peer_flag_modify (struct peer *peer, u_int32_t flag, bool set)
{
- int found;
- int size;
struct peer_group *group;
struct listnode *node, *nnode;
- struct peer_flag_action action;
-
- memset (&action, 0, sizeof (struct peer_flag_action));
- size = sizeof peer_flag_action_list / sizeof (struct peer_flag_action);
+ const struct peer_flag_action* action;
- found = peer_flag_action_set (peer_flag_action_list, size, &action, flag);
+ action = peer_flag_action_set (peer_flag_action_list, flag) ;
/* No flag action is found. */
- if (! found)
- return BGP_ERR_INVALID_FLAG;
+ if (action == NULL)
+ return BGP_ERR_INVALID_FLAG;
/* Not for peer-group member. */
- if (action.not_for_member && peer_group_active (peer))
+ if (action->not_for_member && peer_group_active (peer))
return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
/* When unset the peer-group member's flag we have to check
@@ -2475,12 +2170,10 @@ peer_flag_modify (struct peer *peer, u_int32_t flag, int set)
SET_FLAG (peer->flags, flag);
else
UNSET_FLAG (peer->flags, flag);
-
+
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (action.type == peer_change_reset)
- peer_flag_modify_action (peer, flag);
-
+ peer_flag_modify_action (peer, action, set);
return 0;
}
@@ -2500,22 +2193,27 @@ peer_flag_modify (struct peer *peer, u_int32_t flag, int set)
else
UNSET_FLAG (peer->flags, flag);
- if (action.type == peer_change_reset)
- peer_flag_modify_action (peer, flag);
+ peer_flag_modify_action (peer, action, set);
}
return 0;
}
+/*------------------------------------------------------------------------------
+ * Set specified peer->flags flag.
+ */
int
peer_flag_set (struct peer *peer, u_int32_t flag)
{
- return peer_flag_modify (peer, flag, 1);
+ return peer_flag_modify (peer, flag, true);
}
+/*------------------------------------------------------------------------------
+ * Clear specified peer->flags flag.
+ */
int
peer_flag_unset (struct peer *peer, u_int32_t flag)
{
- return peer_flag_modify (peer, flag, 0);
+ return peer_flag_modify (peer, flag, false);
}
static int
@@ -2526,41 +2224,60 @@ peer_is_group_member (struct peer *peer, afi_t afi, safi_t safi)
return 0;
}
-static int
+/*------------------------------------------------------------------------------
+ * Having set or cleared something in peer->af_flags, make any implied changes.
+ *
+ * NB: clearing PEER_FLAG_SOFT_RECONFIG is a special case.
+ */
+static void
+peer_af_flag_modify_action (struct peer *peer, afi_t afi, safi_t safi,
+ const struct peer_flag_action* action, bool set)
+{
+ if ((action->flag == PEER_FLAG_SOFT_RECONFIG) && !set)
+ {
+ if (peer->state == bgp_peer_pEstablished)
+ bgp_clear_adj_in (peer, afi, safi);
+ }
+ else
+ {
+ peer_change_action (peer, afi, safi, action->type, action->peer_down) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Change specified peer->af_flags flag.
+ *
+ * See: peer_af_flag_action_list above.
+ */
+int
peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag,
- int set)
+ bool set)
{
- int found;
- int size;
struct listnode *node, *nnode;
struct peer_group *group;
- struct peer_flag_action action;
+ const struct peer_flag_action* action;
+
+ action = peer_flag_action_set (peer_af_flag_action_list, flag);
- memset (&action, 0, sizeof (struct peer_flag_action));
- size = sizeof peer_af_flag_action_list / sizeof (struct peer_flag_action);
-
- found = peer_flag_action_set (peer_af_flag_action_list, size, &action, flag);
-
/* No flag action is found. */
- if (! found)
- return BGP_ERR_INVALID_FLAG;
+ if (action == NULL)
+ return BGP_ERR_INVALID_FLAG;
/* Adress family must be activated. */
if (! peer->afc[afi][safi])
return BGP_ERR_PEER_INACTIVE;
/* Not for peer-group member. */
- if (action.not_for_member && peer_is_group_member (peer, afi, safi))
+ if (action->not_for_member && peer_is_group_member (peer, afi, safi))
return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
/* Spcecial check for reflector client. */
- if (flag & PEER_FLAG_REFLECTOR_CLIENT
- && peer_sort (peer) != BGP_PEER_IBGP)
+ if ((flag & PEER_FLAG_REFLECTOR_CLIENT) && (peer_sort(peer) != BGP_PEER_IBGP))
return BGP_ERR_NOT_INTERNAL_PEER;
/* Spcecial check for remove-private-AS. */
- if (flag & PEER_FLAG_REMOVE_PRIVATE_AS
- && peer_sort (peer) == BGP_PEER_IBGP)
+ if ((flag & PEER_FLAG_REMOVE_PRIVATE_AS)
+ && (peer_sort(peer) == BGP_PEER_IBGP))
return BGP_ERR_REMOVE_PRIVATE_AS;
/* When unset the peer-group member's flag we have to check
@@ -2572,7 +2289,7 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag,
/* When current flag configuration is same as requested one. */
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
+ if ( set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
return 0;
if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
return 0;
@@ -2583,183 +2300,233 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag,
else
UNSET_FLAG (peer->af_flags[afi][safi], flag);
- /* Execute action when peer is established. */
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
- && peer->status == Established)
+ /* Execute action. */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (! set && flag == PEER_FLAG_SOFT_RECONFIG)
- bgp_clear_adj_in (peer, afi, safi);
- else
- {
- if (flag == PEER_FLAG_REFLECTOR_CLIENT)
- peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
- else if (flag == PEER_FLAG_RSERVER_CLIENT)
- peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
- else if (flag == PEER_FLAG_ORF_PREFIX_SM)
- peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
- else if (flag == PEER_FLAG_ORF_PREFIX_RM)
- peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
-
- peer_change_action (peer, afi, safi, action.type);
- }
-
- }
+ peer_af_flag_modify_action(peer, afi, safi, action, set) ;
+ return 0 ;
+ } ;
/* Peer group member updates. */
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ group = peer->group;
+
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
{
- group = peer->group;
-
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
- {
- if (! peer->af_group[afi][safi])
- continue;
+ if (! peer->af_group[afi][safi])
+ continue;
- if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
- continue;
+ if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
+ continue;
- if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
- continue;
+ if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
+ continue;
- if (set)
- SET_FLAG (peer->af_flags[afi][safi], flag);
- else
- UNSET_FLAG (peer->af_flags[afi][safi], flag);
+ if (set)
+ SET_FLAG (peer->af_flags[afi][safi], flag);
+ else
+ UNSET_FLAG (peer->af_flags[afi][safi], flag);
+
+ peer_af_flag_modify_action(peer, afi, safi, action, set) ;
+ } ;
- if (peer->status == Established)
- {
- if (! set && flag == PEER_FLAG_SOFT_RECONFIG)
- bgp_clear_adj_in (peer, afi, safi);
- else
- {
- if (flag == PEER_FLAG_REFLECTOR_CLIENT)
- peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
- else if (flag == PEER_FLAG_RSERVER_CLIENT)
- peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
- else if (flag == PEER_FLAG_ORF_PREFIX_SM)
- peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
- else if (flag == PEER_FLAG_ORF_PREFIX_RM)
- peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
-
- peer_change_action (peer, afi, safi, action.type);
- }
- }
- }
- }
return 0;
}
+/*------------------------------------------------------------------------------
+ * Set specified peer->af_flags flag.
+ */
int
peer_af_flag_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
{
- return peer_af_flag_modify (peer, afi, safi, flag, 1);
+ return peer_af_flag_modify (peer, afi, safi, flag, true);
}
+/*------------------------------------------------------------------------------
+ * Clear specified peer->af_flags flag.
+ */
int
peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
{
- return peer_af_flag_modify (peer, afi, safi, flag, 0);
+ return peer_af_flag_modify (peer, afi, safi, flag, false);
}
-
-/* EBGP multihop configuration. */
+
+/*------------------------------------------------------------------------------
+ * eBGP multihop configuration set -- Confed is eBGP for this purpose.
+ *
+ * This is simply ignored if iBGP. For iBGP peer->ttl is set to MAXTTL, and
+ * peer->gtsm is always false.
+ *
+ * For eBGP and for Confed, peer->ttl is set to 1, and peer->gtsm is also
+ * set false -- until either ebgp-multihop or ttl-security is seen.
+ *
+ * NB: cannot set ebgp-multihop if ttl-security (GTSM) is set.
+ *
+ * cannot set ebgp-multihop on a group if ttl-security (GTSM) is set on any
+ * group member.
+ *
+ * NB: setting ebgp-multihop of 1 is the same as unsetting it.
+ *
+ * setting any value < 1 also unsets ebgp-multihop (sets ttl = 1)
+ */
int
peer_ebgp_multihop_set (struct peer *peer, int ttl)
{
struct peer_group *group;
struct listnode *node, *nnode;
- struct peer *peer1;
if (peer_sort (peer) == BGP_PEER_IBGP)
return 0;
- /* see comment in peer_ttl_security_hops_set() */
- if (ttl != MAXTTL)
- {
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
- group = peer->group;
- if (group->conf->gtsm_hops != 0)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ if (peer->gtsm)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
- {
- if (peer_sort (peer1) == BGP_PEER_IBGP)
- continue;
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ {
+ struct peer *pgm;
+ group = peer->group;
- if (peer1->gtsm_hops != 0)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
- }
- }
- else
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm))
{
- if (peer->gtsm_hops != 0)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ if (peer_sort (pgm) == BGP_PEER_IBGP)
+ continue;
+
+ if (pgm->gtsm)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM;
}
- }
+ } ;
- peer->ttl = ttl;
+ if (ttl <= 0)
+ ttl = 1 ;
+
+ peer->ttl = ttl ;
+//qassert(!peer->gtsm) ;
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
+ bgp_session_set_ttl (peer->session, peer->ttl, peer->gtsm);
}
else
{
+ struct peer *pgm;
group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm))
{
- if (peer_sort (peer) == BGP_PEER_IBGP)
- continue;
+ if (peer_sort (pgm) == BGP_PEER_IBGP)
+ continue;
- peer->ttl = group->conf->ttl;
-
- if (peer->fd >= 0)
- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
+ pgm->ttl = ttl;
+// qassert(!pgm->gtsm) ;
+ bgp_session_set_ttl (pgm->session, pgm->ttl, pgm->gtsm);
}
}
return 0;
}
+/*------------------------------------------------------------------------------
+ * eBGP multihop configuration unset -- Confed is eBGP for this purpose.
+ *
+ * Implemented by setting the ttl to 0 !
+ */
int
peer_ebgp_multihop_unset (struct peer *peer)
{
+ return peer_ebgp_multihop_set (peer, 0) ;
+}
+
+/*------------------------------------------------------------------------------
+ * eBGP ttl-security hops configuration set -- Confed is eBGP for this purpose.
+ *
+ * Setting ttl-security hops is equivalent to setting eBGP multi-hop, except
+ * that it also enables the GTSM -- if available.
+ *
+ * This is simply ignored if iBGP. For iBGP peer->ttl is set to MAXTTL, and
+ * peer->gtsm is always false.
+ *
+ * For eBGP and for Confed, peer->ttl is set to 1, and peer->gtsm is also
+ * set false -- until either ebgp-multihop or ttl-security is seen.
+ *
+ * NB: cannot set ttl-security (GTSM) if eBGP multi-hop is set.
+ *
+ * cannot set ttl-security (GTSM) on a group if eBGP multi-hop is set on any
+ * group member.
+ *
+ * NB: setting ebgp-multihop of < 1 is unsets it (sets ttl = 1, and gtsm false)
+ */
+int
+peer_ttl_security_hops_set (struct peer *peer, int ttl)
+{
struct peer_group *group;
struct listnode *node, *nnode;
+ bool gtsm ;
+
+ zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s",
+ ttl, peer->host) ;
if (peer_sort (peer) == BGP_PEER_IBGP)
- return 0;
+ return BGP_ERR_NO_IBGP_WITH_TTLHACK ;
- if (peer->gtsm_hops != 0 && peer->ttl != MAXTTL)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ if (!peer->gtsm && (peer->ttl > 1))
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM;
- if (peer_group_active (peer))
- peer->ttl = peer->group->conf->ttl;
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ {
+ struct peer *pgm;
+ group = peer->group;
+
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm))
+ {
+ if (peer_sort (pgm) == BGP_PEER_IBGP)
+ continue;
+
+ if (!pgm->gtsm && (pgm->ttl > 1))
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM;
+ }
+ } ;
+
+ if (ttl > 0)
+ gtsm = true ;
else
- peer->ttl = 1;
+ {
+ gtsm = false ;
+ ttl = 1 ;
+ } ;
+
+ peer->ttl = ttl ;
+ peer->gtsm = gtsm ;
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
+ bgp_session_set_ttl (peer->session, peer->ttl, peer->gtsm);
}
else
{
+ struct peer *pgm;
group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
- {
- if (peer_sort (peer) == BGP_PEER_IBGP)
- continue;
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm))
+ {
+ if (peer_sort (pgm) == BGP_PEER_IBGP)
+ continue;
- peer->ttl = 1;
-
- if (peer->fd >= 0)
- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
- }
+ pgm->ttl = ttl;
+ pgm->gtsm = gtsm ;
+ bgp_session_set_ttl (pgm->session, pgm->ttl, pgm->gtsm);
+ } ;
}
return 0;
}
-
+
+/*------------------------------------------------------------------------------
+ * eBGP ttl-security hops configuration unset -- Confed is eBGP for this purpose.
+ *
+ * Implemented by setting the ttl to 0 !
+ */
+int
+peer_ttl_security_hops_unset (struct peer *peer)
+{
+ return peer_ttl_security_hops_set(peer, 0) ;
+}
+
+
/* Neighbor description. */
int
peer_description_set (struct peer *peer, char *desc)
@@ -2782,7 +2549,7 @@ peer_description_unset (struct peer *peer)
return 0;
}
-
+
/* Neighbor update-source. */
int
peer_update_source_if_set (struct peer *peer, const char *ifname)
@@ -2810,14 +2577,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname)
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ;
return 0;
}
@@ -2842,14 +2602,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname)
peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname);
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ;
}
return 0;
}
@@ -2879,14 +2632,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su)
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ;
return 0;
}
@@ -2910,14 +2656,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su)
peer->update_source = sockunion_dup (su);
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ;
}
return 0;
}
@@ -2955,20 +2694,13 @@ peer_update_source_unset (struct peer *peer)
peer->update_source = su;
}
else if (group->conf->update_if)
- peer->update_if =
+ peer->update_if =
XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, group->conf->update_if);
}
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ;
return 0;
}
@@ -2991,18 +2723,11 @@ peer_update_source_unset (struct peer *peer)
peer->update_if = NULL;
}
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ;
}
return 0;
}
-
+
int
peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi,
const char *rmap)
@@ -3021,7 +2746,7 @@ peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi,
if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)
|| (rmap && ! peer->default_rmap[afi][safi].name)
|| (rmap && strcmp (rmap, peer->default_rmap[afi][safi].name) != 0))
- {
+ {
SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);
if (rmap)
@@ -3035,7 +2760,7 @@ peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi,
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established && peer->afc_nego[afi][safi])
+ if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi])
bgp_default_originate (peer, afi, safi, 0);
return 0;
}
@@ -3054,7 +2779,7 @@ peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi,
peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap);
}
- if (peer->status == Established && peer->afc_nego[afi][safi])
+ if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi])
bgp_default_originate (peer, afi, safi, 0);
}
return 0;
@@ -3075,7 +2800,7 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi)
return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
- {
+ {
UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);
if (peer->default_rmap[afi][safi].name)
@@ -3086,7 +2811,7 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi)
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established && peer->afc_nego[afi][safi])
+ if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi])
bgp_default_originate (peer, afi, safi, 1);
return 0;
}
@@ -3102,12 +2827,12 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi)
peer->default_rmap[afi][safi].name = NULL;
peer->default_rmap[afi][safi].map = NULL;
- if (peer->status == Established && peer->afc_nego[afi][safi])
+ if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi])
bgp_default_originate (peer, afi, safi, 1);
}
return 0;
}
-
+
int
peer_port_set (struct peer *peer, u_int16_t port)
{
@@ -3121,7 +2846,7 @@ peer_port_unset (struct peer *peer)
peer->port = BGP_PORT_DEFAULT;
return 0;
}
-
+
/* neighbor weight. */
int
peer_weight_set (struct peer *peer, u_int16_t weight)
@@ -3169,7 +2894,7 @@ peer_weight_unset (struct peer *peer)
}
return 0;
}
-
+
int
peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime)
{
@@ -3239,7 +2964,7 @@ peer_timers_unset (struct peer *peer)
return 0;
}
-
+
int
peer_timers_connect_set (struct peer *peer, u_int32_t connect)
{
@@ -3274,7 +2999,7 @@ peer_timers_connect_unset (struct peer *peer)
return 0;
}
-
+
int
peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv)
{
@@ -3304,10 +3029,10 @@ peer_advertise_interval_unset (struct peer *peer)
peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
else
peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
-
+
return 0;
}
-
+
/* neighbor interface */
int
peer_interface_set (struct peer *peer, const char *str)
@@ -3328,7 +3053,7 @@ peer_interface_unset (struct peer *peer)
return 0;
}
-
+
/* Allow-as in. */
int
peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num)
@@ -3343,7 +3068,8 @@ peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num)
{
peer->allowas_in[afi][safi] = allow_num;
SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
- peer_change_action (peer, afi, safi, peer_change_reset_in);
+ peer_change_action (peer, afi, safi, peer_change_reset_in,
+ PEER_DOWN_ALLOWAS_IN_CHANGE) ;
}
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
@@ -3356,9 +3082,10 @@ peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num)
{
peer->allowas_in[afi][safi] = allow_num;
SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
- peer_change_action (peer, afi, safi, peer_change_reset_in);
+ peer_change_action (peer, afi, safi, peer_change_reset_in,
+ PEER_DOWN_ALLOWAS_IN_CHANGE) ;
}
-
+
}
return 0;
}
@@ -3389,7 +3116,7 @@ peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi)
}
return 0;
}
-
+
int
peer_local_as_set (struct peer *peer, as_t as, int no_prepend)
{
@@ -3420,16 +3147,8 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend)
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
- return 0;
+ bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ;
+ return 0 ;
}
group = peer->group;
@@ -3441,14 +3160,7 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend)
else
UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ;
}
return 0;
@@ -3471,15 +3183,7 @@ peer_local_as_unset (struct peer *peer)
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
+ bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ;
return 0;
}
@@ -3489,18 +3193,11 @@ peer_local_as_unset (struct peer *peer)
peer->change_local_as = 0;
UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
- if (peer->status == Established)
- {
- peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- }
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
+ bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ;
}
return 0;
}
-
+
/* Set password for authenticating with the peer. */
int
peer_password_set (struct peer *peer, const char *password)
@@ -3518,36 +3215,26 @@ peer_password_set (struct peer *peer, const char *password)
if (peer->password)
XFREE (MTYPE_PEER_PASSWORD, peer->password);
-
+
peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, password);
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
{
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
- return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED;
+ bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ;
+ return BGP_SUCCESS;
}
for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
{
if (peer->password && strcmp (peer->password, password) == 0)
continue;
-
+
if (peer->password)
XFREE (MTYPE_PEER_PASSWORD, peer->password);
-
+
peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
- if (bgp_md5_set (peer) < 0)
- ret = BGP_ERR_TCPSIG_FAILED;
+ bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ;
}
return ret;
@@ -3569,17 +3256,13 @@ peer_password_unset (struct peer *peer)
&& strcmp (peer->group->conf->password, peer->password) == 0)
return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
if (peer->password)
- XFREE (MTYPE_PEER_PASSWORD, peer->password);
-
- peer->password = NULL;
-
- bgp_md5_set (peer);
+ {
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+ /* sets peer->password NULL */
+
+ bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ;
+ } ;
return 0;
}
@@ -3592,23 +3275,17 @@ peer_password_unset (struct peer *peer)
if (!peer->password)
continue;
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
-
XFREE (MTYPE_PEER_PASSWORD, peer->password);
- peer->password = NULL;
-
- bgp_md5_set (peer);
+ /* sets peer->password NULL */
+ bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ;
}
return 0;
}
-
+
/* Set distribute list to the peer. */
int
-peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
+peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
const char *name)
{
struct bgp_filter *filter;
@@ -3626,7 +3303,7 @@ peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
filter = &peer->filter[afi][safi];
- if (filter->plist[direct].name)
+ if (filter->plist[direct].ref)
return BGP_ERR_PEER_FILTER_CONFLICT;
if (filter->dlist[direct].name)
@@ -3739,7 +3416,7 @@ peer_distribute_update (struct access_list *access)
for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
{
if (filter->dlist[direct].name)
- filter->dlist[direct].alist =
+ filter->dlist[direct].alist =
access_list_lookup (afi, filter->dlist[direct].name);
else
filter->dlist[direct].alist = NULL;
@@ -3756,7 +3433,7 @@ peer_distribute_update (struct access_list *access)
for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
{
if (filter->dlist[direct].name)
- filter->dlist[direct].alist =
+ filter->dlist[direct].alist =
access_list_lookup (afi, filter->dlist[direct].name);
else
filter->dlist[direct].alist = NULL;
@@ -3765,15 +3442,16 @@ peer_distribute_update (struct access_list *access)
}
}
}
-
+
/* Set prefix list to the peer. */
int
-peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
+peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
const char *name)
{
struct bgp_filter *filter;
struct peer_group *group;
struct listnode *node, *nnode;
+ prefix_list_ref ref ;
if (! peer->afc[afi][safi])
return BGP_ERR_PEER_INACTIVE;
@@ -3789,10 +3467,7 @@ peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
if (filter->dlist[direct].name)
return BGP_ERR_PEER_FILTER_CONFLICT;
- if (filter->plist[direct].name)
- free (filter->plist[direct].name);
- filter->plist[direct].name = strdup (name);
- filter->plist[direct].plist = prefix_list_lookup (afi, name);
+ ref = prefix_list_set_ref(&filter->plist[direct].ref, afi, name) ;
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
return 0;
@@ -3805,10 +3480,7 @@ peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
if (! peer->af_group[afi][safi])
continue;
- if (filter->plist[direct].name)
- free (filter->plist[direct].name);
- filter->plist[direct].name = strdup (name);
- filter->plist[direct].plist = prefix_list_lookup (afi, name);
+ prefix_list_copy_ref(&filter->plist[direct].ref, ref) ;
}
return 0;
}
@@ -3837,20 +3509,15 @@ peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
{
gfilter = &peer->group->conf->filter[afi][safi];
- if (gfilter->plist[direct].name)
+ if (gfilter->plist[direct].ref)
{
- if (filter->plist[direct].name)
- free (filter->plist[direct].name);
- filter->plist[direct].name = strdup (gfilter->plist[direct].name);
- filter->plist[direct].plist = gfilter->plist[direct].plist;
+ prefix_list_copy_ref(&filter->plist[direct].ref,
+ gfilter->plist[direct].ref) ;
return 0;
}
}
- if (filter->plist[direct].name)
- free (filter->plist[direct].name);
- filter->plist[direct].name = NULL;
- filter->plist[direct].plist = NULL;
+ prefix_list_unset_ref(&filter->plist[direct].ref) ;
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
return 0;
@@ -3863,10 +3530,7 @@ peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
if (! peer->af_group[afi][safi])
continue;
- if (filter->plist[direct].name)
- free (filter->plist[direct].name);
- filter->plist[direct].name = NULL;
- filter->plist[direct].plist = NULL;
+ prefix_list_unset_ref(&filter->plist[direct].ref) ;
}
return 0;
@@ -3876,55 +3540,15 @@ peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
static void
peer_prefix_list_update (struct prefix_list *plist)
{
- struct listnode *mnode, *mnnode;
- struct listnode *node, *nnode;
- struct bgp *bgp;
- struct peer *peer;
- struct peer_group *group;
- struct bgp_filter *filter;
- afi_t afi;
- safi_t safi;
- int direct;
-
- for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
- {
- for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- {
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- {
- filter = &peer->filter[afi][safi];
-
- for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
- {
- if (filter->plist[direct].name)
- filter->plist[direct].plist =
- prefix_list_lookup (afi, filter->plist[direct].name);
- else
- filter->plist[direct].plist = NULL;
- }
- }
- }
- for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
- {
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
- {
- filter = &group->conf->filter[afi][safi];
-
- for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
- {
- if (filter->plist[direct].name)
- filter->plist[direct].plist =
- prefix_list_lookup (afi, filter->plist[direct].name);
- else
- filter->plist[direct].plist = NULL;
- }
- }
- }
- }
+ /* This function used to fix up the addresses of prefix lists whenever
+ * a prefix list was changed. That is now done by the symbol reference
+ * mechanism.
+ *
+ * This function could have a use in updating a peer when a prefix list
+ * is changed ?
+ */
}
-
+
int
peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
const char *name)
@@ -4052,7 +3676,7 @@ peer_aslist_update (void)
for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
{
if (filter->aslist[direct].name)
- filter->aslist[direct].aslist =
+ filter->aslist[direct].aslist =
as_list_lookup (filter->aslist[direct].name);
else
filter->aslist[direct].aslist = NULL;
@@ -4069,7 +3693,7 @@ peer_aslist_update (void)
for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
{
if (filter->aslist[direct].name)
- filter->aslist[direct].aslist =
+ filter->aslist[direct].aslist =
as_list_lookup (filter->aslist[direct].name);
else
filter->aslist[direct].aslist = NULL;
@@ -4078,10 +3702,10 @@ peer_aslist_update (void)
}
}
}
-
+
/* Set route-map to the peer. */
int
-peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
+peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
const char *name)
{
struct bgp_filter *filter;
@@ -4092,7 +3716,7 @@ peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
return BGP_ERR_PEER_INACTIVE;
if (direct != RMAP_IN && direct != RMAP_OUT &&
- direct != RMAP_IMPORT && direct != RMAP_EXPORT)
+ direct != RMAP_IMPORT && direct != RMAP_EXPORT && direct != RMAP_RS_IN)
return BGP_ERR_INVALID_VALUE;
if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
@@ -4103,7 +3727,7 @@ peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
if (filter->map[direct].name)
free (filter->map[direct].name);
-
+
filter->map[direct].name = strdup (name);
filter->map[direct].map = route_map_lookup_by_name (name);
@@ -4139,7 +3763,7 @@ peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
return BGP_ERR_PEER_INACTIVE;
if (direct != RMAP_IN && direct != RMAP_OUT &&
- direct != RMAP_IMPORT && direct != RMAP_EXPORT)
+ direct != RMAP_IMPORT && direct != RMAP_EXPORT && direct != RMAP_RS_IN)
return BGP_ERR_INVALID_VALUE;
if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
@@ -4186,10 +3810,10 @@ peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
}
return 0;
}
-
+
/* Set unsuppress-map to the peer. */
int
-peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi,
+peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi,
const char *name)
{
struct bgp_filter *filter;
@@ -4201,12 +3825,12 @@ peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi,
if (peer_is_group_member (peer, afi, safi))
return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
-
+
filter = &peer->filter[afi][safi];
if (filter->usmap.name)
free (filter->usmap.name);
-
+
filter->usmap.name = strdup (name);
filter->usmap.map = route_map_lookup_by_name (name);
@@ -4239,7 +3863,7 @@ peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi)
if (! peer->afc[afi][safi])
return BGP_ERR_PEER_INACTIVE;
-
+
if (peer_is_group_member (peer, afi, safi))
return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
@@ -4268,7 +3892,7 @@ peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi)
}
return 0;
}
-
+
int
peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi,
u_int32_t max, u_char threshold,
@@ -4363,172 +3987,26 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
}
return 0;
}
-
-/* Set # of hops between us and BGP peer. */
-int
-peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
-{
- struct peer_group *group;
- struct listnode *node, *nnode;
- struct peer *peer1;
- int ret;
-
- zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host);
-
- if (peer_sort (peer) == BGP_PEER_IBGP)
- return BGP_ERR_NO_IBGP_WITH_TTLHACK;
-
- /* We cannot configure ttl-security hops when ebgp-multihop is already
- set. For non peer-groups, the check is simple. For peer-groups, it's
- slightly messy, because we need to check both the peer-group structure
- and all peer-group members for any trace of ebgp-multihop configuration
- before actually applying the ttl-security rules. Cisco really made a
- mess of this configuration parameter, and OpenBGPD got it right.
- */
-
- if (peer->gtsm_hops == 0) {
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
- group = peer->group;
- if (group->conf->ttl != 1)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
-
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
- {
- if (peer_sort (peer1) == BGP_PEER_IBGP)
- continue;
-
- if (peer1->ttl != 1)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
- }
- }
- else
- {
- if (peer->ttl != 1)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
- }
- /* specify MAXTTL on outgoing packets */
- ret = peer_ebgp_multihop_set (peer, MAXTTL);
- if (ret != 0)
- return ret;
- }
-
- peer->gtsm_hops = gtsm_hops;
-
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
- if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
- sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops);
- }
- else
- {
- group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
- {
- if (peer_sort (peer) == BGP_PEER_IBGP)
- continue;
-
- peer->gtsm_hops = group->conf->gtsm_hops;
-
- /* Change setting of existing peer
- * established then change value (may break connectivity)
- * not established yet (teardown session and restart)
- * no session then do nothing (will get handled by next connection)
- */
- if (peer->status == Established)
- {
- if (peer->fd >= 0 && peer->gtsm_hops != 0)
- sockopt_minttl (peer->su.sa.sa_family, peer->fd,
- MAXTTL + 1 - peer->gtsm_hops);
- }
- else if (peer->status < Established)
- {
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s Min-ttl changed", peer->host);
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
- }
- }
-
- return 0;
-}
int
-peer_ttl_security_hops_unset (struct peer *peer)
+peer_clear (struct peer *peer)
{
- struct peer_group *group;
- struct listnode *node, *nnode;
- struct peer *opeer;
-
- zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host);
-
- if (peer_sort (peer) == BGP_PEER_IBGP)
- return 0;
-
- /* if a peer-group member, then reset to peer-group default rather than 0 */
- if (peer_group_active (peer))
- peer->gtsm_hops = peer->group->conf->gtsm_hops;
- else
- peer->gtsm_hops = 0;
+ /* Overrides any Max Prefix issues. */
+ bgp_maximum_prefix_cancel_timer (peer) ;
- opeer = peer;
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
- if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
- sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
- }
- else
- {
- group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
- {
- if (peer_sort (peer) == BGP_PEER_IBGP)
- continue;
+ /* Overrides any idle hold timer */
+ peer->v_start = BGP_INIT_START_TIMER;
- peer->gtsm_hops = 0;
-
- if (peer->fd >= 0)
- sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
- }
- }
+ bgp_peer_down(peer, PEER_DOWN_USER_RESET) ;
- return peer_ebgp_multihop_unset (opeer);
-}
-
-int
-peer_clear (struct peer *peer)
-{
- if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
- {
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
- {
- UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
- if (peer->t_pmax_restart)
- {
- BGP_TIMER_OFF (peer->t_pmax_restart);
- if (BGP_DEBUG (events, EVENTS))
- zlog_debug ("%s Maximum-prefix restart timer canceled",
- peer->host);
- }
- BGP_EVENT_ADD (peer, BGP_Start);
- return 0;
- }
-
- peer->v_start = BGP_INIT_START_TIMER;
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_ADMIN_RESET);
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
return 0;
}
int
peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi,
- enum bgp_clear_type stype)
+ bgp_clear_type_t stype)
{
- if (peer->status != Established)
+ if (peer->state != bgp_peer_pEstablished)
return 0;
if (! peer->afc[afi][safi])
@@ -4559,7 +4037,7 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi,
else
prefix_type = ORF_TYPE_PREFIX_OLD;
- if (filter->plist[FILTER_IN].plist)
+ if (filter->plist[FILTER_IN].ref)
{
if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND))
bgp_route_refresh_send (peer, afi, safi,
@@ -4579,7 +4057,8 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi,
}
}
- if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH
+ if ( stype == BGP_CLEAR_SOFT_IN
+ || stype == BGP_CLEAR_SOFT_BOTH
|| stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX)
{
/* If neighbor has soft reconfiguration inbound flag.
@@ -4599,9 +4078,11 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi,
}
return 0;
}
-
-/* Display peer uptime.*/
-/* XXX: why does this function return char * when it takes buffer? */
+
+/* Display peer uptime.
+ *
+ * Note that this is a time period -- not an actual time.
+ */
char *
peer_uptime (time_t uptime2, char *buf, size_t len)
{
@@ -4613,7 +4094,7 @@ peer_uptime (time_t uptime2, char *buf, size_t len)
{
zlog_warn ("peer_uptime (): buffer shortage %lu", (u_long)len);
/* XXX: should return status instead of buf... */
- snprintf (buf, len, "<error> ");
+ snprintf (buf, len, "<error> ");
return buf;
}
@@ -4625,8 +4106,7 @@ peer_uptime (time_t uptime2, char *buf, size_t len)
}
/* Get current time. */
- uptime1 = bgp_clock ();
- uptime1 -= uptime2;
+ uptime1 = bgp_clock () - uptime2;
tm = gmtime (&uptime1);
/* Making formatted timer strings. */
@@ -4634,17 +4114,17 @@ peer_uptime (time_t uptime2, char *buf, size_t len)
#define ONE_WEEK_SECOND 60*60*24*7
if (uptime1 < ONE_DAY_SECOND)
- snprintf (buf, len, "%02d:%02d:%02d",
+ snprintf (buf, len, "%02d:%02d:%02d",
tm->tm_hour, tm->tm_min, tm->tm_sec);
else if (uptime1 < ONE_WEEK_SECOND)
- snprintf (buf, len, "%dd%02dh%02dm",
+ snprintf (buf, len, "%dd%02dh%02dm",
tm->tm_yday, tm->tm_hour, tm->tm_min);
else
- snprintf (buf, len, "%02dw%dd%02dh",
+ snprintf (buf, len, "%02dw%dd%02dh",
tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
return buf;
}
-
+
static void
bgp_config_write_filter (struct vty *vty, struct peer *peer,
afi_t afi, safi_t safi)
@@ -4664,38 +4144,49 @@ bgp_config_write_filter (struct vty *vty, struct peer *peer,
if (filter->dlist[in].name)
if (! gfilter || ! gfilter->dlist[in].name
|| strcmp (filter->dlist[in].name, gfilter->dlist[in].name) != 0)
- vty_out (vty, " neighbor %s distribute-list %s in%s", addr,
+ vty_out (vty, " neighbor %s distribute-list %s in%s", addr,
filter->dlist[in].name, VTY_NEWLINE);
if (filter->dlist[out].name && ! gfilter)
- vty_out (vty, " neighbor %s distribute-list %s out%s", addr,
+ vty_out (vty, " neighbor %s distribute-list %s out%s", addr,
filter->dlist[out].name, VTY_NEWLINE);
/* prefix-list. */
- if (filter->plist[in].name)
- if (! gfilter || ! gfilter->plist[in].name
- || strcmp (filter->plist[in].name, gfilter->plist[in].name) != 0)
- vty_out (vty, " neighbor %s prefix-list %s in%s", addr,
- filter->plist[in].name, VTY_NEWLINE);
- if (filter->plist[out].name && ! gfilter)
- vty_out (vty, " neighbor %s prefix-list %s out%s", addr,
- filter->plist[out].name, VTY_NEWLINE);
+ if ( filter->plist[in].ref && (! gfilter
+ || (prefix_list_ref_ident(gfilter->plist[in].ref)
+ != prefix_list_ref_ident(filter->plist[in].ref))) )
+ vty_out (vty, " neighbor %s prefix-list %s in%s", addr,
+ prefix_list_ref_name(filter->plist[in].ref), VTY_NEWLINE);
+
+ if (filter->plist[out].ref && ! gfilter)
+ vty_out (vty, " neighbor %s prefix-list %s out%s", addr,
+ prefix_list_ref_name(filter->plist[out].ref), VTY_NEWLINE);
/* route-map. */
if (filter->map[RMAP_IN].name)
if (! gfilter || ! gfilter->map[RMAP_IN].name
|| strcmp (filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0)
- vty_out (vty, " neighbor %s route-map %s in%s", addr,
+ vty_out (vty, " neighbor %s route-map %s in%s", addr,
filter->map[RMAP_IN].name, VTY_NEWLINE);
+
+ if (filter->map[RMAP_RS_IN].name)
+ if (! gfilter || ! gfilter->map[RMAP_RS_IN].name
+ || strcmp (filter->map[RMAP_RS_IN].name,
+ gfilter->map[RMAP_RS_IN].name) != 0)
+ vty_out (vty, " neighbor %s route-map %s rs-in%s", addr,
+ filter->map[RMAP_RS_IN].name, VTY_NEWLINE);
+
if (filter->map[RMAP_OUT].name && ! gfilter)
- vty_out (vty, " neighbor %s route-map %s out%s", addr,
+ vty_out (vty, " neighbor %s route-map %s out%s", addr,
filter->map[RMAP_OUT].name, VTY_NEWLINE);
+
if (filter->map[RMAP_IMPORT].name && ! gfilter)
vty_out (vty, " neighbor %s route-map %s import%s", addr,
filter->map[RMAP_IMPORT].name, VTY_NEWLINE);
+
if (filter->map[RMAP_EXPORT].name)
if (! gfilter || ! gfilter->map[RMAP_EXPORT].name
- || strcmp (filter->map[RMAP_EXPORT].name,
- gfilter->map[RMAP_EXPORT].name) != 0)
+ || strcmp (filter->map[RMAP_EXPORT].name,
+ gfilter->map[RMAP_EXPORT].name) != 0)
vty_out (vty, " neighbor %s route-map %s export%s", addr,
filter->map[RMAP_EXPORT].name, VTY_NEWLINE);
@@ -4708,10 +4199,10 @@ bgp_config_write_filter (struct vty *vty, struct peer *peer,
if (filter->aslist[in].name)
if (! gfilter || ! gfilter->aslist[in].name
|| strcmp (filter->aslist[in].name, gfilter->aslist[in].name) != 0)
- vty_out (vty, " neighbor %s filter-list %s in%s", addr,
+ vty_out (vty, " neighbor %s filter-list %s in%s", addr,
filter->aslist[in].name, VTY_NEWLINE);
if (filter->aslist[out].name && ! gfilter)
- vty_out (vty, " neighbor %s filter-list %s out%s", addr,
+ vty_out (vty, " neighbor %s filter-list %s out%s", addr,
filter->aslist[out].name, VTY_NEWLINE);
}
@@ -4721,168 +4212,168 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
struct peer *peer, afi_t afi, safi_t safi)
{
struct bgp_filter *filter;
- struct peer *g_peer = NULL;
- char buf[SU_ADDRSTRLEN];
+ struct peer *g_conf ;
+ bool pgm, agm ;
char *addr;
+ bgp_peer_sort_t sort ;
- filter = &peer->filter[afi][safi];
addr = peer->host;
+ sort = peer_sort(peer) ;
if (peer_group_active (peer))
- g_peer = peer->group->conf;
+ g_conf = peer->group->conf ;
+ else
+ g_conf = NULL ;
+ pgm = (g_conf != NULL) ; /* group member for >= 1 address families */
- /************************************
- ****** Global to the neighbor ******
- ************************************/
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ filter = &peer->filter[afi][safi];
+ agm = (peer->af_group[afi][safi] != 0) ;
+
+ if ((afi == AFI_IP) && (safi == SAFI_UNICAST))
{
- /* remote-as. */
- if (! peer_group_active (peer))
+ /* First: stuff which is global to the neighbor */
+
+ /* peer group and/or remote-as. */
+ if (!pgm)
{
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- vty_out (vty, " neighbor %s peer-group%s", addr,
- VTY_NEWLINE);
+ vty_out (vty, " neighbor %s peer-group%s", addr, VTY_NEWLINE);
if (peer->as)
vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as,
- VTY_NEWLINE);
+ VTY_NEWLINE);
}
else
{
- if (! g_peer->as)
+ if (g_conf->as == 0)
vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as,
- VTY_NEWLINE);
- if (peer->af_group[AFI_IP][SAFI_UNICAST])
+ VTY_NEWLINE);
+ if (agm)
vty_out (vty, " neighbor %s peer-group %s%s", addr,
- peer->group->name, VTY_NEWLINE);
+ peer->group->name, VTY_NEWLINE);
}
- /* local-as. */
+ /* local-as. */
if (peer->change_local_as)
- if (! peer_group_active (peer))
+ if (!pgm)
vty_out (vty, " neighbor %s local-as %u%s%s", addr,
- peer->change_local_as,
- CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ?
- " no-prepend" : "", VTY_NEWLINE);
+ peer->change_local_as,
+ CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)
+ ? " no-prepend" : "", VTY_NEWLINE);
- /* Description. */
+ /* Description. */
if (peer->desc)
vty_out (vty, " neighbor %s description %s%s", addr, peer->desc,
- VTY_NEWLINE);
+ VTY_NEWLINE);
- /* Shutdown. */
+ /* Shutdown. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN))
+ if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_SHUTDOWN))
vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE);
- /* Password. */
+ /* Password. */
if (peer->password)
- if (!peer_group_active (peer)
- || ! g_peer->password
- || strcmp (peer->password, g_peer->password) != 0)
+ if (!pgm || (g_conf->password == NULL)
+ || (strcmp (peer->password, g_conf->password) != 0))
vty_out (vty, " neighbor %s password %s%s", addr, peer->password,
- VTY_NEWLINE);
+ VTY_NEWLINE);
- /* BGP port. */
+ /* BGP port. */
if (peer->port != BGP_PORT_DEFAULT)
- vty_out (vty, " neighbor %s port %d%s", addr, peer->port,
- VTY_NEWLINE);
+ vty_out (vty, " neighbor %s port %d%s", addr, peer->port, VTY_NEWLINE);
- /* Local interface name. */
+ /* Local interface name. */
if (peer->ifname)
vty_out (vty, " neighbor %s interface %s%s", addr, peer->ifname,
- VTY_NEWLINE);
-
- /* Passive. */
+ VTY_NEWLINE);
+
+ /* Passive. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE))
+ if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_PASSIVE))
vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE);
- /* EBGP multihop. */
- if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1 &&
- !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL))
- if (! peer_group_active (peer) ||
- g_peer->ttl != peer->ttl)
- vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl,
- VTY_NEWLINE);
-
- /* ttl-security hops */
- if (peer_sort (peer) != BGP_PEER_IBGP && peer->gtsm_hops != 0)
- if (! peer_group_active (peer) || g_peer->gtsm_hops != peer->gtsm_hops)
- vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
- peer->gtsm_hops, VTY_NEWLINE);
-
- /* disable-connected-check. */
+ /* EBGP multihop and ttl-security hops. */
+ if (sort != BGP_PEER_IBGP)
+ {
+ if (peer->gtsm)
+ {
+ /* ttl-security hops */
+ if (!pgm || ! g_conf->gtsm)
+ vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
+ peer->ttl, VTY_NEWLINE) ;
+ }
+ else if (peer->ttl != 1)
+ {
+ /* eBGP multihop */
+ if (!pgm || (g_conf->ttl != peer->ttl))
+ vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr,
+ peer->ttl, VTY_NEWLINE) ;
+ } ;
+ } ;
+
+ /* disable-connected-check. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- vty_out (vty, " neighbor %s disable-connected-check%s", addr, VTY_NEWLINE);
+ if (!pgm ||
+ ! CHECK_FLAG (g_conf->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+ vty_out (vty, " neighbor %s disable-connected-check%s", addr,
+ VTY_NEWLINE);
- /* Update-source. */
+ /* Update-source. */
if (peer->update_if)
- if (! peer_group_active (peer) || ! g_peer->update_if
- || strcmp (g_peer->update_if, peer->update_if) != 0)
+ if (!pgm || ! g_conf->update_if
+ || (strcmp (g_conf->update_if, peer->update_if) != 0))
vty_out (vty, " neighbor %s update-source %s%s", addr,
- peer->update_if, VTY_NEWLINE);
+ peer->update_if, VTY_NEWLINE);
if (peer->update_source)
- if (! peer_group_active (peer) || ! g_peer->update_source
- || sockunion_cmp (g_peer->update_source,
- peer->update_source) != 0)
+ if (!pgm || ! g_conf->update_source
+ || (sockunion_cmp (g_conf->update_source, peer->update_source) != 0))
vty_out (vty, " neighbor %s update-source %s%s", addr,
- sockunion2str (peer->update_source, buf, SU_ADDRSTRLEN),
- VTY_NEWLINE);
+ sutoa(peer->update_source).str,
+ VTY_NEWLINE);
- /* advertisement-interval */
+ /* advertisement-interval */
if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV))
vty_out (vty, " neighbor %s advertisement-interval %d%s",
- addr, peer->v_routeadv, VTY_NEWLINE);
+ addr, peer->v_routeadv, VTY_NEWLINE);
- /* timers. */
- if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)
- && ! peer_group_active (peer))
- vty_out (vty, " neighbor %s timers %d %d%s", addr,
- peer->keepalive, peer->holdtime, VTY_NEWLINE);
+ /* timers. */
+ if (!pgm && CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+ vty_out (vty, " neighbor %s timers %d %d%s", addr,
+ peer->keepalive, peer->holdtime, VTY_NEWLINE);
if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT))
- vty_out (vty, " neighbor %s timers connect %d%s", addr,
- peer->connect, VTY_NEWLINE);
+ vty_out (vty, " neighbor %s timers connect %d%s", addr,
+ peer->connect, VTY_NEWLINE);
- /* Default weight. */
+ /* Default weight. */
if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT))
- if (! peer_group_active (peer) ||
- g_peer->weight != peer->weight)
+ if (!pgm || g_conf->weight != peer->weight)
vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight,
- VTY_NEWLINE);
+ VTY_NEWLINE);
- /* Dynamic capability. */
+ /* Dynamic capability. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+ if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
vty_out (vty, " neighbor %s capability dynamic%s", addr,
- VTY_NEWLINE);
+ VTY_NEWLINE);
- /* dont capability negotiation. */
+ /* dont capability negotiation. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DONT_CAPABILITY))
- vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr,
- VTY_NEWLINE);
+ if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_DONT_CAPABILITY))
+ vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr,
+ VTY_NEWLINE);
- /* override capability negotiation. */
+ /* override capability negotiation. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
- vty_out (vty, " neighbor %s override-capability%s", addr,
- VTY_NEWLINE);
+ if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+ vty_out (vty, " neighbor %s override-capability%s", addr,
+ VTY_NEWLINE);
- /* strict capability negotiation. */
+ /* strict capability negotiation. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
- if (! peer_group_active (peer) ||
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
- vty_out (vty, " neighbor %s strict-capability-match%s", addr,
- VTY_NEWLINE);
+ if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_STRICT_CAP_MATCH))
+ vty_out (vty, " neighbor %s strict-capability-match%s", addr,
+ VTY_NEWLINE);
- if (! peer_group_active (peer))
+ if (!pgm)
{
if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
{
@@ -4896,25 +4387,25 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
}
}
}
-
-
- /************************************
- ****** Per AF to the neighbor ******
- ************************************/
-
- if (! (afi == AFI_IP && safi == SAFI_UNICAST))
+ else
{
- if (peer->af_group[afi][safi])
+ /* Per AF for the neighbor */
+
+ if (agm)
vty_out (vty, " neighbor %s peer-group %s%s", addr,
- peer->group->name, VTY_NEWLINE);
+ peer->group->name, VTY_NEWLINE);
else
vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE);
}
+ /*--------------------------------------------------------------------
+ * From now on we are dealing with the particular address family.
+ */
+
/* ORF capability. */
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
|| CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
- if (! peer->af_group[afi][safi])
+ if (!agm)
{
vty_out (vty, " neighbor %s capability orf prefix-list", addr);
@@ -4929,31 +4420,27 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
}
/* Route reflector client. */
- if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)
- && ! peer->af_group[afi][safi])
- vty_out (vty, " neighbor %s route-reflector-client%s", addr,
+ if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT))
+ vty_out (vty, " neighbor %s route-reflector-client%s", addr,
VTY_NEWLINE);
/* Nexthop self. */
- if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)
- && ! peer->af_group[afi][safi])
+ if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF))
vty_out (vty, " neighbor %s next-hop-self%s", addr, VTY_NEWLINE);
/* Remove private AS. */
- if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)
- && ! peer->af_group[afi][safi])
- vty_out (vty, " neighbor %s remove-private-AS%s",
- addr, VTY_NEWLINE);
+ if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS))
+ vty_out (vty, " neighbor %s remove-private-AS%s", addr, VTY_NEWLINE);
/* send-community print. */
- if (! peer->af_group[afi][safi])
+ if (!agm)
{
if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
{
if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)
&& peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
vty_out (vty, " neighbor %s send-community both%s", addr, VTY_NEWLINE);
- else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
+ else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
vty_out (vty, " neighbor %s send-community extended%s",
addr, VTY_NEWLINE);
else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY))
@@ -4974,9 +4461,8 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
}
}
- /* Default information */
- if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE)
- && ! peer->af_group[afi][safi])
+ /* Default information */
+ if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE))
{
vty_out (vty, " neighbor %s default-originate", addr);
if (peer->default_rmap[afi][safi].name)
@@ -4984,19 +4470,19 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
vty_out (vty, "%s", VTY_NEWLINE);
}
- /* Soft reconfiguration inbound. */
+ /* Soft reconfiguration inbound. */
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
- if (! peer->af_group[afi][safi] ||
- ! CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+ if (!agm ||
+ ! CHECK_FLAG (g_conf->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
vty_out (vty, " neighbor %s soft-reconfiguration inbound%s", addr,
- VTY_NEWLINE);
+ VTY_NEWLINE);
- /* maximum-prefix. */
+ /* maximum-prefix. */
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
- if (! peer->af_group[afi][safi]
- || g_peer->pmax[afi][safi] != peer->pmax[afi][safi]
- || g_peer->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi]
- || CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)
+ if (!agm
+ || g_conf->pmax[afi][safi] != peer->pmax[afi][safi]
+ || g_conf->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi]
+ || CHECK_FLAG (g_conf->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)
!= CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
{
vty_out (vty, " neighbor %s maximum-prefix %ld", addr, peer->pmax[afi][safi]);
@@ -5010,15 +4496,14 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
}
/* Route server client. */
- if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
- && ! peer->af_group[afi][safi])
+ if (!agm && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
vty_out (vty, " neighbor %s route-server-client%s", addr, VTY_NEWLINE);
/* Allow AS in. */
if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_ALLOWAS_IN))
- if (! peer_group_active (peer)
- || ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN)
- || peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi])
+ if (!pgm
+ || ! peer_af_flag_check (g_conf, afi, safi, PEER_FLAG_ALLOWAS_IN)
+ || peer->allowas_in[afi][safi] != g_conf->allowas_in[afi][safi])
{
if (peer->allowas_in[afi][safi] == 3)
vty_out (vty, " neighbor %s allowas-in%s", addr, VTY_NEWLINE);
@@ -5031,17 +4516,17 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
bgp_config_write_filter (vty, peer, afi, safi);
/* atribute-unchanged. */
- if ((CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
+ if (!agm &&
+ ( CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
|| CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
- || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
- && ! peer->af_group[afi][safi])
+ || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED) ))
{
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
&& CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
&& CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
vty_out (vty, " neighbor %s attribute-unchanged%s", addr, VTY_NEWLINE);
else
- vty_out (vty, " neighbor %s attribute-unchanged%s%s%s%s", addr,
+ vty_out (vty, " neighbor %s attribute-unchanged%s%s%s%s", addr,
(CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)) ?
" as-path" : "",
(CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) ?
@@ -5074,7 +4559,7 @@ bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi,
else if (afi == AFI_IP6)
{
vty_out (vty, "ipv6");
-
+
if (safi == SAFI_MULTICAST)
vty_out (vty, " multicast");
}
@@ -5110,11 +4595,8 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi,
{
if (peer->afc[afi][safi])
{
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- {
- bgp_config_write_family_header (vty, afi, safi, &write);
- bgp_config_write_peer (vty, bgp, peer, afi, safi);
- }
+ bgp_config_write_family_header (vty, afi, safi, &write);
+ bgp_config_write_peer (vty, bgp, peer, afi, safi);
}
}
if (write)
@@ -5135,14 +4617,14 @@ bgp_config_write (struct vty *vty)
/* BGP Multiple instance. */
if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
- {
+ {
vty_out (vty, "bgp multiple-instance%s", VTY_NEWLINE);
write++;
}
/* BGP Config type. */
if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
- {
+ {
vty_out (vty, "bgp config-type cisco%s", VTY_NEWLINE);
write++;
}
@@ -5169,11 +4651,11 @@ bgp_config_write (struct vty *vty)
/* BGP fast-external-failover. */
if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
- vty_out (vty, " no bgp fast-external-failover%s", VTY_NEWLINE);
+ vty_out (vty, " no bgp fast-external-failover%s", VTY_NEWLINE);
/* BGP router ID. */
if (CHECK_FLAG (bgp->config, BGP_CONFIG_ROUTER_ID))
- vty_out (vty, " bgp router-id %s%s", inet_ntoa (bgp->router_id),
+ vty_out (vty, " bgp router-id %s%s", safe_inet_ntoa (bgp->router_id),
VTY_NEWLINE);
/* BGP log-neighbor-changes. */
@@ -5196,10 +4678,10 @@ bgp_config_write (struct vty *vty)
/* BGP client-to-client reflection. */
if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
vty_out (vty, " no bgp client-to-client reflection%s", VTY_NEWLINE);
-
+
/* BGP cluster ID. */
if (CHECK_FLAG (bgp->config, BGP_CONFIG_CLUSTER_ID))
- vty_out (vty, " bgp cluster-id %s%s", inet_ntoa (bgp->cluster_id),
+ vty_out (vty, " bgp cluster-id %s%s", safe_inet_ntoa (bgp->cluster_id),
VTY_NEWLINE);
/* Confederation identifier*/
@@ -5274,7 +4756,7 @@ bgp_config_write (struct vty *vty)
/* BGP timers configuration. */
if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE
&& bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
- vty_out (vty, " timers bgp %d %d%s", bgp->default_keepalive,
+ vty_out (vty, " timers bgp %d %d%s", bgp->default_keepalive,
bgp->default_holdtime, VTY_NEWLINE);
/* peer-group */
@@ -5285,14 +4767,11 @@ bgp_config_write (struct vty *vty)
/* Normal neighbor configuration. */
for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- {
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST);
- }
+ bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST);
/* Distance configuration. */
bgp_config_write_distance (vty, bgp);
-
+
/* No auto-summary */
if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
vty_out (vty, " no auto-summary%s", VTY_NEWLINE);
@@ -5320,17 +4799,26 @@ bgp_master_init (void)
memset (&bgp_master, 0, sizeof (struct bgp_master));
bm = &bgp_master;
- bm->bgp = list_new ();
- bm->listen_sockets = list_new ();
- bm->port = BGP_PORT_DEFAULT;
- bm->master = thread_master_create ();
+ bm->bgp = list_new ();
+ bm->master = thread_master_create ();
+ bm->port = BGP_PORT_DEFAULT;
bm->start_time = bgp_clock ();
-}
-
+ /* Implicitly:
+ *
+ * address = NULL -- no special listen address
+ * options = 0 -- no options set
+ * as2_speaker = false -- as4 speaker by default
+ * peer_linger_count = 0 -- no peers lingering
+ */
+} ;
+
void
bgp_init (void)
{
+ /* peer index */
+ bgp_peer_index_init(NULL);
+
/* BGP VTY commands installation. */
bgp_vty_init ();
@@ -5369,30 +4857,36 @@ bgp_init (void)
#endif /* HAVE_SNMP */
}
+/*------------------------------------------------------------------------------
+ * If not terminating, reset all peers now
+ *
+ * If
+ *
+ */
void
-bgp_terminate (void)
+bgp_terminate (int terminating, int retain_mode)
{
struct bgp *bgp;
struct peer *peer;
struct listnode *node, *nnode;
struct listnode *mnode, *mnnode;
- for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
- for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- if (peer->status == Established)
- bgp_notify_send (peer, BGP_NOTIFY_CEASE,
- BGP_NOTIFY_CEASE_PEER_UNCONFIG);
-
- bgp_cleanup_routes ();
-
- if (bm->process_main_queue)
+ /* If we are retaining, then turn off changes to the FIB. */
+ if (retain_mode)
{
- work_queue_free (bm->process_main_queue);
- bm->process_main_queue = NULL;
- }
- if (bm->process_rsclient_queue)
+ assert(terminating) ; /* Can only retain when terminating */
+ bgp_option_set(BGP_OPT_NO_FIB) ;
+ } ;
+
+ /* For all bgp instances... */
+ for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
{
- work_queue_free (bm->process_rsclient_queue);
- bm->process_rsclient_queue = NULL;
- }
-}
+ /* ...delete or down all peers. */
+ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+ if (terminating)
+ bgp_peer_delete(peer) ;
+ else
+ bgp_peer_down(peer, PEER_DOWN_USER_RESET) ;
+ } ;
+} ;
+
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 4da19e71..dad3360e 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -21,37 +21,32 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGPD_H
#define _QUAGGA_BGPD_H
+#include "stdbool.h"
+
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_notification.h"
+#include "bgpd/bgp_peer.h"
+
+#include "plist.h"
+#include "qtime.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
{
- /* BGP instance list. */
+ /* BGP instance list. */
struct list *bgp;
- /* BGP thread master. */
+ /* BGP thread master. */
struct thread_master *master;
- /* work queues */
- struct work_queue *process_main_queue;
- struct work_queue *process_rsclient_queue;
-
- /* Listening sockets */
- struct list *listen_sockets;
-
- /* BGP port number. */
- u_int16_t port;
-
- /* Listener address */
+ /* Listener address & port number */
char *address;
+ u_int16_t port;
- /* BGP start time. */
+ /* BGP start time. */
time_t start_time;
/* Various BGP global configuration. */
@@ -59,18 +54,24 @@ struct bgp_master
#define BGP_OPT_NO_FIB (1 << 0)
#define BGP_OPT_MULTIPLE_INSTANCE (1 << 1)
#define BGP_OPT_CONFIG_CISCO (1 << 2)
+
+ /* Do not announce AS4 */
+ bool as2_speaker ;
+
+ /* Peers lingering in pDeleting */
+ unsigned peer_linger_count ;
};
/* BGP instance structure. */
-struct bgp
+struct bgp
{
/* AS number of this BGP instance. */
as_t as;
/* Name of this BGP instance. */
char *name;
-
- /* Reference count to allow peer_delete to finish after bgp_delete */
+
+ /* Reference count to allow bgp_peer_delete to finish after bgp_delete */
int lock;
/* Self peer. */
@@ -85,6 +86,10 @@ struct bgp
/* BGP route-server-clients. */
struct list *rsclient;
+ /* work queues */
+ struct work_queue *process_main_queue;
+ struct work_queue *process_rsclient_queue;
+
/* BGP configuration. */
u_int16_t config;
#define BGP_CONFIG_ROUTER_ID (1 << 0)
@@ -151,7 +156,7 @@ struct bgp
u_char distance_ebgp;
u_char distance_ibgp;
u_char distance_local;
-
+
/* BGP default local-preference. */
u_int32_t default_local_pref;
@@ -172,7 +177,7 @@ struct peer_group
/* Pointer to BGP. */
struct bgp *bgp;
-
+
/* Peer-group client list. */
struct list *peer;
@@ -180,26 +185,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
@@ -208,341 +193,7 @@ 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
- {
- char *name;
- struct prefix_list *plist;
- } 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. */
- int gtsm_hops; /* minimum hopcount to 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)
@@ -622,64 +273,8 @@ 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. */
+/* Obsolete
#define Idle 1
#define Connect 2
#define Active 3
@@ -689,6 +284,7 @@ struct bgp_nlri
#define Clearing 7
#define Deleted 8
#define BGP_STATUS_MAX 9
+*/
/* BGP finite state machine events. */
#define BGP_Start 1
@@ -711,7 +307,7 @@ struct bgp_nlri
#define BGP_INIT_START_TIMER 5
#define BGP_ERROR_START_TIMER 30
#define BGP_DEFAULT_HOLDTIME 180
-#define BGP_DEFAULT_KEEPALIVE 60
+#define BGP_DEFAULT_KEEPALIVE 60
#define BGP_DEFAULT_ASORIGINATE 15
#define BGP_DEFAULT_EBGP_ROUTEADV 30
#define BGP_DEFAULT_IBGP_ROUTEADV 5
@@ -744,16 +340,16 @@ struct bgp_nlri
/* IBGP/EBGP identifier. We also have a CONFED peer, which is to say,
a peer who's AS is part of our Confederation. */
-enum
+typedef enum
{
BGP_PEER_IBGP,
BGP_PEER_EBGP,
BGP_PEER_INTERNAL,
BGP_PEER_CONFED
-};
+} bgp_peer_sort_t ;
/* Flag for peer_clear_soft(). */
-enum bgp_clear_type
+typedef enum
{
BGP_CLEAR_SOFT_NONE,
BGP_CLEAR_SOFT_OUT,
@@ -761,7 +357,7 @@ enum bgp_clear_type
BGP_CLEAR_SOFT_BOTH,
BGP_CLEAR_SOFT_IN_ORF_PREFIX,
BGP_CLEAR_SOFT_RSCLIENT
-};
+} bgp_clear_type_t ;
/* Macros. */
#define BGP_INPUT(P) ((P)->ibuf)
@@ -801,21 +397,69 @@ enum bgp_clear_type
#define BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP -27
#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS -28
#define BGP_ERR_TCPSIG_FAILED -29
-#define BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK -30
-#define BGP_ERR_NO_IBGP_WITH_TTLHACK -31
-#define BGP_ERR_MAX -32
-
+#define BGP_ERR_PEER_EXISTS -30
+#define BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM -31
+#define BGP_ERR_NO_IBGP_WITH_TTLHACK -32
+#define BGP_ERR_MAX -33
+
+/*------------------------------------------------------------------------------
+ * Globals.
+ */
extern struct bgp_master *bm;
extern struct thread_master *master;
-/* Prototypes. */
-extern void bgp_terminate (void);
+extern qpn_nexus cli_nexus;
+extern qpn_nexus bgp_nexus;
+extern qpn_nexus routing_nexus;
+
+/*------------------------------------------------------------------------------
+ * For many purposes BGP requires a CLOCK_MONOTONIC type time, in seconds.
+ */
+Inline time_t
+bgp_clock(void)
+{
+ return qt_get_mono_secs() ;
+}
+
+/*------------------------------------------------------------------------------
+ * For some purposes BGP requires a Wall Clock version of a time returned by
+ * bgp_clock() above.
+ *
+ * This is calculated from the current Wall Clock, the current bgp_clock and
+ * the bgp_clock time of some moment in the past.
+ *
+ * The fundamental problem is that the Wall Clock *may* (just may) be altered
+ * by the operator or automatically, if the system clock is wrong. So there
+ * are, potentially, two versions of a past moment:
+ *
+ * 1) according to the Wall Clock at the time.
+ *
+ * 2) according to the Wall Clock now.
+ *
+ * There doesn't seem to be a good way of selecting between these if they are
+ * different... Here we take (2), which (a) doesn't require us to fetch and
+ * store both bgp_clock() and Wall Clock times every time we record the time
+ * of some event, and (b) assumes that if the Wall Clock has been adjusted,
+ * it was wrong before. This can still cause confusion, because the Wall
+ * Clock time calculated now may differ from any logged Wall Clock times !!
+ */
+Inline time_t
+bgp_wall_clock(time_t bgp_time)
+{
+ return time(NULL) + (bgp_time - bgp_clock()) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prototypes.
+ */
+extern void bgp_terminate (int, int);
extern void bgp_reset (void);
-extern time_t bgp_clock (void);
-extern void bgp_zclient_reset (void);
-extern int bgp_nexthop_set (union sockunion *, union sockunion *,
- struct bgp_nexthop *, struct peer *);
+
+extern void bgp_zclient_reset (void); /* See bgp_zebra ! */
+extern int bgp_nexthop_set (union sockunion *, union sockunion *,
+ struct bgp_nexthop *, struct peer *); /* See bgp_zebra ! */
+
extern struct bgp *bgp_get_default (void);
extern struct bgp *bgp_lookup (as_t, const char *);
extern struct bgp *bgp_lookup_by_name (const char *);
@@ -824,8 +468,6 @@ extern struct peer_group *peer_group_lookup (struct bgp *, const char *);
extern struct peer_group *peer_group_get (struct bgp *, const char *);
extern struct peer *peer_lookup_with_open (union sockunion *, as_t, struct in_addr *,
int *);
-extern struct peer *peer_lock (struct peer *);
-extern struct peer *peer_unlock (struct peer *);
extern int peer_sort (struct peer *peer);
extern int peer_active (struct peer *);
extern int peer_active_nego (struct peer *);
@@ -833,7 +475,7 @@ extern struct peer *peer_create_accept (struct bgp *);
extern char *peer_uptime (time_t, char *, size_t);
extern int bgp_config_write (struct vty *);
extern void bgp_config_write_family_header (struct vty *, afi_t, safi_t, int *);
-
+
extern void bgp_master_init (void);
extern void bgp_init (void);
@@ -875,13 +517,15 @@ extern int peer_rsclient_active (struct peer *);
extern int peer_remote_as (struct bgp *, union sockunion *, as_t *, afi_t, safi_t);
extern int peer_group_remote_as (struct bgp *, const char *, as_t *);
-extern int peer_delete (struct peer *peer);
extern int peer_group_delete (struct peer_group *);
extern int peer_group_remote_as_delete (struct peer_group *);
extern int peer_activate (struct peer *, afi_t, safi_t);
extern int peer_deactivate (struct peer *, afi_t, safi_t);
+extern void peer_rsclient_unset(struct peer* peer, int afi, int safi,
+ bool keep_export) ;
+
extern int peer_group_bind (struct bgp *, union sockunion *, struct peer_group *,
afi_t, safi_t, as_t *);
extern int peer_group_unbind (struct bgp *, struct peer *, struct peer_group *,
@@ -892,6 +536,8 @@ extern int peer_flag_unset (struct peer *, u_int32_t);
extern int peer_af_flag_set (struct peer *, afi_t, safi_t, u_int32_t);
extern int peer_af_flag_unset (struct peer *, afi_t, safi_t, u_int32_t);
+extern int peer_af_flag_modify (struct peer *, afi_t, safi_t, u_int32_t,
+ bool set) ;
extern int peer_af_flag_check (struct peer *, afi_t, safi_t, u_int32_t);
extern int peer_ebgp_multihop_set (struct peer *, int);
@@ -954,8 +600,9 @@ extern int peer_maximum_prefix_set (struct peer *, afi_t, safi_t, u_int32_t, u_c
extern int peer_maximum_prefix_unset (struct peer *, afi_t, safi_t);
extern int peer_clear (struct peer *);
-extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type);
+extern int peer_clear_soft (struct peer *, afi_t, safi_t, bgp_clear_type_t);
+extern void program_terminate_if_all_disabled(void);
extern int peer_ttl_security_hops_set (struct peer *, int);
extern int peer_ttl_security_hops_unset (struct peer *);
diff --git a/configure-quagga b/configure-quagga
new file mode 100644
index 00000000..bcefd0ba
--- /dev/null
+++ b/configure-quagga
@@ -0,0 +1,6 @@
+./configure --prefix=/home/quagga/local --exec-prefix=/usr/local \
+ --sysconfdir=/home/quagga/etc --localstatedir=/home/quagga/state \
+ --enable-user=quagga --enable-group=quagga \
+ --disable-pie --enable-pthread \
+ --disable-doc --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d
+
diff --git a/configure.ac b/configure.ac
index b981d5b1..6e0c5d5b 100755
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,7 @@
## $Id$
AC_PREREQ(2.53)
-AC_INIT(Quagga, 0.99.18, [https://bugzilla.quagga.net])
+AC_INIT(Quagga, 0.99.18ex17, [http://bugzilla.quagga.net])
AC_CONFIG_SRCDIR(lib/zebra.h)
AC_CONFIG_MACRO_DIR([m4])
@@ -128,6 +128,7 @@ if test "x${cflags_specified}" = "x" ; then
CFLAGS="${CFLAGS} -Wbad-function-cast -Wwrite-strings"
CFLAGS="${CFLAGS} -Wmissing-prototypes -Wmissing-declarations"
CFLAGS="${CFLAGS} -Wchar-subscripts -Wcast-qual"
+ CFLAGS="${CFLAGS} -pthread -rdynamic"
# TODO: conditionally addd -Wpacked if handled
AC_MSG_RESULT([gcc default])
;;
diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
index fe872a95..1d9bf6a7 100644
--- a/isisd/isis_dlpi.c
+++ b/isisd/isis_dlpi.c
@@ -27,10 +27,10 @@
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
-#include <stropts.h>
+//#include <stropts.h>
#include <poll.h>
-#include <sys/dlpi.h>
-#include <sys/pfmod.h>
+//#include <sys/dlpi.h>
+//#include <sys/pfmod.h>
#include "log.h"
#include "stream.h"
diff --git a/isisd/isis_flags.c b/isisd/isis_flags.c
index 03c91101..7d210cfd 100644
--- a/isisd/isis_flags.c
+++ b/isisd/isis_flags.c
@@ -48,10 +48,11 @@ flags_get_index (struct flags *flags)
}
else
{
+ uintptr_t pi ;
node = listhead (flags->free_idcs);
- index = (int) listgetdata (node);
- listnode_delete (flags->free_idcs, (void *) index);
- index--;
+ pi = (uintptr_t)listgetdata (node);
+ listnode_delete (flags->free_idcs, (void*)pi);
+ index = (int)(pi - 1) ;
}
return index;
@@ -60,6 +61,8 @@ flags_get_index (struct flags *flags)
void
flags_free_index (struct flags *flags, int index)
{
+ uintptr_t pi ;
+
if (index + 1 == flags->maxindex)
{
flags->maxindex--;
@@ -71,7 +74,8 @@ flags_free_index (struct flags *flags, int index)
flags->free_idcs = list_new ();
}
- listnode_add (flags->free_idcs, (void *) (index + 1));
+ pi = (uintptr_t)index + 1 ;
+ listnode_add (flags->free_idcs, (void*)pi) ;
return;
}
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 50289db3..545d43e4 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -655,7 +655,7 @@ lspid_print (u_char * lsp_id, u_char * trg, char dynhost, char frag)
if (dyn)
sprintf ((char *)id, "%.14s", dyn->name.name);
- else if (!memcmp (isis->sysid, lsp_id, ISIS_SYS_ID_LEN) & dynhost)
+ else if (!memcmp (isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost)
sprintf ((char *)id, "%.14s", unix_hostname ());
else
{
diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c
index 6b565bcb..fd910835 100644
--- a/isisd/isis_misc.c
+++ b/isisd/isis_misc.c
@@ -107,7 +107,7 @@ dotformat2buff (u_char * buff, const u_char * dotted)
int nextdotpos = 2;
number[2] = '\0';
- dotlen = strlen(dotted);
+ dotlen = strlen((const char*)dotted);
if (dotlen > 50)
{
/* this can't be an iso net, its too long */
@@ -165,7 +165,7 @@ sysid2buff (u_char * buff, const u_char * dotted)
number[2] = '\0';
// surely not a sysid_string if not 14 length
- if (strlen (dotted) != 14)
+ if (strlen ((const char*)dotted) != 14)
{
return 0;
}
@@ -271,7 +271,7 @@ speaks (struct nlpids *nlpids, int family)
* Returns 0 on error, IS-IS Circuit Type on ok
*/
int
-string2circuit_t (const u_char * str)
+string2circuit_t (const char * str)
{
if (!str)
diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h
index d5003a8e..dbf0060a 100644
--- a/isisd/isis_misc.h
+++ b/isisd/isis_misc.h
@@ -24,7 +24,7 @@
#ifndef _ZEBRA_ISIS_MISC_H
#define _ZEBRA_ISIS_MISC_H
-int string2circuit_t (const u_char *);
+int string2circuit_t (const char *);
const char *circuit_t2string (int);
const char *syst2string (int);
struct in_addr newprefix2inaddr (u_char * prefix_start,
diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c
index cff0fa3f..fcc05264 100644
--- a/isisd/isis_routemap.c
+++ b/isisd/isis_routemap.c
@@ -2,22 +2,22 @@
* IS-IS Rout(e)ing protocol - isis_routemap.c
*
* Copyright (C) 2001,2002 Sampo Saaristo
- * Tampere University of Technology
+ * Tampere University of Technology
* Institute of Communications Engineering
*
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public Licenseas published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public Licenseas published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
- * This program 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
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <zebra.h>
@@ -68,8 +68,7 @@ isis_route_map_upd (const char *name)
for (i = 0; i <= ZEBRA_ROUTE_MAX; i++)
{
if (isis->rmap[i].name)
- isis->rmap[i].map = isis->rmap[i].map =
- route_map_lookup_by_name (isis->rmap[i].name);
+ isis->rmap[i].map = route_map_lookup_by_name (isis->rmap[i].name);
else
isis->rmap[i].map = NULL;
}
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index 9ee5ffc5..94c3f351 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -332,7 +332,7 @@ isis_zebra_route_del_ipv4 (struct prefix *prefix,
}
#ifdef HAVE_IPV6
-void
+static void
isis_zebra_route_add_ipv6 (struct prefix *prefix,
struct isis_route_info *route_info)
{
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 1e84a1ce..9b07ab88 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -1016,7 +1016,7 @@ DEFUN (net,
"A Network Entity Title for this process (OSI only)\n"
"XX.XXXX. ... .XXX.XX Network entity title (NET)\n")
{
- return area_net_title (vty, argv[0]);
+ return area_net_title (vty, (const u_char*)argv[0]);
}
/*
@@ -1029,7 +1029,7 @@ DEFUN (no_net,
"A Network Entity Title for this process (OSI only)\n"
"XX.XXXX. ... .XXX.XX Network entity title (NET)\n")
{
- return area_clear_net_title (vty, argv[0]);
+ return area_clear_net_title (vty, (const u_char*)argv[0]);
}
DEFUN (area_passwd,
diff --git a/isisd/topology/spgrid.c b/isisd/topology/spgrid.c
index 611b6727..5197beb1 100644
--- a/isisd/topology/spgrid.c
+++ b/isisd/topology/spgrid.c
@@ -50,8 +50,8 @@ long X, /* horizontal size of grid */
long x,
y,
- y1, y2, yp,
- dl, dx, xn, yn, count,
+ yr1, yr2, yp,
+ dl, dx, xnd, ynd, count,
*mess;
double n;
@@ -670,12 +670,12 @@ gen_spgrid_topology (struct vty *vty, struct list *topology)
for ( k = ax; k > 0; k -- )
{
- y1 = nrand ( Y );
+ yr1 = nrand ( Y );
do
- y2 = nrand ( Y );
- while ( y2 == y1 );
- i = NODE ( x, y1 );
- j = NODE ( x, y2 );
+ yr2 = nrand ( Y );
+ while ( yr2 == yr1 );
+ i = NODE ( x, yr1 );
+ j = NODE ( x, yr2 );
l = am + nrand ( al );
print_arc (vty, topology, i, j, l );
}
@@ -697,9 +697,9 @@ gen_spgrid_topology (struct vty *vty, struct list *topology)
for ( x = 0; x < X-1; x ++ )
{
/* generating arcs from one layer */
- for ( count = 0, xn = x + 1;
- count < ix && xn < X;
- count ++, xn += ih )
+ for ( count = 0, xnd = x + 1;
+ count < ix && xnd < X;
+ count ++, xnd += ih )
{
if ( ip_f )
for ( y = 0; y < Y; y ++ )
@@ -708,16 +708,16 @@ gen_spgrid_topology (struct vty *vty, struct list *topology)
for ( y = 0; y < Y; y ++ )
{
i = NODE ( x, y );
- dx = xn - x;
+ dx = xnd - x;
if ( ip_f )
{
yp = nrand(Y-y);
- yn = mess[ yp ];
+ ynd = mess[ yp ];
mess[ yp ] = mess[ Y - y - 1 ];
}
else
- yn = y;
- j = NODE ( xn, yn );
+ ynd = y;
+ j = NODE ( xnd, ynd );
l = im + nrand ( il );
if ( in != 0 )
l *= (long) ( in * dx );
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 315e919b..e88c5998 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,7 +12,11 @@ libzebra_la_SOURCES = \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
- sigevent.c pqueue.c jhash.c memtypes.c workqueue.c
+ sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c heap.c \
+ qtime.c qpthreads.c mqueue.c qpselect.c qtimers.c qpnexus.c \
+ command_queue.c qlib_init.c pthread_safe.c list_util.c \
+ vty_io.c vty_cli.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
+ qiovec.c qfstring.c errno_names.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -27,7 +31,13 @@ pkginclude_HEADERS = \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
- workqueue.h route_types.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 qafi_safi.h \
+ confirm.h miyagi.h pthread_safe.h list_util.h node_type.h uty.h \
+ vty_io.h vty_cli.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
+ qiovec.h qfstring.h errno_names.h \
+ route_types.h command_execute.h
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/buffer.c b/lib/buffer.c
index f19a9e0c..816b0d1d 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -1,5 +1,5 @@
/*
- * Buffering of output and input.
+ * Buffering of output and input.
* Copyright (C) 1998 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
@@ -8,7 +8,7 @@
* 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
@@ -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.
+ * Boston, MA 02111-1307, USA.
*/
#include <zebra.h>
@@ -29,18 +29,6 @@
#include <stddef.h>
-
-/* Buffer master. */
-struct buffer
-{
- /* Data list. */
- struct buffer_data *head;
- struct buffer_data *tail;
-
- /* Size of each buffer_data chunk. */
- size_t size;
-};
-
/* Data container. */
struct buffer_data
{
@@ -67,11 +55,12 @@ struct buffer_data
/* Make new buffer. */
struct buffer *
-buffer_new (size_t size)
+buffer_init_new (struct buffer* b, size_t size)
{
- struct buffer *b;
-
- b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
+ if (b == NULL)
+ b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
+ else
+ memset(b, 0, sizeof (struct buffer)) ;
if (size)
b->size = size;
@@ -89,6 +78,13 @@ buffer_new (size_t size)
return b;
}
+/* Make new buffer. */
+struct buffer *
+buffer_new (size_t size)
+{
+ return buffer_init_new(NULL, size);
+}
+
/* Free buffer. */
void
buffer_free (struct buffer *b)
@@ -133,7 +129,7 @@ buffer_reset (struct buffer *b)
{
struct buffer_data *data;
struct buffer_data *next;
-
+
for (data = b->head; data; data = next)
{
next = data->next;
@@ -148,7 +144,8 @@ buffer_add (struct buffer *b)
{
struct buffer_data *d;
- d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size]));
+ typedef struct buffer_data buffer_data_t ; /* stop Eclipse whinging */
+ d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(buffer_data_t, data[b->size]));
d->cp = d->sp = 0;
d->next = NULL;
@@ -169,7 +166,7 @@ buffer_put(struct buffer *b, const void *p, size_t size)
const char *ptr = p;
/* We use even last one byte of data buffer. */
- while (size)
+ while (size)
{
size_t chunk;
@@ -226,7 +223,7 @@ buffer_flush_all (struct buffer *b, int fd)
/* Flush enough data to fill a terminal window of the given scene (used only
by vty telnet interface). */
buffer_status_t
-buffer_flush_window (struct buffer *b, int fd, int width, int height,
+buffer_flush_window (struct buffer *b, int fd, int width, int height,
int erase_flag, int no_more_flag)
{
int nbytes;
@@ -353,7 +350,7 @@ buffer_flush_window (struct buffer *b, int fd, int width, int height,
if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
{
zlog_warn("%s: writev to fd %d failed: %s",
- __func__, fd, safe_strerror(errno));
+ __func__, fd, errtoa(errno, 0).str) ;
break;
}
@@ -365,7 +362,7 @@ buffer_flush_window (struct buffer *b, int fd, int width, int height,
#else /* IOV_MAX */
if ((nbytes = writev (fd, iov, iov_index)) < 0)
zlog_warn("%s: writev to fd %d failed: %s",
- __func__, fd, safe_strerror(errno));
+ __func__, fd, errtoa(errno, 0).str);
#endif /* IOV_MAX */
/* Free printed buffer data. */
@@ -427,7 +424,7 @@ in one shot. */
/* Calling code should try again later. */
return BUFFER_PENDING;
zlog_warn("%s: write error on fd %d: %s",
- __func__, fd, safe_strerror(errno));
+ __func__, fd, errtoa(errno, 0).str);
return BUFFER_ERROR;
}
@@ -482,7 +479,7 @@ buffer_write(struct buffer *b, int fd, const void *p, size_t size)
else
{
zlog_warn("%s: write error on fd %d: %s",
- __func__, fd, safe_strerror(errno));
+ __func__, fd, errtoa(errno, 0).str);
return BUFFER_ERROR;
}
}
diff --git a/lib/buffer.h b/lib/buffer.h
index 6c3dc76a..132fe155 100644
--- a/lib/buffer.h
+++ b/lib/buffer.h
@@ -1,5 +1,5 @@
/*
- * Buffering to output and input.
+ * Buffering to output and input.
* Copyright (C) 1998 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
@@ -23,11 +23,22 @@
#ifndef _ZEBRA_BUFFER_H
#define _ZEBRA_BUFFER_H
+/* Buffer master. */
+struct buffer
+{
+ /* Data list. */
+ struct buffer_data *head;
+ struct buffer_data *tail;
+
+ /* Size of each buffer_data chunk. */
+ size_t size;
+};
/* Create a new buffer. Memory will be allocated in chunks of the given
size. If the argument is 0, the library will supply a reasonable
default size suitable for buffering socket I/O. */
extern struct buffer *buffer_new (size_t);
+struct buffer *buffer_init_new (struct buffer* b, size_t size) ;
/* Free all data in the buffer. */
extern void buffer_reset (struct buffer *);
@@ -73,7 +84,7 @@ typedef enum
extern buffer_status_t buffer_write(struct buffer *, int fd,
const void *, size_t);
-/* This function attempts to flush some (but perhaps not all) of
+/* This function attempts to flush some (but perhaps not all) of
the queued data to the given file descriptor. */
extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
@@ -88,7 +99,7 @@ extern buffer_status_t buffer_flush_all (struct buffer *, int fd);
/* Attempt to write enough data to the given fd to fill a window of the
given width and height (and remove the data written from the buffer).
- If !no_more, then a message saying " --More-- " is appended.
+ If !no_more, then a message saying " --More-- " is appended.
If erase is true, then first overwrite the previous " --More-- " message
with spaces.
diff --git a/lib/command.c b/lib/command.c
index 264e0f7b..67a9ab04 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1,11 +1,11 @@
/*
$Id$
-
+
Command interpreter routine for virtual terminal [aka TeletYpe]
Copyright (C) 1997, 98, 99 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
@@ -30,8 +30,12 @@ Boston, MA 02111-1307, USA. */
#include "thread.h"
#include "vector.h"
#include "vty.h"
+#include "uty.h"
+#include "qstring.h"
#include "command.h"
+#include "command_execute.h"
#include "workqueue.h"
+#include "command_queue.h"
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
@@ -89,11 +93,17 @@ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
\r\n";
+#ifdef QDEBUG
+const char* debug_banner =
+ QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG_NAME " "
+ __DATE__ " " __TIME__ ;
+#endif
+
static const struct facility_map {
int facility;
const char *name;
size_t match;
-} syslog_facilities[] =
+} syslog_facilities[] =
{
{ LOG_KERN, "kern", 1 },
{ LOG_USER, "user", 2 },
@@ -145,7 +155,7 @@ static int
level_match(const char *s)
{
int level ;
-
+
for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
if (!strncmp (s, zlog_priority[level], 2))
return level;
@@ -160,11 +170,11 @@ print_version (const char *progname)
printf ("%s\n", QUAGGA_COPYRIGHT);
}
-
+
/* Utility function to concatenate argv argument into a single string
with inserting ' ' character between each argument. */
char *
-argv_concat (const char **argv, int argc, int shift)
+argv_concat (const char* const* argv, int argc, int shift)
{
int i;
size_t len;
@@ -188,16 +198,7 @@ argv_concat (const char **argv, int argc, int shift)
return str;
}
-/* Install top node of command vector. */
-void
-install_node (struct cmd_node *node,
- int (*func) (struct vty *))
-{
- vector_set_index (cmdvec, node->node, node);
- node->func = func;
- node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
-}
-
+#if 0 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* Compare two command's string. Used in sort_node (). */
static int
cmp_node (const void *p, const void *q)
@@ -217,102 +218,245 @@ cmp_desc (const void *p, const void *q)
return strcmp (a->cmd, b->cmd);
}
+#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+/* Install top node of command vector. */
+void
+install_node (struct cmd_node *node,
+ int (*func) (struct vty *))
+{
+ vector_set_index (cmdvec, node->node, node);
+ node->func = func;
+ node->cmd_vector = vector_init (0);
+}
+
+/* Compare two command's string. Used in sort_node (). */
+static int
+cmp_node (const struct cmd_element **a, const struct cmd_element **b)
+{
+ return strcmp ((*a)->string, (*b)->string);
+}
+
+static int
+cmp_desc (const struct desc **a, const struct desc **b)
+{
+ return strcmp ((*a)->cmd, (*b)->cmd);
+}
+
/* Sort each node's command element according to command string. */
void
sort_node ()
{
- unsigned int i, j;
- struct cmd_node *cnode;
- vector descvec;
- struct cmd_element *cmd_element;
+ unsigned int i ;
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((cnode = vector_slot (cmdvec, i)) != NULL)
- {
- vector cmd_vector = cnode->cmd_vector;
- qsort (cmd_vector->index, vector_active (cmd_vector),
- sizeof (void *), cmp_node);
+ for (i = 0; i < vector_length(cmdvec); i++)
+ {
+ struct cmd_node *cnode;
+ vector cmd_vector ;
+ unsigned int j;
- for (j = 0; j < vector_active (cmd_vector); j++)
- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL
- && vector_active (cmd_element->strvec))
- {
- descvec = vector_slot (cmd_element->strvec,
- vector_active (cmd_element->strvec) - 1);
- qsort (descvec->index, vector_active (descvec),
- sizeof (void *), cmp_desc);
- }
- }
-}
+ cnode = vector_get_item(cmdvec, i) ;
-/* Breaking up string into each command piece. I assume given
- character is separated by a space character. Return value is a
- vector which includes char ** data element. */
-vector
-cmd_make_strvec (const char *string)
+ if (cnode == NULL)
+ continue ;
+
+ cmd_vector = cnode->cmd_vector;
+ if (cmd_vector == NULL)
+ continue ;
+
+ vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ;
+
+ for (j = 0; j < vector_length(cmd_vector); j++)
+ {
+ struct cmd_element *cmd_element ;
+ vector descvec ;
+
+ cmd_element = vector_get_item (cmd_vector, j);
+ if (cmd_element == NULL)
+ continue ;
+
+ descvec = vector_get_last_item(cmd_element->strvec) ;
+ if (descvec == NULL)
+ continue ;
+
+ vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ;
+ } ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take string and break it into tokens.
+ *
+ * Discards leading and trailing white-space.
+ *
+ * Treats lines that start with '!' or '#' (after any leading white-space)
+ * as empty -- these are comment lines.
+ *
+ * Tokens are non-whitespace separated by one or more white-space.
+ *
+ * White-space is anything that isspace() thinks is a space. (Which in the
+ * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ *
+ * Returns: NULL => empty line (after white-space trimming) or comment line.
+ * otherwise: is vector containing one or more tokens.
+ *
+ * ....
+ *
+ *
+ * Note: all the tokens in the vector have at least one character, and no
+ * entries are NULL.
+ *
+ * NB: it is the caller's responsibility to release the vector and its contents,
+ * see cmd_free_strvec().
+ */
+static vector
+cmd_make_vline(vector vline, qstring qs, const char *string)
{
- const char *cp, *start;
- char *token;
- int strlen;
- vector strvec;
-
+ char *token, *tp ;
+ const char *cp, *sp, *ep, *op ;
+
+ /* Reset any existing vline, and empty the qstring if given. */
+ if (vline != NULL)
+ vector_set_length(vline, 0) ;
+
+ qs_clear(qs) ;
+
+ /* Strip leading and trailing white-space and deal with empty or effectively
+ * empty lines -- comment lines are treated as effectively empty.
+ */
+ cp = string;
+
if (string == NULL)
return NULL;
-
- cp = string;
- /* Skip white spaces. */
- while (isspace ((int) *cp) && *cp != '\0')
+ while (isspace((int) *cp))
cp++;
- /* Return if there is only white spaces */
- if (*cp == '\0')
- return NULL;
+ ep = cp + strlen(cp) ;
- if (*cp == '!' || *cp == '#')
+ while ((ep > cp) && (isspace((int)*(ep - 1))))
+ --ep ;
+
+ if ((cp == ep) || (*cp == '!') || (*cp == '#'))
return NULL;
- /* Prepare return vector. */
- strvec = vector_init (VECTOR_MIN_SIZE);
+ /* Prepare return vector -- expect some reasonable number of tokens. */
+ if (vline == NULL)
+ vline = vector_init(10) ;
- /* Copy each command piece and set into vector. */
- while (1)
+ /* If writing the words to a qstring, copy the body of the original (less
+ * any leading/trailing whitespace) to the qstring and '\0' terminate.
+ */
+ if (qs != NULL)
{
- start = cp;
- while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
- *cp != '\0')
- cp++;
- strlen = cp - start;
- token = XMALLOC (MTYPE_STRVEC, strlen + 1);
- memcpy (token, start, strlen);
- *(token + strlen) = '\0';
- vector_set (strvec, token);
-
- while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
- *cp != '\0')
- cp++;
-
- if (*cp == '\0')
- return strvec;
+ qs_set_n(qs, cp, ep - cp) ;
+ tp = (char*)qs->body ; /* start at the beginning */
}
+ else
+ tp = NULL ; /* not used, but not undefined */
+
+ op = cp ; /* original start position */
+
+ /* Now have string cp..ep with no leading/trailing whitespace.
+ *
+ * If using a qstring, a copy of that exists at tp, complete with terminating
+ * '\0'. Writes '\0' terminators after each word found -- overwriting first
+ * separating white-space or the '\0' at the end.
+ *
+ * If not using a qstring, construct a new MTYPE_STRVEC for each word.
+ */
+ while (cp < ep)
+ {
+ while (isspace((int) *cp))
+ cp++ ; /* skip white-space */
+
+ sp = cp ;
+ while ((cp < ep) && !isspace((int) *cp))
+ cp++ ; /* eat token characters */
+
+ if (qs == NULL)
+ {
+ /* creating array of MTYPE_STRVEC */
+ size_t len ;
+
+ len = cp - sp ;
+ token = XMALLOC (MTYPE_STRVEC, len + 1);
+ memcpy (token, sp, len);
+ *(token + len) = '\0';
+ }
+ else
+ {
+ /* using qstring */
+ token = tp + (sp - op) ; /* token in qstring */
+ *(tp + (cp - op)) = '\0' ; /* terminate */
+ } ;
+
+ vector_push_item(vline, token);
+ } ;
+
+ return vline ;
}
-/* Free allocated string vector. */
-void
-cmd_free_strvec (vector v)
+/*------------------------------------------------------------------------------
+ * Take string and break it into tokens.
+ *
+ * Discards leading and trailing white-space.
+ *
+ * Treats lines that start with '!' or '#' (after any leading white-space)
+ * as empty -- these are comment lines.
+ *
+ * Tokens are non-whitespace separated by one or more white-space.
+ *
+ * White-space is anything that isspace() thinks is a space. (Which in the
+ * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ *
+ * Returns: NULL => empty line (after white-space trimming) or comment line.
+ * otherwise: is vector containing one or more tokens.
+ *
+ * Note: all the tokens in the vector have at least one character, and no
+ * entries are NULL.
+ *
+ * NB: it is the caller's responsibility to release the vector and its contents,
+ * see cmd_free_strvec().
+ */
+extern vector
+cmd_make_strvec (const char *string)
{
- unsigned int i;
- char *cp;
+ return cmd_make_vline(NULL, NULL, string) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add given string to vector of strings.
+ *
+ * Create vector if required.
+ */
+extern vector
+cmd_add_to_strvec (vector strvec, const char* str)
+{
+ if (strvec == NULL)
+ strvec = vector_init(1) ;
- if (!v)
- return;
+ vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str));
- for (i = 0; i < vector_active (v); i++)
- if ((cp = vector_slot (v, i)) != NULL)
- XFREE (MTYPE_STRVEC, cp);
+ return strvec ;
+} ;
- vector_free (v);
-}
+/*------------------------------------------------------------------------------
+ * Free allocated string vector (if any) and all its contents.
+ *
+ * Note that this is perfectly happy with strvec == NULL.
+ */
+extern void
+cmd_free_strvec (vector strvec)
+{
+ char *cp;
+
+ /* Note that vector_ream_free() returns NULL if strvec == NULL */
+ while((cp = vector_ream_free(strvec)) != NULL)
+ XFREE (MTYPE_STRVEC, cp);
+} ;
+
+/*----------------------------------------------------------------------------*/
/* Fetch next description. Used in cmd_make_descvec(). */
static char *
@@ -321,7 +465,7 @@ cmd_desc_str (const char **string)
const char *cp, *start;
char *token;
int strlen;
-
+
cp = *string;
if (cp == NULL)
@@ -370,7 +514,7 @@ cmd_make_descvec (const char *string, const char *descstr)
if (cp == NULL)
return NULL;
- allvec = vector_init (VECTOR_MIN_SIZE);
+ allvec = vector_init (0);
while (1)
{
@@ -396,7 +540,7 @@ cmd_make_descvec (const char *string, const char *descstr)
}
cp++;
}
-
+
while (isspace ((int) *cp) && *cp != '\0')
cp++;
@@ -406,7 +550,7 @@ cmd_make_descvec (const char *string, const char *descstr)
cp++;
}
- if (*cp == '\0')
+ if (*cp == '\0')
return allvec;
sp = cp;
@@ -428,14 +572,14 @@ cmd_make_descvec (const char *string, const char *descstr)
{
if (multiple == 1)
{
- strvec = vector_init (VECTOR_MIN_SIZE);
+ strvec = vector_init (0);
vector_set (allvec, strvec);
}
multiple++;
}
else
{
- strvec = vector_init (VECTOR_MIN_SIZE);
+ strvec = vector_init (0);
vector_set (allvec, strvec);
}
vector_set (strvec, desc);
@@ -449,23 +593,27 @@ cmd_cmdsize (vector strvec)
{
unsigned int i;
int size = 0;
- vector descvec;
- struct desc *desc;
- for (i = 0; i < vector_active (strvec); i++)
- if ((descvec = vector_slot (strvec, i)) != NULL)
+ for (i = 0; i < vector_length(strvec); i++)
{
- if ((vector_active (descvec)) == 1
- && (desc = vector_slot (descvec, 0)) != NULL)
- {
- if (desc->cmd == NULL || CMD_OPTION (desc->cmd))
- return size;
- else
- size++;
- }
- else
- size++;
- }
+ vector descvec;
+
+ descvec = vector_get_item (strvec, i) ;
+ if (descvec == NULL)
+ continue ;
+
+ if (vector_length(descvec) == 1)
+ {
+ struct desc *desc;
+
+ desc = vector_get_item(descvec, 0) ;
+ if (desc != NULL)
+ if (desc->cmd == NULL || CMD_OPTION (desc->cmd))
+ break ;
+ }
+ size++;
+ } ;
+
return size;
}
@@ -475,7 +623,19 @@ cmd_prompt (enum node_type node)
{
struct cmd_node *cnode;
- cnode = vector_slot (cmdvec, node);
+ assert(cmdvec != NULL) ;
+ assert(cmdvec->p_items != NULL) ;
+
+ cnode = NULL ;
+ if (node < cmdvec->limit)
+ cnode = vector_get_item (cmdvec, node);
+
+ if (cnode == NULL)
+ {
+ zlog_err("Could not find prompt for node %d for", node) ;
+ return NULL ;
+ } ;
+
return cnode->prompt;
}
@@ -484,14 +644,14 @@ void
install_element (enum node_type ntype, struct cmd_element *cmd)
{
struct cmd_node *cnode;
-
+
/* cmd_init hasn't been called */
if (!cmdvec)
return;
-
- cnode = vector_slot (cmdvec, ntype);
- if (cnode == NULL)
+ cnode = vector_get_item (cmdvec, ntype);
+
+ if (cnode == NULL)
{
fprintf (stderr, "Command node %d doesn't exist, please check it\n",
ntype);
@@ -512,7 +672,7 @@ static const unsigned char itoa64[] =
static void
to64(char *s, long v, int n)
{
- while (--n >= 0)
+ while (--n >= 0)
{
*s++ = itoa64[v&0x3f];
v >>= 6;
@@ -527,7 +687,7 @@ zencrypt (const char *passwd)
char *crypt (const char *, const char *);
gettimeofday(&tv,0);
-
+
to64(&salt[0], random(), 3);
to64(&salt[3], tv.tv_usec, 3);
salt[5] = '\0';
@@ -539,15 +699,18 @@ zencrypt (const char *passwd)
static int
config_write_host (struct vty *vty)
{
+ if (qpthreads_enabled)
+ vty_out (vty, "threaded%s", VTY_NEWLINE);
+
if (host.name)
vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
if (host.encrypt)
{
if (host.password_encrypt)
- vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
+ vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
if (host.enable_encrypt)
- vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
+ vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
}
else
{
@@ -557,57 +720,57 @@ config_write_host (struct vty *vty)
vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
}
- if (zlog_default->default_lvl != LOG_DEBUG)
+ if (zlog_get_default_lvl(NULL) != LOG_DEBUG)
{
vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
VTY_NEWLINE);
vty_out (vty, "log trap %s%s",
- zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
+ zlog_priority[zlog_get_default_lvl(NULL)], VTY_NEWLINE);
}
- if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
+ if (host.logfile && (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != ZLOG_DISABLED))
{
vty_out (vty, "log file %s", host.logfile);
- if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != zlog_get_default_lvl(NULL))
vty_out (vty, " %s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)]);
vty_out (vty, "%s", VTY_NEWLINE);
}
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != ZLOG_DISABLED)
{
vty_out (vty, "log stdout");
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != zlog_get_default_lvl(NULL))
vty_out (vty, " %s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]);
vty_out (vty, "%s", VTY_NEWLINE);
}
- if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED)
vty_out(vty,"no log monitor%s",VTY_NEWLINE);
- else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
+ else if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) != zlog_get_default_lvl(NULL))
vty_out(vty,"log monitor %s%s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)],VTY_NEWLINE);
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != ZLOG_DISABLED)
{
vty_out (vty, "log syslog");
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != zlog_get_default_lvl(NULL))
vty_out (vty, " %s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)]);
vty_out (vty, "%s", VTY_NEWLINE);
}
- if (zlog_default->facility != LOG_DAEMON)
+ if (zlog_get_facility(NULL) != LOG_DAEMON)
vty_out (vty, "log facility %s%s",
- facility_name(zlog_default->facility), VTY_NEWLINE);
+ facility_name(zlog_get_facility(NULL)), VTY_NEWLINE);
- if (zlog_default->record_priority == 1)
+ if (zlog_get_record_priority(NULL) == 1)
vty_out (vty, "log record-priority%s", VTY_NEWLINE);
- if (zlog_default->timestamp_precision > 0)
+ if (zlog_get_timestamp_precision(NULL) > 0)
vty_out (vty, "log timestamp precision %d%s",
- zlog_default->timestamp_precision, VTY_NEWLINE);
+ zlog_get_timestamp_precision(NULL), VTY_NEWLINE);
if (host.advanced)
vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
@@ -631,7 +794,7 @@ config_write_host (struct vty *vty)
static vector
cmd_node_vector (vector v, enum node_type ntype)
{
- struct cmd_node *cnode = vector_slot (v, ntype);
+ struct cmd_node *cnode = vector_get_item (v, ntype);
return cnode->cmd_vector;
}
@@ -683,21 +846,41 @@ cmd_filter_by_symbol (char *command, char *symbol)
}
#endif
+/*==============================================================================
+ * Match functions.
+ *
+ * Is the given string a, possibly incomplete, value of the required kind ?
+ */
+
/* Completion match types. */
-enum match_type
+enum match_type
{
- no_match,
+ no_match, /* nope */
extend_match,
+
ipv4_prefix_match,
ipv4_match,
ipv6_prefix_match,
ipv6_match,
range_match,
vararg_match,
- partly_match,
- exact_match
+
+ partly_match, /* OK as far as it went */
+ exact_match /* Syntactically complete */
};
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Address:
+ *
+ * 999.999.999.999 -- where no part may be > 255
+ *
+ * TODO: cmd_ipv4_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
static enum match_type
cmd_ipv4_match (const char *str)
{
@@ -755,6 +938,22 @@ cmd_ipv4_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Prefix:
+ *
+ * 999.999.999.999/99 -- where no part may be > 255,
+ * and prefix length may not be > 32
+ *
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
static enum match_type
cmd_ipv4_prefix_match (const char *str)
{
@@ -834,6 +1033,16 @@ cmd_ipv4_prefix_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address:
+ *
+ * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
+
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
#define STATE_START 1
@@ -866,7 +1075,7 @@ cmd_ipv6_match (const char *str)
* ::1.2.3.4
*/
ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
-
+
if (ret == 1)
return exact_match;
@@ -952,6 +1161,19 @@ cmd_ipv6_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Prefix:
+ *
+ * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
+ * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
static enum match_type
cmd_ipv6_prefix_match (const char *str)
{
@@ -1071,7 +1293,7 @@ cmd_ipv6_prefix_match (const char *str)
if (mask < 0 || mask > 128)
return no_match;
-
+
/* I don't know why mask < 13 makes command match partly.
Forgive me to make this comments. I Want to set static default route
because of lack of function to originate default in ospf6d; sorry
@@ -1085,6 +1307,14 @@ cmd_ipv6_prefix_match (const char *str)
#endif /* HAVE_IPV6 */
+/*------------------------------------------------------------------------------
+ * Is this a decimal number in the allowed range:
+ *
+ * Returns: 1 => OK -- *including* empty string
+ * 0 => not a valid number, or not in required range
+ * (or invalid range !!)
+ */
+
#define DECIMAL_STRLEN_MAX 10
static int
@@ -1132,340 +1362,496 @@ cmd_range_match (const char *range, const char *str)
return 1;
}
-/* Make completion match and return match type flag. */
+/*==============================================================================
+ * Command "filtering".
+ *
+ * The command parsing process starts with a (shallow) copy of the cmd_vector
+ * entry for the current "node".
+ *
+ * So cmd_v contains pointers to struct cmd_element values. When match fails,
+ * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
+ * down to just the entries that match.
+ *
+ * Each cmd_element has a vector "strvec", which contains an entry for each
+ * "token" position. That entry is a vector containing the possible values at
+ * that position.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Make completion match and return match type flag.
+ *
+ * Takes: command -- address of candidate token
+ * cmd_v -- vector of commands that is being reduced/filtered
+ * index -- index of token (position in line -- 0 == first)
+ *
+ * Returns: any of the enum match_type values:
+ *
+ * no_match => no match of any kind
+ *
+ * extend_match => saw an optional token
+ * ipv4_prefix_match )
+ * ipv4_match )
+ * ipv6_prefix_match ) saw full or partial match for this
+ * ipv6_match )
+ * range_match )
+ * vararg_match )
+ *
+ * partly_match => saw partial match for a keyword
+ * exact_match => saw exact match for a keyword
+ *
+ * Note that these return values are in ascending order of preference. So,
+ * if there are multiple possibilities at this position, will return the one
+ * furthest down this list.
+ */
static enum match_type
-cmd_filter_by_completion (char *command, vector v, unsigned int index)
+cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
{
unsigned int i;
- const char *str;
- struct cmd_element *cmd_element;
+ unsigned int k;
enum match_type match_type;
- vector descvec;
- struct desc *desc;
match_type = no_match;
- /* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
- {
- if (index >= vector_active (cmd_element->strvec))
- vector_slot (v, i) = NULL;
- else
- {
- unsigned int j;
- int matched = 0;
-
- descvec = vector_slot (cmd_element->strvec, index);
-
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
+ /* If command and cmd_element string does not match, remove from vector */
+ k = 0 ;
+ for (i = 0; i < vector_length (cmd_v); i++)
+ {
+ const char *str;
+ struct cmd_element *cmd_element;
+ vector descvec;
+ struct desc *desc;
+ unsigned int j;
+ int matched ;
+
+ cmd_element = vector_get_item(cmd_v, i) ;
+
+ /* Skip past cmd_v entries that have already been set NULL */
+ if (cmd_element == NULL)
+ continue ;
+
+ /* Discard cmd_v entry that has no token at the current position */
+ descvec = vector_get_item(cmd_element->strvec, index) ;
+ if (descvec == NULL)
+ continue ;
+
+ /* See if get any sort of match at current position */
+ matched = 0 ;
+ for (j = 0; j < vector_length (descvec); j++)
+ {
+ desc = vector_get_item(descvec, j) ;
+ if (desc == NULL)
+ continue ;
+
+ str = desc->cmd;
+
+ if (CMD_VARARG (str))
+ {
+ if (match_type < vararg_match)
+ match_type = vararg_match;
+ matched++;
+ }
+ else if (CMD_RANGE (str))
+ {
+ if (cmd_range_match (str, command))
+ {
+ if (match_type < range_match)
+ match_type = range_match;
- matched++;
- }
- }
+ matched++;
+ }
+ }
#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command))
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
+ else if (CMD_IPV6 (str))
+ {
+ if (cmd_ipv6_match (command))
+ {
+ if (match_type < ipv6_match)
+ match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command))
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
+ matched++;
+ }
+ }
+ else if (CMD_IPV6_PREFIX (str))
+ {
+ if (cmd_ipv6_prefix_match (command))
+ {
+ if (match_type < ipv6_prefix_match)
+ match_type = ipv6_prefix_match;
- matched++;
- }
- }
+ matched++;
+ }
+ }
#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command))
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
+ else if (CMD_IPV4 (str))
+ {
+ if (cmd_ipv4_match (command))
+ {
+ if (match_type < ipv4_match)
+ match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command))
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else
- /* Check is this point's argument optional ? */
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else if (strncmp (command, str, strlen (command)) == 0)
- {
- if (strcmp (command, str) == 0)
- match_type = exact_match;
- else
- {
- if (match_type < partly_match)
- match_type = partly_match;
- }
- matched++;
- }
- }
- if (!matched)
- vector_slot (v, i) = NULL;
- }
- }
- return match_type;
-}
+ matched++;
+ }
+ }
+ else if (CMD_IPV4_PREFIX (str))
+ {
+ if (cmd_ipv4_prefix_match (command))
+ {
+ if (match_type < ipv4_prefix_match)
+ match_type = ipv4_prefix_match;
+ matched++;
+ }
+ }
+ else if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ /* Check is this point's argument optional ? */
+ {
+ if (match_type < extend_match)
+ match_type = extend_match;
+ matched++;
+ }
+ else if (strncmp (command, str, strlen (command)) == 0)
+ {
+ if (strcmp (command, str) == 0)
+ match_type = exact_match;
+ else
+ {
+ if (match_type < partly_match)
+ match_type = partly_match;
+ }
+ matched++;
+ } ;
+ } ;
+
+ /* Keep cmd_v entry that has a match at this position */
+ if (matched)
+ vector_set_item(cmd_v, k++, cmd_element) ;
+ } ;
-/* Filter vector by command character with index. */
+ vector_set_length(cmd_v, k) ; /* discard what did not keep */
+
+ return match_type;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Filter vector by command character with index.
+ *
+ * This appears to be identical to cmd_filter_by_completion(), except that
+ * when matching keywords, requires an exact match.
+ *
+ * TODO: see if can merge cmd_filter_by_completion() & cmd_filter_by_string()
+ */
static enum match_type
-cmd_filter_by_string (char *command, vector v, unsigned int index)
+cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
{
- unsigned int i;
- const char *str;
- struct cmd_element *cmd_element;
+ unsigned int i ;
+ unsigned int k ;
enum match_type match_type;
- vector descvec;
- struct desc *desc;
match_type = no_match;
- /* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
- {
- /* If given index is bigger than max string vector of command,
- set NULL */
- if (index >= vector_active (cmd_element->strvec))
- vector_slot (v, i) = NULL;
- else
- {
- unsigned int j;
- int matched = 0;
+ /* If command and cmd_element string do match, keep in vector */
+ k = 0 ;
+ for (i = 0; i < vector_length(cmd_v); i++)
+ {
+ unsigned int j;
+ int matched ;
+ const char *str;
+ struct cmd_element *cmd_element;
+ vector descvec;
+ struct desc *desc;
+
+ cmd_element = vector_get_item(cmd_v, i) ;
+
+ /* Skip past NULL cmd_v entries (just in case) */
+ if (cmd_element == NULL)
+ continue ;
+
+ /* Discard cmd_v entry that has no token at the current position */
+ descvec = vector_get_item (cmd_element->strvec, index) ;
+ if (descvec == NULL)
+ continue ;
+
+ /* See if have a match against any of the current possibilities */
+ matched = 0 ;
+ for (j = 0; j < vector_length(descvec); j++)
+ {
+ desc = vector_get_item (descvec, j) ;
+ if (desc == NULL)
+ continue ;
+
+ str = desc->cmd;
+
+ if (CMD_VARARG (str))
+ {
+ if (match_type < vararg_match)
+ match_type = vararg_match;
+ matched++;
+ }
+ else if (CMD_RANGE (str))
+ {
+ if (cmd_range_match (str, command))
+ {
+ if (match_type < range_match)
+ match_type = range_match;
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6 (str))
+ {
+ if (cmd_ipv6_match (command) == exact_match)
+ {
+ if (match_type < ipv6_match)
+ match_type = ipv6_match;
+ matched++;
+ }
+ }
+ else if (CMD_IPV6_PREFIX (str))
+ {
+ if (cmd_ipv6_prefix_match (command) == exact_match)
+ {
+ if (match_type < ipv6_prefix_match)
+ match_type = ipv6_prefix_match;
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4 (str))
+ {
+ if (cmd_ipv4_match (command) == exact_match)
+ {
+ if (match_type < ipv4_match)
+ match_type = ipv4_match;
+ matched++;
+ }
+ }
+ else if (CMD_IPV4_PREFIX (str))
+ {
+ if (cmd_ipv4_prefix_match (command) == exact_match)
+ {
+ if (match_type < ipv4_prefix_match)
+ match_type = ipv4_prefix_match;
+ matched++;
+ }
+ }
+ else if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ {
+ if (match_type < extend_match)
+ match_type = extend_match;
+ matched++;
+ }
+ else
+ {
+ if (strcmp (command, str) == 0)
+ {
+ match_type = exact_match;
+ matched++;
+ } ;
+ } ;
+ } ;
- descvec = vector_slot (cmd_element->strvec, index);
+ /* Keep cmd_element if have a match */
+ if (matched)
+ vector_set_item(cmd_v, k++, cmd_element) ;
+ } ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
+ vector_set_length(cmd_v, k) ; /* discard what did not keep */
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) == exact_match)
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) == exact_match)
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) == exact_match)
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) == exact_match)
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- match_type = exact_match;
- matched++;
- }
- }
- }
- if (!matched)
- vector_slot (v, i) = NULL;
- }
- }
return match_type;
}
-/* Check ambiguous match */
+/*------------------------------------------------------------------------------
+ * Check for ambiguous match
+ *
+ * Given the best that cmd_filter_by_completion() or cmd_filter_by_string()
+ * found, check as follows:
+ *
+ * 1. discard all commands for which do not have the type of match selected.
+ *
+ * See above for the ranking of matches.
+ *
+ * 2. for "partial match", look out for matching more than one keyword, and
+ * return 1 if finds that.
+ *
+ * 3. for "range match", look out for matching more than one range, and
+ * return 1 if finds that.
+ *
+ * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
+ * return 2.
+ *
+ * This appears to catch things which are supposed to be prefixes, but
+ * do not have a '/' or do not have any digits after the '/'.
+ *
+ * Takes: command -- address of candidate token
+ * cmd_v -- vector of commands that is being reduced/filtered
+ * index -- index of token (position in line -- 0 == first)
+ * type -- as returned by cmd_filter_by_completion()
+ * or cmd_filter_by_string()
+ *
+ * Returns: 0 => not ambiguous
+ * 1 => ambiguous -- the candidate token matches more than one
+ * keyword, or the candidate number matches more
+ * than one number range.
+ * 2 => partial match for ipv4_prefix or ipv6_prefix
+ * (missing '/' or no digits after '/').
+ *
+ * NB: it is assumed that cannot find both 1 and 2 states. But in any case,
+ * returns 1 in preference.
+ */
static int
-is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
+is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
{
unsigned int i;
- unsigned int j;
- const char *str = NULL;
- struct cmd_element *cmd_element;
- const char *matched = NULL;
- vector descvec;
- struct desc *desc;
+ unsigned int k;
+ int ret ;
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
- {
- int match = 0;
+ ret = 0 ; /* all's well so far */
+ k = 0 ; /* nothing kept, yet */
+
+ for (i = 0; i < vector_length (cmd_v); i++)
+ {
+ unsigned int j;
+ struct cmd_element *cmd_element;
+ const char *str_matched ;
+ vector descvec;
+ struct desc *desc;
+ int matched ;
+ enum match_type mt ;
+
+ cmd_element = vector_get_item (cmd_v, i) ;
+
+ /* Skip past NULL cmd_v entries (just in case) */
+ if (cmd_element == NULL)
+ continue ;
+
+ /* The cmd_v entry MUST have a token at the current position */
+ descvec = vector_get_item (cmd_element->strvec, index) ;
+ assert(descvec != NULL) ;
+
+ /* See if have a match against any of the current possibilities
+ *
+ * str_matched is set the first time get a partial string match,
+ * or the first time get a number range match.
+ *
+ * If get a second partial string match or number range match, then
+ * unless
+ */
+ str_matched = NULL ;
+ matched = 0;
+ for (j = 0; j < vector_length (descvec); j++)
+ {
+ enum match_type ret;
+ const char *str ;
- descvec = vector_slot (cmd_element->strvec, index);
+ desc = vector_get_item (descvec, j) ;
+ if (desc == NULL)
+ continue ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- enum match_type ret;
-
- str = desc->cmd;
+ str = desc->cmd;
+
+ switch (type)
+ {
+ case exact_match:
+ if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
+ && strcmp (command, str) == 0)
+ matched++;
+ break;
+
+ case partly_match:
+ if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
+ && strncmp (command, str, strlen (command)) == 0)
+ {
+ if (str_matched && (strcmp (str_matched, str) != 0))
+ ret = 1; /* There is ambiguous match. */
+ else
+ str_matched = str;
+ matched++;
+ }
+ break;
+
+ case range_match:
+ if (cmd_range_match (str, command))
+ {
+ if (str_matched && strcmp (str_matched, str) != 0)
+ ret = 1;
+ else
+ str_matched = str;
+ matched++;
+ }
+ break;
- switch (type)
- {
- case exact_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strcmp (command, str) == 0)
- match++;
- break;
- case partly_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strncmp (command, str, strlen (command)) == 0)
- {
- if (matched && strcmp (matched, str) != 0)
- return 1; /* There is ambiguous match. */
- else
- matched = str;
- match++;
- }
- break;
- case range_match:
- if (cmd_range_match (str, command))
- {
- if (matched && strcmp (matched, str) != 0)
- return 1;
- else
- matched = str;
- match++;
- }
- break;
#ifdef HAVE_IPV6
- case ipv6_match:
- if (CMD_IPV6 (str))
- match++;
- break;
- case ipv6_prefix_match:
- if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
- {
- if (ret == partly_match)
- return 2; /* There is incomplete match. */
+ case ipv6_match:
+ if (CMD_IPV6 (str))
+ matched++;
+ break;
- match++;
- }
- break;
+ case ipv6_prefix_match:
+ if ((mt = cmd_ipv6_prefix_match (command)) != no_match)
+ {
+ if (mt == partly_match)
+ if (ret != 1)
+ ret = 2; /* There is incomplete match. */
+
+ matched++;
+ }
+ break;
#endif /* HAVE_IPV6 */
- case ipv4_match:
- if (CMD_IPV4 (str))
- match++;
- break;
- case ipv4_prefix_match:
- if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
- {
- if (ret == partly_match)
- return 2; /* There is incomplete match. */
- match++;
- }
- break;
- case extend_match:
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- match++;
- break;
- case no_match:
- default:
- break;
- }
- }
- if (!match)
- vector_slot (v, i) = NULL;
- }
- return 0;
-}
+ case ipv4_match:
+ if (CMD_IPV4 (str))
+ matched++;
+ break;
-/* If src matches dst return dst string, otherwise return NULL */
+ case ipv4_prefix_match:
+ if ((mt = cmd_ipv4_prefix_match (command)) != no_match)
+ {
+ if (mt == partly_match)
+ if (ret != 1)
+ ret = 2; /* There is incomplete match. */
+
+ matched++;
+ }
+ break;
+
+ case extend_match:
+ if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ matched++;
+ break;
+
+ case no_match:
+ default:
+ break;
+ } ;
+ } ;
+
+ /* Keep cmd_element if have a match */
+ if (matched)
+ vector_set_item(cmd_v, k++, cmd_element) ;
+ } ;
+
+ vector_set_length(cmd_v, k) ; /* discard what did not keep */
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If src matches dst return dst string, otherwise return NULL
+ *
+ * Returns NULL if dst is an option, variable of vararg.
+ *
+ * NULL or empty src are deemed to match.
+ */
static const char *
cmd_entry_function (const char *src, const char *dst)
{
- /* Skip variable arguments. */
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) ||
- CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst))
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
return NULL;
- /* In case of 'command \t', given src is NULL string. */
- if (src == NULL)
+ if ((src == NULL) || (*src == '\0'))
return dst;
- /* Matched with input string. */
if (strncmp (src, dst, strlen (src)) == 0)
return dst;
@@ -1537,16 +1923,20 @@ cmd_entry_function_desc (const char *src, const char *dst)
return NULL;
}
-/* Check same string element existence. If it isn't there return
- 1. */
-static int
+/*------------------------------------------------------------------------------
+ * Check same string element existence.
+ *
+ * Returns: 0 => found same string in the vector
+ * 1 => NOT found same string in the vector
+ */
+static bool
cmd_unique_string (vector v, const char *str)
{
unsigned int i;
char *match;
- for (i = 0; i < vector_active (v); i++)
- if ((match = vector_slot (v, i)) != NULL)
+ for (i = 0; i < vector_length (v); i++)
+ if ((match = vector_get_item (v, i)) != NULL)
if (strcmp (match, str) == 0)
return 0;
return 1;
@@ -1554,35 +1944,29 @@ cmd_unique_string (vector v, const char *str)
/* Compare string to description vector. If there is same string
return 1 else return 0. */
-static int
+static bool
desc_unique_string (vector v, const char *str)
{
unsigned int i;
struct desc *desc;
- for (i = 0; i < vector_active (v); i++)
- if ((desc = vector_slot (v, i)) != NULL)
+ for (i = 0; i < vector_length (v); i++)
+ if ((desc = vector_get_item (v, i)) != NULL)
if (strcmp (desc->cmd, str) == 0)
return 1;
return 0;
}
-static int
+static bool
cmd_try_do_shortcut (enum node_type node, char* first_word) {
- if ( first_word != NULL &&
- node != AUTH_NODE &&
- node != VIEW_NODE &&
- node != AUTH_ENABLE_NODE &&
- node != ENABLE_NODE &&
- node != RESTRICTED_NODE &&
- 0 == strcmp( "do", first_word ) )
- return 1;
- return 0;
+ return (node >= MIN_DO_SHORTCUT_NODE)
+ && (first_word != NULL)
+ && (strcmp( "do", first_word) == 0) ? 1 : 0 ;
}
/* '?' describe command support. */
static vector
-cmd_describe_command_real (vector vline, struct vty *vty, int *status)
+cmd_describe_command_real (vector vline, int node, int *status)
{
unsigned int i;
vector cmd_vector;
@@ -1595,16 +1979,16 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
char *command;
/* Set index. */
- if (vector_active (vline) == 0)
+ if (vector_length (vline) == 0)
{
*status = CMD_ERR_NO_MATCH;
return NULL;
}
else
- index = vector_active (vline) - 1;
-
+ index = vector_length (vline) - 1;
+
/* Make copy vector of current node's command vector. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
/* Prepare match vector */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
@@ -1612,29 +1996,29 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
/* Filter commands. */
/* Only words precedes current word will be checked in this loop. */
for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
+ if ((command = vector_get_item (vline, i)))
{
match = cmd_filter_by_completion (command, cmd_vector, i);
-
+
if (match == vararg_match)
{
struct cmd_element *cmd_element;
vector descvec;
unsigned int j, k;
- for (j = 0; j < vector_active (cmd_vector); j++)
- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL
- && (vector_active (cmd_element->strvec)))
+ for (j = 0; j < vector_length (cmd_vector); j++)
+ if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL
+ && (vector_length (cmd_element->strvec)))
{
- descvec = vector_slot (cmd_element->strvec,
- vector_active (cmd_element->strvec) - 1);
- for (k = 0; k < vector_active (descvec); k++)
+ descvec = vector_get_item (cmd_element->strvec,
+ vector_length (cmd_element->strvec) - 1);
+ for (k = 0; k < vector_length (descvec); k++)
{
- struct desc *desc = vector_slot (descvec, k);
+ struct desc *desc = vector_get_item (descvec, k);
vector_set (matchvec, desc);
}
}
-
+
vector_set (matchvec, &desc_cr);
vector_free (cmd_vector);
@@ -1660,53 +2044,58 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
/* Prepare match vector */
/* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
- /* Make sure that cmd_vector is filtered based on current word */
- command = vector_slot (vline, index);
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_get_item (vline, index);
if (command)
match = cmd_filter_by_completion (command, cmd_vector, index);
/* Make description vector. */
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
- {
- vector strvec = cmd_element->strvec;
+ for (i = 0; i < vector_length (cmd_vector); i++)
+ {
+ vector strvec ;
+
+ cmd_element = vector_get_item (cmd_vector, i) ;
+ if (cmd_element == NULL)
+ continue ;
+
+ /* Ignore cmd_element if no tokens at index position.
+ *
+ * Deal with special case of possible <cr> completion.
+ */
+ strvec = cmd_element->strvec;
+ if (index >= vector_length (strvec))
+ {
+ if (command == NULL && index == vector_length (strvec))
+ {
+ if (!desc_unique_string (matchvec, command_cr))
+ vector_push_item(matchvec, &desc_cr);
+ }
+ continue ;
+ } ;
+
+ /* Check if command is completed. */
+ unsigned int j;
+ vector descvec = vector_get_item (strvec, index);
+ struct desc *desc;
+
+ for (j = 0; j < vector_length (descvec); j++)
+ if ((desc = vector_get_item (descvec, j)))
+ {
+ const char *string;
+
+ string = cmd_entry_function_desc (command, desc->cmd);
+ if (string)
+ {
+ /* Uniqueness check */
+ if (!desc_unique_string (matchvec, string))
+ vector_push_item(matchvec, desc);
+ }
+ } ;
+ } ;
- /* if command is NULL, index may be equal to vector_active */
- if (command && index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- /* Check if command is completed. */
- if (command == NULL && index == vector_active (strvec))
- {
- if (!desc_unique_string (matchvec, command_cr))
- vector_set (matchvec, &desc_cr);
- }
- else
- {
- unsigned int j;
- vector descvec = vector_slot (strvec, index);
- struct desc *desc;
-
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- const char *string;
-
- string = cmd_entry_function_desc (command, desc->cmd);
- if (string)
- {
- /* Uniqueness check */
- if (!desc_unique_string (matchvec, string))
- vector_set (matchvec, desc);
- }
- }
- }
- }
- }
vector_free (cmd_vector);
- if (vector_slot (matchvec, 0) == NULL)
+ if (vector_length(matchvec) == 0)
{
vector_free (matchvec);
*status = CMD_ERR_NO_MATCH;
@@ -1717,269 +2106,285 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
return matchvec;
}
+/*------------------------------------------------------------------------------
+ * Get description of current (partial) command
+ *
+ * Returns: NULL => no description available
+ *
+ * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
+ *
+ * or: address of vector of "struct desc" values available.
+ *
+ * NB: when a vector is returned it is the caller's responsibility to
+ * vector_free() it. (The contents are all effectively const, so do not
+ * themselves need to be freed.)
+ */
vector
-cmd_describe_command (vector vline, struct vty *vty, int *status)
+cmd_describe_command (vector vline, int node, int *status)
{
vector ret;
- if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
{
- enum node_type onode;
- vector shifted_vline;
+ vector shifted_vline;
unsigned int index;
- onode = vty->node;
- vty->node = ENABLE_NODE;
/* We can try it on enable node, cos' the vty is authenticated */
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_length (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
- ret = cmd_describe_command_real (shifted_vline, vty, status);
+ ret = cmd_describe_command_real (shifted_vline, ENABLE_NODE, status);
vector_free(shifted_vline);
- vty->node = onode;
return ret;
}
-
- return cmd_describe_command_real (vline, vty, status);
+ return cmd_describe_command_real (vline, node, status);
}
-
-/* Check LCD of matched command. */
+/*------------------------------------------------------------------------------
+ * Check LCD of matched command.
+ *
+ * Scan list of matched keywords, and by comparing them pair-wise, find the
+ * longest common leading substring.
+ *
+ * Returns: 0 if zero or one matched keywords
+ * length of longest common leading substring, otherwise.
+ */
static int
-cmd_lcd (char **matched)
+cmd_lcd (vector matchvec)
{
- int i;
- int j;
- int lcd = -1;
- char *s1, *s2;
- char c1, c2;
+ int n ;
+ int i ;
+ int lcd ;
+ char *sp, *sq, *ss ;
- if (matched[0] == NULL || matched[1] == NULL)
- return 0;
+ n = vector_end(matchvec) ;
+ if (n < 2)
+ return 0 ;
+
+ ss = vector_get_item(matchvec, 0) ;
+ lcd = strlen(ss) ;
- for (i = 1; matched[i] != NULL; i++)
+ for (i = 1 ; i < n ; i++)
{
- s1 = matched[i - 1];
- s2 = matched[i];
+ sq = ss ;
+ ss = vector_get_item(matchvec, i) ;
+ sp = ss ;
- for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
- if (c1 != c2)
- break;
+ while ((*sp == *sq) && (*sp != '\0'))
+ {
+ ++sp ;
+ ++sq ;
+ } ;
- if (lcd < 0)
- lcd = j;
- else
- {
- if (lcd > j)
- lcd = j;
- }
+ if (lcd > (sp - ss))
+ lcd = (sp - ss) ;
}
return lcd;
}
-/* Command line completion support. */
-static char **
-cmd_complete_command_real (vector vline, struct vty *vty, int *status)
+/*------------------------------------------------------------------------------
+ * Command line completion support.
+ */
+static vector
+cmd_complete_command_real (vector vline, int node, int *status)
{
unsigned int i;
- vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ unsigned int ivl ;
+ unsigned int last_ivl ;
+ vector cmd_v ;
#define INIT_MATCHVEC_SIZE 10
vector matchvec;
struct cmd_element *cmd_element;
unsigned int index;
- char **match_str;
struct desc *desc;
vector descvec;
- char *command;
- int lcd;
+ char *token;
+ int n ;
- if (vector_active (vline) == 0)
+ /* Stop immediately if the vline is empty. */
+ if (vector_length (vline) == 0)
{
- vector_free (cmd_vector);
*status = CMD_ERR_NO_MATCH;
return NULL;
}
- else
- index = vector_active (vline) - 1;
- /* First, filter by preceeding command string */
- for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
- {
- enum match_type match;
- int ret;
+ /* Take (shallow) copy of cmdvec for given node. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
- /* First try completion match, if there is exactly match return 1 */
- match = cmd_filter_by_completion (command, cmd_vector, i);
+ /* First, filter upto, but excluding last token */
+ last_ivl = vector_length (vline) - 1;
- /* If there is exact match then filter ambiguous match else check
- ambiguousness. */
- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1)
- {
- vector_free (cmd_vector);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
- /*
- else if (ret == 2)
- {
- vector_free (cmd_vector);
+ for (ivl = 0; ivl < last_ivl; ivl++)
+ {
+ enum match_type match;
+ int ret;
+
+ /* TODO: does this test make any sense ? */
+ if ((token = vector_get_item (vline, ivl)) == NULL)
+ continue ;
+
+ /* First try completion match, return best kind of match */
+ index = ivl ;
+ match = cmd_filter_by_completion (token, cmd_v, index) ;
+
+ /* Eliminate all but the selected kind of match */
+ ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
+
+ if (ret == 1)
+ {
+ /* ret == 1 => either token matches more than one keyword
+ * or token matches more than one number range
+ */
+ vector_free (cmd_v);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+#if 0
+ /* For command completion purposes do not appear to care about
+ * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
+ */
+ else if (ret == 2)
+ {
+ vector_free (cmd_v);
*status = CMD_ERR_NO_MATCH;
return NULL;
- }
- */
+ }
+#endif
}
-
- /* Prepare match vector. */
+
+ /* Prepare match vector. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
- /* Now we got into completion */
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)))
- {
- const char *string;
- vector strvec = cmd_element->strvec;
+ /* Now we got into completion */
+ index = last_ivl ;
+ token = vector_get_item(vline, last_ivl) ; /* is now the last token */
- /* Check field length */
- if (index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- unsigned int j;
+ for (i = 0; i < vector_length (cmd_v); i++)
+ {
+ unsigned int j;
+ const char *string;
- descvec = vector_slot (strvec, index);
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- if ((string =
- cmd_entry_function (vector_slot (vline, index),
- desc->cmd)))
- if (cmd_unique_string (matchvec, string))
- vector_set (matchvec, XSTRDUP (MTYPE_TMP, string));
- }
- }
- }
+ if ((cmd_element = vector_get_item (cmd_v, i)) == NULL)
+ continue ;
- /* We don't need cmd_vector any more. */
- vector_free (cmd_vector);
+ descvec = vector_get_item (cmd_element->strvec, index);
+ if (descvec == NULL)
+ continue ;
+
+ for (j = 0; j < vector_length (descvec); j++)
+ {
+ desc = vector_get_item (descvec, j) ;
+ if (desc == NULL)
+ continue ;
+
+ string = cmd_entry_function(token, desc->cmd) ;
+ if ((string != NULL) && cmd_unique_string(matchvec, string))
+ cmd_add_to_strvec (matchvec, string) ;
+ } ;
+ } ;
+
+ n = vector_length(matchvec) ; /* number of entries in the matchvec */
+
+ /* We don't need cmd_v any more. */
+ vector_free (cmd_v);
/* No matched command */
- if (vector_slot (matchvec, 0) == NULL)
+ if (n == 0)
{
vector_free (matchvec);
/* In case of 'command \t' pattern. Do you need '?' command at
the end of the line. */
- if (vector_slot (vline, index) == '\0')
- *status = CMD_ERR_NOTHING_TODO;
+ if (*token == '\0')
+ *status = CMD_COMPLETE_ALREADY;
else
*status = CMD_ERR_NO_MATCH;
return NULL;
}
- /* Only one matched */
- if (vector_slot (matchvec, 1) == NULL)
+ /* Only one matched */
+ if (n == 1)
{
- match_str = (char **) matchvec->index;
- vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_FULL_MATCH;
- return match_str;
+ return matchvec ;
}
- /* Make it sure last element is NULL. */
- vector_set (matchvec, NULL);
- /* Check LCD of matched strings. */
- if (vector_slot (vline, index) != NULL)
+ /* Check LCD of matched strings. */
+ if (token != NULL)
{
- lcd = cmd_lcd ((char **) matchvec->index);
+ unsigned lcd = cmd_lcd (matchvec) ;
- if (lcd)
+ if (lcd != 0)
{
- int len = strlen (vector_slot (vline, index));
-
- if (len < lcd)
+ if (strlen(token) < lcd)
{
char *lcdstr;
lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
- memcpy (lcdstr, matchvec->index[0], lcd);
+ memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
lcdstr[lcd] = '\0';
- /* match_str = (char **) &lcdstr; */
+ cmd_free_strvec(matchvec) ; /* discard the match vector */
- /* Free matchvec. */
- for (i = 0; i < vector_active (matchvec); i++)
- {
- if (vector_slot (matchvec, i))
- XFREE (MTYPE_STRVEC, vector_slot (matchvec, i));
- }
- vector_free (matchvec);
-
- /* Make new matchvec. */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
- vector_set (matchvec, lcdstr);
- match_str = (char **) matchvec->index;
- vector_only_wrapper_free (matchvec);
+ matchvec = vector_init (1);
+ vector_push_item(matchvec, lcdstr) ;
*status = CMD_COMPLETE_MATCH;
- return match_str;
+ return matchvec ;
}
}
}
- match_str = (char **) matchvec->index;
- vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_LIST_MATCH;
- return match_str;
+ return matchvec ;
}
-char **
-cmd_complete_command (vector vline, struct vty *vty, int *status)
+/*------------------------------------------------------------------------------
+ * Can the current command be completed ?
+ */
+extern vector
+cmd_complete_command (vector vline, int node, int *status)
{
- char **ret;
+ vector ret;
- if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
{
- enum node_type onode;
vector shifted_vline;
unsigned int index;
- onode = vty->node;
- vty->node = ENABLE_NODE;
/* We can try it on enable node, cos' the vty is authenticated */
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_length (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
- ret = cmd_complete_command_real (shifted_vline, vty, status);
+ ret = cmd_complete_command_real (shifted_vline, ENABLE_NODE, status);
vector_free(shifted_vline);
- vty->node = onode;
return ret;
}
-
- return cmd_complete_command_real (vline, vty, status);
+ return cmd_complete_command_real (vline, node, status);
}
-/* return parent node */
-/* MUST eventually converge on CONFIG_NODE */
+/*------------------------------------------------------------------------------
+ * Return parent node
+ *
+ * All nodes > CONFIG_NODE are descended from CONFIG_NODE
+ */
enum node_type
node_parent ( enum node_type node )
{
- enum node_type ret;
-
assert (node > CONFIG_NODE);
switch (node)
@@ -1989,493 +2394,585 @@ node_parent ( enum node_type node )
case BGP_IPV4M_NODE:
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
- ret = BGP_NODE;
- break;
+ return BGP_NODE;
+
case KEYCHAIN_KEY_NODE:
- ret = KEYCHAIN_NODE;
- break;
+ return KEYCHAIN_NODE;
+
default:
- ret = CONFIG_NODE;
+ return CONFIG_NODE;
}
-
- return ret;
}
-/* Execute command by argument vline vector. */
-static int
-cmd_execute_command_real (vector vline, struct vty *vty,
- struct cmd_element **cmd)
+/*------------------------------------------------------------------------------
+ * Initialise a new struct cmd_parsed, allocating if required
+ */
+extern cmd_parsed
+cmd_parse_init_new(cmd_parsed parsed)
{
- unsigned int i;
- unsigned int index;
- vector cmd_vector;
- struct cmd_element *cmd_element;
- struct cmd_element *matched_element;
- unsigned int matched_count, incomplete_count;
- int argc;
- const char *argv[CMD_ARGC_MAX];
- enum match_type match = 0;
- int varflag;
- char *command;
+ if (parsed == NULL)
+ parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
+ else
+ memset(parsed, 0, sizeof(*parsed)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * cmd = NULL -- no command parsed, yet
+ * cnode -- no node set, yet
+ *
+ * do_shortcut -- false
+ * onode -- not material (do_shortcut is false)
+ *
+ * line = zeroised qstring -- empty
+ * words = zeroised qstring -- empty
+ *
+ * vline = zeroised vector -- empty
+ *
+ * so nothing else to do
+ */
- /* Make copy of command elements. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ return parsed ;
+} ;
- for (index = 0; index < vector_active (vline); index++)
- if ((command = vector_slot (vline, index)))
- {
- int ret;
+/*------------------------------------------------------------------------------
+ * Initialise a new struct cmd_parsed, allocating if required
+ */
+extern cmd_parsed
+cmd_parse_reset(cmd_parsed parsed, bool free_structure)
+{
+ if (parsed != NULL)
+ {
+ qs_reset_keep(&parsed->words) ;
+ vector_reset_keep(&parsed->vline) ;
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ if (free_structure)
+ XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ else
+ cmd_parse_init_new(parsed) ;
+ } ;
+
+ return parsed ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", if possible, ready for execution.
+ *
+ * If 'strict': use cmd_filter_by_string()
+ * otherwise: use cmd_filter_by_completion()
+ *
+ * If 'do': see if there is a 'do' at the front and proceed accordingly.
+ *
+ * If 'tree': move up the node tree to find command if not found in the
+ * current node.
+ */
+
+static enum cmd_return_code
+cmd_parse_this(struct cmd_parsed* parsed, bool strict) ;
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", or (if required) any of its ancestors.
+ *
+ * Returns: CMD_SUCCESS => successfully parsed command, and the result is
+ * in the given parsed structure, ready for execution.
+ *
+ * NB: parsed->cnode may have changed.
+ *
+ * NB: parsed->cmd->daemon => daemon
+ *
+ * CMD_EMPTY => empty or comment line
+ *
+ * NB: parsed->cmd == NULL
+ *
+ * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ??
+ *
+ * CMD_ERR_NO_MATCH )
+ * CMD_ERR_AMBIGUOUS ) failed to parse
+ * CMD_ERR_INCOMPLETE )
+ *
+ * NB: if has failed to parse in the current node
+ * and in any ancestor nodes, returns the error
+ * from the attempt to parse in the current node
+ * (parsed->cnode which is returned unchanged).
+ *
+ * NB: the command line MUST be preserved until the parsed command is no
+ * longer required -- no copy is made.
+ *
+ * NB: expects to have free run of everything in the vty structure (except
+ * the contents of the vty_io sub-structure) until the command completes.
+ *
+ * See elsewhere for description of parsed structure.
+ */
+extern enum cmd_return_code
+cmd_parse_command(struct vty* vty, enum cmd_parse_type type)
+{
+ enum cmd_return_code ret ;
+ enum cmd_return_code first_ret ;
+ cmd_parsed parsed ;
- if (match == vararg_match)
- break;
-
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
+ /* Initialise the parsed structure -- assuming no 'do' */
+ if (vty->parsed == NULL)
+ vty->parsed = cmd_parse_init_new(NULL) ;
+ parsed = vty->parsed ;
- if (ret == 1)
- {
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- return CMD_ERR_NO_MATCH;
- }
- }
+ parsed->onode = parsed->cnode = vty->node ;
- /* Check matched count. */
- matched_element = NULL;
- matched_count = 0;
- incomplete_count = 0;
+ parsed->cmd = NULL ;
+ parsed->do_shortcut = 0 ;
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)))
- {
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
-#if 0
- printf ("DEBUG: %s\n", cmd_element->string);
-#endif
- matched_count++;
- }
- else
- {
- incomplete_count++;
- }
- }
+ /* Parse the line into words -- set up parsed->words and parsed->vline */
+ cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ;
- /* Finish of using cmd_vector. */
- vector_free (cmd_vector);
+ if (vector_length(&parsed->vline) == 0)
+ return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
- /* To execute command, matched_count must be 1. */
- if (matched_count == 0)
+ /* If allowed to 'do', see if there.
+ *
+ * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
+ */
+ if ((type & cmd_parse_do) &&
+ cmd_try_do_shortcut(parsed->cnode, vector_get_item(&parsed->vline, 0)))
{
- if (incomplete_count)
- return CMD_ERR_INCOMPLETE;
- else
- return CMD_ERR_NO_MATCH;
- }
+ parsed->cnode = ENABLE_NODE ;
+ parsed->do_shortcut = 1 ;
+ } ;
- if (matched_count > 1)
- return CMD_ERR_AMBIGUOUS;
+ /* Try in the current node */
+ ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ;
- /* Argument treatment */
- varflag = 0;
- argc = 0;
-
- for (i = 0; i < vector_active (vline); i++)
+ if (ret != CMD_SUCCESS)
{
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
- else
- {
- vector descvec = vector_slot (matched_element->strvec, i);
+ if (((type & cmd_parse_tree) == 0) || parsed->do_shortcut)
+ return ret ; /* done if not allowed to walk tree
+ or just tried to parse a 'do' */
- if (vector_active (descvec) == 1)
- {
- struct desc *desc = vector_slot (descvec, 0);
+ /* Try in parent node(s) */
+ first_ret = ret ;
- if (CMD_VARARG (desc->cmd))
- varflag = 1;
-
- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
- argv[argc++] = vector_slot (vline, i);
- }
- else
- argv[argc++] = vector_slot (vline, i);
- }
+ while (ret != CMD_SUCCESS)
+ {
+ if (parsed->cnode <= CONFIG_NODE)
+ {
+ parsed->cnode = parsed->onode ; /* restore node state */
+ return first_ret ; /* return original result */
+ } ;
+
+ parsed->cnode = node_parent(parsed->cnode) ;
+ ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ;
+ } ;
+ } ;
+
+ return vty->parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
+ : CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Work function for cmd_parse_command
+ *
+ * Takes a parsed structure, with the:
+ *
+ * cnode -- node to parse in
+ * vline -- the line broken into words
+ * do_shortcut -- true if first word is 'do' (to be ignored)
+ *
+ * and parses either strictly or with command completion.
+ *
+ * If successful, reduces the vline structure down to the variable portions,
+ * ie to the argv[] for the command function.
+ *
+ * Returns: CMD_SUCCESS -- parsed successfully
+ * CMD_ERR_NO_MATCH )
+ * CMD_ERR_AMBIGUOUS ) failed to parse
+ * CMD_ERR_INCOMPLETE )
+ */
+static enum cmd_return_code
+cmd_parse_this(cmd_parsed parsed, bool strict)
+{
+ unsigned int i ;
+ unsigned int ivl ;
+ unsigned index ;
+ unsigned first ;
+ unsigned argc ;
+ vector cmd_v;
+ struct cmd_element *cmd_element;
+ struct cmd_element *matched_element;
+ unsigned int matched_count, incomplete_count;
+ enum match_type match = 0;
+ int varflag;
+ char *command;
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
- }
+ /* Need length of vline, discounting the first entry if required */
+ first = parsed->do_shortcut ? 1 : 0 ;
- /* For vtysh execution. */
- if (cmd)
- *cmd = matched_element;
+ assert(vector_length(&parsed->vline) >= first) ;
+ ivl = vector_length(&parsed->vline) - first ;
- if (matched_element->daemon)
- return CMD_SUCCESS_DAEMON;
+ /* Make copy of command elements. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
- /* Execute matched command. */
- return (*matched_element->func) (matched_element, vty, argc, argv);
-}
+ /* Look for an unambiguous result */
+ for (index = 0 ; index < ivl; index++)
+ {
+ int ret ;
-int
-cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
- int vtysh) {
- int ret, saved_ret, tried = 0;
- enum node_type onode, try_node;
+ command = vector_get_item(&parsed->vline, index + first) ;
+ if (command == NULL)
+ continue ;
- onode = try_node = vty->node;
+ match = strict ? cmd_filter_by_string(command, cmd_v, index)
+ : cmd_filter_by_completion(command, cmd_v, index) ;
- if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
- {
- vector shifted_vline;
- unsigned int index;
+ if (match == vararg_match)
+ break;
- vty->node = ENABLE_NODE;
- /* We can try it on enable node, cos' the vty is authenticated */
+ ret = is_cmd_ambiguous (command, cmd_v, index, match);
- shifted_vline = vector_init (vector_count(vline));
- /* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
- {
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
+ if (ret != 0)
+ {
+ assert((ret == 1) || (ret == 2)) ;
+ vector_free (cmd_v);
+ return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ;
}
+ } ;
- ret = cmd_execute_command_real (shifted_vline, vty, cmd);
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
+ incomplete_count = 0;
- vector_free(shifted_vline);
- vty->node = onode;
- return ret;
- }
+ for (i = 0; i < vector_length(cmd_v); i++)
+ {
+ cmd_element = vector_get_item(cmd_v, i) ;
+ if (cmd_element == NULL)
+ continue ;
+ if (match == vararg_match || index >= cmd_element->cmdsize)
+ {
+ matched_element = cmd_element;
+#if 0
+ printf ("DEBUG: %s\n", cmd_element->string);
+#endif
+ matched_count++;
+ }
+ else
+ {
+ incomplete_count++;
+ }
+ } ;
- saved_ret = ret = cmd_execute_command_real (vline, vty, cmd);
+ /* Finished with cmd_v. */
+ vector_free (cmd_v);
- if (vtysh)
- return saved_ret;
+ /* To execute command, matched_count must be 1. */
+ if (matched_count != 1)
+ {
+ if (matched_count == 0)
+ return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ;
+ else
+ return CMD_ERR_AMBIGUOUS ;
+ } ;
+
+ /* Found command -- process the arguments ready for execution */
+ varflag = 0 ;
+ argc = 0 ;
- /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
- while ( ret != CMD_SUCCESS && ret != CMD_WARNING
- && vty->node > CONFIG_NODE )
+ for (index = 0; index < ivl ; index++)
{
- try_node = node_parent(try_node);
- vty->node = try_node;
- ret = cmd_execute_command_real (vline, vty, cmd);
- tried = 1;
- if (ret == CMD_SUCCESS || ret == CMD_WARNING)
+ int take = varflag ;
+
+ if (!varflag)
{
- /* succesfull command, leave the node as is */
- return ret;
+ vector descvec = vector_get_item (matched_element->strvec, index);
+
+ if (vector_length (descvec) == 1)
+ {
+ struct desc *desc = vector_get_item (descvec, 0);
+
+ if (CMD_VARARG (desc->cmd))
+ take = varflag = 1 ;
+ else
+ take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
+ }
+ else
+ take = 1 ;
}
- }
- /* no command succeeded, reset the vty to the original node and
- return the error for this node */
- if ( tried )
- vty->node = onode;
- return saved_ret;
-}
-/* Execute command by argument readline. */
-int
-cmd_execute_command_strict (vector vline, struct vty *vty,
- struct cmd_element **cmd)
-{
- unsigned int i;
- unsigned int index;
- vector cmd_vector;
- struct cmd_element *cmd_element;
- struct cmd_element *matched_element;
- unsigned int matched_count, incomplete_count;
- int argc;
- const char *argv[CMD_ARGC_MAX];
- int varflag;
- enum match_type match = 0;
- char *command;
+ if (take)
+ vector_assign_item(&parsed->vline, argc++, index + first) ;
+ } ;
- /* Make copy of command element */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ vector_set_length(&parsed->vline, argc) ; /* set to new length */
- for (index = 0; index < vector_active (vline); index++)
- if ((command = vector_slot (vline, index)))
- {
- int ret;
-
- match = cmd_filter_by_string (vector_slot (vline, index),
- cmd_vector, index);
+ /* Everything checks out... ready to execute command */
+ parsed->cmd = matched_element ;
- /* If command meets '.VARARG' then finish matching. */
- if (match == vararg_match)
- break;
-
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
- if (ret == 1)
- {
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- if (ret == 2)
- {
- vector_free (cmd_vector);
- return CMD_ERR_NO_MATCH;
- }
- }
+ return CMD_SUCCESS ;
+} ;
- /* Check matched count. */
- matched_element = NULL;
- matched_count = 0;
- incomplete_count = 0;
- for (i = 0; i < vector_active (cmd_vector); i++)
- if (vector_slot (cmd_vector, i) != NULL)
- {
- cmd_element = vector_slot (cmd_vector, i);
+/*------------------------------------------------------------------------------
+ * Dispatch a parsed command.
+ *
+ * Returns: command return code. NB: may be CMD_QUEUED (unless no_queue).
+ *
+ * NB: expects to have free run of everything in the vty structure (except
+ * the contents of the vty_io sub-structure) until the command completes.
+ */
+extern enum cmd_return_code
+cmd_dispatch(struct vty* vty, bool no_queue)
+{
+ cmd_parsed parsed = vty->parsed ;
+ enum cmd_return_code ret ;
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
- matched_count++;
- }
- else
- incomplete_count++;
- }
+ if (parsed->cmd == NULL)
+ return CMD_SUCCESS ; /* NULL commands are easy */
- /* Finish of using cmd_vector. */
- vector_free (cmd_vector);
+ vty->node = parsed->cnode ;
- /* To execute command, matched_count must be 1. */
- if (matched_count == 0)
+ if (no_queue || !vty_cli_nexus)
{
- if (incomplete_count)
- return CMD_ERR_INCOMPLETE;
- else
- return CMD_ERR_NO_MATCH;
+ ret = cmd_dispatch_call(vty) ;
+ cmd_post_command(vty, ret) ;
}
-
- if (matched_count > 1)
- return CMD_ERR_AMBIGUOUS;
-
- /* Argument treatment */
- varflag = 0;
- argc = 0;
-
- for (i = 0; i < vector_active (vline); i++)
+ else
{
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
+ /* Don't do it now, but send to bgp qpthread */
+ if (parsed->cmd->attr & CMD_ATTR_CALL)
+ cq_enqueue(vty, vty_cli_nexus) ;
else
- {
- vector descvec = vector_slot (matched_element->strvec, i);
+ cq_enqueue(vty, vty_cmd_nexus) ;
+
+ ret = CMD_QUEUED ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after executing command.
+ *
+ * This is separated out so that can be called when queued command completes.
+ *
+ * If have just processed a "do" shortcut command, and it has not set the
+ * vty->node to something other than ENABLE_NODE, then restore to the original
+ * state.
+ *
+ * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked
+ */
+extern void
+cmd_post_command(struct vty* vty, int ret)
+{
+ if (vty->parsed->do_shortcut)
+ {
+ if (vty->node == ENABLE_NODE)
+ vty->node = vty->parsed->onode ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Parse and execute a command.
+ *
+ * The command is given by vty->buf and vty->node.
+ *
+ * Uses vty->parsed.
+ *
+ * -- use strict/completion parsing, as required.
+ *
+ * -- parse in current node and in ancestors, as required
+ *
+ * If does not find in any ancestor, return error from current node.
+ *
+ * -- implement the "do" shortcut, as required
+ *
+ * If qpthreads_enabled, then may queue the command rather than execute it
+ * here.
+ *
+ * The vty->node may be changed during the execution of the command, and may
+ * be returned changed once the command has completed.
+ *
+ * NB: expects to have free run of everything in the vty structure (except
+ * the contents of the vty_io sub-structure) until the command completes.
+ */
+extern enum cmd_return_code
+cmd_execute_command(struct vty *vty,
+ enum cmd_parse_type type, struct cmd_element **cmd)
+{
+ enum cmd_return_code ret ;
+
+ /* Try to parse in vty->node or, if required, ancestors thereof. */
+ ret = cmd_parse_command(vty, type) ;
+
+ if (cmd != NULL)
+ *cmd = vty->parsed->cmd ; /* for vtysh */
+
+ if (ret == CMD_SUCCESS)
+ ret = cmd_dispatch(vty, 0) ;
+ else if (ret == CMD_EMPTY)
+ ret = CMD_SUCCESS ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Read configuration from file.
+ *
+ * In the qpthreads world this assumes that it is running with the vty
+ * locked, and that all commands are to be executed directly.
+ *
+ * If the 'first_cmd' argument is not NULL it is the address of the first
+ * command that is expected to appear. If the first command is not this, then
+ * the 'first_cmd' is called with argv == NULL (and argc == 0) to signal the
+ * command is being invoked by default.
+ *
+ * Command processing continues while CMD_SUCCESS is returned by the command
+ * parser and command execution.
+ *
+ * If 'ignore_warning' is set, then any CMD_WARNING returned by command
+ * execution is converted to CMD_SUCCESS. Note that any CMD_WARNING returned
+ * by command parsing (or in execution of any default 'first_cmd').
+ *
+ * Returns: cmd_return_code for last command
+ * vty->buf is last line processed
+ * vty->lineno is number of last line processed (1 is first)
+ *
+ * If the file is empty, will return CMD_SUCCESS.
+ *
+ * Never returns CMD_EMPTY -- that counts as CMD_SUCCESS.
+ *
+ * If
+ *
+ * If return code is not CMD_SUCCESS, the the output buffering contains the
+ * output from the last command attempted.
+ */
+extern enum cmd_return_code
+config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd,
+ qstring buf, bool ignore_warning)
+{
+ enum cmd_return_code ret;
- if (vector_active (descvec) == 1)
- {
- struct desc *desc = vector_slot (descvec, 0);
+ vty->buf = buf->body ;
+ vty->lineno = 0 ;
- if (CMD_VARARG (desc->cmd))
- varflag = 1;
+ ret = CMD_SUCCESS ; /* in case file is empty */
+ vty_out_clear(vty) ;
- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
- argv[argc++] = vector_slot (vline, i);
- }
- else
- argv[argc++] = vector_slot (vline, i);
- }
+ while (fgets (buf->body, buf->size, fp))
+ {
+ ++vty->lineno ;
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
- }
+ /* Execute configuration command : this is strict match */
+ ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
- /* For vtysh execution. */
- if (cmd)
- *cmd = matched_element;
+ if (ret == CMD_EMPTY)
+ continue ; /* skip empty/comment */
- if (matched_element->daemon)
- return CMD_SUCCESS_DAEMON;
+ if (ret != CMD_SUCCESS)
+ break ; /* stop on *any* parsing issue */
- /* Now execute matched command */
- return (*matched_element->func) (matched_element, vty, argc, argv);
-}
+ /* special handling before of first command */
+ if (first_cmd != NULL)
+ {
+ if (first_cmd != vty->parsed->cmd)
+ {
+ ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
+ if (ret != CMD_SUCCESS)
+ break ; /* stop on *any* issue with "default" */
+ } ;
+ first_cmd = NULL ;
+ }
-/* Configration make from file. */
-int
-config_from_file (struct vty *vty, FILE *fp)
-{
- int ret;
- vector vline;
+ /* Standard command handling */
+ ret = cmd_dispatch(vty, cmd_no_queue) ;
- while (fgets (vty->buf, VTY_BUFSIZ, fp))
- {
- vline = cmd_make_strvec (vty->buf);
+ if (ret != CMD_SUCCESS)
+ {
+ /* Ignore CMD_WARNING if required
+ *
+ * Ignore CMD_CLOSE at all times.
+ */
+ if ( ((ret == CMD_WARNING) && ignore_warning)
+ || (ret == CMD_CLOSE) )
+ ret = CMD_SUCCESS ; /* in case at EOF */
+ else
+ break ; /* stop */
+ } ;
- /* In case of comment line */
- if (vline == NULL)
- continue;
- /* Execute configuration command : this is strict match */
- ret = cmd_execute_command_strict (vline, vty, NULL);
+ vty_out_clear(vty) ;
+ } ;
- /* Try again with setting node to CONFIG_NODE */
- while (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE)
- {
- vty->node = node_parent(vty->node);
- ret = cmd_execute_command_strict (vline, vty, NULL);
- }
+ if (ret == CMD_EMPTY)
+ ret = CMD_SUCCESS ; /* OK if end on empty line */
- cmd_free_strvec (vline);
+ return ret ;
+} ;
- if (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO)
- return ret;
- }
- return CMD_SUCCESS;
-}
+/*----------------------------------------------------------------------------*/
/* Configration from terminal */
-DEFUN (config_terminal,
+DEFUN_CALL (config_terminal,
config_terminal_cmd,
"configure terminal",
"Configuration from vty interface\n"
"Configuration terminal\n")
{
- if (vty_config_lock (vty))
- vty->node = CONFIG_NODE;
- else
- {
- vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
+ if (vty_config_lock (vty, CONFIG_NODE))
+ return CMD_SUCCESS;
+
+ vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
+ return CMD_WARNING;
}
/* Enable command */
-DEFUN (enable,
+DEFUN_CALL (enable,
config_enable_cmd,
"enable",
"Turn on privileged mode command\n")
{
/* If enable password is NULL, change to ENABLE_NODE */
if ((host.enable == NULL && host.enable_encrypt == NULL) ||
- vty->type == VTY_SHELL_SERV)
- vty->node = ENABLE_NODE;
+ vty_shell_serv(vty))
+ vty_set_node(vty, ENABLE_NODE);
else
- vty->node = AUTH_ENABLE_NODE;
+ vty_set_node(vty, AUTH_ENABLE_NODE);
return CMD_SUCCESS;
}
/* Disable command */
-DEFUN (disable,
+DEFUN_CALL (disable,
config_disable_cmd,
"disable",
"Turn off privileged mode command\n")
{
- if (vty->node == ENABLE_NODE)
- vty->node = VIEW_NODE;
+ if (vty_get_node(vty) == ENABLE_NODE)
+ vty_set_node(vty, VIEW_NODE);
return CMD_SUCCESS;
}
/* Down vty node level. */
-DEFUN (config_exit,
+DEFUN_CALL (config_exit,
config_exit_cmd,
"exit",
"Exit current mode and down to previous mode\n")
{
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- if (vty_shell (vty))
- exit (0);
- else
- vty->status = VTY_CLOSE;
- break;
- case CONFIG_NODE:
- vty->node = ENABLE_NODE;
- vty_config_unlock (vty);
- break;
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case BGP_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case MASC_NODE:
- case RMAP_NODE:
- case VTY_NODE:
- vty->node = CONFIG_NODE;
- break;
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- vty->node = BGP_NODE;
- break;
- case KEYCHAIN_KEY_NODE:
- vty->node = KEYCHAIN_NODE;
- break;
- default:
- break;
- }
- return CMD_SUCCESS;
+ return vty_cmd_exit(vty) ;
}
/* quit is alias of exit. */
-ALIAS (config_exit,
+ALIAS_CALL (config_exit,
config_quit_cmd,
"quit",
"Exit current mode and down to previous mode\n")
-
+
/* End of configuration. */
-DEFUN (config_end,
+DEFUN_CALL (config_end,
config_end_cmd,
"end",
"End current mode and change to enable mode.")
{
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- vty_config_unlock (vty);
- vty->node = ENABLE_NODE;
- break;
- default:
- break;
- }
- return CMD_SUCCESS;
+ return vty_cmd_end(vty) ;
}
/* Show version. */
-DEFUN (show_version,
+DEFUN_CALL (show_version,
show_version_cmd,
"show version",
SHOW_STR
@@ -2489,12 +2986,12 @@ DEFUN (show_version,
}
/* Help display function for all node. */
-DEFUN (config_help,
+DEFUN_CALL (config_help,
config_help_cmd,
"help",
"Description of the interactive help system\n")
{
- vty_out (vty,
+ vty_out (vty,
"Quagga VTY provides advanced help feature. When you need help,%s\
anytime at the command line please press '?'.%s\
%s\
@@ -2513,39 +3010,38 @@ argument.%s\
}
/* Help display function for all node. */
-DEFUN (config_list,
+DEFUN_CALL (config_list,
config_list_cmd,
"list",
"Print command list\n")
{
unsigned int i;
- struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
+ struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty));
struct cmd_element *cmd;
- for (i = 0; i < vector_active (cnode->cmd_vector); i++)
- if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
- && !(cmd->attr == CMD_ATTR_DEPRECATED
- || cmd->attr == CMD_ATTR_HIDDEN))
+ for (i = 0; i < vector_length (cnode->cmd_vector); i++)
+ if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL
+ && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
vty_out (vty, " %s%s", cmd->string,
VTY_NEWLINE);
return CMD_SUCCESS;
}
/* Write current configuration into file. */
-DEFUN (config_write_file,
+DEFUN (config_write_file,
config_write_file_cmd,
- "write file",
+ "write file",
"Write running configuration to memory, network, or terminal\n"
"Write to configuration file\n")
{
unsigned int i;
int fd;
+ int err;
struct cmd_node *node;
char *config_file;
char *config_file_tmp = NULL;
char *config_file_sav = NULL;
int ret = CMD_WARNING;
- struct vty *file_vty;
/* Check and see if we are operating under vtysh configuration */
if (host.config == NULL)
@@ -2557,7 +3053,7 @@ DEFUN (config_write_file,
/* Get filename. */
config_file = host.config;
-
+
config_file_sav =
XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
strcpy (config_file_sav, config_file);
@@ -2566,7 +3062,7 @@ DEFUN (config_write_file,
config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
sprintf (config_file_tmp, "%s.XXXXXX", config_file);
-
+
/* Open file to configuration write. */
fd = mkstemp (config_file_tmp);
if (fd < 0)
@@ -2575,62 +3071,74 @@ DEFUN (config_write_file,
VTY_NEWLINE);
goto finished;
}
-
+
/* Make vty for configuration file. */
- file_vty = vty_new ();
- file_vty->fd = fd;
- file_vty->type = VTY_FILE;
+ vty_open_config_write(vty, fd) ;
/* Config file header print. */
- vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
- vty_time_print (file_vty, 1);
- vty_out (file_vty, "!\n");
+ vty_out (vty, "!\n! Zebra configuration saved from vty\n! ");
+ vty_time_print (vty, 1);
+ vty_out (vty, "!\n");
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((node = vector_slot (cmdvec, i)) && node->func)
+ for (i = 0; i < vector_length (cmdvec); i++)
+ if ((node = vector_get_item (cmdvec, i)) && node->func)
{
- if ((*node->func) (file_vty))
- vty_out (file_vty, "!\n");
+ if ((*node->func) (vty))
+ vty_out (vty, "!\n");
}
- vty_close (file_vty);
+
+ err = vty_close_config_write(vty) ;
+ close(fd) ;
+
+ if (err != 0)
+ {
+ vty_out (vty, "Failed while writing configuration file %s.%s",
+ config_file_tmp, VTY_NEWLINE);
+ goto finished;
+ }
if (unlink (config_file_sav) != 0)
if (errno != ENOENT)
{
- vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
- VTY_NEWLINE);
+ vty_out (vty, "Can't unlink backup configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
goto finished;
- }
+ } ;
+
if (link (config_file, config_file_sav) != 0)
{
- vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
- VTY_NEWLINE);
+ vty_out (vty, "Can't backup old configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
goto finished;
- }
- sync ();
+ } ;
+
+ sync () ;
+
if (unlink (config_file) != 0)
{
- vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
- VTY_NEWLINE);
+ vty_out (vty, "Can't unlink configuration file %s.%s",
+ config_file, VTY_NEWLINE);
goto finished;
- }
+ } ;
+
if (link (config_file_tmp, config_file) != 0)
{
- vty_out (vty, "Can't save configuration file %s.%s", config_file,
- VTY_NEWLINE);
+ vty_out (vty, "Can't save configuration file %s.%s",
+ config_file, VTY_NEWLINE);
goto finished;
- }
+ } ;
+
sync ();
-
+
if (chmod (config_file, CONFIGFILE_MASK) != 0)
{
- vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
- config_file, safe_strerror(errno), errno, VTY_NEWLINE);
+ vty_out (vty, "Can't chmod configuration file %s: %s (%s).\n",
+ config_file, errtostr(errno, 0).str, errtoname(errno, 0).str);
goto finished;
}
- vty_out (vty, "Configuration saved to %s%s", config_file,
- VTY_NEWLINE);
+ vty_out (vty, "Configuration saved to %s\n", config_file);
+
ret = CMD_SUCCESS;
finished:
@@ -2640,20 +3148,20 @@ finished:
return ret;
}
-ALIAS (config_write_file,
+ALIAS (config_write_file,
config_write_cmd,
- "write",
+ "write",
"Write running configuration to memory, network, or terminal\n")
-ALIAS (config_write_file,
+ALIAS (config_write_file,
config_write_memory_cmd,
- "write memory",
+ "write memory",
"Write running configuration to memory, network, or terminal\n"
"Write configuration to the file (same as write file)\n")
-ALIAS (config_write_file,
+ALIAS (config_write_file,
copy_runningconfig_startupconfig_cmd,
- "copy running-config startup-config",
+ "copy running-config startup-config",
"Copy configuration\n"
"Copy running config to... \n"
"Copy running config to startup config (same as write file)\n")
@@ -2668,10 +3176,10 @@ DEFUN (config_write_terminal,
unsigned int i;
struct cmd_node *node;
- if (vty->type == VTY_SHELL_SERV)
+ if (vty_shell_serv(vty))
{
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
+ for (i = 0; i < vector_length (cmdvec); i++)
+ if ((node = vector_get_item (cmdvec, i)) && node->func && node->vtysh)
{
if ((*node->func) (vty))
vty_out (vty, "!%s", VTY_NEWLINE);
@@ -2683,8 +3191,8 @@ DEFUN (config_write_terminal,
VTY_NEWLINE);
vty_out (vty, "!%s", VTY_NEWLINE);
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((node = vector_slot (cmdvec, i)) && node->func)
+ for (i = 0; i < vector_length (cmdvec); i++)
+ if ((node = vector_get_item (cmdvec, i)) && node->func)
{
if ((*node->func) (vty))
vty_out (vty, "!%s", VTY_NEWLINE);
@@ -2736,7 +3244,7 @@ DEFUN (show_startup_config,
}
/* Hostname configuration */
-DEFUN (config_hostname,
+DEFUN_CALL (config_hostname,
hostname_cmd,
"hostname WORD",
"Set system's network name\n"
@@ -2748,28 +3256,40 @@ DEFUN (config_hostname,
return CMD_WARNING;
}
+ VTY_LOCK() ;
+
if (host.name)
XFREE (MTYPE_HOST, host.name);
-
+
host.name = XSTRDUP (MTYPE_HOST, argv[0]);
+ uty_set_host_name(host.name) ;
+
+ VTY_UNLOCK() ;
+
return CMD_SUCCESS;
}
-DEFUN (config_no_hostname,
+DEFUN_CALL (config_no_hostname,
no_hostname_cmd,
"no hostname [HOSTNAME]",
NO_STR
"Reset system's network name\n"
"Host name of this router\n")
{
+ VTY_LOCK() ;
+
if (host.name)
XFREE (MTYPE_HOST, host.name);
host.name = NULL;
+ uty_set_host_name(host.name) ;
+
+ VTY_UNLOCK() ;
+
return CMD_SUCCESS;
}
/* VTY interface password set. */
-DEFUN (config_password, password_cmd,
+DEFUN_CALL (config_password, password_cmd,
"password (8|) WORD",
"Assign the terminal connection password\n"
"Specifies a HIDDEN password will follow\n"
@@ -2804,7 +3324,7 @@ DEFUN (config_password, password_cmd,
if (!isalnum ((int) *argv[0]))
{
- vty_out (vty,
+ vty_out (vty,
"Please specify string starting with alphanumeric%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -2825,13 +3345,13 @@ DEFUN (config_password, password_cmd,
return CMD_SUCCESS;
}
-ALIAS (config_password, password_text_cmd,
+ALIAS_CALL (config_password, password_text_cmd,
"password LINE",
"Assign the terminal connection password\n"
"The UNENCRYPTED (cleartext) line password\n")
/* VTY enable password set. */
-DEFUN (config_enable_password, enable_password_cmd,
+DEFUN_CALL (config_enable_password, enable_password_cmd,
"enable password (8|) WORD",
"Modify enable password parameters\n"
"Assign the privileged level password\n"
@@ -2870,7 +3390,7 @@ DEFUN (config_enable_password, enable_password_cmd,
if (!isalnum ((int) *argv[0]))
{
- vty_out (vty,
+ vty_out (vty,
"Please specify string starting with alphanumeric%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -2892,7 +3412,7 @@ DEFUN (config_enable_password, enable_password_cmd,
return CMD_SUCCESS;
}
-ALIAS (config_enable_password,
+ALIAS_CALL (config_enable_password,
enable_password_text_cmd,
"enable password LINE",
"Modify enable password parameters\n"
@@ -2900,7 +3420,7 @@ ALIAS (config_enable_password,
"The UNENCRYPTED (cleartext) 'enable' password\n")
/* VTY enable password delete. */
-DEFUN (no_config_enable_password, no_enable_password_cmd,
+DEFUN_CALL (no_config_enable_password, no_enable_password_cmd,
"no enable password",
NO_STR
"Modify enable password parameters\n"
@@ -2916,8 +3436,8 @@ DEFUN (no_config_enable_password, no_enable_password_cmd,
return CMD_SUCCESS;
}
-
-DEFUN (service_password_encrypt,
+
+DEFUN_CALL (service_password_encrypt,
service_password_encrypt_cmd,
"service password-encryption",
"Set up miscellaneous service\n"
@@ -2944,7 +3464,7 @@ DEFUN (service_password_encrypt,
return CMD_SUCCESS;
}
-DEFUN (no_service_password_encrypt,
+DEFUN_CALL (no_service_password_encrypt,
no_service_password_encrypt_cmd,
"no service password-encryption",
NO_STR
@@ -2967,7 +3487,7 @@ DEFUN (no_service_password_encrypt,
return CMD_SUCCESS;
}
-DEFUN (config_terminal_length, config_terminal_length_cmd,
+DEFUN_CALL (config_terminal_length, config_terminal_length_cmd,
"terminal length <0-512>",
"Set terminal line parameters\n"
"Set number of lines on a screen\n"
@@ -2982,22 +3502,22 @@ DEFUN (config_terminal_length, config_terminal_length_cmd,
vty_out (vty, "length is malformed%s", VTY_NEWLINE);
return CMD_WARNING;
}
- vty->lines = lines;
+ vty_set_lines(vty, lines);
return CMD_SUCCESS;
}
-DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
+DEFUN_CALL (config_terminal_no_length, config_terminal_no_length_cmd,
"terminal no length",
"Set terminal line parameters\n"
NO_STR
"Set number of lines on a screen\n")
{
- vty->lines = -1;
+ vty_set_lines(vty, -1);
return CMD_SUCCESS;
}
-DEFUN (service_terminal_length, service_terminal_length_cmd,
+DEFUN_CALL (service_terminal_length, service_terminal_length_cmd,
"service terminal-length <0-512>",
"Set up miscellaneous service\n"
"System wide terminal length configuration\n"
@@ -3017,7 +3537,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd,
return CMD_SUCCESS;
}
-DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
+DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd,
"no service terminal-length [<0-512>]",
NO_STR
"Set up miscellaneous service\n"
@@ -3028,7 +3548,7 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
return CMD_SUCCESS;
}
-DEFUN_HIDDEN (do_echo,
+DEFUN_HID_CALL (do_echo,
echo_cmd,
"echo .MESSAGE",
"Echo a message back to the vty\n"
@@ -3043,7 +3563,7 @@ DEFUN_HIDDEN (do_echo,
return CMD_SUCCESS;
}
-DEFUN (config_logmsg,
+DEFUN_CALL (config_logmsg,
config_logmsg_cmd,
"logmsg "LOG_LEVELS" .MESSAGE",
"Send a message to enabled logging destinations\n"
@@ -3056,76 +3576,79 @@ DEFUN (config_logmsg,
if ((level = level_match(argv[0])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
+ message = argv_concat(argv, argc, 1);
+ zlog(NULL, level, "%s", (message ? message : ""));
if (message)
XFREE(MTYPE_TMP, message);
return CMD_SUCCESS;
}
-DEFUN (show_logging,
+DEFUN_CALL (show_logging,
show_logging_cmd,
"show logging",
SHOW_STR
"Show current logging configuration\n")
{
- struct zlog *zl = zlog_default;
-
vty_out (vty, "Syslog logging: ");
- if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) == ZLOG_DISABLED)
vty_out (vty, "disabled");
else
vty_out (vty, "level %s, facility %s, ident %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
- facility_name(zl->facility), zl->ident);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)],
+ facility_name(zlog_get_facility(NULL)), zlog_get_ident(NULL));
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Stdout logging: ");
- if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) == ZLOG_DISABLED)
vty_out (vty, "disabled");
else
vty_out (vty, "level %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]);
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Monitor logging: ");
- if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED)
vty_out (vty, "disabled");
else
vty_out (vty, "level %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)]);
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "File logging: ");
- if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
- !zl->fp)
+ if ((zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) == ZLOG_DISABLED) ||
+ !zlog_is_file(NULL))
vty_out (vty, "disabled");
else
- vty_out (vty, "level %s, filename %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
- zl->filename);
+ {
+ char * filename = zlog_get_filename(NULL);
+ vty_out (vty, "level %s, filename %s",
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)],
+ filename);
+ free(filename);
+ }
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Protocol name: %s%s",
- zlog_proto_names[zl->protocol], VTY_NEWLINE);
+ zlog_get_proto_name(NULL), VTY_NEWLINE);
vty_out (vty, "Record priority: %s%s",
- (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
+ (zlog_get_record_priority(NULL) ? "enabled" : "disabled"), VTY_NEWLINE);
vty_out (vty, "Timestamp precision: %d%s",
- zl->timestamp_precision, VTY_NEWLINE);
+ zlog_get_timestamp_precision(NULL), VTY_NEWLINE);
return CMD_SUCCESS;
}
-DEFUN (config_log_stdout,
+DEFUN_CALL (config_log_stdout,
config_log_stdout_cmd,
"log stdout",
"Logging control\n"
"Set stdout logging level\n")
{
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+ zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_get_default_lvl(NULL));
return CMD_SUCCESS;
}
-DEFUN (config_log_stdout_level,
+DEFUN_CALL (config_log_stdout_level,
config_log_stdout_level_cmd,
"log stdout "LOG_LEVELS,
"Logging control\n"
@@ -3140,7 +3663,7 @@ DEFUN (config_log_stdout_level,
return CMD_SUCCESS;
}
-DEFUN (no_config_log_stdout,
+DEFUN_CALL (no_config_log_stdout,
no_config_log_stdout_cmd,
"no log stdout [LEVEL]",
NO_STR
@@ -3152,17 +3675,17 @@ DEFUN (no_config_log_stdout,
return CMD_SUCCESS;
}
-DEFUN (config_log_monitor,
+DEFUN_CALL (config_log_monitor,
config_log_monitor_cmd,
"log monitor",
"Logging control\n"
"Set terminal line (monitor) logging level\n")
{
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+ zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_get_default_lvl(NULL));
return CMD_SUCCESS;
}
-DEFUN (config_log_monitor_level,
+DEFUN_CALL (config_log_monitor_level,
config_log_monitor_level_cmd,
"log monitor "LOG_LEVELS,
"Logging control\n"
@@ -3177,7 +3700,7 @@ DEFUN (config_log_monitor_level,
return CMD_SUCCESS;
}
-DEFUN (no_config_log_monitor,
+DEFUN_CALL (no_config_log_monitor,
no_config_log_monitor_cmd,
"no log monitor [LEVEL]",
NO_STR
@@ -3195,19 +3718,19 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)
int ret;
char *p = NULL;
const char *fullpath;
-
+
/* Path detection. */
if (! IS_DIRECTORY_SEP (*fname))
{
char cwd[MAXPATHLEN+1];
cwd[MAXPATHLEN] = '\0';
-
+
if (getcwd (cwd, MAXPATHLEN) == NULL)
{
zlog_err ("config_log_file: Unable to alloc mem!");
return CMD_WARNING;
}
-
+
if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
== NULL)
{
@@ -3239,17 +3762,17 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)
return CMD_SUCCESS;
}
-DEFUN (config_log_file,
+DEFUN_CALL (config_log_file,
config_log_file_cmd,
"log file FILENAME",
"Logging control\n"
"Logging to file\n"
"Logging filename\n")
{
- return set_log_file(vty, argv[0], zlog_default->default_lvl);
+ return set_log_file(vty, argv[0], zlog_get_default_lvl(NULL));
}
-DEFUN (config_log_file_level,
+DEFUN_CALL (config_log_file_level,
config_log_file_level_cmd,
"log file FILENAME "LOG_LEVELS,
"Logging control\n"
@@ -3264,7 +3787,7 @@ DEFUN (config_log_file_level,
return set_log_file(vty, argv[0], level);
}
-DEFUN (no_config_log_file,
+DEFUN_CALL (no_config_log_file,
no_config_log_file_cmd,
"no log file [FILENAME]",
NO_STR
@@ -3282,7 +3805,7 @@ DEFUN (no_config_log_file,
return CMD_SUCCESS;
}
-ALIAS (no_config_log_file,
+ALIAS_CALL (no_config_log_file,
no_config_log_file_level_cmd,
"no log file FILENAME LEVEL",
NO_STR
@@ -3291,17 +3814,17 @@ ALIAS (no_config_log_file,
"Logging file name\n"
"Logging level\n")
-DEFUN (config_log_syslog,
+DEFUN_CALL (config_log_syslog,
config_log_syslog_cmd,
"log syslog",
"Logging control\n"
"Set syslog logging level\n")
{
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_get_default_lvl(NULL));
return CMD_SUCCESS;
}
-DEFUN (config_log_syslog_level,
+DEFUN_CALL (config_log_syslog_level,
config_log_syslog_level_cmd,
"log syslog "LOG_LEVELS,
"Logging control\n"
@@ -3316,7 +3839,7 @@ DEFUN (config_log_syslog_level,
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED (config_log_syslog_facility,
+DEFUN_DEP_CALL (config_log_syslog_facility,
config_log_syslog_facility_cmd,
"log syslog facility "LOG_FACILITIES,
"Logging control\n"
@@ -3329,12 +3852,12 @@ DEFUN_DEPRECATED (config_log_syslog_facility,
if ((facility = facility_match(argv[0])) < 0)
return CMD_ERR_NO_MATCH;
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
- zlog_default->facility = facility;
+ zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_get_default_lvl(NULL));
+ zlog_set_facility(NULL, facility);
return CMD_SUCCESS;
}
-DEFUN (no_config_log_syslog,
+DEFUN_CALL (no_config_log_syslog,
no_config_log_syslog_cmd,
"no log syslog [LEVEL]",
NO_STR
@@ -3346,7 +3869,7 @@ DEFUN (no_config_log_syslog,
return CMD_SUCCESS;
}
-ALIAS (no_config_log_syslog,
+ALIAS_CALL (no_config_log_syslog,
no_config_log_syslog_facility_cmd,
"no log syslog facility "LOG_FACILITIES,
NO_STR
@@ -3355,7 +3878,7 @@ ALIAS (no_config_log_syslog,
"Facility parameter for syslog messages\n"
LOG_FACILITY_DESC)
-DEFUN (config_log_facility,
+DEFUN_CALL (config_log_facility,
config_log_facility_cmd,
"log facility "LOG_FACILITIES,
"Logging control\n"
@@ -3366,11 +3889,11 @@ DEFUN (config_log_facility,
if ((facility = facility_match(argv[0])) < 0)
return CMD_ERR_NO_MATCH;
- zlog_default->facility = facility;
+ zlog_set_facility(NULL, facility);
return CMD_SUCCESS;
}
-DEFUN (no_config_log_facility,
+DEFUN_CALL (no_config_log_facility,
no_config_log_facility_cmd,
"no log facility [FACILITY]",
NO_STR
@@ -3378,11 +3901,11 @@ DEFUN (no_config_log_facility,
"Reset syslog facility to default (daemon)\n"
"Syslog facility\n")
{
- zlog_default->facility = LOG_DAEMON;
+ zlog_set_facility(NULL, LOG_DAEMON);
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED (config_log_trap,
+DEFUN_DEP_CALL (config_log_trap,
config_log_trap_cmd,
"log trap "LOG_LEVELS,
"Logging control\n"
@@ -3390,19 +3913,15 @@ DEFUN_DEPRECATED (config_log_trap,
LOG_LEVEL_DESC)
{
int new_level ;
- int i;
-
+
if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog_default->default_lvl = new_level;
- for (i = 0; i < ZLOG_NUM_DESTS; i++)
- if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
- zlog_default->maxlvl[i] = new_level;
+ zlog_set_default_lvl_dest (NULL, new_level);
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED (no_config_log_trap,
+DEFUN_DEP_CALL (no_config_log_trap,
no_config_log_trap_cmd,
"no log trap [LEVEL]",
NO_STR
@@ -3410,32 +3929,32 @@ DEFUN_DEPRECATED (no_config_log_trap,
"Permit all logging information\n"
"Logging level\n")
{
- zlog_default->default_lvl = LOG_DEBUG;
+ zlog_set_default_lvl(NULL, LOG_DEBUG);
return CMD_SUCCESS;
}
-DEFUN (config_log_record_priority,
+DEFUN_CALL (config_log_record_priority,
config_log_record_priority_cmd,
"log record-priority",
"Logging control\n"
"Log the priority of the message within the message\n")
{
- zlog_default->record_priority = 1 ;
+ zlog_set_record_priority(NULL, 1) ;
return CMD_SUCCESS;
}
-DEFUN (no_config_log_record_priority,
+DEFUN_CALL (no_config_log_record_priority,
no_config_log_record_priority_cmd,
"no log record-priority",
NO_STR
"Logging control\n"
"Do not log the priority of the message within the message\n")
{
- zlog_default->record_priority = 0 ;
+ zlog_set_record_priority(NULL, 0) ;
return CMD_SUCCESS;
}
-DEFUN (config_log_timestamp_precision,
+DEFUN_CALL (config_log_timestamp_precision,
config_log_timestamp_precision_cmd,
"log timestamp precision <0-6>",
"Logging control\n"
@@ -3443,6 +3962,8 @@ DEFUN (config_log_timestamp_precision,
"Set the timestamp precision\n"
"Number of subsecond digits\n")
{
+ int timestamp_precision;
+
if (argc != 1)
{
vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
@@ -3450,11 +3971,13 @@ DEFUN (config_log_timestamp_precision,
}
VTY_GET_INTEGER_RANGE("Timestamp Precision",
- zlog_default->timestamp_precision, argv[0], 0, 6);
+ timestamp_precision, argv[0], 0, 6);
+ zlog_set_timestamp_precision(NULL, timestamp_precision);
+
return CMD_SUCCESS;
}
-DEFUN (no_config_log_timestamp_precision,
+DEFUN_CALL (no_config_log_timestamp_precision,
no_config_log_timestamp_precision_cmd,
"no log timestamp precision",
NO_STR
@@ -3462,11 +3985,11 @@ DEFUN (no_config_log_timestamp_precision,
"Timestamp configuration\n"
"Reset the timestamp precision to the default value of 0\n")
{
- zlog_default->timestamp_precision = 0 ;
+ zlog_set_timestamp_precision(NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (banner_motd_file,
+DEFUN_CALL (banner_motd_file,
banner_motd_file_cmd,
"banner motd file [FILE]",
"Set banner\n"
@@ -3481,7 +4004,7 @@ DEFUN (banner_motd_file,
return CMD_SUCCESS;
}
-DEFUN (banner_motd_default,
+DEFUN_CALL (banner_motd_default,
banner_motd_default_cmd,
"banner motd default",
"Set banner string\n"
@@ -3492,7 +4015,7 @@ DEFUN (banner_motd_default,
return CMD_SUCCESS;
}
-DEFUN (no_banner_motd,
+DEFUN_CALL (no_banner_motd,
no_banner_motd_cmd,
"no banner motd",
NO_STR
@@ -3500,7 +4023,7 @@ DEFUN (no_banner_motd,
"Strings for motd\n")
{
host.motd = NULL;
- if (host.motdfile)
+ if (host.motdfile)
XFREE (MTYPE_HOST, host.motdfile);
host.motdfile = NULL;
return CMD_SUCCESS;
@@ -3540,7 +4063,7 @@ cmd_init (int terminal)
desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
/* Allocate initial top vector of commands. */
- cmdvec = vector_init (VECTOR_MIN_SIZE);
+ cmdvec = vector_init (0);
/* Default host value settings. */
host.name = NULL;
@@ -3604,7 +4127,7 @@ cmd_init (int terminal)
install_default (CONFIG_NODE);
}
-
+
install_element (CONFIG_NODE, &hostname_cmd);
install_element (CONFIG_NODE, &no_hostname_cmd);
@@ -3669,39 +4192,52 @@ cmd_terminate ()
if (cmdvec)
{
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
- {
- cmd_node_v = cmd_node->cmd_vector;
+ for (i = 0; i < vector_length (cmdvec); i++)
+ {
+ cmd_node = vector_get_item (cmdvec, i) ;
+ if (cmd_node == NULL)
+ continue ;
- for (j = 0; j < vector_active (cmd_node_v); j++)
- if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL &&
- cmd_element->strvec != NULL)
- {
- cmd_element_v = cmd_element->strvec;
-
- for (k = 0; k < vector_active (cmd_element_v); k++)
- if ((desc_v = vector_slot (cmd_element_v, k)) != NULL)
- {
- for (l = 0; l < vector_active (desc_v); l++)
- if ((desc = vector_slot (desc_v, l)) != NULL)
- {
- if (desc->cmd)
- XFREE (MTYPE_STRVEC, desc->cmd);
- if (desc->str)
- XFREE (MTYPE_STRVEC, desc->str);
-
- XFREE (MTYPE_DESC, desc);
- }
- vector_free (desc_v);
- }
-
- cmd_element->strvec = NULL;
- vector_free (cmd_element_v);
- }
+ cmd_node_v = cmd_node->cmd_vector;
+
+ for (j = 0; j < vector_length (cmd_node_v); j++)
+ {
+ cmd_element = vector_get_item (cmd_node_v, j) ;
+ if (cmd_element == NULL)
+ continue ;
- vector_free (cmd_node_v);
- }
+ cmd_element_v = cmd_element->strvec ;
+ if (cmd_element_v == NULL)
+ continue ;
+
+ for (k = 0; k < vector_length (cmd_element_v); k++)
+ {
+ desc_v = vector_get_item (cmd_element_v, k) ;
+ if (desc_v == NULL)
+ continue ;
+
+ for (l = 0; l < vector_length (desc_v); l++)
+ {
+ desc = vector_get_item (desc_v, l) ;
+ if (desc == NULL)
+ continue ;
+
+ if (desc->cmd)
+ XFREE (MTYPE_STRVEC, desc->cmd);
+ if (desc->str)
+ XFREE (MTYPE_STRVEC, desc->str);
+
+ XFREE (MTYPE_DESC, desc);
+ } ;
+ vector_free (desc_v);
+ } ;
+
+ cmd_element->strvec = NULL;
+ vector_free (cmd_element_v);
+ } ;
+
+ vector_free (cmd_node_v);
+ } ;
vector_free (cmdvec);
cmdvec = NULL;
diff --git a/lib/command.h b/lib/command.h
index 1275efee..ba7c4bb2 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -8,7 +8,7 @@
* 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
@@ -23,9 +23,17 @@
#ifndef _ZEBRA_COMMAND_H
#define _ZEBRA_COMMAND_H
+#include <stdbool.h>
+
+#include "node_type.h"
#include "vector.h"
-#include "vty.h"
-#include "lib/route_types.h"
+#include "qstring.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+struct vty ; /* in case command.h expanded first */
/* Host configuration variable */
struct host
@@ -59,83 +67,90 @@ struct host
char *motdfile;
};
-/* There are some command levels which called from command node. */
-enum node_type
-{
- AUTH_NODE, /* Authentication mode of vty interface. */
- RESTRICTED_NODE, /* Restricted view mode */
- VIEW_NODE, /* View node. Default mode of vty interface. */
- AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
- ENABLE_NODE, /* Enable node. */
- CONFIG_NODE, /* Config node. Default mode of config file. */
- SERVICE_NODE, /* Service node. */
- DEBUG_NODE, /* Debug node. */
- AAA_NODE, /* AAA node. */
- KEYCHAIN_NODE, /* Key-chain node. */
- KEYCHAIN_KEY_NODE, /* Key-chain key node. */
- INTERFACE_NODE, /* Interface mode node. */
- ZEBRA_NODE, /* zebra connection node. */
- TABLE_NODE, /* rtm_table selection node. */
- RIP_NODE, /* RIP protocol mode node. */
- RIPNG_NODE, /* RIPng protocol mode node. */
- BGP_NODE, /* BGP protocol mode which includes BGP4+ */
- BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
- BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
- BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
- BGP_IPV6_NODE, /* BGP IPv6 address family */
- BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */
- OSPF_NODE, /* OSPF protocol mode */
- OSPF6_NODE, /* OSPF protocol for IPv6 mode */
- ISIS_NODE, /* ISIS protocol mode */
- MASC_NODE, /* MASC for multicast. */
- IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
- IP_NODE, /* Static ip route node. */
- ACCESS_NODE, /* Access list node. */
- PREFIX_NODE, /* Prefix list node. */
- ACCESS_IPV6_NODE, /* Access list node. */
- PREFIX_IPV6_NODE, /* Prefix list node. */
- AS_LIST_NODE, /* AS list node. */
- COMMUNITY_LIST_NODE, /* Community list node. */
- RMAP_NODE, /* Route map node. */
- SMUX_NODE, /* SNMP configuration node. */
- DUMP_NODE, /* Packet dump node. */
- FORWARDING_NODE, /* IP forwarding node. */
- PROTOCOL_NODE, /* protocol filtering node */
- VTY_NODE, /* Vty node. */
-};
-
/* Node which has some commands and prompt string and configuration
function pointer . */
-struct cmd_node
+struct cmd_node
{
/* Node index. */
- enum node_type node;
+ enum node_type node;
/* Prompt character at vty interface. */
- const char *prompt;
+ const char *prompt;
/* Is this node's configuration goes to vtysh ? */
int vtysh;
-
+
/* Node's configuration write function */
int (*func) (struct vty *);
/* Vector of this node's command list. */
- vector cmd_vector;
+ vector cmd_vector;
};
enum
{
- CMD_ATTR_DEPRECATED = 1,
- CMD_ATTR_HIDDEN,
+ /* bit significant */
+ CMD_ATTR_DEPRECATED = 0x01,
+ CMD_ATTR_HIDDEN = 0x02,
+ CMD_ATTR_CALL = 0x04,
};
-/* Structure of command element. */
-struct cmd_element
+/* Return values for command handling.
+ *
+ * NB: when a command is executed it may return CMD_SUCCESS or CMD_WARNING.
+ *
+ * In both cases any output required (including any warning or error
+ * messages) must already have been output.
+ *
+ * All other return codes are for use within the command handler.
+ */
+enum cmd_return_code
{
- const char *string; /* Command specification by string. */
- int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
- const char *doc; /* Documentation of this command. */
+ CMD_SUCCESS = 0,
+ CMD_WARNING = 1,
+ CMD_ERROR,
+
+ CMD_EMPTY,
+ CMD_SUCCESS_DAEMON,
+
+ CMD_CLOSE,
+ CMD_QUEUED,
+
+ CMD_ERR_NO_MATCH,
+ CMD_ERR_AMBIGUOUS,
+ CMD_ERR_INCOMPLETE,
+
+ CMD_COMPLETE_FULL_MATCH,
+ CMD_COMPLETE_MATCH,
+ CMD_COMPLETE_LIST_MATCH,
+ CMD_COMPLETE_ALREADY
+} ;
+
+#define MSG_CMD_ERR_AMBIGUOUS "Ambiguous command"
+#define MSG_CMD_ERR_NO_MATCH "Unrecognised command"
+#define MSG_CMD_ERR_NO_MATCH_old "There is no matched command"
+
+/* Structure of command element. */
+
+struct cmd_element ;
+typedef struct cmd_element* cmd_element ;
+
+typedef const char* const argv_t[] ;
+
+#define DEFUN_CMD_ARG_UNUSED __attribute__ ((unused))
+#define DEFUN_CMD_FUNCTION(name) \
+ enum cmd_return_code name (cmd_element self DEFUN_CMD_ARG_UNUSED, \
+ struct vty* vty DEFUN_CMD_ARG_UNUSED, \
+ int argc DEFUN_CMD_ARG_UNUSED, \
+ argv_t argv DEFUN_CMD_ARG_UNUSED)
+
+typedef DEFUN_CMD_FUNCTION((cmd_function)) ;
+
+struct cmd_element
+{
+ const char *string; /* Command specification by string. */
+ cmd_function* func ;
+ const char *doc; /* Documentation of this command. */
int daemon; /* Daemon to which this command belong. */
vector strvec; /* Pointing out each description vector. */
unsigned int cmdsize; /* Command index count. */
@@ -144,31 +159,57 @@ struct cmd_element
u_char attr; /* Command attributes */
};
-/* Command description structure. */
+/* Command description structure. */
struct desc
{
char *cmd; /* Command string. */
char *str; /* Command's description. */
};
-/* Return value of the commands. */
-#define CMD_SUCCESS 0
-#define CMD_WARNING 1
-#define CMD_ERR_NO_MATCH 2
-#define CMD_ERR_AMBIGUOUS 3
-#define CMD_ERR_INCOMPLETE 4
-#define CMD_ERR_EXEED_ARGC_MAX 5
-#define CMD_ERR_NOTHING_TODO 6
-#define CMD_COMPLETE_FULL_MATCH 7
-#define CMD_COMPLETE_MATCH 8
-#define CMD_COMPLETE_LIST_MATCH 9
-#define CMD_SUCCESS_DAEMON 10
-
-/* Argc max counts. */
-#define CMD_ARGC_MAX 25
+/* Command parsing options */
+enum cmd_parse_type /* bit significant */
+{
+ cmd_parse_completion = 0x00,
+ cmd_parse_strict = 0x01,
+
+ cmd_parse_do = 0x02,
+ cmd_parse_tree = 0x04,
+} ;
+/* Parsed command */
+typedef struct cmd_parsed* cmd_parsed ;
+struct cmd_parsed
+{
+ struct cmd_element *cmd ; /* NULL if empty command
+ or fails to parse */
+
+ enum node_type cnode ; /* node command is in */
+ enum node_type onode ; /* node the parser started in */
+
+ bool do_shortcut ; /* true => is "do" command */
+
+ qstring_t words ; /* the words, '\0' separated */
+
+ vector_t vline ; /* pointers to the words */
+} ;
+
+
+/* Command dispatch options */
+enum {
+ cmd_no_queue = true,
+ cmd_may_queue = false,
+} ;
+
+/*------------------------------------------------------------------------------
+ * Can now include these
+ */
+
+#include "vty.h"
+#include "uty.h"
+
+/*----------------------------------------------------------------------------*/
/* Turn off these macros when uisng cpp with extract.pl */
-#ifndef VTYSH_EXTRACT_PL
+#ifndef VTYSH_EXTRACT_PL
/* helper defines for end-user DEFUN* macros */
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
@@ -182,14 +223,10 @@ struct desc
};
#define DEFUN_CMD_FUNC_DECL(funcname) \
- static int funcname (struct cmd_element *, struct vty *, int, const char *[]);
+ static cmd_function funcname;
#define DEFUN_CMD_FUNC_TEXT(funcname) \
- static int funcname \
- (struct cmd_element *self __attribute__ ((unused)), \
- struct vty *vty __attribute__ ((unused)), \
- int argc __attribute__ ((unused)), \
- const char *argv[] __attribute__ ((unused)) )
+ static DEFUN_CMD_FUNCTION(funcname)
/* DEFUN for vty command interafce. Little bit hacky ;-). */
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
@@ -205,8 +242,17 @@ struct desc
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+#define DEFUN_HID_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_HIDDEN))
+
#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+#define DEFUN_DEP_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_DEPRECATED))
+
+#define DEFUN_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL)
/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
@@ -214,7 +260,7 @@ struct desc
/* DEFSH for vtysh. */
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
/* DEFUN + DEFSH */
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
@@ -247,6 +293,9 @@ struct desc
#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+#define ALIAS_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL, 0)
+
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
@@ -326,6 +375,11 @@ struct desc
#endif /* HAVE_IPV6 */
/* Prototypes. */
+extern void cmd_init (int);
+extern void cmd_terminate (void);
+
+extern void print_version (const char *);
+
extern void install_node (struct cmd_node *, int (*) (struct vty *));
extern void install_default (enum node_type);
extern void install_element (enum node_type, struct cmd_element *);
@@ -334,35 +388,13 @@ extern void sort_node (void);
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
string with a space between each element (allocated using
XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
-extern char *argv_concat (const char **argv, int argc, int shift);
-
-extern vector cmd_make_strvec (const char *);
-extern void cmd_free_strvec (vector);
-extern vector cmd_describe_command (vector, struct vty *, int *status);
-extern char **cmd_complete_command (vector, struct vty *, int *status);
-extern const char *cmd_prompt (enum node_type);
-extern int config_from_file (struct vty *, FILE *);
-extern enum node_type node_parent (enum node_type);
-extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int);
-extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **);
-extern void config_replace_string (struct cmd_element *, char *, ...);
-extern void cmd_init (int);
-extern void cmd_terminate (void);
-
-/* Export typical functions. */
-extern struct cmd_element config_end_cmd;
-extern struct cmd_element config_exit_cmd;
-extern struct cmd_element config_quit_cmd;
-extern struct cmd_element config_help_cmd;
-extern struct cmd_element config_list_cmd;
-extern char *host_config_file (void);
-extern void host_config_set (char *);
-
-extern void print_version (const char *);
+extern char *argv_concat (const char* const* argv, int argc, int shift);
/* struct host global, ick */
-extern struct host host;
+extern struct host host;
+
+#ifdef QDEBUG
+extern const char *debug_banner ;
+#endif
-/* "<cr>" global */
-extern char *command_cr;
#endif /* _ZEBRA_COMMAND_H */
diff --git a/lib/command_execute.h b/lib/command_execute.h
new file mode 100644
index 00000000..a5a807c8
--- /dev/null
+++ b/lib/command_execute.h
@@ -0,0 +1,79 @@
+/*
+ * Zebra configuration command interface routine
+ * Copyright (C) 1997, 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.
+ */
+
+#ifndef _ZEBRA_COMMAND_EXECUTE_H
+#define _ZEBRA_COMMAND_EXECUTE_H
+
+#include "command.h"
+
+extern vector cmd_make_strvec (const char *);
+extern vector cmd_add_to_strvec (vector v, const char* str) ;
+extern void cmd_free_strvec (vector);
+extern vector cmd_describe_command (vector, int, int *status);
+extern vector cmd_complete_command (vector, int, int *status);
+extern const char *cmd_prompt (enum node_type);
+extern enum cmd_return_code
+config_from_file (struct vty* vty, FILE *fp, struct cmd_element* first_cmd,
+ qstring buf, bool stop_on_warning) ;
+extern enum node_type node_parent (enum node_type);
+extern enum cmd_return_code cmd_execute_command (struct vty *vty,
+ enum cmd_parse_type type, struct cmd_element **cmd) ;
+extern enum cmd_return_code cmd_execute_command_strict (struct vty *vty,
+ enum cmd_parse_type type, struct cmd_element **cmd) ;
+
+extern cmd_parsed cmd_parse_init_new(cmd_parsed parsed) ;
+extern cmd_parsed cmd_parse_reset(cmd_parsed parsed, bool free_structure) ;
+extern enum cmd_return_code cmd_parse_command(struct vty* vty,
+ enum cmd_parse_type type) ;
+extern enum cmd_return_code cmd_dispatch(struct vty* vty, bool no_queue) ;
+
+Inline enum cmd_return_code
+cmd_dispatch_call(struct vty* vty)
+{
+ cmd_parsed parsed = vty->parsed ;
+ return (*(parsed->cmd->func))(parsed->cmd, vty,
+ vector_length(&parsed->vline),
+ (const char * const*)vector_body(&parsed->vline)) ;
+} ;
+
+#define cmd_parse_reset_keep(parsed) cmd_parse_reset(parsed, 0)
+#define cmd_parse_reset_free(parsed) cmd_parse_reset(parsed, 1)
+
+extern void config_replace_string (struct cmd_element *, char *, ...);
+
+/* Export typical functions. */
+extern struct cmd_element config_end_cmd;
+extern struct cmd_element config_exit_cmd;
+extern struct cmd_element config_quit_cmd;
+extern struct cmd_element config_help_cmd;
+extern struct cmd_element config_list_cmd;
+extern char *host_config_file (void);
+extern void host_config_set (char *);
+
+/* "<cr>" global */
+extern char *command_cr;
+
+#ifdef QDEBUG
+extern const char *debug_banner;
+#endif
+
+#endif /* _ZEBRA_COMMAND_EXECUTE_H */
diff --git a/lib/command_queue.c b/lib/command_queue.c
new file mode 100644
index 00000000..5f14abae
--- /dev/null
+++ b/lib/command_queue.c
@@ -0,0 +1,154 @@
+/* Command Message Queue
+ * 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 "mqueue.h"
+#include "qpnexus.h"
+#include "memory.h"
+#include "command_queue.h"
+#include "command_execute.h"
+#include "vty.h"
+#include "uty.h"
+#include "vector.h"
+#include "qstring.h"
+
+/*------------------------------------------------------------------------------
+ * Form of message passed with command to be executed
+ */
+
+struct cq_command_args
+{
+ enum cmd_return_code ret ; /* return code from command */
+} ;
+MQB_ARGS_SIZE_OK(cq_command_args) ;
+
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+static void cq_action(mqueue_block mqb, mqb_flag_t flag);
+static void cq_return(mqueue_block mqb, mqb_flag_t flag);
+
+/*------------------------------------------------------------------------------
+ * Enqueue vty and argv[] for execution in given nexus.
+ */
+void
+cq_enqueue(struct vty *vty, qpn_nexus dst)
+{
+ struct cq_command_args* args ;
+ mqueue_block mqb ;
+
+ assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+
+ mqb = mqb_init_new(NULL, cq_action, vty) ;
+ args = mqb_get_args(mqb) ;
+
+ args->ret = CMD_QUEUED ;
+
+ mqueue_enqueue(dst->queue, mqb, 0) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Dispatch a command from the message queue block
+ *
+ * When done (or revoked/deleted) return the message, so that the sender knows
+ * that the command has been dealt with (one way or another).
+ *
+ * Note that if the command is revoked the return is set to CMD_QUEUED.
+ */
+static void
+cq_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct vty *vty;
+ struct cq_command_args* args ;
+
+ assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+
+ vty = mqb_get_arg0(mqb);
+ args = mqb_get_args(mqb) ;
+
+ if (flag == mqb_action)
+ {
+ args->ret = cmd_dispatch_call(vty) ;
+ assert(args->ret != CMD_QUEUED) ; /* avoid confusion ! */
+ }
+ else
+ args->ret = CMD_QUEUED ;
+
+ mqb_set_action(mqb, cq_return) ;
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept return from command which has completed.
+ *
+ * The command line processing for the vty may be stalled (with read mode
+ * disabled) waiting for the return from the command.
+ *
+ * Do not care whether the message is being revoked or not... the command
+ * has completed and that must be signalled to the CLI. Any pending output
+ * is released.
+ *
+ * The command itself may have been revoked before it was executed. That
+ * makes no difference either... the output buffers will simply be empty.
+ * However, the return code is CMD_QUEUED, to signal the fact that the command
+ * was never executed.
+ */
+static void
+cq_return(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct vty *vty ;
+ struct cq_command_args* args ;
+
+ assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+
+ vty = mqb_get_arg0(mqb) ;
+ args = mqb_get_args(mqb) ;
+
+ /* signal end of command */
+ cmd_post_command(vty, args->ret) ;
+ vty_queued_result(vty, args->ret) ;
+
+//if (qpthreads_enabled)
+// qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
+
+ mqb_free(mqb);
+}
+
+/*------------------------------------------------------------------------------
+ * Revoke any messages related to the given VTY -- if running qnexus-wise.
+ *
+ * Revokes in vty_cmd_nexus -- so before command is started
+ * and in vty_cli_nexus -- so after command has completed
+ *
+ * Can do nothing about any command actually being executed in the
+ * vty_cmd_nexus.
+ */
+void
+cq_revoke(struct vty *vty)
+{
+ if (vty_cli_nexus)
+ {
+ mqueue_revoke(vty_cmd_nexus->queue, vty) ;
+ mqueue_revoke(vty_cli_nexus->queue, vty) ;
+ } ;
+}
+
diff --git a/lib/command_queue.h b/lib/command_queue.h
new file mode 100644
index 00000000..804dfa1f
--- /dev/null
+++ b/lib/command_queue.h
@@ -0,0 +1,31 @@
+/* Command Message Queue -- 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 COMMAND_QUEUE_H_
+#define COMMAND_QUEUE_H_
+
+#include "command.h"
+#include "qpnexus.h"
+
+extern void cq_enqueue(struct vty *vty, qpn_nexus dst) ;
+extern void cq_revoke(struct vty* vty) ;
+
+#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/confirm.h b/lib/confirm.h
new file mode 100644
index 00000000..caccf742
--- /dev/null
+++ b/lib/confirm.h
@@ -0,0 +1,28 @@
+/* Compile time CONFIRM gizmo
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *.
+ * This file is part of GNU Zebra.
+ *
+ * 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.
+ */
+
+/*==============================================================================
+ * Compile time CONFIRM gizmo
+ *
+ * Two forms: CONFIRM(e) for use at top (file) level
+ * confirm(e) for use inside compound statements
+ */
+#ifndef CONFIRM
+
+ #define CONFIRM(e) extern void CONFIRMATION(char CONFIRM[(e) ? 1 : -1]) ;
+ #define confirm(e) { CONFIRM(e) }
+
+#endif
diff --git a/lib/daemon.c b/lib/daemon.c
index c473555b..daf3c320 100644
--- a/lib/daemon.c
+++ b/lib/daemon.c
@@ -1,7 +1,7 @@
/*
* Daemonize routine
* Copyright (C) 1997, 1999 Kunihiro Ishiguro
- *
+ *
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify
@@ -36,7 +36,7 @@ daemon (int nochdir, int noclose)
/* In case of fork is error. */
if (pid < 0)
{
- zlog_err ("fork failed: %s", safe_strerror(errno));
+ zlog_err ("fork failed: %s", errtoa(errno, 0).str);
return -1;
}
@@ -49,7 +49,7 @@ daemon (int nochdir, int noclose)
if (pid == -1)
{
- zlog_err ("setsid failed: %s", safe_strerror(errno));
+ zlog_err ("setsid failed: %s", errtoa(errno, 0).str);
return -1;
}
diff --git a/lib/distribute.c b/lib/distribute.c
index 420849da..8c0f057e 100644
--- a/lib/distribute.c
+++ b/lib/distribute.c
@@ -20,6 +20,7 @@
*/
#include <zebra.h>
+#include "miyagi.h"
#include "hash.h"
#include "if.h"
@@ -34,7 +35,7 @@ struct hash *disthash;
/* Hook functions. */
void (*distribute_add_hook) (struct distribute *);
void (*distribute_delete_hook) (struct distribute *);
-
+
static struct distribute *
distribute_new (void)
{
@@ -69,10 +70,10 @@ distribute_lookup (const char *ifname)
struct distribute *dist;
/* temporary reference */
- key.ifname = (char *)ifname;
+ key.ifname = miyagi(ifname) ;
dist = hash_lookup (disthash, &key);
-
+
return dist;
}
@@ -108,8 +109,8 @@ distribute_get (const char *ifname)
struct distribute key;
/* temporary reference */
- key.ifname = (char *)ifname;
-
+ key.ifname = miyagi(ifname) ;
+
return hash_get (disthash, &key, (void * (*) (void *))distribute_hash_alloc);
}
@@ -133,10 +134,10 @@ distribute_cmp (const struct distribute *dist1, const struct distribute *dist2)
return 1;
return 0;
}
-
+
/* Set access-list name to the distribute list. */
static struct distribute *
-distribute_list_set (const char *ifname, enum distribute_type type,
+distribute_list_set (const char *ifname, enum distribute_type type,
const char *alist_name)
{
struct distribute *dist;
@@ -158,14 +159,14 @@ distribute_list_set (const char *ifname, enum distribute_type type,
/* Apply this distribute-list to the interface. */
(*distribute_add_hook) (dist);
-
+
return dist;
}
/* Unset distribute-list. If matched distribute-list exist then
return 1. */
static int
-distribute_list_unset (const char *ifname, enum distribute_type type,
+distribute_list_unset (const char *ifname, enum distribute_type type,
const char *alist_name)
{
struct distribute *dist;
@@ -182,7 +183,7 @@ distribute_list_unset (const char *ifname, enum distribute_type type,
return 0;
free (dist->list[DISTRIBUTE_IN]);
- dist->list[DISTRIBUTE_IN] = NULL;
+ dist->list[DISTRIBUTE_IN] = NULL;
}
if (type == DISTRIBUTE_OUT)
@@ -193,7 +194,7 @@ distribute_list_unset (const char *ifname, enum distribute_type type,
return 0;
free (dist->list[DISTRIBUTE_OUT]);
- dist->list[DISTRIBUTE_OUT] = NULL;
+ dist->list[DISTRIBUTE_OUT] = NULL;
}
/* Apply this distribute-list to the interface. */
@@ -236,7 +237,7 @@ distribute_list_prefix_set (const char *ifname, enum distribute_type type,
/* Apply this distribute-list to the interface. */
(*distribute_add_hook) (dist);
-
+
return dist;
}
@@ -260,7 +261,7 @@ distribute_list_prefix_unset (const char *ifname, enum distribute_type type,
return 0;
free (dist->prefix[DISTRIBUTE_IN]);
- dist->prefix[DISTRIBUTE_IN] = NULL;
+ dist->prefix[DISTRIBUTE_IN] = NULL;
}
if (type == DISTRIBUTE_OUT)
@@ -271,7 +272,7 @@ distribute_list_prefix_unset (const char *ifname, enum distribute_type type,
return 0;
free (dist->prefix[DISTRIBUTE_OUT]);
- dist->prefix[DISTRIBUTE_OUT] = NULL;
+ dist->prefix[DISTRIBUTE_OUT] = NULL;
}
/* Apply this distribute-list to the interface. */
@@ -396,7 +397,7 @@ DEFUN (distribute_list,
dist = distribute_list_set (argv[2], type, argv[0]);
return CMD_SUCCESS;
-}
+}
ALIAS (distribute_list,
ipv6_distribute_list_cmd,
@@ -437,7 +438,7 @@ DEFUN (no_districute_list, no_distribute_list_cmd,
return CMD_WARNING;
}
return CMD_SUCCESS;
-}
+}
ALIAS (no_districute_list, no_ipv6_distribute_list_cmd,
"no distribute-list WORD (in|out) WORD",
@@ -467,7 +468,7 @@ DEFUN (districute_list_prefix_all,
type = DISTRIBUTE_OUT;
else
{
- vty_out (vty, "distribute list direction must be [in|out]%s",
+ vty_out (vty, "distribute list direction must be [in|out]%s",
VTY_NEWLINE);
return CMD_WARNING;
}
@@ -476,7 +477,7 @@ DEFUN (districute_list_prefix_all,
dist = distribute_list_prefix_set (NULL, type, argv[0]);
return CMD_SUCCESS;
-}
+}
ALIAS (districute_list_prefix_all,
ipv6_distribute_list_prefix_all_cmd,
@@ -507,7 +508,7 @@ DEFUN (no_districute_list_prefix_all,
type = DISTRIBUTE_OUT;
else
{
- vty_out (vty, "distribute list direction must be [in|out]%s",
+ vty_out (vty, "distribute list direction must be [in|out]%s",
VTY_NEWLINE);
return CMD_WARNING;
}
@@ -519,7 +520,7 @@ DEFUN (no_districute_list_prefix_all,
return CMD_WARNING;
}
return CMD_SUCCESS;
-}
+}
ALIAS (no_districute_list_prefix_all,
no_ipv6_distribute_list_prefix_all_cmd,
@@ -550,7 +551,7 @@ DEFUN (districute_list_prefix, distribute_list_prefix_cmd,
type = DISTRIBUTE_OUT;
else
{
- vty_out (vty, "distribute list direction must be [in|out]%s",
+ vty_out (vty, "distribute list direction must be [in|out]%s",
VTY_NEWLINE);
return CMD_WARNING;
}
@@ -559,7 +560,7 @@ DEFUN (districute_list_prefix, distribute_list_prefix_cmd,
dist = distribute_list_prefix_set (argv[2], type, argv[0]);
return CMD_SUCCESS;
-}
+}
ALIAS (districute_list_prefix, ipv6_distribute_list_prefix_cmd,
"distribute-list prefix WORD (in|out) WORD",
@@ -590,7 +591,7 @@ DEFUN (no_districute_list_prefix, no_distribute_list_prefix_cmd,
type = DISTRIBUTE_OUT;
else
{
- vty_out (vty, "distribute list direction must be [in|out]%s",
+ vty_out (vty, "distribute list direction must be [in|out]%s",
VTY_NEWLINE);
return CMD_WARNING;
}
@@ -602,7 +603,7 @@ DEFUN (no_districute_list_prefix, no_distribute_list_prefix_cmd,
return CMD_WARNING;
}
return CMD_SUCCESS;
-}
+}
ALIAS (no_districute_list_prefix, no_ipv6_distribute_list_prefix_cmd,
"no distribute-list prefix WORD (in|out) WORD",
@@ -709,7 +710,7 @@ config_write_distribute (struct vty *vty)
if (dist->list[DISTRIBUTE_IN])
{
- vty_out (vty, " distribute-list %s in %s%s",
+ vty_out (vty, " distribute-list %s in %s%s",
dist->list[DISTRIBUTE_IN],
dist->ifname ? dist->ifname : "",
VTY_NEWLINE);
@@ -718,7 +719,7 @@ config_write_distribute (struct vty *vty)
if (dist->list[DISTRIBUTE_OUT])
{
- vty_out (vty, " distribute-list %s out %s%s",
+ vty_out (vty, " distribute-list %s out %s%s",
dist->list[DISTRIBUTE_OUT],
dist->ifname ? dist->ifname : "",
diff --git a/lib/errno_names.c b/lib/errno_names.c
new file mode 100644
index 00000000..7238f1a7
--- /dev/null
+++ b/lib/errno_names.c
@@ -0,0 +1,383 @@
+/* Mapping Error Numbers to their names
+ * Copyright (C) 2010 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 <stddef.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "errno_names.h"
+
+/*==============================================================================
+ * Table to map error number to its name
+ */
+#define ERRNO(err) [err] = #err
+
+static const char* errno_name_table[] =
+{
+ /* Error number for no error
+ *
+ * (123456789012345), /-- no name is more than 15 characters
+ */
+ ERRNO(EOK), /* No error */
+
+ /* POSIX Error Numbers -- taken Open Group Base Specifications Issue 7
+ * IEEE Std 1003.1-2008
+ *
+ * Stream related stuff does not seeem to be fully supported.
+ */
+ ERRNO(E2BIG), /* Argument list too long. */
+ ERRNO(EACCES), /* Permission denied. */
+ ERRNO(EADDRINUSE), /* Address in use. */
+ ERRNO(EADDRNOTAVAIL), /* Address not available. */
+ ERRNO(EAFNOSUPPORT), /* Address family not supported. */
+#if EAGAIN != EWOULDBLOCK
+ ERRNO(EAGAIN), /* Resource unavailable, try again
+ (may be the same value as [EWOULDBLOCK]). */
+#endif
+ ERRNO(EALREADY), /* Connection already in progress. */
+ ERRNO(EBADF), /* Bad file descriptor. */
+ ERRNO(EBADMSG), /* Bad message. */
+ ERRNO(EBUSY), /* Device or resource busy. */
+ ERRNO(ECANCELED), /* Operation canceled. */
+ ERRNO(ECHILD), /* No child processes. */
+ ERRNO(ECONNABORTED), /* Connection aborted. */
+ ERRNO(ECONNREFUSED), /* Connection refused. */
+ ERRNO(ECONNRESET), /* Connection reset. */
+ ERRNO(EDEADLK), /* Resource deadlock would occur. */
+ ERRNO(EDESTADDRREQ), /* Destination address required. */
+ ERRNO(EDOM), /* Mathematics argument out of domain of function. */
+ ERRNO(EDQUOT), /* Reserved. */
+ ERRNO(EEXIST), /* File exists. */
+ ERRNO(EFAULT), /* Bad address. */
+ ERRNO(EFBIG), /* File too large. */
+ ERRNO(EHOSTUNREACH), /* Host is unreachable. */
+ ERRNO(EIDRM), /* Identifier removed. */
+ ERRNO(EILSEQ), /* Illegal byte sequence. */
+ ERRNO(EINPROGRESS), /* Operation in progress. */
+ ERRNO(EINTR), /* Interrupted function. */
+ ERRNO(EINVAL), /* Invalid argument. */
+ ERRNO(EIO), /* I/O error. */
+ ERRNO(EISCONN), /* Socket is connected. */
+ ERRNO(EISDIR), /* Is a directory. */
+ ERRNO(ELOOP), /* Too many levels of symbolic links. */
+ ERRNO(EMFILE), /* File descriptor value too large. */
+ ERRNO(EMLINK), /* Too many links. */
+ ERRNO(EMSGSIZE), /* Message too large. */
+ ERRNO(EMULTIHOP), /* Reserved. */
+ ERRNO(ENAMETOOLONG), /* Filename too long. */
+ ERRNO(ENETDOWN), /* Network is down. */
+ ERRNO(ENETRESET), /* Connection aborted by network. */
+ ERRNO(ENETUNREACH), /* Network unreachable. */
+ ERRNO(ENFILE), /* Too many files open in system. */
+ ERRNO(ENOBUFS), /* No buffer space available. */
+#ifdef ENODATA
+ ERRNO(ENODATA), /* No message is available on the STREAM head read
+ queue. */
+#endif
+ ERRNO(ENODEV), /* No such device. */
+ ERRNO(ENOENT), /* No such file or directory. */
+ ERRNO(ENOEXEC), /* Executable file format error. */
+ ERRNO(ENOLCK), /* No locks available. */
+ ERRNO(ENOLINK), /* Reserved. */
+ ERRNO(ENOMEM), /* Not enough space. */
+ ERRNO(ENOMSG), /* No message of the desired type. */
+ ERRNO(ENOPROTOOPT), /* Protocol not available. */
+ ERRNO(ENOSPC), /* No space left on device. */
+#ifdef ENOSR
+ ERRNO(ENOSR), /* No STREAM resources. */
+#endif
+#ifdef ENOSTR
+ ERRNO(ENOSTR), /* Not a STREAM. */
+#endif
+ ERRNO(ENOSYS), /* Function not supported. */
+ ERRNO(ENOTCONN), /* The socket is not connected. */
+ ERRNO(ENOTDIR), /* Not a directory. */
+ ERRNO(ENOTEMPTY), /* Directory not empty. */
+#ifdef ENOTRECOVERABLE
+ ERRNO(ENOTRECOVERABLE), /* State not recoverable. */
+#endif
+ ERRNO(ENOTSOCK), /* Not a socket. */
+ ERRNO(ENOTSUP), /* Not supported
+ (may be the same value as [EOPNOTSUPP]). */
+ ERRNO(ENOTTY), /* Inappropriate I/O control operation. */
+ ERRNO(ENXIO), /* No such device or address. */
+#if EOPNOTSUPP != ENOTSUP
+ ERRNO(EOPNOTSUPP), /* Operation not supported on socket
+ (may be the same value as [ENOTSUP]). */
+#endif
+ ERRNO(EOVERFLOW), /* Value too large to be stored in data type. */
+#ifdef EOWNERDEAD
+ ERRNO(EOWNERDEAD), /* Previous owner died. */
+#endif
+ ERRNO(EPERM), /* Operation not permitted. */
+ ERRNO(EPIPE), /* Broken pipe. */
+ ERRNO(EPROTO), /* Protocol error. */
+ ERRNO(EPROTONOSUPPORT), /* Protocol not supported. */
+ ERRNO(EPROTOTYPE), /* Protocol wrong type for socket. */
+ ERRNO(ERANGE), /* Result too large. */
+ ERRNO(EROFS), /* Read-only file system. */
+ ERRNO(ESPIPE), /* Invalid seek. */
+ ERRNO(ESRCH), /* No such process. */
+ ERRNO(ESTALE), /* Reserved. */
+#ifdef ETIME
+ ERRNO(ETIME), /* Stream ioctl() timeout. */
+#endif
+ ERRNO(ETIMEDOUT), /* Connection timed out. */
+ ERRNO(ETXTBSY), /* Text file busy. */
+ ERRNO(EWOULDBLOCK), /* Operation would block
+ (may be the same value as [EAGAIN]). */
+ ERRNO(EXDEV), /* Cross-device link. */
+
+/* Linux Error Numbers -- for 2.6.30, taken 8-Apr-2010.
+ *
+ * (123456789012345), /-- no name is more than 15 characters
+ */
+#ifdef EADV
+ ERRNO(EADV), /* Advertise error */
+#endif
+#ifdef EBADE
+ ERRNO(EBADE), /* Invalid exchange */
+#endif
+#ifdef EBADFD
+ ERRNO(EBADFD), /* File descriptor in bad state */
+#endif
+#ifdef EBADR
+ ERRNO(EBADR), /* Invalid request descriptor */
+#endif
+#ifdef EBADRQC
+ ERRNO(EBADRQC), /* Invalid request code */
+#endif
+#ifdef EBADSLT
+ ERRNO(EBADSLT), /* Invalid slot */
+#endif
+#ifdef EBFONT
+ ERRNO(EBFONT), /* Bad font file format */
+#endif
+#ifdef ECHRNG
+ ERRNO(ECHRNG), /* Channel number out of range */
+#endif
+#ifdef ECOMM
+ ERRNO(ECOMM), /* Communication error on send */
+#endif
+#ifdef EDEADLOCK
+ ERRNO(EDEADLOCK), /* same as EDEADLK */
+#endif
+#ifdef EDOTDOT
+ ERRNO(EDOTDOT), /* RFS specific error */
+#endif
+#ifdef EHOSTDOWN
+ ERRNO(EHOSTDOWN), /* Host is down */
+#endif
+#ifdef EISNAM
+ ERRNO(EISNAM), /* Is a named type file */
+#endif
+#ifdef EKEYEXPIRED
+ ERRNO(EKEYEXPIRED), /* Key has expired */
+#endif
+#ifdef EKEYREJECTED
+ ERRNO(EKEYREJECTED), /* Key was rejected by service */
+#endif
+#ifdef EKEYREVOKED
+ ERRNO(EKEYREVOKED), /* Key has been revoked */
+#endif
+#ifdef EL2HLT
+ ERRNO(EL2HLT), /* Level 2 halted */
+#endif
+#ifdef EL2NSYNC
+ ERRNO(EL2NSYNC), /* Level 2 not synchronized */
+#endif
+#ifdef EL3HLT
+ ERRNO(EL3HLT), /* Level 3 halted */
+#endif
+#ifdef EL3RST
+ ERRNO(EL3RST), /* Level 3 reset */
+#endif
+#ifdef ELIBACC
+ ERRNO(ELIBACC), /* Can not access a needed shared library */
+#endif
+#ifdef ELIBBAD
+ ERRNO(ELIBBAD), /* Accessing a corrupted shared library */
+#endif
+#ifdef ELIBEXEC
+ ERRNO(ELIBEXEC), /* Cannot exec a shared library directly */
+#endif
+#ifdef ELIBMAX
+ ERRNO(ELIBMAX), /* Attempting to link in too many shared libraries */
+#endif
+#ifdef ELIBSCN
+ ERRNO(ELIBSCN), /* .lib section in a.out corrupted */
+#endif
+#ifdef ELNRNG
+ ERRNO(ELNRNG), /* Link number out of range */
+#endif
+#ifdef EMEDIUMTYPE
+ ERRNO(EMEDIUMTYPE), /* Wrong medium type */
+#endif
+#ifdef ENAVAIL
+ ERRNO(ENAVAIL), /* No XENIX semaphores available */
+#endif
+#ifdef ENOANO
+ ERRNO(ENOANO), /* No anode */
+#endif
+#ifdef ENOCSI
+ ERRNO(ENOCSI), /* No CSI structure available */
+#endif
+#ifdef ENOKEY
+ ERRNO(ENOKEY), /* Required key not available */
+#endif
+#ifdef ENOMEDIUM
+ ERRNO(ENOMEDIUM), /* No medium found */
+#endif
+#ifdef ENONET
+ ERRNO(ENONET), /* Machine is not on the network */
+#endif
+#ifdef ENOPKG
+ ERRNO(ENOPKG), /* Package not installed */
+#endif
+#ifdef ENOTBLK
+ ERRNO(ENOTBLK), /* Block device required */
+#endif
+#ifdef ENOTNAM
+ ERRNO(ENOTNAM), /* Not a XENIX named type file */
+#endif
+#ifdef ENOTUNIQ
+ ERRNO(ENOTUNIQ), /* Name not unique on network */
+#endif
+#ifdef EPFNOSUPPORT
+ ERRNO(EPFNOSUPPORT), /* Protocol family not supported */
+#endif
+#ifdef EREMCHG
+ ERRNO(EREMCHG), /* Remote address changed */
+#endif
+#ifdef EREMOTE
+ ERRNO(EREMOTE), /* Object is remote */
+#endif
+#ifdef EREMOTEIO
+ ERRNO(EREMOTEIO), /* Remote I/O error */
+#endif
+#ifdef ERESTART
+ ERRNO(ERESTART), /* Interrupted system call should be restarted */
+#endif
+#ifdef ESHUTDOWN
+ ERRNO(ESHUTDOWN), /* Cannot send after transport endpoint shutdown */
+#endif
+#ifdef ESOCKTNOSUPPORT
+ ERRNO(ESOCKTNOSUPPORT), /* Socket type not supported */
+#endif
+#ifdef ESRMNT
+ ERRNO(ESRMNT), /* Srmount error */
+#endif
+#ifdef ESTRPIPE
+ ERRNO(ESTRPIPE), /* Streams pipe error */
+#endif
+#ifdef ETOOMANYREFS
+ ERRNO(ETOOMANYREFS), /* Too many references: cannot splice */
+#endif
+#ifdef EUCLEAN
+ ERRNO(EUCLEAN), /* Structure needs cleaning */
+#endif
+#ifdef EUNATCH
+ ERRNO(EUNATCH), /* Protocol driver not attached */
+#endif
+#ifdef EUSERS
+ ERRNO(EUSERS), /* Too many users */
+#endif
+#ifdef EXFULL
+ ERRNO(EXFULL), /* Exchange full */
+#endif
+} ;
+
+enum { errno_last = (sizeof(errno_name_table) / sizeof(char*)) - 1 } ;
+
+/*------------------------------------------------------------------------------
+ * Lookup the name for given error number.
+ *
+ * Returns: address of string, or NULL if not known
+ *
+ * NB: for 0 returns "EOK".
+ *
+ * NB: async-signal-safe and thread-safe !
+ */
+extern const char*
+errno_name_lookup(int err)
+{
+ if ((err < 0) || (err > errno_last))
+ return NULL ;
+ return errno_name_table[err] ;
+} ;
+
+/*==============================================================================
+ * Table to map EAI error number to its name -- the errors generated by
+ * getaddrinfo() and getnameinfo().
+ *
+ * At least one system uses -ve numbers for these... so the following will
+ * support either +ve or -ve values, provided that all have the same sign.
+ */
+#if EAI_AGAIN < 0
+enum { eai_sgn = -1 } ;
+#else
+enum { eai_sgn = +1 } ;
+#endif
+
+#define EAINO(eai) [eai * eai_sgn] = #eai
+
+static const char* eaino_name_table[] =
+{
+ /* Error number for no error
+ *
+ * (123456789012345), /-- no name is more than 15 characters
+ */
+ EAINO(EAI_OK), /* No error */
+
+ /* POSIX Error Numbers -- taken Open Group Base Specifications Issue 7
+ * IEEE Std 1003.1-2008
+ */
+ EAINO(EAI_AGAIN), /* Temporary failure in name resolution. */
+ EAINO(EAI_BADFLAGS), /* Invalid value for 'ai_flags' field. */
+ EAINO(EAI_FAIL), /* Non-recoverable failure in name res. */
+ EAINO(EAI_FAMILY), /* 'ai_family' not supported. */
+ EAINO(EAI_MEMORY), /* Memory allocation failure. */
+ EAINO(EAI_NONAME), /* NAME or SERVICE is unknown. */
+ EAINO(EAI_OVERFLOW), /* Argument buffer overflow. */
+ EAINO(EAI_SERVICE), /* SERVICE not supported for 'ai_socktype'. */
+ EAINO(EAI_SOCKTYPE), /* 'ai_socktype' not supported. */
+ EAINO(EAI_SYSTEM), /* System error returned in 'errno'. */
+} ;
+
+enum { eaino_last = (sizeof(eaino_name_table) / sizeof(char*)) - 1 } ;
+
+/*------------------------------------------------------------------------------
+ * Lookup the name for given error number.
+ *
+ * Returns: address of string, or NULL if not known
+ *
+ * NB: for 0 returns "EOK".
+ *
+ * NB: async-signal-safe and thread-safe !
+ */
+extern const char*
+eaino_name_lookup(int eai)
+{
+ eai *= eai_sgn ;
+ if ((eai < 0) || (eai > eaino_last))
+ return NULL ;
+ return eaino_name_table[eai] ;
+} ;
diff --git a/lib/errno_names.h b/lib/errno_names.h
new file mode 100644
index 00000000..a4f1f2e3
--- /dev/null
+++ b/lib/errno_names.h
@@ -0,0 +1,31 @@
+/* Mapping Error Numbers to their names -- header
+ * Copyright (C) 2010 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 EOK
+#define EOK 0
+#endif
+
+#ifndef EAI_OK
+#define EAI_OK 0
+#endif
+
+extern const char* errno_name_lookup(int err) ;
+extern const char* eaino_name_lookup(int err) ;
diff --git a/lib/filter.c b/lib/filter.c
index af8d587f..a607cb4e 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -1584,7 +1584,7 @@ filter_show (struct vty *vty, const char *name, afi_t afi)
/* Print the name of the protocol */
if (zlog_default)
vty_out (vty, "%s:%s",
- zlog_proto_names[zlog_default->protocol], VTY_NEWLINE);
+ zlog_get_proto_name(NULL), VTY_NEWLINE);
for (access = master->num.head; access; access = access->next)
{
@@ -1620,9 +1620,9 @@ filter_show (struct vty *vty, const char *name, afi_t afi)
vty_out (vty, " any%s", VTY_NEWLINE);
else
{
- vty_out (vty, " %s", inet_ntoa (filter->addr));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->addr));
if (filter->addr_mask.s_addr != 0)
- vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask));
+ vty_out (vty, ", wildcard bits %s", safe_inet_ntoa (filter->addr_mask));
vty_out (vty, "%s", VTY_NEWLINE);
}
}
@@ -1663,9 +1663,9 @@ filter_show (struct vty *vty, const char *name, afi_t afi)
vty_out (vty, " any%s", VTY_NEWLINE);
else
{
- vty_out (vty, " %s", inet_ntoa (filter->addr));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->addr));
if (filter->addr_mask.s_addr != 0)
- vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask));
+ vty_out (vty, ", wildcard bits %s", safe_inet_ntoa (filter->addr_mask));
vty_out (vty, "%s", VTY_NEWLINE);
}
}
@@ -1735,21 +1735,21 @@ config_write_access_cisco (struct vty *vty, struct filter *mfilter)
if (filter->addr_mask.s_addr == 0xffffffff)
vty_out (vty, " any");
else if (filter->addr_mask.s_addr == 0)
- vty_out (vty, " host %s", inet_ntoa (filter->addr));
+ vty_out (vty, " host %s", safe_inet_ntoa (filter->addr));
else
{
- vty_out (vty, " %s", inet_ntoa (filter->addr));
- vty_out (vty, " %s", inet_ntoa (filter->addr_mask));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->addr));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->addr_mask));
}
if (filter->mask_mask.s_addr == 0xffffffff)
vty_out (vty, " any");
else if (filter->mask_mask.s_addr == 0)
- vty_out (vty, " host %s", inet_ntoa (filter->mask));
+ vty_out (vty, " host %s", safe_inet_ntoa (filter->mask));
else
{
- vty_out (vty, " %s", inet_ntoa (filter->mask));
- vty_out (vty, " %s", inet_ntoa (filter->mask_mask));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->mask));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->mask_mask));
}
vty_out (vty, "%s", VTY_NEWLINE);
}
@@ -1759,9 +1759,9 @@ config_write_access_cisco (struct vty *vty, struct filter *mfilter)
vty_out (vty, " any%s", VTY_NEWLINE);
else
{
- vty_out (vty, " %s", inet_ntoa (filter->addr));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->addr));
if (filter->addr_mask.s_addr != 0)
- vty_out (vty, " %s", inet_ntoa (filter->addr_mask));
+ vty_out (vty, " %s", safe_inet_ntoa (filter->addr_mask));
vty_out (vty, "%s", VTY_NEWLINE);
}
}
diff --git a/lib/getopt.c b/lib/getopt.c
index c784fb6c..ec98feac 100644
--- a/lib/getopt.c
+++ b/lib/getopt.c
@@ -3,11 +3,11 @@
"Keep this file name-space clean" means, talk to drepper@gnu.org
before changing it!
- Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
- Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
+ 1996, 1997, 1998, 2005 Free Software Foundation, Inc.
- NOTE: The canonical source of this file is maintained with the GNU C Library.
- Bugs can be reported to bug-glibc@gnu.org.
+ NOTE: This source is derived from an old version taken from the GNU C
+ Library (glibc).
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@@ -21,9 +21,9 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
USA. */
-
+
/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
Ditto for AIX 3.2 and <stdlib.h>. */
#ifndef _NO_PROTO
@@ -34,16 +34,7 @@
# include <config.h>
#endif
-#include <zebra.h>
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
- reject `defined (const)'. */
-# ifndef const
-# define const
-# endif
-#endif
-
+#include "zebra.h"
#include <stdio.h>
/* Comment out all this code if we are using the GNU C Library, and are not
@@ -67,12 +58,12 @@
/* This needs to come after some library #include
to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
+#ifdef __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
contain conflicting prototypes for getopt. */
# include <stdlib.h>
# include <unistd.h>
-#endif /* GNU C library. */
+#endif /* GNU C library. */
#ifdef VMS
# include <unixlib.h>
@@ -84,11 +75,11 @@
#ifndef _
/* This is for other GNU distributions with internationalized messages.
When compiling libc, the _ macro is predefined. */
-# ifdef HAVE_LIBINTL_H
+# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
# include <libintl.h>
-# define _(msgid) gettext (msgid)
+# define _(msgid) gettext (msgid)
# else
-# define _(msgid) (msgid)
+# define _(msgid) (msgid)
# endif
#endif
@@ -146,6 +137,9 @@ int __getopt_initialized = 0;
static char *nextchar;
+/* Empty string -- not const ! */
+static char empty_string[1] = { '\0' } ;
+
/* Callers store zero here to inhibit the error message
for unrecognized options. */
@@ -193,38 +187,47 @@ static enum
/* Value of POSIXLY_CORRECT environment variable. */
static char *posixly_correct;
-
-#ifdef __GNU_LIBRARY__
+
+#ifdef __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
because there are many ways it can cause trouble.
On some systems, it contains special magic macros that don't work
in GCC. */
# include <string.h>
-# define my_index strchr
-#else
+# define my_index (const char*)strchr
+
+#else /* not __GNU_LIBRARY__ */
# if HAVE_STRING_H
# include <string.h>
# else
-# include <strings.h>
+# if HAVE_STRINGS_H
+# include <strings.h>
+# endif
# endif
/* Avoid depending on library functions or files
whose names are inconsistent. */
-#ifndef getenv
-extern char *getenv ();
-#endif
+# if HAVE_STDLIB_H && HAVE_DECL_GETENV
+# include <stdlib.h>
+# elif !defined(getenv)
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+extern char *getenv (const char *);
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+# endif /* HAVE_STDLIB_H && HAVE_DECL_GETENV */
-static char *
-my_index (str, chr)
- const char *str;
- int chr;
+static const char *
+my_index (const char *str, int chr)
{
while (*str)
{
if (*str == chr)
- return (char *) str;
+ return str;
str++;
}
return 0;
@@ -232,18 +235,18 @@ my_index (str, chr)
/* If using GCC, we can safely declare strlen this way.
If not using GCC, it is ok not to declare it. */
-#ifdef __GNUC__
+# ifdef __GNUC__
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
That was relevant to code that was here before. */
-# if (!defined __STDC__ || !__STDC__) && !defined strlen
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
/* gcc with -traditional declares the built-in strlen to return int,
and has done so at least since version 2.4.5. -- rms. */
extern int strlen (const char *);
-# endif /* not __STDC__ */
-#endif /* __GNUC__ */
+# endif /* not __STDC__ */
+# endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
-#endif /* not __GNU_LIBRARY__ */
-
/* Handle permutation of arguments. */
/* Describe the part of ARGV that contains non-options that have
@@ -283,15 +286,15 @@ text_set_element (__libc_subinit, store_args_and_env);
# endif /* text_set_element */
# define SWAP_FLAGS(ch1, ch2) \
- if (nonoption_flags_len > 0) \
- { \
- char __tmp = __getopt_nonoption_flags[ch1]; \
- __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
- __getopt_nonoption_flags[ch2] = __tmp; \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
}
-#else /* !_LIBC */
+#else /* !_LIBC */
# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
+#endif /* _LIBC */
/* Exchange two adjacent subsequences of ARGV.
One subsequence is elements [first_nonopt,last_nonopt)
@@ -302,13 +305,8 @@ text_set_element (__libc_subinit, store_args_and_env);
`first_nonopt' and `last_nonopt' are relocated so that they describe
the new indices of the non-options in ARGV after they are moved. */
-#if defined __STDC__ && __STDC__
-static void exchange (char **);
-#endif
-
static void
-exchange (argv)
- char **argv;
+exchange (char **argv)
{
int bottom = first_nonopt;
int middle = last_nonopt;
@@ -327,57 +325,57 @@ exchange (argv)
if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
{
/* We must extend the array. The user plays games with us and
- presents new arguments. */
- char *new_str = malloc (top + 1);
+ presents new arguments. */
+ char *new_str = (char *) malloc (top + 1);
if (new_str == NULL)
- nonoption_flags_len = nonoption_flags_max_len = 0;
+ nonoption_flags_len = nonoption_flags_max_len = 0;
else
- {
- memset (__mempcpy (new_str, __getopt_nonoption_flags,
- nonoption_flags_max_len),
- '\0', top + 1 - nonoption_flags_max_len);
- nonoption_flags_max_len = top + 1;
- __getopt_nonoption_flags = new_str;
- }
+ {
+ memset (mempcpy (new_str, __getopt_nonoption_flags,
+ nonoption_flags_max_len),
+ '\0', top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
}
#endif
while (top > middle && middle > bottom)
{
if (top - middle > middle - bottom)
- {
- /* Bottom segment is the short one. */
- int len = middle - bottom;
- register int i;
-
- /* Swap it with the top part of the top segment. */
- for (i = 0; i < len; i++)
- {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[top - (middle - bottom) + i];
- argv[top - (middle - bottom) + i] = tem;
- SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
- }
- /* Exclude the moved bottom segment from further swapping. */
- top -= len;
- }
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
else
- {
- /* Top segment is the short one. */
- int len = top - middle;
- register int i;
-
- /* Swap it with the bottom part of the bottom segment. */
- for (i = 0; i < len; i++)
- {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[middle + i];
- argv[middle + i] = tem;
- SWAP_FLAGS (bottom + i, middle + i);
- }
- /* Exclude the moved top segment from further swapping. */
- bottom += len;
- }
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
}
/* Update records for the slots the non-options now occupy. */
@@ -388,14 +386,10 @@ exchange (argv)
/* Initialize the internal data when the first call is made. */
-#if defined __STDC__ && __STDC__
-static const char *_getopt_initialize (int, char *const *, const char *);
-#endif
static const char *
-_getopt_initialize (argc, argv, optstring)
- int argc;
- char *const *argv;
- const char *optstring;
+_getopt_initialize (int argc,
+ char **argv,
+ const char *optstring)
{
/* Start processing options with ARGV-element 1 (since ARGV-element 0
is the program name); the sequence of previously skipped
@@ -429,25 +423,25 @@ _getopt_initialize (argc, argv, optstring)
&& argc == original_argc && argv == original_argv)
{
if (nonoption_flags_max_len == 0)
- {
- if (__getopt_nonoption_flags == NULL
- || __getopt_nonoption_flags[0] == '\0')
- nonoption_flags_max_len = -1;
- else
- {
- const char *orig_str = __getopt_nonoption_flags;
- int len = nonoption_flags_max_len = strlen (orig_str);
- if (nonoption_flags_max_len < argc)
- nonoption_flags_max_len = argc;
- __getopt_nonoption_flags =
- (char *) malloc (nonoption_flags_max_len);
- if (__getopt_nonoption_flags == NULL)
- nonoption_flags_max_len = -1;
- else
- memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
- '\0', nonoption_flags_max_len - len);
- }
- }
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ memset (mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', nonoption_flags_max_len - len);
+ }
+ }
nonoption_flags_len = nonoption_flags_max_len;
}
else
@@ -456,7 +450,7 @@ _getopt_initialize (argc, argv, optstring)
return optstring;
}
-
+
/* Scan elements of ARGV (whose length is ARGC) for option characters
given in OPTSTRING.
@@ -514,20 +508,26 @@ _getopt_initialize (argc, argv, optstring)
long-named options. */
int
-_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
- int argc;
- char *const *argv;
- const char *optstring;
- const struct option *longopts;
- int *longind;
- int long_only;
+_getopt_internal (int argc, char *const *argv_nominal, const char *optstring,
+ const struct option *longopts,
+ int *longind, int long_only)
{
optarg = NULL;
+ char** argv ;
+
+ union
+ {
+ char *const *nominal ;
+ char **actual ;
+ } miyagi ;
+
+ miyagi.nominal = argv_nominal ;
+ argv = miyagi.actual ;
if (optind == 0 || !__getopt_initialized)
{
if (optind == 0)
- optind = 1; /* Don't scan ARGV[0], the program name. */
+ optind = 1; /* Don't scan ARGV[0], the program name. */
optstring = _getopt_initialize (argc, argv, optstring);
__getopt_initialized = 1;
}
@@ -537,9 +537,9 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
from the shell indicating it is not an option. The later information
is only used when the used in the GNU libc. */
#ifdef _LIBC
-# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
- || (optind < nonoption_flags_len \
- && __getopt_nonoption_flags[optind] == '1'))
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
#else
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
#endif
@@ -549,76 +549,76 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
/* Advance to the next ARGV-element. */
/* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
- moved back by the user (who may also have changed the arguments). */
+ moved back by the user (who may also have changed the arguments). */
if (last_nonopt > optind)
- last_nonopt = optind;
+ last_nonopt = optind;
if (first_nonopt > optind)
- first_nonopt = optind;
+ first_nonopt = optind;
if (ordering == PERMUTE)
- {
- /* If we have just processed some options following some non-options,
- exchange them so that the options come first. */
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
- if (first_nonopt != last_nonopt && last_nonopt != optind)
- exchange ((char **) argv);
- else if (last_nonopt != optind)
- first_nonopt = optind;
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange (argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
- /* Skip any additional non-options
- and extend the range of non-options previously skipped. */
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
- while (optind < argc && NONOPTION_P)
- optind++;
- last_nonopt = optind;
- }
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
/* The special ARGV-element `--' means premature end of options.
- Skip it like a null option,
- then exchange with previous non-options as if it were an option,
- then skip everything else like a non-option. */
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
if (optind != argc && !strcmp (argv[optind], "--"))
- {
- optind++;
+ {
+ optind++;
- if (first_nonopt != last_nonopt && last_nonopt != optind)
- exchange ((char **) argv);
- else if (first_nonopt == last_nonopt)
- first_nonopt = optind;
- last_nonopt = argc;
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange (argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
- optind = argc;
- }
+ optind = argc;
+ }
/* If we have done all the ARGV-elements, stop the scan
- and back over any non-options that we skipped and permuted. */
+ and back over any non-options that we skipped and permuted. */
if (optind == argc)
- {
- /* Set the next-arg-index to point at the non-options
- that we previously skipped, so the caller will digest them. */
- if (first_nonopt != last_nonopt)
- optind = first_nonopt;
- return -1;
- }
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
/* If we have come to a non-option and did not permute it,
- either stop the scan or describe it to the caller and pass it by. */
+ either stop the scan or describe it to the caller and pass it by. */
if (NONOPTION_P)
- {
- if (ordering == REQUIRE_ORDER)
- return -1;
- optarg = argv[optind++];
- return 1;
- }
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
/* We have found another option-ARGV-element.
- Skip the initial punctuation. */
+ Skip the initial punctuation. */
nextchar = (argv[optind] + 1
- + (longopts != NULL && argv[optind][1] == '-'));
+ + (longopts != NULL && argv[optind][1] == '-'));
}
/* Decode the current option-ARGV-element. */
@@ -638,7 +638,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
if (longopts != NULL
&& (argv[optind][1] == '-'
- || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
{
char *nameend;
const struct option *p;
@@ -649,132 +649,132 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
int option_index;
for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */ ;
+ /* Do nothing. */ ;
/* Test all long options for either exact match
- or abbreviated matches. */
+ or abbreviated matches. */
for (p = longopts, option_index = 0; p->name; p++, option_index++)
- if (!strncmp (p->name, nextchar, nameend - nextchar))
- {
- if ((unsigned int) (nameend - nextchar)
- == (unsigned int) strlen (p->name))
- {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- }
- else if (pfound == NULL)
- {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- }
- else
- /* Second or later nonexact match found. */
- ambig = 1;
- }
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
if (ambig && !exact)
- {
- if (opterr)
- fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen (nextchar);
- optind++;
- optopt = 0;
- return '?';
- }
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
if (pfound != NULL)
- {
- option_index = indfound;
- optind++;
- if (*nameend)
- {
- /* Don't test has_arg with >, because some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else
- {
- if (opterr)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
{
- if (argv[optind - 1][1] == '-')
- /* --option */
- fprintf (stderr,
- _("%s: option `--%s' doesn't allow an argument\n"),
- argv[0], pfound->name);
- else
- /* +option or -option */
- fprintf (stderr,
- _("%s: option `%c%s' doesn't allow an argument\n"),
- argv[0], argv[optind - 1][0], pfound->name);
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
}
-
- nextchar += strlen (nextchar);
-
- optopt = pfound->val;
- return '?';
- }
- }
- else if (pfound->has_arg == 1)
- {
- if (optind < argc)
- optarg = argv[optind++];
- else
- {
- if (opterr)
- fprintf (stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- optopt = pfound->val;
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen (nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag)
- {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
/* Can't find it as a long option. If this is not getopt_long_only,
- or the option starts with '--' or is not a valid short
- option, then it's an error.
- Otherwise interpret it as a short option. */
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
if (!long_only || argv[optind][1] == '-'
- || my_index (optstring, *nextchar) == NULL)
- {
- if (opterr)
- {
- if (argv[optind][1] == '-')
- /* --option */
- fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
- argv[0], nextchar);
- else
- /* +option or -option */
- fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
- argv[0], argv[optind][0], nextchar);
- }
- nextchar = (char *) "";
- optind++;
- optopt = 0;
- return '?';
- }
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = empty_string ;
+ optind++;
+ optopt = 0;
+ return '?';
+ }
}
/* Look at and handle the next short option-character. */
{
char c = *nextchar++;
- char *temp = my_index (optstring, c);
+ const char *temp = my_index (optstring, c);
/* Increment `optind' when we start to process its last character. */
if (*nextchar == '\0')
@@ -782,188 +782,188 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
if (temp == NULL || c == ':')
{
- if (opterr)
- {
- if (posixly_correct)
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr, _("%s: illegal option -- %c\n"),
- argv[0], c);
- else
- fprintf (stderr, _("%s: invalid option -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- return '?';
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
}
/* Convenience. Treat POSIX -W foo same as long option --foo */
if (temp[0] == 'W' && temp[1] == ';')
{
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = 0;
- int option_index;
-
- /* This is an option that requires an argument. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the rest as an arg,
- we must advance to the next element now. */
- optind++;
- }
- else if (optind == argc)
- {
- if (opterr)
- {
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr, _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- return c;
- }
- else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt as argument. */
- optarg = argv[optind++];
-
- /* optarg is now the argument, see if it's in the
- table of longopts. */
-
- for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */ ;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name; p++, option_index++)
- if (!strncmp (p->name, nextchar, nameend - nextchar))
- {
- if ((unsigned int) (nameend - nextchar) == strlen (p->name))
- {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- }
- else if (pfound == NULL)
- {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- }
- else
- /* Second or later nonexact match found. */
- ambig = 1;
- }
- if (ambig && !exact)
- {
- if (opterr)
- fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen (nextchar);
- optind++;
- return '?';
- }
- if (pfound != NULL)
- {
- option_index = indfound;
- if (*nameend)
- {
- /* Don't test has_arg with >, because some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else
- {
- if (opterr)
- fprintf (stderr, _("\
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ fprintf (stderr, _("\
%s: option `-W %s' doesn't allow an argument\n"),
- argv[0], pfound->name);
-
- nextchar += strlen (nextchar);
- return '?';
- }
- }
- else if (pfound->has_arg == 1)
- {
- if (optind < argc)
- optarg = argv[optind++];
- else
- {
- if (opterr)
- fprintf (stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen (nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag)
- {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
- nextchar = NULL;
- return 'W'; /* Let the application handle it. */
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
}
if (temp[1] == ':')
{
- if (temp[2] == ':')
- {
- /* This is an option that accepts an argument optionally. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- optind++;
- }
- else
- optarg = NULL;
- nextchar = NULL;
- }
- else
- {
- /* This is an option that requires an argument. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the rest as an arg,
- we must advance to the next element now. */
- optind++;
- }
- else if (optind == argc)
- {
- if (opterr)
- {
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- }
- else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt as argument. */
- optarg = argv[optind++];
- nextchar = NULL;
- }
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
}
return c;
}
@@ -972,30 +972,25 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
#ifdef REALLY_NEED_PLAIN_GETOPT
int
-getopt (argc, argv, optstring)
- int argc;
- char *const *argv;
- const char *optstring;
+getopt (int argc, char *const *argv, const char *optstring)
{
return _getopt_internal (argc, argv, optstring,
- (const struct option *) 0,
- (int *) 0,
- 0);
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
}
#endif /* REALLY_NEED_PLAIN_GETOPT */
-#endif /* Not ELIDE_CODE. */
-
+#endif /* Not ELIDE_CODE. */
+
#ifdef TEST
/* Compile with -DTEST to make an executable for use in testing
the above definition of `getopt'. */
int
-main (argc, argv)
- int argc;
- char **argv;
+main (int argc, char **argv)
{
int c;
int digit_optind = 0;
@@ -1006,51 +1001,51 @@ main (argc, argv)
c = getopt (argc, argv, "abc:d:0123456789");
if (c == -1)
- break;
+ break;
switch (c)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digit_optind != 0 && digit_optind != this_option_optind)
- printf ("digits occur in two different argv-elements.\n");
- digit_optind = this_option_optind;
- printf ("option %c\n", c);
- break;
-
- case 'a':
- printf ("option a\n");
- break;
-
- case 'b':
- printf ("option b\n");
- break;
-
- case 'c':
- printf ("option c with value `%s'\n", optarg);
- break;
-
- case '?':
- break;
-
- default:
- printf ("?? getopt returned character code 0%o ??\n", c);
- }
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
}
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
- printf ("%s ", argv[optind++]);
+ printf ("%s ", argv[optind++]);
printf ("\n");
}
@@ -1058,3 +1053,4 @@ main (argc, argv)
}
#endif /* TEST */
+
diff --git a/lib/getopt.h b/lib/getopt.h
index b359a47b..c2eb9ed3 100644
--- a/lib/getopt.h
+++ b/lib/getopt.h
@@ -1,5 +1,6 @@
/* Declarations for getopt.
- Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+ Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 2000,
+ 2002 Free Software Foundation, Inc.
NOTE: The canonical source of this file is maintained with the GNU C Library.
Bugs can be reported to bug-glibc@gnu.org.
@@ -16,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
USA. */
#ifndef _GETOPT_H
@@ -34,7 +35,7 @@
* to use the system version.
*/
-#ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
#endif
@@ -75,9 +76,9 @@ extern int optopt;
zero.
The field `has_arg' is:
- no_argument (or 0) if the option does not take an argument,
- required_argument (or 1) if the option requires an argument,
- optional_argument (or 2) if the option takes an optional argument.
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
If the field `flag' is not NULL, it points to a variable that is set
to the value given in the field `val' when the option is found, but
@@ -92,11 +93,7 @@ extern int optopt;
struct option
{
-#if defined (__STDC__) && __STDC__
const char *name;
-#else
- char *name;
-#endif
/* has_arg can't be an enum because some compilers complain about
type mismatches in all the code that assumes it is an int. */
int has_arg;
@@ -106,53 +103,38 @@ struct option
/* Names for the values of the `has_arg' field of `struct option'. */
-#define no_argument 0
-#define required_argument 1
-#define optional_argument 2
-
-#if defined (__STDC__) && __STDC__
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
-#if REALLY_NEED_PLAIN_GETOPT
+#if REALLY_NEED_PLAIN_GETOPT /*------------------------------------*/
-/*
- * getopt is defined in POSIX.2. Assume that if the system defines
+/* getopt is defined in POSIX.2. Assume that if the system defines
* getopt that it complies with POSIX.2. If not, an autoconf test
* should be written to define NONPOSIX_GETOPT_DEFINITION.
*/
-#ifndef NONPOSIX_GETOPT_DEFINITION
+
+# ifndef NONPOSIX_GETOPT_DEFINITION
extern int getopt (int argc, char *const *argv, const char *shortopts);
-#else /* NONPOSIX_GETOPT_DEFINITION */
+# else /* NONPOSIX_GETOPT_DEFINITION */
extern int getopt (void);
-#endif /* NONPOSIX_GETOPT_DEFINITION */
-
-#endif
+# endif /* NONPOSIX_GETOPT_DEFINITION */
+#endif /* REALLY_NEED_PLAIN_GETOPT ------------------------------------*/
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind);
+ const struct option *longopts, int *longind);
extern int getopt_long_only (int argc, char *const *argv,
- const char *shortopts,
- const struct option *longopts, int *longind);
+ const char *shortopts,
+ const struct option *longopts, int *longind);
/* Internal only. Users should not call this directly. */
extern int _getopt_internal (int argc, char *const *argv,
- const char *shortopts,
- const struct option *longopts, int *longind,
- int long_only);
-#else /* not __STDC__ */
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-extern int getopt ();
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-extern int getopt_long ();
-extern int getopt_long_only ();
-
-extern int _getopt_internal ();
-
-#endif /* __STDC__ */
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
-#ifdef __cplusplus
+#ifdef __cplusplus
}
#endif
diff --git a/lib/getopt1.c b/lib/getopt1.c
index 985f12c5..dc36fd0e 100644
--- a/lib/getopt1.c
+++ b/lib/getopt1.c
@@ -1,9 +1,9 @@
/* getopt_long and getopt_long_only entry points for GNU getopt.
- Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2005
Free Software Foundation, Inc.
- NOTE: The canonical source of this file is maintained with the GNU C Library.
- Bugs can be reported to bug-glibc@gnu.org.
+ NOTE: This source is derived from an old version taken from the GNU C
+ Library (glibc).
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@@ -17,24 +17,16 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
USA. */
-
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
-#include <zebra.h>
+#include "zebra.h"
#include "getopt.h"
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
- reject `defined (const)'. */
-#ifndef const
-#define const
-#endif
-#endif
-
#include <stdio.h>
/* Comment out all this code if we are using the GNU C Library, and are not
@@ -62,17 +54,13 @@
#include <stdlib.h>
#endif
-#ifndef NULL
+#ifndef NULL
#define NULL 0
#endif
int
-getopt_long (argc, argv, options, long_options, opt_index)
- int argc;
- char *const *argv;
- const char *options;
- const struct option *long_options;
- int *opt_index;
+getopt_long (int argc, char *const *argv, const char *options,
+ const struct option *long_options, int *opt_index)
{
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}
@@ -83,27 +71,21 @@ getopt_long (argc, argv, options, long_options, opt_index)
instead. */
int
-getopt_long_only (argc, argv, options, long_options, opt_index)
- int argc;
- char *const *argv;
- const char *options;
- const struct option *long_options;
- int *opt_index;
+getopt_long_only (int argc, char *const *argv, const char *options,
+ const struct option *long_options, int *opt_index)
{
return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}
-#endif /* Not ELIDE_CODE. */
-
+#endif /* Not ELIDE_CODE. */
+
#ifdef TEST
#include <stdio.h>
int
-main (argc, argv)
- int argc;
- char **argv;
+main (int argc, char **argv)
{
int c;
int digit_optind = 0;
@@ -114,74 +96,74 @@ main (argc, argv)
int option_index = 0;
static struct option long_options[] =
{
- {"add", 1, 0, 0},
- {"append", 0, 0, 0},
- {"delete", 1, 0, 0},
- {"verbose", 0, 0, 0},
- {"create", 0, 0, 0},
- {"file", 1, 0, 0},
- {0, 0, 0, 0}
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
};
c = getopt_long (argc, argv, "abc:d:0123456789",
- long_options, &option_index);
+ long_options, &option_index);
if (c == -1)
- break;
+ break;
switch (c)
- {
- case 0:
- printf ("option %s", long_options[option_index].name);
- if (optarg)
- printf (" with arg %s", optarg);
- printf ("\n");
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digit_optind != 0 && digit_optind != this_option_optind)
- printf ("digits occur in two different argv-elements.\n");
- digit_optind = this_option_optind;
- printf ("option %c\n", c);
- break;
-
- case 'a':
- printf ("option a\n");
- break;
-
- case 'b':
- printf ("option b\n");
- break;
-
- case 'c':
- printf ("option c with value `%s'\n", optarg);
- break;
-
- case 'd':
- printf ("option d with value `%s'\n", optarg);
- break;
-
- case '?':
- break;
-
- default:
- printf ("?? getopt returned character code 0%o ??\n", c);
- }
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
}
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
- printf ("%s ", argv[optind++]);
+ printf ("%s ", argv[optind++]);
printf ("\n");
}
diff --git a/lib/heap.c b/lib/heap.c
new file mode 100644
index 00000000..3a7ed360
--- /dev/null
+++ b/lib/heap.c
@@ -0,0 +1,517 @@
+/* Generic heap data structure -- 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 "zassert.h"
+#include "heap.h"
+#include "memory.h"
+
+/* Heaps are implemented as a structure which includes a vector structure.
+ * So items in the heap are items in a vector, which are pointers to the item
+ * values.
+ *
+ * The heap structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the heap operations
+ * require the address of the heap structure -- see typedef for heap.
+ *
+ * An essential component of a heap is its comparison function. So, a heap
+ * CANNOT be used before it has been initialised and the comparison function
+ * set. (This is unlike a vector, which may be implicitly initialised empty by
+ * zeroising the vector structure.)
+ *
+ * Items may be pushed onto or popped off the heap, which is organised so that
+ * the top item has the smallest value -- according to the heap's comparison
+ * function. (For equal values, the order is undefined.)
+ *
+ * The top item in the heap may be examined, and its value may be updated.
+ * (Updating the top item is more efficient than popping and then pushing it.)
+ *
+ * Items may be deleted from the heap. Items may have their value updated
+ * while they are in the heap. Both of these operations cause the heap to be
+ * reorganised to maintain the heap's partial ordering. Note: these operations
+ * require knowledge of where in the heap the item is -- which, by default, is
+ * done by a linear scan of the heap. For large heaps, there is the option to
+ * keep a "backlink" from the item to it's heap position.
+ *
+ * Vectors may be pushed onto a heap -- copying or moving the contents.
+ *
+ * Heaps may popped to a vector -- copying or moving the contents. The
+ * resulting vector is fully sorted.
+ *
+ * ----------------------------
+ * Comparison function for heap.
+ *
+ * int heap_cmp(...** a, ...** b) ...
+ *
+ * Must return -1, 0, +1 : where -1 => a < b, 0 => a == b & +1 => a > b
+ *
+ * Heap will sort "smallest" to the top. If you want the biggest at the top,
+ * return -1 * usual comparison. Note that the effective heap ordering for
+ * equal values is, essentially, random.
+ *
+ * NB: like other comparison functions (cf qsort) the parameters are pointers
+ * to pointers to the value.
+ *
+ * NB: there should never be NULL items in the heap.
+ */
+
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+static heap
+heap_setup(heap h, int new_vector, vector_index size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset) ;
+
+/* Initialize heap -- allocating heap structure if required.
+ *
+ * Does not allocate the underlying vector if the heap is initialised empty.
+ *
+ * eg:
+ *
+ * ... = heap_new_init_simple(NULL, 0, (heap_cmp*)my_cmp)
+ *
+ * See: #define heap_init_new_simple(h, size, cmp)
+ * #define heap_init_new_backlinked(h, size, cmp, offset)
+ *
+ * NB: when initialising an existing heap structure it is ESSENTIAL that
+ * any previous heap and its contents have been released, because this
+ * function simply discards whatever was there before. (This function may
+ * be called to initialise a heap structure which has never been
+ * initialised.)
+ *
+ * Backlink:
+ *
+ * The heap_delete_item and heap_update_item functions need the heap
+ * position of the item. The default way of finding that is to scan the
+ * underlying heap array, looking for the address of the item.
+ *
+ * If either of these functions is done often and on large heaps, it is
+ * possible to speed this up by implementing a 'backlink'. This requires
+ * a field of type heap_backlink_t in the item structure, and it is the
+ * offset of that which must be initialised here, eg:
+ *
+ * ... = heap_new_init_backlinked(NULL, 0, (heap_cmp*)my_cmp,
+ * offset_of(struct xxx_heap_item, backlink)) ;
+ *
+ * This adds a little extra work to every change in the heap -- keeping the
+ * backlink of any moved item up to date. But avoids a linear search for
+ * every heap_delete_item or heap_update_item.
+ *
+ * Returns the heap which has been initialised.
+ */
+heap
+heap_init_new(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset)
+{
+ if (h == NULL)
+ h = XCALLOC(MTYPE_HEAP, sizeof(struct heap)) ;
+ else
+ memset(h, 0, sizeof(struct heap)) ;
+
+ return heap_setup(h, 1, size, cmp, with_backlink, backlink_offset) ;
+} ;
+
+/* Reinitialise heap (or create a new one, if h == NULL).
+ *
+ * Allocates heap structure if none given -- allocating vector if size != 0.
+ * Otherwise, re-initialise the heap and any vector (reusing its memory).
+ *
+ * See: #define heap_re_init_simple(h, size, cmp)
+ * #define heap_re_init_backlinked(h, size, cmp, offset)
+ *
+ * NB: when reinitialising an existing heap it is the caller's
+ * responsibility to release any item values *before* doing this.
+ *
+ * Returns the heap that has been reinitialised.
+ */
+heap
+heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset)
+{
+ if (h == NULL)
+ return heap_init_new(h, size, cmp, with_backlink, backlink_offset) ;
+ else
+ return heap_setup(h, 0, size, cmp, with_backlink, backlink_offset) ;
+} ;
+
+/* Release heap contents (underlying vector), and (if required) release the
+ * heap structure.
+ *
+ * Returns NULL if releases heap, otherwise the reset heap.
+ *
+ * If does not release the heap, it retains the comparison function and any
+ * backlink setting -- so heap can be reused without reinitialising it.
+ *
+ * NB: it is the callers responsibility to release any heap item values
+ * *before* doing this.
+ */
+heap
+heap_reset(heap h, int free_structure)
+{
+ vector_reset_keep(&h->v) ; /* vector structure is embedded in the heap */
+
+ if (free_structure)
+ XFREE(MTYPE_VECTOR, h) ; /* sets h = NULL */
+
+ return h ;
+} ;
+
+/* Common set-up for heap_init_new() & heap_reset().
+ */
+static heap
+heap_setup(heap h, int new_vector, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset)
+{
+ assert(cmp != NULL) ; /* or there will be tears */
+
+ h->cmp = cmp ;
+ h->state = with_backlink ? Heap_Has_Backlink : 0 ;
+ h->backlink_offset = backlink_offset ;
+
+ if (new_vector)
+ vector_init_new(&h->v, size) ;
+ else
+ vector_re_init(&h->v, size) ;
+
+ return h ;
+} ;
+
+/* Ream (another) item out of the given heap.
+ *
+ * If heap is empty, release the underlying vector, and (if required) release
+ * the heap structure.
+ *
+ * See: #define heap_ream_free(h) heap_ream(h, 1)
+ * #define heap_ream_keep(h) heap_ream(h, 0)
+ *
+ * Useful for emptying out and resetting/discarding a heap:
+ *
+ * while ((p_v = heap_ream_free(h)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL when heap is empty (and structure has been freed, if required).
+ *
+ * If does not release the heap, it retains the comparison function and any
+ * backlink setting -- so heap can be reused without reinitialising it.
+ *
+ * NB: once the process of reaming a heap has started: (a) MUST NOT attempt to
+ * use the heap until process completes, and (b) MUST complete the process.
+ *
+ * NB: items are reamed out in no defined order.
+ */
+p_vector_item
+heap_ream(heap h, int free_structure)
+{
+ p_vector_item p_v ;
+
+ if (h == NULL)
+ return NULL ;
+
+ if ((p_v = vector_ream_keep(&h->v)) == NULL)
+ heap_reset(h, free_structure) ;
+
+ return p_v ;
+} ;
+
+/*==============================================================================
+ * Simple Heap Operations -- see also the Inline functions.
+ */
+
+/* Pop item off the heap.
+ *
+ * Returns the popped value, which is NULL if the heap was (and still is) empty.
+ */
+p_vector_item
+heap_pop_item(heap h)
+{
+ p_vector_item p_v ;
+ p_vector_item p_x ;
+
+ p_v = vector_pop_item(&h->v) ; /* extract last item, if any */
+ if ((p_v == NULL) || (h->v.end == 0))
+ return p_v ; /* done if empty or last was also first */
+
+ p_x = h->v.p_items[0] ; /* this is what we are popping */
+
+ heap_bubble_down(h, 0, p_v) ; /* reposition what was the last item */
+ /* updating any backlink */
+ return p_x ;
+} ;
+
+/* Pop one item off the heap and promptly push another.
+ *
+ * In this combination, the pop is essentially free.
+ *
+ * Returns the popped value, which is NULL if the heap was (and still is) empty.
+ */
+p_vector_item
+heap_pop_push_item(heap h, p_vector_item p_v)
+{
+ p_vector_item p_x ;
+
+ dassert(p_v != NULL) ; /* no NULLs, thank you. */
+
+ p_x = heap_top_item(h) ; /* what we are popping */
+
+ if (p_x == NULL)
+ heap_push_item(h, p_v) ; /* for empty heap, this deals with */
+ /* extending heap etc. */
+ else
+ heap_bubble_down(h, 0, p_v) ; /* position the replacement */
+ /* setting any backlink */
+ return p_x ;
+} ;
+
+/*==============================================================================
+ * Heap Operations which use 'backlink', if implemented.
+ */
+
+/* Delete given item from the heap.
+ *
+ * See notes on backlink, above.
+ *
+ * NB: do NOT try this on items which are not in the given heap !
+ */
+void
+heap_delete_item(heap h, p_vector_item p_v)
+{
+ p_vector_item p_x ;
+ vector_index i ;
+
+ i = heap_find_item(h, p_v) ; /* index of item to be deleted */
+
+ p_x = vector_pop_item(&h->v) ; /* extract last item, if any */
+
+ if (i < h->v.end) /* if not deleting the last item... */
+ heap_bubble(h, i, p_x) ; /* ...reinsert what was last, at the delete */
+ /* position, updating any backlink */
+} ;
+
+/*==============================================================================
+ * Other Heap Operations.
+ */
+
+/* Push entire vector onto heap copying or moving items as required.
+ *
+ * Copy or move vector to end of heap's vector, then move each
+ * (non-NULL) item into heap order (discarding any NULL items).
+ *
+ * See: #define heap_push_vector_copy(h, v)
+ * #define heap_push_vector_move(h, v)
+ */
+void
+heap_push_vector(heap h, vector v, int move_vector)
+{
+ vector_index i = h->v.end ;
+ vector_index e ;
+ vector_index n = v->end ;
+ p_vector_item p_v ;
+
+ if (move_vector)
+ vector_move_append(&h->v, v) ;
+ else
+ vector_copy_append(&h->v, v) ;
+
+ e = i ; /* old end of the heap. */
+ while (n--) {
+ p_v = h->v.p_items[i++] ;
+ if (p_v != NULL)
+ heap_bubble_up(h, e++, p_v) ; /* move new item into position in heap */
+ /* setting any backlink */
+ } ;
+
+ h->v.end = e ; /* new end of heap */
+} ;
+
+/* Pop given heap to vector -- creating vector if required (v == NULL).
+ *
+ * Resulting vector is fully sorted.
+ *
+ * Moves or copies the contents of the heap.
+ *
+ * See: #define heap_pop_vector_copy(v, h)
+ * #define heap_pop_vector_move(v, h)
+ *
+ * NB: when creating new vector, will be exactly the required size.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ */
+vector
+heap_pop_vector(vector v, heap h, int move_heap)
+{
+ vector_index n = h->v.end ;
+ vector_index i ;
+
+ v = vector_re_init(v, n) ; /* guarantees >= 'n' items in vector */
+ v->end = n ;
+
+ for (i = 0 ; i < n ; i++)
+ v->p_items[i] = heap_pop_item(h) ;
+
+ if (!move_heap)
+ vector_copy_here(&h->v, v) ; /* fully sorted is also heap ordered ! */
+
+ return v ;
+} ;
+
+/*==============================================================================
+ * The Heap internal mechanics.
+ */
+
+/* Returns pointer to backlink value in heap item: lvalue or rvalue */
+#define HEAP_BACKLINK(h, p_v) \
+ *(heap_backlink_t*)((char*)(p_v) + (h)->backlink_offset)
+/* Sets backlink, if required. */
+#define heap_set_backlink(h, p_v, i) \
+ if ((h)->state & Heap_Has_Backlink) HEAP_BACKLINK(h, p_v) = (i)
+
+/* Returns index of parent. */
+#define HEAP_UP(i) (((i) - 1) / 2)
+/* Returns index of left child. */
+#define HEAP_DOWN(i) (((i) * 2) + 1)
+
+/* Insert given item in the required place in heap, given that there is now
+ * a hole at the given position -- may move up or down the heap, or stay put.
+ *
+ * Bubbles up or down as required.
+ *
+ * Note that this sets the backlink on the given item.
+ */
+private void
+heap_bubble(heap h, vector_index i, p_vector_item p_v)
+{
+ /* If this is < parent, we bubble upwards. */
+ if ((i != 0) && (h->cmp(&p_v, &h->v.p_items[HEAP_UP(i)]) < 0))
+ heap_bubble_up(h, i, p_v) ;
+ /* Otherwise we try bubbling downwards. */
+ else
+ heap_bubble_down(h, i, p_v) ;
+} ;
+
+/* Insert given item in the required place in heap, given that there is now
+ * a hole at the given position -- where we know may *only* move up the heap.
+ *
+ * Note that this sets the backlink on the given item.
+ *
+ * NB: ignores anything in the heap beyond 'i' -- in particular does not use
+ * v.end at all. So this can be used to work along a vector and bring
+ * items into heap order.
+ */
+private void
+heap_bubble_up(heap h, vector_index i, p_vector_item p_v)
+{
+ p_vector_item* ha = h->v.p_items ; /* underlying array */
+ vector_index ip ; /* index of parent */
+ p_vector_item p_p ; /* pointer to parent item */
+
+ dassert(ha != NULL) ;
+
+ while (i != 0)
+ {
+ ip = HEAP_UP(i) ;
+ p_p = ha[ip] ; /* get parent */
+
+ if (h->cmp(&p_v, &p_p) >= 0)
+ break ; /* stop when value >= parent */
+
+ ha[i] = p_p ; /* move parent down... */
+ heap_set_backlink(h, p_p, i) ; /* ...updating any backlink */
+
+ i = ip ; /* move up the heap */
+ } ;
+
+ ha[i] = p_v ; /* place in new position... */
+ heap_set_backlink(h, p_v, i) ; /* ...updating any backlink */
+} ;
+
+/* Insert given item in the required place in heap, given that there is now
+ * a hole at the given position -- where we know may *only* move down the heap.
+ *
+ * Note that this sets the backlink on the given item.
+ */
+private void
+heap_bubble_down(heap h, vector_index i, p_vector_item p_v)
+{
+ vector_index e = h->v.end ; /* end of heap */
+ vector_index ic ; /* index of child */
+ vector_index is ; /* index of sibling */
+ p_vector_item p_c ; /* pointer to child */
+ p_vector_item p_s ; /* pointer to sibling */
+
+ p_vector_item* ha = h->v.p_items ; /* underlying array */
+ dassert(ha != NULL) ;
+
+ while (1)
+ {
+ ic = HEAP_DOWN(i) ;
+ if (ic >= e)
+ break ; /* Quit if run out of heap ! */
+ p_c = ha[ic] ; /* get left hand child */
+
+ is = ic + 1 ;
+ if (is < e) /* is there a right hand child ? */
+ {
+ p_s = ha[is] ; /* get right hand child */
+ if (h->cmp(&p_s, &p_c) < 0)
+ {
+ ic = is ; /* select smaller sibling */
+ p_c = p_s ;
+ } ;
+ } ;
+
+ if (h->cmp(&p_v, &p_c) <= 0)
+ break ; /* stop when <= both children */
+
+ ha[i] = p_c ; /* move smaller child up */
+ heap_set_backlink(h, p_c, i) ; /* ...updating any backlink */
+
+ i = ic ; /* move down the heap */
+ } ;
+
+ ha[i] = p_v ; /* place in new position... */
+ heap_set_backlink(h, p_v, i) ; /* ...updating any backlink */
+} ;
+
+/* Find index of given item in the given heap. */
+private vector_index
+heap_find_item(heap h, p_vector_item p_v)
+{
+ vector_index i ;
+
+ if (h->state & Heap_Has_Backlink)
+ i = HEAP_BACKLINK(h, p_v) ;
+ else
+ {
+ for (i = 0 ; i < h->v.end ; ++i)
+ if (h->v.p_items[i] == p_v)
+ return i ;
+ } ;
+
+ assert((i < h->v.end) && (h->v.p_items[i] == p_v)) ;
+
+ return i ;
+} ;
diff --git a/lib/heap.h b/lib/heap.h
new file mode 100644
index 00000000..bd984398
--- /dev/null
+++ b/lib/heap.h
@@ -0,0 +1,160 @@
+/* Generic heap data structure -- header.
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *.
+ * This file is part of GNU Zebra.
+ *
+ * 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 _ZEBRA_HEAP_H
+#define _ZEBRA_HEAP_H
+
+#include "vector.h"
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*==============================================================================
+ * Data structures etc.
+ */
+
+typedef int heap_cmp(p_vector_item* a, p_vector_item*) ;
+
+enum heap_state {
+ Heap_Has_Backlink = 0x01, /* Set if backlink set */
+} ;
+
+typedef vector_index heap_backlink_t ;
+
+typedef struct heap* heap ;
+
+struct heap
+{
+ heap_cmp* cmp ;
+
+ enum heap_state state ;
+ unsigned int backlink_offset ;
+
+ struct vector v ;
+} ;
+
+/*==============================================================================
+ * Prototypes.
+ */
+
+extern heap heap_init_new(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset) ;
+#define heap_init_new_simple(h, size, cmp) \
+ heap_init_new(h, size, cmp, 0, 0)
+#define heap_init_new_backlinked(h, size, cmp, offset) \
+ heap_init_new(h, size, cmp, 1, offset)
+
+extern heap heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset) ;
+#define heap_re_init_simple(h, size, cmp) \
+ heap_re_init(h, size, cmp, 0, 0)
+#define heap_re_init_backlinked(h, size, cmp, offset) \
+ heap_re_init(h, size, cmp, 1, offset)
+
+extern heap heap_reset(heap h, int free_structure) ;
+extern p_vector_item heap_ream(heap h, int free_structure) ;
+
+/* Reset heap and free the heap structure. */
+#define heap_reset_free(h) heap_reset(h, 1)
+/* Reset heap but keep the heap structure. */
+#define heap_reset_keep(h) heap_reset(h, 0)
+/* Ream out heap and free the heap structure. */
+#define heap_ream_free(h) heap_ream(h, 1)
+/* Ream out heap but keep the heap structure. */
+#define heap_ream_keep(h) heap_ream(h, 0)
+
+Inline void heap_push_item(heap h, p_vector_item p_v) ;
+extern p_vector_item heap_pop_item(heap h) ;
+extern p_vector_item heap_pop_push_item(heap h, p_vector_item p_v) ;
+Inline p_vector_item heap_top_item(heap h) ;
+Inline void heap_update_top_item(heap h) ;
+
+extern void heap_delete_item(heap h, p_vector_item p_v) ;
+Inline void heap_update_item(heap h, p_vector_item p_v) ;
+
+extern void heap_push_vector(heap h, vector v, int move_vector) ;
+#define heap_push_vector_copy(h, v) \
+ heap_push_vector(h, v, 0)
+#define heap_push_vector_move(h, v) \
+ heap_push_vector(h, v, 1)
+extern vector heap_pop_vector(vector v, heap h, int move_heap) ;
+#define heap_pop_vector_copy(v, h) \
+ heap_pop_vector(v, h, 0)
+#define heap_pop_vector_move(v, h) \
+ heap_pop_vector(v, h, 1)
+
+/*==============================================================================
+ * This are extern only for use in Inline and other friends
+ */
+
+#ifndef private
+ #define private extern
+#endif
+
+private void
+heap_bubble(heap h, vector_index i, p_vector_item p_v) ;
+
+private void
+heap_bubble_up(heap h, vector_index i, p_vector_item p_v) ;
+
+private void
+heap_bubble_down(heap h, vector_index i, p_vector_item p_v) ;
+
+private vector_index
+heap_find_item(heap h, p_vector_item p_v) ;
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/* Push given item onto the heap
+ */
+Inline void
+heap_push_item(heap h, p_vector_item p_v)
+{
+ dassert(p_v != NULL) ; /* no NULLs, thank you. */
+ heap_bubble_up(h, vector_extend_by_1(&h->v), p_v) ;
+} ;
+
+/* Get copy of top heap item (does not pop).
+ *
+ * Returns NULL if heap is empty.
+ */
+Inline p_vector_item
+heap_top_item(heap h)
+{
+ return vector_get_first_item(&h->v) ; /* if any */
+} ;
+
+/* Update heap to reflect new value of top item.
+ */
+Inline void
+heap_update_top_item(heap h)
+{
+ heap_bubble_down(h, 0, heap_top_item(h)) ;
+} ;
+
+/* Update heap to reflect new value of given item.
+ */
+Inline void
+heap_update_item(heap h, p_vector_item p_v)
+{
+ heap_bubble(h, heap_find_item(h, p_v), p_v) ;
+} ;
+
+#endif /* _ZEBRA_HEAP_H */
diff --git a/lib/if.c b/lib/if.c
index 86f754b6..048ee1fb 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1,10 +1,10 @@
-/*
+/*
* Interface functions.
* Copyright (C) 1997, 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
@@ -35,7 +35,7 @@
#include "buffer.h"
#include "str.h"
#include "log.h"
-
+
/* Master list of interfaces. */
struct list *iflist;
@@ -45,7 +45,7 @@ struct if_master
int (*if_new_hook) (struct interface *);
int (*if_delete_hook) (struct interface *);
} if_master;
-
+
/* Compare interface names, returning an integer greater than, equal to, or
* less than 0, (following the strcmp convention), according to the
* relationship between ifp1 and ifp2. Interface names consist of an
@@ -53,7 +53,7 @@ struct if_master
* lexicographic by name, and then numeric by number. No number sorts
* before all numbers. Examples: de0 < de1, de100 < fxp0 < xl0, devpty <
* devpty0, de0 < del0
- */
+ */
int
if_cmp_func (struct interface *ifp1, struct interface *ifp2)
{
@@ -87,9 +87,9 @@ if_cmp_func (struct interface *ifp1, struct interface *ifp2)
p1 += l1;
p2 += l1;
- if (!*p1)
+ if (!*p1)
return -1;
- if (!*p2)
+ if (!*p2)
return 1;
x1 = strtol(p1, &p1, 10);
@@ -119,7 +119,7 @@ if_create (const char *name, int namelen)
ifp = XCALLOC (MTYPE_IF, sizeof (struct interface));
ifp->ifindex = IFINDEX_INTERNAL;
-
+
assert (name);
assert (namelen <= INTERFACE_NAMSIZ); /* Need space for '\0' at end. */
strncpy (ifp->name, name, namelen);
@@ -215,7 +215,7 @@ if_lookup_by_name (const char *name)
{
struct listnode *node;
struct interface *ifp;
-
+
if (name)
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
{
@@ -262,7 +262,7 @@ if_lookup_exact_address (struct in_addr src)
{
if (IPV4_ADDR_SAME (&p->u.prefix4, &src))
return ifp;
- }
+ }
}
}
return NULL;
@@ -434,12 +434,12 @@ if_dump (const struct interface *ifp)
"mtu6 %d "
#endif /* HAVE_IPV6 */
"%s",
- ifp->name, ifp->ifindex, ifp->metric, ifp->mtu,
+ ifp->name, ifp->ifindex, ifp->metric, ifp->mtu,
#ifdef HAVE_IPV6
ifp->mtu6,
#endif /* HAVE_IPV6 */
if_flag_dump (ifp->flags));
-
+
for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, c))
;
}
@@ -455,7 +455,7 @@ if_dump_all (void)
if_dump (p);
}
-DEFUN (interface_desc,
+DEFUN (interface_desc,
interface_desc_cmd,
"description .LINE",
"Interface specific description\n"
@@ -474,7 +474,7 @@ DEFUN (interface_desc,
return CMD_SUCCESS;
}
-DEFUN (no_interface_desc,
+DEFUN (no_interface_desc,
no_interface_desc_cmd,
"no description",
NO_STR
@@ -489,7 +489,7 @@ DEFUN (no_interface_desc,
return CMD_SUCCESS;
}
-
+
#ifdef SUNOS_5
/* Need to handle upgrade from SUNWzebra to Quagga. SUNWzebra created
* a seperate struct interface for each logical interface, so config
@@ -519,11 +519,11 @@ if_sunwzebra_get (const char *name, size_t nlen)
if ( (ifp = if_lookup_by_name_len(name, nlen)) != NULL)
return ifp;
-
+
/* hunt the primary interface name... */
while (seppos < nlen && name[seppos] != ':')
seppos++;
-
+
/* Wont catch seperator as last char, e.g. 'foo0:' but thats invalid */
if (seppos < nlen)
return if_get_by_name_len (name, seppos);
@@ -531,7 +531,7 @@ if_sunwzebra_get (const char *name, size_t nlen)
return if_get_by_name_len (name, nlen);
}
#endif /* SUNOS_5 */
-
+
DEFUN (interface,
interface_cmd,
"interface IFNAME",
@@ -556,7 +556,7 @@ DEFUN (interface,
#endif /* SUNOS_5 */
vty->index = ifp;
- vty->node = INTERFACE_NODE;
+ vty_set_node(vty, INTERFACE_NODE) ;
return CMD_SUCCESS;
}
@@ -579,7 +579,7 @@ DEFUN_NOSH (no_interface,
return CMD_WARNING;
}
- if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+ if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
{
vty_out (vty, "%% Only inactive interfaces can be deleted%s",
VTY_NEWLINE);
@@ -611,7 +611,7 @@ DEFUN (show_address,
p = ifc->address;
if (p->family == AF_INET)
- vty_out (vty, "%s/%d%s", inet_ntoa (p->u.prefix4), p->prefixlen,
+ vty_out (vty, "%s/%d%s", safe_inet_ntoa (p->u.prefix4), p->prefixlen,
VTY_NEWLINE);
}
}
@@ -649,11 +649,11 @@ connected_log (struct connected *connected, char *str)
struct interface *ifp;
char logbuf[BUFSIZ];
char buf[BUFSIZ];
-
+
ifp = connected->ifp;
p = connected->address;
- snprintf (logbuf, BUFSIZ, "%s interface %s %s %s/%d ",
+ snprintf (logbuf, BUFSIZ, "%s interface %s %s %s/%d ",
str, ifp->name, prefix_family_str (p),
inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
@@ -734,7 +734,7 @@ connected_lookup_address (struct interface *ifp, struct in_addr dst)
}
struct connected *
-connected_add_by_prefix (struct interface *ifp, struct prefix *p,
+connected_add_by_prefix (struct interface *ifp, struct prefix *p,
struct prefix *destination)
{
struct connected *ifc;
@@ -782,7 +782,7 @@ if_indextoname (unsigned int ifindex, char *name)
return ifp->name;
}
#endif
-
+
#if 0 /* this route_table of struct connected's is unused
* however, it would be good to use a route_table rather than
* a list..
@@ -807,7 +807,7 @@ ifaddr_ipv4_add (struct in_addr *ifaddr, struct interface *ifp)
{
route_unlock_node (rn);
zlog_info ("ifaddr_ipv4_add(): address %s is already added",
- inet_ntoa (*ifaddr));
+ safe_inet_ntoa (*ifaddr));
return;
}
rn->info = ifp;
@@ -827,7 +827,7 @@ ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp)
if (! rn)
{
zlog_info ("ifaddr_ipv4_delete(): can't find address %s",
- inet_ntoa (*ifaddr));
+ safe_inet_ntoa (*ifaddr));
return;
}
rn->info = NULL;
@@ -852,7 +852,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, unsigned int ifindex)
rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
if (! rn)
return NULL;
-
+
ifp = rn->info;
route_unlock_node (rn);
return ifp;
diff --git a/lib/if_rmap.c b/lib/if_rmap.c
index 9774be4b..e51f1fe5 100644
--- a/lib/if_rmap.c
+++ b/lib/if_rmap.c
@@ -16,10 +16,11 @@
* 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>
+#include "miyagi.h"
#include "hash.h"
#include "command.h"
@@ -32,7 +33,7 @@ struct hash *ifrmaphash;
/* Hook functions. */
static void (*if_rmap_add_hook) (struct if_rmap *) = NULL;
static void (*if_rmap_delete_hook) (struct if_rmap *) = NULL;
-
+
static struct if_rmap *
if_rmap_new (void)
{
@@ -63,11 +64,11 @@ if_rmap_lookup (const char *ifname)
struct if_rmap key;
struct if_rmap *if_rmap;
- /* temporary copy */
- key.ifname = (char *)ifname;
+ /* temporary reference */
+ key.ifname = miyagi(ifname) ;
if_rmap = hash_lookup (ifrmaphash, &key);
-
+
return if_rmap;
}
@@ -100,8 +101,8 @@ if_rmap_get (const char *ifname)
{
struct if_rmap key;
- /* temporary copy */
- key.ifname = (char *)ifname;
+ /* temporary reference */
+ key.ifname = miyagi(ifname) ;
return (struct if_rmap *) hash_get (ifrmaphash, &key, if_rmap_hash_alloc);
}
@@ -122,9 +123,9 @@ if_rmap_hash_cmp (const void *arg1, const void* arg2)
return strcmp (if_rmap1->ifname, if_rmap2->ifname) == 0;
}
-
+
static struct if_rmap *
-if_rmap_set (const char *ifname, enum if_rmap_type type,
+if_rmap_set (const char *ifname, enum if_rmap_type type,
const char *routemap_name)
{
struct if_rmap *if_rmap;
@@ -135,25 +136,25 @@ if_rmap_set (const char *ifname, enum if_rmap_type type,
{
if (if_rmap->routemap[IF_RMAP_IN])
XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
- if_rmap->routemap[IF_RMAP_IN]
+ if_rmap->routemap[IF_RMAP_IN]
= XSTRDUP (MTYPE_IF_RMAP_NAME, routemap_name);
}
if (type == IF_RMAP_OUT)
{
if (if_rmap->routemap[IF_RMAP_OUT])
XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
- if_rmap->routemap[IF_RMAP_OUT]
+ if_rmap->routemap[IF_RMAP_OUT]
= XSTRDUP (MTYPE_IF_RMAP_NAME, routemap_name);
}
if (if_rmap_add_hook)
(*if_rmap_add_hook) (if_rmap);
-
+
return if_rmap;
}
static int
-if_rmap_unset (const char *ifname, enum if_rmap_type type,
+if_rmap_unset (const char *ifname, enum if_rmap_type type,
const char *routemap_name)
{
struct if_rmap *if_rmap;
@@ -170,7 +171,7 @@ if_rmap_unset (const char *ifname, enum if_rmap_type type,
return 0;
XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
- if_rmap->routemap[IF_RMAP_IN] = NULL;
+ if_rmap->routemap[IF_RMAP_IN] = NULL;
}
if (type == IF_RMAP_OUT)
@@ -181,7 +182,7 @@ if_rmap_unset (const char *ifname, enum if_rmap_type type,
return 0;
XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
- if_rmap->routemap[IF_RMAP_OUT] = NULL;
+ if_rmap->routemap[IF_RMAP_OUT] = NULL;
}
if (if_rmap_delete_hook)
@@ -222,7 +223,7 @@ DEFUN (if_rmap,
if_rmap = if_rmap_set (argv[2], type, argv[0]);
return CMD_SUCCESS;
-}
+}
ALIAS (if_rmap,
if_ipv6_rmap_cmd,
@@ -263,7 +264,7 @@ DEFUN (no_if_rmap,
return CMD_WARNING;
}
return CMD_SUCCESS;
-}
+}
ALIAS (no_if_rmap,
no_if_ipv6_rmap_cmd,
@@ -274,7 +275,7 @@ ALIAS (no_if_rmap,
"Route map for input filtering\n"
"Route map for output filtering\n"
"Route map interface name\n")
-
+
/* Configuration write function. */
int
config_write_if_rmap (struct vty *vty)
@@ -292,7 +293,7 @@ config_write_if_rmap (struct vty *vty)
if (if_rmap->routemap[IF_RMAP_IN])
{
- vty_out (vty, " route-map %s in %s%s",
+ vty_out (vty, " route-map %s in %s%s",
if_rmap->routemap[IF_RMAP_IN],
if_rmap->ifname,
VTY_NEWLINE);
@@ -301,7 +302,7 @@ config_write_if_rmap (struct vty *vty)
if (if_rmap->routemap[IF_RMAP_OUT])
{
- vty_out (vty, " route-map %s out %s%s",
+ vty_out (vty, " route-map %s out %s%s",
if_rmap->routemap[IF_RMAP_OUT],
if_rmap->ifname,
VTY_NEWLINE);
diff --git a/lib/keychain.c b/lib/keychain.c
index 6719cebf..2f8a0b77 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -74,7 +74,7 @@ key_cmp_func (void *arg1, void *arg2)
{
const struct key *k1 = arg1;
const struct key *k2 = arg2;
-
+
if (k1->index > k2->index)
return 1;
if (k1->index < k2->index)
@@ -226,7 +226,7 @@ key_delete (struct keychain *keychain, struct key *key)
free (key->string);
key_free (key);
}
-
+
DEFUN (key_chain,
key_chain_cmd,
"key chain WORD",
@@ -238,7 +238,7 @@ DEFUN (key_chain,
keychain = keychain_get (argv[0]);
vty->index = keychain;
- vty->node = KEYCHAIN_NODE;
+ vty_set_node(vty, KEYCHAIN_NODE) ;
return CMD_SUCCESS;
}
@@ -281,8 +281,8 @@ DEFUN (key,
VTY_GET_INTEGER ("key identifier", index, argv[0]);
key = key_get (keychain, index);
vty->index_sub = key;
- vty->node = KEYCHAIN_KEY_NODE;
-
+ vty_set_node(vty, KEYCHAIN_KEY_NODE) ;
+
return CMD_SUCCESS;
}
@@ -296,7 +296,7 @@ DEFUN (no_key,
struct keychain *keychain;
struct key *key;
u_int32_t index;
-
+
keychain = vty->index;
VTY_GET_INTEGER ("key identifier", index, argv[0]);
@@ -309,7 +309,7 @@ DEFUN (no_key,
key_delete (keychain, key);
- vty->node = KEYCHAIN_NODE;
+ vty_set_node(vty, KEYCHAIN_NODE) ;
return CMD_SUCCESS;
}
@@ -353,7 +353,7 @@ DEFUN (no_key_string,
/* Convert HH:MM:SS MON DAY YEAR to time_t value. -1 is returned when
given string is malformed. */
-static time_t
+static time_t
key_str2time (const char *time_str, const char *day_str, const char *month_str,
const char *year_str)
{
@@ -364,7 +364,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
unsigned int sec, min, hour;
unsigned int day, month, year;
- const char *month_name[] =
+ const char *month_name[] =
{
"January",
"February",
@@ -392,7 +392,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
return -1; \
(V) = tmpl; \
}
-
+
/* Check hour field of time_str. */
colon = strchr (time_str, ':');
if (colon == NULL)
@@ -416,10 +416,10 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
time_str = colon + 1;
if (*time_str == '\0')
return -1;
-
+
/* Sec must be between 0 and 59. */
GET_LONG_RANGE (sec, time_str, 0, 59);
-
+
/* Check day_str. Day must be <1-31>. */
GET_LONG_RANGE (day, day_str, 1, 31);
@@ -437,7 +437,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
/* Check year_str. Year must be <1993-2035>. */
GET_LONG_RANGE (year, year_str, 1993, 2035);
-
+
memset (&tm, 0, sizeof (struct tm));
tm.tm_sec = sec;
tm.tm_min = min;
@@ -445,9 +445,9 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
tm.tm_mon = month;
tm.tm_mday = day;
tm.tm_year = year - 1900;
-
+
time = mktime (&tm);
-
+
return time;
#undef GET_LONG_RANGE
}
@@ -461,7 +461,7 @@ key_lifetime_set (struct vty *vty, struct key_range *krange,
{
time_t time_start;
time_t time_end;
-
+
time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
if (time_start < 0)
{
@@ -496,7 +496,7 @@ key_lifetime_duration_set (struct vty *vty, struct key_range *krange,
{
time_t time_start;
u_int32_t duration;
-
+
time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
if (time_start < 0)
{
@@ -518,7 +518,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange,
const char *smonth_str, const char *syear_str)
{
time_t time_start;
-
+
time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
if (time_start < 0)
{
@@ -531,7 +531,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange,
return CMD_SUCCESS;
}
-
+
DEFUN (accept_lifetime_day_month_day_month,
accept_lifetime_day_month_day_month_cmd,
"accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
@@ -689,7 +689,7 @@ DEFUN (accept_lifetime_duration_month_day,
return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2],
argv[1], argv[3], argv[4]);
}
-
+
DEFUN (send_lifetime_day_month_day_month,
send_lifetime_day_month_day_month_cmd,
"send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
@@ -847,7 +847,7 @@ DEFUN (send_lifetime_duration_month_day,
return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1],
argv[3], argv[4]);
}
-
+
static struct cmd_node keychain_node =
{
KEYCHAIN_NODE,
@@ -865,12 +865,12 @@ static struct cmd_node keychain_key_node =
static int
keychain_strftime (char *buf, int bufsiz, time_t *time)
{
- struct tm *tm;
+ struct tm tm;
size_t len;
- tm = localtime (time);
+ localtime_r(time, &tm);
- len = strftime (buf, bufsiz, "%T %b %d %Y", tm);
+ len = strftime (buf, bufsiz, "%T %b %d %Y", &tm);
return len;
}
@@ -887,7 +887,7 @@ keychain_config_write (struct vty *vty)
for (ALL_LIST_ELEMENTS_RO (keychain_list, node, keychain))
{
vty_out (vty, "key chain %s%s", keychain->name, VTY_NEWLINE);
-
+
for (ALL_LIST_ELEMENTS_RO (keychain->key, knode, key))
{
vty_out (vty, " key %d%s", key->index, VTY_NEWLINE);
diff --git a/lib/keystroke.c b/lib/keystroke.c
new file mode 100644
index 00000000..c6eb811e
--- /dev/null
+++ b/lib/keystroke.c
@@ -0,0 +1,1204 @@
+/* Keystroke Buffering
+ * Copyright (C) 2010 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 <stdbool.h>
+#include <string.h>
+
+#include "keystroke.h"
+
+#include "list_util.h"
+
+#include "memory.h"
+#include "mqueue.h"
+#include "zassert.h"
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Parsing of incoming keystrokes.
+ *
+ * At present this code only handles 8-bit characters, which one assumes are
+ * ISO8859-1 (or similar). The encoding of keystrokes allows for characters
+ * of up to 32-bits -- so could parse UTF-8 (or similar).
+ *
+ * Handles:
+ *
+ * 0. Nothing, returned as:
+ *
+ * type = ks_null
+ * value = knull_eof
+ * knull_not_eof
+ *
+ * len = 0
+ * truncated = false
+ * broken = false
+ * buf -- not used
+ *
+ * This is returned when there is nothing there.
+ *
+ * Note that this is NOT returned for NUL ('\0') characters. Those are
+ * real characters (whose value just happens to be null).
+ *
+ * 1. Characters, returned as:
+ *
+ * type = ks_char
+ * value = 0x0.. -- the character value
+ * '\0' if truncated, malformed or EOF met
+ *
+ * len = 1..n => length of character representation, or
+ * number of bytes in raw form if no good.
+ * truncated => too long for buffers
+ * broken => malformed or EOF met
+ * buf -- if OK, the representation for the character (UTF-8 ?)
+ * if truncated or broken, the raw bytes
+ *
+ * See notes below on the handling of '\r' and '\n'.
+ *
+ * 2. ESC X -- where X is single character, other than '['.
+ *
+ * Returned as:
+ *
+ * type = ks_esc
+ * value = 0x0.. -- the character value X
+ * ('\0' if hit EOF)
+ *
+ * len = 1 (or 0 if EOF met)
+ * truncated = false
+ * broken => EOF met
+ * buf = copy of X (unless EOF met)
+ *
+ * 3. ESC [ ... X or CSI ... X -- ANSI escape
+ *
+ * Returned as:
+ *
+ * type = ks_csi
+ * value = 0x0.. -- the character value X
+ * ('\0' if hit EOF or malformed)
+ *
+ * len = number of bytes in buf (excluding '\0' terminator)
+ * truncated => too long for buffers
+ * broken => malformed or EOF met
+ * buf -- bytes between ESC [ or CSI and terminating X
+ * NB: '\0' terminated.
+ *
+ * Note: an ANSI escape is malformed if a byte outside the range
+ * 0x20..0x7F is found before the terminating byte. The illegal
+ * byte is deemed not to be part of the sequence.
+ *
+ * 4. Telnet command -- IAC X ...
+ *
+ * IAC IAC is treated as character 0xFF, so appears as a ks_char, or
+ * possibly as a component of an ESC or other sequence.
+ *
+ * Returned as:
+ *
+ * type = ks_iac
+ * value = 0x0.. -- the value of X
+ * ('\0' if hit EOF)
+ *
+ * len = number of bytes in buf (0 if hit EOF after IAC)
+ * truncated => too long for buffers
+ * broken => malformed or EOF met
+ * buf -- the X and any further bytes,
+ * but *excluding* any terminating IAC SE
+ *
+ * Note: a Telnet command may appear at any time, including in the
+ * middle of any of the above "real" keystrokes.
+ *
+ * This is made invisible to the "real" keystrokes, and the Telnet
+ * command(s) will appear before the incomplete real keystroke in
+ * the keystroke stream.
+ *
+ * Note: there are three forms of Telnet command, and one escape.
+ *
+ * 1) IAC IAC is an escape, and maps simply to the value 0xFF,
+ * wherever it appears (except when reading an <option>).
+ *
+ * 2) IAC X -- two bytes, where X < 250 (SB)
+ *
+ * 3) IAC X O -- three bytes, where X is 251..254 (WILL/WONT/DO/DONT)
+ *
+ * the O may be 0x00..0xFF (where 0xFF is *NOT* escaped)
+ *
+ * 4) IAC SB O ... IAC SE -- many bytes.
+ *
+ * the O may be 0x00..0xFF (where 0xFF is *NOT* escaped)
+ *
+ * the data ... is subject to IAC IAC escaping (so that
+ * the terminating IAC SE is unambiguous).
+ *
+ * This implementation treats IAC X (where X is not IAC
+ * and not SE) as an error -- terminating the command
+ * before the IAC, and marking it malformed. The IAC X
+ * will then be taken as a new command.
+ *
+ * Extended Option objects (O = 0xFF) are exotic, but the above will
+ * parse them.
+ *
+ *------------------------------------------------------------------------------
+ * CR, LF and NUL
+ *
+ * Telnet requires CR LF newlines. Where a CR is to appear alone it must be
+ * followed by NUL.
+ *
+ * This code accepts:
+ *
+ * * CR LF pair, returning LF ('\n') -- discards CR
+ *
+ * * CR NUL pair, returning CR ('\r') -- discards NUL
+ *
+ * * CR CR pair, returning CR ('\r') == discards one CR (seems pointless)
+ *
+ * * CR XX pair, returning CR and XX -- where XX is anything other than
+ * CR, LF or NUL
+ *
+ * It is tempting to throw away all NUL characters... but that doesn't seem
+ * like a job for this level.
+ *
+ * As a small compromise, will not steal a NUL character.
+ *
+ * Note that NUL appears as a real character. ks_null means literally nothing.
+ */
+
+/*------------------------------------------------------------------------------
+ * Encoding of keystrokes in the keystroke FIFO.
+ *
+ * The first byte of a keystroke is as follows:
+ *
+ * 1. 0x00..0x7F -- simple character -- complete keystroke
+ *
+ * 2. 0x80..0xFF -- compound keystroke -- further bytes (may) follow
+ *
+ * A compound keystroke comprises:
+ *
+ * 1. type of keystroke -- see enum keystroke_type
+ *
+ * 2. number of bytes that make up the keystroke
+ *
+ * This is limited to keystroke_max_len. Any keystroke longer than that
+ * is marked as truncated.
+ *
+ * 3. the bytes (0..keystroke_max_len of them)
+ *
+ * This is encoded as <first> <length> [ <bytes> ]
+ *
+ * Where:
+ *
+ * <first> is 0x80..0xFF (as above):
+ *
+ * b7 = 1
+ * b6 = 0 : "reserved"
+ * b5 : 0 => OK
+ * 1 => broken object
+ * b4 : 0 => OK
+ * 1 => truncated
+ *
+ * b3..0 : type of keystroke -- so 16 types of keystroke
+ *
+ * <length> is 0..keystroke_max_len
+ *
+ * <bytes> (if present) are the bytes:
+ *
+ * ks_null -- should not appear in the FIFO
+ *
+ * ks_char -- character value, in network order, length 1..4
+ *
+ * BUT, if broken or truncated, the raw bytes received (must
+ * be at least one).
+ *
+ * ks_esc -- byte that followed the ESC, length = 0..1
+ *
+ * Length 0 => EOF met => broken
+ *
+ * ks_csi -- bytes that followed the ESC [ or CSI, length 1..n
+ *
+ * The last byte is *always* the byte that terminated the
+ * sequence, or '\0' if is badly formed, or hit EOF.
+ *
+ * If the sequence is truncated, then the last byte of the
+ * sequence is written over the last buffered byte.
+ *
+ * ks_iac -- bytes of the telnet command, excluding the leading
+ * IAC and the trailing IAC SE, length 1..n.
+ *
+ * IAC IAC pairs are not telnet commands.
+ *
+ * IAC IAC pairs between SB X O and IAC SE are reduced to
+ * 0xFF.
+ *
+ * Telnet commands are broken if EOF is met before the end
+ * of the command, or get IAC X between SB X O and IAC SE
+ * (where X is not IAC or SE).
+ */
+
+enum stream_state
+{
+ kst_null, /* nothing special (but see iac) */
+
+ kst_char, /* collecting a multi-byte character */
+ kst_cr, /* collecting '\r''\0' or '\r''\n' */
+ kst_esc, /* collecting an ESC sequence */
+ kst_csi, /* collecting an ESC '[' or CSI sequence */
+
+ kst_iac_option, /* waiting for option (just seen IAC X) */
+ kst_iac_sub, /* waiting for IAC SE */
+} ;
+
+struct keystroke_state
+{
+ enum stream_state state ;
+ unsigned len ;
+ uint8_t raw[keystroke_max_len] ;
+} ;
+
+struct keystroke_stream
+{
+ vio_fifo_t fifo ; /* the keystrokes */
+
+ keystroke_callback* iac_callback ;
+ void* iac_callback_context ;
+
+ uint8_t CSI ; /* CSI character value (if any) */
+
+ bool eof_met ; /* nothing more to come */
+
+ bool steal_this ; /* steal current keystroke when complete */
+
+ bool iac ; /* last character was an IAC */
+
+ struct keystroke_state in ; /* current keystroke being collected */
+
+ struct keystroke_state pushed_in ;
+ /* keystroke interrupted by IAC */
+} ;
+
+/* Buffering of keystrokes */
+
+enum { keystroke_buffer_len = 2000 } ; /* should be plenty ! */
+
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+static void keystroke_in_push(keystroke_stream stream, uint8_t u) ;
+static void keystroke_in_pop(keystroke_stream stream) ;
+
+inline static int keystroke_set_null(keystroke_stream stream, keystroke stroke);
+inline static uint8_t keystroke_get_byte(keystroke_stream stream) ;
+inline static void keystroke_add_raw(keystroke_stream stream, uint8_t u) ;
+static void keystroke_put_char(keystroke_stream stream, uint32_t u) ;
+inline static void keystroke_put_esc(keystroke_stream stream, uint8_t u,
+ int len) ;
+inline static void keystroke_put_csi(keystroke_stream stream, uint8_t u) ;
+static void keystroke_put_iac_one(keystroke_stream stream, uint8_t u);
+static void keystroke_put_iac_long(keystroke_stream stream, bool broken) ;
+static void keystroke_clear_iac(keystroke_stream stream) ;
+static void keystroke_steal_char(keystroke steal, keystroke_stream stream,
+ uint8_t u) ;
+static void keystroke_steal_esc(keystroke steal, keystroke_stream stream,
+ uint8_t u) ;
+static void keystroke_steal_csi(keystroke steal, keystroke_stream stream,
+ uint8_t u) ;
+static void keystroke_put(keystroke_stream stream, enum keystroke_type type,
+ bool broken, uint8_t* bytes, int len) ;
+
+/*==============================================================================
+ * Creating and freeing keystroke streams and keystroke stream buffers.
+ */
+
+/*------------------------------------------------------------------------------
+ * Create and initialise a keystroke stream.
+ *
+ * Can set CSI character value. '\0' => none. (As does '\x1B' !)
+ *
+ * The callback function is called when an IAC sequence is seen, the callback
+ * is:
+ *
+ * bool callback(void* context, keystroke stroke)
+ *
+ * see: #define keystroke_iac_callback_args
+ * and: typedef for keystroke_callback
+ *
+ * The callback must return true iff the IAC sequence has been dealt with, and
+ * should NOT be stored for later processing.
+ */
+extern keystroke_stream
+keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
+ void* iac_callback_context)
+{
+ keystroke_stream stream ;
+
+ stream = XCALLOC(MTYPE_KEY_STREAM, sizeof(struct keystroke_stream)) ;
+
+ /* Zeroising the structure sets:
+ *
+ * iac_callback = NULL -- none
+ * iac_callback_context = NULL -- none
+ *
+ * eof_met = false -- no EOF yet
+ * steal = false -- no stealing set
+ * iac = false -- last character was not an IAC
+ *
+ * in.state = kst_null
+ * in.len = 0 -- nothing in the buffer
+ *
+ * pushed_in.state ) ditto
+ * pushed_in.len )
+ */
+ confirm(kst_null == 0) ;
+
+ stream->iac_callback = iac_callback ;
+ stream->iac_callback_context = iac_callback_context ;
+
+ vio_fifo_init_new(&stream->fifo, keystroke_buffer_len) ;
+
+ stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
+
+ return stream ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free keystroke stream and all associated buffers.
+ *
+ * Returns NULL
+ */
+extern keystroke_stream
+keystroke_stream_free(keystroke_stream stream)
+{
+ if (stream != NULL)
+ {
+ vio_fifo_reset_keep(&stream->fifo) ;
+
+ XFREE(MTYPE_KEY_STREAM, stream) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Keystroke stream state
+ */
+
+/*------------------------------------------------------------------------------
+ * See if given keystroke stream is empty
+ *
+ * May or may not be at "EOF", see below.
+ *
+ * Returns: true <=> is empty
+ */
+extern bool
+keystroke_stream_empty(keystroke_stream stream)
+{
+ return (stream == NULL) || vio_fifo_empty(&stream->fifo) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if given keystroke stream is at "EOF", that is:
+ *
+ * * keystroke stream is empty
+ *
+ * * there is no partial keystroke in construction
+ *
+ * * EOF has been signalled by a suitable call of keystroke_input().
+ *
+ * Returns: true <=> is at EOF
+ */
+extern bool
+keystroke_stream_eof(keystroke_stream stream)
+{
+ /* Note that when EOF is signalled, any partial keystroke in construction
+ * is converted to a broken keystroke and placed in the stream.
+ * (So eof_met => no partial keystroke.)
+ */
+ return (stream == NULL) || (vio_fifo_empty(&stream->fifo) && stream->eof_met);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set keystroke stream to "EOF", that is:
+ *
+ * * discard contents of the stream, including any partial keystroke.
+ *
+ * * set the stream "eof_met".
+ */
+extern void
+keystroke_stream_set_eof(keystroke_stream stream)
+{
+ vio_fifo_reset_keep(&stream->fifo) ;
+
+ stream->eof_met = true ; /* essential information */
+
+ stream->steal_this = false ; /* keep tidy */
+ stream->iac = false ;
+ stream->in.state = kst_null ;
+ stream->pushed_in.state = kst_null ;
+} ;
+
+/*==============================================================================
+ * Input raw bytes to given keyboard stream.
+ *
+ * To steal the next keystroke, pass 'steal' = address of a keystroke structure.
+ * Otherwise, pass NULL.
+ *
+ * Note: when trying to steal, will complete any partial keystroke before
+ * stealing the next one. May exit from here:
+ *
+ * a. without having completed the partial keystroke.
+ *
+ * b. without having completed the keystroke to be stolen.
+ *
+ * State (b) is remembered by the keystroke_stream.
+ *
+ * Caller may have to call several times with steal != NULL to get a
+ * keystroke.
+ *
+ * If steal != NULL the keystroke will be set to the stolen keystroke. That
+ * will be type ks_null if nothing was available, and may be knull_eof.
+ *
+ * Note that never steals broken or truncated keystrokes.
+ *
+ * Passing len == 0 and ptr == NULL signals EOF to the keystroke_stream.
+ *
+ * Updates the stream and returns updated raw
+ */
+extern void
+keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
+ keystroke steal)
+{
+ uint8_t* end ;
+
+ /* Deal with EOF if required
+ *
+ * Any partial keystroke is converted into a broken keystroke and placed
+ * at the end of the stream.
+ *
+ * Note that this occurs before any attempt to steal a keystroke -- so can
+ * never steal a broken keystroke.
+ */
+ if ((len == 0) && (ptr == NULL))
+ {
+ stream->eof_met = true ;
+ stream->steal_this = false ;
+
+ /* Loop to deal with any pending IAC and partial keystroke.
+ *
+ * An IAC in the middle of a real keystroke sequence appears before
+ * it. Do the same here, even with broken sequences.
+ *
+ * A partial IAC sequence may have pushed a partial real keystroke
+ * sequence -- so loop until have dealt with that.
+ */
+ do
+ {
+ switch (stream->in.state)
+ {
+ case kst_null: /* not expecting anything, unless iac */
+ keystroke_clear_iac(stream) ;
+ break ;
+
+ case kst_cr: /* expecting something after CR */
+ keystroke_clear_iac(stream) ;
+
+ stream->in.len = 0 ;
+ keystroke_add_raw(stream, '\r') ;
+ keystroke_put(stream, ks_char, true,
+ stream->in.raw, stream->in.len) ;
+ break ;
+
+ case kst_esc: /* expecting rest of escape */
+ keystroke_clear_iac(stream) ;
+
+ keystroke_put_esc(stream, '\0', 0) ;
+ break ;
+
+ case kst_csi:
+ keystroke_clear_iac(stream) ;
+
+ keystroke_put_csi(stream, '\0') ;
+ break ;
+
+ case kst_iac_option: /* expecting rest of IAC */
+ assert(!stream->iac) ;
+ /* fall through */
+ case kst_iac_sub:
+ keystroke_put_iac_long(stream, true) ;
+
+ /* For kst_iac_sub, an incomplete IAC could be anything, so
+ * don't include in the broken IAC, but don't lose it
+ * either.
+ */
+ keystroke_clear_iac(stream) ;
+ break ;
+
+ case kst_char: /* TBD */
+ zabort("impossible keystroke stream state") ;
+
+ default:
+ zabort("unknown keystroke stream state") ;
+ } ;
+
+ assert(!stream->iac) ; /* must have dealt with this */
+
+ keystroke_in_pop(stream) ; /* pops kst_null, when all done */
+
+ } while (stream->in.state != kst_null) ;
+ } ;
+
+ /* Update the stealing state
+ *
+ * steal != NULL => want to steal a keystroke
+ *
+ * If do not wish to steal now, must clear any
+ * remembered steal_this state.
+ *
+ * stream->steal_this => steal the next keystroke to complete.
+ *
+ * If want to steal a keystroke, this is set if
+ * currently "between" keystrokes, or later when
+ * reach that condition.
+ *
+ * Once set, this is remembered across calls to
+ * keystroke_input(), while still wish to steal.
+ */
+ if (steal == NULL)
+ stream->steal_this = false; /* not now required */
+ else
+ stream->steal_this = (stream->in.state == kst_null) ;
+ /* want to and can can steal the next
+ keystroke that completes */
+
+ /* Once EOF has been signalled, do not expect to receive any further input.
+ *
+ * However, keystroke_stream_set_eof() can set the stream artificially at
+ * EOF, and that is honoured here.
+ */
+ if (stream->eof_met)
+ len = 0 ;
+
+ /* Normal processing
+ *
+ * Note that when manages to steal a keystroke sets steal == NULL, and
+ * proceeds to collect any following keystrokes.
+ */
+ end = ptr + len ;
+ while (ptr < end)
+ {
+ uint8_t u = *ptr++ ;
+
+ /* IAC handling takes precedence over everything, except the <option>
+ * byte, which may be EXOPL, which happens to be 255 as well !
+ *
+ * stream->iac means that the last thing seen was an IAC.
+ *
+ * First IAC sets the flag. On the next byte:
+ *
+ * * if is IAC, clears stream->iac, and lets the escaped IAC value
+ * through for further processing -- NOT in IAC state.
+ *
+ * * if is not IAC, will be let through for further processing, in
+ * IAC state.
+ */
+ if ((u == tn_IAC) && (stream->in.state != kst_iac_option))
+ {
+ if (stream->iac)
+ stream->iac = false ; /* IAC IAC => single IAC byte value */
+ else
+ {
+ stream->iac = true ; /* seen an IAC */
+ continue ; /* wait for next character */
+ } ;
+ } ;
+
+ /* If stream->iac, then need to worry about IAC XX
+ *
+ * Note that IAC sequences are entirely invisible to the general
+ * stream of keystrokes. So... IAC sequences may appear in the middle
+ * of multi-byte general keystroke objects.
+ *
+ * If this is not a simple 2 byte IAC, then must put whatever was
+ * collecting to one side, and deal with the IAC.
+ *
+ * Note: not interested in stealing an IAC object.
+ */
+ if (stream->iac)
+ {
+ stream->iac = false ; /* expect will eat the IAC XX */
+
+ switch (stream->in.state)
+ {
+ case kst_null:
+ case kst_cr:
+ case kst_esc:
+ case kst_csi:
+ if (u < tn_SB)
+ /* This is a simple IAC XX, one byte IAC */
+ keystroke_put_iac_one(stream, u) ;
+ else
+ /* This is a multi-byte IAC, so push whatever real
+ * keystroke sequence is currently on preparation, and
+ * set into kst_iac_option state.
+ */
+ keystroke_in_push(stream, u) ;
+ break ;
+
+ case kst_iac_sub:
+ assert(stream->in.raw[0] == tn_SB) ;
+
+ if (u != tn_SE)
+ {
+ --ptr ; /* put back the XX */
+ stream->iac = true ; /* put back the IAC */
+ } ;
+
+ keystroke_put_iac_long(stream, (u != tn_SE)) ;
+ keystroke_in_pop(stream) ;
+ break ;
+
+ case kst_char: /* TBD */
+ case kst_iac_option:
+ zabort("impossible keystroke stream state") ;
+
+ default:
+ zabort("unknown keystroke stream state") ;
+ } ;
+
+ continue ;
+ } ;
+
+ /* No IAC complications... proceed per current state */
+ switch (stream->in.state)
+ {
+ case kst_null: /* Expecting anything */
+ stream->steal_this = (steal != NULL) ;
+
+ if (u == '\r')
+ stream->in.state = kst_cr ;
+ else if (u == 0x1B)
+ stream->in.state = kst_esc ;
+ else if (u == stream->CSI) /* NB: CSI == 0x1B => no CSI */
+ {
+ stream->in.len = 0 ;
+ stream->in.state = kst_csi ;
+ }
+ else
+ {
+ /* Won't steal NUL */
+ if (!stream->steal_this || (u == '\0'))
+ keystroke_put_char(stream, u) ;
+ else
+ {
+ keystroke_steal_char(steal, stream, u) ;
+ stream->steal_this = false ;
+ steal = NULL ;
+ } ;
+
+ stream->in.state = kst_null ;
+ } ;
+ break ;
+
+ case kst_char: /* TBD */
+ zabort("impossible keystroke stream state") ;
+
+ case kst_cr: /* expecting something after CR */
+ if ((u != '\n') && (u != '\r'))
+ {
+ if (u != '\0')
+ {
+ --ptr ; /* put back the duff XX */
+ stream->iac = (u == tn_IAC) ;
+ /* re=escape if is IAC */
+ } ;
+ u = '\r' ;
+ } ;
+
+ if (!stream->steal_this)
+ keystroke_put_char(stream, u) ;
+ else
+ {
+ keystroke_steal_char(steal, stream, u) ;
+ stream->steal_this = false ;
+ steal = NULL ;
+ } ;
+
+ stream->in.state = kst_null ;
+ break ;
+
+ case kst_esc: /* Expecting XX after ESC */
+ if (u == '[')
+ {
+ stream->in.len = 0 ;
+ stream->in.state = kst_csi ;
+ }
+ else
+ {
+ if (!stream->steal_this)
+ keystroke_put_esc(stream, u, 1) ;
+ else
+ {
+ keystroke_steal_esc(steal, stream, u) ;
+ stream->steal_this = false ;
+ steal = NULL ;
+ } ;
+
+ stream->in.state = kst_null ;
+ } ;
+ break ;
+
+ case kst_csi: /* Expecting ... after ESC [ or CSI */
+ if ((u >= 0x20) && (u <= 0x3F))
+ keystroke_add_raw(stream, u) ;
+ else
+ {
+ bool ok ;
+
+ ok = stream->in.len < keystroke_max_len ;
+ /* have room for terminator */
+
+ if ((u < 0x40) || (u > 0x7E))
+ {
+ --ptr ; /* put back the duff XX */
+ stream->iac = (u == tn_IAC) ;
+ /* re=escape if is IAC */
+ u = '\0' ;
+ ok = false ; /* broken */
+ } ;
+
+ if (!stream->steal_this || !ok)
+ keystroke_put_csi(stream, u) ;
+ else
+ {
+ keystroke_steal_csi(steal, stream, u) ;
+ stream->steal_this = false ;
+ steal = NULL ;
+ } ;
+ stream->in.state = kst_null ;
+ } ;
+ break ;
+
+ case kst_iac_option: /* Expecting <option> after IAC XX */
+ assert(stream->in.len == 1) ;
+ keystroke_add_raw(stream, u) ;
+
+ if (stream->in.raw[0] == tn_SB)
+ stream->in.state = kst_iac_sub ;
+ else
+ {
+ keystroke_put_iac_long(stream, false) ;
+ keystroke_in_pop(stream) ;
+ } ;
+ break ;
+
+ case kst_iac_sub: /* Expecting sub stuff */
+ assert(stream->in.raw[0]== tn_SB) ;
+
+ keystroke_add_raw(stream, u) ;
+ break ;
+
+ default:
+ zabort("unknown keystroke stream state") ;
+ }
+ } ;
+ assert(ptr == end) ;
+
+ /* If did not steal a keystroke, return a ks_null -- which may be
+ * a knull_eof.
+ */
+ if (steal != NULL)
+ keystroke_set_null(stream, steal) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Single level stack for keystroke input state, so that can handle IAC
+ * sequences transparently.
+ */
+
+/* Push current state and set new current state for start of IAC option
+ * sequence.
+ */
+static void
+keystroke_in_push(keystroke_stream stream, uint8_t u)
+{
+ assert(stream->pushed_in.state == kst_null) ;
+
+ stream->pushed_in = stream->in ;
+
+ stream->in.len = 1 ;
+ stream->in.raw[0] = u ;
+
+ stream->in.state = kst_iac_option ;
+} ;
+
+/* Pop the pushed state and clear the pushed state to kst_null */
+static void
+keystroke_in_pop(keystroke_stream stream)
+{
+ stream->in = stream->pushed_in ;
+
+ stream->pushed_in.state = kst_null ;
+ stream->pushed_in.len = 0 ;
+} ;
+
+/*==============================================================================
+ * Fetch next keystroke from keystroke stream
+ *
+ * Returns: 1 => have a stroke type != ks_null
+ * 0 => stroke type is ks_null (may be EOF).
+ */
+extern int
+keystroke_get(keystroke_stream stream, keystroke stroke)
+{
+ int b ;
+ uint8_t* p ;
+ uint8_t* e ;
+
+ /* Get first byte and deal with FIFO empty response */
+ b = vio_fifo_get_byte(&stream->fifo) ;
+
+ if (b < 0)
+ return keystroke_set_null(stream, stroke) ;
+
+ /* Fetch first byte and deal with the simple character case */
+ if ((b & kf_compound) == 0) /* Simple character ? */
+ {
+ stroke->type = ks_char ;
+ stroke->value = b ;
+
+ stroke->flags = 0 ;
+ stroke->len = 1 ;
+ stroke->buf[0] = b ;
+
+ return 1 ;
+ } ;
+
+ /* Sex the compound keystroke */
+
+ stroke->type = b & kf_type_mask ;
+ stroke->value = 0 ;
+ stroke->flags = b & kf_flag_mask ;
+ stroke->len = keystroke_get_byte(stream) ;
+
+ assert(stroke->len <= keystroke_max_len) ;
+
+ /* Fetch what we need to the stroke buffer */
+ p = stroke->buf ;
+ e = p + stroke->len ;
+
+ while (p < e)
+ *p++ = keystroke_get_byte(stream) ;
+
+ p = stroke->buf ;
+
+ /* Complete the process, depending on the type */
+ switch (stroke->type)
+ {
+ case ks_null:
+ zabort("ks_null found in FIFO") ;
+
+ case ks_char:
+ /* If character is well formed, set its value */
+ if (stroke->flags == 0)
+ {
+ assert((stroke->len > 0) && (stroke->len <= 4)) ;
+ while (p < e)
+ stroke->value = (stroke->value << 8) + *p++ ;
+
+ /* NB: to do UTF-8 would need to create UTF form here */
+
+ } ;
+ break ;
+
+ case ks_esc:
+ /* If have ESC X, set value = X */
+ if (stroke->len == 1)
+ stroke->value = *p ;
+ else
+ assert(stroke->len == 0) ;
+ break ;
+
+ case ks_csi:
+ /* If have the final X, set value = X */
+ /* Null terminate the parameters */
+ if (stroke->len != 0)
+ {
+ --e ;
+ stroke->value = *e ;
+ --stroke->len ;
+ } ;
+ *e = '\0' ;
+ break ;
+
+ case ks_iac:
+ /* If have the command byte after IAC, set value */
+ if (stroke->len > 0)
+ stroke->value = *p ;
+ break ;
+
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given keystroke to ks_null -- and set to knull_eof if stream->eof_met
+ *
+ * Returns: 0
+ */
+inline static int
+keystroke_set_null(keystroke_stream stream, keystroke stroke)
+{
+ stroke->type = ks_null ;
+ stroke->value = stream->eof_met ? knull_eof : knull_not_eof ;
+
+ stroke->flags = 0 ;
+ stroke->len = 0 ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Fetch 2nd or subsequent byte of keystroke.
+ *
+ * NB: it is impossible for partial keystrokes to be written, so this treats
+ * buffer empty as a FATAL error.
+ */
+inline static uint8_t
+keystroke_get_byte(keystroke_stream stream)
+{
+ int b = vio_fifo_get_byte(&stream->fifo) ;
+
+ passert(b >= 0) ;
+
+ return b ;
+} ;
+
+/*==============================================================================
+ * Functions to support keystroke_input.
+ */
+
+/*------------------------------------------------------------------------------
+ * If possible, add character to the stream->in.raw[] buffer
+ */
+static inline void
+keystroke_add_raw(keystroke_stream stream, uint8_t u)
+{
+ if (stream->in.len < keystroke_max_len)
+ stream->in.raw[stream->in.len] = u ;
+
+ ++stream->in.len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store simple character value
+ */
+static void
+keystroke_put_char(keystroke_stream stream, uint32_t u)
+{
+ if (u < 0x80)
+ vio_fifo_put_byte(&stream->fifo, (uint8_t)u) ;
+ else
+ {
+ uint8_t buf[4] ;
+ uint8_t* p ;
+
+ p = buf + 4 ;
+
+ do
+ {
+ *(--p) = u & 0xFF ;
+ u >>= 8 ;
+ }
+ while (u != 0) ;
+
+ keystroke_put(stream, ks_char, 0, p, (buf + 4) - p) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store simple ESC. Is broken if length (after ESC) == 0 !
+ */
+static void
+keystroke_put_esc(keystroke_stream stream, uint8_t u, int len)
+{
+ keystroke_put(stream, ks_esc, (len == 0), &u, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store CSI.
+ *
+ * Plants the last character of the CSI in the buffer, even if has to overwrite
+ * the existing last character -- the sequence is truncated, but this way at
+ * least the end of the sequence is preserved.
+ *
+ * Is broken if u == '\0'. May also be truncated !
+ */
+static void
+keystroke_put_csi(keystroke_stream stream, uint8_t u)
+{
+ int l ;
+
+ l = stream->in.len++ ;
+
+ if (l >= keystroke_max_len)
+ l = keystroke_max_len - 1 ;
+
+ stream->in.raw[l] = u ; /* plant terminator */
+
+ keystroke_put(stream, ks_csi, (u == '\0'), stream->in.raw, stream->in.len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store IAC -- if not broken, send it via any call-back.
+ */
+static void
+keystroke_put_iac(keystroke_stream stream, bool broken, uint8_t* bytes, int len)
+{
+ bool dealt_with = false ;
+
+ if (!broken && (stream->iac_callback != NULL))
+ {
+ struct keystroke stroke ;
+
+ assert((len >= 1) && (bytes != NULL) && (len <= keystroke_max_len)) ;
+
+ stroke.type = ks_iac ;
+ stroke.value = bytes[0] ;
+ stroke.flags = 0 ;
+ stroke.len = len ;
+
+ memcpy(&stroke.buf, bytes, len) ;
+
+ dealt_with = (*stream->iac_callback)(stream->iac_callback_context,
+ &stroke) ;
+ } ;
+
+ if (!dealt_with)
+ keystroke_put(stream, ks_iac, broken, bytes, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store one byte IAC.
+ */
+static void
+keystroke_put_iac_one(keystroke_stream stream, uint8_t u)
+{
+ keystroke_put_iac(stream, false, &u, 1) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store long IAC. Is broken if says it is.
+ */
+static void
+keystroke_put_iac_long(keystroke_stream stream, bool broken)
+{
+ keystroke_put_iac(stream, broken, stream->in.raw, stream->in.len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If in IAC state, issue broken IAC and clear state.
+ */
+static void
+keystroke_clear_iac(keystroke_stream stream)
+{
+ if (stream->iac)
+ {
+ keystroke_put_iac(stream, true, NULL, 0) ;
+ stream->iac = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store <first> <len> [<bytes>]
+ *
+ * If len == 0, bytes may be NULL
+ */
+static void
+keystroke_put(keystroke_stream stream, enum keystroke_type type, bool broken,
+ uint8_t* bytes, int len)
+{
+ if (len > keystroke_max_len)
+ {
+ len = keystroke_max_len ;
+ type |= kf_truncated ;
+ } ;
+
+ vio_fifo_put_byte(&stream->fifo,
+ kf_compound | (broken ? kf_broken : 0) | type) ;
+ vio_fifo_put_byte(&stream->fifo, len) ;
+
+ if (len > 0)
+ vio_fifo_put(&stream->fifo, (void*)bytes, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal character value -- cannot be broken
+ */
+static void
+keystroke_steal_char(keystroke steal, keystroke_stream stream, uint8_t u)
+{
+ steal->type = ks_char ;
+ steal->value = u ;
+ steal->flags = 0 ;
+ steal->len = 1 ;
+ steal->buf[0] = u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal simple escape -- cannot be broken
+ */
+static void
+keystroke_steal_esc(keystroke steal, keystroke_stream stream, uint8_t u)
+{
+ steal->type = ks_esc ;
+ steal->value = u ;
+ steal->flags = 0 ;
+ steal->len = 1 ;
+ steal->buf[0] = u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal CSI escape -- cannot be broken or truncated.
+ *
+ * In the stream-in.raw buffer the last character is the escape terminator,
+ * after the escape parameters.
+ *
+ * In keystroke buffer the escape parameters are '\0' terminated, and the
+ * escape terminator is the keystroke value.
+ */
+static void
+keystroke_steal_csi(keystroke steal, keystroke_stream stream, uint8_t u)
+{
+ int len ;
+
+ len = stream->in.len ; /* excludes the escape terminator */
+ assert((len < keystroke_max_len) && (u >= 0x40) && (u <= 0x7E)) ;
+
+ steal->type = ks_esc ;
+ steal->value = u ;
+ steal->flags = 0 ;
+ steal->len = len ;
+
+ memcpy(steal->buf, stream->in.raw, len) ;
+ steal->buf[len] = '\0' ;
+} ;
+
+/*==============================================================================
+ */
diff --git a/lib/keystroke.h b/lib/keystroke.h
new file mode 100644
index 00000000..76fba15e
--- /dev/null
+++ b/lib/keystroke.h
@@ -0,0 +1,194 @@
+/* Keystroke Buffering -- header
+ * Copyright (C) 2010 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 _ZEBRA_KEYSTROKE_H
+#define _ZEBRA_KEYSTROKE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/telnet.h>
+
+#include "zassert.h"
+#include "vio_fifo.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Keystroke buffering
+ */
+
+enum { keystroke_max_len = 100 } ;
+
+enum keystroke_type
+{
+ ks_null = 0, /* nothing, nada, bupkis... */
+ ks_char, /* character -- uint32_t */
+ ks_esc, /* ESC xx */
+ ks_csi, /* ESC [ ... or CSI ... */
+ ks_iac, /* Telnet command */
+
+ ks_type_count,
+
+ ks_type_reserved = 0x0F,
+} ;
+CONFIRM(ks_type_count <= ks_type_reserved) ;
+
+enum keystroke_null
+{
+ knull_not_eof,
+ knull_eof
+};
+
+enum keystroke_flags
+{
+ kf_compound = 0x80, /* marker on all compound characters */
+
+ kf_reserved = 0x40,
+ kf_broken = 0x20, /* badly formed in some way */
+ kf_truncated = 0x10, /* too big for buffer ! */
+ /* for ks_null => EOF */
+
+ kf_flag_mask = 0x70, /* flags for the keystroke */
+
+ kf_type_mask = 0x0F, /* extraction of type */
+} ;
+
+CONFIRM(ks_type_reserved == (enum keystroke_type)kf_type_mask) ;
+
+typedef struct keystroke* keystroke ;
+typedef struct keystroke_stream* keystroke_stream ;
+
+struct keystroke
+{
+ enum keystroke_type type ;
+ uint8_t flags ; /* the kf_flag_mask flags */
+
+ uint32_t value ;
+
+ unsigned len ;
+ uint8_t buf[keystroke_max_len] ;
+} ;
+
+#define keystroke_iac_callback_args void* context, keystroke stroke
+typedef bool (keystroke_callback)(keystroke_iac_callback_args) ;
+
+/* Telnet commands/options */
+enum tn_Command
+{
+ tn_IAC = IAC, /* IAC IAC interpret as command: */
+ tn_DONT = DONT, /* IAC DONT <opt> you are not to use option */
+ tn_DO = DO, /* IAC DO <opt> please, you use option */
+ tn_WONT = WONT, /* IAC WONT <opt> I won't use option */
+ tn_WILL = WILL, /* IAC WILL <opt> I will use option */
+ tn_SB = SB, /* IAC SB <opt> interpret as subnegotiation */
+ tn_GA = GA, /* IAC GA you may reverse the line */
+ tn_EL = EL, /* IAC EL erase the current line */
+ tn_EC = EC, /* IAC EC erase the current character */
+ tn_AYT = AYT, /* IAC AYT are you there */
+ tn_AO = AO, /* IAC AO abort output--but let prog finish */
+ tn_IP = IP, /* IAC IP interrupt process--permanently */
+ tn_BREAK = BREAK, /* IAC BREAK break */
+ tn_DM = DM, /* IAC DM data mark--for connect. cleaning */
+ tn_NOP = NOP, /* IAC NOP nop */
+ tn_SE = SE, /* IAC SE end sub negotiation */
+ tn_EOR = EOR, /* IAC EOR end of record (transparent mode) */
+ tn_ABORT = ABORT, /* IAC ABORT Abort process */
+ tn_SUSP = SUSP, /* IAC SUSP Suspend process */
+ tn_EOF = xEOF, /* IAC xEOF End of file: EOF is already used... */
+
+ tn_SYNCH = SYNCH, /* IAC SYNCH for telfunc calls */
+} ;
+
+enum tn_Option
+{
+ to_BINARY = TELOPT_BINARY, /* 8-bit data path */
+ to_ECHO = TELOPT_ECHO, /* echo */
+ to_RCP = TELOPT_RCP, /* prepare to reconnect */
+ to_SGA = TELOPT_SGA, /* suppress go ahead */
+ to_NAMS = TELOPT_NAMS, /* approximate message size */
+ to_STATUS = TELOPT_STATUS, /* give status */
+ to_TM = TELOPT_TM, /* timing mark */
+ to_RCTE = TELOPT_RCTE, /* remote controlled tx and echo */
+ to_NAOL = TELOPT_NAOL, /* neg. about output line width */
+ to_NAOP = TELOPT_NAOP, /* neg. about output page size */
+ to_NAOCRD = TELOPT_NAOCRD, /* neg. about CR disposition */
+ to_NAOHTS = TELOPT_NAOHTS, /* neg. about horizontal tabstops */
+ to_NAOHTD = TELOPT_NAOHTD, /* neg. about horizontal tab disp. */
+ to_NAOFFD = TELOPT_NAOFFD, /* neg. about formfeed disposition */
+ to_NAOVTS = TELOPT_NAOVTS, /* neg. about vertical tab stops */
+ to_NAOVTD = TELOPT_NAOVTD, /* neg. about vertical tab disp. */
+ to_NAOLFD = TELOPT_NAOLFD, /* neg. about output LF disposition */
+ to_XASCII = TELOPT_XASCII, /* extended ascii character set */
+ to_LOGOUT = TELOPT_LOGOUT, /* force logout */
+ to_BM = TELOPT_BM, /* byte macro */
+ to_DET = TELOPT_DET, /* data entry terminal */
+ to_SUPDUP = TELOPT_SUPDUP, /* supdup protocol */
+ to_SUPDUPOUTPUT = TELOPT_SUPDUPOUTPUT,/* supdup output */
+ to_SNDLOC = TELOPT_SNDLOC, /* send location */
+ to_TTYPE = TELOPT_TTYPE, /* terminal type */
+ to_EOR = TELOPT_EOR, /* end or record */
+ to_TUID = TELOPT_TUID, /* TACACS user identification */
+ to_OUTMRK = TELOPT_OUTMRK, /* output marking */
+ to_TTYLOC = TELOPT_TTYLOC, /* terminal location number */
+ to_3270REGIME = TELOPT_3270REGIME, /* 3270 regime */
+ to_X3PAD = TELOPT_X3PAD, /* X.3 PAD */
+ to_NAWS = TELOPT_NAWS, /* window size */
+ to_TSPEED = TELOPT_TSPEED, /* terminal speed */
+ to_LFLOW = TELOPT_LFLOW, /* remote flow control */
+ to_LINEMODE = TELOPT_LINEMODE, /* Linemode option */
+ to_XDISPLOC = TELOPT_XDISPLOC, /* X Display Location */
+ to_OLD_ENVIRON = TELOPT_OLD_ENVIRON, /* Old - Environment variables */
+ to_AUTHENTICATION = TELOPT_AUTHENTICATION, /* Authenticate */
+ to_ENCRYPT = TELOPT_ENCRYPT, /* Encryption option */
+ to_NEW_ENVIRON = TELOPT_NEW_ENVIRON, /* New - Environment variables */
+ to_EXOPL = TELOPT_EXOPL, /* extended-options-list */
+} ;
+
+
+/*==============================================================================
+ * Functions
+ */
+extern keystroke_stream
+keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
+ void* iac_callback_context) ;
+
+extern void
+keystroke_stream_set_eof(keystroke_stream stream) ;
+
+extern keystroke_stream
+keystroke_stream_free(keystroke_stream stream) ;
+
+extern bool
+keystroke_stream_empty(keystroke_stream stream) ;
+extern bool
+keystroke_stream_eof(keystroke_stream stream) ;
+
+extern void
+keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
+ keystroke steal) ;
+extern int
+keystroke_get(keystroke_stream stream, keystroke stroke) ;
+
+#endif /* _ZEBRA_KEYSTROKE_H */
diff --git a/lib/list_util.c b/lib/list_util.c
new file mode 100644
index 00000000..720b8ca7
--- /dev/null
+++ b/lib/list_util.c
@@ -0,0 +1,80 @@
+/* List Utilities
+ * 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 <list_util.h>
+
+
+/*==============================================================================
+ * Single Base, Single Link
+ */
+
+/*------------------------------------------------------------------------------
+ * Deleting item
+ *
+ * Have to chase down list to find item.
+ *
+ * Note that p_this:
+ *
+ * * starts as pointer to the base pointer, so should really be void**,
+ * but that causes all sorts of problems with strict-aliasing.
+ *
+ * So: have to cast to (void**) before dereferencing to get the address
+ * of the first item on the list.
+ *
+ * * as steps along the list p_this points to the "next pointer" in the
+ * previous item.
+ *
+ * The _sl_p_next() macro adds the offset of the "next pointer" to the
+ * address of the this item.
+ *
+ * * at the end, assigns the item's "next pointer" to the "next pointer"
+ * field pointed at by p_this.
+ *
+ * Note again the cast to (void**).
+ *
+ * Returns: 0 => OK -- removed item from list (OR item == NULL)
+ * -1 => item not found on list
+ */
+extern int
+ssl_del_func(void* p_this, void* item, size_t link_offset)
+{
+ void* this ;
+
+ if (item == NULL)
+ return 0 ;
+
+ while ((this = *(void**)p_this) != item)
+ {
+ if (this == NULL)
+ return -1 ;
+
+ p_this = _sl_p_next(this, link_offset) ;
+ } ;
+
+ *(void**)p_this = _sl_next(item, link_offset) ;
+
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Single Base, Double Link
+ */
+
diff --git a/lib/list_util.h b/lib/list_util.h
new file mode 100644
index 00000000..876b7b11
--- /dev/null
+++ b/lib/list_util.h
@@ -0,0 +1,729 @@
+/* List Utilities -- 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 _ZEBRA_LIST_UTIL_H
+#define _ZEBRA_LIST_UTIL_H
+
+#include <stddef.h>
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*------------------------------------------------------------------------------
+ * Note that the following fell foul of "strict-aliasing":
+ *
+ * #define ssl_del_head(base, next) \
+ * ssl_del_head_func_i((void**)&(base), _lu_off(base, next))
+ *
+ * Inline void*
+ * ssl_del_head_func_i(void** p_base, size_t link_offset)
+ * {
+ * void* item = *p_base ;
+ *
+ * if (item != NULL)
+ * *p_base = _sl_next(item, link_offset) ;
+ *
+ * return item ;
+ * } ;
+ *
+ * the assignment to *p_base is, apparently, unacceptable. This works
+ * perfectly well as am ordinary function. Using a GNUC extension it is
+ * possible to avoid the function call... hence the ugly skips.
+ */
+#ifdef __GNUC__
+#define __GNUC__LIST_UTIL
+#endif
+
+/*==============================================================================
+ * These utilities provide for linked lists of items, where the list pointers
+ * are fields in the items.
+ *
+ * This is a little less general that the linklist stuff, but carries less
+ * overhead.
+ *
+ * The items will be structures of some sort, and are described here as being
+ * of type "struct item". Pointers to those items will be of type
+ * "struct item*".
+ *
+ * Most of these utilities are implemented as macros.
+ *
+ *------------------------------------------------------------------------------
+ * Links and Bases.
+ *
+ * For a singly linked list, the item declaration is straightforward:
+ *
+ * struct item
+ * {
+ * ....
+ * struct item* foo_next ;
+ * ....
+ * }
+ *
+ * The item can live on more than one list, all that is required is that each
+ * list has its next pointer.
+ *
+ * For double linked lists, the item may be declared:
+ *
+ * struct item
+ * {
+ * ....
+ * struct dl_list_pair(struct item*) foo_list ;
+ * ....
+ * } ;
+ *
+ * A single base is straighforward:
+ *
+ * struct item* foo_base ;
+ *
+ * and that may be a variable or a structure field.
+ *
+ * A double base may be declared:
+ *
+ * struct dl_base_pair(struct item*) foo_base ;
+ *
+ * Various ways to construct structures or structure types:
+ *
+ * typedef struct dl_list_pair(struct foo*) foo_list ;
+ *
+ * struct foo_list dl_list_pair(struct foo*) ;
+ *
+ * struct foo_base dl_base_pair(struct foo*) ;
+ */
+
+#define dl_list_pair(ptr_t) { ptr_t next ; ptr_t prev ; }
+
+#define dl_base_pair(ptr_t) { ptr_t head ; ptr_t tail ; }
+
+struct dl_void_list_pair list_pair(void*) ;
+struct dl_void_base_pair base_pair(void*) ;
+
+#define _lu_off(obj, field) ((char*)&((obj)->field) - (char*)(obj))
+
+/*==============================================================================
+ * Single Base, Single Link
+ *
+ * To delete entry must chase down list to find it.
+ *
+ * Supports:
+ *
+ * ssl_init(base) -- initialise base
+ *
+ * An empty list has a NULL base.
+ *
+ * ssl_push(base, item, next) -- add at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ssl_del(base, item, next) -- delete from list
+ *
+ * Treat as function returning int. Does nothing if the item is NULL.
+ *
+ * Returns: 0 => OK -- removed item from list (OR item == NULL)
+ * -1 => item not found on list
+ *
+ * ssl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ssl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ssl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * ssl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if item is NULL.
+ *
+ * Note that ssl_del() and ssl_pop() do NOT affect the item->next pointer.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type struct item*
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "next" to be the name of a field in struct item, with type struct item*
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct item* bar_next ;
+ * ...
+ * } ;
+ *
+ * static struct item* bar_base ; // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * ssl_push(bar_base, q, bar_next) ;
+ *
+ * // remove item from list
+ * ssl_del(bar_base, q, bar_next) ;
+ *
+ * // walk a list
+ * struct item* t = ssl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = ssl_next(t, bar_next) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (ssl_pop(&t, bar_base, bar_next) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = ssl_head(bar_base) != NULL)
+ * {
+ * ....
+ * ssl_del_head(bar_base, bar_next) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct item* bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * ssl_push(parent->bar_base, item, bar_next) ;
+ * ....
+ * }
+ */
+
+#define ssl_init(base) \
+ ((base) = NULL)
+
+#define ssl_push(base, item, next) \
+ do { (item)->next = (base) ; \
+ (base) = item ; \
+ } while (0)
+
+extern int ssl_del_func(void* p_this, void* obj, size_t link_offset)
+ __attribute__((noinline)) ;
+
+#define ssl_del(base, item, next) \
+ ssl_del_func((void*)(&base), item, _lu_off(item, next))
+
+#define ssl_del_head(base, next) \
+ do { if ((base) != NULL) \
+ (base) = (base)->next ; \
+ } while (0)
+
+#define ssl_pop(dst, base, next) \
+ ((*(dst) = (base)) != NULL ? ((base) = (base)->next, *(dst)) : NULL)
+
+#define ssl_head(base) (base)
+
+#define ssl_next(item, next) \
+ ((item) != NULL ? (item)->next : NULL)
+
+/* _sl_p_next(item, off) -- pointer to next pointer at given offset
+ * _sl_next(item, off) -- contents of next pointer at given offset
+ */
+
+#define _sl_p_next(item, off) \
+ ( (char*)(item) + (off) )
+
+#define _sl_next(item, off) \
+ *(void**)_sl_p_next(item, off)
+
+/*==============================================================================
+ * Single Base, Double Link
+ *
+ * Can delete entry directly.
+ *
+ * Supports:
+ *
+ * sdl_init(base) -- initialise base
+ *
+ * An empty list has a NULL base.
+ *
+ * sdl_push(base, item, list) -- add at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * sdl_del(base, item, list) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * sdl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * sdl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * sdl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * sdl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * sdl_prev(item, next) -- step to prev item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that sdl_del() and sdl_pop() do NOT affect the item->list.next
+ * or item->list.prev pointers.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "list" to be the name of a field in struct item
+ * of type: struct list_pair(struct item*)
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct list_pair(struct item*) bar_list ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * sdl_push(bar_base, q, bar_list) ;
+ *
+ * // remove item from list
+ * sdl_del(bar_base, q, bar_list) ;
+ *
+ * // walk a list
+ * struct item* t = sdl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = sdl_next(t, bar_list) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (sdl_pop(&t, bar_base, bar_list) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = sdl_head(bar_base) != NULL)
+ * {
+ * ....
+ * sdl_del_head(bar_base, bar_list) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * sdl_push(parent->bar_base, item, bar_list) ;
+ * ....
+ * }
+ */
+
+#define sdl_init(base) \
+ ((base) = NULL)
+
+#define sdl_push(base, item, list) \
+ do { confirm(_lu_off(base, list.next) == _lu_off(item, list.next)) ; \
+ confirm(_lu_off(base, list.prev) == _lu_off(item, list.prev)) ; \
+ (item)->list.next = (base) ; \
+ (item)->list.prev = NULL ; \
+ if ((base) != NULL) \
+ (base)->list.prev = (item) ; \
+ (base) = (item) ; \
+ } while (0)
+
+#define sdl_del(base, item, list) \
+ do { confirm(_lu_off(base, list.next) == _lu_off(item, list.next)) ; \
+ confirm(_lu_off(base, list.prev) == _lu_off(item, list.prev)) ; \
+ if ((item) != NULL) \
+ { \
+ if ((item)->list.next != NULL) \
+ (item)->list.next->list.prev = (item)->list.prev ; \
+ if ((item)->list.prev != NULL) \
+ (item)->list.prev->list.next = (item)->list.next ; \
+ else \
+ (base) = (item)->list.next ; \
+ } ; \
+ } while (0)
+
+#define sdl_del_head(base, list) \
+ do { if ((base) != NULL) \
+ { \
+ (base) = (base)->list.next ; \
+ if ((base) != NULL) \
+ (base)->list.prev = NULL ; \
+ } \
+ } while (0)
+
+#define sdl_pop(dst, base, list) \
+ ((*(dst) = (base)) != NULL \
+ ? ( ((base) = (base)->list.next) != NULL \
+ ? ( (base)->list.prev = NULL, *(dst) ) : *(dst) ) : NULL)
+
+#define sdl_head(base) (base)
+
+#define sdl_next(item, list) \
+ ((item) != NULL ? (item)->list.next : NULL)
+
+#define sdl_prev(item, list) \
+ ((item) != NULL ? (item)->list.prev : NULL)
+
+/* _dl_p_next(obj, off) -- pointer to next pointer at given offset
+ * _dl_next(obj, off) -- contents of next pointer at given offset
+ * _dl_p_prev(obj, off) -- pointer to prev pointer at given offset
+ * _dl_prev(obj, off) -- contents of prev pointer at given offset
+ */
+#define _dl_p_next(obj, off) \
+ ( (void**)( (char*)(obj) + (off) + 0 ) )
+
+#define _dl_next(obj, off) \
+ *_dl_p_next(obj, off)
+
+#define _dl_p_prev(obj, off) \
+ ( (void**)( (char*)(obj) + (off) _ sizeof(void*) ) )
+
+#define _dl_prev(obj, off) \
+ *_dl_p_next(obj, off)
+
+/*==============================================================================
+ * Double Base, Double Link
+ *
+ * Can delete entry directly. Can insert and remove at tail.
+ *
+ * Supports:
+ *
+ * ddl_init(base) -- initialise base
+ *
+ * An empty list has *both* head and tail pointers NULL.
+ *
+ * NB: confusion will arise if only one of these pointers is NULL.
+ *
+ * ddl_push(base, item, list) -- insert at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ddl_append(base, item, list) -- insert at tail of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ddl_in_after(after, base, item, list) -- insert after
+ *
+ * Treat as void function. The after & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * after is not on the list.
+ *
+ * ddl_in_before(before, base, item, list) -- insert before
+ *
+ * Treat as void function. The before & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * before is not on the list.
+ *
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ddl_del(base, item, list) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * ddl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ddl_del_tail(base, next) -- delete tail of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ddl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * ddl_tail(base) -- return tail of list
+ *
+ * Treat as function returning void*.
+ *
+ * ddl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that ddl_del() and ddl_pop() do NOT affect the item->list.next
+ * or item->list.prev pointers.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "list" to be the name of a field in struct item
+ * of type: struct list_pair(struct item*)
+ *
+ *
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct list_pair(struct item*) bar_list ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * ddl_push(bar_base, q, bar_list) ;
+ *
+ * // remove item from list
+ * ddl_del(bar_base, q, bar_list) ;
+ *
+ * // walk a list
+ * struct item* t = ddl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = ddl_next(t, bar_list) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (ddl_pop(&t, bar_base, bar_list) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = ddl_head(bar_base) != NULL)
+ * {
+ * ....
+ * ddl_del_head(bar_base, bar_list) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * ddl_push(parent->bar_base, item, bar_list) ;
+ * ....
+ * }
+ */
+
+#define ddl_init(base) \
+ ((base).head = (base).tail = NULL)
+
+#define ddl_push(base, item, list) \
+ do { (item)->list.next = (base).head ; \
+ (item)->list.prev = NULL ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (base).head = (item) ; \
+ } while (0)
+
+#define ddl_append(base, item, list) \
+ do { (item)->list.next = NULL ; \
+ (item)->list.prev = (base).tail ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (base).tail = (item) ; \
+ } while (0)
+
+#define ddl_in_after(after, base, item, list) \
+ do { (item)->list.next = (after)->list.next ; \
+ (item)->list.prev = (after) ; \
+ if ((after)->list.next != NULL) \
+ (after)->list.next->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (after)->list.next = (item) ; \
+ } while (0)
+
+#define ddl_in_before(before, base, item, list) \
+ do { (item)->list.next = (before) ; \
+ (item)->list.prev = (before)->list.prev ; \
+ if ((before)->list.prev != NULL) \
+ (before)->list.prev->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (before)->list.prev = (item) ; \
+ } while (0)
+
+#define ddl_del(base, item, list) \
+ do { if ((item) != NULL) \
+ { \
+ if ((item)->list.next != NULL) \
+ (item)->list.next->list.prev = (item)->list.prev ; \
+ else \
+ (base).tail = (item)->list.prev ; \
+ if ((item)->list.prev != NULL) \
+ (item)->list.prev->list.next = (item)->list.next ; \
+ else \
+ (base).head = (item)->list.next ; \
+ } ; \
+ } while (0)
+
+#define ddl_del_head(base, list) \
+ do { if ((base).head != NULL) \
+ { \
+ (base).head = (base).head->list.next ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = NULL ; \
+ else \
+ (base).tail = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_del_tail(base, list) \
+ do { if ((base).tail != NULL) \
+ { \
+ (base).tail = (base).tail->list.prev ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = NULL ; \
+ else \
+ (base).head = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_pop(dst, base, list) \
+ ((*(dst) = (base).head) != NULL \
+ ? ( ((base).head = (base).head->list.next) != NULL \
+ ? ( (base).head->list.prev = NULL, *(dst) ) \
+ : ( (base).tail = NULL, *(dst) ) ) \
+ : NULL)
+
+#define ddl_crop(dst, base, list) \
+ ((*(dst) = (base).tail) != NULL \
+ ? ( ((base).tail = (base).tail->list.prev) != NULL \
+ ? ( (base).tail->list.next = NULL, *(dst) ) \
+ : ( (base).head = NULL, *(dst) ) ) \
+ : NULL)
+
+#define ddl_head(base) ((base).head)
+
+#define ddl_tail(base) ((base).tail)
+
+#define ddl_next(item, list) \
+ ((item) != NULL ? (item)->list.next : NULL)
+
+#define ddl_prev(item, list) \
+ ((item) != NULL ? (item)->list.prev : NULL)
+
+#endif /* _ZEBRA_LIST_UTIL_H */
diff --git a/lib/log.c b/lib/log.c
index df6e13db..18878094 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -19,12 +19,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.
*/
#include <zebra.h>
#include "log.h"
+#include "vty.h"
+#include "uty.h"
#include "memory.h"
#include "command.h"
#ifndef SUNOS_5
@@ -34,12 +36,24 @@
#ifdef HAVE_UCONTEXT_H
#include <ucontext.h>
#endif
+#include "qpthreads.h"
+#include "qfstring.h"
+#include "sigevent.h"
+
+/* log is protected by the same mutext as vty, see comments in vty.c */
+
+/* prototypes */
+static int uzlog_reset_file (struct zlog *zl);
+static void zlog_abort (const char *mess) __attribute__ ((noreturn));
+static void vzlog (struct zlog *zl, int priority, const char *format, va_list args);
+static void uzlog_backtrace(int priority);
+static void uvzlog (struct zlog *zl, int priority, const char *format, va_list args);
static int logfile_fd = -1; /* Used in signal handler. */
struct zlog *zlog_default = NULL;
-const char *zlog_proto_names[] =
+const char *zlog_proto_names[] =
{
"NONE",
"DEFAULT",
@@ -66,19 +80,56 @@ const char *zlog_priority[] =
"debugging",
NULL,
};
-
-
-/* For time string format. */
+/*==============================================================================
+ * Time stamp handling -- gettimeofday(), localtime() and strftime().
+ *
+ * Maintains a cached form of the current time (under the vty/log mutex), so
+ * that can avoid multiple calls of localtime() and strftime() per second.
+ *
+ * The value from gettimeofday() is in micro-seconds. Can provide timestamp
+ * with any number of decimal digits, but at most 6 will be significant.
+ */
+
+static void uquagga_timestamp(qf_str qfs, int timestamp_precision) ;
-size_t
+/*------------------------------------------------------------------------------
+ * Fill buffer with current time, to given number of decimal digits.
+ *
+ * If given buffer is too small, provides as many characters as possible.
+ *
+ * Returns: number of characters in buffer, not including trailing '\0'.
+ *
+ * NB: does no rounding.
+ *
+ * NB: buflen MUST be > 1 and buf MUST NOT be NULL.
+ */
+extern size_t
quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
{
+ qf_str_t qfs ;
+
+ VTY_LOCK() ;
+
+ qfs_init(&qfs, buf, buflen) ;
+ uquagga_timestamp(&qfs, timestamp_precision) ;
+
+ VTY_UNLOCK() ;
+ return qfs_len(&qfs) ;
+}
+
+/*------------------------------------------------------------------------------
+ * unprotected version for when mutex already held
+ */
+static void
+uquagga_timestamp(qf_str qfs, int timestamp_precision)
+{
static struct {
time_t last;
size_t len;
- char buf[28];
+ char buf[timestamp_buffer_len];
} cache;
+
struct timeval clock;
/* would it be sufficient to use global 'recent_time' here? I fear not... */
@@ -87,134 +138,175 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
/* first, we update the cache if the time has changed */
if (cache.last != clock.tv_sec)
{
- struct tm *tm;
+ struct tm tm;
cache.last = clock.tv_sec;
- tm = localtime(&cache.last);
- cache.len = strftime(cache.buf, sizeof(cache.buf),
- "%Y/%m/%d %H:%M:%S", tm);
+ localtime_r(&cache.last, &tm);
+ cache.len = strftime(cache.buf, sizeof(cache.buf), TIMESTAMP_FORM, &tm) ;
+ assert(cache.len > 0) ;
}
+
/* note: it's not worth caching the subsecond part, because
- chances are that back-to-back calls are not sufficiently close together
- for the clock not to have ticked forward */
-
- if (buflen > cache.len)
- {
- memcpy(buf, cache.buf, cache.len);
- if ((timestamp_precision > 0) &&
- (buflen > cache.len+1+timestamp_precision))
- {
- /* should we worry about locale issues? */
- static const int divisor[] = {0, 100000, 10000, 1000, 100, 10, 1};
- int prec;
- char *p = buf+cache.len+1+(prec = timestamp_precision);
- *p-- = '\0';
- while (prec > 6)
- /* this is unlikely to happen, but protect anyway */
- {
- *p-- = '0';
- prec--;
- }
- clock.tv_usec /= divisor[prec];
- do
- {
- *p-- = '0'+(clock.tv_usec % 10);
- clock.tv_usec /= 10;
- }
- while (--prec > 0);
- *p = '.';
- return cache.len+1+timestamp_precision;
- }
- buf[cache.len] = '\0';
- return cache.len;
- }
- if (buflen > 0)
- buf[0] = '\0';
- return 0;
-}
+ * chances are that back-to-back calls are not sufficiently close together
+ * for the clock not to have ticked forward
+ */
+
+ qfs_append_n(qfs, cache.buf, cache.len) ;
-/* Utility routine for current time printing. */
+ /* Add decimal part as required. */
+ if (timestamp_precision > 0)
+ {
+ /* should we worry about locale issues? */
+ static const int divisor[] = { 1, /* 0 */
+ 100000, 10000, 1000, /* 1, 2, 3 */
+ 100, 10, 1}; /* 4, 5, 6 */
+ int prec;
+
+ prec = timestamp_precision ;
+ if (prec > 6)
+ prec = 6 ;
+
+ qfs_append_n(qfs, ".", 1) ;
+ qfs_unsigned(qfs, clock.tv_usec / divisor[prec], 0, 0, prec) ;
+
+ if (prec < timestamp_precision)
+ qfs_append_ch_x_n(qfs, '0', timestamp_precision - prec) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * va_list version of zlog
+ */
static void
-time_print(FILE *fp, struct timestamp_control *ctl)
+vzlog (struct zlog *zl, int priority, const char *format, va_list args)
{
- if (!ctl->already_rendered)
- {
- ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
- ctl->already_rendered = 1;
- }
- fprintf(fp, "%s ", ctl->buf);
+ VTY_LOCK() ;
+ uvzlog(zl, priority, format, args);
+ VTY_UNLOCK() ;
}
-
-
-/* va_list version of zlog. */
+
+/* va_list version of zlog. Unprotected assumes mutex already held*/
static void
-vzlog (struct zlog *zl, int priority, const char *format, va_list args)
+uvzlog (struct zlog *zl, int priority, const char *format, va_list va)
{
- struct timestamp_control tsctl;
- tsctl.already_rendered = 0;
+ struct logline ll ; /* prepares line for output, here */
+
+ VTY_ASSERT_LOCKED() ;
- /* If zlog is not specified, use default one. */
+ ll.p_nl = NULL ; /* Nothing generated, yet */
+
+ /* If zlog is not specified, use default one. */
if (zl == NULL)
- zl = zlog_default;
+ zl = zlog_default ;
- /* When zlog_default is also NULL, use stderr for logging. */
+ /* When zlog_default is also NULL, use stderr for logging. */
if (zl == NULL)
{
- tsctl.precision = 0;
- time_print(stderr, &tsctl);
- fprintf (stderr, "%s: ", "unknown");
- vfprintf (stderr, format, args);
- fprintf (stderr, "\n");
- fflush (stderr);
-
- /* In this case we return at here. */
- return;
+ uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
+ write(fileno(stderr), ll.line, ll.len) ;
}
- tsctl.precision = zl->timestamp_precision;
-
- /* Syslog output */
- if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
+ else
{
- va_list ac;
- va_copy(ac, args);
- vsyslog (priority|zlog_default->facility, format, ac);
- va_end(ac);
- }
+ /* Syslog output */
+ if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
+ {
+ va_list ac;
+ va_copy(ac, va);
+ vsyslog (priority|zlog_default->facility, format, ac);
+ va_end(ac);
+ }
- /* File output. */
- if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
- {
- va_list ac;
- time_print (zl->fp, &tsctl);
- if (zl->record_priority)
- fprintf (zl->fp, "%s: ", zlog_priority[priority]);
- fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]);
- va_copy(ac, args);
- vfprintf (zl->fp, format, ac);
- va_end(ac);
- fprintf (zl->fp, "\n");
- fflush (zl->fp);
+ /* File output. */
+ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
+ {
+ uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
+ write(fileno(zl->fp), ll.line, ll.len) ;
+ }
+
+ /* stdout output. */
+ if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
+ {
+ uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
+ write(fileno(zl->fp), ll.line, ll.len) ;
+ }
+
+ /* Terminal monitor. */
+ if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
+ uty_log(&ll, zl, priority, format, va) ;
}
+}
- /* stdout output. */
- if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
+/*------------------------------------------------------------------------------
+ * Preparation of line to send to logging: file, stdout or "monitor" terminals.
+ *
+ * Takes copy of va_list before using it, so the va_list is unchanged.
+ */
+extern void
+uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va, enum ll_term term)
+{
+ char* p ;
+ const char* q ;
+
+ p = ll->p_nl ;
+
+ if (p != NULL)
{
- va_list ac;
- time_print (stdout, &tsctl);
- if (zl->record_priority)
- fprintf (stdout, "%s: ", zlog_priority[priority]);
- fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]);
- va_copy(ac, args);
- vfprintf (stdout, format, ac);
- va_end(ac);
- fprintf (stdout, "\n");
- fflush (stdout);
+ /* we have the line -- just need to worry about the crlf state */
+ if (term == ll->term)
+ return ; /* exit here if all set */
}
+ else
+ {
+ /* must construct the line */
+ qf_str_t qfs ;
+ va_list vac ;
- /* Terminal monitor. */
- if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
- vty_log ((zl->record_priority ? zlog_priority[priority] : NULL),
- zlog_proto_names[zl->protocol], format, &tsctl, args);
-}
+ qfs_init(&qfs, ll->line, sizeof(ll->line) - 2) ;
+ /* leave space for '\n' or '\r''\n' */
+ /* "<time stamp>" */
+ uquagga_timestamp(&qfs, (zl != NULL) ? zl->timestamp_precision : 0) ;
+
+ qfs_append_n(&qfs, " ", 1) ;
+
+ /* "<priority>: " if required */
+ if ((zl != NULL) && (zl->record_priority))
+ {
+ qfs_append(&qfs, zlog_priority[priority]) ;
+ qfs_append(&qfs, ": ") ;
+ } ;
+
+ /* "<protocol>: " or "unknown: " */
+ if (zl != NULL)
+ q = zlog_proto_names[zl->protocol] ;
+ else
+ q = "unknown" ;
+
+ qfs_append(&qfs, q) ;
+ qfs_append(&qfs, ": ") ;
+
+ /* Now the log line itself */
+ va_copy(vac, va);
+ qfs_vprintf(&qfs, format, vac) ;
+ va_end(vac);
+
+ /* Set pointer to where the '\0' is. */
+ p = ll->p_nl = qfs_ptr(&qfs) ;
+ } ;
+
+ /* finish off with '\r''\n''\0' or '\n''\0' as required */
+ if (term == llt_crlf)
+ *p++ = '\r' ;
+
+ if (term != llt_nul)
+ *p++ = '\n' ;
+
+ *p = '\0' ;
+
+ ll->len = p - ll->line ;
+ ll->term = term ;
+} ;
+
+/*============================================================================*/
static char *
str_append(char *dst, int len, const char *src)
@@ -428,6 +520,13 @@ zlog_signal(int signo, const char *action
#undef LOC
}
+/* Ring down the curtain -- turn of SIGABRT handler and abort() */
+void zabort_abort(void)
+{
+ quagga_sigabrt_no_trap() ;
+ abort() ;
+}
+
/* Log a backtrace using only async-signal-safe functions.
Needs to be enhanced to support syslog logging. */
void
@@ -492,7 +591,7 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
for (i = 0; i < size; i++)
{
s = buf;
- if (bt)
+ if (bt)
s = str_append(LOC, bt[i]);
else {
s = str_append(LOC,"[bt ");
@@ -518,8 +617,16 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
void
zlog_backtrace(int priority)
{
+ VTY_LOCK() ;
+ uzlog_backtrace(priority);
+ VTY_UNLOCK() ;
+}
+
+static void
+uzlog_backtrace(int priority)
+{
#ifndef HAVE_GLIBC_BACKTRACE
- zlog(NULL, priority, "No backtrace available on this platform.");
+ uzlog(NULL, priority, "No backtrace available on this platform.");
#else
void *array[20];
int size, i;
@@ -528,27 +635,38 @@ zlog_backtrace(int priority)
if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
((size_t)size > sizeof(array)/sizeof(array[0])))
{
- zlog_err("Cannot get backtrace, returned invalid # of frames %d "
+ uzlog(NULL, LOG_ERR, "Cannot get backtrace, returned invalid # of frames %d "
"(valid range is between 1 and %lu)",
size, (unsigned long)(sizeof(array)/sizeof(array[0])));
return;
}
- zlog(NULL, priority, "Backtrace for %d stack frames:", size);
+ uzlog(NULL, priority, "Backtrace for %d stack frames:", size);
if (!(strings = backtrace_symbols(array, size)))
{
- zlog_err("Cannot get backtrace symbols (out of memory?)");
+ uzlog(NULL, LOG_ERR, "Cannot get backtrace symbols (out of memory?)");
for (i = 0; i < size; i++)
- zlog(NULL, priority, "[bt %d] %p",i,array[i]);
+ uzlog(NULL, priority, "[bt %d] %p",i,array[i]);
}
else
{
for (i = 0; i < size; i++)
- zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
+ uzlog(NULL, priority, "[bt %d] %s",i,strings[i]);
free(strings);
}
#endif /* HAVE_GLIBC_BACKTRACE */
}
+/* unlocked version */
+void
+uzlog (struct zlog *zl, int priority, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ uvzlog (zl, priority, format, args);
+ va_end (args);
+}
+
void
zlog (struct zlog *zl, int priority, const char *format, ...)
{
@@ -605,20 +723,71 @@ PLOG_FUNC(plog_debug, LOG_DEBUG)
void
_zlog_assert_failed (const char *assertion, const char *file,
- unsigned int line, const char *function)
+ unsigned int line, const char *function)
{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size,
+ "Assertion `%s' failed in file %s, line %u, function %s",
+ assertion, file, line, (function ? function : "?"));
+ zlog_abort(buff);
+}
+
+/* Abort with message */
+void
+_zlog_abort_mess (const char *mess, const char *file,
+ unsigned int line, const char *function)
+{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size, "%s, in file %s, line %u, function %s",
+ mess, file, line, (function ? function : "?"));
+ zlog_abort(buff);
+}
+
+/* Abort with message and errno and strerror() thereof */
+void
+_zlog_abort_errno (const char *mess, const char *file,
+ unsigned int line, const char *function)
+{
+ _zlog_abort_err(mess, errno, file, line, function);
+}
+
+/* Abort with message and given error and strerror() thereof */
+void
+_zlog_abort_err (const char *mess, int err, const char *file,
+ unsigned int line, const char *function)
+{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size,
+ "%s, in file %s, line %u, function %s, %s",
+ mess, file, line, (function ? function : "?"),
+ errtoa(err, 0).str);
+ zlog_abort(buff);
+}
+
+
+static void
+zlog_abort (const char *mess)
+{
+#if VTY_DEBUG
+ /* May not be locked -- but that doesn't matter any more */
+ ++vty_lock_count ;
+#endif
+
/* Force fallback file logging? */
if (zlog_default && !zlog_default->fp &&
((logfile_fd = open_crashlog()) >= 0) &&
((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
- zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
- assertion,file,line,(function ? function : "?"));
- zlog_backtrace(LOG_CRIT);
- abort();
+
+ uzlog(NULL, LOG_CRIT, "%s", mess);
+ uzlog_backtrace(LOG_CRIT);
+ zabort_abort();
}
-
+
/* Open log stream */
struct zlog *
openzlog (const char *progname, zlog_proto_t protocol,
@@ -641,7 +810,7 @@ openzlog (const char *progname, zlog_proto_t protocol,
zl->default_lvl = LOG_DEBUG;
openlog (progname, syslog_flags, zl->facility);
-
+
return zl;
}
@@ -663,10 +832,17 @@ closezlog (struct zlog *zl)
void
zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
{
+ VTY_LOCK() ;
+
if (zl == NULL)
zl = zlog_default;
- zl->maxlvl[dest] = log_level;
+ if (zl != NULL)
+ {
+ zl->maxlvl[dest] = log_level;
+ }
+
+ VTY_UNLOCK() ;
}
int
@@ -674,46 +850,68 @@ zlog_set_file (struct zlog *zl, const char *filename, int log_level)
{
FILE *fp;
mode_t oldumask;
+ int result = 1;
+
+ VTY_LOCK() ;
/* There is opend file. */
- zlog_reset_file (zl);
+ uzlog_reset_file (zl);
/* Set default zl. */
if (zl == NULL)
zl = zlog_default;
- /* Open file. */
- oldumask = umask (0777 & ~LOGFILE_MASK);
- fp = fopen (filename, "a");
- umask(oldumask);
- if (fp == NULL)
- return 0;
-
- /* Set flags. */
- zl->filename = strdup (filename);
- zl->maxlvl[ZLOG_DEST_FILE] = log_level;
- zl->fp = fp;
- logfile_fd = fileno(fp);
+ if (zl != NULL)
+ {
+ /* Open file. */
+ oldumask = umask (0777 & ~LOGFILE_MASK);
+ fp = fopen (filename, "a");
+ umask(oldumask);
+ if (fp == NULL)
+ result = 0;
+ else
+ {
+ /* Set flags. */
+ zl->filename = strdup (filename);
+ zl->maxlvl[ZLOG_DEST_FILE] = log_level;
+ zl->fp = fp;
+ logfile_fd = fileno(fp);
+ }
+ }
- return 1;
+ VTY_UNLOCK() ;
+ return result;
}
/* Reset opend file. */
int
zlog_reset_file (struct zlog *zl)
{
+ int result;
+ VTY_LOCK() ;
+ result = uzlog_reset_file(zl);
+ VTY_UNLOCK() ;
+ return result;
+}
+
+static int
+uzlog_reset_file (struct zlog *zl)
+ {
if (zl == NULL)
zl = zlog_default;
- if (zl->fp)
- fclose (zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
-
- if (zl->filename)
- free (zl->filename);
- zl->filename = NULL;
+ if (zl != NULL)
+ {
+ if (zl->fp)
+ fclose (zl->fp);
+ zl->fp = NULL;
+ logfile_fd = -1;
+ zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+
+ if (zl->filename)
+ free (zl->filename);
+ zl->filename = NULL;
+ }
return 1;
}
@@ -723,54 +921,334 @@ int
zlog_rotate (struct zlog *zl)
{
int level;
+ int result = 1;
+
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
- if (zl->fp)
- fclose (zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- level = zl->maxlvl[ZLOG_DEST_FILE];
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+ if (zl != NULL)
+ {
+ if (zl->fp)
+ fclose (zl->fp);
+ zl->fp = NULL;
+ logfile_fd = -1;
+ level = zl->maxlvl[ZLOG_DEST_FILE];
+ zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+
+ if (zl->filename)
+ {
+ mode_t oldumask;
+ int save_errno;
+
+ oldumask = umask (0777 & ~LOGFILE_MASK);
+ zl->fp = fopen (zl->filename, "a");
+ save_errno = errno;
+ umask(oldumask);
+ if (zl->fp == NULL)
+ {
+ /* can't call logging while locked */
+ char *fname = strdup(zl->filename);
+ uzlog(NULL, LOG_ERR,
+ "Log rotate failed: cannot open file %s for append: %s",
+ fname, errtoa(save_errno, 0).str);
+ free(fname);
+ result = -1;
+ }
+ else
+ {
+ logfile_fd = fileno(zl->fp);
+ zl->maxlvl[ZLOG_DEST_FILE] = level;
+ }
+ }
+ }
+ VTY_UNLOCK() ;
+ return result;
+}
+
+int
+zlog_get_default_lvl (struct zlog *zl)
+{
+ int result = LOG_DEBUG;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
- if (zl->filename)
+ if (zl != NULL)
{
- mode_t oldumask;
- int save_errno;
+ result = zl->default_lvl;
+ }
- oldumask = umask (0777 & ~LOGFILE_MASK);
- zl->fp = fopen (zl->filename, "a");
- save_errno = errno;
- umask(oldumask);
- if (zl->fp == NULL)
- {
- zlog_err("Log rotate failed: cannot open file %s for append: %s",
- zl->filename, safe_strerror(save_errno));
- return -1;
- }
- logfile_fd = fileno(zl->fp);
- zl->maxlvl[ZLOG_DEST_FILE] = level;
+ VTY_UNLOCK() ;
+ return result;
+}
+
+void
+zlog_set_default_lvl (struct zlog *zl, int level)
+{
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->default_lvl = level;
}
- return 1;
+ VTY_UNLOCK() ;
+}
+
+/* Set logging level and default for all destinations */
+void
+zlog_set_default_lvl_dest (struct zlog *zl, int level)
+{
+ int i;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->default_lvl = level;
+
+ for (i = 0; i < ZLOG_NUM_DESTS; i++)
+ if (zl->maxlvl[i] != ZLOG_DISABLED)
+ zl->maxlvl[i] = level;
+ }
+
+ VTY_UNLOCK() ;
+}
+
+int
+zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest)
+{
+ int result = ZLOG_DISABLED;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->maxlvl[dest];
+ }
+
+ VTY_UNLOCK() ;
+ return result;
+}
+
+int
+zlog_get_facility (struct zlog *zl)
+{
+ int result = LOG_DAEMON;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->facility;
+ }
+
+ VTY_UNLOCK() ;
+ return result;
+}
+
+void
+zlog_set_facility (struct zlog *zl, int facility)
+{
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->facility = facility;
+ }
+
+ VTY_UNLOCK() ;
+}
+
+int
+zlog_get_record_priority (struct zlog *zl)
+{
+ int result = 0;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->record_priority;
+ }
+
+ VTY_UNLOCK() ;
+ return result;
+}
+
+void
+zlog_set_record_priority (struct zlog *zl, int record_priority)
+{
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->record_priority = record_priority;
+ }
+ VTY_UNLOCK() ;
+}
+
+int
+zlog_get_timestamp_precision (struct zlog *zl)
+{
+ int result = 0;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->timestamp_precision;
+ }
+ VTY_UNLOCK() ;
+ return result;
+}
+
+void
+zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision)
+{
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->timestamp_precision = timestamp_precision;
+ }
+
+ VTY_UNLOCK() ;
}
-
+
+/* returns name of ZLOG_NONE if no zlog given and no default set */
+const char *
+zlog_get_proto_name (struct zlog *zl)
+{
+ const char * result;
+ VTY_LOCK() ;
+ result = uzlog_get_proto_name(zl);
+ VTY_UNLOCK() ;
+ return result;
+}
+
+/* unprotected version, assumes mutex held */
+const char *
+uzlog_get_proto_name (struct zlog *zl)
+{
+ zlog_proto_t protocol = ZLOG_NONE;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ protocol = zl->protocol;
+ }
+
+ return zlog_proto_names[protocol];
+}
+
+/* caller must free result */
+char *
+zlog_get_filename (struct zlog *zl)
+{
+ char * result = NULL;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL && zl->filename != NULL)
+ {
+ result = strdup(zl->filename);
+ }
+
+ VTY_UNLOCK() ;
+ return result;
+}
+
+const char *
+zlog_get_ident (struct zlog *zl)
+{
+ const char * result = NULL;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->ident;
+ }
+
+ VTY_UNLOCK() ;
+ return result;
+}
+
+/* logging to a file? */
+int
+zlog_is_file (struct zlog *zl)
+{
+ int result = 0;
+
+ VTY_LOCK() ;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = (zl->fp != NULL);
+ }
+
+ VTY_UNLOCK() ;;
+ return result;
+}
+
/* Message lookup function. */
const char *
lookup (const struct message *mes, int key)
{
const struct message *pnt;
- for (pnt = mes; pnt->key != 0; pnt++)
- if (pnt->key == key)
+ for (pnt = mes; pnt->key != 0; pnt++)
+ if (pnt->key == key)
return pnt->str;
return "";
}
/* Older/faster version of message lookup function, but requires caller to pass
- * in the array size (instead of relying on a 0 key to terminate the search).
+ * in the array size (instead of relying on a 0 key to terminate the search).
*
* The return value is the message string if found, or the 'none' pointer
* provided otherwise.
@@ -779,7 +1257,7 @@ const char *
mes_lookup (const struct message *meslist, int max, int index, const char *none)
{
int pos = index - meslist[0].key;
-
+
/* first check for best case: index is in range and matches the key
* value in that slot.
* NB: key numbering might be offset from 0. E.g. protocol constants
@@ -798,7 +1276,7 @@ mes_lookup (const struct message *meslist, int max, int index, const char *none)
if (meslist->key == index)
{
const char *str = (meslist->str ? meslist->str : none);
-
+
zlog_debug ("message index %d [%s] found in position %d (max is %d)",
index, str, i, max);
return str;
@@ -810,14 +1288,6 @@ mes_lookup (const struct message *meslist, int max, int index, const char *none)
return none;
}
-/* Wrapper around strerror to handle case where it returns NULL. */
-const char *
-safe_strerror(int errnum)
-{
- const char *s = strerror(errnum);
- return (s != NULL) ? s : "Unknown error";
-}
-
struct zebra_desc_table
{
unsigned int type;
diff --git a/lib/log.h b/lib/log.h
index 2dd1d313..dda773ea 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -19,13 +19,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_LOG_H
#define _ZEBRA_LOG_H
#include <syslog.h>
+#include "pthread_safe.h"
/* Here is some guidance on logging levels to use:
*
@@ -43,7 +44,7 @@
* please use LOG_ERR instead.
*/
-typedef enum
+typedef enum
{
ZLOG_NONE,
ZLOG_DEFAULT,
@@ -51,7 +52,7 @@ typedef enum
ZLOG_RIP,
ZLOG_BGP,
ZLOG_OSPF,
- ZLOG_RIPNG,
+ ZLOG_RIPNG,
ZLOG_OSPF6,
ZLOG_ISIS,
ZLOG_MASC
@@ -70,7 +71,7 @@ typedef enum
} zlog_dest_t;
#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
-struct zlog
+struct zlog
{
const char *ident; /* daemon name (first arg to openlog) */
zlog_proto_t protocol;
@@ -93,7 +94,11 @@ struct message
const char *str;
};
-/* Default logging strucutre. */
+/* module initialization */
+extern void zlog_init_r(void);
+extern void zlog_destroy_r(void);
+
+/* Default logging structure. */
extern struct zlog *zlog_default;
/* Open zlog function */
@@ -113,6 +118,9 @@ extern void closezlog (struct zlog *zl);
/* Generic function for zlog. */
extern void zlog (struct zlog *zl, int priority, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
+/* assumed locked version for close friends */
+extern void uzlog (struct zlog *zl, int priority, const char *format, ...)
+ PRINTF_ATTRIBUTE(3, 4);
/* Handy zlog functions. */
extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
@@ -147,11 +155,28 @@ extern int zlog_reset_file (struct zlog *zl);
/* Rotate log. */
extern int zlog_rotate (struct zlog *);
+/* getters & setters */
+extern int zlog_get_default_lvl (struct zlog *zl);
+extern void zlog_set_default_lvl (struct zlog *zl, int level);
+extern void zlog_set_default_lvl_dest (struct zlog *zl, int level);
+extern int zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest);
+extern int zlog_get_facility (struct zlog *zl);
+extern void zlog_set_facility (struct zlog *zl, int facility);
+extern int zlog_get_record_priority (struct zlog *zl);
+extern void zlog_set_record_priority (struct zlog *zl, int record_priority);
+extern int zlog_get_timestamp_precision (struct zlog *zl);
+extern void zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision);
+extern const char * zlog_get_ident (struct zlog *zl);
+extern char * zlog_get_filename (struct zlog *zl);
+extern int zlog_is_file (struct zlog *zl);
+extern const char * zlog_get_proto_name (struct zlog *zl);
+extern const char * uzlog_get_proto_name (struct zlog *zl);
+
/* For hackey massage lookup and check */
#define LOOKUP(x, y) mes_lookup(x, x ## _max, y, "(no item found)")
extern const char *lookup (const struct message *, int);
-extern const char *mes_lookup (const struct message *meslist,
+extern const char *mes_lookup (const struct message *meslist,
int max, int index,
const char *no_item);
@@ -168,6 +193,9 @@ extern void zlog_signal(int signo, const char *action
#endif
);
+/* Ring down the curtain -- turn of SIGABRT handler and abort() */
+extern void zabort_abort(void) __attribute__ ((noreturn)) ;
+
/* Log a backtrace. */
extern void zlog_backtrace(int priority);
@@ -178,25 +206,66 @@ extern void zlog_backtrace(int priority);
extern void zlog_backtrace_sigsafe(int priority, void *program_counter);
/* Puts a current timestamp in buf and returns the number of characters
- written (not including the terminating NUL). The purpose of
- this function is to avoid calls to localtime appearing all over the code.
- It caches the most recent localtime result and can therefore
- avoid multiple calls within the same second. If buflen is too small,
- *buf will be set to '\0', and 0 will be returned. */
+ * written (not including the terminating NUL). The purpose of
+ * this function is to avoid calls to localtime appearing all over the code.
+ * It caches the most recent localtime result and can therefore
+ * avoid multiple calls within the same second.
+ *
+ * The buflen MUST be > 1 and the buffer address MUST NOT be NULL.
+ *
+ * If buflen is too small, writes buflen-1 characters followed by '\0'.
+ *
+ * Time stamp is rendered in the form: %Y/%m/%d %H:%M:%S
+ *
+ * This has a fixed length (leading zeros are included) of 19 characters
+ * (unless this code is still in use beyond the year 9999 !)
+ *
+ * Which may be followed by "." and a number of decimal digits, usually 1..6.
+ *
+ * So the maximum time stamp is 19 + 1 + 6 = 26. Adding the trailing '\n', and
+ * rounding up for good measure -- buffer size = 32.
+ */
+#define TIMESTAMP_FORM "%Y/%m/%d %H:%M:%S"
+
+enum { timestamp_buffer_len = 32 } ;
+
extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
char *buf, size_t buflen);
-/* structure useful for avoiding repeated rendering of the same timestamp */
-struct timestamp_control {
- size_t len; /* length of rendered timestamp */
- int precision; /* configuration parameter */
- int already_rendered; /* should be initialized to 0 */
- char buf[40]; /* will contain the rendered timestamp */
-};
+/* Generate line to be logged
+ *
+ * Structure used to hold line for log output -- so that need be generated
+ * just once even if output to multiple destinations.
+ *
+ * Note that the buffer length is a hard limit (including terminating '\n''\0'
+ * or '\r''\n''\0'). Do not wish to malloc any larger buffer while logging.
+ */
+enum { logline_buffer_len = 1008 } ;
+enum ll_term
+{
+ llt_nul = 0, /* NB: also length of the terminator */
+ llt_lf = 1,
+ llt_crlf = 2,
+} ;
+
+struct logline {
+ char* p_nl ; /* address of the terminator */
+
+ enum ll_term term ; /* how line is terminated */
+
+ size_t len ; /* length including either '\r''\n' or '\n' */
+
+ char line[logline_buffer_len]; /* buffer */
+} ;
+
+extern void
+uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va, enum ll_term term) ;
/* Defines for use in command construction: */
-#define LOG_LEVELS "(emergencies|alerts|critical|errors|warnings|notifications|informational|debugging)"
+#define LOG_LEVELS "(emergencies|alerts|critical|errors|" \
+ "warnings|notifications|informational|debugging)"
#define LOG_LEVEL_DESC \
"System is unusable\n" \
diff --git a/lib/mem_tracker.c b/lib/mem_tracker.c
new file mode 100644
index 00000000..b468ad2a
--- /dev/null
+++ b/lib/mem_tracker.c
@@ -0,0 +1,590 @@
+/* Memory Allocation Tracker
+ * Copyright (C) 2010 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 "vty.h"
+
+/*==============================================================================
+ * Memory Tracker
+ */
+typedef struct mem_descriptor* mem_descriptor ;
+struct mem_descriptor
+{
+ void* addr ;
+ const char* name ;
+
+ uint32_t next ; /* MS Type is encoded as MS 4 bits */
+ uint32_t size ; /* LS Type is encoded as MS 4 bits */
+} ;
+
+typedef uint32_t md_index ;
+
+enum
+{
+ md_next_bits = 28, /* up to 256M allocated objects */
+ md_next_mask = (1 << md_next_bits) - 1,
+
+ md_index_max = md_next_mask + 1,
+
+ md_size_bits = 28, /* up to 256M individual item */
+ md_size_mask = (1 << md_size_bits) - 1,
+
+ md_size_max = md_size_mask,
+
+ md_next_type_bits = 32 - md_next_bits,
+ md_next_type_mask = (1 << md_next_type_bits) - 1,
+ md_size_type_bits = 32 - md_size_bits,
+ md_size_type_mask = (1 << md_size_type_bits) - 1,
+
+ md_i_index_bits = 16,
+ md_i_index_count = 1 << md_i_index_bits,
+ md_i_index_mask = md_i_index_count - 1,
+
+ md_page_bits = md_next_bits - md_i_index_bits,
+ md_page_count = 1 << md_page_bits,
+ md_page_mask = md_page_count - 1,
+} ;
+
+CONFIRM(MTYPE_MAX < (1 << (md_next_type_bits + md_size_type_bits))) ;
+
+static struct mem_type_tracker
+{
+ struct mem_tracker mt[MTYPE_MAX] ;
+} mem_type_tracker ;
+
+static mem_descriptor mem_page_table[md_page_count] ;
+
+static mem_descriptor mem_free_descriptors ;
+static md_index mem_next_index ;
+
+static struct mem_tracker mem ;
+
+uint32_t mem_base_count ;
+
+md_index* mem_bases ;
+
+inline static void
+mem_md_set_type(mem_descriptor md, enum MTYPE mtype)
+{
+ uint32_t t_ms ;
+ uint32_t t_ls ;
+
+ t_ms = mtype >> md_size_type_bits ;
+ t_ls = mtype ;
+
+ t_ms = (t_ms & md_next_type_mask) << md_next_bits ;
+ t_ls = (t_ls & md_size_type_mask) << md_size_bits ;
+
+ md->next = (md->next & md_next_mask) | t_ms ;
+ md->size = (md->size & md_size_mask) | t_ls ;
+} ;
+
+inline static void
+mem_md_set_next(mem_descriptor md, md_index next)
+{
+ md->next = (md->next & ~md_next_mask) | (next & md_next_mask) ;
+} ;
+
+inline static void
+mem_md_set_size(mem_descriptor md, size_t size)
+{
+ md->size = (md->size & ~md_size_mask) | (size & md_size_mask) ;
+} ;
+
+inline static uint8_t
+mem_md_type(mem_descriptor md)
+{
+ return ( (md->next >> (md_next_bits - md_size_type_bits))
+ & (md_next_type_mask << md_size_type_bits) )
+ | ( (md->size >> md_size_bits) & md_size_type_mask ) ;
+} ;
+
+inline static md_index
+mem_md_next(mem_descriptor md)
+{
+ return md->next & md_next_mask ;
+} ;
+
+inline static size_t
+mem_md_size(mem_descriptor md)
+{
+ return md->size & md_size_mask ;
+} ;
+
+inline static mem_descriptor
+mem_md_ptr(md_index mdi)
+{
+ mem_descriptor page ;
+
+ if (mdi == 0)
+ return NULL ;
+
+ page = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask] ;
+ passert(page != NULL) ;
+ return page + (mdi & md_i_index_mask) ;
+} ;
+
+static void mem_md_make_bases(void) ;
+
+inline static md_index*
+mem_md_base(void* address)
+{
+ if (mem_bases == NULL)
+ mem_md_make_bases() ;
+
+ return mem_bases + ((uintptr_t)address % mem_base_count) ;
+} ;
+
+static void
+mem_md_make_bases(void)
+{
+ md_index* bases_was = mem_bases ;
+ uint32_t count_was = mem_base_count ;
+
+ mem_base_count += 256 * 1024 ;
+ mem_base_count |= 1 ;
+ mem_bases = calloc(mem_base_count, sizeof(md_index)) ;
+
+ passert(mem_bases != NULL) ;
+
+ if (bases_was == NULL)
+ passert(count_was == 0) ;
+ else
+ {
+ md_index* base = bases_was ;
+ md_index* new_base ;
+ md_index this ;
+ md_index next ;
+ mem_descriptor md ;
+
+ while (count_was)
+ {
+ next = *base++ ;
+ while (next != 0)
+ {
+ this = next ;
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ new_base = mem_md_base(md->addr) ;
+ mem_md_set_next(md, *new_base) ;
+ *new_base = this ;
+ } ;
+ --count_was ;
+ } ;
+
+ free(bases_was) ;
+ } ;
+} ;
+
+static void
+mem_md_make_descriptors(void)
+{
+ mem_descriptor md ;
+ md_index mdi ;
+
+ mdi = mem_next_index ;
+ passert(mdi < md_index_max) ;
+
+ mem_free_descriptors
+ = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask]
+ = calloc(md_i_index_count, sizeof(struct mem_descriptor)) ;
+
+ passert(mem_free_descriptors != NULL) ;
+
+ mem_next_index += md_i_index_count ;
+
+ if (mdi == 0)
+ {
+ ++mem_free_descriptors ; /* don't use index == 0 */
+ ++mdi ;
+ } ;
+
+ md = mem_free_descriptors ;
+ while (mdi < mem_next_index)
+ {
+ md->addr = md + 1 ; /* point at next entry */
+ md->next = mdi ; /* set to point at self */
+ ++md ;
+ ++mdi ;
+ } ;
+ (md-1)->addr = NULL ; /* set end of list */
+} ;
+
+inline static void
+mem_md_malloc(enum MTYPE mtype, void* address, size_t size, const char* name)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md ;
+ md_index mdi ;
+
+ passert(size <= md_size_max) ;
+
+ if (mem_free_descriptors == NULL)
+ mem_md_make_descriptors() ;
+
+ md = mem_free_descriptors ;
+ mem_free_descriptors = md->addr ;
+ mdi = md->next ;
+
+ if (mem.tracked_count >= (mem_base_count * 4))
+ mem_md_make_bases() ;
+
+ base = mem_md_base(address) ;
+
+ md->addr = address ;
+ md->name = name ;
+ md->size = size ;
+ md->next = *base ;
+ mem_md_set_type(md, mtype) ;
+
+ *base = mdi ;
+
+ ++mem.malloc_count ;
+ ++mem.tracked_count ;
+
+ mem.tracked_size += size ;
+
+ if (mem.tracked_max_count < mem.tracked_count)
+ mem.tracked_max_count = mem.tracked_count ;
+
+ if (mem.tracked_max_size < mem.tracked_size)
+ mem.tracked_max_size = mem.tracked_size ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->malloc_count) ;
+ ++(mtt->tracked_count) ;
+ mtt->tracked_size += size ;
+
+ if (mtt->tracked_max_count < mtt->tracked_count)
+ mtt->tracked_max_count = mtt->tracked_count ;
+
+ if (mtt->tracked_max_size < mtt->tracked_size)
+ mtt->tracked_max_size = mtt->tracked_size ;
+} ;
+
+inline static void
+mem_md_free(enum MTYPE mtype, void* address)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md, prev_md ;
+ md_index this, next ;
+
+ if (address == NULL)
+ return ;
+
+ base = mem_md_base(address) ;
+
+ prev_md = NULL ;
+ this = *base ;
+ while (this != 0)
+ {
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ if (md->addr == address)
+ {
+ if (mem_md_type(md) != mtype)
+ zabort("memory type mismatch in free") ;
+
+ ++mem.free_count ;
+ --mem.tracked_count ;
+
+ mem.tracked_size -= mem_md_size(md) ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->free_count) ;
+ --(mtt->tracked_count) ;
+ mtt->tracked_size -= mem_md_size(md) ;
+
+ if (prev_md == NULL)
+ *base = next ;
+ else
+ mem_md_set_next(prev_md, next) ;
+
+ md->addr = mem_free_descriptors ;
+ mem_free_descriptors = md ;
+ md->next = this ;
+
+ return ;
+ }
+ else
+ {
+ prev_md = md ;
+ this = next ;
+ } ;
+ } ;
+
+ zabort("Failed to find memory being freed") ;
+} ;
+
+inline static void
+mem_md_realloc(enum MTYPE mtype, void* old_address, void* new_address,
+ size_t size, const char* name)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md, prev_md ;
+ md_index this, next ;
+
+ if (old_address == NULL)
+ {
+ mem_md_malloc(mtype, new_address, size, name) ;
+ return ;
+ } ;
+
+ passert(size <= md_size_max) ;
+
+ base = mem_md_base(old_address) ;
+
+ prev_md = NULL ;
+ this = *base ;
+ while (this != 0)
+ {
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ if (md->addr == old_address)
+ {
+ if (mem_md_type(md) != mtype)
+ zabort("memory type mismatch in realloc") ;
+
+ ++mem.realloc_count ;
+
+ mem.tracked_size += size - mem_md_size(md) ;
+
+ if (mem.tracked_max_size < mem.tracked_size)
+ mem.tracked_max_size = mem.tracked_size ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->realloc_count) ;
+ mtt->tracked_size += size - mem_md_size(md) ;
+
+ if (mtt->tracked_max_size < mtt->tracked_size)
+ mtt->tracked_max_size = mtt->tracked_size ;
+
+ md->name = name ;
+ mem_md_set_size(md, size) ;
+
+ if (old_address == new_address)
+ return ;
+
+ if (prev_md == NULL)
+ *base = next ;
+ else
+ mem_md_set_next(prev_md, next) ;
+
+ base = mem_md_base(new_address) ;
+ mem_md_set_next(md, *base) ;
+ *base = this ;
+
+ md->addr = new_address ;
+
+ return ;
+ }
+ else
+ {
+ prev_md = md ;
+ this = next ;
+ } ;
+ } ;
+
+ zabort("Failed to find memory being realloced") ;
+} ;
+
+/*==============================================================================
+ * Memory Tracker Display
+ */
+
+static const char* scale_d_tags [] =
+{
+ [0] = " " ,
+ [1] = "k",
+ [2] = "m",
+ [3] = "g",
+} ;
+
+static const char* scale_b_tags [] =
+{
+ [0] = " " ,
+ [1] = "KiB",
+ [2] = "MiB",
+ [3] = "GiB",
+} ;
+
+static char*
+mem_show_commas(char* buff, size_t size, uint64_t val, const char* tag)
+{
+ char* p ;
+ const char* q ;
+ int n ;
+
+ passert(size > 10) ;
+
+ p = buff + size ;
+ *(--p) = '\0' ;
+
+ q = tag + strlen(tag) ;
+ while ((p > buff) && (q > tag))
+ *(--p) = *(--q) ;
+
+ n = 3 ;
+ while (p > buff)
+ {
+ *(--p) = '0' + (val % 10) ;
+ val /= 10 ;
+ if (val == 0)
+ break ;
+
+ if ((--n == 0) && (p > buff))
+ {
+ *(--p) = ',' ;
+ n = 3 ;
+ } ;
+ } ;
+
+ return p ;
+} ;
+
+static char*
+mem_show_count(char* buff, size_t size, uint64_t val, int scale)
+{
+ int i, r ;
+
+ i = 0 ;
+ if (scale)
+ {
+ r = 0 ;
+ while ((i < 3) && (val >= 10000))
+ {
+ r = (val % 1000) ;
+ val /= 1000 ;
+ ++i ;
+ } ;
+ if (r >= 500) {
+ val += 1 ;
+ if ((val == 10000) && (i < 3))
+ {
+ val /= 1000 ;
+ ++i ;
+ } ;
+ } ;
+ } ;
+
+ return mem_show_commas(buff, size, val, scale_d_tags[i]) ;
+} ;
+
+static char*
+mem_show_byte_count(char* buff, size_t size, uint64_t val, int scale)
+{
+ int i, r ;
+
+ i = 0 ;
+ if (scale)
+ {
+ r = 0 ;
+ while ((i < 3) && (val >= 10000))
+ {
+ r = (val % 1024) ;
+ val /= 1024 ;
+ ++i ;
+ } ;
+ if (r >= 512) {
+ val += 1 ;
+ if ((val == 10000) && (i < 3))
+ {
+ val /= 1024 ;
+ ++i ;
+ } ;
+ } ;
+ } ;
+
+ return mem_show_commas(buff, size, val, scale_b_tags[i]) ;
+} ;
+
+static int
+show_memory_tracker_summary(struct vty *vty)
+{
+ struct mem_tracker mt ;
+ enum { sbs = 100 } ;
+ char buf[sbs];
+ size_t overhead ;
+
+ LOCK ;
+ overhead = (sizeof(struct mem_descriptor) * mem_next_index)
+ + (sizeof(md_index) * mem_base_count)
+ + (sizeof(mem_descriptor) * md_page_count) ;
+
+ mt = mem ; /* copy the overall memory information */
+ UNLOCK ;
+
+ vty_out (vty, "Memory Tracker Statistics:%s", VTY_NEWLINE);
+ vty_out (vty, " Current memory allocated: %10s%s",
+ mem_show_byte_count(buf, sbs, mt.tracked_size, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Current allocated objects: %8s%s",
+ mem_show_count (buf, sbs, mt.tracked_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Maximum memory allocated: %10s%s",
+ mem_show_byte_count(buf, sbs, mt.tracked_max_size, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Maximum allocated objects: %8s%s",
+ mem_show_count (buf, sbs, mt.tracked_max_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " malloc/calloc call count: %8s%s",
+ mem_show_count (buf, sbs, mt.malloc_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " realloc_call_count: %8s%s",
+ mem_show_count (buf, sbs, mt.realloc_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " free call count: %8s%s",
+ mem_show_count (buf, sbs, mt.free_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Memory Tracker overhead: %10s%s",
+ mem_show_byte_count(buf, sbs, overhead, 1),
+ VTY_NEWLINE);
+ return 1;
+} ;
+
+static int
+show_memory_tracker_detail(struct vty *vty, struct mem_tracker* mt,
+ unsigned long alloc)
+{
+ enum { sbs = 100 } ;
+ char buf[sbs];
+
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_count, 1)) ;
+ vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_size, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_max_count, 1)) ;
+ vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_max_size, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->malloc_count, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->realloc_count, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->free_count, 1)) ;
+
+ if (alloc != mt->tracked_count)
+ vty_out(vty, " %8s!!", mem_show_count(buf, sbs, alloc, 1)) ;
+
+ return 1;
+} ;
diff --git a/lib/memory.c b/lib/memory.c
index 4090fd90..49b20c14 100644
--- a/lib/memory.c
+++ b/lib/memory.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 <zebra.h>
@@ -28,131 +28,260 @@
#include "log.h"
#include "memory.h"
+#include "qpthreads.h"
+
+/* Needs to be qpthread safe. The system malloc etc are already
+ * thread safe, but we need to protect the stats
+ */
+static qpt_mutex_t memory_mutex;
+
+#define LOCK qpt_mutex_lock(&memory_mutex);
+#define UNLOCK qpt_mutex_unlock(&memory_mutex);
-static void alloc_inc (int);
-static void alloc_dec (int);
static void log_memstats(int log_priority);
-
+
static const struct message mstr [] =
{
{ MTYPE_THREAD, "thread" },
{ MTYPE_THREAD_MASTER, "thread_master" },
{ MTYPE_VECTOR, "vector" },
- { MTYPE_VECTOR_INDEX, "vector_index" },
+ { MTYPE_VECTOR_BODY, "vector_index" },
{ MTYPE_IF, "interface" },
{ 0, NULL },
};
-
+
+/* If using the mem_tracker, include it now. */
+
+typedef struct mem_tracker* mem_tracker ;
+struct mem_tracker
+{
+ uint64_t malloc_count ;
+ uint64_t realloc_count ;
+ uint64_t free_count ;
+
+ uint32_t tracked_count ;
+ size_t tracked_size ;
+
+ uint32_t tracked_max_count ;
+ size_t tracked_max_size ;
+} ;
+
+static void
+mem_tracker_zeroise(struct mem_tracker* mem)
+{
+ memset(mem, 0, sizeof(struct mem_tracker)) ;
+} ;
+
+#ifdef MEMORY_TRACKER
+#include "mem_tracker.c"
+#endif
+
+/*==============================================================================
+ * Keeping track of number of allocated objects of given type
+ */
+
+static struct mstat
+{
+ struct
+ {
+ char *name ;
+ long alloc ;
+ } mt[MTYPE_MAX] ;
+} mstat ;
+
+/*==============================================================================
+ * Memory allocation functions.
+ *
+ * NB: failure to allocate is FATAL -- so no need to test return value.
+ */
+
/* Fatal memory allocation error occured. */
static void __attribute__ ((noreturn))
zerror (const char *fname, int type, size_t size)
{
- zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n",
- fname, lookup (mstr, type), (int) size, safe_strerror(errno));
+ zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n",
+ fname, lookup (mstr, type), (int) size, errtoa(errno, 0).str);
log_memstats(LOG_WARNING);
/* N.B. It might be preferable to call zlog_backtrace_sigsafe here, since
that function should definitely be safe in an OOM condition. But
unfortunately zlog_backtrace_sigsafe does not support syslog logging at
this time... */
zlog_backtrace(LOG_WARNING);
- abort();
+ zabort_abort();
}
-/*
+/*------------------------------------------------------------------------------
+ * Memory allocation.
+ *
* Allocate memory of a given size, to be tracked by a given type.
- * Effects: Returns a pointer to usable memory. If memory cannot
- * be allocated, aborts execution.
+ *
+ * Returns: pointer to usable memory.
+ *
+ * NB: If memory cannot be allocated, aborts execution.
*/
void *
-zmalloc (int type, size_t size)
+zmalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
{
void *memory;
+ LOCK ;
+
memory = malloc (size);
if (memory == NULL)
- zerror ("malloc", type, size);
-
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("malloc", mtype, size); /* NO RETURN ! */
+ }
+ else
+ {
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_malloc(mtype, memory, size, name) ;
+#endif
+ UNLOCK ;
+ } ;
return memory;
}
-/*
- * Allocate memory as in zmalloc, and also clear the memory.
+/*------------------------------------------------------------------------------
+ * Memory allocation zeroising the allocated area.
+ *
+ * As zmalloc, plus zeroises the allocated memory.
*/
void *
-zcalloc (int type, size_t size)
+zcalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
{
void *memory;
+ LOCK ;
+
memory = calloc (1, size);
if (memory == NULL)
- zerror ("calloc", type, size);
-
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("calloc", mtype, size); /* NO RETURN ! */
+ }
+ else
+ {
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_malloc(mtype, memory, size, name) ;
+#endif
+ UNLOCK ;
+ } ;
return memory;
}
-/*
- * Given a pointer returned by zmalloc or zcalloc, free it and
- * return a pointer to a new size, basically acting like realloc().
- * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the
- * same type.
- * Effects: Returns a pointer to the new memory, or aborts.
+/*------------------------------------------------------------------------------
+ * Memory reallocation.
+ *
+ * Given a pointer returned by zmalloc()/zcalloc()/zrealloc(), extend or
+ * contract the allocation to the given size -- retaining current type.
+ * The type given MUST be the original type.
+ *
+ * Given a NULL, allocate memory as zmalloc().
+ *
+ * Returns: pointer to usable memory.
+ *
+ * NB: If memory cannot be allocated, aborts execution.
*/
void *
-zrealloc (int type, void *ptr, size_t size)
+zrealloc (enum MTYPE mtype, void *ptr, size_t size MEMORY_TRACKER_NAME)
{
void *memory;
+ LOCK ;
+
memory = realloc (ptr, size);
if (memory == NULL)
- zerror ("realloc", type, size);
- if (ptr == NULL)
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("realloc", mtype, size); /* NO RETURN ! */
+ }
+ else
+ {
+ if (ptr == NULL)
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_realloc(mtype, ptr, memory, size, name) ;
+#endif
+ UNLOCK ;
+ } ;
return memory;
-}
+} ;
-/*
- * Free memory allocated by z*alloc or zstrdup.
- * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the
- * same type.
- * Effects: The memory is freed and may no longer be referenced.
+/*------------------------------------------------------------------------------
+ * Memory free.
+ *
+ * Free memory allocated by zmalloc()/zcalloc()/zrealloc()/zstrdup().
+ * The type given MUST be the original type.
+ *
+ * Does nothing if the given pointer is NULL.
*/
void
-zfree (int type, void *ptr)
+zfree (enum MTYPE mtype, void *ptr)
{
if (ptr != NULL)
{
- alloc_dec (type);
+ LOCK ;
+
+ assert(mstat.mt[mtype].alloc > 0) ;
+
+ mstat.mt[mtype].alloc--;
+#ifdef MEMORY_TRACKER
+ mem_md_free(mtype, ptr) ;
+#endif
+
free (ptr);
- }
-}
-/*
- * Duplicate a string, counting memory usage by type.
- * Effects: The string is duplicated, and the return value must
- * eventually be passed to zfree with the same type. The function will
- * succeed or abort.
+ UNLOCK ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * String duplication.
+ *
+ * Memory is allocated as zmalloc() and must later be freed by zfree().
+ *
+ * NB: If memory cannot be allocated, aborts execution.
*/
char *
-zstrdup (int type, const char *str)
+zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
{
void *dup;
+ LOCK ;
+
dup = strdup (str);
if (dup == NULL)
- zerror ("strdup", type, strlen (str));
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("strdup", mtype, strlen (str)); /* NO RETURN ! */
+ }
+ else
+ {
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_malloc(mtype, dup, strlen(str)+1, name) ;
+#endif
+ UNLOCK ;
+ } ;
+
return dup;
}
-
+
+/*==============================================================================
+ * Memory allocation with built in logging
+ */
+
#ifdef MEMORY_LOG
-static struct
+
+static struct
{
const char *name;
long alloc;
@@ -163,10 +292,11 @@ static struct
unsigned long t_realloc;
unsigned long t_free;
unsigned long c_strdup;
-} mstat [MTYPE_MAX];
+} mlog_stat [MTYPE_MAX];
static void
-mtype_log (char *func, void *memory, const char *file, int line, int type)
+mtype_log (char *func, void *memory, const char *file, int line,
+ enum MTYPE type)
{
zlog_debug ("%s: %s %p %s %d", func, lookup (mstr, type), memory, file, line);
}
@@ -176,8 +306,10 @@ mtype_zmalloc (const char *file, int line, int type, size_t size)
{
void *memory;
- mstat[type].c_malloc++;
- mstat[type].t_malloc++;
+ LOCK
+ mlog_stat[type].c_malloc++;
+ mlog_stat[type].t_malloc++;
+ UNLOCK
memory = zmalloc (type, size);
mtype_log ("zmalloc", memory, file, line, type);
@@ -186,12 +318,14 @@ mtype_zmalloc (const char *file, int line, int type, size_t size)
}
void *
-mtype_zcalloc (const char *file, int line, int type, size_t size)
+mtype_zcalloc (const char *file, int line, enum MTYPE type, size_t size)
{
void *memory;
- mstat[type].c_calloc++;
- mstat[type].t_calloc++;
+ LOCK
+ mlog_stat[type].c_calloc++;
+ mlog_stat[type].t_calloc++;
+ UNLOCK
memory = zcalloc (type, size);
mtype_log ("xcalloc", memory, file, line, type);
@@ -200,12 +334,15 @@ mtype_zcalloc (const char *file, int line, int type, size_t size)
}
void *
-mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
+mtype_zrealloc (const char *file, int line, enum MTYPE type, void *ptr,
+ size_t size)
{
void *memory;
/* Realloc need before allocated pointer. */
- mstat[type].t_realloc++;
+ LOCK
+ mlog_stat[type].t_realloc++;
+ UNLOCK
memory = zrealloc (type, ptr, size);
@@ -215,10 +352,12 @@ mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
}
/* Important function. */
-void
-mtype_zfree (const char *file, int line, int type, void *ptr)
+void
+mtype_zfree (const char *file, int line, enum MTYPE type, void *ptr)
{
- mstat[type].t_free++;
+ LOCK
+ mlog_stat[type].t_free++;
+ UNLOCK
mtype_log ("xfree", ptr, file, line, type);
@@ -226,40 +365,26 @@ mtype_zfree (const char *file, int line, int type, void *ptr)
}
char *
-mtype_zstrdup (const char *file, int line, int type, const char *str)
+mtype_zstrdup (const char *file, int line, enum MTYPE type, const char *str)
{
char *memory;
- mstat[type].c_strdup++;
+ LOCK
+ mlog_stat[type].c_strdup++;
+ UNLOCK
memory = zstrdup (type, str);
-
+
mtype_log ("xstrdup", memory, file, line, type);
return memory;
}
-#else
-static struct
-{
- char *name;
- long alloc;
-} mstat [MTYPE_MAX];
-#endif /* MEMORY_LOG */
+#endif
-/* Increment allocation counter. */
-static void
-alloc_inc (int type)
-{
- mstat[type].alloc++;
-}
+/*==============================================================================
+ * Showing memory allocation
+ */
-/* Decrement allocation counter. */
-static void
-alloc_dec (int type)
-{
- mstat[type].alloc--;
-}
-
/* Looking up memory status from vty interface. */
#include "vector.h"
#include "vty.h"
@@ -268,47 +393,62 @@ alloc_dec (int type)
static void
log_memstats(int pri)
{
+ struct mstat mst ;
struct mlist *ml;
+ LOCK ;
+ mst = mstat ;
+ UNLOCK ;
+
for (ml = mlists; ml->list; ml++)
{
struct memory_list *m;
zlog (NULL, pri, "Memory utilization in module %s:", ml->name);
for (m = ml->list; m->index >= 0; m++)
- if (m->index && mstat[m->index].alloc)
- zlog (NULL, pri, " %-30s: %10ld", m->format, mstat[m->index].alloc);
+ {
+ unsigned long alloc = mst.mt[m->index].alloc ;
+ if (m->index && alloc)
+ zlog (NULL, pri, " %-30s: %10ld", m->format, alloc);
+ }
}
}
void
log_memstats_stderr (const char *prefix)
{
+ struct mstat mst ;
struct mlist *ml;
struct memory_list *m;
int i;
int j = 0;
+ LOCK ;
+ mst = mstat ;
+ UNLOCK ;
+
for (ml = mlists; ml->list; ml++)
{
i = 0;
-
for (m = ml->list; m->index >= 0; m++)
- if (m->index && mstat[m->index].alloc)
- {
- if (!i)
+ {
+ unsigned long alloc = mst.mt[m->index].alloc ;
+ if (m->index && alloc)
+ {
+ if (!i)
+ fprintf (stderr,
+ "%s: memstats: Current memory utilization in module %s:\n",
+ prefix,
+ ml->name);
fprintf (stderr,
- "%s: memstats: Current memory utilization in module %s:\n",
- prefix,
- ml->name);
- fprintf (stderr,
- "%s: memstats: %-30s: %10ld%s\n",
- prefix,
- m->format,
- mstat[m->index].alloc,
- mstat[m->index].alloc < 0 ? " (REPORT THIS BUG!)" : "");
- i = j = 1;
- }
+ "%s: memstats: %-30s: %10ld%s\n",
+ prefix,
+ m->format,
+ alloc,
+ alloc < 0 ? " (REPORT THIS BUG!)" : "");
+ i = j = 1;
+ }
+ }
}
if (j)
@@ -323,33 +463,99 @@ log_memstats_stderr (const char *prefix)
}
static void
-show_separator(struct vty *vty)
+show_memory_type_vty (struct vty *vty, const char* name,
+ struct mem_tracker* mt, long int alloc, int sep)
{
- vty_out (vty, "-----------------------------\r\n");
-}
+ if (sep)
+ vty_out (vty, "-----------------------------%s", VTY_NEWLINE) ;
+
+ vty_out (vty, "%-30s:", name) ;
+#ifdef MEMORY_TRACKER
+ show_memory_tracker_detail(vty, mt, alloc) ;
+#else
+ vty_out (vty, " %10ld", alloc) ;
+#endif
+ vty_out (vty, "%s", VTY_NEWLINE);
+} ;
static int
-show_memory_vty (struct vty *vty, struct memory_list *list)
+show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
+ int needsep)
{
- struct memory_list *m;
- int needsep = 0;
+ int notempty = 0 ;
- for (m = list; m->index >= 0; m++)
- if (m->index == 0)
- {
- if (needsep)
- {
- show_separator (vty);
- needsep = 0;
- }
- }
- else if (mstat[m->index].alloc)
- {
- vty_out (vty, "%-30s: %10ld\r\n", m->format, mstat[m->index].alloc);
- needsep = 1;
- }
- return needsep;
-}
+ long int alloc ;
+
+ struct mstat mst ;
+ struct mem_tracker mem_tot ;
+ struct mem_tracker mem_one ;
+ struct mem_tracker* mt ;
+
+#ifdef MEMORY_TRACKER
+ struct mem_type_tracker mem_tt ;
+#endif
+
+ LOCK ;
+ mst = mstat ;
+#ifdef MEMORY_TRACKER
+ mem_tt = mem_type_tracker ;
+#endif
+ UNLOCK ;
+
+ mem_tracker_zeroise(&mem_tot) ;
+ mem_tracker_zeroise(&mem_one) ;
+
+ if ((m == NULL) && (ml != NULL))
+ m = (ml++)->list ;
+
+ while (m != NULL)
+ {
+ if (m->index <= 0)
+ {
+ needsep = notempty ;
+ if (m->index < 0)
+ {
+ if (ml == NULL)
+ m = NULL ;
+ else
+ m = (ml++)->list ;
+ }
+ else
+ ++m ;
+ }
+ else
+ {
+ alloc = mst.mt[m->index].alloc ;
+#ifdef MEMORY_TRACKER
+ mt = &(mem_tt.mt[m->index]) ;
+#else
+ mt = &mem_one ;
+ mt->tracked_count = alloc ;
+#endif
+
+ mem_tot.malloc_count += mt->malloc_count ;
+ mem_tot.free_count += mt->free_count ;
+ mem_tot.realloc_count += mt->realloc_count ;
+ mem_tot.tracked_count += mt->tracked_count ;
+ mem_tot.tracked_max_count += mt->tracked_max_count ;
+ mem_tot.tracked_size += mt->tracked_size ;
+ mem_tot.tracked_max_size += mt->tracked_max_size ;
+
+ if (alloc || mt->tracked_count)
+ {
+ show_memory_type_vty(vty, m->format, mt, alloc, needsep) ;
+ needsep = 0 ;
+ notempty = 1 ;
+ } ;
+
+ ++m ;
+ } ;
+ } ;
+
+ show_memory_type_vty(vty, "Total", &mem_tot, mem_tot.tracked_count, notempty);
+
+ return 1 ;
+} ;
#ifdef HAVE_MALLINFO
static int
@@ -357,7 +563,7 @@ show_memory_mallinfo (struct vty *vty)
{
struct mallinfo minfo = mallinfo();
char buf[MTYPE_MEMSTR_LEN];
-
+
vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE);
vty_out (vty, " Total heap allocated: %s%s",
mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena),
@@ -388,131 +594,173 @@ show_memory_mallinfo (struct vty *vty)
VTY_NEWLINE);
vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s",
VTY_NEWLINE);
+
return 1;
}
#endif /* HAVE_MALLINFO */
-DEFUN (show_memory_all,
+
+DEFUN_CALL (show_memory_summary,
+ show_memory_summary_cmd,
+ "show memory summary",
+ "Show running system information\n"
+ "Memory statistics\n"
+ "Summary memory statistics\n")
+{
+#ifdef MEMORY_TRACKER
+ show_memory_tracker_summary(vty) ;
+#else
+ long alloc = 0 ;
+ int mtype ;
+
+# ifdef HAVE_MALLINFO
+ show_memory_mallinfo (vty);
+# endif /* HAVE_MALLINFO */
+
+ LOCK ;
+ for (mtype = 1 ; mtype < MTYPE_MAX ; ++mtype)
+ alloc += mstat.mt[mtype].alloc ;
+ UNLOCK
+ vty_out(vty, "%ld items allocated%s", alloc, VTY_NEWLINE) ;
+
+#endif /* MEMORY_TRACKER */
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_CALL (show_memory_all,
show_memory_all_cmd,
"show memory all",
"Show running system information\n"
"Memory statistics\n"
"All memory statistics\n")
{
- struct mlist *ml;
int needsep = 0;
-
+
#ifdef HAVE_MALLINFO
- needsep = show_memory_mallinfo (vty);
+ needsep |= show_memory_mallinfo (vty);
#endif /* HAVE_MALLINFO */
-
- for (ml = mlists; ml->list; ml++)
- {
- if (needsep)
- show_separator (vty);
- needsep = show_memory_vty (vty, ml->list);
- }
+#ifdef MEMORY_TRACKER
+ needsep |= show_memory_tracker_summary(vty) ;
+#endif
+
+ show_memory_vty (vty, NULL, mlists, needsep);
return CMD_SUCCESS;
}
-ALIAS (show_memory_all,
+ALIAS_CALL (show_memory_all,
show_memory_cmd,
"show memory",
"Show running system information\n"
"Memory statistics\n")
-DEFUN (show_memory_lib,
+DEFUN_CALL (show_memory_lib,
show_memory_lib_cmd,
"show memory lib",
SHOW_STR
"Memory statistics\n"
"Library memory\n")
{
- show_memory_vty (vty, memory_list_lib);
+ show_memory_vty (vty, memory_list_lib, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_zebra,
+DEFUN_CALL (show_memory_zebra,
show_memory_zebra_cmd,
"show memory zebra",
SHOW_STR
"Memory statistics\n"
"Zebra memory\n")
{
- show_memory_vty (vty, memory_list_zebra);
+ show_memory_vty (vty, memory_list_zebra, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_rip,
+DEFUN_CALL (show_memory_rip,
show_memory_rip_cmd,
"show memory rip",
SHOW_STR
"Memory statistics\n"
"RIP memory\n")
{
- show_memory_vty (vty, memory_list_rip);
+ show_memory_vty (vty, memory_list_rip, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_ripng,
+DEFUN_CALL (show_memory_ripng,
show_memory_ripng_cmd,
"show memory ripng",
SHOW_STR
"Memory statistics\n"
"RIPng memory\n")
{
- show_memory_vty (vty, memory_list_ripng);
+ show_memory_vty (vty, memory_list_ripng, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_bgp,
+DEFUN_CALL (show_memory_bgp,
show_memory_bgp_cmd,
"show memory bgp",
SHOW_STR
"Memory statistics\n"
"BGP memory\n")
{
- show_memory_vty (vty, memory_list_bgp);
+ show_memory_vty (vty, memory_list_bgp, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_ospf,
+DEFUN_CALL (show_memory_ospf,
show_memory_ospf_cmd,
"show memory ospf",
SHOW_STR
"Memory statistics\n"
"OSPF memory\n")
{
- show_memory_vty (vty, memory_list_ospf);
+ show_memory_vty (vty, memory_list_ospf, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_ospf6,
+DEFUN_CALL (show_memory_ospf6,
show_memory_ospf6_cmd,
"show memory ospf6",
SHOW_STR
"Memory statistics\n"
"OSPF6 memory\n")
{
- show_memory_vty (vty, memory_list_ospf6);
+ show_memory_vty (vty, memory_list_ospf6, NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (show_memory_isis,
+DEFUN_CALL (show_memory_isis,
show_memory_isis_cmd,
"show memory isis",
SHOW_STR
"Memory statistics\n"
"ISIS memory\n")
{
- show_memory_vty (vty, memory_list_isis);
+ show_memory_vty (vty, memory_list_isis, NULL, 0);
return CMD_SUCCESS;
}
+/* Second state initialisation if qpthreaded */
+void
+memory_init_r (void)
+{
+ qpt_mutex_init(&memory_mutex, qpt_mutex_quagga);
+}
+
+/* Finished with module */
+void
+memory_finish (void)
+{
+ qpt_mutex_destroy(&memory_mutex, 0);
+}
+
void
memory_init (void)
{
+ install_element (RESTRICTED_NODE, &show_memory_summary_cmd);
install_element (RESTRICTED_NODE, &show_memory_cmd);
install_element (RESTRICTED_NODE, &show_memory_all_cmd);
install_element (RESTRICTED_NODE, &show_memory_lib_cmd);
@@ -523,6 +771,7 @@ memory_init (void)
install_element (RESTRICTED_NODE, &show_memory_ospf6_cmd);
install_element (RESTRICTED_NODE, &show_memory_isis_cmd);
+ install_element (VIEW_NODE, &show_memory_summary_cmd);
install_element (VIEW_NODE, &show_memory_cmd);
install_element (VIEW_NODE, &show_memory_all_cmd);
install_element (VIEW_NODE, &show_memory_lib_cmd);
@@ -533,6 +782,7 @@ memory_init (void)
install_element (VIEW_NODE, &show_memory_ospf6_cmd);
install_element (VIEW_NODE, &show_memory_isis_cmd);
+ install_element (ENABLE_NODE, &show_memory_summary_cmd);
install_element (ENABLE_NODE, &show_memory_cmd);
install_element (ENABLE_NODE, &show_memory_all_cmd);
install_element (ENABLE_NODE, &show_memory_lib_cmd);
@@ -544,7 +794,7 @@ memory_init (void)
install_element (ENABLE_NODE, &show_memory_ospf6_cmd);
install_element (ENABLE_NODE, &show_memory_isis_cmd);
}
-
+
/* Stats querying from users */
/* Return a pointer to a human friendly string describing
* the byte count passed in. E.g:
@@ -557,13 +807,13 @@ const char *
mtype_memstr (char *buf, size_t len, unsigned long bytes)
{
unsigned int t, g, m, k;
-
+
/* easy cases */
if (!bytes)
return "0 bytes";
if (bytes == 1)
return "1 byte";
-
+
if (sizeof (unsigned long) >= 8)
/* Hacked to make it not warn on ILP32 machines
* Shift will always be 40 at runtime. See below too */
@@ -573,11 +823,11 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
g = bytes >> 30;
m = bytes >> 20;
k = bytes >> 10;
-
+
if (t > 10)
{
/* The shift will always be 39 at runtime.
- * Just hacked to make it not warn on 'smaller' machines.
+ * Just hacked to make it not warn on 'smaller' machines.
* Static compiler analysis should mean no extra code
*/
if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0)))
@@ -604,12 +854,19 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
}
else
snprintf (buf, len, "%ld bytes", bytes);
-
+
return buf;
}
unsigned long
-mtype_stats_alloc (int type)
+mtype_stats_alloc (enum MTYPE type)
{
- return mstat[type].alloc;
+ unsigned long result;
+ LOCK
+ result = mstat.mt[type].alloc;
+ UNLOCK
+ return result;
}
+
+#undef UNLOCK
+#undef LOCK
diff --git a/lib/memory.h b/lib/memory.h
index a7eddce4..6c95d73a 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _ZEBRA_MEMORY_H
#define _ZEBRA_MEMORY_H
+#include <stddef.h>
+
/* For pretty printing of memory allocate information. */
struct memory_list
{
@@ -32,11 +34,11 @@ struct mlist {
struct memory_list *list;
const char *name;
};
-
-#include "lib/memtypes.h"
extern struct mlist mlists[];
+#include "lib/memtypes.h"
+
/* #define MEMORY_LOG */
#ifdef MEMORY_LOG
#define XMALLOC(mtype, size) \
@@ -53,40 +55,65 @@ extern struct mlist mlists[];
#define XSTRDUP(mtype, str) \
mtype_zstrdup (__FILE__, __LINE__, (mtype), (str))
#else
-#define XMALLOC(mtype, size) zmalloc ((mtype), (size))
-#define XCALLOC(mtype, size) zcalloc ((mtype), (size))
-#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size))
+
+#ifdef QDEBUG
+#define MEMORY_TRACKER 1
+#endif
+
+#ifdef MEMORY_TRACKER
+#define MEMORY_TRACKER_NAME , const char* name
+#define MEMORY_TRACKER_FUNC , __func__
+#else
+#define MEMORY_TRACKER_NAME
+#define MEMORY_TRACKER_FUNC
+#endif
+
+#define XMALLOC(mtype, size) zmalloc ((mtype), (size) \
+ MEMORY_TRACKER_FUNC)
+#define XCALLOC(mtype, size) zcalloc ((mtype), (size) \
+ MEMORY_TRACKER_FUNC)
+#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size) \
+ MEMORY_TRACKER_FUNC)
#define XFREE(mtype, ptr) do { \
zfree ((mtype), (ptr)); \
ptr = NULL; } \
while (0)
-#define XSTRDUP(mtype, str) zstrdup ((mtype), (str))
+#define XSTRDUP(mtype, str) zstrdup ((mtype), (str) \
+ MEMORY_TRACKER_FUNC)
+
#endif /* MEMORY_LOG */
+#define SIZE(t,n) (sizeof(t) * (n))
+
/* Prototypes of memory function. */
-extern void *zmalloc (int type, size_t size);
-extern void *zcalloc (int type, size_t size);
-extern void *zrealloc (int type, void *ptr, size_t size);
-extern void zfree (int type, void *ptr);
-extern char *zstrdup (int type, const char *str);
+extern void *zmalloc (enum MTYPE type, size_t size MEMORY_TRACKER_NAME);
+extern void *zcalloc (enum MTYPE type, size_t size MEMORY_TRACKER_NAME);
+extern void *zrealloc (enum MTYPE type, void *ptr, size_t size
+ MEMORY_TRACKER_NAME);
+extern void zfree (enum MTYPE type, void *ptr);
+extern char *zstrdup (enum MTYPE type, const char *str MEMORY_TRACKER_NAME);
-extern void *mtype_zmalloc (const char *file, int line, int type, size_t size);
+extern void *mtype_zmalloc (const char *file, int line, enum MTYPE type,
+ size_t size);
-extern void *mtype_zcalloc (const char *file, int line, int type, size_t size);
+extern void *mtype_zcalloc (const char *file, int line, enum MTYPE type,
+ size_t size);
-extern void *mtype_zrealloc (const char *file, int line, int type, void *ptr,
- size_t size);
+extern void *mtype_zrealloc (const char *file, int line, enum MTYPE type,
+ void *ptr, size_t size);
-extern void mtype_zfree (const char *file, int line, int type,
- void *ptr);
+extern void mtype_zfree (const char *file, int line, enum MTYPE type,
+ void *ptr);
-extern char *mtype_zstrdup (const char *file, int line, int type,
- const char *str);
+extern char *mtype_zstrdup (const char *file, int line, enum MTYPE type,
+ const char *str);
extern void memory_init (void);
+extern void memory_init_r (void);
+extern void memory_finish (void);
extern void log_memstats_stderr (const char *);
/* return number of allocations outstanding for the type */
-extern unsigned long mtype_stats_alloc (int);
+extern unsigned long mtype_stats_alloc (enum MTYPE);
/* Human friendly string for given byte count */
#define MTYPE_MEMSTR_LEN 20
diff --git a/lib/memtypes.awk b/lib/memtypes.awk
index 5429f6e8..a8004977 100644
--- a/lib/memtypes.awk
+++ b/lib/memtypes.awk
@@ -54,7 +54,7 @@ BEGIN {
}
END {
- printf("enum\n{\n MTYPE_TMP = 1,\n");
+ printf("enum MTYPE\n{\n MTYPE_TMP = 1,\n");
for (i = 0; i < tcount; i++) {
if (mtype[i] != "" && mtype[i] != "MTYPE_TMP")
printf (" %s,\n", mtype[i]);
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 59020671..98b53209 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -1,6 +1,6 @@
/*
* Memory type definitions. This file is parsed by memtypes.awk to extract
- * MTYPE_ and memory_list_.. information in order to autogenerate
+ * MTYPE_ and memory_list_.. information in order to autogenerate
* memtypes.h.
*
* The script is sensitive to the format (though not whitespace), see
@@ -15,18 +15,47 @@
struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
- { MTYPE_STRVEC, "String vector" },
- { MTYPE_VECTOR, "Vector" },
- { MTYPE_VECTOR_INDEX, "Vector index" },
+ { MTYPE_STRVEC, "String vector" },
+ { MTYPE_VECTOR, "Vector structure" },
+ { MTYPE_VECTOR_BODY, "Vector body" },
+ { MTYPE_SYMBOL_TABLE, "Symbol Table structure" },
+ { MTYPE_SYMBOL_BASES, "Symbol Table chain bases" },
+ { MTYPE_SYMBOL, "Symbol" },
+ { MTYPE_SYMBOL_REF, "Symbol Reference" },
+ { MTYPE_HEAP, "Heap structure" },
{ MTYPE_LINK_LIST, "Link List" },
{ MTYPE_LINK_NODE, "Link Node" },
{ MTYPE_THREAD, "Thread" },
{ MTYPE_THREAD_MASTER, "Thread master" },
{ MTYPE_THREAD_STATS, "Thread stats" },
- { MTYPE_THREAD_FUNCNAME, "Thread function name" },
+ { MTYPE_THREAD_FUNCNAME, "Thread function name" },
+ { MTYPE_QPT_THREAD_ATTR, "qpt thread attributes" },
+ { MTYPE_QPT_MUTEX, "qpt mutex" },
+ { MTYPE_QPT_COND, "qpt condition variable" },
+ { MTYPE_MQUEUE_QUEUE, "Mqueue queue structure" },
+ { MTYPE_MQUEUE_BLOCK, "Mqueue message block" },
+ { MTYPE_MQUEUE_BLOCK_ARGV, "Mqueue message block argv" },
+ { MTYPE_MQUEUE_THREAD_SIGNAL, "Mqueue thread signal" },
+ { MTYPE_QPS_SELECTION, "qpselect selection" },
+ { MTYPE_QPS_FILE, "qpselect file" },
+ { MTYPE_QTIMER_PILE, "qtimer pile structure" },
+ { MTYPE_QTIMER, "qtimer timer" },
+ { MTYPE_QPN_NEXUS, "qtn nexus" },
+ { MTYPE_TSD, "Thread specific data" },
{ MTYPE_VTY, "VTY" },
+ { MTYPE_CMD_PARSED, "Parsed command" },
+ { MTYPE_MARSHAL, "marshalled commands" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
- { MTYPE_VTY_HIST, "VTY history" },
+ { MTYPE_VTY_HIST, "VTY history" },
+ { MTYPE_VTY_NAME, "VTY name" },
+ { MTYPE_KEY_STREAM, "Keystroke Stream" },
+ { MTYPE_VIO_FIFO, "VTY IO FIFO" },
+ { MTYPE_VIO_FIFO_LUMP, "VTY IO FIFO Lump" },
+ { MTYPE_VIO_LC, "VTY IO Line Control" },
+ { MTYPE_QSTRING, "qstring structure" },
+ { MTYPE_QSTRING_BODY, "qstring body" },
+ { MTYPE_QIOVEC, "qiovec structure" },
+ { MTYPE_QIOVEC_VEC, "qiovec iovec vector" },
{ MTYPE_IF, "Interface" },
{ MTYPE_CONNECTED, "Connected" },
{ MTYPE_CONNECTED_LABEL, "Connected interface label" },
@@ -75,7 +104,7 @@ struct memory_list memory_list_lib[] =
{ -1, NULL },
};
-struct memory_list memory_list_zebra[] =
+struct memory_list memory_list_zebra[] =
{
{ MTYPE_RTADV_PREFIX, "Router Advertisement Prefix" },
{ MTYPE_VRF, "VRF" },
@@ -97,6 +126,12 @@ struct memory_list memory_list_bgp[] =
{ MTYPE_PEER_GROUP, "Peer group" },
{ MTYPE_PEER_DESC, "Peer description" },
{ MTYPE_PEER_PASSWORD, "Peer password string" },
+ { MTYPE_BGP_PEER_ID_TABLE, "Peer ID table" },
+ { MTYPE_BGP_SESSION, "BGP session" },
+ { MTYPE_BGP_CONNECTION, "BGP connection" },
+ { MTYPE_BGP_NOTIFY, "BGP notification" },
+ { MTYPE_BGP_ROUTE_REFRESH, "BGP route refresh" },
+ { MTYPE_BGP_ORF_ENTRY, "BGP ORF entry" },
{ MTYPE_ATTR, "BGP attribute" },
{ MTYPE_ATTR_EXTRA, "BGP extra attributes" },
{ MTYPE_AS_PATH, "BGP aspath" },
@@ -108,7 +143,7 @@ struct memory_list memory_list_bgp[] =
{ MTYPE_BGP_NODE, "BGP node" },
{ MTYPE_BGP_ROUTE, "BGP route" },
{ MTYPE_BGP_ROUTE_EXTRA, "BGP ancillary route info" },
- { MTYPE_BGP_CONN, "BGP connected" },
+ { MTYPE_BGP_CONN, "BGP connected" },
{ MTYPE_BGP_STATIC, "BGP static" },
{ MTYPE_BGP_ADVERTISE_ATTR, "BGP adv attr" },
{ MTYPE_BGP_ADVERTISE, "BGP adv" },
@@ -151,6 +186,7 @@ struct memory_list memory_list_bgp[] =
{ MTYPE_BGP_DAMP_ARRAY, "BGP Dampening array" },
{ MTYPE_BGP_REGEXP, "BGP regexp" },
{ MTYPE_BGP_AGGREGATE, "BGP aggregate" },
+ { MTYPE_BGP_OPEN_STATE, "BGP Open State" },
{ -1, NULL }
};
diff --git a/lib/miyagi.h b/lib/miyagi.h
new file mode 100644
index 00000000..569d2da9
--- /dev/null
+++ b/lib/miyagi.h
@@ -0,0 +1,40 @@
+/* Kludge to discard "const" from pointer
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *.
+ * This file is part of GNU Zebra.
+ *
+ * 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 _ZEBRA_MIYAGI_H
+#define _ZEBRA_MIYAGI_H
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Ghastly kludge to discard "const" from pointer
+ */
+Inline void*
+miyagi(const void* ptr)
+{
+ union {
+ const void* waxon ;
+ void* waxoff ;
+ } shuffle ;
+
+ shuffle.waxon = ptr ;
+
+ return shuffle.waxoff ;
+} ;
+
+#endif /* _ZEBRA_MIYAGI_H */
diff --git a/lib/mqueue.c b/lib/mqueue.c
new file mode 100644
index 00000000..8fa9fbd5
--- /dev/null
+++ b/lib/mqueue.c
@@ -0,0 +1,1319 @@
+/* Message Queue data structure -- 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 <string.h>
+
+#include "memory.h"
+#include "mqueue.h"
+#include "zassert.h"
+
+/*==============================================================================
+ * These message queues are designed for inter-qpthread communication.
+ *
+ * A message queue carries messages from one or more qpthreads to one or more
+ * other qpthreads.
+ *
+ * If !qpthreads_enabled, then a message queue holds messages for the program
+ * to consume later. There are never any waiters. Timeouts are ignored.
+ *
+ * A message queue has one ordinary priority queue and one high priority
+ * queue.
+ *
+ * There are four types of queue, depending on how qpthreads wait and how they
+ * are woken up:
+ *
+ * mqt_cond_unicast -- wait on condition variable, one waiter kicked
+ * mqt_cond_broadcast -- wait on condition variable, all waiters kicked
+ * mqt_signal_unicast -- wait for signal, one waiter kicked
+ * mqt_signal_broadcast -- wait for signal, all waiters kicked
+ *
+ * For condition variables there is a timeout mechanism so that waiters
+ * are woken up at least every now and then. The message queue maintains
+ * a timeout time and a timeout interval. The timeout time is a qtime_mono_t
+ * time -- so is monotonic.
+ *
+ * When waiting, an explicit timeout may be given, otherwise the stored timeout
+ * will be used:
+ *
+ * wait until explicit/stored timeout
+ * if times out and there is a stored interval:
+ * new stored timeout = stored timeout + stored interval
+ * if new stored timeout < time now
+ * new stored timeout = time now + stored interval
+ *
+ * Left to its own devices, this will produce a regular timeout every interval,
+ * assuming that the queue is waited on within the interval. Otherwise the
+ * "clock" will slip.
+ *
+ * There is a default timeout period. The period may be set "infinite".
+ *
+ * For waiters kicked by signal, the wait does not occur within the message
+ * queue code, but the need for a signal is recorded in the message queue.
+ *
+ *------------------------------------------------------------------------------
+ * Message Blocks and Arguments
+ *
+ * Messages take the form of a small block of information which contain:
+ *
+ * * action -- void action(mqueue_block) message dispatch
+ * * arg0 -- void* argument
+ * * struct args -- embedded argument structure
+ * * argv -- optional array of union of: *void/uintptr_t/intptr_t
+ *
+ * There are set/get functions for action/arguments -- users should not poke
+ * around inside the structure.
+ *
+ * To send a message, first initialise/allocate a message block
+ * (see mqb_init_new), then fill in the arguments and enqueue it.
+ *
+ * NB: arg0 is expected to be used as the "context" for the message -- to
+ * point to some data common to both ends of the conversation.
+ *
+ * For specific revoke, arg0 is assumed to identify the messages to be
+ * revoked.
+ *
+ * NB: the struct args is expected to be a modest sized structure, carrying
+ * the key elements of the message.
+ *
+ * Some other structure must be overlaid on this, in the same way by sender
+ * and receiver of the message. So:
+ *
+ * mqueue_block mqb = mqb_init_new(NULL, arg0, action_func) ;
+ *
+ * struct my_message* args = mqb_get_args(mqb) ;
+ *
+ * allocates mqueue block, filling in arg0 and the action func. Then
+ * args can be used to fill in a "struct my_message" form of args.
+ *
+ * NB: the sizeof(struct my_message) MUST BE <= mqb_args_size_max !!!
+ *
+ * The argv is an optional, flexible list/array of optional array of
+ * union of: *void/uintptr_t/intptr_t -- see mqb_arg_t et al.
+ *
+ * May set any number of arguments in argv.
+ *
+ * A count of arguments is maintained, and is the highest index set + 1. That
+ * count can be fetched. (So there is no need to maintain it separately.)
+ *
+ * May get any argument by its index -- but it is a fatal error to attempt to
+ * access a non-existent argument (one beyond the known count).
+ *
+ * There is support for pushing values onto the argv "list" and for iterating
+ * along the "list". May also push and pop entire arrays of items.
+ *
+ *==============================================================================
+ * Local Queues
+ *
+ * A local queue may be used within a thread to requeue messages for later
+ * processing.
+ *
+ * Local queues are simple FIFO queues.
+ */
+
+/*==============================================================================
+ * Message Block allocation statics
+ *
+ * Once a message block is allocated it is not deallocated, but kept ready
+ * for future use.
+ *
+ * Keeps a count of free message blocks. (Could at some later date reduce the
+ * number of free message blocks if it is known that some burst of messages has
+ * now passed.)
+ */
+
+static pthread_mutex_t mqb_mutex ; /* for allocation of mqueue blocks */
+
+static mqueue_block mqb_free_list = NULL ;
+static unsigned mqb_free_count = 0 ;
+
+/*==============================================================================
+ * Initialise and shut down Message Queue and Message Block handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise Message Queue handling.
+ *
+ * Must be called before any qpt_threads are started.
+ *
+ * Freezes qpthreads_enabled.
+ */
+extern void
+mqueue_initialise(void)
+{
+ if (qpthreads_enabled_freeze)
+ qpt_mutex_init_new(&mqb_mutex, qpt_mutex_quagga) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shut down Message Queue handling.
+ *
+ * Release all resources used.
+ *
+ * NB: all pthreads must have stopped -- mutex must be free and no further
+ * uses may be made.
+ */
+extern void
+mqueue_finish(void)
+{
+ mqueue_block mqb ;
+
+ while ((mqb = mqb_free_list) != NULL)
+ {
+ assert(mqb_free_count != 0) ;
+ mqb_free_count-- ;
+ mqb_free_list = mqb->next ;
+ XFREE(MTYPE_MQUEUE_BLOCK, mqb) ;
+ } ;
+
+ assert(mqb_free_count == 0) ;
+
+ qpt_mutex_destroy_keep(&mqb_mutex) ;
+} ;
+
+/*==============================================================================
+ * Initialisation etc. for Message Queue
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * 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.
+ *
+ * NB: once any message queue has been initialised, it is TOO LATE to enable
+ * qpthreads.
+ */
+extern mqueue_queue
+mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
+{
+ if (mq == NULL)
+ mq = XCALLOC(MTYPE_MQUEUE_QUEUE, sizeof(struct mqueue_queue)) ;
+ else
+ memset(mq, 0, sizeof(struct mqueue_queue)) ;
+
+ if (qpt_freeze_qpthreads_enabled())
+ qpt_mutex_init_new(&mq->mutex, qpt_mutex_quagga) ;
+
+ /* head, tail and tail_priority set NULL already */
+ /* count set zero already */
+ /* waiters set zero already */
+
+ mq->type = type ;
+ switch (type)
+ {
+ case mqt_cond_unicast:
+ case mqt_cond_broadcast:
+ qpt_cond_init_new(&mq->kick.cond.wait_here, qpt_cond_quagga) ;
+
+ if (MQUEUE_DEFAULT_INTERVAL != 0)
+ {
+ mq->kick.cond.interval = MQUEUE_DEFAULT_INTERVAL ;
+ mq->kick.cond.timeout = qt_get_monotonic()
+ + MQUEUE_DEFAULT_INTERVAL ;
+ } ;
+ break;
+
+ case mqt_signal_unicast:
+ case mqt_signal_broadcast:
+ /* head/tail pointers set NULL already */
+ break;
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+
+ return mq ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty message queue -- by revoking everything.
+ *
+ * Leaves queue ready for continued use with all existing settings.
+ *
+ * If there were any waiters, they are still waiting.
+ */
+extern void
+mqueue_empty(mqueue_queue mq)
+{
+ mqueue_revoke(mq, NULL) ;
+
+ assert((mq->head == NULL) && (mq->count == 0)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset message queue -- empty it out by revoking everything.
+ *
+ * Frees the structure if required, and returns NULL.
+ * Otherwise zeroises the structure, and returns address of same.
+ *
+ * NB: there MUST NOT be ANY waiters !
+ */
+extern mqueue_queue
+mqueue_reset(mqueue_queue mq, int free_structure)
+{
+ mqueue_empty(mq) ;
+
+ passert(mq->waiters == 0) ;
+
+ qpt_mutex_destroy_keep(&mq->mutex) ;
+
+ switch (mq->type)
+ {
+ case mqt_cond_unicast:
+ case mqt_cond_broadcast:
+ qpt_cond_destroy_keep(&mq->kick.cond.wait_here) ;
+ break;
+
+ case mqt_signal_unicast:
+ case mqt_signal_broadcast:
+ passert(mq->kick.signal.head == NULL) ;
+ break;
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+
+ if (free_structure)
+ XFREE(MTYPE_MQUEUE_QUEUE, mq) ; /* sets mq == NULL */
+ else
+ memset(mq, 0, sizeof(struct mqueue_queue)) ;
+
+ return mq ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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_destroy", 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 ;
+ mqb_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).
+ *
+ * This is a waste of time if !qpthreads_enabled, but does no harm. The
+ * timeout is ignored.
+ */
+extern void
+mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval)
+{
+ qpt_mutex_lock(&mq->mutex) ;
+
+ dassert( (mq->type == mqt_cond_unicast) ||
+ (mq->type == mqt_cond_broadcast) ) ;
+
+ mq->kick.cond.interval = interval ;
+ mq->kick.cond.timeout = (interval > 0) ? qt_add_monotonic(interval)
+ : 0 ;
+ qpt_mutex_unlock(&mq->mutex) ;
+} ;
+
+/*==============================================================================
+ * Message Block memory management.
+ *
+ * Allocates message block structures when required.
+ *
+ * Places those structures on the free list when they are freed.
+ *
+ * Keeps a count of free structures. (Could at some later date reduce the
+ * number of free structures if it is known that some burst of messages has
+ * now passed.)
+ *
+ * mqueue_initialise MUST be called before the first message block is allocated.
+ */
+
+inline static size_t mqb_argv_size(mqb_index_t alloc)
+{
+ return alloc * sizeof(mqb_arg_t) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise message block (allocate if required) and set action & arg0.
+ *
+ * Zeroises the struct args.
+ *
+ * Returns address of message block.
+ */
+extern mqueue_block
+mqb_init_new(mqueue_block mqb, mqueue_action action, void* arg0)
+{
+ if (mqb == NULL)
+ {
+ qpt_mutex_lock(&mqb_mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ mqb = mqb_free_list ;
+ if (mqb == NULL)
+ {
+ dassert(mqb_free_count == 0) ;
+ mqb = XMALLOC(MTYPE_MQUEUE_BLOCK, sizeof(struct mqueue_block)) ;
+ }
+ else
+ {
+ dassert(mqb_free_count >= 0) ;
+ mqb_free_list = mqb->next ;
+ --mqb_free_count ;
+ } ;
+
+ qpt_mutex_unlock(&mqb_mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ } ;
+
+ memset(mqb, 0, sizeof(struct mqueue_block)) ;
+
+ mqb->action = action ;
+ mqb->arg0 = arg0 ;
+
+ /* Zeroising the mqb sets:
+ *
+ * next -- NULL
+ *
+ * args -- zeroised
+ *
+ * argv -- NULL -- empty list/array
+ *
+ * argv_count -- 0 -- empty
+ * argv_alloc -- 0 -- nothing allocated
+ * argv_next -- 0 -- iterator reset
+ */
+
+ return mqb ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-initialise message block (or allocate if required) and set action & arg0.
+ *
+ * NB: preserves any existing argv, but empties it.
+ *
+ * NB: it is the caller's responsibility to free the value of any argument that
+ * requires it.
+ */
+extern mqueue_block
+mqb_re_init(mqueue_block mqb, mqueue_action action, void* arg0)
+{
+ mqb_index_t argv_alloc ;
+ mqb_arg_t* argv ;
+
+ /* Exactly mqb_init_new if mqb is NULL */
+ if (mqb == NULL)
+ return mqb_init_new(NULL, action, arg0) ;
+
+ /* Otherwise, need to put argv to one side first */
+ argv = mqb->argv ;
+ argv_alloc = mqb->argv_alloc ;
+
+ mqb_init_new(mqb, action, arg0) ;
+
+ /* Now zeroize the argv, and restore it */
+ memset(argv, 0, mqb_argv_size(argv_alloc)) ;
+
+ mqb->argv = argv ;
+ mqb->argv_alloc = argv_alloc ;
+
+ return mqb ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free message block when done with it.
+ *
+ * Frees any argv argument vector.
+ *
+ * NB: it is the caller's responsibility to free the value of any argument that
+ * requires it.
+ */
+extern void
+mqb_free(mqueue_block mqb)
+{
+ if (mqb->argv != NULL)
+ XFREE(MTYPE_MQUEUE_BLOCK_ARGV, mqb->argv) ;
+
+ qpt_mutex_lock(&mqb_mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ mqb->next = mqb_free_list ;
+ mqb_free_list = mqb ;
+ ++mqb_free_count ;
+
+ qpt_mutex_unlock(&mqb_mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+} ;
+
+/*==============================================================================
+ * Enqueue and dequeue messages.
+ */
+
+static void mqueue_kick_signal(mqueue_queue mq, unsigned n) ;
+static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
+
+/*------------------------------------------------------------------------------
+ * Enqueue message.
+ *
+ * If priority, will enqueue after any previously enqueued priority
+ * messages. (See enum: mqb_priority and mqb_ordinary.)
+ *
+ * If there are any waiters, then we kick one or all of them.
+ *
+ * Note that we decrement or zero the waiters count here -- because if the
+ * waiter did it, they might not run before something else is enqueued.
+ * Similarly, if the kick uses a signal, the signal block is dequeued here.
+ *
+ * The waiter count is only incremented when a dequeue is attempted and the
+ * queue is empty, then:
+ *
+ * for a broadcast type message queue, the first message that arrives will
+ * kick all the waiters into action.
+ *
+ * for a signal type message queue, each message that arrives will kick one
+ * waiter.
+ *
+ * If mq is NULL, the message is not queued but is immediately destroyed.
+ *
+ * NB: this works perfectly well if !qpthreads enabled. Of course, there can
+ * never be any waiters... so no kicking is ever done.
+ */
+extern void
+mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority)
+{
+ if (mq == NULL)
+ return mqb_dispatch_destroy(mqb) ;
+
+ qpt_mutex_lock(&mq->mutex) ;
+
+ if (mq->head == NULL)
+ {
+ assert(mq->count == 0) ;
+ mqb->next = NULL ;
+ mq->head = mqb ;
+ mq->tail_priority = priority ? mqb : NULL ;
+ mq->tail = mqb ;
+ }
+ else
+ {
+ assert(mq->count > 0) ;
+ if (priority)
+ {
+ mqueue_block after = mq->tail_priority ;
+ if (after == NULL)
+ {
+ mqb->next = mq->head ;
+ mq->head = mqb ;
+ /* mq non-empty, enchain at head, therefore tail unaffected */
+ }
+ else
+ {
+ mqb->next = after->next ;
+ after->next = mqb ;
+ /* if only have priority messages then fix tail */
+ if (mq->tail == after)
+ mq->tail = mqb;
+ }
+ mq->tail_priority = mqb ;
+ }
+ else
+ {
+ dassert(mq->tail != NULL) ;
+ mqb->next = NULL ;
+ mq->tail->next = mqb ;
+ mq->tail = mqb ;
+ } ;
+ } ;
+
+ ++mq->count ;
+
+ if (mq->waiters != 0)
+ {
+ dassert(qpthreads_enabled) ; /* waiters == 0 if !qpthreads_enabled */
+
+ switch (mq->type)
+ {
+ case mqt_cond_unicast:
+ qpt_cond_signal(&mq->kick.cond.wait_here) ;
+ --mq->waiters ;
+ break ;
+
+ case mqt_cond_broadcast:
+ qpt_cond_broadcast(&mq->kick.cond.wait_here) ;
+ mq->waiters = 0 ;
+ break ;
+
+ case mqt_signal_unicast:
+ mqueue_kick_signal(mq, 1) ; /* pick off first and kick it (MUST be */
+ /* one) and decrement the waiters count */
+ break ;
+
+ case mqt_signal_broadcast:
+ mqueue_kick_signal(mq, mq->waiters) ;
+ dassert(mq->kick.signal.head == NULL) ;
+ break;
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+ } ;
+
+ qpt_mutex_unlock(&mq->mutex) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dequeue message.
+ *
+ * If the queue is empty and wait != 0 (and qpthreads_enabled), will wait for a
+ * message. In which case for:
+ *
+ * * mqt_cond_xxxx type message queues, will wait on the condition variable,
+ * and may timeout.
+ *
+ * If the argument is NULL, uses the already set up timeout, if there is
+ * one.
+ *
+ * If the argument is not NULL, it is a pointer to a qtime_mono_t time,
+ * to be used as the new timeout time.
+ *
+ * * mqt_signal_xxxx type message queues, will register the given signal
+ * (mtsig argument MUST be provided), and return immediately.
+ *
+ * NB: if !qpthreads_enabled, will not wait on the queue. No how.
+ *
+ * Note this means that waiters == 0 all the time if !qpthreads_enabled !
+ *
+ * NB: the argument is ignored if !wait or !qpthreads_enabled, so may be NULL.
+ *
+ * Returns a message block if one is available. (And not otherwise.)
+ *
+ * NB: if mq is NULL, returns NULL -- nothing available
+ */
+extern mqueue_block
+mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
+{
+ mqueue_block mqb ;
+ mqueue_thread_signal last ;
+
+ mqueue_thread_signal mtsig ;
+ qtime_mono_t timeout_time ;
+
+ if (mq == NULL)
+ return NULL ;
+
+ qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ while (1)
+ {
+ mqb = mq->head ;
+ if (mqb != NULL)
+ break ; /* Easy if queue not empty */
+
+ assert(mq->count == 0) ;
+
+ if (!wait || !qpthreads_enabled)
+ goto done ; /* Easy if not waiting ! mqb == NULL */
+ /* Short circuit if !qpthreads_enabled */
+
+ ++mq->waiters ; /* Another waiter */
+
+ switch (mq->type)
+ {
+ case mqt_cond_unicast: /* Now wait here */
+ case mqt_cond_broadcast:
+ if ((arg == NULL) && (mq->kick.cond.interval <= 0))
+ qpt_cond_wait(&mq->kick.cond.wait_here, &mq->mutex) ;
+ else
+ {
+ timeout_time = (arg != NULL) ? *(qtime_mono_t*)arg
+ : mq->kick.cond.timeout ;
+
+ if (qpt_cond_timedwait(&mq->kick.cond.wait_here, &mq->mutex,
+ timeout_time) == 0)
+ {
+ /* Timed out -- update timeout time, if required */
+ if (mq->kick.cond.interval > 0)
+ {
+ qtime_mono_t now = qt_get_monotonic() ;
+ timeout_time = mq->kick.cond.timeout
+ + mq->kick.cond.interval ;
+ if (timeout_time < now)
+ timeout_time = now + mq->kick.cond.interval ;
+
+ mq->kick.cond.timeout = timeout_time ;
+ } ;
+
+ goto done ; /* immediate return. mqb == NULL */
+ } ;
+ } ;
+ break ;
+
+ case mqt_signal_unicast: /* Register desire for signal */
+ case mqt_signal_broadcast:
+ mtsig = arg ;
+ dassert(mtsig != NULL) ;
+
+ if (mq->kick.signal.head == NULL)
+ {
+ mq->kick.signal.head = mtsig ;
+ mtsig->prev = (void*)mq ;
+ }
+ else
+ {
+ last = mq->kick.signal.tail ;
+ last->next = mtsig ;
+ mtsig->prev = last ;
+ }
+ mtsig->next = NULL ;
+ mq->kick.signal.tail = mtsig ;
+
+ goto done ; /* BUT do not wait ! mqb == NULL */
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+ } ;
+
+ /* Have something to pull off the queue */
+
+ assert(mq->count > 0) ;
+ --mq->count ;
+
+ mq->head = mqb->next ;
+
+ /* fix tails if at tail */
+ if (mqb == mq->tail)
+ mq->tail = NULL ;
+ if (mqb == mq->tail_priority)
+ mq->tail_priority = NULL ;
+
+done:
+ qpt_mutex_unlock(&mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return mqb ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Revoke message(s)
+ *
+ * Revokes all messages, or only messages whose arg0 matches the given value.
+ * (If the given value is NULL revokes everything.)
+ *
+ * Revokes by calling mqb_dispatch_destroy().
+ *
+ * During a revoke() operation more items may be enqueued, but no other mqueue
+ * operations may be performed. Enqueued items may promptly be revoked, except
+ * for priority items if the revoke operation has already moved past the last
+ * priority item.
+ *
+ * If mq is NULL, does nothing.
+ */
+extern void
+mqueue_revoke(mqueue_queue mq, void* arg0)
+{
+ mqueue_block mqb ;
+ mqueue_block prev ;
+
+ if (mq == NULL)
+ return ;
+
+ qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ prev = NULL ;
+ while (1)
+ {
+ if (prev == NULL)
+ mqb = mq->head ;
+ else
+ mqb = prev->next ;
+
+ if (mqb == NULL)
+ break ;
+
+ if ((arg0 == NULL) || (arg0 == mqb->arg0))
+ {
+ assert(mq->count > 0) ;
+
+ if (prev == NULL)
+ mq->head = mqb->next ;
+ else
+ prev->next = mqb->next ;
+
+ if (mqb == mq->tail)
+ mq->tail = prev ;
+
+ if (mqb == mq->tail_priority)
+ mq->tail_priority = prev ;
+
+ --mq->count ;
+
+ qpt_mutex_unlock(&mq->mutex) ;
+ mqb_dispatch_destroy(mqb) ;
+ qpt_mutex_lock(&mq->mutex) ;
+ }
+ else
+ prev = mqb ;
+ } ;
+
+ qpt_mutex_unlock(&mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+} ;
+
+/*------------------------------------------------------------------------------
+ * No longer waiting for a signal -- does nothing if !qpthreads_enabled.
+ *
+ * Returns true <=> signal has been kicked
+ *
+ * (Signal will never be kicked if !qpthreads_enabled.)
+ */
+extern int
+mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig)
+{
+ int kicked ;
+
+ if (!qpthreads_enabled)
+ return 0 ;
+
+ qpt_mutex_lock(&mq->mutex) ;
+
+ dassert( (mq->type == mqt_signal_unicast) ||
+ (mq->type == mqt_signal_broadcast) ) ;
+ dassert(mtsig != NULL) ;
+
+ /* When the thread is signalled, the prev entry is set NULL and the */
+ /* waiters count is decremented. */
+ /* */
+ /* So, only need to do something here if the prev is not NULL (ie the */
+ /* mqueue_thread_signal is still on the list. */
+
+ kicked = (mtsig->prev == NULL) ;
+
+ if (!kicked)
+ mqueue_dequeue_signal(mq, mtsig) ;
+
+ qpt_mutex_unlock(&mq->mutex) ;
+
+ return kicked ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Enqueue message on local queue -- at tail
+ */
+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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Enqueue message on local queue -- at head
+ */
+extern void
+mqueue_local_enqueue_head(mqueue_local_queue lmq, mqueue_block mqb)
+{
+ if (lmq->head == NULL)
+ lmq->tail = mqb ;
+
+ mqb->next = lmq->head ;
+ lmq->head = mqb ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a message queue signal structure (struct mqueue_thread_signal).
+ * Allocate one if required.
+ *
+ * If !pthreads_enabled, then this structure is entirely redundant, but there
+ * is no harm in creating it -- but the signal will never be used.
+ *
+ * Returns address of the structure.
+ */
+extern mqueue_thread_signal
+mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
+ int signum)
+{
+ if (mqt == NULL)
+ mqt = XCALLOC(MTYPE_MQUEUE_THREAD_SIGNAL,
+ sizeof(struct mqueue_thread_signal)) ;
+ else
+ memset(mqt, 0, sizeof(struct mqueue_thread_signal)) ;
+
+ /* next and prev fields set to NULL already. */
+
+ mqt->qpthread = thread ;
+ mqt->signum = signum ;
+
+ return mqt ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset a message queue signal structure and release if required.
+ *
+ * NB: MUST NOT be queued as a waiter anywhere !!
+ *
+ * Frees the structure if required, and returns NULL.
+ * Otherwise zeroises the structure, and returns address of same.
+ */
+extern mqueue_thread_signal
+mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure)
+{
+ passert(mqt->prev == NULL) ;
+
+ if (free_structure)
+ XFREE(MTYPE_MQUEUE_THREAD_SIGNAL, mqt) ; /* sets mqt = NULL */
+ else
+ memset(mqt, 0, sizeof(struct mqueue_thread_signal)) ;
+
+ return mqt ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal the first 'n' threads on the to be signalled list.
+ *
+ * Removes the threads from the list and reduces the waiters count.
+ *
+ * NB: must be qpthreads_enabled with at least 'n' waiters.
+ *
+ * NB: sets the prev entry in the mqueue_thread_signal block to NULL, so that
+ * the thread can tell that its signal has been kicked.
+ *
+ * NB: *** MUST own the mqueue_queue mutex. ***
+ */
+static void
+mqueue_kick_signal(mqueue_queue mq, unsigned n)
+{
+ mqueue_thread_signal mtsig ;
+
+ dassert( (qpthreads_enabled) && (mq->waiters >= n) ) ;
+ while (n--)
+ {
+ mqueue_dequeue_signal(mq, mtsig = mq->kick.signal.head) ;
+ qpt_thread_signal(mtsig->qpthread, mtsig->signum) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove given signal from given message queue.
+ *
+ * NB: sets the prev entry in the mqueue_thread_signal block to NULL, so that
+ * the thread can tell that its signal has been kicked.
+ *
+ * NB: *** MUST own the mqueue_queue mutex. ***
+ */
+static void
+mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig)
+{
+ mqueue_thread_signal next ;
+ mqueue_thread_signal prev ;
+
+ next = mtsig->next ;
+ prev = mtsig->prev ;
+
+ if (prev == (void*)mq) /* marker for head of list */
+ {
+ dassert(mq->kick.signal.head == mtsig) ;
+ mq->kick.signal.head = next ;
+ }
+ else
+ {
+ dassert((prev != NULL) && (prev->next == mtsig)) ;
+ prev->next = next ;
+ } ;
+
+ if (next != NULL)
+ next->prev = prev ;
+
+ mtsig->next = NULL ;
+ mtsig->prev = NULL ; /* essential to show signal kicked */
+ --mq->waiters ; /* one fewer waiter */
+
+ dassert( ((mq->kick.signal.head == NULL) && (mq->waiters == 0)) ||
+ ((mq->kick.signal.head != NULL) && (mq->waiters != 0)) ) ;
+} ;
+
+/*==============================================================================
+ * Message Queue Block Argument Handling
+ */
+
+static void mqb_argv_extend(mqueue_block mqb, mqb_index_t iv) ;
+
+/*------------------------------------------------------------------------------
+ * Get pointer to argv[iv] -- extending if required
+ */
+inline static mqb_arg_t*
+mqb_p_arg_set(mqueue_block mqb, mqb_index_t iv)
+{
+ if (iv >= mqb->argv_count)
+ {
+ if (iv >= mqb->argv_alloc)
+ mqb_argv_extend(mqb, iv) ;
+ mqb->argv_count = iv + 1 ;
+ } ;
+
+ return &mqb->argv[iv] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer argv[iv] to given value.
+ */
+extern void
+mqb_set_argv_p(mqueue_block mqb, mqb_index_t iv, mqb_ptr_t p)
+{
+ mqb_arg_t* p_arg = mqb_p_arg_set(mqb, iv) ;
+ p_arg->p = p ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set integer argv[iv] to given value.
+ */
+extern void
+mqb_set_argv_i(mqueue_block mqb, mqb_index_t iv, mqb_int_t i)
+{
+ mqb_arg_t* p_arg = mqb_p_arg_set(mqb, iv) ;
+ p_arg->i = i ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set unsigned integer argv[iv] to given value.
+ */
+extern void
+mqb_set_argv_u(mqueue_block mqb, mqb_index_t iv, mqb_uint_t u)
+{
+ mqb_arg_t* p_arg = mqb_p_arg_set(mqb, iv) ;
+ p_arg->u = u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set size of argv[].
+ *
+ * This is entirely optional, and may be used to ensure that at least the given
+ * number of elements have been allocated.
+ *
+ * Does not change the "count". Will not reduce the allocated size.
+ *
+ * Just avoids repeated extensions of argv if it is known that it will become
+ * large.
+ */
+extern void
+mqb_set_argv_size(mqueue_block mqb, unsigned n)
+{
+ if (n > mqb->argv_alloc)
+ mqb_argv_extend(mqb, n - 1) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push a pointer onto the argv "list"
+ */
+extern void
+mqb_push_argv_p(mqueue_block mqb, mqb_ptr_t p)
+{
+ mqb_arg_t* p_arg ;
+
+ p_arg = mqb_p_arg_set(mqb, mqb->argv_count) ;
+ p_arg->p = p ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push an integer onto the argv "list"
+ */
+extern void
+mqb_push_argv_i(mqueue_block mqb, mqb_int_t i)
+{
+ mqb_arg_t* p_arg ;
+
+ p_arg = mqb_p_arg_set(mqb, mqb->argv_count) ;
+ p_arg->i = i ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push an unsigned integer onto the argv "list"
+ */
+extern void
+mqb_push_argv_u(mqueue_block mqb, mqb_uint_t u)
+{
+ mqb_arg_t* p_arg ;
+
+ p_arg = mqb_p_arg_set(mqb, mqb->argv_count) ;
+ p_arg->u = u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push an array of 'n' void* pointers onto the argv "list"
+ */
+extern void
+mqb_push_argv_array(mqueue_block mqb, unsigned n, void** array)
+{
+ mqb_index_t iv ;
+
+ /* need do nothing if n == 0, get out now to avoid edge cases */
+ if (n == 0)
+ return ;
+
+ /* make sure we are allocated upto and including the last array item */
+ iv = mqb->argv_count ;
+ mqb_set_argv_size(mqb, iv + n - 1) ;
+
+ /* require that mqb_ptr_t values exactly fill mqb_arg_t entries */
+ /* and that mqb_ptr_t values are exactly same as void* values */
+ CONFIRM(sizeof(mqb_ptr_t) == sizeof(mqb_arg_t)) ;
+ CONFIRM(sizeof(mqb_ptr_t) == sizeof(void*)) ;
+
+ /* copy the pointers */
+ memcpy(&mqb->argv[iv], array, sizeof(void*) * n) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get pointer to argv[iv] -- which MUST exist
+ *
+ * NB: it is a FATAL error to reference an argument beyond the last one set.
+ */
+inline static mqb_arg_t*
+mqb_p_arg_get(mqueue_block mqb, mqb_index_t iv)
+{
+ if (iv >= mqb->argv_count)
+ zabort("invalid message block argument index") ;
+
+ return &mqb->argv[iv] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get pointer value of argv[iv]
+ *
+ * NB: it is a FATAL error to reference an argument beyond the last one set.
+ *
+ * mqb_get_argv_count() returns the number of arguments set in argv.
+ */
+extern mqb_ptr_t
+mqb_get_argv_p(mqueue_block mqb, mqb_index_t iv)
+{
+ mqb_arg_t* p_arg = mqb_p_arg_get(mqb, iv) ;
+ return p_arg->p ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get integer value of argv[iv]
+ *
+ * NB: it is a FATAL error to reference an argument beyond the last one set.
+ *
+ * mqb_get_argv_count() returns the number of arguments set in argv.
+ */
+extern mqb_int_t
+mqb_get_argv_i(mqueue_block mqb, mqb_index_t iv)
+{
+ mqb_arg_t* p_arg = mqb_p_arg_get(mqb, iv) ;
+ return p_arg->i ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get unsigned integer value of argv[iv]
+ *
+ * NB: it is a FATAL error to reference an argument beyond the last one set.
+ *
+ * mqb_get_argv_count() returns the number of arguments set in argv.
+ */
+extern mqb_uint_t
+mqb_get_argv_u(mqueue_block mqb, mqb_index_t iv)
+{
+ mqb_arg_t* p_arg = mqb_p_arg_get(mqb, iv) ;
+ return p_arg->u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get pointer value of next argv "list" argument -- if any.
+ *
+ * There is a "next" counter in the message queue block, which is reset when
+ * the mqb is initialised or re-initialised, and by mqb_reset_argv_next().
+ *
+ * NB: returns NULL if there is no "list" or if already at the end of same.
+ */
+extern mqb_ptr_t
+mqb_next_argv_p(mqueue_block mqb)
+{
+ if (mqb->argv_next >= mqb->argv_count)
+ return NULL ;
+
+ return mqb_get_argv_p(mqb, mqb->argv_next++) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get integer value of next argv "list" argument -- if any.
+ *
+ * There is a "next" counter in the message queue block, which is reset when
+ * the mqb is initialised or re-initialised, and by mqb_reset_argv_next().
+ *
+ * NB: returns 0 if there is no "list" or if already at the end of same.
+ */
+extern mqb_int_t
+mqb_next_argv_i(mqueue_block mqb)
+{
+ if (mqb->argv_next >= mqb->argv_count)
+ return 0 ;
+
+ return mqb_get_argv_i(mqb, mqb->argv_next++) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get unsigned integer value of next argv "list" argument -- if any.
+ *
+ * There is a "next" counter in the message queue block, which is reset when
+ * the mqb is initialised or re-initialised, and by mqb_reset_argv_next().
+ *
+ * NB: returns 0 if there is no "list" or if already at the end of same.
+ */
+extern mqb_uint_t
+mqb_next_argv_u(mqueue_block mqb)
+{
+ if (mqb->argv_next >= mqb->argv_count)
+ return 0 ;
+
+ return mqb_get_argv_u(mqb, mqb->argv_next++) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop an array of 'n' void* pointers from the argv "list"
+ *
+ * There is a "next" counter in the message queue block, which is reset when
+ * the mqb is initialised or re-initialised, and by mqb_reset_argv_next().
+ *
+ * Treats from "next" to the end of the "list" as an array of void* pointers.
+ *
+ * Creates a temporary void* [] array (MTYPE_TMP), which caller must free.
+ *
+ * NB: returns NULL if there is no "list" or if already at the end of same.
+ */
+extern void**
+mqb_pop_argv_array(mqueue_block mqb)
+{
+ void** array ;
+ unsigned n ;
+
+ mqb_index_t iv = mqb->argv_next ;
+
+ /* worry about state of "next" and get out if nothing to do. */
+ if (iv >= mqb->argv_count)
+ return NULL ;
+
+ /* work out how much to pop and update "next" */
+ n = mqb->argv_count - iv ;
+
+ mqb->argv_next = mqb->argv_count ;
+
+ /* construct target array */
+ array = XMALLOC(MTYPE_TMP, sizeof(void*) * n) ;
+
+ /* require that mqb_ptr_t values exactly fill mqb_arg_t entries */
+ /* and that mqb_ptr_t values are exactly same as void* values */
+ CONFIRM(sizeof(mqb_ptr_t) == sizeof(mqb_arg_t)) ;
+ CONFIRM(sizeof(mqb_ptr_t) == sizeof(void*)) ;
+
+ /* now transfer pointers to the array */
+ memcpy(array, mqb->argv + iv, sizeof(void*) * n) ;
+
+ return array ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend the argv to include at least given iv.
+ *
+ * The number of argv slots allocated is arranged to be a multiple of
+ * mqb_argv_size_unit.
+ *
+ * Ensures that newly created slots are zeroised.
+ */
+static void
+mqb_argv_extend(mqueue_block mqb, mqb_index_t iv)
+{
+ mqb_index_t need ; /* total slots required */
+ mqb_index_t have ;
+
+ have = mqb->argv_alloc ;
+ assert(have <= iv) ;
+
+ need = ((iv / mqb_argv_size_unit) + 1) * mqb_argv_size_unit ;
+
+ if (mqb->argv == NULL)
+ mqb->argv = XCALLOC(MTYPE_MQUEUE_BLOCK_ARGV, mqb_argv_size(need)) ;
+ else
+ {
+ mqb->argv = XREALLOC(MTYPE_MQUEUE_BLOCK_ARGV, mqb->argv,
+ mqb_argv_size(need)) ;
+ memset(&mqb->argv[have], 0, mqb_argv_size(need - have)) ;
+ } ;
+
+ mqb->argv_alloc = need ;
+} ;
diff --git a/lib/mqueue.h b/lib/mqueue.h
new file mode 100644
index 00000000..a28b6606
--- /dev/null
+++ b/lib/mqueue.h
@@ -0,0 +1,393 @@
+/* Message Queue data structure -- 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 _ZEBRA_MQUEUE_H
+#define _ZEBRA_MQUEUE_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "qpthreads.h"
+#include "qtime.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Message Queue Blocks -- mqb
+ *
+ * Messages in a message queue are held as Message Queue Blocks:
+ *
+ * * action -- function to call when message is dispatched
+ *
+ * * arg0 -- always a pointer -- used in specific revoke
+ *
+ * * args -- embedded structure -- to be overlaid by user structure
+ *
+ * * argv -- pointer to: list/array of pointer/integer/unsigned
+ *
+ * NB: the elements of argv are all exactly the same size and alignment.
+ *
+ * So, as well as using the access functions, it is possible to use the
+ * argv array directly, as any of:
+ *
+ * mqb_arg_t* argv = mqb_get_argv(mqb) ;
+ *
+ * void** argv = mqb_get_argv(mqb) ;
+ * char** argv = mqb_get_argv(mqb) ;
+ *
+ * mqb_ptr_t* argv = mqb_get_argv(mqb) ;
+ * mqb_int_t* argv = mqb_get_argv(mqb) ;
+ * mqb_uint_t* argv = mqb_get_argv(mqb) ;
+ */
+
+typedef struct mqueue_block* mqueue_block ;
+
+typedef void* mqb_ptr_t ;
+typedef intptr_t mqb_int_t ;
+typedef uintptr_t mqb_uint_t ;
+
+typedef unsigned short mqb_index_t ;
+
+typedef union
+{
+ mqb_ptr_t p ;
+ mqb_int_t i ;
+ mqb_uint_t u ;
+} mqb_arg_t ;
+
+/* argv is an array of mqb_arg_t, which is the same as an array of.... */
+CONFIRM(sizeof(mqb_arg_t) == sizeof(void*)) ; /* ... pointers */
+CONFIRM(sizeof(mqb_arg_t) == sizeof(mqb_ptr_t)) ; /* ... mqb_ptr_t */
+CONFIRM(sizeof(mqb_arg_t) == sizeof(mqb_int_t)) ; /* ... mqb_int_t */
+CONFIRM(sizeof(mqb_arg_t) == sizeof(mqb_uint_t)) ; /* ... mqb_uint_t */
+ /* ... or any combination */
+enum mqb_flag
+{
+ mqb_destroy = 0,
+ mqb_action = 1
+} ;
+
+typedef enum mqb_flag mqb_flag_t ;
+
+typedef void mqueue_action(mqueue_block mqb, mqb_flag_t flag) ;
+
+enum { mqb_args_size_max = 64 } ; /* maximum size of struct args */
+enum { mqb_argv_size_unit = 16 } ; /* allocate argv in these units */
+
+struct mqb_args
+{
+ char bytes[mqb_args_size_max] ; /* empty space */
+} ;
+
+#define MQB_ARGS_SIZE_OK(s) CONFIRM(sizeof(struct s) <= mqb_args_size_max)
+
+struct mqueue_block
+{
+ struct mqb_args args ; /* user structure */
+
+ mqueue_block next ; /* single linked list */
+
+ mqueue_action* action ; /* for message dispatch */
+
+ void* arg0 ;
+ mqb_arg_t* argv ; /* argv, if any */
+
+ mqb_index_t argv_count ; /* count of elements in argv */
+ mqb_index_t argv_alloc ; /* count of elements allocated */
+ mqb_index_t argv_next ; /* iterator */
+} ;
+
+/* mqueue_block structures are malloced. That guarantees maximum alignment.
+ * To guarantee maximum alignment for "struct args", it must be first item !
+ *
+ * (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
+ * about first argument of offsetof().)
+ */
+typedef struct mqueue_block mqueue_block_t ;
+CONFIRM(offsetof(mqueue_block_t, args) == 0) ;
+
+/*==============================================================================
+ * The Message Queue itself
+ */
+
+typedef struct mqueue_thread_signal* mqueue_thread_signal ;
+
+struct mqueue_thread_signal {
+ mqueue_thread_signal next ; /* NULL => last on list */
+ mqueue_thread_signal prev ; /* NULL => NOT on list -- vital ! */
+
+ qpt_thread_t qpthread ; /* qpthread to kick */
+ int signum ; /* signal to kick with */
+} ;
+
+enum mqueue_queue_type {
+ mqt_cond_unicast, /* use qpt_cond_signal to kick the queue */
+ mqt_cond_broadcast, /* use qpt_cond_broadcast to kick the queue */
+ mqt_signal_unicast, /* use single qpt_signal to kick the queue */
+ mqt_signal_broadcast, /* use multiple qpt_signal to kick the queue */
+};
+
+#ifndef MQUEUE_DEFAULT_INTERVAL
+# define MQUEUE_DEFAULT_INTERVAL QTIME(5)
+#endif
+
+struct mqueue_queue_cond {
+ qpt_cond_t wait_here ;
+ qtime_mono_t timeout ;
+ qtime_t interval ;
+} ;
+
+struct mqueue_queue_signal {
+ mqueue_thread_signal head ; /* NULL => list is empty */
+ mqueue_thread_signal tail ;
+};
+
+typedef struct mqueue_queue* mqueue_queue ;
+
+struct mqueue_queue
+{
+ qpt_mutex_t mutex ;
+
+ mqueue_block head ; /* NULL => list is empty */
+ mqueue_block tail_priority ; /* last priority message (if any & not empty) */
+ mqueue_block tail ; /* last message (if not empty) */
+
+ unsigned count ; /* of items on the queue */
+
+ enum mqueue_queue_type type ;
+
+ unsigned waiters ;
+
+ union {
+ struct mqueue_queue_cond cond ;
+ struct mqueue_queue_signal signal ;
+ } 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
+ */
+
+extern void
+mqueue_initialise(void) ;
+
+extern void
+mqueue_finish(void) ;
+
+extern mqueue_queue
+mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type) ;
+
+extern void
+mqueue_empty(mqueue_queue mq) ;
+
+extern mqueue_queue
+mqueue_reset(mqueue_queue mq, int free_structure) ;
+
+#define mqueue_reset_keep(mq) mqueue_reset(mq, 0)
+#define mqueue_reset_free(mq) mqueue_reset(mq, 1)
+
+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) ;
+
+extern mqueue_thread_signal
+mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
+ int signum) ;
+mqueue_thread_signal
+mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure) ;
+
+#define mqueue_thread_signal_reset_keep(mqt) mqueue_thread_signal_reset(mqt, 0)
+#define mqueue_thread_signal_reset_free(mqt) mqueue_thread_signal_reset(mqt, 1)
+
+extern mqueue_block
+mqb_init_new(mqueue_block mqb, mqueue_action action, void* arg0) ;
+
+extern mqueue_block
+mqb_re_init(mqueue_block mqb, mqueue_action action, void* arg0) ;
+
+extern void
+mqb_free(mqueue_block mqb) ;
+
+enum mqb_rank
+{
+ mqb_priority = true,
+ mqb_ordinary = false
+} ;
+
+extern void
+mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority) ;
+
+extern mqueue_block
+mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
+
+extern void
+mqueue_revoke(mqueue_queue mq, void* arg0) ;
+
+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 void
+mqueue_local_enqueue_head(mqueue_local_queue lmq, mqueue_block mqb) ;
+
+Inline mqueue_block
+mqueue_local_head(mqueue_local_queue lmq) ;
+
+extern mqueue_block
+mqueue_local_dequeue(mqueue_local_queue lmq) ;
+
+/*==============================================================================
+ * Access functions for mqueue_block fields -- mqb_set_xxx/mqb_get_xxx
+ *
+ * Users should not poke around inside the mqueue_block structure.
+ */
+
+Inline void mqb_set_action(mqueue_block mqb, mqueue_action action) ;
+
+Inline void mqb_set_arg0(mqueue_block mqb, void* p) ;
+
+extern void mqb_set_argv_size(mqueue_block mqb, unsigned n) ;
+
+extern void mqb_set_argv_p(mqueue_block mqb, mqb_index_t iv, mqb_ptr_t p) ;
+extern void mqb_set_argv_i(mqueue_block mqb, mqb_index_t iv, mqb_int_t i) ;
+extern void mqb_set_argv_u(mqueue_block mqb, mqb_index_t iv, mqb_uint_t u) ;
+
+extern void mqb_push_argv_p(mqueue_block mqb, mqb_ptr_t p) ;
+extern void mqb_push_argv_i(mqueue_block mqb, mqb_int_t i) ;
+extern void mqb_push_argv_u(mqueue_block mqb, mqb_uint_t u) ;
+
+extern void mqb_push_argv_array(mqueue_block mqb, unsigned n, void** array) ;
+
+Inline void mqb_dispatch(mqueue_block mqb, mqb_flag_t flag) ;
+Inline void mqb_dispatch_action(mqueue_block mqb) ;
+Inline void mqb_dispatch_destroy(mqueue_block mqb) ;
+
+Inline void* mqb_get_arg0(mqueue_block mqb) ;
+Inline void* mqb_get_args(mqueue_block mqb) ;
+Inline void* mqb_get_argv(mqueue_block mqb) ;
+
+Inline mqb_index_t mqb_get_argv_count(mqueue_block mqb) ;
+
+extern mqb_ptr_t mqb_get_argv_p(mqueue_block mqb, mqb_index_t iv) ;
+extern mqb_int_t mqb_get_argv_i(mqueue_block mqb, mqb_index_t iv) ;
+extern mqb_uint_t mqb_get_argv_u(mqueue_block mqb, mqb_index_t iv) ;
+
+extern mqb_ptr_t mqb_next_argv_p(mqueue_block mqb) ;
+extern mqb_int_t mqb_next_argv_i(mqueue_block mqb) ;
+extern mqb_uint_t mqb_next_argv_u(mqueue_block mqb) ;
+
+extern void** mqb_pop_argv_array(mqueue_block mqb) ;
+
+/*==============================================================================
+ * The Inline functions.
+ */
+
+Inline mqueue_block
+mqueue_local_head(mqueue_local_queue lmq)
+{
+ return lmq->head ;
+} ;
+
+/* Set operations. */
+
+Inline void
+mqb_set_action(mqueue_block mqb, mqueue_action action)
+{
+ mqb->action = action ;
+} ;
+
+Inline void
+mqb_set_arg0(mqueue_block mqb, void* arg0)
+{
+ mqb->arg0 = arg0 ;
+} ;
+
+/* Get operations */
+
+Inline void
+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)
+{
+ return mqb->arg0 ;
+} ;
+
+Inline void*
+mqb_get_args(mqueue_block mqb)
+{
+ return &mqb->args ;
+} ;
+
+Inline void*
+mqb_get_argv(mqueue_block mqb)
+{
+ return mqb->argv ;
+} ;
+
+Inline mqb_index_t
+mqb_get_argv_count(mqueue_block mqb)
+{
+ return mqb->argv_count ;
+} ;
+
+Inline void
+mqb_reset_argv_next(mqueue_block mqb)
+{
+ mqb->argv_next = 0 ;
+} ;
+
+#endif /* _ZEBRA_MQUEUE_H */
diff --git a/lib/network.c b/lib/network.c
index 3373983b..ae67a402 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -17,14 +17,23 @@
* 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>
#include "log.h"
#include "network.h"
-/* Read nbytes from fd and store into ptr. */
+/*------------------------------------------------------------------------------
+ * Read nbytes from fd and store into ptr -- BLOCKING
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: >= 0 -- number of bytes read
+ * < 0 => error
+ *
+ * NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
+ */
int
readn (int fd, u_char *ptr, int nbytes)
{
@@ -33,24 +42,86 @@ readn (int fd, u_char *ptr, int nbytes)
nleft = nbytes;
- while (nleft > 0)
+ while (nleft > 0)
{
nread = read (fd, ptr, nleft);
- if (nread < 0)
- return (nread);
+ if (nread > 0)
+ {
+ nleft -= nread;
+ ptr += nread;
+ }
+ else if (nread == 0)
+ break;
else
- if (nread == 0)
- break;
-
- nleft -= nread;
- ptr += nread;
+ {
+ if (errno != EINTR)
+ return (nread);
+ }
}
return nbytes - nleft;
-}
+}
+
+/*------------------------------------------------------------------------------
+ * Read up to nbyte bytes into buf -- assuming NON-BLOCKING.
+ *
+ * Loops internally if gets EINTR -- so if does not read everything asked for,
+ * that must be because the read would otherwise block.
+ *
+ * Returns: 0..n -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met immediately
+ *
+ * NB: if asked to write zero bytes, does nothing and will return 0.
+ *
+ * Reading zero bytes is defined for all types of files, and may be used
+ * to probe for error state.
+ */
+int
+read_nb(int fd, void* buf, size_t nbyte)
+{
+ size_t nleft = nbyte ;
+
+ do
+ {
+ int ret = read(fd, buf, nleft);
+
+ if (ret > 0)
+ {
+ buf = (char*)buf + ret ;
+ nleft -= ret ;
+ }
+ else if (ret == 0)
+ {
+ if (nleft < nbyte)
+ break ; /* if read something before EOF */
+
+ return -2 ; /* hit EOF immediately */
+ }
+ else
+ {
+ int err = errno ;
+ if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ break ;
+ if (err != EINTR)
+ return -1 ; /* failed */
+ } ;
+ } while (nleft > 0) ;
+
+ return (nbyte - nleft) ;
+} ;
-/* Write nbytes from ptr to fd. */
+/*------------------------------------------------------------------------------
+ * Write nbytes to fd from ptr -- BLOCKING
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: >= 0 -- number of bytes written
+ * < 0 => error
+ *
+ * NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
+ */
int
writen(int fd, const u_char *ptr, int nbytes)
{
@@ -59,19 +130,75 @@ writen(int fd, const u_char *ptr, int nbytes)
nleft = nbytes;
- while (nleft > 0)
+ while (nleft > 0)
{
nwritten = write(fd, ptr, nleft);
-
- if (nwritten <= 0)
- return (nwritten);
- nleft -= nwritten;
- ptr += nwritten;
+ if (nwritten > 0)
+ {
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+ else if (nwritten == 0)
+ break ;
+ else
+ {
+ if (errno != EINTR)
+ return (nwritten);
+ }
}
return nbytes - nleft;
}
+/*------------------------------------------------------------------------------
+ * Write up to nbyte bytes from buf -- assuming NON-BLOCKING.
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: 0..n -- number of bytes written
+ * -1 => failed -- see errno
+ *
+ * NB: if asked to write zero bytes, does nothing and will return 0.
+ *
+ * Writing zero bytes is defined for "regular files", but not for anything
+ * else.
+ */
+int
+write_nb(int fd, void* buf, size_t nbyte)
+{
+ size_t nleft = nbyte ;
+
+ while (nleft > 0)
+ {
+ int ret = write(fd, buf, nleft);
+
+ if (ret > 0)
+ {
+ buf = (char*)buf + ret ;
+ nleft -= ret ;
+ }
+ else if (ret == 0)
+ break ; /* not sure can happen... but
+ cannot assume will go away */
+ else
+ {
+ int err = errno ;
+ if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ break ;
+ if (err != EINTR)
+ return -1 ; /* failed */
+ } ;
+ } ;
+
+ return (nbyte - nleft) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set fd to non-blocking
+ *
+ * Returns: 0 => OK
+ * -1 => failed
+ */
int
set_nonblocking(int fd)
{
@@ -82,13 +209,13 @@ set_nonblocking(int fd)
if ((flags = fcntl(fd, F_GETFL)) < 0)
{
zlog_warn("fcntl(F_GETFL) failed for fd %d: %s",
- fd, safe_strerror(errno));
+ fd, errtoa(errno, 0).str);
return -1;
}
if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) < 0)
{
zlog_warn("fcntl failed setting fd %d non-blocking: %s",
- fd, safe_strerror(errno));
+ fd, errtoa(errno, 0).str);
return -1;
}
return 0;
diff --git a/lib/network.h b/lib/network.h
index 4d9c2284..72d38b52 100644
--- a/lib/network.h
+++ b/lib/network.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_NETWORK_H
@@ -33,6 +33,10 @@ extern int writen (int, const u_char *, int);
-1 on error. */
extern int set_nonblocking(int fd);
+/* Non-Blocking versions of read/write */
+int read_nb(int fd, void* buf, size_t nbyte) ;
+int write_nb(int fd, void* buf, size_t nbyte) ;
+
/* Does the I/O error indicate that the operation should be retried later? */
#define ERRNO_IO_RETRY(EN) \
(((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
diff --git a/lib/node_type.h b/lib/node_type.h
new file mode 100644
index 00000000..7ec1107d
--- /dev/null
+++ b/lib/node_type.h
@@ -0,0 +1,81 @@
+/* Command handler node_type stuff -- header
+ * Copyright (C) 1997, 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.
+ */
+
+#ifndef _ZEBRA_NODE_TYPE_H
+#define _ZEBRA_NODE_TYPE_H
+
+/* There are some command levels which called from command node. */
+enum node_type
+{
+ AUTH_NODE, /* Authentication mode of vty interface. */
+ RESTRICTED_NODE, /* Restricted view mode */
+ VIEW_NODE, /* View node. Default mode of vty interface. */
+ AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
+ ENABLE_NODE, /* Enable node. */
+
+ MIN_DO_SHORTCUT_NODE = ENABLE_NODE,
+ /* May not "do xxx" at any node lower */
+ MAX_NON_CONFIG_NODE = ENABLE_NODE,
+ /* May not be higher than this without owning
+ * the configuration symbol of power */
+
+ CONFIG_NODE, /* Config node. Default mode of config file. */
+
+ MIN_CONTEXT_NODE = CONFIG_NODE,
+ /* May not change context to any node lower */
+
+ SERVICE_NODE, /* Service node. */
+ DEBUG_NODE, /* Debug node. */
+ AAA_NODE, /* AAA node. */
+ KEYCHAIN_NODE, /* Key-chain node. */
+ KEYCHAIN_KEY_NODE, /* Key-chain key node. */
+ INTERFACE_NODE, /* Interface mode node. */
+ ZEBRA_NODE, /* zebra connection node. */
+ TABLE_NODE, /* rtm_table selection node. */
+ RIP_NODE, /* RIP protocol mode node. */
+ RIPNG_NODE, /* RIPng protocol mode node. */
+ BGP_NODE, /* BGP protocol mode which includes BGP4+ */
+ BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
+ BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
+ BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
+ BGP_IPV6_NODE, /* BGP IPv6 address family */
+ BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */
+ OSPF_NODE, /* OSPF protocol mode */
+ OSPF6_NODE, /* OSPF protocol for IPv6 mode */
+ ISIS_NODE, /* ISIS protocol mode */
+ MASC_NODE, /* MASC for multicast. */
+ IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
+ IP_NODE, /* Static ip route node. */
+ ACCESS_NODE, /* Access list node. */
+ PREFIX_NODE, /* Prefix list node. */
+ ACCESS_IPV6_NODE, /* Access list node. */
+ PREFIX_IPV6_NODE, /* Prefix list node. */
+ AS_LIST_NODE, /* AS list node. */
+ COMMUNITY_LIST_NODE, /* Community list node. */
+ RMAP_NODE, /* Route map node. */
+ SMUX_NODE, /* SNMP configuration node. */
+ DUMP_NODE, /* Packet dump node. */
+ FORWARDING_NODE, /* IP forwarding node. */
+ PROTOCOL_NODE, /* protocol filtering node */
+ VTY_NODE, /* Vty node. */
+};
+
+#endif /* _ZEBRA_NODE_TYPE_H */
diff --git a/lib/pid_output.c b/lib/pid_output.c
index 5261babc..df1862bb 100644
--- a/lib/pid_output.c
+++ b/lib/pid_output.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 <zebra.h>
@@ -39,7 +39,7 @@ pid_output (const char *path)
oldumask = umask(0777 & ~PIDFILE_MASK);
fp = fopen (path, "w");
- if (fp != NULL)
+ if (fp != NULL)
{
fprintf (fp, "%d\n", (int) pid);
fclose (fp);
@@ -49,7 +49,7 @@ pid_output (const char *path)
/* XXX Why do we continue instead of exiting? This seems incompatible
with the behavior of the fcntl version below. */
zlog_warn("Can't fopen pid lock file %s (%s), continuing",
- path, safe_strerror(errno));
+ path, errtoa(errno, 0).str);
umask(oldumask);
return -1;
}
@@ -59,50 +59,74 @@ pid_output (const char *path)
pid_t
pid_output (const char *path)
{
- int tmp;
- int fd;
- pid_t pid;
- char buf[16];
- struct flock lock;
- mode_t oldumask;
+ const char* fail ;
+ int err ;
+
+ pid_t pid ;
+ mode_t oldumask ;
+ int fd ;
+ struct flock lock ;
+ char buf[32] ;
+ size_t pidsize ;
pid = getpid ();
oldumask = umask(0777 & ~PIDFILE_MASK);
- fd = open (path, O_RDWR | O_CREAT, PIDFILE_MASK);
+
+ fail = "Failed to open pid lock file '%s' for pid %d (%s)" ;
+ fd = open (path, O_RDWR | O_CREAT, PIDFILE_MASK) ;
+ err = errno ;
+
+ umask(oldumask);
+
if (fd < 0)
- {
- zlog_err("Can't create pid lock file %s (%s), exiting",
- path, safe_strerror(errno));
- umask(oldumask);
- exit(1);
- }
- else
- {
- size_t pidsize;
+ goto failed_err ;
- umask(oldumask);
- memset (&lock, 0, sizeof(lock));
+ memset (&lock, 0, sizeof(lock));
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
+ if (fcntl(fd, F_SETLK, &lock) < 0)
+ {
+ fail = "Failed to write lock pid lock file '%s' for pid %d (%s)" ;
+ err = errno ;
- if (fcntl(fd, F_SETLK, &lock) < 0)
+ if ((err == EACCES) || (err == EAGAIN))
{
- zlog_err("Could not lock pid_file %s, exiting", path);
- exit(1);
- }
-
- sprintf (buf, "%d\n", (int) pid);
- pidsize = strlen(buf);
- if ((tmp = write (fd, buf, pidsize)) != (int)pidsize)
- zlog_err("Could not write pid %d to pid_file %s, rc was %d: %s",
- (int)pid,path,tmp,safe_strerror(errno));
- else if (ftruncate(fd, pidsize) < 0)
- zlog_err("Could not truncate pid_file %s to %u bytes: %s",
- path,(u_int)pidsize,safe_strerror(errno));
- }
+ fail = "Failed to write lock pid lock file '%s', "
+ "blocked by pid %d (%s)" ;
+ fcntl(fd, F_GETLK, &lock) ;
+ pid = lock.l_pid ;
+ } ;
+
+ goto failed_err ;
+ } ;
+
+ pidsize = sprintf (buf, "%d\n", (int)pid) ;
+
+ fail = "Failed to write pid to pid lock file '%s' for pid %d (%s)" ;
+ if (write(fd, buf, pidsize) != (ssize_t)pidsize)
+ goto failed ;
+
+ fail = "Failed to truncate pid lock file '%s' to length for pid %d (%s)" ;
+ if (ftruncate(fd, pidsize) < 0)
+ goto failed ;
+
+ fail = "Failed to fsync pid lock file '%s' for pid %d (%s)" ;
+ if (fsync(fd) < 0)
+ goto failed ;
+
return pid;
+
+failed:
+ err = errno ;
+
+failed_err:
+ zlog_err(fail, path, (int)pid, errtoa(err, 0).str) ;
+ fprintf(stderr, fail, path, (int)pid, errtoa(err, 0).str) ;
+ fprintf(stderr, "\n") ;
+
+ exit(1) ;
}
#endif /* HAVE_FCNTL */
diff --git a/lib/plist.c b/lib/plist.c
index 0f802a83..e5c71d35 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1,6 +1,10 @@
/* Prefix list functions.
* Copyright (C) 1999 Kunihiro Ishiguro
*
+ * 24-Nov-2009 -- substantially re-cast to speed up the handling of very
+ * large prefix-lists (10,000+ entries).
+ * 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
@@ -29,494 +33,1005 @@
#include "buffer.h"
#include "stream.h"
#include "log.h"
+#include "symtab.h"
+#include "vector.h"
-/* Each prefix-list's entry. */
-struct prefix_list_entry
+/* This implements ip prefix-list functions.
+ *
+ * A prefix-list is referred to by name, where a name is an arbitrary string,
+ * case-sensitive. When showing prefix-lists the names are sorted
+ * "alphabetically", except for any digit sections, which sort numerically.
+ * Note that leading zeros are significant... "01" is not the same as "1",
+ * and sorts after it.
+*/
+
+enum prefix_flags {
+ PREFIX_ANY = 0x01, /* prefix declared as 'any' */
+ PREFIX_LE = 0x02, /* explicit 'le' */
+ PREFIX_GE = 0x04, /* explicit 'ge' */
+ PREFIX_SEQ = 0x10, /* explicit sequence number */
+} ;
+
+struct prefix_list ;
+struct prefix_list_entry ;
+
+/* Master structure of prefix_list.
+ *
+ * Each address family has it's own distinct set of prefix lists. (Including
+ * the fake AFI_ORF_PREFIX "family".)
+ *
+ * This means that a prefix_list name is local to an address family, but
+ * global wrt router instances.
+ * */
+struct prefix_master
{
- int seq;
+ struct symbol_table table ; /* table of prefix_list by name. */
- int le;
- int ge;
-
- enum prefix_list_type type;
+ int seqnum_flag ; /* ip prefix-list sequence-number state. */
- int any;
- struct prefix prefix;
+ struct prefix_list *recent ; /* the latest update. */
- unsigned long refcnt;
- unsigned long hitcnt;
+ struct prefix_list *cache_owner ;
+ /* prefix_list that owns the dup_cache */
+ struct vector dup_cache ; /* the dup_cache vector */
- struct prefix_list_entry *next;
- struct prefix_list_entry *prev;
+ void (*add_hook) (struct prefix_list *);
+ /* executed when new prefix_list is added. */
+ void (*delete_hook) (struct prefix_list *);
+ /* executed when a prefix_list is deleted. */
};
-/* List of struct prefix_list. */
-struct prefix_list_list
+/* Each prefix_list is described by one of these.*/
+struct prefix_list
{
- struct prefix_list *head;
- struct prefix_list *tail;
-};
+ struct prefix_master *master ; /* parent table: scope of this list. */
+ struct symbol* sym ; /* symbol in parent's symbol table */
-/* Master structure of prefix_list. */
-struct prefix_master
+ char *desc ; /* ip prefix-list description */
+
+ afi_t afi ; /* address family for all prefixes */
+ /* this is the *real* address family, so */
+ /* not "AFI_ORF_PREFIX" or similar. */
+
+ struct vector list ; /* the actual list of prefix matches */
+
+ int (*cmp)(struct prefix_list_entry** p_a, struct prefix_list_entry** p_b) ;
+ /* used when searching for duplicates */
+
+ int rangecount; /* XXX TODO: discover what this is for ?? */
+ /* Is not changed anywhere !! */
+} ;
+
+/* Each prefix-list's entry. */
+struct prefix_list_entry
{
- /* List of prefix_list which name is number. */
- struct prefix_list_list num;
+ int seq;
- /* List of prefix_list which name is string. */
- struct prefix_list_list str;
+ enum prefix_list_type type;
- /* Whether sequential number is used. */
- int seqnum;
+ int flags ; /* zero or more of PREFIX_ANY, PREFIX_LE and PREFIX_GE */
+ int le ; /* for exact match, set to prefix length */
+ int ge ; /* ditto */
- /* The latest update. */
- struct prefix_list *recent;
+ struct prefix prefix;
- /* Hook function which is executed when new prefix_list is added. */
- void (*add_hook) (struct prefix_list *);
+ u_int32_t mask ; /* for IPv4 -- host order. */
+ /* for last significant word of IPv6 -- network order */
+ int last ; /* for IPv4 -- not used. */
+ /* for IPv6 -- word to apply mask to. */
- /* Hook function which is executed when prefix_list is deleted. */
- void (*delete_hook) (struct prefix_list *);
+ unsigned long refcnt;
+ unsigned long hitcnt;
};
/* Static structure of IPv4 prefix_list's master. */
-static struct prefix_master prefix_master_ipv4 =
-{
- {NULL, NULL},
- {NULL, NULL},
- 1,
- NULL,
- NULL,
-};
+static struct prefix_master prefix_master_ipv4 ;
#ifdef HAVE_IPV6
/* Static structure of IPv6 prefix-list's master. */
-static struct prefix_master prefix_master_ipv6 =
-{
- {NULL, NULL},
- {NULL, NULL},
- 1,
- NULL,
- NULL,
-};
+static struct prefix_master prefix_master_ipv6 ;
#endif /* HAVE_IPV6*/
/* Static structure of BGP ORF prefix_list's master. */
-static struct prefix_master prefix_master_orf =
-{
- {NULL, NULL},
- {NULL, NULL},
- 1,
- NULL,
- NULL,
-};
-
-static struct prefix_master *
-prefix_master_get (afi_t afi)
+static struct prefix_master prefix_master_orf ;
+
+/* For real afi, the choice is strictly limited, and includes IPv6
+ * only if HAVE_IPV6 ! */
+#ifdef HAVE_IPV6
+#define assert_afi_real(a) assert(((a) == AFI_IP) || ((a) == AFI_IP6))
+#else
+#define assert_afi_real(a) assert((a) == AFI_IP)
+#endif
+
+/* Map afi to prefix_master.
+ *
+ * Note: there is no ipv6 master if not HAVE_IPV6.
+ *
+ * Returns address of prefix_master, or NULL if unknown afi.
+ */
+static inline struct prefix_master *
+prefix_master_get(afi_t afi)
{
- if (afi == AFI_IP)
- return &prefix_master_ipv4;
+ switch (afi)
+ {
+ case AFI_IP:
+ return &prefix_master_ipv4;
#ifdef HAVE_IPV6
- else if (afi == AFI_IP6)
- return &prefix_master_ipv6;
+ case AFI_IP6:
+ return &prefix_master_ipv6;
#endif /* HAVE_IPV6 */
- else if (afi == AFI_ORF_PREFIX)
- return &prefix_master_orf;
- return NULL;
-}
+ case AFI_ORF_PREFIX:
+ return &prefix_master_orf;
+ default:
+ return NULL;
+ } ;
+} ;
-/* Lookup prefix_list from list of prefix_list by name. */
-struct prefix_list *
-prefix_list_lookup (afi_t afi, const char *name)
+/* Map afi to maximum prefix length. Implied assert_afi_real(). */
+static int
+prefix_max_length(afi_t afi)
{
- struct prefix_list *plist;
- struct prefix_master *master;
+ switch (afi)
+ {
+ case AFI_IP:
+ return IPV4_MAX_BITLEN ;
+#ifdef HAVE_IPV6
+ case AFI_IP6:
+ return IPV6_MAX_BITLEN ;
+#endif
+ default:
+ zabort("invalid address family") ;
+ return 0 ;
+ } ;
+} ;
- if (name == NULL)
- return NULL;
+/* Compare prefix list entries, taking into account:
+ *
+ * -- prefix value -- assumes is masked down correctly
+ * -- prefix length
+ * -- ge
+ * -- le
+ * -- type
+ *
+ * ... everything *except* the sequence number.
+ */
- master = prefix_master_get (afi);
- if (master == NULL)
- return NULL;
+#define PREFIX_LIST_CMP_RET(f) \
+ if ((*p_a)->f != (*p_b)->f) \
+ return ( (*p_a)->f < (*p_b)->f) ? -1 : +1
+#define PREFIX_LIST_CMP_RET_NL(f) \
+ if ((*p_a)->f != (*p_b)->f) \
+ return (ntohl((*p_a)->f) < ntohl((*p_b)->f)) ? -1 : +1
+#define PREFIX_LIST_CMP_REST \
+PREFIX_LIST_CMP_RET(prefix.prefixlen) ; \
+PREFIX_LIST_CMP_RET(ge) ; \
+PREFIX_LIST_CMP_RET(le) ; \
+PREFIX_LIST_CMP_RET(type)
- for (plist = master->num.head; plist; plist = plist->next)
- if (strcmp (plist->name, name) == 0)
- return plist;
+static int
+prefix_list_ipv4_cmp(struct prefix_list_entry** p_a,
+ struct prefix_list_entry** p_b)
+{
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix4.s_addr) ;
+ PREFIX_LIST_CMP_REST ;
+ return 0 ;
+} ;
- for (plist = master->str.head; plist; plist = plist->next)
- if (strcmp (plist->name, name) == 0)
- return plist;
+#ifdef HAVE_IPV6 /*----------------------------------------------------*/
+static int
+prefix_list_ipv6_cmp(struct prefix_list_entry** p_a,
+ struct prefix_list_entry** p_b)
+{
+#ifdef s6_addr32
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[0]) ;
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[1]) ;
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[2]) ;
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[3]) ;
+#else
+ int c ;
+ if ((c = memcmp(&(*p_a)->prefix.u.prefix6.s6_addr,
+ &(*p_b)->prefix.u.prefix6.s6_addr, 16)) != 0)
+ return c ;
+#endif
+ PREFIX_LIST_CMP_REST ;
+ return 0 ;
+} ;
+#endif /*----------------------------------------------------*/
+
+/*==============================================================================
+ * Operations on prefix_master.
+ */
- return NULL;
-}
+static void prefix_list_delete (struct prefix_list* plist) ;
+static void prefix_dup_cache_free(struct prefix_master* pm) ;
-static struct prefix_list *
-prefix_list_new (void)
+/* Initialise given prefix_master. */
+static void
+prefix_master_init(struct prefix_master * pm)
{
- struct prefix_list *new;
+ memset(pm, 0, sizeof(struct prefix_master)) ;
- new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list));
- return new;
-}
+ symbol_table_init_new(&pm->table, pm, 20, 200, NULL, NULL) ;
+ pm->seqnum_flag = 1 ; /* Default is to generate sequence numbers. */
+} ;
+/* Reset given prefix_master.
+ *
+ * Frees all prefix lists and empties the symbol table. Any references to
+ * prefix lists are the responsibility of the reference owners.
+ *
+ * Resets to the default sequence numbering state.
+ *
+ * Retains current add_hook and delete_hook functions.
+ */
static void
-prefix_list_free (struct prefix_list *plist)
+prefix_master_reset(struct prefix_master * pm)
{
- XFREE (MTYPE_PREFIX_LIST, plist);
-}
+ struct prefix_list* plist ;
+ while ((plist = symbol_table_ream_keep(&(pm->table))))
+ prefix_list_delete(plist) ;
-static struct prefix_list_entry *
-prefix_list_entry_new (void)
-{
- struct prefix_list_entry *new;
+ pm->seqnum_flag = 1 ; /* Default is to generate sequence numbers. */
- new = XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry));
- return new;
+ pm->recent = NULL ;
+ prefix_dup_cache_free(pm) ;
+} ;
+
+/* Add hook function. */
+void
+prefix_list_add_hook (void (*func)(struct prefix_list *plist))
+{
+ prefix_master_ipv4.add_hook = func;
+#ifdef HAVE_IPV6
+ prefix_master_ipv6.add_hook = func;
+#endif /* HAVE_IPV6 */
}
-static void
-prefix_list_entry_free (struct prefix_list_entry *pentry)
+/* Delete hook function. */
+void
+prefix_list_delete_hook (void (*func)(struct prefix_list *plist))
{
- XFREE (MTYPE_PREFIX_LIST_ENTRY, pentry);
+ prefix_master_ipv4.delete_hook = func;
+#ifdef HAVE_IPV6
+ prefix_master_ipv6.delete_hook = func;
+#endif /* HAVE_IPVt6 */
}
-/* Insert new prefix list to list of prefix_list. Each prefix_list
- is sorted by the name. */
-static struct prefix_list *
-prefix_list_insert (afi_t afi, const char *name)
+/*==============================================================================
+ * Prefix List Use.
+ *
+ * The prefix_list_ref type is a struct symbol_ref*, so we can operate on
+ * prefix_list_ref* arguments, keeping the stored reference values up to date.
+ */
+
+const char*
+prefix_list_get_name(struct prefix_list* plist)
{
- unsigned int i;
- long number;
- struct prefix_list *plist;
- struct prefix_list *point;
- struct prefix_list_list *list;
- struct prefix_master *master;
+ return symbol_get_name(plist->sym) ;
+} ;
- master = prefix_master_get (afi);
- if (master == NULL)
- return NULL;
+/* Set reference to prefix_list, by name.
+ * Replaces any existing reference.
+ *
+ * Returns the new value of the prefix_list_ref.
+ *
+ * NB: if reference existed, the parent and tag fields are preserved.
+ * Otherwise they are set to 0.
+ */
+prefix_list_ref
+prefix_list_set_ref(prefix_list_ref* p_ref, afi_t afi, const char* name)
+{
+ struct prefix_master* pm = prefix_master_get(afi) ;
- /* Allocate new prefix_list and copy given name. */
- plist = prefix_list_new ();
- plist->name = XSTRDUP (MTYPE_PREFIX_LIST_STR, name);
- plist->master = master;
+ if (pm == NULL)
+ return NULL ;
- /* If name is made by all digit character. We treat it as
- number. */
- for (number = 0, i = 0; i < strlen (name); i++)
- {
- if (isdigit ((int) name[i]))
- number = (number * 10) + (name[i] - '0');
- else
- break;
- }
+ return *p_ref = symbol_set_ref(*p_ref, symbol_find(&(pm->table), name)) ;
+} ;
- /* In case of name is all digit character */
- if (i == strlen (name))
- {
- plist->type = PREFIX_TYPE_NUMBER;
+/* Copy reference to prefix_list.
+ * Replaces any existing reference (by NULL if reference is NULL).
+ *
+ * Returns the new value of the prefix_list_ref.
+ *
+ * NB: if reference existed, the parent and tag fields are preserved.
+ * Otherwise they are set to 0.
+ */
+prefix_list_ref
+prefix_list_copy_ref(prefix_list_ref* p_dst, prefix_list_ref src)
+{
+ return *p_dst = symbol_set_ref(*p_dst, sym_ref_symbol(src)) ;
+} ;
- /* Set prefix_list to number list. */
- list = &master->num;
+/* Unset reference to prefix_list (does nothing if reference is NULL).
+ *
+ * Returns the new value of the prefix_list_ref -- ie NULL.
+ */
+prefix_list_ref
+prefix_list_unset_ref(prefix_list_ref* p_ref)
+{
+ return *p_ref = symbol_unset_ref(*p_ref, 1) ;
+} ;
- for (point = list->head; point; point = point->next)
- if (atol (point->name) >= number)
- break;
- }
- else
- {
- plist->type = PREFIX_TYPE_STRING;
-
- /* Set prefix_list to string list. */
- list = &master->str;
-
- /* Set point to insertion point. */
- for (point = list->head; point; point = point->next)
- if (strcmp (point->name, name) >= 0)
- break;
- }
+/* Get name of prefix_list to which given reference (if any) refers.
+ *
+ * Returns NULL if the reference is NULL.
+ */
+const char*
+prefix_list_ref_name(prefix_list_ref ref)
+{
+ return sym_ref_name(ref) ;
+} ;
- /* In case of this is the first element of master. */
- if (list->head == NULL)
- {
- list->head = list->tail = plist;
- return plist;
- }
+/* Return "identity" of prefix_list referred to by the given reference.
+ * Will be NULL if the reference is NULL.
+ *
+ * Two references to the same prefix_list will have the same "identity".
+ */
+void* prefix_list_ref_ident(prefix_list_ref ref)
+{
+ return (void*)sym_ref_symbol(ref) ;
+} ;
- /* In case of insertion is made at the tail of access_list. */
- if (point == NULL)
- {
- plist->prev = list->tail;
- list->tail->next = plist;
- list->tail = plist;
- return plist;
- }
+/* Return prefix_list referred to by the given reference.
+ * Will be NULL If the reference is NULL *OR* if the prefix_list is undefined.
+ */
+struct prefix_list* prefix_list_ref_plist(prefix_list_ref ref)
+{
+ return (struct prefix_list*)sym_ref_value(ref) ;
+} ;
- /* In case of insertion is made at the head of access_list. */
- if (point == list->head)
- {
- plist->next = list->head;
- list->head->prev = plist;
- list->head = plist;
- return plist;
- }
- /* Insertion is made at middle of the access_list. */
- plist->next = point;
- plist->prev = point->prev;
+/* Apply a prefix_list to the given prefix. */
+enum prefix_list_type
+prefix_list_apply (struct prefix_list *plist, void *object)
+{
+ struct prefix *p ;
+ int plen ;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+
+ in_addr_t ip ;
+#ifdef s6_addr32
+ u_int32_t* pp ;
+ u_int32_t* pep ;
+#else
+ unsigned char* pp ;
+ unsigned char* pep ;
+#endif
+ /* Deny if prefix_list is undefined or empty */
+ if ((plist == NULL) || (vector_end(&plist->list) == 0))
+ return PREFIX_DENY;
- if (point->prev)
- point->prev->next = plist;
- point->prev = plist;
+ p = object ;
+ plen = p->prefixlen ;
- return plist;
-}
+ /* For maximum performance we have separate loops for IPv4 and IPv6 */
+ assert_afi_real(plist->afi) ;
-static struct prefix_list *
-prefix_list_get (afi_t afi, const char *name)
-{
- struct prefix_list *plist;
+ switch (plist->afi)
+ {
+ case AFI_IP:
+ ip = p->u.prefix4.s_addr ;
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ ++pe->refcnt ;
+ if ((plen < pe->ge) || (plen > pe->le))
+ continue ;
+ if (((pe->prefix.u.prefix4.s_addr ^ ip) & pe->mask) == 0)
+ {
+ ++pe->hitcnt;
+ return pe->type ;
+ } ;
+ }
+ break ;
+
+ case AFI_IP6:
+#ifdef s6_addr32
+ pp = p->u.prefix6.s6_addr32 ;
+#else
+ pp = p->u.prefix6.s6_addr ;
+#endif
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ int j, l ;
+ ++pe->refcnt ;
+ if ((plen < pe->ge) || (plen > pe->le))
+ continue ;
+#ifdef s6_addr32
+ pep = pe->prefix.u.prefix6.s6_addr32 ;
+#else
+ pep = pe->prefix.u.prefix6.s6_addr ;
+#endif
+ l = pe->last ;
+ for (j = 0 ; j < l ; j++)
+ if (pep[j] != pp[j])
+ goto NEXT ;
+ if (((pep[l] ^ pp[l]) & pe->mask) == 0)
+ {
+ ++pe->hitcnt;
+ return pe->type ;
+ } ;
+ NEXT: ;
+ }
+ break ;
- plist = prefix_list_lookup (afi, name);
+ default:
+ zabort("invalid address family") ;
+ } ;
- if (plist == NULL)
- plist = prefix_list_insert (afi, name);
- return plist;
+ return PREFIX_DENY;
}
+/*==============================================================================
+ * Basic constructors and destructors.
+ */
-/* Delete prefix-list from prefix_list_master and free it. */
-static void
-prefix_list_delete (struct prefix_list *plist)
+/* Construct a new prefix_list and set the the associated symbol's value.
+ *
+ * Implied assert_afi_real().
+ */
+static struct prefix_list *
+prefix_list_new(struct prefix_master* pm, struct symbol* sym, afi_t afi)
{
- struct prefix_list_list *list;
- struct prefix_master *master;
- struct prefix_list_entry *pentry;
- struct prefix_list_entry *next;
+ struct prefix_list* new ;
- /* If prefix-list contain prefix_list_entry free all of it. */
- for (pentry = plist->head; pentry; pentry = next)
- {
- next = pentry->next;
- prefix_list_entry_free (pentry);
- plist->count--;
- }
-
- master = plist->master;
-
- if (plist->type == PREFIX_TYPE_NUMBER)
- list = &master->num;
- else
- list = &master->str;
+ new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list));
+ /* NB: implicitly sets the list empty. */
- if (plist->next)
- plist->next->prev = plist->prev;
- else
- list->tail = plist->prev;
+ new->sym = symbol_inc_ref(sym) ;
+ new->master = pm ;
- if (plist->prev)
- plist->prev->next = plist->next;
- else
- list->head = plist->next;
+ new->afi = afi ;
+ switch (afi)
+ {
+ case AFI_IP:
+ new->cmp = prefix_list_ipv4_cmp ;
+ break ;
+#ifdef HAVE_IPV6
+ case AFI_IP6:
+ new->cmp = prefix_list_ipv6_cmp ;
+ break ;
+#endif
+ default:
+ zabort("invalid address family") ;
+ } ;
- if (plist->desc)
- XFREE (MTYPE_TMP, plist->desc);
+ symbol_set_value(sym, new) ;
- /* Make sure master's recent changed prefix-list information is
- cleared. */
- master->recent = NULL;
+ return new ;
+} ;
- if (plist->name)
- XFREE (MTYPE_PREFIX_LIST_STR, plist->name);
-
- prefix_list_free (plist);
-
- if (master->delete_hook)
- (*master->delete_hook) (NULL);
+/* Initialise prefix_list entry -- cleared to zeros. */
+static struct prefix_list_entry *
+prefix_list_entry_init(struct prefix_list_entry * pe)
+{
+ return memset(pe, 0, sizeof(struct prefix_list_entry));
}
+/* Allocate new prefix_list entry -- cleared to zeros. */
static struct prefix_list_entry *
-prefix_list_entry_make (struct prefix *prefix, enum prefix_list_type type,
- int seq, int le, int ge, int any)
+prefix_list_entry_new (void)
{
- struct prefix_list_entry *pentry;
-
- pentry = prefix_list_entry_new ();
-
- if (any)
- pentry->any = 1;
-
- prefix_copy (&pentry->prefix, prefix);
- pentry->type = type;
- pentry->seq = seq;
- pentry->le = le;
- pentry->ge = ge;
-
- return pentry;
+ return XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry));
}
-/* Add hook function. */
-void
-prefix_list_add_hook (void (*func) (struct prefix_list *plist))
+/* Free given prefix list entry. */
+static void
+prefix_list_entry_free (struct prefix_list_entry* pe)
{
- prefix_master_ipv4.add_hook = func;
-#ifdef HAVE_IPV6
- prefix_master_ipv6.add_hook = func;
-#endif /* HAVE_IPV6 */
+ XFREE (MTYPE_PREFIX_LIST_ENTRY, pe);
}
-/* Delete hook function. */
-void
-prefix_list_delete_hook (void (*func) (struct prefix_list *plist))
+/* Set cache owned by nobody, and free the contents of the cache (if any). */
+static void
+prefix_dup_cache_free(struct prefix_master* pm)
{
- prefix_master_ipv4.delete_hook = func;
-#ifdef HAVE_IPV6
- prefix_master_ipv6.delete_hook = func;
-#endif /* HAVE_IPVt6 */
+ pm->cache_owner = NULL ;
+ vector_reset(&pm->dup_cache, 0) ;
}
-/* Calculate new sequential number. */
-static int
-prefix_new_seq_get (struct prefix_list *plist)
+/* Delete prefix_list from prefix_list_master and free it and its contents. */
+/* The prefix_list's symbol is set undefined. */
+static void
+prefix_list_delete (struct prefix_list* plist)
{
- int maxseq;
- int newseq;
- struct prefix_list_entry *pentry;
+ struct prefix_master* pm = plist->master ;
+ unsigned int i ;
+ struct prefix_list_entry* pe ;
- maxseq = newseq = 0;
+ /* Free all the prefix_list_entries, then free the vector they live in. */
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ prefix_list_entry_free(pe) ;
+ vector_reset(&plist->list, 0) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
- {
- if (maxseq < pentry->seq)
- maxseq = pentry->seq;
- }
+ /* If there is a description, release that now. */
+ if (plist->desc)
+ XFREE (MTYPE_TMP, plist->desc);
- newseq = ((maxseq / 5) * 5) + 5;
-
- return newseq;
-}
+ /* Can no longer own the dup_cache. */
+ if (pm->cache_owner == plist)
+ prefix_dup_cache_free(pm) ;
-/* Return prefix list entry which has same seq number. */
-static struct prefix_list_entry *
-prefix_seq_check (struct prefix_list *plist, int seq)
+ /* Symbol no longer has a value & drop reference. */
+ symbol_unset_value(plist->sym) ;
+ plist->sym = symbol_dec_ref(plist->sym) ;
+
+ /* Finally, release the prefix_list structure. */
+ XFREE (MTYPE_PREFIX_LIST, plist) ;
+
+ /* No longer have a recently changed prefix-list */
+ pm->recent = NULL ;
+
+ /* Tell the world. */
+ if (pm->delete_hook)
+ (*pm->delete_hook) (NULL);
+} ;
+
+/*==============================================================================
+ * Operations on prefix_lists
+ */
+
+/* Seek prefix_list by name in give prefix master. Does NOT create. */
+static struct prefix_list *
+prefix_list_seek (struct prefix_master* pm, const char *name)
{
- struct prefix_list_entry *pentry;
+ return symbol_get_value(symbol_seek(&(pm->table), name)) ;
+} ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
- if (pentry->seq == seq)
- return pentry;
- return NULL;
-}
+/* Lookup prefix_list by afi and name -- if afi is known, and name not NULL.
+ *
+ * Returns NULL if no prefix_list by that afi and name. Tolerates unknown afi
+ * and allows "fake" afi (eg. AFI_ORF_PREFIX).
+ */
+struct prefix_list *
+prefix_list_lookup (afi_t afi, const char *name)
+{
+ struct prefix_master* pm = prefix_master_get(afi) ;
-static struct prefix_list_entry *
-prefix_list_entry_lookup (struct prefix_list *plist, struct prefix *prefix,
- enum prefix_list_type type, int seq, int le, int ge)
+ if ((name == NULL) || (pm == NULL))
+ return NULL;
+
+ return prefix_list_seek(pm, name) ;
+} ;
+
+/* Get prefix_list -- creating empty one if required. */
+static struct prefix_list *
+prefix_list_get (struct prefix_master* pm, const char *name, afi_t afi)
{
- struct prefix_list_entry *pentry;
+ struct symbol* sym ;
+ struct prefix_list* plist ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
- if (prefix_same (&pentry->prefix, prefix) && pentry->type == type)
- {
- if (seq >= 0 && pentry->seq != seq)
- continue;
+ assert((pm != NULL) && (name != NULL)) ;
- if (pentry->le != le)
- continue;
- if (pentry->ge != ge)
- continue;
+ sym = symbol_find(&(pm->table), name) ; /* creates if required */
+ plist = symbol_get_value(sym) ;
- return pentry;
- }
+ return plist ? plist : prefix_list_new(pm, sym, afi) ;
+} ;
- return NULL;
-}
+/*==============================================================================
+ * Operations on prefix_list_entry
+ */
-static void
-prefix_list_entry_delete (struct prefix_list *plist,
- struct prefix_list_entry *pentry,
- int update_list)
+/* Sequence comparison function -- used in prefix_list_entry_lookup_seq */
+static int
+prefix_seq_cmp(const int** seq, const struct prefix_list_entry** pe)
{
- if (plist == NULL || pentry == NULL)
- return;
- if (pentry->prev)
- pentry->prev->next = pentry->next;
+ if (**seq != (*pe)->seq)
+ return (**seq < (*pe)->seq) ? -1 : + 1 ;
+ return 0 ;
+} ;
+
+/* Check any ge and le settings -- set defaults as required.
+ *
+ * -- sets the implied le for "any" or ge, if no explicit le set
+ *
+ * -- checks le & ge and updates as required the filter.
+ *
+ * Note that filter requires le = ge = prefix-length for an exact match.
+ *
+ * Returns: CMD_SUCCESS -- it's OK
+ * CMD_WARNING -- something amiss with the ge and/or le setting
+ *
+ * Cisco say:
+ *
+ * If ge: must be <= maximum prefix length and > actual prefix length
+ * else: set to prefix length
+ *
+ * If le: must be <= maximum prefix length and > actual prefix length
+ * else: if ge or any set to maximum prefix length
+ * else: set to prefix length
+ *
+ * If both ge and le: must have length < ge < le <= maximum
+ *
+ * But Cisco will apparently allow: length < ge <= le <= maximum
+ *
+ * We allow: length <= ge <= le <= maximum
+ *
+ * GMCH TODO: check on wisdom of all this.
+ */
+static int
+prefix_list_entry_ge_le_check(struct prefix_list_entry* pe, afi_t afi)
+{
+ int pl_max = prefix_max_length(afi) ;
+ int pl = pe->prefix.prefixlen ;
+
+ /* If we had ge, check in range, otherwise set to prefixlen. */
+ if (pe->flags & PREFIX_GE)
+ {
+ if ( !( (pl <= pe->ge) && (pe->ge <= pl_max) ) )
+ return CMD_WARNING ;
+ }
else
- plist->head = pentry->next;
- if (pentry->next)
- pentry->next->prev = pentry->prev;
+ pe->ge = pl ;
+
+ /* If we had le, check in range, otherwise set as required.
+ *
+ * Note that if had ge, then we've checked that already, otherwise
+ * we have set ge = pl -- so can check ge <= le.
+ */
+ if (pe->flags & PREFIX_LE)
+ {
+ if ( !( (pe->ge <= pe->le) && (pe->le <= pl_max) ) )
+ return CMD_WARNING ;
+ }
else
- plist->tail = pentry->prev;
+ pe->le = (pe->flags & (PREFIX_ANY | PREFIX_GE)) ? pl_max : pl ;
- prefix_list_entry_free (pentry);
+ return CMD_SUCCESS ;
+} ;
- plist->count--;
+/* Lookup prefix_list_entry by its sequence number. Returns index of an entry
+ * in the prefix_list, and sets:
+ *
+ * result < 0 -- not found. index returned is of first entry in the
+ * prefix list, and this sequence number comes
+ * before it. (Or list is empty.)
+ * result == 0 -- found. index is of the entry found.
+ * result > 0 -- not found. index returned is of the entry with the largest
+ * sequence number smaller than the given one.
+ */
+static vector_index
+prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result)
+{
+ return vector_bsearch(&plist->list, (vector_bsearch_cmp*)prefix_seq_cmp,
+ &seq, result) ;
+} ;
- if (update_list)
+/* Lookup prefix_list_entry by its contents.
+ *
+ * For large prefix_lists this uses the "dup_cache", which is an auxiliary
+ * vector, sorted by prefix value. Each prefix_master has a dup_cache,
+ * which is co-opted by the last prefix_list to use it.
+ *
+ * Returns index of an entry in the prefix_list or the dup_cache, and sets:
+ *
+ * cache -- NULL if not using dup_cache for this prefix_list.
+ * The index and result values refer to the main prefix_list.
+ *
+ * result < 0 -- not found. prefix list is empty, index == 0
+ * result == 0 -- found. index is of the prefix list entry
+ * result > 0 -- not found. prefix is not empty, index == last
+ *
+ * -- address of the cache (a vector).
+ * The index and result values refer to the *cache*. << NB
+ *
+ * result < 0 -- not found. index == 0, prefix value given is
+ * less than first entry in the *cache*
+ * result == 0 -- found. index is of the *cache* entry
+ * result > 0 -- not found. index is of entry in the *cache*
+ * with the largest prefix value less
+ * than the given prefix value.
+ *
+ * Note that the cache is never empty.
+ */
+static vector_index
+prefix_list_entry_lookup_val(struct prefix_list *plist,
+ struct prefix_list_entry* temp,
+ vector* cache,
+ int* result)
+{
+ /* See if we already have an entry like this one. */
+ if (vector_end(&plist->list) > 10)
{
- if (plist->master->delete_hook)
- (*plist->master->delete_hook) (plist);
-
- if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
- prefix_list_delete (plist);
- else
- plist->master->recent = plist;
+ struct prefix_master* pm = plist->master ;
+
+ if (pm->cache_owner != plist)
+ {
+ /* Create new cache by copying vector. Releases any old cache. */
+ vector_copy_here(&pm->dup_cache, &plist->list) ;
+ /* Sort the result so can binary chop it. */
+ vector_sort(&pm->dup_cache, (vector_sort_cmp*)plist->cmp) ;
+ /* Now we own the cache. */
+ pm->cache_owner = plist ;
+ } ;
+
+ *cache = &pm->dup_cache ;
+ return vector_bsearch(*cache, (vector_bsearch_cmp*)plist->cmp, temp,
+ result) ;
}
-}
-
-static void
-prefix_list_entry_add (struct prefix_list *plist,
- struct prefix_list_entry *pentry)
-{
- struct prefix_list_entry *replace;
- struct prefix_list_entry *point;
+ else
+ {
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ *cache = NULL ; /* Not found in cache. */
+ *result = 0 ; /* Assume found ! */
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ if (plist->cmp(&pe, &temp) == 0)
+ return i ; /* Found ! */
+ } ;
- /* Automatic asignment of seq no. */
- if (pentry->seq == -1)
- pentry->seq = prefix_new_seq_get (plist);
+ *result = (vector_end(&plist->list) == 0) ? -1 : +1 ;
+ /* Not found. */
+ return i ;
+ } ;
+} ;
- /* Is there any same seq prefix list entry? */
- replace = prefix_seq_check (plist, pentry->seq);
- if (replace)
- prefix_list_entry_delete (plist, replace, 0);
+/* Look up prefix_list_entry looking for an exact match.
+ *
+ * If we have an explicit sequence number, then we look that up and then
+ * see if that prefix_list_entry is identical.
+ *
+ * Otherwise, look for entry whose value is identical, if any.
+ *
+ * Returns an index of a prefix_list entry and sets:
+ *
+ * -- result == 0 found -- index is of entry found
+ * -- result != 0 not found -- index is immaterial
+ *
+ * */
+static vector_index
+prefix_list_entry_lookup (struct prefix_list* plist,
+ struct prefix_list_entry* pe_seek, int* result)
+{
+ struct prefix_list_entry* pe_found ;
+ vector_index i ;
- /* Check insert point. */
- for (point = plist->head; point; point = point->next)
- if (point->seq >= pentry->seq)
- break;
+ if (pe_seek->flags & PREFIX_SEQ)
+ {
+ /* Lookup by sequence number. */
+ i = prefix_list_entry_lookup_seq(plist, pe_seek->seq, result) ;
+ if (*result == 0)
+ {
+ /* found by sequence number, now see if value matches. */
+ pe_found = vector_get_item(&plist->list, i) ;
+ *result = plist->cmp(&pe_seek, &pe_found) ;
+ } ;
+ }
+ else
+ {
+ /* Lookup by value. */
+ vector cache ;
+ i = prefix_list_entry_lookup_val(plist, pe_seek, &cache, result) ;
+ if ((*result == 0) && cache)
+ {
+ /* Found in the cache. We need it's position in prefix_list */
+ pe_found = vector_get_item(cache, i) ;
+ i = prefix_list_entry_lookup_seq(plist, pe_found->seq, result) ;
+ assert(*result == 0) ; /* MUST Find it !! */
+ } ;
+ }
- /* In case of this is the first element of the list. */
- pentry->next = point;
+ return i ;
+} ;
- if (point)
+/* Insert prefix_list_entry or replace an existing one, if we can.
+ *
+ * May NOT insert or replace if an entry already exists with the same value,
+ * (where the value excludes the sequence number).
+ *
+ * Except that, if a sequence number is given, it is (trivially) possible to
+ * "replace" an entry with the same sequence number and the same value.
+ *
+ * Then, if no sequence number is given, one is allocated. The allocation
+ * rounds the last sequence number up to a multiple of 5 and adds 5.
+ *
+ * The prefix_list_entry is then put in the list by sequence number, replacing
+ * any existing entry.
+ *
+ * Returns: CMD_SUCCESS -- OK
+ * CMD_WARNING -- Nope, and NB: temp->seq set to sequence number
+ * of existing prefix_list_entry.
+ */
+static int
+prefix_list_entry_insert(struct prefix_list *plist,
+ struct prefix_list_entry *temp)
+{
+ struct prefix_list_entry* pe ;
+ vector cache ;
+ vector_index i, ic ;
+ int ret, retc ;
+ u_int32_t mask ;
+ int pl, sh ;
+
+ /* See if we have an entry like this one, if we do:
+ *
+ * OK if sequence number is the same too -- nothing more to do !
+ * Fail if sequence number differs or was not set.
+ *
+ * If not found, and we own the cache, ic and retc tell us where in the
+ * cache the new entry belongs.
+ */
+ ic = prefix_list_entry_lookup_val(plist, temp, &cache, &retc) ;
+ if (retc == 0)
{
- if (point->prev)
- point->prev->next = pentry;
- else
- plist->head = pentry;
-
- pentry->prev = point->prev;
- point->prev = pentry;
- }
+ pe = vector_get_item(cache ? cache : &plist->list, ic) ;
+ if ((temp->flags & PREFIX_SEQ) && (pe->seq == temp->seq))
+ return CMD_SUCCESS ;
+ temp->seq = pe->seq ; /* capture clashing sequence number */
+ return CMD_WARNING ;
+ } ;
+
+ /* Now we need to find where to insert in the list. */
+ /* If required, we set implied sequence number, and insert at end. */
+ if (temp->flags & PREFIX_SEQ)
+ i = prefix_list_entry_lookup_seq(plist, temp->seq, &ret) ;
else
{
- if (plist->tail)
- plist->tail->next = pentry;
+ int last_seq ;
+ i = vector_end(&plist->list) ;
+ if (i == 0)
+ {
+ last_seq = 0 ; /* initial value for empty list */
+ ret = -1 ; /* insert before first item of empty list */
+ }
else
- plist->head = pentry;
-
- pentry->prev = plist->tail;
- plist->tail = pentry;
+ {
+ --i ; /* step back to last entry */
+ pe = vector_get_item(&plist->list, i) ;
+ last_seq = pe->seq ;
+ ret = 1 ; /* insert after last entry */
+ } ;
+ temp->seq = (((last_seq + 5 - 1) / 5) * 5) + 5 ;
+ } ;
+
+ /* If we found it with same sequence number, then replace entry,
+ * otherwise we need to create a new entry and insert it. i & ret show
+ * where we need to put the value in the list.
+ *
+ * If a dup cache exists, that must be kept up to date. ic & retc show
+ * where need to put the new value in the cache.
+ */
+ if (ret == 0)
+ {
+ /* We are going to replace an existing list entry. */
+ pe = vector_get_item(&plist->list, i) ; /* address of current entry */
+ /* If we have a cache, need to move the entry to it's new place */
+ if (cache)
+ {
+ /* We need to know where the old value was. */
+ vector_index io ;
+ int reto ;
+ io = vector_bsearch(cache, (vector_bsearch_cmp*)plist->cmp, pe,
+ &reto) ;
+ assert(reto == 0) ; /* MUST find it !! */
+ vector_move_item_here(cache, ic, retc, io) ;
+ } ;
}
+ else
+ {
+ /* We are going to insert a new list entry item. */
+ pe = prefix_list_entry_new() ;
+ vector_insert_item_here(&plist->list, i, ret, pe) ;
+ /* If we have a cache, need to insert the entry to it's place */
+ if (cache)
+ vector_insert_item_here(cache, ic, retc, pe) ;
+ } ;
+
+ /* Now we can set the value of the entry. */
+ *pe = *temp ;
+
+ /* Set mask and last ready to apply the filter.
+ *
+ * pl sh mask last
+ * 0 0 0x00000000 0 case sh == 0 && pl == 0
+ * 1 1 0x80000000 0
+ * .. .. .
+ * 31 31 0xFFFFFFFE 0
+ * 32 0 0xFFFFFFFF 0 case sh == 0
+ * 33 1 0x80000000 1
+ * .. .. .
+ * 64 0 0xFFFFFFFF 1 case sh == 0
+ * 65 1 0x80000000 2
+ * .. .. .
+ * 128 0 0xFFFFFFFF 3 case sh == 0
+ *
+ * Note: if we don't have s6_addr32, we must handle IPv6 byte-wise !
+ */
+ pl = pe->prefix.prefixlen ;
+
+ sh = pl & 0x1F ;
+ if (sh == 0)
+ mask = (pl == 0) ? 0x00000000 : 0xFFFFFFFF ;
+ else
+ mask = (0xFFFFFFFF >> sh) ^ 0xFFFFFFFF ;
- /* Increment count. */
- plist->count++;
+ switch (plist->afi)
+ {
+ case AFI_IP:
+ pe->mask = htonl(mask) ;
+ pe->last = 0 ;
+ break ;
+
+ case AFI_IP6:
+#ifdef s6_addr32
+ pe->mask = htonl(mask) ;
+ pe->last = (pl == 0) ? 0 : (pl - 1) >> 5 ;
+#else
+ /* Need to shift 32 bit mask to 8 bit mask
+ *
+ * For pl == 0 mask == 0, otherwise:
+ *
+ * (pl - 1) & 0x18 -> 0 for pl = 1.. 8, 33..40, 65..72, 97..104
+ * 8 for pl = 9..16, etc
+ * (0x10) 16 for pl = 17..24, etc
+ * (0x18) 24 for pl = 25..32, etc
+ *
+ * So need to shift down by 24 - that, and then mask to byte.
+ */
+ if (pl != 0)
+ mask >>= (24 - ((pl - 1) & 0x18)) ;
+ pe->mask = mask & 0xFF ;
+ pe->last = (pl == 0) ? 0 : (pl - 1) >> 3 ;
+#endif
+ break ;
+
+ default:
+ zabort("invalid address family") ;
+ } ;
/* Run hook function. */
if (plist->master->add_hook)
(*plist->master->add_hook) (plist);
plist->master->recent = plist;
+
+ return CMD_SUCCESS ;
}
+/* Delete prefix_list_entry, if we can.
+ *
+ * To delete an entry the caller must specify the exact value of an existing
+ * entry. If a sequence number is specified, that entry must exist, and its
+ * value must exactly match the given value. If no sequence number is
+ * specified, an entry must exist with exactly the given value.
+ *
+ * Returns: CMD_SUCCESS -- OK
+ * CMD_WARNING -- entry not found.
+ */
+static int
+prefix_list_entry_delete (struct prefix_list *plist,
+ struct prefix_list_entry *pe_seek)
+{
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ int ret ;
+
+ i = prefix_list_entry_lookup (plist, pe_seek, &ret) ;
+ if (ret)
+ return CMD_WARNING ;
+
+ pe = vector_delete_item(&plist->list, i) ;
+ assert(pe != NULL) ;
+
+ prefix_list_entry_free(pe) ; /* now release memory */
+
+ if (plist->master->delete_hook)
+ (*plist->master->delete_hook) (plist);
+
+ if ((vector_end(&plist->list) == 0) && (plist->desc == NULL))
+ prefix_list_delete (plist);
+ else
+ plist->master->recent = plist;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*==============================================================================
+ * Common printing operations
+ */
+
/* Return string of prefix_list_type. */
static const char *
prefix_list_type_str (struct prefix_list_entry *pentry)
@@ -532,179 +1047,192 @@ prefix_list_type_str (struct prefix_list_entry *pentry)
}
}
-static int
-prefix_list_entry_match (struct prefix_list_entry *pentry, struct prefix *p)
+/* Map afi to name of same: "ip" or "ipv6". Implied assert_afi_real(). */
+static const char*
+prefix_afi_name_str(afi_t afi)
{
- int ret;
+ switch (afi)
+ {
+ case AFI_IP:
+ return "ip" ;
+#ifdef HAVE_IPV6
+ case AFI_IP6:
+ return "ipv6" ;
+#endif
+ default:
+ zabort("invalid address family") ;
+ return "?" ;
+ } ;
+} ;
- ret = prefix_match (&pentry->prefix, p);
- if (! ret)
- return 0;
-
- /* In case of le nor ge is specified, exact match is performed. */
- if (! pentry->le && ! pentry->ge)
- {
- if (pentry->prefix.prefixlen != p->prefixlen)
- return 0;
- }
- else
- {
- if (pentry->le)
- if (p->prefixlen > pentry->le)
- return 0;
-
- if (pentry->ge)
- if (p->prefixlen < pentry->ge)
- return 0;
- }
- return 1;
-}
+/* Print: "(ip|ipv6) prefix-list NAME" <post> */
+static void
+vty_prefix_list_name_print(struct vty* vty, struct prefix_list* plist,
+ const char* post)
+{
+ vty_out(vty, "%s prefix-list %s%s", prefix_afi_name_str(plist->afi),
+ (const char*)symbol_get_name(plist->sym), post) ;
+} ;
-enum prefix_list_type
-prefix_list_apply (struct prefix_list *plist, void *object)
+/* Print: "(ip|ipv6) prefix-list NAME: 99 entries" <post> */
+static void
+vty_prefix_list_name_count_print(struct vty* vty, struct prefix_list* plist,
+ const char* post)
{
- struct prefix_list_entry *pentry;
- struct prefix *p;
+ vty_prefix_list_name_print(vty, plist, "") ;
+ vty_out(vty, ": %d entries%s", vector_end(&plist->list), post);
+} ;
- p = (struct prefix *) object;
+/* Print: "(ip|ipv6) prefix-list NAME" UNDEFINED<post> */
+static void
+vty_prefix_list_undefined_print(struct vty* vty, afi_t afi, struct symbol* sym,
+ const char* post)
+{
+ vty_out(vty, "%s prefix-list %s UNDEFINED%s", prefix_afi_name_str(afi),
+ (const char*)symbol_get_name(sym), post) ;
+} ;
- if (plist == NULL)
- return PREFIX_DENY;
+/* Print: <indent>"Description: xxxx"<post>, if there is a description */
+static void
+vty_prefix_list_desc_print(struct vty* vty, struct prefix_list* plist,
+ int indent, const char* post)
+{
+ if (plist->desc)
+ vty_out (vty, "%sDescription: %s%s", VTY_SPACES(indent), plist->desc,
+ VTY_NEWLINE) ;
+}
+
+/* Size of buffer to hold either IPv4 or IPv6 string. */
+#ifndef INETX_ADDRSTRLEN
+# if INET_ADDRSTRLEN < INET6_ADDRSTRLEN
+# define INETX_ADDRSTRLEN INET6_ADDRSTRLEN
+# else
+# define INETX_ADDRSTRLEN INET_ADDRSTLEN
+# endif
+#endif
+
+/* Print value of given prefix_list_entry:
+ *
+ * "[seq 999 ](permit|deny) (any|XXXXX/999)[ ge 99][ le 99]"
+ * "[ '('hit count: 999, refcount: 999')']" <post>
+ *
+ * where: sequence number is included if "with_seq" specified
+ * ge and/or le are included if explicitly set
+ * the hit count and refcount are included if "with_stats" specified
+ */
+static void
+vty_prefix_list_value_print(struct vty* vty, struct prefix_list_entry* pe,
+ const char* post, int with_seq, int with_stats)
+{
+ if (with_seq)
+ vty_out(vty, "seq %d ", pe->seq) ;
- if (plist->count == 0)
- return PREFIX_PERMIT;
+ vty_out(vty, "%s ", prefix_list_type_str(pe)) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ if (pe->flags & PREFIX_ANY)
+ vty_out(vty, "any");
+ else
{
- pentry->refcnt++;
- if (prefix_list_entry_match (pentry, p))
- {
- pentry->hitcnt++;
- return pentry->type;
- }
- }
+ struct prefix *p = &pe->prefix ;
+ char buf[INETX_ADDRSTRLEN];
+ vty_out(vty, "%s/%d",
+ inet_ntop (p->family, &p->u.prefix, buf, INETX_ADDRSTRLEN),
+ p->prefixlen);
+ } ;
- return PREFIX_DENY;
+ if (pe->flags & PREFIX_GE)
+ vty_out(vty, " ge %d", pe->ge);
+ if (pe->flags & PREFIX_LE)
+ vty_out(vty, " le %d", pe->le);
+
+ if (with_stats)
+ vty_out (vty, " (hit count: %lu, refcount: %lu)", pe->hitcnt, pe->refcnt);
+
+ vty_out(vty, post) ;
}
static void __attribute__ ((unused))
prefix_list_print (struct prefix_list *plist)
{
- struct prefix_list_entry *pentry;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ struct vty* vty = NULL ;
if (plist == NULL)
return;
- printf ("ip prefix-list %s: %d entries\n", plist->name, plist->count);
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
- {
- if (pentry->any)
- printf ("any %s\n", prefix_list_type_str (pentry));
- else
- {
- struct prefix *p;
- char buf[BUFSIZ];
-
- p = &pentry->prefix;
-
- printf (" seq %d %s %s/%d",
- pentry->seq,
- prefix_list_type_str (pentry),
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
- if (pentry->ge)
- printf (" ge %d", pentry->ge);
- if (pentry->le)
- printf (" le %d", pentry->le);
- printf ("\n");
- }
- }
-}
-
-/* Retrun 1 when plist already include pentry policy. */
-static struct prefix_list_entry *
-prefix_entry_dup_check (struct prefix_list *plist,
- struct prefix_list_entry *new)
-{
- struct prefix_list_entry *pentry;
- int seq = 0;
-
- if (new->seq == -1)
- seq = prefix_new_seq_get (plist);
- else
- seq = new->seq;
+ vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
- if (prefix_same (&pentry->prefix, &new->prefix)
- && pentry->type == new->type
- && pentry->le == new->le
- && pentry->ge == new->ge
- && pentry->seq != seq)
- return pentry;
+ vty_out_indent(vty, 2) ;
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE, 1, 1) ;
}
- return NULL;
}
+/*==============================================================================
+ * vty prefix_list operations.
+ */
-static int
-vty_invalid_prefix_range (struct vty *vty, const char *prefix)
+/* Look up given prefix_list -- complain if not found. */
+static struct prefix_list*
+vty_prefix_list_lookup(struct vty *vty, afi_t afi, const char* name)
{
- vty_out (vty, "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value%s",
- prefix, VTY_NEWLINE);
- return CMD_WARNING;
-}
+ struct prefix_list* plist = prefix_list_lookup(afi, name);
+ if (plist == NULL)
+ vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+ return plist ;
+} ;
+/* Process parameters for (ip|ipv6) prefix-list and no (ip|ipv6) prefix-list
+ *
+ * Fills in the given prefix_list_entry structure, ready for looking up,
+ * inserting or deleting prefix_list_entry.
+ *
+ * Checks parameters for validity/legality.
+ *
+ * Returns a CMD_xxxx return code. CMD_SUCCESS => OK !
+ */
static int
-vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
- const char *seq, const char *typestr,
- const char *prefix, const char *ge, const char *le)
+vty_prefix_list_process(struct vty *vty, struct prefix_list_entry* pe,
+ struct prefix_list* plist,
+ afi_t afi, const char *seq_str, const char *type_str,
+ const char *prefix_str,
+ const char *ge_str, const char *le_str)
{
- int ret;
- enum prefix_list_type type;
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix_list_entry *dup;
- struct prefix p;
- int any = 0;
- int seqnum = -1;
- int lenum = 0;
- int genum = 0;
+ int ret ;
- /* Sequential number. */
- if (seq)
- seqnum = atoi (seq);
+ assert_afi_real(afi) ; /* require real (and supported) afi */
- /* ge and le number */
- if (ge)
- genum = atoi (ge);
- if (le)
- lenum = atoi (le);
+ prefix_list_entry_init(pe) ; /* clears everything, including flags */
+
+ /* Sequence number. */
+ if (seq_str)
+ {
+ pe->flags |= PREFIX_SEQ ;
+ pe->seq = atoi(seq_str) ;
+ } ;
/* Check filter type. */
- if (strncmp ("permit", typestr, 1) == 0)
- type = PREFIX_PERMIT;
- else if (strncmp ("deny", typestr, 1) == 0)
- type = PREFIX_DENY;
+ if (strncmp ("permit", type_str, 1) == 0)
+ pe->type = PREFIX_PERMIT;
+ else if (strncmp ("deny", type_str, 1) == 0)
+ pe->type = PREFIX_DENY;
else
{
vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE);
return CMD_WARNING;
}
- /* "any" is special token for matching any IPv4 addresses. */
+ /* Watch out for "any" */
+ if (strncmp ("any", prefix_str, strlen (prefix_str)) == 0)
+ pe->flags |= PREFIX_ANY ;
+
+ /* Process the prefix. */
if (afi == AFI_IP)
{
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p);
- genum = 0;
- lenum = IPV4_MAX_BITLEN;
- any = 1;
- }
- else
- ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p);
-
+ if (pe->flags & PREFIX_ANY)
+ prefix_str = "0.0.0.0/0" ;
+ ret = str2prefix_ipv4 (prefix_str, (struct prefix_ipv4 *)&pe->prefix);
if (ret <= 0)
{
vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE);
@@ -714,16 +1242,9 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
#ifdef HAVE_IPV6
else if (afi == AFI_IP6)
{
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p);
- genum = 0;
- lenum = IPV6_MAX_BITLEN;
- any = 1;
- }
- else
- ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p);
-
+ if (pe->flags & PREFIX_ANY)
+ prefix_str = "::/0" ;
+ ret = str2prefix_ipv6 (prefix_str, (struct prefix_ipv6 *)&pe->prefix);
if (ret <= 0)
{
vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE);
@@ -732,155 +1253,147 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
}
#endif /* HAVE_IPV6 */
- /* ge and le check. */
- if (genum && genum <= p.prefixlen)
- return vty_invalid_prefix_range (vty, prefix);
+ /* ge and le number */
+ if (ge_str)
+ {
+ pe->ge = atoi (ge_str) ;
+ pe->flags |= PREFIX_GE ;
+ }
- if (lenum && lenum <= p.prefixlen)
- return vty_invalid_prefix_range (vty, prefix);
+ if (le_str)
+ {
+ pe->le = atoi (le_str);
+ pe->flags |= PREFIX_LE ;
+ } ;
- if (lenum && genum > lenum)
- return vty_invalid_prefix_range (vty, prefix);
+ /* Complete the entry we've constructed, and check ge and le. */
+ ret = prefix_list_entry_ge_le_check(pe, afi) ;
- if (genum && lenum == (afi == AFI_IP ? 32 : 128))
- lenum = 0;
+ if (ret != CMD_SUCCESS)
+ vty_out (vty, "%% Invalid prefix range for %s, make sure: "
+ "len <= ge-value <= le-value%s",
+ prefix_str, VTY_NEWLINE);
- /* Get prefix_list with name. */
- plist = prefix_list_get (afi, name);
+ return ret ;
+} ;
- /* Make prefix entry. */
- pentry = prefix_list_entry_make (&p, type, seqnum, lenum, genum, any);
-
- /* Check same policy. */
- dup = prefix_entry_dup_check (plist, pentry);
+/* Install a prefix_list_entry.
+ *
+ * Deals with all of ip prefix-list and ipv6 prefix-list commands.
+ *
+ * Note:
+ *
+ */
- if (dup)
+static int
+vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
+ const char *seq_str, const char *type_str,
+ const char *prefix_str,
+ const char *ge_str, const char *le_str)
+{
+ struct prefix_master* pm ;
+ struct prefix_list *plist;
+ struct prefix_list_entry temp ;
+ int ret;
+
+ assert_afi_real(afi) ; /* UI stuff should ensure this */
+ pm = prefix_master_get(afi) ;
+
+ /* Get prefix_list with name. Make new list if required. */
+ plist = prefix_list_get(pm, name, afi) ;
+
+ /* Do the grunt work on the parameters.
+ * Completely fill in the temp prefix_list_entry structure.
+ */
+ ret = vty_prefix_list_process(vty, &temp, plist, afi, seq_str, type_str,
+ prefix_str, ge_str, le_str) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+
+ /* Insert into the list, unless list contains an entry which is the same
+ * apart from the sequence number.
+ * If fails, sets the sequence no. in temp to the sequence number found.
+ */
+ ret = prefix_list_entry_insert(plist, &temp);
+
+ if (ret != CMD_SUCCESS)
{
- prefix_list_entry_free (pentry);
vty_out (vty, "%% Insertion failed - prefix-list entry exists:%s",
VTY_NEWLINE);
- vty_out (vty, " seq %d %s %s", dup->seq, typestr, prefix);
- if (! any && genum)
- vty_out (vty, " ge %d", genum);
- if (! any && lenum)
- vty_out (vty, " le %d", lenum);
- vty_out (vty, "%s", VTY_NEWLINE);
- return CMD_WARNING;
+ vty_out_indent(vty, 2) ;
+ vty_prefix_list_value_print(vty, &temp, VTY_NEWLINE, 1, 0) ;
}
- /* Install new filter to the access_list. */
- prefix_list_entry_add (plist, pentry);
-
return CMD_SUCCESS;
}
+/* Remove a prefix_list_entry. */
static int
-vty_prefix_list_uninstall (struct vty *vty, afi_t afi, const char *name,
- const char *seq, const char *typestr,
- const char *prefix, const char *ge, const char *le)
+vty_prefix_list_uninstall(struct vty *vty, afi_t afi, const char *name,
+ const char *seq_str, const char *type_str,
+ const char *prefix_str,
+ const char *ge_str, const char *le_str)
{
- int ret;
- enum prefix_list_type type;
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix p;
- int seqnum = -1;
- int lenum = 0;
- int genum = 0;
+ struct prefix_list_entry temp ;
+ int ret;
- /* Check prefix list name. */
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ assert_afi_real(afi) ; /* UI should guarantee this. */
+
+ /* Seek prefix_list with name -- error if not found. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING ;
/* Only prefix-list name specified, delete the entire prefix-list. */
- if (seq == NULL && typestr == NULL && prefix == NULL &&
- ge == NULL && le == NULL)
+ if (seq_str == NULL && type_str == NULL && prefix_str == NULL &&
+ ge_str == NULL && le_str == NULL)
{
prefix_list_delete (plist);
return CMD_SUCCESS;
}
/* We must have, at a minimum, both the type and prefix here */
- if ((typestr == NULL) || (prefix == NULL))
+ if ((type_str == NULL) || (prefix_str == NULL))
{
vty_out (vty, "%% Both prefix and type required%s", VTY_NEWLINE);
return CMD_WARNING;
}
- /* Check sequence number. */
- if (seq)
- seqnum = atoi (seq);
+ /* Do the grunt work on the parameters.
+ * Completely fill in the temp prefix_list_entry structure.
+ */
+ ret = vty_prefix_list_process(vty, &temp, plist, afi, seq_str, type_str,
+ prefix_str, ge_str, le_str) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- /* ge and le number */
- if (ge)
- genum = atoi (ge);
- if (le)
- lenum = atoi (le);
-
- /* Check of filter type. */
- if (strncmp ("permit", typestr, 1) == 0)
- type = PREFIX_PERMIT;
- else if (strncmp ("deny", typestr, 1) == 0)
- type = PREFIX_DENY;
- else
- {
- vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Remove prefix_list_entry if we can. */
+ ret = prefix_list_entry_delete (plist, &temp);
- /* "any" is special token for matching any IPv4 addresses. */
- if (afi == AFI_IP)
- {
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p);
- genum = 0;
- lenum = IPV4_MAX_BITLEN;
- }
- else
- ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p);
+ if (ret != CMD_SUCCESS)
+ vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- if (ret <= 0)
- {
- vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-#ifdef HAVE_IPV6
- else if (afi == AFI_IP6)
- {
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p);
- genum = 0;
- lenum = IPV6_MAX_BITLEN;
- }
- else
- ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p);
+ return ret;
+}
- if (ret <= 0)
- {
- vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-#endif /* HAVE_IPV6 */
+static int
+vty_prefix_list_desc_set (struct vty *vty, afi_t afi, const char *name,
+ char* desc)
+{
+ struct prefix_master* pm ;
+ struct prefix_list *plist;
- /* Lookup prefix entry. */
- pentry = prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum);
+ assert_afi_real(afi) ; /* UI stuff should ensure this */
+ pm = prefix_master_get(afi) ;
- if (pentry == NULL)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Get prefix_list with name. Make new list if required. */
+ plist = prefix_list_get(pm, name, afi) ;
- /* Install new filter to the access_list. */
- prefix_list_entry_delete (plist, pentry, 1);
+ if (plist->desc)
+ XFREE (MTYPE_TMP, plist->desc) ; /* Discard any existing value */
+
+ plist->desc = desc ;
return CMD_SUCCESS;
}
@@ -890,21 +1403,15 @@ vty_prefix_list_desc_unset (struct vty *vty, afi_t afi, const char *name)
{
struct prefix_list *plist;
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING ;
if (plist->desc)
- {
- XFREE (MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
+ XFREE (MTYPE_TMP, plist->desc) ; /* sets plist->dec to NULL */
- if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
- prefix_list_delete (plist);
+ if (vector_end(&plist->list) == 0)
+ prefix_list_delete(plist) ; /* delete list if all gone now */
return CMD_SUCCESS;
}
@@ -916,143 +1423,135 @@ enum display_type
detail_display,
sequential_display,
longer_display,
- first_match_display
+ first_match_display,
};
+/* Show given prefix_list */
static void
-vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist,
- struct prefix_master *master, enum display_type dtype,
+vty_show_prefix_entry (struct vty *vty, struct prefix_list *plist,
+ struct prefix_master* pm, enum display_type dtype,
int seqnum)
{
- struct prefix_list_entry *pentry;
-
/* Print the name of the protocol */
if (zlog_default)
- vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]);
-
+ vty_out (vty, "%s: ", zlog_get_proto_name(NULL));
+
if (dtype == normal_display)
{
- vty_out (vty, "ip%s prefix-list %s: %d entries%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->count, VTY_NEWLINE);
- if (plist->desc)
- vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE);
+ vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
+ vty_prefix_list_desc_print(vty, plist, 3, VTY_NEWLINE) ;
}
else if (dtype == summary_display || dtype == detail_display)
{
- vty_out (vty, "ip%s prefix-list %s:%s",
- afi == AFI_IP ? "" : "v6", plist->name, VTY_NEWLINE);
+ struct prefix_list_entry* p_f = vector_get_first_item(&plist->list) ;
+ struct prefix_list_entry* p_l = vector_get_last_item(&plist->list) ;
+
+ vty_prefix_list_name_print(vty, plist, ":") ;
+ vty_out(vty, VTY_NEWLINE) ;
- if (plist->desc)
- vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE);
+ vty_prefix_list_desc_print(vty, plist, 3, VTY_NEWLINE) ;
vty_out (vty, " count: %d, range entries: %d, sequences: %d - %d%s",
- plist->count, plist->rangecount,
- plist->head ? plist->head->seq : 0,
- plist->tail ? plist->tail->seq : 0,
+ vector_end(&plist->list), plist->rangecount,
+ p_f ? p_f->seq : 0,
+ p_l ? p_l->seq : 0,
VTY_NEWLINE);
- }
+ } ;
if (dtype != summary_display)
{
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ int with_seq = pm->seqnum_flag ;
+ int with_stats = (dtype == detail_display)
+ ||(dtype == sequential_display) ;
+
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
- if (dtype == sequential_display && pentry->seq != seqnum)
+ if ((dtype == sequential_display) && (pe->seq != seqnum))
continue;
-
- vty_out (vty, " ");
- if (master->seqnum)
- vty_out (vty, "seq %d ", pentry->seq);
-
- vty_out (vty, "%s ", prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, "any");
- else
- {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, "%s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
- }
-
- if (dtype == detail_display || dtype == sequential_display)
- vty_out (vty, " (hit count: %ld, refcount: %ld)",
- pentry->hitcnt, pentry->refcnt);
-
- vty_out (vty, "%s", VTY_NEWLINE);
+ vty_out_indent(vty, 3);
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE,
+ with_seq, with_stats) ;
}
}
}
+/* Show given prefix list in given afi, or all prefix lists in given afi. */
+
static int
vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name,
- const char *seq, enum display_type dtype)
+ const char *seq_str, enum display_type dtype)
{
struct prefix_list *plist;
- struct prefix_master *master;
- int seqnum = 0;
+ struct prefix_master *pm;
+ int seq = 0;
- master = prefix_master_get (afi);
- if (master == NULL)
+ pm = prefix_master_get(afi) ;
+ if (pm == NULL)
return CMD_WARNING;
- if (seq)
- seqnum = atoi (seq);
+ if (seq_str)
+ seq = atoi (seq_str);
if (name)
{
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+ /* Note that asking after an undefined prefix_list is an error. */
+ /* Does not mention references to an undefined prefix_list. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING;
+ vty_show_prefix_entry (vty, plist, pm, dtype, seq);
}
else
{
+ vector extract ;
+ vector_index i ;
+ struct symbol* sym ;
+
if (dtype == detail_display || dtype == summary_display)
{
- if (master->recent)
+ if (pm->recent)
vty_out (vty, "Prefix-list with the last deletion/insertion: %s%s",
- master->recent->name, VTY_NEWLINE);
+ (const char*)symbol_get_name(pm->recent->sym), VTY_NEWLINE) ;
}
- for (plist = master->num.head; plist; plist = plist->next)
- vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+ /* Extract a vector of all prefix_list symbols, in name order. */
+ extract = symbol_table_extract(&pm->table, NULL, NULL, 0,
+ symbol_mixed_name_cmp) ;
+
+ for (VECTOR_ITEMS(extract, sym, i))
+ {
+ plist = symbol_get_value(sym) ;
+ if (plist != NULL)
+ vty_show_prefix_entry(vty, plist, pm, dtype, seq);
+ else
+ vty_prefix_list_undefined_print(vty, afi, sym, VTY_NEWLINE) ;
+ }
- for (plist = master->str.head; plist; plist = plist->next)
- vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+ vector_free(extract) ; /* throw away temporary vector */
}
return CMD_SUCCESS;
}
static int
-vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
+vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
const char *prefix, enum display_type type)
{
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
struct prefix p;
int ret;
int match;
+ int with_stats ;
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Error if cannot find prefix list. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING;
ret = str2prefix (prefix, &p);
if (ret <= 0)
@@ -1061,88 +1560,71 @@ vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
return CMD_WARNING;
}
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ with_stats = (type == normal_display) || (type == first_match_display) ;
+
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
match = 0;
- if (type == normal_display || type == first_match_display)
- if (prefix_same (&p, &pentry->prefix))
- match = 1;
-
- if (type == longer_display)
- if (prefix_match (&p, &pentry->prefix))
- match = 1;
+ if ((type == normal_display || type == first_match_display))
+ match = prefix_same(&p, &pe->prefix) ;
+ else if (type == longer_display)
+ match = (prefix_match (&p, &pe->prefix)) ;
if (match)
{
- vty_out (vty, " seq %d %s ",
- pentry->seq,
- prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, "any");
- else
- {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, "%s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
- }
-
- if (type == normal_display || type == first_match_display)
- vty_out (vty, " (hit count: %ld, refcount: %ld)",
- pentry->hitcnt, pentry->refcnt);
-
- vty_out (vty, "%s", VTY_NEWLINE);
+ vty_out_indent(vty, 3);
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE, 1, with_stats) ;
if (type == first_match_display)
- return CMD_SUCCESS;
+ break ;
}
}
return CMD_SUCCESS;
}
+/* Clear hit counters in all prefix_list_entries:
+ *
+ * a) in all prefix_lists -- name NULL
+ * b) in given prefix list -- prefix NULL
+ * c) that match given prefix, in given prefix_list
+ */
static int
-vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
+vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
const char *prefix)
{
- struct prefix_master *master;
+ struct prefix_master *pm;
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
int ret;
struct prefix p;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
- master = prefix_master_get (afi);
- if (master == NULL)
+ pm = prefix_master_get (afi);
+ if (pm == NULL)
return CMD_WARNING;
- if (name == NULL && prefix == NULL)
+ if (name == NULL)
{
- for (plist = master->num.head; plist; plist = plist->next)
- for (pentry = plist->head; pentry; pentry = pentry->next)
- pentry->hitcnt = 0;
-
- for (plist = master->str.head; plist; plist = plist->next)
- for (pentry = plist->head; pentry; pentry = pentry->next)
- pentry->hitcnt = 0;
+ struct symbol_walker walker ;
+ symbol_walk_start(&pm->table, &walker) ;
+ while ((plist = symbol_get_value(symbol_walk_next(&walker))))
+ {
+ if (plist == NULL)
+ continue ;
+
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ pe->hitcnt = 0 ;
+ } ;
}
else
{
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Error if cannot find prefix list. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING;
- if (prefix)
+ if (prefix != NULL)
{
ret = str2prefix (prefix, &p);
if (ret <= 0)
@@ -1152,20 +1634,13 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
}
}
- for (pentry = plist->head; pentry; pentry = pentry->next)
- {
- if (prefix)
- {
- if (prefix_match (&pentry->prefix, &p))
- pentry->hitcnt = 0;
- }
- else
- pentry->hitcnt = 0;
- }
- }
- return CMD_SUCCESS;
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ if ((prefix == NULL) || prefix_match(&pe->prefix, &p))
+ pe->hitcnt = 0;
+ } ;
+return CMD_SUCCESS;
}
-
+
DEFUN (ip_prefix_list,
ip_prefix_list_cmd,
"ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)",
@@ -1177,7 +1652,7 @@ DEFUN (ip_prefix_list,
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
"Any prefix match. Same as \"0.0.0.0/0 le 32\"\n")
{
- return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL,
+ return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL,
argv[1], argv[2], NULL, NULL);
}
@@ -1193,7 +1668,7 @@ DEFUN (ip_prefix_list_ge,
"Minimum prefix length to be matched\n"
"Minimum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
argv[2], argv[3], NULL);
}
@@ -1211,7 +1686,7 @@ DEFUN (ip_prefix_list_ge_le,
"Maximum prefix length to be matched\n"
"Maximum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
argv[2], argv[3], argv[4]);
}
@@ -1547,7 +2022,7 @@ DEFUN (ip_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv4.seqnum = 1;
+ prefix_master_ipv4.seqnum_flag = 1;
return CMD_SUCCESS;
}
@@ -1559,7 +2034,7 @@ DEFUN (no_ip_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv4.seqnum = 0;
+ prefix_master_ipv4.seqnum_flag = 0;
return CMD_SUCCESS;
}
@@ -1572,19 +2047,9 @@ DEFUN (ip_prefix_list_description,
"Prefix-list specific description\n"
"Up to 80 characters describing this prefix-list\n")
{
- struct prefix_list *plist;
-
- plist = prefix_list_get (AFI_IP, argv[0]);
-
- if (plist->desc)
- {
- XFREE (MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
- plist->desc = argv_concat(argv, argc, 1);
-
- return CMD_SUCCESS;
-}
+ return vty_prefix_list_desc_set (vty, AFI_IP, argv[0],
+ argv_concat(argv, argc, 1));
+} ;
DEFUN (no_ip_prefix_list_description,
no_ip_prefix_list_description_cmd,
@@ -1759,7 +2224,7 @@ DEFUN (clear_ip_prefix_list_name_prefix,
{
return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]);
}
-
+
#ifdef HAVE_IPV6
DEFUN (ipv6_prefix_list,
ipv6_prefix_list_cmd,
@@ -1772,7 +2237,7 @@ DEFUN (ipv6_prefix_list,
"IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
"Any prefix match. Same as \"::0/0 le 128\"\n")
{
- return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL,
+ return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL,
argv[1], argv[2], NULL, NULL);
}
@@ -1788,7 +2253,7 @@ DEFUN (ipv6_prefix_list_ge,
"Minimum prefix length to be matched\n"
"Minimum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
argv[2], argv[3], NULL);
}
@@ -1807,7 +2272,7 @@ DEFUN (ipv6_prefix_list_ge_le,
"Maximum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
argv[2], argv[3], argv[4]);
}
@@ -2143,7 +2608,7 @@ DEFUN (ipv6_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv6.seqnum = 1;
+ prefix_master_ipv6.seqnum_flag = 1;
return CMD_SUCCESS;
}
@@ -2155,7 +2620,7 @@ DEFUN (no_ipv6_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv6.seqnum = 0;
+ prefix_master_ipv6.seqnum_flag = 0;
return CMD_SUCCESS;
}
@@ -2168,19 +2633,9 @@ DEFUN (ipv6_prefix_list_description,
"Prefix-list specific description\n"
"Up to 80 characters describing this prefix-list\n")
{
- struct prefix_list *plist;
-
- plist = prefix_list_get (AFI_IP6, argv[0]);
-
- if (plist->desc)
- {
- XFREE (MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
- plist->desc = argv_concat(argv, argc, 1);
-
- return CMD_SUCCESS;
-}
+ return vty_prefix_list_desc_set (vty, AFI_IP6, argv[0],
+ argv_concat(argv, argc, 1));
+}
DEFUN (no_ipv6_prefix_list_description,
no_ipv6_prefix_list_description_cmd,
@@ -2355,190 +2810,162 @@ DEFUN (clear_ipv6_prefix_list_name_prefix,
return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]);
}
#endif /* HAVE_IPV6 */
-
+
/* Configuration write function. */
static int
config_write_prefix_afi (afi_t afi, struct vty *vty)
{
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix_master *master;
+ struct prefix_list_entry *pe;
+ struct prefix_master *pm;
int write = 0;
+ vector extract ;
+ vector_index i, ipe ;
+ struct symbol* sym ;
- master = prefix_master_get (afi);
- if (master == NULL)
+ pm = prefix_master_get (afi);
+ if (pm == NULL)
return 0;
- if (! master->seqnum)
+ if (! pm->seqnum_flag)
{
- vty_out (vty, "no ip%s prefix-list sequence-number%s",
+ vty_out (vty, "no ip%s prefix-list sequence-number%s",
afi == AFI_IP ? "" : "v6", VTY_NEWLINE);
vty_out (vty, "!%s", VTY_NEWLINE);
}
- for (plist = master->num.head; plist; plist = plist->next)
+ /* Extract a vector of all prefix_list symbols, in name order. */
+ extract = symbol_table_extract(&pm->table, NULL, NULL, 0,
+ symbol_mixed_name_cmp) ;
+ for (VECTOR_ITEMS(extract, sym, i))
{
- if (plist->desc)
- {
- vty_out (vty, "ip%s prefix-list %s description %s%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->desc, VTY_NEWLINE);
- write++;
- }
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ plist = symbol_get_value(sym) ;
+ if (plist)
{
- vty_out (vty, "ip%s prefix-list %s ",
- afi == AFI_IP ? "" : "v6",
- plist->name);
-
- if (master->seqnum)
- vty_out (vty, "seq %d ", pentry->seq);
-
- vty_out (vty, "%s ", prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, "any");
- else
+ if (plist->desc)
{
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, "%s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
+ vty_prefix_list_name_print(vty, plist, "") ;
+ vty_out (vty, " description %s%s", plist->desc, VTY_NEWLINE) ;
+ write++ ;
}
- vty_out (vty, "%s", VTY_NEWLINE);
- write++;
- }
- /* vty_out (vty, "!%s", VTY_NEWLINE); */
- }
- for (plist = master->str.head; plist; plist = plist->next)
- {
- if (plist->desc)
- {
- vty_out (vty, "ip%s prefix-list %s description %s%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->desc, VTY_NEWLINE);
- write++;
+ for (VECTOR_ITEMS(&plist->list, pe, ipe))
+ {
+ vty_prefix_list_name_print(vty, plist, " ") ;
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE,
+ pm->seqnum_flag, 0) ;
+ write++ ;
+ }
}
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ else
{
- vty_out (vty, "ip%s prefix-list %s ",
- afi == AFI_IP ? "" : "v6",
- plist->name);
-
- if (master->seqnum)
- vty_out (vty, "seq %d ", pentry->seq);
-
- vty_out (vty, "%s", prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, " any");
- else
- {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
+ vty_out(vty, "!! ") ;
+ vty_prefix_list_undefined_print(vty, afi, sym, VTY_NEWLINE) ;
+ write++ ;
+ } ;
+ } ;
- vty_out (vty, " %s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
+ vector_free(extract) ; /* discard temporary vector */
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
- }
- vty_out (vty, "%s", VTY_NEWLINE);
- write++;
- }
- }
-
return write;
}
struct stream *
-prefix_bgp_orf_entry (struct stream *s, struct prefix_list *plist,
+prefix_bgp_orf_entry (struct stream *s, prefix_list_ref ref,
u_char init_flag, u_char permit_flag, u_char deny_flag)
{
- struct prefix_list_entry *pentry;
+ struct prefix_list_entry *pe;
+ vector_index i ;
+
+ struct prefix_list *plist = prefix_list_ref_plist(ref) ;
if (! plist)
return s;
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
- u_char flag = init_flag;
- struct prefix *p = &pentry->prefix;
-
- flag |= (pentry->type == PREFIX_PERMIT ?
- permit_flag : deny_flag);
- stream_putc (s, flag);
- stream_putl (s, (u_int32_t)pentry->seq);
- stream_putc (s, (u_char)pentry->ge);
- stream_putc (s, (u_char)pentry->le);
- stream_put_prefix (s, p);
+ stream_putc (s, init_flag | (pe->type == PREFIX_PERMIT ? permit_flag
+ : deny_flag));
+ stream_putl (s, (u_int32_t)pe->seq);
+ stream_putc (s, (u_char)pe->ge);
+ stream_putc (s, (u_char)pe->le);
+ stream_put_prefix (s, &pe->prefix);
}
return s;
}
+/* Get the i'th BGP ORF prefix from the given list.
+ * return 1 - got ORF prefix.
+ * return 0 - no such entry
+ */
+int
+prefix_bgp_orf_get(struct prefix_list *plist, vector_index i,
+ struct orf_prefix *orfpe, enum prefix_list_type *pe_type)
+{
+ struct prefix_list_entry *pe = NULL;
+
+ if (!plist || i >= plist->list.end)
+ return 0;
+
+ pe = vector_slot(&plist->list, i);
+ orfpe->seq = pe->seq;
+ orfpe->ge = pe->ge;
+ orfpe->le = pe->le;
+ orfpe->p = pe->prefix;
+
+ *pe_type = pe->type;
+
+ return 1;
+}
+
+/* Set or Unset a BGP ORF entry. */
int
prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp,
int permit, int set)
{
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
-
- /* ge and le value check */
- if (orfp->ge && orfp->ge <= orfp->p.prefixlen)
- return CMD_WARNING;
- if (orfp->le && orfp->le <= orfp->p.prefixlen)
- return CMD_WARNING;
- if (orfp->le && orfp->ge > orfp->le)
- return CMD_WARNING;
+ struct prefix_list *plist ;
+ struct prefix_list_entry temp ;
+ int ret ;
- if (orfp->ge && orfp->le == (afi == AFI_IP ? 32 : 128))
- orfp->le = 0;
+ assert_afi_real(afi) ;
- plist = prefix_list_get (AFI_ORF_PREFIX, name);
- if (! plist)
- return CMD_WARNING;
+ /* Transfer the values from the orf_prefix */
+ prefix_list_entry_init(&temp) ;
- if (set)
+ temp.type = permit ? PREFIX_PERMIT : PREFIX_DENY ;
+ temp.prefix = orfp->p ;
+ temp.seq = orfp->seq ; /* NB: U32 and may be zero */
+ if (orfp->ge)
+ {
+ temp.flags |= PREFIX_GE ;
+ temp.ge = orfp->ge ;
+ }
+ if (orfp->le)
{
- pentry = prefix_list_entry_make (&orfp->p,
- (permit ? PREFIX_PERMIT : PREFIX_DENY),
- orfp->seq, orfp->le, orfp->ge, 0);
+ temp.flags |= PREFIX_LE ;
+ temp.le = orfp->le ;
+ }
- if (prefix_entry_dup_check (plist, pentry))
- {
- prefix_list_entry_free (pentry);
- return CMD_WARNING;
- }
+ /* Make sure ge & le are acceptable and set as required */
+ ret = prefix_list_entry_ge_le_check(&temp, afi) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- prefix_list_entry_add (plist, pentry);
+ /* Now insert or delete */
+ if (set)
+ {
+ plist = prefix_list_get(&prefix_master_orf, name, afi);
+ return prefix_list_entry_insert(plist, &temp) ;
}
else
{
- pentry = prefix_list_entry_lookup (plist, &orfp->p,
- (permit ? PREFIX_PERMIT : PREFIX_DENY),
- orfp->seq, orfp->le, orfp->ge);
+ plist = prefix_list_seek(&prefix_master_orf, name) ;
+ if (plist == NULL)
+ return CMD_WARNING ;
- if (! pentry)
- return CMD_WARNING;
-
- prefix_list_entry_delete (plist, pentry, 1);
+ return prefix_list_entry_delete(plist, &temp) ;
}
-
- return CMD_SUCCESS;
}
void
@@ -2555,70 +2982,33 @@ prefix_bgp_orf_remove_all (char *name)
int
prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name)
{
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
+ struct prefix_list *plist ;
plist = prefix_list_lookup (AFI_ORF_PREFIX, name);
if (! plist)
return 0;
- if (! vty)
- return plist->count;
-
- vty_out (vty, "ip%s prefix-list %s: %d entries%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->count, VTY_NEWLINE);
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ if (vty)
{
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, " seq %d %s %s/%d", pentry->seq,
- prefix_list_type_str (pentry),
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
+ struct prefix_list_entry* pe ;
+ vector_index i ;
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
+ vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
- vty_out (vty, "%s", VTY_NEWLINE);
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ vty_out_indent(vty, 3) ;
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE, 1, 1) ;
+ }
}
- return plist->count;
+
+ return vector_end(&plist->list);
}
static void
prefix_list_reset_orf (void)
{
- struct prefix_list *plist;
- struct prefix_list *next;
- struct prefix_master *master;
-
- master = prefix_master_get (AFI_ORF_PREFIX);
- if (master == NULL)
- return;
-
- for (plist = master->num.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
- for (plist = master->str.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
-
- assert (master->num.head == NULL);
- assert (master->num.tail == NULL);
-
- assert (master->str.head == NULL);
- assert (master->str.tail == NULL);
-
- master->seqnum = 1;
- master->recent = NULL;
+ prefix_master_reset(&prefix_master_orf) ;
}
@@ -2639,38 +3029,14 @@ config_write_prefix_ipv4 (struct vty *vty)
static void
prefix_list_reset_ipv4 (void)
{
- struct prefix_list *plist;
- struct prefix_list *next;
- struct prefix_master *master;
-
- master = prefix_master_get (AFI_IP);
- if (master == NULL)
- return;
-
- for (plist = master->num.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
- for (plist = master->str.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
-
- assert (master->num.head == NULL);
- assert (master->num.tail == NULL);
-
- assert (master->str.head == NULL);
- assert (master->str.tail == NULL);
-
- master->seqnum = 1;
- master->recent = NULL;
+ prefix_master_reset(&prefix_master_ipv4) ;
}
static void
prefix_list_init_ipv4 (void)
{
+ prefix_master_init(&prefix_master_ipv4) ;
+
install_node (&prefix_node, config_write_prefix_ipv4);
install_element (CONFIG_NODE, &ip_prefix_list_cmd);
@@ -2734,10 +3100,10 @@ prefix_list_init_ipv4 (void)
/* Prefix-list node. */
static struct cmd_node prefix_ipv6_node =
{
- PREFIX_IPV6_NODE,
- "", /* Prefix list has no interface. */
- 1
-};
+ PREFIX_IPV6_NODE,
+ "", /* Prefix list has no interface. */
+ 1
+ };
static int
config_write_prefix_ipv6 (struct vty *vty)
@@ -2748,38 +3114,16 @@ config_write_prefix_ipv6 (struct vty *vty)
static void
prefix_list_reset_ipv6 (void)
{
- struct prefix_list *plist;
- struct prefix_list *next;
- struct prefix_master *master;
-
- master = prefix_master_get (AFI_IP6);
- if (master == NULL)
- return;
-
- for (plist = master->num.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
- for (plist = master->str.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
-
- assert (master->num.head == NULL);
- assert (master->num.tail == NULL);
-
- assert (master->str.head == NULL);
- assert (master->str.tail == NULL);
-
- master->seqnum = 1;
- master->recent = NULL;
-}
+#ifdef HAVE_IPV6
+ prefix_master_reset(&prefix_master_ipv6) ;
+#endif
+} ;
static void
prefix_list_init_ipv6 (void)
{
+ prefix_master_init(&prefix_master_ipv6) ;
+
install_node (&prefix_ipv6_node, config_write_prefix_ipv6);
install_element (CONFIG_NODE, &ipv6_prefix_list_cmd);
@@ -2847,6 +3191,7 @@ prefix_list_init ()
#ifdef HAVE_IPV6
prefix_list_init_ipv6 ();
#endif /* HAVE_IPV6 */
+ prefix_master_init(&prefix_master_orf) ;
}
void
diff --git a/lib/plist.h b/lib/plist.h
index fb3168a6..4f383417 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -23,38 +23,22 @@
#ifndef _QUAGGA_PLIST_H
#define _QUAGGA_PLIST_H
+#include "prefix.h"
+#include "symtab.h"
+#include "vector.h"
+#include "vty.h"
+
#define AFI_ORF_PREFIX 65535
-enum prefix_list_type
+enum prefix_list_type
{
PREFIX_DENY,
PREFIX_PERMIT,
};
-enum prefix_name_type
-{
- PREFIX_TYPE_STRING,
- PREFIX_TYPE_NUMBER
-};
-
-struct prefix_list
-{
- char *name;
- char *desc;
-
- struct prefix_master *master;
-
- enum prefix_name_type type;
+struct prefix_list ;
- int count;
- int rangecount;
-
- struct prefix_list_entry *head;
- struct prefix_list_entry *tail;
-
- struct prefix_list *next;
- struct prefix_list *prev;
-};
+typedef struct symbol_ref* prefix_list_ref ;
struct orf_prefix
{
@@ -73,11 +57,25 @@ extern void prefix_list_delete_hook (void (*func) (struct prefix_list *));
extern struct prefix_list *prefix_list_lookup (afi_t, const char *);
extern enum prefix_list_type prefix_list_apply (struct prefix_list *, void *);
+extern const char* prefix_list_get_name(struct prefix_list* plist) ;
+
extern struct stream * prefix_bgp_orf_entry (struct stream *,
- struct prefix_list *,
+ prefix_list_ref ref,
u_char, u_char, u_char);
+extern int prefix_bgp_orf_get(struct prefix_list *plist, vector_index i,
+ struct orf_prefix *orfpe, enum prefix_list_type *pe_type);
extern int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int);
extern void prefix_bgp_orf_remove_all (char *);
extern int prefix_bgp_show_prefix_list (struct vty *, afi_t, char *);
+extern prefix_list_ref prefix_list_set_ref(prefix_list_ref* p_ref, afi_t afi,
+ const char* name) ;
+extern prefix_list_ref prefix_list_copy_ref(prefix_list_ref* p_dst,
+ prefix_list_ref src) ;
+extern prefix_list_ref prefix_list_unset_ref(prefix_list_ref* p_ref) ;
+
+extern const char* prefix_list_ref_name(prefix_list_ref ref) ;
+extern void* prefix_list_ref_ident(prefix_list_ref ref) ;
+extern struct prefix_list* prefix_list_ref_plist(prefix_list_ref ref) ;
+
#endif /* _QUAGGA_PLIST_H */
diff --git a/lib/prefix.c b/lib/prefix.c
index 61a278ca..867c86a3 100644
--- a/lib/prefix.c
+++ b/lib/prefix.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 <zebra.h>
@@ -27,10 +27,10 @@
#include "sockunion.h"
#include "memory.h"
#include "log.h"
-
+
/* Maskbit. */
-static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
- 0xf8, 0xfc, 0xfe, 0xff};
+static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
+ 0xf8, 0xfc, 0xfe, 0xff };
/* Number of bits in prefix type. */
#ifndef PNBBY
@@ -86,7 +86,7 @@ prefix_match (const struct prefix *n, const struct prefix *p)
if (shift)
if (maskbit[shift] & (np[offset] ^ pp[offset]))
return 0;
-
+
while (offset--)
if (np[offset] != pp[offset])
return 0;
@@ -119,7 +119,7 @@ prefix_copy (struct prefix *dest, const struct prefix *src)
}
}
-/*
+/*
* Return 1 if the address/netmask contained in the prefix structure
* is the same, and else return 0. For this routine, 'same' requires
* that not only the prefix length and the network part be the same,
@@ -181,6 +181,46 @@ prefix_cmp (const struct prefix *p1, const struct prefix *p2)
return 0;
}
+/*
+ * Count the number of common bits in 2 prefixes. The prefix length is
+ * ignored for this function; the whole prefix is compared. If the prefix
+ * address families don't match, return -1; otherwise the return value is
+ * in range 0 ... maximum prefix length for the address family.
+ */
+int
+prefix_common_bits (const struct prefix *p1, const struct prefix *p2)
+{
+ int pos, bit;
+ int length = 0;
+ u_char xor;
+
+ /* Set both prefix's head pointer. */
+ const u_char *pp1 = (const u_char *)&p1->u.prefix;
+ const u_char *pp2 = (const u_char *)&p2->u.prefix;
+
+ if (p1->family == AF_INET)
+ length = IPV4_MAX_BYTELEN;
+#ifdef HAVE_IPV6
+ if (p1->family == AF_INET6)
+ length = IPV6_MAX_BYTELEN;
+#endif
+ if (p1->family != p2->family || !length)
+ return -1;
+
+ for (pos = 0; pos < length; pos++)
+ if (pp1[pos] != pp2[pos])
+ break;
+ if (pos == length)
+ return pos * 8;
+
+ xor = pp1[pos] ^ pp2[pos];
+ for (bit = 0; bit < 8; bit++)
+ if (xor & (1 << (7 - bit)))
+ break;
+
+ return pos * 8 + bit;
+}
+
/* Return prefix family type string. */
const char *
prefix_family_str (const struct prefix *p)
@@ -215,50 +255,58 @@ prefix_ipv4_free (struct prefix_ipv4 *p)
prefix_free((struct prefix *)p);
}
-/* When string format is invalid return 0. */
+/* When string format is valid return 1 otherwise return 0.
+ *
+ * inet_aton() returns 1 <=> valid, 0 <=> invalid.
+ * inet_pton() returns 1 <=> valid, 0 <=> invalid, -1 <=> error
+ * where error => unknown address family argument
+ *
+ * Callers of this function vary in how they test the return:
+ *
+ * 1) some treat non-0 as OK and 0 as invalid -- consistent with inet_aton().
+ *
+ * 2) some treat > 0 as OK and <= 0 as invalid -- consistent with inet_pton().
+ *
+ * Since this function returns 1 <=> valid and 0 <=> invalid, both the above
+ * work.
+ */
int
str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
{
- int ret;
- int plen;
- char *pnt;
- char *cp;
+ char* pnt ;
+ char* cp ;
+ int ret ;
+ unsigned plen ;
- /* Find slash inside string. */
pnt = strchr (str, '/');
- /* String doesn't contail slash. */
- if (pnt == NULL)
+ if (pnt == NULL)
{
- /* Convert string to prefix. */
+ /* No / => simple address */
+ plen = IPV4_MAX_BITLEN;
ret = inet_aton (str, &p->prefix);
- if (ret == 0)
- return 0;
-
- /* If address doesn't contain slash we assume it host address. */
- p->family = AF_INET;
- p->prefixlen = IPV4_MAX_BITLEN;
-
- return ret;
}
else
{
+ /* With / => prefix */
+ plen = (unsigned)atoi (pnt + 1) ;
+ if (plen > IPV4_MAX_PREFIXLEN)
+ return 0;
+
cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
strncpy (cp, str, pnt - str);
*(cp + (pnt - str)) = '\0';
ret = inet_aton (cp, &p->prefix);
XFREE (MTYPE_TMP, cp);
+ }
- /* Get prefix length. */
- plen = (u_char) atoi (++pnt);
- if (plen > IPV4_MAX_PREFIXLEN)
- return 0;
+ if (ret <= 0) /* should not return < 0, but it would not be valid ! */
+ return 0;
- p->family = AF_INET;
- p->prefixlen = plen;
- }
+ p->family = AF_INET;
+ p->prefixlen = plen;
- return ret;
+ return 1 ;
}
/* Convert masklen into IP address's netmask. */
@@ -274,7 +322,7 @@ masklen2ip (int masklen, struct in_addr *netmask)
offset = masklen / 8;
bit = masklen % 8;
-
+
while (offset--)
*pnt++ = 0xff;
@@ -300,7 +348,7 @@ ip_masklen (struct in_addr netmask)
{
len+= 8;
pnt++;
- }
+ }
if (pnt < end)
{
@@ -343,7 +391,7 @@ prefix_ipv4_any (const struct prefix_ipv4 *p)
{
return (p->prefix.s_addr == 0 && p->prefixlen == 0);
}
-
+
#ifdef HAVE_IPV6
/* Allocate a new ip version 6 route */
@@ -366,43 +414,61 @@ prefix_ipv6_free (struct prefix_ipv6 *p)
prefix_free((struct prefix *)p);
}
-/* If given string is valid return pin6 else return NULL */
+/* If given string is valid IPv6 address or prefix return 1 else return 0
+ *
+ * inet_aton() returns 1 <=> valid, 0 <=> invalid.
+ * inet_pton() returns 1 <=> valid, 0 <=> invalid, -1 <=> error
+ * where error => unknown address family argument
+ *
+ * Any error returned by inet_pton() is reported as an invalid address or
+ * prefix. So best not to call this if IPv6 is not supported.
+ *
+ * Callers of this function vary in how they test the return:
+ *
+ * 1) some treat non-0 as OK and 0 as invalid -- consistent with inet_aton().
+ *
+ * 2) some treat > 0 as OK and <= 0 as invalid -- consistent with inet_pton().
+ *
+ * Since this function returns 1 <=> valid and 0 <=> invalid, both the above
+ * work.
+ */
int
str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
- char *pnt;
- char *cp;
- int ret;
+ char* pnt ;
+ char* cp ;
+ int ret ;
+ unsigned plen ;
pnt = strchr (str, '/');
- /* If string doesn't contain `/' treat it as host route. */
- if (pnt == NULL)
+ if (pnt == NULL)
{
- ret = inet_pton (AF_INET6, str, &p->prefix);
- if (ret == 0)
- return 0;
- p->prefixlen = IPV6_MAX_BITLEN;
+ /* No / => simple address */
+ plen = IPV6_MAX_BITLEN;
+ ret = inet_pton (AF_INET6, str, &p->prefix);
}
- else
+ else
{
- int plen;
+ /* With / => prefix */
+ plen = (unsigned) atoi (pnt + 1) ;
+ if (plen > IPV6_MAX_PREFIXLEN)
+ return 0 ;
- cp = XMALLOC (0, (pnt - str) + 1);
+ cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
strncpy (cp, str, pnt - str);
*(cp + (pnt - str)) = '\0';
ret = inet_pton (AF_INET6, cp, &p->prefix);
- free (cp);
- if (ret == 0)
- return 0;
- plen = (u_char) atoi (++pnt);
- if (plen > 128)
- return 0;
- p->prefixlen = plen;
+ XFREE (MTYPE_TMP, cp);
}
- p->family = AF_INET6;
- return ret;
+ if (ret <= 0)
+ return 0 ;
+
+ p->family = AF_INET6;
+ p->prefixlen = plen;
+
+ return 1 ;
}
/* Convert struct in6_addr netmask into integer.
@@ -413,19 +479,19 @@ ip6_masklen (struct in6_addr netmask)
int len = 0;
unsigned char val;
unsigned char *pnt;
-
+
pnt = (unsigned char *) & netmask;
- while ((*pnt == 0xff) && len < 128)
+ while ((*pnt == 0xff) && len < 128)
{
len += 8;
pnt++;
- }
-
- if (len < 128)
+ }
+
+ if (len < 128)
{
val = *pnt;
- while (val)
+ while (val)
{
len++;
val <<= 1;
@@ -570,10 +636,23 @@ sockunion2hostprefix (const union sockunion *su)
return NULL;
}
+void
+prefix2sockunion (const struct prefix *p, union sockunion *su) {
+ memset (su, 0, sizeof (*su));
+
+ su->sa.sa_family = p->family;
+ if (p->family == AF_INET)
+ su->sin.sin_addr = p->u.prefix4;
+#ifdef HAVE_IPV6
+ if (p->family == AF_INET6)
+ memcpy (&su->sin6.sin6_addr, &p->u.prefix6, sizeof (struct in6_addr));
+#endif /* HAVE_IPV6 */
+}
+
int
prefix_blen (const struct prefix *p)
{
- switch (p->family)
+ switch (p->family)
{
case AF_INET:
return IPV4_MAX_BYTELEN;
@@ -587,25 +666,28 @@ prefix_blen (const struct prefix *p)
return 0;
}
-/* Generic function for conversion string to struct prefix. */
+/* Generic function for conversion string to struct prefix.
+ *
+ * Accepts addresses without '/' and prefixes with.
+ *
+ * Returns 1 <=> valid IPv4 or (if HAVE_IPV6) IPv6 address or prefix.
+ * 0 <=> not a a valid address or prefix
+ */
int
str2prefix (const char *str, struct prefix *p)
{
int ret;
- /* First we try to convert string to struct prefix_ipv4. */
+ /* First we try to convert string to struct prefix_ipv4. */
ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p);
- if (ret)
- return ret;
#ifdef HAVE_IPV6
- /* Next we try to convert string to struct prefix_ipv6. */
- ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
- if (ret)
- return ret;
+ /* If not IPv4, try to convert to struct prefix_ipv6. */
+ if (ret == 0)
+ ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
#endif /* HAVE_IPV6 */
- return 0;
+ return ret;
}
int
@@ -651,22 +733,22 @@ void apply_classful_mask_ipv4 (struct prefix_ipv4 *p)
{
u_int32_t destination;
-
+
destination = ntohl (p->prefix.s_addr);
-
+
if (p->prefixlen == IPV4_MAX_PREFIXLEN);
/* do nothing for host routes */
- else if (IN_CLASSC (destination))
+ else if (IN_CLASSC (destination))
{
p->prefixlen=24;
apply_mask_ipv4(p);
}
- else if (IN_CLASSB(destination))
+ else if (IN_CLASSB(destination))
{
p->prefixlen=16;
apply_mask_ipv4(p);
}
- else
+ else
{
p->prefixlen=8;
apply_mask_ipv4(p);
@@ -695,7 +777,7 @@ ipv4_broadcast_addr (in_addr_t hostaddr, int masklen)
(hostaddr ^ ~mask.s_addr);
}
-/* Utility function to convert ipv4 netmask to prefixes
+/* Utility function to convert ipv4 netmask to prefixes
ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16"
ex.) "1.0.0.0" NULL => "1.0.0.0/8" */
int
@@ -720,7 +802,7 @@ netmask_str2prefix_str (const char *net_str, const char *mask_str,
prefixlen = ip_masklen (mask);
}
- else
+ else
{
destination = ntohl (network.s_addr);
@@ -752,3 +834,4 @@ inet6_ntoa (struct in6_addr addr)
return buf;
}
#endif /* HAVE_IPV6 */
+
diff --git a/lib/prefix.h b/lib/prefix.h
index 5f1ff05c..ca7ee687 100644
--- a/lib/prefix.h
+++ b/lib/prefix.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_PREFIX_H
@@ -25,6 +25,12 @@
#include "sockunion.h"
+#ifndef Inline
+#define Inline static inline
+#endif
+
+typedef const union sockunion* const_sockunion ;
+
/*
* A struct prefix contains an address family, a prefix length, and an
* address. This can represent either a 'network prefix' as defined
@@ -37,16 +43,16 @@
/* IPv4 and IPv6 unified prefix structure. */
struct prefix
{
- u_char family;
- u_char prefixlen;
- union
+ sa_family_t family;
+ u_char prefixlen;
+ union
{
u_char prefix;
struct in_addr prefix4;
#ifdef HAVE_IPV6
struct in6_addr prefix6;
#endif /* HAVE_IPV6 */
- struct
+ struct
{
struct in_addr id;
struct in_addr adv_router;
@@ -58,36 +64,66 @@ struct prefix
/* IPv4 prefix structure. */
struct prefix_ipv4
{
- u_char family;
- u_char prefixlen;
+ sa_family_t family;
+ u_char prefixlen;
struct in_addr prefix __attribute__ ((aligned (8)));
};
+CONFIRM(offsetof(struct prefix_ipv4, family)
+ == offsetof(struct prefix, family)) ;
+CONFIRM(offsetof(struct prefix_ipv4, prefixlen)
+ == offsetof(struct prefix, prefixlen)) ;
+CONFIRM(offsetof(struct prefix_ipv4, prefix)
+ == offsetof(struct prefix, u.prefix4)) ;
+CONFIRM(sizeof(struct prefix_ipv4) <= sizeof(struct prefix)) ;
/* IPv6 prefix structure. */
#ifdef HAVE_IPV6
struct prefix_ipv6
{
- u_char family;
- u_char prefixlen;
+ sa_family_t family;
+ u_char prefixlen;
struct in6_addr prefix __attribute__ ((aligned (8)));
};
+CONFIRM(offsetof(struct prefix_ipv6, family)
+ == offsetof(struct prefix, family)) ;
+CONFIRM(offsetof(struct prefix_ipv6, prefixlen)
+ == offsetof(struct prefix, prefixlen)) ;
+CONFIRM(offsetof(struct prefix_ipv6, prefix)
+ == offsetof(struct prefix, u.prefix6)) ;
+CONFIRM(sizeof(struct prefix_ipv6) <= sizeof(struct prefix)) ;
#endif /* HAVE_IPV6 */
struct prefix_ls
{
- u_char family;
- u_char prefixlen;
+ sa_family_t family;
+ u_char prefixlen;
struct in_addr id __attribute__ ((aligned (8)));
struct in_addr adv_router;
};
+CONFIRM(offsetof(struct prefix_ls, family)
+ == offsetof(struct prefix, family)) ;
+CONFIRM(offsetof(struct prefix_ls, prefixlen)
+ == offsetof(struct prefix, prefixlen)) ;
+CONFIRM(offsetof(struct prefix_ls, id)
+ == offsetof(struct prefix, u.lp.id)) ;
+CONFIRM(offsetof(struct prefix_ls, adv_router)
+ == offsetof(struct prefix, u.lp.adv_router)) ;
+CONFIRM(sizeof(struct prefix_ls) <= sizeof(struct prefix)) ;
/* Prefix for routing distinguisher. */
struct prefix_rd
{
- u_char family;
- u_char prefixlen;
- u_char val[8] __attribute__ ((aligned (8)));
+ sa_family_t family;
+ u_char prefixlen;
+ u_char val[8] __attribute__ ((aligned (8)));
};
+CONFIRM(offsetof(struct prefix_rd, family)
+ == offsetof(struct prefix, family)) ;
+CONFIRM(offsetof(struct prefix_rd, prefixlen)
+ == offsetof(struct prefix, prefixlen)) ;
+CONFIRM(offsetof(struct prefix_rd, val)
+ == offsetof(struct prefix, u.val)) ;
+CONFIRM(sizeof(struct prefix_rd) <= sizeof(struct prefix)) ;
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
@@ -156,12 +192,14 @@ extern int prefix2str (const struct prefix *, char *, int);
extern int prefix_match (const struct prefix *, const struct prefix *);
extern int prefix_same (const struct prefix *, const struct prefix *);
extern int prefix_cmp (const struct prefix *, const struct prefix *);
+extern int prefix_common_bits (const struct prefix *, const struct prefix *);
extern void prefix_copy (struct prefix *dest, const struct prefix *src);
extern void apply_mask (struct prefix *);
-extern struct prefix *sockunion2prefix (const union sockunion *dest,
- const union sockunion *mask);
-extern struct prefix *sockunion2hostprefix (const union sockunion *);
+extern struct prefix *sockunion2prefix (const_sockunion dest,
+ const_sockunion mask);
+extern struct prefix *sockunion2hostprefix (const_sockunion src);
+extern void prefix2sockunion (const struct prefix *, union sockunion *);
extern struct prefix_ipv4 *prefix_ipv4_new (void);
extern void prefix_ipv4_free (struct prefix_ipv4 *);
@@ -171,6 +209,12 @@ extern void apply_mask_ipv4 (struct prefix_ipv4 *);
#define PREFIX_COPY_IPV4(DST, SRC) \
*((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC));
+Inline void
+prefix_copy_ipv4(struct prefix* dst, struct prefix* src)
+{
+ *dst = *src ;
+} ;
+
extern int prefix_ipv4_any (const struct prefix_ipv4 *);
extern void apply_classful_mask_ipv4 (struct prefix_ipv4 *);
@@ -193,7 +237,13 @@ extern int str2prefix_ipv6 (const char *, struct prefix_ipv6 *);
extern void apply_mask_ipv6 (struct prefix_ipv6 *);
#define PREFIX_COPY_IPV6(DST, SRC) \
- *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC));
+ *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC));
+
+Inline void
+prefix_copy_ipv6(struct prefix* dst, struct prefix* src)
+{
+ *dst = *src ;
+} ;
extern int ip6_masklen (struct in6_addr);
extern void masklen2ip6 (int, struct in6_addr *);
diff --git a/lib/privs.c b/lib/privs.c
index 69606f57..be3265ed 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -1,4 +1,4 @@
-/*
+/*
* Zebra privileges.
*
* Copyright (C) 2003 Paul Jakma.
@@ -19,12 +19,18 @@
* 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>
#include "log.h"
#include "privs.h"
#include "memory.h"
+#include "qpthreads.h"
+
+/* Needs to be qpthread safe */
+static qpt_mutex_t privs_mutex;
+#define LOCK qpt_mutex_lock(&privs_mutex);
+#define UNLOCK qpt_mutex_unlock(&privs_mutex);
#ifdef HAVE_CAPABILITIES
/* sort out some generic internal types for:
@@ -37,7 +43,7 @@
* sets are mostly opaque, to hold a set of privileges, related in some way.
* storage binds together a set of sets we're interested in.
* (in reality: cap_value_t and priv_t are ints)
- */
+ */
#ifdef HAVE_LCAPS
/* Linux doesn't have a 'set' type: a set of related privileges */
struct _pset {
@@ -47,7 +53,7 @@ struct _pset {
typedef cap_value_t pvalue_t;
typedef struct _pset pset_t;
typedef cap_t pstorage_t;
-
+
#elif defined (HAVE_SOLARIS_CAPABILITIES)
typedef priv_t pvalue_t;
typedef priv_set_t pset_t;
@@ -56,11 +62,12 @@ typedef priv_set_t *pstorage_t;
#error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!"
#endif /* HAVE_LCAPS */
#endif /* HAVE_CAPABILITIES */
-
+
/* the default NULL state we report is RAISED, but could be LOWERED if
* zprivs_terminate is called and the NULL handler is installed.
*/
static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED;
+static int raise_count = 0; /* keep raised until all pthreads have lowered */
/* internal privileges state */
static struct _zprivs_t
@@ -128,7 +135,7 @@ static struct
[ZCAP_CHROOT] = { 1, (pvalue_t []) { PRIV_PROC_CHROOT }, },
[ZCAP_NICE] = { 1, (pvalue_t []) { PRIV_PROC_PRIOCNTL }, },
[ZCAP_PTRACE] = { 1, (pvalue_t []) { PRIV_PROC_SESSION }, },
- [ZCAP_DAC_OVERRIDE] = { 2, (pvalue_t []) { PRIV_FILE_DAC_EXECUTE,
+ [ZCAP_DAC_OVERRIDE] = { 2, (pvalue_t []) { PRIV_FILE_DAC_EXECUTE,
PRIV_FILE_DAC_READ,
PRIV_FILE_DAC_SEARCH,
PRIV_FILE_DAC_WRITE,
@@ -139,7 +146,7 @@ static struct
[ZCAP_FOWNER] = { 1, (pvalue_t []) { PRIV_FILE_OWNER }, },
#endif /* HAVE_SOLARIS_CAPABILITIES */
};
-
+
#ifdef HAVE_LCAPS
/* Linux forms of capabilities methods */
/* convert zebras privileges to system capabilities */
@@ -148,64 +155,81 @@ zcaps2sys (zebra_capabilities_t *zcaps, int num)
{
pset_t *syscaps;
int i, j = 0, count = 0;
-
+
if (!num)
return NULL;
-
+
/* first count up how many system caps we have */
for (i= 0; i < num; i++)
count += cap_map[zcaps[i]].num;
-
+
if ( (syscaps = XCALLOC (MTYPE_PRIVS, (sizeof(pset_t) * num))) == NULL)
{
fprintf (stderr, "%s: could not allocate syscaps!", __func__);
return NULL;
}
-
+
syscaps->caps = XCALLOC (MTYPE_PRIVS, (sizeof (pvalue_t) * count));
-
+
if (!syscaps->caps)
{
fprintf (stderr, "%s: could not XCALLOC caps!", __func__);
return NULL;
}
-
+
/* copy the capabilities over */
count = 0;
for (i=0; i < num; i++)
for (j = 0; j < cap_map[zcaps[i]].num; j++)
syscaps->caps[count++] = cap_map[zcaps[i]].system_caps[j];
-
+
/* iterations above should be exact same as previous count, obviously.. */
syscaps->num = count;
-
+
return syscaps;
}
/* set or clear the effective capabilities to/from permitted */
-int
+int
zprivs_change_caps (zebra_privs_ops_t op)
{
cap_flag_value_t cflag;
-
+ int result = 0;
+ int change = 0;
+
+ LOCK
+
/* should be no possibility of being called without valid caps */
assert (zprivs_state.syscaps_p && zprivs_state.caps);
if (! (zprivs_state.syscaps_p && zprivs_state.caps))
- exit (1);
-
+ {
+ UNLOCK
+ exit (1);
+ }
+
if (op == ZPRIVS_RAISE)
- cflag = CAP_SET;
+ {
+ cflag = CAP_SET;
+ change = (raise_count++ == 0);
+ }
else if (op == ZPRIVS_LOWER)
- cflag = CAP_CLEAR;
+ {
+ cflag = CAP_CLEAR;
+ change = (--raise_count == 0);
+ }
else
- return -1;
+ {
+ result = -1;
+ }
- if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
- zprivs_state.syscaps_p->num,
- zprivs_state.syscaps_p->caps,
+ if ( change && !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
+ zprivs_state.syscaps_p->num,
+ zprivs_state.syscaps_p->caps,
cflag))
- return cap_set_proc (zprivs_state.caps);
- return -1;
+ result = cap_set_proc (zprivs_state.caps);
+
+ UNLOCK
+ return result;
}
zebra_privs_current_t
@@ -213,25 +237,37 @@ zprivs_state_caps (void)
{
int i;
cap_flag_value_t val;
+ zebra_privs_current_t result = ZPRIVS_LOWERED;
+
+ LOCK
/* should be no possibility of being called without valid caps */
assert (zprivs_state.syscaps_p && zprivs_state.caps);
if (! (zprivs_state.syscaps_p && zprivs_state.caps))
- exit (1);
-
+ {
+ UNLOCK
+ exit (1);
+ }
+
for (i=0; i < zprivs_state.syscaps_p->num; i++)
{
- if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p->caps[i],
+ if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p->caps[i],
CAP_EFFECTIVE, &val) )
{
zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
- safe_strerror (errno) );
- return ZPRIVS_UNKNOWN;
+ errtoa(errno, 0).str) ;
+ result = ZPRIVS_UNKNOWN;
+ break;
}
if (val == CAP_SET)
- return ZPRIVS_RAISED;
+ {
+ result = ZPRIVS_RAISED;
+ break;
+ }
}
- return ZPRIVS_LOWERED;
+
+ UNLOCK
+ return result;
}
static void
@@ -244,7 +280,7 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
{
fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n",
- safe_strerror (errno) );
+ errtostr(errno, 0).str) ;
exit(1);
}
@@ -259,50 +295,50 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
{
if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
{
- fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n",
- safe_strerror (errno));
- exit (1);
+ fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n",
+ errtostr(errno, 0).str) ;
+ exit (1);
}
}
-
+
if ( !(zprivs_state.caps = cap_init()) )
{
- fprintf (stderr, "privs_init: failed to cap_init, %s\n",
- safe_strerror (errno));
+ fprintf (stderr, "privs_init: failed to cap_init, %s\n",
+ errtostr(errno, 0).str) ;
exit (1);
}
if ( cap_clear (zprivs_state.caps) )
{
- fprintf (stderr, "privs_init: failed to cap_clear, %s\n",
- safe_strerror (errno));
+ fprintf (stderr, "privs_init: failed to cap_clear, %s\n",
+ errtostr(errno, 0).str) ;
exit (1);
}
-
+
/* set permitted caps */
- cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
+ cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
zprivs_state.syscaps_p->num,
zprivs_state.syscaps_p->caps,
CAP_SET);
-
+
/* set inheritable caps, if any */
if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num)
{
- cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
- zprivs_state.syscaps_i->num,
- zprivs_state.syscaps_i->caps,
+ cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
+ zprivs_state.syscaps_i->num,
+ zprivs_state.syscaps_i->caps,
CAP_SET);
}
-
- /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as
+
+ /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as
* and when, and only when, they are needed.
*/
- if ( cap_set_proc (zprivs_state.caps) )
+ if ( cap_set_proc (zprivs_state.caps) )
{
fprintf (stderr, "privs_init: initial cap_set_proc failed\n");
exit (1);
}
-
+
/* set methods for the caller to use */
zprivs->change = zprivs_change_caps;
zprivs->current_state = zprivs_state_caps;
@@ -316,12 +352,12 @@ zprivs_caps_terminate (void)
cap_clear (zprivs_state.caps);
/* and boom, capabilities are gone forever */
- if ( cap_set_proc (zprivs_state.caps) )
+ if ( cap_set_proc (zprivs_state.caps) )
{
fprintf (stderr, "privs_terminate: cap_set_proc failed, %s",
- safe_strerror (errno) );
+ errtostr(errno, 0).str) ;
exit (1);
- }
+ }
/* free up private state */
if (zprivs_state.syscaps_p->num)
@@ -329,18 +365,18 @@ zprivs_caps_terminate (void)
XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p->caps);
XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p);
}
-
+
if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num)
{
XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i->caps);
XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i);
}
-
+
cap_free (zprivs_state.caps);
}
#elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */
-
-/* Solaris specific capability/privilege methods
+
+/* Solaris specific capability/privilege methods
*
* Resources:
* - the 'privileges' man page
@@ -354,84 +390,105 @@ zcaps2sys (zebra_capabilities_t *zcaps, int num)
{
pset_t *syscaps;
int i, j = 0;
-
+
if ((syscaps = priv_allocset()) == NULL)
{
fprintf (stderr, "%s: could not allocate syscaps!\n", __func__);
exit (1);
}
-
+
priv_emptyset (syscaps);
-
+
for (i=0; i < num; i++)
for (j = 0; j < cap_map[zcaps[i]].num; j++)
priv_addset (syscaps, cap_map[zcaps[i]].system_caps[j]);
-
+
return syscaps;
}
/* callback exported to users to RAISE and LOWER effective privileges
* from nothing to the given permitted set and back down
*/
-int
+int
zprivs_change_caps (zebra_privs_ops_t op)
{
-
+ int result = 0;
+
+ LOCK
+
/* should be no possibility of being called without valid caps */
assert (zprivs_state.syscaps_p);
if (!zprivs_state.syscaps_p)
{
fprintf (stderr, "%s: Eek, missing caps!", __func__);
+ UNLOCK
exit (1);
}
-
+
/* to raise: copy original permitted into our working effective set
* to lower: just clear the working effective set
*/
if (op == ZPRIVS_RAISE)
- priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps);
+ {
+ if (raise_count++ == 0)
+ {
+ priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps);
+ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
+ result = -1;
+ }
+ }
else if (op == ZPRIVS_LOWER)
- priv_emptyset (zprivs_state.caps);
+ {
+ if (--raise_count == 0)
+ {
+ priv_emptyset (zprivs_state.caps);
+ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
+ result = -1;
+ }
+ }
else
- return -1;
-
- if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
- return -1;
-
- return 0;
+ result = -1;
+
+ UNLOCK
+
+ return result;
}
/* Retrieve current privilege state, is it RAISED or LOWERED? */
-zebra_privs_current_t
+zebra_privs_current_t
zprivs_state_caps (void)
{
- zebra_privs_current_t result;
+ zebra_privs_current_t result = ZPRIVS_UNKNOWN;
pset_t *effective;
-
+
+ LOCK
+
if ( (effective = priv_allocset()) == NULL)
{
fprintf (stderr, "%s: failed to get priv_allocset! %s\n", __func__,
- safe_strerror (errno));
- return ZPRIVS_UNKNOWN;
- }
-
- if (getppriv (PRIV_EFFECTIVE, effective))
- {
- fprintf (stderr, "%s: failed to get state! %s\n", __func__,
- safe_strerror (errno));
- result = ZPRIVS_UNKNOWN;
+ errtoa(errno, 0).str);
}
else
{
- if (priv_isemptyset (effective) == B_TRUE)
- result = ZPRIVS_LOWERED;
+
+ if (getppriv (PRIV_EFFECTIVE, effective))
+ {
+ fprintf (stderr, "%s: failed to get state! %s\n", __func__,
+ errtoa(errno, 0).str);
+ }
else
- result = ZPRIVS_RAISED;
+ {
+ if (priv_isemptyset (effective) == B_TRUE)
+ result = ZPRIVS_LOWERED;
+ else
+ result = ZPRIVS_RAISED;
+ }
+
+ if (effective)
+ priv_freeset (effective);
}
-
- if (effective)
- priv_freeset (effective);
-
+
+ UNLOCK
return result;
}
@@ -440,11 +497,11 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
{
pset_t *basic;
pset_t *empty;
-
+
/* the specified sets */
zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
-
+
/* nonsensical to have gotten here but not have capabilities */
if (!zprivs_state.syscaps_p)
{
@@ -452,7 +509,7 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
"but no valid capabilities supplied\n",
__func__);
}
-
+
/* We retain the basic set in our permitted set, as Linux has no
* equivalent. The basic set on Linux hence is implicit, always
* there.
@@ -462,11 +519,11 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
fprintf (stderr, "%s: couldn't get basic set!\n", __func__);
exit (1);
}
-
+
/* Add the basic set to the permitted set */
priv_union (basic, zprivs_state.syscaps_p);
priv_freeset (basic);
-
+
/* we need an empty set for 'effective', potentially for inheritable too */
if ( (empty = priv_allocset()) == NULL)
{
@@ -474,20 +531,20 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
exit (1);
}
priv_emptyset (empty);
-
- /* Hey kernel, we know about privileges!
+
+ /* Hey kernel, we know about privileges!
* this isn't strictly required, use of setppriv should have same effect
*/
if (setpflags (PRIV_AWARE, 1))
{
fprintf (stderr, "%s: error setting PRIV_AWARE!, %s\n", __func__,
- safe_strerror (errno) );
+ errtoa(errno, 0).str );
exit (1);
}
-
+
/* need either valid or empty sets for both p and i.. */
assert (zprivs_state.syscaps_i && zprivs_state.syscaps_p);
-
+
/* we have caps, we have no need to ever change back the original user
* change real, effective and saved to the specified user.
*/
@@ -495,25 +552,25 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
{
if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
{
- fprintf (stderr, "%s: could not setreuid, %s\n",
- __func__, safe_strerror (errno));
+ fprintf (stderr, "%s: could not setreuid, %s\n",
+ __func__, errtoa(errno, 0).str);
exit (1);
}
}
-
+
/* set the permitted set */
if (setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.syscaps_p))
{
fprintf (stderr, "%s: error setting permitted set!, %s\n", __func__,
- safe_strerror (errno) );
+ errtoa(errno, 0).str );
exit (1);
}
-
+
/* set the inheritable set */
if (setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.syscaps_i))
{
fprintf (stderr, "%s: error setting inheritable set!, %s\n", __func__,
- safe_strerror (errno) );
+ errtoa(errno, 0).str );
exit (1);
}
@@ -521,13 +578,13 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
if (setppriv (PRIV_SET, PRIV_EFFECTIVE, empty))
{
fprintf (stderr, "%s: error setting effective set!, %s\n", __func__,
- safe_strerror (errno) );
+ errtoa(errno, 0).str );
exit (1);
}
-
+
/* we'll use this as our working-storage privset */
zprivs_state.caps = empty;
-
+
/* set methods for the caller to use */
zprivs->change = zprivs_change_caps;
zprivs->current_state = zprivs_state_caps;
@@ -537,42 +594,65 @@ static void
zprivs_caps_terminate (void)
{
assert (zprivs_state.caps);
-
+
/* clear all capabilities */
priv_emptyset (zprivs_state.caps);
setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps);
setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.caps);
setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.caps);
-
+
/* free up private state */
if (zprivs_state.syscaps_p)
priv_freeset (zprivs_state.syscaps_p);
if (zprivs_state.syscaps_i)
priv_freeset (zprivs_state.syscaps_i);
-
+
priv_freeset (zprivs_state.caps);
}
#else /* !HAVE_LCAPS && ! HAVE_SOLARIS_CAPABILITIES */
#error "Neither Solaris nor Linux capabilities, dazed and confused..."
#endif /* HAVE_LCAPS */
#endif /* HAVE_CAPABILITIES */
-
+
int
zprivs_change_uid (zebra_privs_ops_t op)
{
+ int result = 0;
+
+ LOCK
if (op == ZPRIVS_RAISE)
- return seteuid (zprivs_state.zsuid);
+ {
+ if (raise_count++ == 0)
+ {
+ result = seteuid (zprivs_state.zsuid);
+ }
+ }
else if (op == ZPRIVS_LOWER)
- return seteuid (zprivs_state.zuid);
+ {
+ if (--raise_count == 0)
+ {
+ result = seteuid (zprivs_state.zuid);
+ }
+ }
else
- return -1;
+ {
+ result = -1;
+ }
+
+ UNLOCK
+ return result;
}
zebra_privs_current_t
zprivs_state_uid (void)
{
- return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
+ zebra_privs_current_t result;
+
+ LOCK
+ result = ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
+ UNLOCK
+ return result;
}
int
@@ -584,7 +664,24 @@ zprivs_change_null (zebra_privs_ops_t op)
zebra_privs_current_t
zprivs_state_null (void)
{
- return zprivs_null_state;
+ int result;
+
+ LOCK
+ result = zprivs_null_state;
+ UNLOCK
+ return result;
+}
+
+void
+zprivs_init_r()
+{
+ qpt_mutex_init(&privs_mutex, qpt_mutex_quagga);
+}
+
+void
+zprivs_finish(void)
+{
+ qpt_mutex_destroy(&privs_mutex, 0);
}
void
@@ -599,12 +696,15 @@ zprivs_init(struct zebra_privs_t *zprivs)
exit (1);
}
+ LOCK
+
/* NULL privs */
- if (! (zprivs->user || zprivs->group
+ if (! (zprivs->user || zprivs->group
|| zprivs->cap_num_p || zprivs->cap_num_i) )
{
zprivs->change = zprivs_change_null;
zprivs->current_state = zprivs_state_null;
+ UNLOCK
return;
}
@@ -619,6 +719,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
/* cant use log.h here as it depends on vty */
fprintf (stderr, "privs_init: could not lookup user %s\n",
zprivs->user);
+ UNLOCK
exit (1);
}
}
@@ -634,18 +735,20 @@ zprivs_init(struct zebra_privs_t *zprivs)
if ( setgroups (1, &zprivs_state.vtygrp) )
{
fprintf (stderr, "privs_init: could not setgroups, %s\n",
- safe_strerror (errno) );
+ errtostr(errno, 0).str) ;
+ UNLOCK
exit (1);
- }
+ }
}
else
{
fprintf (stderr, "privs_init: could not lookup vty group %s\n",
zprivs->vty_group);
+ UNLOCK
exit (1);
}
}
-
+
if (zprivs->group)
{
if ( (grentry = getgrnam (zprivs->group)) )
@@ -656,43 +759,48 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not lookup group %s\n",
zprivs->group);
+ UNLOCK
exit (1);
}
/* change group now, forever. uid we do later */
if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
{
fprintf (stderr, "zprivs_init: could not setregid, %s\n",
- safe_strerror (errno) );
+ errtostr(errno, 0).str) ;
+ UNLOCK
exit (1);
}
}
-
+
#ifdef HAVE_CAPABILITIES
zprivs_caps_init (zprivs);
#else /* !HAVE_CAPABILITIES */
/* we dont have caps. we'll need to maintain rid and saved uid
- * and change euid back to saved uid (who we presume has all neccessary
+ * and change euid back to saved uid (who we presume has all necessary
* privileges) whenever we are asked to raise our privileges.
*
* This is not worth that much security wise, but all we can do.
*/
- zprivs_state.zsuid = geteuid();
+ zprivs_state.zsuid = geteuid();
if ( zprivs_state.zuid )
{
if ( setreuid (-1, zprivs_state.zuid) )
{
- fprintf (stderr, "privs_init (uid): could not setreuid, %s\n",
- safe_strerror (errno));
+ fprintf (stderr, "privs_init (uid): could not setreuid, %s\n",
+ errtoa(errno, 0).str);
+ UNLOCK
exit (1);
}
}
-
+
zprivs->change = zprivs_change_uid;
zprivs->current_state = zprivs_state_uid;
#endif /* HAVE_CAPABILITIES */
+
+ UNLOCK
}
-void
+void
zprivs_terminate (struct zebra_privs_t *zprivs)
{
if (!zprivs)
@@ -700,7 +808,9 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
fprintf (stderr, "%s: no privs struct given, terminating", __func__);
exit (0);
}
-
+
+ LOCK
+
#ifdef HAVE_CAPABILITIES
zprivs_caps_terminate();
#else /* !HAVE_CAPABILITIES */
@@ -708,8 +818,9 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
{
if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
{
- fprintf (stderr, "privs_terminate: could not setreuid, %s",
- safe_strerror (errno) );
+ fprintf (stderr, "privs_terminate: could not setreuid, %s",
+ errtoa(errno, 0).str );
+ UNLOCK
exit (1);
}
}
@@ -718,20 +829,25 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
zprivs->change = zprivs_change_null;
zprivs->current_state = zprivs_state_null;
zprivs_null_state = ZPRIVS_LOWERED;
+ raise_count = 0;
+
+ UNLOCK
return;
}
void
zprivs_get_ids(struct zprivs_ids_t *ids)
{
+ LOCK
- ids->uid_priv = getuid();
- (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
+ ids->uid_priv = getuid();
+ (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
: (ids->uid_normal = -1);
- (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
+ (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
: (ids->gid_normal = -1);
- (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
+ (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
: (ids->gid_vty = -1);
-
+
+ UNLOCK
return;
}
diff --git a/lib/privs.h b/lib/privs.h
index 46d614e0..45c5f49a 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -81,9 +81,13 @@ struct zprivs_ids_t
};
/* initialise zebra privileges */
+extern void zprivs_init_r (void);
extern void zprivs_init (struct zebra_privs_t *zprivs);
+extern void zprivs_finish (void);
+
/* drop all and terminate privileges */
extern void zprivs_terminate (struct zebra_privs_t *);
+
/* query for runtime uid's and gid's, eg vty needs this */
extern void zprivs_get_ids(struct zprivs_ids_t *);
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
new file mode 100644
index 00000000..c4ab9679
--- /dev/null
+++ b/lib/pthread_safe.c
@@ -0,0 +1,516 @@
+/* Quagga Pthreads support -- thread safe versions of standard 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.
+ */
+
+/* We use thread specific storeage to provide buufers for the _r versions
+ * of standard functions, so that the callers don't need to provide
+ * their own. The contents of a buffer will remain intact until another
+ * safe_ function is called on the same thread
+ */
+
+#include "pthread_safe.h"
+#include "qpthreads.h"
+#include "memory.h"
+
+#include <pthread.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "qfstring.h"
+#include "errno_names.h"
+
+/* prototypes */
+static void destructor(void* data);
+static char * thread_buff(void);
+
+static pthread_key_t tsd_key;
+static const int buff_size = 1024;
+
+/* Module initialization, before any threads have been created */
+void
+safe_init_r(void)
+{
+ if (qpthreads_enabled)
+ {
+ int status;
+ status = pthread_key_create(&tsd_key, destructor);
+ if (status != 0)
+ zabort("Can't create thread specific data key");
+ }
+}
+
+/* Clean up */
+void
+safe_finish(void)
+{
+ if (qpthreads_enabled)
+ pthread_key_delete(tsd_key);
+}
+
+/* called when thread terminates, clean up */
+static void
+destructor(void* data)
+{
+ XFREE(MTYPE_TSD, data);
+}
+
+/* Thread safe version of strerror. Never returns NULL.
+ * Contents of result remains intact until another call of
+ * a safe_ function.
+ */
+const char *
+safe_strerror(int errnum)
+{
+ static const char * unknown = "Unknown error";
+ if (qpthreads_enabled)
+ {
+ char * buff = thread_buff();
+ int ret = strerror_r(errnum, buff, buff_size);
+
+ return (ret >= 0)
+ ? buff
+ : unknown;
+ }
+ else
+ {
+ const char *s = strerror(errnum);
+ return (s != NULL)
+ ? s
+ : unknown;
+ }
+}
+
+/*==============================================================================
+ * Alternative error number handling.
+ *
+ * The descriptive strings for error numbers are all very well, but for some
+ * purposes knowing the name for the error is more useful -- the name, not the
+ * number, as the number is system dependent.
+ *
+ * The following provides:
+ *
+ * * errtoa() -- which maps error number to: ENAME '<strerror>'
+ *
+ * * errtoname() -- which maps error number to: ENAME
+ *
+ * * errtostr() -- which maps error number to: <strerror>
+ *
+ * where:
+ *
+ * * if name is not known gives: ERRNO=999
+ *
+ * * if strerror rejects the number gives: *unknown error*
+ *
+ * * err == 0 gives: EOK -- for the name
+ * 'no error' -- for the <strerror>
+ *
+ * These functions take a 'len' argument, and truncates the result to the given
+ * len (0 => no truncation) -- silently imposing the maximum length allowed by
+ * the strerror_t.
+ *
+ * If has to truncate the <strerror>, places "..." at the end of the message
+ * to show this has happened.
+ */
+
+static void errtox(strerror_t* st, int err, int len, int want) ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given error of the form:
+ *
+ * ENAME '<strerror>'
+ *
+ * Thread safe extension to strerror. Never returns NULL.
+ */
+extern strerror_t
+errtoa(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 3) ; /* name and message */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert error number to its name
+ *
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+errtoname(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 1) ; /* name */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Alternative thread-safe safe_strerror()
+ *
+ * Thread safe replacement for strerror. Never returns NULL.
+ */
+extern strerror_t
+errtostr(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 2) ; /* message */
+
+ return st ;
+} ;
+
+/*-----------------------------------------------------------------------------
+ * Common code for errto<x> above.
+ *
+ * want == 1 -- return just name
+ * want == 2 -- return just the strerror()
+ * want == 3 -- return both, with the strerror() in single quotes.
+ */
+static void
+errtox(strerror_t* st, int err, int len, int want)
+{
+ qf_str_t qfs ;
+
+ const char* q ;
+ int ql ;
+
+ /* Prepare. */
+ int errno_saved = errno ;
+
+ if ((len <= 0) || (len >= (int)sizeof(st->str)))
+ len = sizeof(st->str) - 1 ;
+ qfs_init(&qfs, st->str, len + 1) ;
+
+ q = "" ;
+ ql = 0 ;
+
+ /* If want the error name, do that now. */
+ if (want & 1)
+ {
+ const char* name = errno_name_lookup(err) ;
+
+ if (name != NULL)
+ qfs_append(&qfs, name) ;
+ else
+ qfs_printf(&qfs, "ERRNO=%d", err) ;
+ } ;
+
+ /* name and string ? */
+ if (want == 3)
+ {
+ qfs_append(&qfs, " ") ;
+ q = "'" ;
+ ql = 2 ;
+ } ;
+
+ /* If want the error string, do that now */
+ if (want & 2)
+ {
+ char buf[400] ; /* impossibly vast */
+ int ret ;
+ const char* errm ;
+
+ if (err == 0)
+ errm = "no error" ;
+ else
+ {
+ if (qpthreads_enabled)
+ {
+ /* POSIX is not explicit about what happens if the buffer is not
+ * big enough to accommodate the message, except that an ERANGE
+ * error may be raised.
+ *
+ * By experiment: glibc-2.10.2-1.x86_64 returns -1, with errno
+ * set to ERANGE and no string at all if the buffer is too small.
+ *
+ * A huge buffer is used to get the message, and that is later
+ * truncated, if necessary, to fit in the strerror_t structure.
+ */
+
+ buf[0] = '\0' ; /* make sure starts empty */
+ ret = strerror_r(err, buf, sizeof(buf)) ;
+ errm = buf ;
+ if (ret != 0)
+ ret = errno ;
+ }
+ else
+ {
+ /* POSIX says that strerror *will* return something, but it is
+ * known that it may return NULL if the error number is not
+ * recognised.
+ */
+ errno = 0 ;
+ errm = strerror(err) ;
+ ret = errno ;
+ if ((ret == 0) && ((errm == NULL) || (*errm == '\0')))
+ ret = EINVAL ;
+ } ;
+
+ /* Deal with errors, however exotic. */
+ if (ret != 0)
+ {
+ q = "*" ;
+ ql = 2 ; /* force "*" "quotes" */
+ if (ret == EINVAL)
+ errm = "unknown error" ;
+ else if (ret == ERANGE)
+ {
+ if (*errm == '\0')
+ errm = "vast error message" ;
+ }
+ else
+ {
+ qf_str_t qfs_b ;
+ qfs_init(&qfs_b, buf, sizeof(buf)) ;
+ qfs_printf(&qfs_b, "strerror%s(%d) returned error %d",
+ qpthreads_enabled ? "_r" : "", err, ret) ;
+ errm = buf ;
+ } ;
+ } ;
+ } ;
+
+ /* Add strerror to the result... looking out for overflow. */
+ len = strlen(errm) ;
+
+ if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
+ qfs_printf(&qfs, "%s%s%s", q, errm, q) ;
+ else
+ qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, errm, q) ;
+ /* -ve precision is ignored ! */
+ } ;
+
+ /* Put back errno */
+ errno = errno_saved ;
+} ;
+
+/*==============================================================================
+ * getaddrinfo() and getnameinfo() "EAI_XXXX" error number handling.
+ *
+ * This is similar to the above for errno.
+ *
+ * The following provides:
+ *
+ * * eaitoa() -- which maps error number to: EAI_XXX '<gai_strerror>'
+ * or: as errtoa()
+ *
+ * * eaitoname() -- which maps error number to: EAI_XXX
+ * or: as errtoname()
+ *
+ * * eaitostr() -- which maps error number to: <gai_strerror>
+ * or: as errtostr()
+ *
+ * where:
+ *
+ * * if given EAI_SYSTEM, and given a non-zero errno type error number,
+ * produce the errno string.
+ *
+ * * if name is not known gives: EAI=999
+ *
+ * * gai_strerror returns a string saying the error is not known if that is
+ * the case.
+ *
+ * * eai == 0 gives: EAI_OK -- for the name
+ * 'no error' -- for the <sgai_strerror>
+ *
+ * NB: EAI_SYSTEM is an invitation to look at errno to discover the true
+ * error.
+ */
+
+static void eaitox(strerror_t* st, int eai, int err, int len, int want) ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given EAI_XXX error of the form:
+ *
+ * EAI_XXX '<gai_strerror>'
+ * or: ENAME '<strerror>' -- if EAI_SYSTEM and err != 0
+ *
+ * Thread safe. Never returns NULL.
+ */
+extern strerror_t
+eaitoa(int eai, int err, int len)
+{
+ strerror_t st ;
+
+ eaitox(&st, eai, err, len, 3) ; /* name and message */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert EAI_XXX error number to its name...
+ *
+ * ...or, if EAI_SYSTEM and err != 0, convert err to its name.
+ *
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+eaitoname(int eai, int err, int len)
+{
+ strerror_t st ;
+
+ eaitox(&st, eai, err, len, 1) ; /* name */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Alternative to gai_strerror()...
+ *
+ * ...or, if EAI_SYSTEM and err != 0, do strerror(err) or strerror_r(err).
+ *
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+eaitostr(int eai, int err, int len)
+{
+ strerror_t st ;
+
+ eaitox(&st, eai, err, len, 2) ; /* message */
+
+ return st ;
+} ;
+
+/*-----------------------------------------------------------------------------
+ * Common code for eaito<x> above.
+ *
+ * want == 1 -- return just name
+ * want == 2 -- return just the gai_strerror()
+ * want == 3 -- return both, with the gai_strerror() in single quotes.
+ *
+ * err != 0 => if EAI_SYSTEM, return result for errno == err instead.
+ */
+static void
+eaitox(strerror_t* st, int eai, int err, int len, int want)
+{
+ qf_str_t qfs ;
+
+ const char* q ;
+ int ql ;
+
+ /* Look out for mapping EAI_SYSTEM */
+ if ((eai == EAI_SYSTEM) && (err != 0))
+ return errtox(st, err, len, want) ;
+
+ /* Prepare. */
+ int errno_saved = errno ;
+
+ if ((len <= 0) || (len >= (int)sizeof(st->str)))
+ len = sizeof(st->str) - 1 ;
+ qfs_init(&qfs, st->str, len + 1) ;
+
+ q = "" ;
+ ql = 0 ;
+
+ /* If want the error name, do that now. */
+ if (want & 1)
+ {
+ const char* name = eaino_name_lookup(eai) ;
+
+ if (name != NULL)
+ qfs_append(&qfs, name) ;
+ else
+ qfs_printf(&qfs, "EAI=%d", eai) ;
+ } ;
+
+ /* name and string ? */
+ if (want == 3)
+ {
+ qfs_append(&qfs, " ") ;
+ q = "'" ;
+ ql = 2 ;
+ } ;
+
+ /* If want the error string, do that now */
+ if (want & 2)
+ {
+ const char* eaim ;
+
+ if (eai == 0)
+ eaim = "no error" ;
+ else
+ eaim = gai_strerror(eai) ;
+
+ /* Add strerror to the result... looking out for overflow. */
+ len = strlen(eaim) ;
+
+ if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
+ qfs_printf(&qfs, "%s%s%s", q, eaim, q) ;
+ else
+ qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, eaim, q) ;
+ /* -ve precision is ignored ! */
+ } ;
+
+ /* Put back errno */
+ errno = errno_saved ;
+} ;
+
+/*============================================================================*/
+
+/* Thread safe version of inet_ntoa. Never returns NULL.
+ * Contents of result remains intact until another call of
+ * a safe_ function.
+ */
+const char *
+safe_inet_ntoa (struct in_addr in)
+{
+ static const char * unknown = "Unknown address";
+ const char * buff;
+
+ buff = (qpthreads_enabled)
+ ? inet_ntop(AF_INET, &in, thread_buff(), buff_size)
+ : inet_ntoa(in);
+
+ return buff != NULL
+ ? buff
+ : unknown;
+}
+
+/* Return the thread's buffer, create it if necessary.
+ * (pthread Thread Specific Data)
+ */
+static char *
+thread_buff(void)
+{
+ int ret;
+ char * buff = pthread_getspecific(tsd_key);
+ if (buff == NULL)
+ {
+ buff = XMALLOC(MTYPE_TSD, buff_size);
+ ret = pthread_setspecific(tsd_key, buff);
+ if (ret != 0)
+ zabort("Can't set thread specific data");
+ }
+
+ return buff;
+}
+
+
+
+
+
+
+
diff --git a/lib/pthread_safe.h b/lib/pthread_safe.h
new file mode 100644
index 00000000..9518f3d1
--- /dev/null
+++ b/lib/pthread_safe.h
@@ -0,0 +1,46 @@
+/* Quagga Pthreads support -- thread safe versions of standard 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 PTHREAD_SAFE_H_
+#define PTHREAD_SAFE_H_
+
+#include <netinet/in.h>
+
+typedef struct strerror strerror_t ;
+struct strerror
+{
+ char str[121] ; /* cannot imagine anything as big */
+} ;
+
+extern void safe_init_r(void);
+extern void safe_finish(void);
+extern const char * safe_strerror(int errnum);
+extern const char * safe_inet_ntoa (struct in_addr in);
+
+extern strerror_t errtoa(int err, int len) ;
+extern strerror_t errtoname(int err, int len) ;
+extern strerror_t errtostr(int err, int len) ;
+
+extern strerror_t eaitoa(int eai, int err, int len) ;
+extern strerror_t eaitoname(int eai, int err, int len) ;
+extern strerror_t eaitostr(int eai, int err, int len) ;
+
+#endif /* PTHREAD_SAFE_H_ */
diff --git a/lib/qafi_safi.h b/lib/qafi_safi.h
new file mode 100644
index 00000000..337532d5
--- /dev/null
+++ b/lib/qafi_safi.h
@@ -0,0 +1,172 @@
+/* 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 = 0, /* 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 */
+} ;
+
+/*==============================================================================
+ * iAFI_SAFI and qAFI_SAFI structures
+ */
+struct iAFI_SAFI
+{
+ iAFI_t afi ;
+ iSAFI_t safi ;
+} ;
+
+typedef struct iAFI_SAFI iAFI_SAFI_t ;
+typedef struct iAFI_SAFI* iAFI_SAFI ;
+
+struct qAFI_SAFI
+{
+ qAFI_t afi ;
+ qSAFI_t safi ;
+} ;
+
+typedef struct qAFI_SAFI qAFI_SAFI_t ;
+typedef struct qAFI_SAFI* qAFI_SAFI ;
+
+/*==============================================================================
+ * 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/qfstring.c b/lib/qfstring.c
new file mode 100644
index 00000000..35708640
--- /dev/null
+++ b/lib/qfstring.c
@@ -0,0 +1,1204 @@
+/* Some string handling
+ * Copyright (C) 2010 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 "qfstring.h"
+#include "zassert.h"
+#include <stdbool.h>
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise qf_str -- to given size (which includes the '\0')
+ *
+ * Sets pointers and terminates an empty string with one byte reserved for the
+ * terminating '\0'.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_init(qf_str qfs, char* str, size_t size)
+{
+ assert(size > 0) ;
+
+ qfs->str = str ;
+ qfs->end = str + size - 1 ;
+
+ *str = '\0' ;
+ qfs->ptr = str ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise qf_str which already contains string -- to given size (which
+ * includes the '\0')
+ *
+ * This may be used to prepare for appending to a buffer which already contains
+ * something.
+ *
+ * Sets pointers, setting the write pointer to the existing terminating '\0'.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_init_as_is(qf_str qfs, char* str, size_t size)
+{
+ assert(size > 0) ;
+
+ qfs->str = str ;
+ qfs->end = str + size - 1 ;
+
+ qfs->ptr = strchr(str, '\0') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminate string with the given string.
+ *
+ * If necessary, characters are discarded from the end of the string in order
+ * to fit in the terminating stuff.
+ *
+ * If the terminating stuff won't fit, as much of the end if the terminating
+ * stuff as possible is copied to the string -- displacing any existing
+ * contents.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_term(qf_str qfs, const char* src)
+{
+ int len ;
+ int excess ;
+
+ if ((src == NULL) || (*src == '\0'))
+ {
+ *qfs->ptr = '\0' ; /* should be true anyway */
+ return ;
+ } ;
+
+ len = strlen(src) ;
+ excess = qfs_len(qfs) - len ;
+ if (excess > 0)
+ {
+ if (excess <= (qfs->ptr - qfs->str))
+ qfs->ptr -= excess ;
+ else
+ {
+ int want = len ;
+ len = qfs->end - qfs->str ; /* take what can... */
+ src += (want - len) ; /* ... from the end */
+ qfs->ptr = qfs->str ;
+ } ;
+ } ;
+
+ memcpy(qfs->ptr, src, len + 1) ; /* include the '\0' */
+ qfs->ptr += len ;
+} ;
+
+/*==============================================================================
+ * Appending to the string
+ */
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the source string to the given qf_str.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append(qf_str qfs, const char* src)
+{
+ int n ;
+
+ if ((src == NULL) || (*src == '\0'))
+ return ;
+
+ n = strlen(src) ;
+
+ if (n > qfs_left(qfs))
+ n = qfs_left(qfs) ;
+
+ if (n == 0)
+ return ;
+
+ memcpy(qfs->ptr, src, n + 1) ;
+ qfs->ptr += n ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the first 'n' bytes of the source string to
+ * the given qf_str.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_n(qf_str qfs, const char* src, size_t n)
+{
+ if ((int)n > qfs_left(qfs))
+ n = qfs_left(qfs) ;
+
+ if (n <= 0)
+ return ;
+
+ memcpy(qfs->ptr, src, n) ;
+ qfs->ptr += n ;
+
+ *qfs->ptr = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append upto 'n' copies of the given character to the qf_str
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_ch_x_n(qf_str qfs, char ch, size_t n)
+{
+ if ((int)n > qfs_left(qfs))
+ n = qfs_left(qfs) ;
+
+ if (n <= 0)
+ return ;
+
+ while (n--)
+ *qfs->ptr++ = ch ;
+
+ *qfs->ptr = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the source string to the given qf_str, left or
+ * right justified to the given width.
+ *
+ * Ignores the width if the string is longer than it.
+ *
+ * Negative width => left justify.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_justified(qf_str qfs, const char* src, int width)
+{
+ size_t n ;
+
+ if ((src == NULL) || (*src == '\0'))
+ n = 0 ;
+ else
+ n = strlen(src) ;
+
+ qfs_append_justified_n(qfs, src, n, width) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the first 'n' bytes of the source string to
+ * the given qf_str, left or right justified to the given width.
+ *
+ * Ignores the width if the string is longer than it.
+ *
+ * Negative width => left justify.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width)
+{
+ if ((int)n >= abs(width))
+ width = 0 ;
+
+ if (width > 0)
+ qfs_append_ch_x_n(qfs, ' ', width - n) ;
+
+ qfs_append_n(qfs, src, n) ;
+
+ if (width < 0)
+ qfs_append_ch_x_n(qfs, ' ', - width - n) ;
+} ;
+
+/*==============================================================================
+ * Number conversion
+ */
+
+static void
+qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
+ int width, int precision) ;
+
+/*------------------------------------------------------------------------------
+ * Signed integer -- converted as per flags, width and precision.
+ *
+ * Result is appended to the given qf_str.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
+ int width, int precision)
+{
+ uintmax_t u_val ;
+ int sign ;
+
+ if (s_val < 0)
+ {
+ sign = -1 ;
+ u_val = (uintmax_t)(-(s_val + 1)) + 1 ;
+ }
+ else
+ {
+ sign = +1 ;
+ u_val = s_val ;
+ } ;
+
+ qfs_number(qfs, u_val, sign, flags & ~pf_unsigned, width, precision) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unsigned integer -- converted as per flags, width and precision.
+ *
+ * Result is appended to the given qf_str.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
+ int width, int precision)
+{
+ qfs_number(qfs, u_val, 0, flags | pf_unsigned, width, precision) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Address -- converted as per flags, width and precision.
+ *
+ * Result is appended to the given qf_str.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
+ int width, int precision)
+{
+ confirm(sizeof(uintmax_t) >= sizeof(uintptr_t)) ;
+ qfs_number(qfs, (uintptr_t)p_val, 0, flags | pf_unsigned, width, precision) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Number conversion function.
+ *
+ * All number conversion ends up here.
+ *
+ * Accepts: pf_commas -- format with commas
+ * pf_plus -- requires '+' or '-'
+ * pf_space -- requires space or '-'
+ * pf_zeros -- zero fill to width
+ * pf_alt -- add '0x' or '0X' if hex -- depending on pf_uc
+ * add '0' if octal and not zero.
+ * no effect otherwise
+ *
+ * pf_precision -- explicit precision (needed if precision == 0)
+ *
+ * pf_hex -- render in hex
+ * pf_uc -- render in upper case
+ *
+ * pf_unsigned -- value is unsigned
+ * pf_ptr -- value is a void* pointer
+ *
+ * NB: pf_hex does NOT imply pf_unsigned.
+ * pf_uc does NOT imply pf_hex
+ *
+ * If the width is < 0 -- left justify in abs(width) -- zero fill ignored
+ * == 0 -- no width -- zero fill ignored
+ * > 0 -- right justify in width -- zero filling if req.
+ *
+ * If the precision is < 0 it is ignored (unless pf_hex, see below).
+ *
+ * If the precision is 0 it is ignored unless pf_precision is set.
+ *
+ * Precedence issues:
+ *
+ * * precision comes first. Disables zero fill.
+ *
+ * * commas come before zero fill.
+ *
+ * * signs and prefixes come before zero fill
+ *
+ * * pf_plus takes precedence over pf_space
+ *
+ * * pf_unsigned or sign == 0 takes precedence over pf_plus and pf_space.
+ *
+ * For decimal output, pf_commas groups digits in 3's, separated by ','.
+ * For hex output, pf_commas groups digits in 4's, separated by '_'.
+ * For oct output, pf_commas is ignored.
+ *
+ * Note that pf_commas is a glibc extension, which does not apply to hex !
+ *
+ * For hex output if precision is:
+ *
+ * -1 set precision to multiple of 2, just long enough for the value
+ * -2 set precision to multiple of 4, just long enough for the value
+ *
+ * (under all other conditions, -ve precision is ignored).
+ *
+ * Note: if the precision is explicitly 0, and the value is 0, and no other
+ * characters are to be generated -- ie no: pf_plus, pf_space, pf_zeros,
+ * or pf_alt (with pf_hex) -- then nothing is generated.
+ *
+ * This operation is async-signal-safe.
+ */
+static void
+qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
+ int width, int precision)
+{
+ enum
+ {
+ max_bits = 256, /* size of number can convert */
+ max_digits = 90, /* could do octal ! */
+ buf_size = 128, /* buffer to use for that */
+ } ;
+
+ confirm((sizeof(uintmax_t) * 8) <= max_bits) ; /* check max_bits */
+ confirm((max_digits * 3) >= max_bits) ; /* check max_digits */
+
+ /* Buffer requires space for sign, '0x', digits, '00', commas, '\0'
+ *
+ * The '00' is for zero fill will commas, and is enough to extend the
+ * number to "000,...." -- that is, a full leading triple.
+ */
+ confirm(buf_size > (1 + 2 + max_digits + (2 + (max_digits / 3)) + 1)) ;
+
+ /* For hex commas the sum is similar, but smaller. */
+ confirm((3 + (max_digits / 4)) < (2 + (max_digits / 3))) ;
+
+ unsigned base ;
+ const char* digits ;
+ const char* radix_str ;
+ const char* sign_str ;
+ char num[buf_size] ;
+ char* p ;
+ char* e ;
+ int len ;
+ int radix_len ;
+ int sign_len ;
+ uintmax_t v ;
+
+ char comma ;
+ int interval ;
+
+ int zeros ;
+
+ static const char lc[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f' } ;
+ static const char uc[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F' } ;
+
+ /* Tidy up the options */
+ if (precision < 0)
+ {
+ if ((flags & pf_hex) && (precision >= -2))
+ {
+ /* special precision for hex output */
+ int unit = (precision == -1) ? 2 : 4 ;
+ v = val | 1 ;
+ precision = 0 ;
+ while (v != 0)
+ {
+ precision += unit ;
+ v >>= (unit * 4) ;
+ } ;
+ }
+ else
+ {
+ /* mostly, -ve precision is ignored */
+ precision = 0 ;
+ flags &= ~pf_precision ; /* ignore precision < 0 */
+ } ;
+ } ;
+
+ if (precision > 0)
+ flags |= pf_precision ; /* act on precision > 0 */
+
+ if ((flags & pf_precision) || (width <= 0))
+ flags &= ~pf_zeros ; /* turn off zero fill */
+
+ if (flags & pf_oct)
+ flags &= ~pf_commas ; /* turn off commas */
+
+ /* Set up any required sign and radix prefix */
+ if ((flags & pf_unsigned) || (sign == 0))
+ sign_str = "" ;
+ else if (sign < 0)
+ sign_str = "-" ;
+ else if (flags & pf_plus)
+ sign_str = "+" ;
+ else if (flags & pf_space)
+ sign_str = " " ;
+ else
+ sign_str = "" ;
+
+ sign_len = strlen(sign_str) ;
+
+ radix_str = "" ;
+ if (flags & pf_alt)
+ {
+ if (flags & pf_hex)
+ radix_str = (flags & pf_uc) ? "0X" : "0x" ;
+ else if ((flags & pf_oct) && (val != 0))
+ radix_str = "0" ;
+
+ confirm(pf_uc != 0) ;
+ } ;
+
+ radix_len = strlen(radix_str) ;
+
+ /* Turn off zero fill if left justify (width < 0) */
+ if (width < 0)
+ flags &= ~pf_zeros ;
+
+ /* Special case of explicit zero precision and value == 0 */
+ if ((flags & pf_precision) && (precision == 0) && (val == 0))
+ {
+ if (((flags & pf_zeros) == 0) && (sign_len == 0) && (radix_len == 0))
+ {
+ qfs_append_justified_n(qfs, NULL, 0, width) ;
+ return ;
+ } ;
+ } ;
+
+ /* Start with the basic digit conversion. */
+ base = 10 ;
+ if (flags & pf_hex)
+ base = 16 ;
+ else if (flags & pf_oct)
+ base = 8 ;
+
+ digits = (flags & pf_uc) ? uc : lc ;
+ confirm(pf_uc != 0) ;
+
+ e = p = num + sizeof(num) - 1 ;
+ *p = '\0' ;
+ v = val ;
+ do
+ {
+ *--p = digits[v % base] ;
+ v /= base ;
+ } while ((v > 0) && (p > num)) ;
+
+ assert(v == 0) ;
+
+ len = e - p ;
+
+ /* Worry about the precision */
+ while ((precision > len) && (len < max_digits))
+ {
+ *--p = '0' ;
+ ++len ;
+ } ;
+
+ /* Worry about commas */
+ comma = (flags & pf_hex) ? '_' : ',' ;
+ interval = (flags & pf_hex) ? 4 : 3 ;
+
+ if (flags & pf_commas)
+ {
+ int c ;
+ int t ;
+ char* cq ;
+ char* cp ;
+
+ c = (len - 1) / interval ; /* number of commas to insert */
+ t = len % interval ; /* digits before first comma */
+ if (t == 0)
+ t = interval ;
+
+ len += c ; /* account for the commas */
+
+ cq = p ;
+ p -= c ;
+ cp = p ;
+
+ assert(p > num) ;
+
+ while (c--)
+ {
+ while (t--)
+ *cp++ = *cq++ ;
+ *cp++ = comma ;
+ } ;
+
+ assert(len == (e - p)) ;
+
+ /* commas and zero fill interact. Here fill the leading group. */
+ zeros = width - (sign_len + radix_len + len) ;
+ if ((flags & pf_zeros) && (zeros > 0))
+ {
+ int group_fill = interval - (len % (interval + 1)) ;
+ assert(group_fill < interval) ;
+ if (group_fill > zeros)
+ group_fill = zeros ;
+
+ len += group_fill ;
+ while (group_fill--)
+ {
+ assert(p > num) ;
+ *--p = '0' ;
+ } ;
+ } ;
+ } ;
+
+ assert(len == (e - p)) ;
+
+ /* See if still need to worry about zero fill */
+ zeros = width - (sign_len + radix_len + len) ;
+ if ((flags & pf_zeros) && (zeros > 0))
+ {
+ /* Need to insert zeros and possible commas between sign and radix
+ * and the start of the number.
+ *
+ * Note that for commas the number has been arranged to have a full
+ * leading group.
+ *
+ * The width can be large... so do this by appending any sign and
+ * radix to the qf_str, and then the required leading zeros (with or
+ * without commas).
+ */
+ if (sign_len != 0)
+ qfs_append_n(qfs, sign_str, sign_len) ;
+
+ if (radix_len != 0)
+ qfs_append_n(qfs, radix_str, radix_len) ;
+
+ if (flags & pf_commas)
+ {
+ /* Leading zeros with commas !
+ *
+ * Start with ',', '0,', '00,' etc to complete the first group.
+ * Thereafter add complete groups.
+ */
+ int g ;
+ int r ;
+ g = (zeros + interval - 1) / (interval + 1) ;
+ r = (zeros - 1) % (interval + 1) ;
+
+ if (r == 0)
+ {
+ qfs_append_ch_x_n(qfs, comma, 1) ;
+ r = interval ;
+ }
+
+ while (g--)
+ {
+ qfs_append_ch_x_n(qfs, '0', r) ;
+ qfs_append_ch_x_n(qfs, comma, 1) ;
+ r = interval ;
+ } ;
+ }
+ else
+ qfs_append_ch_x_n(qfs, '0', zeros) ;
+
+ width = 0 ; /* have dealt with the width. */
+ }
+ else
+ {
+ /* No leading zeros, so complete the number by adding any sign
+ * and radix.
+ */
+ char* cp ;
+
+ p -= sign_len + radix_len ;
+ len += sign_len + radix_len ;
+ assert(p >= num) ;
+
+ cp = p ;
+ while (sign_len--)
+ *cp++ = *sign_str++ ;
+ while (radix_len--)
+ *cp++ = *radix_str++ ;
+ } ;
+
+ /* Finally, can append the number -- respecting any remaining width */
+ assert(len == (e - p)) ;
+
+ qfs_append_justified_n(qfs, p, len, width) ;
+} ;
+
+/*==============================================================================
+ * printf() and vprintf() type functions
+ */
+
+enum pf_phase
+{
+ pfp_null, /* in ascending order */
+ pfp_flags,
+ pfp_width,
+ pfp_precision,
+ pfp_int_type,
+ pfp_float_type,
+
+ pfp_done,
+ pfp_failed
+} ;
+
+CONFIRM(pfp_float_type > pfp_int_type) ;
+
+/* Number types for printing */
+enum arg_num_type
+{
+ ant_char, /* hh */
+ ant_short, /* h */
+ ant_int, /* default */
+ ant_long, /* l */
+ ant_long_long, /* ll */
+ ant_intmax_t, /* j */
+ ant_size_t, /* z */
+ ant_ptr_t, /* void* */
+ ant_long_double, /* L for float */
+
+ ant_default = ant_int,
+};
+
+static enum pf_phase qfs_arg_string(qf_str qfs, const char* src,
+ enum pf_flags flags, int width, int precision) ;
+static enum pf_phase qfs_arg_char(qf_str qfs, char ch,
+ enum pf_flags flags, int width, int precision) ;
+static enum pf_phase qfs_arg_integer(qf_str qfs, va_list* p_va,
+ enum pf_flags flags, int width, int precision, enum arg_num_type ant) ;
+static enum pf_phase qfs_arg_float(qf_str qfs, va_list* p_va,
+ const char* start, size_t flen, enum arg_num_type ant) ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qf_str -- cf printf()
+ *
+ * This operation is async-signal-safe -- EXCEPT for floating point values.
+ */
+extern void
+qfs_printf(qf_str qfs, const char* format, ...)
+{
+ va_list va ;
+
+ va_start (va, format);
+ qfs_vprintf(qfs, format, va);
+ va_end (va);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qf_str -- cf vprintf()
+ *
+ * This operation is async-signal-safe -- EXCEPT for floating point values.
+ *
+ * Operates on a copy of the va_list -- so the original is *unchanged*.
+ */
+extern void
+qfs_vprintf(qf_str qfs, const char *format, va_list va)
+{
+ va_list vac ;
+
+ if (format == NULL)
+ return ;
+
+ va_copy(vac, va) ;
+
+ while ((qfs->ptr < qfs->end) && (*format != '\0'))
+ {
+ /* Have space for one byte and current format byte is not '\0' */
+ if (*format != '%')
+ *qfs->ptr++ = *format++ ;
+ else
+ {
+ const char* start = format++ ; /* start points at the '%' ...
+ ... step past it now */
+ bool star = false ;
+ bool digit = false ;
+ int d = 0 ;
+ int width_sign = +1 ;
+ int width = 0 ;
+ int precision = 0 ;
+ enum arg_num_type ant = ant_default ;
+ enum pf_flags flags = pf_none ;
+ enum pf_phase phase = pfp_null ;
+
+ while (phase < pfp_done)
+ {
+ switch (*format++) /* get next and step past it */
+ {
+ case '%': /* %% only */
+ if (phase == pfp_null)
+ *qfs->ptr++ = '%' ;
+ phase = (phase == pfp_null) ? pfp_done : pfp_failed ;
+ break ;
+
+ case '\'':
+ flags |= pf_commas ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '-':
+ width_sign = -1 ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '+':
+ flags |= pf_plus ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '#':
+ flags |= pf_alt ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case ' ':
+ flags |= pf_space ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '0':
+ if (phase <= pfp_flags)
+ {
+ flags |= pf_zeros ;
+ phase = pfp_flags ;
+ break ;
+ } ;
+ /* fall through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ d = *(format - 1) - '0' ;
+ if (!star && (phase <= pfp_width))
+ {
+ phase = pfp_width ;
+ width = (width * 10) + (d * width_sign) ;
+ }
+ else if (!star && (phase == pfp_precision))
+ precision = (precision * 10) + d ;
+ else
+ phase = pfp_failed ;
+
+ digit = true ;
+ break ;
+
+ case '*':
+ if (!star && !digit && (phase <= pfp_width))
+ {
+ phase = pfp_width ;
+ width = va_arg(vac, int) ;
+ }
+ else if (!star && !digit && (phase == pfp_precision))
+ {
+ precision = va_arg(vac, int) ;
+ if (precision < 0)
+ {
+ precision = 0 ;
+ flags &= ~pf_precision ; /* completely ignore */
+ } ;
+ }
+ else
+ phase = pfp_failed ;
+
+ star = true ;
+ break ;
+
+ case '.':
+ phase = (phase < pfp_precision) ? pfp_precision : pfp_failed ;
+ flags |= pf_precision ;
+ precision = 0 ;
+ break ;
+
+ case 'l': /* 1 or 2 'l', not 'h', 'j' or 'z' */
+ phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ;
+ if (ant == ant_default)
+ ant = ant_long ;
+ else if (ant == ant_long)
+ ant = ant_long_long ;
+ else
+ phase = pfp_failed ;
+ break ;
+
+ case 'h': /* 1 or 2 'h', not 'l', 'j' or 'z' */
+ phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ;
+ if (ant == ant_default)
+ ant = ant_short ;
+ else if (ant == ant_short)
+ ant = ant_char ;
+ else
+ phase = pfp_failed ;
+ break ;
+
+ case 'j': /* 1 'j', not 'h', 'l' or 'z' */
+ phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ;
+ ant = ant_intmax_t ;
+ break ;
+
+ case 'z': /* 1 'z', not 'h', 'l' or 'j' */
+ phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ;
+ ant = ant_size_t ;
+ break ;
+
+ case 'L': /* 1 'L', not for integers ! */
+ phase = (phase < pfp_int_type) ? pfp_float_type : pfp_failed ;
+ ant = ant_long_double ;
+ break ;
+
+ case 's':
+ if (phase == pfp_int_type)
+ phase = pfp_failed ; /* don't do 'l' etc. */
+ else
+ phase = qfs_arg_string(qfs, va_arg(vac, char*),
+ flags, width, precision) ;
+ break ;
+
+ case 'c':
+ if (phase == pfp_int_type)
+ phase = pfp_failed ; /* don't do 'l' etc. */
+ else
+ phase = qfs_arg_char(qfs, (char)va_arg(vac, int),
+ flags, width, precision) ;
+ break ;
+
+ case 'd':
+ case 'i':
+ phase = qfs_arg_integer(qfs, &vac, flags, width, precision,
+ ant) ;
+ break ;
+
+ case 'u':
+ phase = qfs_arg_integer(qfs, &vac, flags | pf_unsigned, width,
+ precision, ant) ;
+ break ;
+
+ case 'o':
+ phase = qfs_arg_integer(qfs, &vac, flags | pf_oct, width,
+ precision, ant) ;
+ break ;
+
+ case 'x':
+ phase = qfs_arg_integer(qfs, &vac, flags | pf_hex_x, width,
+ precision, ant) ;
+ break ;
+
+ case 'X':
+ phase = qfs_arg_integer(qfs, &vac, flags | pf_hex_X, width,
+ precision, ant) ;
+ break ;
+
+ case 'p':
+ if (phase == pfp_int_type)
+ phase = pfp_failed ;
+ else
+ phase = qfs_arg_integer(qfs, &vac, flags | pf_void_p, width,
+ precision, ant_ptr_t) ;
+ break ;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ case 'a':
+ case 'A':
+ if (phase == pfp_int_type)
+ phase = pfp_failed ;
+ else
+ phase = qfs_arg_float(qfs, &vac, start, format - start,
+ ant) ;
+ break ;
+
+ default: /* unrecognised format */
+ phase = pfp_failed ;
+ break ;
+ } ;
+ } ;
+
+ if (phase == pfp_failed)
+ {
+ format = start ; /* back to the start */
+ *qfs->ptr++ = *format++ ; /* copy the '%' */
+ } ;
+ } ;
+ } ;
+
+ *qfs->ptr = '\0' ;
+
+ va_end(vac) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %s handler -- tolerates NULL pointer
+ *
+ * Accepts: width
+ * precision -- ignored if < 0
+ * pf_precision -- explicit precision
+ *
+ * Rejects: pf_commas -- "'" seen
+ * pf_plus -- "+" seen
+ * pf_space -- " " seen
+ * pf_zeros -- "0" seen
+ * pf_alt -- "#" seen
+ *
+ * Won't get: pf_hex
+ * pf_uc
+ * pf_unsigned
+ * pf_ptr
+ *
+ * This operation is async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags,
+ int width, int precision)
+{
+ int len ;
+
+ if (flags != (flags & pf_precision))
+ return pfp_failed ;
+
+ if (precision < 0) /* make sure */
+ {
+ precision = 0 ;
+ flags &= ~pf_precision ;
+ } ;
+
+ len = (src != NULL) ? strlen(src) : 0 ;
+ if (((precision > 0) || (flags & pf_precision)) && (len > precision))
+ len = precision ;
+
+ qfs_append_justified_n(qfs, src, len, width) ;
+
+ return pfp_done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %c handler
+ *
+ * Accepts: width
+ *
+ * Rejects: precision
+ * pf_precision -- explicit precision
+ * pf_commas -- "'" seen
+ * pf_plus -- "+" seen
+ * pf_space -- " " seen
+ * pf_zeros -- "0" seen
+ * pf_alt -- "#" seen
+ *
+ * Won't get: pf_hex
+ * pf_uc
+ * pf_unsigned
+ * pf_ptr
+ *
+ * This operation is async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision)
+{
+ if ((flags != 0) || (precision != 0))
+ return pfp_failed ;
+
+ qfs_append_justified_n(qfs, (char*)&ch, 1, width) ;
+
+ return pfp_done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %d, %i, %u, %o, %x, %X and %p handler
+ *
+ * Accepts: pf_commas -- format with commas or '_' for hex (non-standard)
+ * ignored for octal.
+ * pf_minus -- left justify (any width will be -ve)
+ * pf_plus -- requires sign
+ * pf_space -- requires space or '-'
+ * pf_zeros -- zero fill to width
+ * pf_alt -- '0x' or '0X' for hex
+ * '0' for octal
+ *
+ * pf_precision -- precision specified
+ *
+ * pf_unsigned -- value is unsigned
+ * pf_ptr -- value is a void* pointer
+ * pf_hex -- render in hex
+ * pf_uc -- render hex in upper case
+ *
+ * and: all the number argument types.
+ *
+ * Rejects: ant == ant_long_double -- which is how the parser spots an
+ * erroneous %Ld for example.
+ *
+ * This operation is async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags,
+ int width, int precision, enum arg_num_type ant)
+{
+ uintmax_t u_val ;
+ intmax_t s_val ;
+
+ /* Reject if seen an 'L'
+ */
+ if (ant == ant_long_double)
+ return pfp_failed ;
+
+ /* Special for hex with '0... if no explicit precision, set -1 for byte
+ * and -2 for everything else -- see qfs_number().
+ */
+ if ((flags & (pf_hex | pf_precision)) == pf_hex)
+ {
+ if ((flags & (pf_commas | pf_zeros)) == (pf_commas | pf_zeros))
+ {
+ precision = (ant == ant_char) ? -1 : -2 ;
+ flags |= pf_precision ;
+ } ;
+ } ;
+
+ /* It is assumed that all values can be mapped to a uintmax_t */
+ confirm(sizeof(uintmax_t) >= sizeof(uintptr_t)) ;
+
+ if (flags & pf_unsigned)
+ {
+ switch (ant)
+ {
+ case ant_char:
+ case ant_short:
+ case ant_int:
+ u_val = va_arg(*p_va, unsigned int) ;
+ break ;
+
+ case ant_long:
+ u_val = va_arg(*p_va, unsigned long) ;
+ break ;
+
+ case ant_long_long:
+ u_val = va_arg(*p_va, unsigned long long) ;
+ break ;
+
+ case ant_intmax_t:
+ u_val = va_arg(*p_va, uintmax_t) ;
+ break ;
+
+ case ant_size_t:
+ u_val = va_arg(*p_va, size_t) ;
+ break ;
+
+ case ant_ptr_t:
+ u_val = va_arg(*p_va, uintptr_t) ;
+ break ;
+
+ default:
+ zabort("impossible integer size") ;
+ } ;
+
+ qfs_unsigned(qfs, u_val, flags, width, precision) ;
+ }
+ else
+ {
+ switch (ant)
+ {
+ case ant_char:
+ case ant_short:
+ case ant_int:
+ s_val = va_arg(*p_va, signed int) ;
+ break ;
+
+ case ant_long:
+ s_val = va_arg(*p_va, signed long) ;
+ break ;
+
+ case ant_long_long:
+ s_val = va_arg(*p_va, signed long long) ;
+ break ;
+
+ case ant_intmax_t:
+ s_val = va_arg(*p_va, intmax_t) ;
+ break ;
+
+ case ant_size_t:
+ s_val = va_arg(*p_va, ssize_t) ;
+ break ;
+
+ case ant_ptr_t:
+ s_val = va_arg(*p_va, intptr_t) ;
+ break ;
+
+ default:
+ zabort("impossible integer size") ;
+ } ;
+
+ qfs_signed(qfs, s_val, flags, width, precision) ;
+ } ;
+
+ return pfp_done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %e, %E, %f, %F, %g, %G, %a and %A handler
+ *
+ * This uses the standard library sprintf() to do the business, so this is
+ * NOT async-signal-safe. This means that we get the full precision supported
+ * by the system ! Attempting to construct async-signal-safe conversion is
+ * doomed to failure, because any floating point operation may affect flags
+ * and other state in the processor :-(
+ *
+ * This operation is *NOT* async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_float(qf_str qfs, va_list* p_va, const char* start, size_t flen,
+ enum arg_num_type ant)
+{
+ char format[flen + 1] ;
+ int want ;
+ int have ;
+
+ memcpy(format, start, flen) ;
+ format[flen + 1] = '\0' ;
+
+ have = qfs_left(qfs) + 1 ;
+
+ if (ant == ant_default)
+ {
+ double val ;
+ val = va_arg(*p_va, double) ;
+ want = snprintf(qfs_ptr(qfs), have, format, val) ;
+ }
+ else
+ {
+ long double val ;
+ assert(ant == ant_long_double) ;
+ val = va_arg(*p_va, long double) ;
+ want = snprintf(qfs_ptr(qfs), have, format, val) ;
+ } ;
+
+ if (want < 0)
+ return pfp_failed ;
+
+ if (want < have)
+ qfs->ptr += want ;
+ else
+ qfs->ptr = qfs->end ;
+
+ return pfp_done ;
+} ;
diff --git a/lib/qfstring.h b/lib/qfstring.h
new file mode 100644
index 00000000..d9a51d21
--- /dev/null
+++ b/lib/qfstring.h
@@ -0,0 +1,163 @@
+/* Some string 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 _ZEBRA_QFSTRING_H
+#define _ZEBRA_QFSTRING_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/* GCC have printf type attribute check. */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/*==============================================================================
+ * These "qfstring" address the issues of dealing with *fixed* length
+ * strings, particularly where the string handling must be async-signal-safe.
+ *
+ * All operations that can possibly be async-signal-safe, are. Notable
+ * exception is anything involving floating point values -- because of the
+ * state contain in floating point status/option registers !
+ */
+
+typedef struct qf_str qf_str_t ;
+typedef struct qf_str* qf_str ;
+
+/* When initialised a qf_string is set:
+ *
+ * str = start of string -- and this is never changed
+ * ptr = start of string -- this is moved as stuff is appended
+ * end = last possible position for terminating '\0'
+ * -- and this is never changed
+ */
+struct qf_str
+{
+ char* str ; /* start of string */
+ char* ptr ; /* current position */
+ char* end ; /* end of string */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Print format flags for number printing
+ */
+enum pf_flags
+{
+ pf_none = 0,
+
+ /* The following correspond to the "flags" */
+ pf_commas = 1 << 0, /* "'" seen */
+ pf_plus = 1 << 1, /* "+" seen */
+ pf_space = 1 << 2, /* " " seen */
+ pf_zeros = 1 << 3, /* "0" seen */
+ pf_alt = 1 << 4, /* "#" seen */
+
+ pf_precision = 1 << 7, /* '.' seen */
+
+ /* The following signal how to render the value */
+ pf_oct = 1 << 8, /* octal */
+ pf_hex = 1 << 9, /* hex */
+ pf_uc = 1 << 10, /* upper-case */
+
+ /* The following signal the type of value */
+ pf_ptr = 1 << 14, /* is a pointer */
+ pf_unsigned = 1 << 15, /* unsigned value */
+
+ /* Common combination */
+ pf_hex_x = pf_unsigned | pf_hex,
+ pf_hex_X = pf_unsigned | pf_hex | pf_uc,
+
+ pf_void_p = pf_ptr | pf_hex_x,
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern void qfs_init(qf_str qfs, char* str, size_t size) ;
+extern void qfs_init_as_is(qf_str qfs, char* str, size_t size) ;
+
+extern void qfs_term(qf_str qfs, const char* src) ;
+
+Inline int qfs_len(qf_str qfs) ;
+Inline void* qfs_ptr(qf_str qfs) ;
+Inline int qfs_left(qf_str qfs) ;
+
+extern void qfs_append(qf_str qfs, const char* src) ;
+extern void qfs_append_n(qf_str qfs, const char* src, size_t n) ;
+
+extern void qfs_append_ch_x_n(qf_str qfs, char ch, size_t n) ;
+extern void qfs_append_justified(qf_str qfs, const char* src, int width) ;
+extern void qfs_append_justified_n(qf_str qfs, const char* src,
+ size_t n, int width) ;
+
+extern void qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
+ int width, int precision) ;
+extern void qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
+ int width, int precision) ;
+extern void qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
+ int width, int precision) ;
+
+extern void qfs_printf(qf_str qfs, const char* format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern void qfs_vprintf(qf_str qfs, const char *format, va_list args) ;
+
+/*==============================================================================
+ * The Inline functions.
+ */
+
+/*------------------------------------------------------------------------------
+ * Current length of qf_str, not counting the terminating '\0'.
+ */
+Inline int
+qfs_len(qf_str qfs)
+{
+ return qfs->ptr - qfs->str ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Address of the terminating '\0'.
+ */
+Inline void*
+qfs_ptr(qf_str qfs)
+{
+ return qfs->ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Current space left in the qstr, given what has been reserved for terminating
+ * '\0' and any other reservation.
+ */
+Inline int
+qfs_left(qf_str qfs)
+{
+ return qfs->end - qfs->ptr ;
+} ;
+
+#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qiovec.c b/lib/qiovec.c
new file mode 100644
index 00000000..546dfcb0
--- /dev/null
+++ b/lib/qiovec.c
@@ -0,0 +1,261 @@
+/* Flexible iovec handler
+ * Copyright (C) 2010 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 "memory.h"
+#include "zassert.h"
+#include "miyagi.h"
+
+#include "qiovec.h"
+
+/*==============================================================================
+ * Initialise, allocate and reset qiovec
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise new qiovec -- allocate if required.
+ *
+ * This is for initialising a new structure. Any pre-exiting contents are
+ * lost.
+ *
+ * Returns: address of qiovec
+ */
+extern qiovec
+qiovec_init_new(qiovec viov)
+{
+ if (viov == NULL)
+ viov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ;
+ else
+ memset(viov, 0, sizeof(struct qiovec)) ;
+
+ /* Zeroising has set:
+ *
+ * vec = NULL - no array, yet
+ * writing = false -- no writing going on
+ *
+ * i_get = 0 -- next entry to get
+ * i_put = 0 -- next entry to put
+ *
+ * i_alloc = 0; -- no entries allocated
+ *
+ * Nothing more is required.
+ */
+
+ return viov ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset qiovec (if any) -- release body and (if required) the structure.
+ *
+ * Returns: address of qiovec (if any) -- NULL if structure released
+ */
+extern qiovec
+qiovec_reset(qiovec viov, bool free_structure)
+{
+ if (viov != NULL)
+ {
+ if (viov->vec != NULL)
+ XFREE(MTYPE_QIOVEC_VEC, viov->vec) ;
+
+ if (free_structure)
+ XFREE(MTYPE_QIOVEC, viov) ; /* sets viov = NULL */
+ else
+ qiovec_init_new(viov) ; /* re-initialise */
+ } ;
+
+ return viov ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear given qiovec.
+ */
+extern void
+qiovec_clear(qiovec viov)
+{
+ viov->i_get = 0 ;
+ viov->i_put = 0 ;
+ viov->writing = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push item to given qiovec
+ *
+ * NB: avoids pushing zero length items.
+ */
+extern void
+qiovec_push(qiovec viov, const void* base, size_t len)
+{
+ struct iovec* p_iov ;
+
+ if (len == 0)
+ return ;
+
+ if (viov->i_put >= viov->i_alloc)
+ {
+ size_t size ;
+ assert(viov->i_put == viov->i_alloc) ;
+
+ assert( ((viov->i_alloc == 0) && (viov->vec == NULL))
+ || ((viov->i_alloc != 0) && (viov->vec != NULL)) ) ;
+
+ if (viov->i_get > 200) /* keep in check */
+ {
+ size = (viov->i_put - viov->i_get) * sizeof(struct iovec) ;
+ if (size != 0)
+ memmove(viov->vec, &viov->vec[viov->i_get], size) ;
+ viov->i_put -= viov->i_get ;
+ viov->i_get = 0 ;
+ }
+ else
+ {
+ viov->i_alloc += 100 ; /* a sizable chunk */
+
+ size = viov->i_alloc * sizeof(struct iovec) ;
+ if (viov->vec == NULL)
+ viov->vec = XMALLOC(MTYPE_QIOVEC_VEC, size) ;
+ else
+ viov->vec = XREALLOC(MTYPE_QIOVEC_VEC, viov->vec, size) ;
+ } ;
+ } ;
+
+ p_iov = &viov->vec[viov->i_put++] ;
+
+ p_iov->iov_base = miyagi(base) ;
+ p_iov->iov_len = len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write given qiovec -- assuming NON-BLOCKING.
+ *
+ * Does nothing if the qiovec is empty.
+ *
+ * Loops internally if gets EINTR.
+ *
+ * When there is nothing left to output, resets the i_put & i_get to zero.
+ *
+ * Returns: > 0 => one or more bytes left to output
+ * 0 => all done -- zero bytes left to output
+ * -1 => failed -- see errno
+ */
+extern int
+qiovec_write_nb(int fd, qiovec viov)
+{
+ int n ;
+ int l ;
+
+ n = viov->i_put - viov->i_get ;
+
+ l = iovec_write_nb(fd, &viov->vec[viov->i_get], n) ;
+
+ if (l == 0)
+ {
+ viov->writing = 0 ;
+ viov->i_get = viov->i_put = 0 ;
+ }
+ else
+ {
+ viov->writing = 1 ;
+ viov->i_get += (n - l) ;
+ } ;
+
+ return l ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write given iovec -- assuming NON-BLOCKING.
+ *
+ * Does nothing if given zero iovec entries (and array may be NULL).
+ *
+ * Loops internally if gets EINTR.
+ *
+ * If does not manage to write everything, then:
+ *
+ * -- updates the length field of all entries up to and including the
+ * last one for which data has been written.
+ *
+ * -- updates the address field of the first entry that still has some
+ * data to be output.
+ *
+ * Can call this again with the same 'p_iov' and the same 'n' -- the entries
+ * which have zero lengths will be stepped over. Output will continue from
+ * where it left off.
+ *
+ * Alternatively, if this returns 'l', then do "p_iov += n - l", and set
+ * "n = l" before calling this again.
+ *
+ * Returns: > 0 => number of entries left to output
+ * 0 => all done -- nothing left to output
+ * -1 => failed -- see errno
+ */
+extern int
+iovec_write_nb(int fd, struct iovec p_iov[], int n)
+{
+ ssize_t ret ;
+
+ assert(n >= 0) ;
+
+ /* Skip past any leading zero length entries */
+ while ((n > 0) && (p_iov->iov_len == 0))
+ {
+ ++p_iov ;
+ --n ;
+ } ;
+
+ while (n > 0)
+ {
+ ret = writev(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
+
+ if (ret > 0)
+ {
+ while (ret > 0)
+ {
+ if (ret >= (ssize_t)p_iov->iov_len)
+ {
+ assert(n > 0) ;
+ ret -= p_iov->iov_len ;
+ p_iov->iov_len = 0 ;
+ ++p_iov ;
+ --n ;
+ }
+ else
+ {
+ p_iov->iov_base = (char*)p_iov->iov_base + ret ;
+ p_iov->iov_len -= ret ;
+ ret = 0 ;
+ } ;
+ } ;
+ }
+ else if (ret == 0)
+ break ; /* not sure can happen... but
+ cannot assume will go away */
+ else
+ {
+ int err = errno ;
+ if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ break ;
+ if (err != EINTR)
+ return -1 ; /* failed */
+ } ;
+ } ;
+
+ return n ;
+} ;
diff --git a/lib/qiovec.h b/lib/qiovec.h
new file mode 100644
index 00000000..ee03d2f2
--- /dev/null
+++ b/lib/qiovec.h
@@ -0,0 +1,99 @@
+/* Flexible iovec -- header
+ * Copyright (C) 2010 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 _ZEBRA_QIOVEC_H
+#define _ZEBRA_QIOVEC_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Flexible size "struct iovec"
+ *
+ * NB: a completely zero structure is a valid, empty qiovec.
+ */
+typedef struct qiovec* qiovec ;
+typedef struct qiovec qiovec_t ;
+struct qiovec
+{
+ struct iovec* vec ; /* the actual iovec array */
+
+ bool writing ; /* started, but not finished */
+
+ unsigned i_get ; /* next entry to get */
+ unsigned i_put ; /* next entry to put */
+
+ unsigned i_alloc ; /* number of entries allocated */
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qiovec qiovec_init_new(qiovec qiov) ;
+extern qiovec qiovec_reset(qiovec qiov, bool free_structure) ;
+
+#define qiovec_reset_keep(qiov) qiovec_reset(qiov, 0)
+#define qiovec_reset_free(qiov) qiovec_reset(qiov, 1)
+
+Inline bool qiovec_empty(qiovec qiov) ;
+extern void qiovec_clear(qiovec qiov) ;
+extern void qiovec_push(qiovec qiov, const void* base, size_t len) ;
+extern int qiovec_write_nb(int fd, qiovec qiov) ;
+
+extern int iovec_write_nb(int fd, struct iovec* p_iov, int n) ;
+Inline void iovec_set(struct iovec* p_iov, const void* base, size_t len) ;
+
+/*------------------------------------------------------------------------------
+ * Is given qiov empty ?
+ *
+ * NB: arranges to never add zero length entries to the iovec vector, so
+ * is empty when there are no active entries.
+ */
+Inline bool
+qiovec_empty(qiovec qiov)
+{
+ return (qiov->i_get == qiov->i_put) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set a given struct iovec
+ *
+ * Gets around the fact that the standard structure does not have a const
+ * pointer !
+ */
+#include "miyagi.h"
+
+Inline void
+iovec_set(struct iovec* p_iov, const void* base, size_t len)
+{
+ p_iov->iov_base = miyagi(base) ;
+ p_iov->iov_len = len ;
+} ;
+
+#endif /* _ZEBRA_QIOVEC_H */
diff --git a/lib/qlib_init.c b/lib/qlib_init.c
new file mode 100644
index 00000000..c44de575
--- /dev/null
+++ b/lib/qlib_init.c
@@ -0,0 +1,98 @@
+/* Quagga library initialise/closedown -- 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 "qlib_init.h"
+#include "zassert.h"
+#include "memory.h"
+#include "qpthreads.h"
+#include "qpselect.h"
+#include "thread.h"
+#include "privs.h"
+#include "mqueue.h"
+#include "pthread_safe.h"
+
+/*==============================================================================
+ * Quagga Library Initialise/Closedown
+ *
+ * This gathers together the essential initialisation and closedown for the
+ * library. This ensures that any changes in the library are contained here,
+ * and do not require changes in all users of the library.
+ *
+ * There are two stages of initialisation:
+ *
+ * 1) first stage
+ *
+ * this is expected to be called before the program does anything at all.
+ *
+ * This performs all initialisation required to support asserts, logging,
+ * basic I/O (but not the remote console), trap signals... and so on.
+ *
+ * After this has been done, the system is in good shape to deal with
+ * command line options, configuration files and so on.
+ *
+ * 2) second stage
+ *
+ * this is expected to be called before the program does any serious work.
+ *
+ * This performs all initialisation required to support socket I/O,
+ * thread handling, timers, and so on.
+ *
+ * In particular, at this stage the system is set into Pthread Mode, if
+ * required. No pthreads may be started before this. Up to this point
+ * the system operates in non-Pthread Mode -- all mutexes are implicitly
+ * free.
+ *
+ * There is one stage of closedown. This is expected to be called last, and
+ * is passed the exit code.
+ *
+ *
+ */
+
+void
+qlib_init_first_stage(void)
+{
+ qps_start_up() ;
+}
+
+void
+qlib_init_second_stage(int pthreads)
+{
+ qpt_set_qpthreads_enabled(pthreads);
+ memory_init_r();
+ thread_init_r();
+ zprivs_init_r();
+ mqueue_initialise();
+ safe_init_r();
+}
+
+
+void
+qexit(int exit_code)
+{
+ safe_finish();
+ mqueue_finish();
+ zprivs_finish();
+ thread_finish();
+ memory_finish();
+ exit (exit_code);
+}
+
+
diff --git a/lib/qlib_init.h b/lib/qlib_init.h
new file mode 100644
index 00000000..0d5fbd7e
--- /dev/null
+++ b/lib/qlib_init.h
@@ -0,0 +1,40 @@
+/* Quagga library initialise/closedown -- 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 _ZEBRA_QLIB_INIT_H
+#define _ZEBRA_QLIB_INIT_H
+
+/*==============================================================================
+ * Quagga Library Initialise/Closedown
+ *
+ * This gathers together the essential initialisation and closedown for the
+ * library.
+ *
+ * See qlib_init.c.
+ */
+
+extern void qlib_init_first_stage(void) ;
+
+extern void qlib_init_second_stage(int pthreads) ;
+
+extern void qexit(int exit_code) ;
+
+#endif /* _ZEBRA_QLIB_INIT_H */
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
new file mode 100644
index 00000000..3b014be2
--- /dev/null
+++ b/lib/qpnexus.c
@@ -0,0 +1,327 @@
+/* Quagga Pthreads support -- 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 <zebra.h>
+#include <stdbool.h>
+
+#include "qpnexus.h"
+#include "memory.h"
+#include "thread.h"
+#include "sigevent.h"
+
+/* prototypes */
+static void* qpn_start(void* arg);
+static void qpn_in_thread_init(qpn_nexus qpn);
+
+/*==============================================================================
+ * Quagga Nexus Interface -- qpn_xxxx
+ *
+
+ */
+
+/*==============================================================================
+ * Initialisation, add hook, free etc.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a nexus -- allocating it if required.
+ *
+ * If main_thread is set then no new thread will be created
+ * when qpn_exec() is called, instead the finite state machine will be
+ * run in the calling thread. The main thread will only block the
+ * message queue's signal. Non-main threads will block most signals.
+ *
+ * Returns the qpn_nexus.
+ */
+extern qpn_nexus
+qpn_init_new(qpn_nexus qpn, bool main_thread)
+{
+ if (qpn == NULL)
+ qpn = XCALLOC(MTYPE_QPN_NEXUS, sizeof(struct qpn_nexus)) ;
+ else
+ memset(qpn, 0, sizeof(struct qpn_nexus)) ;
+
+ qpn->selection = qps_selection_init_new(qpn->selection);
+ qpn->pile = qtimer_pile_init_new(qpn->pile);
+ qpn->queue = mqueue_init_new(qpn->queue, mqt_signal_unicast);
+ qpn->main_thread = main_thread;
+ qpn->start = qpn_start;
+
+ if (main_thread)
+ qpn->thread_id = qpt_thread_self();
+
+ return qpn;
+}
+
+/*------------------------------------------------------------------------------
+ * Add a hook function to the given nexus.
+ */
+extern void
+qpn_add_hook_function(qpn_hook_list list, void* hook)
+{
+ passert(list->count < qpn_hooks_max) ;
+ list->hooks[list->count++] = hook ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset given nexus and, if required, free the nexus structure.
+ *
+ * Free timers, selection, message queue and its thread signal.
+ *
+ * Leaves all pointers to these things NULL -- which generally means that the
+ * object is empty or otherwise out of action.
+ */
+extern qpn_nexus
+qpn_reset(qpn_nexus qpn, bool free_structure)
+{
+ qps_file qf;
+ qtimer qtr;
+
+ if (qpn == NULL)
+ return NULL;
+
+ /* timers and the pile */
+ if (qpn->pile != NULL)
+ {
+ while ((qtr = qtimer_pile_ream(qpn->pile, 1)))
+ qtimer_free(qtr);
+ qpn->pile = NULL ;
+ }
+
+ /* files and selection */
+ if (qpn->selection != NULL)
+ {
+ while ((qf = qps_selection_ream(qpn->selection, 1)))
+ qps_file_free(qf);
+ qpn->selection = NULL ;
+ }
+
+ if (qpn->queue != NULL)
+ qpn->queue = mqueue_reset(qpn->queue, 1);
+
+ if (qpn->mts != NULL)
+ qpn->mts = mqueue_thread_signal_reset(qpn->mts, 1);
+
+ if (free_structure)
+ XFREE(MTYPE_QPN_NEXUS, qpn) ; /* sets qpn = NULL */
+
+ return qpn ;
+} ;
+
+/*==============================================================================
+ * Execution of a nexus
+ */
+
+/*------------------------------------------------------------------------------
+ * If not main qpthread create new qpthread.
+ *
+ * For all qpthreads: start the thread !
+ */
+extern void
+qpn_exec(qpn_nexus qpn)
+{
+ if (qpn->main_thread)
+ qpn->start(qpn);
+ else
+ qpt_thread_create(qpn->start, qpn, NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pthread routine
+ *
+ * Processes:
+ *
+ * 1) Main thread only -- signals.
+ *
+ * 2) High priority pending work -- event hooks.
+ *
+ * 3) Messages coming from other pthreads -- mqueue_queue.
+ *
+ * 4) All priority pending work -- event hooks.
+ *
+ * 5) 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.
+ * The pselect timeout is set to be when the next timer is due.
+ *
+ * 6) Timers -- qtimers
+ *
+ */
+static void*
+qpn_start(void* arg)
+{
+ qpn_nexus qpn = arg;
+ mqueue_block mqb;
+ int actions;
+ qtime_mono_t now ;
+ qtime_t max_wait ;
+ unsigned i;
+ unsigned done ;
+ unsigned wait ;
+
+ /* now in our thread, complete initialisation */
+ qpn_in_thread_init(qpn);
+
+ /* custom in-thread initialization */
+ for (i = 0; i < qpn->in_thread_init.count ;)
+ ((qpn_init_function*)(qpn->in_thread_init.hooks[i++]))() ;
+
+ /* Until required to terminate, loop */
+ done = 1 ;
+ while (!qpn->terminate)
+ {
+ /* Signals are highest priority -- only execute for main thread */
+ if (qpn->main_thread)
+ done |= quagga_sigevent_process() ;
+
+ /* Foreground hooks, if any. */
+ for (i = 0; i < qpn->foreground.count ;)
+ done |= ((qpn_hook_function*)(qpn->foreground.hooks[i++]))() ;
+
+ /* take stuff from the message queue
+ *
+ * If nothing done the last time around the loop then may wait this
+ * time if the queue is empty first time through.
+ */
+ wait = (done == 0) ; /* may wait this time only if nothing
+ found to do on the last pass */
+ done = 0 ;
+ do
+ {
+ mqb = mqueue_dequeue(qpn->queue, wait, qpn->mts) ;
+ if (mqb == NULL)
+ break;
+
+ mqb_dispatch(mqb, mqb_action);
+
+ ++done ; /* done another */
+ wait = 0 ; /* done something, so turn off wait */
+ } while (done < 200) ;
+
+ /* block for some input, output, signal or timeout
+ *
+ * wait will be true iff did nothing the last time round the loop, and
+ * not found anything to be done up to this point either.
+ */
+ if (wait)
+ max_wait = qtimer_pile_top_wait(qpn->pile, QTIME(MAX_PSELECT_WAIT)) ;
+ else
+ max_wait = 0 ;
+
+ actions = qps_pselect(qpn->selection, max_wait) ;
+ done |= actions ;
+
+ if (wait)
+ mqueue_done_waiting(qpn->queue, qpn->mts);
+
+ /* process I/O actions */
+ while (actions)
+ actions = qps_dispatch_next(qpn->selection) ;
+
+ /* process timers */
+ now = qt_get_monotonic() ;
+ while (qtimer_pile_dispatch_next(qpn->pile, now))
+ done = 1 ;
+
+ /* If nothing done in this pass, see if anything in the background */
+ if (done == 0)
+ for (i = 0; i < qpn->background.count ; ++i)
+ done |= ((qpn_hook_function*)(qpn->background.hooks[i]))() ;
+ } ;
+
+ /* custom in-thread finalization */
+ for (i = qpn->in_thread_final.count; i > 0 ;)
+ ((qpn_init_function*)(qpn->in_thread_final.hooks[--i]))() ;
+
+ return NULL;
+}
+
+/*------------------------------------------------------------------------------
+ * Now running in our thread, do common initialisation
+ */
+static void
+qpn_in_thread_init(qpn_nexus qpn)
+{
+ sigset_t newmask;
+
+ qpn->thread_id = qpt_thread_self();
+
+ if (qpn->main_thread)
+ {
+ /* Main thread, block the message queue's signal */
+ sigemptyset (&newmask);
+ sigaddset (&newmask, SIGMQUEUE);
+ }
+ else
+ {
+ /*
+ * Not main thread. Block most signals, but be careful not to
+ * defer SIGTRAP because doing so breaks gdb, at least on
+ * NetBSD 2.0. Avoid asking to block SIGKILL, just because
+ * we shouldn't be able to do so. Avoid blocking SIGFPE,
+ * SIGILL, SIGSEGV, SIGBUS as this is undefined by POSIX.
+ * Don't block SIGPIPE so that is gets ignored on this thread.
+ */
+ sigfillset (&newmask);
+ sigdelset (&newmask, SIGTRAP);
+ sigdelset (&newmask, SIGKILL);
+ sigdelset (&newmask, SIGPIPE);
+ sigdelset (&newmask, SIGFPE);
+ sigdelset (&newmask, SIGILL);
+ sigdelset (&newmask, SIGSEGV);
+ sigdelset (&newmask, SIGBUS);
+ }
+
+ if (qpthreads_enabled)
+ qpt_thread_sigmask(SIG_BLOCK, &newmask, NULL);
+ else
+ {
+ if (sigprocmask(SIG_BLOCK, &newmask, NULL) != 0)
+ zabort_errno("sigprocmask failed") ;
+ }
+
+ /* Now we have thread_id and mask, prep for using message queue. */
+ if (qpn->queue != NULL)
+ qpn->mts = mqueue_thread_signal_init(qpn->mts, qpn->thread_id, SIGMQUEUE);
+ if (qpn->selection != NULL)
+ qps_set_signal(qpn->selection, SIGMQUEUE, newmask);
+}
+
+/*------------------------------------------------------------------------------
+ * Ask the thread to terminate itself quickly and cleanly.
+ *
+ * Does nothing if terminate already set.
+ */
+void
+qpn_terminate(qpn_nexus qpn)
+{
+ if (!qpn->terminate)
+ {
+ qpn->terminate = true ;
+
+ /* wake up any pselect */
+ if (qpthreads_enabled)
+ qpt_thread_signal(qpn->thread_id, SIGMQUEUE);
+ } ;
+}
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
new file mode 100644
index 00000000..0cf5a824
--- /dev/null
+++ b/lib/qpnexus.h
@@ -0,0 +1,158 @@
+/* Quagga Pthreads support -- 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 _ZEBRA_QPNEXUS_H
+#define _ZEBRA_QPNEXUS_H
+
+#include <stdint.h>
+#include <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qpthreads.h"
+#include "qtimers.h"
+#include "mqueue.h"
+#include "qpselect.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga Nexus Interface -- qpn_xxxx
+ *
+ * Object to hold, a qpthread, a qps_selection, a qtimer_pile, a mqueue_queue
+ * together with the thread routine to poll and dispatch their respective
+ * action routines.
+ *
+ */
+
+/* maximum time in seconds to sit in a pselect */
+#define MAX_PSELECT_WAIT 10
+
+/* signal for message queues */
+#define SIGMQUEUE SIGUSR2
+
+/* number of hooks per hook list */
+enum { qpn_hooks_max = 4 } ;
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef int qpn_hook_function(void) ; /* dispatch of tasks */
+typedef int qpn_init_function(void) ; /* start/stop work */
+
+typedef struct qpn_hook_list* qpn_hook_list ;
+struct qpn_hook_list
+{
+ void* hooks[qpn_hooks_max] ;
+ unsigned count ;
+} ;
+
+typedef struct qpn_nexus* qpn_nexus ;
+
+struct qpn_nexus
+{
+ /* set true to terminate the thread (eventually) */
+ bool terminate;
+
+ /* true if this is the main thread */
+ bool main_thread;
+
+ /* thread ID */
+ qpt_thread_t thread_id;
+
+ /* pselect handler */
+ qps_selection selection;
+
+ /* timer pile */
+ qtimer_pile pile;
+
+ /* message queue */
+ mqueue_queue queue;
+ mqueue_thread_signal mts;
+
+ /* qpthread routine, can override */
+ void* (*start)(void*);
+
+ /* in-thread initialise, can override. Called within the thread after all
+ * other initialisation just before thread loop
+ *
+ * These are typedef int qpn_init_function(void).
+ *
+ * These are executed in the order given.
+ */
+ struct qpn_hook_list in_thread_init ;
+
+ /* in-thread finalise, can override. Called within thread just before
+ * thread dies. Nexus components all exist but thread loop is no longer
+ * executed
+ *
+ * These are typedef int qpn_init_function(void).
+ *
+ * These are executed in the reverse of the order given.
+ */
+ struct qpn_hook_list in_thread_final ;
+
+ /* in-thread queue(s) of events or other work.
+ *
+ * The hook function(s) are called in the qpnexus loop, at the top of the
+ * loop. So in addition to the mqueue, I/O, timers and any background stuff,
+ * the thread may have other queue(s) of things to be done.
+ *
+ * These are typedef int qpn_hook_function(void).
+ *
+ * Hook function can process some queue(s) of things to be done. It does not
+ * have to empty its queues, but it MUST only return 0 if all queues are now
+ * empty.
+ */
+ struct qpn_hook_list foreground ;
+
+ /* in-thread background queue(s) of events or other work.
+ *
+ * The hook functions are called at the bottom of the qpnexus loop, but only
+ * when there is absolutely nothing else to do.
+ *
+ * These are typedef int qpn_hook_function(void).
+ *
+ * The hook function should do some unit of background work (if there is any)
+ * and return. MUST return 0 iff there is no more work to do.
+ */
+ struct qpn_hook_list background ;
+};
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qpn_nexus qpn_init_new(qpn_nexus qpn, bool main_thread);
+extern void qpn_add_hook_function(qpn_hook_list list, void* hook) ;
+extern void qpn_exec(qpn_nexus qpn);
+extern void qpn_terminate(qpn_nexus qpn);
+extern qpn_nexus qpn_reset(qpn_nexus qpn, bool free_structure);
+
+#define qpn_reset_free(qpn) qpn_reset(qpn, 1)
+#define qpn_reset_keep(qpn) qpn_reset(qpn, 0)
+
+#endif /* _ZEBRA_QPNEXUS_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
new file mode 100644
index 00000000..fd20d421
--- /dev/null
+++ b/lib/qpselect.c
@@ -0,0 +1,1414 @@
+/* Quagga pselect support -- 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 <signal.h>
+#include <string.h>
+
+#include "zassert.h"
+#include "qpselect.h"
+#include "qpthreads.h"
+#include "qtime.h"
+#include "memory.h"
+#include "vector.h"
+
+enum { qdebug =
+#ifdef QDEBUG
+ 1
+#else
+ 0
+#endif
+};
+
+/*==============================================================================
+ * Quagga pselect -- qps_xxxx
+ *
+ * Here and in qpselect.h is a data structure for managing multiple file
+ * descriptors and running pselect to wait for I/O activity and to multiplex
+ * between the file descriptors.
+ *
+ * The qps_selection structure manages a collection of file descriptors which
+ * are to be waited on together in a pselect statement.
+ *
+ * NB: it is ASSUMED that a qps_selection will be private to the thread in
+ * which it is created and used.
+ *
+ * There is NO mutex handling here.
+ *
+ * This supports pselect, so supports:
+ *
+ * * waiting for file descriptors, which may each be expecting any combination
+ * of error/read/write events.
+ *
+ * Files may be added or removed from the selection. Files in the selection
+ * may then be enabled/disabled for any combination of error/read/write
+ * "mode" events.
+ *
+ * * a timeout *time*
+ *
+ * This is a qtime monotonic time at which to time out. (This is unlike
+ * pselect() itself, which takes a timeout interval.)
+ *
+ * Infinite timeouts are not supported.
+ *
+ * * an optional signal number and sigmask
+ *
+ * So that a signal may be used to interrupt a waiting pselect.
+ *
+ * For this to work there must be a signal which is generally masked, and
+ * is unmasked for the duration of the pselect.
+ *
+ * When a pselect returns there may be a number of files with events pending.
+ * The qps_dispatch_next() calls the action routine for the next event to be
+ * dealt with. Events are dispatched in the order: error, read and write, and
+ * then in file descriptor order. (So all error events in fd order, then all
+ * read events, and so on.)
+ *
+ * Note that at no time are any modes automatically disabled. So the system is
+ * level triggered. So, for example, a read event that is not dealt with will
+ * be triggered again on the next pselect -- unless the read mode is explicitly
+ * disabled for the file.
+ *
+ * Action Functions
+ * ----------------
+ *
+ * There is a separate action function for each mode. Each file has its own
+ * set of action functions -- so these may be used to implement a form of
+ * state machine for the file.
+ *
+ * When the action function is called it is passed the qps_file structure and
+ * the file_info pointer from that structure.
+ *
+ * During an action function modes may be enabled/disabled, actions changed,
+ * the file removed from the selection... there are no restrictions.
+ */
+
+/*==============================================================================
+ * qps_selection handling
+ */
+
+/* See qps_make_super_set_map() below. */
+static short fd_byte_count[FD_SETSIZE] ; /* number of bytes for fds 0..fd */
+
+/* Forward references */
+static void qps_make_super_set_map(void) ;
+static void qps_selection_re_init(qps_selection qps) ;
+static qps_file qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert) ;
+static void qps_file_remove(qps_selection qps, qps_file qf) ;
+static void qps_super_set_zero(fd_super_set* p_set, int n) ;
+static int qps_super_set_cmp(fd_super_set* p_a, fd_super_set* p_b, int n) ;
+static int qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last) ;
+static void qps_selection_validate(qps_selection qps) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a selection -- allocating it if required.
+ *
+ * Returns the qps_selection.
+ */
+
+extern void
+qps_start_up(void)
+{
+ qps_make_super_set_map() ; /* map the fd_super_set */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a selection -- allocating it if required.
+ *
+ * Returns the qps_selection.
+ *
+ * NB: when initialising an existing selection which has been used before, it
+ * is the caller's responsibility to have dealt with its contents before
+ * calling this.
+ */
+qps_selection
+qps_selection_init_new(qps_selection qps)
+{
+ if (qps == NULL)
+ qps = XCALLOC(MTYPE_QPS_SELECTION, sizeof(struct qps_selection)) ;
+
+ qps_selection_re_init(qps) ;
+
+ return qps ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-initialise a selection.
+ *
+ * It is the caller's responsibility to have dealt with any active files before
+ * calling this.
+ */
+static void
+qps_selection_re_init(qps_selection qps)
+{
+ memset(qps, 0, sizeof(struct qps_selection)) ;
+
+ /* Zeroising initialises:
+ *
+ * fd_count -- no fd's yet
+ * fd_direct -- not direct lookup
+ *
+ * files -- empty vector
+ *
+ * enabled_count -- no fd's enabled in any mode
+ * enabled -- empty bit vectors
+ *
+ * tried_fd_last -- nothing tried yet
+ * tried_count -- nothing tried yet
+ * results -- empty bit vectors
+ *
+ * pend_count -- no results to dispatch
+ * pend_mnum -- unset
+ * pend_fd -- unset
+ *
+ * signum -- no signal to be enabled
+ * sigmask -- unset
+ *
+ * So nothing much else to do:
+ */
+ qps->fd_last = -1 ; /* not an fd in sight. */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add given file to the selection, setting its fd and pointer to further
+ * file information. All modes are disabled.
+ *
+ * This initialises most of the qps_file structure, but not the actions.
+ *
+ * Adding a file using the same fd as an existing file is a FATAL error.
+ *
+ * Adding a file which is already a member a selection is a FATAL error.
+ */
+void
+qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info)
+{
+ passert(qf->selection == NULL) ;
+
+ qf->selection = qps ;
+
+ qf->file_info = file_info ;
+ qf->fd = fd ;
+
+ qf->enabled_bits = 0 ;
+
+ qps_file_lookup_fd(qps, fd, qf) ; /* Add. */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove given file from its selection, if any.
+ *
+ * It is the callers responsibility to ensure that the file is in a suitable
+ * state to be removed from the selection.
+ *
+ * When the file is removed it is disabled in all modes.
+ */
+void
+qps_remove_file(qps_file qf)
+{
+ if (qf->selection != NULL)
+ qps_file_remove(qf->selection, qf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Ream (another) file out of the selection.
+ *
+ * If selection is empty, release the qps_selection structure, if required.
+ *
+ * See: #define qps_selection_ream_free(qps)
+ * #define qps_selection_ream_keep(qps)
+ *
+ * Useful for emptying out and discarding a selection:
+ *
+ * while ((qf = qps_selection_ream_free(qps)))
+ * ... do what's required to release the qps_file
+ *
+ * The file is removed from the selection before being returned.
+ *
+ * Returns NULL when selection is empty (and has been released, if required).
+ *
+ * If the selection is not released, it may be reused without reinitialisation.
+ *
+ * NB: once reaming has started, the selection MUST NOT be used for anything,
+ * and the process MUST be run to completion.
+ */
+qps_file
+qps_selection_ream(qps_selection qps, int free_structure)
+{
+ qps_file qf ;
+
+ qf = vector_get_last_item(&qps->files) ;
+ if (qf != NULL)
+ qps_file_remove(qps, qf) ;
+ else
+ {
+ passert(qps->fd_count == 0) ;
+
+ if (free_structure)
+ XFREE(MTYPE_QPS_SELECTION, qps) ;
+ else
+ qps_selection_re_init(qps) ;
+ } ;
+
+ return qf ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the signal mask for the selection.
+ *
+ * This supports the unmasking of a single signal for the duration of the
+ * pselect operation.
+ *
+ * It is assumed that the set of signals generally masked by a thread is
+ * essentially static. So this function is passed that set. (So the sigmask
+ * argument must have the signum signal masked.)
+ *
+ * If the set of signals masked by the thread changes, then this function
+ * should be called again.
+ *
+ * Setting a signum == 0 turns OFF the use of the sigmask.
+ */
+void
+qps_set_signal(qps_selection qps, int signum, sigset_t sigmask)
+{
+ qps->signum = signum ;
+
+ if (signum != 0)
+ {
+ assert(sigismember(&sigmask, signum)) ;
+ sigdelset(&sigmask, signum) ;
+ qps->sigmask = sigmask ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Execute a pselect for the given selection -- subject to the given maximum
+ * time to wait.
+ *
+ * There is no support for an infinite timeout.
+ *
+ * Returns: -1 => EINTR occurred -- ie a signal has gone off
+ * 0 => hit timeout -- no files are ready
+ * > 0 => there are this many files ready in one or more modes
+ *
+ * All other errors are FATAL.
+ *
+ * The qps_dispatch_next() processes the returns from pselect().
+ */
+int
+qps_pselect(qps_selection qps, qtime_t max_wait)
+{
+ struct timespec ts ;
+ qps_mnum_t mnum ;
+ fd_set* p_fds[qps_mnum_count] ;
+ int n ;
+
+ if (qdebug)
+ qps_selection_validate(qps) ;
+
+ /* If there is stuff still pending, tidy up by zeroising the result */
+ /* vectors. This is to make sure that when bits are copied from */
+ /* the enabled vectors, there are none from a previous run of pselect */
+ /* left hanging about. (pselect SHOULD ignore everything above the */
+ /* given count of fds -- but it does no harm to be tidy, and should */
+ /* not have to do this often.) */
+ if (qps->pend_count != 0)
+ qps_super_set_zero(qps->results, qps_mnum_count) ;
+
+ /* Prepare the argument/result bitmaps */
+ /* Capture pend_mnum and tried_count[] */
+
+ n = (qps->fd_last >= 0)
+ ? fd_byte_count[qps->fd_last] /* copy up to last sig. byte */
+ : 0 ;
+
+ qps->pend_mnum = qps_mnum_count ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if ((qps->tried_count[mnum] = qps->enabled_count[mnum]) != 0)
+ {
+ dassert(n != 0) ; /* n == 0 => no fds ! */
+
+ memcpy(&(qps->results[mnum].bytes), &(qps->enabled[mnum].bytes), n) ;
+ p_fds[mnum] = &(qps->results[mnum].fdset) ;
+
+ if (mnum < qps->pend_mnum)
+ qps->pend_mnum = mnum ; /* collect first active mode */
+ }
+ else
+ p_fds[mnum] = NULL ;
+
+ /* Capture tried_fd_last and set initial pend_fd. */
+ qps->tried_fd_last = qps->fd_last ;
+ qps->pend_fd = 0 ;
+
+ /* Make sure not trying to do something stupid */
+ if (max_wait < 0)
+ max_wait = 0 ;
+
+ /* Finally ready for the main event */
+ n = pselect(qps->fd_last + 1, p_fds[qps_read_mnum],
+ p_fds[qps_write_mnum],
+ p_fds[qps_error_mnum],
+ qtime2timespec(&ts, max_wait),
+ (qps->signum != 0) ? &qps->sigmask : NULL) ;
+
+ /* If have something, set and return the pending count. */
+ if (n > 0)
+ {
+ assert(qps->pend_mnum < qps_mnum_count) ; /* expected something */
+
+ return qps->pend_count = n ; /* set and return pending count */
+ } ;
+
+ /* Nothing pending at all */
+ qps->pend_count = 0 ; /* qps_dispatch_next() does nothing */
+
+ /* Flush the results vectors -- not apparently done if n <= 0) */
+ qps_super_set_zero(qps->results, qps_mnum_count) ;
+
+ /* Return appropriately, if we can */
+ if ((n == 0) || (errno == EINTR))
+ return n ;
+
+ zabort_errno("Failed in pselect") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch the next errored/readable/writeable file, as returned by the
+ * most recent qps_pselect().
+ *
+ * Processes the errored files, then the readable and lastly the writeable.
+ *
+ * Processes one file per call of this function, by invoking the file's
+ * "action" routine.
+ *
+ * If a given file is ready in more than one mode, all modes will be processed,
+ * unless the action routine for one mode disables the file for other modes, or
+ * removes it from the selection.
+ *
+ * Returns the number of files left to process (after the one just processed).
+ *
+ * NB: clears result bits as it finds them -- so at end of process, the
+ * result bit vectors should be zeroised again. Also, this allows the
+ * search to proceed from the last known fd -- won't find it again !
+ */
+int
+qps_dispatch_next(qps_selection qps)
+{
+ int fd ;
+ qps_file qf ;
+ qps_mnum_t mnum ;
+
+ if (qdebug)
+ qps_selection_validate(qps) ;
+
+ if (qps->pend_count == 0)
+ return 0 ; /* quit immediately of nothing to do. */
+
+ fd = qps->pend_fd ; /* look for fd >= this */
+ mnum = qps->pend_mnum ; /* starting with this mode */
+
+ dassert( (mnum >= 0) && (mnum < qps_mnum_count)
+ && (qps->tried_count[mnum] != 0)
+ && (qps->pend_count > 0)
+ && (qps->tried_fd_last >= 0)) ;
+
+ while (1)
+ {
+ fd = qps_next_fd_pending(&(qps->results[mnum]), fd, qps->tried_fd_last) ;
+ if (fd >= 0)
+ break ; /* easy if have another fd in current mode. */
+
+ do /* step to next mode that was not empty */
+ {
+ qps->tried_count[mnum] = 0 ; /* tidy up as we go */
+ ++mnum ;
+ if (mnum >= qps_mnum_count)
+ zabort("Unexpectedly ran out of pending stuff") ;
+ } while (qps->tried_count[mnum] == 0) ;
+
+ qps->pend_mnum = mnum ; /* update mode */
+ fd = 0 ; /* back to the beginning */
+ } ;
+
+ qps->pend_count -= 1 ; /* one less pending */
+ qps->pend_fd = fd ; /* update scan */
+
+ qf = qps_file_lookup_fd(qps, fd, NULL) ;
+
+ dassert( ((qf->enabled_bits && qps_mbit(mnum)) != 0) &&
+ (qf->actions[mnum] != NULL) ) ;
+
+ qf->actions[mnum](qf, qf->file_info) ; /* dispatch the required action */
+
+ return qps->pend_count ;
+} ;
+
+/*==============================================================================
+ * qps_file structure handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise qps_file structure -- allocating one if required.
+ *
+ * If a template is given, then the action functions are copied from there to
+ * the new structure. See above for discussion of action functions.
+ *
+ * Once initialised, the file may be added to a selection.
+ *
+ * Returns the qps_file.
+ */
+qps_file
+qps_file_init_new(qps_file qf, qps_file template)
+{
+ if (qf == NULL)
+ qf = XCALLOC(MTYPE_QPS_FILE, sizeof(struct qps_file)) ;
+ else
+ memset(qf, 0, sizeof(struct qps_file)) ;
+
+ /* Zeroising has initialised:
+ *
+ * selection -- NULL -- ditto
+ *
+ * file_info -- NULL -- is set by qps_add_file()
+ * fd -- unset -- ditto
+ *
+ * enabled_bits -- nothing enabled
+ *
+ * actions[] -- all set to NULL
+ */
+
+ qf->fd = fd_undef ; /* no fd set yet */
+
+ if (template != NULL)
+ memcpy(qf->actions, template->actions, sizeof(qf->actions)) ;
+
+ return qf ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free dynamically allocated qps_file structure -- if any.
+ *
+ * Removes from any selection may be a member of.
+ *
+ * If there is a valid fd -- close it !
+ *
+ * Returns: NULL
+ */
+extern qps_file
+qps_file_free(qps_file qf)
+{
+ if (qf != NULL)
+ {
+ if (qf->selection != NULL)
+ qps_remove_file(qf) ;
+
+ if (qf->fd >= 0)
+ {
+ close(qf->fd) ;
+ qf->fd = fd_undef ;
+ } ;
+
+ XFREE(MTYPE_QPS_FILE, qf) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Enable (or re-enable) file for the given mode.
+ *
+ * If the action argument is not NULL, set the action for the mode.
+ *
+ * NB: It is a FATAL error to enable a mode with a NULL action.
+ *
+ * NB: It is a FATAL error to enable modes for a file which is not in a
+ * selection.
+ */
+void
+qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action)
+{
+ qps_mbit_t mbit = qps_mbit(mnum) ;
+ qps_selection qps = qf->selection ;
+
+ dassert(qps != NULL) ;
+ dassert((mnum >= 0) && (mnum <= qps_mnum_count)) ;
+
+ if (action != NULL)
+ qf->actions[mnum] = action ;
+ else
+ dassert(qf->actions[mnum] != NULL) ;
+
+ if (qf->enabled_bits & mbit)
+ dassert(FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+ else
+ {
+ dassert( ! FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+ FD_SET(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ ++qps->enabled_count[mnum] ;
+ qf->enabled_bits |= mbit ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set action for given mode -- does not enable/disable.
+ *
+ * May unset an action by setting it NULL !
+ *
+ * See above for discussion of action functions.
+ *
+ * NB: it is a fatal error to unset an action for a mode which is enabled.
+ */
+void
+qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
+{
+ dassert((mnum >= 0) && (mnum <= qps_mnum_count)) ;
+
+ if (action == NULL)
+ passert((qf->enabled_bits & qps_mbit(mnum)) == 0) ;
+
+ qf->actions[mnum] = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Disable file for one or more modes.
+ *
+ * If there are any pending results for the modes, those are discarded.
+ *
+ * Note that this is modestly "optimised" to deal with disabling a single mode.
+ * (Much of the time only the write mode will be being disabled !)
+ *
+ * NB: it is safe to disable modes which are not enabled -- even if the file
+ * is not currently a member of a selection. (If it is not a member of a
+ * collection no modes should be enabled !)
+ */
+
+static qps_mnum_t qps_first_mnum[qps_mbit(qps_mnum_count)] =
+ {
+ -1, /* 0 -> -1 -- no bit set */
+ 0, /* 1 -> 0 -- B0 is first bit */
+ 1, /* 2 -> 1 -- B1 is first bit */
+ 1, /* 3 -> 1 -- B1 is first bit */
+ 2, /* 4 -> 2 -- B2 is first bit */
+ 2, /* 5 -> 2 -- B2 is first bit */
+ 2, /* 6 -> 2 -- B2 is first bit */
+ 2 /* 7 -> 2 -- B2 is first bit */
+ } ;
+
+CONFIRM(qps_mbit(qps_mnum_count) == 8) ;
+
+void
+qps_disable_modes(qps_file qf, qps_mbit_t mbits)
+{
+ qps_mnum_t mnum ;
+
+ qps_selection qps = qf->selection ;
+
+ dassert((mbits >= 0) && (mbits <= qps_all_mbits)) ;
+
+ mbits &= qf->enabled_bits ; /* don't bother with any not enabled */
+ qf->enabled_bits ^= mbits ; /* unset what we're about to disable */
+
+ while (mbits != 0)
+ {
+ mnum = qps_first_mnum[mbits] ;
+
+ dassert(qps->enabled_count[mnum] > 0) ;
+ dassert(FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+
+ FD_CLR(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ --qps->enabled_count[mnum] ;
+
+ if ((qps->pend_count != 0) && (qps->tried_count[mnum] != 0)
+ && (FD_ISSET(qf->fd, &(qps->results[mnum].fdset))))
+ {
+ FD_CLR(qf->fd, &(qps->results[mnum].fdset)) ;
+ --qps->pend_count ;
+ } ;
+
+ mbits ^= qps_mbit(mnum) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Handling the files vector.
+ *
+ * For small numbers of fd's, the files vector is kept as a list, in fd order.
+ * Files are found by binary chop, and added/removed by insert/delete in the
+ * list.
+ *
+ * For large numbers of fd's, the files vector is kept as an array, indexed by
+ * fd.
+ */
+
+/*------------------------------------------------------------------------------
+ * Comparison function for binary chop
+ */
+static int
+qps_fd_cmp(const int** pp_fd, const qps_file* p_qf)
+{
+ if (**pp_fd < (*p_qf)->fd)
+ return -1 ;
+ if (**pp_fd > (*p_qf)->fd)
+ return +1 ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Lookup/Insert file by file-descriptor.
+ *
+ * Inserts if insert argument is not NULL.
+ *
+ * Returns the file we found (if any) or the file we just inserted.
+ *
+ * NB: FATAL error to insert file with same fd as an existing one.
+ */
+static qps_file
+qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
+{
+ qps_file qf ;
+ vector_index i ;
+ int ret ;
+
+ dassert((fd >= 0) && (fd < (int)FD_SETSIZE)) ;
+
+ /* Look-up */
+ /* */
+ /* Set i = index for entry in files vector */
+ /* Set ret = 0 <=> i is exact index. */
+ /* < 0 <=> i is just after where entry may be inserted */
+ /* > 0 <=> i is just before where entry may be inserted */
+ if (qps->fd_direct)
+ {
+ i = fd ; /* index of entry */
+ ret = 0 ; /* how to insert, if do */
+ }
+ else
+ i = vector_bsearch(&qps->files, (vector_bsearch_cmp*)qps_fd_cmp,
+ &fd, &ret) ;
+ if (ret == 0)
+ qf = vector_get_item(&qps->files, i) ; /* NULL if not there */
+ else
+ qf = NULL ; /* not there */
+
+ /* Insert now, if required and can: keep fd_count and fd_last up to date. */
+ if (insert != NULL)
+ {
+ if (qf != NULL)
+ zabort("File with given fd already exists in qps_selection") ;
+
+ /* If required, change up to a directly addressed files vector. */
+ if (!qps->fd_direct && (qps->fd_count > 9))
+ {
+ vector tmp ;
+
+ tmp = vector_move_here(NULL, &qps->files) ;
+
+ while ((qf = vector_pop_item(tmp)) != NULL)
+ vector_set_item(&qps->files, qf->fd, qf) ;
+
+ vector_free(tmp) ;
+
+ qps->fd_direct = 1 ;
+
+ i = fd ; /* index is now the fd */
+ ret = 0 ; /* and insert there */
+ } ;
+
+ /* Now can insert accordint to i & ret */
+ vector_insert_item_here(&qps->files, i, ret, insert) ;
+
+ ++qps->fd_count ;
+ if (fd > qps->fd_last)
+ qps->fd_last = fd ;
+
+ qf = insert ; /* will return what we just inserted. */
+ } ;
+
+ /* Sanity checking. */
+ dassert( (qf == NULL) || ((qps == qf->selection) && (fd == qf->fd)) ) ;
+
+ /* Return the file we found or inserted. */
+ return qf ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove file from selection.
+ *
+ * NB: FATAL error if file is not in the selection, or the file-descriptor
+ * is invalid (or refers to some other file !).
+ */
+static void
+qps_file_remove(qps_selection qps, qps_file qf)
+{
+ qps_file qfd ;
+ int fd_last ;
+
+ passert((qf->fd >= 0) && (qf->fd <= qps->fd_last) && (qps == qf->selection)) ;
+
+ /* Look-up and remove. */
+ if (qps->fd_direct)
+ {
+ qfd = vector_unset_item(&qps->files, qf->fd) ; /* NULL if not there */
+ fd_last = vector_end(&qps->files) - 1 ;
+ }
+ else
+ {
+ qps_file qf_last ;
+ int ret ;
+ vector_index i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &qf->fd, &ret) ;
+ if (ret == 0)
+ qfd = vector_delete_item(&qps->files, i) ;
+ else
+ qfd = NULL ;
+
+ qf_last = vector_get_last_item(&qps->files) ;
+ if (qf_last != NULL)
+ fd_last = qf_last->fd ;
+ else
+ fd_last = -1 ;
+ } ;
+
+ passert(qfd == qf) ; /* must have been there and be the expected file */
+
+ /* Keep fd_count and fd_last up to date. */
+ dassert(qps->fd_count > 0) ;
+ --qps->fd_count ;
+
+ dassert(fd_last >= (qps->fd_count - 1)) ;
+
+ qps->fd_last = fd_last ;
+
+ /* Also, remove the from all vectors. */
+ qps_disable_modes(qf, qps_all_mbits) ;
+
+ /* Is no longer in the selection. */
+ qf->selection = NULL ;
+} ;
+
+/*==============================================================================
+ * fd_super_set support.
+ *
+ * For large sets of file descriptors something faster than testing for all
+ * possible bits is required. The fd_super_set assumes that the fd_set is a
+ * straightforward bit-vector, and overlays a 32-bit word array and a byte
+ * array over that.
+ *
+ * Cannot tell if the underlying bit vector is arranged in bytes, or some
+ * longer words. Cannot tell if words are held big or little endian. Cannot
+ * tell if lowest numbered fd will be highest or lowest in whatever unit it's
+ * held in.
+ *
+ * So... we have maps for fd -> our word index, and fd -> byte index.
+ *
+ * we have a map for fd -> mask for bit used in its byte.
+ *
+ * We require that fds will be numbered consistently in bytes. That is,
+ * every (fd mod 8) == n will appear in the same bit in a byte, for all fd (
+ * for n = 0..7). This allows the final map, which takes a byte value and
+ * returns the lowest numbered fd in the byte, mod 8.
+ *
+ * To copy all the bytes for all descriptors 0..fd, also construct
+ * fd_byte_count[] -- which copes with the fact that on a big-endian machine
+ * it is possible that descriptor fd - 8 may be in a higher numbered byte than
+ * fd ! Using this count assumes that the underlying system really does not
+ * look at bits beyond the given maximum fd.
+ */
+
+static short fd_word_map[FD_SETSIZE] ; /* maps fd to word index */
+static short fd_byte_map[FD_SETSIZE] ; /* maps fd to byte index */
+static uint8_t fd_bit_map [FD_SETSIZE] ; /* maps fd to bit in byte */
+
+static int8_t fd_first_map[256] ; /* maps byte value to 0..7, where that */
+ /* is the lowest fd bit set in byte. */
+
+/*------------------------------------------------------------------------------
+ * Cross Check
+ *
+ * Where the shape of the bit map is known, this will test that the correct
+ * bit map has been deduced.
+ *
+ * Requires the following to be defined:
+ *
+ * QPS_CROSS_CHECK -- weebb
+ *
+ * where: w -- number of bytes per word, 1..
+ * ee -- 10 => big-endian bytes in word
+ * 01 => little-endian
+ * bb -- 70 => b7 is MS bit, b0 is LS bit
+ * 07 => b0 is MS bit, b7 is LS bit
+ *
+ * So:
+ *
+ * 10170 => a bit map handled as bytes
+ *
+ * 40170 => a bit map handled as little-endian 32-bit words
+ *
+ * ...though this is actually no different to handling the bit map
+ * as bytes.
+ *
+ * 41070 => a bit map handled as big-endian 32-bit words
+ *
+ * 10107 => a bit map handled as bytes, where the "leftmost" bit is the first
+ * bit in the bitmap:
+ *
+ * ...a big-endian machine, where the bit map is handled as n-bit
+ * words, with the "leftmost" bit being the first would be like
+ * this too.
+ */
+
+#define QPS_CROSS_CHECK 40170
+
+enum {
+#ifdef QPS_CROSS_CHECK
+ qps_cross_check = 1,
+ qps_cc_word_bytes = QPS_CROSS_CHECK / 10000,
+ qps_cc_byte_ord = (QPS_CROSS_CHECK / 100) % 100,
+ qps_cc_bit_ord = QPS_CROSS_CHECK % 100,
+#else
+ qps_cross_check = 0, /* no cross check */
+ qps_cc_word_bytes = 1, /* byte_wise */
+ qps_cc_byte_ord = 1, /* little-endian */
+ qps_cc_bit_ord = 70, /* standard bit order */
+#endif
+ qps_cc_word_bits = qps_cc_word_bytes * 8
+} ;
+
+CONFIRM((qps_cc_word_bytes == 16) || (qps_cc_word_bytes == 8)
+ || (qps_cc_word_bytes == 4)
+ || (qps_cc_word_bytes == 2)
+ || (qps_cc_word_bytes == 1)) ;
+CONFIRM((qps_cc_byte_ord == 10) || (qps_cc_byte_ord == 1)) ;
+CONFIRM((qps_cc_bit_ord == 70) || (qps_cc_bit_ord == 7)) ;
+
+/* Functions required for the cross check. */
+
+static inline int
+qpd_cc_word(int fd)
+{
+ return fd / qps_cc_word_bits ;
+} ;
+
+static inline int
+qps_cc_byte(int fd)
+{
+ if (qps_cc_byte_ord == 10)
+ return (qpd_cc_word(fd) * qps_cc_word_bytes)
+ + qps_cc_word_bytes - 1 - ((fd % qps_cc_word_bits) / 8) ;
+ else
+ return fd / 8 ;
+} ;
+
+static inline uint8_t
+qps_cc_bit(int fd)
+{
+ if (qps_cc_bit_ord == 70)
+ return 0x01 << (fd & 0x7) ;
+ else
+ return 0x80 >> (fd & 0x7) ;
+} ;
+
+static int
+ccFD_ISSET(int fd, fd_set* set)
+{
+ return (*((uint8_t*)set + qps_cc_byte(fd)) & qps_cc_bit(fd)) != 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Scan for next fd in given fd set, and clear it.
+ *
+ * Starts at the given fd, will not consider anything above fd_last.
+ *
+ * Returns next fd, or -1 if none.
+ */
+static int
+qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last)
+{
+ uint8_t b ;
+
+ dassert((fd >= 0) && (fd <= fd_last)) ;
+
+ while (pending->words[fd_word_map[fd]] == 0) /* step past zero words */
+ {
+ fd = (fd & ~ (FD_WORD_BITS - 1)) + FD_WORD_BITS ;
+ /* step to start of next word */
+ if (fd > fd_last)
+ return -1 ; /* quit if past last */
+ } ;
+
+ fd &= ~0x0007 ; /* step back to first in byte */
+ while ((b = pending->bytes[fd_byte_map[fd]]) == 0)
+ {
+ fd += 8 ;
+ if (fd > fd_last)
+ return -1 ;
+ } ;
+
+ fd += fd_first_map[b] ;
+
+ dassert(fd <= fd_last) ;
+ dassert((b & fd_bit_map[fd]) == fd_bit_map[fd]) ;
+
+ FD_CLR(fd, &pending->fdset) ;
+
+ dassert((b ^ fd_bit_map[fd]) == pending->bytes[fd_byte_map[fd]]) ;
+
+ return fd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a map of the fd_super_set.
+ *
+ * The form of an fd_set is not defined. This code verifies that it is, in
+ * fact a bit vector, and hence that the fd_super_set works here !
+ *
+ * It is a FATAL error if things don't work out.
+ */
+static void
+qps_make_super_set_map(void)
+{
+ fd_super_set test ;
+ int fd, i, iw, ib ;
+
+ /* (1) check that a zeroised fd_super_set is an empty one. */
+ qps_super_set_zero(&test, 1) ;
+
+ for (fd = 0 ; fd < (int)FD_SETSIZE ; ++fd)
+ if (FD_ISSET(fd, &test.fdset))
+ zabort("Zeroised fd_super_set is not empty") ;
+
+ /* (2) check that zeroising the fd_set doesn't change things */
+ FD_ZERO(&test.fdset) ;
+ for (iw = 0 ; iw < (int)FD_SUPER_SET_WORD_SIZE ; ++iw)
+ if (test.words[iw] != 0)
+ zabort("Zeroised fd_super_set is not all zero words") ;
+
+ /* (3) check that setting one fd sets one bit, and construct the */
+ /* fd_word_map[], fd_byte_map[] and fd_bit_map[]. */
+ for (fd = 0 ; fd < (int)FD_SETSIZE ; ++fd)
+ {
+ fd_word_t w ;
+
+ FD_SET(fd, &test.fdset) ;
+
+ w = 0 ;
+ for (iw = 0 ; iw < (int)FD_SUPER_SET_WORD_SIZE ; ++iw)
+ {
+ if (test.words[iw] != 0)
+ {
+ if (w != 0)
+ zabort("FD_SET set a bit in more than one word") ;
+
+ w = test.words[iw] ;
+ if ((w == 0) || ((w & (w - 1)) != 0))
+ zabort("FD_SET set more than one bit in a word") ;
+
+ fd_word_map[fd] = iw ;
+
+ ib = iw * FD_WORD_BYTES ;
+ while (test.bytes[ib] == 0)
+ {
+ ++ib ;
+ if (ib >= ((iw + 1) * FD_WORD_BYTES))
+ zabort("FD_SET set something beyond the expected bytes") ;
+ } ;
+ fd_byte_map[fd] = ib ;
+ fd_bit_map[fd] = test.bytes[ib] ;
+ } ;
+ } ;
+
+ if (w == 0)
+ zabort("FD_SET did not set any bit in any word") ;
+
+ FD_CLR(fd, &test.fdset) ;
+
+ for (iw = 0 ; iw < (int)FD_SUPER_SET_WORD_SIZE ; ++iw)
+ if (test.words[iw] != 0)
+ zabort("FD_CLR did not leave the fd_super_set empty") ;
+ } ;
+
+ /* (4) check the fd_byte_map. */
+ /* make sure that have 8 contiguous fd to a byte. */
+ /* make sure that have 32 contiguous fd to a word. */
+
+ for (fd = 0 ; fd < (int)FD_SETSIZE ; fd += 8)
+ {
+ int fds ;
+ ib = fd_byte_map[fd] ;
+ iw = fd_word_map[fd] ;
+
+ /* Must share the same byte as the next 7 fds */
+ for (fds = fd + 1 ; fds < (fd + 8) ; ++fds)
+ if (fd_byte_map[fds] != ib)
+ zabort("Broken fd_byte_map -- not 8 contiguous fd's in a byte") ;
+
+ /* Must not share the same byte as any other set of 8 fd's */
+ for (fds = 0 ; fds < (int)FD_SETSIZE ; fds += 8)
+ if ((fd_byte_map[fds] == ib) && (fds != fd))
+ zabort("Broken fd_byte_map -- fd's not in expected bytes") ;
+
+ /* Must be one of the bytes in the current word's fd's */
+ if ( (ib < (iw * FD_WORD_BYTES)) || (ib >= ((iw + 1) * FD_WORD_BYTES)) )
+ zabort("Broken fd_byte_map -- fd's not in expected words") ;
+ } ;
+
+ /* (5) check the fd_bit_map */
+ /* make sure that all fd mod 8 map to the same byte value */
+
+ for (i = 0 ; i < 8 ; ++i)
+ {
+ uint8_t b = fd_bit_map[i] ;
+ for (fd = 8 + i ; fd < (int)FD_SETSIZE ; fd += 8)
+ if (fd_bit_map[fd] != b)
+ zabort("Broken fd_bit_map -- inconsistent bit mapping") ;
+ } ;
+
+ /* (6) construct fd_first_map, to get lowest numbered fd (mod 8) from */
+ /* a given byte value. */
+
+ for (i = 0 ; i < 256 ; ++i)
+ fd_first_map[i] = -1 ;
+
+ for (fd = 0 ; fd < 8 ; ++fd)
+ {
+ uint8_t fdb = fd_bit_map[fd] ;
+ for (i = 1 ; i < 256 ; ++i)
+ if ((fd_first_map[i] == -1) && ((i & fdb) != 0))
+ fd_first_map[i] = fd ;
+ } ;
+
+ if (fd_first_map[0] != -1)
+ zabort("Broken fd_first_map -- invalid result for 0") ;
+
+ for (i = 1 ; i < 256 ; ++i)
+ if (fd_first_map[i] == -1)
+ zabort("Broken fd_first_map -- missing bits") ;
+
+ /* (7) construct fd_byte_count[] -- number of bytes required to */
+ /* include fds 0..fd. */
+
+ i = 0 ;
+ for (fd = 0 ; fd < (int)FD_SETSIZE ; ++fd)
+ {
+ int c = fd_byte_map[fd] + 1 ;
+
+ if (c < i)
+ c = i ; /* use largest so far. => big-endian */
+ else
+ i = c ; /* keep largest so far up to date */
+
+ fd_byte_count[fd] = c ;
+ } ;
+
+ if (!qps_cross_check)
+ return ;
+
+ /*----------------------------------------------------------------------------
+ * Checking that the maps have been correctly deduced -- where know what
+ * the mapping really is !
+ */
+ for (fd = 0 ; fd < (int)FD_SETSIZE ; ++fd)
+ {
+ uint8_t b ;
+ short c ;
+
+ FD_ZERO(&test.fdset) ;
+ FD_SET(fd, &test.fdset) ;
+ if (!ccFD_ISSET(fd, &test.fdset))
+ zabort("FD_SET and ccFD_ISSET differ") ;
+
+ iw = qpd_cc_word(fd) ;
+ ib = qps_cc_byte(fd) ;
+ b = qps_cc_bit(fd) ;
+
+ if (qps_cc_byte_ord == 10)
+ c = (iw + 1) * 4 ;
+ else
+ c = ib + 1 ;
+
+ if (fd_word_map[fd] != iw)
+ zabort("Broken fd_word_map") ;
+ if (fd_byte_map[fd] != ib)
+ zabort("Broken fd_byte_map") ;
+ if (fd_bit_map[fd] != b)
+ zabort("Broken fd_bit_map") ;
+ if (fd_byte_count[fd] != c)
+ zabort("Broken fd_byte_count") ;
+ } ;
+
+ for (i = 1 ; i < 256 ; ++i)
+ {
+ uint8_t b = i ;
+ fd = 0 ;
+ if (qps_cc_bit_ord == 70)
+ {
+ while ((b & 1) == 0)
+ {
+ b >>= 1 ;
+ ++fd ;
+ } ;
+ }
+ else
+ {
+ while ((b & 0x80) == 0)
+ {
+ b <<= 1 ;
+ ++fd ;
+ } ;
+ } ;
+
+ if (fd_first_map[i] != fd)
+ zabort("Broken fd_first_map") ;
+ } ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Zeroise 'n' contiguous fd_super_sets
+ *
+ * NB: this MUST be used in place of FD_ZERO because the fd_set may be shorter
+ * than the overlayed words/bytes vectors.
+ *
+ * NB: it is CONFIRMED elsewhere that the fd_set is no longer than the overlays.
+ */
+static void
+qps_super_set_zero(fd_super_set* p_set, int n)
+{
+ memset(p_set, 0, SIZE(fd_super_set, n)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare 'n' contiguous fd_super_sets
+ *
+ * Returns 0 <=> equal
+ */
+static int
+qps_super_set_cmp(fd_super_set* p_a, fd_super_set* p_b, int n)
+{
+ return memcmp(p_a, p_b, SIZE(fd_super_set, n)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Count the number of bits set in 'n' contiguous fd_super_sets.
+ */
+static int
+qps_super_set_count(fd_super_set* p_set, int n)
+{
+ fd_word_t* p ;
+ int count = 0 ;
+
+ n *= FD_SUPER_SET_WORD_SIZE ;
+ confirm(sizeof(fd_super_set) == (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)) ;
+
+ p = (fd_word_t*)p_set ;
+ while (n--)
+ {
+ fd_word_t w = *p++ ;
+ while (w != 0)
+ {
+ ++count ;
+ w &= (w - 1) ;
+ } ;
+ } ;
+
+ return count ;
+} ;
+
+/*==============================================================================
+ * Selection state check -- for debug purposes.
+ *
+ * Runs a check across a given selection and verifies that:
+ *
+ * 1) for !fd_direct that the files are in fd order in the vector
+ * and are unique, and there are no NULL entries.
+ * 2) for fd_direct that the file fd and the index match
+ * and the last entry is not NULL
+ * 3) that all files point at the selection
+ * 4) that the enabled modes in each file are valid
+ * 5) the number of files in the selection matches fd_count.
+ * 6) the highest numbered fd matches fd_last
+ * 7) that the enabled counts in the selection are correct
+ * 8) that the enabled modes in each file match the enabled modes in the
+ * selection
+ * 9) that no extraneous fds are set in the enabled vectors
+ *
+ * If there are no pending fds:
+ *
+ * 10) if there are no pending fds, that the results vectors are empty.
+ *
+ * If there are pending fds:
+ *
+ * 11) that pend_mnum is valid and pend_fd <= tried_fd_last.
+ *
+ * 12) that the tried_count for modes 0..pend_mnum-1 is zero,
+ * and the tried_count for pend_mnum is not.
+ *
+ * 13) that the result vectors for modes where tried count == 0 are empty.
+ *
+ * 14) that the remaining result bits are a subset of the enabled bits.
+ *
+ * 15) that no bits beyond tried_fd_last are set in the result vectors.
+ *
+ * 16) that no bits before pend_fd are set in the pemd_mnum result vector.
+ *
+ * 17) that the number of bits remaining matches pend_count.
+ */
+static void
+qps_selection_validate(qps_selection qps)
+{
+ int fd_last ;
+ int enabled_count[qps_mnum_count] ;
+ fd_full_set enabled ;
+
+ qps_file qf ;
+ int fd, n, mnum, p_mnum ;
+ vector_index i ;
+
+ /* 1..4) Run down the selection vector and check. */
+ /* Collect new enabled_count and enabled bit vectors. */
+
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ enabled_count[mnum] = 0 ;
+ qps_super_set_zero(enabled, qps_mnum_count) ;
+
+ n = 0 ;
+ fd_last = -1 ;
+ for (VECTOR_ITEMS(&qps->files, qf, i))
+ {
+ if (qf != NULL)
+ {
+ ++n ; /* Number of files */
+
+ if (qps->fd_direct)
+ {
+ if (qf->fd != (int)i) /* index and fd must match */
+ zabort("File vector index and fd mismatch") ;
+ }
+ else
+ {
+ if (qf->fd <= fd_last) /* must be unique and in order */
+ zabort("File vector not in order") ;
+ } ;
+
+ fd_last = qf->fd ; /* keep track of last fd */
+
+ if (qf->selection != qps) /* file must refer to selection */
+ zabort("File does not refer to its selection") ;
+
+ if ((qf->enabled_bits < 0) || (qf->enabled_bits > qps_all_mbits))
+ zabort("File enabled bits are invalid") ;
+
+ /* Capture enabled state of all files. */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (qf->enabled_bits & qps_mbit(mnum))
+ {
+ ++enabled_count[mnum] ;
+ FD_SET(qf->fd, &enabled[mnum].fdset) ;
+ } ;
+ }
+ else
+ if (!qps->fd_direct)
+ zabort("Found NULL entry in !fd_direct files vector") ;
+ } ;
+
+ if ((n != 0) && (vector_get_last_item(&qps->files) == NULL))
+ zabort("Last entry in file vector is NULL") ;
+
+ /* 5) check that the number of files tallies. */
+ if (n != qps->fd_count)
+ zabort("Number of files in the selection does not tally") ;
+
+ /* 6) check the last fd */
+ if ( (qps->fd_last < (n - 1)) || (fd_last != qps->fd_last) )
+ zabort("The last fd does not tally") ;
+
+ /* 7) check that the enabled counts tally. */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (enabled_count[mnum] != qps->enabled_count[mnum])
+ zabort("Enabled counts do not tally") ;
+
+ /* 8..9) Check that the enabled vectors are the same as the ones just */
+ /* created by scanning the files. */
+ if (qps_super_set_cmp(enabled, qps->enabled, qps_mnum_count) != 0)
+ zabort("Enabled bit vectors do not tally") ;
+
+ /* 10) if there are no pending fds, check result vectors empty. */
+ if (qps->pend_count == 0)
+ {
+ if (qps_super_set_count(qps->results, qps_mnum_count) != 0)
+ zabort("Nothing pending, but result vectors not empty") ;
+
+ return ;
+ } ;
+
+ /* This is to stop gcc whining about signed/unsigned comparisons. */
+ p_mnum = qps->pend_mnum ;
+
+ /* 11) that pend_mnum is valid and pend_fd <= tried_fd_last. */
+ if ( (p_mnum < 0) || (p_mnum > qps_mnum_count)
+ || (qps->pend_fd < 0)
+ || (qps->pend_fd > qps->tried_fd_last) )
+ zabort("Invalid pend_mnum or pend_fd") ;
+
+ /* 12) check tried_count[] */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ {
+ if ((mnum < p_mnum) && (qps->tried_count[mnum] != 0))
+ zabort("Non-zero tried_count for mode < pend_mnum") ;
+ if ((mnum == p_mnum) && (qps->tried_count[qps->pend_mnum] <= 0))
+ zabort("Zero tried_count for pend_mnum") ;
+ if ((mnum > p_mnum) && (qps->tried_count[mnum] < 0))
+ zabort("Invalid tried_count for mode > pend_mnum") ;
+ } ;
+
+ /* 13) check result vectors for modes where tried count == 0 */
+ n = (qps_super_set_count(qps->results, qps_mnum_count) != 0) ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if ((qps->tried_count[mnum] == 0)
+ && (qps_super_set_count(&qps->results[mnum], 1) != 0))
+ zabort("Non-empty bit vector where tried count == 0") ;
+
+ /* 14) check remaining results are a subset of the enableds. */
+ /* 15) check no bit beyond tried_fd_last is set in the results. */
+ /* 16) check no bit before pend_fd is set in the pemd_mnum results. */
+ /* 17) check the number of bits remaining matches pend_count. */
+
+ n = 0 ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (qps->tried_count[mnum] != 0)
+ {
+ for (fd = 0 ; fd < (int)FD_SETSIZE ; ++fd)
+ if (FD_ISSET(fd, &qps->results[mnum].fdset))
+ {
+ ++n ;
+ if (fd > qps->tried_fd_last)
+ zabort("Found pending fd beyond tried_fd_last") ;
+ if ( ! FD_ISSET(fd, &qps->results[mnum].fdset))
+ zabort("Found pending fd which is not enabled") ;
+ if ((mnum == p_mnum) && (fd < qps->pend_fd))
+ zabort("Found pending fd < current next pending") ;
+ } ;
+ } ;
+
+ if (n != qps->pend_count)
+ zabort("Non-empty bit vector where tried count == 0") ;
+} ;
diff --git a/lib/qpselect.h b/lib/qpselect.h
new file mode 100644
index 00000000..8901ea36
--- /dev/null
+++ b/lib/qpselect.h
@@ -0,0 +1,241 @@
+/* Quagga pselect support -- 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 _ZEBRA_QPSELECT_H
+#define _ZEBRA_QPSELECT_H
+
+#include <sys/select.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qtime.h"
+#include "vector.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga pselect -- qps_xxxx
+ *
+ * Here and in qpselect.c is a data structure for managing multiple file
+ * descriptors and running pselect to wait for I/O activity and to multiplex
+ * between the file descriptors.
+ */
+
+enum qps_mnum /* "mode" numbers: error/read/write */
+{
+ qps_mnum_first = 0,
+
+ qps_error_mnum = 0,
+ qps_read_mnum = 1,
+ qps_write_mnum = 2,
+
+ qps_mnum_count = 3
+} ;
+
+typedef enum qps_mnum qps_mnum_t ;
+
+#define qps_mbit(mnum) (1 << mnum)
+
+enum qps_mbits /* "mode" bits: error/read/write */
+{
+ qps_error_mbit = qps_mbit(qps_error_mnum),
+ qps_read_mbit = qps_mbit(qps_read_mnum),
+ qps_write_mbit = qps_mbit(qps_write_mnum),
+
+ qps_all_mbits = qps_mbit(qps_mnum_count) - 1
+} ;
+
+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 ;
+
+/*==============================================================================
+ * fd_super_set.
+ *
+ * To speed up scanning of large fd_set's this structure overlays a 32-bit
+ * word and a byte array over the (assumed) fd_set bit vector.
+ *
+ * There is no guarantee that FD_SETSIZE is a multiple of 32 (or of 8, for
+ * that matter) -- so some care must be taken.
+ */
+
+typedef uint32_t fd_word_t ;
+
+#define FD_WORD_BITS 32
+#define FD_WORD_BYTES (FD_WORD_BITS / 8)
+
+CONFIRM(FD_WORD_BITS == (FD_WORD_BYTES * 8)) ; /* for completeness */
+
+#define FD_SUPER_SET_WORD_SIZE ((FD_SETSIZE + FD_WORD_BITS - 1) / FD_WORD_BITS)
+#define FD_SUPER_SET_BYTE_SIZE (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)
+
+/* Make sure that the overlay is at least as big as the fd_set ! */
+CONFIRM(FD_SUPER_SET_BYTE_SIZE >= sizeof(fd_set)) ;
+
+typedef union /* see qps_make_super_set_map() */
+{
+ fd_word_t words[FD_SUPER_SET_WORD_SIZE] ;
+ uint8_t bytes[FD_SUPER_SET_BYTE_SIZE] ;
+ fd_set fdset ;
+} fd_super_set ;
+
+/* Make sure that the fd_super_set is an exact number of fd_word_t words */
+CONFIRM(sizeof(fd_super_set) == (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)) ;
+
+/*==============================================================================
+ * Action function.
+ *
+ * Each file has three action functions, to be called in qps_dispatch_next()
+ * when pselect() has reported error/read/write for the file.
+ *
+ * For further discussion, see: qps_file_init.
+ */
+
+typedef void qps_action(qps_file qf, void* file_info) ;
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef fd_super_set fd_full_set[qps_mnum_count] ;
+
+struct qps_selection
+{
+ int fd_count ; /* number of fds we are looking after */
+ int fd_direct ; /* direct lookup in vector or not */
+
+ struct vector files ; /* mapping fd to qps_file */
+
+ int fd_last ; /* highest numbered fd; -1 => none at all */
+ int enabled_count[qps_mnum_count] ; /* no. enabled fds in each mode */
+ fd_full_set enabled ; /* bit vectors for pselect enabled stuff */
+
+ int tried_fd_last ; /* highest numbered fd on last pselect */
+ int tried_count[qps_mnum_count] ; /* enabled_count on last pselect */
+ fd_full_set results ; /* last set of results from pselect */
+
+ int pend_count ; /* results pending (if any) */
+ qps_mnum_t pend_mnum ; /* error/read/write mode pending (if any) */
+ int pend_fd ; /* fd pending (if any) */
+
+ int signum ; /* signal that sigmask is enabling -- 0 => none */
+ sigset_t sigmask ; /* sigmask to use for duration of pselect */
+} ;
+
+struct qps_file
+{
+ qps_selection selection ;
+
+ void* file_info ;
+ int fd ;
+
+ qps_mbit_t enabled_bits ;
+
+ qps_action* actions[qps_mnum_count] ;
+} ;
+
+/*==============================================================================
+ * qps_selection handling
+ */
+
+extern void
+qps_start_up(void) ;
+
+extern qps_selection
+qps_selection_init_new(qps_selection qps) ;
+
+extern void
+qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info) ;
+
+extern void
+qps_remove_file(qps_file qf) ;
+
+extern qps_file
+qps_selection_ream(qps_selection qps, int free_structure) ;
+
+/* Ream out selection and free the selection structure. */
+#define qps_selection_ream_free(qps) qps_selection_ream(qps, 1)
+/* Ream out selection but keep the selection structure. */
+#define qps_selection_ream_keep(qps) qps_selection_ream(qps, 0)
+
+extern void
+qps_set_signal(qps_selection qps, int signum, sigset_t sigmask) ;
+
+extern int
+qps_pselect(qps_selection qps, qtime_mono_t timeout) ;
+
+extern int
+qps_dispatch_next(qps_selection qps) ;
+
+/*==============================================================================
+ * qps_file structure handling
+ */
+
+extern qps_file
+qps_file_init_new(qps_file qf, qps_file template) ;
+
+extern qps_file
+qps_file_free(qps_file qf) ;
+
+extern void
+qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
+
+extern void
+qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
+
+extern 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
new file mode 100644
index 00000000..baa34d52
--- /dev/null
+++ b/lib/qpthreads.c
@@ -0,0 +1,775 @@
+/* Quagga Pthreads support -- 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.
+ */
+
+/* This MUST come first... otherwise we don't get __USE_UNIX98, which is */
+/* essential if glibc is to allow pthread_mutexattr_settype() to be used. */
+#include "config.h"
+
+#include <signal.h>
+#include <string.h>
+
+#include "qpthreads.h"
+#include "memory.h"
+
+/* If this is not set, will get errors later. */
+//#ifndef __USE_UNIX98
+//#error "_USE_UNIX98 not defined"
+//#endif
+
+/*==============================================================================
+ * Quagga Pthread Interface -- qpt_xxxx
+ *
+ * Here (and in qpthreads.h) are captured all the pthreads features used in
+ * Quagga.
+ *
+ * This provides:
+ *
+ * * "wrappers" around functions which should not fail, but whose return
+ * code it is best to check... at least in a debug environment.
+ *
+ * * the possibility of a separate no pthreads build where pthread facilities
+ * are either dummied out or otherwise dealt with.
+ *
+ * * the ability to add any work-arounds which may be required if poorly
+ * conforming pthreads implementations are encountered
+ *
+ * Continued Working Without Pthreads
+ * ==================================
+ *
+ * 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.
+ *
+ * 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
+ * ====================
+ *
+ * This is assuming support for 1003.1-2004 -- XOPEN Issue 6, with [THR] and
+ * [XSI] options.
+ *
+ * The [XSI] is required for pthread_mutexattr_settype(), only.
+ *
+ * If qpt_thread_attr_init() uses:
+ *
+ * pthread_attr_getinheritsched()/_setinheritshed() [TPS]
+ * pthread_attr_getscope()/_setscope() [TPS]
+ * pthread_attr_getschedpolicy()/_setschedpolicy() [TPS]
+ * pthread_attr_getschedparam()/_setschedparam() [THR]
+ *
+ * but they are only required if explicit scheduling attributes are being set.
+ * (So, could be dropped where not supported.)
+ *
+ * Amongst the things which are NOT required:
+ *
+ * pthread_attr_getguardsize()/_setguardsize() [XSI]
+ * pthread_attr_getstack()/_setstack() [TSA TSS]
+ * pthread_attr_getstackaddr()/_setstackaddr() [TSA OB]
+ * pthread_attr_getstacksize()/_setstacksize() [TSA TSS]
+ *
+ * pthread_barrier_xxx() [BAR]
+ *
+ * pthread_condattr_getpshared()/_setpshared() [TSH]
+ *
+ * pthread_mutex_getprioceiling()/_setprioceiling() [TPP]
+ * pthread_mutex_timedlock() [TMO] pro tem
+ * pthread_mutexattr_getprioceiling()/_setprioceiling() [TPP]
+ * pthread_mutexattr_getprotocol()/_setprotocol() [TPP TPI]
+ * pthread_mutexattr_getpshared()/_setpshared() [TSH]
+ *
+ * pthread_rwlock_xxx() [THR] pro tem
+ * pthread_rwlockattr_init()/_destroy() [THR] pro tem
+ * pthread_rwlockattr_getpshared()/_setpshared() [TSH]
+ *
+ * pthread_spin_xxx() [SPI]
+ *
+ * [CS] (Clock Select) is assumed if HAVE_CLOCK_MONOTONIC.
+ *
+ * In 1003.1-2008, XOPEN issue 7, [THR] and pthread_mutexattr_settype() have
+ * been moved to Base.
+ *
+ * NB: it is essential that pthread_kill() delivers the signal to the target
+ * thread only -- ie, it must be POSIX compliant. That rules out the old
+ * (2.4) LinuxThreads. For Linux, 2.6 (or greater) is required, with
+ * NPTL (these days generally included in glibc).
+ *
+ * NB: for glibc to give all the required features, either _GNU_SOURCE or
+ * _XOPEN_SOURCE must be set *before* the first #include <features.h>.
+ * _XOPEN_SOURCE=600 is sufficient.
+ *
+ * Pthread Thread Attributes -- Scheduling
+ * =======================================
+ *
+ * Pthreads defines some useful looking real-time scheduling features.
+ *
+ * One would like to be able to give I/O intensive threads an advantage over
+ * CPU bound threads.
+ *
+ * Unfortunately, conformance allows a system to have its own scheduling
+ * system -- so long as the standard ones are implemented. Further, there is
+ * no way of telling what priority values are reasonable, even in the standard
+ * scheduling policies.
+ *
+ * The approach taken here is that by default a thread will be created with
+ * the system default attributes -- which may mean inheriting the creating
+ * thread's scheduling attributes.
+ *
+ * It is also possible to construct a set of attributes, using the most
+ * obviously useful properties. It is envisaged that this may be used when a
+ * configuration file is used to set locally sensible values. The attributes
+ * supported are:
+ *
+ * * attr_detached -- whether to start detached or not
+ * * attr_inherit_sched -- whether to inherit scheduling attributes
+ * * attr_sched_scope -- scheduling scope
+ * * attr_sched_policy -- scheduling policy
+ * * attr_sched_priority -- scheduling priority
+ *
+ * See qpt_thread_attr_init, below.
+ *
+ * Not supported here are:
+ *
+ * * attr_guardsize
+ * * attr_stack
+ * * attr_stacksize
+ *
+ * Pthread Mutex Attributes -- Error Checking
+ * ==========================================
+ *
+ * Mutexes are kept simple, only attr_type is used, and that by default.
+ *
+ * POSIX defines four types of mutex:
+ *
+ * _NORMAL no ownership check -- owner will deadlock if locks mutex !
+ * -- undefined what happens if unlock
+ * mutex not owned by self !
+ * no recursive locking
+ *
+ * _ERRORCHECK checks for ownership on lock and unlock
+ * no recursive locking
+ *
+ * _RECURSIVE checks for ownership on lock and unlock
+ * counts up locks and counts down unlocks
+ *
+ * This looks useful, but goes wrong with condition variables !
+ *
+ * _DEFAULT undefined whether checks owner or not, on lock and/or unlock.
+ * no recursive locking
+ *
+ * See qpthreads.h for discussion of Quagga's standard type (QPT_MUTEX_TYPE).
+ *
+ * Other attributes are left in their default state:
+ *
+ * * attr_prioceiling -- default undefined
+ * * attr_protocol -- default undefined
+ * * attr_pshared -- defaults to _PROCESS_PRIVATE
+ *
+ * For the time being it is assumed that these are too exotic.
+ *
+ * Pthread Condition Variable Attributes
+ * =====================================
+ *
+ * Condition variables have only two attributes:
+ *
+ * * attr_clock -- which clock to use
+ * * attr_pshared -- defaults to _PROCESS_PRIVATE
+ *
+ * The use a clock other than Quagga's standard (QPT_COND_CLOCK_ID) is possible,
+ * but not recommended. (See qpthreads.h for discussion of this.)
+ *
+ * Pthread Specific Signal Handling
+ * ================================
+ *
+ * In a threaded application, need to use pthread_sigmask (not sigproc_mask).
+ * (Can use pthread_sigmask in a single threaded application.)
+ *
+ * To direct a signal at a given thread need pthread_kill. *
+ */
+
+/*==============================================================================
+ * The Global Switch
+ *
+ * The state of the switch is: unset -- implicitly not enabled
+ * set_frozen -- implicitly not enabled & frozen
+ * set_disabled -- explicitly not enabled
+ * set_enabled -- explicitly set enabled
+ *
+ * "set_frozen" means that "qpthreads_freeze_enabled_state()" has been called,
+ * and the state was unset at the time. This means that some initialisation
+ * has been done on the basis of !qpthreads_enabled, and it is TOO LATE to
+ * enable qpthreads afterwards.
+ */
+
+enum qpthreads_enabled_state
+{
+ qpt_state_unset = 0,
+ qpt_state_set_frozen = 1,
+ qpt_state_set_disabled = 2,
+ qpt_state_set_enabled = 3,
+} ;
+
+static enum qpthreads_enabled_state qpthreads_enabled_state = qpt_state_unset ;
+
+uint8_t qpthreads_enabled_flag = 0 ;
+uint8_t qpthreads_thread_created_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.
+ */
+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)
+ return 0 ;
+ break ;
+ case qpt_state_set_disabled:
+ if (how != 0)
+ zabort("qpthreads_enabled is already set: cannot set enabled") ;
+ break ;
+ case qpt_state_set_enabled:
+ if (how == 0)
+ zabort("qpthreads_enabled is already set: cannot set disabled") ;
+ break ;
+ default:
+ break ;
+ }
+
+ 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.
+ *
+ * Where some initialisation depends on the state of qpthreads_enabled(), this
+ * returns the state and freezes it if it is implicitly not enabled.
+ */
+extern int
+qpt_freeze_qpthreads_enabled(void)
+{
+ if (qpthreads_enabled_state == qpt_state_unset)
+ qpthreads_enabled_state = qpt_state_set_frozen ;
+
+ return qpthreads_enabled_flag ;
+} ;
+
+/*==============================================================================
+ * Thread creation and attributes.
+ *
+ * Threads may be created with a given set of attributes if required.
+ *
+ * qpt_thread_attr_init() will initialise a set of attributes including the
+ * current standard scheduling attributes. It is envisaged that configuration
+ * options may be used to specify these.
+ *
+ * qpt_thread_create() creates a thread using the given attributes. If those
+ * are NULL, then the system defaults are used.
+ */
+
+/* Initialise a set of attributes -- setting the scheduling options.
+ *
+ * Options:
+ *
+ * qpt_attr_joinable -- the default if nothing specified.
+ * qpt_attr_detached -- overrides qpt_attr_joinable.
+ *
+ * qpt_attr_sched_inherit -- all scheduling attributes are to be inherited.
+ * No explicit scheduling attributes may be set.
+ *
+ * qpt_attr_sched_scope -- set explicit, given, scope.
+ * qpt_attr_sched_policy -- set explicit, given, policy
+ * qpt_attr_sched_priority -- set explicit, given, priority
+ *
+ * If none of the _sched_ options are given, then the scheduling attributes are
+ * left to whatever default values the system chooses.
+ *
+ * If the _sched_inherit option is specified, none of the other _sched_ options
+ * may be specified.
+ *
+ * If any of the explicit scheduling options are given, they are set in this
+ * order. If only some of these options are given, then the caller is
+ * assuming that the system will choose sensible defaults.
+ *
+ * The scope, policy and priority arguments are use only if the corresponding
+ * option is specified.
+ *
+ * NB: FATAL error to attempt this is !qptthreads_enabled.
+ *
+ * Returns the address of the qpt_thread_attr_t structure.
+ */
+qpt_thread_attr_t*
+qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
+ int scope, int policy, int priority)
+{
+ int err ;
+
+ assert((opts & ~qpt_attr_known) == 0) ;
+ passert(qpthreads_enabled) ;
+
+ /* Initialise thread attributes structure (allocating if required.) */
+ if (attr == NULL)
+ attr = XMALLOC(MTYPE_QPT_THREAD_ATTR, sizeof(qpt_thread_attr_t)) ;
+
+ err = pthread_attr_init(attr) ;
+ if (err != 0)
+ zabort_err("pthread_attr_init failed", err) ;
+
+ /* If not qpt_attr_detached, then set joinable. */
+ err = pthread_attr_setdetachstate(attr,
+ (opts & qpt_attr_detached) ? PTHREAD_CREATE_DETACHED
+ : PTHREAD_CREATE_JOINABLE) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setdetachstate failed", err) ;
+
+ /* If setting anything to do with scheduling... */
+ if (opts & qpt_attr_sched_setting)
+ {
+ /* Either we inherit or we set explicit parameters. */
+
+ err = pthread_attr_setinheritsched(attr,
+ (opts & qpt_attr_sched_inherit) ? PTHREAD_INHERIT_SCHED
+ : PTHREAD_EXPLICIT_SCHED) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setinheritsched", err) ;
+
+ if (opts & qpt_attr_sched_inherit)
+ assert((opts & qpt_attr_sched_explicit) == 0) ;
+ else
+ {
+ if (opts & qpt_attr_sched_scope)
+ {
+ err = pthread_attr_setscope(attr, scope) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setscope failed", err) ;
+ } ;
+ if (opts & qpt_attr_sched_policy)
+ {
+ err = pthread_attr_setschedpolicy(attr, scope) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setschedpolicy failed", err) ;
+ } ;
+ if (opts & qpt_attr_sched_priority)
+ {
+ struct sched_param sparm ;
+ err = pthread_attr_getschedparam(attr, &sparm) ;
+ if (err != 0)
+ zabort_err("pthread_attr_getschedparam failed", err) ;
+ sparm.sched_priority = priority ;
+ err = pthread_attr_setschedparam(attr, &sparm) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setschedparam failed", err) ;
+ } ;
+ } ;
+ } ;
+
+ /* Done -- return qpt_thread_attr_t* */
+ return attr ;
+} ;
+
+/* Create Thread with given attributes (if any).
+ *
+ * If no attributes are given (attr == NULL) the thread is created with system
+ * default attributes -- *except* that it is created joinable.
+ *
+ * NB: FATAL error to attempt this is !qptthreads_enabled.
+ *
+ * Returns the qpt_thread_t "thread id".
+ */
+qpt_thread_t
+qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr)
+{
+ qpt_thread_attr_t thread_attr ;
+ qpt_thread_t thread_id ;
+ int default_attr ;
+ int err ;
+
+ passert(qpthreads_enabled) ;
+ qpthreads_thread_created_flag = 1 ; /* and at least one thread created */
+
+ default_attr = (attr == NULL) ;
+ if (default_attr)
+ attr = qpt_thread_attr_init(&thread_attr, qpt_attr_joinable, 0, 0, 0) ;
+
+ err = pthread_create(&thread_id, attr, start, arg) ;
+ if (err != 0)
+ zabort_err("pthread_create failed", err) ;
+
+ if (default_attr)
+ {
+ err = pthread_attr_destroy(attr) ; /* being tidy */
+ if (err != 0)
+ zabort_err("pthread_attr_destroy failed", err) ;
+ } ;
+
+ return thread_id ;
+} ;
+
+/* Join given thread -- do nothing if !qpthreads_enabled
+ *
+ * Tolerates ESRCH (no thread known by given id).
+ *
+ * Returns whatever the thread returns, NULL otherwise.
+ *
+ * NB: all other errors are FATAL.
+ */
+extern void*
+qpt_thread_join(qpt_thread_t thread_id)
+{
+ int err ;
+ void* ret ;
+
+ if (!qpthreads_enabled)
+ return NULL ;
+
+ err = pthread_join(thread_id, &ret) ;
+
+ if (err == 0)
+ return ret ;
+
+ if (err == ESRCH)
+ return NULL ;
+
+ zabort_err("pthread_join failed", err) ;
+} ;
+
+/*==============================================================================
+ * Mutex initialise and destroy.
+ */
+
+/* Initialise Mutex (allocating if required)
+ *
+ * Does nothing if !qpthreads_enabled -- but freezes the state (attempting to
+ * later enable qpthreads will be a FATAL error).
+ *
+ * Options:
+ *
+ * qpt_mutex_quagga -- see qpthreads.h for discussion of this.
+ * qpt_mutex_normal -- ie PTHREAD_MUTEX_NORMAL
+ * qpt_mutex_recursive -- ie PTHREAD_MUTEX_RECURSIVE
+ * qpt_mutex_errorcheck -- ie PTHREAD_MUTEX_ERRORCHECK
+ * qpt_mutex_default -- system default
+ *
+ * Of these _recursive is the most likely alternative to _quagga... BUT do
+ * remember that such mutexes DO NOT play well with condition variables.
+ *
+ * Returns the mutex -- or original mx if !qpthreads_enabled.
+ */
+qpt_mutex
+qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts)
+{
+ pthread_mutexattr_t mutex_attr ;
+ int type ;
+ int err ;
+
+ if (!qpthreads_enabled_freeze)
+ {
+ if (mx != NULL)
+ memset(mx, 0x0F, sizeof(qpt_mutex_t)) ;
+ return mx ;
+ } ;
+
+ if (mx == NULL)
+ mx = XMALLOC(MTYPE_QPT_MUTEX, sizeof(qpt_mutex_t)) ;
+
+ /* Set up attributes so we can set the mutex type */
+ err = pthread_mutexattr_init(&mutex_attr);
+ if (err != 0)
+ zabort_err("pthread_mutexattr_init failed", err) ;
+
+ switch(opts)
+ {
+ case qpt_mutex_quagga:
+ type = QPT_MUTEX_TYPE ;
+ break ;
+ case qpt_mutex_normal:
+ type = PTHREAD_MUTEX_NORMAL ;
+ break ;
+ case qpt_mutex_recursive:
+ type = PTHREAD_MUTEX_RECURSIVE ;
+ break ;
+ case qpt_mutex_errorcheck:
+ type = PTHREAD_MUTEX_ERRORCHECK ;
+ break ;
+ case qpt_mutex_default:
+ type = PTHREAD_MUTEX_DEFAULT ;
+ break ;
+ default:
+ zabort("Invalid qpt_mutex option") ;
+ } ;
+
+ err = pthread_mutexattr_settype(&mutex_attr, type);
+ if (err != 0)
+ zabort_err("pthread_mutexattr_settype failed", err) ;
+
+ /* Now we're ready to initialize the mutex itself */
+ err = pthread_mutex_init(mx, &mutex_attr) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_init failed", err) ;
+
+ /* Be tidy with the attributes */
+ err = pthread_mutexattr_destroy(&mutex_attr) ;
+ if (err != 0)
+ zabort_err("pthread_mutexattr_destroy failed", err) ;
+
+ /* Done: return the mutex */
+ return mx ;
+} ;
+
+/* Destroy given mutex, and (if required) free it.
+ * -- or do nothing if !qpthreads_enabled.
+ *
+ * Returns NULL if freed the mutex, otherwise the address of same.
+ *
+ * NB: if !qpthreads_enabled qpt_mutex_init_new() will not have allocated
+ * anything, so there can be nothing to release -- so does nothing, but
+ * returns the original mutex address (if any).
+ */
+qpt_mutex
+qpt_mutex_destroy(qpt_mutex mx, int free_mutex)
+{
+ int err ;
+
+ if (qpthreads_enabled)
+ {
+ err = pthread_mutex_destroy(mx) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_destroy failed", err) ;
+
+ if (free_mutex)
+ XFREE(MTYPE_QPT_MUTEX, mx) ; /* sets mx == NULL */
+ } ;
+
+ return mx ;
+} ;
+
+/*==============================================================================
+ * Condition Variable initialise and destroy.
+ */
+
+/* Initialise Condition Variable (allocating if required).
+ *
+ * Does nothing if !qpthreads_enabled -- but freezes the state (attempting to
+ * later enable qpthreads will be a FATAL error).
+ *
+ * Options:
+ *
+ * qpt_cond_quagga -- use Quagga's default clock
+ * qpt_cond_realtime -- force CLOCK_REALTIME
+ * qpt_cond_monotonic -- force CLOCK_MONOTONIC (if available)
+ *
+ * NB: FATAL error to attempt this is !qptthreads_enabled.
+ *
+ * Returns the condition variable -- or original cv id !qpthreads_enabled.
+ */
+qpt_cond
+qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts)
+{
+ pthread_condattr_t cond_attr ;
+ int err ;
+
+ if (!qpthreads_enabled_freeze)
+ {
+ if (cv != NULL)
+ memset(cv, 0x0F, sizeof(qpt_cond_t)) ;
+ return cv ;
+ } ;
+
+ if (cv == NULL)
+ cv = XMALLOC(MTYPE_QPT_COND, sizeof(qpt_cond_t)) ;
+
+ /* Set up attributes so we can set the type */
+ err = pthread_condattr_init(&cond_attr);
+ if (err != 0)
+ zabort_err("pthread_condattr_init failed", err) ;
+
+ switch(opts)
+ {
+ case qpt_cond_quagga:
+ break ;
+ default:
+ zabort("Invalid qpt_cond option") ;
+ } ;
+
+ err = pthread_condattr_setclock(&cond_attr, QPT_COND_CLOCK_ID);
+ if (err != 0)
+ zabort_err("pthread_condattr_setclock failed", err) ;
+
+ /* Now we're ready to initialize the condition variable itself */
+ err = pthread_cond_init(cv, &cond_attr) ;
+ if (err != 0)
+ zabort_err("pthread_cond_init failed", err) ;
+
+ /* Be tidy with the attributes */
+ err = pthread_condattr_destroy(&cond_attr) ;
+ if (err != 0)
+ zabort_err("pthread_condattr_destroy failed", err) ;
+
+ /* Done: return the condition variable */
+ return cv ;
+} ;
+
+/* Destroy given condition variable, and (if required) free it
+ * -- or do nothing if !qpthreads_enabled.
+ *
+ * NB: if !qpthreads_enabled qpt_cond_init_new() will not have allocated
+ * anything, so there can be nothing to release -- so does nothing, but
+ * returns the original condition variable address (if any).
+ *
+ * Returns NULL if freed the condition variable, otherwise the address of same.
+ */
+qpt_cond
+qpt_cond_destroy(qpt_cond cv, int free_cond)
+{
+ int err ;
+
+ if (qpthreads_enabled)
+ {
+ err = pthread_cond_destroy(cv) ;
+ if (err != 0)
+ zabort_err("pthread_cond_destroy failed", err) ;
+
+ if (free_cond)
+ XFREE(MTYPE_QPT_COND, cv) ; /* sets cv == NULL */
+ } ;
+
+ return cv ;
+} ;
+
+/* Wait for given condition variable or time-out
+ * -- or return immediate success if !qpthreads_enabled.
+ *
+ * Returns: wait succeeded (1 => success, 0 => timed-out).
+ *
+ * NB: timeout time is a qtime_mono_t (monotonic time).
+ *
+ * Has to check the return value, so zabort_errno if not EBUSY.
+ */
+
+int
+qpt_cond_timedwait(qpt_cond cv, qpt_mutex mx, qtime_mono_t timeout_time)
+{
+ struct timespec ts ;
+ int err ;
+
+ if (qpthreads_enabled)
+ {
+ if (QPT_COND_CLOCK_ID != CLOCK_MONOTONIC)
+ {
+ timeout_time = qt_clock_gettime(QPT_COND_CLOCK_ID)
+ + (timeout_time - qt_get_monotonic()) ;
+ } ;
+
+ err = pthread_cond_timedwait(cv, mx, qtime2timespec(&ts, timeout_time)) ;
+ if (err == 0)
+ return 1 ; /* got condition */
+ if (err == ETIMEDOUT)
+ return 0 ; /* got time-out */
+
+ zabort_err("pthread_cond_timedwait failed", err) ;
+ }
+ else
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Signal Handling.
+ */
+
+/* Set thread signal mask -- requires qpthreads_enabled.
+ *
+ * Thin wrapper around pthread_sigmask.
+ *
+ * zaborts if gets any error.
+ *
+ * NB: it is a FATAL error to do this if !qpthreads_enabled.
+ *
+ * This is mostly because wish to avoid all pthreads_xxx calls when not
+ * using pthreads. There is no reason not to use this in a single threaded
+ * program.
+ */
+void
+qpt_thread_sigmask(int how, const sigset_t* set, sigset_t* oset)
+{
+ int err ;
+
+ passert(qpthreads_enabled) ;
+
+ if (oset != NULL)
+ sigemptyset(oset) ; /* to make absolutely sure */
+
+ err = pthread_sigmask(how, set, oset) ;
+ if (err != 0)
+ zabort_err("pthread_sigmask failed", err) ;
+} ;
+
+/* Send given thread the given signal -- requires qpthreads_enabled (!)
+ *
+ * Thin wrapper around pthread_kill.
+ *
+ * zaborts if gets any error.
+ */
+void
+qpt_thread_signal(qpt_thread_t thread, int signum)
+{
+ int err ;
+
+ passert(qpthreads_enabled) ;
+
+ err = pthread_kill(thread, signum) ;
+ if (err != 0)
+ zabort_err("pthread_kill failed", err) ;
+} ;
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
new file mode 100644
index 00000000..d73182ef
--- /dev/null
+++ b/lib/qpthreads.h
@@ -0,0 +1,443 @@
+/* Quagga Pthreads support -- 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 _ZEBRA_QPTHREADS_H
+#define _ZEBRA_QPTHREADS_H
+
+#include <stdint.h>
+#include <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "zassert.h"
+#include "qtime.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+#ifndef private
+#define private extern
+#endif
+
+/*==============================================================================
+ * Quagga Pthread Interface -- qpt_xxxx
+ *
+ * Here are captured all the pthreads features used in Quagga.
+ *
+ * This provides:
+ *
+ * * "wrappers" around functions which should not fail, but whose return
+ * code it is best to check... at least in a debug environment.
+ *
+ * * the possibility of a separate no pthreads build where pthread facilities
+ * are either dummied out or otherwise dealt with.
+ *
+ * * the ability to add any work-arounds which may be required if poorly
+ * conforming pthreads implementations are encountered
+ */
+
+#if !defined(_POSIX_THREADS) || (_POSIX_THREADS <= 0)
+#error Require _POSIX_THREADS
+#endif
+
+/*==============================================================================
+ * Global Switch -- this allows the library to be run WITHOUT pthreads !
+ *
+ * Nearly every qpthreads function is a NOP if !qpthreads_enabled.
+ *
+ * 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 (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
+ */
+
+#define qpthreads_enabled ((const uint8_t)qpthreads_enabled_flag)
+#define qpthreads_enabled_freeze qpt_freeze_qpthreads_enabled()
+
+#define qpthreads_thread_created ((const uint8_t) \
+ qpthreads_thread_created_flag)
+
+/*==============================================================================
+ * Data types
+ */
+
+typedef pthread_t qpt_thread_t ;
+typedef pthread_mutex_t qpt_mutex_t ;
+typedef pthread_cond_t qpt_cond_t ;
+
+typedef pthread_attr_t qpt_thread_attr_t ;
+
+typedef qpt_mutex_t* qpt_mutex ;
+typedef qpt_cond_t* qpt_cond ;
+
+/*==============================================================================
+ * Thread Creation -- see qpthreads.c for further discussion.
+ *
+ * NB: it is a FATAL error to attempt these if !qpthreads_enabled.
+ */
+
+enum qpt_attr_options
+{
+ qpt_attr_joinable = 0, /* the default for Quagga */
+
+ qpt_attr_detached = 0x0001, /* otherwise will set joinable */
+
+ qpt_attr_sched_inherit = 0x0002, /* otherwise will use default */
+
+ qpt_attr_sched_scope = 0x0004, /* otherwise inherit/default */
+ qpt_attr_sched_policy = 0x0008, /* otherwise inherit/default */
+ qpt_attr_sched_priority = 0x0010, /* otherwise inherit/default */
+} ;
+
+#define qpt_attr_sched_explicit ( qpt_attr_sched_scope \
+ | qpt_attr_sched_policy \
+ | qpt_attr_sched_priority )
+
+#define qpt_attr_sched_setting ( qpt_attr_sched_inherit \
+ | qpt_attr_sched_explicit )
+
+#define qpt_attr_known ( qpt_attr_detached | qpt_attr_sched_setting )
+
+extern qpt_thread_attr_t* /* FATAL error if !qpthreads_enabled */
+qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
+ int scope, int policy, int priority) ;
+extern qpt_thread_t /* FATAL error if !qpthreads_enabled */
+qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr) ;
+
+extern void* /* do nothing if !qpthreads_enabled */
+qpt_thread_join(qpt_thread_t thread_id) ;
+
+/*==============================================================================
+ * qpthreads_enabled support -- NOT FOR PUBLIC CONSUMPTION !
+ */
+private uint8_t qpthreads_enabled_flag ; /* DO NOT TOUCH THIS PLEASE */
+private uint8_t qpthreads_thread_created_flag ; /* DO NOT TOUCH THIS PLEASE */
+
+private int
+qpt_set_qpthreads_enabled(int how) ; /* qpthreads_enabled := how */
+
+private int
+qpt_freeze_qpthreads_enabled(void) ; /* get and freeze qpthreads_enabled */
+
+/*==============================================================================
+ * Thread self knowledge -- even when !qpthreads_enabled there is one thread
+ */
+Inline qpt_thread_t qpt_thread_self(void)
+{
+ return pthread_self() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Thread equality -- returns true iff threads are *equal*
+ * -- even when !qpthreads_enabled there is one thread
+ */
+Inline bool qpt_threads_equal(qpt_thread_t a_id, qpt_thread_t b_id)
+{
+ return pthread_equal(a_id, b_id) != 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Thread identity -- returns true iff current thread is the given thread
+ * -- even when !qpthreads_enabled there is one thread
+ */
+Inline bool qpt_thread_is_self(qpt_thread_t id)
+{
+ pthread_t self = pthread_self() ;
+ return pthread_equal(self, id) != 0 ;
+} ;
+
+/*==============================================================================
+ * Mutex handling.
+ *
+ * Quagga's default mutex type is:
+ *
+ * * PTHREAD_MUTEX_ERRORCHECK unless NDEBUG && NDEBUG_QPTHREADS
+ * * QPT_MUTEX_TYPE_DEFAULT
+ *
+ * QPT_MUTEX_TYPE_DEFAULT may be set elsewhere. If it is not set then it is
+ * set here to be PTHREAD_MUTEX_NORMAL.
+ *
+ * NB: on the face of it PTHREAD_MUTEX_NORMAL should be the fastest. It is
+ * possible that PTHREAD_MUTEX_DEFAULT may have system specific semantics
+ * that make it faster than the standard _NORMAL. It is also possible that
+ * a given system may elect to provide a safer mutex than the _NORMAL by
+ * default.
+ *
+ * If _DEFAULT is faster than _NORMAL, then QPT_MUTEX_TYPE_DEFAULT may be
+ * used to override this choice.
+ *
+ * NB: if NOT qpthreads_enabled, all mutex actions are EMPTY. This allows
+ * code to be made thread-safe for when pthreads is running, but to work
+ * perfectly well without pthreads.
+ *
+ * NB: do not (currently) support pthread_mutex_timedlock().
+ */
+
+enum qpt_mutex_options
+{
+ qpt_mutex_quagga = 0x0000, /* Quagga's default */
+ qpt_mutex_normal = 0x0001,
+ qpt_mutex_recursive = 0x0002,
+ qpt_mutex_errorcheck = 0x0003,
+ qpt_mutex_default = 0x0004, /* system default */
+} ;
+
+#ifndef QPT_MUTEX_TYPE_DEFAULT
+# define QPT_MUTEX_TYPE_DEFAULT PTHREAD_MUTEX_NORMAL
+#endif
+
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
+#else
+# define QPT_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
+#endif
+
+extern qpt_mutex /* freezes qpthreads_enabled */
+qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts) ;
+
+#define qpt_mutex_init qpt_mutex_init_new
+
+extern qpt_mutex /* do nothing if !qpthreads_enabled */
+qpt_mutex_destroy(qpt_mutex mx, int free_mutex) ;
+
+#define qpt_mutex_destroy_keep(mx) qpt_mutex_destroy(mx, 0)
+#define qpt_mutex_destroy_free(mx) qpt_mutex_destroy(mx, 1)
+
+Inline void
+qpt_mutex_lock(qpt_mutex mx) ; /* do nothing if !qpthreads_enabled */
+
+Inline int
+qpt_mutex_trylock(qpt_mutex mx) ; /* always succeeds if !qpthreads_enabled */
+
+Inline void
+qpt_mutex_unlock(qpt_mutex mx) ; /* do nothing if !qpthreads_enabled */
+
+/*==============================================================================
+ * Condition Variable handling
+ *
+ * Quagga's clock for condition variables is QPT_COND_CLOCK_ID, which
+ * may be set elsewhere. If it is not set then it is set here to be:
+ *
+ * * CLOCK_MONOTONIC if available
+ * * CLOCK_REALTIME otherwise -- this is the standard default.
+ *
+ * QPT_COND_CLOCK_MONOTONIC is set if CLOCK_MONOTONIC is used (and must be set
+ * if QPT_COND_CLOCK_ID is set elsewhere to something that is monotonic).
+ *
+ * NB: the time-out time passed to qpt_cond_timedwait() is a qtime_mono_t
+ * time (so based on qtime's monotonic time, which is CLOCK_MONOTONIC if
+ * that is available.
+ *
+ * Otherwise, there is a conversion step from qtime_mono_t to whatever the
+ * timebase for the condition variable is.
+ *
+ * NB: static initialisation of condition variables is not supported, to avoid
+ * confusion between the standard default and Quagga's default.
+
+ * NB: if NOT qpthreads_enabled, all condition actions are EMPTY. This allows
+ * code to be made thread-safe for when pthreads is running, but to work
+ * perfectly well without pthreads.
+ */
+
+#ifndef QPT_COND_CLOCK_ID
+# ifdef HAVE_CLOCK_MONOTONIC
+# define QPT_COND_CLOCK_ID CLOCK_MONOTONIC
+# define QPT_COND_CLOCK_MONOTONIC 1
+# else
+# define QPT_COND_CLOCK_ID CLOCK_REALTIME
+# undef QPT_COND_CLOCK_MONOTONIC
+# endif
+#endif
+
+enum qpt_cond_options
+{
+ qpt_cond_quagga = 0x0000, /* Quagga's default */
+} ;
+
+extern qpt_cond /* freezes qpthreads_enabled */
+qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts) ;
+
+extern qpt_cond /* do nothing if !qpthreads_enabled */
+qpt_cond_destroy(qpt_cond cv, int free_cond) ;
+
+#define qpt_cond_destroy_keep(cv) qpt_cond_destroy(cv, 0)
+#define qpt_cond_destroy_free(cv) qpt_cond_destroy(cv, 1)
+
+Inline void /* do nothing if !qpthreads_enabled */
+qpt_cond_wait(qpt_cond cv, qpt_mutex mx) ;
+
+extern int /* returns !qpthreads_enabled */
+qpt_cond_timedwait(qpt_cond cv, qpt_mutex mx, qtime_mono_t timeout_time) ;
+
+Inline void /* do nothing if !qpthreads_enabled */
+qpt_cond_signal(qpt_cond cv) ;
+
+Inline void /* do nothing if !qpthreads_enabled */
+qpt_cond_broadcast(qpt_cond cv) ;
+
+/*==============================================================================
+ * Mutex inline functions
+ */
+
+/* Lock given mutex -- or do nothing if !qpthreads_enabled.
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+
+Inline void
+qpt_mutex_lock(qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_mutex_lock(mx) ;
+#else
+ int err = pthread_mutex_lock(mx) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_lock failed", err) ;
+#endif
+ } ;
+} ;
+
+/* Try to lock given mutex -- every time a winner if !qpthreads_enabled.
+ *
+ * Returns: lock succeeded (1 => have locked, 0 => unable to lock).
+ *
+ * Has to check the return value, so zabort_errno if not EBUSY.
+ */
+
+Inline int
+qpt_mutex_trylock(qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+ int err = pthread_mutex_trylock(mx) ;
+ if (err == 0)
+ return 1 ; /* success: it's locked. */
+ if (err == EBUSY)
+ return 0 ; /* unable to lock */
+
+ zabort_err("pthread_mutex_trylock failed", err) ;
+ }
+ else
+ return 1 ;
+} ;
+
+/* Unlock given mutex -- or do nothing if !qpthreads_enabled.
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_mutex_unlock(qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_mutex_unlock(mx) ;
+#else
+ int err = pthread_mutex_unlock(mx) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_unlock failed", err) ;
+#endif
+ } ;
+} ;
+
+/*==============================================================================
+ * Condition variable inline functions
+ */
+
+/* Wait for given condition variable -- do nothing if !qpthreads_enabled
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_cond_wait(qpt_cond cv, qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_cond_wait(cv, mx) ;
+#else
+ int err = pthread_cond_wait(cv, mx) ;
+ if (err != 0)
+ zabort_err("pthread_cond_wait failed", err) ;
+#endif
+ } ;
+} ;
+
+/* Signal given condition -- do nothing if !qpthreads_enabled
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_cond_signal(qpt_cond cv)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_cond_signal(cv) ;
+#else
+ int err = pthread_cond_signal(cv) ;
+ if (err != 0)
+ zabort_err("pthread_cond_signal failed", err) ;
+#endif
+ } ;
+} ;
+
+/* Broadcast given condition -- do nothing if !qpthreads_enabled
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_cond_broadcast(qpt_cond cv)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_cond_broadcast(cv) ;
+#else
+ int err = pthread_cond_broadcast(cv) ;
+ if (err != 0)
+ zabort_err("pthread_cond_broadcast failed", err) ;
+#endif
+ } ;
+} ;
+
+/*==============================================================================
+ * Signal Handling.
+ */
+void /* FATAL error if !qpthreads_enabled */
+qpt_thread_sigmask(int how, const sigset_t* set, sigset_t* oset) ;
+
+void /* FATAL error if !qpthreads_enabled */
+qpt_thread_signal(qpt_thread_t thread, int signum) ;
+
+#endif /* _ZEBRA_QPTHREADS_H */
diff --git a/lib/qstring.c b/lib/qstring.c
new file mode 100644
index 00000000..5ca4d868
--- /dev/null
+++ b/lib/qstring.c
@@ -0,0 +1,511 @@
+/* Some string handling
+ * Copyright (C) 2010 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 "qstring.h"
+
+#include "memory.h"
+#include "zassert.h"
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise qstring -- allocate if required.
+ *
+ * If non-zero len is given, a body is allocated (for at least len + 1).
+ *
+ * Returns: address of qstring
+ *
+ * NB: assumes initialising a new structure. If not, then caller should
+ * use qs_reset() or qs_clear().
+ */
+extern qstring
+qs_init_new(qstring qs, size_t len)
+{
+ if (qs == NULL)
+ qs = qs_new() ;
+ else
+ memset(qs, 0, sizeof(qstring_t)) ; /* see qs_new() */
+
+ if (len != 0)
+ return qs_make_to_length(qs, len) ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate or reallocate so that string is big enough for the given length.
+ *
+ * Allocate qstring if required. Returns with a body with size > 0.
+ *
+ * Allocates to 16 byte boundaries with space for '\0' beyond given length.
+ *
+ * Returns: address of qstring
+ *
+ * NB: allocates new body if the size == 0.
+ *
+ * If the qstring is a "dummy", its contents are now copied to the new
+ * real qstring body -- up to a maximum of the new length.
+ */
+extern qstring
+qs_make_to_length(qstring qs, size_t len)
+{
+ size_t size = (len + 0x10) & ~(size_t)(0x10 - 1) ;
+
+ if (qs == NULL)
+ qs = qs_new() ;
+
+ if (size > qs->size)
+ {
+ if (qs->size == 0)
+ {
+ void* old ;
+ old = qs->body ;
+
+ qs->size = size ;
+ qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ;
+
+ if ((qs->len != 0) && (old != NULL))
+ memcpy(qs->body, old, (qs->len <= len) ? qs->len : len) ;
+ }
+ else
+ {
+ qs->size *= 2 ;
+ if (qs->size < size)
+ qs->size = size ;
+ qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ;
+ } ;
+ };
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add 'n' to the current string length, allocating or extending the body as
+ * required.
+ *
+ * Allocate qstring if required. Returns with a body with size > 0.
+ *
+ * Allocates to 16 byte boundaries with space for '\0' beyond new length.
+ *
+ * Returns: address of qstring
+ *
+ * also: sets char** p_ep to point at the *end* of the new len.
+ *
+ * NB: allocates new body if the size == 0.
+ *
+ * If the qstring is a "dummy", its contents are now copied to the new
+ * real qstring body -- up to a maximum of the new length.
+ */
+extern qstring
+qs_add_len(qstring qs, size_t n, char** p_ep)
+{
+ size_t len ;
+ len = (qs != NULL) ? qs->len + n : n ;
+
+ qs = qs_make_to_length(qs, len) ;
+
+ qs->len = len ;
+
+ *p_ep = (char*)qs->body + len ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free body of qstring -- zeroise size, len and cp
+ *
+ * Does nothing if qstring is NULL
+ *
+ * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
+ * the old body.
+ */
+extern void
+qs_free_body(qstring qs)
+{
+ if (qs != NULL)
+ {
+ if (qs->size != 0)
+ XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+
+ qs->size = 0 ;
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset qstring -- free body and, if required, free the structure.
+ *
+ * If not freeing the structure, zeroise size, len and cp -- qs_free_body()
+ *
+ * Returns: NULL if freed the structure
+ * address of structure (if any), otherwise
+ *
+ * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
+ * the old body.
+ */
+extern qstring
+qs_reset(qstring qs, int free_structure)
+{
+ if (qs != NULL)
+ {
+ if (qs->size != 0)
+ XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+
+ if (free_structure)
+ XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
+ else
+ {
+ qs->size = 0 ;
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ } ;
+ } ;
+ return qs ;
+} ;
+
+/*==============================================================================
+ * printf() and vprintf() type functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf printf()
+ *
+ * Allocate qstring if required.
+ *
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is)
+ */
+extern qstring
+qs_printf(qstring qs, const char* format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ qs = qs_vprintf(qs, format, args);
+ va_end (args);
+
+ return qs;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf vprintf()
+ *
+ * Allocate qstring if required.
+ *
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is)
+ */
+extern qstring
+qs_vprintf(qstring qs, const char *format, va_list args)
+{
+ va_list ac ;
+ int len ;
+ qstring qqs ;
+
+ qqs = qs ;
+ if (qs == NULL)
+ qs = qs_new() ;
+
+ while (1)
+ {
+ /* Note that vsnprintf() returns the length of what it would like to have
+ * produced, if it had the space. That length does not include the
+ * trailing '\0'.
+ *
+ * Also note that given a zero length the string address may be NULL, and
+ * the result is still the length required.
+ */
+ va_copy(ac, args);
+ qs->len = len = vsnprintf (qs->body, qs->size, format, ac) ;
+ va_end(ac);
+
+ if (len < 0)
+ break ;
+
+ if (len < (int)qs->size)
+ return qs ;
+
+ qs_make_to_length(qs, len) ;
+ } ;
+
+ if (qqs == NULL)
+ qs_reset_free(qs) ; /* discard what was allocated */
+ else
+ qs->len = 0 ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Other operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be copy of the given string.
+ *
+ * Allocates a qstring, if required.
+ *
+ * Sets qs->len to the length of the string (excluding trailing '\0')
+ *
+ * NB: if src == NULL, sets qstring to be zero length string.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring
+qs_set(qstring qs, const char* src)
+{
+ qs = qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ;
+ if (qs->len != 0)
+ memcpy(qs->body, src, qs->len + 1) ;
+ else
+ *((char*)qs->body) = '\0' ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be leading 'n' bytes of given string.
+ *
+ * Allocates qstring if required.
+ *
+ * Inserts '\0' terminator after the 'n' bytes copied.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: src string MUST be at least 'n' bytes long.
+ *
+ * NB: src may not be NULL unless n == 0.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring
+qs_set_n(qstring qs, const char* src, size_t n)
+{
+ qs = qs_set_len(qs, n) ; /* ensures have body > n */
+ if (n != 0)
+ memcpy(qs->body, src, n) ;
+
+ *((char*)qs->body + n) = '\0' ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append given string to a qstring.
+ *
+ * Allocates a qstring, if required.
+ *
+ * Sets qs->len to the length of the result (excluding trailing '\0')
+ *
+ * NB: if src == NULL, appends nothing -- but result will be '\0' terminated.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring qs_append(qstring qs, const char* src)
+{
+ size_t n ;
+ char* ep ;
+
+ n = (src != NULL) ? strlen(src) : 0 ;
+
+ qs = qs_add_len(qs, n, &ep) ;
+ ep = (char*)qs->body + qs->len ;
+
+ if (n != 0)
+ memcpy(ep - n, src, n + 1) ;
+ else
+ *ep = '\0' ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be leading 'n' bytes of given string.
+ *
+ * Allocates qstring if required.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: src string MUST be at least 'n' bytes long.
+ *
+ * NB: src may not be NULL unless n == 0.
+ *
+ * NB: if n == 0, appends nothing -- but result will be '\0' terminated.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring
+qs_append_n(qstring qs, const char* src, size_t n)
+{
+ char* ep ;
+
+ qs = qs_add_len(qs, n, &ep) ;
+
+ if (n != 0)
+ memcpy(ep - n, src, n) ;
+
+ *ep = '\0' ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy one qstring to another
+ *
+ * If both are NULL, returns NULL.
+ *
+ * Otherwise if dst is NULL, creates a new qstring.
+ *
+ * Sets dst: body = copy of src->len bytes of src->body -- '\0' terminated.
+ * cp = src->cp
+ * len = src->len
+ *
+ * Where a NULL src has zero cp and len.
+ *
+ * If not NULL, the destination is guaranteed to have a body, and that will be
+ * '\0' terminated.
+ *
+ * Returns: the destination qstring
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring
+qs_copy(qstring dst, qstring src)
+{
+ size_t n ;
+
+ if (src == NULL)
+ {
+ if (dst == NULL)
+ return dst ;
+
+ n = 0 ;
+ dst->cp = 0 ;
+ }
+ else
+ {
+ if (dst == NULL)
+ dst = qs_new() ;
+
+ n = src->len ;
+ dst->cp = src->cp ;
+ } ;
+
+ qs_set_len(dst, n) ;
+
+ if (n > 0)
+ memcpy(dst->body, src->body, n) ;
+
+ *((char*)dst->body + n) = '\0' ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare significant parts of two qstrings.
+ *
+ * By significant, mean excluding leading/trailing isspace() and treating
+ * multiple isspace() as single isspace().
+ *
+ * Compares the 'len' portions of the strings.
+ *
+ * If either is NULL, it is deemed to be an empty string.
+ *
+ * Returns: -1 => a < b
+ * 0 => a == b
+ * +1 => a > b
+ */
+extern int
+qs_cmp_sig(qstring a, qstring b)
+{
+ const unsigned char* p_a ;
+ const unsigned char* e_a ;
+ const unsigned char* p_b ;
+ const unsigned char* e_b ;
+
+ /* Set up pointers and dispense with leading and trailing isspace()
+ *
+ * Dummy up if NULL
+ */
+ if (a != NULL)
+ {
+ p_a = a->body ;
+ e_a = p_a + a->len ;
+
+ while ((p_a < e_a) && isspace(*p_a))
+ ++p_a ;
+ while ((p_a < e_a) && isspace(*(e_a - 1)))
+ --e_a ;
+ }
+ else
+ {
+ p_a = NULL ;
+ e_a = NULL ;
+ }
+
+ if (b != NULL)
+ {
+ p_b = b->body ;
+ e_b = p_b + b->len ;
+
+ while ((p_b < e_b) && isspace(*p_b))
+ ++p_b ;
+ while ((p_b < e_b) && isspace(*(e_b - 1)))
+ --e_b ;
+ }
+ else
+ {
+ p_b = NULL ;
+ e_b = NULL ;
+ } ;
+
+ /* Now set about finding the first difference */
+ while ((p_a != e_a) && (p_b != e_b))
+ {
+ if (isspace(*p_a) && isspace(*p_b))
+ {
+ do { ++p_a ; } while (isspace(*p_a)) ;
+ do { ++p_b ; } while (isspace(*p_b)) ;
+ } ;
+
+ if (*p_a != *p_b)
+ return (*p_a < *p_b) ? -1 : +1 ;
+
+ ++p_a ;
+ ++p_b ;
+ } ;
+
+ /* No difference before ran out of one or both */
+ if (p_a != e_a)
+ return +1 ;
+ else if (p_b != e_b)
+ return -1 ;
+ else
+ return 0 ;
+} ;
diff --git a/lib/qstring.h b/lib/qstring.h
new file mode 100644
index 00000000..0597eda8
--- /dev/null
+++ b/lib/qstring.h
@@ -0,0 +1,468 @@
+/* Some string 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 _ZEBRA_QSTRING_H
+#define _ZEBRA_QSTRING_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "memory.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/* GCC have printf type attribute check. */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/*==============================================================================
+ * These "qstrings" address address the lack of a flexible length string in 'C'.
+ *
+ * This is not a general purpose strings module, but provides a limited number
+ * of useful string operations such that the caller does not need to worry
+ * about the length of the string, and allocating space and so on.
+ *
+ * The caller does, however, have to explicitly release the contents of a
+ * qstring when it is done with.
+ */
+
+typedef struct qstring qstring_t ;
+typedef struct qstring* qstring ;
+
+struct qstring
+{
+ union
+ {
+ void* body ;
+ const void* const_body ;
+ char* char_body ;
+ unsigned char* uchar_body ;
+ } ;
+ size_t size ;
+
+ size_t len ;
+ size_t cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Access functions for body of qstring -- to take care of casting pointers
+ *
+ * NB: if the body has not yet been allocated, these functions will return
+ * NULL or NULL + the offset.
+ */
+Inline char* /* pointer to body of qstring */
+qs_chars(qstring qs)
+{
+ return (char*)qs->body ;
+} ;
+
+Inline unsigned char* /* pointer to body of qstring */
+qs_bytes(qstring qs)
+{
+ return (unsigned char*)qs->body ;
+} ;
+
+Inline char* /* pointer to given offset in qstring */
+qs_chars_at(qstring qs, size_t off)
+{
+ return qs_chars(qs) + off ;
+} ;
+
+Inline unsigned char* /* pointer to given offset in qstring */
+qs_bytes_at(qstring qs, size_t off)
+{
+ return qs_bytes(qs) + off ;
+} ;
+
+Inline char* /* pointer to 'cp' offset in qstring */
+qs_cp_char(qstring qs)
+{
+ return qs_chars_at(qs, qs->cp) ;
+} ;
+
+Inline unsigned char* /* pointer to 'cp' offset in qstring */
+qs_cp_byte(qstring qs)
+{
+ return qs_bytes_at(qs, qs->cp) ;
+} ;
+
+Inline char* /* pointer to 'len' offset in qstring */
+qs_ep_char(qstring qs)
+{
+ return qs_chars_at(qs, qs->len) ;
+} ;
+
+Inline unsigned char* /* pointer to 'len' offset in qstring */
+qs_ep_byte(qstring qs)
+{
+ return qs_bytes_at(qs, qs->len) ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qstring qs_init_new(qstring qs, size_t len) ;
+extern qstring qs_make_to_length(qstring qs, size_t len) ;
+extern void qs_free_body(qstring qs) ;
+extern qstring qs_reset(qstring qs, int free_structure) ;
+
+#define qs_reset_keep(qs) qs_reset(qs, 0)
+#define qs_reset_free(qs) qs_reset(qs, 1)
+
+Inline qstring qs_new(void) ;
+Inline qstring qs_dummy(qstring qs, const char* src, int pos) ;
+
+extern qstring qs_printf(qstring qs, const char* format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ;
+
+extern qstring qs_set(qstring qs, const char* src) ;
+extern qstring qs_set_n(qstring qs, const char* src, size_t n) ;
+
+extern qstring qs_append(qstring qs, const char* src) ;
+extern qstring qs_append_n(qstring qs, const char* src, size_t n) ;
+
+Inline qstring qs_need(qstring qs, size_t len) ;
+Inline qstring qs_set_len(qstring qs, size_t len) ;
+extern qstring qs_add_len(qstring qs, size_t n, char** p_ep) ;
+Inline void qs_clear(qstring qs) ;
+Inline size_t qs_len(qstring qs) ;
+Inline size_t qs_size(qstring qs) ;
+Inline void* qs_term(qstring qs) ;
+
+Inline size_t qs_insert(qstring qs, const void* src, size_t n) ;
+Inline void qs_replace(qstring qs, const void* src, size_t n) ;
+Inline size_t qs_delete(qstring qs, size_t n) ;
+
+extern qstring qs_copy(qstring dst, qstring src) ;
+extern int qs_cmp_sig(qstring a, qstring b) ;
+
+/*==============================================================================
+ * The Inline functions.
+ */
+
+/*------------------------------------------------------------------------------
+ * Make a brand new, completely empty qstring
+ */
+Inline qstring
+qs_new(void)
+{
+ /* Zeroising has set:
+ *
+ * body = NULL -- no body
+ * size = 0 -- no body
+ *
+ * len = 0
+ * cp = 0
+ *
+ * Nothing more to do unless initial size != 0
+ */
+ return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct a "dummy" qstring from the given string.
+ *
+ * Allocates a qstring if required.
+ *
+ * This sets: body = the src
+ * len = strlen(src) (0 if src is NULL)
+ * cp = 0 if 'pos' is zero
+ * len otherwise
+ * size = 0
+ *
+ * The zero size means that the qstring handling will not attempt to free
+ * the body, nor will it write to it... Operations which require the qstring
+ * to have a size will allocate a new body, and discard this one.
+ *
+ * Returns: the address of the dummy qstring.
+ */
+Inline qstring
+qs_dummy(qstring qs, const char* src, int pos)
+{
+ if (qs == NULL)
+ qs = qs_new() ;
+
+ qs->const_body = src ;
+ qs->len = (src != NULL) ? strlen(src) : 0 ;
+ qs->cp = (pos == 0) ? 0 : qs->len ;
+ qs->size = 0 ;
+
+ return qs ;
+}
+
+/*------------------------------------------------------------------------------
+ * Need space for a string of 'len' characters (plus possible '\0').
+ *
+ * Allocates the qstring, if required.
+ *
+ * Returns: address of qstring
+ *
+ * NB: asking for 0 bytes will cause a body to be allocated, ready for any
+ * '\0' !
+ *
+ * NB: has no effect on 'cp' or 'len'. (Will be zero if new qstring allocated.)
+ */
+Inline qstring
+qs_need(qstring qs, size_t len)
+{
+ if ((qs == NULL) || (len >= qs->size))
+ return qs_make_to_length(qs, len) ;
+
+ assert(qs->body != NULL) ;
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set 'len' -- allocate or extend body as required.
+ *
+ * Allocates the qstring, if required.
+ *
+ * Returns: address of qstring
+ *
+ * NB: setting len == 0 bytes will cause a body to be allocated, ready for any
+ * '\0' !
+ *
+ * NB: has no effect on 'cp' -- even if 'cp' > 'len'.
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ */
+Inline qstring
+qs_set_len(qstring qs, size_t len)
+{
+ qs = qs_need(qs, len) ;
+ qs->len = len ;
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset contents of qstring.
+ *
+ * Does nothing if qstring is NULL
+ *
+ * Sets 'cp' = 'len' = 0. Sets first byte of body (if any) to NULL.
+ *
+ * For "dummy" qstring, discards the body.
+ */
+Inline void
+qs_clear(qstring qs)
+{
+ if (qs != NULL)
+ {
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ if (qs->size > 0)
+ *((char*)qs->body) = '\0' ;
+ else
+ qs->body = NULL ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get length of qstring -- by doing strlen() -- and record it in qs->len.
+ *
+ * Returns: the string length
+ *
+ * NB: if no body has been allocated, length = 0
+ */
+Inline size_t
+qs_len(qstring qs)
+{
+ return (qs != NULL) ? (qs->len = (qs->body != NULL) ? strlen(qs_chars(qs))
+ : 0)
+ : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get size of qstring body.
+ *
+ * NB: if no body has been allocated, size == 0
+ * if qstring is NULL, size == 0
+ *
+ * NB: if this is a "dummy" qstring, size == 0.
+ */
+Inline size_t
+qs_size(qstring qs)
+{
+ return (qs != NULL) ? qs->size : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get address of current end of qstring body -- ie byte at 'len'.
+ *
+ * NB: allocates body if required.
+ *
+ * There will be space for '\0' after 'len', so the address returned
+ * is within the real body of the string.
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ *
+ * NB: address of qstring may NOT be NULL.
+ */
+Inline void*
+qs_end(qstring qs)
+{
+ if (qs->len >= qs->size)
+ qs_make_to_length(qs, qs->len) ; /* allows for trailing '\0' */
+
+ return (char*)qs->body + qs->len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set '\0' at qs->len -- allocate or extend body as required.
+ *
+ * Returns address of body -- NULL if the qstring is NULL
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ */
+Inline void*
+qs_term(qstring qs)
+{
+ size_t len ;
+
+ if (qs == NULL)
+ return NULL ;
+
+ if ((len = qs->len) >= qs->size)
+ qs_make_to_length(qs, len) ;
+
+ *qs_chars_at(qs, len) = '\0' ;
+
+ return qs->body ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' bytes at 'cp' -- moves anything cp..len up.
+ *
+ * Increases 'len'. but does not affect 'cp'.
+ *
+ * Returns: number of bytes beyond 'cp' that were moved before insert.
+ *
+ * NB: qstring MUST NOT be NULL
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ */
+Inline size_t
+qs_insert(qstring qs, const void* src, size_t n)
+{
+ size_t after ;
+ char* p ;
+
+ if (qs->len < qs->cp)
+ qs->len = qs->cp ;
+ after = qs->len - qs->cp ;
+
+ qs_set_len(qs, qs->len + n) ; /* set len and ensure have space */
+
+ p = qs_cp_char(qs) ;
+ if (after > 0)
+ memmove (p + n, p, after) ;
+
+ if (n > 0)
+ memmove(p, src, n) ;
+
+ return after ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Replace 'n' bytes at 'cp' -- extending if required.
+ *
+ * May increase 'len'. but does not affect 'cp'.
+ *
+ * NB: qstring MUST NOT be NULL
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ */
+Inline void
+qs_replace(qstring qs, const void* src, size_t n)
+{
+ if ((qs->len < qs->cp + n) || (qs->size == 0))
+ qs_set_len(qs, qs->cp + n) ; /* set len and ensure have space */
+
+ if (n > 0)
+ memmove(qs_cp_char(qs), src, n) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove 'n' bytes at 'cp' -- extending if required.
+ *
+ * May change 'len'. but does not affect 'cp'.
+ *
+ * Returns: number of bytes beyond 'cp' that were moved before insert.
+ *
+ * NB: qstring MUST NOT be NULL
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ */
+Inline size_t
+qs_delete(qstring qs, size_t n)
+{
+ size_t after ;
+ char* p ;
+
+ /* Watch out for "dummy" */
+ if (qs->size == 0)
+ qs_make_to_length(qs, qs->len) ;
+
+ /* If deleting up to or beyond len, then simply set len == cp */
+ if ((qs->cp + n) >= qs->len)
+ {
+ qs_set_len(qs, qs->cp) ; /* set len, looks after cp > len */
+ return 0 ; /* nothing after */
+ }
+
+ /* There is at least one byte after cp (so body must exist) */
+ after = qs->len - (qs->cp + n) ;
+
+ if (n > 0)
+ {
+ p = qs_cp_char(qs) ;
+ memmove (p, p + n, after) ;
+
+ qs->len -= n ;
+ } ;
+
+ return after ;
+} ;
+
+
+#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtime.c b/lib/qtime.c
new file mode 100644
index 00000000..42b903da
--- /dev/null
+++ b/lib/qtime.c
@@ -0,0 +1,205 @@
+/* Quagga realtime and monotonic clock 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 <sys/times.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qtime.h"
+
+/*==============================================================================
+ * This is a collection of functions and (in qtime.h) macros and inline
+ * functions which support system time and a monotonic clock.
+ *
+ * TODO: introduce mutex for crafted monotonic time, and initialisation
+ * routine for that: which can preset the various variables... but
+ * unless is guaranteed to be called, must leave the on-the-fly
+ * initialisation... could also start a watchdog at that point.
+ */
+
+/*==============================================================================
+ * Replacement for CLOCK_MONOTONIC.
+ *
+ * With thanks to Joakim Tjernlund for reminding everyone of the return value
+ * from times() !
+ *
+ * times() is defined to return a value which is the time since some fixed time
+ * before the application started (or when the application started). This time
+ * is measured in units of sysconf(_SC_CLK_TCK) ticks per second.
+ *
+ * The only tricky bit is that the value returned (of type clock_t) is a
+ * signed integer, which can overflow. It is not defined exactly how it
+ * does this... This code assumes that the system will wrap around in some
+ * obvious way. The base of the time for this clock may be when the *system*
+ * started... so when it overflows may depend on how long the system has been
+ * up... which suggests that some sensible wrap around is likely (?).
+ *
+ * The qtime_t value is in nano-seconds.
+ *
+ * The result from times() is in units of sysconf(_SC_CLK_TCK) ticks per second.
+ *
+ * If clock_t is a signed 32-bit integer, which is kept +ve, then the clock
+ * overflows/wraps round in 2^31 ticks which is:
+ *
+ * at 100 ticks/sec: > 248 days
+ * at 1,000 ticks/sec: > 24 days
+ * at 10,000 ticks/sec: > 59 hours
+ *
+ * For safety, this asserts that sysconf(_SC_CLK_TCK) <= 1,000,000 for
+ * sizeof(clock_t) > 4, but <= 1,000 for sizeof(clock_t) == 4.
+ *
+ * (It appears that 60, 100, 250 and 1,000 ticks/sec. are popular options.)
+ *
+ * If sizeof(clock_t) > 4, it is assumed large enough never to wrap around.
+ *
+ * When clock_t is a 32-bit integer must be at least ready for wrap around.
+ * There are two cases:
+ *
+ * * +ve wrap around. new < old value, and new >= 0
+ *
+ * step = (INT32_MAX - old + 1) + new
+ *
+ * * -ve wrap around. new < old value, and new < 0 (and old > 0)
+ *
+ * step = (INT32_MAX - old + 1) - (INT32_MIN - new)
+ *
+ * In any event, a step > 24 hours is taken to means that something has gone
+ * very, very badly wrong.
+ *
+ * NB: it is assumed that qt_craft_monotonic will be called often enough to
+ * ensure that the check on the step size will not be triggered !
+ *
+ * NB: it is assumed that times() does not simply stick if it overflows.
+ *
+ * TODO: Add a watchdog to monitor the behaviour of this clock ?
+ */
+
+CONFIRM((sizeof(clock_t) >= 4) && (sizeof(clock_t) <= 8)) ;
+
+#ifdef GNU_LINUX
+#define TIMES_TAKES_NULL 1
+#else
+#undef TIMES_TAKES_NULL
+#endif
+
+static uint64_t monotonic = 0 ; /* monotonic clock in _SC_CLK_TCK's */
+static int64_t last_times_sample = 0 ; /* last value returned by times() */
+
+static uint64_t step_limit = 0 ; /* for sanity check */
+
+static int64_t times_clk_tcks = 0 ; /* sysconf(_SC_CLK_TCK) */
+static qtime_t times_scale_q = 0 ; /* 10**9 / times_clk_tcks */
+static qtime_t times_scale_r = 0 ; /* 10**9 % times_clk_tcks */
+
+qtime_mono_t
+qt_craft_monotonic(void) {
+ struct tms dummy ;
+ int64_t this_times_sample ;
+ uint64_t step ;
+
+ /* Set up times_scale_q & times_scale_q if not yet done. */
+ if (times_clk_tcks == 0) /* Is zero until it's initialized */
+ {
+ lldiv_t qr ;
+ confirm(sizeof(qtime_t) <= sizeof(long long int)) ;
+
+ times_clk_tcks = sysconf(_SC_CLK_TCK) ;
+ passert((times_clk_tcks > 0) &&
+ (times_clk_tcks <= (sizeof(clock_t) > 4) ? 1000000
+ : 1000)) ;
+
+ qr = lldiv(QTIME_SECOND, times_clk_tcks) ;
+ times_scale_q = qr.quot ;
+ times_scale_r = qr.rem ;
+
+ step_limit = (uint64_t)24 * 60 * 60 * times_clk_tcks ;
+ } ;
+
+ /* No errors are defined for times(), but a return of -1 is defined */
+ /* to indicate an error condition, with errno saying what it is ! */
+ /* */
+ /* The following deals carefully with this -- cannot afford for the */
+ /* clock either to jump or to get stuck ! */
+
+#ifdef TIMES_TAKES_NULL
+ this_times_sample = times(NULL) ; /* assume this saves effort ! */
+#else
+ this_times_sample = times(&dummy) ;
+#endif
+
+ if (this_times_sample == -1) /* deal with theoretical error */
+ {
+ errno = 0 ;
+ this_times_sample = times(&dummy) ;
+ if (errno != 0)
+ zabort_errno("times() failed") ;
+ } ;
+
+ /* Calculate the step and verify sensible. */
+ /* */
+ /* Watch out for huge jumps and/or time going backwards. */
+ /* For 32-bit clock_t, look out for wrap-around. */
+
+ if ((sizeof(clock_t) > 4) || (this_times_sample > last_times_sample))
+ /* time going backwards will appear as HUGE step forwards. */
+ step = (uint64_t)(this_times_sample - last_times_sample) ;
+ else
+ {
+ if (this_times_sample > 0)
+ /* both samples +ve => +ve wrap around. */
+ step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
+ + this_times_sample ) ;
+ else
+ /* this sample -ve and last sample +ve => -ve wrap round */
+ /* this sample -ve and last sample -ve => time gone backwards */
+ /* (which appears as a HUGE step forwards). */
+ step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
+ - ((int64_t)INT32_MIN - this_times_sample) ) ;
+ } ;
+
+ /* TODO: better error messaging for large clock jumps. */
+ if (step > step_limit)
+ zabort("Sudden large monotonic clock jump") ;
+
+ /* Advance the monotonic clock in sysconf(_SC_CLK_TCK) units. */
+ monotonic += step ;
+
+ /* Remember what we got, for next time. */
+ last_times_sample = this_times_sample ;
+
+ /* Scale to qtime_t units. */
+ if (times_scale_r == 0)
+ return monotonic * times_scale_q ;
+ else
+ return (monotonic * times_scale_q) +
+ ((monotonic * times_scale_r) / times_clk_tcks) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get crafted monotonic time -- in seconds
+ */
+extern time_t
+qt_craft_mono_secs(void)
+{
+ qt_craft_monotonic() ; /* update the monotonic counter */
+
+ return monotonic / times_clk_tcks ;
+} ;
diff --git a/lib/qtime.h b/lib/qtime.h
new file mode 100644
index 00000000..df486dfd
--- /dev/null
+++ b/lib/qtime.h
@@ -0,0 +1,333 @@
+/* Quagga realtime and monotonic clock 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 _ZEBRA_QTIME_H
+#define _ZEBRA_QTIME_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "zassert.h"
+#include "config.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * qtime_t -- signed 64-bit integer.
+ *
+ * The various time functions work in terms of the structures:
+ *
+ * timespec -- tv_secs seconds
+ * tv_nsecs nano-seconds
+ *
+ * timeval -- tv_secs seconds
+ * tv_usecs micro-seconds
+ *
+ * Given a 64-bit integer it is much easier to do operations on a 64 bit
+ * (signed) nano-second value. That gives > 34 bits for the seconds count,
+ * and counts from zero to > 290 years.
+ */
+
+typedef int64_t qtime_t ;
+
+typedef qtime_t qtime_real_t ; /* qtime_t value, realtime time-base */
+typedef qtime_t qtime_mono_t ; /* qtime_t value, monotonic time-base */
+
+typedef qtime_t qtime_tod_t ; /* qtime_t value, timeofday time-base... */
+ /* ...just in case != CLOCK_REALTIME ! */
+
+/* A qtime_t second 123456789 -- nano-seconds */
+#define QTIME_SECOND 1000000000
+#define TIMESPEC_SECOND 1000000000
+#define TIMEVAL_SECOND 1000000
+
+/* Macro to convert time in seconds to a qtime_t */
+/* Note that the time to convert may be a float. */
+#define QTIME(s) ((qtime_t)((s) * (qtime_t)QTIME_SECOND))
+
+Inline qtime_t
+timespec2qtime(struct timespec* p_ts) ;
+
+Inline qtime_t
+timeval2qtime(struct timeval* p_tv) ;
+
+Inline struct timespec*
+qtime2timespec(struct timespec* p_ts, qtime_t qt) ;
+
+Inline struct timeval*
+qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
+
+/*==============================================================================
+ * Clocks.
+ *
+ * Here is support for:
+ *
+ * * System Clock
+ *
+ * This can be read using either clock_gettime(CLOCK_REALTIME, &ts) or
+ * gettimeofday(&tv, NULL) -- which (are believed to) return the same clock,
+ * but in different units.
+ *
+ * * Monotonic Clock
+ *
+ * Using clock_gettime(CLOCK_MONOTONIC, &ts) if it is available, otherwise
+ * a manufactured equivalent using times() -- see qt_craft_monotonic().
+ */
+
+Inline qtime_real_t
+qt_get_realtime(void) ; /* clock_gettime(CLOCK_REALTIME, ...) */
+
+Inline qtime_mono_t
+qt_add_realtime(qtime_t interval) ; /* qt_get_realtime() + interval */
+
+Inline qtime_mono_t
+qt_get_monotonic(void) ; /* clock_gettime(CLOCK_MONOTONIC, ...) */
+ /* OR equivalent using times() */
+Inline qtime_mono_t
+qt_add_monotonic(qtime_t interval) ; /* qt_get_monotonic() + interval */
+Inline time_t qt_get_mono_secs(void) ;
+ /* clock_gettime(CLOCK_MONOTONIC, ...) */
+ /* OR equivalent using times() */
+
+Inline qtime_mono_t /* monotonic time from CLOCK_REALTIME */
+qt_realtime2monotonic(qtime_real_t realtime) ;
+Inline qtime_real_t /* CLOCK_REALTIME from monotonic time */
+qt_monotonic2realtime(qtime_mono_t monotonic) ;
+
+/* Function to manufacture a monotonic clock. */
+extern qtime_mono_t qt_craft_monotonic(void) ;
+extern time_t qt_craft_mono_secs(void) ;
+
+/* These are provided just in case gettimeofday() != CLOCK_REALTIME */
+Inline qtime_tod_t
+qt_get_timeofday(void) ; /* gettimeofday(&tv, NULL) */
+
+Inline qtime_tod_t
+qt_add_timeofday(qtime_t interval) ; /* qt_get_timeofday() + interval */
+
+Inline qtime_mono_t /* monotonic time from timeofday */
+qt_timeofday2monotonic(qtime_tod_t timeofday) ;
+Inline qtime_tod_t /* timeofday from monotonic time */
+qt_monotonic2timeofday(qtime_mono_t monotonic) ;
+
+/*==============================================================================
+ * Inline conversion functions
+ */
+
+/* Convert timespec to qtime_t
+ *
+ * Returns qtime_t value.
+ */
+Inline qtime_t
+timespec2qtime(struct timespec* p_ts)
+{
+ return QTIME(p_ts->tv_sec) + p_ts->tv_nsec ;
+ confirm(QTIME_SECOND == TIMESPEC_SECOND) ;
+} ;
+
+/* Convert timeval to qtime_t
+ *
+ * Returns qtime_t value.
+ */
+Inline qtime_t
+timeval2qtime(struct timeval* p_tv)
+{
+ return QTIME(p_tv->tv_sec) + (p_tv->tv_usec * 1000) ;
+ confirm(QTIME_SECOND == TIMEVAL_SECOND * 1000) ;
+} ;
+
+/* Convert qtime_t to timespec
+ *
+ * Takes address of struct timespec and returns that address.
+ */
+Inline struct timespec*
+qtime2timespec(struct timespec* p_ts, qtime_t qt)
+{
+ lldiv_t imd = lldiv(qt, QTIME_SECOND) ;
+ confirm(sizeof(long long) >= sizeof(qtime_t)) ;
+
+ p_ts->tv_sec = imd.quot ;
+ p_ts->tv_nsec = imd.rem ;
+ confirm(TIMESPEC_SECOND == QTIME_SECOND) ;
+
+ return p_ts ;
+} ;
+
+/* Convert timespec to qtime_t
+ *
+ * Takes address of struct timespec and returns that address.
+ */
+Inline struct timeval*
+qtime2timeval(struct timeval* p_tv, qtime_t qt)
+{
+ lldiv_t imd = lldiv(qt, QTIME_SECOND) ;
+ confirm(sizeof(long long) >= sizeof(qtime_t)) ;
+
+ p_tv->tv_sec = imd.quot ;
+ p_tv->tv_usec = imd.rem / 1000 ;
+ confirm(TIMEVAL_SECOND * 1000 == QTIME_SECOND) ;
+
+ return p_tv ;
+} ;
+
+/*==============================================================================
+ * Inline Clock Functions.
+ */
+
+/* Read given clock & return a qtime_t value.
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+
+Inline qtime_t
+qt_clock_gettime(clockid_t clock_id)
+{
+ struct timespec ts ;
+
+ if (clock_gettime(clock_id, &ts) != 0)
+ zabort_errno("clock_gettime failed") ;
+
+ return timespec2qtime(&ts) ;
+} ;
+
+/* clock_gettime(CLOCK_REALTIME, ...) -- returning qtime_t value
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+Inline qtime_real_t
+qt_get_realtime(void)
+{
+ return qt_clock_gettime(CLOCK_REALTIME) ;
+} ;
+
+/* qt_get_realtime() + interval
+ */
+Inline qtime_real_t
+qt_add_realtime(qtime_t interval)
+{
+ return qt_get_realtime() + interval;
+} ;
+
+/* clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
+ * -- returning qtime_t value
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+Inline qtime_mono_t
+qt_get_monotonic(void)
+{
+#ifdef HAVE_CLOCK_MONOTONIC
+ return qt_clock_gettime(CLOCK_MONOTONIC) ;
+#else
+ return qt_craft_monotonic() ;
+#endif
+} ;
+
+/* qt_get_monotonic() + interval
+ */
+Inline qtime_mono_t
+qt_add_monotonic(qtime_t interval)
+{
+ return qt_get_monotonic() + interval;
+} ;
+
+/* clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
+ * -- returning time_t value
+ *
+ * Value returned is in seconds -- for coarser grain timings.
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+Inline time_t
+qt_get_mono_secs(void)
+{
+#ifdef HAVE_CLOCK_MONOTONIC
+ struct timespec ts ;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ zabort_errno("clock_gettime failed") ;
+
+ return ts.tv_sec ;
+#else
+ return qt_craft_mono_secs() ;
+#endif
+} ;
+
+/* gettimeofday(&tv, NULL) -- returning qtime_t value
+ */
+Inline qtime_tod_t
+qt_get_timeofday(void)
+{
+ struct timeval tv ;
+ gettimeofday(&tv, NULL) ;
+ return timeval2qtime(&tv) ;
+}
+
+/* qt_get_timeofday() + interval
+ */
+Inline qtime_tod_t
+qt_add_timeofday(qtime_t interval)
+{
+ return qt_get_timeofday() + interval;
+} ;
+
+/*==============================================================================
+ * Conversion between realtime/timeofday and monotonic
+ */
+
+/* Convert a CLOCK_REALTIME time to our local monotonic time. */
+Inline qtime_mono_t
+qt_realtime2monotonic(qtime_real_t realtime)
+{
+ return qt_get_monotonic() + (realtime - qt_get_realtime()) ;
+} ;
+
+/* Convert a local monotonic time to CLOCK_REALTIME time. */
+Inline qtime_real_t
+qt_monotonic2realtime(qtime_mono_t monotonic)
+{
+ return qt_get_realtime() + (monotonic - qt_get_monotonic()) ;
+} ;
+
+/* Convert a gettimeofday() time to our local monotonic time. */
+Inline qtime_mono_t
+qt_timeofday2monotonic(qtime_tod_t timeofday)
+{
+ return qt_get_monotonic() + (timeofday - qt_get_timeofday()) ;
+} ;
+
+/* Convert a local monotonic time to gettimeofday() time. */
+Inline qtime_tod_t
+qt_monotonic2timeofday(qtime_mono_t monotonic)
+{
+ return qt_get_timeofday() + (monotonic - qt_get_monotonic()) ;
+} ;
+
+#endif /* _ZEBRA_QTIME_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
new file mode 100644
index 00000000..8c08a6bc
--- /dev/null
+++ b/lib/qtimers.c
@@ -0,0 +1,450 @@
+/* Quagga timers support -- 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 <stddef.h>
+#include <string.h>
+
+#include "zassert.h"
+#include "qtimers.h"
+#include "memory.h"
+#include "heap.h"
+
+enum { qdebug =
+#ifdef QDEBUG
+ 1
+#else
+ 0
+#endif
+};
+
+/*==============================================================================
+ * Quagga Timers -- qtimer_xxxx
+ *
+ * Here and in qtimers.h is a data structure for managing multiple timers
+ * each with an action to be executed when the timer expires.
+ *
+ * The qtime_pile structure manages a "pile" of qtimer structures which are
+ * waiting for the right time to go off.
+ *
+ * NB: it is ASSUMED that a qtime_pile will be private to the thread in which
+ * it is created and used.
+ *
+ * There is NO mutex handling here.
+ *
+ * Timers are triggered by calling qtimer_dispatch_next(). This is given the
+ * current qtimer time (see below), and it dispatches the first timer whose
+ * time has come (or been passed). Dispatching a timer means calling its
+ * action function (see below). Each call of qtimer_dispatch_next() triggers
+ * at most one timer.
+ *
+ * Time Base
+ * ---------
+ *
+ * The time base for qtimers is the monotonic time provided in qtime.c/.h.
+ *
+ * Interval
+ * --------
+ *
+ * There is an optional interval associated with each timer.
+ *
+ * The timer may be set to "now + interval", and the interval is stored with
+ * the timer.
+ *
+ * The timer may be set to its current time + stored interval (to provide a
+ * "steady" clock).
+ *
+ * Action Functions
+ * ----------------
+ *
+ * There is a separate action function for each timer.
+ *
+ * When the action function is called it is passed the qtimer structure, the
+ * timer_info pointer from that structure and the time which triggered the
+ * timer (which may, or may not, be the current qtimer time).
+ *
+ * During an action function timers may be set/unset, actions changed, and so
+ * on... there are no restrictions EXCEPT that may NOT recurse into the
+ * dispatch function.
+ *
+ * If nothing is done with the time during the action function, the timer is
+ * implicitly unset when the action function returns.
+ */
+
+static int
+qtimer_cmp(qtimer* a, qtimer* b) /* the heap discipline */
+{
+ if ((**a).time < (**b).time)
+ return -1 ;
+ if ((**a).time > (**b).time)
+ return +1 ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * qtimer_pile handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a timer pile -- allocating it if required.
+ *
+ * Returns the qtimer_pile.
+ */
+qtimer_pile
+qtimer_pile_init_new(qtimer_pile qtp)
+{
+ if (qtp == NULL)
+ qtp = XCALLOC(MTYPE_QTIMER_PILE, sizeof(struct qtimer_pile)) ;
+ else
+ memset(qtp, 0, sizeof(struct qtimer_pile)) ;
+
+ /* Zeroising has initialised:
+ *
+ * timers -- invalid heap -- need to properly initialise
+ * current = NULL -- no current timer
+ */
+
+ /* (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
+ * about first argument of offsetof().)
+ */
+ typedef struct qtimer qtimer_t ;
+
+ heap_init_new_backlinked(&qtp->timers, 0, (heap_cmp*)qtimer_cmp,
+ offsetof(qtimer_t, backlink)) ;
+ return qtp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the timer time for the first timer due to go off in the given pile.
+ *
+ * The caller must provide a maximum acceptable time. If the qtimer pile is
+ * empty, or the top entry times out after the maximum time, then the maximum
+ * is returned.
+ */
+extern qtime_t
+qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait)
+{
+ qtime_t top_wait ;
+ qtimer qtr = heap_top_item(&qtp->timers) ;
+
+ if (qtr == NULL)
+ return max_wait ;
+
+ top_wait = qtr->time - qt_get_monotonic() ;
+
+ return (top_wait < max_wait) ? top_wait : max_wait ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch the next timer whose time is <= the given "upto" time.
+ *
+ * The upto time must be a qtimer time (!) -- see qtimer_time_now().
+ *
+ * The upto argument allows the caller to get a qtimer_time_now() value, and
+ * then process all timers upto that time.
+ *
+ * Returns true <=> dispatched a timer, and there may be more to do.
+ * false <=> nothing to do (and nothing done).
+ *
+ * NB: it is a sad, very sad, mistake to recurse into this !
+ */
+extern bool
+qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
+{
+ qtimer qtr ;
+
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
+
+ qtr = heap_top_item(&qtp->timers) ;
+
+ if ((qtr == NULL) || (qtr->time > upto))
+ return 0 ;
+
+ passert((qtp == qtr->pile) && (qtr->active)) ;
+
+ qtp->implicit_unset = qtr ; /* Timer must be unset if is still here
+ when the action function returns */
+ qtr->action(qtr, qtr->timer_info, upto) ;
+
+ if (qtp->implicit_unset == qtr)
+ qtimer_unset(qtr) ;
+ else
+ assert(qtp->implicit_unset == NULL) ; /* check for tidy-ness */
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Ream out (another) item from qtimer_pile.
+ *
+ * If pile is empty, release the qtimer_pile structure, if required.
+ *
+ * See: #define qtimer_pile_ream_free(qtp)
+ * #define qtimer_pile_ream_keep(qtp)
+ *
+ * Useful for emptying out and discarding a pile of timers:
+ *
+ * while ((p_qtr = qtimer_pile_ream_free(qtp)))
+ * ... do what's required to release the item p_qtr
+ *
+ * Returns NULL when timer pile is empty (and has been released, if required).
+ *
+ * If the timer pile is not released, it may be reused without reinitialisation.
+ *
+ * NB: once reaming has started, the timer pile MUST NOT be used for anything,
+ * and the process MUST be run to completion.
+ */
+qtimer
+qtimer_pile_ream(qtimer_pile qtp, int free_structure)
+{
+ qtimer qtr ;
+
+ qtr = heap_ream_keep(&qtp->timers) ; /* ream, keeping the heap structure */
+ if (qtr != NULL)
+ qtr->active = false ; /* has been removed from pile */
+ else
+ if (free_structure) /* pile is empty, may now free it */
+ XFREE(MTYPE_QTIMER_PILE, qtp) ;
+
+ return qtr ;
+} ;
+
+/*==============================================================================
+ * qtimer handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise qtimer structure -- allocating one if required.
+ *
+ * Associates qtimer with the given pile of timers, and sets up the action and
+ * the timer_info.
+ *
+ * Once initialised, the timer may be set.
+ *
+ * Returns the qtimer.
+ */
+qtimer
+qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+ qtimer_action* action, void* timer_info)
+{
+ if (qtr == NULL)
+ qtr = XCALLOC(MTYPE_QTIMER, sizeof(struct qtimer)) ;
+ else
+ memset(qtr, 0, sizeof(struct qtimer)) ;
+
+ /* Zeroising has initialised:
+ *
+ * pile -- NULL -- not in any pile (yet)
+ * backlink -- unset
+ *
+ * active -- false
+ *
+ * time -- unset
+ * action -- NULL -- no action set (yet)
+ * timer_info -- NULL -- no timer info set (yet)
+ *
+ * interval -- unset
+ */
+
+ qtr->pile = qtp ;
+ qtr->action = action ;
+ qtr->timer_info = timer_info ;
+
+ return qtr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free given timer -- if any.
+ *
+ * Unsets it first if it is active or pending unset.
+ *
+ * Returns: NULL
+ */
+extern qtimer
+qtimer_free(qtimer qtr)
+{
+ /* Note that if is the current dispatched timer and an unset is still
+ * pending, then it must still be active.
+ */
+ if (qtr != NULL)
+ {
+ if (qtr->active)
+ qtimer_unset(qtr) ;
+
+ XFREE(MTYPE_QTIMER, qtr) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pile in which given timer belongs.
+ *
+ * Does nothing if timer already belongs to the given pile.
+ *
+ * Unsets the timer if active in another pile, before reassigning it.
+ */
+extern void
+qtimer_set_pile(qtimer qtr, qtimer_pile qtp)
+{
+ if (qtr->pile == qtp)
+ return ;
+
+ /* Note that if is the current dispatched timer and an unset is still
+ * pending, then it must still be active.
+ */
+ if (qtr->active)
+ qtimer_unset(qtr) ;
+
+ qtr->pile = qtp ;
+}
+
+/*------------------------------------------------------------------------------
+ * Set given timer.
+ *
+ * Setting a -ve time => qtimer_unset.
+ *
+ * Sets any given action -- if the action given is NULL, retains previously set
+ * action.
+ *
+ * If the timer is already active, sets the new time & updates pile.
+ *
+ * Otherwise, sets the time and adds to pile -- making timer active.
+ *
+ * It is an error to set a timer which has a NULL action.
+ */
+extern void
+qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
+{
+ qtimer_pile qtp ;
+
+ if (when < 0)
+ return qtimer_unset(qtr) ;
+
+ qtp = qtr->pile ;
+ assert(qtp != NULL) ;
+
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
+
+ qtr->time = when ;
+
+ if (qtr->active)
+ {
+ /* Is active, so update the timer in the pile. */
+ heap_update_item(&qtp->timers, qtr) ;
+
+ if (qtr == qtp->implicit_unset)
+ qtp->implicit_unset = NULL ; /* no unset required, now */
+ }
+ else
+ {
+ /* Is not active, so insert the timer into the pile. */
+ heap_push_item(&qtp->timers, qtr) ;
+
+ assert(qtr != qtp->implicit_unset) ; /* because it's not active */
+
+ qtr->active = true ;
+ } ;
+
+ if (action != NULL)
+ qtr->action = action ;
+ else
+ assert(qtr->action != NULL) ;
+
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unset given timer
+ *
+ * If the timer is active, removes from pile and sets inactive.
+ */
+extern void
+qtimer_unset(qtimer qtr)
+{
+ qtimer_pile qtp = qtr->pile ;
+
+ assert(qtp != NULL) ;
+
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
+
+ if (qtr->active)
+ {
+ if (qtr == qtp->implicit_unset)
+ qtp->implicit_unset = NULL ; /* no unset required, now */
+
+ heap_delete_item(&qtp->timers, qtr) ;
+
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
+
+ qtr->active = false ;
+ }
+ else
+ assert(qtr != qtp->implicit_unset) ;
+} ;
+
+/*==============================================================================
+ * Verification code for debug purposes.
+ */
+extern void
+qtimer_pile_verify(qtimer_pile qtp)
+{
+ heap th = &qtp->timers ;
+ vector v ;
+ vector_index i ;
+ vector_index e ;
+ qtimer qtr ;
+ bool seen ;
+
+ assert(qtp != NULL) ;
+
+ /* (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
+ * about first argument of offsetof().)
+ */
+ typedef struct qtimer qtimer_t ;
+
+ assert(th->cmp == (heap_cmp*)qtimer_cmp) ;
+ assert(th->state == Heap_Has_Backlink) ;
+ assert(th->backlink_offset == offsetof(qtimer_t, backlink)) ;
+
+ v = &th->v ;
+ e = vector_end(v) ;
+ for (i = 0 ; i < e ; ++i)
+ {
+ qtr = vector_get_item(v, i) ;
+ assert(qtr != NULL) ;
+
+ if (qtr == qtp->implicit_unset)
+ seen = 1 ;
+
+ assert(qtr->active) ;
+
+ assert(qtr->pile == qtp) ;
+ assert(qtr->backlink == i) ;
+ assert(qtr->action != NULL) ;
+ } ;
+
+ assert(seen || (qtp->implicit_unset == NULL)) ;
+} ;
diff --git a/lib/qtimers.h b/lib/qtimers.h
new file mode 100644
index 00000000..5beb931b
--- /dev/null
+++ b/lib/qtimers.h
@@ -0,0 +1,174 @@
+/* Quagga timers support -- 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 _ZEBRA_QTIMERS_H
+#define _ZEBRA_QTIMERS_H
+
+#include <stdbool.h>
+
+#include "zassert.h"
+#include "qtime.h"
+#include "heap.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga Timers -- qtimer_xxxx
+ *
+ * Here and in qtimers.c is a data structure for managing multiple timers
+ * each with an action to be executed when the timer expires.
+ */
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef struct qtimer* qtimer ;
+typedef struct qtimer_pile* qtimer_pile ;
+
+typedef void (qtimer_action)(qtimer qtr, void* timer_info, qtime_mono_t when) ;
+
+struct qtimer
+{
+ qtimer_pile pile ; /* pile currently allocated to */
+ heap_backlink_t backlink ;
+
+ bool active ; /* true => in the pile */
+
+ qtime_mono_t time ; /* current time to trigger action */
+ qtimer_action* action ;
+ void* timer_info ;
+
+ qtime_t interval ; /* optional timer interval */
+} ;
+
+struct qtimer_pile
+{
+ struct heap timers ;
+
+ qtimer implicit_unset ; /* used during dispatch */
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qtimer_pile qtimer_pile_init_new(qtimer_pile qtp) ;
+extern bool qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto) ;
+extern qtime_t qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait) ;
+extern qtimer qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
+
+/* Ream out qtimer pile and free the qtimer structure. */
+#define qtimer_pile_ream_free(qtp) qtimer_pile_ream(qtp, 1)
+/* Ream out qtimer pile but keep the qtimer structure. */
+#define qtimer_pile_ream_keep(qtp) qtimer_pile_ream(qtp, 0)
+
+extern qtimer qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+ qtimer_action* action, void* timer_info) ;
+extern void qtimer_set_pile(qtimer qtr, qtimer_pile qtp) ;
+Inline void qtimer_set_action(qtimer qtr, qtimer_action* action) ;
+Inline void qtimer_set_info(qtimer qtr, void* timer_info) ;
+
+extern qtimer qtimer_free(qtimer qtr) ;
+extern void qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action) ;
+extern void qtimer_unset(qtimer qtr) ;
+
+Inline void qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action) ;
+Inline qtime_mono_t qtimer_get(qtimer qtr) ;
+Inline void qtimer_set_interval(qtimer qtr, qtime_t interval,
+ qtimer_action* action) ;
+Inline void qtimer_add_interval(qtimer qtr, qtimer_action* action) ;
+
+Inline qtime_t qtimer_get_interval(qtimer qtr) ;
+extern void qtimer_pile_verify(qtimer_pile qtp) ;
+
+/*==============================================================================
+ * Inline functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Set given timer to given time later than *its* current time.
+ */
+Inline void
+qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action)
+{
+ qtimer_set(qtr, qtimer_get(qtr) + interval, action);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the given timer's time.
+ */
+Inline qtime_mono_t
+qtimer_get(qtimer qtr)
+{
+ return qtr->time ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set action for given timer -- setting a NULL action unsets the timer.
+ */
+Inline void
+qtimer_set_action(qtimer qtr, qtimer_action* action)
+{
+ if (action == NULL)
+ qtimer_unset(qtr) ;
+ qtr->action = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set timer_info for given timer.
+ */
+Inline void
+qtimer_set_info(qtimer qtr, void* timer_info)
+{
+ qtr->timer_info = timer_info ;
+} ;
+
+
+/* Interval handling ---------------------------------------------------------*/
+
+/* Set the interval field
+ */
+
+Inline void
+qtimer_set_interval(qtimer qtr, qtime_t interval, qtimer_action* action)
+{
+ qtr->interval = interval ;
+ qtimer_set(qtr, qt_add_monotonic(interval), action) ;
+} ;
+
+Inline void
+qtimer_add_interval(qtimer qtr, qtimer_action* action)
+{
+ qtimer_add(qtr, qtr->interval, action) ;
+} ;
+
+/* Get the current value of the interval field
+ */
+Inline qtime_t
+qtimer_get_interval(qtimer qtr)
+{
+ return qtr->interval ;
+} ;
+
+#endif /* _ZEBRA_QTIMERS_H */
diff --git a/lib/routemap.c b/lib/routemap.c
index 4f4e6d62..b452530d 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -28,7 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "command.h"
#include "vty.h"
#include "log.h"
-
+
/* Vector for route match rules. */
static vector route_match_vec;
@@ -60,7 +60,7 @@ struct route_map_list
void (*add_hook) (const char *);
void (*delete_hook) (const char *);
- void (*event_hook) (route_map_event_t, const char *);
+ void (*event_hook) (route_map_event_t, const char *);
};
/* Master list of route map. */
@@ -72,7 +72,7 @@ route_map_rule_delete (struct route_map_rule_list *,
static void
route_map_index_delete (struct route_map_index *, int);
-
+
/* New route map allocation. Please note route map's name must be
specified. */
static struct route_map *
@@ -94,7 +94,7 @@ route_map_add (const char *name)
map = route_map_new (name);
list = &route_map_master;
-
+
map->next = NULL;
map->prev = list->tail;
if (list->tail)
@@ -117,7 +117,7 @@ route_map_delete (struct route_map *map)
struct route_map_list *list;
struct route_map_index *index;
char *name;
-
+
while ((index = map->head) != NULL)
route_map_index_delete (index, 0);
@@ -207,44 +207,58 @@ vty_show_route_map_entry (struct vty *vty, struct route_map *map)
/* Print the name of the protocol */
if (zlog_default)
- vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol],
+ vty_out (vty, "%s:%s", zlog_get_proto_name(NULL),
VTY_NEWLINE);
for (index = map->head; index; index = index->next)
{
- vty_out (vty, "route-map %s, %s, sequence %d%s",
+ vty_out (vty, "route-map %s, %s, sequence %lu%s",
map->name, route_map_type_str (index->type),
- index->pref, VTY_NEWLINE);
+ (unsigned long)index->seq, VTY_NEWLINE);
+ confirm(sizeof(index->seq) <= sizeof(unsigned long)) ;
/* Description */
if (index->description)
vty_out (vty, " Description:%s %s%s", VTY_NEWLINE,
index->description, VTY_NEWLINE);
-
+
/* Match clauses */
vty_out (vty, " Match clauses:%s", VTY_NEWLINE);
for (rule = index->match_list.head; rule; rule = rule->next)
- vty_out (vty, " %s %s%s",
+ vty_out (vty, " %s %s%s",
rule->cmd->str, rule->rule_str, VTY_NEWLINE);
-
+
vty_out (vty, " Set clauses:%s", VTY_NEWLINE);
for (rule = index->set_list.head; rule; rule = rule->next)
vty_out (vty, " %s %s%s",
rule->cmd->str, rule->rule_str, VTY_NEWLINE);
-
+
/* Call clause */
vty_out (vty, " Call clause:%s", VTY_NEWLINE);
if (index->nextrm)
vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE);
-
+
/* Exit Policy */
vty_out (vty, " Action:%s", VTY_NEWLINE);
- if (index->exitpolicy == RMAP_GOTO)
- vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE);
- else if (index->exitpolicy == RMAP_NEXT)
- vty_out (vty, " Continue to next entry%s", VTY_NEWLINE);
- else if (index->exitpolicy == RMAP_EXIT)
- vty_out (vty, " Exit routemap%s", VTY_NEWLINE);
+ switch (index->exitpolicy)
+ {
+ case RMAP_GOTO:
+ vty_out (vty, " Goto %lu%s", (unsigned long)index->goto_seq,
+ VTY_NEWLINE);
+ confirm(sizeof(index->goto_seq) <= sizeof(unsigned long)) ;
+ break ;
+
+ case RMAP_NEXT:
+ vty_out (vty, " Continue to next entry%s", VTY_NEWLINE);
+ break ;
+
+ case RMAP_EXIT:
+ vty_out (vty, " Exit routemap%s", VTY_NEWLINE);
+ break ;
+
+ default:
+ zabort("invalid route-map 'exitpolicy'") ;
+ } ;
}
}
@@ -329,13 +343,12 @@ route_map_index_delete (struct route_map_index *index, int notify)
/* Lookup index from route map. */
static struct route_map_index *
route_map_index_lookup (struct route_map *map, enum route_map_type type,
- int pref)
+ route_map_seq_t seq)
{
struct route_map_index *index;
for (index = map->head; index; index = index->next)
- if ((index->type == type || type == RMAP_ANY)
- && index->pref == pref)
+ if ((index->type == type || type == RMAP_ANY) && index->seq == seq)
return index;
return NULL;
}
@@ -343,20 +356,20 @@ route_map_index_lookup (struct route_map *map, enum route_map_type type,
/* Add new index to route map. */
static struct route_map_index *
route_map_index_add (struct route_map *map, enum route_map_type type,
- int pref)
+ route_map_seq_t seq)
{
struct route_map_index *index;
struct route_map_index *point;
- /* Allocate new route map inex. */
+ /* Allocate new route map index. */
index = route_map_index_new ();
- index->map = map;
+ index->map = map;
index->type = type;
- index->pref = pref;
-
- /* Compare preference. */
+ index->seq = seq;
+
+ /* Compare sequence number */
for (point = map->head; point; point = point->next)
- if (point->pref >= pref)
+ if (point->seq >= seq)
break;
if (map->head == NULL)
@@ -394,12 +407,12 @@ route_map_index_add (struct route_map *map, enum route_map_type type,
/* Get route map index. */
static struct route_map_index *
-route_map_index_get (struct route_map *map, enum route_map_type type,
- int pref)
+route_map_index_get (struct route_map *map, enum route_map_type type,
+ route_map_seq_t seq)
{
struct route_map_index *index;
- index = route_map_index_lookup (map, RMAP_ANY, pref);
+ index = route_map_index_lookup (map, RMAP_ANY, seq);
if (index && index->type != type)
{
/* Delete index from route map. */
@@ -407,7 +420,7 @@ route_map_index_get (struct route_map *map, enum route_map_type type,
index = NULL;
}
if (index == NULL)
- index = route_map_index_add (map, type, pref);
+ index = route_map_index_add (map, type, seq);
return index;
}
@@ -420,7 +433,7 @@ route_map_rule_new (void)
new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
return new;
}
-
+
/* Install rule command to the match list. */
void
route_map_install_match (struct route_map_rule_cmd *cmd)
@@ -552,7 +565,7 @@ route_map_add_match (struct route_map_index *index, const char *match_name,
{
next = rule->next;
if (rule->cmd == cmd)
- {
+ {
route_map_rule_delete (&index->match_list, rule);
replaced = 1;
}
@@ -591,9 +604,9 @@ route_map_delete_match (struct route_map_index *index, const char *match_name,
cmd = route_map_lookup_match (match_name);
if (cmd == NULL)
return 1;
-
+
for (rule = index->match_list.head; rule; rule = rule->next)
- if (rule->cmd == cmd &&
+ if (rule->cmd == cmd &&
(rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
{
route_map_rule_delete (&index->match_list, rule);
@@ -677,7 +690,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
cmd = route_map_lookup_set (set_name);
if (cmd == NULL)
return 1;
-
+
for (rule = index->set_list.head; rule; rule = rule->next)
if ((rule->cmd == cmd) &&
(rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
@@ -698,7 +711,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
The matrix for a route-map looks like this:
(note, this includes the description for the "NEXT"
and "GOTO" frobs now
-
+
Match | No Match
|
permit action | cont
@@ -707,22 +720,22 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
|
deny deny | cont
|
-
+
action)
-Apply Set statements, accept route
-If Call statement is present jump to the specified route-map, if it
denies the route we finish.
-If NEXT is specified, goto NEXT statement
- -If GOTO is specified, goto the first clause where pref > nextpref
+ -If GOTO is specified, goto the first clause where seq > goto_seq
-If nothing is specified, do as Cisco and finish
deny)
-Route is denied by route-map.
cont)
-Goto Next index
-
+
If we get no matches after we've processed all updates, then the route
is dropped too.
-
+
Some notes on the new "CALL", "NEXT" and "GOTO"
call WORD - If this clause is matched, then the set statements
are executed and then we jump to route-map 'WORD'. If
@@ -735,7 +748,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
first clause greater than this. In order to ensure
route-maps *always* exit, you cannot jump backwards.
Sorry ;)
-
+
We need to make sure our route-map processing matches the above
*/
@@ -757,7 +770,7 @@ route_map_apply_match (struct route_map_rule_list *match_list,
for (match = match_list->head; match; match = match->next)
{
/* Try each match statement in turn, If any do not return
- RMAP_MATCH, return, otherwise continue on to next match
+ RMAP_MATCH, return, otherwise continue on to next match
statement. All match statements must match for end-result
to be a match. */
ret = (*match->cmd->func_apply) (match->value, prefix,
@@ -827,7 +840,7 @@ route_map_apply (struct route_map *map, struct prefix *prefix,
if (ret == RMAP_DENYMATCH)
return ret;
}
-
+
switch (index->exitpolicy)
{
case RMAP_EXIT:
@@ -838,9 +851,9 @@ route_map_apply (struct route_map *map, struct prefix *prefix,
{
/* Find the next clause to jump to */
struct route_map_index *next = index->next;
- int nextpref = index->nextpref;
+ route_map_seq_t goto_seq = index->goto_seq;
- while (next && next->pref < nextpref)
+ while (next && next->seq < goto_seq)
{
index = next;
next = next->next;
@@ -898,11 +911,11 @@ route_map_finish (void)
vector_free (route_set_vec);
route_set_vec = NULL;
}
-
+
/* VTY related functions. */
DEFUN (route_map,
route_map_cmd,
- "route-map WORD (deny|permit) <1-65535>",
+ "route-map WORD (deny|permit) <1-4294967295>",
"Create route-map or enter route-map command mode\n"
"Route map tag\n"
"Route map denies set operations\n"
@@ -910,7 +923,7 @@ DEFUN (route_map,
"Sequence to insert to/delete from existing route-map entry\n")
{
int permit;
- unsigned long pref;
+ unsigned long seq;
struct route_map *map;
struct route_map_index *index;
char *endptr = NULL;
@@ -926,26 +939,27 @@ DEFUN (route_map,
return CMD_WARNING;
}
- /* Preference check. */
- pref = strtoul (argv[2], &endptr, 10);
- if (pref == ULONG_MAX || *endptr != '\0')
+ /* Sequence number check. */
+ seq = strtoul (argv[2], &endptr, 10);
+ confirm(sizeof(route_map_seq_t) <= sizeof(unsigned long)) ;
+ if (seq == ULONG_MAX || *endptr != '\0')
{
vty_out (vty, "the fourth field must be positive integer%s",
VTY_NEWLINE);
return CMD_WARNING;
}
- if (pref == 0 || pref > 65535)
+ if (seq == 0 || seq > 4294967295)
{
- vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
+ vty_out (vty, "the fourth field must be <1-4294967295>%s", VTY_NEWLINE);
return CMD_WARNING;
}
/* Get route map. */
map = route_map_get (argv[0]);
- index = route_map_index_get (map, permit, pref);
+ index = route_map_index_get (map, permit, seq);
vty->index = index;
- vty->node = RMAP_NODE;
+ vty_set_node(vty, RMAP_NODE) ;
return CMD_SUCCESS;
}
@@ -973,7 +987,7 @@ DEFUN (no_route_map_all,
DEFUN (no_route_map,
no_route_map_cmd,
- "no route-map WORD (deny|permit) <1-65535>",
+ "no route-map WORD (deny|permit) <1-4294967295>",
NO_STR
"Create route-map or enter route-map command mode\n"
"Route map tag\n"
@@ -982,7 +996,7 @@ DEFUN (no_route_map,
"Sequence to insert to/delete from existing route-map entry\n")
{
int permit;
- unsigned long pref;
+ unsigned long seq;
struct route_map *map;
struct route_map_index *index;
char *endptr = NULL;
@@ -998,17 +1012,18 @@ DEFUN (no_route_map,
return CMD_WARNING;
}
- /* Preference. */
- pref = strtoul (argv[2], &endptr, 10);
- if (pref == ULONG_MAX || *endptr != '\0')
+ /* Sequence number */
+ seq = strtoul (argv[2], &endptr, 10);
+ confirm(sizeof(route_map_seq_t) <= sizeof(unsigned long)) ;
+ if (seq == ULONG_MAX || *endptr != '\0')
{
vty_out (vty, "the fourth field must be positive integer%s",
VTY_NEWLINE);
return CMD_WARNING;
}
- if (pref == 0 || pref > 65535)
+ if (seq == 0 || seq > 4294967295)
{
- vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
+ vty_out (vty, "the fourth field must be <1-4294967295>%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1022,10 +1037,10 @@ DEFUN (no_route_map,
}
/* Lookup route map index. */
- index = route_map_index_lookup (map, permit, pref);
+ index = route_map_index_lookup (map, permit, seq);
if (index == NULL)
{
- vty_out (vty, "%% Could not find route-map entry %s %s%s",
+ vty_out (vty, "%% Could not find route-map entry %s %s%s",
argv[0], argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1066,7 +1081,7 @@ DEFUN (no_rmap_onmatch_next,
struct route_map_index *index;
index = vty->index;
-
+
if (index)
index->exitpolicy = RMAP_EXIT;
@@ -1075,32 +1090,33 @@ DEFUN (no_rmap_onmatch_next,
DEFUN (rmap_onmatch_goto,
rmap_onmatch_goto_cmd,
- "on-match goto <1-65535>",
+ "on-match goto <1-4294967295>",
"Exit policy on matches\n"
"Goto Clause number\n"
"Number\n")
{
struct route_map_index *index = vty->index;
- int d = 0;
+ route_map_seq_t d = 0;
if (index)
{
if (argc == 1 && argv[0])
- VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536);
+ /* TODO: why did the on-match goto range include 65536 ? */
+ VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 4294967295);
else
- d = index->pref + 1;
-
- if (d <= index->pref)
+ d = index->seq + 1;
+
+ if (d <= index->seq)
{
/* Can't allow you to do that, Dave */
- vty_out (vty, "can't jump backwards in route-maps%s",
+ vty_out (vty, "can't jump backwards in route-maps%s",
VTY_NEWLINE);
return CMD_WARNING;
}
else
{
index->exitpolicy = RMAP_GOTO;
- index->nextpref = d;
+ index->goto_seq = d;
}
}
return CMD_SUCCESS;
@@ -1119,7 +1135,7 @@ DEFUN (no_rmap_onmatch_goto,
if (index)
index->exitpolicy = RMAP_EXIT;
-
+
return CMD_SUCCESS;
}
@@ -1138,13 +1154,13 @@ ALIAS (no_rmap_onmatch_goto,
/* GNU Zebra compatible */
ALIAS (rmap_onmatch_goto,
rmap_continue_seq_cmd,
- "continue <1-65535>",
+ "continue <1-4294967295>",
"Continue on a different entry within the route-map\n"
"Route-map entry sequence number\n")
ALIAS (no_rmap_onmatch_goto,
no_rmap_continue_seq,
- "no continue <1-65535>",
+ "no continue <1-4294967295>",
NO_STR
"Continue on a different entry within the route-map\n"
"Route-map entry sequence number\n")
@@ -1164,7 +1180,7 @@ DEFUN (rmap_show_name,
ALIAS (rmap_onmatch_goto,
rmap_continue_index_cmd,
- "continue <1-65536>",
+ "continue <1-4294967295>",
"Exit policy on matches\n"
"Goto Clause number\n")
@@ -1259,16 +1275,17 @@ route_map_config_write (struct vty *vty)
else
first = 0;
- vty_out (vty, "route-map %s %s %d%s",
+ vty_out (vty, "route-map %s %s %lu%s",
map->name,
route_map_type_str (index->type),
- index->pref, VTY_NEWLINE);
+ (unsigned long)index->seq, VTY_NEWLINE);
+ confirm(sizeof(index->seq) <= sizeof(unsigned long)) ;
if (index->description)
vty_out (vty, " description %s%s", index->description, VTY_NEWLINE);
for (rule = index->match_list.head; rule; rule = rule->next)
- vty_out (vty, " match %s %s%s", rule->cmd->str,
+ vty_out (vty, " match %s %s%s", rule->cmd->str,
rule->rule_str ? rule->rule_str : "",
VTY_NEWLINE);
@@ -1276,13 +1293,16 @@ route_map_config_write (struct vty *vty)
vty_out (vty, " set %s %s%s", rule->cmd->str,
rule->rule_str ? rule->rule_str : "",
VTY_NEWLINE);
- if (index->nextrm)
- vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
- if (index->exitpolicy == RMAP_GOTO)
- vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
- if (index->exitpolicy == RMAP_NEXT)
- vty_out (vty," on-match next%s", VTY_NEWLINE);
-
+
+ if (index->nextrm)
+ vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
+ if (index->exitpolicy == RMAP_GOTO)
+ vty_out (vty, " on-match goto %lu%s", (unsigned long)index->goto_seq,
+ VTY_NEWLINE);
+ confirm(sizeof(index->goto_seq) <= sizeof(unsigned long)) ;
+ if (index->exitpolicy == RMAP_NEXT)
+ vty_out (vty," on-match next%s", VTY_NEWLINE);
+
write++;
}
return write;
@@ -1315,12 +1335,12 @@ route_map_init_vty (void)
install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
-
+
/* Install the continue stuff (ALIAS of on-match). */
install_element (RMAP_NODE, &rmap_continue_cmd);
install_element (RMAP_NODE, &no_rmap_continue_cmd);
install_element (RMAP_NODE, &rmap_continue_index_cmd);
-
+
/* Install the call stuff. */
install_element (RMAP_NODE, &rmap_call_cmd);
install_element (RMAP_NODE, &no_rmap_call_cmd);
@@ -1328,7 +1348,7 @@ route_map_init_vty (void)
/* Install description commands. */
install_element (RMAP_NODE, &rmap_description_cmd);
install_element (RMAP_NODE, &no_rmap_description_cmd);
-
+
/* Install show command */
install_element (ENABLE_NODE, &rmap_show_name_cmd);
}
diff --git a/lib/routemap.h b/lib/routemap.h
index 1402f5c8..ac6cb999 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -16,12 +16,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_ROUTEMAP_H
#define _ZEBRA_ROUTEMAP_H
+#include <stdint.h>
+
/* Route map's type. */
enum route_map_type
{
@@ -30,7 +32,9 @@ enum route_map_type
RMAP_ANY
};
-typedef enum
+typedef enum route_map_type route_map_type_t ;
+
+typedef enum
{
RMAP_MATCH,
RMAP_DENYMATCH,
@@ -78,7 +82,7 @@ struct route_map_rule_cmd
const char *str;
/* Function for value set or match. */
- route_map_result_t (*func_apply)(void *, struct prefix *,
+ route_map_result_t (*func_apply)(void *, struct prefix *,
route_map_object_t, void *);
/* Compile argument and return result as void *. */
@@ -105,25 +109,28 @@ struct route_map_rule_list
struct route_map_rule *tail;
};
-/* Route map index structure. */
+/* Route map sequence number */
+typedef uint32_t route_map_seq_t ;
+
+/* Route map index structure. */
struct route_map_index
{
struct route_map *map;
char *description;
/* Preference of this route map rule. */
- int pref;
+ route_map_seq_t seq;
/* Route map type permit or deny. */
- enum route_map_type type;
+ enum route_map_type type;
/* Do we follow old rules, or hop forward? */
- route_map_end_t exitpolicy;
+ route_map_end_t exitpolicy;
/* If we're using "GOTO", to where do we go? */
- int nextpref;
+ route_map_seq_t goto_seq;
- /* If we're using "CALL", to which route-map do ew go? */
+ /* If we're using "CALL", to which route-map do we go? */
char *nextrm;
/* Matching rule list. */
@@ -166,7 +173,7 @@ extern int route_map_delete_match (struct route_map_index *index,
const char *match_arg);
/* Add route-map set statement to the route map. */
-extern int route_map_add_set (struct route_map_index *index,
+extern int route_map_add_set (struct route_map_index *index,
const char *set_name,
const char *set_arg);
diff --git a/lib/sigevent.c b/lib/sigevent.c
index 30e9a3d1..18fcffb0 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -16,7 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with Quagga; 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>
@@ -41,13 +41,13 @@ struct quagga_sigevent_master_t
{
struct thread *t;
- struct quagga_signal_t *signals;
+ struct quagga_signal_t *signals;
int sigc;
-
+
volatile sig_atomic_t caught;
} sigmaster;
-/* Generic signal handler
+/* Generic signal handler
* Schedules signal event thread
*/
static void
@@ -55,24 +55,30 @@ quagga_signal_handler (int signo)
{
int i;
struct quagga_signal_t *sig;
-
+
for (i = 0; i < sigmaster.sigc; i++)
{
sig = &(sigmaster.signals[i]);
-
+
if (sig->signal == signo)
sig->caught = 1;
}
-
+
sigmaster.caught = 1;
-}
+}
-/* check if signals have been caught and run appropriate handlers */
+/* check if signals have been caught and run appropriate handlers
+ *
+ * Returns: 0 => nothing to do
+ * -1 => failed
+ * > 0 => done this many signals
+ */
int
quagga_sigevent_process (void)
{
struct quagga_signal_t *sig;
int i;
+ int done ;
#ifdef SIGEVENT_BLOCK_SIGNALS
/* shouldnt need to block signals, but potentially may be needed */
sigset_t newmask, oldmask;
@@ -85,7 +91,7 @@ quagga_sigevent_process (void)
sigfillset (&newmask);
sigdelset (&newmask, SIGTRAP);
sigdelset (&newmask, SIGKILL);
-
+
if ( (sigprocmask (SIG_BLOCK, &newmask, &oldmask)) < 0)
{
zlog_err ("quagga_signal_timer: couldnt block signals!");
@@ -93,13 +99,14 @@ quagga_sigevent_process (void)
}
#endif /* SIGEVENT_BLOCK_SIGNALS */
+ done = 0 ;
if (sigmaster.caught > 0)
{
sigmaster.caught = 0;
/* must not read or set sigmaster.caught after here,
* race condition with per-sig caught flags if one does
*/
-
+
for (i = 0; i < sigmaster.sigc; i++)
{
sig = &(sigmaster.signals[i]);
@@ -108,6 +115,7 @@ quagga_sigevent_process (void)
{
sig->caught = 0;
sig->handler ();
+ ++done ;
}
}
}
@@ -117,7 +125,7 @@ quagga_sigevent_process (void)
return -1;
#endif /* SIGEVENT_BLOCK_SIGNALS */
- return 0;
+ return done ;
}
#ifdef SIGEVENT_SCHEDULE_THREAD
@@ -159,7 +167,7 @@ signal_set (int signo)
}
ret = sigaction (signo, &sig, &osig);
- if (ret < 0)
+ if (ret < 0)
return ret;
else
return 0;
@@ -212,18 +220,25 @@ core_handler(int signo
, siginfo, program_counter(context)
#endif
);
- abort();
+ zabort_abort();
}
+/* For the signals known to Quagga, and which are in their default state,
+ * set a Quagga default handler.
+ */
static void
trap_default_signals(void)
{
static const int core_signals[] = {
SIGQUIT,
SIGILL,
+ SIGABRT,
#ifdef SIGEMT
SIGEMT,
#endif
+#ifdef SIGIOT
+ SIGIOT,
+#endif
SIGFPE,
SIGBUS,
SIGSEGV,
@@ -237,6 +252,7 @@ trap_default_signals(void)
SIGXFSZ,
#endif
};
+
static const int exit_signals[] = {
SIGHUP,
SIGINT,
@@ -245,18 +261,20 @@ trap_default_signals(void)
SIGUSR1,
SIGUSR2,
#ifdef SIGPOLL
- SIGPOLL,
+ SIGPOLL,
#endif
#ifdef SIGVTALRM
SIGVTALRM,
#endif
#ifdef SIGSTKFLT
- SIGSTKFLT,
+ SIGSTKFLT,
#endif
};
+
static const int ignore_signals[] = {
SIGPIPE,
};
+
static const struct {
const int *sigs;
u_int nsigs;
@@ -279,38 +297,49 @@ trap_default_signals(void)
for (j = 0; j < sigmap[i].nsigs; j++)
{
struct sigaction oact;
- if ((sigaction(sigmap[i].sigs[j],NULL,&oact) == 0) &&
- (oact.sa_handler == SIG_DFL))
+ if (sigaction(sigmap[i].sigs[j], NULL, &oact) < 0)
+ zlog_warn("Unable to get signal handler for signal %d: %s",
+ sigmap[i].sigs[j], errtoa(errno, 0).str);
+ else {
+#ifdef SA_SIGINFO
+ if (oact.sa_flags & SA_SIGINFO)
+ continue ; /* Don't set again */
+#endif
+ if (oact.sa_handler != SIG_DFL)
+ continue ; /* Don't set again */
+ }
+ if ( (sigaction(sigmap[i].sigs[j], NULL, &oact) == 0) &&
+ (oact.sa_handler == SIG_DFL) )
{
struct sigaction act;
sigfillset (&act.sa_mask);
if (sigmap[i].handler == NULL)
{
act.sa_handler = SIG_IGN;
- act.sa_flags = 0;
+ act.sa_flags = 0;
}
else
{
#ifdef SA_SIGINFO
/* Request extra arguments to signal handler. */
act.sa_sigaction = sigmap[i].handler;
- act.sa_flags = SA_SIGINFO;
+ act.sa_flags = SA_SIGINFO;
#else
- act.sa_handler = sigmap[i].handler;
- act.sa_flags = 0;
+ act.sa_handler = sigmap[i].handler;
+ act.sa_flags = 0;
#endif
}
- if (sigaction(sigmap[i].sigs[j],&act,NULL) < 0)
+ if (sigaction(sigmap[i].sigs[j], &act, NULL) < 0)
zlog_warn("Unable to set signal handler for signal %d: %s",
- sigmap[i].sigs[j],safe_strerror(errno));
+ sigmap[i].sigs[j], errtoa(errno, 0).str);
}
}
}
}
-void
-signal_init (struct thread_master *m, int sigc,
+void
+signal_init (struct thread_master *m, int sigc,
struct quagga_signal_t signals[])
{
@@ -320,7 +349,7 @@ signal_init (struct thread_master *m, int sigc,
/* First establish some default handlers that can be overridden by
the application. */
trap_default_signals();
-
+
while (i < sigc)
{
sig = &signals[i];
@@ -332,9 +361,29 @@ signal_init (struct thread_master *m, int sigc,
sigmaster.sigc = sigc;
sigmaster.signals = signals;
-#ifdef SIGEVENT_SCHEDULE_THREAD
- sigmaster.t =
- thread_add_timer (m, quagga_signal_timer, &sigmaster,
+#ifdef SIGEVENT_SCHEDULE_THREAD
+ sigmaster.t =
+ thread_add_timer (m, quagga_signal_timer, &sigmaster,
QUAGGA_SIGNAL_TIMER_INTERVAL);
#endif /* SIGEVENT_SCHEDULE_THREAD */
}
+
+/* turn off trap for SIGABRT ! */
+extern void quagga_sigabrt_no_trap(void)
+{
+ struct sigaction new_act ;
+ sigset_t set ;
+
+ sigfillset(&set) ;
+
+ new_act.sa_handler = SIG_DFL ;
+ new_act.sa_mask = set ;
+ new_act.sa_flags = 0 ;
+ sigaction(SIGABRT, &new_act, NULL) ;
+
+ sigemptyset(&set) ;
+ sigaddset(&set, SIGABRT) ;
+ sigprocmask(SIG_UNBLOCK, &set, NULL) ;
+
+} ;
+
diff --git a/lib/sigevent.h b/lib/sigevent.h
index 62b944a7..57486bc2 100644
--- a/lib/sigevent.h
+++ b/lib/sigevent.h
@@ -1,4 +1,4 @@
-/*
+/*
* Quagga Signal handling header.
*
* Copyright (C) 2004 Paul Jakma.
@@ -18,7 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Quagga; 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 _QUAGGA_SIGNAL_H
@@ -44,10 +44,13 @@ struct quagga_signal_t
* - array of quagga_signal_t's describing signals to handle
* and handlers to use for each signal
*/
-extern void signal_init (struct thread_master *m, int sigc,
+extern void signal_init (struct thread_master *m, int sigc,
struct quagga_signal_t *signals);
/* check whether there are signals to handle, process any found */
extern int quagga_sigevent_process (void);
+/* turn off trap for SIGABRT ! */
+extern void quagga_sigabrt_no_trap(void) ;
+
#endif /* _QUAGGA_SIGNAL_H */
diff --git a/lib/smux.c b/lib/smux.c
index 1941cf8c..cd3d2edf 100644
--- a/lib/smux.c
+++ b/lib/smux.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>
@@ -38,6 +38,7 @@
#include <lib/version.h>
#include "memory.h"
#include "sockunion.h"
+#include "sockopt.h"
#include "smux.h"
#define min(A,B) ((A) < (B) ? (A) : (B))
@@ -45,7 +46,7 @@
enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ};
void smux_event (enum smux_event, int);
-
+
/* SMUX socket. */
int smux_sock = -1;
@@ -81,7 +82,7 @@ static struct cmd_node smux_node =
/* thread master */
static struct thread_master *master;
-
+
void *
oid_copy (void *dest, const void *src, size_t size)
{
@@ -93,7 +94,7 @@ oid2in_addr (oid oid[], int len, struct in_addr *addr)
{
int i;
u_char *pnt;
-
+
if (len == 0)
return;
@@ -108,7 +109,7 @@ oid_copy_addr (oid oid[], struct in_addr *addr, int len)
{
int i;
u_char *pnt;
-
+
if (len == 0)
return;
@@ -155,7 +156,7 @@ oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len)
return 0;
}
-
+
static void
smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len)
{
@@ -205,7 +206,7 @@ smux_socket (void)
}
for(res=res0; res; res=res->ai_next)
{
- if (res->ai_family != AF_INET
+ if (res->ai_family != AF_INET
#ifdef HAVE_IPV6
&& res->ai_family != AF_INET6
#endif /* HAVE_IPV6 */
@@ -215,8 +216,8 @@ smux_socket (void)
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock < 0)
continue;
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
+ setsockopt_reuseaddr (sock);
+ setsockopt_reuseport (sock);
ret = connect (sock, res->ai_addr, res->ai_addrlen);
if (ret < 0)
{
@@ -244,15 +245,15 @@ smux_socket (void)
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
sp = getservbyname ("smux", "tcp");
- if (sp != NULL)
+ if (sp != NULL)
serv.sin_port = sp->s_port;
else
serv.sin_port = htons (SMUX_PORT_DEFAULT);
serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
+ setsockopt_reuseaddr (sock);
+ setsockopt_reuseport (sock);
ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in));
if (ret < 0)
@@ -289,7 +290,7 @@ smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
/* Place holder h1 for complete sequence */
ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0);
h1e = ptr;
-
+
ptr = asn_build_int (ptr, &len,
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&reqid, sizeof (reqid));
@@ -309,12 +310,12 @@ smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
h2 = ptr;
/* Place holder h2 for one variable */
- ptr = asn_build_sequence (ptr, &len,
+ ptr = asn_build_sequence (ptr, &len,
(u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
0);
h2e = ptr;
- ptr = snmp_build_var_op (ptr, objid, &objid_len,
+ ptr = snmp_build_var_op (ptr, objid, &objid_len,
val_type, arg_len, arg, &len);
/* Now variable size is known, fill in size */
@@ -325,7 +326,7 @@ smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
if (debug_smux)
zlog_debug ("SMUX getresp send: %td", (ptr - buf));
-
+
ret = send (smux_sock, buf, (ptr - buf), 0);
}
@@ -345,17 +346,17 @@ smux_var (u_char *ptr, size_t len, oid objid[], size_t *objid_len,
/* Parse header. */
ptr = asn_parse_header (ptr, &len, &type);
-
+
if (debug_smux)
{
zlog_debug ("SMUX var parse: type %d len %zd", type, len);
- zlog_debug ("SMUX var parse: type must be %d",
+ zlog_debug ("SMUX var parse: type must be %d",
(ASN_SEQUENCE | ASN_CONSTRUCTOR));
}
/* Parse var option. */
*objid_len = MAX_OID_LEN;
- ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type,
+ ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type,
&val_len, &val, &len);
if (var_val_len)
@@ -473,7 +474,7 @@ smux_set (oid *reqid, size_t *reqid_len,
{
if (debug_smux)
zlog_debug ("SMUX function call index is %d", v->magic);
-
+
statP = (*v->findVar) (v, suffix, &suffix_len, 1,
&val_len, &write_method);
@@ -499,7 +500,7 @@ smux_set (oid *reqid, size_t *reqid_len,
}
static int
-smux_get (oid *reqid, size_t *reqid_len, int exact,
+smux_get (oid *reqid, size_t *reqid_len, int exact,
u_char *val_type,void **val, size_t *val_len)
{
int j;
@@ -515,7 +516,7 @@ smux_get (oid *reqid, size_t *reqid_len, int exact,
/* Check */
for (ALL_LIST_ELEMENTS (treelist, node, nnode,subtree))
{
- subresult = oid_compare_part (reqid, *reqid_len,
+ subresult = oid_compare_part (reqid, *reqid_len,
subtree->name, subtree->name_len);
/* Subtree matched. */
@@ -565,7 +566,7 @@ smux_get (oid *reqid, size_t *reqid_len, int exact,
}
static int
-smux_getnext (oid *reqid, size_t *reqid_len, int exact,
+smux_getnext (oid *reqid, size_t *reqid_len, int exact,
u_char *val_type,void **val, size_t *val_len)
{
int j;
@@ -588,7 +589,7 @@ smux_getnext (oid *reqid, size_t *reqid_len, int exact,
/* Check */
for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
{
- subresult = oid_compare_part (reqid, *reqid_len,
+ subresult = oid_compare_part (reqid, *reqid_len,
subtree->name, subtree->name_len);
/* If request is in the tree. The agent has to make sure we
@@ -722,10 +723,10 @@ smux_parse_get (u_char *ptr, size_t len, int exact)
if (debug_smux)
zlog_debug ("SMUX GET message parse: len %zd", len);
-
+
/* Parse GET message header. */
ptr = smux_parse_get_header (ptr, &len, &reqid);
-
+
/* Parse GET message object ID. We needn't the value come */
ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL);
@@ -762,7 +763,7 @@ smux_parse_rrsp (u_char *ptr, size_t len)
{
u_char val;
long errstat;
-
+
ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat));
if (debug_smux)
@@ -818,7 +819,7 @@ process_rest: /* see note below: YYY */
else
zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len);
- if (len_income > 3)
+ if (len_income > 3)
{
/* YYY: this strange code has to solve the "slow peer"
problem: When agent sends SMUX_SOUT message it doesn't
@@ -903,7 +904,7 @@ smux_read (struct thread *t)
if (len < 0)
{
- zlog_warn ("Can't read all SMUX packet: %s", safe_strerror (errno));
+ zlog_warn ("Can't read all SMUX packet: %s", errtoa(errno, 0).str);
close (sock);
smux_sock = -1;
smux_event (SMUX_CONNECT, 0);
@@ -963,24 +964,24 @@ smux_open (int sock)
/* SMUX Open. */
version = 0;
- ptr = asn_build_int (ptr, &len,
+ ptr = asn_build_int (ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&version, sizeof (version));
/* SMUX connection oid. */
ptr = asn_build_objid (ptr, &len,
- (u_char)
+ (u_char)
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
smux_oid, smux_oid_len);
/* SMUX connection description. */
- ptr = asn_build_string (ptr, &len,
+ ptr = asn_build_string (ptr, &len,
(u_char)
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
- progname, strlen (progname));
+ progname, strlen ((void*)progname));
/* SMUX connection password. */
- ptr = asn_build_string (ptr, &len,
+ ptr = asn_build_string (ptr, &len,
(u_char)
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
(u_char *)smux_passwd, strlen (smux_passwd));
@@ -1019,38 +1020,38 @@ smux_trap (const oid *name, size_t namelen,
/* Sub agent enterprise oid. */
ptr = asn_build_objid (ptr, &len,
- (u_char)
+ (u_char)
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
smux_oid, smux_oid_len);
/* IP address. */
addr.s_addr = 0;
- ptr = asn_build_string (ptr, &len,
+ ptr = asn_build_string (ptr, &len,
(u_char)
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS),
(u_char *)&addr, sizeof (addr));
/* Generic trap integer. */
val = SNMP_TRAP_ENTERPRISESPECIFIC;
- ptr = asn_build_int (ptr, &len,
+ ptr = asn_build_int (ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
(long *)&val, sizeof (val));
/* Specific trap integer. */
val = sptrap;
- ptr = asn_build_int (ptr, &len,
+ ptr = asn_build_int (ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
(long *)&val, sizeof (val));
/* Timeticks timestamp. */
val = 0;
- ptr = asn_build_unsigned_int (ptr, &len,
+ ptr = asn_build_unsigned_int (ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS),
&val, sizeof (val));
-
+
/* Variables. */
h1 = ptr;
- ptr = asn_build_sequence (ptr, &len,
+ ptr = asn_build_sequence (ptr, &len,
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
0);
@@ -1067,27 +1068,27 @@ smux_trap (const oid *name, size_t namelen,
u_char val_type;
/* Make OID. */
- if (trapobj[i].namelen > 0)
+ if (trapobj[i].namelen > 0)
{
oid_copy (oid, name, namelen);
oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen);
oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen);
oid_len = namelen + trapobj[i].namelen + inamelen;
}
- else
+ else
{
oid_copy (oid, name, namelen);
oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen * (-1));
oid_len = namelen + trapobj[i].namelen * (-1) ;
}
- if (debug_smux)
+ if (debug_smux)
{
smux_oid_dump ("Trap", name, namelen);
if (trapobj[i].namelen < 0)
- smux_oid_dump ("Trap",
+ smux_oid_dump ("Trap",
trapobj[i].name, (- 1) * (trapobj[i].namelen));
- else
+ else
{
smux_oid_dump ("Trap", trapobj[i].name, (trapobj[i].namelen));
smux_oid_dump ("Trap", iname, inamelen);
@@ -1148,13 +1149,13 @@ smux_register (int sock)
/* Priority. */
priority = -1;
- ptr = asn_build_int (ptr, &len,
+ ptr = asn_build_int (ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&priority, sizeof (priority));
/* Operation. */
operation = 2; /* Register R/W */
- ptr = asn_build_int (ptr, &len,
+ ptr = asn_build_int (ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&operation, sizeof (operation));
@@ -1199,7 +1200,7 @@ smux_connect (struct thread *t)
ret = smux_open (smux_sock);
if (ret < 0)
{
- zlog_warn ("SMUX open message send failed: %s", safe_strerror (errno));
+ zlog_warn ("SMUX open message send failed: %s", errtoa(errno, 0).str);
close (smux_sock);
smux_sock = -1;
if (++fail < SMUX_MAX_FAILURE)
@@ -1211,7 +1212,7 @@ smux_connect (struct thread *t)
ret = smux_register (smux_sock);
if (ret < 0)
{
- zlog_warn ("SMUX register message send failed: %s", safe_strerror (errno));
+ zlog_warn ("SMUX register message send failed: %s", errtoa(errno, 0).str);
close (smux_sock);
smux_sock = -1;
if (++fail < SMUX_MAX_FAILURE)
@@ -1247,7 +1248,7 @@ smux_stop (void)
smux_sock = -1;
}
}
-
+
void
@@ -1268,7 +1269,7 @@ smux_event (enum smux_event event, int sock)
break;
}
}
-
+
static int
smux_str2oid (const char *str, oid *oid, size_t *oid_len)
{
@@ -1395,7 +1396,7 @@ smux_peer_default (void)
free (smux_oid);
smux_oid = NULL;
}
-
+
/* careful, smux_passwd might be pointing at string constant */
if (smux_passwd)
{
@@ -1488,8 +1489,8 @@ config_write_smux (struct vty *vty)
/* Register subtree to smux master tree. */
void
-smux_register_mib (const char *descr, struct variable *var,
- size_t width, int num,
+smux_register_mib (const char *descr, struct variable *var,
+ size_t width, int num,
oid name[], size_t namelen)
{
struct subtree *tree;
@@ -1508,7 +1509,7 @@ smux_register_mib (const char *descr, struct variable *var,
static int
smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
{
- return oid_compare(tree1->name, tree1->name_len,
+ return oid_compare(tree1->name, tree1->name_len,
tree2->name, tree2->name_len);
}
@@ -1518,7 +1519,7 @@ smux_init (struct thread_master *tm)
{
/* copy callers thread master */
master = tm;
-
+
/* Make MIB tree. */
treelist = list_new();
treelist->cmp = (int (*)(void *, void *))smux_tree_cmp;
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 55c6226b..083cafc2 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -1,4 +1,4 @@
-/* setsockopt functions
+/* Setting and getting socket options -- utility functions.
* Copyright (C) 1999 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
@@ -16,173 +16,589 @@
* 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>
+
#include "log.h"
#include "sockopt.h"
#include "sockunion.h"
+#include "pthread_safe.h"
-int
-setsockopt_so_recvbuf (int sock, int size)
+/*------------------------------------------------------------------------------
+ * Set socket SO_REUSEADDR option
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
+extern int
+setsockopt_reuseaddr (int sock_fd)
{
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",
- sock,size,safe_strerror(errno));
+ int on = 1;
- return ret;
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_warn ("cannot set sockopt SO_REUSEADDR on socket %d: %s", sock_fd,
+ errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+
+ return ret ;
}
-int
-setsockopt_so_sendbuf (const int sock, int size)
+/*------------------------------------------------------------------------------
+ * Set socket SO_REUSEPORT option -- if it is locally supported.
+ *
+ * Returns: >= 0 => OK -- or not supported
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
+extern int
+setsockopt_reuseport (int sock_fd)
{
- int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF,
- (char *)&size, sizeof (int));
-
+ int ret;
+
+#ifdef SO_REUSEPORT
+ int on = 1;
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
+#else
+ ret = 0 ;
+#endif
+
if (ret < 0)
- zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
- sock, size, safe_strerror (errno));
+ {
+ int err = errno ;
+ zlog_warn ("cannot set sockopt SO_REUSEPORT on socket %d: %s", sock_fd,
+ errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
- return ret;
-}
+ return ret ;
+} ;
-int
-getsockopt_so_sendbuf (const int sock)
+/*------------------------------------------------------------------------------
+ * Set socket SO_BROADCAST option
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
+extern int
+setsockopt_broadcast (int sock_fd)
{
- u_int32_t optval;
- socklen_t optlen = sizeof (optval);
- int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF,
- (char *)&optval, &optlen);
+ int ret;
+ int on = 1;
+
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
if (ret < 0)
- {
- zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
- sock, errno, safe_strerror (errno));
- return ret;
- }
- return optval;
+ {
+ int err = errno ;
+ zlog_warn ("cannot set sockopt SO_BROADCAST on socket %d: %s", sock_fd,
+ errtoa(err, 0).str) ;
+ errno = err ;
+ }
+ return ret ;
}
-static void *
-getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
+/*------------------------------------------------------------------------------
+ * Set TCP_CORK, if available.
+ *
+ * Returns: >= 0 => OK -- or not supported
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
+extern int
+setsockopt_cork (int sock_fd, int onoff)
{
- struct cmsghdr *cmsg;
- void *ptr = NULL;
-
- for (cmsg = ZCMSG_FIRSTHDR(msgh);
- cmsg != NULL;
- cmsg = CMSG_NXTHDR(msgh, cmsg))
- if (cmsg->cmsg_level == level && cmsg->cmsg_type)
- return (ptr = CMSG_DATA(cmsg));
+#ifdef TCP_CORK
+ int ret;
- return NULL;
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_warn ("cannot set sockopt TCP_CORK to %d on socket %d: %s", onoff,
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ }
+ return ret ;
+#else
+ return 0;
+#endif
}
-#ifdef HAVE_IPV6
-/* Set IPv6 packet info to the socket. */
-int
-setsockopt_ipv6_pktinfo (int sock, int val)
+/*------------------------------------------------------------------------------
+ * Set IP_TTL/IPV6_UNICAST_HOPS for socket, if available
+ *
+ * The ttl given is the maximum number of hops to allow -- so will generally
+ * be 1 or 255 or some small number.
+ *
+ * NB: This code treats any ttl outside the range 1..MAXTTL as MAXTTL.
+ *
+ * Returns: >= 0 => OK -- or not supported
+ * < 0 => failed, see errno.
+ *
+ * Logs a LOG_WARNING message if fails.
+ *
+ * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question
+ * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS.
+ *
+ * Here we try first to use the socket family, and if that fails on
+ * AF_INET6, then if the protocol family is AF_INET, then tries that.
+ */
+extern int
+setsockopt_ttl (int sock_fd, int ttl)
{
- int ret;
-
-#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
- if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
-#else /*RFC2292*/
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
+ const char* name ;
+ int af ;
+ int ret ;
+
+ af = sockunion_getsockfamily(sock_fd) ;
+ if (af < 0)
+ return af ;
+
+ if ((ttl < 1) || (ttl > MAXTTL))
+ ttl = MAXTTL ;
+
+ ret = 0 ;
+ name = NULL ;
+
+ while (1)
+ {
+ switch (af)
+ {
+ case AF_INET:
+#ifdef IP_TTL
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
+ name = "IP_TTL" ;
+#endif /* IP_TTL */
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &ttl, sizeof(ttl));
+ name = "IPV6_UNICAST_HOPS" ;
+
+ if (ret < 0)
+ {
+ af = sockunion_getprotofamily(sock_fd) ;
+ if (af == AF_INET)
+ continue ;
+ } ;
+ break ;
+#endif
+
+ default: /* ignore unknown family */
+ break ;
+ } ;
+
+ break ;
+ } ;
+
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
-#endif /* INIA_IPV6 */
- return ret;
-}
+ {
+ int err = errno ;
+ zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, ttl,
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+
+ return ret ;
+} ;
-/* Set multicast hops val to the socket. */
-int
-setsockopt_ipv6_checksum (int sock, int val)
+/*------------------------------------------------------------------------------
+ * Set IP_MINTTL/IPV6_MINHOPCOUNT (GTSM), if available.
+ *
+ * The ttl given is the maximum number of hops to allow -- so will generally
+ * be 1 -- which is the same as IP_TTL/IPV6_UNICAST_HOPS.
+ *
+ * NB: to turn off GTSM, need to set ttl = MAXTTL. This code treats any ttl
+ * outside the range 1..MAXTTL as MAXTTL.
+ *
+ * The underlying mechanics want MAX_TTL - (ttl - 1) -- and may not
+ * accept a value of zero.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed or not supported -- see errno
+ * EOPNOTSUPP if not supported
+ *
+ * Logs a LOG_WARNING message if fails (and is supported).
+ *
+ * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question
+ * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS.
+ *
+ * Here we try first to use the socket family, and if that fails on
+ * AF_INET6, then if the protocol family is AF_INET, then tries that.
+ */
+extern int
+setsockopt_minttl (int sock_fd, int ttl)
{
- int ret;
+ const char* name ;
+ int af ;
+ int minttl ;
+ int ret ;
-#ifdef GNU_LINUX
- ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
+ af = sockunion_getsockfamily(sock_fd) ;
+ if (af < 0)
+ return af ;
+
+ ret = 0 ;
+ name = NULL ;
+
+ if ((ttl < 1) || (ttl > MAXTTL))
+ ttl = MAXTTL ;
+
+ minttl = MAXTTL - (ttl - 1) ;
+
+ while (1)
+ {
+ enum
+ {
+#ifdef IP_MINTTL
+ have_ip_minttl = true,
#else
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
-#endif /* GNU_LINUX */
+ have_ip_minttl = false,
+#endif
+ ip_minttl = IP_MINTTL + 0
+ } ;
+
+#ifdef HAVE_IPV6
+
+# ifdef GNU_LINUX
+ /* The #include to bring in IPV6_MINHOPCOUNT is buried more or less as
+ * deep as we can get it, because it also redefines a number of things
+ * that we do not want redefined.
+ */
+ #include <linux/in6.h>
+# endif
+
+ enum
+ {
+# ifdef IPV6_MINHOPCOUNT
+ have_ipv6_minhopcount = true,
+# else
+ have_ipv6_minhopcount = false,
+# endif
+ ipv6_minhopcount = IPV6_MINHOPCOUNT + 0
+ } ;
+#endif /* HAVE_IPV6 */
+
+ switch (af)
+ {
+ case AF_INET:
+ name = "IP_MINTTL" ;
+ if (have_ip_minttl)
+ ret = setsockopt (sock_fd, IPPROTO_IP, ip_minttl,
+ &minttl, sizeof(minttl));
+ else
+ {
+ ret = -1 ;
+ errno = EOPNOTSUPP ;
+ } ;
+
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ name = "IPV6_MINHOPCOUNT" ;
+ if (have_ipv6_minhopcount)
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, ipv6_minhopcount,
+ &minttl, sizeof(minttl));
+ else
+ {
+ ret = -1 ;
+ errno = EOPNOTSUPP ;
+ } ;
+
+ if (ret < 0)
+ {
+ af = sockunion_getprotofamily(sock_fd) ;
+ if (af == AF_INET)
+ continue ;
+ } ;
+
+ break ;
+#endif /* HAVE_IPV6 */
+
+ default: /* ignore unknown family */
+ break ;
+ } ;
+
+ break ;
+ } ;
+
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_CHECKSUM");
- return ret;
-}
+ {
+ int err = errno ;
+ zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, minttl,
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
-/* Set multicast hops val to the socket. */
-int
-setsockopt_ipv6_multicast_hops (int sock, int val)
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set TCP MD5 signature socket option, if available.
+ *
+ * A NULL password or an empty password both signify unsetting the MD5
+ * signature.
+ *
+ * Returns: >= 0 => OK (or not supported, but password NULL or empty)
+ * < 0 => failed or not supported, see errno.
+ *
+ * NB: returns EOPNOTSUPP if TCP MD5 is not supported and password is not NULL
+ * and is not empty.
+ *
+ * Logs a LOG_ERR message if fails (and is supported).
+ */
+extern int
+setsockopt_tcp_signature (int sock_fd, sockunion su, const char *password)
{
- int ret;
+ int ret ;
+
+ if ((password != NULL) && (*password == '\0'))
+ password = NULL ;
+
+ ret = 0 ; /* so far, so good */
+
+#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
+ * Simpson)
+ */
+#define TCP_MD5_AUTH 13
+#define TCP_MD5_AUTH_ADD 1
+#define TCP_MD5_AUTH_DEL 2
+ struct tcp_rfc2385_cmd {
+ u_int8_t command; /* Command - Add/Delete */
+ u_int32_t address; /* IPV4 address associated */
+ u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */
+ 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;
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ;
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
- return ret;
-}
+ {
+ int err = errno ;
+ zlog_err("sockopt_tcp_signature: setsockopt(%d): %s",
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ }
+
+#elif HAVE_DECL_TCP_MD5SIG
+
+# ifdef GNU_LINUX
+ struct tcp_md5sig md5sig ;
+# ifdef HAVE_IPV6
+ int saf ;
+ union sockunion sux[1] ;
+# endif
+
+ /* Testing reveals that in order to set an MD5 password for an AF_INET6
+ * socket, the address passed in must be AF_INET6, even if what we are
+ * dealing with here is an IN6_IS_ADDR_V4MAPPED socket.
+ */
+# ifdef HAVE_IPV6
+ saf = sockunion_getsockfamily(sock_fd) ;
+ if (saf < 0)
+ return saf ;
+
+ if ((saf == AF_INET6) && (sockunion_family(su) == AF_INET))
+ {
+ sockunion_copy (sux, su) ;
+ sockunion_map_ipv4 (sux) ;
+ su = sux ; /* substitute v4 mapped address */
+ } ;
+# endif
+
+ /* Set address to AF_UNSPEC and key length and everything else to zero,
+ * then copy in the address and the key.
+ */
+ memset (&md5sig, 0, sizeof (md5sig)) ;
+ confirm(AF_UNSPEC == 0) ;
+
+ memcpy (&md5sig.tcpm_addr, &su->sa, sockunion_get_len(su)) ;
+
+ if (password != NULL)
+ {
+ size_t keylen = strlen(password) ;
+
+ if (md5sig.tcpm_keylen <= TCP_MD5SIG_MAXKEYLEN)
+ {
+ md5sig.tcpm_keylen = keylen ;
+ memcpy (md5sig.tcpm_key, password, keylen);
+ }
+ else
+ {
+ errno = EINVAL ; /* manufactured error */
+ ret = -1 ;
+ } ;
+ } ;
+
+# else
+ /*
+ * 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 != NULL) ? 1 : 0;
-/* Set multicast hops val to the socket. */
-int
-setsockopt_ipv6_unicast_hops (int sock, int val)
+# endif /* GNU_LINUX */
+
+ if (ret >= 0)
+ {
+ ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG,
+ &md5sig, sizeof(md5sig)) ;
+ if (ret < 0)
+ /* ENOENT is harmless. It is returned when we clear a password where
+ * one was not previously set.
+ */
+ if ((errno == ENOENT) && (password == NULL))
+ ret = 0 ;
+ } ;
+
+#else
+
+ /* TCP MD5 is not supported */
+
+ if (password != NULL)
+ {
+ errno = EOPNOTSUPP ; /* manufactured error */
+ ret = -1 ;
+ } ;
+
+#endif /* !HAVE_TCP_MD5SIG */
+
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("sockopt_tcp_signature: setsockopt(%d): %s",
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set SO_RCVBUF option on socket.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed, see errno.
+ *
+ * Logs a LOG_ERR message if fails
+ */
+extern int
+setsockopt_so_recvbuf (int sock_fd, int size)
{
int ret;
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ;
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
+ {
+ int err = errno ;
+ zlog_err ("cannot set sockopt SO_RCVBUF to %d on socket %d: %s",
+ size, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+
return ret;
}
-int
-setsockopt_ipv6_hoplimit (int sock, int val)
+/*------------------------------------------------------------------------------
+ * Set SO_SNDBUF option on socket.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed, see errno.
+ *
+ * Logs a LOG_ERR message if fails
+ */
+extern int
+setsockopt_so_sendbuf (int sock_fd, int size)
{
- int ret;
+ int ret ;
+
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
-#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
- if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
-#else /*RFC2292*/
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
-#endif
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt SO_SNDBUF to %d on socket %d: %s",
+ size, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+
return ret;
}
-/* Set multicast loop zero to the socket. */
-int
-setsockopt_ipv6_multicast_loop (int sock, int val)
+/*------------------------------------------------------------------------------
+ * Get SO_SNDBUF option value from socket.
+ *
+ * Returns: >= 0 => OK == value of option
+ * < 0 => failed, see errno.
+ *
+ * Logs a LOG_ERR message if fails
+ */
+extern int
+getsockopt_so_sendbuf (int sock_fd)
{
- int ret;
-
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
- sizeof (val));
+ u_int32_t optval;
+ socklen_t optlen = sizeof (optval);
+
+ int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen);
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
- return ret;
+ {
+ int err = errno ;
+ zlog_err ("cannot get sockopt SO_SNDBUF on socket %d: %s",
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ return ret;
+ }
+
+ return optval;
}
-static int
-getsockopt_ipv6_ifindex (struct msghdr *msgh)
+/*------------------------------------------------------------------------------
+ * Set IP_TOS option for AF_INET socket.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed, see errno.
+ *
+ * Logs a LOG_ERR message if fails
+ */
+extern int
+setsockopt_ipv4_tos(int sock_fd, int tos)
{
- struct in6_pktinfo *pktinfo;
-
- pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
-
- return pktinfo->ipi6_ifindex;
-}
-#endif /* HAVE_IPV6 */
+ int ret;
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt IP_TOS option %#x on socket %d: %s",
+ tos, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+ return ret;
+} ;
-/*
+/*------------------------------------------------------------------------------
* Process multicast socket options for IPv4 in an OS-dependent manner.
* Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP.
*
@@ -203,20 +619,22 @@ getsockopt_ipv6_ifindex (struct msghdr *msgh)
* but this behavior should not be harmful if they behave the same way,
* allow leaves, or implicitly leave all groups joined to down interfaces.
*/
-int
-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
- used instead of if_addr */)
+extern int
+setsockopt_multicast_ipv4(int sock_fd,
+ int optname,
+ struct in_addr if_addr /* required */,
+ unsigned int mcast_addr,
+ unsigned int ifindex /* optional: if non-zero,
+ may be used instead of
+ if_addr */
+ )
{
#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
/* This is better because it uses ifindex directly */
struct ip_mreqn mreqn;
int ret;
-
+
switch (optname)
{
case IP_MULTICAST_IF:
@@ -226,37 +644,34 @@ 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));
+
+ ret = setsockopt(sock_fd, IPPROTO_IP, optname, &mreqn, sizeof(mreqn));
if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
{
/* see above: handle possible problem when interface comes back up */
char buf[2][INET_ADDRSTRLEN];
zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
"re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
- sock,
+ sock_fd,
inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])),
inet_ntop(AF_INET, &mreqn.imr_multiaddr,
buf[1], sizeof(buf[1])), ifindex);
- setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
- (void *)&mreqn, sizeof(mreqn));
- ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (void *)&mreqn, sizeof(mreqn));
+ setsockopt(sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &mreqn, sizeof(mreqn));
+ ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreqn, sizeof(mreqn));
}
return ret;
- break;
default:
/* Can out and give an understandable error */
errno = EINVAL;
return -1;
- break;
}
/* Example defines for another OS, boilerplate off other code in this
@@ -264,7 +679,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 +696,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_fd, IPPROTO_IP, optname, (void *)&m, sizeof(m));
break;
case IP_ADD_MEMBERSHIP:
@@ -289,94 +704,194 @@ 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));
+
+ ret = setsockopt (sock_fd, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
{
/* see above: handle possible problem when interface comes back up */
char buf[2][INET_ADDRSTRLEN];
zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
"re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
- sock,
+ sock_fd,
inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])),
inet_ntop(AF_INET, &mreq.imr_multiaddr,
buf[1], sizeof(buf[1])), ifindex);
- setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
- (void *)&mreq, sizeof(mreq));
- ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (void *)&mreq, sizeof(mreq));
+ setsockopt (sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq));
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq));
}
return ret;
- break;
-
+
default:
/* Can out and give an understandable error */
errno = EINVAL;
return -1;
- break;
}
#endif /* #if OS_TYPE */
}
+/*==============================================================================
+ * Set pktinfo and get ifindex etc
+ */
+
+static int setsockopt_ipv4_pktinfo (int sock_fd, int val) ;
+static int getsockopt_ipv4_ifindex (struct msghdr *msgh) ;
+
+#ifdef HAVE_IPV6
+static int getsockopt_ipv6_ifindex (struct msghdr *msgh) ;
+#endif
+
+static void * getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) ;
+
+/*------------------------------------------------------------------------------
+ * Set IP_PKTINFO/IP_RECVIF or IPV6_RECVPKTINFO/IPV6_PKTINFO -- if available.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_pktinfo (int af, int sock_fd, int val)
+{
+ int ret = -1;
+
+ switch (af)
+ {
+ case AF_INET:
+ ret = setsockopt_ipv4_pktinfo (sock_fd, val);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ ret = setsockopt_ipv6_pktinfo (sock_fd, val);
+ break;
+#endif
+ default:
+ zlog_warn("setsockopt_ifindex: unknown address family %d", af) ;
+ ret = -1 ;
+ errno = EINVAL;
+ break ;
+ }
+ return ret;
+}
+
+/*------------------------------------------------------------------------------
+ * Set IP_PKTINFO or IP_RECVIF -- if available.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
static int
-setsockopt_ipv4_ifindex (int sock, int val)
+setsockopt_ipv4_pktinfo (int sock_fd, int val)
{
int ret;
-#if defined (IP_PKTINFO)
- if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
- zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
- sock,val,safe_strerror(errno));
-#elif defined (IP_RECVIF)
- if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
- zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
- sock,val,safe_strerror(errno));
+#if defined(IP_PKTINFO) || defined(IP_RECVIF)
+
+ int opt ;
+ const char* name ;
+
+# if defined(IP_PKTINFO)
+ opt = IP_PKTINFO ;
+ name = "IP_PKTINFO" ;
+# else
+ opt = IP_RECVIF ;
+ name = "IP_RECVIF" ;
+# endif
+
+ ret = setsockopt (sock_fd, IPPROTO_IP, opt, &val, sizeof (val)) ;
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt %s to %d on socket %d: %s", name,
+ val, sock_fd, errtoa(err, 0).str);
+ errno = err ;
+ } ;
#else
#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
#warning "Will not be able to receive link info."
#warning "Things might be seriously broken.."
/* XXX Does this ever happen? Should there be a zlog_warn message here? */
- ret = -1;
+ ret = -1;
+ errno = EOPNOTSUPP ; /* manufactured error */
#endif
return ret;
}
-int
-setsockopt_ipv4_tos(int sock, int tos)
+/*------------------------------------------------------------------------------
+ * Set IPV6_RECVPKTINFO (RFC3542) or IPV6_RECVIF (RFC2292) -- if available.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+#ifdef HAVE_IPV6
+extern int
+setsockopt_ipv6_pktinfo (int sock_fd, int val)
{
int ret;
+ int opt ;
+ const char* name ;
- ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
+# ifdef IPV6_RECVPKTINFO
+ opt = IPV6_RECVPKTINFO ; /* RFC3542 == RFC2292-bis */
+ name = "IPV6_RECVPKTINFO" ;
+# else
+ opt = IPV6_PKTINFO ; /* RFC2292 */
+ name = "IPV6_PKTINFO" ;
+# endif
+
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val));
if (ret < 0)
- zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
- sock, tos, safe_strerror(errno));
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt %s to %d on socket %d: %s", name,
+ val, sock_fd, errtoa(err, 0).str);
+ errno = err ;
+ } ;
+
return ret;
}
+#endif
-
-int
-setsockopt_ifindex (int af, int sock, int val)
+/*------------------------------------------------------------------------------
+ * Given a struct msghdr*, extract and return ifindex.
+ *
+ * Returns: > 0 => OK == ifindex
+ * 0 => none found or error
+ *
+ * Note: this is badly named, since it is not really a getsockopt() operation,
+ * but extracting data from a sendmsg/recvmsg struct msghdr.
+ *
+ * To have the ifindex returned, need to have setsockopt_pktinfo().
+ */
+extern int
+getsockopt_ifindex (int af, struct msghdr *msgh)
{
- int ret = -1;
-
switch (af)
{
case AF_INET:
- ret = setsockopt_ipv4_ifindex (sock, val);
- break;
+ return (getsockopt_ipv4_ifindex (msgh));
+
#ifdef HAVE_IPV6
case AF_INET6:
- ret = setsockopt_ipv6_pktinfo (sock, val);
- break;
+ return (getsockopt_ipv6_ifindex (msgh));
#endif
+
default:
- zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
+ zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
+ return 0 ;
}
- return ret;
}
-
-/*
+
+/*------------------------------------------------------------------------------
+ * AF_INET: extract ifindex from struct msghdr, if can
+ *
* Requires: msgh is not NULL and points to a valid struct msghdr, which
* may or may not have control data about the incoming interface.
*
@@ -386,18 +901,18 @@ setsockopt_ifindex (int af, int sock, int val)
static int
getsockopt_ipv4_ifindex (struct msghdr *msgh)
{
- /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */
- int ifindex = -1;
+ int ifindex ;
#if defined(IP_PKTINFO)
/* Linux pktinfo based ifindex retrieval */
struct in_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;
-
+
+ pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
+ if (pktinfo != NULL)
+ ifindex = pktinfo->ipi_ifindex ;
+ else
+ ifindex = 0 ;
+
#elif defined(IP_RECVIF)
/* retrieval based on IP_RECVIF */
@@ -412,7 +927,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 +939,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,39 +957,78 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh)
#warning "Some daemons may fail to operate correctly!"
ifindex = 0;
-#endif /* IP_PKTINFO */
+#endif /* IP_PKTINFO */
return ifindex;
}
-/* return ifindex, 0 if none found */
-int
-getsockopt_ifindex (int af, struct msghdr *msgh)
-{
- int ifindex = 0;
-
- switch (af)
- {
- case AF_INET:
- return (getsockopt_ipv4_ifindex (msgh));
- break;
+/*------------------------------------------------------------------------------
+ * AF_INET6: extract ifindex from struct msghdr, if can
+ *
+ * Requires: msgh is not NULL and points to a valid struct msghdr, which
+ * may or may not have control data about the incoming interface.
+ *
+ * Returns the interface index (small integer >= 1) if it can be
+ * determined, or else 0.
+ */
#ifdef HAVE_IPV6
- case AF_INET6:
- return (getsockopt_ipv6_ifindex (msgh));
- break;
+static int
+getsockopt_ipv6_ifindex (struct msghdr *msgh)
+{
+ struct in6_pktinfo *pktinfo;
+
+ pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
+
+ if (pktinfo != NULL)
+ return pktinfo->ipi6_ifindex;
+ else
+ return 0 ;
+}
#endif
- default:
- zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
- return (ifindex = 0);
- }
+
+/*------------------------------------------------------------------------------
+ * Scan msg_control portion of struct msghdr, looking for a cmsg with the given
+ * level and type.
+ *
+ * Requires: msgh is not NULL and points to a valid struct msghdr, which
+ * may or may not have control data about the incoming interface.
+ *
+ * Returns: address of data part of cmsg
+ * or: NULL => not found
+ */
+static void *
+getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = ZCMSG_FIRSTHDR(msgh);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msgh, cmsg))
+ if ((cmsg->cmsg_level == level) && (cmsg->cmsg_type == type))
+ return (void*)CMSG_DATA(cmsg);
+
+ return NULL;
}
-/* swab iph between order system uses for IP_HDRINCL and host order */
-void
+/*------------------------------------------------------------------------------
+ * swab iph between order system uses for IP_HDRINCL and host order
+ *
+ * This is done before handing struct ip to the system.
+ *
+ * There are four u_short fields in the IPv4 header:
+ *
+ * u_short ip_len -- convert to network order, except as noted below
+ * u_short ip_id -- convert to network order
+ * u_short ip_off -- convert to network order, except as noted below
+ * u_short ip_sum -- which we don't touch -- set by kernel
+ */
+extern void
sockopt_iphdrincl_swab_htosys (struct ip *iph)
{
- /* BSD and derived take iph in network order, except for
- * ip_len and ip_off
+ /* BSD and derived take iph in network order, except for ip_len and ip_off.
+ *
+ * So if *not* BSD-like, then need to convert ip_len and ip_off to network
+ * order.
*/
#ifndef HAVE_IP_HDRINCL_BSD_ORDER
iph->ip_len = htons(iph->ip_len);
@@ -484,7 +1038,12 @@ sockopt_iphdrincl_swab_htosys (struct ip *iph)
iph->ip_id = htons(iph->ip_id);
}
-void
+/*------------------------------------------------------------------------------
+ * swab iph between order system uses for IP_HDRINCL and host order
+ *
+ * This is done after receiving struct ip from the system -- see notes above.
+ */
+extern void
sockopt_iphdrincl_swab_systoh (struct ip *iph)
{
#ifndef HAVE_IP_HDRINCL_BSD_ORDER
@@ -495,103 +1054,172 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph)
iph->ip_id = ntohs(iph->ip_id);
}
-int
-sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
+/*==============================================================================
+ * IPv6 Stuff
+ */
+#ifdef HAVE_IPV6
+
+/*------------------------------------------------------------------------------
+ * Set IPV6_V6ONLY.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_ipv6_v6only(int sock_fd)
{
-#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
- * Simpson)
- */
-#define TCP_MD5_AUTH 13
-#define TCP_MD5_AUTH_ADD 1
-#define TCP_MD5_AUTH_DEL 2
- struct tcp_rfc2385_cmd {
- u_int8_t command; /* Command - Add/Delete */
- u_int32_t address; /* IPV4 address associated */
- u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */
- 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);
-
-#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;
-#else
- int keylen = password ? strlen (password) : 0;
- struct tcp_md5sig md5sig;
- union sockunion *su2, *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;
- else
+ int on = 1 ;
+
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ if (ret < 0)
{
- /* oops.. */
- su2 = susock;
-
- if (su2->sa.sa_family == AF_INET)
- {
- sockunion_free (susock);
- return 0;
- }
-
-#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.
- *
- * Sadly, it doesn't seem to work at present. It's unknown whether
- * this is a bug or not.
- */
- if (su2->sa.sa_family == AF_INET6
- && su->sa.sa_family == AF_INET)
- {
- su2->sin6.sin6_family = AF_INET6;
- /* V4Map the address */
- memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr));
- su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
- memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4);
- }
-#endif
+ int err = errno ;
+ zlog_err ("cannot set sockopt IPV6_V6ONLY on socket %d: %s",
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
}
-
- 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);
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set IPV6_CHECKSUM
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_ipv6_checksum (int sock_fd, int val)
+{
+ int ret;
+
+#ifdef GNU_LINUX
+ ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
+#else
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
#endif /* GNU_LINUX */
- if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0)
+ if (ret < 0)
{
- /* ENOENT is harmless. It is returned when we clear a password for which
- one was not previously set. */
- if (ENOENT == errno)
- ret = 0;
- else
- zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
- sock, safe_strerror(errno));
- }
+ int err = errno ;
+ zlog_err("cannot set sockopt IPV6_CHECKSUM to %d on socket %d: %s", val,
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+ return ret;
+}
+
+/*------------------------------------------------------------------------------
+ * Set unicast hops val to the socket (cf IP_TTL).
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_ipv6_unicast_hops (int sock_fd, int val)
+{
+ int ret;
+
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt IPV6_UNICAST_HOPS to %d on socket %d: %s",
+ val, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
+ return ret;
+}
+
+/*------------------------------------------------------------------------------
+ * Set multicast hops val to the socket.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_ipv6_multicast_hops (int sock_fd, int val)
+{
+ int ret;
+
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val,
+ sizeof(val));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt IPV6_MULTICAST_HOPS to %d on socket %d: %s",
+ val, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
return ret;
-#else /* HAVE_TCP_MD5SIG */
- return -2;
-#endif /* !HAVE_TCP_MD5SIG */
}
+
+/*------------------------------------------------------------------------------
+ * Set IPV6_RECVHOPLIMIT option (or IPV6_HOPLIMIT)
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_ipv6_hoplimit (int sock_fd, int val)
+{
+ int ret;
+ int opt ;
+ const char* name ;
+
+# ifdef IPV6_RECVHOPLIMIT
+ opt = IPV6_RECVHOPLIMIT ; /* RFC3542 == RFC2292-bis */
+ name = "IPV6_RECVHOPLIMIT" ;
+# else
+ opt = IPV6_HOPLIMIT ; /* RFC2292 */
+ name = "IPV6_HOPLIMIT" ;
+# endif
+
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt %s to %d on socket %d: %s", name,
+ val, sock_fd, errtoa(err, 0).str);
+ errno = err ;
+ } ;
+
+ return ret;
+}
+
+/*------------------------------------------------------------------------------
+ * Set IPV6_MULTICAST_LOOP option
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+setsockopt_ipv6_multicast_loop (int sock_fd, int val)
+{
+ int ret;
+
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof (val));
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err("cannot set sockopt IPV6_MULTICAST_LOOP to %d on socket %d: %s",
+ val, sock_fd, errtoa(err, 0).str);
+ errno = err ;
+ } ;
+ return ret;
+}
+#endif /* HAVE_IPV6 */
+
+
diff --git a/lib/sockopt.h b/lib/sockopt.h
index cb05c6fb..c706a74f 100644
--- a/lib/sockopt.h
+++ b/lib/sockopt.h
@@ -1,4 +1,4 @@
-/* Router advertisement
+/* Setting and getting socket options -- utility functions.
* Copyright (C) 1999 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
@@ -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.
*/
#ifndef _ZEBRA_SOCKOPT_H
@@ -24,18 +24,20 @@
#include "sockunion.h"
-extern int setsockopt_so_recvbuf (int sock, int size);
-extern int setsockopt_so_sendbuf (const int sock, int size);
-extern int getsockopt_so_sendbuf (const int sock);
+extern int setsockopt_reuseaddr (int sock_fd) ;
+extern int setsockopt_reuseport (int sock_fd) ;
+extern int setsockopt_broadcast (int sock_fd) ;
-#ifdef HAVE_IPV6
-extern int setsockopt_ipv6_pktinfo (int, int);
-extern int setsockopt_ipv6_checksum (int, int);
-extern int setsockopt_ipv6_multicast_hops (int, int);
-extern int setsockopt_ipv6_unicast_hops (int, int);
-extern int setsockopt_ipv6_hoplimit (int, int);
-extern int setsockopt_ipv6_multicast_loop (int, int);
-#endif /* HAVE_IPV6 */
+extern int setsockopt_ttl (int sock_fd, int ttl);
+extern int setsockopt_minttl (int sock_fd, int ttl);
+extern int setsockopt_cork (int sock_fd, int onoff);
+
+extern int setsockopt_so_recvbuf (int sock_fd, int size);
+extern int setsockopt_so_sendbuf (int sock_fd, int size);
+extern int getsockopt_so_sendbuf (int sock_fd);
+
+extern int setsockopt_tcp_signature(int sock_fd, union sockunion *su,
+ const char *password);
/*
* It is OK to reference in6_pktinfo here without a protecting #if
@@ -82,25 +84,35 @@ extern int setsockopt_ipv6_multicast_loop (int, int);
(((af) == AF_INET) : SOPT_SIZE_CMSG_IFINDEX_IPV4() \
? SOPT_SIZE_CMSG_PKTINFO_IPV6())
-extern int setsockopt_multicast_ipv4(int sock, int optname,
+extern int setsockopt_multicast_ipv4(int sock_fd, int optname,
struct in_addr if_addr
/* required: interface to join on */,
unsigned int mcast_addr,
unsigned int ifindex
/* optional: if non-zero, may be used
instead of if_addr */);
-extern int setsockopt_ipv4_tos(int sock, int tos);
+extern int setsockopt_ipv4_tos(int sock_fd, int tos);
/* Ask for, and get, ifindex, by whatever method is supported. */
-extern int setsockopt_ifindex (int, int, int);
+extern int setsockopt_pktinfo (int, int, int);
extern int getsockopt_ifindex (int, struct msghdr *);
-/* swab the fields in iph between the host order and system order expected
+/* swab the fields in iph between the host order and system order expected
* for IP_HDRINCL.
*/
extern void sockopt_iphdrincl_swab_htosys (struct ip *iph);
extern void sockopt_iphdrincl_swab_systoh (struct ip *iph);
-extern int sockopt_tcp_signature(int sock, union sockunion *su,
- const char *password);
+#ifdef HAVE_IPV6
+
+extern int setsockopt_ipv6_v6only(int sock_fd) ;
+extern int setsockopt_ipv6_pktinfo (int, int);
+extern int setsockopt_ipv6_checksum (int, int);
+extern int setsockopt_ipv6_multicast_hops (int, int);
+extern int setsockopt_ipv6_unicast_hops (int, int);
+extern int setsockopt_ipv6_hoplimit (int, int);
+extern int setsockopt_ipv6_multicast_loop (int, int);
+
+#endif /* HAVE_IPV6 */
+
#endif /*_ZEBRA_SOCKOPT_H */
diff --git a/lib/sockunion.c b/lib/sockunion.c
index a5382a72..b34d7047 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;
@@ -115,460 +117,563 @@ inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
}
#endif /* ! HAVE_INET_NTOP */
-const char *
-inet_sutop (union sockunion *su, char *str)
+/*------------------------------------------------------------------------------
+ * Set the sockunion size (sin_len or sin6_len), if required.
+ *
+ * NB: POSIX does not require this and Stevens et al say that even where it
+ * is supported, the application need not worry about it.
+ *
+ * However... the code as found does this.
+ *
+ * TODO: is it *really* necessary to set sin_len or sin6_len ??
+ *
+ * Returns: the sockunion size
+ */
+inline static int
+sockunion_sin_len(sockunion su)
+{
+ return
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ su->sin.sin_len =
+#endif
+ sizeof(struct sockaddr_in);
+} ;
+
+#ifdef HAVE_IPV6
+inline static int
+sockunion_sin6_len(sockunion su)
+{
+ return
+#ifdef SIN6_LEN
+ su->sin6.sin6_len =
+#endif
+ sizeof(struct sockaddr_in6);
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+ * Set the address family for the given sockunion.
+ *
+ * If sin_len or sin6_len entry is present, fill that in too.
+ *
+ * Assumes the address family is valid !
+ *
+ * Returns: 0
+ */
+inline static int
+sockunion_set_family(sockunion su, sa_family_t family)
+{
+ su->sa.sa_family = family ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ if (family == AF_INET)
+ sockunion_sin_len(su) ;
+#endif
+#if defined(HAVE_IPV6) && defined(SIN6_LEN)
+ if (family == AF_INET6)
+ sockunion_sin6_len(su) ;
+#endif
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the given sockunion address to "any"
+ */
+static void
+sockunion_set_addr_any(sockunion su)
{
switch (su->sa.sa_family)
- {
+ {
case AF_INET:
- inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
- break;
+ su->sin.sin_addr.s_addr = htonl (INADDR_ANY);
+ return ;
+
#ifdef HAVE_IPV6
case AF_INET6:
- inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
- break;
-#endif /* HAVE_IPV6 */
- }
- return str;
-}
+# if defined(LINUX_IPV6) || defined(NRL)
+ memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
+# else
+ su->sin6.sin6_addr = in6addr_any;
+# endif /* LINUX_IPV6 || defined(NRL) */
+ return ;
+#endif
-int
-str2sockunion (const char *str, union sockunion *su)
-{
- int ret;
+ default:
+ return ;
+ } ;
+} ;
- memset (su, 0, sizeof (union sockunion));
+/*------------------------------------------------------------------------------
+ * Set the port number in the given sockunion.
+ *
+ * For good measure, set the size (if that's required) and return same.
+ */
+extern int
+sockunion_set_port(sockunion su, in_port_t port)
+{
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ su->sin.sin_port = htons(port) ;
+ return sockunion_sin_len(su) ;
- ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
- if (ret > 0) /* Valid IPv4 address format. */
- {
- su->sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- return 0;
- }
#ifdef HAVE_IPV6
- ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
- if (ret > 0) /* Valid IPv6 address format. */
- {
- su->sin6.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
- return 0;
- }
-#endif /* HAVE_IPV6 */
- return -1;
-}
+ case AF_INET6:
+ su->sin6.sin6_port = htons(port) ;
+ return sockunion_sin6_len(su) ;
+#endif
-const char *
-sockunion2str (union sockunion *su, char *buf, size_t len)
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a new sockunion -- for the given address family (if any)
+ *
+ * Allocates a sockunion if required.
+ *
+ * Result is set "any".
+ *
+ * Advice is to zeroize sockaddr_in6, in particular.
+ */
+extern sockunion
+sockunion_init_new(sockunion su, sa_family_t family)
{
- if (su->sa.sa_family == AF_INET)
- return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
+ if (su == NULL)
+ su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)) ;
+ else
+ memset(su, 0, sizeof(union sockunion)) ;
+
+ if (family != AF_UNSPEC)
+ sockunion_set_family(su, family) ;
+ else
+ confirm(AF_UNSPEC == 0) ;
+
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the length of the address in the given sockunion.
+ *
+ * Returns zero if AF_UNSPEC or not any known address family.
+ */
+extern int
+sockunion_get_len(sockunion su)
+{
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ return sizeof(struct sockaddr_in) ;
+
#ifdef HAVE_IPV6
- else if (su->sa.sa_family == AF_INET6)
- return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
-#endif /* HAVE_IPV6 */
- return NULL;
-}
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6) ;
+#endif
-union sockunion *
-sockunion_str2su (const char *str)
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * From the given string, fill in the given sockunion.
+ *
+ * Returns: 0 => OK -- sockunion filled in
+ * -1 => not a valid address (or not a known address family)
+ */
+int
+str2sockunion (const char *str, union sockunion *su)
{
int ret;
- union sockunion *su;
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+ assert(su != NULL) ;
+
+ sockunion_init_new(su, AF_UNSPEC) ;
ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
if (ret > 0) /* Valid IPv4 address format. */
- {
- su->sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- return su;
- }
+ return sockunion_set_family(su, AF_INET) ;
+
#ifdef HAVE_IPV6
ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
if (ret > 0) /* Valid IPv6 address format. */
- {
- su->sin6.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
- return su;
- }
+ return sockunion_set_family(su, AF_INET6) ;
#endif /* HAVE_IPV6 */
- XFREE (MTYPE_SOCKUNION, su);
- return NULL;
+ return -1;
}
-char *
-sockunion_su2str (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Construct string for sockunion IP address.
+ *
+ * Requires buffer of at least SU_ADDRSTRLEN characters.
+ */
+const char *
+sockunion2str (union sockunion *su, char *buf, size_t size)
{
- char str[SU_ADDRSTRLEN];
+ assert(size >= SU_ADDRSTRLEN) ;
switch (su->sa.sa_family)
- {
+ {
case AF_INET:
- inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str));
+ inet_ntop (AF_INET, &su->sin.sin_addr, buf, size);
break;
#ifdef HAVE_IPV6
case AF_INET6:
- inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str));
+ inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, size);
break;
#endif /* HAVE_IPV6 */
- }
- return XSTRDUP (MTYPE_TMP, str);
+ default:
+ snprintf (buf, size, "?af=%d?", (int)su->sa.sa_family) ;
+ } ;
+
+ return buf;
}
-/* Convert IPv4 compatible IPv6 address to IPv4 address. */
-static void
-sockunion_normalise_mapped (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Fill in and return a sockunion_string
+ */
+extern sockunion_string_t
+sutoa(sockunion su)
{
- struct sockaddr_in sin;
-
-#ifdef HAVE_IPV6
- 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;
- memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
- memcpy (su, &sin, sizeof (struct sockaddr_in));
- }
-#endif /* HAVE_IPV6 */
-}
+ sockunion_string_t sus ;
-/* Return socket of sockunion. */
-int
-sockunion_socket (union sockunion *su)
+ sockunion2str(su, sus.str, sizeof(sus.str)) ;
+ return sus ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * From the given string, construct and fill in a sockunion.
+ *
+ * Returns: NULL => not a valid address (or not a known address family)
+ * otherwise is address of new sockunion.
+ *
+ * NB: the caller is responsible for freeing the sockunion created.
+ */
+union sockunion *
+sockunion_str2su (const char *str)
{
- int sock;
+ union sockunion *su;
- 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;
- }
+ su = XMALLOC (MTYPE_SOCKUNION, sizeof(union sockunion));
- return sock;
+ if (str2sockunion (str, su) != 0)
+ XFREE (MTYPE_SOCKUNION, su); /* sets su = NULL */
+
+ return su ;
}
-/* Return accepted new socket file descriptor. */
-int
-sockunion_accept (int sock, union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Convert given sockunion to string, and return a new piece of memory
+ * containing same.
+ *
+ * It is the callers responsibility to free the memory in due course.
+ */
+extern char *
+sockunion_su2str (union sockunion *su, enum MTYPE type)
{
- socklen_t len;
- int client_sock;
-
- len = sizeof (union sockunion);
- client_sock = accept (sock, (struct sockaddr *) su, &len);
-
- sockunion_normalise_mapped (su);
- return client_sock;
+ return XSTRDUP (type, sutoa(su).str) ;
}
-/* Return sizeof union sockunion. */
-static int
-sockunion_sizeof (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address.
+ */
+extern void
+sockunion_unmap_ipv4 (sockunion su)
{
- int ret;
-
- ret = 0;
- switch (su->sa.sa_family)
- {
- case AF_INET:
- ret = sizeof (struct sockaddr_in);
- break;
#ifdef HAVE_IPV6
- case AF_INET6:
- ret = sizeof (struct sockaddr_in6);
- break;
-#endif /* AF_INET6 */
+ if ( (sockunion_family(su) == AF_INET6)
+ && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) )
+ {
+ union sockunion sux[1] ;
+
+ sockunion_init_new(sux, AF_INET) ;
+ memcpy (&sux->sin.sin_addr, &su->sin6.sin6_addr.s6_addr[12], 4) ;
+ sux->sin.sin_port = su->sin6.sin6_port ;
+ memcpy (su, sux, sizeof(*sux)) ;
+ confirm(sizeof(*su) == sizeof(*sux)) ;
}
- return ret;
+#endif /* HAVE_IPV6 */
}
-/* return sockunion structure : this function should be revised. */
-static char *
-sockunion_log (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address.
+ */
+extern void
+sockunion_map_ipv4 (sockunion su)
{
- static char buf[SU_ADDRSTRLEN];
-
- switch (su->sa.sa_family)
- {
- case AF_INET:
- snprintf (buf, SU_ADDRSTRLEN, "%s", inet_ntoa (su->sin.sin_addr));
- break;
#ifdef HAVE_IPV6
- case AF_INET6:
- snprintf (buf, SU_ADDRSTRLEN, "%s",
- inet_ntop (AF_INET6, &(su->sin6.sin6_addr), buf, SU_ADDRSTRLEN));
- break;
-#endif /* HAVE_IPV6 */
- default:
- snprintf (buf, SU_ADDRSTRLEN, "af_unknown %d ", su->sa.sa_family);
- break;
+ if (sockunion_family(su) == AF_INET)
+ {
+ union sockunion sux[1] ;
+
+ sockunion_init_new(sux, AF_INET6) ;
+ memset (&sux->sin6.sin6_addr.s6_addr[10], 0xFF, 2) ;
+ memcpy (&sux->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4) ;
+ sux->sin6.sin6_port = su->sin.sin_port ;
+ memcpy (su, sux, sizeof(*sux)) ;
+ confirm(sizeof(*su) == sizeof(*sux)) ;
}
- return (XSTRDUP (MTYPE_TMP, buf));
+#endif /* HAVE_IPV6 */
}
-/* 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 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.
+ *
+ * Sets the given sockunion to the result of the accept(), converting any
+ * IPv6 mapped IPv4 addresses to IPv4 form. (Hiding the family for the socket.)
+ *
+ * Returns: >= 0 -- OK, this is the fd (socket)
+ * -1 -- error -- not one of the above
+ * -2 -- error -- one of the above
+ */
+extern int
+sockunion_accept (int sock_fd, union sockunion *su)
{
- int ret;
- int val;
- union sockunion su;
-
- memcpy (&su, peersu, sizeof (union sockunion));
-
- switch (su.sa.sa_family)
- {
- case AF_INET:
- su.sin.sin_port = port;
- break;
-#ifdef HAVE_IPV6
- case AF_INET6:
- su.sin6.sin6_port = 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;
-#endif
-#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
-#ifndef MUSICA
- SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
-#endif
- }
-#endif /* KAME */
- break;
-#endif /* HAVE_IPV6 */
- }
-
- /* Make socket non-block. */
- val = fcntl (fd, F_GETFL, 0);
- fcntl (fd, F_SETFL, val|O_NONBLOCK);
+ socklen_t len;
+ int new_fd, err ;
- /* Call connect function. */
- ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));
+ len = sizeof(*su);
+ memset(su, 0, len) ;
+ new_fd = accept(sock_fd, &su->sa, &len) ;
- /* Immediate success */
- if (ret == 0)
+ if (new_fd >= 0)
{
- fcntl (fd, F_SETFL, val);
- return connect_success;
- }
+ sockunion_unmap_ipv4(su);
+ return new_fd ; /* OK -- got socket */
+ } ;
+
+ err = errno ;
+ return ( (err == EAGAIN)
+ || (err == EWOULDBLOCK)
+ || (err == ECONNABORTED)
+ || (err == EINTR) ) ? -2 : -1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make socket for given family, type and protocol
+ *
+ * Returns: -1 : failed -- see errno
+ * otherwise : socket
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+sockunion_socket(sockunion su, int type, int protocol)
+{
+ int sock_fd ;
+ int err ;
- /* 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;
- }
- }
+ sock_fd = socket(sockunion_family(su), type, protocol);
- fcntl (fd, F_SETFL, val);
+ if (sock_fd >= 0)
+ return sock_fd ;
- return connect_in_progress;
+ err = errno ;
+ zlog_err("Cannot make socket family=%d, type=%d, protocol=%d: %s",
+ (int)sockunion_family(su), type, protocol, errtoa(err, 0).str) ;
+ errno = err ;
+ return -1;
}
-/* Make socket from sockunion union. */
-int
-sockunion_stream_socket (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Make socket for family from given sockunion, type=SOCK_STREAM, protocol=0.
+ *
+ * Returns: -1 : failed -- see errno
+ * otherwise : socket
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+sockunion_stream_socket (sockunion su)
{
- int sock;
-
if (su->sa.sa_family == 0)
su->sa.sa_family = AF_INET_UNION;
- sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
-
- if (sock < 0)
- zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket");
-
- return sock;
+ return sockunion_socket (su, SOCK_STREAM, 0);
}
-/* Bind socket to specified address. */
-int
-sockunion_bind (int sock, union sockunion *su, unsigned short port,
- union sockunion *su_addr)
+/*------------------------------------------------------------------------------
+ * Initiate a connection
+ *
+ * Reports EINPROGRESS as success.
+ *
+ * TODO: discover how the ifindex thing is supposed to work !!
+ *
+ * Returns: 0 : OK (so far so good)
+ * < 0 : failed -- see errno
+ *
+ * Logs a LOG_INFO message if fails.
+ */
+extern int
+sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port,
+ unsigned int ifindex)
{
- int size = 0;
- int ret;
+ union sockunion su ;
+ int ret, err ;
+ int sa_len ;
+
+ memcpy(&su, peer_su, sizeof(union sockunion)) ;
+
+ sa_len = sockunion_set_port(&su, port) ;
- if (su->sa.sa_family == AF_INET)
- {
- size = sizeof (struct sockaddr_in);
- su->sin.sin_port = htons (port);
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = size;
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- if (su_addr == NULL)
- su->sin.sin_addr.s_addr = htonl (INADDR_ANY);
- }
#ifdef HAVE_IPV6
- else if (su->sa.sa_family == AF_INET6)
+# ifdef KAME
+ if (su.sa.sa_family == AF_INET6)
{
- size = sizeof (struct sockaddr_in6);
- su->sin6.sin6_port = htons (port);
-#ifdef SIN6_LEN
- su->sin6.sin6_len = size;
-#endif /* SIN6_LEN */
- if (su_addr == NULL)
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
{
-#if defined(LINUX_IPV6) || defined(NRL)
- memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
-#else
- su->sin6.sin6_addr = in6addr_any;
-#endif /* LINUX_IPV6 */
+# ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
+ /* su.sin6.sin6_scope_id = ifindex; */
+# ifdef MUSICA
+ su.sin6.sin6_scope_id = ifindex;
+# endif
+# endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
+# ifndef MUSICA
+ SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
+# endif
}
- }
+ } ;
+# endif /* KAME */
#endif /* HAVE_IPV6 */
-
- ret = bind (sock, (struct sockaddr *)su, size);
- if (ret < 0)
- zlog (NULL, LOG_WARNING, "can't bind socket : %s", safe_strerror (errno));
+ ret = connect(sock_fd, &su.sa, sa_len) ;
+ err = (ret >= 0) ? 0 : errno ;
- return ret;
-}
+ if ((err == 0) || (err == EINPROGRESS))
+ return 0 ; /* instant success or EINPROGRESS as expected */
-int
-sockopt_reuseaddr (int sock)
-{
- int ret;
- int on = 1;
+ zlog_info("cannot connect to %s port %d socket %d: %s",
+ sutoa(&su).str, port, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
- (void *) &on, sizeof (on));
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock);
- return -1;
- }
- return 0;
-}
+ return ret ;
+} ;
-#ifdef SO_REUSEPORT
-int
-sockopt_reuseport (int sock)
+/*------------------------------------------------------------------------------
+ * Start listening on given socket
+ *
+ * Returns: >= 0 : OK (so far so good)
+ * < 0 : failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+sockunion_listen(int sock_fd, int backlog)
{
- int ret;
- int on = 1;
+ int ret ;
+
+ ret = listen(sock_fd, backlog) ;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
- (void *) &on, sizeof (on));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock);
- return -1;
- }
- return 0;
-}
-#else
-int
-sockopt_reuseport (int sock)
-{
- return 0;
-}
-#endif /* 0 */
+ int err = errno ;
+ zlog_err("cannot listen on socket %d: %s", sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
-int
-sockopt_ttl (int family, int sock, int ttl)
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Bind socket to address/port.
+ *
+ * If the 'any' parameter is true, sets the given sockunion to INADDR_ANY or
+ * the *socket* address family equivalent.
+ *
+ * Sets the given port into the sockunion su.
+ *
+ * For good measure, sets sin_len or family equivalent if required.
+ *
+ * If not 'any', and the given su does not have the same address family as the
+ * socket, then attempts to convert the su to the same family as the socket,
+ * by mapping or unmapping IPv4.
+ *
+ * Performs bind() and logs a LOG_WARNING message if fails.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ */
+extern int
+sockunion_bind(int sock_fd, sockunion su, unsigned short port, bool any)
{
- int ret;
+ int sa_len ;
+ int ret ;
+ int sock_family ;
-#ifdef IP_TTL
- if (family == AF_INET)
+ sock_family = sockunion_getsockfamily(sock_fd) ;
+
+ if (any)
{
- 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;
+ /* Create an "any" -- of same family as the socket */
+ sockunion_init_new(su, sock_family) ;
+ sockunion_set_addr_any(su) ;
}
-#endif /* IP_TTL */
-#ifdef HAVE_IPV6
- if (family == AF_INET6)
+ else
{
- 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;
-}
-
-int
-sockopt_cork (int sock, int onoff)
-{
-#ifdef TCP_CORK
- return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
-#else
- return 0;
+ /* Want to bind to a specific address.
+ *
+ * We provide bind with an address which matches the address family of
+ * the *socket*.
+ *
+ * If the socket is AF_INET, address may be AF_INET, or an AF_INET6
+ * *provided* it is an IPv4 mapped address.
+ *
+ * If the socket is AF_INET6, address may be AF_INET or AF_NET6, and
+ * will map any IPv4 address.
+ *
+ * If we don't HAVE_IPV6, or we don't recognise an address family,
+ * then do nothing and let bind() return some sort of error.
+ */
+#ifdef HAVE_IPV6
+ if (sock_family != sockunion_family(su))
+ {
+ switch (sock_family)
+ {
+ case AF_INET:
+ sockunion_unmap_ipv4(su) ; /* unmap if AF_INET6 mapped IPv4 */
+ break ;
+
+ case AF_INET6:
+ sockunion_map_ipv4(su) ; /* map if AF_INET */
+ break ;
+
+ default:
+ break ;
+ } ;
+ } ;
#endif
-}
+ } ;
-int
-sockopt_minttl (int family, int sock, int minttl)
-{
-#ifdef IP_MINTTL
- if (family == AF_INET)
- {
- int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));
- if (ret < 0)
- zlog (NULL, LOG_WARNING,
- "can't set sockopt IP_MINTTL to %d on socket %d: %s",
- minttl, sock, safe_strerror (errno));
- return ret;
- }
-#endif /* IP_MINTTL */
-#ifdef IPV6_MINHOPCNT
- if (family == AF_INET6)
+ sa_len = sockunion_set_port(su, port) ;
+
+ ret = bind (sock_fd, &su->sa, sa_len);
+ if (ret < 0)
{
- int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCNT, &minttl, sizeof(minttl));
- if (ret < 0)
- zlog (NULL, LOG_WARNING,
- "can't set sockopt IPV6_MINHOPCNT to %d on socket %d: %s",
- minttl, sock, safe_strerror (errno));
- return ret;
- }
-#endif
+ int err = errno ;
+ zlog_warn("cannot bind to %s port %d socket %d: %s",
+ sutoa(su).str, port, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
- errno = EOPNOTSUPP;
- return -1;
+ return ret;
}
-/* If same family and same prefix return 1. */
-int
+/*------------------------------------------------------------------------------
+ * If same (known) family and same prefix return 1, otherwise return 0.
+ *
+ * Returns 0 if same family, but not a known family.
+ */
+extern int
sockunion_same (union sockunion *su1, union sockunion *su2)
{
int ret = 0;
@@ -577,126 +682,174 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
return 0;
switch (su1->sa.sa_family)
- {
+ {
case AF_INET:
- ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr,
- sizeof (struct in_addr));
- break;
+ return (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr) ;
+
#ifdef HAVE_IPV6
case AF_INET6:
ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
- sizeof (struct in6_addr));
- break;
+ sizeof (struct in6_addr));
+ return (ret == 0) ;
#endif /* HAVE_IPV6 */
- }
- if (ret == 0)
- return 1;
- else
- return 0;
-}
-/* After TCP connection is established. Get local address and port. */
-union sockunion *
-sockunion_getsockname (int fd)
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get local (getsockname) or remote (getpeername) address and port.
+ *
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * If "unmap": if address is an IPv4 mapped IPv6 address, returns AF_INET.
+ *
+ * NB: returns EAFNOSUPPORT if don't recognise the address family.
+ *
+ * Logs a LOG_ERR message if fails in getsockname/getpeername.
+ */
+static int
+sockunion_get_name(int sock_fd, union sockunion* su, bool local, bool unmap)
{
- int ret;
- socklen_t len;
+ int ret ;
+ socklen_t len ;
union
{
- struct sockaddr sa;
- struct sockaddr_in sin;
-#ifdef HAVE_IPV6
- struct sockaddr_in6 sin6;
-#endif /* HAVE_IPV6 */
+ union sockunion su ;
char tmp_buffer[128];
- } name;
- union sockunion *su;
+ } name ;
+
+ memset(su, 0, sizeof(union sockunion)) ;
+
+ confirm(AF_UNSPEC == 0) ;
- memset (&name, 0, sizeof name);
- len = sizeof name;
+ if (sock_fd < 0)
+ return AF_UNSPEC ;
+
+ len = sizeof(name) ;
+ memset(&name, 0, len);
+
+ if (local)
+ ret = getsockname(sock_fd, &name.su.sa, &len) ;
+ else
+ ret = getpeername(sock_fd, &name.su.sa, &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;
+ int err = errno ;
+ zlog_err("failed in %s for socket %d: %s",
+ local ? "getsockname" : "getpeername",
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
}
-
- if (name.sa.sa_family == AF_INET)
+ else
{
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in));
- return su;
- }
+ ret = name.su.sa.sa_family ;
+
+ switch (ret)
+ {
+ case AF_INET:
+ su->sin = name.su.sin ;
+ 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:
+ su->sin6 = name.su.sin6 ;
+ if (unmap)
+ sockunion_unmap_ipv4(su) ;
+ break ;
#endif /* HAVE_IPV6 */
- return NULL;
-}
-/* After TCP connection is established. Get remote address and port. */
-union sockunion *
-sockunion_getpeername (int fd)
+ default:
+ errno = EAFNOSUPPORT ;
+ ret = -1 ;
+ } ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the address family the given socket is set to.
+ *
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * NB: gets the actual address family -- does NOT look for mapped IPv4.
+ */
+extern int
+sockunion_getsockfamily(int sock_fd)
{
- 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;
+ union sockunion su[1] ;
+ int ret ;
- 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;
- }
+ ret = sockunion_get_name(sock_fd, su, true, /* true => local */
+ false) ; /* false => don't unmap */
+ return (ret >= 0) ? sockunion_family(su) : ret ;
+} ;
- 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;
-}
+/*------------------------------------------------------------------------------
+ * Get the address family the given socket's protocol is set to.
+ *
+ * If this is an AF_INET, that's easy.
+ *
+ * If this is an AF_INET6, then needs to look out for IN6_IS_ADDR_V4MAPPED.
+ *
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * NB: gets the underlying address family -- ie: looks for mapped IPv4.
+ */
+extern int
+sockunion_getprotofamily(int sock_fd)
+{
+ union sockunion su[1] ;
+ int ret ;
+
+ ret = sockunion_get_name(sock_fd, su, true, /* true => local */
+ true) ; /* true => unmap */
+ return (ret >= 0) ? sockunion_family(su) : ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get local address and port -- ie getsockname(), except unmaps IPv4 mapped.
+ *
+ * See: sockunion_get_name()
+ */
+extern int
+sockunion_getsockname(int sock_fd, sockunion su_local)
+{
+ return sockunion_get_name(sock_fd, su_local, true, /* true => local */
+ true) ; /* true => unmap */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get remote address and port -- ie getpeername(), except unmaps IPv4 mapped.
+ *
+ * See: sockunion_get_name()
+ */
+extern int
+sockunion_getpeername (int sock_fd, sockunion su_remote)
+{
+ return sockunion_get_name(sock_fd, su_remote, false, /* false => remote */
+ true) ; /* true => unmap */
+} ;
-/* Print sockunion structure */
+/*------------------------------------------------------------------------------
+ * Print sockunion structure to stdout
+ */
static void __attribute__ ((unused))
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));
+ printf ("%s\n", safe_inet_ntoa (su->sin.sin_addr));
break;
#ifdef HAVE_IPV6
case AF_INET6:
@@ -725,27 +878,15 @@ sockunion_print (union sockunion *su)
}
}
-#ifdef HAVE_IPV6
-static int
-in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2)
-{
- unsigned int i;
- u_char *p1, *p2;
-
- p1 = (u_char *)addr1;
- p2 = (u_char *)addr2;
-
- for (i = 0; i < sizeof (struct in6_addr); i++)
- {
- if (p1[i] > p2[i])
- return 1;
- else if (p1[i] < p2[i])
- return -1;
- }
- return 0;
-}
-#endif /* HAVE_IPV6 */
-
+/*------------------------------------------------------------------------------
+ * Compare two sockunion values
+ *
+ * Compares family values first, then the body of the address.
+ *
+ * Returns: +1 => su1 > su2
+ * 0 => su1 == su2 (or same, but unknown, family)
+ * -1 => su1 < su2
+ */
int
sockunion_cmp (union sockunion *su1, union sockunion *su2)
{
@@ -754,23 +895,33 @@ sockunion_cmp (union sockunion *su1, union sockunion *su2)
if (su1->sa.sa_family < su2->sa.sa_family)
return -1;
- if (su1->sa.sa_family == AF_INET)
- {
- if (ntohl (su1->sin.sin_addr.s_addr) == ntohl (su2->sin.sin_addr.s_addr))
- return 0;
- if (ntohl (su1->sin.sin_addr.s_addr) > ntohl (su2->sin.sin_addr.s_addr))
- return 1;
+ switch (su1->sa.sa_family)
+ {
+ case AF_INET:
+ if (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr)
+ return 0;
+ if (ntohl(su1->sin.sin_addr.s_addr) > ntohl(su2->sin.sin_addr.s_addr))
+ return +1;
else
return -1;
- }
+
#ifdef HAVE_IPV6
- if (su1->sa.sa_family == AF_INET6)
- return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr);
+ case AF_INET6:
+ return memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
+ sizeof(struct in6_addr)) ;
#endif /* HAVE_IPV6 */
- return 0;
+
+ default:
+ return 0 ;
+ } ;
}
-/* Duplicate sockunion. */
+/*------------------------------------------------------------------------------
+ * Create copy of existing sockunion.
+ *
+ * It is the caller's responsibility to free the sockunion at some point --see
+ * sockunion_free()
+ */
union sockunion *
sockunion_dup (union sockunion *su)
{
@@ -779,8 +930,188 @@ sockunion_dup (union sockunion *su)
return dup;
}
+/*------------------------------------------------------------------------------
+ * Copy one sockunion to another
+ */
+extern void
+sockunion_copy (sockunion dst, sockunion src)
+{
+ memcpy (dst, src, sizeof(*dst)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free given sockunion (if any).
+ */
void
sockunion_free (union sockunion *su)
{
- XFREE (MTYPE_SOCKUNION, su);
+ if (su != NULL)
+ XFREE (MTYPE_SOCKUNION, su);
+}
+
+/*==============================================================================
+ * Sockunion reference utilities
+ */
+
+/*------------------------------------------------------------------------------
+ * Set sockunion from given prefix -- allocate new sockunion, if required.
+ *
+ * It is the caller's responsibility to free the sockunion at some point.
+ * (See sockunion_free() or sockunion_unset().)
+ *
+ * For unknown family, returns an empty sockunion of that family.
+ */
+extern sockunion
+sockunion_new_prefix(sockunion su, struct prefix* p)
+{
+ sa_family_t family ;
+
+ family = (p != NULL) ? p->family : 0 ;
+
+ su = sockunion_init_new(su, family) ;
+
+ switch (family)
+ {
+ case 0:
+ break ;
+
+ case AF_INET:
+ su->sin.sin_addr = p->u.prefix4 ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ su->sin6.sin6_addr = p->u.prefix6 ;
+ break ;
+#endif
+
+ default:
+ break ;
+ } ;
+
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new sockunion from given sockaddr -- taking only the address part
+ *
+ * It is the caller's responsibility to free the sockunion at some point.
+ * (See sockunion_free() or sockunion_unset().)
+ *
+ * For unknown family, returns an empty sockunion of that family.
+ */
+extern sockunion
+sockunion_new_sockaddr(sockunion su, struct sockaddr* sa)
+{
+ sa_family_t family ;
+
+ family = (sa != NULL) ? sa->sa_family : AF_UNSPEC ;
+
+ su = sockunion_init_new(su, family) ;
+
+ switch (family)
+ {
+ case AF_UNSPEC:
+ break ;
+
+ case AF_INET:
+ su->sin.sin_addr = ((struct sockaddr_in*)sa)->sin_addr ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ su->sin6.sin6_addr = ((struct sockaddr_in6*)sa)->sin6_addr ;
+ break ;
+#endif
+
+ default:
+ break ;
+ } ;
+
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unset pointer to sockunion -- free any sockunion referenced
+ *
+ * Does nothing if there is no sockunion
+ */
+extern void
+sockunion_unset(sockunion* p_su)
+{
+ if (*p_su != NULL)
+ XFREE(MTYPE_SOCKUNION, *p_su) ; /* sets *p_su NULL */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to sockunion (if any)
+ *
+ * Frees any existing sockunion at the destination.
+ *
+ * NB: copies the source pointer -- so must be clear about responsibility
+ * for the sockunion.
+ */
+extern void
+sockunion_set(sockunion* p_dst, sockunion su)
+{
+ sockunion_unset(p_dst) ;
+ *p_dst = su ;
}
+
+/*------------------------------------------------------------------------------
+ * Set pointer to a *copy* of the given sockunion
+ *
+ * Frees any existing sockunion at the destination.
+ *
+ * NB: copies the source pointer -- so must be clear about responsibility
+ * for the sockunion structure.
+ */
+extern void
+sockunion_set_dup(sockunion* p_dst, sockunion su)
+{
+ sockunion_set(p_dst, sockunion_dup(su)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to sockunion (if any) and unset source pointer.
+ *
+ * Frees any existing sockunion at the destination.
+ *
+ * NB: responsibility for the sockunion passes to the destination.
+ */
+extern void
+sockunion_set_mov(sockunion* p_dst, sockunion* p_src)
+{
+ sockunion_unset(p_dst) ;
+ *p_dst = *p_src ;
+ *p_src = NULL ;
+} ;
+
+/*==============================================================================
+ * 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 0ee2d63b..eeae72d5 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -17,12 +17,18 @@
* 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 "zebra.h"
+#include <stdbool.h>
+#include "symtab.h"
+#include "prefix.h"
+#include "memory.h"
+
#if 0
union sockunion {
struct sockinet {
@@ -38,7 +44,10 @@ union sockunion {
#define su_port su_si.si_port
#endif /* 0 */
-union sockunion
+typedef struct prefix* prefix ;
+
+typedef union sockunion* sockunion ;
+union sockunion
{
struct sockaddr sa;
struct sockaddr_in sin;
@@ -47,13 +56,6 @@ union sockunion
#endif /* HAVE_IPV6 */
};
-enum connect_result
-{
- connect_error,
- connect_success,
- connect_in_progress
-};
-
/* Default address family. */
#ifdef HAVE_IPV6
#define AF_INET_UNION AF_INET6
@@ -61,9 +63,21 @@ enum connect_result
#define AF_INET_UNION AF_INET
#endif
-/* Sockunion address string length. Same as INET6_ADDRSTRLEN. */
+/* Sockunion address string length. Accommodate either IPv4 or IPv6. */
#define SU_ADDRSTRLEN 46
+CONFIRM(SU_ADDRSTRLEN >= INET_ADDRSTRLEN) ;
+#if HAVE_IPV6
+CONFIRM(SU_ADDRSTRLEN >= INET6_ADDRSTRLEN) ;
+#endif
+
+/* Sockunion String Object */
+typedef struct sockunion_string sockunion_string_t ;
+struct sockunion_string
+{
+ char str[SU_ADDRSTRLEN] ;
+};
+
/* Macro to set link local index to the IPv6 address. For KAME IPv6
stack. */
#ifdef KAME
@@ -87,35 +101,47 @@ enum connect_result
#define sockunion_family(X) (X)->sa.sa_family
/* Prototypes. */
-extern int str2sockunion (const char *, union sockunion *);
-extern const char *sockunion2str (union sockunion *, char *, size_t);
-extern int sockunion_cmp (union sockunion *, union sockunion *);
-extern int sockunion_same (union sockunion *, union sockunion *);
-
-extern char *sockunion_su2str (union sockunion *su);
-extern union sockunion *sockunion_str2su (const char *str);
-extern struct in_addr sockunion_get_in_addr (union sockunion *su);
-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 *,
- unsigned short, union sockunion *);
-extern int sockopt_ttl (int family, int sock, int ttl);
-extern int sockopt_minttl (int family, int sock, int minttl);
-extern int sockopt_cork (int sock, int onoff);
-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 union sockunion *sockunion_dup (union sockunion *);
-extern void sockunion_free (union sockunion *);
+extern sockunion sockunion_init_new(sockunion su, sa_family_t family) ;
+extern int sockunion_get_len(sockunion su) ;
+extern int sockunion_set_port(sockunion su, in_port_t port) ;
+extern int str2sockunion (const char * str, sockunion su);
+extern const char *sockunion2str (sockunion su, char* buf, size_t size);
+extern sockunion_string_t sutoa(sockunion su) ;
+extern int sockunion_cmp (sockunion su1, sockunion su2);
+extern int sockunion_same (sockunion su1, sockunion su2);
+
+extern char* sockunion_su2str (sockunion su, enum MTYPE type) ;
+extern sockunion sockunion_str2su (const char *str);
+extern struct in_addr sockunion_get_in_addr (sockunion su);
+extern int sockunion_accept (int sock_fd, sockunion su);
+extern int sockunion_stream_socket (sockunion su);
+extern int sockunion_bind (int sock_fd, sockunion su,
+ unsigned short port, bool any) ;
+extern int sockunion_socket (sockunion su, int type, int protocol) ;
+extern int sockunion_connect (int sock_fd, sockunion su,
+ unsigned short port, unsigned int ifindex) ;
+extern int sockunion_listen(int sock_fd, int backlog) ;
+
+extern int sockunion_getsockfamily(int sock_fd) ;
+extern int sockunion_getprotofamily(int sock_fd) ;
+extern int sockunion_getsockname (int sock_fd, sockunion su);
+extern int sockunion_getpeername (int sock_fd, sockunion su);
+extern void sockunion_unmap_ipv4 (sockunion su) ;
+extern void sockunion_map_ipv4 (sockunion su) ;
+
+extern sockunion sockunion_dup (sockunion src);
+extern void sockunion_copy (sockunion dst, sockunion src) ;
+extern void sockunion_free (sockunion su);
+
+extern sockunion sockunion_new_prefix(sockunion su, prefix p) ;
+extern sockunion sockunion_new_sockaddr(sockunion su, struct sockaddr* sa) ;
+extern void sockunion_unset(sockunion* p_su) ;
+extern void sockunion_set(sockunion* p_dst, sockunion su) ;
+extern void sockunion_set_dup(sockunion* p_dst, sockunion su) ;
+extern void sockunion_set_mov(sockunion* p_dst, sockunion* p_src) ;
#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 */
@@ -127,4 +153,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..1fce7103 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;
+ size_t 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)
{
@@ -193,6 +215,13 @@ stream_get_endp (struct stream *s)
}
size_t
+stream_get_left (struct stream *s)
+{
+ STREAM_VERIFY_SANE(s);
+ return s->endp - s->getp ;
+}
+
+size_t
stream_get_size (struct stream *s)
{
STREAM_VERIFY_SANE(s);
@@ -204,7 +233,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");
@@ -214,18 +243,32 @@ stream_set_getp (struct stream *s, size_t pos)
s->getp = pos;
}
+void
+stream_set_endp (struct stream *s, size_t pos)
+{
+ STREAM_VERIFY_SANE(s);
+
+ if (!ENDP_VALID (s, pos))
+ {
+ STREAM_BOUND_WARN (s, "set endp");
+ return ;
+ }
+
+ s->endp = pos;
+}
+
/* Forward pointer. */
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 +276,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 +307,7 @@ u_char
stream_getc (struct stream *s)
{
u_char c;
-
+
STREAM_VERIFY_SANE (s);
if (STREAM_READABLE(s) < sizeof (u_char))
@@ -273,7 +316,7 @@ stream_getc (struct stream *s)
return 0;
}
c = s->data[s->getp++];
-
+
return c;
}
@@ -284,15 +327,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 +352,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 +366,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 +386,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 +407,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 +429,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 +454,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 +480,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 +506,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 +528,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 +550,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 +568,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 +588,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 +605,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 +621,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 +638,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 +648,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 +656,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 +670,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 +679,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 +696,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 +714,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 +739,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 +760,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 +776,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");
@@ -760,21 +853,21 @@ stream_read_try(struct stream *s, int fd, size_t size)
/* Error: was it transient (return -2) or fatal (return -1)? */
if (ERRNO_IO_RETRY(errno))
return -2;
- zlog_warn("%s: read failed on fd %d: %s", __func__, fd, safe_strerror(errno));
+ zlog_warn("%s: read failed on fd %d: %s", __func__, fd, errtoa(errno, 0).str);
return -1;
}
/* 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 +876,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;
@@ -792,7 +885,7 @@ stream_recvfrom (struct stream *s, int fd, size_t size, int flags,
/* Error: was it transient (return -2) or fatal (return -1)? */
if (ERRNO_IO_RETRY(errno))
return -2;
- zlog_warn("%s: read failed on fd %d: %s", __func__, fd, safe_strerror(errno));
+ zlog_warn("%s: read failed on fd %d: %s", __func__, fd, errtoa(errno, 0).str);
return -1;
}
@@ -802,15 +895,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 +911,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 +932,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 +975,57 @@ 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;
}
-
+
+/*------------------------------------------------------------------------------
+ * 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(((uint8_t*)p + have) <= (uint8_t*)limit) ;
+
+ memcpy(p, s->data, have) ;
+
+ s->getp = s->endp = 0;
+
+ return (uint8_t*)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 +1038,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 +1049,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. */
@@ -949,6 +1073,13 @@ stream_fifo_head (struct stream_fifo *fifo)
}
void
+stream_fifo_reset (struct stream_fifo *fifo)
+{
+ fifo->head = fifo->tail = NULL;
+ fifo->count = 0;
+}
+
+void
stream_fifo_clean (struct stream_fifo *fifo)
{
struct stream *s;
diff --git a/lib/stream.h b/lib/stream.h
index 3e4ba7b4..e7303652 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,13 +139,16 @@ 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 *);
+extern size_t stream_get_left (struct stream *s) ;
extern size_t stream_get_size (struct stream *);
extern u_char *stream_get_data (struct stream *);
extern void stream_set_getp (struct stream *, size_t);
+extern void stream_set_endp (struct stream *, size_t);
extern void stream_forward_getp (struct stream *, size_t);
extern void stream_forward_endp (struct stream *, size_t);
@@ -177,7 +180,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 +188,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 +202,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 +212,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 *);
@@ -215,6 +224,7 @@ extern struct stream_fifo *stream_fifo_new (void);
extern void stream_fifo_push (struct stream_fifo *fifo, struct stream *s);
extern struct stream *stream_fifo_pop (struct stream_fifo *fifo);
extern struct stream *stream_fifo_head (struct stream_fifo *fifo);
+extern void stream_fifo_reset (struct stream_fifo *fifo);
extern void stream_fifo_clean (struct stream_fifo *fifo);
extern void stream_fifo_free (struct stream_fifo *fifo);
diff --git a/lib/symtab.c b/lib/symtab.c
new file mode 100644
index 00000000..57a49396
--- /dev/null
+++ b/lib/symtab.c
@@ -0,0 +1,1186 @@
+/* Symbol Table data structure -- 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 <stddef.h>
+
+#include "symtab.h"
+#include "memory.h"
+
+/* A symbol table maps symbol "names" to symbol values and, for each symbol,
+ * has two ways of keeping track of references to the symbol.
+ *
+ * The symbol name can be an arbitrary collection of bytes, or a string. All
+ * names in a symbol table are unique.
+ *
+ * The symbol value is a void* -- whose contents are no concern of this code.
+ *
+ * A symbol table comprises:
+ *
+ * * symbol table structure -- containing all "red-tape"
+ * * array of chain-bases -- for the hash table
+ * * symbol entries -- each containing name and value of a symbol
+ *
+ * The symbol table structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the symbol table operations
+ * require the address of the symbol table structure -- see typedef for
+ * symbol_table.
+ *
+ * A symbol table may point to its "parent" -- which could be an enclosing
+ * structure, or any other higher level data. The symbol table code does not
+ * use or need this -- it is for the convenience of the caller.
+ *
+ * A symbol table structure which is zeroised is implicitly an empty symbol
+ * table, using the default symbol name type -- a null terminated string.
+ *
+ * Each Symbol Table requires a hash function, which takes a pointer to
+ * whatever and returns a 32-bit hash value and a canonical form of the
+ * symbol "name". When a new symbol is created the canonical form of the name
+ * is copied to the symbol entry.
+ *
+ * The array of chain-bases is dynamically allocated and will grow to maintain
+ * an approximate given maximum number of symbols per chain base. This density
+ * is set when the symbol table is initialised. The array does not
+ * automatically reduce in size.
+ *
+ * The number of chain bases is always odd. The hash function returns a 32-bit
+ * unsigned value, which is mapped to the chain bases modulo their number.
+ * Since that is odd, it is co-prime with the hash, so contributes to the hash
+ * process.
+ *
+ * Each symbol in the table has a dynamically allocated entry, which includes:
+ *
+ * * symbol value -- void*
+ * * symbol name
+ * * count of references
+ * * list of references
+ *
+ * Symbols may have the value NULL. Deleting a symbol is not the same as
+ * setting its value to NULL. If the value is set NULL and later set to
+ * something else, all references to the symbol will see the new value. If the
+ * symbol is deleted, all references will see its value set to NULL, but if a
+ * new symbol with the same name is later created, all references to the old
+ * symbol are unchanged, and continue to see the (orphan) NULL.
+ *
+ * Keeping track of references is important because it ensures that symbols
+ * can be deleted without leaving dangling references. When a symbol is
+ * deleted, it is removed from the symbol table and its value is set to NULL.
+ * If there are no references to the symbol, the symbol entry is released.
+ * If there are references to the symbol, it is preserved (as an orphan) until
+ * all references are unset. The value of an orphaned symbol should not be
+ * changed (but may be unset, since it is already unset).
+ *
+ * There are two, parallel mechanisms for keeping track of references to a
+ * symbol:
+ *
+ * 1. reference count -- this is for when all that is required is a pointer
+ * to the symbol, ie:
+ *
+ * * ptr_to_symbol = symbol_inc_ref(sym) -- set reference & count up
+ * * ptr_to_symbol = symbol_dec_ref(sym) -- unset reference & count down
+ *
+ * 2. list of references -- this is for when it is useful to visit all
+ * references to a given symbol, for example when the value of the symbol
+ * is set and all users of the symbol need to adjust to the new state.
+ *
+ * A symbol reference contains a pointer to the symbol, and lives on the
+ * list of references to the symbol. When the value is set, a call-back
+ * associated with the table is called (if it is set). That call-back may
+ * walk the list of references to the symbol. Each reference contains a
+ * pointer and a pointer/long int value, which may be used to identify the
+ * reference.
+ *
+ * A symbol reference may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the symbol reference
+ * operations require the address of the symbol reference structure -- see
+ * typedef for symbol_ref.
+ *
+ * Symbol references are the responsibility of of the owner of the reference,
+ * who must look after setting, unsetting and (if required) releasing them.
+ *
+ * It may seem profligate to have two mechanisms. However, it is simpler than
+ * having two types of symbol, or two types of symbol table. It may also be
+ * useful to have both simple references and references for dealing with
+ * value changes. Suppose, for instance, that the value of a symbol has a
+ * pointer to the symbol -- so that the value of the symbol can refer back to
+ * its name, for example. This requires only a simple reference, even if
+ * more generally a list of references is required.
+ *
+ * A symbol table can be walked to visit all symbols, and there is support for
+ * extracting a vector of all symbols which satisfy a given criterion.
+ */
+
+static void symbol_extend_bases(symbol_table table) ;
+static void symbol_free(symbol sym) ;
+
+/* Return true iff there are no references to the given symbol */
+inline static int
+symbol_no_references(symbol sym)
+{
+ return (sym->ref_count == 0) && (sym->ref_list == NULL) ;
+} ;
+
+/* If symbol is an orphan with no references/bookmarks, free it.
+ *
+ * NB: if symbol is an orphan, then it implicitly has a NULL value, because
+ * to become an orphan it must have been deleted, which unsets the value.
+ */
+inline static void
+symbol_free_if_redundant(symbol sym)
+{
+ if ((sym->table == NULL) && symbol_no_references(sym))
+ symbol_free(sym) ;
+} ;
+
+/* Return chain base for given hash value. */
+inline static symbol*
+symbol_base(symbol_table table, u_int32_t hash)
+{
+ return &table->bases[hash % table->base_count] ;
+} ;
+
+/* Initialise a new symbol table -- allocate if required.
+ *
+ * table -- address of table to initialise. NULL -> allocate.
+ * NB: if allocated, it is the caller's responsibility to free.
+ *
+ * parent -- address of some parent or other higher level data structure.
+ * This is not used by the symbol table code and may be NULL if
+ * the caller has no use for it.
+ *
+ * bases -- number of list bases to start the symbol table at.
+ * Symbol table grows as required, but can set initial size if
+ * have some expectations and wish to avoid growth steps.
+ *
+ * density -- %-age of entries/bases. 0 => use default.
+ *
+ * hash_function
+ * -- function to fill in symbol_hash from a given "name".
+ * see: description of struct symbol_hash and default function,
+ * symbol_hash_string, below.
+ * NULL => the names in this symbol table are null terminated
+ * strings, so use symbol_hash_string.
+ *
+ * value_call_back
+ * -- function to be called when the symbol value is set or unset.
+ * (Or the symbol is about to be deleted -- which starts by
+ * unsetting it).
+ *
+ * The function is passed the new state of the symbol and its
+ * previous value.
+ *
+ * The value of the symbol is a pointer to some data. If the
+ * data changes symbol_set_value() must be called if its
+ * address changes and may be called even if its address doesn't
+ * change.
+ *
+ * In any event, value_call_back is called when symbol_set_value()
+ * is called -- except where the symbol is being set to NULL and
+ * the value is already NULL.
+ *
+ * During the call-back the symbol may be set again or unset,
+ * which will cause the call-back to be called inside itself.
+ * Also, references may be set or unset.
+ *
+ * In the call-back the reference list may be walked to signal
+ * to all holders of the reference that something has changed.
+ *
+ * NB: A completely zeroized symbol_table structure is a valid, empty
+ * symbol table. The first time it is used it will be set up to default
+ * state -- in particular: symbol names are null terminated strings (a
+ * state which *cannot* then be changed).
+ *
+ * NB: when this is used to re-initialising an existing symbol table structure,
+ * any existing chain base array, symbols and symbol references are simply
+ * discarded -- which will leak memory and is probably a mistake.
+ */
+symbol_table
+symbol_table_init_new(symbol_table table,
+ void* parent,
+ unsigned int base_count,
+ unsigned int density,
+ symbol_hash_function* hash_function,
+ symbol_call_back_function* value_call_back)
+{
+ assert(base_count <= SYMBOL_TABLE_BASES_MAX) ;
+
+ if (table == NULL)
+ table = XCALLOC(MTYPE_SYMBOL_TABLE, sizeof (struct symbol_table)) ;
+
+ table->parent = parent ;
+
+ table->bases = NULL ; /* Allocated when required */
+ table->base_count = base_count ;
+
+ table->entry_count = 0 ;
+ table->extend_thresh = density ; /* Fixed up when required */
+
+ table->hash_function = hash_function ;
+ symbol_table_set_value_call_back(table, value_call_back) ;
+
+ return table ;
+} ;
+
+/* Set "parent" of symbol table. */
+void
+symbol_table_set_parent(symbol_table table, void* parent)
+{
+ table->parent = parent ;
+} ;
+
+/* Get "parent" of symbol table. */
+void*
+symbol_table_get_parent(symbol_table table)
+{
+ return table->parent ;
+} ;
+
+/* Set the value_call_back */
+void
+symbol_table_set_value_call_back(symbol_table table,
+ symbol_call_back_function* value_call_back)
+{
+ table->value_call_back = value_call_back ;
+} ;
+
+/* Create and set new chain bases and threshold for next extension. */
+/* */
+/* Ensures that the base count is at least the minimum and is odd, */
+/* and returns the value set. */
+static unsigned int
+symbol_table_new_bases(symbol_table table,
+ unsigned int new_base_count, float density)
+{
+ if (new_base_count < SYMBOL_TABLE_BASES_MIN)
+ new_base_count = SYMBOL_TABLE_BASES_MIN ;
+ new_base_count |= 1 ;
+
+ table->bases = XCALLOC(MTYPE_SYMBOL_BASES, new_base_count * sizeof(symbol)) ;
+ table->extend_thresh = new_base_count * density ;
+ return table->base_count = new_base_count ;
+} ;
+
+/* Setup symbol table body for use.
+ *
+ * Used for "lazy" allocation of chain bases and allows symbol_lookup
+ * to operate on a completely zeroized symbol_table structure.
+ */
+static void
+symbol_table_setup(symbol_table table)
+{
+ float density ;
+
+ /* If density was set explicitly, extend_thresh entry is a %age. */
+
+ if (table->extend_thresh != 0)
+ density = (float)table->extend_thresh / (float)100 ;
+ else
+ density = (float)2 ; /* Default density */
+
+ /* Initialise the chain bases -- enforces minimum base_count and odd-ness */
+ symbol_table_new_bases(table, table->base_count, density) ;
+
+ /* make default hash_function explicit. */
+ if (table->hash_function == NULL)
+ table->hash_function = (symbol_hash_function*)symbol_hash_string ;
+} ;
+
+/* Reset symbol table.
+ *
+ * Free the symbol table body, and free the symbol table structure or reset it.
+ *
+ * Return NULL if frees symbol table structure, otherwise the address of same.
+ *
+ * NB: must only be done when the table is empty -- see assertion !
+ */
+symbol_table
+symbol_table_reset(symbol_table table, int free_structure)
+{
+ if (table== NULL)
+ return NULL ; /* allow for already freed table */
+
+ assert(table->entry_count == 0) ;
+
+ if (table->bases)
+ XFREE(MTYPE_SYMBOL_BASES, table->bases);
+
+ if (free_structure)
+ {
+ XFREE(MTYPE_VECTOR, table) ;
+ return NULL ;
+ }
+ else
+ return memset(table, 0, sizeof(struct symbol_table)) ;
+} ;
+
+/* Remove symbol from its symbol table (if any). */
+
+static void
+symbol_remove(symbol sym)
+{
+ symbol_table table ;
+ symbol* base ;
+ symbol prev ;
+
+ table = sym->table ;
+ if (table != NULL) /* Deleted symbols have no parent table. */
+ {
+ assert(table->entry_count != 0) ;
+
+ base = symbol_base(table, sym->hash) ;
+ if (*base == sym)
+ *base = sym->next ;
+ else
+ {
+ prev = *base ;
+ while (prev->next != sym)
+ prev = prev->next ;
+ prev->next = sym->next ;
+ } ;
+
+ sym->table = NULL ; /* Symbol is now an orphan. */
+ --table->entry_count ;
+ } ;
+} ;
+
+/* Free symbol, removing it from the symbol table.
+ *
+ * NB: the value and all references MUST already have been unset, because:
+ *
+ * * any value may well need to be released, and have no idea how to do
+ * that here.
+ *
+ * * similarly, references may need to be released and should not, in
+ * any case, be left dangling.
+ */
+static void
+symbol_free(symbol sym)
+{
+ assert((sym->value == NULL) && symbol_no_references(sym)) ;
+
+ symbol_remove(sym) ; /* Remove from table, if any. */
+
+ XFREE(MTYPE_SYMBOL, sym) ;
+} ;
+
+/* Ream out symbols.
+ *
+ * Delete symbols -- but do not invoke the value_call_back.
+ *
+ * When the table is (or becomes) empty, the chain bases are freed, and the
+ * structure freed or reset (depending on the free_structure argument).
+ *
+ * This is intended for use when the symbol table is being destroyed, and all
+ * references have been, or will be unset.
+ *
+ * Returns the value of the next non-NULL symbol (if any). So may be used, for
+ * example:
+ *
+ * xxxx* val ;
+ * ...
+ * while ((val = symbol_table_ream(table, free_structure)) != NULL)
+ * {
+ * ... do what's required to release the value ...
+ * }
+ *
+ * Noting that the symbol may already have been released when its value is
+ * returned. (If the symbol is required when the value is released, then the
+ * value should hold a simple reference to the symbol.)
+ *
+ * Returns NULL when the table is empty.
+ *
+ * Symbols which have one or more references when they are deleted are left as
+ * orphans, which will be freed when all their references are unset.
+ *
+ * NB: do NOT attempt to do anything else with the symbol table once reaming
+ * has started.
+ *
+ * NB: it is the caller's responsibility to unset all references and release
+ * any that need to be released -- either before or after this operation.
+ */
+void*
+symbol_table_ream(symbol_table table, int free_structure)
+{
+ void* value ;
+ symbol sym ;
+ unsigned int i ;
+
+ /* There are no actual bases until they have been allocated. */
+ i = (table->bases != NULL) ? table->base_count : 0 ;
+
+ while (i--)
+ {
+ while ((sym = table->bases[i]) != NULL)
+ {
+ assert(table->entry_count != 0) ;
+
+ /* the following is effectively symbol_delete, but avoids the */
+ /* value_call_back and returns only if the value is not NULL. */
+
+ table->bases[i] = sym->next ; /* remove from table */
+ --table->entry_count ; /* count down */
+
+ sym->table = NULL ; /* orphan symbol */
+ value = sym->value ; /* pick up value. */
+ sym->value = NULL ; /* and set symbol undefined */
+
+ if (symbol_no_references(sym))
+ symbol_free(sym) ; /* not in table, no value, no references */
+
+ if (value != NULL)
+ {
+ table->base_count = i + 1 ; /* where we've got to */
+ return value ; /* <<< RETURN: caller must deal with value */
+ } ;
+ } ;
+ } ;
+
+ symbol_table_reset(table, free_structure) ;
+ /* asserts(table->entry_count == 0) */
+ return NULL ;
+} ;
+
+/* Look-up name in given symbol table. Add if required.
+ *
+ * Returns NULL if not found and not required to add.
+ *
+ * NB: the name argument is passed to the symbol table's hash function. That
+ * function is required to return with a 32-bit hash
+ *
+ * NB: if required to add, the caller cannot distinguish between a symbol
+ * which did not previously exist, and one which did exist but had no
+ * value and no references. Where that distinction matters, it is
+ * necessary to do an extra lookup.
+ */
+symbol
+symbol_lookup(symbol_table table, const void* name, int add)
+{
+ struct symbol* this ;
+ struct symbol** base ;
+ struct symbol_hash hash ;
+
+ assert(table != NULL) ;
+ if (table->bases == NULL)
+ symbol_table_setup(table) ; /* Lazy allocation of chain bases etc. */
+
+ table->hash_function(&hash, name) ;
+
+ base = symbol_base(table, hash.hash) ;
+ this = *base ;
+ while (this)
+ {
+ if ((this->hash == hash.hash)
+ && (this->name_len == hash.name_len)
+ && (memcmp(this->name, hash.name, this->name_len) == 0))
+ return this ;
+ this = this->next ;
+ } ;
+
+ /* Not found -- quit now if not required to add */
+ if (!add) return NULL ;
+
+ /* Adding: first, carve a new, empty symbol entry */
+ this = XCALLOC(MTYPE_SYMBOL, sizeof(struct symbol) + hash.name_copy_len) ;
+
+ this->table = table ;
+ this->value = NULL ;
+ this->ref_list = NULL ;
+ this->ref_count = 0 ;
+ this->hash = hash.hash ;
+ this->name_len = hash.name_len ;
+ memcpy(this->name, hash.name, hash.name_copy_len) ;
+
+ /* Second, if required, extend the array of list bases. We extend if */
+ /* we have a collision *and* we exceed threshold of number of entries. */
+ if ((*base != NULL) && (table->entry_count > table->extend_thresh))
+ {
+ symbol_extend_bases(table) ;
+ base = symbol_base(table, hash.hash) ;
+ } ;
+
+ /* Third, chain in the new entry, count it in and return */
+ this->next = *base ;
+ *base = this ;
+
+ ++table->entry_count ;
+
+ return this ;
+} ;
+
+/* Delete symbol.
+ *
+ * The first effect of this is to set the symbol value to NULL, which may
+ * trigger a value_call_back etc.
+ *
+ * Then the symbol is removed from the table (and the symbol becomes an orphan).
+ *
+ * Then, if there are no (remaining) references the symbol is freed. Otherwise
+ * the symbol entry remains in existence until there are no more references
+ * (at which point it will finally be destroyed).
+ *
+ * Returns the last value of the symbol -- which may itself need to be
+ * destroyed -- noting that the symbol may already have been released. (If the
+ * symbol is required when the value is released, then the value should hold a
+ * simple reference to the symbol.)
+ *
+ * NB: the effect of deleting a symbol is to leave all remaining references
+ * pointing at an NULL value, orphaned symbol.
+ *
+ * If a new symbol is created with the same name, that will be a
+ * completely different symbol -- references to the old symbol will
+ * continue to be to the vestigial NULL value.
+ *
+ * This is different from setting the symbol value to NULL and later
+ * giving it a new value.
+ *
+ * NB: orphan symbols can be deleted. The effect is to free the symbol if
+ * possible.
+ */
+void*
+symbol_delete(symbol sym)
+{
+ void* old_value = symbol_unset_value(sym) ;
+
+ if (symbol_no_references(sym))
+ symbol_free(sym) ; /* free symbol now if no references */
+ else
+ symbol_remove(sym) ; /* else just remove it from the table -- will be */
+ /* freed when all references are unset. */
+ return old_value ;
+} ;
+
+/* The hash functions provided here use CRC32 as a hash.
+ *
+ * CRC32 is not intended as a hash function, and is not a perfect one.
+ * However it is fast -- requiring a few simple operations per byte. Taken
+ * with the secondary effect of using the hash produced modulo an odd number,
+ * experience suggests this is sufficient.
+ */
+
+static u_int32_t crc_table[] ;
+
+/* Standard symbol string hash function. */
+void
+symbol_hash_string(symbol_hash p_hash, const char* string) {
+ u_int32_t h = 0 ;
+ const char* p = string ;
+
+ while (*p != 0)
+ h = crc_table[(h & 0xFF) ^ (u_int8_t)*p++] ^ (h >> 8) ;
+
+ assert((p - string) < 0xFFFF) ;
+
+ p_hash->hash = h ;
+ p_hash->name = string ;
+ p_hash->name_len = (p - string) ;
+ p_hash->name_copy_len = p_hash->name_len + 1 ;
+} ;
+
+/* Standard symbol byte vector hash function. */
+void
+symbol_hash_bytes(symbol_hash p_hash, const void* bytes, size_t len) {
+ assert(len < 0xFFFF) ;
+
+ u_int32_t h = len ; /* So strings of zeros don't CRC the same ! */
+ const u_int8_t* p = bytes ;
+ const u_int8_t* e = p + len ;
+
+ while (p < e)
+ h = crc_table[(h & 0xFF) ^ *p++] ^ (h >> 8) ;
+
+ p_hash->hash = h ;
+ p_hash->name = (const void*)bytes ;
+ p_hash->name_len = len ;
+ p_hash->name_copy_len = len ;
+} ;
+
+/* Extend the array of list bases. */
+static void
+symbol_extend_bases(symbol_table table)
+{
+ symbol this ;
+ symbol next ;
+ symbol* old_bases ;
+ symbol* new_bases ;
+ symbol* base ;
+ unsigned int new_base_count ;
+ unsigned int old_base_count ;
+
+ old_bases = table->bases ;
+ old_base_count = table->base_count ;
+
+ assert((old_bases != NULL) && (old_base_count != 0)) ;
+
+ /* TODO: should look out for overflowing base_count and requiring */
+ /* impossible amounts of memory ?! */
+
+ new_base_count = (table->base_count | 1) - 1 ; /* trim enforced odd-ness */
+
+ if (new_base_count <= SYMBOL_TABLE_BASES_DOUBLE_MAX)
+ new_base_count *= 2 ;
+ else
+ new_base_count += SYMBOL_TABLE_BASES_DOUBLE_MAX ;
+
+ new_base_count = symbol_table_new_bases(table, new_base_count,
+ (float)table->extend_thresh / table->base_count) ;
+
+ /* Rehome everything on the new chain bases. */
+ new_bases = table->bases ;
+ while (old_base_count--)
+ {
+ next = old_bases[old_base_count] ;
+ while (next != NULL)
+ {
+ this = next ;
+ next = this->next ;
+ base = &new_bases[this->hash % new_base_count] ;
+ this->next = *base ;
+ *base = this ;
+ } ;
+ } ;
+
+ /* Release the old chain bases, and we're done. */
+ XFREE(MTYPE_SYMBOL_BASES, old_bases) ;
+} ;
+
+/*==============================================================================
+ * Reference count handling.
+ *
+ * symbol_inc_ref(sym) -- declared Inline
+ * symbol_dec_ref(sym) -- declared Inline
+ */
+
+/* Zeroise the reference count.*/
+
+symbol
+symbol_zero_ref(symbol sym, int force)
+{
+ assert((sym->ref_count == 1) || force) ;
+
+ sym->ref_count = 0 ;
+ symbol_free_if_redundant(sym) ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Reference list handling.
+ *
+ * References are added at the head of the list -- which is significant when
+ * adding references during a symbol reference walk.
+ */
+
+/* Insert symbol_ref at head of symbol's list of references. */
+static inline void
+symbol_add_ref(symbol sym, symbol_ref ref)
+{
+ symbol_ref next = sym->ref_list ;
+ sym->ref_list = ref ;
+ if (next)
+ next->prev = ref ;
+ ref->next = next ;
+ ref->prev = (void*)sym ; /* marker for first on list */
+} ;
+
+/* Clip symbol_ref from symbol's list of references.
+ *
+ * If symbol_ref has already been deleted the prev pointer is NULL, and this
+ * function copes -- and does not need the symbol to be valid (sym may be NULL).
+ */
+static inline void
+symbol_del_ref(symbol sym, symbol_ref ref)
+{
+ symbol_ref prev = ref->prev ;
+ symbol_ref next = ref->next ;
+
+ if (prev != NULL)
+ {
+ if (prev == (void*)sym)
+ {
+ assert(sym->ref_list == ref) ;
+ sym->ref_list = next ;
+ }
+ else
+ prev->next = next ;
+
+ if (next != NULL)
+ next->prev = prev ;
+ } ;
+ ref->next = ref->prev = NULL ;
+} ;
+
+/*==============================================================================
+ * The value_call_back handling and symbol reference list walking.
+ *
+ * If there is one, the value_call_back function is called when the value of
+ * a symbol is set -- except when it is set NULL and is already NULL. Note
+ * that setting the same non-NULL value *does* invoke the value_call_back.
+ *
+ * The value_call_back function is passed the current state of the symbol,
+ * complete with new value, and the old value of the symbol.
+ *
+ * During the value_call_back the symbol reference list may be walked, so that
+ * users of the value may be updated.
+ *
+ * During the value_call_back the symbol may be set, unset or deleted, and
+ * references added or taken away. This may cause nested calls of the
+ * call-back. Note that each call-back holds a reference to the symbol, so if
+ * the symbol is deleted it won't be freed until the outermost call-back
+ * returns.
+ *
+ * Procedure for walking the references to a symbol:
+ *
+ * struct symbol_ref walk ;
+ * symbol sym ;
+ * symbol_ref ref ;
+ * symbol_ref_walk_start(sym, &walk) ;
+ * while ((ref = symbol_ref_walk_step(&walk)) != NULL)
+ * .... whatever
+ * symbol_ref_walk_end(&walk) ;
+ *
+ * NB: it is *essential* to call symbol_ref_walk_end() exactly once at some
+ * time after symbol_ref_walk_start.
+ *
+ * The symbol table walk uses a "bookmark" which is a special from of entry in
+ * the symbol's reference list. This mechanism:
+ *
+ * (a) prevents the symbol being freed while the reference walk is in
+ * progress -- that may happen during symbol_ref_walk_end.
+ *
+ * (b) allows for the current and other references to be set or unset.
+ *
+ * Setting a reference inserts it upstream of the bookmark -- so it will
+ * not be visited during the walk.
+ *
+ * Unsetting a reference that has yet to be visited eliminates it from
+ * the walk.
+ *
+ * Note that setting a reference to refer to the symbol it already
+ * refers to has no effect at all.
+ *
+ * (c) allows the symbol to be defined, undefined or redefined during a
+ * symbol reference walk.
+ *
+ * If that triggers another symbol reference walk, then that walk will
+ * proceed until it hits the point reached by the walk it is nested
+ * inside, and then stop.
+ *
+ * Suppose the outer walk was dealing with the value having changed from
+ * 'A' to 'B'. The inner walk will do from 'B' to the latest value 'C'
+ * for the references that have already seen 'A' to 'B'. When the outer
+ * walk resumes, it will deal with the change 'A' to 'C', unaware of the
+ * intermediate step.
+ *
+ * If that does not suit, don't fiddle with symbol values during a
+ * symbol reference walk.
+ */
+
+/* Bookmarks are symbol_ref structures, distinguished from ordinary symbol_ref
+ * structures by setting the sym field to point at the bookmark symbol_ref
+ * itself.
+ *
+ * (It would be nicer to use the parent field for this... but what is put
+ * there in ordinary symbol_ref structures is not guaranteed...)
+ */
+static inline int
+symbol_ref_is_bookmark(symbol_ref ref)
+{
+ return (void*)ref->sym == (void*)ref ;
+} ;
+
+/* Start walk of symbol references */
+void
+symbol_ref_walk_start(symbol sym, symbol_ref walk)
+{
+ symbol_init_ref(walk) ; /* keeping things tidy */
+ walk->sym = (void*)walk ; /* bookmark signature */
+ walk->parent = sym ;
+ symbol_add_ref(sym, walk) ; /* insert bookmark at head of list */
+} ;
+
+/* Step walk and return the next reference (if any). */
+symbol_ref
+symbol_ref_walk_step(symbol_ref walk)
+{
+ symbol_ref next_ref ;
+
+ assert(symbol_ref_is_bookmark(walk)) ; /* must be a bookmark ! */
+
+ /* Pick up reference following the bookmark, before deleting it. */
+ next_ref = walk->next ;
+ symbol_del_ref((symbol)walk->parent, walk) ;
+
+ /* Stop immediately if bookmark was at the end of the list or the next */
+ /* item is a bookmark (for a walk that started earlier). */
+ if ((next_ref == NULL) || symbol_ref_is_bookmark(next_ref))
+ return NULL ;
+
+ /* Now we move the bookmark from where it is now to after next_ref. */
+
+ walk->next = next_ref->next ;
+ next_ref->next = walk ;
+ walk->prev = next_ref ;
+ if (walk->next != NULL)
+ walk->next->prev = walk ;
+
+ /* Return the next real reference to be processed. */
+ return next_ref ;
+} ;
+
+/* End of symbol reference walk.
+ *
+ * NB: if the symbol is not defined and has no references or bookmarks it
+ * will now be freed.
+ */
+void
+symbol_ref_walk_end(symbol_ref walk)
+{
+ assert(symbol_ref_is_bookmark(walk)) ; /* must be a bookmark ! */
+
+ symbol_del_ref((symbol)(walk->parent), walk) ; /* make sure */
+
+ symbol_free_if_redundant((symbol)(walk->parent)) ;
+} ;
+
+/*==============================================================================
+ * Symbol Value handling.
+ */
+
+/* Set symbol value. NB: setting to NULL == symbol_unset_value.
+ * NB: setting same value as currently looks like a change.
+ * (except for setting NULL to NULL !)
+ *
+ * Invokes change call-back, if any -- except when setting to NULL and is
+ * already NULL.
+ *
+ * It is possible for the call-back to set the value again, to unset it, to
+ * change references, etc.
+ *
+ * Returns previous value -- which may require releasing.
+ */
+void*
+symbol_set_value(symbol sym, void* new_value)
+{
+ void* old_value ;
+
+ old_value = sym->value ;
+ sym->value = new_value ;
+
+ if (sym->table == NULL) /* watch out for orphans */
+ {
+ assert((new_value == NULL) && (old_value == NULL)) ;
+ return NULL ;
+ } ;
+
+ /* Invoke value_call_back (if any). */
+ /* Note that the value_call_back may set/unset references and/or */
+ /* define/undefine the value. */
+ if (((sym)->table->value_call_back != NULL)
+ && ( (new_value != NULL) || (old_value != NULL) ))
+ {
+ symbol_inc_ref(sym) ; /* preserve until call-back returns */
+ sym->table->value_call_back(sym, old_value) ;
+ symbol_dec_ref(sym) ; /* may now free if has been deleted */
+ } ;
+
+ return old_value ;
+} ;
+
+/*==============================================================================
+ * Symbol Reference handling.
+ *
+ * Implementation note: the next and prev pointers in the symbol_ref structure
+ * are significant only if the sym pointer is not NULL.
+ */
+
+/* Initialise symbol reference -- allocate if required. */
+symbol_ref
+symbol_init_ref(symbol_ref ref)
+{
+ if (ref == NULL)
+ return XCALLOC(MTYPE_SYMBOL_REF, sizeof(struct symbol_ref)) ;
+ else
+ return memset(ref, 0, sizeof(struct symbol_ref)) ;
+} ;
+
+/* Set symbol reference -- allocate if required (ref == NULL).
+ *
+ * NB: does nothing if reference already set to the given symbol.
+ *
+ * NB: unsets (but does not free) reference if was not NULL (and is not
+ * same as symbol being set to) before setting new reference.
+ *
+ * NB: setting reference to NULL unsets any existing reference, but does NOT
+ * release the reference structure.
+ *
+ * NB: if reference is allocated, the parent is set NULL and the tag is set
+ * NULL/0.
+ *
+ * if reference is not allocated, the parent and tag are unchanged.
+ */
+symbol_ref
+symbol_set_ref(symbol_ref ref, struct symbol* sym)
+{
+ if (ref != NULL)
+ {
+ if (ref->sym == sym)
+ return ref ; /* Nothing more to do if already set to given value */
+ if (ref->sym != NULL)
+ symbol_unset_ref_keep(ref) ;
+ }
+ else
+ ref = symbol_init_ref(NULL) ;
+
+ ref->sym = sym ;
+ if (sym != NULL)
+ symbol_add_ref(sym, ref) ;
+
+ return ref ;
+} ;
+
+/* Unset symbol reference. Free the structure if required.
+ *
+ * NB: does nothing if address of reference is NULL.
+ *
+ * NB: if reference is not freed, the parent and tag are unchanged.
+ *
+ * NB: removing the last reference to an symbol that has been deleted causes
+ * the symbol to be freed.
+ *
+ * NB: copes if the reference is already unset, of course.
+ */
+symbol_ref
+symbol_unset_ref(symbol_ref ref, int free_ref_structure)
+{
+ if (ref == NULL) return ref ;
+
+ if (ref->sym != NULL) /* NULL => reference already unset */
+ {
+ symbol_del_ref(ref->sym, ref) ;
+ symbol_free_if_redundant(ref->sym) ;
+ ref->sym = NULL ;
+ } ;
+
+ if (free_ref_structure)
+ XFREE(MTYPE_SYMBOL_REF, ref) ; /* ref is set to NULL */
+
+ return ref ;
+} ;
+
+/*==============================================================================
+ * Walking a symbol table
+ *
+ * Simple walk: visits all entries in the table, in the order they are hashed
+ * to. Simple iterator.
+ *
+ * Extract: makes vector of pointers to selected entries, and sorts that
+ * vector as required.
+ */
+
+/* Walk the given symbol table. Usage:
+ *
+ * struct symbol_walker walker ;
+ * symbol sym ;
+ * ....
+ * symbol_walk_start(table, &walker) ;
+ * while ((sym = symbol_walk_next(&walker)))
+ * ....
+ *
+ * NB: it is possible to change the current symbol while the walk is in
+ * progress -- up to and including deleting it. Any other changes to
+ * the table must NOT be attempted.
+ */
+void
+symbol_walk_start(symbol_table table, struct symbol_walker* walker)
+{
+ walker->next = NULL ;
+ walker->base = table->bases ;
+ walker->base_count = table->base_count ;
+} ;
+
+symbol
+symbol_walk_next(struct symbol_walker* walker)
+{
+ symbol this = walker->next ;
+
+ while (this == NULL)
+ {
+ if (walker->base_count == 0)
+ return NULL ;
+ --walker->base_count ;
+ this = *(walker->base++) ;
+ } ;
+
+ walker->next = this->next ;
+ return this ;
+} ;
+
+/* Extract Symbols.
+ *
+ * Walk symbol table and select symbols to add to a new vector. Then sort the
+ * vector, if required. Takes:
+ *
+ * -- selector: NULL => select all
+ * -- p_val: pointer is passed to the select function (if any)
+ * -- most: if there is a select function, this flag hints that most of
+ * the symbols will be selected -- so it is worth preallocating
+ * a vector big enough for all symbols.
+ * -- sort: NULL => no sort (!)
+ *
+ * NB: the vector contains pointers to the selected symbols. It is the
+ * caller's responsibility to avoid deleting any symbol whose pointer
+ * in the vector they expect to rely on !
+ */
+vector
+symbol_table_extract(symbol_table table,
+ symbol_select_cmp* selector, const void* p_val, int most,
+ symbol_sort_cmp* sort)
+{
+ vector extract ;
+ symbol* base ;
+ unsigned int count ;
+ symbol sym ;
+
+ extract = vector_init_new(NULL, (most || (selector == NULL))
+ ? table->entry_count : 8) ;
+ base = table->bases ;
+
+ if (base == NULL)
+ return extract ; /* Quit if symbol table is "reset" */
+
+ count = table->base_count ;
+ while (count--)
+ {
+ sym = *base++ ;
+ while (sym != NULL)
+ {
+ if ((selector == NULL) || selector(sym, p_val))
+ vector_push_item(extract, sym) ;
+ sym = sym->next ;
+ } ;
+ } ;
+
+ if (sort != NULL)
+ vector_sort(extract, (vector_sort_cmp*)sort) ;
+
+ return extract ;
+} ;
+
+/*==============================================================================
+ * Some common comparison functions for symbol table extracts.
+ */
+
+/* Comparison function to sort names which are a mixture of digits and other
+ * characters.
+ *
+ * This comparison treats substrings of digits as numbers, so "a10" is > "a1".
+ */
+int
+symbol_mixed_name_cmp(const symbol* p_a,
+ const symbol* p_b)
+{
+ const char* a = symbol_get_name(*p_a) ;
+ const char* b = symbol_get_name(*p_b) ;
+ int la, lb ;
+
+ while (1) {
+ if (isdigit(*a) && isdigit(*b))
+ {
+ char* as ; /* Required to stop the compiler whining */
+ char* bs ;
+ unsigned long int na = strtoul(a, (char** restrict)&as, 10) ;
+ unsigned long int nb = strtoul(b, (char** restrict)&bs, 10) ;
+ if (na != nb)
+ return (na < nb) ? -1 : +1 ;
+ a = as ;
+ b = bs ;
+ }
+ else
+ {
+ if (*a != *b)
+ return (*a < *b) ? -1 : +1 ;
+ if (*a == '\0')
+ break ;
+ ++a ;
+ ++b ;
+ }
+ } ;
+
+ /* Looks like the names are equal.
+ * But may be different lengths if have number part(s) with leading zeros,
+ */
+
+ la = symbol_get_name_len(*p_a) ;
+ lb = symbol_get_name_len(*p_b) ;
+ if (la != lb)
+ return (la < lb) ? -1 : +1 ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Table for generating CRC-32 -- Standard (0x1_04C1_1DB7 0xEDB8_8320)
+ */
+static u_int32_t crc_table[] =
+{
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
+} ;
diff --git a/lib/symtab.h b/lib/symtab.h
new file mode 100644
index 00000000..a8a6e622
--- /dev/null
+++ b/lib/symtab.h
@@ -0,0 +1,320 @@
+/* Symbol Table data structure -- 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 _ZEBRA_SYMTAB_H
+#define _ZEBRA_SYMTAB_H
+
+#include "vector.h"
+#include <stddef.h>
+#include <stdint.h>
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/* Maximum number of symbol table bases -- something has gone tragically wrong
+ * if we hit this. Assume can multiply this by 2 and get valid size_t result.
+ */
+#define SYMBOL_TABLE_BASES_MAX (1024 * 1024 * 1024)
+
+/* Minimum number of symbol table bases. */
+#define SYMBOL_TABLE_BASES_MIN 10
+/* Point at which stops doubling the symbol table size (bases) */
+#define SYMBOL_TABLE_BASES_DOUBLE_MAX 2000
+
+/* Structures defined below. */
+struct symbol_table ;
+struct symbol ;
+struct symbol_ref ;
+struct symbol_hash ;
+
+typedef struct symbol_table* symbol_table ;
+typedef struct symbol* symbol ;
+typedef struct symbol_ref* symbol_ref ;
+typedef struct symbol_hash* symbol_hash ;
+
+/* Function types used. */
+typedef void symbol_hash_function(symbol_hash hash, const void* name) ;
+typedef void symbol_call_back_function(symbol sym, void* value) ;
+typedef void symbol_destroy_function(symbol sym) ;
+
+/* Symbol Table.
+ *
+ * Don't fiddle with this directly... see access functions below.
+ */
+
+struct symbol_table
+{
+ void* parent ; /* to identify the table. */
+
+ symbol* bases ; /* ref:array of chain bases */
+ unsigned int base_count ; /* number of chain bases */
+
+ unsigned int entry_count ; /* number of entries in the table */
+ unsigned int extend_thresh ; /* when to extend the hash table */
+
+ symbol_hash_function* hash_function ; /* function to hash given "name" */
+ /* NULL => use default */
+ symbol_call_back_function* value_call_back ;
+ /* called when symbol value is set */
+} ;
+
+/* Symbol Table Entry.
+ *
+ * Don't fiddle with this directly... see access macros/functions below.
+ */
+
+struct symbol
+{
+ symbol_table table ; /* so can go from symbol to enclosing table */
+ /* NULL => orphan symbol, with NULL value. */
+
+ symbol next ; /* assume chains are short and seldom remove */
+ /* symbols -- so single pointer will do. */
+
+ void* value ; /* see: symbol_get_value(sym) etc. */
+
+ symbol_ref ref_list ; /* list of symbol_ref references */
+ unsigned ref_count ; /* count of simple references */
+
+ uint32_t hash ; /* used in lookup and when extending bases. */
+
+ uint16_t name_len ; /* see: symbol_get_name_len(sym) */
+ char name[] ; /* see: symbol_get_name(sym) */
+} ;
+
+/* Symbol Reference (or "bookmark").
+ *
+ * Don't fiddle with this directly... see access macros/functions below
+ */
+
+typedef union
+{
+ void* p ;
+ unsigned long u ;
+ signed long i ;
+} symbol_ref_tag_t ;
+
+struct symbol_ref
+{
+ symbol sym ; /* Address of symbol referred to (if any). */
+ /* (In "bookmark" this points to self.) */
+
+ symbol_ref next ; /* fellow references to the symbol ... */
+ symbol_ref prev ; /* ... ignore if sym is NULL. */
+
+ void* parent ; /* see: sym_ref_parent(sym_ref) etc. */
+ symbol_ref_tag_t tag ; /* see: sym_ref_tag(sym_ref) etc. */
+} ;
+
+/* Result of a hash function for a symbol name. */
+struct symbol_hash
+{
+ 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 */
+struct symbol_walker
+{
+ symbol next ; /* next symbol to return (if any) */
+ symbol* base ; /* next chain base to process (if any) */
+ unsigned base_count ; /* number of chain bases left to process */
+} ;
+
+/* Symbol Table Operations. */
+
+extern symbol_table
+symbol_table_init_new(symbol_table table,
+ void* parent,
+ unsigned bases,
+ unsigned density,
+ symbol_hash_function* hash_function,
+ symbol_call_back_function* value_call_back) ;
+void symbol_table_set_parent(symbol_table table, void* parent) ;
+void* symbol_table_get_parent(symbol_table table) ;
+void* symbol_table_ream(symbol_table table, int free_structure) ;
+#define symbol_table_ream_free(table) symbol_table_ream(table, 1)
+#define symbol_table_ream_keep(table) symbol_table_ream(table, 0)
+symbol_table symbol_table_reset(symbol_table table, int free_structure) ;
+#define symbol_table_reset_free(table) symbol_table_reset(table, 1)
+#define symbol_table_reset_keep(table) symbol_table_reset(table, 0)
+
+extern void symbol_hash_string(struct symbol_hash* p_hash, const char* string) ;
+extern void symbol_hash_bytes(struct symbol_hash* p_hash, const void* bytes,
+ size_t len) ;
+extern void symbol_table_set_value_call_back(symbol_table table,
+ symbol_call_back_function* value_call_back) ;
+
+extern void symbol_table_free(symbol_table) ;
+
+extern symbol symbol_lookup(symbol_table table, const void* name, int add) ;
+
+#define symbol_seek(table, name) symbol_lookup(table, name, 0)
+#define symbol_find(table, name) symbol_lookup(table, name, 1)
+
+extern void* symbol_delete(symbol sym) ;
+
+extern void* symbol_set_value(symbol sym, void* new_value) ;
+#define symbol_unset_value(sym) symbol_set_value(sym, NULL)
+
+void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
+symbol_ref symbol_ref_walk_step(symbol_ref walk) ;
+void symbol_ref_walk_end(symbol_ref walk) ;
+
+void symbol_walk_start(symbol_table table, struct symbol_walker* walker) ;
+symbol symbol_walk_next(struct symbol_walker* walker) ;
+
+typedef int symbol_select_cmp(const symbol, const void*) ;
+typedef int symbol_sort_cmp(const symbol*, const symbol*) ;
+vector symbol_table_extract(symbol_table table,
+ symbol_select_cmp* select, const void* p_value,
+ int most, symbol_sort_cmp* sort) ;
+
+extern symbol_sort_cmp symbol_mixed_name_cmp ;
+
+/* Access functions -- argument is address of symbol (may be NULL). */
+Inline void*
+symbol_get_value(const symbol sym)
+{
+ return (sym != NULL) ? sym->value : NULL ;
+} ;
+
+Inline const void*
+symbol_get_name(const symbol sym)
+{
+ return (sym != NULL) ? sym->name : NULL ;
+} ;
+
+Inline unsigned
+symbol_get_name_len(const symbol sym)
+{
+ return (sym != NULL) ? sym->name_len : 0 ;
+} ;
+
+Inline struct symbol_table*
+symbol_get_table(const symbol sym)
+{
+ return (sym != NULL) ? sym->table : NULL ;
+} ;
+
+Inline symbol
+symbol_inc_ref(symbol sym)
+{
+ ++sym->ref_count ;
+ return sym ;
+} ;
+
+extern symbol symbol_zero_ref(symbol sym, int force) ;
+
+Inline symbol
+symbol_dec_ref(symbol sym)
+{
+ if (sym->ref_count <= 1)
+ return symbol_zero_ref(sym, 0) ;
+
+ --sym->ref_count ;
+ return sym ;
+} ;
+
+extern symbol_ref
+symbol_init_ref(symbol_ref ref) ;
+
+extern symbol_ref
+symbol_set_ref(symbol_ref ref, symbol sym) ;
+
+extern symbol_ref
+symbol_unset_ref(symbol_ref ref, int free_ref_structure) ;
+
+#define symbol_unset_ref_free(ref) symbol_unset_ref(ref, 1) ;
+#define symbol_unset_ref_keep(ref) symbol_unset_ref(ref, 0) ;
+
+/* Access functions -- argument is address of symbol_ref. */
+/* These cope if address of symbol_ref is null, or reference is undefined. */
+Inline void*
+sym_ref_symbol(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->sym : NULL ;
+}
+Inline void*
+sym_ref_value(symbol_ref ref)
+{
+ return symbol_get_value(sym_ref_symbol(ref)) ;
+}
+Inline const void*
+sym_ref_name(symbol_ref ref)
+{
+ return symbol_get_name(sym_ref_symbol(ref)) ;
+}
+Inline uint16_t
+sym_ref_name_len(symbol_ref ref)
+{
+ return symbol_get_name_len(sym_ref_symbol(ref)) ;
+}
+
+Inline void*
+sym_ref_parent(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->parent : NULL ;
+}
+Inline void*
+sym_ref_p_tag(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->tag.p : NULL ;
+}
+Inline unsigned long int
+sym_ref_u_tag(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->tag.u : 0 ;
+}
+Inline signed long int
+sym_ref_i_tag(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->tag.i : 0 ;
+}
+
+/* Set properties of reference -- argument is address of symbol_ref, which is */
+/* assumed to NOT be NULL. */
+Inline void
+sym_ref_set_parent(symbol_ref ref, void* pa)
+{
+ ref->parent = pa ;
+}
+Inline void
+sym_ref_set_p_tag(symbol_ref ref, void* p_tag)
+{
+ ref->tag.p = p_tag ;
+}
+Inline void
+sym_ref_set_u_tag(symbol_ref ref, unsigned long int u_tag)
+{
+ ref->tag.u = u_tag ;
+}
+Inline void
+sym_ref_set_i_tag(symbol_ref ref, signed long int i_tag)
+{
+ ref->tag.i = i_tag ;
+}
+
+#endif /* _ZEBRA_SYMTAB_H */
diff --git a/lib/thread.c b/lib/thread.c
index fd841c21..a15df557 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -16,12 +16,13 @@
* 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.
*/
/* #define DEBUG */
#include <zebra.h>
+#include "miyagi.h"
#include "thread.h"
#include "memory.h"
@@ -29,7 +30,9 @@
#include "hash.h"
#include "command.h"
#include "sigevent.h"
-
+#include "qpthreads.h"
+#include "qtimers.h"
+
/* Recent absolute time of day */
struct timeval recent_time;
static struct timeval last_recent_time;
@@ -38,10 +41,19 @@ static struct timeval relative_time;
static struct timeval relative_time_base;
/* init flag */
static unsigned short timers_inited;
-
+
+/* cpu stats needs to be qpthread safe. */
+static qpt_mutex_t thread_mutex;
+#define LOCK qpt_mutex_lock(&thread_mutex);
+#define UNLOCK qpt_mutex_unlock(&thread_mutex);
static struct hash *cpu_record = NULL;
-
-/* Struct timeval's tv_usec one second value. */
+
+/* Pointer to qtimer pile to be used, if any */
+static qtimer_pile use_qtimer_pile = NULL ;
+static qtimer spare_qtimers = NULL ;
+static unsigned used_standard_timer = 0 ;
+
+/* Struct timeval's tv_usec one second value. */
#define TIMER_SECOND_MICRO 1000000L
/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
@@ -92,7 +104,7 @@ timeval_elapsed (struct timeval a, struct timeval b)
return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
+ (a.tv_usec - b.tv_usec));
}
-
+
#ifndef HAVE_CLOCK_MONOTONIC
static void
quagga_gettimeofday_relative_adjust (void)
@@ -119,9 +131,9 @@ static int
quagga_gettimeofday (struct timeval *tv)
{
int ret;
-
+
assert (tv);
-
+
if (!(ret = gettimeofday (&recent_time, NULL)))
{
/* init... */
@@ -196,7 +208,7 @@ quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
}
}
-/* time_t value in terms of stabilised absolute time.
+/* time_t value in terms of stabilised absolute time.
* replacement for POSIX time()
*/
time_t
@@ -215,14 +227,17 @@ recent_relative_time (void)
{
return relative_time;
}
-
+
+/* Uses the address of the function (or at least ls part of same) as the hash
+ * key. (The function name is for display, only.)
+ */
static unsigned int
cpu_record_hash_key (struct cpu_thread_history *a)
{
return (uintptr_t) a->func;
}
-static int
+static int
cpu_record_hash_cmp (const struct cpu_thread_history *a,
const struct cpu_thread_history *b)
{
@@ -232,25 +247,58 @@ cpu_record_hash_cmp (const struct cpu_thread_history *a,
static void *
cpu_record_hash_alloc (struct cpu_thread_history *a)
{
- struct cpu_thread_history *new;
+ const char* b ;
+ const char* e ;
+ char* n ;
+ int l ;
+ struct cpu_thread_history *new ;
+
+ /* Establish start and length of name, removing leading/trailing
+ * spaces and any enclosing (...) -- recursively.
+ */
+ b = a->funcname ;
+ e = b + strlen(b) - 1 ;
+
+ while (1)
+ {
+ while (*b == ' ')
+ ++b ; /* strip leading spaces */
+ if (*b == '\0')
+ break ; /* quit if now empty */
+ while (*e == ' ')
+ --e ; /* strip trailing spaces */
+ if ((*b != '(') || (*e != ')'))
+ break ; /* quit if not now (...) */
+ ++b ;
+ --e ; /* discard ( and ) */
+ } ;
+
+ l = (e + 1) - b ; /* length excluding trailing \0 */
+
+ n = XMALLOC(MTYPE_THREAD_FUNCNAME, l + 1) ;
+ memcpy(n, b, l) ;
+ n[l] = '\0' ;
+
+ /* Allocate empty structure and set address and name */
new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
- new->func = a->func;
- new->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname);
- return new;
+ new->func = a->func;
+ new->funcname = n ;
+
+ return new ;
}
static void
cpu_record_hash_free (void *a)
{
struct cpu_thread_history *hist = a;
-
+
XFREE (MTYPE_THREAD_FUNCNAME, hist->funcname);
XFREE (MTYPE_THREAD_STATS, hist);
}
-static inline void
+static inline void
vty_out_cpu_thread_history(struct vty* vty,
- struct cpu_thread_history *a)
+ const struct cpu_thread_history *a)
{
#ifdef HAVE_RUSAGE
vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
@@ -273,14 +321,14 @@ vty_out_cpu_thread_history(struct vty* vty,
}
static void
-cpu_record_hash_print(struct hash_backet *bucket,
+cpu_record_hash_print(struct hash_backet *bucket,
void *args[])
{
struct cpu_thread_history *totals = args[0];
struct vty *vty = args[1];
thread_type *filter = args[2];
struct cpu_thread_history *a = bucket->data;
-
+
a = bucket->data;
if ( !(a->types & *filter) )
return;
@@ -303,7 +351,9 @@ cpu_record_print(struct vty *vty, thread_type filter)
void *args[3] = {&tmp, vty, &filter};
memset(&tmp, 0, sizeof tmp);
- tmp.funcname = (char *)"TOTAL";
+ tmp.funcname = miyagi("TOTAL"); /* NB: will not free tmp in the usual way,
+ in particular, will not attempt
+ to free this !! */
tmp.types = filter;
#ifdef HAVE_RUSAGE
@@ -315,15 +365,19 @@ cpu_record_print(struct vty *vty, thread_type filter)
vty_out(vty, " Avg uSec Max uSecs");
#endif
vty_out(vty, " Type Thread%s", VTY_NEWLINE);
+
+ LOCK
hash_iterate(cpu_record,
(void(*)(struct hash_backet*,void*))cpu_record_hash_print,
args);
if (tmp.total_calls > 0)
vty_out_cpu_thread_history(vty, &tmp);
+
+ UNLOCK
}
-DEFUN(show_thread_cpu,
+DEFUN_CALL(show_thread_cpu,
show_thread_cpu_cmd,
"show thread cpu [FILTER]",
SHOW_STR
@@ -384,16 +438,15 @@ DEFUN(show_thread_cpu,
}
static void
-cpu_record_hash_clear (struct hash_backet *bucket,
- void *args)
+cpu_record_hash_clear (struct hash_backet *bucket, void *args)
{
thread_type *filter = args;
struct cpu_thread_history *a = bucket->data;
-
+
a = bucket->data;
if ( !(a->types & *filter) )
return;
-
+
hash_release (cpu_record, bucket->data);
}
@@ -402,8 +455,8 @@ cpu_record_clear (thread_type filter)
{
thread_type *tmp = &filter;
hash_iterate (cpu_record,
- (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
- tmp);
+ (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
+ tmp);
}
DEFUN(clear_thread_cpu,
@@ -421,51 +474,51 @@ DEFUN(clear_thread_cpu,
{
filter = 0;
while (argv[0][i] != '\0')
- {
- switch ( argv[0][i] )
- {
- case 'r':
- case 'R':
- filter |= (1 << THREAD_READ);
- break;
- case 'w':
- case 'W':
- filter |= (1 << THREAD_WRITE);
- break;
- case 't':
- case 'T':
- filter |= (1 << THREAD_TIMER);
- break;
- case 'e':
- case 'E':
- filter |= (1 << THREAD_EVENT);
- break;
- case 'x':
- case 'X':
- filter |= (1 << THREAD_EXECUTE);
- break;
- case 'b':
- case 'B':
- filter |= (1 << THREAD_BACKGROUND);
- break;
- default:
- break;
- }
- ++i;
- }
+ {
+ switch ( argv[0][i] )
+ {
+ case 'r':
+ case 'R':
+ filter |= (1 << THREAD_READ);
+ break;
+ case 'w':
+ case 'W':
+ filter |= (1 << THREAD_WRITE);
+ break;
+ case 't':
+ case 'T':
+ filter |= (1 << THREAD_TIMER);
+ break;
+ case 'e':
+ case 'E':
+ filter |= (1 << THREAD_EVENT);
+ break;
+ case 'x':
+ case 'X':
+ filter |= (1 << THREAD_EXECUTE);
+ break;
+ case 'b':
+ case 'B':
+ filter |= (1 << THREAD_BACKGROUND);
+ break;
+ default:
+ break;
+ }
+ ++i;
+ }
if (filter == 0)
- {
- vty_out(vty, "Invalid filter \"%s\" specified,"
+ {
+ vty_out(vty, "Invalid filter \"%s\" specified,"
" must contain at least one of 'RWTEXB'%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
}
cpu_record_clear (filter);
return CMD_SUCCESS;
}
-
+
/* List allocation and head/tail print out. */
static void
thread_list_debug (struct thread_list *list)
@@ -494,16 +547,21 @@ thread_master_debug (struct thread_master *m)
printf ("total alloc: [%ld]\n", m->alloc);
printf ("-----------\n");
}
-
+
/* Allocate new thread master. */
struct thread_master *
thread_master_create ()
{
- if (cpu_record == NULL)
- cpu_record
- = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
+#ifdef USE_MQUEUE
+ sigfillset (&newmask);
+ sigdelset (&newmask, SIGMQUEUE);
+#endif
+
+ if (cpu_record == NULL)
+ cpu_record
+ = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
(int (*) (const void *, const void *))cpu_record_hash_cmp);
-
+
return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
sizeof (struct thread_master));
}
@@ -524,8 +582,8 @@ thread_list_add (struct thread_list *list, struct thread *thread)
/* Add a new thread just before the point. */
static void
-thread_list_add_before (struct thread_list *list,
- struct thread *point,
+thread_list_add_before (struct thread_list *list,
+ struct thread *point,
struct thread *thread)
{
thread->next = point;
@@ -564,7 +622,6 @@ thread_add_unuse (struct thread_master *m, struct thread *thread)
assert (thread->prev == NULL);
assert (thread->type == THREAD_UNUSED);
thread_list_add (&m->unuse, thread);
- /* XXX: Should we deallocate funcname here? */
}
/* Free all unused thread. */
@@ -577,8 +634,13 @@ thread_list_free (struct thread_master *m, struct thread_list *list)
for (t = list->head; t; t = next)
{
next = t->next;
- if (t->funcname)
- XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
+
+ if ( (use_qtimer_pile != NULL)
+ && ( (t->type == THREAD_TIMER || t->type == THREAD_BACKGROUND) )
+ && (t->u.qtr != NULL)
+ )
+ qtimer_free(t->u.qtr) ;
+
XFREE (MTYPE_THREAD, t);
list->count--;
m->alloc--;
@@ -589,6 +651,8 @@ thread_list_free (struct thread_master *m, struct thread_list *list)
void
thread_master_free (struct thread_master *m)
{
+ qtimer qtr ;
+
thread_list_free (m, &m->read);
thread_list_free (m, &m->write);
thread_list_free (m, &m->timer);
@@ -596,15 +660,23 @@ thread_master_free (struct thread_master *m)
thread_list_free (m, &m->ready);
thread_list_free (m, &m->unuse);
thread_list_free (m, &m->background);
-
+
XFREE (MTYPE_THREAD_MASTER, m);
+ LOCK
if (cpu_record)
{
hash_clean (cpu_record, cpu_record_hash_free);
hash_free (cpu_record);
cpu_record = NULL;
}
+ UNLOCK
+
+ while ((qtr = spare_qtimers) != NULL)
+ {
+ spare_qtimers = (void*)(qtr->pile) ;
+ qtimer_free(qtr) ;
+ } ;
}
/* Thread list is empty or not. */
@@ -628,41 +700,40 @@ unsigned long
thread_timer_remain_second (struct thread *thread)
{
quagga_get_relative (NULL);
-
+
if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
return thread->u.sands.tv_sec - relative_time.tv_sec;
else
return 0;
}
-/* Trim blankspace and "()"s */
-static char *
-strip_funcname (const char *funcname)
-{
- char buff[100];
- char tmp, *ret, *e, *b = buff;
-
- strncpy(buff, funcname, sizeof(buff));
- buff[ sizeof(buff) -1] = '\0';
- e = buff +strlen(buff) -1;
-
- /* Wont work for funcname == "Word (explanation)" */
+/* Get new cpu history */
- while (*b == ' ' || *b == '(')
- ++b;
- while (*e == ' ' || *e == ')')
- --e;
- e++;
+static struct cpu_thread_history*
+thread_get_hist(struct thread* thread, const char* funcname)
+{
+ struct cpu_thread_history tmp ;
+ struct cpu_thread_history* hist ;
+
+ tmp.func = thread->func ;
+ tmp.funcname = miyagi(funcname); /* NB: will not free tmp in the usual way,
+ in particular, will not attempt
+ to free this !! */
+ LOCK
+
+ /* This looks up entry which matches the tmp just set up.
+ *
+ * If does not find one, allocates a new one -- taking a copy of the
+ * funcname.
+ */
+ hist = hash_get (cpu_record, &tmp,
+ (void * (*) (void *))cpu_record_hash_alloc);
+ UNLOCK
- tmp = *e;
- *e = '\0';
- ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
- *e = tmp;
+ return hist ;
+} ;
- return ret;
-}
-
-/* Get new thread. */
+/* Get new thread. */
static struct thread *
thread_get (struct thread_master *m, u_char type,
int (*func) (struct thread *), void *arg, const char* funcname)
@@ -672,28 +743,27 @@ thread_get (struct thread_master *m, u_char type,
if (!thread_empty (&m->unuse))
{
thread = thread_trim_head (&m->unuse);
- if (thread->funcname)
- XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
+ memset(thread, 0, sizeof (struct thread)) ;
}
else
{
thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
m->alloc++;
}
- thread->type = type;
+ thread->type = type;
thread->add_type = type;
- thread->master = m;
- thread->func = func;
- thread->arg = arg;
-
- thread->funcname = strip_funcname(funcname);
+ thread->master = m;
+ thread->func = func;
+ thread->arg = arg;
- return thread;
+ thread->hist = thread_get_hist(thread, funcname) ;
+
+ return thread ;
}
/* Add new read thread. */
struct thread *
-funcname_thread_add_read (struct thread_master *m,
+funcname_thread_add_read (struct thread_master *m,
int (*func) (struct thread *), void *arg, int fd, const char* funcname)
{
struct thread *thread;
@@ -737,111 +807,266 @@ funcname_thread_add_write (struct thread_master *m,
return thread;
}
+/*==============================================================================
+ * Timer Threads -- THREAD_TIMER and THREAD_BACKGROUND
+ *
+ * Standard Timer Threads are sorted by the "struct timeval sands", and
+ * processed by thread_timer_process() -- which moves any expired timer
+ * threads onto the THREAD_READY queue. So, the scheduling of background stuff
+ * is done by not processing the THREAD_BACKGROUND queue until there is
+ * nothing else to do.
+ *
+ * When using a qtimer_pile:
+ *
+ * * THREAD_TIMER threads have an associated qtimer.
+ *
+ * When the timer expires, the qtimer is cut from the thread (and put onto
+ * the spare_qtimers list). The thread is then queued on the THREAD_READY
+ * queue (as before).
+ *
+ * * THREAD_BACKGROUND threads which have a non-zero delay are treated much
+ * as THREAD_TIMER, except that when the timer expires, the thread is
+ * queued on the THREAD_BACKGROUND queue.
+ *
+ * The THREAD_BACKGROUND queue is visited only when there is nothing else
+ * to do.
+ *
+ * Note that when using a qtimer_pile, and there is an active qtimer associated
+ * with the thread, the thread will be on the THREAD_TIMER queue -- so that it
+ * can be collected up and released if required.
+ *
+ * NB: when using a qtimer_pile, if there is a qtimer associated with a
+ * THREAD_TIMER or a THREAD_BACKGROUND thread, then thread->u.qtr points
+ * at the qtimer.
+ *
+ * AND, conversely, if there is no qtimer, then thread->u.ptr == NULL.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set use_qtimer_pile !
+ */
+extern void
+thread_set_qtimer_pile(qtimer_pile pile)
+{
+ passert(!used_standard_timer) ;
+
+ use_qtimer_pile = pile ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unset qtimer associated with the given THREAD_TIMER or THREAD_BACKGROUND
+ * thread -- if any.
+ *
+ * Moves any qtimer onto the spare_qtimers list.
+ */
+static void
+thread_qtimer_unset(struct thread* thread)
+{
+ qtimer qtr ;
+ assert (thread->type == THREAD_TIMER || thread->type == THREAD_BACKGROUND);
+ assert (use_qtimer_pile != NULL) ;
+
+ qtr = thread->u.qtr ;
+ if (qtr != NULL)
+ {
+ qtimer_unset(qtr) ;
+
+ qtr->pile = (void*)spare_qtimers ;
+ spare_qtimers = qtr ;
+
+ thread->u.qtr = NULL ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The qtimer action function -- when using qtimer pile (!)
+ *
+ * Remove thread from the THREAD_TIMER queue and unset the qtimer, place
+ * thread on the THREAD_READY or the THREAD_BACKGROUND queue as required.
+ */
+static void
+thread_qtimer_dispatch(qtimer qtr, void* timer_info, qtime_mono_t when)
+{
+ struct thread* thread = timer_info ;
+
+ thread_list_delete (&thread->master->timer, thread) ;
+ thread_qtimer_unset(thread) ;
+
+ switch (thread->type)
+ {
+ case THREAD_TIMER:
+ thread->type = THREAD_READY;
+ thread_list_add (&thread->master->ready, thread);
+ break ;
+
+ case THREAD_BACKGROUND:
+ thread_list_add (&thread->master->background, thread);
+ break ;
+
+ default:
+ zabort("invalid thread type in thread_qtimer_dispatch") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For standard timers, return time left on first timer on the given list.
+ */
+static struct timeval *
+thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
+{
+ if (!thread_empty (tlist))
+ {
+ *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
+ return timer_val;
+ }
+ return NULL;
+}
+
+/*------------------------------------------------------------------------------
+ * Add timer of given type -- either standard or qtimer_pile as required.
+ *
+ * Timer interval is given as a struct timeval.
+ */
static struct thread *
-funcname_thread_add_timer_timeval (struct thread_master *m,
- int (*func) (struct thread *),
+funcname_thread_add_timer_timeval(struct thread_master *m,
+ int (*func) (struct thread *),
int type,
- void *arg,
- struct timeval *time_relative,
+ void *arg,
+ struct timeval *time_relative,
const char* funcname)
{
struct thread *thread;
- struct thread_list *list;
- struct timeval alarm_time;
- struct thread *tt;
assert (m != NULL);
+ assert (time_relative != NULL);
assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
- assert (time_relative);
-
- list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
- thread = thread_get (m, type, func, arg, funcname);
- /* Do we need jitter here? */
- quagga_get_relative (NULL);
- alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
- alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
- thread->u.sands = timeval_adjust(alarm_time);
+ thread = thread_get (m, type, func, arg, funcname);
- /* Sort by timeval. */
- for (tt = list->head; tt; tt = tt->next)
- if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
- break;
+ if (use_qtimer_pile == NULL)
+ {
+ struct thread_list *list;
+ struct timeval alarm_time;
+ struct thread *tt;
- if (tt)
- thread_list_add_before (list, tt, thread);
+ /* Do we need jitter here? */
+ quagga_get_relative (NULL);
+ alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
+ alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
+ thread->u.sands = timeval_adjust(alarm_time);
+
+ /* Sort by timeval. */
+ list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
+ for (tt = list->head; tt; tt = tt->next)
+ if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
+ break;
+
+ if (tt)
+ thread_list_add_before (list, tt, thread);
+ else
+ thread_list_add (list, thread);
+
+ used_standard_timer = 1 ;
+ }
else
- thread_list_add (list, thread);
+ {
+ qtimer qtr = spare_qtimers ;
+ if (qtr != NULL)
+ spare_qtimers = (qtimer)(qtr->pile) ;
+
+ qtr = qtimer_init_new(qtr, use_qtimer_pile, NULL, thread) ;
+ thread->u.qtr = qtr ;
+
+ qtimer_set_interval(qtr, timeval2qtime(time_relative),
+ thread_qtimer_dispatch) ;
+ thread_list_add(&m->timer, thread) ;
+ } ;
return thread;
}
-
-/* Add timer event thread. */
+/*------------------------------------------------------------------------------
+ * Add a THREAD_TIMER timer -- either standard or qtimer_pile as required.
+ *
+ * Timer interval is given in seconds.
+ */
struct thread *
funcname_thread_add_timer (struct thread_master *m,
- int (*func) (struct thread *),
+ int (*func) (struct thread *),
void *arg, long timer, const char* funcname)
{
struct timeval trel;
- assert (m != NULL);
-
- trel.tv_sec = timer;
+ trel.tv_sec = timer;
trel.tv_usec = 0;
- return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
+ return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
&trel, funcname);
}
-/* Add timer event thread with "millisecond" resolution */
+/*------------------------------------------------------------------------------
+ * Add a THREAD_TIMER timer -- either standard or qtimer_pile as required.
+ *
+ * Timer interval is given in milliseconds.
+ */
struct thread *
funcname_thread_add_timer_msec (struct thread_master *m,
- int (*func) (struct thread *),
+ int (*func) (struct thread *),
void *arg, long timer, const char* funcname)
{
struct timeval trel;
- assert (m != NULL);
-
- trel.tv_sec = timer / 1000;
- trel.tv_usec = 1000*(timer % 1000);
+ trel.tv_sec = timer / 1000 ;
+ trel.tv_usec = (timer % 1000) * 1000 ;
- return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
- arg, &trel, funcname);
+ return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
+ arg, &trel, funcname);
}
-/* Add a background thread, with an optional millisec delay */
+/*------------------------------------------------------------------------------
+ * Add a THREAD_BACKGROUND thread -- either standard or qtimer_pile as required.
+ *
+ * Timer interval is given in milliseconds.
+ *
+ * For qtimer_pile, if the delay is zero, the thread is placed straight onto
+ * the THREAD_BACKGROUND queue.
+ */
struct thread *
funcname_thread_add_background (struct thread_master *m,
int (*func) (struct thread *),
- void *arg, long delay,
+ void *arg, long delay,
const char *funcname)
{
- struct timeval trel;
-
- assert (m != NULL);
-
- if (delay)
+ if ((delay != 0) || (use_qtimer_pile == NULL))
{
- trel.tv_sec = delay / 1000;
- trel.tv_usec = 1000*(delay % 1000);
+ struct timeval trel;
+
+ trel.tv_sec = delay / 1000;
+ trel.tv_usec = (delay % 1000) * 1000 ;
+
+ return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
+ arg, &trel, funcname);
}
else
{
- trel.tv_sec = 0;
- trel.tv_usec = 0;
- }
+ struct thread* thread ;
+
+ assert (m != NULL);
+
+ thread = thread_get (m, THREAD_BACKGROUND, func, arg, funcname);
+ thread_list_add (&m->background, thread) ;
- return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
- arg, &trel, funcname);
+ return thread ;
+ } ;
}
+/*----------------------------------------------------------------------------*/
/* Add simple event thread. */
struct thread *
funcname_thread_add_event (struct thread_master *m,
- int (*func) (struct thread *), void *arg, int val, const char* funcname)
+ int (*func) (struct thread *), void *arg, int val,
+ const char* funcname)
{
struct thread *thread;
@@ -854,12 +1079,16 @@ funcname_thread_add_event (struct thread_master *m,
return thread;
}
-/* Cancel thread from scheduler. */
+/*------------------------------------------------------------------------------
+ * Cancel thread from scheduler.
+ *
+ * Note that when using qtimer_pile need to unset any associated qtimer.
+ */
void
thread_cancel (struct thread *thread)
{
struct thread_list *list;
-
+
switch (thread->type)
{
case THREAD_READ:
@@ -873,6 +1102,8 @@ thread_cancel (struct thread *thread)
list = &thread->master->write;
break;
case THREAD_TIMER:
+ if ((use_qtimer_pile != NULL) && (thread->u.qtr != NULL))
+ thread_qtimer_unset(thread) ;
list = &thread->master->timer;
break;
case THREAD_EVENT:
@@ -882,13 +1113,21 @@ thread_cancel (struct thread *thread)
list = &thread->master->ready;
break;
case THREAD_BACKGROUND:
- list = &thread->master->background;
+ if ((use_qtimer_pile != NULL) && (thread->u.qtr != NULL))
+ {
+ thread_qtimer_unset(thread) ;
+ list = &thread->master->timer;
+ }
+ else
+ list = &thread->master->background;
break;
+
default:
- return;
- break;
+ return ;
}
+
thread_list_delete (list, thread);
+
thread->type = THREAD_UNUSED;
thread_add_unuse (thread->master, thread);
}
@@ -919,24 +1158,12 @@ thread_cancel_event (struct thread_master *m, void *arg)
return ret;
}
-static struct timeval *
-thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
-{
- if (!thread_empty (tlist))
- {
- *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
- return timer_val;
- }
- return NULL;
-}
-
static struct thread *
thread_run (struct thread_master *m, struct thread *thread,
struct thread *fetch)
{
*fetch = *thread;
thread->type = THREAD_UNUSED;
- thread->funcname = NULL; /* thread_call will free fetch's copied pointer */
thread_add_unuse (m, thread);
return fetch;
}
@@ -947,9 +1174,9 @@ thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
struct thread *thread;
struct thread *next;
int ready = 0;
-
+
assert (list);
-
+
for (thread = list->head; thread; thread = next)
{
next = thread->next;
@@ -973,7 +1200,7 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow)
{
struct thread *thread;
unsigned int ready = 0;
-
+
for (thread = list->head; thread; thread = thread->next)
{
if (timeval_cmp (*timenow, thread->u.sands) < 0)
@@ -986,13 +1213,15 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow)
return ready;
}
-/* process a list en masse, e.g. for event thread lists */
+/*------------------------------------------------------------------------------
+ * Move the given list of threads to the back of the THREAD_READY queue.
+ */
static unsigned int
thread_process (struct thread_list *list)
{
struct thread *thread;
unsigned int ready = 0;
-
+
for (thread = list->head; thread; thread = thread->next)
{
thread_list_delete (list, thread);
@@ -1003,8 +1232,11 @@ thread_process (struct thread_list *list)
return ready;
}
-
-/* Fetch next ready thread. */
+/*------------------------------------------------------------------------------
+ * Fetch next ready thread -- for standard thread handing.
+ *
+ * (This is not used when using qtimer_pile, or qnexus stuff.)
+ */
struct thread *
thread_fetch (struct thread_master *m, struct thread *fetch)
{
@@ -1012,57 +1244,63 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
fd_set readfd;
fd_set writefd;
fd_set exceptfd;
- struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
+ struct timeval timer_val ;
struct timeval timer_val_bg;
- struct timeval *timer_wait = &timer_val;
+ struct timeval *timer_wait ;
struct timeval *timer_wait_bg;
while (1)
{
int num = 0;
-
+
/* Signals pre-empt everything */
quagga_sigevent_process ();
-
+
/* Drain the ready queue of already scheduled jobs, before scheduling
* more.
*/
if ((thread = thread_trim_head (&m->ready)) != NULL)
return thread_run (m, thread, fetch);
-
+
/* To be fair to all kinds of threads, and avoid starvation, we
* need to be careful to consider all thread types for scheduling
* in each quanta. I.e. we should not return early from here on.
*/
-
+
/* Normal event are the next highest priority. */
thread_process (&m->event);
-
+
/* Structure copy. */
readfd = m->readfd;
writefd = m->writefd;
exceptfd = m->exceptfd;
-
+
/* Calculate select wait timer if nothing else to do */
if (m->ready.count == 0)
{
quagga_get_relative (NULL);
timer_wait = thread_timer_wait (&m->timer, &timer_val);
timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
-
+
if (timer_wait_bg &&
(!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
timer_wait = timer_wait_bg;
}
-
+ else
+ {
+ timer_val.tv_sec = 0 ;
+ timer_val.tv_usec = 0 ;
+ timer_wait = &timer_val ;
+ } ;
+
num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
-
+
/* Signals should get quick treatment */
if (num < 0)
{
if (errno == EINTR)
- continue; /* signal received - process it */
- zlog_warn ("select() error: %s", safe_strerror (errno));
+ continue; /* signal received - process it */
+ zlog_warn ("select() error: %s", errtoa(errno, 0).str);
return NULL;
}
@@ -1071,7 +1309,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
list in front of the I/O threads. */
quagga_get_relative (NULL);
thread_timer_process (&m->timer, &relative_time);
-
+
/* Got IO, process it */
if (num > 0)
{
@@ -1092,12 +1330,73 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
/* Background timer/events, lowest priority */
thread_timer_process (&m->background, &relative_time);
-
+
if ((thread = thread_trim_head (&m->ready)) != NULL)
return thread_run (m, thread, fetch);
}
}
+/*------------------------------------------------------------------------------
+ * Empties the event and ready queues.
+ *
+ * This is used when qnexus is managing most things, including I/O. Must be
+ * using qtimer_pile !
+ *
+ * This runs "legacy" event and ready queues only.
+ *
+ * Returns: the number of threads dispatched.
+ *
+ * Legacy timers are handled by the qtimer_pile, and their related threads will
+ * be placed on the ready queue when they expire.
+ *
+ * The background queue is handled separately.
+ */
+extern int
+thread_dispatch(struct thread_master *m)
+{
+ struct thread_list* list ;
+ struct thread fetch ;
+ int count = 0 ;
+
+ while (1)
+ {
+ if (thread_empty(list = &m->event))
+ if (thread_empty(list = &m->ready))
+ return count ;
+
+ thread_call(thread_run(m, thread_list_delete(list, list->head), &fetch)) ;
+
+ ++count ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch first item on the background queue, if any.
+ *
+ * This is used when qnexus is managing most things.
+ *
+ * Background threads spend their lives being cycled around the background
+ * queue -- possibly via the timer queue, if a delay is put in before the next
+ * invocation.
+ *
+ * Returns: 1 if dispatched a background thread
+ * 0 if there are no background threads
+ */
+extern int
+thread_dispatch_background(struct thread_master *m)
+{
+ struct thread* thread ;
+ struct thread fetch ;
+
+ if ((thread = thread_trim_head (&m->background)) == NULL)
+ return 0 ;
+
+ thread_call(thread_run(m, thread, &fetch)) ;
+
+ return 1 ;
+} ;
+
+
unsigned long
thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
{
@@ -1111,13 +1410,13 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
return timeval_elapsed (now->real, start->real);
}
-/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
+/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
Note: we are using real (wall clock) time for this calculation.
It could be argued that CPU time may make more sense in certain
contexts. The things to consider are whether the thread may have
blocked (in which case wall time increases, but CPU time does not),
or whether the system is heavily loaded with other processes competing
- for CPU time. On balance, wall clock time seems to make sense.
+ for CPU time. On balance, wall clock time seems to make sense.
Plus it has the added benefit that gettimeofday should be faster
than calling getrusage. */
int
@@ -1155,23 +1454,6 @@ thread_call (struct thread *thread)
unsigned long realtime, cputime;
RUSAGE_T ru;
- /* Cache a pointer to the relevant cpu history thread, if the thread
- * does not have it yet.
- *
- * Callers submitting 'dummy threads' hence must take care that
- * thread->cpu is NULL
- */
- if (!thread->hist)
- {
- struct cpu_thread_history tmp;
-
- tmp.func = thread->func;
- tmp.funcname = thread->funcname;
-
- thread->hist = hash_get (cpu_record, &tmp,
- (void * (*) (void *))cpu_record_hash_alloc);
- }
-
GETRUSAGE (&thread->ru);
(*thread->func) (thread);
@@ -1179,17 +1461,23 @@ thread_call (struct thread *thread)
GETRUSAGE (&ru);
realtime = thread_consumed_time (&ru, &thread->ru, &cputime);
- thread->hist->real.total += realtime;
- if (thread->hist->real.max < realtime)
- thread->hist->real.max = realtime;
+
+ if (thread->hist != NULL)
+ {
+ LOCK
+ thread->hist->real.total += realtime;
+ if (thread->hist->real.max < realtime)
+ thread->hist->real.max = realtime;
#ifdef HAVE_RUSAGE
- thread->hist->cpu.total += cputime;
- if (thread->hist->cpu.max < cputime)
- thread->hist->cpu.max = cputime;
+ thread->hist->cpu.total += cputime;
+ if (thread->hist->cpu.max < cputime)
+ thread->hist->cpu.max = cputime;
#endif
- ++(thread->hist->total_calls);
- thread->hist->types |= (1 << thread->add_type);
+ ++(thread->hist->total_calls);
+ thread->hist->types |= (1 << thread->add_type);
+ UNLOCK
+ } ;
#ifdef CONSUMED_TIME_CHECK
if (realtime > CONSUMED_TIME_CHECK)
@@ -1200,24 +1488,23 @@ thread_call (struct thread *thread)
* to fix.
*/
zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
- thread->funcname,
+ (thread->hist != NULL) ? thread->hist->funcname : "??",
(unsigned long) thread->func,
realtime/1000, cputime/1000);
}
#endif /* CONSUMED_TIME_CHECK */
- XFREE (MTYPE_THREAD_FUNCNAME, thread->funcname);
}
/* Execute thread */
struct thread *
funcname_thread_execute (struct thread_master *m,
- int (*func)(struct thread *),
+ int (*func)(struct thread *),
void *arg,
int val,
const char* funcname)
{
- struct thread dummy;
+ struct thread dummy;
memset (&dummy, 0, sizeof (struct thread));
@@ -1227,10 +1514,24 @@ funcname_thread_execute (struct thread_master *m,
dummy.func = func;
dummy.arg = arg;
dummy.u.val = val;
- dummy.funcname = strip_funcname (funcname);
+ dummy.hist = thread_get_hist(&dummy, funcname) ;
thread_call (&dummy);
- XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
-
return NULL;
}
+
+/* Second state initialisation if qpthreaded */
+void
+thread_init_r (void)
+{
+ qpt_mutex_init(&thread_mutex, qpt_mutex_quagga);
+}
+
+/* Finished with module */
+void
+thread_finish (void)
+{
+ qpt_mutex_destroy(&thread_mutex, 0);
+}
+
+#undef USE_MQUEUE
diff --git a/lib/thread.h b/lib/thread.h
index 978aa6b0..a38fb19a 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -16,12 +16,17 @@
* 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_THREAD_H
#define _ZEBRA_THREAD_H
+#include <sys/resource.h>
+#include "qtime.h"
+#include "qpnexus.h"
+#include "qtimers.h"
+
struct rusage_t
{
#ifdef HAVE_RUSAGE
@@ -64,22 +69,22 @@ struct thread
{
thread_type type; /* thread type */
thread_type add_type; /* thread type */
- struct thread *next; /* next pointer of the thread */
+ struct thread *next; /* next pointer of the thread */
struct thread *prev; /* previous pointer of the thread */
struct thread_master *master; /* pointer to the struct thread_master. */
int (*func) (struct thread *); /* event function */
void *arg; /* event argument */
union {
- int val; /* second argument of the event. */
+ int val; /* second argument of the event. */
int fd; /* file descriptor in case of read/write. */
- struct timeval sands; /* rest of time sands value. */
+ struct timeval sands; /* rest of time sands value. */
+ qtimer qtr ; /* pointer to related qtimer */
} u;
RUSAGE_T ru; /* Indepth usage info. */
struct cpu_thread_history *hist; /* cache pointer to cpu_history */
- char* funcname;
};
-struct cpu_thread_history
+struct cpu_thread_history
{
int (*func)(struct thread *);
char *funcname;
@@ -163,8 +168,11 @@ enum quagga_clkid {
/* Prototypes. */
extern struct thread_master *thread_master_create (void);
extern void thread_master_free (struct thread_master *);
+extern void thread_init_r (void);
+extern void thread_finish (void);
+extern void thread_set_qtimer_pile(qtimer_pile pile) ;
-extern struct thread *funcname_thread_add_read (struct thread_master *,
+extern struct thread *funcname_thread_add_read (struct thread_master *,
int (*)(struct thread *),
void *, int, const char*);
extern struct thread *funcname_thread_add_write (struct thread_master *,
@@ -190,6 +198,8 @@ extern struct thread *funcname_thread_execute (struct thread_master *,
extern void thread_cancel (struct thread *);
extern unsigned int thread_cancel_event (struct thread_master *, void *);
extern struct thread *thread_fetch (struct thread_master *, struct thread *);
+extern int thread_dispatch(struct thread_master *m) ;
+extern int thread_dispatch_background(struct thread_master *m) ;
extern void thread_call (struct thread *);
extern unsigned long thread_timer_remain_second (struct thread *);
extern int thread_should_yield (struct thread *);
diff --git a/lib/uty.h b/lib/uty.h
new file mode 100644
index 00000000..d1a8b584
--- /dev/null
+++ b/lib/uty.h
@@ -0,0 +1,239 @@
+/* VTY internal stuff -- header
+ * Copyright (C) 1997 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 _ZEBRA_UTY_H
+#define _ZEBRA_UTY_H
+
+#include <stdbool.h>
+
+#include "qpthreads.h"
+#include "qpnexus.h"
+#include "thread.h"
+#include "list_util.h"
+#include "vty.h"
+#include "node_type.h"
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*==============================================================================
+ * This is stuff which is used by the close family of:
+ *
+ * vty
+ * command
+ * command_queue
+ * log
+ *
+ * and which is not for use elsewhere.
+ *
+ * There are also:
+ *
+ * vty_io
+ * vty_cli
+ *
+ * Which are the "immediate family" of vty:
+ *
+ * * *nothing* in their ".h" is for use anywhere except the immediate family.
+ *
+ * * things for use within the rest of the family are published here.
+ */
+
+/*==============================================================================
+ * Variables in vty.c -- used in any of the family
+ */
+extern vty_io vio_list_base ;
+extern vty_io vio_monitors_base ;
+extern vty_io vio_death_watch ;
+
+union vty_watch_dog
+{
+ qtimer qnexus ; /* when running qnexus */
+ struct thread* thread; /* when running threads */
+ void* anon ;
+};
+
+extern union vty_watch_dog vty_watch_dog ;
+
+extern struct thread_master* vty_master ;
+
+extern unsigned long vty_timeout_val ;
+
+extern bool vty_config ;
+
+extern bool no_password_check ;
+extern const bool restricted_mode_default ;
+extern bool restricted_mode ;
+
+char *vty_accesslist_name ;
+char *vty_ipv6_accesslist_name ;
+
+extern qpn_nexus vty_cli_nexus ;
+extern qpn_nexus vty_cmd_nexus ;
+
+/*==============================================================================
+ * To make vty qpthread safe we use a single mutex.
+ *
+ * vty and log recurse through each other, so the same mutex is used
+ * for both, i.e. they are treated as being part of the same monitor.
+ *
+ * A recursive mutex is used. This simplifies the calling from log to vty and
+ * back again. It also allows for the vty internals to call each other.
+ *
+ * There are some "uty" functions which assume the mutex is locked.
+ *
+ * vty is closely bound to the command handling -- the main vty structure
+ * contains the context in which commands are parsed and executed.
+ */
+
+extern qpt_mutex_t vty_mutex ;
+
+#ifdef NDEBUG
+# define VTY_DEBUG 0 /* NDEBUG override */
+#else
+# ifndef VTY_DEBUG
+# define VTY_DEBUG 1 /* Set to 1 to turn on debug checks */
+# endif
+#endif
+
+#if VTY_DEBUG
+
+extern int vty_lock_count ;
+extern int vty_assert_fail ;
+
+#endif
+
+Inline void
+VTY_LOCK(void)
+{
+ qpt_mutex_lock(&vty_mutex) ;
+ if (VTY_DEBUG)
+ ++vty_lock_count ;
+} ;
+
+Inline void
+VTY_UNLOCK(void)
+{
+ if (VTY_DEBUG)
+ --vty_lock_count ;
+ qpt_mutex_unlock(&vty_mutex) ;
+} ;
+
+/* For debug (and documentation) purposes, will VTY_ASSERT_LOCKED where that
+ * is required.
+ *
+ * In some cases, need also to be running in the CLI thread as well.
+ */
+#if VTY_DEBUG
+
+Inline void
+VTY_ASSERT_FAILED(void)
+{
+ if (vty_assert_fail == 0) ;
+ {
+ vty_assert_fail = 1 ;
+ assert(0) ;
+ } ;
+} ;
+
+Inline void
+VTY_ASSERT_LOCKED(void)
+{
+ if (vty_lock_count == 0)
+ VTY_ASSERT_FAILED() ;
+} ;
+
+Inline void
+VTY_ASSERT_CLI_THREAD(void)
+{
+ if (qpthreads_enabled)
+ if (!qpt_thread_is_self(vty_cli_nexus->thread_id))
+ VTY_ASSERT_FAILED() ;
+} ;
+
+#else
+
+#define VTY_ASSERT_LOCKED()
+#define VTY_ASSERT_CLI_THREAD()
+
+#endif
+
+/*==============================================================================
+ * Shared definitions
+ */
+
+enum cli_do
+{
+ cli_do_nothing = 0, /* no action required */
+
+ cli_do_command, /* dispatch the current command line */
+ cli_do_ctrl_c, /* received ^c */
+ cli_do_ctrl_d, /* received ^d on empty line */
+ cli_do_ctrl_z, /* received ^z */
+
+ cli_do_eof, /* hit "EOF" */
+
+ cli_do_count /* number of different cli_do_xxx */
+} ;
+
+/*==============================================================================
+ * Functions in vty.c -- used in any of the family
+ */
+extern enum cmd_return_code uty_command(struct vty *vty) ;
+extern enum cmd_return_code uty_auth (struct vty *vty, const char *buf,
+ enum cli_do cli_do) ;
+extern enum cmd_return_code vty_cmd_exit(struct vty* vty) ;
+extern enum cmd_return_code vty_cmd_end(struct vty* vty) ;
+extern enum cmd_return_code uty_cmd_close(struct vty *vty, const char* reason) ;
+extern enum cmd_return_code uty_stop_input(struct vty *vty) ;
+extern enum cmd_return_code uty_end_config (struct vty *vty) ;
+extern enum cmd_return_code uty_down_level (struct vty *vty) ;
+
+extern bool vty_config_lock (struct vty *, enum node_type node);
+extern void vty_config_unlock (struct vty *, enum node_type node);
+extern void uty_config_unlock (struct vty *vty, enum node_type node) ;
+
+/*==============================================================================
+ * Functions in vty_cli -- used outside the immediate vty family
+ */
+extern void vty_queued_result(struct vty* vty, enum cmd_return_code ret);
+extern void uty_set_host_name(const char* name) ;
+
+/*==============================================================================
+ * Functions in vty_io -- used outside the immediate vty family
+ */
+extern void vty_open_config_write(struct vty* vty, int fd) ;
+extern int vty_close_config_write(struct vty*) ;
+
+extern void vty_log_fixed (const char *buf, size_t len);
+
+extern void uty_log (struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va);
+
+/*==============================================================================
+ * Functions in command.c
+ */
+extern void cmd_post_command(struct vty* vty, int ret) ;
+
+#endif /* _ZEBRA_UTY_H */
diff --git a/lib/vector.c b/lib/vector.c
index 7c148628..e03febdc 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -1,6 +1,9 @@
-/* Generic vector interface routine
+/* Generic vector interface routine -- functions
* Copyright (C) 1997 Kunihiro Ishiguro
*
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * 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
@@ -16,7 +19,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>
@@ -24,166 +27,1262 @@
#include "vector.h"
#include "memory.h"
-/* Initialize vector : allocate memory and return vector. */
+/* Vectors are implemented as a structure which points to an array of pointers
+ * to vector items. That array -- the body of the vector -- can change size,
+ * and therefore may move around in memory.
+ *
+ * The vector structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the vector operations
+ * require the address of the vector structure -- see typedef for vector.
+ *
+ * Vector items are accessed by index, fetching and storing pointers to
+ * those items. Can also push and pop items.
+ *
+ * A vector has a known (logical) end position. Everything beyond the end is
+ * defined to be NULL. When a vector is initialised it is set empty.
+ *
+ * At any given moment the vector body has a limit on the number of items it
+ * can accommodate (the physical end).
+ *
+ * A vector will grow to accommodate what is put into it. Adding items beyond
+ * the (logical) end moves it. Adding items beyond the (physical) limit causes
+ * the body to be extended to suit, and to leave some spare space for future
+ * expansion.
+ *
+ * While the vector is small (see VECTOR_LIMIT_DOUBLE_MAX) the body will grow by
+ * doubling in size. When it is larger, it grows to be multiples of
+ * VECTOR_LIMIT_DOUBLE_MAX.
+ *
+ * Deleting items reduces the (logical) end position, but does NOT release
+ * memory -- the (physical) limit is not changed.
+ *
+ * To release memory: vector_chop will release everything beyond the current
+ * end; vector_decant will create a new body of exactly the current size,
+ * releasing the old body; vector_discard will release everything beyond a
+ * given position.
+ *
+ * NB: you can set a vector item to be NULL. If you set a vector item beyond
+ * the current end, NULL items are inserted in the vector.
+ *
+ * NB: when setting a vector item it is the caller's responsibility to
+ * deallocate any pre-existing value of the item.
+ *
+ * NB: when deleting items it is also the caller's responsibility to deallocate
+ * any values that require it.
+ *
+ * Implementation Notes
+ *
+ * Everything beyond the (logical) end is implicitly NULL.
+ *
+ * Actual memory between (logical) end and (physical) limit is UNDEFINED. So
+ * when advancing the end some care has to be taken to ensure that any new
+ * items in the vector are either set to something or cleared to NULL.
+ *
+ * It would have been possible to ensure that everything between end and limit
+ * is cleared to NULL, but that is more work -- in particular it creates work
+ * when it is not always required.
+ */
+
+#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+/* Initialise a brand new vector, setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, otherwise body of exactly
+ * the required size is allocated.
+ *
+ * NB: discards any existing vector body -- so it is the caller's responsibility
+ * to release any existing body, and any items in that body.
+ */
vector
-vector_init (unsigned int size)
+vector_init_new(vector v, unsigned int size)
{
- vector v = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector));
+ if (v == NULL)
+ v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
+ else
+ memset(v, 0, sizeof(struct vector)) ;
- /* allocate at least one slot */
- if (size == 0)
- size = 1;
+ if (size != 0)
+ {
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(size)) ;
+ v->limit = size ;
+ } ;
- v->alloced = size;
- v->active = 0;
- v->index = XCALLOC (MTYPE_VECTOR_INDEX, sizeof (void *) * size);
- return v;
-}
+ return v ;
+} ;
+
+/* Initialize vector : allocate memory and return vector.
+ * allocates body with at least 1 entry.
+ */
+vector
+vector_init (unsigned int size)
+{
+ return vector_init_new(NULL, size ? size : 1) ; /* at least 1 entry */
+} ;
+/* Basic: free the vector body and the vector structure. */
void
-vector_only_wrapper_free (vector v)
+vector_free (vector v)
{
+ XFREE (MTYPE_VECTOR_BODY, v->p_items);
XFREE (MTYPE_VECTOR, v);
}
+/* Re-Initialize vector (or create new one), setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, but any existing body is
+ * retained. (vector_reset() will discard body.)
+ *
+ * Otherwise ensures existing body is at least the required size, or a body
+ * of exactly the required size is allocated.
+ *
+ * NB: when re-initialising an existing vector it is the caller's
+ * responsibility to release any vector item values *before* doing this.
+ * */
+vector
+vector_re_init(vector v, unsigned int size)
+{
+ if ((v == NULL) || (v->p_items == NULL))
+ return vector_init_new(v, size) ;
+
+ v->end = 0 ;
+
+ if (v->limit < size)
+ {
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(size)) ;
+ v->limit = size ;
+ } ;
+
+ return v ;
+} ;
+
+/* Free the vector body, and free the vector structure or reset it.
+ *
+ * Return NULL if releases vector, otherwise the address of the vector.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+vector
+vector_reset(vector v, int free_structure)
+{
+ if (v == NULL)
+ return NULL ; /* allow for already freed vector */
+
+ if (v->p_items != NULL)
+ XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
+
+ if (free_structure)
+ {
+ XFREE(MTYPE_VECTOR, v) ;
+ return NULL ;
+ }
+ else
+ return vector_init_new(v, 0) ;
+} ;
+
+/* Set vector length to be (at least) the given fixed length.
+ *
+ * There must be a vector.
+ *
+ * Does nothing if the vector is already as long or longer than the given
+ * length.
+ *
+ * If the body is not big enough for the new length, allocates or extends to
+ * exactly the new length. Otherwise, leaves body as it is.
+ *
+ * Appends NULLs as required to extend to the required length.
+ *
+ * Note that the existing contents of the vector are preserved in all cases.
+ */
+extern void
+vector_set_new_min_length(vector v, unsigned int len)
+{
+ assert (v != NULL) ;
+
+ if (len > v->limit)
+ {
+ if (v->p_items == NULL)
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
+ else
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(len));
+ v->limit = len ;
+ } ;
+
+ if (v->end < len)
+ vector_extend(v, len) ;
+} ;
+
+/* Pop item from vector, stepping past any NULLs.
+ *
+ * If vector is empty, free the body and, if required, the vector structure.
+ *
+ * Useful for emptying out and discarding a vector:
+ *
+ * while ((p_v = vector_ream_out(v, 1)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL if vector was empty and has now been freed as required.
+ */
+p_vector_item
+vector_ream(vector v, int free_structure)
+{
+ p_vector_item p_v ;
+
+ if (v == NULL)
+ return NULL ;
+
+ while (v->end != 0)
+ {
+ p_v = v->p_items[--v->end] ;
+ if (p_v != NULL)
+ return p_v ; /* return non-NULL item */
+ } ;
+
+ /* vector is empty: free the body, and (if required) the vector structure. */
+ vector_reset(v, free_structure) ;
+
+ return NULL ; /* signals end */
+} ;
+
+/*==============================================================================
+ * Unset item, condensing and trimming vector.
+ */
+
+/* Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) end of the vector.
+ */
+extern vector_index
+vector_trim(vector v)
+{
+ vector_index i = v->end ;
+ while ((i > 0) && (v->p_items[i - 1] == NULL))
+ --i ;
+ v->end = i ;
+ return i ;
+} ;
+
+/* Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) end of the vector.
+ */
+extern vector_index vector_condense(vector v)
+{
+ vector_index i = 0 ;
+ vector_index j ;
+
+ /* Find first NULL, if any */
+ while ((i < v->end) && (v->p_items[i] != NULL))
+ ++i ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (i == v->end)
+ return i ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = i + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[i++] = v->p_items[j] ;
+
+ v->end = i ;
+
+ return i ;
+} ;
+
+/* Unset item at given index (ie set it NULL).
+ *
+ * Return the old value of the item.
+ *
+ * If the item at the current (logical) end of the vector is NULL, move the
+ * end backwards until finds a non-NULL item, or the vector becomes empty.
+ */
+extern p_vector_item
+vector_unset_item(vector v, vector_index i)
+{
+ p_vector_item was ;
+
+ if (i < v->end)
+ {
+ was = v->p_items[i] ;
+ v->p_items[i] = NULL ;
+ }
+ else if (v->end == 0)
+ return NULL ; /* avoid test for last entry NULL if is empty */
+ else
+ was = NULL ;
+
+ if (v->p_items[v->end - 1] == NULL)
+ vector_trim(v) ;
+
+ return was ;
+} ;
+
+/*==============================================================================
+ * Inserting and deleting items.
+ */
+
+/* Insert item in vector, before item at given position
+ * Move items and extend vector as required.
+ */
void
-vector_only_index_free (void *index)
+vector_insert_item(vector v, vector_index i, void* p_v)
{
- XFREE (MTYPE_VECTOR_INDEX, index);
-}
+ if ((i == v->end) && (i < v->limit))
+ ++v->end ;
+ else
+ vector_insert(v, i, 1) ;
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/* Insert item in vector at given position with rider:
+ *
+ * rider < 0 -- insert before the item at the given position
+ * rider == 0 -- insert at the given position -- REPLACING any existing value
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: when an item is replaced, it is the caller's responsibility to release
+ * any memory used by the item, if required.
+ *
+ * Move items and extend vector as required.
+ */
void
-vector_free (vector v)
+vector_insert_item_here(vector v, vector_index i, int rider,
+ p_vector_item p_v)
{
- XFREE (MTYPE_VECTOR_INDEX, v->index);
- XFREE (MTYPE_VECTOR, v);
-}
+ if (rider == 0)
+ return vector_set_item(v, i, p_v) ;
+
+ if (rider > 0)
+ ++i ; /* insert before next item */
+ vector_insert_item(v, i, p_v) ;
+} ;
+
+/* Delete item from vector.
+ *
+ * Move items as required. Reduces number of items in the vector (unless
+ * the item in question is beyond the end of the vector !)
+ *
+ * NB: it is the caller's responsibility to release memory used by any
+ * current value of the item, if required.
+ *
+ * NB: does NOT change the size of the vector body.
+ */
+p_vector_item
+vector_delete_item(vector v, vector_index i)
+{
+ p_vector_item p_e ;
+ if (i < v->end)
+ {
+ p_e = v->p_items[i] ; /* pick up the current value */
+ if (i != (v->end - 1))
+ vector_delete(v, i, 1) ;
+ else
+ v->end = i ;
+ return p_e ;
+ }
+ else
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Moving items within vector.
+ */
+
+/* Move item in vector from source position to destination position.
+ * Moves intervening items up or down as required.
+ * Extends vector to include the destination, if required.
+ * A source item beyond the end of the vector is implicitly NULL.
+ */
+void
+vector_move_item(vector v, vector_index i_dst, vector_index i_src)
+{
+ p_vector_item* pp_s ;
+ p_vector_item* pp_d ;
+ p_vector_item p_e ;
+
+ vector_index old_end = v->end ;
+
+ /* Worry about whether both source and destination exist. */
+ if (i_dst >= old_end)
+ {
+ vector_insert(v, i_dst, 1) ; /* ensure destination exists */
+ if (i_src >= old_end)
+ return ; /* both were beyond the end */
+ }
+ else if (i_src >= old_end)
+ {
+ i_src = old_end ; /* clamp to just beyond last */
+ vector_insert(v, i_src, 1) ; /* create empty entry */
+ } ;
+
+ if (i_dst == i_src) /* avoid work and edge case */
+ return ;
+
+ /* Both src and dst are within the vector and src != dst */
+ pp_s = &v->p_items[i_src] ; /* address of src entry */
+ pp_d = &v->p_items[i_dst] ; /* address of dst entry */
+ p_e = *pp_s ; /* pick up item to move */
+ if (i_src < i_dst)
+ memmove(pp_s, pp_s+1, P_ITEMS_SIZE(i_dst - i_src)) ;
+ else
+ memmove(pp_d+1, pp_d, P_ITEMS_SIZE(i_src - i_dst)) ;
+ *pp_d = p_e ; /* put down the item to move */
+} ;
+
+/* Move item in vector to given position with rider:
+ *
+ * rider < 0 -- move to before the item at the given position
+ * rider == 0 -- move to replace item at the given position
+ * rider > 0 -- move to after the item at the given position
+ *
+ * NB: it is the caller's responsibility to release the any existing value
+ * that will be replaced.
+ *
+ * NB: replacing an item by itself (rider == 0, i_dst == i_src) deletes it !
+ *
+ * Move items and extend vector as required.
+ */
+void
+vector_move_item_here(vector v, vector_index i_dst, int rider,
+ vector_index i_src)
+{
+ if (rider != 0)
+ {
+ if (i_src < i_dst)
+ {
+ if (rider < 0)
+ --i_dst ; /* moving up places src after dst */
+ }
+ else if (i_src > i_dst)
+ {
+ if (rider > 0)
+ ++i_dst ; /* moving down places src before dst */
+ } ;
+
+ vector_move_item(v, i_dst, i_src) ;
+ }
+ else
+ {
+ /* to replace: copy and then delete. */
+ vector_set_item(v, i_dst, vector_get_item(v, i_src)) ;
+ vector_delete_item(v, i_src) ;
+ } ;
+} ;
+
+/* Reverse vector: reverse the order of items in the vector.
+ */
+void
+vector_reverse(vector v)
+{
+ if (v != NULL)
+ vector_part_reverse(v, 0, v->end) ;
+} ;
+
+/* Reverse portion of vector.
+ */
+void
+vector_part_reverse(vector v, vector_index i, unsigned int n)
+{
+ vector_index j ;
+
+ if (v == NULL)
+ return ;
+
+ if ((i + n) > v->limit)
+ vector_extend(v, i + n) ; /* ensure portion exists */
+
+ if (n <= 1)
+ return ;
+ j = i + n - 1 ; /* j > i, because n > 1 */
+ do
+ {
+ p_vector_item p_i = v->p_items[i] ;
+ v->p_items[i++] = v->p_items[j] ;
+ v->p_items[j--] = p_i ;
+ } while (j > i) ;
+} ;
+
+/*==============================================================================
+ * Copying, moving and appending entire vectors.
+ */
+
+static void vector_new_limit(vector v, vector_index new_end) ;
+
+/* Shallow vector copy -- copies pointers to item values, not the values. */
+/* Creates a new vector. */
+/* NB: copies whole body, including stuff beyond (logical) end ! */
+/* TODO: is this behaviour required ? */
vector
vector_copy (vector v)
{
- unsigned int size;
- vector new = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector));
+ vector new = vector_init_new(NULL, v->limit) ;
- new->active = v->active;
- new->alloced = v->alloced;
+ new->end = v->end;
- size = sizeof (void *) * (v->alloced);
- new->index = XCALLOC (MTYPE_VECTOR_INDEX, size);
- memcpy (new->index, v->index, size);
+ if (v->limit > 0)
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->limit)) ;
return new;
}
-/* Check assigned index, and if it runs short double index pointer */
-void
-vector_ensure (vector v, unsigned int num)
-{
- if (v->alloced > num)
- return;
-
- v->index = XREALLOC (MTYPE_VECTOR_INDEX,
- v->index, sizeof (void *) * (v->alloced * 2));
- memset (&v->index[v->alloced], 0, sizeof (void *) * v->alloced);
- v->alloced *= 2;
-
- if (v->alloced <= num)
- vector_ensure (v, num);
-}
+/* Shallow vector copy -- copies pointers to item values, not the values.
+ * Creates a new vector or re-initialises an existing one.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try copying a vector to itself !!
+ */
+vector
+vector_copy_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
-/* This function only returns next empty slot index. It dose not mean
- the slot's index memory is assigned, please call vector_ensure()
- after calling this function. */
-int
-vector_empty_slot (vector v)
+ dst = vector_re_init(dst, src->limit) ;
+
+ dst->end = src->end;
+
+ if (src->end > 0)
+ memcpy(dst->p_items, src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ return dst ;
+} ;
+
+/* Vector move -- moves body of vector.
+ * Creates a new vector or re-initialises an existing one.
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+vector
+vector_move_here(vector dst, vector src)
{
- unsigned int i;
+ assert((src != NULL) && (src != dst)) ;
- if (v->active == 0)
- return 0;
+ if (dst != NULL)
+ dst = vector_reset(dst, 0) ; /* Reset to deallocate any existing body */
+ else
+ dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
- for (i = 0; i < v->active; i++)
- if (v->index[i] == 0)
- return i;
+ *dst = *src ; /* Copy the vector structure */
- return i;
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
}
+/* Shallow vector copy append -- copies pointers to item values, not the values.
+ * Appends copied pointers to the destination vector.
+ * Creates a new destination vector if required.
+ *
+ * NB: Can append to self.
+ */
+vector
+vector_copy_append(vector dst, vector src)
+{
+ vector_index new_end ;
+
+ assert(src != NULL) ;
+
+ if (dst != NULL)
+ {
+ new_end = dst->end + src->end ;
+ if (new_end > dst->limit)
+ vector_new_limit(dst, new_end) ;
+ }
+ else
+ {
+ new_end = src->end ;
+ vector_init_new(dst, new_end) ;
+ } ;
+
+ if (src->end)
+ memcpy(&dst->p_items[dst->end], src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ dst->end = new_end ; /* Done last, allows for append to self ! */
+ return dst ;
+} ;
+
+/* Vector move append -- moves pointers to item values.
+ * Appends moved pointers to the destination vector.
+ * Creates a new destination vector if required (dst == NULL).
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+vector
+vector_move_append(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if ((dst == NULL) || (dst->end == 0))
+ return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
+
+ vector_copy_append(dst, src) ; /* Extend dst and copy src */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+} ;
+
+/*==============================================================================
+ * Portmanteau splice/extract/replace function.
+ *
+ * All take a portion of 'src' vector and:
+ *
+ * splice:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * copying the replaced portion of the 'dst' vector to the 'to' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to', 'dst' and 'src' must be distinct vectors.
+ *
+ * extract:
+ *
+ * a) copy a portion of the 'src' vector to the 'to' vector
+ * c) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- NULL
+ * i_dst -- ignored
+ * n_dst -- ignored
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to' and 'src' must be distinct vectors.
+ *
+ * replace:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- false
+ * to -- ignored
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: original 'to' argument
+ *
+ * NB: 'dst' and 'src' must be distinct vectors.
+ *
+ * All copies are shallow -- pointers to item values are copied, not the values.
+ *
+ * NB: any existing contents of the 'to' vector are discarded.
+ *
+ * NB: it is the caller's responsibility to release memory allocated to any
+ * items which are discarded in these operations.
+ *
+ * NB: for splice and replace, the resulting destination vector will be at
+ * least i_dst + n_src long. (Even if is copying actual or implied NULLs
+ * from the source.)
+ *
+ * NB: where new vectors are created, they will be of exactly the required size.
+ *
+ * NB: where an existing vector is reused, it is the caller's responsibility
+ * to ensure the vector structure is currently valid (by vector_init_new()
+ * or by ensuring it is zeroized).
+ */
+vector
+vector_sak(int to_copy, vector to,
+ vector dst, vector_index i_dst, unsigned int n_dst,
+ vector src, vector_index i_src, unsigned int n_src, int src_move)
+{
+ int dst_replace ; /* true => replace portion of 'dst' */
+
+ vector_index new_dst_end = 0 ; /* new end of dst */
+
+ unsigned int n_dst_nulls ; /* number of implicit NULLs to add */
+ unsigned int n_dst_move ; /* number of items to move up or down */
+ unsigned int n_src_real ; /* number of items to really copy */
+ unsigned int n_src_nulls ; /* number of implicit NULLs to "copy" */
+
+ assert((to == NULL) || (dst == NULL) || (to != dst)) ;
+ assert((src == NULL) || (dst == NULL) || (src != dst)) ;
+
+ /* Worry about how much we really have in the source vector. */
+
+ n_src_real = n_src ; /* assume all real */
+ n_src_nulls = 0 ; /* so no NULLs to "copy" */
+
+ if (n_src != 0)
+ {
+ if ((src == NULL) || (i_src >= src->end))
+ n_src_real = 0 ;
+ else if ((i_src + n_src) > src->end)
+ n_src_real = src->end - i_src ;
+ n_src_nulls = n_src - n_src_real ;
+ } ;
+
+ /* If no 'dst' vector, then this is an extract. */
+
+ n_dst_move = 0 ; /* assume nothing to move */
+ n_dst_nulls = 0 ; /* assume no NULLs to add */
+
+ if (dst == NULL)
+ /* For extract: set up dst, i_dst and n_dst so that can copy to the */
+ /* 'to' vector as if from 'dst'. */
+ {
+ dst_replace = 0 ; /* no replacement operation */
+ dst = src ; /* copy from here */
+ i_dst = i_src ;
+ n_dst = n_src_real ;
+ }
+ else
+ /* Reduce n_dst to the number of actual items to be replaced. */
+ /* */
+ /* Calculate the new end of 'dst'. */
+ {
+ dst_replace = 1 ; /* have replacement to do */
+ if (i_dst >= dst->end)
+ /* If i_dst is beyond the end of 'dst', then there is nothing */
+ /* to replace (so set n_dst == 0). Will be adding n_src items */
+ /* at i_dst -- so new end must be i_dst + n_src. */
+ {
+ n_dst_nulls = i_dst - dst->end ; /* fill from end to i_dst */
+ n_dst = 0 ; /* nothing to replace */
+ new_dst_end = i_dst + n_src ; /* all beyond current end */
+ }
+ else
+ /* If i_dst + n_dst is beyond the end of 'dst', reduce n_dst to */
+ /* number of items up to the end. */
+ /* Will remove n_dst items and insert n_src, so end will move */
+ /* by n_src - n_dst. */
+ {
+ if ((i_dst + n_dst) > dst->end)
+ n_dst = dst->end - i_dst ; /* what we actually replace */
+ else if (n_dst != n_src)
+ n_dst_move = dst->end - (i_dst + n_dst) ;
+ /* what we move up or down */
+
+ new_dst_end = dst->end + n_src - n_dst ;
+ /* end depends on amount added */
+ /* & amount actually replaced */
+ } ;
+ } ;
+
+ /* Copy portion of 'dst' (or of 'src') to 'to', if required. */
+ /* */
+ /* Have arranged: n_dst -- number of items to copy, all existent */
+ /* dst -- vector to copy from -- if n_dst > 0 */
+ /* i_dst -- first item to copy -- if n_dst > 0 */
+
+ if (to_copy)
+ {
+ to = vector_re_init(to, n_dst) ; /* reinitialise or create */
+ to->end = n_dst ;
+ if (n_dst > 0)
+ memcpy(to->p_items, &dst->p_items[i_dst], P_ITEMS_SIZE(n_dst)) ;
+ } ;
+
+ /* Replace portion of 'dst' by portion of 'src', if required. */
+ /* */
+ /* Have arranged: */
+ /* */
+ /* new_dst_end -- end of dst once dust settles */
+ /* n_dst_nulls -- number of NULLs to insert at dst->end to fill up */
+ /* to i_dst (when i_dst is beyond old end.) */
+ /* n_dst_move -- number of items in dst to move up or down to */
+ /* leave n_src item hole at i_dst to fill in. */
+ /* n_src_real -- number of real src items at i_src to copy to dst */
+ /* at i_dst. */
+ /* n_src_nulls -- number of nulls to add to fill to i_dst + n_src. */
+
+ if (dst_replace)
+ {
+ if (new_dst_end > dst->limit) /* extend body if required */
+ vector_new_limit(dst, new_dst_end) ;
+
+ if (n_dst_nulls > 0)
+ memset(&dst->p_items[dst->end], 0, P_ITEMS_SIZE(n_dst_nulls)) ;
+ if (n_dst_move > 0)
+ memmove(&dst->p_items[i_dst + n_dst], &dst->p_items[i_dst + n_src],
+ P_ITEMS_SIZE(n_dst_move)) ;
+ if (n_src_real > 0)
+ memcpy(&dst->p_items[i_dst], &src->p_items[i_src],
+ P_ITEMS_SIZE(n_src_real)) ;
+ if (n_src_nulls > 0)
+ memset(&dst->p_items[i_dst + n_src_real], 0,
+ P_ITEMS_SIZE(n_src_nulls)) ;
+ dst->end = new_dst_end ;
+ } ;
+
+ /* Delete portion of 'src', if required (and have 'src' !) */
+
+ if (src_move && (n_src_real != 0))
+ vector_delete(src, i_src, n_src_real) ;
+
+ /* Done -- return 'to' vector as promised. */
+
+ return to ;
+} ;
+
+/*==============================================================================
+ * Legacy Vector Operations
+ */
+
/* Set value to the smallest empty slot. */
int
vector_set (vector v, void *val)
{
- unsigned int i;
+ vector_index i;
- i = vector_empty_slot (v);
- vector_ensure (v, i);
+ i = 0 ;
+ while (1)
+ {
+ if (i == v->end)
+ {
+ i = vector_extend_by_1(v) ;
+ break ;
+ }
+
+ if (v->p_items[i] == NULL)
+ break ;
- v->index[i] = val;
+ ++i ;
+ } ;
- if (v->active <= i)
- v->active = i + 1;
+ v->p_items[i] = val;
return i;
}
/* Set value to specified index slot. */
int
-vector_set_index (vector v, unsigned int i, void *val)
+vector_set_index (vector v, vector_index i, void *val)
{
vector_ensure (v, i);
-
- v->index[i] = val;
-
- if (v->active <= i)
- v->active = i + 1;
-
+ v->p_items[i] = val;
return i;
}
/* Look up vector. */
-void *
-vector_lookup (vector v, unsigned int i)
+p_vector_item
+vector_lookup (vector v, vector_index i)
{
- if (i >= v->active)
+ if (i >= v->end)
return NULL;
- return v->index[i];
+ return v->p_items[i];
}
/* Lookup vector, ensure it. */
-void *
-vector_lookup_ensure (vector v, unsigned int i)
+p_vector_item
+vector_lookup_ensure (vector v, vector_index i)
{
vector_ensure (v, i);
- return v->index[i];
+ return v->p_items[i];
}
-/* Unset value at specified index slot. */
+/* Count the number of not empty slots. */
+vector_index
+vector_count (vector v)
+{
+ vector_index i;
+ unsigned count = 0;
+
+ for (i = 0; i < v->end; i++)
+ if (v->p_items[i] != NULL)
+ count++;
+
+ return count;
+}
+
+/*==============================================================================
+ * Sorting and Searching vector.
+ */
+
+/* Sort the given vector.
+ *
+ * NB: the comparison function receives a pointer to the pointer to the
+ * vector item's value.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+void
+vector_sort(vector v, vector_sort_cmp* cmp)
+{
+ if (v->end <= 1)
+ return ; /* Stop dead if 0 or 1 items */
+
+ typedef int qsort_cmp(const void*, const void*) ;
+
+ qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
+} ;
+
+/* Perform binary search on the given vector.
+ *
+ * The vector MUST be sorted in the order implied by the comparison function
+ * given. The vector need not contain unique values, but this search makes
+ * no effort to select any particular instance of a sequence of equals.
+ *
+ * Returns:
+ *
+ * result == 0: found an equal value.
+ * index returned is of first entry found which is equal to
+ * the value sought. There may be other equal values, before
+ * and/or after this one in the vector.
+ *
+ * result == +1: value not found and vector not empty.
+ * index returned is of the largest entry whose value is less
+ * than the value sought.
+ * (The value sought belongs after this point.)
+ *
+ * result == -1: value is less than everything in the vector, or the
+ * vector is empty.
+ * index returned is 0
+ *
+ * NB: The comparison function takes arguments which are:
+ *
+ * const void** pointer to pointer to value being searched for.
+ * const void** pointer to pointer to vector item to compare with
+ *
+ * The value being searched for need not be in the same form as a vector
+ * item. However, if it is then the same comparison function can be used
+ * to sort and search.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+vector_index
+vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
+ int* result)
+{
+ vector_index il, iv, ih ;
+ int c ;
+
+ if (v->end <= 1)
+ {
+ *result = (v->end == 0) ? -1 :
+ cmp(&p_val, (const void* const*)&v->p_items[0]) ;
+ return 0 ; /* Stop dead if 0 or 1 items */
+ } ;
+
+ /* We have at least two items. */
+ il = 0 ;
+ ih = v->end - 1 ;
+
+ /* Pick off the edge cases: >= last and <= first. */
+ if ((c = cmp(&p_val, (const void* const*)&v->p_items[ih])) >= 0)
+ {
+ *result = c ; /* 0 => found. +1 => val > last */
+ return ih ; /* return high index. */
+ } ;
+ if ((c = cmp(&p_val, (const void* const*)&v->p_items[il])) <= 0)
+ {
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
+ }
+
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
+ while (1)
+ {
+ iv = (il + ih) / 2 ;
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ return il ; /* return il: item[il] < val < item[il+1] */
+ } ;
+ /* We now know that il < iv < ih */
+ c = cmp(&p_val, (const void* const*)&v->p_items[iv]) ;
+ if (c == 0)
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
+ if (c < 0)
+ ih = iv ; /* step down iv > il, so new ih > il */
+ else
+ il = iv ; /* step up iv < ih, so new il < ih */
+ } ;
+} ;
+
+/*==============================================================================
+ * Mechanics for adding/deleting items and managing the vector (logical) end
+ * and (physical) limit.
+ */
+
+/* Extract the LS bit of unsigned integer 'x'. */
+#define lsbit(x) ((x) & ((~(x)) + 1))
+/* Round 'x' up to a multiple of 'm' */
+#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
+
+/* Set new limit to suit new end for given vector.
+ *
+ * The new limit will be at least: VECTOR_LIMIT_MIN.
+ *
+ * While the vector is relatively small, the limit is doubled until there
+ * is at least 1/8 of the new vector free.
+ *
+ * Beyond VECTOR_LIMIT_DOUBLE_MAX, however, the limit is set to the
+ * smallest multiple of VECTOR_LIMIT_DOUBLE_MAX which gives at least
+ * VECTOR_LIMIT_SLACK_MIN free entries beyond the new end.
+ *
+ * This is an attempt to balance the cost of repeated reallocations of
+ * memory against the cost of possible wasted space at the end of the
+ * vector.
+ *
+ * NB: the new_limit depends entirely on the new end position. (Current
+ * end position is ignored.)
+ *
+ * NB: the new limit may be less than the current limit, in which case the
+ * vector body is reduced in size.
+ *
+ * Except for any size set when the vector is initialised, the vector body
+ * size will be a power of 2 or a multiple of VECTOR_LIMIT_DOUBLE_MAX.
+ * (Vectors are regular in their habits, which may help the memory allocator).
+ *
+ * TODO: what to do if calculation of new_limit overflows, or calculation
+ * of P_ITEMS_SIZE will ?
+ */
+static void
+vector_new_limit(vector v, vector_index new_end)
+{
+ vector_index old_limit = v->limit ;
+ vector_index new_limit ;
+
+ if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
+ {
+ new_limit = multiple(new_end + VECTOR_LIMIT_SLACK_MIN,
+ VECTOR_LIMIT_DOUBLE_MAX) ;
+ }
+ else
+ {
+ /* Want the new_limit to be a power of 2. */
+ /* If the old_limit was a power of 2, start from there. */
+ /* Otherwise start from a power of 2 less than new_end: either the */
+ /* minimum value or a value mid way to VECTOR_LIMIT_DOUBLE_MAX. */
+ if ( (old_limit != 0) && (old_limit == lsbit(old_limit))
+ && (new_end >= old_limit) )
+ new_limit = old_limit ;
+ else
+ new_limit = (new_end < VECTOR_LIMIT_MID) ? VECTOR_LIMIT_MIN
+ : VECTOR_LIMIT_MID ;
+ while (new_end > ((new_limit * 7) / 8))
+ new_limit *= 2 ;
+ } ;
+
+ v->p_items =
+ XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+
+ v->limit = new_limit ;
+} ;
+
+/* Extend vector and set new (logical) end.
+ *
+ * Extends body if required.
+ * Ensures everything between old and new end is set NULL.
+ *
+ * NB: expects new end > old end, but copes with new end <= old end.
+ */
+void
+vector_extend(vector v, vector_index new_end)
+{
+ vector_index old_end = v->end ;
+
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (new_end > old_end)
+ memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
+} ;
+
+/* Insert entries into vector: insert 'n' NULL entries at location 'i'.
+ *
+ * Updates end (and limit) to be at least 'i' + 'n'.
+ * (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
+ */
+void
+vector_insert(vector v, vector_index i, unsigned int n)
+{
+ vector_index old_end, new_end ;
+ unsigned int n_above ;
+
+ /* If i < old end, then we are inserting n NULLs, and need
+ * to shuffle at least one item up.
+ * else we are setting new end to i + n and need to NULL
+ * fill from old end to the new end.
+ */
+ old_end = v->end ;
+ if (i < old_end)
+ {
+ if (n == 0)
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ new_end = old_end + n ;
+ }
+ else
+ {
+ n_above = 0 ; /* nothing to shuffle up. */
+ new_end = i + n ;
+ i = old_end ; /* where to zeroize from */
+ n = new_end - old_end ; /* how much to zeroize */
+ } ;
+
+ /* Now we extend the body if we need to. */
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (n_above > 0)
+ memmove(&v->p_items[i + n], &v->p_items[i], P_ITEMS_SIZE(n_above)) ;
+
+ memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
+} ;
+
+/* Delete items from vector: delete 'n' items at location 'i'.
+ *
+ * Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
+ *
+ * Deletes from 'i' to end if less than 'n' items to the end.
+ *
+ * NB: does NOT change the size of the body.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being deleted.
+*/
void
-vector_unset (vector v, unsigned int i)
+vector_delete(vector v, vector_index i, unsigned int n)
{
- if (i >= v->alloced)
- return;
+ vector_index old_end, new_end ;
+
+ old_end = v->end ;
- v->index[i] = NULL;
+ if ((i >= old_end) || (n == 0))
+ return ;
- if (i + 1 == v->active)
+ /* If i + n < old_end, we have 1 or more items to keep and move down */
+ if ((i + n) < old_end)
{
- v->active--;
- while (i && v->index[--i] == NULL && v->active--)
- ; /* Is this ugly ? */
+ memmove(&v->p_items[i], &v->p_items[i + n],
+ P_ITEMS_SIZE(old_end - (i + n))) ;
+ new_end = old_end - n ;
}
-}
+ else
+ {
+ new_end = i ; /* We are keeping nothing above 'i' */
+ /* NB: new_end < old_end */
+ } ;
-/* Count the number of not emplty slot. */
-unsigned int
-vector_count (vector v)
+ v->end = new_end ; /* account for stuff dropped */
+} ;
+
+/* Discard entries from vector: discard entries from location 'i' onwards.
+ *
+ * Releases memory from 'i' onwards.
+ * Releases the body altogether if this sets the vector empty ('i' == 0).
+ * Sets new end of vector iff 'i' < current end.
+ *
+ * Does nothing if 'i' is beyond current limit (physical end) of vector.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being discarded.
+*/
+void
+vector_discard(vector v, vector_index i)
{
- unsigned int i;
- unsigned count = 0;
+ if (i >= v->limit)
+ return ;
- for (i = 0; i < v->active; i++)
- if (v->index[i] != NULL)
- count++;
+ if (i == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ v->limit = i ;
+ if (i < v->end)
+ v->end = i ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(i)) ;
+ } ;
+} ;
- return count;
-}
+/* Chop vector at the current (logical) end.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_chop(vector v)
+{
+ vector_index new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else if (new_limit != v->limit)
+ {
+ v->limit = new_limit ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ } ;
+} ;
+
+/* Decant vector into a new body allocated to current logical size, and
+ * release the old body.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_decant(vector v)
+{
+ p_vector_item* p_old_body ;
+ vector_index new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ p_old_body = v->p_items ;
+
+ vector_init_new(v, new_limit) ; /* initialise with new body */
+
+ memcpy(v->p_items, p_old_body, P_ITEMS_SIZE(new_limit)) ;
+ /* copy the old body across */
+ v->end = new_limit ;
+
+ XFREE(MTYPE_VECTOR_BODY, p_old_body) ;
+ } ;
+} ;
diff --git a/lib/vector.h b/lib/vector.h
index 6b27fd96..e69e628a 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -1,7 +1,9 @@
-/*
- * Generic vector interface header.
+/* Generic vector interface header.
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * 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
@@ -17,47 +19,337 @@
* 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_VECTOR_H
#define _ZEBRA_VECTOR_H
-/* struct for vector */
-struct _vector
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*------------------------------------------------------------------------------
+ * types and struct for vector
+ *
+ * NB: an entirely zero structure represents an entirely empty vector.
+ *
+ * TODO: could force vector_index to be 32 bits ?
+ */
+typedef void* p_vector_item ;
+typedef unsigned int vector_index ;
+
+typedef struct vector* vector ; /* pointer to vector structure */
+typedef struct vector vector_t ; /* embedded vector structure */
+struct vector
{
- unsigned int active; /* number of active slots */
- unsigned int alloced; /* number of allocated slot */
- void **index; /* index to data */
+ p_vector_item* p_items ; /* pointer to array of vector item pointers */
+ vector_index end ; /* number of "active" item entries */
+ vector_index limit ; /* number of allocated item entries */
};
-typedef struct _vector *vector;
-#define VECTOR_MIN_SIZE 1
+/* Under very controlled circumstances, may access the vector body */
+typedef p_vector_item const* vector_body_t ;
+
+/* Values that control the allocation of the vector body. */
+/* NB: these must all be powers of 2. */
+
+/* The body, when allocated, will have at least this many entries. */
+#define VECTOR_LIMIT_MIN 8
+/* When the body grows, it doubles in size, until it is this big. */
+/* After that it grows in units of this much. */
+#define VECTOR_LIMIT_DOUBLE_MAX 2048
+/* "Midway" between VECTOR_LIMIT_MIN and VECTOR_LIMIT_DOUBLE_MAX. */
+#define VECTOR_LIMIT_MID 128
+/* When growing in units of VECTOR_LIMIT_DOUBLE_MAX, this is the */
+/* minimum slack space to leave after the logical end of the vector. */
+#define VECTOR_LIMIT_SLACK_MIN ((VECTOR_LIMIT_DOUBLE_MAX) / 8)
+
+/* (Sometimes) useful macros. */
-/* (Sometimes) usefull macros. This macro convert index expression to
- array expression. */
-/* Reference slot at given index, caller must ensure slot is active */
-#define vector_slot(V,I) ((V)->index[(I)])
-/* Number of active slots.
- * Note that this differs from vector_count() as it the count returned
- * will include any empty slots
+/* Reference item at given index. */
+/* NB: does not guarantee the item is "active" (that is: within the */
+/* (logical) vector) -- but see vector_ensure. */
+/* Returns address of vector item. */
+/* See: VECTOR_ITEMS() for walking items in a vector. */
+/* See: vector_get_item(), which is preferable. */
+#define vector_slot(V,I) ((V)->p_items[(I)])
+
+/* Number of "active" item entries -- (logical) end of the vector. */
+/* Note that this differs from vector_count() as this count will */
+/* include any NULL items. */
+#define vector_active(V) ((V)->end)
+
+/* To walk all items in a vector:
+ *
+ * vector_index i ;
+ * xxxxx* p_v ;
+ *
+ * for (VECTOR_ITEMS(v, p_v, i))
+ * {
+ * ... i is index of current item
+ * ... p_v is address of current item value -- may be NULL
+ * } ;
+ *
+ * ... i is number of items in vector (including any NULLs)
+ */
+#define VECTOR_ITEMS(v, p_v, i)\
+ (i) = 0 ;\
+ (i) < (v)->end ? (((p_v) = (void*)(v)->p_items[i]), 1) \
+ : (((p_v) = NULL), 0) ;\
+ ++(i)
+
+/*==============================================================================
+ * Prototypes.
*/
-#define vector_active(V) ((V)->active)
-/* Prototypes. */
extern vector vector_init (unsigned int size);
-extern void vector_ensure (vector v, unsigned int num);
-extern int vector_empty_slot (vector v);
+Inline void vector_ensure(vector v, vector_index i) ;
extern int vector_set (vector v, void *val);
-extern int vector_set_index (vector v, unsigned int i, void *val);
-extern void vector_unset (vector v, unsigned int i);
-extern unsigned int vector_count (vector v);
-extern void vector_only_wrapper_free (vector v);
-extern void vector_only_index_free (void *index);
+extern int vector_set_index (vector v, vector_index i, void *val);
+#define vector_unset(v, i) (void)vector_unset_item(v, i)
+extern vector_index vector_count (vector v);
extern void vector_free (vector v);
extern vector vector_copy (vector v);
-extern void *vector_lookup (vector, unsigned int);
-extern void *vector_lookup_ensure (vector, unsigned int);
+extern void *vector_lookup (vector, vector_index);
+extern void *vector_lookup_ensure (vector, vector_index);
+
+extern vector vector_init_new(vector v, unsigned int size) ;
+extern vector vector_re_init(vector v, unsigned int size) ;
+extern vector vector_reset(vector v, int free_structure) ;
+extern p_vector_item vector_ream(vector v, int free_structure) ;
+
+/* Reset vector and free the vector structure. */
+#define vector_reset_free(v) vector_reset(v, 1)
+/* Reset vector but free the heap structure. */
+#define vector_reset_keep(v) vector_reset(v, 0)
+/* Ream out vector and free the vector structure. */
+#define vector_ream_free(v) vector_ream(v, 1)
+/* Ream out vector but keep the vector structure. */
+#define vector_ream_keep(v) vector_ream(v, 0)
+
+Inline void vector_set_min_length(vector v, unsigned int len) ;
+extern void vector_set_new_min_length(vector v, unsigned int len) ;
+
+Inline void vector_set_length(vector v, unsigned int len) ;
+#define vector_set_end(v, l) vector_set_length(v, l)
+
+Inline vector_index vector_length(vector v) ;
+#define vector_end(v) vector_length(v)
+Inline int vector_is_empty(vector v) ;
+
+Inline vector_body_t vector_body(vector v) ;
+
+Inline p_vector_item vector_get_item(vector v, vector_index i) ;
+Inline p_vector_item vector_get_first_item(vector v) ;
+Inline p_vector_item vector_get_last_item(vector v) ;
+Inline void vector_set_item(vector v, vector_index i, p_vector_item p_v) ;
+Inline void vector_assign_item(vector v, vector_index dst, vector_index src) ;
+extern p_vector_item vector_unset_item(vector v, vector_index i) ;
+extern vector_index vector_trim(vector v) ;
+extern vector_index vector_condense(vector v) ;
+
+extern void vector_insert_item(vector v, vector_index i, p_vector_item p_v) ;
+extern void vector_insert_item_here(vector v, vector_index i, int rider,
+ p_vector_item p_v) ;
+extern void vector_move_item(vector v, vector_index dst, vector_index src) ;
+extern void vector_move_item_here(vector v, vector_index dst, int rider,
+ vector_index src) ;
+extern p_vector_item vector_delete_item(vector v, vector_index i) ;
+extern void vector_reverse(vector v) ;
+extern void vector_part_reverse(vector v, vector_index i, unsigned int n) ;
+
+Inline void vector_push_item(vector v, p_vector_item p_v) ;
+Inline p_vector_item vector_pop_item(vector v) ;
+
+extern void vector_insert(vector v, vector_index i, unsigned int n) ;
+extern void vector_delete(vector v, vector_index i, unsigned int n) ;
+
+typedef int vector_bsearch_cmp(const void* const* pp_val,
+ const void* const* item) ;
+vector_index vector_bsearch(vector v, vector_bsearch_cmp* cmp,
+ const void* p_val, int* result) ;
+typedef int vector_sort_cmp(const void* const* a, const void* const* b) ;
+void vector_sort(vector v, vector_sort_cmp* cmp) ;
+
+extern vector vector_copy_here(vector dst, vector src) ;
+extern vector vector_move_here(vector dst, vector src) ;
+extern vector vector_copy_append(vector dst, vector src) ;
+extern vector vector_move_append(vector dst, vector src) ;
+
+#define vector_copy_extract(to, src, i_src, n_src) \
+ vector_sak(1, to, NULL, 0, 0, src, i_src, n_src, 0)
+#define vector_move_extract(to, src, i_src, n_src) \
+ vector_sak(1, to, NULL, 0, 0, src, i_src, n_src, 1)
+#define vector_copy_splice(to, dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(1, to, dst, i_dst, n_dst, src, i_src, n_src, 0)
+#define vector_move_splice(to, dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(1, to, dst, i_dst, n_dst, src, i_src, n_src, 1)
+#define vector_copy_replace(dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(0, NULL, dst, i_dst, n_dst, src, i_src, n_src, 0)
+#define vector_move_replace(dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(0, NULL, dst, i_dst, n_dst, src, i_src, n_src, 1)
+
+extern vector vector_sak(int to_copy, vector to,
+ vector dst, vector_index i_dst, unsigned int n_dst,
+ vector src, vector_index i_src, unsigned int n_src, int src_move) ;
+
+extern void vector_discard(vector v, vector_index i) ;
+extern void vector_chop(vector v) ;
+extern void vector_decant(vector v) ;
+
+Inline vector_index vector_extend_by_1(vector v) ;
+extern void vector_extend(vector v, vector_index new_end) ;
+
+/*==============================================================================
+ * The inline functions:
+ */
+
+/* Extend vector by one item at the end, which is about to be set. */
+/* Returns index of new least item in the vector. */
+/* NB: if left unset, the item may be UNDEFINED. */
+Inline vector_index
+vector_extend_by_1(vector v)
+{
+ vector_index i = v->end ;
+
+ if (i < v->limit)
+ return v->end++ ; /* simple if we have room */
+
+ vector_extend(v, i + 1) ; /* the hard way */
+ return i ;
+} ;
+
+/* Ensure given index is "active". */
+/* Adjusts logical and physical end of the vector as required, filling */
+/* with NULLs upto any new logical end. */
+Inline void
+vector_ensure(vector v, vector_index i)
+{
+ if (i < v->end) /* trivial if within vector */
+ return ;
+ if ((i == v->end) && (i < v->limit)) /* simple if end and have room */
+ v->p_items[v->end++] = NULL ; /* set NULL for complete safety */
+ else
+ vector_extend(v, i + 1) ; /* do it the hard way */
+} ;
+
+/* Want vector to be at least the given length. */
+/* */
+/* Adjusts logical and physical end of the vector as required, filling */
+/* with NULLs upto any new logical end -- does not allocate any more */
+/* than is exactly necessary. */
+Inline void
+vector_set_min_length(vector v, unsigned int len)
+{
+ if (len > v->end) /* will not reduce the length */
+ vector_set_new_min_length(v, len) ;
+} ;
+
+/* Want vector to be the given length. */
+/* */
+/* If this is less than the current length, items are discarded. It */
+/* is the caller's responsibility to have freed anything that needs it. */
+/* */
+/* Adjusts logical and physical end of the vector as required, filling */
+/* with NULLs upto any new logical end -- does not allocate any more */
+/* than is exactly necessary. */
+Inline void
+vector_set_length(vector v, unsigned int len)
+{
+ if (len > v->end)
+ vector_set_new_min_length(v, len) ; /* Extend if new length greater */
+ else
+ v->end = len ; /* chop */
+} ;
+
+/* Return index of end of vector (index of last item + 1) */
+Inline vector_index
+vector_length(vector v)
+{
+ return v->end ;
+} ;
+
+/* Returns whether vector is empty or not. */
+Inline int
+vector_is_empty(vector v)
+{
+ return (v->end == 0) ;
+} ;
+
+/* Returns highly restricted pointer to vector body */
+//Inline const void* const*
+//vector_body(vector v)
+//{
+// return (const void* const*)v->p_items ;
+//} ;
+
+Inline vector_body_t
+vector_body(vector v)
+{
+ return (vector_body_t)v->p_items ;
+} ;
+
+/* Access functions -- Inline for obvious reasons. */
+
+/* Get pointer to item. Returns NULL if accessing beyond end. */
+Inline p_vector_item
+vector_get_item(vector v, vector_index i)
+{
+ return (i < v->end) ? v->p_items[i] : NULL ;
+} ;
+
+/* Get pointer to first item. Returns NULL if vector empty. */
+Inline p_vector_item
+vector_get_first_item(vector v)
+{
+ return (v->end != 0) ? v->p_items[0] : NULL ;
+} ;
+
+/* Get pointer to last item. Returns NULL if vector empty. */
+Inline p_vector_item
+vector_get_last_item(vector v)
+{
+ return (v->end != 0) ? v->p_items[v->end - 1] : NULL ;
+} ;
+
+/* Set item value in vector. Extend vector if required. */
+/* NB: it is the caller's responsibility to release memory used by any */
+/* current value of the item, if required. */
+Inline void
+vector_set_item(vector v, vector_index i, void* p_v)
+{
+ vector_ensure(v, i) ;
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/* Set dst item to be a copy of the src item. Extend vector if required.
+ *
+ * NB: it is the caller's responsibility to look after the memory being
+ * used by the current dst item or the new (duplicated) src item.
+ */
+Inline void
+vector_assign_item(vector v, vector_index dst, vector_index src)
+{
+ vector_set_item(v, dst, vector_get_item(v, src)) ;
+} ;
+
+/* Push value onto vector, extending as required. */
+Inline void
+vector_push_item(vector v, void* p_v)
+{
+ vector_index i = vector_extend_by_1(v) ;
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/* Pop value from vector. Returns NULL if vector is empty. */
+/* NB: does NOT change the size of the vector body. */
+Inline p_vector_item
+vector_pop_item(vector v)
+{
+ return (v->end > 0) ? v->p_items[--v->end] : NULL ;
+} ;
#endif /* _ZEBRA_VECTOR_H */
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
new file mode 100644
index 00000000..685f33ec
--- /dev/null
+++ b/lib/vio_fifo.c
@@ -0,0 +1,1165 @@
+/* VTY I/O FIFO
+ * Copyright (C) 2010 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 <stddef.h>
+#include <string.h>
+
+#include "vio_fifo.h"
+#include "network.h"
+#include "list_util.h"
+
+#include "memory.h"
+#include "zassert.h"
+
+/*==============================================================================
+ * VTY I/O FIFO manages an arbitrary length byte-wise FIFO buffer.
+ *
+ * The FIFO is arranged as lumps of some given size. Lumps are allocated
+ * as and when necessary, and released once emptied.
+ *
+ * The last lump is never released. So, it may be that only one lump is
+ * ever needed.
+ *
+ * When releasing lumps, keeps one lump "spare", to be reused as necessary.
+ * This is used in ... TODO <<<< And is released...
+ *
+ *------------------------------------------------------------------------------
+ * Implementation notes:
+ *
+ * The FIFO is initialised with all pointers NULL -- so with no lumps at all.
+ *
+ * Once a lump has been allocated there is always one lump in the FIFO.
+ *
+ * The following are expected to be true:
+ *
+ * * put_ptr == get_ptr => FIFO empty
+ *
+ * * put_ptr == tail->end -- at all times (NULL when no lumps)
+ *
+ * put_ptr >= tail->data ) otherwise something is broken
+ * put_ptr <= tail->end )
+ *
+ * * get_ptr == head->end -- when there is more than one lump
+ * get_ptr <= put_ptr -- when there is only one lump
+ *
+ * get_ptr >= head->data ) otherwise something is broken
+ * get_ptr <= head->end )
+ *
+ * * put_ptr == put_end => tail lump is full
+ * put_ptr < put_end => space exists in the tail lump
+ * put_ptr > put_end => broken
+ *
+ * * get_ptr == get_end => head lump is empty
+ * BUT if there is only one lump, make sure that
+ * get_end == put_ptr.
+ * get_ptr < get_end => data exists in the head lump
+ * get_ptr > get_end => broken
+ *
+ * Note that:
+ *
+ * * when the get_ptr reaches the put_ptr the pointers are reset to the
+ * start of the one and only lump.
+ *
+ * Everywhere that the get_ptr is moved, must check for meeting the
+ * put_ptr and reset pointers. At the same time, when reaches the end of
+ * a lump, gets rid of it.
+ *
+ * * when advancing the put_ptr does not check for advancing the get_end.
+ *
+ * The one exception to this, is that when the put_ptr advances to a new
+ * block, if there was one lump, sets the get_end to the end of that block.
+ *
+ * Everywhere that the get_end is used, must check for there being one
+ * lump and the possibility that put_ptr has changed.
+ */
+
+/*==============================================================================
+ * Initialisation, allocation and freeing of FIFO and lumps thereof.
+ */
+
+/* Return default size, or given size rounded up to 16 byte boundary */
+static size_t
+vio_fifo_size(size_t size)
+{
+ if (size == 0)
+ return 4096 ;
+ else
+ return ((size + 16 - 1) / 16) * 16 ;
+} ;
+
+/*==============================================================================
+ * Initialise VTY I/O FIFO -- allocating if required.
+ */
+extern vio_fifo
+vio_fifo_init_new(vio_fifo vf, size_t size)
+{
+ if (vf == NULL)
+ vf = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
+ else
+ memset(vf, 0, sizeof(vio_fifo_t)) ;
+
+ /* Zeroising the the vio_fifo_t has set:
+ *
+ * lump -- base pair, both pointers NULL => list is empty
+ *
+ * put_ptr -- NULL ) no lump to put anything into
+ * put_end -- NULL ) put_ptr == put_end => no room in current lump
+ *
+ * get_ptr -- NULL ) no lump to get anything from
+ * get_end -- NULL ) get_ptr -- get_end => nothing left in current lump
+ *
+ * rdr_lump -- NULL ) no rdr_lump
+ * rdr_ptr -- NULL
+ *
+ * spare -- NULL no spare lump
+ *
+ * ALSO put_ptr == get_ptr => FIFO is empty !
+ */
+
+ vf->size = vio_fifo_size(size) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return vf ;
+}
+
+/*------------------------------------------------------------------------------
+ * Free contents of given FIFO, and free FIFO structure as well, if required.
+ *
+ * Does nothing if given a NULL pointer -- must already have been freed !
+ *
+ * If does not free the FIFO structure, resets it all empty.
+ *
+ * Frees *all* FIFO lumps.
+ *
+ * See also: vio_fifo_reset_keep(vio_fifo)
+ * vio_fifo_reset_free(vio_fifo)
+ */
+extern vio_fifo
+vio_fifo_reset(vio_fifo vf, int free_structure)
+{
+ vio_fifo_lump lump ;
+
+ if (vf == NULL)
+ return NULL ;
+
+ while (ddl_pop(&lump, vf->base, list) != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+
+ if (vf->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ;
+
+ if (free_structure)
+ XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
+ else
+ vio_fifo_init_new(vf, vf->size) ;
+
+ return vf ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The FIFO is empty, with one lump -- reset all pointers.
+ */
+inline static void
+vio_fifo_ptr_reset(vio_fifo vf, vio_fifo_lump lump)
+{
+ if (vf->rdr_lump != NULL)
+ {
+ assert((lump == vf->rdr_lump) && (vf->rdr_ptr == vf->get_ptr)) ;
+ vf->rdr_ptr = lump->data ;
+ } ;
+
+ /* Note that sets the lump->end to the true lump->end */
+ vf->get_ptr = vf->get_end = vf->put_ptr = lump->data ;
+ vf->put_end = lump->end = lump->data + lump->size ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The FIFO is utterly empty, with ZERO lumps -- unset all pointers.
+ */
+inline static void
+vio_fifo_ptr_unset(vio_fifo vf)
+{
+ assert((ddl_head(vf->base) == NULL) && (ddl_tail(vf->base) == NULL)) ;
+
+ vf->one = false ;
+
+ vf->put_ptr = NULL ;
+ vf->put_end = NULL ;
+ vf->get_ptr = NULL ;
+ vf->get_end = NULL ;
+
+ vf->rdr_lump = NULL ;
+ vf->rdr_ptr = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear out contents of FIFO -- will continue to use the FIFO.
+ *
+ * Keeps one FIFO lump. (Frees everything else, including any spare.)
+ */
+extern void
+vio_fifo_clear(vio_fifo vf)
+{
+ vio_fifo_lump tail ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ assert(vf != NULL) ;
+
+ tail = ddl_tail(vf->base) ;
+
+ if (tail != NULL)
+ {
+ while (ddl_head(vf->base) != tail)
+ {
+ vio_fifo_lump lump ;
+ ddl_pop(&lump, vf->base, list) ;
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+ } ;
+
+ vf->rdr_lump = NULL ; /* clear rdr */
+ vf->rdr_ptr = NULL ;
+
+ vf->one = true ;
+ vio_fifo_ptr_reset(vf, tail) ;
+ }
+ else
+ vio_fifo_ptr_unset(vf) ;
+
+ if (vf->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ; /* sets vf->spare = NULL */
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See how much room there is in the FIFO.
+ *
+ * If no lumps have been allocated, returns the size of the lump that would
+ * allocate.
+ *
+ * Otherwise, returns the amount of space available *without* allocating any
+ * further lumps.
+ *
+ * Returns: room available as described
+ */
+extern size_t
+vio_fifo_room(vio_fifo vf)
+{
+ if (vf->put_ptr != NULL)
+ return vf->put_end - vf->put_ptr ;
+ else
+ return vf->size ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate another lump for putting into.
+ *
+ * Call when (vf->put_ptr >= vf->put_end) -- asserts that they are equal.
+ *
+ * Set the put_ptr/put_end pointers to point at the new lump.
+ *
+ * If this is the first lump allocated, set the get_ptr/get_end pointers too.
+ *
+ * If have just filled the first lump on the list, update the get_end pointer
+ * to reflect the fact that the out lump is now full.
+ */
+extern void
+vio_fifo_lump_new(vio_fifo vf, size_t size)
+{
+ vio_fifo_lump lump ;
+ int first_alloc ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ passert(vf->put_ptr == vf->put_end) ; /* must be end of tail lump */
+
+ if (vf->one)
+ vf->get_end = vf->put_ptr ; /* update get_end */
+
+ lump = ddl_tail(vf->base) ;
+
+ first_alloc = (lump == NULL) ; /* extra initialisation needed */
+
+ if (first_alloc)
+ assert(vf->put_ptr == NULL) ; /* must all be NULL together */
+ else
+ assert(vf->put_ptr == lump->end) ; /* must be end of tail lump */
+
+ size = vio_fifo_size(size) ;
+
+ if ((vf->spare != NULL) && (vf->spare->size >= size))
+ {
+ lump = vf->spare ;
+ vf->spare = NULL ;
+ }
+ else
+ {
+ lump = XMALLOC(MTYPE_VIO_FIFO_LUMP,
+ offsetof(vio_fifo_lump_t, data[size])) ;
+ lump->size = size ;
+ } ;
+
+ lump->end = lump->data + lump->size ;
+
+ ddl_append(vf->base, lump, list) ;
+
+ vf->one = first_alloc ;
+
+ vf->put_ptr = lump->data ;
+ vf->put_end = lump->end ;
+
+ if (first_alloc)
+ {
+ vf->get_ptr = vf->put_ptr ; /* get_ptr == put_ptr => empty */
+ vf->get_end = vf->put_ptr ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Release lump, head or tail (or both) and update pointers.
+ *
+ * Note that this does release the lump if it is the only lump.
+ *
+ * Do nothing if nothing is yet allocated.
+ *
+ * If releasing the only lump in the FIFO, resets all pointers to NULL.
+ *
+ * If releasing the head lump:
+ *
+ * * the lump MUST be finished with -- so vf->get_ptr must be at the end
+ *
+ * If releasing the only lump, the FIFO MUST be empty.
+ *
+ * * if the lump is the current vf->rdr_lump, the reader must be at the
+ * end too -- ie it must be the same as the vf->get_ptr !
+ *
+ * If releasing the tail lump:
+ *
+ * * the lump MUST be empty
+ *
+ * If releasing the only lump, the FIFO MUST be empty.
+ *
+ * * if the lump is the current vf->rdr_lump, the reader must be at the
+ * end too -- ie it must be the same as the vf->get_ptr !
+ */
+static void
+vio_fifo_lump_release(vio_fifo vf, vio_fifo_lump lump)
+{
+ vio_fifo_lump head ;
+ vio_fifo_lump tail ;
+ vio_fifo_lump free ;
+ bool release_head ;
+ bool release_tail ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ /* Prepare and check whether removing head or tail (or both) */
+ head = ddl_head(vf->base) ;
+ tail = ddl_tail(vf->base) ;
+
+ release_head = (lump == head) ;
+ release_tail = (lump == tail) ;
+
+ assert(release_head || release_tail) ;
+
+ /* Unless nothing ever allocated -- release the lump. */
+ free = lump ; /* expect to free the lump */
+ if (lump != NULL)
+ {
+ vio_fifo_lump keep ;
+
+ /* Consistency checks */
+ if (release_head)
+ {
+ if (release_tail)
+ assert(vf->get_ptr == vf->put_ptr) ;
+ else
+ assert(vf->get_ptr == lump->end) ;
+
+ if (vf->rdr_lump == lump)
+ assert(vf->rdr_ptr == vf->get_ptr) ;
+ }
+ else if (release_tail)
+ {
+ assert(vf->put_ptr == lump->data) ;
+
+ if (vf->rdr_lump == lump)
+ assert(vf->rdr_ptr == vf->put_ptr) ;
+ } ;
+
+ /* Remove lump from FIFO and decide whether to keep as spare, or
+ * which of spare and this to free.
+ */
+ ddl_del(vf->base, lump, list) ;
+
+ keep = vf->spare ; /* expect to keep current spare */
+
+ if ((keep == NULL) || (keep->size < lump->size))
+ {
+ keep = lump ;
+ free = vf->spare ;
+ } ;
+
+ vf->spare = keep ;
+
+ head = ddl_head(vf->base) ; /* changed if released head */
+ tail = ddl_tail(vf->base) ; /* changed if released tail */
+ } ;
+
+ /* Now update pointers... depending on what was released and what have
+ * left.
+ */
+ if (head == NULL)
+ {
+ /* Deal with FIFO that now has no lumps or had none to start with */
+ if (lump != NULL)
+ assert(vf->one) ;
+
+ vio_fifo_ptr_unset(vf) ;
+ }
+ else
+ {
+ /* Have at least one lump left -- so must have had at least two ! */
+ assert(!vf->one) ;
+
+ vf->one = (head == tail) ; /* update */
+
+ if (release_head)
+ {
+ /* Released the head.
+ *
+ * Update the vf->get_ptr and the vf->get_end.
+ */
+ vf->get_ptr = head->data ;
+ if (vf->one)
+ vf->get_end = vf->put_ptr ;
+ else
+ vf->get_end = head->end ;
+
+ /* Update vf->rdr_ptr and vf->rdr_lump. */
+ if (vf->rdr_lump == lump)
+ {
+ vf->rdr_lump = head ;
+ vf->rdr_ptr = head->data ;
+ } ;
+ }
+ else
+ {
+ /* Released the tail.
+ * Update the vf->put_ptr and vf->put_end
+ */
+ vf->put_ptr = vf->put_end = tail->end ;
+
+ /* Update vf->rdr_ptr and vf->rdr_lump. */
+ if (vf->rdr_lump == lump)
+ {
+ vf->rdr_lump = tail ;
+ vf->rdr_ptr = tail->end ;
+ } ;
+ } ;
+ } ;
+
+ /* Finally, free any lump that is actually to be freed */
+
+ if (free != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-allocate lump for putting into.
+ *
+ * Call when vf->put_ptr == start of last lump, and that lump is not big
+ * enough !
+ *
+ * There must be at least one lump.
+ *
+ * Updates put_ptr/put_end pointers to point at the new lump.
+ *
+ * Updates get_ptr/get_end pointers if required.
+ *
+ * Updates rdr_ptr if required.
+ */
+static void
+vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
+{
+ bool rdr_set ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ /* FIFO may not be completely empty.
+ * This must be the last lump.
+ * The last lump must be empty.
+ */
+ assert((lump != NULL) && (lump == ddl_tail(vf->base))) ;
+
+ /* Remove the last, *empty* lump, and update all pointers to suit. */
+ rdr_set = (vf->rdr_lump == lump) ;
+
+ vio_fifo_lump_release(vf, lump) ;
+
+ /* Now allocate a new lump with the required size */
+ vio_fifo_lump_new(vf, size) ;
+
+ /* Restore the rdr_ptr, if required */
+ if (rdr_set)
+ {
+ vio_fifo_lump tail ;
+
+ tail = ddl_tail(vf->base) ;
+
+ vf->rdr_lump = tail ;
+ vf->rdr_ptr = tail->data ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*==============================================================================
+ * Put data to the FIFO.
+ */
+
+/*------------------------------------------------------------------------------
+ * Store 'n' bytes -- allocate new lump if current is exhausted.
+ */
+extern void
+vio_fifo_put(vio_fifo vf, const char* src, size_t n)
+{
+ size_t take ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ while (n > 0)
+ {
+ if (vf->put_ptr >= vf->put_end)
+ vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+
+ take = (vf->put_end - vf->put_ptr) ;
+ if (take > n)
+ take = n ;
+
+ memcpy(vf->put_ptr, src, take) ;
+ vf->put_ptr += take ;
+
+ src += take ;
+ n -= take ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to fifo -- cf printf()
+ *
+ * Returns: >= 0 -- number of bytes written
+ * < 0 -- failed (unlikely though that is)
+ */
+extern int
+vio_fifo_printf(vio_fifo vf, const char* format, ...)
+{
+ va_list args;
+ int len ;
+
+ va_start (args, format);
+ len = vio_fifo_vprintf(vf, format, args);
+ va_end (args);
+
+ return len;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to fifo -- cf vprintf()
+ *
+ * Returns: >= 0 -- number of bytes written
+ * < 0 -- failed (unlikely though that is)
+ */
+extern int
+vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args)
+{
+ va_list ac ;
+ int len ;
+ int have ;
+ size_t size ;
+ vio_fifo_lump lump ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ size = vf->size ; /* standard allocation size */
+ while (1)
+ {
+ /* Find what space is left in the tail lump, and allocate a new,
+ * empty lump if required.
+ */
+ if (vf->put_ptr >= vf->put_end)
+ vio_fifo_lump_new(vf, size) ; /* traps put_ptr > put_end */
+
+ have = vf->put_end - vf->put_ptr ;
+ assert(have > 0) ;
+
+ /* Note that vsnprintf() returns the length of what it would like to
+ * have produced, if it had the space. That length does not include
+ * the trailing '\0'.
+ */
+ va_copy(ac, args) ;
+ len = vsnprintf(vf->put_ptr, have, format, ac) ;
+ va_end(ac) ;
+
+ if (len < have)
+ {
+ if (len < 0)
+ break ; /* quit if failed */
+
+ vf->put_ptr += len ;
+ break ; /* done */
+ } ;
+
+ /* Not able to complete the operation in the current buffer.
+ *
+ * If the required space (len + 1) is greater than the standard
+ * allocation, then need to increase the allocation for the next lump.
+ *
+ * If the current lump is empty, need to renew it with a fresh lump of
+ * the now known required size.
+ *
+ * If the current lump is not empty, need to cut the end off and then
+ * allocate a fresh lump (of the standard or now known required size).
+ */
+ if (len >= (int)size)
+ size = len + 1 ; /* need a non-standard size */
+
+ lump = ddl_tail(vf->base) ;
+
+ if (vf->put_ptr == lump->data)
+ /* Need to replace the last, empty, lump with another empty lump, but
+ * big enough.
+ */
+ vio_fifo_lump_renew(vf, lump, size) ;
+ else
+ /* Need to cut this lump short, and allocate new lump at top of loop.
+ */
+ lump->end = vf->put_end = vf->put_ptr ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Get data from the FIFO.
+ */
+
+static bool vio_fifo_get_next_lump(vio_fifo vf) ;
+
+/*------------------------------------------------------------------------------
+ * Get ready to read something out of the FIFO.
+ *
+ * Makes sure vf->get_end is up to date (if required) and if the FIFO is not
+ * empty, makes sure vf->get_ptr points at the next byte to be read.
+ *
+ * Returns: true <=> there is something in the FIFO.
+ */
+static inline bool
+vio_fifo_get_ready(vio_fifo vf)
+{
+ assert(vf->rdr_lump == NULL) ;
+
+ if (vf->one)
+ vf->get_end = vf->put_ptr ; /* make sure have everything */
+
+ if (vf->get_ptr >= vf->get_end)
+ if (!vio_fifo_get_next_lump(vf))
+ return 0 ; /* quit now if nothing there */
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get upto 'n' bytes.
+ *
+ * Returns: number of bytes got -- may be zero.
+ */
+extern size_t
+vio_fifo_get(vio_fifo vf, void* dst, size_t n)
+{
+ size_t have ;
+ void* dst_in ;
+
+ if (!vio_fifo_get_ready(vf))
+ return 0 ; /* quit now if nothing there */
+
+ dst_in = dst ;
+ while (n > 0)
+ {
+ have = vf->get_end - vf->get_ptr ;
+
+ if (have > n)
+ have = n ;
+
+ memcpy(dst, vf->get_ptr, have) ;
+ vf->get_ptr += have ;
+ dst = (char*)dst + have ;
+
+ if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
+ if (!vio_fifo_get_next_lump(vf))
+ break ; /* quit if nothing more to come */
+
+ n -= have ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return (char*)dst - (char*)dst_in ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get byte -- the long winded way.
+ *
+ * See the inline vio_fifo_get_byte().
+ *
+ * The version is used when the get_ptr is at or just before the end of the
+ * current lump. Looks after all the necessary pointer updates associated with
+ * hitting end of lump, or hitting end of FIFO.
+ *
+ * Returns: 0x00..0xFF -- byte value (as an int)
+ * -1 => FIFO is empty.
+ */
+
+extern int
+vio_fifo_get_next_byte(vio_fifo vf)
+{
+ unsigned char u ;
+
+ if (!vio_fifo_get_ready(vf))
+ return -1 ; /* quit now if nothing there */
+
+ u = *vf->get_ptr++ ;
+
+ /* As soon as reach the end want either to discard empty lump, or reset
+ * the pointers.
+ */
+ if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
+ vio_fifo_get_next_lump(vf) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get pointer to a lump of bytes.
+ *
+ * Returns: address of next byte to get, *p_have = number of bytes available
+ * or: NULL => FIFO is empty, *p_have = 0
+ *
+ * If the FIFO is not empty, will return pointer to at least one byte.
+ *
+ * Returns number of bytes to the end of the current lump. There may be
+ * further lumps beyond the current one.
+ */
+extern void*
+vio_fifo_get_lump(vio_fifo vf, size_t* p_have)
+{
+ if (!vio_fifo_get_ready(vf))
+ {
+ *p_have = 0 ;
+ return NULL ;
+ } ;
+
+ *p_have = (vf->get_end - vf->get_ptr) ;
+ return vf->get_ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Advance FIFO to position reached.
+ *
+ * Having done vio_fifo_get_lump(), can take any number of bytes (up to the
+ * number that "have"), then call this function to advance the pointers.
+ *
+ * The "here" argument must the the address returned by vio_fifo_get_lump()
+ * plus the number of bytes taken.
+ */
+extern void
+vio_fifo_got_upto(vio_fifo vf, void* here)
+{
+ vf->get_ptr = here ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ if (vf->get_ptr >= vf->get_end)
+ vio_fifo_get_next_lump(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write contents of FIFO -- assuming non-blocking file
+ *
+ * Will write all of FIFO, or upto but excluding the last lump.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see errno
+ *
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
+ */
+extern int
+vio_fifo_write_nb(vio_fifo vf, int fd, bool all)
+{
+ char* src ;
+ size_t have ;
+ int done ;
+
+ while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
+ {
+ if (!all && vf->one)
+ break ; /* don't write last lump */
+
+ done = write_nb(fd, src, have) ;
+
+ if (done < 0)
+ return -1 ; /* failed */
+
+ vio_fifo_got_upto(vf, src + done) ;
+
+ if (done < (int)have)
+ return 1 ; /* blocked */
+ } ;
+
+ return 0 ; /* all gone */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the current rdr_end value.
+ *
+ * Unlike get_end, do not have a field for this, but find it each time.
+ */
+inline static char*
+vio_fifo_rdr_end(vio_fifo vf)
+{
+ if (vf->rdr_lump == ddl_tail(vf->base))
+ return vf->put_ptr ;
+ else
+ return vf->rdr_lump->end ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the current rdr position -- sets it up if not currently set.
+ *
+ * Returns: address of next byte to get, *p_have = number of bytes available
+ * or: NULL => FIFO is empty, *p_have = 0
+ *
+ * If the FIFO is not empty, will return pointer to at least one byte.
+ *
+ * Returns number of bytes to the end of the current lump. There may be
+ * further lumps beyond the current one.
+ *
+ * NB: unless returns FIFO is empty, it is a mistake to now do any "get"
+ * operation other than vio_fifo_step_rdr(), until do vio_fifo_sync_rdr()
+ * or vio_fifo_drop_rdr.
+ */
+extern void*
+vio_fifo_get_rdr(vio_fifo vf, size_t* p_have)
+{
+ if (!vio_fifo_get_ready(vf))
+ {
+ *p_have = 0 ;
+ return NULL ;
+ } ;
+
+ if (vf->rdr_lump == NULL) /* set up new rdr if required */
+ {
+ vf->rdr_lump = ddl_head(vf->base) ;
+ vf->rdr_ptr = vf->get_ptr ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ *p_have = vio_fifo_rdr_end(vf) - vf->rdr_ptr ;
+ return vf->rdr_ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Step the rdr forward by the given number of bytes.
+ *
+ * Returns: address of next byte to get, *p_have = number of bytes available
+ * or: NULL => FIFO is empty, *p_have = 0
+ *
+ * If the FIFO is not empty, will return pointer to at least one byte.
+ *
+ * Returns number of bytes to the end of the current lump. There may be
+ * further lumps beyond the current one.
+ *
+ * NB: this does not change the get pointers, so all the data being stepped
+ * over is preserved in the FIFO, until vio_fifo_sync_rdr().
+ *
+ * NB: the step may NOT exceed the last reported "have".
+ */
+extern void*
+vio_fifo_step_rdr(vio_fifo vf, size_t* p_have, size_t step)
+{
+ char* rdr_end ;
+
+ assert(vf->rdr_lump != NULL) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ rdr_end = vio_fifo_rdr_end(vf) ;
+ vf->rdr_ptr += step ;
+
+ if (vf->rdr_ptr >= rdr_end)
+ {
+ assert(vf->rdr_ptr == rdr_end) ;
+
+ if (vf->rdr_lump != ddl_tail(vf->base))
+ {
+ vf->rdr_lump = ddl_next(vf->rdr_lump, list) ;
+ vf->rdr_ptr = vf->rdr_lump->data ;
+
+ rdr_end = vio_fifo_rdr_end(vf) ;
+ } ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ *p_have = (rdr_end - vf->rdr_ptr) ;
+ return (*p_have > 0) ? vf->rdr_ptr : NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move FIFO get position to the rdr position, if any.
+ *
+ * This clears the rdr position, and removes all data between the current and
+ * new get positions from the FIFO.
+ */
+extern void
+vio_fifo_sync_rdr(vio_fifo vf)
+{
+ vio_fifo_lump head ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ if (vf->rdr_lump == NULL)
+ return ;
+
+ while ((head = ddl_head(vf->base)) != vf->rdr_lump)
+ {
+ vf->get_ptr = vf->get_end ; /* jump to end of lump */
+ vio_fifo_lump_release(vf, head) ;
+ } ;
+
+ vf->get_ptr = vf->rdr_ptr ; /* jump to rdr_ptr */
+
+ vf->rdr_lump = NULL ; /* clear the rdr */
+ vf->rdr_ptr = NULL ;
+
+ if (vf->one)
+ {
+ if (vf->put_ptr == vf->get_ptr) /* reset pointers if FIFO empty */
+ vio_fifo_ptr_reset(vf, head) ;
+ else
+ vf->get_end = vf->put_ptr ;
+ }
+ else
+ vf->get_end = head->end ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Drop the rdr position (if any).
+ *
+ * This clears the rdr position leaving the get position and FIFO unchanged.
+ */
+extern void
+vio_fifo_drop_rdr(vio_fifo vf)
+{
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ vf->rdr_lump = NULL ;
+ vf->rdr_ptr = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move on to next lump to get stuff from.
+ *
+ * Advance pointers etc. so that have at least one byte available, unless
+ * the FIFO is entirely empty.
+ *
+ * This should be called if (vf->get_ptr >= vf->get_end) -- asserts that
+ * these are equal !
+ *
+ * NB: when there is only one block, it may be that get_end is out of date,
+ * and should be advanced to the current put_ptr position.
+ *
+ * That is done here, but may be worth updating get_end before testing
+ * against get_ptr.
+ *
+ * Returns: true <=> at least one byte in FIFO.
+ *
+ * NB: if finds that the FIFO is empty, resets the pointers to the start
+ * of the last lump -- does not release the last lump.
+ */
+static bool
+vio_fifo_get_next_lump(vio_fifo vf)
+{
+ vio_fifo_lump head ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+ assert(vf->get_ptr == vf->get_end) ;
+
+ head = ddl_head(vf->base) ; /* current lump for get */
+
+ /* Deal with the simple case of one lump, first.
+ *
+ * To save work when putting data into the FIFO (particularly when putting
+ * a byte at a time) does not keep the vf->get_end up to date (when there is
+ * only one lump).
+ *
+ * If the FIFO is empty, reset pointers and return empty.
+ */
+ if (vf->one)
+ {
+ assert( (head != NULL) && (head == ddl_tail(vf->base)) ) ;
+
+ if (vf->get_ptr < vf->put_ptr)
+ {
+ /* Had an out of date vf->get_end */
+ vf->get_end = vf->put_ptr ;
+
+ return true ; /* FIFO not empty */
+ } ;
+
+ assert(vf->get_ptr == vf->put_ptr) ;
+
+ /* FIFO is empty -- reset pointers and exit */
+ vio_fifo_ptr_reset(vf, head) ;
+
+ return false ; /* FIFO empty */
+ } ;
+
+ /* Release the head and update pointers
+ *
+ * Deals with possibility that nothing has yet been allocated
+ */
+ vio_fifo_lump_release(vf, head) ;
+
+ return (vf->get_ptr < vf->get_end) ;
+} ;
+
+/*==============================================================================
+ * For debug purposes -- verify the state of the given FIFO
+ */
+Private void
+vio_fifo_verify(vio_fifo vf)
+{
+ vio_fifo_lump head ;
+ vio_fifo_lump lump ;
+ vio_fifo_lump tail ;
+
+ head = ddl_head(vf->base) ;
+ tail = ddl_tail(vf->base) ;
+
+ /* If nothing allocated, should all be NULL & !vf->one */
+ /* If something allocated, tail must not be NULL */
+ if (head == NULL)
+ {
+ if ( (tail != NULL)
+ || (vf->put_ptr != NULL)
+ || (vf->put_end != NULL)
+ || (vf->get_ptr != NULL)
+ || (vf->rdr_lump != NULL)
+ || (vf->rdr_ptr != NULL)
+ || (vf->one) )
+ zabort("nothing allocated, but not all NULL") ;
+ return ;
+ }
+ else
+ {
+ if (tail == NULL)
+ zabort("head pointer not NULL, but tail pointer is") ;
+ } ;
+
+ /* Check that all the pointers are within respective lumps
+ *
+ * Know that put_end is always tail->end, but get_end need not be.
+ */
+ if ( (tail->data > vf->put_ptr)
+ || (vf->put_ptr > vf->put_end)
+ || (vf->put_end != tail->end) )
+ zabort("put pointers outside the tail lump") ;
+
+ if ( (head->data > vf->get_ptr)
+ || (vf->get_ptr > vf->get_end)
+ || (vf->get_end > head->end) )
+ zabort("get pointers outside the head lump") ;
+
+ /* If head == tail, should be vf->one, etc. */
+ if (head == tail)
+ {
+ if (!vf->one)
+ zabort("have one lump, but !vf->one") ;
+
+ if (vf->get_end > vf->put_ptr)
+ zabort("get_end is greater than put_ptr when vf->one") ;
+ }
+ else
+ {
+ if (vf->one)
+ zabort("have two or more lumps, but vf->one is true") ;
+
+ if (vf->get_end != head->end)
+ zabort("get_end is not head->end when !vf->one") ;
+ } ;
+
+ /* If have an rdr_lump -- make sure everything else is valid */
+ if (vf->rdr_lump != NULL)
+ {
+ lump = head ;
+ while (lump != vf->rdr_lump)
+ {
+ if (lump == tail)
+ zabort("rdr_lump is not part of FIFO") ;
+ lump = ddl_next(lump, list) ;
+ } ;
+
+ if ( (lump->data > vf->rdr_ptr)
+ || (vf->rdr_ptr > lump->end) )
+ zabort("rdr_ptr outside its lump") ;
+
+ if ( (lump == head) && (vf->rdr_ptr < vf->get_ptr))
+ zabort("rdr_ptr is less than get_ptr in first lump") ;
+
+ if ( (lump == tail) && (vf->rdr_ptr > vf->put_ptr))
+ zabort("rdr_ptr is greater than put_ptr in last lump") ;
+ }
+ else
+ {
+ if (vf->rdr_ptr != NULL)
+ zabort("rdr_ptr not NULL when rdr_lump is") ;
+ }
+} ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
new file mode 100644
index 00000000..52f3455e
--- /dev/null
+++ b/lib/vio_fifo.h
@@ -0,0 +1,205 @@
+/* VTY I/O FIFO -- header
+ * Copyright (C) 2010 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 _ZEBRA_VIO_FIFO_H
+#define _ZEBRA_VIO_FIFO_H
+
+#include "zebra.h"
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "list_util.h"
+#include "zassert.h"
+
+#ifndef Inline /* in case of compiler issues */
+#define Inline static inline
+#endif
+
+#ifndef Private /* extern, but for "friends" only */
+#define Private extern
+#endif
+
+/* GCC have printf type attribute check. */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/*==============================================================================
+ * VTY I/O FIFO -- buffering of arbitrary amounts of I/O.
+ */
+
+#ifdef NDEBUG
+# define VIO_FIFO_DEBUG 0 /* NDEBUG override */
+#else
+# ifndef VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG 1 /* Set to 1 to turn on debug checks */
+# endif
+#endif
+
+/*==============================================================================
+ * Data Structures
+ */
+typedef struct vio_fifo vio_fifo_t ;
+typedef struct vio_fifo* vio_fifo ;
+
+typedef struct vio_fifo_lump vio_fifo_lump_t ;
+typedef struct vio_fifo_lump* vio_fifo_lump ;
+
+struct vio_fifo
+{
+ struct dl_base_pair(vio_fifo_lump) base ;
+
+ bool one ;
+
+ char* put_ptr ;
+ char* put_end ;
+
+ char* get_ptr ;
+ char* get_end ;
+
+ vio_fifo_lump rdr_lump ;
+ char* rdr_ptr ;
+
+ size_t size ;
+
+ vio_fifo_lump spare ;
+} ;
+
+struct vio_fifo_lump
+{
+ struct dl_list_pair(vio_fifo_lump) list ;
+
+ char* end ; /* end of this particular lump */
+ size_t size ; /* size of lump when allocated */
+ char data[] ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern vio_fifo vio_fifo_init_new(vio_fifo vf, size_t size) ;
+extern vio_fifo vio_fifo_reset(vio_fifo vf, int free_structure) ;
+
+#define vio_fifo_reset_keep(vf) vio_fifo_reset(vf, 0)
+#define vio_fifo_reset_free(vf) vio_fifo_reset(vf, 1)
+
+extern void vio_fifo_clear(vio_fifo vf) ;
+Inline bool vio_fifo_empty(vio_fifo vf) ;
+extern size_t vio_fifo_room(vio_fifo vf) ;
+
+extern void vio_fifo_put(vio_fifo vf, const char* src, size_t n) ;
+Inline void vio_fifo_put_byte(vio_fifo vf, char b) ;
+
+extern int vio_fifo_printf(vio_fifo vf, const char* format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern int vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args) ;
+
+extern size_t vio_fifo_get(vio_fifo vf, void* dst, size_t n) ;
+Inline int vio_fifo_get_byte(vio_fifo vf) ;
+extern void* vio_fifo_get_lump(vio_fifo vf, size_t* have) ;
+extern void vio_fifo_got_upto(vio_fifo vf, void* here) ;
+
+Inline bool vio_fifo_full_lump(vio_fifo vf) ;
+extern int vio_fifo_write_nb(vio_fifo vf, int fd, bool all) ;
+
+extern void* vio_fifo_get_rdr(vio_fifo vf, size_t* have) ;
+extern void* vio_fifo_step_rdr(vio_fifo vf, size_t* have, size_t step) ;
+extern void vio_fifo_sync_rdr(vio_fifo vf) ;
+extern void vio_fifo_drop_rdr(vio_fifo vf) ;
+
+Private void vio_fifo_lump_new(vio_fifo vf, size_t size) ;
+Private int vio_fifo_get_next_byte(vio_fifo vf) ;
+
+/*==============================================================================
+ * Debug -- verification function
+ */
+
+Private void vio_fifo_verify(vio_fifo vf) ;
+
+#if VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG_VERIFY(vf) vio_fifo_verify(vf)
+#else
+# define VIO_FIFO_DEBUG_VERIFY(vf)
+#endif
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Returns true <=> FIFO is empty
+ */
+Inline bool
+vio_fifo_empty(vio_fifo vf)
+{
+ return (vf->get_ptr == vf->put_ptr) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Put one byte to the FIFO
+ */
+Inline void
+vio_fifo_put_byte(vio_fifo vf, char b)
+{
+ if (vf->put_ptr >= vf->put_end)
+ vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ *vf->put_ptr++ = b ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get one byte from the FIFO.
+ *
+ * Returns: 0x00..0xFF -- byte value (as an int)
+ * -1 => FIFO is empty.
+ */
+Inline int
+vio_fifo_get_byte(vio_fifo vf)
+{
+ if (vf->get_end <= (vf->get_ptr + 1))
+ return vio_fifo_get_next_byte(vf) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return (unsigned char)*vf->get_ptr++ ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if have at least one full lump.
+ *
+ * This may be used with vio_fifo_write_nb(..., false) to use FIFO as a sort of
+ * double buffer.
+ *
+ * Returns: true <=> there is at least one full lump in the FIFO
+ * (excluding the last lump if it happens to be full)
+ */
+Inline bool
+vio_fifo_full_lump(vio_fifo vf)
+{
+ return (!vf->one && (vf->put_ptr != NULL)) ;
+} ;
+
+#endif /* _ZEBRA_VIO_FIFO_H */
diff --git a/lib/vio_lines.c b/lib/vio_lines.c
new file mode 100644
index 00000000..c2e9c43c
--- /dev/null
+++ b/lib/vio_lines.c
@@ -0,0 +1,380 @@
+/* Line Control for VTY Terminal output
+ * Copyright (C) 2010 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 <stdint.h>
+
+#include "memory.h"
+#include "zassert.h"
+
+#include "vio_lines.h"
+#include "qiovec.h"
+
+/*==============================================================================
+ * Line control handles the output of simple text to a telnet connection,
+ * folding and counting lines (for "--more--" purposes) if required.
+ *
+ * LIMITATIONS:
+ *
+ * 1) does not handle '\r' except as part of '\r''\n' pairs.
+ *
+ * Telnet requires that bare '\r' be sent as '\r''\0'. That is not
+ * implemented.
+ *
+ * The handling of '\r' which is not part of '\r''\n' is UNDEFINED.
+ * (In particular, the '\r' may be sent as is, or not sent at all.)
+ *
+ * 2) does not worry about '\t' or '\b' or any other control character.
+ *
+ * Apart from '\r' and '\n' all characters are deemed to be printing
+ * characters -- and to have width == 1.
+ *
+ * 3) has no idea about escape sequences or telnet commands.
+ *
+ * In particular: when looking for '\n' (and '\r') has no way of telling
+ * if those are part of an escape sequence.
+ *
+ * 4) DOES NOT handle 0xFF character value.
+ *
+ * For Telnet this should be escaped. It isn't.
+ *
+ * Current use of VTY command output will not be troubled by these limitations.
+ * To do more would cost code and cpu unnecessarily.
+ *
+ * WHAT IT DOES DO:
+ *
+ * 1) maps bare '\n' to '\r''\n'.
+ *
+ * Swallows '\r' immediately before '\n' if present.
+ *
+ * 2) if required, breaks output into screen width chunks, and counts
+ * down the height of a "screen full".
+ *
+ */
+
+/*==============================================================================
+ * Initialise, allocate, reset etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise new vio_line_control -- allocate if required.
+ *
+ * This is for initialising a new structure. Any current contents are lost.
+ *
+ * A width of <= 0 => very large width indeed.
+ * A height of <= 0 => indefinite height
+ *
+ * Pause is unset. vio_lc_append will collect an indefinite number of lines.
+ *
+ * Column and line position set to zero.
+ *
+ * Returns: address of vio_line_control
+ */
+extern vio_line_control
+vio_lc_init_new(vio_line_control lc, int width, int height)
+{
+ if (lc == NULL)
+ lc = XCALLOC(MTYPE_VIO_LC, sizeof(struct vio_line_control)) ;
+ else
+ memset(lc, 0, sizeof(struct vio_line_control)) ;
+
+ /* Zeroising has set:
+ *
+ * pause = 0 -- no limit on the number of lines to append
+ * paused = 0 -- not paused
+ *
+ * col = 0 -- at column 0
+ * lines = 0 -- no lines collected, yet
+ *
+ * iov = all 0 -- empty
+ * writing = 0 -- not writing
+ */
+
+ lc->width = width >= 0 ? width : 0 ;
+ lc->height = height >= 0 ? height : 0 ;
+
+ return lc ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset vio_line_control (if any) -- release body and (if required) the
+ * structure.
+ *
+ * Returns: address of vio_line_control (if any) -- NULL if structure released
+ */
+extern vio_line_control
+vio_lc_reset(vio_line_control lc, bool free_structure)
+{
+ if (lc != NULL)
+ {
+ if (free_structure)
+ XFREE(MTYPE_VIO_LC, lc) ; /* sets lc = NULL */
+ else
+ vio_lc_init_new(lc, lc->width, lc->height) ;
+ /* re-initialise */
+ } ;
+
+ return lc ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear given vio_line_control.
+ *
+ * Sets: pause = 0
+ * paused = 0
+ * col = 0
+ * writing = 0
+ *
+ * NB: it is the callers responsibility to release anything buffered because
+ * it was earlier appended.
+ */
+extern void
+vio_lc_clear(vio_line_control lc)
+{
+ qiovec_clear(&lc->qiov) ;
+
+ lc->pause = 0 ;
+ lc->paused = 0 ;
+ lc->col = 0 ;
+ lc->writing = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Sets width and height for line control
+ *
+ * A width of <= 0 => very large width indeed.
+ * A height of <= 0 => indefinite height
+ *
+ * Pause is adjusted if it is not zero, and may become zero and set paused.
+ */
+extern void
+vio_lc_set_window(vio_line_control lc, int width, int height)
+{
+ unsigned old_height ;
+
+ old_height = lc->height ;
+
+ lc->width = width >= 0 ? width : 0 ;
+ lc->height = height >= 0 ? height : 0 ;
+
+ if (lc->pause != 0)
+ {
+ if (lc->height > old_height)
+ lc->pause += lc->height - old_height ;
+ else
+ {
+ if (lc->pause >= (old_height - lc->height))
+ lc->pause = 0 ;
+ else
+ lc->pause -= old_height - lc->height ;
+ } ;
+ lc->paused = (lc->pause == 0) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Appending and writing
+ */
+
+/*------------------------------------------------------------------------------
+ * Sets pause to the current height and clear paused.
+ */
+extern void
+vio_lc_set_pause(vio_line_control lc)
+{
+ lc->pause = lc->height ;
+ lc->paused = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put newline (if required) and account for it
+ */
+static inline void
+vio_lc_newline(vio_line_control lc, bool required)
+{
+ if (required)
+ qiovec_push(&lc->qiov, "\r\n", 2) ;
+
+ lc->col = 0 ;
+ lc->line += 1 ;
+ if (lc->pause != 0)
+ {
+ lc->pause -= 1 ;
+ lc->paused = (lc->pause == 0) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append a lump of output to the given line control's buffers.
+ *
+ * Breaks the output into lines which are no longer than the lc->width.
+ *
+ * Maps '\n' to '\r''\n'.
+ *
+ * Discards '\r' if found before '\n', and possibly at other times.
+ *
+ * If lc->width == 0, use a very large width indeed.
+ *
+ * If lc->pause == 0, append an indefinite number of lines
+ *
+ * NB: the buffer presented MUST be retained until the contents of the
+ * line control's buffers have been written.
+ *
+ * Returns: number of bytes able to append
+ */
+extern size_t
+vio_lc_append(vio_line_control lc, const void* buf, size_t len)
+{
+ const char* p ;
+ const char* end ;
+
+ unsigned width ;
+ unsigned pause ;
+
+ /* Prepare local width and pause */
+ if (lc->width > 0)
+ width = lc->width ;
+ else
+ width = UINT_MAX ;
+
+ if (lc->pause > 0)
+ pause = 0 ;
+ else
+ pause = 1 ;
+
+ lc->paused = 0 ;
+
+ /* Append: stop when run out of data or run out of lines */
+ end = (const char*)buf + len ;
+ p = buf ;
+
+ while ((p < end) && (lc->pause != pause))
+ {
+ const char* e ;
+ bool nl ;
+ int nlx ;
+
+ nlx = 0 ; /* no line ending chars yet */
+
+ /* scan for '\n'. */
+ e = memchr(p, '\n', (end - p)) ;
+ nl = (e != NULL) ;
+ if (nl)
+ ++nlx ; /* account for the '\n' */
+ else
+ e = end ; /* use all there is */
+
+ /* peel off trailing '\r'.
+ *
+ * NB: if have not got a '\n', then this may discard a bare
+ * '\r' -- but bare '\r' are undefined in any case.
+ */
+ if ((e > p) && (*(e - 1) == '\r'))
+ {
+ --e ; /* strip the '\r' */
+ ++nlx ; /* but account for it */
+ }
+
+ /* have p..e characters and possibly nl to add to the output.
+ *
+ * Note that if enters the while, (e - p) > 0. So there is at least one
+ * character to add. This avoids generating a spurious line ending if
+ * the width has been reduced, and the next thing output is a line end.
+ */
+ while ((p < e) && (lc->pause != pause))
+ {
+ const char* t ;
+ unsigned col ;
+
+ col = lc->col + (e - p) ; /* NB: e > p */
+
+ if (col > width)
+ {
+ /* can use only part of what there is */
+ if (width > lc->col)
+ t = p + (width - lc->col) ;
+ /* take to edge of screen */
+ else
+ t = p ;
+ assert(t < e) ; /* if not need to deal with nl */
+ }
+ else
+ {
+ /* can use all of what there is */
+ if (nlx == 2) /* if have crlf, use it */
+ {
+ e += nlx ; /* use the crlf that's there */
+ nlx = 0 ; /* used it */
+ } ;
+
+ t = e ; /* take it all */
+ } ;
+
+ assert(t >= p) ;
+ if (t != p)
+ qiovec_push(&lc->qiov, p, (t - p)) ;
+
+ /* advance. If not taken all the line, need a crlf */
+ p = t ;
+
+ if (p < e)
+ vio_lc_newline(lc, 1) ;
+ } ;
+
+ /* If taken all of line, deal with any outstanding nl and nlx */
+ if (p == e)
+ {
+ if (nl)
+ vio_lc_newline(lc, (nlx != 0)) ;
+
+ p += nlx ; /* step past '\r' or '\n' */
+ } ;
+ } ;
+
+ /* Exhausted the available data or the line count */
+ assert(p <= end) ;
+
+ return (p - (const char*)buf) ; /* what have taken */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away any collected output -- assuming NON-BLOCKING.
+ *
+ * Does nothing if the line control is empty.
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: > 0 => one or more bytes left to output
+ * 0 => all done -- zero bytes left to output
+ * -1 => failed -- see errno
+ *
+ * Sets lc->writing if write does not complete
+ */
+extern int
+vio_lc_write_nb(int fd, vio_line_control lc)
+{
+ int ret ;
+
+ ret = qiovec_write_nb(fd, &lc->qiov) ;
+
+ lc->writing = (ret > 0) ;
+
+ return ret ;
+} ;
diff --git a/lib/vio_lines.h b/lib/vio_lines.h
new file mode 100644
index 00000000..ffef94ec
--- /dev/null
+++ b/lib/vio_lines.h
@@ -0,0 +1,91 @@
+/* Line Control for VTY Terminal output -- 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 _ZEBRA_VIO_LINES_H
+#define _ZEBRA_VIO_LINES_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "qiovec.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Line control -- collecting lines of a given width for output.
+ *
+ * NB: a completely zero structure is a valid, clear vio_line_control.
+ */
+
+typedef struct vio_line_control* vio_line_control ;
+typedef struct vio_line_control vio_line_control_t ;
+struct vio_line_control
+{
+ unsigned width ; /* console width -- 0 => HUGE */
+ unsigned height ; /* console height -- 0 => indefinite */
+
+ unsigned pause ; /* number of lines to next pause
+ 0 => indefinite */
+ bool paused ; /* true <=> last append stopped on pause */
+
+ unsigned col ; /* current column position */
+ unsigned line ; /* line number of last complete line */
+
+ struct qiovec qiov ; /* iovec control */
+ bool writing ; /* write started, but not completed */
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+extern vio_line_control vio_lc_init_new(vio_line_control lc, int width,
+ int height) ;
+extern vio_line_control vio_lc_reset(vio_line_control lc, bool free_structure) ;
+
+#define vio_lc_reset_keep(lc) vio_lc_reset(lc, 0)
+#define vio_lc_reset_free(lc) vio_lc_reset(lc, 1)
+
+Inline bool vio_lc_empty(vio_line_control lc) ;
+extern void vio_lc_clear(vio_line_control lc) ;
+extern void vio_lc_set_window(vio_line_control lc, int width, int height) ;
+
+extern void vio_lc_set_pause(vio_line_control lc) ;
+extern size_t vio_lc_append(vio_line_control lc, const void* buf, size_t len) ;
+extern int vio_lc_write_nb(int fd, vio_line_control lc) ;
+
+/*------------------------------------------------------------------------------
+ * Is given line control empty ?
+ */
+Inline bool
+vio_lc_empty(vio_line_control lc)
+{
+ return qiovec_empty(&lc->qiov) ;
+} ;
+
+#endif /* _ZEBRA_VIO_LINES_H */
diff --git a/lib/vty.c b/lib/vty.c
index bd1dbac0..d5d07676 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1,7 +1,8 @@
-/*
- * Virtual terminal [aka TeletYpe] interface routine.
+/* VTY top level
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
@@ -17,1083 +18,872 @@
* 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>
+#include "zebra.h"
+#include <stdbool.h>
+#include "lib/version.h"
-#include "linklist.h"
-#include "thread.h"
-#include "buffer.h"
-#include <lib/version.h>
-#include "command.h"
-#include "sockunion.h"
-#include "memory.h"
-#include "str.h"
-#include "log.h"
-#include "prefix.h"
-#include "filter.h"
+#include "vty_io.h"
#include "vty.h"
-#include "privs.h"
-#include "network.h"
-
-#include <arpa/telnet.h>
-
-/* Vty events */
-enum event
-{
- VTY_SERV,
- VTY_READ,
- VTY_WRITE,
- VTY_TIMEOUT_RESET,
-#ifdef VTYSH
- VTYSH_SERV,
- VTYSH_READ,
- VTYSH_WRITE
-#endif /* VTYSH */
-};
+#include "uty.h"
+#include "vty_cli.h"
-static void vty_event (enum event, int, struct vty *);
+#include "list_util.h"
-/* Extern host structure from command.c */
-extern struct host host;
-
-/* Vector which store each vty structure. */
-static vector vtyvec;
+#include "command.h"
+#include "command_queue.h"
+#include "command_execute.h"
+#include "memory.h"
+#include "log.h"
+#include "mqueue.h"
-/* Vty timeout value. */
-static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
+/*==============================================================================
+ * Variables etc. (see uty.h)
+ */
-/* Vty access-class command */
-static char *vty_accesslist_name = NULL;
+/* The mutex and related debug counters */
+qpt_mutex_t vty_mutex ;
-/* Vty access-calss for IPv6. */
-static char *vty_ipv6_accesslist_name = NULL;
+#if VTY_DEBUG
-/* VTY server thread. */
-vector Vvty_serv_thread;
+int vty_lock_count = 0 ;
+int vty_assert_fail = 0 ;
-/* Current directory. */
-char *vty_cwd = NULL;
+#endif
-/* Configure lock. */
-static int vty_config;
+/* For thread handling -- initialised in vty_init */
+struct thread_master* vty_master = NULL ;
-/* Login password check. */
-static int no_password_check = 0;
+/* In the qpthreads world, have nexus for the CLI and one for the Routeing
+ * Engine. Some commands are processed directly in the CLI, most have to
+ * be sent to the Routeing Engine.
+ */
+qpn_nexus vty_cli_nexus = NULL ;
+qpn_nexus vty_cmd_nexus = NULL ;
-/* Restrict unauthenticated logins? */
-static const u_char restricted_mode_default = 0;
-static u_char restricted_mode = 0;
+/* List of all known vio */
+vty_io vio_list_base = NULL ;
-/* Integrated configuration file path */
-char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
+/* List of all vty which are in monitor state. */
+vty_io vio_monitors_base = NULL ;
-
-/* VTY standard output function. */
-int
-vty_out (struct vty *vty, const char *format, ...)
-{
- va_list args;
- int len = 0;
- int size = 1024;
- char buf[1024];
- char *p = NULL;
+/* List of all vty which are on death watch */
+vty_io vio_death_watch = NULL ;
- if (vty_shell (vty))
- {
- va_start (args, format);
- vprintf (format, args);
- va_end (args);
- }
- else
- {
- /* Try to write to initial buffer. */
- va_start (args, format);
- len = vsnprintf (buf, sizeof buf, format, args);
- va_end (args);
+/* Vty timeout value -- see "exec timeout" command */
+unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
- /* Initial buffer is not enough. */
- if (len < 0 || len >= size)
- {
- while (1)
- {
- if (len > -1)
- size = len + 1;
- else
- size = size * 2;
+/* Vty access-class command */
+char *vty_accesslist_name = NULL;
- p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
- if (! p)
- return -1;
+/* Vty access-class for IPv6. */
+char *vty_ipv6_accesslist_name = NULL;
- va_start (args, format);
- len = vsnprintf (p, size, format, args);
- va_end (args);
+/* Current directory -- initialised in vty_init() */
+static char *vty_cwd = NULL;
- if (len > -1 && len < size)
- break;
- }
- }
+/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */
+bool vty_config = 0 ;
- /* When initial buffer is enough to store all output. */
- if (! p)
- p = buf;
+/* Login password check override. */
+bool no_password_check = 0;
- /* Pointer p must point out buffer. */
- buffer_put (vty->obuf, (u_char *) p, len);
+/* Restrict unauthenticated logins? */
+const bool restricted_mode_default = 0 ;
+ bool restricted_mode = 0 ;
- /* If p is not different with buf, it is allocated buffer. */
- if (p != buf)
- XFREE (MTYPE_VTY_OUT_BUF, p);
- }
+/* Watch-dog timer. */
+union vty_watch_dog vty_watch_dog = { NULL } ;
- return len;
-}
+/*------------------------------------------------------------------------------
+ * VTYSH stuff
+ */
-static int
-vty_log_out (struct vty *vty, const char *level, const char *proto_str,
- const char *format, struct timestamp_control *ctl, va_list va)
-{
- int ret;
- int len;
- char buf[1024];
+/* Integrated configuration file path -- for VTYSH */
+char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
- if (!ctl->already_rendered)
- {
- ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
- ctl->already_rendered = 1;
- }
- if (ctl->len+1 >= sizeof(buf))
- return -1;
- memcpy(buf, ctl->buf, len = ctl->len);
- buf[len++] = ' ';
- buf[len] = '\0';
-
- if (level)
- ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
- else
- ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
- if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
- return -1;
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+static void uty_reset (bool final, const char* why) ;
+static void uty_init_commands (void) ;
+static void vty_save_cwd (void) ;
- if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
- ((size_t)((len += ret)+2) > sizeof(buf)))
- return -1;
+/*------------------------------------------------------------------------------
+ * Tracking the initialisation state.
+ */
+enum vty_init_state
+{
+ vty_init_pending = 0, /* first and lowest numbered state */
+ vty_init_1st_stage,
+ vty_init_2nd_stage,
+ vty_init_started,
+ vty_init_reset,
+ vty_init_terminated /* final and highest numbered state */
+};
- buf[len++] = '\r';
- buf[len++] = '\n';
+static enum vty_init_state vty_init_state ;
- if (write(vty->fd, buf, len) < 0)
- {
- if (ERRNO_IO_RETRY(errno))
- /* Kernel buffer is full, probably too much debugging output, so just
- drop the data and ignore. */
- return -1;
- /* Fatal I/O error. */
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: write failed to vty client fd %d, closing: %s",
- __func__, vty->fd, safe_strerror(errno));
- buffer_reset(vty->obuf);
- /* cannot call vty_close, because a parent routine may still try
- to access the vty struct */
- vty->status = VTY_CLOSE;
- shutdown(vty->fd, SHUT_RDWR);
- return -1;
- }
- return 0;
-}
+/*==============================================================================
+ * Public Interface
+ */
-/* Output current time to the vty. */
-void
-vty_time_print (struct vty *vty, int cr)
+/*------------------------------------------------------------------------------
+ * Initialise vty handling (threads and pthreads)
+ *
+ * Install vty's own commands like `who' command.
+ *
+ * This runs before any pthreads or nexus stuff starts -- so is, implicitly,
+ * in the CLI thread.
+ *
+ * NB: may be called once and once only.
+ */
+extern void
+vty_init (struct thread_master *master_thread)
{
- char buf [25];
-
- if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
- {
- zlog (NULL, LOG_INFO, "quagga_timestamp error");
- return;
- }
- if (cr)
- vty_out (vty, "%s\n", buf);
- else
- vty_out (vty, "%s ", buf);
+ VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
+ VTY_ASSERT_CLI_THREAD() ; /* True if !qpthreads_enabled */
- return;
-}
+ assert(vty_init_state == vty_init_pending) ;
-/* Say hello to vty interface. */
-void
-vty_hello (struct vty *vty)
-{
- if (host.motdfile)
- {
- FILE *f;
- char buf[4096];
+ vty_master = master_thread; /* Local pointer to the master thread */
- f = fopen (host.motdfile, "r");
- if (f)
- {
- while (fgets (buf, sizeof (buf), f))
- {
- char *s;
- /* work backwards to ignore trailling isspace() */
- for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
- s--);
- *s = '\0';
- vty_out (vty, "%s%s", buf, VTY_NEWLINE);
- }
- fclose (f);
- }
- else
- vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
- }
- else if (host.motd)
- vty_out (vty, "%s", host.motd);
-}
+ vty_save_cwd (); /* need cwd for config reading */
-/* Put out prompt and wait input from user. */
-static void
-vty_prompt (struct vty *vty)
-{
- struct utsname names;
- const char*hostname;
+ vio_list_base = NULL ; /* no VTYs yet */
+ vio_monitors_base = NULL ;
+ vio_death_watch = NULL ;
- if (vty->type == VTY_TERM)
- {
- hostname = host.name;
- if (!hostname)
- {
- uname (&names);
- hostname = names.nodename;
- }
- vty_out (vty, cmd_prompt (vty->node), hostname);
- }
-}
+ vty_cli_nexus = NULL ; /* not running qnexus-wise */
+ vty_cmd_nexus = NULL ;
-/* Send WILL TELOPT_ECHO to remote server. */
-static void
-vty_will_echo (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
- vty_out (vty, "%s", cmd);
-}
+ vty_watch_dog.anon = NULL ; /* no watch dog */
-/* Make suppress Go-Ahead telnet option. */
-static void
-vty_will_suppress_go_ahead (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
- vty_out (vty, "%s", cmd);
-}
+ uty_init_commands() ; /* install nodes */
-/* Make don't use linemode over telnet. */
-static void
-vty_dont_linemode (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
- vty_out (vty, "%s", cmd);
-}
+ vty_init_state = vty_init_1st_stage ;
-/* Use window size. */
-static void
-vty_do_window_size (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
- vty_out (vty, "%s", cmd);
+ VTY_UNLOCK() ;
}
-#if 0 /* Currently not used. */
-/* Make don't use lflow vty interface. */
-static void
-vty_dont_lflow_ahead (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Further initialisation for qpthreads.
+ *
+ * This is done during "second stage" initialisation, when all nexuses have
+ * been set up and the qpthread_enabled state established.
+ *
+ * This is before any threads have been started, so is, implicitly, in the
+ * CLI thread.
+ *
+ * Need to know where the CLI nexus and the Routeing Engine nexus are.
+ *
+ * Initialise mutex.
+ *
+ * Cannot lock or assert in CLI thread while initialising those things !
+ *
+ * NB: may be called once and once only.
+ */
+extern void
+vty_init_r (qpn_nexus cli, qpn_nexus cmd)
{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
- vty_out (vty, "%s", cmd);
-}
-#endif /* 0 */
+ assert(vty_init_state == vty_init_1st_stage) ;
-/* Allocate new vty struct. */
-struct vty *
-vty_new ()
-{
- struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+ vty_cli_nexus = cli ;
+ vty_cmd_nexus = cmd ;
- new->obuf = buffer_new(0); /* Use default buffer size. */
- new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
- new->max = VTY_BUFSIZ;
+ qpt_mutex_init(&vty_mutex, qpt_mutex_recursive);
- return new;
-}
+ vty_init_state = vty_init_2nd_stage ;
+} ;
-/* Authentication of vty */
-static void
-vty_auth (struct vty *vty, char *buf)
+/*------------------------------------------------------------------------------
+ * Initialisation for vtysh application.
+ *
+ * TODO: work out what this needs to do ! (If anything.)
+ */
+extern void
+vty_init_vtysh (void)
{
- char *passwd = NULL;
- enum node_type next_node = 0;
- int fail;
- char *crypt (const char *, const char *);
+ VTY_LOCK() ;
- switch (vty->node)
- {
- case AUTH_NODE:
- if (host.encrypt)
- passwd = host.password_encrypt;
- else
- passwd = host.password;
- if (host.advanced)
- next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
- else
- next_node = VIEW_NODE;
- break;
- case AUTH_ENABLE_NODE:
- if (host.encrypt)
- passwd = host.enable_encrypt;
- else
- passwd = host.enable;
- next_node = ENABLE_NODE;
- break;
- }
+ VTY_UNLOCK() ;
+} ;
- if (passwd)
- {
- if (host.encrypt)
- fail = strcmp (crypt(buf, passwd), passwd);
- else
- fail = strcmp (buf, passwd);
- }
- else
- fail = 1;
-
- if (! fail)
- {
- vty->fail = 0;
- vty->node = next_node; /* Success ! */
- }
- else
- {
- vty->fail++;
- if (vty->fail >= 3)
- {
- if (vty->node == AUTH_NODE)
- {
- vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
- vty->status = VTY_CLOSE;
- }
- else
- {
- /* AUTH_ENABLE_NODE */
- vty->fail = 0;
- vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
- vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
- }
- }
- }
-}
-
-/* Command execution over the vty interface. */
-static int
-vty_command (struct vty *vty, char *buf)
+/*------------------------------------------------------------------------------
+ * Start the VTY going.
+ *
+ * This starts the listeners for VTY_TERM and VTY_SHELL_SERV.
+ *
+ * Also starts the watch dog.
+ *
+ * This is run during early morning start, after any daemonisation, but before
+ * any threads are started -- so is, implicitly, in the CLI thread.
+ *
+ * NB: may be called once and once only.
+ *
+ * NB: MUST be in the CLI thread (if any).
+ */
+extern void
+vty_start(const char *addr, unsigned short port, const char *path)
{
- int ret;
- vector vline;
- const char *protocolname;
-
- /* Split readline string up into the vector */
- vline = cmd_make_strvec (buf);
-
- if (vline == NULL)
- return CMD_SUCCESS;
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
-#ifdef CONSUMED_TIME_CHECK
- {
- RUSAGE_T before;
- RUSAGE_T after;
- unsigned long realtime, cputime;
+ assert( (vty_init_state == vty_init_1st_stage)
+ || (vty_init_state == vty_init_2nd_stage) ) ;
- GETRUSAGE(&before);
-#endif /* CONSUMED_TIME_CHECK */
+ uty_watch_dog_start() ;
- ret = cmd_execute_command (vline, vty, NULL, 0);
+ uty_open_listeners(addr, port, path) ;
- /* Get the name of the protocol if any */
- if (zlog_default)
- protocolname = zlog_proto_names[zlog_default->protocol];
- else
- protocolname = zlog_proto_names[ZLOG_NONE];
-
-#ifdef CONSUMED_TIME_CHECK
- GETRUSAGE(&after);
- if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
- CONSUMED_TIME_CHECK)
- /* Warn about CPU hog that must be fixed. */
- zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
- realtime/1000, cputime/1000, buf);
- }
-#endif /* CONSUMED_TIME_CHECK */
-
- if (ret != CMD_SUCCESS)
- switch (ret)
- {
- case CMD_WARNING:
- if (vty->type == VTY_FILE)
- vty_out (vty, "Warning...%s", VTY_NEWLINE);
- break;
- case CMD_ERR_AMBIGUOUS:
- vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- break;
- case CMD_ERR_NO_MATCH:
- vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
- break;
- case CMD_ERR_INCOMPLETE:
- vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
- break;
- }
- cmd_free_strvec (vline);
-
- return ret;
-}
-
-static const char telnet_backward_char = 0x08;
-static const char telnet_space_char = ' ';
+ vty_init_state = vty_init_started ;
+ VTY_UNLOCK() ;
+} ;
-/* Basic function to write buffer to vty. */
-static void
-vty_write (struct vty *vty, const char *buf, size_t nbytes)
+/*------------------------------------------------------------------------------
+ * Reset all VTY status for reasons unknown -- probably SIGHUP
+ */
+extern void
+vty_reset()
{
- if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
- return;
-
- /* Should we do buffering here ? And make vty_flush (vty) ? */
- buffer_put (vty->obuf, buf, nbytes);
+ vty_reset_because("Reset") ;
}
-/* Ensure length of input buffer. Is buffer is short, double it. */
-static void
-vty_ensure (struct vty *vty, int length)
-{
- if (vty->max <= length)
- {
- vty->max *= 2;
- vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max);
- }
-}
-
-/* Basic function to insert character into vty. */
-static void
-vty_self_insert (struct vty *vty, char c)
+/*------------------------------------------------------------------------------
+ * Reset all VTY status
+ *
+ * This is done in response to SIGHUP -- and runs in the CLI thread.
+ *
+ * Half closes all VTY, leaving the death watch to tidy up once all output
+ * and any command in progress have completed.
+ *
+ * Closes all listening sockets.
+ *
+ * TODO: revoke ?
+ *
+ * NB: old code discarded all output and hard closed all the VTY...
+ */
+extern void
+vty_reset_because(const char* why)
{
- int i;
- int length;
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
- vty_ensure (vty, vty->length + 1);
- length = vty->length - vty->cp;
- memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
- vty->buf[vty->cp] = c;
+ assert(vty_init_state == vty_init_started) ;
- vty_write (vty, &vty->buf[vty->cp], length + 1);
- for (i = 0; i < length; i++)
- vty_write (vty, &telnet_backward_char, 1);
+ uty_reset(0, why) ; /* not final ! */
- vty->cp++;
- vty->length++;
+ vty_init_state = vty_init_reset ;
+ VTY_UNLOCK() ;
}
-/* Self insert character 'c' in overwrite mode. */
-static void
-vty_self_insert_overwrite (struct vty *vty, char c)
-{
- vty_ensure (vty, vty->length + 1);
- vty->buf[vty->cp++] = c;
+/*------------------------------------------------------------------------------
+ * Restart the VTY, following a vty_reset().
+ *
+ * This starts the listeners for VTY_TERM and VTY_SHELL_SERV, again.
+ *
+ * NB: may be called once, and once only, *after* a vty_reset().
+ *
+ * NB: need not be in the CLI thread (if any).
+ */
+struct vty_restart_args
+{
+ char* addr ;
+ unsigned short port ;
+ char* path ;
+} ;
+MQB_ARGS_SIZE_OK(vty_restart_args) ;
+
+static void uty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void uty_restart(const char *addr, unsigned short port,
+ const char *path) ;
+extern void
+vty_restart(const char *addr, unsigned short port, const char *path)
+{
+ VTY_LOCK() ;
+
+ /* If not running qnexus-wise, call uty_restart directly.
+ *
+ * Otherwise, construct and dispatch message to do a uty_restart.
+ */
+ if (!vty_cli_nexus)
+ uty_restart(addr, port, path) ;
+ else
+ {
+ mqueue_block mqb ;
+ struct vty_restart_args* args ;
- if (vty->cp > vty->length)
- vty->length++;
+ mqb = mqb_init_new(NULL, uty_restart_action, vty_cli_nexus) ;
+ args = mqb_get_args(mqb) ;
- if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
- return;
+ if (addr != NULL)
+ args->addr = XSTRDUP(MTYPE_TMP, addr) ;
+ else
+ args->addr = NULL ;
- vty_write (vty, &c, 1);
-}
+ args->port = port ;
-/* Insert a word into vty interface with overwrite mode. */
-static void
-vty_insert_word_overwrite (struct vty *vty, char *str)
-{
- int len = strlen (str);
- vty_write (vty, str, len);
- strcpy (&vty->buf[vty->cp], str);
- vty->cp += len;
- vty->length = vty->cp;
-}
+ if (path != NULL)
+ args->path = XSTRDUP(MTYPE_TMP, path) ;
+ else
+ args->path = NULL ;
-/* Forward character. */
-static void
-vty_forward_char (struct vty *vty)
-{
- if (vty->cp < vty->length)
- {
- vty_write (vty, &vty->buf[vty->cp], 1);
- vty->cp++;
- }
-}
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
-/* Backward character. */
+/* Deal with the uty_restart message */
static void
-vty_backward_char (struct vty *vty)
+uty_restart_action(mqueue_block mqb, mqb_flag_t flag)
{
- if (vty->cp > 0)
+ struct vty_restart_args* args ;
+ args = mqb_get_args(mqb) ;
+
+ if (flag == mqb_action)
{
- vty->cp--;
- vty_write (vty, &telnet_backward_char, 1);
- }
-}
+ VTY_LOCK() ;
-/* Move to the beginning of the line. */
-static void
-vty_beginning_of_line (struct vty *vty)
-{
- while (vty->cp)
- vty_backward_char (vty);
-}
+ uty_restart(args->addr, args->port, args->path) ;
-/* Move to the end of the line. */
-static void
-vty_end_of_line (struct vty *vty)
-{
- while (vty->cp < vty->length)
- vty_forward_char (vty);
-}
+ VTY_UNLOCK() ;
+ } ;
-static void vty_kill_line_from_beginning (struct vty *);
-static void vty_redraw_line (struct vty *);
+ if (args->addr != NULL)
+ XFREE(MTYPE_TMP, args->addr) ;
+ if (args->path != NULL)
+ XFREE(MTYPE_TMP, args->path) ;
+} ;
-/* Print command line history. This function is called from
- vty_next_line and vty_previous_line. */
+/* Do the actual restart */
static void
-vty_history_print (struct vty *vty)
+uty_restart(const char *addr, unsigned short port, const char *path)
{
- int length;
+ VTY_ASSERT_LOCKED() ;
+ assert(vty_init_state == vty_init_reset) ;
- vty_kill_line_from_beginning (vty);
+ uty_open_listeners(addr, port, path) ;
- /* Get previous line from history buffer */
- length = strlen (vty->hist[vty->hp]);
- memcpy (vty->buf, vty->hist[vty->hp], length);
- vty->cp = vty->length = length;
-
- /* Redraw current line */
- vty_redraw_line (vty);
-}
+ vty_init_state = vty_init_started ;
+} ;
-/* Show next command line history. */
-static void
-vty_next_line (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * System shut-down
+ *
+ * In the pthreads world, all threads other than the main (CLI) thread have
+ * been joined -- so this is, implicitly, in the CLI thread.
+ *
+ * Close all known vty and release all memory -- discard all pending output.
+ *
+ * NB: this may be done in any initialisation state.
+ *
+ * Note that all the locking stuff does nothing if not qpthreads_enabled, so
+ * these may be done in any state of initialisation. (It is assumed that the
+ * switch into qpthreads_enabled is an atomic action... so all second stage
+ * initialisation completes together.)
+ */
+extern void
+vty_terminate (void)
{
- int try_index;
+ if ( (vty_init_state == vty_init_pending)
+ || (vty_init_state == vty_init_terminated) )
+ return ; /* nothing to do ! */
- if (vty->hp == vty->hindex)
- return;
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
- /* Try is there history exist or not. */
- try_index = vty->hp;
- if (try_index == (VTY_MAXHIST - 1))
- try_index = 0;
- else
- try_index++;
+ assert( (vty_init_state > vty_init_pending)
+ && (vty_init_state < vty_init_terminated) ) ;
- /* If there is not history return. */
- if (vty->hist[try_index] == NULL)
- return;
- else
- vty->hp = try_index;
-
- vty_history_print (vty);
-}
-
-/* Show previous command line history. */
-static void
-vty_previous_line (struct vty *vty)
-{
- int try_index;
+ uty_reset(1, "Shut down") ; /* final reset */
- try_index = vty->hp;
- if (try_index == 0)
- try_index = VTY_MAXHIST - 1;
- else
- try_index--;
+ VTY_UNLOCK() ;
- if (vty->hist[try_index] == NULL)
- return;
- else
- vty->hp = try_index;
+ qpt_mutex_destroy(&vty_mutex, 0);
- vty_history_print (vty);
+ vty_init_state = vty_init_terminated ;
}
-/* This function redraw all of the command line character. */
+/*------------------------------------------------------------------------------
+ * Reset -- final or for SIGHUP
+ *
+ * Closes listeners.
+ *
+ * Closes (final) or half closes (SIGHUP) all VTY, and revokes any outstanding
+ * commands.
+ *
+ * Resets the vty timeout and access lists.
+ *
+ * When reach final reset it should not be possible for there to be any
+ * commands still in progress. If there are, they are simply left on the
+ * death-watch list... there is no pressing need to do anything more radical,
+ * and the presence of anything on the death watch is grounds for some debug
+ * activity !
+ */
static void
-vty_redraw_line (struct vty *vty)
+uty_reset (bool curtains, const char* why)
{
- vty_write (vty, vty->buf, vty->length);
- vty->cp = vty->length;
-}
+ vty_io vio ;
+ vty_io next ;
-/* Forward word. */
-static void
-vty_forward_word (struct vty *vty)
-{
- while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
- vty_forward_char (vty);
-
- while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
- vty_forward_char (vty);
-}
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
-/* Backward word without skipping training space. */
-static void
-vty_backward_pure_word (struct vty *vty)
-{
- while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
- vty_backward_char (vty);
-}
+ uty_close_listeners() ;
-/* Backward word. */
-static void
-vty_backward_word (struct vty *vty)
-{
- while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
- vty_backward_char (vty);
+ next = sdl_head(vio_list_base) ;
+ while (next != NULL)
+ {
+ vio = next ;
+ next = sdl_next(vio, vio_list) ;
- while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
- vty_backward_char (vty);
-}
+ if (vio->type == VTY_TERM)
+ cq_revoke(vio->vty) ;
-/* When '^D' is typed at the beginning of the line we move to the down
- level. */
-static void
-vty_down_level (struct vty *vty)
-{
- vty_out (vty, "%s", VTY_NEWLINE);
- (*config_exit_cmd.func)(NULL, vty, 0, NULL);
- vty_prompt (vty);
- vty->cp = 0;
-}
+ if (why != NULL)
+ vio->close_reason = why ;
-/* When '^Z' is received from vty, move down to the enable mode. */
-static void
-vty_end_config (struct vty *vty)
-{
- vty_out (vty, "%s", VTY_NEWLINE);
+ if (curtains)
+ uty_close(vio) ;
+ else
+ uty_half_close(vio, why) ;
+ } ;
- switch (vty->node)
+ vty_timeout_val = VTY_TIMEOUT_DEFAULT;
+
+ if (vty_accesslist_name)
{
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- vty_config_unlock (vty);
- vty->node = ENABLE_NODE;
- break;
- default:
- /* Unknown node, we have to ignore it. */
- break;
+ XFREE(MTYPE_VTY, vty_accesslist_name);
+ vty_accesslist_name = NULL;
}
- vty_prompt (vty);
- vty->cp = 0;
-}
-
-/* Delete a charcter at the current point. */
-static void
-vty_delete_char (struct vty *vty)
-{
- int i;
- int size;
-
- if (vty->length == 0)
+ if (vty_ipv6_accesslist_name)
{
- vty_down_level (vty);
- return;
+ XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
+ vty_ipv6_accesslist_name = NULL;
}
- if (vty->cp == vty->length)
- return; /* completion need here? */
+ if (curtains && vty_cwd)
+ XFREE (MTYPE_TMP, vty_cwd);
+
+ if (curtains)
+ uty_watch_dog_stop() ; /* and final death watch run */
+} ;
- size = vty->length - vty->cp;
+/*==============================================================================
+ * Opening and closing VTY.
+ *
+ * VTY without a socket may be opened and closed at will.
+ *
+ * TODO: sort out the relationship between the non-socket VTY and vty_reset()
+ */
- vty->length--;
- memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
- vty->buf[vty->length] = '\0';
-
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- return;
+/*------------------------------------------------------------------------------
+ * Create a new VTY of the given type
+ *
+ * The type may NOT be: VTY_TERM or VTY_SHELL_SERV
+ */
+extern struct vty *
+vty_open(enum vty_type type)
+{
+ struct vty* vty ;
- vty_write (vty, &vty->buf[vty->cp], size - 1);
- vty_write (vty, &telnet_space_char, 1);
+ VTY_LOCK() ;
+ vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */
+ VTY_UNLOCK() ;
- for (i = 0; i < size; i++)
- vty_write (vty, &telnet_backward_char, 1);
-}
+ return vty ;
+} ;
-/* Delete a character before the point. */
-static void
-vty_delete_backward_char (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Close the given VTY
+ */
+extern void
+vty_close (struct vty *vty)
{
- if (vty->cp == 0)
- return;
-
- vty_backward_char (vty);
- vty_delete_char (vty);
+ VTY_LOCK() ;
+ uty_close(vty->vio) ;
+ VTY_UNLOCK() ;
}
-/* Kill rest of line from current point. */
-static void
-vty_kill_line (struct vty *vty)
+/*==============================================================================
+ * General VTY output.
+ *
+ * This is mostly used during command execution, to output the results of the
+ * command.
+ *
+ * All these end up in uty_vout -- see vty_io.
+ */
+
+/*------------------------------------------------------------------------------
+ * VTY output -- cf fprintf !
+ */
+extern int
+vty_out (struct vty *vty, const char *format, ...)
{
- int i;
- int size;
+ int result;
- size = vty->length - vty->cp;
-
- if (size == 0)
- return;
+ VTY_LOCK() ;
+ va_list args;
+ va_start (args, format);
+ result = uty_vout(vty, format, args);
+ va_end (args);
+ VTY_UNLOCK() ;
+ return result;
+}
- for (i = 0; i < size; i++)
- vty_write (vty, &telnet_space_char, 1);
- for (i = 0; i < size; i++)
- vty_write (vty, &telnet_backward_char, 1);
+/*------------------------------------------------------------------------------
+ * VTY output -- output a given numnber of spaces
+ */
- memset (&vty->buf[vty->cp], 0, size);
- vty->length = vty->cp;
-}
+/* 1 2 3 4 */
+/* 1234567890123456789012345678901234567890 */
+const char vty_spaces_string[] = " " ;
+CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ;
-/* Kill line from the beginning. */
-static void
-vty_kill_line_from_beginning (struct vty *vty)
+extern void
+vty_out_indent(struct vty *vty, int indent)
{
- vty_beginning_of_line (vty);
- vty_kill_line (vty);
-}
+ while (indent > 0)
+ {
+ vty_out(vty, VTY_SPACES(indent)) ;
+ indent -= VTY_MAX_SPACES ;
+ }
+} ;
-/* Delete a word before the point. */
-static void
-vty_forward_kill_word (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * VTY output -- output the current time in standard form, to the second.
+ */
+extern void
+vty_time_print (struct vty *vty, int cr)
{
- while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
- vty_delete_char (vty);
- while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
- vty_delete_char (vty);
-}
+ char buf [timestamp_buffer_len];
-/* Delete a word before the point. */
-static void
-vty_backward_kill_word (struct vty *vty)
-{
- while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
- vty_delete_backward_char (vty);
- while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
- vty_delete_backward_char (vty);
+ quagga_timestamp(0, buf, sizeof(buf)) ;
+
+ if (cr)
+ vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+ else
+ vty_out (vty, "%s ", buf);
+
+ return;
}
-/* Transpose chars before or at the point. */
-static void
-vty_transpose_chars (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Say hello to vty interface.
+ */
+void
+vty_hello (struct vty *vty)
{
- char c1, c2;
-
- /* If length is short or point is near by the beginning of line then
- return. */
- if (vty->length < 2 || vty->cp < 1)
- return;
+ VTY_LOCK() ;
- /* In case of point is located at the end of the line. */
- if (vty->cp == vty->length)
+#ifdef QDEBUG
+ uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE);
+#endif
+ if (host.motdfile)
{
- c1 = vty->buf[vty->cp - 1];
- c2 = vty->buf[vty->cp - 2];
+ FILE *f;
+ char buf[4096];
- vty_backward_char (vty);
- vty_backward_char (vty);
- vty_self_insert_overwrite (vty, c1);
- vty_self_insert_overwrite (vty, c2);
+ f = fopen (host.motdfile, "r");
+ if (f)
+ {
+ while (fgets (buf, sizeof (buf), f))
+ {
+ char *s;
+ /* work backwards to ignore trailing isspace() */
+ for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
+ s--);
+ *s = '\0';
+ uty_out (vty, "%s%s", buf, VTY_NEWLINE);
+ }
+ fclose (f);
+ }
+ else
+ uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE);
}
- else
- {
- c1 = vty->buf[vty->cp];
- c2 = vty->buf[vty->cp - 1];
+ else if (host.motd)
+ uty_out (vty, "%s", host.motd);
- vty_backward_char (vty);
- vty_self_insert_overwrite (vty, c1);
- vty_self_insert_overwrite (vty, c2);
- }
+ VTY_UNLOCK() ;
}
-/* Do completion at vty interface. */
-static void
-vty_complete_command (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Clear the contents of the command output FIFO etc.
+ */
+extern void
+vty_out_clear(struct vty* vty)
{
- int i;
- int ret;
- char **matched = NULL;
- vector vline;
+ VTY_LOCK() ;
+ uty_out_clear(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- return;
+/*==============================================================================
+ * Command Execution
+ */
+
+/*------------------------------------------------------------------------------
+ * Execute command -- adding to history is not empty or just comment
+ *
+ * This is for VTY_TERM type VTY.
+ *
+ * Outputs diagnostics if fails to parse.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+uty_command(struct vty *vty)
+{
+ enum cmd_return_code ret;
- vline = cmd_make_strvec (vty->buf);
- if (vline == NULL)
- return;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- /* In case of 'help \t'. */
- if (isspace ((int) vty->buf[vty->length - 1]))
- vector_set (vline, '\0');
+ assert(vty->vio->type == VTY_TERM) ;
- matched = cmd_complete_command (vline, vty, &ret);
-
- cmd_free_strvec (vline);
+ /* Parse the command and add to history (if not empty) */
+ ret = cmd_parse_command(vty,
+ cmd_parse_completion + cmd_parse_do + cmd_parse_tree) ;
+ if (ret != CMD_EMPTY)
+ uty_cli_hist_add (vty->vio, vty->buf) ;
- vty_out (vty, "%s", VTY_NEWLINE);
- switch (ret)
+ /* If parsed and not empty, dispatch */
+ if (ret == CMD_SUCCESS)
{
+#ifdef CONSUMED_TIME_CHECK
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
+
+ GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
+
+ ret = cmd_dispatch(vty, cmd_may_queue) ;
+
+#ifdef CONSUMED_TIME_CHECK
+ GETRUSAGE(&after);
+ if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
+ CONSUMED_TIME_CHECK)
+ /* Warn about CPU hog that must be fixed. */
+ uzlog(NULL, LOG_WARNING,
+ "SLOW COMMAND: command took %lums (cpu time %lums): %s",
+ realtime/1000, cputime/1000, vty->buf) ;
+#endif /* CONSUMED_TIME_CHECK */
+ } ;
+
+ /* Deal with the return code */
+ switch (ret)
+ {
case CMD_ERR_AMBIGUOUS:
- vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- vty_prompt (vty);
- vty_redraw_line (vty);
+ uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
break;
+
case CMD_ERR_NO_MATCH:
- /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
- vty_prompt (vty);
- vty_redraw_line (vty);
+ uty_out (vty, "%% Unknown command.%s", VTY_NEWLINE) ;
break;
- case CMD_COMPLETE_FULL_MATCH:
- vty_prompt (vty);
- vty_redraw_line (vty);
- vty_backward_pure_word (vty);
- vty_insert_word_overwrite (vty, matched[0]);
- vty_self_insert (vty, ' ');
- XFREE (MTYPE_TMP, matched[0]);
- break;
- case CMD_COMPLETE_MATCH:
- vty_prompt (vty);
- vty_redraw_line (vty);
- vty_backward_pure_word (vty);
- vty_insert_word_overwrite (vty, matched[0]);
- XFREE (MTYPE_TMP, matched[0]);
- vector_only_index_free (matched);
- return;
- break;
- case CMD_COMPLETE_LIST_MATCH:
- for (i = 0; matched[i] != NULL; i++)
- {
- if (i != 0 && ((i % 6) == 0))
- vty_out (vty, "%s", VTY_NEWLINE);
- vty_out (vty, "%-10s ", matched[i]);
- XFREE (MTYPE_TMP, matched[i]);
- }
- vty_out (vty, "%s", VTY_NEWLINE);
- vty_prompt (vty);
- vty_redraw_line (vty);
+ case CMD_ERR_INCOMPLETE:
+ uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
break;
- case CMD_ERR_NOTHING_TODO:
- vty_prompt (vty);
- vty_redraw_line (vty);
- break;
- default:
- break;
- }
- if (matched)
- vector_only_index_free (matched);
-}
-static void
-vty_describe_fold (struct vty *vty, int cmd_width,
- unsigned int desc_width, struct desc *desc)
-{
- char *buf;
- const char *cmd, *p;
- int pos;
+ default:
+ break ;
+ } ;
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+ return ret ;
+} ;
- if (desc_width <= 0)
- {
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE);
- return;
- }
+/*------------------------------------------------------------------------------
+ * Authentication of vty
+ *
+ * During AUTH_NODE and AUTH_ENABLE_NODE, when a command line is dispatched by
+ * any means this function is called.
+ *
+ * Note that if the AUTH_NODE password fails too many times, the terminal is
+ * closed.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
+{
+ char *passwd = NULL;
+ enum node_type next_node = 0;
+ int fail;
+ char *crypt (const char *, const char *);
+ enum cmd_return_code ret ;
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+ vty_io vio = vty->vio ;
- for (p = desc->str; strlen (p) > desc_width; p += pos + 1)
- {
- for (pos = desc_width; pos > 0; pos--)
- if (*(p + pos) == ' ')
- break;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- if (pos == 0)
- break;
+ /* What to do ?
+ *
+ * In fact, all the exotic command terminators simply discard any input
+ * and return.
+ */
+ switch (cli_do)
+ {
+ case cli_do_nothing:
+ case cli_do_ctrl_c:
+ case cli_do_ctrl_z:
+ return CMD_SUCCESS ;
- strncpy (buf, p, pos);
- buf[pos] = '\0';
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
+ case cli_do_command:
+ break ;
- cmd = "";
- }
+ case cli_do_ctrl_d:
+ case cli_do_eof:
+ return uty_cmd_close(vty, "End") ;
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
+ default:
+ zabort("unknown or invalid cli_do") ;
+ } ;
- XFREE (MTYPE_TMP, buf);
-}
+ /* Ordinary command dispatch -- see if password is OK. */
+ switch (vty->node)
+ {
+ case AUTH_NODE:
+ if (host.encrypt)
+ passwd = host.password_encrypt;
+ else
+ passwd = host.password;
+ if (host.advanced)
+ next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+ else
+ next_node = VIEW_NODE;
+ break;
-/* Describe matched command function. */
-static void
-vty_describe_command (struct vty *vty)
-{
- int ret;
- vector vline;
- vector describe;
- unsigned int i, width, desc_width;
- struct desc *desc, *desc_cr = NULL;
+ case AUTH_ENABLE_NODE:
+ if (host.encrypt)
+ passwd = host.enable_encrypt;
+ else
+ passwd = host.enable;
+ next_node = ENABLE_NODE;
+ break;
- vline = cmd_make_strvec (vty->buf);
+ default:
+ zabort("unknown node type") ;
+ }
- /* In case of '> ?'. */
- if (vline == NULL)
+ if (passwd)
{
- vline = vector_init (1);
- vector_set (vline, '\0');
+ if (host.encrypt)
+ fail = strcmp (crypt(buf, passwd), passwd);
+ else
+ fail = strcmp (buf, passwd);
}
- else
- if (isspace ((int) vty->buf[vty->length - 1]))
- vector_set (vline, '\0');
-
- describe = cmd_describe_command (vline, vty, &ret);
+ else
+ fail = 1;
- vty_out (vty, "%s", VTY_NEWLINE);
+ ret = CMD_SUCCESS ;
- /* Ambiguous error. */
- switch (ret)
+ if (! fail)
{
- case CMD_ERR_AMBIGUOUS:
- vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- goto out;
- break;
- case CMD_ERR_NO_MATCH:
- vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
- goto out;
- break;
- }
-
- /* Get width of command string. */
- width = 0;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- unsigned int len;
+ vio->fail = 0;
+ vty->node = next_node; /* Success ! */
+ }
+ else
+ {
+ vio->fail++;
+ if (vio->fail >= 3)
+ {
+ if (vty->node == AUTH_NODE)
+ {
+ ret = uty_cmd_close(vty, "Bad passwords, too many failures!%s") ;
+ }
+ else
+ {
+ /* AUTH_ENABLE_NODE */
+ vio->fail = 0;
+ uty_out (vty, "%% Bad enable passwords, too many failures!%s",
+ VTY_NEWLINE);
+ vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
- if (desc->cmd[0] == '\0')
- continue;
+ ret = CMD_WARNING ;
+ }
+ }
+ }
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
- len--;
+ return ret ;
+} ;
- if (width < len)
- width = len;
- }
+/*------------------------------------------------------------------------------
+ * Command line "exit" command -- aka "quit"
+ *
+ * Falls back one NODE level.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+vty_cmd_exit(struct vty* vty)
+{
+ enum cmd_return_code ret ;
- /* Get width of description string. */
- desc_width = vty->width - (width + 6);
+ VTY_LOCK() ;
- /* Print out description. */
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- if (desc->cmd[0] == '\0')
- continue;
-
- if (strcmp (desc->cmd, command_cr) == 0)
- {
- desc_cr = desc;
- continue;
- }
-
- if (!desc->str)
- vty_out (vty, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- VTY_NEWLINE);
- else if (desc_width >= strlen (desc->str))
- vty_out (vty, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, VTY_NEWLINE);
- else
- vty_describe_fold (vty, width, desc_width, desc);
-
-#if 0
- vty_out (vty, " %-*s %s%s", width
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str ? desc->str : "", VTY_NEWLINE);
-#endif /* 0 */
- }
-
- if ((desc = desc_cr))
+ ret = CMD_SUCCESS ;
+ switch (vty->node)
{
- if (!desc->str)
- vty_out (vty, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- VTY_NEWLINE);
- else if (desc_width >= strlen (desc->str))
- vty_out (vty, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, VTY_NEWLINE);
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ case RESTRICTED_NODE:
+ if (vty_shell (vty))
+ exit (0);
else
- vty_describe_fold (vty, width, desc_width, desc);
+ ret = uty_cmd_close(vty, "Exit") ;
+ break;
+ case CONFIG_NODE:
+ uty_config_unlock (vty, ENABLE_NODE);
+ break;
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case BGP_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case MASC_NODE:
+ case RMAP_NODE:
+ case VTY_NODE:
+ vty->node = CONFIG_NODE ;
+ break;
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case BGP_IPV6M_NODE:
+ vty->node = BGP_NODE ;
+ break;
+ case KEYCHAIN_KEY_NODE:
+ vty->node = KEYCHAIN_NODE ;
+ break;
+ default:
+ break;
}
-out:
- cmd_free_strvec (vline);
- if (describe)
- vector_free (describe);
-
- vty_prompt (vty);
- vty_redraw_line (vty);
+ VTY_UNLOCK() ;
+ return ret ;
}
-static void
-vty_clear_buf (struct vty *vty)
-{
- memset (vty->buf, 0, vty->max);
-}
-
-/* ^C stop current input and do not add command line to the history. */
-static void
-vty_stop_input (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command line "end" command
+ *
+ * Falls back to ENABLE_NODE.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+vty_cmd_end(struct vty* vty)
{
- vty->cp = vty->length = 0;
- vty_clear_buf (vty);
- vty_out (vty, "%s", VTY_NEWLINE);
+ VTY_LOCK() ;
switch (vty->node)
{
@@ -1108,6 +898,11 @@ vty_stop_input (struct vty *vty)
case RIP_NODE:
case RIPNG_NODE:
case BGP_NODE:
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case BGP_IPV6M_NODE:
case RMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
@@ -1116,1465 +911,557 @@ vty_stop_input (struct vty *vty)
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case VTY_NODE:
- vty_config_unlock (vty);
- vty->node = ENABLE_NODE;
+ uty_config_unlock (vty, ENABLE_NODE);
break;
default:
- /* Unknown node, we have to ignore it. */
break;
}
- vty_prompt (vty);
-
- /* Set history pointer to the latest one. */
- vty->hp = vty->hindex;
-}
-
-/* Add current command line to the history buffer. */
-static void
-vty_hist_add (struct vty *vty)
-{
- int index;
-
- if (vty->length == 0)
- return;
-
- index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
-
- /* Ignore the same string as previous one. */
- if (vty->hist[index])
- if (strcmp (vty->buf, vty->hist[index]) == 0)
- {
- vty->hp = vty->hindex;
- return;
- }
- /* Insert history entry. */
- if (vty->hist[vty->hindex])
- XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
- vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
- /* History index rotation. */
- vty->hindex++;
- if (vty->hindex == VTY_MAXHIST)
- vty->hindex = 0;
-
- vty->hp = vty->hindex;
-}
-
-/* #define TELNET_OPTION_DEBUG */
-
-/* Get telnet window size. */
-static int
-vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
+/*------------------------------------------------------------------------------
+ * Result of command is to close the input.
+ *
+ * Posts the reason for the close.
+ *
+ * Returns: CMD_CLOSE
+ */
+extern enum cmd_return_code
+uty_cmd_close(struct vty *vty, const char* reason)
{
-#ifdef TELNET_OPTION_DEBUG
- int i;
+ vty->vio->close_reason = reason ;
+ return CMD_CLOSE ;
+} ;
- for (i = 0; i < nbytes; i++)
- {
- switch (buf[i])
- {
- case IAC:
- vty_out (vty, "IAC ");
- break;
- case WILL:
- vty_out (vty, "WILL ");
- break;
- case WONT:
- vty_out (vty, "WONT ");
- break;
- case DO:
- vty_out (vty, "DO ");
- break;
- case DONT:
- vty_out (vty, "DONT ");
- break;
- case SB:
- vty_out (vty, "SB ");
- break;
- case SE:
- vty_out (vty, "SE ");
- break;
- case TELOPT_ECHO:
- vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
- break;
- case TELOPT_SGA:
- vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
- break;
- case TELOPT_NAWS:
- vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
- break;
- default:
- vty_out (vty, "%x ", buf[i]);
- break;
- }
- }
- vty_out (vty, "%s", VTY_NEWLINE);
-
-#endif /* TELNET_OPTION_DEBUG */
-
- switch (buf[0])
- {
- case SB:
- vty->sb_len = 0;
- vty->iac_sb_in_progress = 1;
- return 0;
- break;
- case SE:
- {
- if (!vty->iac_sb_in_progress)
- return 0;
-
- if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
- {
- vty->iac_sb_in_progress = 0;
- return 0;
- }
- switch (vty->sb_buf[0])
- {
- case TELOPT_NAWS:
- if (vty->sb_len != TELNET_NAWS_SB_LEN)
- zlog_warn("RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %lu",
- TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
- else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
- zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
- "too small to handle the telnet NAWS option",
- (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
- else
- {
- vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
- vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
-#ifdef TELNET_OPTION_DEBUG
- vty_out(vty, "TELNET NAWS window size negotiation completed: "
- "width %d, height %d%s",
- vty->width, vty->height, VTY_NEWLINE);
-#endif
- }
- break;
- }
- vty->iac_sb_in_progress = 0;
- return 0;
- break;
- }
- default:
- break;
- }
- return 1;
-}
-
-/* Execute current command line. */
-static int
-vty_execute (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command line ^C action.
+ *
+ * Ignores contents of command line (including not adding to history).
+ *
+ * Fall back to ENABLE_NODE if in any one of a number of nodes.
+ *
+ * Resets the history pointer.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+uty_stop_input(struct vty *vty)
{
- int ret;
+ vty_io vio = vty->vio ;
- ret = CMD_SUCCESS;
+ VTY_ASSERT_LOCKED() ;
switch (vty->node)
{
- case AUTH_NODE:
- case AUTH_ENABLE_NODE:
- vty_auth (vty, vty->buf);
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_NODE:
+ case VTY_NODE:
+ uty_config_unlock (vty, ENABLE_NODE) ;
break;
default:
- ret = vty_command (vty, vty->buf);
- if (vty->type == VTY_TERM)
- vty_hist_add (vty);
+ /* Unknown node, we have to ignore it. */
break;
}
- /* Clear command line buffer. */
- vty->cp = vty->length = 0;
- vty_clear_buf (vty);
-
- if (vty->status != VTY_CLOSE )
- vty_prompt (vty);
-
- return ret;
-}
+ /* Set history pointer to the latest one. */
+ vio->hp = vio->hindex;
-#define CONTROL(X) ((X) - '@')
-#define VTY_NORMAL 0
-#define VTY_PRE_ESCAPE 1
-#define VTY_ESCAPE 2
+ return CMD_SUCCESS ;
+} ;
-/* Escape character command map. */
-static void
-vty_escape_map (unsigned char c, struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command ^Z action.
+ *
+ * Ignores contents of command line (including not adding to history).
+ *
+ * Fall back to ENABLE_NODE if in any one of a number of nodes.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+uty_end_config (struct vty *vty)
{
- switch (c)
+ VTY_ASSERT_LOCKED() ;
+
+ switch (vty->node)
{
- case ('A'):
- vty_previous_line (vty);
- break;
- case ('B'):
- vty_next_line (vty);
- break;
- case ('C'):
- vty_forward_char (vty);
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ case RESTRICTED_NODE:
+ /* Nothing to do. */
break;
- case ('D'):
- vty_backward_char (vty);
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case BGP_IPV6M_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_NODE:
+ case VTY_NODE:
+ uty_config_unlock (vty, ENABLE_NODE) ;
break;
default:
+ /* Unknown node, we have to ignore it. */
break;
}
- /* Go back to normal mode. */
- vty->escape = VTY_NORMAL;
+ return CMD_SUCCESS ;
}
-/* Quit print out to the buffer. */
-static void
-vty_buffer_reset (struct vty *vty)
-{
- buffer_reset (vty->obuf);
- vty_prompt (vty);
- vty_redraw_line (vty);
-}
-
-/* Read data via vty socket. */
-static int
-vty_read (struct thread *thread)
+/*------------------------------------------------------------------------------
+ * Command ^D action -- when nothing else on command line.
+ *
+ * Same as "exit" command.
+ *
+ * Returns: command return code
+ */
+extern enum cmd_return_code
+uty_down_level (struct vty *vty)
{
- int i;
- int nbytes;
- unsigned char buf[VTY_READ_BUFSIZ];
-
- int vty_sock = THREAD_FD (thread);
- struct vty *vty = THREAD_ARG (thread);
- vty->t_read = NULL;
-
- /* Read raw data from socket */
- if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
- {
- if (nbytes < 0)
- {
- if (ERRNO_IO_RETRY(errno))
- {
- vty_event (VTY_READ, vty_sock, vty);
- return 0;
- }
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: read error on vty client fd %d, closing: %s",
- __func__, vty->fd, safe_strerror(errno));
- }
- buffer_reset(vty->obuf);
- vty->status = VTY_CLOSE;
- }
-
- for (i = 0; i < nbytes; i++)
- {
- if (buf[i] == IAC)
- {
- if (!vty->iac)
- {
- vty->iac = 1;
- continue;
- }
- else
- {
- vty->iac = 0;
- }
- }
-
- if (vty->iac_sb_in_progress && !vty->iac)
- {
- if (vty->sb_len < sizeof(vty->sb_buf))
- vty->sb_buf[vty->sb_len] = buf[i];
- vty->sb_len++;
- continue;
- }
+ return vty_cmd_exit(vty) ;
+} ;
- if (vty->iac)
- {
- /* In case of telnet command */
- int ret = 0;
- ret = vty_telnet_option (vty, buf + i, nbytes - i);
- vty->iac = 0;
- i += ret;
- continue;
- }
-
-
- if (vty->status == VTY_MORE)
- {
- switch (buf[i])
- {
- case CONTROL('C'):
- case 'q':
- case 'Q':
- vty_buffer_reset (vty);
- break;
-#if 0 /* More line does not work for "show ip bgp". */
- case '\n':
- case '\r':
- vty->status = VTY_MORELINE;
- break;
-#endif
- default:
- break;
- }
- continue;
- }
-
- /* Escape character. */
- if (vty->escape == VTY_ESCAPE)
- {
- vty_escape_map (buf[i], vty);
- continue;
- }
-
- /* Pre-escape status. */
- if (vty->escape == VTY_PRE_ESCAPE)
- {
- switch (buf[i])
- {
- case '[':
- vty->escape = VTY_ESCAPE;
- break;
- case 'b':
- vty_backward_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- case 'f':
- vty_forward_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- case 'd':
- vty_forward_kill_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- case CONTROL('H'):
- case 0x7f:
- vty_backward_kill_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- default:
- vty->escape = VTY_NORMAL;
- break;
- }
- continue;
- }
+/*==============================================================================
+ * Reading of configuration file
+ *
+ * The reading of the configuration file occurs at two times:
+ *
+ * 1. early in the morning, before daemonisation, and before any threads
+ * or nexuses have been set up.
+ *
+ * In the qpthreads world, this means that it is running in the main (CLI)
+ * and only thread and nexus.
+ *
+ * 2. at SIGHUP time.
+ *
+ * In the qpthreads world, this is running in whatever thread is executing
+ * commands.
+ *
+ * Sets up a VTY_CONFIG_READ in which to execute commands. This has no CLI
+ * and no socket. All output is buffered in the cmd_obuf. All commands are
+ * run directly in the thread -- no commands are queued.
+ */
- switch (buf[i])
- {
- case CONTROL('A'):
- vty_beginning_of_line (vty);
- break;
- case CONTROL('B'):
- vty_backward_char (vty);
- break;
- case CONTROL('C'):
- vty_stop_input (vty);
- break;
- case CONTROL('D'):
- vty_delete_char (vty);
- break;
- case CONTROL('E'):
- vty_end_of_line (vty);
- break;
- case CONTROL('F'):
- vty_forward_char (vty);
- break;
- case CONTROL('H'):
- case 0x7f:
- vty_delete_backward_char (vty);
- break;
- case CONTROL('K'):
- vty_kill_line (vty);
- break;
- case CONTROL('N'):
- vty_next_line (vty);
- break;
- case CONTROL('P'):
- vty_previous_line (vty);
- break;
- case CONTROL('T'):
- vty_transpose_chars (vty);
- break;
- case CONTROL('U'):
- vty_kill_line_from_beginning (vty);
- break;
- case CONTROL('W'):
- vty_backward_kill_word (vty);
- break;
- case CONTROL('Z'):
- vty_end_config (vty);
- break;
- case '\n':
- case '\r':
- vty_out (vty, "%s", VTY_NEWLINE);
- vty_execute (vty);
- break;
- case '\t':
- vty_complete_command (vty);
- break;
- case '?':
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- vty_self_insert (vty, buf[i]);
- else
- vty_describe_command (vty);
- break;
- case '\033':
- if (i + 1 < nbytes && buf[i + 1] == '[')
- {
- vty->escape = VTY_ESCAPE;
- i++;
- }
- else
- vty->escape = VTY_PRE_ESCAPE;
- break;
- default:
- if (buf[i] > 31 && buf[i] < 127)
- vty_self_insert (vty, buf[i]);
- break;
- }
- }
+static FILE * vty_use_backup_config (char *fullpath) ;
+static void vty_read_file (FILE *confp, struct cmd_element* first_cmd,
+ bool ignore_warnings) ;
- /* Check status. */
- if (vty->status == VTY_CLOSE)
- vty_close (vty);
- else
- {
- vty_event (VTY_WRITE, vty_sock, vty);
- vty_event (VTY_READ, vty_sock, vty);
- }
- return 0;
+/*------------------------------------------------------------------------------
+ * Read the given configuration file.
+ */
+extern void
+vty_read_config (char *config_file,
+ char *config_default)
+{
+ vty_read_config_first_cmd_special(config_file, config_default, NULL, 1);
}
-/* Flush buffer to the vty. */
-static int
-vty_flush (struct thread *thread)
+/*------------------------------------------------------------------------------
+ * Read the given configuration file.
+ *
+ * The config_file (-f argument) is used if specified.
+ *
+ * If config_file is NULL, use the config_default.
+ *
+ * If using the config_default, if VTYSH_ENABLED, look for "vtysh" in the name.
+ * If find "vtysh" and find the "integrate_default" file, then do nothing
+ * now -- expect vtysh to connect in due course and provide the configuration.
+ *
+ * The config_file or config_default may be relative file names.
+ *
+ * May have a function to call after the first actual command is processed.
+ * This mechanism supports the setting of qpthreads-ness by configuration file
+ * command.
+ */
+extern void
+vty_read_config_first_cmd_special(char *config_file,
+ char *config_default,
+ struct cmd_element* first_cmd,
+ bool ignore_warnings)
{
- int erase;
- buffer_status_t flushrc;
- int vty_sock = THREAD_FD (thread);
- struct vty *vty = THREAD_ARG (thread);
-
- vty->t_write = NULL;
+ char cwd[MAXPATHLEN];
+ FILE *confp = NULL;
+ char *fullpath;
+ char *tmp = NULL;
- /* Tempolary disable read thread. */
- if ((vty->lines == 0) && vty->t_read)
+ /* Deal with VTYSH_ENABLED magic */
+ if (VTYSH_ENABLED && (config_file == NULL))
{
- thread_cancel (vty->t_read);
- vty->t_read = NULL;
- }
+ int ret;
+ struct stat conf_stat;
- /* Function execution continue. */
- erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
+ /* !!!!PLEASE LEAVE!!!!
+ * This is NEEDED for use with vtysh -b, or else you can get
+ * a real configuration food fight with a lot garbage in the
+ * merged configuration file it creates coming from the per
+ * daemon configuration files. This also allows the daemons
+ * to start if there default configuration file is not
+ * present or ignore them, as needed when using vtysh -b to
+ * configure the daemons at boot - MAG
+ */
- /* N.B. if width is 0, that means we don't know the window size. */
- if ((vty->lines == 0) || (vty->width == 0))
- flushrc = buffer_flush_available(vty->obuf, vty->fd);
- else if (vty->status == VTY_MORELINE)
- flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
- 1, erase, 0);
- else
- flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
- vty->lines >= 0 ? vty->lines :
- vty->height,
- erase, 0);
- switch (flushrc)
- {
- case BUFFER_ERROR:
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("buffer_flush failed on vty client fd %d, closing",
- vty->fd);
- buffer_reset(vty->obuf);
- vty_close(vty);
- return 0;
- case BUFFER_EMPTY:
- if (vty->status == VTY_CLOSE)
- vty_close (vty);
- else
- {
- vty->status = VTY_NORMAL;
- if (vty->lines == 0)
- vty_event (VTY_READ, vty_sock, vty);
- }
- break;
- case BUFFER_PENDING:
- /* There is more data waiting to be written. */
- vty->status = VTY_MORE;
- if (vty->lines == 0)
- vty_event (VTY_WRITE, vty_sock, vty);
- break;
- }
+ /* Stat for vtysh Zebra.conf, if found startup and wait for
+ * boot configuration
+ */
- return 0;
-}
+ if ( strstr(config_default, "vtysh") == NULL)
+ {
+ ret = stat (integrate_default, &conf_stat);
+ if (ret >= 0)
+ return;
+ }
+ } ;
-/* Create new vty structure. */
-static struct vty *
-vty_create (int vty_sock, union sockunion *su)
-{
- struct vty *vty;
+ /* Use default if necessary, and deal with constructing full path */
+ if (config_file == NULL)
+ config_file = config_default ;
- /* Allocate new vty structure and set up default values. */
- vty = vty_new ();
- vty->fd = vty_sock;
- vty->type = VTY_TERM;
- vty->address = sockunion_su2str (su);
- if (no_password_check)
+ if (! IS_DIRECTORY_SEP (config_file[0]))
{
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
+ getcwd (cwd, sizeof(cwd)) ;
+ tmp = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (config_file) + 2) ;
+ sprintf (tmp, "%s/%s", cwd, config_file);
+ fullpath = tmp;
}
else
- vty->node = AUTH_NODE;
- vty->fail = 0;
- vty->cp = 0;
- vty_clear_buf (vty);
- vty->length = 0;
- memset (vty->hist, 0, sizeof (vty->hist));
- vty->hp = 0;
- vty->hindex = 0;
- vector_set_index (vtyvec, vty_sock, vty);
- vty->status = VTY_NORMAL;
- vty->v_timeout = vty_timeout_val;
- if (host.lines >= 0)
- vty->lines = host.lines;
- else
- vty->lines = -1;
- vty->iac = 0;
- vty->iac_sb_in_progress = 0;
- vty->sb_len = 0;
-
- if (! no_password_check)
{
- /* Vty is not available if password isn't set. */
- if (host.password == NULL && host.password_encrypt == NULL)
- {
- vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
- vty->status = VTY_CLOSE;
- vty_close (vty);
- return NULL;
- }
- }
-
- /* Say hello to the world. */
- vty_hello (vty);
- if (! no_password_check)
- vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
- /* Setting up terminal. */
- vty_will_echo (vty);
- vty_will_suppress_go_ahead (vty);
-
- vty_dont_linemode (vty);
- vty_do_window_size (vty);
- /* vty_dont_lflow_ahead (vty); */
-
- vty_prompt (vty);
-
- /* Add read/write thread. */
- vty_event (VTY_WRITE, vty_sock, vty);
- vty_event (VTY_READ, vty_sock, vty);
-
- return vty;
-}
-
-/* Accept connection from the network. */
-static int
-vty_accept (struct thread *thread)
-{
- int vty_sock;
- struct vty *vty;
- union sockunion su;
- int ret;
- unsigned int on;
- int accept_sock;
- struct prefix *p = NULL;
- struct access_list *acl = NULL;
- char *bufp;
-
- accept_sock = THREAD_FD (thread);
+ tmp = NULL ;
+ fullpath = config_file;
+ } ;
- /* We continue hearing vty socket. */
- vty_event (VTY_SERV, accept_sock, NULL);
+ /* try to open the configuration file */
+ confp = fopen (fullpath, "r");
- memset (&su, 0, sizeof (union sockunion));
-
- /* We can handle IPv4 or IPv6 socket. */
- vty_sock = sockunion_accept (accept_sock, &su);
- if (vty_sock < 0)
+ if (confp == NULL)
{
- zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
- return -1;
- }
- set_nonblocking(vty_sock);
+ fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
+ __func__, fullpath, errtostr(errno, 0).str);
- p = sockunion2hostprefix (&su);
-
- /* VTY's accesslist apply. */
- if (p->family == AF_INET && vty_accesslist_name)
- {
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- {
- char *buf;
- zlog (NULL, LOG_INFO, "Vty connection refused from %s",
- (buf = sockunion_su2str (&su)));
- free (buf);
- close (vty_sock);
-
- /* continue accepting connections */
- vty_event (VTY_SERV, accept_sock, NULL);
-
- prefix_free (p);
-
- return 0;
- }
- }
+ confp = vty_use_backup_config (fullpath);
+ if (confp)
+ fprintf (stderr, "WARNING: using backup configuration file!\n");
+ else
+ {
+ fprintf (stderr, "can't open backup configuration file [%s%s]\n",
+ fullpath, CONF_BACKUP_EXT);
+ exit(1);
+ }
+ } ;
-#ifdef HAVE_IPV6
- /* VTY's ipv6 accesslist apply. */
- if (p->family == AF_INET6 && vty_ipv6_accesslist_name)
- {
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- {
- char *buf;
- zlog (NULL, LOG_INFO, "Vty connection refused from %s",
- (buf = sockunion_su2str (&su)));
- free (buf);
- close (vty_sock);
-
- /* continue accepting connections */
- vty_event (VTY_SERV, accept_sock, NULL);
-
- prefix_free (p);
-
- return 0;
- }
- }
-#endif /* HAVE_IPV6 */
-
- prefix_free (p);
+#ifdef QDEBUG
+ fprintf(stderr, "Reading config file: %s\n", fullpath);
+#endif
- on = 1;
- ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
- (char *) &on, sizeof (on));
- if (ret < 0)
- zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
- safe_strerror (errno));
+ vty_read_file (confp, first_cmd, ignore_warnings);
+ fclose (confp);
- zlog (NULL, LOG_INFO, "Vty connection from %s",
- (bufp = sockunion_su2str (&su)));
- if (bufp)
- XFREE (MTYPE_TMP, bufp);
+ host_config_set (fullpath);
- vty = vty_create (vty_sock, &su);
+#ifdef QDEBUG
+ fprintf(stderr, "Finished reading config file\n");
+#endif
- return 0;
+ if (tmp)
+ XFREE (MTYPE_TMP, tmp);
}
-#if defined(HAVE_IPV6) && !defined(NRL)
-static void
-vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
+/*------------------------------------------------------------------------------
+ * Try to use a backup configuration file.
+ *
+ * Having failed to open the file "<fullpath>", if there is a file called
+ * "<fullpath>.sav" that can be opened for reading, then:
+ *
+ * - make a copy of that file
+ * - call it "<fullpath>"
+ * - return an open FILE
+ *
+ * Returns: NULL => no "<fullpath>.sav", or faild doing any of the above
+ * otherwise, returns FILE* for open file.
+ */
+static FILE *
+vty_use_backup_config (char *fullpath)
{
- int ret;
- struct addrinfo req;
- struct addrinfo *ainfo;
- struct addrinfo *ainfo_save;
- int sock;
- char port_str[BUFSIZ];
+ char *tmp_path ;
+ struct stat buf;
+ int ret, tmp, sav;
+ int c;
+ char buffer[4096] ;
- memset (&req, 0, sizeof (struct addrinfo));
- req.ai_flags = AI_PASSIVE;
- req.ai_family = AF_UNSPEC;
- req.ai_socktype = SOCK_STREAM;
- sprintf (port_str, "%d", port);
- port_str[sizeof (port_str) - 1] = '\0';
+ enum { xl = 32 } ;
+ tmp_path = malloc(strlen(fullpath) + xl) ;
- ret = getaddrinfo (hostname, port_str, &req, &ainfo);
+ /* construct the name "<fullname>.sav", and try to open it. */
+ confirm(xl > sizeof(CONF_BACKUP_EXT)) ;
+ sprintf (tmp_path, "%s%s", fullpath, CONF_BACKUP_EXT) ;
- if (ret != 0)
- {
- fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
- exit (1);
- }
-
- ainfo_save = ainfo;
+ sav = -1 ;
+ if (stat (tmp_path, &buf) != -1)
+ sav = open (tmp_path, O_RDONLY);
- do
+ if (sav < 0)
{
- if (ainfo->ai_family != AF_INET
-#ifdef HAVE_IPV6
- && ainfo->ai_family != AF_INET6
-#endif /* HAVE_IPV6 */
- )
- continue;
-
- sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
- if (sock < 0)
- continue;
-
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
-
- ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
- if (ret < 0)
- {
- close (sock); /* Avoid sd leak. */
- continue;
- }
-
- ret = listen (sock, 3);
- if (ret < 0)
- {
- close (sock); /* Avoid sd leak. */
- continue;
- }
-
- vty_event (VTY_SERV, sock, NULL);
- }
- while ((ainfo = ainfo->ai_next) != NULL);
-
- freeaddrinfo (ainfo_save);
-}
-#endif /* HAVE_IPV6 && ! NRL */
+ free (tmp_path);
+ return NULL;
+ } ;
-/* Make vty server socket. */
-static void
-vty_serv_sock_family (const char* addr, unsigned short port, int family)
-{
- int ret;
- union sockunion su;
- int accept_sock;
- void* naddr=NULL;
+ /* construct a temporary file and copy "<fullpath.sav>" to it. */
+ confirm(xl > sizeof(".XXXXXX"))
+ sprintf (tmp_path, "%s%s", fullpath, ".XXXXXX") ;
- memset (&su, 0, sizeof (union sockunion));
- su.sa.sa_family = family;
- if(addr)
- switch(family)
- {
- case AF_INET:
- naddr=&su.sin.sin_addr;
-#ifdef HAVE_IPV6
- case AF_INET6:
- naddr=&su.sin6.sin6_addr;
-#endif
- }
-
- if(naddr)
- switch(inet_pton(family,addr,naddr))
+ /* Open file to configuration write. */
+ tmp = mkstemp (tmp_path);
+ if (tmp < 0)
{
- case -1:
- zlog_err("bad address %s",addr);
- naddr=NULL;
- break;
- case 0:
- zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
- naddr=NULL;
+ free (tmp_path);
+ close(sav);
+ return NULL;
}
- /* Make new socket. */
- accept_sock = sockunion_stream_socket (&su);
- if (accept_sock < 0)
- return;
+ while((c = read (sav, buffer, sizeof(buffer))) > 0)
+ write (tmp, buffer, c);
- /* This is server, so reuse address. */
- sockopt_reuseaddr (accept_sock);
- sockopt_reuseport (accept_sock);
+ close (sav);
+ close (tmp);
- /* Bind socket to universal address and given port. */
- ret = sockunion_bind (accept_sock, &su, port, naddr);
- if (ret < 0)
+ /* Make sure that have the required file status */
+ if (chmod(tmp_path, CONFIGFILE_MASK) != 0)
{
- zlog_warn("can't bind socket");
- close (accept_sock); /* Avoid sd leak. */
- return;
+ unlink (tmp_path);
+ free (tmp_path);
+ return NULL;
}
- /* Listen socket under queue 3. */
- ret = listen (accept_sock, 3);
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't listen socket");
- close (accept_sock); /* Avoid sd leak. */
- return;
- }
+ /* Make <fullpath> be a name for the new file just created. */
+ ret = link (tmp_path, fullpath) ;
- /* Add vty server event. */
- vty_event (VTY_SERV, accept_sock, NULL);
-}
+ /* Discard the temporary, now */
+ unlink (tmp_path) ;
+ free (tmp_path) ;
-#ifdef VTYSH
-/* For sockaddr_un. */
-#include <sys/un.h>
+ /* If link was successful, try to open -- otherwise, failed. */
+ return (ret == 0) ? fopen (fullpath, "r") : NULL ;
+} ;
-/* VTY shell UNIX domain socket. */
+/*------------------------------------------------------------------------------
+ * Read the given configuration file.
+ *
+ * May have a function to call after the first actual command is processed.
+ * This mechanism supports the setting of qpthreads-ness by configuration file
+ * command.
+ *
+ * In the qpthreads world:
+ *
+ * * when the configuration is first read, this runs in the CLI thread
+ * (the main and only thread).
+ *
+ * * when the configuration is reread, this runs in the command processor
+ * thread.
+ *
+ * All consoles are shut down, so there can be no interference from that
+ * quarter.
+ *
+ * so all commands are executed directly.
+ */
static void
-vty_serv_un (const char *path)
-{
- int ret;
- int sock, len;
- struct sockaddr_un serv;
- mode_t old_mask;
- struct zprivs_ids_t ids;
-
- /* First of all, unlink existing socket */
- unlink (path);
-
- /* Set umask */
- old_mask = umask (0007);
-
- /* Make UNIX domain socket. */
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- {
- zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
- return;
- }
-
- /* Make server socket. */
- memset (&serv, 0, sizeof (struct sockaddr_un));
- serv.sun_family = AF_UNIX;
- strncpy (serv.sun_path, path, strlen (path));
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- len = serv.sun_len = SUN_LEN(&serv);
-#else
- len = sizeof (serv.sun_family) + strlen (serv.sun_path);
-#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
-
- ret = bind (sock, (struct sockaddr *) &serv, len);
- if (ret < 0)
- {
- zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
- close (sock); /* Avoid sd leak. */
- return;
- }
-
- ret = listen (sock, 5);
- if (ret < 0)
- {
- zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
- close (sock); /* Avoid sd leak. */
- return;
- }
-
- umask (old_mask);
-
- zprivs_get_ids(&ids);
-
- if (ids.gid_vty > 0)
- {
- /* set group of socket */
- if ( chown (path, -1, ids.gid_vty) )
- {
- zlog_err ("vty_serv_un: could chown socket, %s",
- safe_strerror (errno) );
- }
- }
-
- vty_event (VTYSH_SERV, sock, NULL);
-}
-
-/* #define VTYSH_DEBUG 1 */
-
-static int
-vtysh_accept (struct thread *thread)
+vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings)
{
- int accept_sock;
- int sock;
- int client_len;
- struct sockaddr_un client;
- struct vty *vty;
-
- accept_sock = THREAD_FD (thread);
-
- vty_event (VTYSH_SERV, accept_sock, NULL);
-
- memset (&client, 0, sizeof (struct sockaddr_un));
- client_len = sizeof (struct sockaddr_un);
-
- sock = accept (accept_sock, (struct sockaddr *) &client,
- (socklen_t *) &client_len);
-
- if (sock < 0)
- {
- zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
- return -1;
- }
-
- if (set_nonblocking(sock) < 0)
- {
- zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
- " %s, closing", sock, safe_strerror (errno));
- close (sock);
- return -1;
- }
-
-#ifdef VTYSH_DEBUG
- printf ("VTY shell accept\n");
-#endif /* VTYSH_DEBUG */
-
- vty = vty_new ();
- vty->fd = sock;
- vty->type = VTY_SHELL_SERV;
- vty->node = VIEW_NODE;
+ enum cmd_return_code ret ;
+ struct vty *vty ;
+ qtime_t taking ;
- vty_event (VTYSH_READ, sock, vty);
-
- return 0;
-}
-
-static int
-vtysh_flush(struct vty *vty)
-{
- switch (buffer_flush_available(vty->obuf, vty->fd))
- {
- case BUFFER_PENDING:
- vty_event(VTYSH_WRITE, vty->fd, vty);
- break;
- case BUFFER_ERROR:
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
- buffer_reset(vty->obuf);
- vty_close(vty);
- return -1;
- break;
- case BUFFER_EMPTY:
- break;
- }
- return 0;
-}
-
-static int
-vtysh_read (struct thread *thread)
-{
- int ret;
- int sock;
- int nbytes;
- struct vty *vty;
- unsigned char buf[VTY_READ_BUFSIZ];
- unsigned char *p;
- u_char header[4] = {0, 0, 0, 0};
-
- sock = THREAD_FD (thread);
- vty = THREAD_ARG (thread);
- vty->t_read = NULL;
-
- if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
- {
- if (nbytes < 0)
- {
- if (ERRNO_IO_RETRY(errno))
- {
- vty_event (VTYSH_READ, sock, vty);
- return 0;
- }
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
- __func__, sock, safe_strerror(errno));
- }
- buffer_reset(vty->obuf);
- vty_close (vty);
-#ifdef VTYSH_DEBUG
- printf ("close vtysh\n");
-#endif /* VTYSH_DEBUG */
- return 0;
- }
-
-#ifdef VTYSH_DEBUG
- printf ("line: %.*s\n", nbytes, buf);
-#endif /* VTYSH_DEBUG */
+ /* Set up configuration file reader VTY -- which buffers all output */
+ vty = vty_open(VTY_CONFIG_READ);
+ vty->node = CONFIG_NODE;
- for (p = buf; p < buf+nbytes; p++)
- {
- vty_ensure(vty, vty->length+1);
- vty->buf[vty->length++] = *p;
- if (*p == '\0')
- {
- /* Pass this line to parser. */
- ret = vty_execute (vty);
- /* Note that vty_execute clears the command buffer and resets
- vty->length to 0. */
-
- /* Return result. */
-#ifdef VTYSH_DEBUG
- printf ("result: %d\n", ret);
- printf ("vtysh node: %d\n", vty->node);
-#endif /* VTYSH_DEBUG */
-
- header[3] = ret;
- buffer_put(vty->obuf, header, 4);
-
- if (!vty->t_write && (vtysh_flush(vty) < 0))
- /* Try to flush results; exit if a write error occurs. */
- return 0;
- }
- }
+ /* Make sure we have a suitable buffer, and set vty->buf to point at
+ * it -- same like other command execution.
+ */
+ qs_need(&vty->vio->clx, VTY_BUFSIZ) ;
+ vty->buf = qs_chars(&vty->vio->clx) ;
- vty_event (VTYSH_READ, sock, vty);
+ taking = qt_get_monotonic() ;
- return 0;
-}
+ /* Execute configuration file */
+ ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx,
+ ignore_warnings) ;
-static int
-vtysh_write (struct thread *thread)
-{
- struct vty *vty = THREAD_ARG (thread);
+ taking = (qt_get_monotonic() - taking) / (QTIME_SECOND / 1000) ;
- vty->t_write = NULL;
- vtysh_flush(vty);
- return 0;
-}
+ zlog_info("Finished reading configuration in %d.%dsecs%s",
+ (int)(taking / 1000), (int)(taking % 1000),
+ (ret == CMD_SUCCESS) ? "." : " -- FAILED") ;
-#endif /* VTYSH */
+ VTY_LOCK() ;
-/* Determine address family to bind. */
-void
-vty_serv_sock (const char *addr, unsigned short port, const char *path)
-{
- /* If port is set to 0, do not listen on TCP/IP at all! */
- if (port)
+ if (ret != CMD_SUCCESS)
{
+ fprintf (stderr, "%% while processing line %u of the configuration:\n"
+ "%s", vty->lineno, vty->buf) ;
-#ifdef HAVE_IPV6
-#ifdef NRL
- vty_serv_sock_family (addr, port, AF_INET);
- vty_serv_sock_family (addr, port, AF_INET6);
-#else /* ! NRL */
- vty_serv_sock_addrinfo (addr, port);
-#endif /* NRL*/
-#else /* ! HAVE_IPV6 */
- vty_serv_sock_family (addr,port, AF_INET);
-#endif /* HAVE_IPV6 */
- }
-
-#ifdef VTYSH
- vty_serv_un (path);
-#endif /* VTYSH */
-}
-
-/* Close vty interface. Warning: call this only from functions that
- will be careful not to access the vty afterwards (since it has
- now been freed). This is safest from top-level functions (called
- directly by the thread dispatcher). */
-void
-vty_close (struct vty *vty)
-{
- int i;
-
- /* Cancel threads.*/
- if (vty->t_read)
- thread_cancel (vty->t_read);
- if (vty->t_write)
- thread_cancel (vty->t_write);
- if (vty->t_timeout)
- thread_cancel (vty->t_timeout);
-
- /* Flush buffer. */
- buffer_flush_all (vty->obuf, vty->fd);
-
- /* Free input buffer. */
- buffer_free (vty->obuf);
-
- /* Free command history. */
- for (i = 0; i < VTY_MAXHIST; i++)
- if (vty->hist[i])
- XFREE (MTYPE_VTY_HIST, vty->hist[i]);
-
- /* Unset vector. */
- vector_unset (vtyvec, vty->fd);
-
- /* Close socket. */
- if (vty->fd > 0)
- close (vty->fd);
-
- if (vty->address)
- XFREE (MTYPE_TMP, vty->address);
- if (vty->buf)
- XFREE (MTYPE_VTY, vty->buf);
-
- /* Check configure. */
- vty_config_unlock (vty);
-
- /* OK free vty. */
- XFREE (MTYPE_VTY, vty);
-}
-
-/* When time out occur output message then close connection. */
-static int
-vty_timeout (struct thread *thread)
-{
- struct vty *vty;
+ switch (ret)
+ {
+ case CMD_WARNING:
+ fprintf (stderr, "%% Warning...\n");
+ break;
- vty = THREAD_ARG (thread);
- vty->t_timeout = NULL;
- vty->v_timeout = 0;
+ case CMD_ERROR:
+ fprintf (stderr, "%% Error...\n");
+ break;
- /* Clear buffer*/
- buffer_reset (vty->obuf);
- vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
+ case CMD_ERR_AMBIGUOUS:
+ fprintf (stderr, "%% Ambiguous command.\n");
+ break;
- /* Close connection. */
- vty->status = VTY_CLOSE;
- vty_close (vty);
+ case CMD_ERR_NO_MATCH:
+ fprintf (stderr, "%% There is no such command.\n");
+ break;
- return 0;
-}
+ case CMD_ERR_INCOMPLETE:
+ fprintf (stderr, "%% Incomplete command.\n");
+ break;
-/* Read up configuration file from file_name. */
-static void
-vty_read_file (FILE *confp)
-{
- int ret;
- struct vty *vty;
+ default:
+ fprintf(stderr, "%% (unknown cause %d)\n", ret) ;
+ break ;
+ } ;
- vty = vty_new ();
- vty->fd = 0; /* stdout */
- vty->type = VTY_TERM;
- vty->node = CONFIG_NODE;
-
- /* Execute configuration file */
- ret = config_from_file (vty, confp);
+ uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */
- if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
- {
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "Ambiguous command.\n");
- break;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "There is no such command.\n");
- break;
- }
- fprintf (stderr, "Error occured during reading below line.\n%s\n",
- vty->buf);
- vty_close (vty);
exit (1);
- }
+ } ;
- vty_close (vty);
-}
+ uty_close(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
-static FILE *
-vty_use_backup_config (char *fullpath)
-{
- char *fullpath_sav, *fullpath_tmp;
- FILE *ret = NULL;
- struct stat buf;
- int tmp, sav;
- int c;
- char buffer[512];
-
- fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
- strcpy (fullpath_sav, fullpath);
- strcat (fullpath_sav, CONF_BACKUP_EXT);
- if (stat (fullpath_sav, &buf) == -1)
- {
- free (fullpath_sav);
- return NULL;
- }
-
- fullpath_tmp = malloc (strlen (fullpath) + 8);
- sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
-
- /* Open file to configuration write. */
- tmp = mkstemp (fullpath_tmp);
- if (tmp < 0)
- {
- free (fullpath_sav);
- free (fullpath_tmp);
- return NULL;
- }
-
- sav = open (fullpath_sav, O_RDONLY);
- if (sav < 0)
- {
- unlink (fullpath_tmp);
- free (fullpath_sav);
- free (fullpath_tmp);
- return NULL;
- }
-
- while((c = read (sav, buffer, 512)) > 0)
- write (tmp, buffer, c);
-
- close (sav);
- close (tmp);
-
- if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
- {
- unlink (fullpath_tmp);
- free (fullpath_sav);
- free (fullpath_tmp);
- return NULL;
- }
-
- if (link (fullpath_tmp, fullpath) == 0)
- ret = fopen (fullpath, "r");
-
- unlink (fullpath_tmp);
-
- free (fullpath_sav);
- free (fullpath_tmp);
- return ret;
-}
+/*==============================================================================
+ * Configuration node/state handling
+ *
+ * At most one VTY may hold the configuration symbol of power at any time.
+ */
-/* Read up configuration file from file_name. */
-void
-vty_read_config (char *config_file,
- char *config_default_dir)
+/*------------------------------------------------------------------------------
+ * Attempt to gain the configuration symbol of power
+ *
+ * If succeeds, set the given node.
+ *
+ * Returns: true <=> now own the symbol of power.
+ */
+extern bool
+vty_config_lock (struct vty *vty, enum node_type node)
{
- char cwd[MAXPATHLEN];
- FILE *confp = NULL;
- char *fullpath;
- char *tmp = NULL;
+ bool result;
- /* If -f flag specified. */
- if (config_file != NULL)
- {
- if (! IS_DIRECTORY_SEP (config_file[0]))
- {
- getcwd (cwd, MAXPATHLEN);
- tmp = XMALLOC (MTYPE_TMP,
- strlen (cwd) + strlen (config_file) + 2);
- sprintf (tmp, "%s/%s", cwd, config_file);
- fullpath = tmp;
- }
- else
- fullpath = config_file;
+ VTY_LOCK() ;
- confp = fopen (fullpath, "r");
-
- if (confp == NULL)
- {
- fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
- __func__, fullpath, safe_strerror (errno));
-
- confp = vty_use_backup_config (fullpath);
- if (confp)
- fprintf (stderr, "WARNING: using backup configuration file!\n");
- else
- {
- fprintf (stderr, "can't open configuration file [%s]\n",
- config_file);
- exit(1);
- }
- }
- }
- else
+ if (vty_config == 0)
{
-#ifdef VTYSH
- int ret;
- struct stat conf_stat;
+ vty->vio->config = 1 ;
+ vty_config = 1 ;
+ } ;
- /* !!!!PLEASE LEAVE!!!!
- * This is NEEDED for use with vtysh -b, or else you can get
- * a real configuration food fight with a lot garbage in the
- * merged configuration file it creates coming from the per
- * daemon configuration files. This also allows the daemons
- * to start if there default configuration file is not
- * present or ignore them, as needed when using vtysh -b to
- * configure the daemons at boot - MAG
- */
-
- /* Stat for vtysh Zebra.conf, if found startup and wait for
- * boot configuration
- */
-
- if ( strstr(config_default_dir, "vtysh") == NULL)
- {
- ret = stat (integrate_default, &conf_stat);
- if (ret >= 0)
- return;
- }
-#endif /* VTYSH */
-
- confp = fopen (config_default_dir, "r");
- if (confp == NULL)
- {
- fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
- __func__, config_default_dir, safe_strerror (errno));
-
- confp = vty_use_backup_config (config_default_dir);
- if (confp)
- {
- fprintf (stderr, "WARNING: using backup configuration file!\n");
- fullpath = config_default_dir;
- }
- else
- {
- fprintf (stderr, "can't open configuration file [%s]\n",
- config_default_dir);
- exit (1);
- }
- }
- else
- fullpath = config_default_dir;
- }
-
- vty_read_file (confp);
-
- fclose (confp);
-
- host_config_set (fullpath);
-
- if (tmp)
- XFREE (MTYPE_TMP, fullpath);
-}
-
-/* Small utility function which output log to the VTY. */
-void
-vty_log (const char *level, const char *proto_str,
- const char *format, struct timestamp_control *ctl, va_list va)
-{
- unsigned int i;
- struct vty *vty;
-
- if (!vtyvec)
- return;
-
- for (i = 0; i < vector_active (vtyvec); i++)
- if ((vty = vector_slot (vtyvec, i)) != NULL)
- if (vty->monitor)
- {
- va_list ac;
- va_copy(ac, va);
- vty_log_out (vty, level, proto_str, format, ctl, ac);
- va_end(ac);
- }
-}
+ result = vty->vio->config;
-/* Async-signal-safe version of vty_log for fixed strings. */
-void
-vty_log_fixed (const char *buf, size_t len)
-{
- unsigned int i;
- struct iovec iov[2];
+ if (result)
+ vty->node = node ;
- /* vty may not have been initialised */
- if (!vtyvec)
- return;
-
- iov[0].iov_base = (void *)buf;
- iov[0].iov_len = len;
- iov[1].iov_base = (void *)"\r\n";
- iov[1].iov_len = 2;
+ VTY_UNLOCK() ;
- for (i = 0; i < vector_active (vtyvec); i++)
- {
- struct vty *vty;
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
- /* N.B. We don't care about the return code, since process is
- most likely just about to die anyway. */
- writev(vty->fd, iov, 2);
- }
+ return result;
}
-int
-vty_config_lock (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Give back the configuration symbol of power -- if own it.
+ *
+ * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ */
+extern void
+vty_config_unlock (struct vty *vty, enum node_type node)
{
- if (vty_config == 0)
- {
- vty->config = 1;
- vty_config = 1;
- }
- return vty->config;
+ VTY_LOCK() ;
+ uty_config_unlock(vty, node);
+ VTY_UNLOCK() ;
}
-int
-vty_config_unlock (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Give back the configuration symbol of power -- if own it.
+ *
+ * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ */
+extern void
+uty_config_unlock (struct vty *vty, enum node_type node)
{
- if (vty_config == 1 && vty->config == 1)
+ VTY_ASSERT_LOCKED() ;
+ if ((vty_config == 1) && (vty->vio->config == 1))
{
- vty->config = 0;
- vty_config = 0;
+ vty->vio->config = 0;
+ vty_config = 0;
}
- return vty->config;
-}
-
-/* Master of the threads. */
-static struct thread_master *master;
-static void
-vty_event (enum event event, int sock, struct vty *vty)
-{
- struct thread *vty_serv_thread;
-
- switch (event)
- {
- case VTY_SERV:
- vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);
- vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
- break;
-#ifdef VTYSH
- case VTYSH_SERV:
- thread_add_read (master, vtysh_accept, vty, sock);
- break;
- case VTYSH_READ:
- vty->t_read = thread_add_read (master, vtysh_read, vty, sock);
- break;
- case VTYSH_WRITE:
- vty->t_write = thread_add_write (master, vtysh_write, vty, sock);
- break;
-#endif /* VTYSH */
- case VTY_READ:
- vty->t_read = thread_add_read (master, vty_read, vty, sock);
+ assert(node <= MAX_NON_CONFIG_NODE) ;
+ vty->node = node ;
+} ;
- /* Time out treatment. */
- if (vty->v_timeout)
- {
- if (vty->t_timeout)
- thread_cancel (vty->t_timeout);
- vty->t_timeout =
- thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
- }
- break;
- case VTY_WRITE:
- if (! vty->t_write)
- vty->t_write = thread_add_write (master, vty_flush, vty, sock);
- break;
- case VTY_TIMEOUT_RESET:
- if (vty->t_timeout)
- {
- thread_cancel (vty->t_timeout);
- vty->t_timeout = NULL;
- }
- if (vty->v_timeout)
- {
- vty->t_timeout =
- thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
- }
- break;
- }
-}
-
-DEFUN (config_who,
+/*==============================================================================
+ * Commands
+ *
+ */
+DEFUN_CALL (config_who,
config_who_cmd,
"who",
"Display who is on vty\n")
{
- unsigned int i;
- struct vty *v;
+ unsigned int i = 0;
+ vty_io vio ;
- for (i = 0; i < vector_active (vtyvec); i++)
- if ((v = vector_slot (vtyvec, i)) != NULL)
- vty_out (vty, "%svty[%d] connected from %s.%s",
- v->config ? "*" : " ",
- i, v->address, VTY_NEWLINE);
+ VTY_LOCK() ;
+
+ vio = vio_list_base ;
+ while (vio != NULL) /* TODO: show only VTY_TERM ??? */
+ {
+ uty_out (vty, "%svty[%d] connected from %s.%s",
+ vio->config ? "*" : " ",
+ i, uty_get_name(vio), VTY_NEWLINE);
+ vio = sdl_next(vio, vio_list) ;
+ } ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
/* Move to vty configuration mode. */
-DEFUN (line_vty,
+DEFUN_CALL (line_vty,
line_vty_cmd,
"line vty",
"Configure a terminal line\n"
"Virtual terminal\n")
{
+ VTY_LOCK() ;
vty->node = VTY_NODE;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2584,6 +1471,8 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
{
unsigned long timeout = 0;
+ VTY_LOCK() ;
+
/* min_str and sec_str are already checked by parser. So it must be
all digit string. */
if (min_str)
@@ -2595,14 +1484,15 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
timeout += strtol (sec_str, NULL, 10);
vty_timeout_val = timeout;
- vty->v_timeout = timeout;
- vty_event (VTY_TIMEOUT_RESET, 0, vty);
+ if (vty_term(vty) || vty_shell_serv(vty))
+ uty_sock_set_timer(&vty->vio->sock, timeout) ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (exec_timeout_min,
+DEFUN_CALL (exec_timeout_min,
exec_timeout_min_cmd,
"exec-timeout <0-35791>",
"Set timeout value\n"
@@ -2611,7 +1501,7 @@ DEFUN (exec_timeout_min,
return exec_timeout (vty, argv[0], NULL);
}
-DEFUN (exec_timeout_sec,
+DEFUN_CALL (exec_timeout_sec,
exec_timeout_sec_cmd,
"exec-timeout <0-35791> <0-2147483>",
"Set the EXEC timeout\n"
@@ -2621,7 +1511,7 @@ DEFUN (exec_timeout_sec,
return exec_timeout (vty, argv[0], argv[1]);
}
-DEFUN (no_exec_timeout,
+DEFUN_CALL (no_exec_timeout,
no_exec_timeout_cmd,
"no exec-timeout",
NO_STR
@@ -2631,61 +1521,71 @@ DEFUN (no_exec_timeout,
}
/* Set vty access class. */
-DEFUN (vty_access_class,
+DEFUN_CALL (vty_access_class,
vty_access_class_cmd,
"access-class WORD",
"Filter connections based on an IP access list\n"
"IP access list\n")
{
+ VTY_LOCK() ;
+
if (vty_accesslist_name)
XFREE(MTYPE_VTY, vty_accesslist_name);
vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
/* Clear vty access class. */
-DEFUN (no_vty_access_class,
+DEFUN_CALL (no_vty_access_class,
no_vty_access_class_cmd,
"no access-class [WORD]",
NO_STR
"Filter connections based on an IP access list\n"
"IP access list\n")
{
+ int result = CMD_SUCCESS;
+
+ VTY_LOCK() ;
if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
{
- vty_out (vty, "Access-class is not currently applied to vty%s",
+ uty_out (vty, "Access-class is not currently applied to vty%s",
VTY_NEWLINE);
- return CMD_WARNING;
+ result = CMD_WARNING;
+ }
+ else
+ {
+ XFREE(MTYPE_VTY, vty_accesslist_name);
+ vty_accesslist_name = NULL;
}
- XFREE(MTYPE_VTY, vty_accesslist_name);
-
- vty_accesslist_name = NULL;
-
- return CMD_SUCCESS;
+ VTY_UNLOCK() ;
+ return result;
}
#ifdef HAVE_IPV6
/* Set vty access class. */
-DEFUN (vty_ipv6_access_class,
+DEFUN_CALL (vty_ipv6_access_class,
vty_ipv6_access_class_cmd,
"ipv6 access-class WORD",
IPV6_STR
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
+ VTY_LOCK() ;
if (vty_ipv6_accesslist_name)
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
/* Clear vty access class. */
-DEFUN (no_vty_ipv6_access_class,
+DEFUN_CALL (no_vty_ipv6_access_class,
no_vty_ipv6_access_class_cmd,
"no ipv6 access-class [WORD]",
NO_STR
@@ -2693,112 +1593,135 @@ DEFUN (no_vty_ipv6_access_class,
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
+ int result = CMD_SUCCESS;
+
+ VTY_LOCK() ;
+
if (! vty_ipv6_accesslist_name ||
(argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
{
- vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
+ uty_out (vty, "IPv6 access-class is not currently applied to vty%s",
VTY_NEWLINE);
- return CMD_WARNING;
+ result = CMD_WARNING;
}
+ else
+ {
+ XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-
- vty_ipv6_accesslist_name = NULL;
+ vty_ipv6_accesslist_name = NULL;
+ }
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
#endif /* HAVE_IPV6 */
/* vty login. */
-DEFUN (vty_login,
+DEFUN_CALL (vty_login,
vty_login_cmd,
"login",
"Enable password checking\n")
{
+ VTY_LOCK() ;
no_password_check = 0;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (no_vty_login,
+DEFUN_CALL (no_vty_login,
no_vty_login_cmd,
"no login",
NO_STR
"Enable password checking\n")
{
+ VTY_LOCK() ;
no_password_check = 1;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
/* initial mode. */
-DEFUN (vty_restricted_mode,
+DEFUN_CALL (vty_restricted_mode,
vty_restricted_mode_cmd,
"anonymous restricted",
"Restrict view commands available in anonymous, unauthenticated vty\n")
{
+ VTY_LOCK() ;
restricted_mode = 1;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (vty_no_restricted_mode,
+DEFUN_CALL (vty_no_restricted_mode,
vty_no_restricted_mode_cmd,
"no anonymous restricted",
NO_STR
"Enable password checking\n")
{
+ VTY_LOCK() ;
restricted_mode = 0;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (service_advanced_vty,
+DEFUN_CALL (service_advanced_vty,
service_advanced_vty_cmd,
"service advanced-vty",
"Set up miscellaneous service\n"
"Enable advanced mode vty interface\n")
{
+ VTY_LOCK() ;
host.advanced = 1;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (no_service_advanced_vty,
+DEFUN_CALL (no_service_advanced_vty,
no_service_advanced_vty_cmd,
"no service advanced-vty",
NO_STR
"Set up miscellaneous service\n"
"Enable advanced mode vty interface\n")
{
+ VTY_LOCK() ;
host.advanced = 0;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (terminal_monitor,
+DEFUN_CALL (terminal_monitor,
terminal_monitor_cmd,
"terminal monitor",
"Set terminal line parameters\n"
"Copy debug output to the current terminal line\n")
{
- vty->monitor = 1;
+ VTY_LOCK() ;
+ uty_set_monitor(vty->vio, true);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-DEFUN (terminal_no_monitor,
+DEFUN_CALL (terminal_no_monitor,
terminal_no_monitor_cmd,
"terminal no monitor",
"Set terminal line parameters\n"
NO_STR
"Copy debug output to the current terminal line\n")
{
- vty->monitor = 0;
+ VTY_LOCK() ;
+ uty_set_monitor(vty->vio, false);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-ALIAS (terminal_no_monitor,
+ALIAS_CALL (terminal_no_monitor,
no_terminal_monitor_cmd,
"no terminal monitor",
NO_STR
"Set terminal line parameters\n"
"Copy debug output to the current terminal line\n")
-DEFUN (show_history,
+DEFUN_CALL (show_history,
show_history_cmd,
"show history",
SHOW_STR
@@ -2806,24 +1729,34 @@ DEFUN (show_history,
{
int index;
- for (index = vty->hindex + 1; index != vty->hindex;)
+ VTY_LOCK() ;
+
+ for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
{
+ qstring line ;
+
if (index == VTY_MAXHIST)
{
index = 0;
continue;
}
- if (vty->hist[index] != NULL)
- vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
+ line = vector_get_item(&vty->vio->hist, index) ;
+ if (line != NULL)
+ uty_out (vty, " %s%s", line->char_body, VTY_NEWLINE);
index++;
}
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-/* Display current configuration. */
+/*==============================================================================
+ * Output the current configuration
+ *
+ * Returns: CMD_SUCCESS
+ */
static int
vty_config_write (struct vty *vty)
{
@@ -2839,14 +1772,14 @@ vty_config_write (struct vty *vty)
/* exec-timeout */
if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
- vty_out (vty, " exec-timeout %ld %ld%s",
+ vty_out (vty, " exec-timeout %ld %ld%s",
vty_timeout_val / 60,
vty_timeout_val % 60, VTY_NEWLINE);
/* login */
if (no_password_check)
vty_out (vty, " no login%s", VTY_NEWLINE);
-
+
if (restricted_mode != restricted_mode_default)
{
if (restricted_mode_default)
@@ -2854,58 +1787,22 @@ vty_config_write (struct vty *vty)
else
vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
}
-
+
vty_out (vty, "!%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
-struct cmd_node vty_node =
-{
- VTY_NODE,
- "%s(config-line)# ",
- 1,
-};
-
-/* Reset all VTY status. */
-void
-vty_reset ()
-{
- unsigned int i;
- struct vty *vty;
- struct thread *vty_serv_thread;
-
- for (i = 0; i < vector_active (vtyvec); i++)
- if ((vty = vector_slot (vtyvec, i)) != NULL)
- {
- buffer_reset (vty->obuf);
- vty->status = VTY_CLOSE;
- vty_close (vty);
- }
-
- for (i = 0; i < vector_active (Vvty_serv_thread); i++)
- if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
- {
- thread_cancel (vty_serv_thread);
- vector_slot (Vvty_serv_thread, i) = NULL;
- close (i);
- }
-
- vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
- if (vty_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
-
- if (vty_ipv6_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- vty_ipv6_accesslist_name = NULL;
- }
-}
+/*==============================================================================
+ * The cwd at start-up.
+ */
+/*------------------------------------------------------------------------------
+ * Save cwd
+ *
+ * This is done early in the morning so that any future operations on files
+ * can use the original cwd if required.
+ */
static void
vty_save_cwd (void)
{
@@ -2920,51 +1817,212 @@ vty_save_cwd (void)
getcwd (cwd, MAXPATHLEN);
}
- vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
- strcpy (vty_cwd, cwd);
-}
+ vty_cwd = XSTRDUP(MTYPE_TMP, cwd) ;
+} ;
+/*------------------------------------------------------------------------------
+ * Get cwd as at start-up. Never changed -- so no locking required.
+ */
char *
vty_get_cwd ()
{
return vty_cwd;
}
-int
+/*==============================================================================
+ * Access functions for VTY values, where locking is or might be required.
+ */
+
+bool
vty_shell (struct vty *vty)
{
- return vty->type == VTY_SHELL ? 1 : 0;
+ bool result;
+ VTY_LOCK() ;
+ result = (vty->vio->type == VTY_SHELL) ;
+ VTY_UNLOCK() ;
+ return result;
+}
+
+bool
+vty_term(struct vty *vty)
+{
+ bool result;
+ VTY_LOCK() ;
+ result = (vty->vio->type == VTY_TERM);
+ VTY_UNLOCK() ;
+ return result;
}
-int
+bool
vty_shell_serv (struct vty *vty)
{
- return vty->type == VTY_SHELL_SERV ? 1 : 0;
+ bool result;
+ VTY_LOCK() ;
+ result = (vty->vio->type == VTY_SHELL_SERV);
+ VTY_UNLOCK() ;
+ return result;
+}
+
+enum node_type
+vty_get_node(struct vty *vty)
+{
+ int result;
+ VTY_LOCK() ;
+ result = vty->node;
+ VTY_UNLOCK() ;
+ return result;
}
void
-vty_init_vtysh ()
+vty_set_node(struct vty *vty, enum node_type node)
{
- vtyvec = vector_init (VECTOR_MIN_SIZE);
+ VTY_LOCK() ;
+ vty->node = node;
+ VTY_UNLOCK() ;
}
-/* Install vty's own commands like `who' command. */
void
-vty_init (struct thread_master *master_thread)
+vty_set_lines(struct vty *vty, int lines)
{
- /* For further configuration read, preserve current directory. */
- vty_save_cwd ();
+ VTY_LOCK() ;
+ vty->vio->lines = lines;
+ vty->vio->lines_set = 1 ;
+ uty_set_height(vty->vio) ;
+ VTY_UNLOCK() ;
+}
- vtyvec = vector_init (VECTOR_MIN_SIZE);
+/*==============================================================================
+ *
+ */
+const char* wordlist[] =
+ {
+ "Lorem",
+ "ipsum",
+ "dolor",
+ "magna",
+ "vita",
+ "brevis",
+ "Aliquot",
+ "in",
+ "tempura",
+ "mores",
+ "ad",
+ "Astronomica",
+ "per",
+ "impedimenta",
+ "quod",
+ "et",
+ "sed",
+ "semper",
+ "ut",
+ "Elisium",
+ "est",
+ };
+
+
+DEFUN (delay_secs,
+ delay_secs_cmd,
+ "delay <0-600> secs <0-10000> lines",
+ "Delay for a number of seconds and spit out a number of lines\n"
+ "Delay time\n"
+ "Delay time units\n"
+ "How much to output\n"
+ "Output units\n")
+{
+ unsigned long delay ;
+ unsigned long lines ;
+
+ unsigned long unit ;
+
+ delay = strtol(argv[0], NULL, 10) ;
+ lines = strtol(argv[1], NULL, 10) ;
+
+ vty_out(vty, "delay %d secs %d lines\n", (int)delay, (int)lines) ;
+
+ unit = (lines * 100) / delay ;
+
+ while (delay--)
+ {
+ char buf[200] ;
+ char* e ;
+ int n ;
+ int w = sizeof(wordlist) / sizeof(char*) ;
+
+ sleep(1) ;
+
+ n = ((rand() % (unit + 1)) + (unit / 2)) / 100 ;
+
+ if ((n > (int)lines) || (delay == 0))
+ n = lines ;
+
+ lines -= n ;
+
+ while (n--)
+ {
+ char* q ;
+ const char* p ;
+ int a ;
+
+ q = buf ;
+ e = buf + (rand() % 120) + 30 ;
+
+ if ((rand() % 6) == 0)
+ e = buf ;
+
+ a = (rand() % 4) == 1 ;
+ while (q < e)
+ {
+ int s ;
+ s = 0 ;
+ if (a == 1)
+ s = (rand() % 5) + 1 ;
+ else if (a > 1)
+ s = 1 ;
+
+ while (s--)
+ *q++ = ' ' ;
+
+ p = wordlist[rand() % w] ;
+ while (*p != '\0')
+ *q++ = *p++ ;
+
+ a = (rand() % 4) + 1 ;
+ } ;
+
+ *q++ = '\n' ;
+ *q++ = '\0' ;
- master = master_thread;
+ vty_out(vty, buf) ;
+ } ;
+ } ;
- /* Initilize server thread vector. */
- Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
+ return CMD_SUCCESS;
+}
+
+/*==============================================================================
+ * The VTY command nodes
+ */
+
+struct cmd_node vty_node =
+{
+ VTY_NODE,
+ "%s(config-line)# ",
+ 1,
+};
+
+/*------------------------------------------------------------------------------
+ * Install vty's own commands like `who' command.
+ */
+static void
+uty_init_commands (void)
+{
+ VTY_ASSERT_LOCKED() ;
- /* Install bgp top node. */
install_node (&vty_node, vty_config_write);
+ install_element (VIEW_NODE, &delay_secs_cmd);
+ install_element (ENABLE_NODE, &delay_secs_cmd);
+
install_element (RESTRICTED_NODE, &config_who_cmd);
install_element (RESTRICTED_NODE, &show_history_cmd);
install_element (VIEW_NODE, &config_who_cmd);
@@ -2993,18 +2051,4 @@ vty_init (struct thread_master *master_thread)
install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
#endif /* HAVE_IPV6 */
-}
-
-void
-vty_terminate (void)
-{
- if (vty_cwd)
- XFREE (MTYPE_TMP, vty_cwd);
-
- if (vtyvec && Vvty_serv_thread)
- {
- vty_reset ();
- vector_free (vtyvec);
- vector_free (Vvty_serv_thread);
- }
-}
+} ;
diff --git a/lib/vty.h b/lib/vty.h
index 7df04b5f..126218ab 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -1,130 +1,171 @@
-/* Virtual terminal [aka TeletYpe] interface routine
- 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. */
+/* VTY top level
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 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 _ZEBRA_VTY_H
#define _ZEBRA_VTY_H
+#include <stdbool.h>
+
#include "thread.h"
#include "log.h"
+#include "qpthreads.h"
+#include "qpselect.h"
+#include "qtimers.h"
+#include "qpnexus.h"
+#include "list_util.h"
+#include "vector.h"
+#include "qstring.h"
+#include "node_type.h"
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
-#define VTY_BUFSIZ 512
-#define VTY_MAXHIST 20
-
-/* VTY struct. */
-struct vty
-{
- /* File descripter of this vty. */
- int fd;
-
- /* Is this vty connect to file or not */
- enum {VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV} type;
-
- /* Node status of this vty */
- int node;
-
- /* What address is this vty comming from. */
- char *address;
-
- /* Failure count */
- int fail;
-
- /* Output buffer. */
- struct buffer *obuf;
-
- /* Command input buffer */
- char *buf;
-
- /* Command cursor point */
- int cp;
-
- /* Command length */
- int length;
+/*==============================================================================
+ * The VTYSH uses a unix socket to talk to the daemon.
+ *
+ * The ability to respond to a connection from VTYSH appears to be a *compile*
+ * time option. In the interests of keeping the code up to date, the VTYSH
+ * option is turned into a testable constant.
+ */
+#ifdef VTYSH
+# define VTYSH_DEFINED 1
+#else
+# define VTYSH_DEFINED 0
+#endif
- /* Command max length. */
- int max;
+enum { VTYSH_ENABLED = VTYSH_DEFINED } ;
- /* Histry of command */
- char *hist[VTY_MAXHIST];
+#undef VTYSH_DEFINED
- /* History lookup current point */
- int hp;
+/*==============================================================================
+ * VTY Types
+ */
+enum vty_type
+{
+ VTY_NONE = 0, /* no type at all */
- /* History insert end point */
- int hindex;
+ VTY_TERM, /* a telnet terminal -- input and output */
+ VTY_SHELL_SERV, /* a vty_shell slave -- input and output */
- /* For current referencing point of interface, route-map,
- access-list etc... */
- void *index;
+ VTY_CONFIG_READ, /* reading config file -- output is to buffer
+ -- no input */
- /* For multiple level index treatment such as key chain and key. */
- void *index_sub;
+ VTY_CONFIG_WRITE, /* writing config file -- output is to file
+ -- no input */
- /* For escape character. */
- unsigned char escape;
+ VTY_STDOUT, /* general output -- output is to stdout
+ -- no input */
- /* Current vty status. */
- enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} status;
+ VTY_STDERR, /* general output -- output is to stderr
+ -- no input */
- /* IAC handling: was the last character received the
- IAC (interpret-as-command) escape character (and therefore the next
- character will be the command code)? Refer to Telnet RFC 854. */
- unsigned char iac;
+ VTY_SHELL, /* vty in vtysh -- output is to stdout */
+} ;
- /* IAC SB (option subnegotiation) handling */
- unsigned char iac_sb_in_progress;
- /* At the moment, we care only about the NAWS (window size) negotiation,
- and that requires just a 5-character buffer (RFC 1073):
- <NAWS char> <16-bit width> <16-bit height> */
-#define TELNET_NAWS_SB_LEN 5
- unsigned char sb_buf[TELNET_NAWS_SB_LEN];
- /* How many subnegotiation characters have we received? We just drop
- those that do not fit in the buffer. */
- size_t sb_len;
+/*==============================================================================
+ * VTY struct.
+ */
- /* Window width/height. */
- int width;
- int height;
+typedef struct vty_io* vty_io ; /* private to vty.c */
- /* Configure lines. */
- int lines;
+struct cmd_parsed ; /* in case vty.h expanded before command.h */
- /* Terminal monitor. */
- int monitor;
+struct vty
+{
+ /*----------------------------------------------------------------------
+ * The following are the context in which commands are executed.
+ */
+
+ /* Node status of this vty
+ *
+ * NB: when using qpthreads should lock VTY to access this -- so use
+ * vty_get_node() and vty_set_node().
+ */
+ enum node_type node ;
+
+ /* For current referencing point of interface, route-map, access-list
+ * etc...
+ *
+ * NB: this value is private to the command execution, which is assumed
+ * to all be in the one thread... so no lock required.
+ */
+ void *index ;
+
+ /* For multiple level index treatment such as key chain and key.
+ *
+ * NB: this value is private to the command execution, which is assumed
+ * to all be in the one thread... so no lock required.
+ */
+ void *index_sub ;
+
+ /* String which is newline... read only -- no locking */
+ const char* newline ;
+
+ /*----------------------------------------------------------------------------
+ * The current command line.
+ *
+ * These are set when a command is parsed and dispatched.
+ *
+ * They are not touched until the command completes -- so may be read while
+ * the command is being parsed and executed.
+ */
+ const char* buf ;
+ struct cmd_parsed* parsed ;
+ unsigned lineno ;
+
+ /*----------------------------------------------------------------------
+ * The following are used inside vty.c only.
+ */
+
+ /* Pointer to related vty_io structure */
+ vty_io vio ;
+};
- /* In configure mode. */
- int config;
+/*------------------------------------------------------------------------------
+ * Can now include this
+ */
- /* Read and write thread. */
- struct thread *t_read;
- struct thread *t_write;
+#include "command.h"
- /* Timeout seconds and thread. */
- unsigned long v_timeout;
- struct thread *t_timeout;
-};
+/*------------------------------------------------------------------------------
+ *
+ */
+#define VTY_BUFSIZ 512
+#define VTY_MAXHIST 51
/* Integrated configuration file. */
#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
/* Small macro to determine newline is newline only or linefeed needed. */
-#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+#define VTY_NEWLINE (((vty) != NULL) ? (vty)->newline : "\n")
+
+/* For indenting, mostly. */
+extern const char vty_spaces_string[] ;
+enum { VTY_MAX_SPACES = 40 } ;
+#define VTY_SPACES(n) (vty_spaces_string + ((n) < VTY_MAX_SPACES \
+ ? VTY_MAX_SPACES - (n) : 0))
/* Default time out value */
#define VTY_TIMEOUT_DEFAULT 600
@@ -197,31 +238,47 @@ do {
} \
} while (0)
-/* Exported variables */
+/*------------------------------------------------------------------------------
+ * Exported variables
+ */
extern char integrate_default[];
-/* Prototypes. */
+/*------------------------------------------------------------------------------
+ * Prototypes.
+ */
extern void vty_init (struct thread_master *);
+extern void vty_init_r (qpn_nexus, qpn_nexus);
+extern void vty_start(const char *addr, unsigned short port, const char *path) ;
+#define vty_serv_sock(addr, port, path) vty_start(addr, port, path)
+extern void vty_restart(const char *addr, unsigned short port,
+ const char *path) ;
+extern struct vty* vty_open(enum vty_type type) ;
+extern void vty_close(struct vty *);
+
extern void vty_init_vtysh (void);
extern void vty_terminate (void);
extern void vty_reset (void);
-extern struct vty *vty_new (void);
+extern void vty_reset_because(const char* why) ;
+
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
-extern void vty_read_config (char *, char *);
+extern void vty_out_indent(struct vty *vty, int indent) ;
+extern void vty_out_clear(struct vty *vty) ;
+
+extern void vty_read_config (char *config_file, char *config_default);
+extern void vty_read_config_first_cmd_special(
+ char *config_file, char *config_default,
+ struct cmd_element* first_cmd, bool ignore_warnings) ;
+
extern void vty_time_print (struct vty *, int);
-extern void vty_serv_sock (const char *, unsigned short, const char *);
-extern void vty_close (struct vty *);
+
extern char *vty_get_cwd (void);
-extern void vty_log (const char *level, const char *proto,
- const char *fmt, struct timestamp_control *, va_list);
-extern int vty_config_lock (struct vty *);
-extern int vty_config_unlock (struct vty *);
-extern int vty_shell (struct vty *);
-extern int vty_shell_serv (struct vty *);
-extern void vty_hello (struct vty *);
-/* Send a fixed-size message to all vty terminal monitors; this should be
- an async-signal-safe function. */
-extern void vty_log_fixed (const char *buf, size_t len);
+extern bool vty_shell (struct vty *);
+extern bool vty_term (struct vty *);
+extern bool vty_shell_serv (struct vty *);
+extern void vty_hello (struct vty *);
+extern enum node_type vty_get_node(struct vty *);
+extern void vty_set_node(struct vty *, enum node_type);
+extern void vty_set_lines(struct vty *, int);
#endif /* _ZEBRA_VTY_H */
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
new file mode 100644
index 00000000..bdaf1128
--- /dev/null
+++ b/lib/vty_cli.c
@@ -0,0 +1,2705 @@
+/* VTY Command Line Handler
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 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 "keystroke.h"
+#include "vty.h"
+#include "uty.h"
+#include "vty_cli.h"
+#include "vty_io.h"
+#include "vio_lines.h"
+
+#include "command.h"
+#include "command_execute.h"
+#include "command_queue.h"
+
+#include "memory.h"
+
+/*==============================================================================
+ * Host name handling
+ *
+ * The host name is used in the command line prompt. The name used is either
+ * the name set by "hostname" command, or the current machine host name.
+ *
+ * Static variables -- under the VTY_LOCK !
+ */
+
+static char* vty_host_name = NULL ;
+int vty_host_name_set = 0 ;
+
+static void uty_new_host_name(const char* name) ;
+
+/*------------------------------------------------------------------------------
+ * Update vty_host_name as per "hostname" or "no hostname" command
+ */
+extern void
+uty_set_host_name(const char* name)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vty_host_name_set = (name != NULL) ;
+
+ if (vty_host_name_set)
+ uty_new_host_name(name) ;
+ else
+ uty_check_host_name() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If vty_host_name is set, free it.
+ */
+extern void
+uty_free_host_name(void)
+{
+ if (vty_host_name != NULL)
+ XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the host name is not set by command, see if the actual host name has
+ * changed, and if so change it.
+ *
+ * This is done periodically in case the actual host name changes !
+ */
+extern void
+uty_check_host_name(void)
+{
+ struct utsname names ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vty_host_name_set)
+ return ; /* nothing to do if set by command */
+
+ uname (&names) ;
+
+ if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0))
+ uty_new_host_name(names.nodename) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set new vty_host_name and run along list of VTYs to mark the change.
+ */
+static void
+uty_new_host_name(const char* name)
+{
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ uty_free_host_name() ;
+ vty_host_name = XSTRDUP(MTYPE_HOST, name) ;
+
+ vio = vio_list_base ;
+ while (vio != NULL)
+ {
+ vio->cli_prompt_set = 0 ;
+ vio = sdl_next(vio, vio_list) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * General mechanism for command execution.
+ *
+ * Command execution is driven by select/pselect -- which means that the
+ * processing of commands is multiplexed with all other activity. In the
+ * following:
+ *
+ * -- read_ready and write_ready are events signalled by select/pselect
+ *
+ * -- setting read or write on, means enabling the file for select/pselect to
+ * consider it for read_ready or write_ready, respectively.
+ *
+ * State of the CLI:
+ *
+ * cli_blocked -- the CLI is unable to process any further keystrokes.
+ *
+ * cmd_in_progress -- a command has been dispatched and has not yet
+ * completed (may have been queued).
+ *
+ * cmd_out_enabled -- the command FIFO is may be emptied.
+ *
+ * This is set when a command completes, and cleared when
+ * everything is written away.
+ *
+ * cli_more_wait -- is in "--more--" wait state
+ *
+ * The following are the valid combinations:
+ *
+ * blkd : cip : o_en : m_wt :
+ * -----:------:------:------:--------------------------------------------
+ * 0 : 0 : 0 : 0 : collecting a new command
+ * 0 : 1 : 0 : 0 : command dispatched
+ * 1 : 1 : 0 : 0 : waiting for (queued) command to complete
+ * 1 : 0 : 1 : 0 : waiting for command output to complete
+ * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away
+ * 0 : 0 : 0 : 1 : waiting for "--more--" response
+ * 1 : 1 : 1 : 0 : waiting for command to complete, after the
+ * CLI has been closed
+ *
+ * There are two output FIFOs:
+ *
+ * 1. for the CLI itself -- uty_cli_out and friends
+ *
+ * 2. for output generated by commands -- vty_out and friends.
+ *
+ * The CLI FIFO is emptied whenever possible, in preference to the command
+ * FIFO. The command FIFO is emptied when cmd_out_enabled. While
+ * cmd_in_progress is also !cmd_out_enabled -- so that all the output from a
+ * given command is collected together before being sent to the file.
+ *
+ * Note that only sets read on when the keystroke stream is empty and has not
+ * yet hit eof. The CLI process is driven mostly by write_ready -- which
+ * invokes read_ready.
+ *
+ * Note also that after each command dispatch the CLI processor exits, to be
+ * re-entered again on write_ready/read_ready -- so does one command line at
+ * a time, yielding the processor after each one.
+ *
+ * Note that select/pselect treat a socket which is at "EOF", or has seen an
+ * error, or has been half closed, etc. as readable and writable. This means
+ * that the CLI will continue to move forward even after the socket is no
+ * longer delivering any data.
+ *
+ *------------------------------------------------------------------------------
+ * The "--more--" handling.
+ *
+ * This is largely buried in the output handling.
+ *
+ * While cmd_in_progress is true cmd_out_enabled will be false. When the
+ * command completes:
+ *
+ * * cmd_in_progress is cleared
+ *
+ * * cmd_out_enabled is set
+ *
+ * * cli_blocked will be set
+ *
+ * * the line_control structure is reset
+ *
+ * * the output process is kicked off by setting write on
+ *
+ * The output process used the line_control structure to manage the output, and
+ * occasionally enter the trivial "--more--" CLI. This is invisible to the
+ * main CLI. (See the cli_more_wait flag and its handling.)
+ *
+ * When all the output has completed the CLI will be kicked, which will see
+ * that the output buffer is now empty, and it can proceed.
+ *
+ * It is expected that the output will end with a newline -- so that when the
+ * CLI is kicked, the cursor will be at the start of an empty line.
+ *
+ * This mechanism means that the command output FIFO only ever contains the
+ * output from the last command executed.
+ *
+ * If the user decides to abandon output at the "--more--" prompt, then the
+ * contents of the command output FIFO are discarded.
+ *
+ *------------------------------------------------------------------------------
+ * The qpthreads/qpnexus extension.
+ *
+ * When running in qnexus mode, many commands are not executed directly in the
+ * CLI, but are queued for execution by the main "routeing" nexus.
+ *
+ * The parsing of commands is context sensitive. The context depends may
+ * change during the execution of a command. So... it is not possible to
+ * dispatch a command until the previous one has completed.
+ *
+ * In qnexus mode, when a command is queued, the CLI does not go cli_blocked,
+ * even if some output has already been generated. This allows a further
+ * command to be entered while the previous one is executed. However, if the
+ * command is dispatched before the previous one completes, then the cli will
+ * block.
+ *
+ * While the previous command is executing, the current command line has a
+ * minimal prompt -- to show that the context is not known. When the previous
+ * command completes, the command line is redrawn in the new context.
+ *
+ *------------------------------------------------------------------------------
+ * Command line drawn state.
+ *
+ * When the cli_drawn flag is set, the current console line contains the
+ * current prompt and the user input to date. The cursor is positioned where
+ * the user last placed it.
+ *
+ * The command line can be "wiped" -- see uty_cli_wipe() -- which removes all
+ * output and prompt, and leaves the console at the start of an empty line
+ * where the command line used to be.
+ *
+ * On entry to the CLI, it will draw the command line again if it has been
+ * wiped.
+ *
+ * This mechanism is used to support the partial command line that may be
+ * entered while a queued command executes.
+ *
+ * It is also used for the command help/completion system.
+ *
+ * It is also used to support the "monitor" output.
+ */
+
+/*==============================================================================
+ * The CLI
+ */
+
+#define CONTROL(X) ((X) - '@')
+
+static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
+static enum vty_readiness uty_cli_standard(vty_io vio) ;
+static enum vty_readiness uty_cli_more_wait(vty_io vio) ;
+static void uty_cli_draw(vty_io vio) ;
+static void uty_cli_draw_this(vty_io vio, enum node_type node) ;
+static void uty_cli_wipe(vty_io vio, int len) ;
+
+static void uty_will_echo (vty_io vio) ;
+static void uty_will_suppress_go_ahead (vty_io vio) ;
+static void uty_dont_linemode (vty_io vio) ;
+static void uty_do_window_size (vty_io vio) ;
+static void uty_dont_lflow_ahead (vty_io vio) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise CLI.
+ *
+ * It is assumed that the following have been initialised, empty or zero:
+ *
+ * cli_prompt_for_node
+ * cl
+ * clx
+ * cli_vbuf
+ * cli_obuf
+ *
+ * cli_drawn
+ * cli_dirty
+ *
+ * cli_prompt_set
+ *
+ * cli_blocked
+ * cmd_in_progress
+ * cmd_out_enabled
+ * cli_wait_more
+ *
+ * cli_more_enabled
+ *
+ * Sets the CLI such that there is apparently a command in progress, so that
+ * further initialisation (in particular hello messages and the like) is
+ * treated as a "start up command".
+ *
+ * Sends a suitable set of Telnet commands to start the process.
+ */
+extern void
+uty_cli_init(vty_io vio)
+{
+ assert(vio->type == VTY_TERM) ;
+
+ vio->cmd_in_progress = 1 ;
+ vio->cli_blocked = 1 ;
+
+ vio->cli_do = cli_do_nothing ;
+
+ /* Setting up terminal. */
+ uty_will_echo (vio);
+ uty_will_suppress_go_ahead (vio);
+ uty_dont_linemode (vio);
+ uty_do_window_size (vio);
+ if (0)
+ uty_dont_lflow_ahead (vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start the CLI.
+ *
+ * All start-up operations are complete -- so the "command" is now complete.
+ *
+ * Returns: write_ready -- so the first event is a write event, to flush
+ * any output to date.
+ */
+extern enum vty_readiness
+uty_cli_start(vty_io vio)
+{
+ uty_cli_cmd_complete(vio, CMD_SUCCESS) ;
+ return write_ready ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the CLI
+ *
+ * Note that if any command is revoked, then will clear cmd_in_progress and
+ * set cmd_out_enabled -- so any output can now clear.
+ */
+extern void
+uty_cli_close(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
+
+ cq_revoke(vio->vty) ;
+
+ vio->cli_blocked = 1 ; /* don't attempt any more */
+ vio->cmd_out_enabled = 1 ; /* allow output to clear */
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI for VTY_TERM
+ *
+ * Do nothing at all if half closed.
+ *
+ * Otherwise do: standard CLI
+ * or: "--more--" CLI
+ *
+ * NB: on return, requires that an attempt is made to write away anything that
+ * may be ready for that.
+ */
+extern enum vty_readiness
+uty_cli(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert((vio->type == VTY_TERM) || (vio->real_type == VTY_TERM)) ;
+
+ if (vio->half_closed)
+ return not_ready ; /* Nothing more if half closed */
+
+ /* Standard or "--more--" CLI ? */
+ if (vio->cli_more_wait)
+ return uty_cli_more_wait(vio) ;
+ else
+ return uty_cli_standard(vio) ;
+} ;
+
+/*==============================================================================
+ * The Standard CLI
+ */
+
+static enum cli_do uty_cli_process(vty_io vio, enum node_type node) ;
+static void uty_cli_response(vty_io vio, enum cli_do cli_do) ;
+static bool uty_cli_dispatch(vty_io vio) ;
+
+/*------------------------------------------------------------------------------
+ * Standard CLI for VTY_TERM -- if not blocked, runs until:
+ *
+ * * runs out of keystrokes
+ * * executes a command
+ *
+ * Note that this executes at most one command each time it is called. This
+ * is to allow for a modicum of sharing of the system. For real keyboard input
+ * this will make no difference at all !
+ *
+ * Returns: not_ready blocked and was blocked when entered
+ * write_ready if there is anything in the keystroke stream
+ * read_ready otherwise
+ */
+static enum vty_readiness
+uty_cli_standard(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ /* cli_blocked is set when is waiting for a command, or its output to
+ * complete -- unless either of those has happened, is still blocked.
+ *
+ * NB: in both these cases, assumes that other forces are at work to
+ * keep things moving.
+ */
+ if (vio->cli_blocked)
+ {
+ assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
+
+ if (vio->cmd_in_progress)
+ {
+ assert(!vio->cmd_out_enabled) ;
+ return not_ready ;
+ } ;
+
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ return not_ready ;
+
+ vio->cli_blocked = 0 ;
+ vio->cmd_out_enabled = 0 ;
+ } ;
+
+ /* If there is nothing pending, then can run the CLI until there is
+ * something to do, or runs out of input.
+ *
+ * If there is something to do, that is because a previous command has
+ * now completed, which may have wiped the pending command or changed
+ * the required prompt.
+ */
+ if (vio->cli_do == cli_do_nothing)
+ vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+ else
+ uty_cli_draw_this(vio, vio->vty->node) ;
+
+ /* If have something to do, do it. */
+ if (vio->cli_do != cli_do_nothing)
+ {
+ /* Reflect immediate response */
+ uty_cli_response(vio, vio->cli_do) ;
+
+ /* If command not already in progress, dispatch this one, which may
+ * set the CLI blocked.
+ *
+ * Otherwise is now blocked until queued command completes.
+ */
+ if (!vio->cmd_in_progress)
+ vio->cli_blocked = uty_cli_dispatch(vio) ;
+ else
+ vio->cli_blocked = 1 ;
+ } ;
+
+ /* Use write_ready as a proxy for read_ready on the keystroke stream.
+ *
+ * Also, if the command line is not drawn, then return write_ready, so
+ * that
+ *
+ * Note that if has just gone cli_blocked, still returns ready. This is
+ * defensive: at worst will generate one unnecessary read_ready/write_ready
+ * event.
+ */
+ if (keystroke_stream_empty(vio->key_stream))
+ return read_ready ;
+ else
+ return write_ready ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch the current vio->cli_do -- queueing it if necessary.
+ *
+ * Requires that are NOT blocked and NO command is queued.
+ *
+ * Expects to be on new blank line, and when returns will be on new, blank
+ * line.
+ *
+ * Returns: true <=> command completed and output is pending
+ * false => command has been queued and is now in progress
+ *
+ * Generally sets vio->cl_do = cli_do_nothing and clears vio->cl to empty.
+ *
+ * Can set vio->cl_do = and vio->cl to be a follow-on command.
+ */
+static bool
+uty_cli_dispatch(vty_io vio)
+{
+ qstring_t tmp ;
+ enum cli_do cli_do ;
+ enum cmd_return_code ret ;
+
+ struct vty* vty = vio->vty ;
+
+ VTY_ASSERT_LOCKED() ;
+ assert(!vio->cli_blocked && !vio->cmd_in_progress) ;
+
+ /* Set vio->clx to the command about to execute.
+ *
+ * Clear vio->cl and vio->cl_do.
+ */
+ vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
+ vio->cmd_out_enabled = 0 ; /* => collect all output */
+
+ tmp = vio->clx ; /* swap clx and cl */
+ vio->clx = vio->cl ;
+ vio->cl = tmp ;
+
+ qs_term(&vio->clx) ; /* ensure string is terminated */
+ vty->buf = qs_chars(&vio->clx) ; /* terminated command line */
+ cli_do = vio->cli_do ; /* current operation */
+
+ vio->cli_do = cli_do_nothing ; /* clear */
+ qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
+
+ /* Reset the command output FIFO and line_control */
+ assert(vio_fifo_empty(&vio->cmd_obuf)) ;
+ uty_out_clear(vio) ; /* clears FIFO and line control */
+
+ /* Dispatch command */
+ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+ {
+ /* AUTH_NODE and AUTH_ENABLE_NODE are unique */
+ ret = uty_auth(vty, vty->buf, cli_do) ;
+ }
+ else
+ {
+ /* All other nodes... */
+ switch (cli_do)
+ {
+ case cli_do_nothing:
+ ret = CMD_SUCCESS ;
+ break ;
+
+ case cli_do_command:
+ ret = uty_command(vty) ;
+ break ;
+
+ case cli_do_ctrl_c:
+ ret = uty_stop_input(vty) ;
+ break ;
+
+ case cli_do_ctrl_d:
+ ret = uty_down_level(vty) ;
+ break ;
+
+ case cli_do_ctrl_z:
+ ret = uty_command(vty) ;
+ if (ret == CMD_QUEUED)
+ vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
+ else
+ ret = uty_end_config(vty) ; /* do the ^Z now */
+ break ;
+
+ case cli_do_eof:
+ ret = uty_cmd_close(vio->vty, "End") ;
+ break ;
+
+ default:
+ zabort("unknown cli_do_xxx value") ;
+ } ;
+ } ;
+
+ if (ret == CMD_QUEUED)
+ {
+ uty_cli_draw(vio) ; /* draw the prompt */
+ return false ; /* command not complete */
+ }
+ else
+ {
+ uty_cli_cmd_complete(vio, ret) ;
+ return true ; /* command complete */
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Queued command has completed.
+ *
+ * Note that sets write on whether there is anything in the output buffer
+ * or not... write_ready will kick read_ready.
+ */
+extern void
+vty_queued_result(struct vty *vty, enum cmd_return_code ret)
+{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ if (!vio->closed)
+ {
+ uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
+
+ /* Do the command completion actions that were deferred because the
+ * command was queued.
+ *
+ * Return of CMD_QUEUED => command was revoked before being executed.
+ * However interesting that might be... frankly don't care.
+ */
+ uty_cli_cmd_complete(vio, ret) ;
+
+ /* Kick the socket -- to write away any outstanding output, and
+ * re-enter the CLI when that's done.
+ */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+ }
+ else
+ {
+ /* If the VTY is closed, the only reason it still exists is because
+ * there was cmd_in_progress.
+ */
+ vio->cmd_in_progress = 0 ;
+
+ uty_close(vio) ; /* Final close */
+ } ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Command has completed, so:
+ *
+ * * clear cmd_in_progress
+ * * set cmd_out_enabled -- so any output can now proceed
+ * * set cli_blocked -- waiting for output to complete
+ * * and prepare the line control for output
+ *
+ * If the return is CMD_CLOSE, then also now does the required half close.
+ *
+ * Note that apart from CMD_CLOSE, don't really care what the return was. Any
+ * diagnostics or other action must be dealt with elsewhere (as part of the
+ * command execution.
+ *
+ * Note that everything proceeds as if there is some output. So after every
+ * command goes through at least one write_ready event.
+ *
+ * This ensures some multiplexing at the command level.
+ *
+ * It also means that the decision about whether there is anything to output
+ * is left to the output code.
+ */
+static void
+uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
+
+ if (ret == CMD_CLOSE)
+ uty_half_close(vio, NULL) ;
+
+ vio->cmd_in_progress = 0 ; /* command complete */
+ vio->cmd_out_enabled = 1 ; /* enable the output */
+ vio->cli_blocked = 1 ; /* now blocked waiting for output */
+
+ vio->vty->buf = NULL ; /* finished with command line */
+
+ uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+} ;
+
+/*==============================================================================
+ * The "--more--" CLI
+ *
+ * While command output is being cleared from its FIFO, the CLI is cli_blocked.
+ *
+ * When the output side signals that "--more--" is required, it sets the
+ * cli_more_wait flag and clears the cmd_out_enabled flag.
+ *
+ * The first stage of handling "--more--" is to suck the input dry, so that
+ * (as far as is reasonably possible) does not steal a keystroke as the
+ * "--more--" response which was typed before the prompt was issued.
+ *
+ * The cli_blocked flag indicates that the CLI is in this first stage.
+ */
+
+/*------------------------------------------------------------------------------
+ * Change the CLI to the "--more--" CLI.
+ *
+ * Outputs the new prompt line.
+ */
+extern void
+uty_cli_enter_more_wait(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
+
+ uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is
+ wiped before change the CLI state */
+
+ vio->cmd_out_enabled = 0 ; /* stop output pro tem */
+ vio->cli_more_wait = 1 ; /* new state */
+
+ uty_cli_draw(vio) ; /* draw the "--more--" */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Exit the "--more--" CLI.
+ *
+ * Wipes the "--more--" prompt.
+ *
+ * This is used when the user responds to the prompt.
+ *
+ * It is also used when the vty is "half-closed". In this case, it is (just)
+ * possible that the '--more--' prompt is yet to be completely written away,
+ * so:
+ *
+ * * assert that is either: !vio->cli_blocked (most of the time it will)
+ * or: !vio_fifo_empty(&vio->cli_obuf)
+ *
+ * * note that can wipe the prompt even though it hasn't been fully
+ * written away yet. (The effect is to append the wipe action to the
+ * cli_obuf !)
+ */
+extern void
+uty_cli_exit_more_wait(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert( (!vio->cli_blocked || !vio_fifo_empty(&vio->cli_obuf))
+ && !vio->cmd_out_enabled && vio->cli_more_wait) ;
+
+ uty_cli_wipe(vio, 0) ; /* wipe the prompt ('--more--')
+ before changing the CLI state */
+
+ vio->cli_blocked = 1 ; /* back to blocked waiting for output */
+ vio->cli_more_wait = 0 ; /* exit more_wait */
+ vio->cmd_out_enabled = 1 ; /* re-enable output */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Handle the "--more--" state.
+ *
+ * Deals with the first stage if cli_blocked.
+ *
+ * Tries to steal a keystroke, and when succeeds wipes the "--more--"
+ * prompt and exits cli_more_wait -- and may cancel all outstanding output.
+ *
+ * EOF on input causes immediate exit from cli_more_state.
+ *
+ * Returns: read_ready -- waiting to steal a keystroke
+ * now_ready -- just left cli_more_wait
+ * not_ready -- otherwise
+ */
+static enum vty_readiness
+uty_cli_more_wait(vty_io vio)
+{
+ struct keystroke steal ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Deal with the first stage of "--more--" */
+ if (vio->cli_blocked)
+ {
+ int get ;
+
+ /* If the CLI buffer is not yet empty, then is waiting for the
+ * initial prompt to clear, so nothing to be done here.
+ */
+ if (!vio_fifo_empty(&vio->cli_obuf))
+ return not_ready ;
+
+ vio->cli_blocked = 0 ;
+
+ /* empty the input buffer into the keystroke stream */
+ do
+ {
+ get = uty_read(vio, NULL) ;
+ } while (get > 0) ;
+ } ;
+
+ /* Go through the "--more--" process, unless no longer write_open (!) */
+ if (vio->sock.write_open)
+ {
+ /* The read fetches a reasonable lump from the I/O -- so if there
+ * is a complete keystroke available, expect to get it.
+ *
+ * If no complete keystroke available to steal, returns ks_null.
+ *
+ * If has hit EOF (or error etc), returns knull_eof.
+ */
+ uty_read(vio, &steal) ;
+
+ /* If nothing stolen, make sure prompt is drawn and wait for more
+ * input.
+ */
+ if ((steal.type == ks_null) && (steal.value != knull_eof))
+ {
+ if (uty_cli_draw_if_required(vio)) /* "--more--" if req. */
+ return write_ready ;
+ else
+ return read_ready ;
+ } ;
+
+ /* Stolen a keystroke -- a (very) few terminate all output */
+ if (steal.type == ks_char)
+ {
+ switch (steal.value)
+ {
+ case CONTROL('C'):
+ case 'q':
+ case 'Q':
+ uty_out_clear(vio) ;
+ break;
+
+ default: /* everything else, thrown away */
+ break ;
+ } ;
+ } ;
+ } ;
+
+ /* End of "--more--" process
+ *
+ * Wipe out the prompt and update state.
+ *
+ * Return write_ready to tidy up the screen and, unless cleared, write
+ * some more.
+ */
+ uty_cli_exit_more_wait(vio) ;
+
+ return now_ready ;
+} ;
+
+/*==============================================================================
+ * CLI VTY output
+ *
+ * This is buffered separately from the general (command) VTY output above.
+ *
+ * Has a dedicated buffer in the struct vty, which is flushed regularly during
+ * command processing.
+ *
+ * It is expected that can flush straight to the file, since this is running at
+ * CLI speed. However, if the CLI is being driven by something other than a
+ * keyboard, or "monitor" output has filled the buffers, then may need to
+ * have intermediate buffering.
+ *
+ * No actual I/O takes place here-- all "output" is to vio->cli_obuf
+ * and/or vio->cli_ex_obuf
+ *
+ * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
+ * This is used while passwords are entered and to allow command line changes
+ * to be made while the line is not visible.
+ */
+
+enum { cli_rep_count = 32 } ;
+
+typedef const char cli_rep_char[cli_rep_count] ;
+
+static const char telnet_backspaces[] =
+ { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ } ;
+CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
+
+static const char telnet_spaces[] =
+ { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
+ } ;
+CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ;
+
+static const char* telnet_newline = "\r\n" ;
+
+static void uty_cli_write(vty_io vio, const char *this, int len) ;
+static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- cf fprintf()
+ */
+static void
+uty_cli_out(vty_io vio, const char *format, ...)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->sock.write_open)
+ {
+ va_list args ;
+
+ va_start (args, format);
+ vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
+ va_end(args);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- echo user input
+ *
+ * Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
+ */
+static void
+uty_cli_echo(vty_io vio, const char *this, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
+ return ;
+
+ uty_cli_write(vio, this, len) ;
+}
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- echo 'n' characters using a cli_rep_char string
+ *
+ * Do nothing if echo suppressed (eg in AUTH_NODE)
+ */
+static void
+uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
+ return ;
+
+ uty_cli_write_n(vio, chars, n) ;
+}
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- cf write()
+ */
+inline static void
+uty_cli_write(vty_io vio, const char *this, int len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->sock.write_open)
+ vio_fifo_put(&vio->cli_obuf, this, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- write 'n' characters using a cli_rep_char string
+ */
+static void
+uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
+{
+ int len ;
+
+ len = sizeof(cli_rep_char) ;
+ while (n > 0)
+ {
+ if (n < len)
+ len = n ;
+ uty_cli_write(vio, chars, len) ;
+ n -= len ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- write given string
+ *
+ * Returns length of string.
+ */
+static int
+uty_cli_write_s(vty_io vio, const char *str)
+{
+ int len ;
+
+ len = strlen(str) ;
+ if (len != 0)
+ uty_cli_write(vio, str, len) ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Standard Messages
+ */
+
+/*------------------------------------------------------------------------------
+ * Send newline to the console.
+ *
+ * Clears the cli_drawn and the cli_dirty flags.
+ */
+static void
+uty_cli_out_newline(vty_io vio)
+{
+ uty_cli_write(vio, telnet_newline, 2) ;
+ vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Wipe 'n' characters.
+ *
+ * If 'n' < 0, wipes characters backwards and moves cursor back.
+ * 'n' > 0, wipes characters forwards, leaving cursor where it is
+ */
+static void
+uty_cli_out_wipe_n(vty_io vio, int n)
+{
+ if (n < 0)
+ {
+ n = abs(n) ;
+ uty_cli_write_n(vio, telnet_backspaces, n);
+ } ;
+
+ if (n > 0)
+ {
+ uty_cli_write_n(vio, telnet_spaces, n) ;
+ uty_cli_write_n(vio, telnet_backspaces, n) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send response to the given cli_do
+ *
+ * If no command is in progress, then will send newline to signal that the
+ * command is about to be dispatched.
+ *
+ * If command is in progress, then leaves cursor on '^' to signal that is now
+ * waiting for previous command to complete.
+ */
+static const char* cli_response [2][cli_do_count] =
+{
+ { /* when not waiting for previous command to complete */
+ [cli_do_command] = "",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
+ },
+ { /* when waiting for a previous command to complete */
+ [cli_do_command] = "^",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
+ }
+} ;
+
+static void
+uty_cli_response(vty_io vio, enum cli_do cli_do)
+{
+ const char* str ;
+ int len ;
+
+ if ((cli_do == cli_do_nothing) || (vio->half_closed))
+ return ;
+
+ str = (cli_do < cli_do_count)
+ ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ;
+ assert(str != NULL) ;
+
+ len = uty_cli_write_s(vio, str) ;
+
+ if (vio->cmd_in_progress)
+ {
+ vio->cli_extra_len = len ;
+ uty_cli_write_n(vio, telnet_backspaces, len) ;
+ }
+ else
+ {
+ uty_cli_out_newline(vio) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send various messages with trailing newline.
+ */
+
+static void
+uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
+{
+ uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+static void
+uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
+{
+ uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+/*==============================================================================
+ * Command line draw and wipe
+ */
+
+/*------------------------------------------------------------------------------
+ * Wipe the current console line -- if any.
+ */
+static void
+uty_cli_wipe(vty_io vio, int len)
+{
+ int a ;
+ int b ;
+
+ if (!vio->cli_drawn)
+ return ; /* quit if already wiped */
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ /* Establish how much ahead and how much behind the cursor */
+ a = vio->cli_extra_len ;
+ b = vio->cli_prompt_len ;
+
+ if (!vio->cli_echo_suppress && !vio->cli_more_wait)
+ {
+ a += vio->cl.len - vio->cl.cp ;
+ b += vio->cl.cp ;
+ } ;
+
+ /* Wipe anything ahead of the current position and ahead of new len */
+ if ((a + b) > len)
+ uty_cli_out_wipe_n(vio, +a) ;
+
+ /* Wipe anything behind current position, but ahead of new len */
+ if (b > len)
+ {
+ uty_cli_out_wipe_n(vio, -(b - len)) ;
+ b = len ; /* moved the cursor back */
+ } ;
+
+ /* Back to the beginning of the line */
+ uty_cli_write_n(vio, telnet_backspaces, b) ;
+
+ /* Nothing there any more */
+ vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If not currently drawn, draw prompt etc according to the current state
+ * and node.
+ *
+ * See uty_cli_draw().
+ */
+extern bool
+uty_cli_draw_if_required(vty_io vio)
+{
+ if (vio->cli_drawn)
+ return false ;
+
+ uty_cli_draw(vio) ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt etc for the current vty node.
+ *
+ * See uty_cli_draw_this()
+ */
+static void
+uty_cli_draw(vty_io vio)
+{
+ uty_cli_draw_this(vio, vio->vty->node) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt and entire command line, leaving current position where it
+ * should be.
+ *
+ * If command line is currently drawn, this wipes and redraws.
+ *
+ * Otherwise, assumes is positioned at start of an empty line.
+ *
+ * Draws prompt according to the given 'node', except:
+ *
+ * * if is half_closed, draw nothing -- wipes the current line
+ *
+ * * if is cli_more_wait, draw the "--more--" prompt
+ *
+ * * if is cmd_in_progress, draw the vestigial prompt.
+ *
+ * By the time the current command completes, the node may have changed, so
+ * the current prompt may be invalid.
+ *
+ * Sets: cli_drawn = true
+ * cli_dirty = false
+ * cli_prompt_len = length of prompt used
+ * cli_extra_len = 0
+ * cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
+ */
+static void
+uty_cli_draw_this(vty_io vio, enum node_type node)
+{
+ const char* prompt ;
+ size_t l_len ;
+ int p_len ;
+
+ if (vio->cli_dirty)
+ uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
+
+ /* Sort out what the prompt is. */
+ if (vio->half_closed)
+ {
+ prompt = "" ;
+ p_len = 0 ;
+ l_len = 0 ;
+ }
+ else if (vio->cli_more_wait)
+ {
+ prompt = "--more--" ;
+ p_len = strlen(prompt) ;
+ l_len = 0 ;
+ }
+ else if (vio->cmd_in_progress)
+ {
+ /* If there is a queued command, the prompt is a minimal affair. */
+ prompt = "~ " ;
+ p_len = strlen(prompt) ;
+ l_len = vio->cl.len ;
+ }
+ else
+ {
+ /* The prompt depends on the node, and is expected to include the
+ * host name.
+ *
+ * Caches the prompt so doesn't re-evaluate it every time.
+ *
+ * If the host name changes, the cli_prompt_set flag is cleared.
+ */
+ if (!vio->cli_prompt_set || (node != vio->cli_prompt_node))
+ {
+ const char* prompt ;
+
+ if (vty_host_name == NULL)
+ uty_check_host_name() ; /* should never be required */
+
+ prompt = cmd_prompt(node) ;
+ if (prompt == NULL)
+ {
+ zlog_err("vty %s has node %d", uty_get_name(vio), node) ;
+ prompt = "%s ???: " ;
+ } ;
+
+ qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+
+ vio->cli_prompt_node = node ;
+ vio->cli_prompt_set = 1 ;
+ } ;
+
+ prompt = vio->cli_prompt_for_node.body ;
+ p_len = vio->cli_prompt_for_node.len ;
+ l_len = vio->cl.len ;
+ } ;
+
+ /* Now, if line is currently drawn, time to wipe it */
+ if (vio->cli_drawn)
+ uty_cli_wipe(vio, p_len + l_len) ;
+
+ /* Set about writing the prompt and the line */
+ vio->cli_drawn = 1 ;
+ vio->cli_extra_len = 0 ;
+ vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+
+ vio->cli_prompt_len = p_len ;
+
+ uty_cli_write(vio, prompt, p_len) ;
+
+ if (l_len != 0)
+ {
+ uty_cli_write(vio, qs_chars(&vio->cl), l_len) ;
+ if (vio->cl.cp < l_len)
+ uty_cli_write_n(vio, telnet_backspaces, l_len - vio->cl.cp) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Monitor output.
+ *
+ * To prepare for monitor output, wipe as much as is necessary for the
+ * monitor line to appear correctly.
+ *
+ * After monitor output, may need to do two things:
+ *
+ * * if the output was incomplete, place the rump in the CLI buffer,
+ * so that:
+ *
+ * a. don't mess up the console with partial lines
+ *
+ * b. don't lose part of a message
+ *
+ * c. act as a brake on further monitor output -- cannot do any more
+ * until the last, part, line is dealt with.
+ *
+ * * restore the command line, unless it is empty !
+ */
+
+ /*-----------------------------------------------------------------------------
+ * Prepare for new monitor output line.
+ *
+ * Wipe any existing command line.
+ */
+extern void
+uty_cli_pre_monitor(vty_io vio, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_wipe(vio, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Recover from monitor line output.
+ *
+ * If monitor line failed to complete, append the rump to the CLI buffer.
+ *
+ * If have a non-empty command line, or is cli_more_wait, redraw the command
+ * line.
+ *
+ * Returns: 0 => rump was empty and no command line stuff written
+ * > 0 => rump not empty or some command line stuff written
+ */
+extern int
+uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (len != 0)
+ uty_cli_write(vio, buf, len) ;
+
+ if (vio->cli_more_wait || (vio->cl.len != 0))
+ {
+ uty_cli_draw(vio) ;
+ ++len ;
+ } ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Command line processing loop
+ */
+
+static bool uty_telnet_command(vty_io vio, keystroke stroke, bool callback) ;
+static int uty_cli_insert (vty_io vio, const char* chars, int n) ;
+static int uty_cli_overwrite (vty_io vio, char* chars, int n) ;
+static int uty_cli_word_overwrite (vty_io vio, char *str) ;
+static int uty_cli_forwards(vty_io vio, int n) ;
+static int uty_cli_backwards(vty_io vio, int n) ;
+static int uty_cli_del_forwards(vty_io vio, int n) ;
+static int uty_cli_del_backwards(vty_io vio, int n) ;
+static int uty_cli_bol (vty_io vio) ;
+static int uty_cli_eol (vty_io vio) ;
+static int uty_cli_word_forwards_delta(vty_io vio) ;
+static int uty_cli_word_forwards(vty_io vio) ;
+static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ;
+static int uty_cli_word_backwards_pure (vty_io vio) ;
+static int uty_cli_word_backwards (vty_io vio) ;
+static int uty_cli_del_word_forwards(vty_io vio) ;
+static int uty_cli_del_word_backwards(vty_io vio) ;
+static int uty_cli_del_to_eol (vty_io vio) ;
+static int uty_cli_clear_line(vty_io vio) ;
+static int uty_cli_transpose_chars(vty_io vio) ;
+static void uty_cli_history_use(vty_io vio, int step) ;
+static void uty_cli_next_line(vty_io vio) ;
+static void uty_cli_previous_line (vty_io vio) ;
+static void uty_cli_complete_command (vty_io vio, enum node_type node) ;
+static void uty_cli_describe_command (vty_io vio, enum node_type node) ;
+
+/*------------------------------------------------------------------------------
+ * Fetch next keystroke, reading from the file if required.
+ */
+static inline bool
+uty_cli_get_keystroke(vty_io vio, keystroke stroke)
+{
+ if (keystroke_get(vio->key_stream, stroke))
+ return 1 ;
+
+ uty_read(vio, NULL) ; /* not stealing */
+
+ return keystroke_get(vio->key_stream, stroke) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process keystrokes until run out of input, or get something to cli_do.
+ *
+ * If required, draw the prompt and command line.
+ *
+ * Process keystrokes until run out of stuff to do, or have a "command line"
+ * that must now be executed.
+ *
+ * Processes the contents of the keystroke stream. If exhausts that, will set
+ * ready to read and return. (To give some "sharing".)
+ *
+ * Returns: cli_do_xxxx
+ *
+ * When returns the cl is '\0' terminated.
+ */
+static enum cli_do
+uty_cli_process(vty_io vio, enum node_type node)
+{
+ struct keystroke stroke ;
+ uint8_t u ;
+ int auth ;
+ enum cli_do ret ;
+
+ auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+
+ /* Now process as much as possible of what there is */
+ ret = cli_do_nothing ;
+ while (1)
+ {
+ if (!vio->cli_drawn)
+ uty_cli_draw_this(vio, node) ;
+
+ if (!uty_cli_get_keystroke(vio, &stroke))
+ {
+ ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
+ break ;
+ } ;
+
+ if (stroke.flags != 0)
+ {
+ /* TODO: deal with broken keystrokes */
+ continue ;
+ } ;
+
+ switch (stroke.type)
+ {
+ /* Straightforward character -----------------------------------*/
+ /* Note: only interested in 8-bit characters ! */
+ case ks_char:
+ u = (uint8_t)stroke.value ;
+
+ switch (stroke.value)
+ {
+ case CONTROL('A'):
+ uty_cli_bol (vio);
+ break;
+
+ case CONTROL('B'):
+ uty_cli_backwards(vio, 1);
+ break;
+
+ case CONTROL('C'):
+ ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/
+ break ;
+
+ case CONTROL('D'):
+ if (vio->cl.len == 0) /* if at start of empty line */
+ ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/
+ else
+ uty_cli_del_forwards(vio, 1);
+ break;
+
+ case CONTROL('E'):
+ uty_cli_eol (vio);
+ break;
+
+ case CONTROL('F'):
+ uty_cli_forwards(vio, 1);
+ break;
+
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_del_backwards(vio, 1);
+ break;
+
+ case CONTROL('K'):
+ uty_cli_del_to_eol (vio);
+ break;
+
+ case CONTROL('N'):
+ uty_cli_next_line (vio);
+ break;
+
+ case CONTROL('P'):
+ uty_cli_previous_line (vio);
+ break;
+
+ case CONTROL('T'):
+ uty_cli_transpose_chars (vio);
+ break;
+
+ case CONTROL('U'):
+ uty_cli_clear_line(vio);
+ break;
+
+ case CONTROL('W'):
+ uty_cli_del_word_backwards (vio);
+ break;
+
+ case CONTROL('Z'):
+ ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/
+ break;
+
+ case '\n':
+ case '\r':
+ ret = cli_do_command ; /* Exit on CR or LF.............*/
+ break ;
+
+ case '\t':
+ if (auth)
+ break ;
+ else
+ uty_cli_complete_command (vio, node);
+ break;
+
+ case '?':
+ if (auth)
+ uty_cli_insert (vio, (char*)&u, 1);
+ else
+ uty_cli_describe_command (vio, node);
+ break;
+
+ default:
+ if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
+ uty_cli_insert (vio, (char*)&u, 1) ;
+ break;
+ }
+ break ;
+
+ /* ESC X -------------------------------------------------------------*/
+ case ks_esc:
+ switch (stroke.value)
+ {
+ case 'b':
+ uty_cli_word_backwards (vio);
+ break;
+
+ case 'f':
+ uty_cli_word_forwards (vio);
+ break;
+
+ case 'd':
+ uty_cli_del_word_forwards (vio);
+ break;
+
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_del_word_backwards (vio);
+ break;
+
+ default:
+ break;
+ } ;
+ break ;
+
+ /* ESC [ X -----------------------------------------------------------*/
+ case ks_csi:
+ if (stroke.len != 0)
+ break ; /* only recognise 3 byte sequences */
+
+ switch (stroke.value)
+ {
+ case ('A'):
+ uty_cli_previous_line (vio);
+ break;
+
+ case ('B'):
+ uty_cli_next_line (vio);
+ break;
+
+ case ('C'):
+ uty_cli_forwards(vio, 1);
+ break;
+
+ case ('D'):
+ uty_cli_backwards(vio, 1);
+ break;
+ default:
+ break ;
+ } ;
+ break ;
+
+ /* Telnet Command ----------------------------------------------------*/
+ case ks_iac:
+ uty_telnet_command(vio, &stroke, false) ;
+ break ;
+
+ /* Unknown -----------------------------------------------------------*/
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+
+ /* After each keystroke..... */
+
+ if (ret != cli_do_nothing)
+ {
+ uty_cli_eol (vio) ; /* go to the end of the line */
+ break ; /* stop processing */
+ } ;
+ } ;
+
+ /* Tidy up and return where got to. */
+
+ qs_term(&vio->cl) ; /* add '\0' */
+
+ return ret ;
+} ;
+
+/*==============================================================================
+ * Command line operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' characters at current position in the command line
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_insert (vty_io vio, const char* chars, int n)
+{
+ int after ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+
+ if (n <= 0)
+ return n ; /* avoid trouble */
+
+ after = qs_insert(&vio->cl, chars, n) ;
+
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+
+ if (after != 0)
+ uty_cli_echo_n(vio, telnet_backspaces, after) ;
+
+ vio->cl.cp += n ;
+
+ return n ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Overstrike 'n' characters at current position in the command line
+ *
+ * Move current position forwards.
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_overwrite (vty_io vio, char* chars, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+
+ if (n > 0)
+ {
+ qs_replace(&vio->cl, chars, n) ;
+ uty_cli_echo(vio, chars, n) ;
+
+ vio->cl.cp += n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Insert a word into vty interface with overwrite mode.
+ *
+ * NB: Assumes result will then be the end of the line.
+ *
+ * Returns number of characters inserted -- ie length of string
+ */
+static int
+uty_cli_word_overwrite (vty_io vio, char *str)
+{
+ int n ;
+ VTY_ASSERT_LOCKED() ;
+
+ n = uty_cli_overwrite(vio, str, strlen(str)) ;
+
+ vio->cl.len = vio->cl.cp ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Forward 'n' characters -- stop at end of line.
+ *
+ * Returns number of characters actually moved
+ */
+static int
+uty_cli_forwards(vty_io vio, int n)
+{
+ int have ;
+ VTY_ASSERT_LOCKED() ;
+
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ;
+
+ assert(n >= 0) ;
+
+ if (n > 0)
+ {
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
+ vio->cl.cp += n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Backwards 'n' characters -- stop at start of line.
+ *
+ * Returns number of characters actually moved
+ */
+static int
+uty_cli_backwards(vty_io vio, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if ((int)vio->cl.cp < n)
+ n = vio->cl.cp ;
+
+ assert(n >= 0) ;
+
+ if (n > 0)
+ {
+ uty_cli_echo_n(vio, telnet_backspaces, n) ;
+ vio->cl.cp -= n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete 'n' characters -- forwards -- stop at end of line.
+ *
+ * Returns number of characters actually deleted.
+ */
+static int
+uty_cli_del_forwards(vty_io vio, int n)
+{
+ int after ;
+ int have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ; /* cannot delete more than have */
+
+ assert(n >= 0) ;
+
+ if (n <= 0)
+ return 0 ;
+
+ after = qs_delete(&vio->cl, n) ;
+
+ if (after > 0)
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+
+ uty_cli_echo_n(vio, telnet_spaces, n) ;
+ uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete 'n' characters before the point.
+ *
+ * Returns number of characters actually deleted.
+ */
+static int
+uty_cli_del_backwards(vty_io vio, int n)
+{
+ return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Move to the beginning of the line.
+ *
+ * Returns number of characters moved over.
+ */
+static int
+uty_cli_bol (vty_io vio)
+{
+ return uty_cli_backwards(vio, vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move to the end of the line.
+ *
+ * Returns number of characters moved over
+ */
+static int
+uty_cli_eol (vty_io vio)
+{
+ return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Forward word delta -- distance to start of next word.
+ *
+ * Return number of characters to step over to reach next word.
+ *
+ * Steps over non-space characters and then any spaces.
+ */
+static int
+uty_cli_word_forwards_delta(vty_io vio)
+{
+ char* cp ;
+ char* tp ;
+ char* ep ;
+
+ VTY_ASSERT_LOCKED() ; ;
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ cp = qs_cp_char(&vio->cl) ;
+ ep = qs_ep_char(&vio->cl) ;
+
+ tp = cp ;
+
+ while ((tp < ep) && (*tp != ' '))
+ ++tp ;
+
+ while ((tp < ep) && (*tp == ' '))
+ ++tp ;
+
+ return tp - cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Forward word -- move to start of next word.
+ *
+ * Moves past any non-spaces, then past any spaces.
+ */
+static int
+uty_cli_word_forwards(vty_io vio)
+{
+ return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Backward word delta -- distance to start of next word, back.
+ *
+ * Return number of characters to step over to reach next word.
+ *
+ * If "eat_spaces", starts by stepping over spaces.
+ * Steps back until next (backwards) character is space, or hits start of line.
+ */
+static int
+uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
+{
+ char* cp ;
+ char* tp ;
+ char* sp ;
+
+ VTY_ASSERT_LOCKED() ; ;
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ cp = qs_cp_char(&vio->cl) ;
+ sp = qs_chars(&vio->cl) ;
+
+ tp = cp ;
+
+ if (eat_spaces)
+ while ((tp > sp) && (*(tp - 1) == ' '))
+ --tp ;
+
+ while ((tp > sp) && (*(tp - 1) != ' '))
+ --tp ;
+
+ return cp - tp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Backward word, but not trailing spaces.
+ *
+ * Move back until next (backwards) character is space or start of line.
+ *
+ * Returns number of characters stepped over.
+ */
+static int
+uty_cli_word_backwards_pure (vty_io vio)
+{
+ return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Backward word -- move to start of previous word.
+ *
+ * Moves past any spaces, then move back until next (backwards) character is
+ * space or start of line.
+ *
+ * Returns number of characters stepped over.
+ */
+static int
+uty_cli_word_backwards (vty_io vio)
+{
+ return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete to end of word -- forwards.
+ *
+ * Deletes any leading spaces, then deletes upto next space or end of line.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_word_forwards(vty_io vio)
+{
+ return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete to start of word -- backwards.
+ *
+ * Deletes any trailing spaces, then deletes upto next space or start of line.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_word_backwards(vty_io vio)
+{
+ return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill rest of line from current point.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_to_eol (vty_io vio)
+{
+ return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill line from the beginning.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_clear_line(vty_io vio)
+{
+ uty_cli_bol(vio) ;
+ return uty_cli_del_to_eol(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Transpose chars before or at the point.
+ *
+ * Return number of characters affected.
+ */
+static int
+uty_cli_transpose_chars(vty_io vio)
+{
+ char chars[2] ;
+ char* cp ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Give up if < 2 characters or at start of line. */
+ if ((vio->cl.len < 2) || (vio->cl.cp < 1))
+ return 0 ;
+
+ /* Move back to first of characters to exchange */
+ if (vio->cl.cp == vio->cl.len)
+ uty_cli_backwards(vio, 2) ;
+ else
+ uty_cli_backwards(vio, 1) ;
+
+ /* Pick up in the new order */
+ cp = qs_cp_char(&vio->cl) ;
+ chars[1] = *cp++ ;
+ chars[0] = *cp ;
+
+ /* And overwrite */
+ return uty_cli_overwrite(vio, chars, 2) ;
+} ;
+
+/*==============================================================================
+ * Command line history handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Add given command line to the history buffer.
+ *
+ * This is inserting the vty->buf line into the history.
+ */
+extern void
+uty_cli_hist_add (vty_io vio, const char* cmd_line)
+{
+ qstring prev_line ;
+ qstring_t line ;
+ int prev_index ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Construct a dummy qstring for the given command line */
+ qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */
+
+ /* make sure have a suitable history vector */
+ vector_set_min_length(&vio->hist, VTY_MAXHIST) ;
+
+ /* find the previous command line in the history */
+ prev_index = vio->hindex - 1 ;
+ if (prev_index < 0)
+ prev_index = VTY_MAXHIST - 1 ;
+
+ prev_line = vector_get_item(&vio->hist, prev_index) ;
+
+ /* If the previous line is NULL, that means the history is empty.
+ *
+ * If the previous line is essentially the same as the current line,
+ * replace it with the current line -- so that the latest whitespace
+ * version is saved.
+ *
+ * Either way, replace the the previous line entry by moving hindex
+ * back !
+ */
+ if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
+ vio->hindex = prev_index ;
+ else
+ prev_line = vector_get_item(&vio->hist, vio->hindex) ;
+
+ /* Now replace the hindex entry */
+ vector_set_item(&vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+
+ /* Advance to the near future and reset the history pointer */
+ vio->hindex++;
+ if (vio->hindex == VTY_MAXHIST)
+ vio->hindex = 0;
+
+ vio->hp = vio->hindex;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Replace command line by current history.
+ *
+ * This function is called from vty_next_line and vty_previous_line.
+ *
+ * Step +1 is towards the present
+ * -1 is into the past
+ */
+static void
+uty_cli_history_use(vty_io vio, int step)
+{
+ int index ;
+ unsigned old_len ;
+ unsigned after ;
+ unsigned back ;
+ qstring hist ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((step == +1) || (step == -1)) ;
+
+ index = vio->hp ;
+
+ /* Special case of being at the insertion point */
+ if (index == vio->hindex)
+ {
+ if (step > 0)
+ return ; /* already in the present */
+
+ /* before stepping back from the present, take a copy of the
+ * current command line -- so can get back to it.
+ */
+ hist = vector_get_item(&vio->hist, vio->hindex) ;
+ vector_set_item(&vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ } ;
+
+ /* Advance or retreat */
+ index += step ;
+ if (index < 0)
+ index = VTY_MAXHIST - 1 ;
+ else if (index >= VTY_MAXHIST)
+ index = 0 ;
+
+ hist = vector_get_item(&vio->hist, index) ;
+
+ /* If moving backwards in time, may not move back to the insertion
+ * point (that would be wrapping round to the present) and may not
+ * move back to a NULL entry (that would be going back before '.').
+ */
+ if (step < 0)
+ if ((hist == NULL) || (index == vio->hindex))
+ return ;
+
+ /* Now, if arrived at the insertion point, this is returning to the
+ * present, which is fine.
+ */
+ vio->hp = index;
+
+ /* Move back to the start of the current line */
+ uty_cli_bol(vio) ;
+
+ /* Get previous line from history buffer and echo that */
+ old_len = vio->cl.len ;
+ qs_copy(&vio->cl, hist) ;
+
+ /* Sort out wiping out any excess and setting the cursor position */
+ if (old_len > vio->cl.len)
+ after = old_len - vio->cl.len ;
+ else
+ after = 0 ;
+
+ back = after ;
+ if (vio->cl.len > vio->cl.cp)
+ back += (vio->cl.len - vio->cl.cp) ;
+
+ if (vio->cl.len > 0)
+ uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+
+ if (after > 0)
+ uty_cli_echo_n(vio, telnet_spaces, after) ;
+
+ if (back > 0)
+ uty_cli_echo_n(vio, telnet_backspaces, back) ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Use next history line, if any.
+ */
+static void
+uty_cli_next_line(vty_io vio)
+{
+ uty_cli_history_use(vio, +1) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Use previous history line, if any.
+ */
+static void
+uty_cli_previous_line (vty_io vio)
+{
+ uty_cli_history_use(vio, -1) ;
+}
+
+/*==============================================================================
+ * Command Completion and Command Description
+ *
+ */
+static void uty_cli_describe_show(vty_io vio, vector describe) ;
+static void uty_cli_describe_fold (vty_io vio, int cmd_width,
+ unsigned int desc_width, struct desc *desc) ;
+static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
+ const char* str) ;
+
+static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
+
+/*------------------------------------------------------------------------------
+ * Command completion
+ */
+static void
+uty_cli_complete_command (vty_io vio, enum node_type node)
+{
+ unsigned i ;
+ int ret ;
+ int len ;
+ int n ;
+ vector matched ;
+ vector vline ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Try and match the tokenised command line */
+ vline = uty_cli_cmd_prepare(vio, 1) ;
+ matched = cmd_complete_command (vline, node, &ret);
+ cmd_free_strvec (vline);
+
+ /* Show the result. */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
+
+ case CMD_COMPLETE_FULL_MATCH:
+ uty_cli_eol (vio) ;
+ uty_cli_word_backwards_pure (vio);
+ uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
+ uty_cli_insert(vio, " ", 1);
+ break ;
+
+ case CMD_COMPLETE_MATCH:
+ uty_cli_eol (vio) ;
+ uty_cli_word_backwards_pure (vio);
+ uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
+ break ;
+
+ case CMD_COMPLETE_LIST_MATCH:
+ len = 6 ;
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ int sl = strlen((char*)vector_get_item(matched, i)) ;
+ if (len < sl)
+ len = sl ;
+ } ;
+
+ n = vio->width ;
+ if (n == 0)
+ n = 60 ;
+ n = n / (len + 2) ;
+ if (n == 0)
+ n = 1 ;
+
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ if ((i % n) == 0)
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i));
+ }
+ uty_cli_out_newline(vio) ;
+
+ break;
+
+ case CMD_COMPLETE_ALREADY:
+ default:
+ break;
+ } ;
+
+ cmd_free_strvec(matched);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command Description
+ */
+static void
+uty_cli_describe_command (vty_io vio, enum node_type node)
+{
+ int ret;
+ vector vline;
+ vector describe;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Try and match the tokenised command line */
+ vline = uty_cli_cmd_prepare(vio, 1) ;
+ describe = cmd_describe_command (vline, node, &ret);
+ cmd_free_strvec (vline);
+
+ uty_cli_out_newline(vio); /* clears cli_drawn */
+
+ /* Deal with result. */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
+
+ default:
+ uty_cli_describe_show(vio, describe) ;
+ break ;
+ } ;
+
+ if (describe != NULL)
+ vector_free (describe);
+}
+
+/*------------------------------------------------------------------------------
+ * Show the command description.
+ *
+ * Generates lines of the form:
+ *
+ * word description text
+ *
+ * Where the word field is adjusted to suit the longest word, and the
+ * description text is wrapped if required (if the width of the console is
+ * known) so that get:
+ *
+ * word description ..................................
+ * .............text
+ *
+ * If one of the options is '<cr>', that is always shown last.
+ */
+static void
+uty_cli_describe_show(vty_io vio, vector describe)
+{
+ unsigned int i, cmd_width, desc_width;
+ struct desc *desc, *desc_cr ;
+
+ /* Get width of the longest "word" */
+ cmd_width = 0;
+ for (i = 0; i < vector_active (describe); i++)
+ if ((desc = vector_slot (describe, i)) != NULL)
+ {
+ unsigned int len;
+
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ len = strlen (desc->cmd);
+ if (desc->cmd[0] == '.')
+ len--;
+
+ if (cmd_width < len)
+ cmd_width = len;
+ }
+
+ /* Set width of description string. */
+ desc_width = vio->width - (cmd_width + 6);
+
+ /* Print out description. */
+ desc_cr = NULL ; /* put <cr> last if it appears */
+
+ for (i = 0; i < vector_active (describe); i++)
+ if ((desc = vector_slot (describe, i)) != NULL)
+ {
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ if (strcmp (desc->cmd, command_cr) == 0)
+ {
+ desc_cr = desc;
+ continue;
+ }
+
+ uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
+ }
+
+ if (desc_cr != NULL)
+ uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Show one word and the description, folding the description as required.
+ */
+static void
+uty_cli_describe_fold (vty_io vio, int cmd_width,
+ unsigned int desc_width, struct desc *desc)
+{
+ char *buf;
+ const char *cmd, *p;
+ int pos;
+
+ VTY_ASSERT_LOCKED() ;
+
+ cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+ p = desc->str ;
+
+ /* If have a sensible description width */
+ if (desc_width > 20)
+ {
+ buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+
+ while (strlen (p) > desc_width)
+ {
+ /* move back to first space */
+ for (pos = desc_width; pos > 0; pos--)
+ if (*(p + pos) == ' ')
+ break;
+
+ /* if did not find a space, break at width */
+ if (pos == 0)
+ pos = desc_width ;
+
+ strncpy (buf, p, pos);
+ buf[pos] = '\0';
+ uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
+
+ cmd = ""; /* for 2nd and subsequent lines */
+
+ p += pos ; /* step past what just wrote */
+ while (*p == ' ')
+ ++p ; /* skip spaces */
+ } ;
+
+ XFREE (MTYPE_TMP, buf);
+ } ;
+
+ uty_cli_describe_line(vio, cmd_width, cmd, p) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Show one description line.
+ */
+static void
+uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
+ const char* str)
+{
+ if (str != NULL)
+ uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
+ else
+ uty_cli_out (vio, " %-s", cmd) ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepare "vline" token array for command handler.
+ *
+ * For "help" (command completion/description), if the command line is empty,
+ * or ends in ' ', adds an empty token to the end of the token array.
+ */
+static vector
+uty_cli_cmd_prepare(vty_io vio, int help)
+{
+ vector vline ;
+
+ vline = cmd_make_strvec(qs_term(&vio->cl)) ;
+
+ /* Note that if there is a vector of tokens, then there is at least one
+ * token, so can guarantee that vio->cl.len >= 1 !
+ */
+ if (help)
+ if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
+ vline = cmd_add_to_strvec(vline, "") ;
+
+ return vline ;
+} ;
+
+/*==============================================================================
+ * VTY telnet stuff
+ */
+
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
+
+static const char* telnet_commands[256] =
+{
+ [tn_IAC ] = "IAC",
+ [tn_DONT ] = "DONT",
+ [tn_DO ] = "DO",
+ [tn_WONT ] = "WONT",
+ [tn_WILL ] = "WILL",
+ [tn_SB ] = "SB",
+ [tn_GA ] = "GA",
+ [tn_EL ] = "EL",
+ [tn_EC ] = "EC",
+ [tn_AYT ] = "AYT",
+ [tn_AO ] = "AO",
+ [tn_IP ] = "IP",
+ [tn_BREAK] = "BREAK",
+ [tn_DM ] = "DM",
+ [tn_NOP ] = "NOP",
+ [tn_SE ] = "SE",
+ [tn_EOR ] = "EOR",
+ [tn_ABORT] = "ABORT",
+ [tn_SUSP ] = "SUSP",
+ [tn_EOF ] = "EOF",
+} ;
+
+static const char* telnet_options[256] =
+{
+ [to_BINARY] = "BINARY", /* 8-bit data path */
+ [to_ECHO] = "ECHO", /* echo */
+ [to_RCP] = "RCP", /* prepare to reconnect */
+ [to_SGA] = "SGA", /* suppress go ahead */
+ [to_NAMS] = "NAMS", /* approximate message size */
+ [to_STATUS] = "STATUS", /* give status */
+ [to_TM] = "TM", /* timing mark */
+ [to_RCTE] = "RCTE", /* remote controlled tx and echo */
+ [to_NAOL] = "NAOL", /* neg. about output line width */
+ [to_NAOP] = "NAOP", /* neg. about output page size */
+ [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
+ [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
+ [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
+ [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
+ [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
+ [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
+ [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
+ [to_XASCII] = "XASCII", /* extended ascii character set */
+ [to_LOGOUT] = "LOGOUT", /* force logout */
+ [to_BM] = "BM", /* byte macro */
+ [to_DET] = "DET", /* data entry terminal */
+ [to_SUPDUP] = "SUPDUP", /* supdup protocol */
+ [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
+ [to_SNDLOC] = "SNDLOC", /* send location */
+ [to_TTYPE] = "TTYPE", /* terminal type */
+ [to_EOR] = "EOR", /* end or record */
+ [to_TUID] = "TUID", /* TACACS user identification */
+ [to_OUTMRK] = "OUTMRK", /* output marking */
+ [to_TTYLOC] = "TTYLOC", /* terminal location number */
+ [to_3270REGIME] = "3270REGIME", /* 3270 regime */
+ [to_X3PAD] = "X3PAD", /* X.3 PAD */
+ [to_NAWS] = "NAWS", /* window size */
+ [to_TSPEED] = "TSPEED", /* terminal speed */
+ [to_LFLOW] = "LFLOW", /* remote flow control */
+ [to_LINEMODE] = "LINEMODE", /* Linemode option */
+ [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
+ [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
+ [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
+ [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
+ [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
+ [to_EXOPL] = "EXOPL", /* extended-options-list */
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as decimal.
+ */
+static void
+uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(vio, "%s ", str) ;
+ else
+ uty_cli_out(vio, "%d ", (int)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as hex.
+ */
+static void
+uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(vio, "%s ", str) ;
+ else
+ uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "WILL TELOPT_ECHO"
+ */
+static void
+uty_will_echo (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "suppress Go-Ahead"
+ */
+static void
+uty_will_suppress_go_ahead (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use linemode"
+ */
+static void
+uty_dont_linemode (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "Use window size"
+ */
+static void
+uty_do_window_size (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use lflow" -- not currently used
+ */
+static void
+uty_dont_lflow_ahead (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * The keystroke iac callback function.
+ *
+ * This deals with IAC sequences that should be dealt with as soon as they
+ * are read -- not stored in the keystroke stream for later processing.
+ */
+extern bool
+uty_cli_iac_callback(keystroke_iac_callback_args)
+{
+ return uty_telnet_command((vty_io)context, stroke, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process incoming Telnet Option(s)
+ *
+ * May be called during keystroke iac callback, or when processing CLI
+ * keystrokes.
+ *
+ * In particular: get telnet window size.
+ *
+ * Returns: true <=> dealt with, for:
+ *
+ * * telnet window size.
+ */
+static bool
+uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
+{
+ uint8_t* p ;
+ uint8_t o ;
+ int left ;
+ bool dealt_with ;
+
+ /* Echo to the other end if required */
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_wipe(vio, 0) ;
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
+ ++p ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(vio, telnet_options[*p], *p) ;
+ ++p ;
+
+ if (left > 0)
+ {
+ while(left-- > 0)
+ uty_cli_out_hex(vio, NULL, *p++) ;
+
+ if (stroke->flags & kf_truncated)
+ uty_cli_out(vio, "... ") ;
+
+ if (!(stroke->flags & kf_broken))
+ {
+ uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+ uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
+ }
+ } ;
+
+ if (stroke->flags & kf_broken)
+ uty_cli_out (vio, "BROKEN") ;
+
+ uty_cli_out (vio, "\r\n") ;
+ } ;
+
+ /* Process the telnet command */
+ dealt_with = false ;
+
+ if (stroke->flags != 0)
+ return dealt_with ; /* go no further if broken */
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ passert(left >= 1) ; /* must be if not broken ! */
+ passert(stroke->value == *p) ; /* or something is wrong */
+
+ ++p ; /* step past X of IAC X */
+ --left ;
+
+ /* Decode the one command that is interesting -- "NAWS" */
+ switch (stroke->value)
+ {
+ case tn_SB:
+ passert(left > 0) ; /* or parser failed */
+
+ o = *p++ ; /* the option byte */
+ --left ;
+ switch(o)
+ {
+ case to_NAWS:
+ if (left != 4)
+ {
+ uzlog(NULL, LOG_WARNING,
+ "RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %d",
+ (3 + 4 + 2), (3 + left + 2)) ;
+ }
+ else
+ {
+ vio->width = *p++ << 8 ;
+ vio->width += *p++ ;
+ vio->height = *p++ << 8 ;
+ vio->height += *p ;
+
+ if (TELNET_OPTION_DEBUG)
+ uty_cli_out(vio, "TELNET NAWS window size received: "
+ "width %d, height %d%s",
+ vio->width, vio->height, telnet_newline) ;
+ uty_set_height(vio) ;
+
+ dealt_with = true ;
+ } ;
+ break ;
+
+ default: /* no other IAC SB <option> */
+ break ;
+ } ;
+ break ;
+
+ default: /* no other IAC X */
+ break ;
+ } ;
+
+ return dealt_with ;
+} ;
diff --git a/lib/vty_cli.h b/lib/vty_cli.h
new file mode 100644
index 00000000..f532616a
--- /dev/null
+++ b/lib/vty_cli.h
@@ -0,0 +1,49 @@
+/* VTY Command Line Handler
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 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 _ZEBRA_VTY_CLI_H
+#define _ZEBRA_VTY_CLI_H
+
+#include "vty_io.h"
+#include "keystroke.h"
+
+extern void uty_cli_init(vty_io vio) ;
+extern enum vty_readiness uty_cli_start(vty_io vio) ;
+extern void uty_cli_close(vty_io vio) ;
+
+extern enum vty_readiness uty_cli(vty_io vio) ;
+extern keystroke_callback uty_cli_iac_callback ;
+
+extern void uty_cli_hist_add (vty_io vio, const char* cmd_line) ;
+extern void uty_cli_enter_more_wait(vty_io vio) ;
+extern void uty_cli_exit_more_wait(vty_io vio) ;
+
+extern void uty_free_host_name(void) ;
+extern void uty_check_host_name(void) ;
+
+extern bool uty_cli_draw_if_required(vty_io vio) ;
+
+extern void uty_cli_pre_monitor(vty_io vio, size_t len) ;
+extern int uty_cli_post_monitor(vty_io vio, const char* buf, size_t len) ;
+
+#endif /* _ZEBRA_VTY_CLI_H */
diff --git a/lib/vty_io.c b/lib/vty_io.c
new file mode 100644
index 00000000..74e32c75
--- /dev/null
+++ b/lib/vty_io.c
@@ -0,0 +1,2742 @@
+/* VTY IO Functions
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 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 "vty.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+#include "qstring.h"
+#include "keystroke.h"
+
+#include "memory.h"
+
+#include "prefix.h"
+#include "filter.h"
+#include "privs.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
+#include <sys/un.h> /* for VTYSH */
+#include <sys/socket.h>
+
+#define VTYSH_DEBUG 0
+
+/*==============================================================================
+ * VTY Command Output -- base functions
+ *
+ * During command processing the output sent here is held until the command
+ * completes.
+ */
+
+static int uty_config_write(vty_io vio, bool all) ;
+
+/*------------------------------------------------------------------------------
+ * VTY output function -- cf fprintf
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+uty_out (struct vty *vty, const char *format, ...)
+{
+ int result;
+ VTY_ASSERT_LOCKED() ;
+ va_list args;
+ va_start (args, format);
+ result = uty_vout(vty, format, args);
+ va_end (args);
+ return result;
+}
+
+/*------------------------------------------------------------------------------
+ * VTY output function -- cf vfprintf
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ *
+ * NB: for VTY_TERM and for VTY_SHELL_SERV -- this is command output:
+ *
+ * * MAY NOT do any command output if !cmd_enabled
+ *
+ * * first, the life of a vty is not guaranteed unless cmd_in_progress,
+ * so should not attempt to use a vty anywhere other than command
+ * execution.
+ *
+ * * second, cmd_out_enabled is false most of the time, and is only
+ * set true when a command completes, and it is time to write away
+ * the results.
+ *
+ * * all output is placed in the vio->cmd_obuf. When the command completes,
+ * the contents of the cmd_obuf will be written away -- subject to line
+ * control.
+ *
+ * * output is discarded if the vty is no longer write_open
+ */
+extern int
+uty_vout(struct vty *vty, const char *format, va_list args)
+{
+ vty_io vio ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio = vty->vio ;
+
+ switch (vio->type)
+ {
+ case VTY_STDOUT:
+ case VTY_SHELL:
+ ret = vprintf (format, args) ;
+ break ;
+
+ case VTY_STDERR:
+ ret = vfprintf (stderr, format, args) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ if ((ret > 0) && vio_fifo_full_lump(&vio->cmd_obuf))
+ ret = uty_config_write(vio, false) ;
+ break ;
+
+ case VTY_TERM:
+ case VTY_SHELL_SERV:
+ assert(vio->cmd_in_progress) ;
+
+ if (!vio->sock.write_open)
+ return 0 ; /* discard output if not open ! */
+
+ /* fall through.... */
+
+ case VTY_CONFIG_READ:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ break ;
+
+ default:
+ zabort("impossible VTY type") ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear the contents of the command output FIFO etc.
+ *
+ * NB: does not change any of the cli_blocked/cmd_in_progress/cli_wait_more/etc
+ * flags -- competent parties must deal with those
+ */
+extern void
+uty_out_clear(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vio_fifo_clear(&vio->cmd_obuf) ;
+
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush the contents of the command output FIFO to the given file.
+ *
+ * Takes no notice of any errors !
+ */
+extern void
+uty_out_fflush(vty_io vio, FILE* file)
+{
+ char* src ;
+ size_t have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ fflush(file) ;
+
+ while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ {
+ fwrite(src, 1, have, file) ;
+ vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ } ;
+
+ fflush(file) ;
+} ;
+
+/*==============================================================================
+ * The watch dog.
+ *
+ * The watch dog starts up every now and checks:
+ *
+ * * for changes to the host name, which should be reflected in the
+ * prompt for any terminals.
+ *
+ * * the death watch list
+ */
+
+enum { vty_watch_dog_interval = 5 } ;
+
+static void vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when) ;
+static int vty_watch_dog_thread(struct thread *thread) ;
+
+static void uty_watch_dog_bark(void) ;
+static bool uty_death_watch_scan(void) ;
+
+/*------------------------------------------------------------------------------
+ * Start watch dog -- the first time a VTY is created.
+ */
+extern void
+uty_watch_dog_start()
+{
+ if (vty_cli_nexus)
+ vty_watch_dog.qnexus = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ NULL, NULL) ;
+
+ uty_watch_dog_bark() ; /* start up by barking the first time */
+}
+
+/*------------------------------------------------------------------------------
+ * Stop watch dog timer -- at close down.
+ *
+ * Final run along the death-watch
+ *
+ */
+extern void
+uty_watch_dog_stop(void)
+{
+ if (vty_watch_dog.anon != NULL)
+ {
+ if (vty_cli_nexus)
+ qtimer_free(vty_watch_dog.qnexus) ;
+ else
+ thread_cancel(vty_watch_dog.thread) ;
+ } ;
+
+ uty_death_watch_scan() ; /* scan the death-watch list */
+}
+
+/*------------------------------------------------------------------------------
+ * qnexus watch dog action
+ */
+static void
+vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when)
+{
+ VTY_LOCK() ;
+
+ uty_watch_dog_bark() ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * thread watch dog action
+ */
+static int
+vty_watch_dog_thread(struct thread *thread)
+{
+ VTY_LOCK() ;
+
+ vty_watch_dog.thread = NULL ;
+ uty_watch_dog_bark() ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Watch dog action
+ */
+static void
+uty_watch_dog_bark(void)
+{
+ uty_check_host_name() ; /* check for host name change */
+
+ uty_death_watch_scan() ; /* scan the death-watch list */
+
+ /* Set timer to go off again later */
+ if (vty_cli_nexus)
+ qtimer_set(vty_watch_dog.qnexus,
+ qt_add_monotonic(QTIME(vty_watch_dog_interval)),
+ vty_watch_dog_qnexus) ;
+ else
+ {
+ if (vty_watch_dog.thread != NULL)
+ thread_cancel (vty_watch_dog.thread);
+ vty_watch_dog.thread = thread_add_timer (vty_master,
+ vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Scan the death watch list.
+ *
+ * A vty may finally be freed if it is closed and there is no command in
+ * progress.
+ */
+static bool
+uty_death_watch_scan(void)
+{
+ vty_io vio ;
+ vty_io next ;
+
+ next = vio_death_watch ;
+ while (next != NULL)
+ {
+ vio = next ;
+ next = sdl_next(vio, vio_list) ;
+
+ if (vio->closed && !vio->cmd_in_progress)
+ {
+ uty_close(vio) ; /* closes again to ensure that all buffers
+ are released. */
+
+ sdl_del(vio_death_watch, vio, vio_list) ;
+
+ XFREE(MTYPE_VTY, vio->vty) ;
+ XFREE(MTYPE_VTY, vio) ;
+ } ;
+ } ;
+
+ return (vio_death_watch == NULL) ;
+} ;
+
+/*==============================================================================
+ * Prototypes.
+ */
+static void uty_sock_init_new(vio_sock sock, int fd, void* info) ;
+static void uty_sock_half_close(vio_sock sock) ;
+static void uty_sock_close(vio_sock sock) ;
+
+static void vty_read_qnexus (qps_file qf, void* file_info) ;
+static void vty_write_qnexus (qps_file qf, void* file_info) ;
+static void vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when) ;
+
+static int vty_read_thread (struct thread *thread) ;
+static int vty_write_thread (struct thread *thread) ;
+static int vty_timer_thread (struct thread *thread) ;
+
+static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
+static int vtysh_read_thread (struct thread *thread) ;
+
+static enum vty_readiness uty_write(vty_io vio) ;
+
+/*==============================================================================
+ * Creation and destruction of VTY objects
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate new vty struct
+ *
+ * Allocates and initialises basic vty and vty_io structures, setting the
+ * given type.
+ *
+ * Note that where is not setting up a vty_sock, this *may* be called from
+ * any thread.
+ *
+ * NB: may not create a VTY_CONFIG_WRITE type vty directly
+ *
+ * see: vty_open_config_write() and vty_close_config_write()
+ *
+ * NB: the sock_fd *must* be valid for VTY_TERM and VTY_SHELL_SERV.
+ * (So MUST be in the CLI thread to set those up !)
+ *
+ * the sock_fd is ignored for everything else.
+ *
+ * Returns: new vty
+ */
+extern struct vty *
+uty_new(enum vty_type type, int sock_fd)
+{
+ struct vty *vty ;
+ struct vty_io* vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* If this is a VTY_TERM or a VTY_SHELL, place */
+ switch (type)
+ {
+ case VTY_TERM: /* Require fd -- Telnet session */
+ case VTY_SHELL_SERV: /* Require fd -- Unix socket */
+ assert(sock_fd >= 0) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ zabort("may not make a new VTY_CONFIG_WRITE VTY") ;
+ break ;
+
+ case VTY_CONFIG_READ:
+ case VTY_STDOUT:
+ case VTY_STDERR:
+ case VTY_SHELL:
+ sock_fd = -1 ; /* No fd -- output to stdout/stderr */
+ break ;
+
+ default:
+ zabort("unknown VTY type") ;
+ } ;
+
+ /* Basic allocation */
+ vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+ vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
+
+ vty->vio = vio ;
+ vio->vty = vty ;
+
+ /* Zeroising the vty_io structure has set:
+ *
+ * name = NULL -- no name, yet
+ *
+ * vio_list both pointers NULL
+ * mon_list both pointers NULL
+ *
+ * half_closed = 0 -- NOT half closed (important !)
+ * closed = 0 -- NOT closed (important !)
+ * close_reason = NULL -- no reason, yet
+ *
+ * real_type = 0 -- not material
+ * file_fd = 0 -- not material
+ * file_error = 0 -- not material
+ *
+ * key_stream = NULL -- no key stream (always empty, at EOF)
+ *
+ * cli_drawn = 0 -- not drawn
+ * cli_dirty = 0 -- not dirty
+ * cli_prompt_len = 0 )
+ * cli_extra_len = 0 ) not material
+ * cli_echo_suppress = 0 )
+ *
+ * cli_prompt_node = 0 -- not material
+ * cli_prompt_set = 0 -- so prompt needs to be constructed
+ *
+ * cli_blocked = 0 -- not blocked
+ * cmd_in_progress = 0 -- no command in progress
+ * cmd_out_enabled = 0 -- command output is disabled
+ * cli_wait_more = 0 -- not waiting for response to "--more--"
+ *
+ * cli_more_enabled = 0 -- not enabled for "--more--"
+ *
+ * cmd_out_done = 0 -- not material
+ *
+ * cli_do = 0 == cli_do_nothing
+ *
+ * cmd_lc = NULL -- no line control
+ *
+ * fail = 0 -- no login failures yet
+ *
+ * hist = empty vector
+ * hp = 0 -- at the beginning
+ * hindex = 0 -- the beginning
+ *
+ * width = 0 -- unknown console width
+ * height = 0 -- unknown console height
+ *
+ * lines = 0 -- no limit
+ * lines_set = 0 -- no explicit setting
+ *
+ * monitor = 0 -- not a monitor
+ * monitor_busy = 0 -- not a busy monitor
+ *
+ * config = 0 -- not holder of "config" mode
+ */
+ confirm(cli_do_nothing == 0) ;
+ confirm(AUTH_NODE == 0) ; /* default node type */
+
+ vio->type = type ;
+
+ /* Zeroising the vty structure has set:
+ *
+ * node = 0 TODO: something better for node value ????
+ * buf = NULL -- no command line, yet
+ * parsed = NULL -- no parsed command, yet
+ * lineno = 0 -- nothing read, yet
+ * index = NULL -- nothing, yet
+ * index_sub = NULL -- nothing, yet
+ */
+ if (type == VTY_TERM)
+ vty->newline = "\n" ; /* line control looks after "\r\n" */
+ else
+ vty->newline = "\n" ;
+
+ /* Initialise the vio_sock, */
+ uty_sock_init_new(&vio->sock, sock_fd, vio) ;
+
+ /* Make sure all buffers etc. are initialised clean and empty.
+ *
+ * Note that no buffers are actually allocated at this stage.
+ */
+ qs_init_new(&vio->cli_prompt_for_node, 0) ;
+
+ qs_init_new(&vio->cl, 0) ;
+ qs_init_new(&vio->clx, 0) ;
+
+ vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
+
+ vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
+
+ /* Place on list of known vio/vty */
+ sdl_push(vio_list_base, vio, vio_list) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_TERM -- ie attached to a telnet session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_term(int sock_fd, union sockunion *su)
+{
+ struct vty *vty ;
+ vty_io vio ;
+ enum vty_readiness ready ;
+
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (VTY_TERM, sock_fd) ;
+ vio = vty->vio ;
+
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+
+ /* Set the socket action functions */
+ if (vty_cli_nexus)
+ {
+ vio->sock.action.read.qnexus = vty_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = vty_timer_qnexus ;
+ }
+ else
+ {
+ vio->sock.action.read.thread = vty_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = vty_timer_thread ;
+ } ;
+
+ /* The text form of the address identifies the VTY */
+ vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
+
+ /* Set the initial node */
+ if (no_password_check)
+ {
+ if (restricted_mode)
+ vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = VIEW_NODE;
+ }
+ else
+ vty->node = AUTH_NODE;
+
+ /* Pick up current timeout setting */
+ vio->sock.v_timeout = vty_timeout_val;
+
+ /* Use global 'lines' setting, as default. May be -1 => unset */
+ vio->lines = host.lines ;
+
+ /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
+ vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
+ uty_set_height(vio) ; /* set initial state */
+
+ /* Initialise the CLI, ready for start-up messages etc. */
+ uty_cli_init(vio) ;
+
+ /* Reject connection if password isn't set, and not "no password" */
+ if ((host.password == NULL) && (host.password_encrypt == NULL)
+ && ! no_password_check)
+ {
+ uty_half_close (vio, "Vty password is not set.");
+ vty = NULL;
+ }
+ else
+ {
+ /* Say hello to the world. */
+ vty_hello (vty);
+
+ if (! no_password_check)
+ uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE);
+ } ;
+
+ /* Now start the CLI and set a suitable state of readiness */
+ ready = uty_cli_start(vio) ;
+ uty_sock_set_readiness(&vio->sock, ready) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_shell_serv(int sock_fd)
+{
+ struct vty *vty ;
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
+ vio = vty->vio ;
+
+ /* Set the action functions */
+ if (vty_cli_nexus)
+ {
+ vio->sock.action.read.qnexus = vtysh_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = NULL ;
+ }
+ else
+ {
+ vio->sock.action.read.thread = vtysh_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = NULL ;
+ } ;
+
+ vty->node = VIEW_NODE;
+
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set/Clear "monitor" state:
+ *
+ * set: if VTY_TERM and not already "monitor" (and write_open !)
+ * clear: if is "monitor"
+ */
+extern void
+uty_set_monitor(vty_io vio, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (on && !vio->monitor)
+ {
+ if ((vio->type == VTY_TERM) && vio->sock.write_open)
+ {
+ vio->monitor = 1 ;
+ sdl_push(vio_monitors_base, vio, mon_list) ;
+ } ;
+ }
+ else if (!on && vio->monitor)
+ {
+ vio->monitor = 0 ;
+ sdl_del(vio_monitors_base, vio, mon_list) ;
+ }
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return "name" of VTY
+ *
+ * For VTY_TERM this is the IP address of the far end of the telnet connection.
+ */
+extern const char*
+uty_get_name(vty_io vio)
+{
+ return (vio->name != NULL) ? vio->name : "?" ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Closing down VTY for reading.
+ *
+ * For VTY_TERM (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input, setting it to "EOF"
+ * * turns off any monitor status !
+ * * drop down to RESTRICTED_NODE
+ *
+ * For VTY_SHELL_SERV (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input
+ * * drop down to RESTRICTED_NODE
+ *
+ * In all cases:
+ *
+ * * place on death watch
+ * * set the vty half_closed
+ * * sets the reason for closing (if any given)
+ *
+ * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
+ * the buffers, the VTY is closed.
+ *
+ * May already have set the vio->close_reason, or can set it now. (Passing a
+ * NULL reason has no effect on any existing posted reason.)
+ */
+extern void
+uty_half_close (vty_io vio, const char* reason)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return ;
+
+ if (reason != NULL)
+ vio->close_reason = reason ;
+
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+ uty_sock_half_close(&vio->sock) ;
+ uty_set_monitor(vio, 0) ;
+
+ /* Discard everything in the keystroke stream and force it to EOF */
+ if (vio->key_stream != NULL)
+ keystroke_stream_set_eof(vio->key_stream) ;
+
+ /* Turn off "--more--" so that all output clears without interruption.
+ *
+ * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
+ */
+ vio->cli_more_enabled = 0 ;
+
+ if (vio->cli_more_wait)
+ uty_cli_exit_more_wait(vio) ;
+
+ /* If a command is not in progress, enable output, which will clear
+ * the output buffer if there is anything there, plus any close reason,
+ * and then close.
+ *
+ * If command is in progress, then this process will start when it
+ * completes.
+ */
+ if (!vio->cmd_in_progress)
+ vio->cmd_out_enabled = 1 ;
+
+ /* Make sure no longer holding the config symbol of power */
+ uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
+
+ /* Log closing of VTY_TERM */
+ if (vio->type == VTY_TERM)
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->sock.fd) ;
+
+ /* Move to the death watch list */
+ sdl_del(vio_list_base, vio, vio_list) ;
+ sdl_push(vio_death_watch, vio, vio_list) ;
+
+ vio->half_closed = 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Closing down VTY.
+ *
+ * Shuts down everything and discards all buffers etc. etc.
+ *
+ * If cmd_in_progress, cannot complete the process -- but sets the closed
+ * flag.
+ *
+ * Can call vty_close() any number of times.
+ *
+ * The vty structure is placed on death watch, which will finally free the
+ * structure once no longer cmd_in_progress.
+ */
+extern void
+uty_close (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ /* Empty all the output buffers */
+ vio_fifo_reset_keep(&vio->cli_obuf) ;
+ vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ vio->cmd_lc = vio_lc_reset_free(vio->cmd_lc) ;
+
+ /* If not already closed, close. */
+ if (!vio->closed)
+ {
+ uty_half_close(vio, NULL) ; /* place on death watch -- if not
+ already done */
+ if (vio->type == VTY_TERM)
+ uty_cli_close(vio) ; /* tell the CLI to stop */
+
+ vio->closed = 1 ; /* now closed (stop uty_write()
+ from recursing) */
+
+ if (vio->sock.write_open)
+ uty_write(vio) ; /* last gasp attempt */
+
+ uty_sock_close(&vio->sock) ;
+
+ } ;
+
+ /* Nothing more should happen, so can now release almost everything,
+ * the exceptions being the things that are related to a cmd_in_progress.
+ *
+ * All writing to buffers is suppressed, and as the sock has been closed,
+ * there will be no more read_ready or write_ready events.
+ */
+ if (vio->name != NULL)
+ XFREE(MTYPE_VTY_NAME, vio->name) ;
+
+ vio->key_stream = keystroke_stream_free(vio->key_stream) ;
+
+ qs_free_body(&vio->cli_prompt_for_node) ;
+ qs_free_body(&vio->cl) ;
+
+ {
+ qstring line ;
+ while ((line = vector_ream_keep(&vio->hist)) != NULL)
+ qs_reset_free(line) ;
+ } ;
+
+ /* The final stage cannot be completed if cmd_in_progress.
+ *
+ * The clx is pointed at by vty->buf -- containing the current command.
+ *
+ * Once everything is released, can take the vty off death watch, and
+ * release the vio and the vty.
+ */
+ if (!vio->cmd_in_progress)
+ {
+ qs_free_body(&vio->clx) ;
+ vio->vty->buf = NULL ;
+ } ;
+} ;
+
+/*==============================================================================
+ * For writing configuration file by command, temporarily redirect output to
+ * an actual file.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set the given fd as the VTY_FILE output.
+ */
+extern void
+vty_open_config_write(struct vty* vty, int fd)
+{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
+
+ vio->real_type = vio->type ;
+
+ vio->type = VTY_CONFIG_WRITE ;
+ vio->file_fd = fd ;
+ vio->file_error = 0 ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away configuration file stuff -- all or just the full lump(s).
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see vio->file_error
+ */
+static int
+uty_config_write(vty_io vio, bool all)
+{
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->file_error == 0)
+ {
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
+
+ if (ret < 0)
+ vio->file_error = errno ;
+ }
+ else
+ ret = -1 ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away any pending stuff, and return the VTY to normal.
+ */
+extern int
+vty_close_config_write(struct vty* vty)
+{
+ vty_io vio ;
+ int err ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
+
+ uty_config_write(vio, true) ; /* write all that is left */
+
+ err = vio->file_error ;
+
+ vio->type = vio->real_type ;
+ vio->file_fd = -1 ;
+ vio->file_error = 0 ;
+
+ VTY_UNLOCK() ;
+
+ return err ;
+} ;
+
+/*==============================================================================
+ * vio_sock level operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a new vio_sock structure.
+ *
+ * Requires that: the vio_sock structure is not currently in use.
+ *
+ * if fd >= 0 then: sock is open and ready read and write
+ * otherwise: sock is not open
+ *
+ * there are no errors, yet.
+ *
+ * Sets timeout to no timeout at all -- timeout is optional.
+ *
+ * NB: MUST be in the CLI thread if the fd is >= 0 !
+ */
+static void
+uty_sock_init_new(vio_sock sock, int fd, void* info)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (fd >= 0)
+ VTY_ASSERT_CLI_THREAD() ;
+
+ memset(sock, 0, sizeof(struct vio_sock)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * action = all the actions set NULL
+ *
+ * error_seen = 0 -- no error, yet
+ *
+ * qf = NULL -- no qfile, yet
+ * t_read = NULL ) no threads, yet
+ * t_write = NULL )
+ *
+ * v_timeout = 0 -- no timeout set
+ * timer_runing = 0 -- not running, yet
+ * t_timer = NULL -- no timer thread, yet
+ * qtr = NULL -- no qtimer, yet
+ */
+ sock->fd = fd ;
+ sock->info = info ;
+
+ sock->read_open = (fd >= 0) ;
+ sock->write_open = (fd >= 0) ;
+
+ if ((fd >= 0) && vty_cli_nexus)
+ {
+ sock->qf = qps_file_init_new(NULL, NULL);
+ qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Restart the timer.
+ *
+ * If a timeout time is set, then start or restart the timer with that value.
+ *
+ * If no timeout time is set, and the timer is running, unset it.
+ */
+static void
+uty_sock_restart_timer(vio_sock sock)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->v_timeout != 0)
+ {
+ assert(sock->action.timer.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ if (sock->qtr == NULL) /* allocate qtr if required */
+ sock->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ NULL, sock->info) ;
+ qtimer_set(sock->qtr, qt_add_monotonic(QTIME(sock->v_timeout)),
+ sock->action.timer.qnexus) ;
+ }
+ else
+ {
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer);
+ sock->t_timer = thread_add_timer (vty_master,
+ sock->action.timer.thread, sock->info, sock->v_timeout) ;
+ } ;
+
+ sock->timer_running = 1 ;
+ }
+ else if (sock->timer_running)
+ {
+ if (vty_cli_nexus)
+ {
+ if (sock->qtr != NULL)
+ qtimer_unset(sock->qtr) ;
+ }
+ else
+ {
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer) ;
+ } ;
+
+ sock->timer_running = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read on/off
+ *
+ * Returns: the on/off state set
+ */
+static bool
+uty_sock_set_read(vio_sock sock, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->fd < 0)
+ return 0 ;
+
+ if (on)
+ {
+ assert(sock->action.read.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
+ else
+ {
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_read) ;
+
+ sock->t_read = thread_add_read(vty_master,
+ sock->action.read.thread, sock->info, sock->fd) ;
+ } ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ qps_disable_modes(sock->qf, qps_read_mbit) ;
+ else
+ {
+ if (sock->t_read != NULL)
+ thread_cancel (sock->t_read) ;
+ } ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set write on/off
+ *
+ * Returns: the on/off state set
+ */
+static bool
+uty_sock_set_write(vio_sock sock, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->fd < 0)
+ return 0 ;
+
+ if (on)
+ {
+ assert(sock->action.write.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
+ else
+ {
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
+
+ sock->t_write = thread_add_write(vty_master,
+ sock->action.write.thread, sock->info, sock->fd) ;
+ } ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ qps_disable_modes(sock->qf, qps_write_mbit) ;
+ else
+ {
+ if (sock->t_write != NULL)
+ thread_cancel (sock->t_write) ;
+ } ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VTY_TERM
+ *
+ * Note that for VTY_TERM, set only one of read or write, and sets write for
+ * preference.
+ */
+extern void
+uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ uty_sock_set_read(sock, (ready == read_ready)) ;
+ uty_sock_set_write(sock, (ready >= write_ready)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set a new timer value.
+ */
+extern void
+uty_sock_set_timer(vio_sock sock, unsigned long timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ sock->v_timeout = timeout ;
+ if (sock->timer_running)
+ uty_sock_restart_timer(sock) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vty sock for reading.
+ *
+ * Sets timer to timeout for clearing any pending output.
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
+ */
+static void
+uty_sock_half_close(vio_sock sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ sock->read_open = 0 ; /* make sure */
+
+ if (sock->fd < 0)
+ return ; /* nothing more if no socket */
+
+ VTY_ASSERT_CLI_THREAD() ;
+
+ shutdown(sock->fd, SHUT_RD) ; /* actual half close */
+
+ uty_sock_set_read(sock, off) ;
+ uty_sock_set_write(sock, on) ;
+ sock->v_timeout = 30 ; /* for output to clear */
+ uty_sock_restart_timer(sock) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vio_sock, completely -- shut down any timer.
+ *
+ * Structure is cleared of everything except the last error !
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
+ */
+static void
+uty_sock_close(vio_sock sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ sock->read_open = 0 ; /* make sure */
+ sock->write_open = 0 ;
+
+ if (sock->fd < 0)
+ {
+ assert( (sock->qf == NULL)
+ && (sock->qtr == NULL)
+ && (sock->t_read == NULL)
+ && (sock->t_write == NULL)
+ && (sock->t_timer == NULL) ) ;
+ return ; /* no more to be done here */
+ } ;
+
+ VTY_ASSERT_CLI_THREAD() ;
+ close(sock->fd) ;
+
+ if (vty_cli_nexus)
+ {
+ assert((sock->qf != NULL) && (sock->fd == qps_file_fd(sock->qf))) ;
+ qps_remove_file(sock->qf) ;
+ qps_file_free(sock->qf) ;
+ sock->qf = NULL ;
+ } ;
+
+ sock->fd = -1 ;
+
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_read) ;
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
+
+ sock->t_read = NULL ;
+ sock->t_write = NULL ;
+
+ sock->info = NULL ;
+ sock->action.read.anon = NULL ;
+ sock->action.write.anon = NULL ;
+ sock->action.timer.anon = NULL ;
+
+ if (sock->qtr != NULL)
+ qtimer_free(sock->qtr) ;
+ if (sock->t_timer != NULL)
+ thread_cancel(sock->t_timer) ;
+
+ sock->v_timeout = 0 ;
+ sock->qtr = NULL ;
+ sock->t_timer = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dealing with an I/O error on VTY socket
+ *
+ * If this is the first error for this VTY, produce suitable log message.
+ *
+ * If is a "monitor", turn that off, *before* issuing log message.
+ */
+static int
+uty_sock_error(vty_io vio, const char* what)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* can no longer be a monitor ! *before* any logging ! */
+ uty_set_monitor(vio, 0) ;
+
+ /* if this is the first error, log it */
+ if (vio->sock.error_seen == 0)
+ {
+ const char* type ;
+ switch (vio->type)
+ {
+ case VTY_TERM:
+ type = "VTY Terminal" ;
+ break ;
+ case VTY_SHELL_SERV:
+ type = "VTY Shell Server" ;
+ break ;
+ default:
+ zabort("unknown VTY type for uty_sock_error()") ;
+ } ;
+
+ vio->sock.error_seen = errno ;
+ uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
+ type, what, vio->sock.fd, errtoa(vio->sock.error_seen, 0).str) ;
+ } ;
+
+ return -1 ;
+} ;
+
+/*==============================================================================
+ * Readiness and the VTY_TERM type VTY.
+ *
+ * For VTY_TERM the driving force is write ready. This is used to prompt the
+ * VTY_TERM when there is outstanding output (obviously), but also if there
+ * is buffered input in the keystroke stream.
+ *
+ * The VTY_TERM uses read ready only when it doesn't set write ready. Does
+ * not set both at once.
+ *
+ * So there is only one, common, uty_ready function, which:
+ *
+ * 1. attempts to clear any output it can.
+ *
+ * The state of the output affects the CLI, so must always do this before
+ * before invoking the CLI.
+ *
+ * If this write enters the "--more--" state, then will have tried to
+ * write away the prompt.
+ *
+ * 2. invokes the CLI
+ *
+ * Which will do either the standard CLI stuff or the special "--more--"
+ * stuff.
+ *
+ * 3. attempts to write any output there now is.
+ *
+ * If the CLI generated new output, as much as possible is written away
+ * now.
+ *
+ * If this write enters the "--more--" state, then it returns now_ready,
+ * if the prompt was written away, which loops back to the CLI.
+ *
+ * Note that this is arranging:
+ *
+ * a. to write away the "--more--" prompt as soon as the tranche of output to
+ * which it refers, completes
+ *
+ * b. to enter the cli_more_wait CLI for the first time immediately after the
+ * "--more--" prompt is written away.
+ *
+ * The loop limits itself to one trache of command output each time.
+ *
+ * Resets the timer because something happened.
+ */
+static void
+uty_ready(vty_io vio)
+{
+ enum vty_readiness ready ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio->cmd_out_done = 0 ; /* not done any command output yet */
+
+ uty_write(vio) ; /* try to clear outstanding stuff */
+ do
+ {
+ ready = uty_cli(vio) ; /* do any CLI work... */
+ ready |= uty_write(vio) ; /* ...and any output that generates */
+ } while (ready >= now_ready) ;
+
+ uty_sock_set_readiness(&vio->sock, ready) ;
+ uty_sock_restart_timer(&vio->sock) ;
+} ;
+
+/*==============================================================================
+ * Reading from VTY_TERM.
+ *
+ * The select/pselect call-back ends up in uty_read_ready().
+ *
+ * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
+ * current CLI.
+ */
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking CLI
+ */
+static void
+vty_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ uty_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking CLI
+ */
+static int
+vty_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_read = NULL ; /* implicitly */
+ uty_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the keystroke stream
+ *
+ * Steal keystroke if required -- see keystroke_input()
+ *
+ * Returns: 0 => nothing available
+ * > 0 => read at least one byte
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+uty_read (vty_io vio, keystroke steal)
+{
+ unsigned char buf[500] ;
+ int get ;
+
+ if (!vio->sock.read_open)
+ return -1 ; /* at EOF if not open */
+
+ get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ if (get >= 0)
+ keystroke_input(vio->key_stream, buf, get, steal) ;
+ else if (get < 0)
+ {
+ if (get == -1)
+ uty_sock_error(vio, "read") ;
+
+ vio->sock.read_open = 0 ;
+ keystroke_input(vio->key_stream, NULL, 0, steal) ;
+
+ get = -1 ;
+ } ;
+
+ return get ;
+} ;
+
+/*==============================================================================
+ * The write sock action for VTY_TERM type VTY
+ *
+ * There are two sets of buffering:
+ *
+ * cli -- command line -- which reflects the status of the command line
+ *
+ * cmd -- command output -- which is written to the file only while
+ * cmd_out_enabled.
+ *
+ * The cli output takes precedence.
+ *
+ * Output of command stuff is subject to line_control, and may go through the
+ * "--more--" mechanism.
+ */
+
+static int uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
+static int uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to write -> try to empty buffers
+ */
+static void
+vty_write_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info ;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ uty_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to write -> try to empty buffers
+ */
+static int
+vty_write_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_write = NULL; /* implicitly */
+ uty_ready(vio) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible of what there is.
+ *
+ * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
+ * empty.
+ *
+ * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
+ * and all output instantly successful.
+ *
+ * Sets write on if prevented from writing everything available for output
+ * by write() threatening to block.
+ *
+ * Returns: write_ready if should now set write on
+ * now_ready if should loop back and try again
+ * not_ready otherwise
+ */
+static enum vty_readiness
+uty_write(vty_io vio)
+{
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ ret = -1 ;
+ while (vio->sock.write_open)
+ {
+ /* Any outstanding line control output takes precedence */
+ if (vio->cmd_lc != NULL)
+ {
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ if (ret != 0)
+ break ;
+ }
+
+ /* Next: empty out the cli output */
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret != 0)
+ break ;
+
+ /* Finished now if not allowed to progress the command stuff */
+ if (!vio->cmd_out_enabled)
+ return not_ready ; /* done all can do */
+
+ /* Last: if there is something in the command buffer, do that */
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ {
+ if (vio->cmd_out_done)
+ break ; /* ...but not if done once */
+
+ vio->cmd_out_done = 1 ; /* done this once */
+
+ assert(!vio->cli_more_wait) ;
+
+ if (vio->cmd_lc != NULL)
+ ret = uty_write_fifo_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ else
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->sock.fd, true) ;
+
+ /* If moved into "--more--" state@
+ *
+ * * the "--more--" prompt is ready to be written, so do that now
+ *
+ * * if that completes, then want to run the CLI *now* to perform the
+ * first stage of the "--more--" process.
+ */
+ if (vio->cli_more_wait)
+ {
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret == 0)
+ return now_ready ;
+ } ;
+
+ if (ret != 0)
+ break ;
+ }
+
+ /* Exciting stuff: there is nothing left to output...
+ *
+ * ... watch out for half closed state.
+ */
+ if (vio->half_closed)
+ {
+ if (vio->close_reason != NULL)
+ {
+ vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
+
+ struct vty* vty = vio->vty ;
+ if (vio->cli_drawn || vio->cli_dirty)
+ vty_out(vty, VTY_NEWLINE) ;
+ vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
+
+ vio->cmd_in_progress = 0 ;
+
+ vio->close_reason = NULL ; /* MUST discard now... */
+ continue ; /* ... and write away */
+ } ;
+
+ if (!vio->closed) /* avoid recursion */
+ uty_close(vio) ;
+
+ return not_ready ; /* it's all over */
+ } ;
+
+ /* For VTY_TERM: if the command line is not drawn, now is a good
+ * time to do that.
+ */
+ if (vio->type == VTY_TERM)
+ if (uty_cli_draw_if_required(vio))
+ continue ; /* do that now. */
+
+ /* There really is nothing left to output */
+ return not_ready ;
+ } ;
+
+ /* Arrives here if there is more to do, or failed (or was !write_open) */
+
+ if (ret >= 0)
+ return write_ready ;
+
+ /* If is write_open, then report the error
+ *
+ * If still read_open, let the reader pick up and report the error, when it
+ * has finished anything it has buffered.
+ */
+ if (vio->sock.write_open)
+ {
+ if (!vio->sock.read_open)
+ uty_sock_error(vio, "write") ;
+
+ vio->sock.write_open = 0 ; /* crash close write */
+ } ;
+
+ /* For whatever reason, is no longer write_open -- clear all buffers.
+ */
+ vio_fifo_clear(&vio->cli_obuf) ; /* throw away cli stuff */
+ uty_out_clear(vio) ; /* throw away cmd stuff */
+
+ vio->close_reason = NULL ; /* too late for this */
+
+ return not_ready ; /* NB: NOT blocked by I/O */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible -- for "monitor" output.
+ *
+ * Outputs only:
+ *
+ * a. outstanding line control stuff.
+ *
+ * b. contents of CLI buffer
+ *
+ * And:
+ *
+ * a. does not report any errors.
+ *
+ * b. does not change anything except the state of the buffers.
+ *
+ * In particular, for the qpthreaded world, does not attempt to change
+ * the state of the qfile or any other "thread private" structures.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed (or !write_open)
+ */
+static int
+uty_write_monitor(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vio->sock.write_open)
+ return -1 ;
+
+ if (vio->cmd_lc != NULL)
+ {
+ int ret ;
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+
+ if (ret != 0)
+ return ret ;
+ } ;
+
+ return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write the given FIFO to output -- subject to possible line control.
+ *
+ * Note that even if no "--more--" is set, will have set some height, so
+ * that does not attempt to empty the FIFO completely all in one go.
+ *
+ * If the line control becomes "paused", it is time to enter "--more--" state
+ * -- unless the FIFO is empty (or "--more--" is not enabled).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * Returns: > 0 => blocked or completed one tranche
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+{
+ int ret ;
+ char* src ;
+ size_t have ;
+
+ /* Collect another line_control height's worth of output.
+ *
+ * Expect the line control to be empty at this point, but it does not have
+ * to be.
+ */
+ vio_lc_set_pause(lc) ; /* clears lc->paused */
+
+ src = vio_fifo_get_rdr(vf, &have) ;
+
+ while ((src != NULL) && (!lc->paused))
+ {
+ size_t take ;
+ take = vio_lc_append(lc, src, have) ;
+ src = vio_fifo_step_rdr(vf, &have, take) ;
+ } ;
+
+ vio->cli_dirty = (lc->col != 0) ;
+
+ /* Write the contents of the line control */
+ ret = uty_write_lc(vio, vf, lc) ;
+
+ if (ret < 0)
+ return ret ; /* give up now if failed. */
+
+ if ((ret == 0) && vio_fifo_empty(vf))
+ return 0 ; /* FIFO and line control empty */
+
+ /* If should now do "--more--", now is the time to prepare for that.
+ *
+ * Entering more state issues a new prompt in the CLI buffer, which can
+ * be written once line control write completes.
+ *
+ * The "--more--" cli will not do anything until the CLI buffer has
+ * cleared.
+ */
+ if (lc->paused && vio->cli_more_enabled)
+ uty_cli_enter_more_wait(vio) ;
+
+ return 1 ; /* FIFO or line control, not empty */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write contents of line control (if any).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * NB: does nothing other than write() and buffer management.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+{
+ int ret ;
+
+ ret = vio_lc_write_nb(vio->sock.fd, lc) ;
+
+ if (ret <= 0)
+ vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start command output -- clears down the line control.
+ *
+ * Requires that that current line is empty -- restarts the line control
+ * on the basis that is at column 0.
+ */
+extern void
+uty_cmd_output_start(vty_io vio)
+{
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the effective height for line control (if any)
+ *
+ * If using line_control, may enable the "--more--" output handling.
+ *
+ * If not, want some limit on the amount of stuff output at a time.
+ *
+ * Sets the line control window width and height.
+ * Sets cli_more_enabled if "--more--" is enabled.
+ */
+extern void
+uty_set_height(vty_io vio)
+{
+ bool on ;
+
+ on = 0 ; /* default state */
+
+ if ((vio->cmd_lc != NULL) && !vio->half_closed)
+ {
+ int height ;
+
+ height = 0 ; /* default state */
+
+ if ((vio->width) != 0)
+ {
+ /* If window size is known, use lines or given height */
+ if (vio->lines >= 0)
+ height = vio->lines ;
+ else
+ {
+ /* Window height, leaving one line from previous "page"
+ * and one line for the "--more--" -- if at all possible
+ */
+ height = vio->height - 2 ;
+ if (height < 1)
+ height = 1 ;
+ } ;
+ }
+ else
+ {
+ /* If window size not known, use lines if that has been set
+ * explicitly for this terminal.
+ */
+ if (vio->lines_set)
+ height = vio->lines ;
+ } ;
+
+ if (height > 0)
+ on = 1 ; /* have a defined height */
+ else
+ height = 200 ; /* but no "--more--" */
+
+ vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
+ } ;
+
+ vio->cli_more_enabled = on ;
+} ;
+
+/*==============================================================================
+ * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ */
+
+/*------------------------------------------------------------------------------
+ * Timer has expired.
+ *
+ * If half_closed, then this is curtains -- have waited long enough !
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static void
+uty_timer_expired (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return uty_close(vio) ; /* curtains */
+
+ uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
+ } ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: deal with timer timeout.
+ */
+static void
+vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when)
+{
+ vty_io vio = timer_info ;
+
+ VTY_LOCK() ;
+
+ uty_timer_expired(vio);
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: deal with timer timeout.
+ */
+static int
+vty_timer_thread (struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ vio->sock.t_timer = NULL ; /* implicitly */
+
+ uty_timer_expired(vio) ;
+
+ VTY_UNLOCK() ;
+ return 0;
+}
+
+/*==============================================================================
+ * VTY Listener(s)
+ *
+ * Have listeners for VTY_TERM and VTY_SHELL_SERV types of VTY.
+ */
+
+typedef struct vty_listener* vty_listener ;
+
+struct vty_listener
+{
+ vty_listener next ; /* ssl type list */
+
+ enum vty_type type ;
+
+ struct vio_sock sock ;
+};
+
+/* List of listeners so can tidy up. */
+static vty_listener vty_listeners_list = NULL ;
+
+/* Prototypes for listener stuff */
+static int uty_serv_sock_addrinfo (const char *hostname, unsigned short port) ;
+static int uty_serv_sock(const char* addr, unsigned short port) ;
+static int uty_serv_sock_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port) ;
+static int uty_serv_vtysh(const char *path) ;
+static int vty_accept_thread(struct thread *thread) ;
+static void vty_accept_qnexus(qps_file qf, void* listener) ;
+static int uty_accept(vty_listener listener, int listen_sock) ;
+static int uty_accept_term(vty_listener listener) ;
+static int uty_accept_shell_serv (vty_listener listener) ;
+
+static void uty_serv_start_listener(int fd, enum vty_type type) ;
+
+/*------------------------------------------------------------------------------
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define VTY_USE_ADDRINFO 1
+#else
+# define VTY_USE_ADDRINFO 0
+#endif
+
+/*------------------------------------------------------------------------------
+ * Open VTY listener(s)
+ *
+ * addr -- address ) to listen for VTY_TERM connections
+ * port -- port )
+ * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ */
+extern void
+uty_open_listeners(const char *addr, unsigned short port, const char *path)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ /* If port is set to 0, do not listen on TCP/IP at all! */
+ if (port)
+ {
+ int n ;
+
+ if (VTY_USE_ADDRINFO)
+ n = uty_serv_sock_addrinfo(addr, port);
+ else
+ n = uty_serv_sock(addr, port);
+
+ if (n == 0)
+ uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
+ }
+
+ /* If want to listen for vtysh, set up listener now */
+ if (VTYSH_ENABLED && (path != NULL))
+ uty_serv_vtysh(path) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VTY listener
+ *
+ * addr -- address ) to listen for VTY_TERM connections
+ * port -- port )
+ * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ */
+extern void
+uty_close_listeners(void)
+{
+ vty_listener listener ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
+ {
+ uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
+ XFREE(MTYPE_VTY, listener) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- using getaddrinfo().
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
+{
+#if VTY_USE_ADDRINFO
+
+# ifndef HAVE_IPV6
+# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
+# endif
+
+ int ret;
+ int n ;
+ struct addrinfo req;
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ char port_str[16];
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Want to listen, TCP-wise, on all available address families, on the
+ * given port.
+ */
+ memset (&req, 0, sizeof (struct addrinfo));
+ req.ai_flags = AI_PASSIVE;
+ req.ai_family = AF_UNSPEC;
+ req.ai_socktype = SOCK_STREAM;
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ ret = getaddrinfo (hostname, port_str, &req, &ainfo);
+
+ if (ret != 0)
+ {
+ fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
+ exit (1);
+ }
+
+ /* Open up sockets on all AF_INET and AF_INET6 addresses */
+ ainfo_save = ainfo;
+
+ n = 0 ;
+ do
+ {
+ if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
+ continue;
+
+ assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
+
+ ret = uty_serv_sock_open(ainfo->ai_family, ainfo->ai_socktype,
+ ainfo->ai_protocol, ainfo->ai_addr, port) ;
+ if (ret >= 0)
+ ++n ;
+ }
+ while ((ainfo = ainfo->ai_next) != NULL);
+
+ freeaddrinfo (ainfo_save);
+
+ return n ;
+
+#else
+ zabort("uty_serv_sock_addrinfo not implemented") ;
+#endif /* VTY_USE_ADDRINFO */
+}
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_serv_sock(const char* addr, unsigned short port)
+{
+ int ret;
+ int n ;
+ union sockunion su_addr ;
+ struct sockaddr* sa ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ n = 0 ; /* nothing opened yet */
+
+ /* If have an address, see what kind and whether valid */
+ sa = NULL ;
+
+ if (addr != NULL)
+ {
+ ret = str2sockunion (addr, &su_addr) ;
+ if (ret == 0)
+ sa = &su_addr.sa ;
+ else
+ uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
+ } ;
+
+ /* Try for AF_INET */
+ ret = uty_serv_sock_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+
+#if HAVE_IPV6
+ /* Try for AF_INET6 */
+ ret = uty_serv_sock_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+#endif
+
+ /* If not used the address... something wrong */
+ if (sa != NULL)
+ zlog_err("could not use address %s, to listen for VTY", addr);
+
+ /* Done */
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_TERM listener socket.
+ *
+ * The sockaddr 'sa' may be NULL or of a different address family, in which
+ * case "any" address is used.
+ *
+ * If the sockaddr 'sa' is used, only the address portion is used.
+ *
+ * Returns: < 0 => failed
+ * == 0 => OK -- did not use the sockaddr 'sa'.
+ * > 1 => OK -- and did use the sockaddr 'sa'
+ */
+static int
+uty_serv_sock_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port)
+{
+ union sockunion su[1] ;
+ int sock_fd ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Is there an address and is it for this family ? */
+ if ((sa != NULL) || (sa->sa_family == family))
+ /* Set up sockunion containing required family and address */
+ sockunion_new_sockaddr(su, sa) ;
+ else
+ {
+ /* no address or wrong family -- set up empty sockunion of
+ * required family */
+ sockunion_init_new(su, family) ;
+ sa = NULL ;
+ } ;
+
+ /* Open the socket and set its properties */
+ sock_fd = sockunion_socket(su, type, protocol) ;
+ if (sock_fd < 0)
+ return -1 ;
+
+ ret = setsockopt_reuseaddr (sock_fd);
+
+ if (ret >= 0)
+ ret = setsockopt_reuseport (sock_fd);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock_fd);
+
+#ifdef HAVE_IPV6
+ /* Want only IPv6 on AF_INET6 socket (not mapped addresses)
+ *
+ * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
+ * attempt to bind to :: after binding to 0.0.0.0.
+ */
+ if ((ret >= 0) && (family == AF_INET6))
+ ret = setsockopt_ipv6_v6only(sock_fd) ;
+#endif
+
+ if (ret >= 0)
+ ret = sockunion_bind (sock_fd, su, port, (sa == NULL)) ;
+
+ if (ret >= 0)
+ ret = sockunion_listen (sock_fd, 3);
+
+ if (ret < 0)
+ {
+ close (sock_fd);
+ return -1 ;
+ }
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock_fd, VTY_TERM) ;
+
+ /* Return OK and signal whether used address or not */
+ return (sa != NULL) ? 1 : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_SHEL_SERV listener socket (UNIX domain).
+ *
+ * Returns: < 0 => failed
+ * >= 0 => OK
+ */
+static int
+uty_serv_vtysh(const char *path)
+{
+ int ret;
+ int sock, sa_len, path_len ;
+ struct sockaddr_un sa_un ;
+ mode_t old_mask;
+ struct zprivs_ids_t ids;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* worry about the path length */
+ path_len = strlen(path) + 1 ;
+ if (path_len >= (int)sizeof(sa_un.sun_path))
+ {
+ uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
+ return -1 ;
+ } ;
+
+ /* First of all, unlink existing socket */
+ unlink (path);
+
+ /* Make UNIX domain socket. */
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
+ errtoa(errno, 0).str) ;
+ return -1 ;
+ }
+
+ /* Bind to the required path */
+ memset (&sa_un, 0, sizeof(sa_un));
+ sa_un.sun_family = AF_UNIX;
+ strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
+
+ sa_len = SUN_LEN(&sa_un) ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ sa_un.sun_len = sa_len ;
+#endif
+
+ old_mask = umask (0007);
+
+ ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ {
+ ret = listen (sock, 5);
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
+ errtoa(errno, 0).str) ;
+ } ;
+
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ errtoa(errno, 0).str) ;
+ }
+
+ umask (old_mask);
+
+ /* Give up now if failed along the way */
+ if (ret < 0)
+ {
+ close (sock) ;
+ return -1 ;
+ } ;
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Socket is open -- set a VTY listener going
+ *
+ * Note that the vyt_listener structure is passed to the accept action function.
+ */
+static void
+uty_serv_start_listener(int fd, enum vty_type type)
+{
+ vty_listener listener ;
+
+ listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
+
+ ssl_push(vty_listeners_list, listener, next) ;
+ uty_sock_init_new(&listener->sock, fd, listener) ;
+
+ listener->type = type ;
+
+ if (vty_cli_nexus)
+ listener->sock.action.read.qnexus = vty_accept_qnexus ;
+ else
+ listener->sock.action.read.thread = vty_accept_thread ;
+
+ uty_sock_set_read(&listener->sock, on) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action for the thread world -- create and dispatch VTY
+ */
+static int
+vty_accept_thread(struct thread *thread)
+{
+ vty_listener listener = THREAD_ARG(thread) ;
+ int result ;
+
+ VTY_LOCK() ;
+
+ result = uty_accept(listener, THREAD_FD(thread));
+
+ uty_sock_set_read(&listener->sock, on) ;
+
+ VTY_UNLOCK() ;
+ return result ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action for the qnexus world -- create and dispatch VTY
+ */
+static void
+vty_accept_qnexus(qps_file qf, void* listener)
+{
+ VTY_LOCK() ;
+
+ uty_accept(listener, qf->fd);
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
+ */
+static int
+uty_accept(vty_listener listener, int listen_sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(listener->sock.fd == listen_sock) ;
+
+ switch (listener->type)
+ {
+ case VTY_TERM:
+ return uty_accept_term(listener) ;
+
+ case VTY_SHELL_SERV:
+ return uty_accept_shell_serv(listener) ;
+
+ default:
+ zabort("unknown vty type") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM
+ */
+static int
+uty_accept_term(vty_listener listener)
+{
+ int sock_fd;
+ union sockunion su;
+ int ret;
+ unsigned int on;
+ struct prefix *p ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* We can handle IPv4 or IPv6 socket. */
+ sockunion_init_new(&su, AF_UNSPEC) ;
+
+ sock_fd = sockunion_accept (listener->sock.fd, &su);
+
+ if (sock_fd < 0)
+ {
+ if (sock_fd == -1)
+ uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
+ errtoa(errno, 0).str) ;
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return -1 ;
+ } ;
+
+ /* New socket is open... worry about access lists */
+ p = sockunion2hostprefix (&su);
+ ret = 0 ; /* so far, so good */
+
+ if ((p->family == AF_INET) && vty_accesslist_name)
+ {
+ /* VTY's accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+
+#ifdef HAVE_IPV6
+ if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ {
+ /* VTY's ipv6 accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+#endif /* HAVE_IPV6 */
+
+ prefix_free (p);
+
+ if (ret != 0)
+ {
+ uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
+ close (sock_fd);
+ return 0;
+ } ;
+
+ /* Final options (optional) */
+ on = 1 ;
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof (on));
+ if (ret < 0)
+ uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
+ sock_fd, errtoa(errno, 0).str) ;
+
+ /* All set -- create the VTY_TERM */
+ uty_new_term(sock_fd, &su);
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
+ sock_fd) ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_SHELL_SERV
+ */
+static int
+uty_accept_shell_serv (vty_listener listener)
+{
+ int sock_fd ;
+ int ret ;
+ int client_len ;
+ struct sockaddr_un client ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ client_len = sizeof(client);
+ memset (&client, 0, client_len);
+
+ sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
+ (socklen_t *) &client_len) ;
+
+ if (sock_fd < 0)
+ {
+ uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
+ errtoa(errno, 0).str) ;
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return -1 ;
+ } ;
+
+ /* All set -- create the VTY_SHELL_SERV */
+ if (VTYSH_DEBUG)
+ printf ("VTY shell accept\n");
+
+ uty_new_shell_serv(sock_fd) ;
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock_fd);
+ return 0;
+}
+
+/*==============================================================================
+ * Reading from the VTY_SHELL_SERV type sock.
+ *
+ * The select/pselect call-back ends up in utysh_read_ready().
+ */
+
+/*------------------------------------------------------------------------------
+ * Ready to read -> kicking the "SHELL_SERV CLI"
+ *
+ * End up here when there is something ready to be read.
+ *
+ * Will also end up here if an error has occurred, the other end has closed,
+ * this end has half closed, etc. This fact is used to kick the CLI even when
+ * there is no data to be read.
+ *
+ * Note that nothing is actually read here -- reading is done in the CLI itself,
+ * if required.
+ *
+ * The CLI decides whether to re-enable read, or enable write, or both.
+ */
+static void
+utysh_read_ready(vty_io vio)
+{
+ uty_sock_set_read(&vio->sock, off) ;
+
+ /* TODO: need minimal "CLI" for VTY_SHELL_SERV
+ * NB: when output from command is flushed out, must append the
+ * following four bytes: '\0' '\0' '\0' <ret>
+ * Where <ret> is the command return code.
+ */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static void
+vtysh_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ utysh_read_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static int
+vtysh_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_read = NULL ; /* implicitly */
+ utysh_read_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the command line buffer
+ *
+ * Lines coming in are terminated by '\0'.
+ *
+ * Assumes that the incoming command line is empty or otherwise incomplete.
+ *
+ * Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
+ * when get '\0' or empties the "buf".
+ *
+ * When empties "buf", reads a lump from the sock.
+ *
+ * Returns: 0 => command line is incomplete
+ * 1 => have a complete command line
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+utysh_read (vty_io vio, qstring cl, qstring buf)
+{
+ int get ;
+ char* cp ;
+ char* ep ;
+ size_t have ;
+
+ while (1)
+ {
+ /* process what there is in the buffer */
+ if (buf->len > buf->cp)
+ {
+ cp = qs_cp_char(buf) ;
+ ep = qs_ep_char(buf) ;
+ have = ep - cp ;
+
+ ep = memchr(cp, '\0', have) ;
+ if (ep != NULL)
+ have = ep - cp ; /* have upto, but excluding '\0' */
+
+ if (have > 0) /* take what have */
+ {
+ qs_insert(cl, cp, have) ;
+ cl->cp += have ;
+ buf->cp += have ;
+ } ;
+
+ if (ep != NULL) /* if found '\0' */
+ {
+ qs_term(cl) ; /* '\0' terminate */
+ ++buf->cp ; /* step past it */
+ return 1 ; /* have a complete line <<<<<<<<<<<<< */
+ }
+ } ;
+
+ /* buffer is empty -- try and get some more stuff */
+ assert(buf->len == buf->cp) ;
+
+ if (!vio->sock.read_open)
+ return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
+
+ qs_need(buf, 500) ; /* need a reasonable lump */
+ qs_clear(buf) ; /* set cp = len = 0 */
+
+ get = read_nb(vio->sock.fd, buf->body, buf->size) ;
+ if (get > 0)
+ buf->len = get ;
+ else if (get == 0)
+ return 0 ; /* have an incomplete line <<<<<<<<<<<< */
+ else
+ {
+ if (get == -1)
+ uty_sock_error(vio, "read") ;
+
+ vio->sock.read_open = 0 ;
+
+ return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
+ } ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Output to vty which are set to "monitor".
+ *
+ * This is VERY TRICKY.
+ *
+ * If not running qpthreaded, then the objective is to get the message away
+ * immediately -- do not wish it to be delayed in any way by the thread
+ * system.
+ *
+ * So proceed as follows:
+ *
+ * a. wipe command line -- which adds output to the CLI buffer
+ *
+ * b. write the CLI buffer to the sock and any outstanding line control.
+ *
+ * c. write the monitor output.
+ *
+ * If that does not complete, put the tail end to the CLI buffer.
+ *
+ * d. restore any command line -- which adds output to the CLI buffer
+ *
+ * e. write the CLI buffer to the sock
+ *
+ * If that all succeeds, nothing has changed as far as the VTY stuff is
+ * concerned -- except that possibly some CLI output was sent before it got
+ * round to it.
+ *
+ * Note that step (b) will deal with any output hanging around from an
+ * earlier step (e). If cannot complete that, then does not add fuel to the
+ * fire -- but the message will be discarded.
+ *
+ * If that fails, or does not complete, then can set write on, to signal that
+ * there is some output in the CLI buffer that needs to be sent, or some
+ * error to be dealt with.
+ *
+ * The output should be tidy.
+ *
+ * To cut down the clutter, step (d) is performed only if the command line
+ * is not empty (or if in cli_more_wait). Once a the user has started to enter
+ * a command, the prompt and the command will remain visible.
+ *
+ * When logging an I/O error for a vty that happens to be a monitor, the
+ * monitor-ness has already been turned off. The monitor output code does not
+ * attempt to log any errors, sets write on so that the error will be picked
+ * up that way.
+ *
+ * However, in the event of an assertion failure, it is possible that an
+ * assertion will fail inside the monitor output. The monitor_busy flag
+ * prevents disaster. It is also left set if I/O fails in monitor output, so
+ * will not try to use the monitor again.
+ *
+ * Note that an assertion which is false for all vty monitors will recurse
+ * through all the monitors, setting each one busy, in turn !
+ *
+
+
+ * TODO: sort out write on in the qpthreads world ??
+ *
+ * The problem is that the qpselect structure is designed to be accessed ONLY
+ * within the thread to which it belongs. This makes it impossible for the
+ * monitor output to set/clear read/write on the vty sock... so some way
+ * around this is required.
+ */
+
+/*------------------------------------------------------------------------------
+ * Output logging information to all vty which are set to "monitor".
+ */
+extern void
+uty_log(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va)
+{
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio = sdl_head(vio_monitors_base) ;
+
+ if (vio == NULL)
+ return ; /* go no further if no "monitor" vtys */
+
+ /* Prepare line for output. */
+ uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
+
+ /* write to all known "monitor" vty
+ *
+ */
+ while (vio != NULL)
+ {
+ if (!vio->monitor_busy)
+ {
+ int ret ;
+
+ vio->monitor_busy = 1 ; /* close the door */
+
+ uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
+
+ ret = uty_write_monitor(vio) ;
+ if (ret == 0)
+ {
+ ret = write_nb(vio->sock.fd, ll->line, ll->len) ;
+
+ if (ret >= 0)
+ {
+ ret = uty_cli_post_monitor(vio, ll->line + ret,
+ ll->len - ret) ;
+ if (ret > 0)
+ ret = uty_write_monitor(vio) ;
+ } ;
+ } ;
+
+ if (ret != 0)
+ /* need to prod */ ;
+
+ if (ret >= 0)
+ vio->monitor_busy = 0 ;
+ } ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Async-signal-safe version of vty_log for fixed strings.
+ *
+ * This is last gasp operation.
+ */
+void
+vty_log_fixed (const char *buf, size_t len)
+{
+ vty_io vio ;
+
+ /* Write to all known "monitor" vty
+ *
+ * Forget all the niceties -- about to die in any case.
+ */
+ vio = sdl_head(vio_monitors_base) ;
+ while (vio != NULL)
+ {
+ write(vio->sock.fd, buf, len) ;
+ write(vio->sock.fd, "\r\n", 2) ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
new file mode 100644
index 00000000..19689853
--- /dev/null
+++ b/lib/vty_io.h
@@ -0,0 +1,310 @@
+/* VTY IO Structure and Functions -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 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 _ZEBRA_VTY_IO_H
+#define _ZEBRA_VTY_IO_H
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include "uty.h"
+#include "vty.h"
+#include "vio_fifo.h"
+#include "vio_lines.h"
+#include "keystroke.h"
+#include "thread.h"
+#include "command.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty.c -- the main vty handler
+ * vty_cli.c -- which handles the command line stuff
+ * vty_io.c -- ....
+ *
+ * The "struct vty" is used extensively across the Quagga daemons, where it
+ * has two functions relating to command handling as:
+ *
+ * 1) a "file handle" for output produced by commands
+ *
+ * 2) the holder of some context -- notably the current command "node" -- for
+ * command execution to use
+ *
+ * The bulk of "struct vty" is, therefore, private to vty.c and is factored
+ * out into the "struct vty_io".
+ *
+ * To reduce the size of vty.c, some groups of functions are separated into:
+ *
+ * vty_cli.c -- which looks after the keystroke by keystroke handling
+ * of the command line.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * VTY sock structure
+ *
+ * Used for VTY_TERM and VTY_SHELL_SERV VTY types, which are attached to TCP
+ * and UNIX sockets, respectively.
+ *
+ * Also used for the associated listeners.
+ */
+
+typedef int thread_action(struct thread *) ;
+
+union sock_action
+{
+ qps_action* qnexus ;
+ thread_action* thread ;
+ void* anon ;
+} ;
+
+union timer_action
+{
+ qtimer_action* qnexus ;
+ thread_action* thread ;
+ void* anon ;
+} ;
+
+struct vio_sock_actions
+{
+ union sock_action read ;
+ union sock_action write ;
+ union timer_action timer ;
+};
+
+typedef struct vio_sock* vio_sock ;
+struct vio_sock
+{
+ int fd ;
+
+ void* info ; /* for action routines */
+
+ struct vio_sock_actions action ;
+
+ bool read_open ; /* read returns 0 if not open */
+ bool write_open ; /* write completes instantly if not open */
+ int error_seen ; /* non-zero => failed */
+
+ qps_file qf ; /* when running qnexus */
+
+ struct thread *t_read; /* when running threads */
+ struct thread *t_write;
+
+ unsigned long v_timeout; /* time-out in seconds -- 0 => none */
+ bool timer_running ; /* true when timer is running */
+
+ qtimer qtr; /* when running qnexus */
+ struct thread *t_timer; /* when running threads */
+
+} ;
+
+enum
+{
+ on = true,
+ off = false
+} ;
+
+enum vty_readiness /* bit significant */
+{
+ not_ready = 0,
+ read_ready = 1,
+ write_ready = 2, /* takes precedence */
+ now_ready = 4
+} ;
+
+/*------------------------------------------------------------------------------
+ * The vty_io structure
+ */
+
+struct vty_io {
+ struct vty* vty ; /* the related vty */
+ char *name ; /* for VTY_TERM is IP address) */
+
+ /* List of all vty_io objects */
+ struct dl_list_pair(vty_io) vio_list ;
+
+ /* List of all vty_io that are in monitor state */
+ struct dl_list_pair(vty_io) mon_list ;
+
+ /* VTY type and sock stuff */
+ enum vty_type type;
+
+ struct vio_sock sock ; /* for VTY_TERM and VTY_SHELL_SERV */
+
+ bool half_closed ; /* => on death watch list */
+ bool closed ; /* => all I/O terminated
+ will also be half_closed */
+
+ const char* close_reason ; /* message to be sent, once all other
+ output has completed, giving reason
+ for closing the VTY. */
+
+ /* When writing configuration file */
+ enum vty_type real_type ;
+
+ int file_fd ;
+ int file_error ;
+
+ /*--------------------------------------------------------------------*/
+ /* Command line and related state */
+
+ keystroke_stream key_stream ;
+
+ /* cli_drawn <=> the current prompt and user input occupy the current
+ * line on the screen.
+ *
+ * cli_dirty <=> the last command output did not end with a newline.
+ *
+ * If cli_drawn is true, the following are valid:
+ *
+ * cli_prompt_len -- the length of the prompt part.
+ * (will be the "--more--" prompt in cli_more_wait)
+ *
+ * cli_extra_len -- the length of any ^X at the cursor position
+ * (for when blocked waiting for queued command)
+ *
+ * cli_echo_suppress -- the user part of the command line is suppressed
+ *
+ * NB: cli_echo_suppress is only used for password entry.
+ */
+ bool cli_drawn ;
+ bool cli_dirty ;
+
+ int cli_prompt_len ;
+ int cli_extra_len ;
+
+ bool cli_echo_suppress ;
+
+ /* "cache" for prompt -- when node or host name changes, prompt does */
+ enum node_type cli_prompt_node ;
+ bool cli_prompt_set ;
+ qstring_t cli_prompt_for_node ;
+
+ /* State of the CLI
+ *
+ * cli_blocked -- blocked from processing keystrokes
+ * cmd_in_progress -- command dispatched (may be queued)
+ * cmd_out_enabled -- contents of the command FIFO may be written away
+ * cli_more_wait -- is in "--more--" wait state
+ */
+ bool cli_blocked ;
+ bool cmd_in_progress ;
+ bool cmd_out_enabled ;
+ bool cli_more_wait ;
+
+ /* This is used to control command output, so that each write_ready event
+ * generates at most one tranche of output.
+ */
+ bool cmd_out_done ;
+
+ /* This is set only if the "--more--" handling is enabled */
+ bool cli_more_enabled ;
+
+ /* Command Line(s)
+ *
+ * cli_do -- when current command being prepared is completed (by
+ * CR/LF or otherwise) this says what there now is to be done.
+ *
+ * cl -- current command line being prepared.
+ *
+ * clx -- current command line being executed.
+ *
+ * NB: during command execution vty->buf is set to point at the '\0'
+ * terminated current command line being executed.
+ */
+ enum cli_do cli_do ;
+
+ qstring_t cl ;
+ qstring_t clx ;
+
+ /* CLI output buffering */
+ vio_fifo_t cli_obuf ;
+
+ /* Command output buffering */
+ vio_fifo_t cmd_obuf ;
+
+ vio_line_control cmd_lc ;
+
+ /* Failure count for login attempts */
+ int fail;
+
+ /* History of commands */
+ vector_t hist ;
+ int hp ; /* History lookup current point */
+ int hindex; /* History insert end point */
+
+ /* Window width/height as reported by Telnet. 0 => unknown */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+ bool lines_set ; /* true <=> explicitly set */
+
+ /* Terminal monitor. */
+ bool monitor ;
+ bool monitor_busy ;
+
+ /* In configure mode. */
+ bool config;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern struct vty* uty_new (enum vty_type type, int sock_fd) ;
+
+extern void uty_open_listeners(const char *addr, unsigned short port,
+ const char *path) ;
+extern void uty_close_listeners(void) ;
+
+extern void uty_watch_dog_start(void) ;
+extern void uty_watch_dog_stop(void) ;
+
+extern void uty_half_close (vty_io vio, const char* reason) ;
+extern void uty_close (vty_io vio) ;
+
+extern int uty_out (struct vty *vty, const char *format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern int uty_vout(struct vty *vty, const char *format, va_list args) ;
+extern void uty_out_clear(vty_io vio) ;
+extern void uty_out_fflush(vty_io vio, FILE* file) ;
+
+extern void uty_set_height(vty_io vio) ;
+extern void uty_cmd_output_start(vty_io vio) ;
+
+extern void uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready) ;
+extern void uty_sock_set_timer(vio_sock sock, unsigned long timeout) ;
+
+extern int uty_read (vty_io vio, keystroke steal) ;
+extern int utysh_read (vty_io vio, qstring cl, qstring buf) ;
+
+
+extern const char* uty_get_name(vty_io vio) ;
+
+extern void uty_set_monitor(vty_io vio, bool on) ;
+
+#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/workqueue.c b/lib/workqueue.c
index 52b5f41c..56959db0 100644
--- a/lib/workqueue.c
+++ b/lib/workqueue.c
@@ -1,4 +1,4 @@
-/*
+/*
* Quagga Work Queue Support.
*
* Copyright (C) 2005 Sun Microsystems, Inc.
@@ -18,38 +18,33 @@
* You should have received a copy of the GNU General Public License
* along with Quagga; 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 <lib/zebra.h>
#include "thread.h"
#include "memory.h"
#include "workqueue.h"
-#include "linklist.h"
#include "command.h"
#include "log.h"
+#include "linklist.h"
/* master list of work_queues */
static struct list work_queues;
-#define WORK_QUEUE_MIN_GRANULARITY 1
-
-static struct work_queue_item *
-work_queue_item_new (struct work_queue *wq)
-{
- struct work_queue_item *item;
- assert (wq);
-
- item = XCALLOC (MTYPE_WORK_QUEUE_ITEM,
- sizeof (struct work_queue_item));
-
- return item;
-}
+enum {
+ WQ_MIN_GRANULARITY = 1,
+ WQ_HYSTERESIS_FACTOR = 4,
+} ;
static void
-work_queue_item_free (struct work_queue_item *item)
+work_queue_item_free (struct work_queue *wq, struct work_queue_item *item)
{
- XFREE (MTYPE_WORK_QUEUE_ITEM, item);
+ /* call private data deletion callback if needed */
+ if (wq->spec.del_item_data != NULL)
+ wq->spec.del_item_data (wq, item) ;
+
+ XFREE (MTYPE_WORK_QUEUE_ITEM, item) ;
return;
}
@@ -58,46 +53,40 @@ struct work_queue *
work_queue_new (struct thread_master *m, const char *queue_name)
{
struct work_queue *new;
-
+
new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct work_queue));
if (new == NULL)
return new;
-
- new->name = XSTRDUP (MTYPE_WORK_QUEUE_NAME, queue_name);
+
+ new->name = XSTRDUP (MTYPE_WORK_QUEUE_NAME, queue_name);
new->master = m;
SET_FLAG (new->flags, WQ_UNPLUGGED);
-
- if ( (new->items = list_new ()) == NULL)
- {
- XFREE (MTYPE_WORK_QUEUE_NAME, new->name);
- XFREE (MTYPE_WORK_QUEUE, new);
-
- return NULL;
- }
-
- new->items->del = (void (*)(void *)) work_queue_item_free;
-
+
listnode_add (&work_queues, new);
-
- new->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY;
+
+ new->cycles.granularity = WQ_MIN_GRANULARITY;
/* Default values, can be overriden by caller */
new->spec.hold = WORK_QUEUE_DEFAULT_HOLD;
-
+
return new;
}
void
work_queue_free (struct work_queue *wq)
{
+ work_queue_item item ;
+
if (wq->thread != NULL)
thread_cancel(wq->thread);
-
- /* list_delete frees items via callback */
- list_delete (wq->items);
- listnode_delete (&work_queues, wq);
-
+
+ while ((item = wq->head) != NULL)
+ {
+ wq->head = item->next ;
+ work_queue_item_free(wq, item) ;
+ } ;
+
XFREE (MTYPE_WORK_QUEUE_NAME, wq->name);
XFREE (MTYPE_WORK_QUEUE, wq);
return;
@@ -109,59 +98,151 @@ work_queue_schedule (struct work_queue *wq, unsigned int delay)
/* if appropriate, schedule work queue thread */
if ( CHECK_FLAG (wq->flags, WQ_UNPLUGGED)
&& (wq->thread == NULL)
- && (listcount (wq->items) > 0) )
+ && (wq->head != NULL) )
{
- wq->thread = thread_add_background (wq->master, work_queue_run,
+ wq->thread = thread_add_background (wq->master, work_queue_run,
wq, delay);
return 1;
}
else
return 0;
}
-
-void
-work_queue_add (struct work_queue *wq, void *data)
+
+/*------------------------------------------------------------------------------
+ * Create new work queue item and place on the end of the given work queue.
+ *
+ * Schedules the work queue if there were no items (unless already scheduled
+ * or plugged).
+ *
+ * Returns the address of the args area in the new item.
+ */
+extern void*
+work_queue_item_add (struct work_queue *wq)
{
- struct work_queue_item *item;
-
+ work_queue_item item ;
+
assert (wq);
- if (!(item = work_queue_item_new (wq)))
+ item = XCALLOC (MTYPE_WORK_QUEUE_ITEM, sizeof (struct work_queue_item));
+
+ if (item == NULL)
{
zlog_err ("%s: unable to get new queue item", __func__);
- return;
+ return NULL ;
+ }
+
+ item->next = NULL ;
+ if (wq->head == NULL)
+ {
+ assert(wq->list_count == 0) ;
+ wq->head = item ;
+ item->prev = NULL ;
}
-
- item->data = data;
- listnode_add (wq->items, item);
-
+ else
+ {
+ assert((wq->tail != NULL) && (wq->list_count > 0)) ;
+ wq->tail->next = item ;
+ item->prev = wq->tail ;
+ } ;
+ wq->tail = item ;
+
+ ++wq->list_count ;
work_queue_schedule (wq, wq->spec.hold);
-
- return;
+
+ return work_queue_item_args(item) ;
}
static void
-work_queue_item_remove (struct work_queue *wq, struct listnode *ln)
+work_queue_item_remove (struct work_queue *wq, work_queue_item item)
{
- struct work_queue_item *item = listgetdata (ln);
+ assert ((wq != NULL) && (item != NULL)) ;
+
+ if (wq->head == item)
+ {
+ /* Removing the first item */
+ assert(item->prev == NULL) ;
+
+ wq->head = item->next ;
- assert (item && item->data);
+ if (wq->tail == item)
+ {
+ /* Removing the only item */
+ assert((item->next == NULL) && (wq->list_count == 1)) ;
+ wq->tail = NULL ;
+ }
+ else
+ {
+ /* First, but not the only item */
+ assert((item->next != NULL) && (wq->list_count > 1)) ;
+ wq->head->prev = NULL ;
+ } ;
+ }
+ else if (wq->tail == item)
+ {
+ /* Removing last, but not only item */
+ assert(item->next == NULL) ;
+ assert((item->prev != NULL) && (wq->list_count > 1)) ;
+
+ wq->tail = item->prev ;
+ wq->tail->next = NULL ;
+ }
+ else
+ {
+ /* Removing from somewhere in middle */
+ assert(item->next != NULL) ;
+ assert((item->prev != NULL) && (wq->list_count > 2)) ;
+
+ item->prev->next = item->next ;
+ item->next->prev = item->prev ;
+ } ;
- /* call private data deletion callback if needed */
- if (wq->spec.del_item_data)
- wq->spec.del_item_data (wq, item->data);
+ --wq->list_count ;
+ work_queue_item_free (wq, item);
- list_delete_node (wq->items, ln);
- work_queue_item_free (item);
-
return;
}
-static void
-work_queue_item_requeue (struct work_queue *wq, struct listnode *ln)
+static work_queue_item
+work_queue_item_requeue (struct work_queue *wq, work_queue_item item)
{
- LISTNODE_DETACH (wq->items, ln);
- LISTNODE_ATTACH (wq->items, ln); /* attach to end of list */
+ work_queue_item next = item->next ;
+ work_queue_item last = wq->tail ;
+
+ assert(last != NULL) ;
+
+ if (last == item)
+ {
+ /* Requeuing last item -- easy ! */
+ assert(next == NULL) ;
+ return item ;
+ } ;
+
+ assert(next != NULL) ;
+
+ if (wq->head == item)
+ {
+ /* Requeuing first, but not only item */
+ assert(item->prev == NULL) ;
+
+ wq->head = next ;
+ next->prev = NULL ;
+ }
+ else
+ {
+ /* Requeuing something in middle */
+ assert(item->prev != NULL) ;
+
+ item->prev->next = item->next ;
+ item->next->prev = item->prev ;
+ } ;
+
+ item->next = NULL ;
+ item->prev = last ;
+
+ last->next = item ;
+ wq->tail = item ;
+
+ return next ;
}
DEFUN(show_work_queues,
@@ -172,8 +253,8 @@ DEFUN(show_work_queues,
{
struct listnode *node;
struct work_queue *wq;
-
- vty_out (vty,
+
+ vty_out (vty,
"%c %8s %5s %8s %21s%s",
' ', "List","(ms) ","Q. Runs","Cycle Counts ",
VTY_NEWLINE);
@@ -183,24 +264,24 @@ DEFUN(show_work_queues,
"Items",
"Hold",
"Total",
- "Best","Gran.","Avg.",
- "Name",
+ "Best","Gran.","Avg.",
+ "Name",
VTY_NEWLINE);
-
+
for (ALL_LIST_ELEMENTS_RO ((&work_queues), node, wq))
{
vty_out (vty,"%c %8d %5d %8ld %7d %6d %6u %s%s",
(CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'),
- listcount (wq->items),
+ wq->list_count,
wq->spec.hold,
wq->runs,
wq->cycles.best, wq->cycles.granularity,
- (wq->runs) ?
+ (wq->runs) ?
(unsigned int) (wq->cycles.total / wq->runs) : 0,
wq->name,
VTY_NEWLINE);
}
-
+
return CMD_SUCCESS;
}
@@ -212,9 +293,9 @@ work_queue_plug (struct work_queue *wq)
{
if (wq->thread)
thread_cancel (wq->thread);
-
+
wq->thread = NULL;
-
+
UNSET_FLAG (wq->flags, WQ_UNPLUGGED);
}
@@ -232,22 +313,21 @@ work_queue_unplug (struct work_queue *wq)
/* timer thread to process a work queue
* will reschedule itself if required,
- * otherwise work_queue_item_add
+ * otherwise work_queue_item_add
*/
int
work_queue_run (struct thread *thread)
{
struct work_queue *wq;
- struct work_queue_item *item;
+ work_queue_item next, item ;
wq_item_status ret;
unsigned int cycles = 0;
- struct listnode *node, *nnode;
char yielded = 0;
wq = THREAD_ARG (thread);
wq->thread = NULL;
- assert (wq && wq->items);
+ assert (wq != NULL) ;
/* calculate cycle granularity:
* list iteration == 1 cycle
@@ -258,38 +338,40 @@ work_queue_run (struct thread *thread)
*
* Best: starts low, can only increase
*
- * Granularity: starts at WORK_QUEUE_MIN_GRANULARITY, can be decreased
- * if we run to end of time slot, can increase otherwise
+ * Granularity: starts at WQ_MIN_GRANULARITY, can be decreased
+ * if we run to end of time slot, can increase otherwise
* by a small factor.
*
* We could use just the average and save some work, however we want to be
* able to adjust quickly to CPU pressure. Average wont shift much if
* daemon has been running a long time.
*/
- if (wq->cycles.granularity == 0)
- wq->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY;
+ if (wq->cycles.granularity == 0)
+ wq->cycles.granularity = WQ_MIN_GRANULARITY;
- for (ALL_LIST_ELEMENTS (wq->items, node, nnode, item))
+ next = wq->head ;
+ while (next != NULL)
{
- assert (item && item->data);
-
+ item = next ;
+ next = item->next ; /* default next item */
+
/* dont run items which are past their allowed retries */
if (item->ran > wq->spec.max_retries)
{
/* run error handler, if any */
- if (wq->spec.errorfunc)
- wq->spec.errorfunc (wq, item->data);
- work_queue_item_remove (wq, node);
+ if (wq->spec.errorfunc != NULL)
+ wq->spec.errorfunc (wq, item);
+ work_queue_item_remove (wq, item);
continue;
}
/* run and take care of items that want to be retried immediately */
do
{
- ret = wq->spec.workfunc (wq, item->data);
+ ret = wq->spec.workfunc (wq, item);
item->ran++;
}
- while ((ret == WQ_RETRY_NOW)
+ while ((ret == WQ_RETRY_NOW)
&& (item->ran < wq->spec.max_retries));
switch (ret)
@@ -308,21 +390,21 @@ work_queue_run (struct thread *thread)
case WQ_REQUEUE:
{
item->ran--;
- work_queue_item_requeue (wq, node);
+ next = work_queue_item_requeue (wq, item);
break;
}
case WQ_RETRY_NOW:
/* a RETRY_NOW that gets here has exceeded max_tries, same as ERROR */
case WQ_ERROR:
{
- if (wq->spec.errorfunc)
+ if (wq->spec.errorfunc != NULL)
wq->spec.errorfunc (wq, item);
}
/* fall through here is deliberate */
case WQ_SUCCESS:
default:
{
- work_queue_item_remove (wq, node);
+ work_queue_item_remove (wq, item);
break;
}
}
@@ -331,7 +413,7 @@ work_queue_run (struct thread *thread)
cycles++;
/* test if we should yield */
- if ( !(cycles % wq->cycles.granularity)
+ if ( !(cycles % wq->cycles.granularity)
&& thread_should_yield (thread))
{
yielded = 1;
@@ -341,20 +423,18 @@ work_queue_run (struct thread *thread)
stats:
-#define WQ_HYSTERESIS_FACTOR 4
-
/* we yielded, check whether granularity should be reduced */
if (yielded && (cycles < wq->cycles.granularity))
{
- wq->cycles.granularity = ((cycles > 0) ? cycles
- : WORK_QUEUE_MIN_GRANULARITY);
+ wq->cycles.granularity = ((cycles > 0) ? cycles
+ : WQ_MIN_GRANULARITY);
}
/* otherwise, should granularity increase? */
else if (cycles >= (wq->cycles.granularity))
{
if (cycles > wq->cycles.best)
wq->cycles.best = cycles;
-
+
/* along with yielded check, provides hysteresis for granularity */
if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR
* WQ_HYSTERESIS_FACTOR))
@@ -362,8 +442,7 @@ stats:
else if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR))
wq->cycles.granularity += WQ_HYSTERESIS_FACTOR;
}
-#undef WQ_HYSTERIS_FACTOR
-
+
wq->runs++;
wq->cycles.total += cycles;
@@ -371,12 +450,12 @@ stats:
printf ("%s: cycles %d, new: best %d, worst %d\n",
__func__, cycles, wq->cycles.best, wq->cycles.granularity);
#endif
-
+
/* Is the queue done yet? If it is, call the completion callback. */
- if (listcount (wq->items) > 0)
+ if (wq->head != NULL)
work_queue_schedule (wq, 0);
else if (wq->spec.completion_func)
wq->spec.completion_func (wq);
-
+
return 0;
}
diff --git a/lib/workqueue.h b/lib/workqueue.h
index f59499a0..369cd871 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -1,4 +1,4 @@
-/*
+/*
* Quagga Work Queues.
*
* Copyright (C) 2005 Sun Microsystems, Inc.
@@ -18,14 +18,18 @@
* You should have received a copy of the GNU General Public License
* along with Quagga; 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 _QUAGGA_WORK_QUEUE_H
#define _QUAGGA_WORK_QUEUE_H
+#ifndef Inline
+#define Inline static inline
+#endif
+
/* Hold time for the initial schedule of a queue run, in millisec */
-#define WORK_QUEUE_DEFAULT_HOLD 50
+enum { WORK_QUEUE_DEFAULT_HOLD = 50 } ;
/* action value, for use by item processor and item error handlers */
typedef enum
@@ -40,15 +44,47 @@ typedef enum
* the particular item.. */
} wq_item_status;
+enum { wq_args_size_max = 24 } ; /* maximum size of union wq_args */
+
+union wq_args
+{
+ void* data ;
+ char bytes[wq_args_size_max] ; /* empty space `*/
+} ;
+
+#define WQ_ARGS_SIZE_OK(s) CONFIRM(sizeof(struct s) <= wq_args_size_max)
+
/* A single work queue item, unsurprisingly */
+typedef struct work_queue_item* work_queue_item ;
struct work_queue_item
{
- void *data; /* opaque data */
+ union wq_args args ; /* cast as required */
+
+ struct work_queue_item* next ; /* the queue itself */
+ struct work_queue_item* prev ;
+
unsigned short ran; /* # of times item has been run */
-};
+} ;
+
+/* work_queue_item structures are malloced. That guarantees maximum alignment.
+ * To guarantee maximum alignment for "struct args", it must be first item !
+ *
+ * (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
+ * about first argument of offsetof().)
+ */
+typedef struct work_queue_item work_queue_item_t ;
+CONFIRM(offsetof(work_queue_item_t, args) == 0) ;
+ /* so guaranteed max alignment */
#define WQ_UNPLUGGED (1 << 0) /* available for draining */
+typedef struct work_queue* work_queue ;
+
+typedef wq_item_status wq_workfunc(work_queue, work_queue_item);
+typedef void wq_errorfunc(work_queue, work_queue_item);
+typedef void wq_del_item_data(work_queue, work_queue_item);
+typedef void wq_completion_func(work_queue);
+
struct work_queue
{
/* Everything but the specification struct is private
@@ -57,52 +93,55 @@ struct work_queue
struct thread_master *master; /* thread master */
struct thread *thread; /* thread, if one is active */
char *name; /* work queue name */
-
+
/* Specification for this work queue.
* Public, must be set before use by caller. May be modified at will.
*/
struct {
- /* optional opaque user data, global to the queue. */
+ /* optional opaque user data, global to the queue. */
void *data;
-
+
/* work function to process items with:
* First argument is the workqueue queue.
* Second argument is the item data
*/
- wq_item_status (*workfunc) (struct work_queue *, void *);
-
- /* error handling function, optional */
- void (*errorfunc) (struct work_queue *, struct work_queue_item *);
-
- /* callback to delete user specific item data */
- void (*del_item_data) (struct work_queue *, void *);
-
- /* completion callback, called when queue is emptied, optional */
- void (*completion_func) (struct work_queue *);
-
+ wq_workfunc* workfunc ;
+
+ /* error handling function -- optional */
+ wq_errorfunc* errorfunc ;
+
+ /* callback to delete user specific item data -- optional */
+ wq_del_item_data* del_item_data ;
+
+ /* completion callback, called when queue is emptied -- optional */
+ wq_completion_func* completion_func ;
+
/* max number of retries to make for item that errors */
- unsigned int max_retries;
+ unsigned int max_retries;
unsigned int hold; /* hold time for first run, in ms */
} spec;
-
+
/* remaining fields should be opaque to users */
- struct list *items; /* queue item list */
- unsigned long runs; /* runs count */
-
+ work_queue_item head ; /* queue item list */
+ work_queue_item tail ;
+ unsigned list_count ;
+
+ unsigned long runs; /* runs count */
+
struct {
unsigned int best;
unsigned int granularity;
unsigned long total;
} cycles; /* cycle counts */
-
+
/* private state */
u_int16_t flags; /* user set flag */
};
/* User API */
-/* create a new work queue, of given name.
+/* create a new work queue, of given name.
* user must fill in the spec of the returned work queue before adding
* anything to it
*/
@@ -112,7 +151,10 @@ extern struct work_queue *work_queue_new (struct thread_master *,
extern void work_queue_free (struct work_queue *);
/* Add the supplied data as an item onto the workqueue */
-extern void work_queue_add (struct work_queue *, void *);
+Inline void work_queue_add (struct work_queue *, void *);
+
+extern void* work_queue_item_add(struct work_queue* wq) ;
+Inline void* work_queue_item_args(work_queue_item item) ;
/* plug the queue, ie prevent it from being drained / processed */
extern void work_queue_plug (struct work_queue *wq);
@@ -122,4 +164,22 @@ extern void work_queue_unplug (struct work_queue *wq);
/* Helpers, exported for thread.c and command.c */
extern int work_queue_run (struct thread *);
extern struct cmd_element show_work_queues_cmd;
+
+/*==============================================================================
+ * The Inline functions
+ */
+
+Inline void work_queue_add (struct work_queue* wq, void* data)
+{
+ union wq_args* args = work_queue_item_add(wq) ;
+ args->data = data ;
+}
+
+/* Return pointer to the args area in the given work queue item */
+Inline void*
+work_queue_item_args(work_queue_item item)
+{
+ return &item->args ;
+} ;
+
#endif /* _QUAGGA_WORK_QUEUE_H */
diff --git a/lib/zassert.h b/lib/zassert.h
index 79126760..8ca2203f 100644
--- a/lib/zassert.h
+++ b/lib/zassert.h
@@ -5,9 +5,23 @@
#ifndef _QUAGGA_ASSERT_H
#define _QUAGGA_ASSERT_H
+#include "confirm.h"
+
extern void _zlog_assert_failed (const char *assertion, const char *file,
unsigned int line, const char *function)
__attribute__ ((noreturn));
+extern void _zlog_abort_mess (const char *mess, const char *file,
+ unsigned int line, const char *function)
+ __attribute__ ((noreturn));
+
+extern void _zlog_abort_errno (const char *mess, const char *file,
+ unsigned int line, const char *function)
+ __attribute__ ((noreturn));
+
+extern void _zlog_abort_err (const char *mess, int err, const char *file,
+ unsigned int line, const char *function)
+ __attribute__ ((noreturn));
+
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define __ASSERT_FUNCTION __func__
@@ -21,7 +35,29 @@ extern void _zlog_assert_failed (const char *assertion, const char *file,
(_zlog_assert_failed(#EX, __FILE__, __LINE__, \
__ASSERT_FUNCTION), 0)))
+/* Implicitly *permanent* assert() -- irrespective of NDEBUG */
#undef assert
#define assert(EX) zassert(EX)
+/* Explicitly permanent assert() */
+#define passert(EX) zassert(EX)
+
+/* NDEBUG time assert() */
+#ifndef NDEBUG
+#define dassert(EX) zassert(EX)
+#else
+#define dassert(EX)
+#endif
+
+/* Abort with message */
+#define zabort(MS) _zlog_abort_mess(MS, __FILE__, __LINE__, __ASSERT_FUNCTION)
+
+/* Abort with message and errno and strerror() thereof */
+#define zabort_errno(MS) _zlog_abort_errno(MS, __FILE__, __LINE__, \
+ __ASSERT_FUNCTION)
+
+/* Abort with message and given error and strerror() thereof */
+#define zabort_err(MS, ERR) _zlog_abort_err(MS, ERR, __FILE__, __LINE__, \
+ __ASSERT_FUNCTION)
+
#endif /* _QUAGGA_ASSERT_H */
diff --git a/lib/zclient.c b/lib/zclient.c
index 52a3627d..6803aa4a 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -32,18 +32,25 @@
#include "zclient.h"
#include "memory.h"
#include "table.h"
-
-/* Zebra client events. */
-enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT};
-/* Prototype for event manager. */
-static void zclient_event (enum event, struct zclient *);
+/* Zebra client events. */
+enum event {ZLOOKUP_SCHEDULE, ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT};
extern struct thread_master *master;
/* This file local debug flag. */
int zclient_debug = 0;
-
+
+/* Nexus to use, if any */
+static qpn_nexus zclient_nexus = NULL;
+
+/* prototypes */
+static int zclient_read (struct zclient *zclient);
+static void zclient_event (enum event, struct zclient *);
+static void zclient_event_r (enum event event, struct zclient *zclient);
+static void zclient_event_t (enum event event, struct zclient *zclient);
+static void zclient_connect_r (qtimer qtr, void* timer_info, qtime_t when);
+
/* Allocate zclient structure. */
struct zclient *
zclient_new ()
@@ -55,6 +62,13 @@ zclient_new ()
zclient->obuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
zclient->wb = buffer_new(0);
+ if (zclient_nexus)
+ {
+ zclient->qf = qps_file_init_new(zclient->qf, NULL);
+ zclient->qtr = qtimer_init_new(zclient->qtr, zclient_nexus->pile,
+ zclient_connect_r, zclient);
+ }
+
return zclient;
}
@@ -73,16 +87,36 @@ zclient_free (struct zclient *zclient)
if (zclient->wb)
buffer_free(zclient->wb);
+ /* qfile and qtimer */
+ if (zclient->qf)
+ {
+ qps_remove_file(zclient->qf);
+ qps_file_free(zclient->qf);
+ zclient->qf = NULL;
+ }
+ if (zclient->qtr)
+ {
+ qtimer_free(zclient->qtr);
+ zclient->qtr = NULL;
+ }
+
XFREE (MTYPE_ZCLIENT, zclient);
}
+/* Initialize to use a nexus (qpselect etc). */
+void
+zclient_init_r (qpn_nexus n)
+{
+ zclient_nexus = n;
+}
+
/* Initialize zebra client. Argument redist_default is unwanted
redistribute route type. */
void
zclient_init (struct zclient *zclient, int redist_default)
{
int i;
-
+
/* Enable zebra client connection by default. */
zclient->enable = 1;
@@ -108,6 +142,13 @@ zclient_init (struct zclient *zclient, int redist_default)
zclient_event (ZCLIENT_SCHEDULE, zclient);
}
+/* Schedule lookup connection */
+void
+zlookup_schedule(struct zclient *zclient)
+{
+ zclient_event (ZLOOKUP_SCHEDULE, zclient);
+}
+
/* Stop zebra client services. */
void
zclient_stop (struct zclient *zclient)
@@ -120,6 +161,12 @@ zclient_stop (struct zclient *zclient)
THREAD_OFF(zclient->t_connect);
THREAD_OFF(zclient->t_write);
+ if (zclient->qf)
+ qps_remove_file(zclient->qf);
+
+ if (zclient->qtr)
+ qtimer_unset(zclient->qtr);
+
/* Reset streams. */
stream_reset(zclient->ibuf);
stream_reset(zclient->obuf);
@@ -155,8 +202,8 @@ zclient_socket(void)
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
return -1;
-
- /* Make server socket. */
+
+ /* Make server socket. */
memset (&serv, 0, sizeof (struct sockaddr_in));
serv.sin_family = AF_INET;
serv.sin_port = htons (ZEBRA_PORT);
@@ -188,8 +235,8 @@ zclient_socket_un (const char *path)
sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
return -1;
-
- /* Make server socket. */
+
+ /* Make server socket. */
memset (&addr, 0, sizeof (struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, path, strlen (path));
@@ -217,8 +264,37 @@ zclient_failed(struct zclient *zclient)
return -1;
}
+/* Write as much data as possible.
+ * nexus version */
+static void
+zclient_flush_data_r(qps_file qf, void* file_info)
+{
+ struct zclient *zclient = file_info;
+
+ qps_disable_modes(qf, qps_write_mbit);
+
+ if (zclient->sock < 0)
+ return;
+
+ switch (buffer_flush_available(zclient->wb, zclient->sock))
+ {
+ case BUFFER_ERROR:
+ zlog_warn("%s: buffer_flush_available failed on zclient fd %d, closing",
+ __func__, zclient->sock);
+ zclient_failed(zclient);
+ break;
+ case BUFFER_PENDING:
+ qps_enable_mode(qf, qps_write_mnum, zclient_flush_data_r) ;
+ break;
+ case BUFFER_EMPTY:
+ break;
+ }
+}
+
+/* Write as much data as possible.
+ * thread version */
static int
-zclient_flush_data(struct thread *thread)
+zclient_flush_data_t(struct thread *thread)
{
struct zclient *zclient = THREAD_ARG(thread);
@@ -233,7 +309,7 @@ zclient_flush_data(struct thread *thread)
return zclient_failed(zclient);
break;
case BUFFER_PENDING:
- zclient->t_write = thread_add_write(master, zclient_flush_data,
+ zclient->t_write = thread_add_write(master, zclient_flush_data_t,
zclient, zclient->sock);
break;
case BUFFER_EMPTY:
@@ -256,11 +332,17 @@ zclient_send_message(struct zclient *zclient)
return zclient_failed(zclient);
break;
case BUFFER_EMPTY:
- THREAD_OFF(zclient->t_write);
+ if (zclient_nexus)
+ qps_disable_modes(zclient->qf, qps_write_mbit);
+ else
+ THREAD_OFF(zclient->t_write);
break;
case BUFFER_PENDING:
- THREAD_WRITE_ON(master, zclient->t_write,
- zclient_flush_data, zclient, zclient->sock);
+ if (zclient_nexus)
+ qps_enable_mode(zclient->qf, qps_write_mnum, zclient_flush_data_r) ;
+ else
+ THREAD_WRITE_ON(master, zclient->t_write,
+ zclient_flush_data_t, zclient, zclient->sock);
break;
}
return 0;
@@ -288,7 +370,7 @@ zebra_message_send (struct zclient *zclient, int command)
/* Send very simple command only Zebra message. */
zclient_create_header (s, command);
-
+
return zclient_send_message(zclient);
}
@@ -313,6 +395,10 @@ zclient_start (struct zclient *zclient)
if (zclient->t_connect)
return 0;
+ /* Check timer */
+ if (zclient->qtr && zclient->qtr->active)
+ return 0;
+
/* Make socket. */
#ifdef HAVE_TCP_ZEBRA
zclient->sock = zclient_socket ();
@@ -335,7 +421,10 @@ zclient_start (struct zclient *zclient)
zclient->fail = 0;
if (zclient_debug)
zlog_debug ("zclient connect success with socket [%d]", zclient->sock);
-
+
+ if (zclient_nexus)
+ qps_add_file(zclient_nexus->selection, zclient->qf, zclient->sock, zclient);
+
/* Create read thread. */
zclient_event (ZCLIENT_READ, zclient);
@@ -358,9 +447,24 @@ zclient_start (struct zclient *zclient)
}
/* This function is a wrapper function for calling zclient_start from
+ qtimer. */
+static void
+zclient_connect_r (qtimer qtr, void* timer_info, qtime_t when)
+{
+ struct zclient *zclient = timer_info;
+
+ qtimer_unset(qtr);
+
+ if (zclient_debug)
+ zlog_debug ("zclient_connect is called");
+
+ zclient_start (zclient);
+}
+
+/* This function is a wrapper function for calling zclient_start from
timer or event thread. */
static int
-zclient_connect (struct thread *t)
+zclient_connect_t (struct thread *t)
{
struct zclient *zclient;
@@ -372,14 +476,58 @@ zclient_connect (struct thread *t)
return zclient_start (zclient);
}
-
- /*
+
+/* Connect to zebra for nexthop lookup.
+ * thread version */
+static int
+zlookup_connect_t (struct thread *t)
+{
+ struct zclient *zlookup;
+
+ zlookup = THREAD_ARG (t);
+ zlookup->t_connect = NULL;
+
+ if (zlookup->sock != -1)
+ return 0;
+
+#ifdef HAVE_TCP_ZEBRA
+ zlookup->sock = zclient_socket ();
+#else
+ zlookup->sock = zclient_socket_un (ZEBRA_SERV_PATH);
+#endif /* HAVE_TCP_ZEBRA */
+ if (zlookup->sock < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Connect to zebra for nexthop lookup.
+ * nexus version */
+static void
+zlookup_connect_r (qtimer qtr, void* timer_info, qtime_t when)
+{
+ struct zclient *zlookup = timer_info;
+
+ qtimer_unset(qtr);
+
+ if (zlookup->sock != -1)
+ return;
+
+#ifdef HAVE_TCP_ZEBRA
+ zlookup->sock = zclient_socket ();
+#else
+ zlookup->sock = zclient_socket_un (ZEBRA_SERV_PATH);
+#endif /* HAVE_TCP_ZEBRA */
+}
+
+
+ /*
* "xdr_encode"-like interface that allows daemon (client) to send
* a message to zebra server for a route that needs to be
* added/deleted to the kernel. Info about the route is specified
* by the caller in a struct zapi_ipv4. zapi_ipv4_read() then writes
* the info down the zclient socket using the stream_* functions.
- *
+ *
* The corresponding read ("xdr_decode") function on the server
* side is zread_ipv4_add()/zread_ipv4_delete().
*
@@ -391,11 +539,11 @@ zclient_connect (struct thread *t)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Destination IPv4 Prefix for route |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Nexthop count |
+ * | Nexthop count |
* +-+-+-+-+-+-+-+-+
*
- *
- * A number of IPv4 nexthop(s) or nexthop interface index(es) are then
+ *
+ * A number of IPv4 nexthop(s) or nexthop interface index(es) are then
* described, as per the Nexthop count. Each nexthop described as:
*
* +-+-+-+-+-+-+-+-+
@@ -405,18 +553,18 @@ zclient_connect (struct thread *t)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Alternatively, if the flags field has ZEBRA_FLAG_BLACKHOLE or
- * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_
+ * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_
* nexthop information is provided, and the message describes a prefix
* to blackhole or reject route.
*
* If ZAPI_MESSAGE_DISTANCE is set, the distance value is written as a 1
* byte value.
- *
+ *
* If ZAPI_MESSAGE_METRIC is set, the metric value is written as an 8
* byte value.
*
* XXX: No attention paid to alignment.
- */
+ */
int
zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p,
struct zapi_ipv4 *api)
@@ -428,9 +576,9 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p,
/* Reset stream. */
s = zclient->obuf;
stream_reset (s);
-
+
zclient_create_header (s, cmd);
-
+
/* Put type and nexthop. */
stream_putc (s, api->type);
stream_putc (s, api->flags);
@@ -496,7 +644,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
stream_putc (s, api->type);
stream_putc (s, api->flags);
stream_putc (s, api->message);
-
+
/* Put prefix information. */
psize = PSIZE (p->prefixlen);
stream_putc (s, p->prefixlen);
@@ -531,10 +679,10 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
}
#endif /* HAVE_IPV6 */
-/*
+/*
* send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
* for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
- * then set/unset redist[type] in the client handle (a struct zserv) for the
+ * then set/unset redist[type] in the client handle (a struct zserv) for the
* sending client
*/
int
@@ -544,12 +692,12 @@ zebra_redistribute_send (int command, struct zclient *zclient, int type)
s = zclient->obuf;
stream_reset(s);
-
+
zclient_create_header (s, command);
stream_putc (s, type);
-
+
stream_putw_at (s, 0, stream_get_endp (s));
-
+
return zclient_send_message(zclient);
}
@@ -568,7 +716,7 @@ zebra_router_id_update_read (struct stream *s, struct prefix *rid)
}
/* Interface addition from zebra daemon. */
-/*
+/*
* The format of the message sent with type ZEBRA_INTERFACE_ADD or
* ZEBRA_INTERFACE_DELETE from zebra to the client is:
* 0 1 2 3
@@ -628,11 +776,11 @@ zebra_interface_add_read (struct stream *s)
if (ifp->hw_addr_len)
stream_get (ifp->hw_addr, s, ifp->hw_addr_len);
#endif /* HAVE_STRUCT_SOCKADDR_DL */
-
+
return ifp;
}
-/*
+/*
* Read interface up/down msg (ZEBRA_INTERFACE_UP/ZEBRA_INTERFACE_DOWN)
* from zebra server. The format of this message is the same as
* that sent for ZEBRA_INTERFACE_ADD/ZEBRA_INTERFACE_DELETE (see
@@ -670,7 +818,7 @@ zebra_interface_state_read (struct stream *s)
return ifp;
}
-/*
+/*
* format of message for address additon is:
* 0
* 0 1 2 3 4 5 6 7
@@ -770,7 +918,7 @@ zebra_interface_address_read (int type, struct stream *s)
stream_get (&d.u.prefix, s, plen);
d.family = family;
- if (type == ZEBRA_INTERFACE_ADDRESS_ADD)
+ if (type == ZEBRA_INTERFACE_ADDRESS_ADD)
{
/* N.B. NULL destination pointers are encoded as all zeroes */
ifc = connected_add_by_prefix(ifp, &p,(memconstant(&d.u.prefix,0,plen) ?
@@ -791,20 +939,32 @@ zebra_interface_address_read (int type, struct stream *s)
return ifc;
}
-
+/* nexus: Zebra client message read function. */
+static void
+zclient_read_r (qps_file qf, void* file_info)
+{
+ struct zclient *zclient = file_info;
+ qps_disable_modes(qf, qps_read_mbit);
+ zclient_read(zclient);
+}
+
+/* thread: Zebra client message read function. */
+static int
+zclient_read_t (struct thread *thread)
+{
+ struct zclient *zclient = THREAD_ARG (thread);
+ zclient->t_read = NULL;
+ return zclient_read(zclient);
+}
+
/* Zebra client message read function. */
static int
-zclient_read (struct thread *thread)
+zclient_read (struct zclient *zclient)
{
int ret;
size_t already;
uint16_t length, command;
uint8_t marker, version;
- struct zclient *zclient;
-
- /* Get socket to zebra. */
- zclient = THREAD_ARG (thread);
- zclient->t_read = NULL;
/* Read zebra header (if we don't have it already). */
if ((already = stream_get_endp(zclient->ibuf)) < ZEBRA_HEADER_SIZE)
@@ -835,15 +995,15 @@ zclient_read (struct thread *thread)
marker = stream_getc (zclient->ibuf);
version = stream_getc (zclient->ibuf);
command = stream_getw (zclient->ibuf);
-
+
if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION)
{
zlog_err("%s: socket %d version mismatch, marker %d, version %d",
__func__, zclient->sock, marker, version);
return zclient_failed(zclient);
}
-
- if (length < ZEBRA_HEADER_SIZE)
+
+ if (length < ZEBRA_HEADER_SIZE)
{
zlog_err("%s: socket %d message length %u is less than %d ",
__func__, zclient->sock, length, ZEBRA_HEADER_SIZE);
@@ -952,7 +1112,7 @@ void
zclient_redistribute (int command, struct zclient *zclient, int type)
{
- if (command == ZEBRA_REDISTRIBUTE_ADD)
+ if (command == ZEBRA_REDISTRIBUTE_ADD)
{
if (zclient->redist[type])
return;
@@ -980,7 +1140,7 @@ zclient_redistribute_default (int command, struct zclient *zclient)
return;
zclient->default_information = 1;
}
- else
+ else
{
if (!zclient->default_information)
return;
@@ -991,30 +1151,78 @@ zclient_redistribute_default (int command, struct zclient *zclient)
zebra_message_send (zclient, command);
}
+/* Arm event. */
static void
zclient_event (enum event event, struct zclient *zclient)
{
+ if (zclient_nexus)
+ zclient_event_r(event, zclient);
+ else
+ zclient_event_t(event, zclient);
+}
+
+/* Arm event.
+ * nexus version */
+static void
+zclient_event_r (enum event event, struct zclient *zclient)
+{
switch (event)
{
+ case ZLOOKUP_SCHEDULE:
+ if (!zclient->qtr->active)
+ qtimer_set(zclient->qtr, qt_get_monotonic(), zlookup_connect_r) ;
+ break;
case ZCLIENT_SCHEDULE:
- if (! zclient->t_connect)
- zclient->t_connect =
- thread_add_event (master, zclient_connect, zclient, 0);
+ if (!zclient->qtr->active)
+ qtimer_set(zclient->qtr, qt_get_monotonic(), zclient_connect_r) ;
break;
case ZCLIENT_CONNECT:
if (zclient->fail >= 10)
return;
if (zclient_debug)
- zlog_debug ("zclient connect schedule interval is %d",
+ zlog_debug ("zclient connect schedule interval is %d",
zclient->fail < 3 ? 10 : 60);
+ if (!zclient->qtr->active)
+ qtimer_set(zclient->qtr,
+ qt_add_monotonic(QTIME(zclient->fail < 3 ? 10 : 60)), zclient_connect_r) ;
+ break;
+ case ZCLIENT_READ:
+ qps_enable_mode(zclient->qf, qps_read_mnum, zclient_read_r) ;
+ break;
+ }
+}
+
+/* Arm event.
+ * thread version */
+static void
+zclient_event_t (enum event event, struct zclient *zclient)
+{
+ switch (event)
+ {
+ case ZLOOKUP_SCHEDULE:
+ if (! zclient->t_connect)
+ zclient->t_connect =
+ thread_add_event (master, zlookup_connect_t, zclient, 0);
+ break;
+ case ZCLIENT_SCHEDULE:
+ if (! zclient->t_connect)
+ zclient->t_connect =
+ thread_add_event (master, zclient_connect_t, zclient, 0);
+ break;
+ case ZCLIENT_CONNECT:
+ if (zclient->fail >= 10)
+ return;
+ if (zclient_debug)
+ zlog_debug ("zclient connect schedule interval is %d",
+ zclient->fail < 3 ? 10 : 60);
if (! zclient->t_connect)
- zclient->t_connect =
- thread_add_timer (master, zclient_connect, zclient,
- zclient->fail < 3 ? 10 : 60);
+ zclient->t_connect =
+ thread_add_timer (master, zclient_connect_t, zclient,
+ zclient->fail < 3 ? 10 : 60);
break;
case ZCLIENT_READ:
- zclient->t_read =
- thread_add_read (master, zclient_read, zclient, zclient->sock);
+ zclient->t_read =
+ thread_add_read (master, zclient_read_t, zclient, zclient->sock);
break;
}
}
diff --git a/lib/zclient.h b/lib/zclient.h
index 21786ab8..17f4e317 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -7,12 +7,12 @@
* 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,
@@ -24,6 +24,8 @@
/* For struct interface and struct connected. */
#include "if.h"
+#include "qpnexus.h"
+#include "prefix.h"
/* For input/output buffer to zebra. */
#define ZEBRA_MAX_PACKET_SIZ 4096
@@ -60,6 +62,10 @@ struct zclient
/* Thread to write buffered data to zebra. */
struct thread *t_write;
+ /* If using nexus, qfile and qtimer */
+ qps_file qf;
+ qtimer qtr;
+
/* Redistribute information. */
u_char redist_default;
u_char redist[ZEBRA_ROUTE_MAX];
@@ -121,11 +127,13 @@ struct zapi_ipv4
/* Prototypes of zebra client service functions. */
extern struct zclient *zclient_new (void);
+extern void zclient_init_r (qpn_nexus);
extern void zclient_init (struct zclient *, int);
extern int zclient_start (struct zclient *);
extern void zclient_stop (struct zclient *);
extern void zclient_reset (struct zclient *);
extern void zclient_free (struct zclient *);
+extern void zlookup_schedule(struct zclient *);
/* Get TCP socket connection to zebra daemon at loopback address. */
extern int zclient_socket (void);
@@ -154,7 +162,7 @@ extern struct interface *zebra_interface_state_read (struct stream *s);
extern struct connected *zebra_interface_address_read (int, struct stream *);
extern void zebra_interface_if_set_value (struct stream *, struct interface *);
extern void zebra_router_id_update_read (struct stream *s, struct prefix *rid);
-extern int zapi_ipv4_route (u_char, struct zclient *, struct prefix_ipv4 *,
+extern int zapi_ipv4_route (u_char, struct zclient *, struct prefix_ipv4 *,
struct zapi_ipv4 *);
#ifdef HAVE_IPV6
@@ -179,7 +187,7 @@ struct zapi_ipv6
u_int32_t metric;
};
-extern int zapi_ipv6_route (u_char cmd, struct zclient *zclient,
+extern int zapi_ipv6_route (u_char cmd, struct zclient *zclient,
struct prefix_ipv6 *p, struct zapi_ipv6 *api);
#endif /* HAVE_IPV6 */
diff --git a/lib/zebra.h b/lib/zebra.h
index 2dc84514..799cfc3d 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,36 +521,10 @@ 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;
-/* FIFO -- first in first out structure and macros. */
-struct fifo
-{
- struct fifo *next;
- struct fifo *prev;
-};
-
-#define FIFO_INIT(F) \
- do { \
- struct fifo *Xfifo = (struct fifo *)(F); \
- Xfifo->next = Xfifo->prev = Xfifo; \
- } while (0)
-
-#define FIFO_ADD(F,N) \
- do { \
- struct fifo *Xfifo = (struct fifo *)(F); \
- struct fifo *Xnode = (struct fifo *)(N); \
- Xnode->next = Xfifo; \
- Xnode->prev = Xfifo->prev; \
- Xfifo->prev = Xfifo->prev->next = Xnode; \
- } while (0)
-
#define FIFO_DEL(N) \
do { \
struct fifo *Xnode = (struct fifo *)(N); \
@@ -567,14 +532,4 @@ struct fifo
Xnode->next->prev = Xnode->prev; \
} while (0)
-#define FIFO_HEAD(F) \
- ((((struct fifo *)(F))->next == (struct fifo *)(F)) \
- ? NULL : (F)->next)
-
-#define FIFO_EMPTY(F) \
- (((struct fifo *)(F))->next == (struct fifo *)(F))
-
-#define FIFO_TOP(F) \
- (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next)
-
#endif /* _ZEBRA_H */
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index d38ef21a..e03ec842 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -162,7 +162,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route,
if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE (INTER_ROUTER))
{
is_debug++;
- inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)),
+ inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (route->prefix)),
buf, sizeof (buf));
zlog_debug ("Originating summary in area %s for ASBR %s",
area->name, buf);
@@ -355,7 +355,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route,
{
if (is_debug)
{
- inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)),
+ inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (route->prefix)),
buf, sizeof(buf));
zlog_debug ("prefix %s was denied by export list", buf);
}
@@ -376,7 +376,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route,
{
if (is_debug)
{
- inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)),
+ inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (route->prefix)),
buf, sizeof (buf));
zlog_debug ("prefix %s was denied by filter-list out", buf);
}
@@ -430,7 +430,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route,
router_lsa->options[1] = route->path.options[1];
router_lsa->options[2] = route->path.options[2];
OSPF6_ABR_SUMMARY_METRIC_SET (router_lsa, route->path.cost);
- router_lsa->router_id = ADV_ROUTER_IN_PREFIX (&route->prefix);
+ router_lsa->router_id = ADV_ROUTER_IN_PREFIX (route->prefix);
type = htons (OSPF6_LSTYPE_INTER_ROUTER);
}
else
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 3efaab44..b1351bff 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -302,14 +302,14 @@ ospf6_asbr_lsentry_add (struct ospf6_route *asbr_entry)
if (! CHECK_FLAG (asbr_entry->flag, OSPF6_ROUTE_BEST))
{
char buf[16];
- inet_ntop (AF_INET, &ADV_ROUTER_IN_PREFIX (&asbr_entry->prefix),
+ inet_ntop (AF_INET, &ADV_ROUTER_IN_PREFIX (asbr_entry->prefix),
buf, sizeof (buf));
zlog_info ("ignore non-best path: lsentry %s add", buf);
return;
}
type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
- router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix);
+ router = ospf6_linkstate_prefix_adv_router (asbr_entry->prefix);
for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb); lsa;
lsa = ospf6_lsdb_type_router_next (type, router, lsa))
{
@@ -326,7 +326,7 @@ ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry)
u_int32_t router;
type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
- router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix);
+ router = ospf6_linkstate_prefix_adv_router (asbr_entry->prefix);
for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb);
lsa; lsa = ospf6_lsdb_type_router_next (type, router, lsa))
ospf6_asbr_lsa_remove (lsa);
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
index 6deb93ef..d7bad420 100644
--- a/ospf6d/ospf6_asbr.h
+++ b/ospf6d/ospf6_asbr.h
@@ -14,14 +14,19 @@
* 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.
+ * 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 OSPF6_ASBR_H
#define OSPF6_ASBR_H
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_route.h"
+#include "ospf6_top.h"
+
/* Debug option */
extern unsigned char conf_debug_ospf6_asbr;
#define OSPF6_DEBUG_ASBR_ON() \
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index 9bc603b3..9d94f408 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -1252,7 +1252,7 @@ ospf6_brouter_debug_print (struct ospf6_route *brouter)
char id[16], adv_router[16];
char capa[16], options[16];
- brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+ brouter_id = ADV_ROUTER_IN_PREFIX (brouter->prefix);
inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
inet_ntop (AF_INET, &brouter->path.area_id, area_name, sizeof (area_name));
ospf6_linkstate_prefix2str (&brouter->prefix, destination,
@@ -1313,7 +1313,7 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa)
for (brouter = ospf6_route_head (oa->ospf6->brouter_table); brouter;
brouter = ospf6_route_next (brouter))
{
- brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+ brouter_id = ADV_ROUTER_IN_PREFIX (brouter->prefix);
inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
if (brouter->path.area_id != oa->area_id)
continue;
@@ -1331,12 +1331,12 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa)
for (brouter = ospf6_route_head (oa->spf_table); brouter;
brouter = ospf6_route_next (brouter))
{
- brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+ brouter_id = ADV_ROUTER_IN_PREFIX (brouter->prefix);
inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
if (brouter->type != OSPF6_DEST_TYPE_LINKSTATE)
continue;
- if (ospf6_linkstate_prefix_id (&brouter->prefix) != htonl (0))
+ if (ospf6_linkstate_prefix_id (brouter->prefix) != htonl (0))
continue;
if (! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_E) &&
! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_B))
@@ -1362,7 +1362,7 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa)
for (brouter = ospf6_route_head (oa->ospf6->brouter_table); brouter;
brouter = ospf6_route_next (brouter))
{
- brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+ brouter_id = ADV_ROUTER_IN_PREFIX (brouter->prefix);
inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
if (brouter->path.area_id != oa->area_id)
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
index 800fae4b..cd00fa86 100644
--- a/ospf6d/ospf6_main.c
+++ b/ospf6d/ospf6_main.c
@@ -14,13 +14,14 @@
* 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.
+ * 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 <lib/version.h>
+#include <lib/zclient.h>
#include "getopt.h"
#include "thread.h"
@@ -36,6 +37,8 @@
#include "sigevent.h"
#include "ospf6d.h"
+#include "ospf6_asbr.h"
+#include "ospf6_message.h"
/* Default configuration file name for ospf6d. */
#define OSPF6_DEFAULT_CONFIG "ospf6d.conf"
@@ -67,7 +70,7 @@ struct zebra_privs_t ospf6d_privs =
};
/* ospf6d options, we use GNU getopt library. */
-struct option longopts[] =
+struct option longopts[] =
{
{ "daemon", no_argument, NULL, 'd'},
{ "config_file", required_argument, NULL, 'f'},
@@ -104,7 +107,7 @@ usage (char *progname, int status)
if (status != 0)
fprintf (stderr, "Try `%s --help' for more information.\n", progname);
else
- {
+ {
printf ("Usage : %s [OPTION...]\n\n\
Daemon which manages OSPF version 3.\n\n\
-d, --daemon Runs in daemon mode\n\
@@ -154,7 +157,7 @@ ospf6_exit (int status)
}
/* SIGHUP handler. */
-static void
+static void
sighup (void)
{
zlog_info ("SIGHUP received");
@@ -224,14 +227,14 @@ main (int argc, char *argv[], char *envp[])
progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
/* Command line argument treatment. */
- while (1)
+ while (1)
{
opt = getopt_long (argc, argv, "df:i:hp:A:P:u:g:vC", longopts, 0);
-
+
if (opt == EOF)
break;
- switch (opt)
+ switch (opt)
{
case 0:
break;
@@ -310,7 +313,7 @@ main (int argc, char *argv[], char *envp[])
/* Start execution only if not in dry-run mode */
if (dryrun)
return(0);
-
+
if (daemon_mode && daemon (0, 0) < 0)
{
zlog_err("OSPF6d daemon failed: %s", strerror(errno));
diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c
index 96b82af3..d9783385 100644
--- a/ospf6d/ospf6_network.c
+++ b/ospf6d/ospf6_network.c
@@ -96,7 +96,7 @@ ospf6_serv_sock (void)
/* set socket options */
#if 1
- sockopt_reuseaddr (ospf6_sock);
+ setsockopt_reuseaddr (ospf6_sock);
#else
ospf6_set_reuseaddr ();
#endif /*1*/
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 398acfa8..b0ecea6c 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -1071,7 +1071,7 @@ ospf6_route_show_table (struct vty *vty, int detail,
}
int
-ospf6_route_table_show (struct vty *vty, int argc, const char *argv[],
+ospf6_route_table_show (struct vty *vty, int argc, argv_t argv,
struct ospf6_route_table *table)
{
int summary = 0;
@@ -1184,9 +1184,9 @@ ospf6_linkstate_show (struct vty *vty, struct ospf6_route *route)
u_int32_t router, id;
char routername[16], idname[16], rbits[16], options[16];
- router = ospf6_linkstate_prefix_adv_router (&route->prefix);
+ router = ospf6_linkstate_prefix_adv_router (route->prefix);
inet_ntop (AF_INET, &router, routername, sizeof (routername));
- id = ospf6_linkstate_prefix_id (&route->prefix);
+ id = ospf6_linkstate_prefix_id (route->prefix);
inet_ntop (AF_INET, &id, idname, sizeof (idname));
ospf6_capability_printbuf (route->path.router_bits, rbits, sizeof (rbits));
@@ -1246,7 +1246,7 @@ ospf6_linkstate_show_table (struct vty *vty, int detail,
}
int
-ospf6_linkstate_table_show (struct vty *vty, int argc, const char *argv[],
+ospf6_linkstate_table_show (struct vty *vty, int argc, argv_t argv,
struct ospf6_route_table *table)
{
int detail = 0;
@@ -1321,7 +1321,7 @@ ospf6_brouter_show (struct vty *vty, struct ospf6_route *route)
u_int32_t adv_router;
char adv[16], rbits[16], options[16], area[16];
- adv_router = ospf6_linkstate_prefix_adv_router (&route->prefix);
+ adv_router = ospf6_linkstate_prefix_adv_router (route->prefix);
inet_ntop (AF_INET, &adv_router, adv, sizeof (adv));
ospf6_capability_printbuf (route->path.router_bits, rbits, sizeof (rbits));
ospf6_options_printbuf (route->path.options, options, sizeof (options));
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index 8dcc877f..22ecd3ff 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -235,17 +235,21 @@ extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX];
sizeof (struct ospf6_nexthop) * OSPF6_MULTI_PATH_LIMIT) == 0)
#define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST))
-#define ospf6_linkstate_prefix_adv_router(x) \
- (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[0]))
-#define ospf6_linkstate_prefix_id(x) \
- (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[4]))
+#ifdef s6_addr32
+#define OSPF6_PREFIX_PART(x, n) ((x).u.prefix6.s6_addr32[n])
+#else
+#define OSPF6_PREFIX_PART(x, n) ( ( (uint32_t*)((x).u.prefix6.s6_addr) )[n] )
+#endif
-#define ADV_ROUTER_IN_PREFIX(x) \
- (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[0]))
-#define ID_IN_PREFIX(x) \
- (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[4]))
+#define ospf6_linkstate_prefix_adv_router(x) OSPF6_PREFIX_PART(x, 0)
+#define ospf6_linkstate_prefix_id(x) OSPF6_PREFIX_PART(x, 1)
+
+#define ADV_ROUTER_IN_PREFIX(x) OSPF6_PREFIX_PART(x, 0)
+#define ID_IN_PREFIX(x) OSPF6_PREFIX_PART(x, 1)
/* Function prototype */
+#include "command.h"
+
extern void ospf6_linkstate_prefix (u_int32_t adv_router, u_int32_t id,
struct prefix *prefix);
extern void ospf6_linkstate_prefix2str (struct prefix *prefix, char *buf,
@@ -288,10 +292,10 @@ extern void ospf6_route_dump (struct ospf6_route_table *table);
extern void ospf6_route_show (struct vty *vty, struct ospf6_route *route);
extern void ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route);
-extern int ospf6_route_table_show (struct vty *, int, const char *[],
+extern int ospf6_route_table_show (struct vty *, int, argv_t,
struct ospf6_route_table *);
extern int ospf6_linkstate_table_show (struct vty *vty, int argc,
- const char *argv[],
+ argv_t argv,
struct ospf6_route_table *table);
extern void ospf6_brouter_show_header (struct vty *vty);
diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c
index 5ac7846d..25572a7c 100644
--- a/ospf6d/ospf6_snmp.c
+++ b/ospf6d/ospf6_snmp.c
@@ -306,6 +306,7 @@ ospfv3AreaEntry (struct variable *v, oid *name, size_t *length,
{
struct ospf6_area *oa, *area = NULL;
u_int32_t area_id = 0;
+ struct in_addr in_area_id ;
struct listnode *node;
unsigned int len;
@@ -319,9 +320,9 @@ ospfv3AreaEntry (struct variable *v, oid *name, size_t *length,
if (len)
oid2in_addr (name + v->namelen, len, (struct in_addr *) &area_id);
+ in_area_id.s_addr = area_id ;
zlog_debug ("SNMP access by area: %s, exact=%d len=%d length=%lu",
- inet_ntoa (* (struct in_addr *) &area_id),
- exact, len, (u_long)*length);
+ inet_ntoa (in_area_id), exact, len, (u_long)*length);
for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
{
@@ -398,7 +399,7 @@ ospfv3AreaLsdbEntry (struct variable *v, oid *name, size_t *length,
return NULL;
/* Parse area-id */
- len = (offsetlen < IN_ADDR_SIZE ? offsetlen : IN_ADDR_SIZE);
+ len = (offsetlen < (int)IN_ADDR_SIZE ? offsetlen : (int)IN_ADDR_SIZE);
if (len)
oid2in_addr (offset, len, &area_id);
offset += len;
@@ -412,14 +413,14 @@ ospfv3AreaLsdbEntry (struct variable *v, oid *name, size_t *length,
offsetlen -= len;
/* Parse Router-ID */
- len = (offsetlen < IN_ADDR_SIZE ? offsetlen : IN_ADDR_SIZE);
+ len = (offsetlen < (int)IN_ADDR_SIZE ? offsetlen : (int)IN_ADDR_SIZE);
if (len)
oid2in_addr (offset, len, &adv_router);
offset += len;
offsetlen -= len;
/* Parse LS-ID */
- len = (offsetlen < IN_ADDR_SIZE ? offsetlen : IN_ADDR_SIZE);
+ len = (offsetlen < (int)IN_ADDR_SIZE ? offsetlen : (int)IN_ADDR_SIZE);
if (len)
oid2in_addr (offset, len, &id);
offset += len;
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index cb549618..25b86007 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -14,9 +14,9 @@
* 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.
+ * 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.
*/
/* Shortest Path First calculation for OSPFv3 */
@@ -62,13 +62,13 @@ ospf6_vertex_id_cmp (void *a, void *b)
struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
int ret = 0;
- ret = ntohl (ospf6_linkstate_prefix_adv_router (&va->vertex_id)) -
- ntohl (ospf6_linkstate_prefix_adv_router (&vb->vertex_id));
+ ret = ntohl (ospf6_linkstate_prefix_adv_router (va->vertex_id)) -
+ ntohl (ospf6_linkstate_prefix_adv_router (vb->vertex_id));
if (ret)
return ret;
- ret = ntohl (ospf6_linkstate_prefix_id (&va->vertex_id)) -
- ntohl (ospf6_linkstate_prefix_id (&vb->vertex_id));
+ ret = ntohl (ospf6_linkstate_prefix_id (va->vertex_id)) -
+ ntohl (ospf6_linkstate_prefix_id (vb->vertex_id));
return ret;
}
@@ -282,8 +282,8 @@ ospf6_spf_install (struct ospf6_vertex *v,
{
struct ospf6_route *route;
int i, j;
- struct ospf6_vertex *prev, *w;
- struct listnode *node, *nnode;
+ struct ospf6_vertex *prev; //, *w;
+//struct listnode *node, *nnode;
if (IS_OSPF6_DEBUG_SPF (PROCESS))
zlog_debug ("SPF install %s hops %d cost %d",
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index 2b65be82..bc0b477e 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -14,9 +14,9 @@
* 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.
+ * 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>
@@ -98,7 +98,7 @@ ospf6_top_route_hook_remove (struct ospf6_route *route)
static void
ospf6_top_brouter_hook_add (struct ospf6_route *route)
{
- ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (&route->prefix));
+ ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (route->prefix));
ospf6_asbr_lsentry_add (route);
ospf6_abr_originate_summary (route);
}
@@ -106,7 +106,7 @@ ospf6_top_brouter_hook_add (struct ospf6_route *route)
static void
ospf6_top_brouter_hook_remove (struct ospf6_route *route)
{
- ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (&route->prefix));
+ ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (route->prefix));
ospf6_asbr_lsentry_remove (route);
ospf6_abr_originate_summary (route);
}
@@ -192,7 +192,7 @@ ospf6_disable (struct ospf6 *o)
if (! CHECK_FLAG (o->flag, OSPF6_DISABLED))
{
SET_FLAG (o->flag, OSPF6_DISABLED);
-
+
for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa))
ospf6_area_disable (oa);
@@ -232,7 +232,7 @@ ospf6_maxage_remover (struct thread *thread)
{
for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
OSPF6_LSDB_MAXAGE_REMOVER (oi->lsdb);
-
+
OSPF6_LSDB_MAXAGE_REMOVER (oa->lsdb);
}
OSPF6_LSDB_MAXAGE_REMOVER (o->lsdb);
@@ -523,9 +523,11 @@ DEFUN (show_ipv6_ospf6_route_match,
"Display routes which match the specified route\n"
)
{
- const char *sargv[CMD_ARGC_MAX];
+ const char** sargv ;
int i, sargc;
+ sargv = XMALLOC(MTYPE_TMP, (argc + 2) * sizeof(char*)) ;
+
/* copy argv to sargv and then append "match" */
for (i = 0; i < argc; i++)
sargv[i] = argv[i];
@@ -534,6 +536,8 @@ DEFUN (show_ipv6_ospf6_route_match,
sargv[sargc] = NULL;
ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table);
+
+ XFREE(MTYPE_TMP, sargv) ;
return CMD_SUCCESS;
}
@@ -549,9 +553,11 @@ DEFUN (show_ipv6_ospf6_route_match_detail,
"Detailed information\n"
)
{
- const char *sargv[CMD_ARGC_MAX];
+ const char** sargv ;
int i, sargc;
+ sargv = XMALLOC(MTYPE_TMP, (argc + 2) * sizeof(char*)) ;
+
/* copy argv to sargv and then append "match" and "detail" */
for (i = 0; i < argc; i++)
sargv[i] = argv[i];
@@ -561,6 +567,7 @@ DEFUN (show_ipv6_ospf6_route_match_detail,
sargv[sargc] = NULL;
ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table);
+ XFREE(MTYPE_TMP, sargv) ;
return CMD_SUCCESS;
}
@@ -614,9 +621,11 @@ DEFUN (show_ipv6_ospf6_route_type_detail,
"Detailed information\n"
)
{
- const char *sargv[CMD_ARGC_MAX];
+ const char** sargv ;
int i, sargc;
+ sargv = XMALLOC(MTYPE_TMP, (argc + 2) * sizeof(char*)) ;
+
/* copy argv to sargv and then append "detail" */
for (i = 0; i < argc; i++)
sargv[i] = argv[i];
@@ -625,6 +634,7 @@ DEFUN (show_ipv6_ospf6_route_type_detail,
sargv[sargc] = NULL;
ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table);
+ XFREE(MTYPE_TMP, sargv) ;
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c
index bb091d4f..0b7a1ff5 100644
--- a/ospf6d/ospf6d.c
+++ b/ospf6d/ospf6d.c
@@ -14,9 +14,9 @@
* 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.
+ * 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>
@@ -77,7 +77,7 @@ route_prev (struct route_node *node)
return prev;
}
-
+
/* show database functions */
DEFUN (show_version_ospf6,
show_version_ospf6_cmd,
@@ -125,7 +125,7 @@ config_write_ospf6_debug (struct vty *vty)
"%s AS Scoped Link State Database%s%s"
static int
-parse_show_level (int argc, const char *argv[])
+parse_show_level (int argc, argv_t argv)
{
int level = 0;
if (argc)
@@ -143,7 +143,7 @@ parse_show_level (int argc, const char *argv[])
}
static u_int16_t
-parse_type_spec (int argc, const char *argv[])
+parse_type_spec (int argc, argv_t argv)
{
u_int16_t type = 0;
assert (argc);
@@ -1733,12 +1733,14 @@ DEFUN (show_ipv6_ospf6_linkstate_detail,
"Display linkstate routing table\n"
)
{
- const char *sargv[CMD_ARGC_MAX];
+ const char** sargv;
int i, sargc;
struct listnode *node;
struct ospf6_area *oa;
- /* copy argv to sargv and then append "detail" */
+ sargv = XMALLOC(MTYPE_TMP, (argc + 2) * sizeof(char*)) ;
+
+ /* copy argv to sargv and then append "detail" */
for (i = 0; i < argc; i++)
sargv[i] = argv[i];
sargc = argc;
@@ -1753,6 +1755,7 @@ DEFUN (show_ipv6_ospf6_linkstate_detail,
}
vty_out (vty, "%s", VNL);
+ XFREE(MTYPE_TMP, sargv) ;
return CMD_SUCCESS;
}
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index 7a75194a..01da8808 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -615,6 +615,10 @@ set_metric (struct ospf_lsa *lsa, u_int32_t metric)
static int
ospf_abr_check_nssa_range (struct prefix_ipv4 *p, u_int32_t cost,
+ struct ospf_area *area)
+ __attribute__((unused)) ;
+static int
+ospf_abr_check_nssa_range (struct prefix_ipv4 *p, u_int32_t cost,
struct ospf_area *area)
{
/* The Type-7 is tested against the aggregated prefix and forwarded
@@ -1577,6 +1581,9 @@ ospf_abr_send_nssa_aggregates (struct ospf *ospf) /* temporarily turned off */
}
static void
+ospf_abr_announce_nssa_defaults (struct ospf *ospf) __attribute__((unused)) ;
+
+static void
ospf_abr_announce_nssa_defaults (struct ospf *ospf) /* By ABR-Translator */
{
struct listnode *node;
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index 2a9003b7..a964e690 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -3,7 +3,7 @@
* Copyright (C) 2001, 2002 Ralph Keller
*
* 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
@@ -38,7 +38,8 @@
#include "log.h"
#include "thread.h"
#include "hash.h"
-#include "sockunion.h" /* for inet_aton() */
+#include "sockunion.h" /* for inet_aton() */
+#include "sockopt.h"
#include "buffer.h"
#include <sys/types.h>
@@ -150,7 +151,7 @@ ospf_apiserver_init (void)
/* Register opaque-independent call back functions. These functions
are invoked on ISM, NSM changes and LSA update and LSA deletes */
rc =
- ospf_register_opaque_functab (0 /* all LSAs */,
+ ospf_register_opaque_functab (0 /* all LSAs */,
0 /* all opaque types */,
ospf_apiserver_new_if,
ospf_apiserver_del_if,
@@ -182,7 +183,7 @@ ospf_apiserver_term (void)
struct ospf_apiserver *apiserv;
/* Unregister wildcard [0/0] type */
- ospf_delete_opaque_functab (0 /* all LSAs */,
+ ospf_delete_opaque_functab (0 /* all LSAs */,
0 /* all opaque types */);
/*
@@ -335,7 +336,7 @@ ospf_apiserver_event (enum event event, int fd,
}
/* Free instance. First unregister all opaque types used by
- application, flush opaque LSAs injected by application
+ application, flush opaque LSAs injected by application
from network and close connection. */
void
ospf_apiserver_free (struct ospf_apiserver *apiserv)
@@ -363,7 +364,7 @@ ospf_apiserver_free (struct ospf_apiserver *apiserv)
thread_cancel (apiserv->t_async_write);
}
- /* Unregister all opaque types that application registered
+ /* Unregister all opaque types that application registered
and flush opaque LSAs if still in LSDB. */
while ((node = listhead (apiserv->opaque_types)) != NULL)
@@ -528,7 +529,7 @@ ospf_apiserver_sync_write (struct thread *thread)
ospf_apiserver_event (OSPF_APISERVER_SYNC_WRITE, apiserv->fd_sync,
apiserv);
}
-
+
out:
if (rc < 0)
@@ -617,8 +618,7 @@ ospf_apiserver_serv_sock_family (unsigned short port, int family)
int accept_sock;
int rc;
- memset (&su, 0, sizeof (union sockunion));
- su.sa.sa_family = family;
+ sockunion_init_new(&su, family) ;
/* Make new socket */
accept_sock = sockunion_stream_socket (&su);
@@ -626,11 +626,11 @@ ospf_apiserver_serv_sock_family (unsigned short port, int family)
return accept_sock;
/* This is a server, so reuse address and port */
- sockopt_reuseaddr (accept_sock);
- sockopt_reuseport (accept_sock);
+ setsockopt_reuseaddr (accept_sock);
+ setsockopt_reuseport (accept_sock);
/* Bind socket to address and given port. */
- rc = sockunion_bind (accept_sock, &su, port, NULL);
+ rc = sockunion_bind (accept_sock, &su, port, true); /* true => any */
if (rc < 0)
{
close (accept_sock); /* Close socket */
@@ -906,7 +906,7 @@ ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserv,
lsa_type);
return OSPF_API_ILLEGALLSATYPE;
}
-
+
/* Register opaque function table */
/* NB: Duplicated registration will be detected inside the function. */
@@ -946,8 +946,8 @@ ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserv,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("API: Add LSA-type(%d)/Opaque-type(%d) into"
- " apiserv(%p), total#(%d)",
- lsa_type, opaque_type, apiserv,
+ " apiserv(%p), total#(%d)",
+ lsa_type, opaque_type, apiserv,
listcount (apiserv->opaque_types));
return 0;
@@ -978,8 +978,8 @@ ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserv,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("API: Del LSA-type(%d)/Opaque-type(%d)"
- " from apiserv(%p), total#(%d)",
- lsa_type, opaque_type, apiserv,
+ " from apiserv(%p), total#(%d)",
+ lsa_type, opaque_type, apiserv,
listcount (apiserv->opaque_types));
return 0;
@@ -1114,13 +1114,13 @@ ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv)
struct listnode *node2, *nnode2;
struct ospf *ospf;
struct ospf_area *area;
-
+
ospf = ospf_lookup ();
for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
{
struct registered_opaque_type *r;
-
+
if (!ospf_apiserver_is_ready_type10 (area))
{
continue;
@@ -1131,7 +1131,7 @@ ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv)
for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r))
{
struct msg *msg;
-
+
if (r->lsa_type == OSPF_OPAQUE_AREA_LSA)
{
/* Yes, this opaque type is ready */
@@ -1176,7 +1176,7 @@ ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv)
{
struct msg *msg;
struct in_addr noarea_id = { .s_addr = 0L };
-
+
if (r->lsa_type == OSPF_OPAQUE_AS_LSA)
{
/* Yes, this opaque type is ready */
@@ -1301,7 +1301,7 @@ apiserver_sync_callback (struct ospf_lsa *lsa, void *p_arg, int int_arg)
/* Default interface for non Opaque9 LSAs */
struct in_addr ifaddr = { .s_addr = 0L };
-
+
if (lsa->area)
{
area_id = lsa->area->area_id;
@@ -1497,7 +1497,7 @@ ospf_apiserver_opaque_lsa_new (struct ospf_area *area,
}
/* Set opaque-LSA header fields. */
- lsa_header_set (s, options, protolsa->type, protolsa->id,
+ lsa_header_set (s, options, protolsa->type, protolsa->id,
ospf->router_id);
/* Set opaque-LSA body fields. */
@@ -1596,7 +1596,7 @@ ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv,
int lsa_type, opaque_type;
int ready = 0;
int rc = 0;
-
+
ospf = ospf_lookup();
/* Extract opaque LSA data from message */
@@ -1713,12 +1713,12 @@ out:
/* -----------------------------------------------------------
- * Flood an LSA within its flooding scope.
+ * Flood an LSA within its flooding scope.
* -----------------------------------------------------------
*/
/* XXX We can probably use ospf_flood_through instead of this function
- but then we need the neighbor parameter. If we set nbr to
+ but then we need the neighbor parameter. If we set nbr to
NULL then ospf_flood_through crashes due to dereferencing NULL. */
void
@@ -1831,7 +1831,7 @@ ospf_apiserver_lsa11_originator (void *arg)
/* Periodically refresh opaque LSAs so that they do not expire in
other routers. */
-struct ospf_lsa *
+extern struct ospf_lsa *
ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa)
{
struct ospf_apiserver *apiserv;
@@ -2041,7 +2041,7 @@ ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv,
struct listnode *node, *nnode;
struct ospf * ospf;
struct ospf_area *area;
-
+
ospf = ospf_lookup();
assert(ospf);
@@ -2077,7 +2077,7 @@ ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv,
/* -----------------------------------------------------------
- * Followings are callback functions to handle opaque types
+ * Followings are callback functions to handle opaque types
* -----------------------------------------------------------
*/
@@ -2108,10 +2108,10 @@ ospf_apiserver_new_if (struct interface *ifp)
}
oi = ospf_apiserver_if_lookup_by_ifp (ifp);
-
+
if (!oi) {
/* This interface is known to Zebra but not to OSPF daemon yet. */
- zlog_warn ("ospf_apiserver_new_if: interface %s not known to OSPFd?",
+ zlog_warn ("ospf_apiserver_new_if: interface %s not known to OSPFd?",
ifp->name);
return 0;
}
@@ -2359,9 +2359,9 @@ ospf_apiserver_clients_notify_ready_type11 (struct ospf *top)
struct msg *msg;
struct in_addr id_null = { .s_addr = 0L };
struct ospf_apiserver *apiserv;
-
+
assert (top);
-
+
if (!ospf_apiserver_is_ready_type11 (top))
{
zlog_warn ("AS not ready for type 11?");
@@ -2432,10 +2432,10 @@ ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi)
struct msg *msg;
struct in_addr ifaddr = { .s_addr = 0L };
struct in_addr area_id = { .s_addr = 0L };
-
+
assert (oi);
assert (oi->ifp);
-
+
if (oi->address)
{
ifaddr = oi->address->u.prefix4;
diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h
index b60f56b4..45716d9d 100644
--- a/ospfd/ospf_apiserver.h
+++ b/ospfd/ospf_apiserver.h
@@ -3,7 +3,7 @@
* Copyright (C) 2001, 2002 Ralph Keller
*
* 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
@@ -168,7 +168,7 @@ extern void ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa);
/* -----------------------------------------------------------
- * Followings are callback functions to handle opaque types
+ * Followings are callback functions to handle opaque types
* -----------------------------------------------------------
*/
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index 3c199311..8ef545c2 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -135,7 +135,6 @@ ospf_ase_complete_direct_routes (struct ospf_route *ro, struct in_addr nexthop)
{
struct listnode *node;
struct ospf_path *op;
- struct interface *ifp;
for (ALL_LIST_ELEMENTS_RO (ro->paths, node, op))
if (op->nexthop.s_addr == 0)
@@ -451,6 +450,7 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
/* if there is a Intra/Inter area route to the N
do not install external route */
+ if ( (rn = route_node_lookup (ospf->new_table, (struct prefix *) &p)) )
if ((rn = route_node_lookup (ospf->new_table,
(struct prefix *) &p)))
{
diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c
index 1e2d44e6..dc06cb61 100644
--- a/ospfd/ospf_network.c
+++ b/ospfd/ospf_network.c
@@ -214,7 +214,7 @@ ospf_sock_init (void)
zlog_warn ("IP_HDRINCL option not available");
#endif /* IP_HDRINCL */
- ret = setsockopt_ifindex (AF_INET, ospf_sock, 1);
+ ret = setsockopt_pktinfo (AF_INET, ospf_sock, 1);
if (ret < 0)
zlog_warn ("Can't set pktinfo option for fd %d", ospf_sock);
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index aa126e19..f82927ef 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -9,7 +9,7 @@
* 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
@@ -68,7 +68,7 @@
#ifdef SUPPORT_OSPF_API
int ospf_apiserver_init (void);
-void ospf_apiserver_term (void);
+void ospf_apiserver_term (void);
/* Init apiserver? It's disabled by default. */
int ospf_apiserver_enable;
#endif /* SUPPORT_OSPF_API */
@@ -158,7 +158,7 @@ void
ospf_opaque_type10_lsa_term (struct ospf_area *area)
{
#ifdef MONITOR_LSDB_CHANGE
- area->lsdb->new_lsa_hook =
+ area->lsdb->new_lsa_hook =
area->lsdb->del_lsa_hook = NULL;
#endif /* MONITOR_LSDB_CHANGE */
@@ -190,7 +190,7 @@ void
ospf_opaque_type11_lsa_term (struct ospf *top)
{
#ifdef MONITOR_LSDB_CHANGE
- top->lsdb->new_lsa_hook =
+ top->lsdb->new_lsa_hook =
top->lsdb->del_lsa_hook = NULL;
#endif /* MONITOR_LSDB_CHANGE */
@@ -373,12 +373,12 @@ ospf_register_opaque_functab (
{
struct listnode *node, *nnode;
struct ospf_opaque_functab *functab;
-
+
for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
if (functab->opaque_type == opaque_type)
{
zlog_warn ("ospf_register_opaque_functab: Duplicated entry?:"
- " lsa_type(%u), opaque_type(%u)",
+ " lsa_type(%u), opaque_type(%u)",
lsa_type, opaque_type);
goto out;
}
@@ -881,7 +881,7 @@ opaque_lsa_nsm_change_callback (struct list *funclist,
}
static void
-opaque_lsa_config_write_router_callback (struct list *funclist,
+opaque_lsa_config_write_router_callback (struct list *funclist,
struct vty *vty)
{
struct listnode *node, *nnode;
@@ -1380,7 +1380,7 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0)
{
for (ALL_LIST_ELEMENTS (oi->opaque_lsa_self, node, nnode, oipt))
{
- /*
+ /*
* removed the test for
* (! list_isempty (oipt->id_list)) * Handler is already active. *
* because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list)
@@ -1400,7 +1400,7 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0)
{
for (ALL_LIST_ELEMENTS (area->opaque_lsa_self, node, nnode, oipt))
{
- /*
+ /*
* removed the test for
* (! list_isempty (oipt->id_list)) * Handler is already active. *
* because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list)
@@ -1420,7 +1420,7 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0)
{
for (ALL_LIST_ELEMENTS (top->opaque_lsa_self, node, nnode, oipt))
{
- /*
+ /*
* removed the test for
* (! list_isempty (oipt->id_list)) * Handler is already active. *
* because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list)
@@ -1614,7 +1614,7 @@ ospf_opaque_lsa_refresh (struct ospf_lsa *lsa)
struct ospf *ospf;
struct ospf_opaque_functab *functab;
struct ospf_lsa *new = NULL;
-
+
ospf = ospf_lookup ();
if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL
@@ -1804,8 +1804,8 @@ ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d"
- " sec later: [opaque-type=%u]",
- lsa_type, delay,
+ " sec later: [opaque-type=%u]",
+ lsa_type, delay,
GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)));
OSPF_OPAQUE_TIMER_ON (oipt->t_opaque_lsa_self, func, oipt, delay);
@@ -1865,7 +1865,7 @@ ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t)
{
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type);
-
+
oipt->status = PROC_SUSPEND;
rc = 0;
goto out;
@@ -1919,7 +1919,7 @@ ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t)
{
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Suspend re-origination of Type-10 Opaque-LSAs"
- " (opaque-type=%u) for a while...",
+ " (opaque-type=%u) for a while...",
oipt->opaque_type);
oipt->status = PROC_SUSPEND;
@@ -1929,7 +1929,7 @@ ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t)
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Timer[Type10-LSA]: Re-originate Opaque-LSAs"
- " (opaque-type=%u) for Area %s",
+ " (opaque-type=%u) for Area %s",
oipt->opaque_type, inet_ntoa (area->area_id));
rc = (* functab->lsa_originator)(area);
@@ -1966,7 +1966,7 @@ ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t)
{
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type);
-
+
oipt->status = PROC_SUSPEND;
rc = 0;
goto out;
@@ -2228,7 +2228,7 @@ ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs,
}
void
-ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr,
+ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr,
struct ospf_lsa *lsa)
{
struct ospf *top;
@@ -2282,10 +2282,10 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
if ((top = oi_to_top (nbr->oi)) == NULL)
return;
-
+
if (!IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque))
return;
-
+
switch (lsa->data->type)
{
case OSPF_OPAQUE_LINK_LSA:
@@ -2307,14 +2307,14 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
zlog_warn ("ospf_opaque_ls_ack_received: Unexpected LSA-type(%u)", lsa->data->type);
return;
}
-
+
if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque))
{
if (IS_DEBUG_OSPF_EVENT)
zlog_debug ("Block Opaque-LSA origination: ON -> OFF");
return; /* Blocking still in progress. */
}
-
+
if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE))
return; /* Opaque capability condition must have changed. */
@@ -2329,7 +2329,7 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
ospf_opaque_lsa_originate_schedule (oi, &delay);
}
-
+
return;
}
@@ -2359,7 +2359,7 @@ ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area)
for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
{
if (area->area_id.s_addr != OSPF_AREA_BACKBONE
- && oi->type == OSPF_IFTYPE_VIRTUALLINK)
+ && oi->type == OSPF_IFTYPE_VIRTUALLINK)
continue;
n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_AREA_LSA);
diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h
index 22730645..083e0fa5 100644
--- a/ospfd/ospf_opaque.h
+++ b/ospfd/ospf_opaque.h
@@ -9,7 +9,7 @@
* 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
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index be137d91..23eae565 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -641,8 +641,7 @@ ospf_write (struct thread *thread)
* and reliability - not more data, than our
* socket can accept
*/
- maxdatasize = MIN (oi->ifp->mtu, ospf->maxsndbuflen) -
- sizeof (struct ip);
+ maxdatasize = MIN((int)oi->ifp->mtu, ospf->maxsndbuflen) - sizeof (struct ip);
/* Get one packet from queue. */
op = ospf_fifo_head (oi->obuf);
@@ -1777,7 +1776,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh,
{
if (IS_DEBUG_OSPF_NSSA)
zlog_debug("Incoming External LSA Discarded: We are NSSA/STUB Area");
- DISCARD_LSA (lsa, 1);
+ DISCARD_LSA (lsa, 1);
}
if (lsa->data->type == OSPF_AS_NSSA_LSA)
@@ -1785,7 +1784,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh,
{
if (IS_DEBUG_OSPF_NSSA)
zlog_debug("Incoming NSSA LSA Discarded: Not NSSA Area");
- DISCARD_LSA (lsa,2);
+ DISCARD_LSA (lsa,2);
}
/* Find the LSA in the current database. */
@@ -2005,7 +2004,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh,
}
}
#undef DISCARD_LSA
-
+
assert (listcount (lsas) == 0);
list_delete (lsas);
}
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
index 267237b8..f1251e99 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -274,7 +274,11 @@ ospf_route_install (struct ospf *ospf, struct route_table *rt)
static void
ospf_intra_route_add (struct route_table *rt, struct vertex *v,
- struct ospf_area *area)
+ struct ospf_area *area)
+ __attribute__((unused)) ;
+static void
+ospf_intra_route_add (struct route_table *rt, struct vertex *v,
+ struct ospf_area *area)
{
struct route_node *rn;
struct ospf_route *or;
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index ca200222..e41492a5 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -1044,6 +1044,8 @@ ospf_rtrs_free (struct route_table *rtrs)
}
route_table_finish (rtrs);
}
+static void
+ospf_rtrs_print (struct route_table *rtrs) __attribute__((unused)) ;
static void
ospf_rtrs_print (struct route_table *rtrs)
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index 24e81052..5970a51f 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -9,7 +9,7 @@
* 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
@@ -282,7 +282,7 @@ ospf_mpls_te_foreach_area (
void (*func)(struct mpls_te_link *lp, enum sched_opcode),
enum sched_opcode sched_opcode)
{
- struct listnode *node, *nnode;
+ struct listnode *node, *nnode;
struct listnode *node2;
struct mpls_te_link *lp;
struct ospf_area *area;
@@ -1566,7 +1566,7 @@ DEFUN (mpls_te_router_addr,
break;
}
}
-
+
for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
{
if (lp->area == NULL)
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 46e7ffa5..19dfb1c4 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -28,6 +28,7 @@
#include "table.h"
#include "vty.h"
#include "command.h"
+#include "lib/route_types.h"
#include "plist.h"
#include "log.h"
#include "zclient.h"
@@ -1568,7 +1569,7 @@ DEFUN (no_ospf_area_stub_no_summary,
}
static int
-ospf_area_nssa_cmd_handler (struct vty *vty, int argc, const char *argv[],
+ospf_area_nssa_cmd_handler (struct vty *vty, int argc, argv_t argv,
int nosum)
{
struct ospf *ospf = vty->index;
@@ -3763,6 +3764,9 @@ show_as_external_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
/* N.B. This function currently seems to be unused. */
static int
+show_as_external_lsa_stdvty (struct ospf_lsa *lsa) __attribute__((unused)) ;
+
+static int
show_as_external_lsa_stdvty (struct ospf_lsa *lsa)
{
struct as_external_lsa *al = (struct as_external_lsa *) lsa->data;
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 50ca85e1..0a486199 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -133,8 +133,9 @@ ospf_interface_delete (int command, struct zclient *zclient,
if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
zlog_debug
- ("Zebra: interface delete %s index %d flags %lld metric %d mtu %d",
- ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
+ ("Zebra: interface delete %s index %d flags %llu metric %d mtu %d",
+ ifp->name, ifp->ifindex, (long long unsigned)ifp->flags,
+ ifp->metric, ifp->mtu);
#ifdef HAVE_SNMP
ospf_snmp_if_delete (ifp);
@@ -969,6 +970,7 @@ void
ospf_distribute_list_update (struct ospf *ospf, int type)
{
struct route_table *rt;
+ uintptr_t pi ;
/* External info does not exist. */
if (!(rt = EXTERNAL_INFO (type)))
@@ -979,9 +981,10 @@ ospf_distribute_list_update (struct ospf *ospf, int type)
return;
/* Set timer. */
+ pi = type ;
ospf->t_distribute_update =
thread_add_timer (master, ospf_distribute_list_update_timer,
- (void *) type, OSPF_DISTRIBUTE_UPDATE_DELAY);
+ (void*)pi, OSPF_DISTRIBUTE_UPDATE_DELAY) ;
}
/* If access-list is updated, apply some check. */
@@ -1088,7 +1091,7 @@ ospf_prefix_list_update (struct prefix_list *plist)
{
/* Update filter-list in. */
if (PREFIX_NAME_IN (area))
- if (strcmp (PREFIX_NAME_IN (area), plist->name) == 0)
+ if (strcmp (PREFIX_NAME_IN (area), prefix_list_get_name(plist)) == 0)
{
PREFIX_LIST_IN (area) =
prefix_list_lookup (AFI_IP, PREFIX_NAME_IN (area));
@@ -1097,7 +1100,7 @@ ospf_prefix_list_update (struct prefix_list *plist)
/* Update filter-list out. */
if (PREFIX_NAME_OUT (area))
- if (strcmp (PREFIX_NAME_OUT (area), plist->name) == 0)
+ if (strcmp (PREFIX_NAME_OUT (area), prefix_list_get_name(plist)) == 0)
{
PREFIX_LIST_IN (area) =
prefix_list_lookup (AFI_IP, PREFIX_NAME_OUT (area));
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 0188ffda..48453a9b 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -1207,7 +1207,11 @@ ospf_area_nssa_translator_role_set (struct ospf *ospf, struct in_addr area_id,
/* XXX: unused? Leave for symmetry? */
static int
ospf_area_nssa_translator_role_unset (struct ospf *ospf,
- struct in_addr area_id)
+ struct in_addr area_id)
+ __attribute__((unused)) ;
+static int
+ospf_area_nssa_translator_role_unset (struct ospf *ospf,
+ struct in_addr area_id)
{
struct ospf_area *area;
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index d3b55fc0..c1291a1d 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -839,7 +839,7 @@ rip_enable_network_add (struct prefix *p)
return -1;
}
else
- node->info = (char *) "enabled";
+ node->info = rip_enabled_string.p ;
/* XXX: One should find a better solution than a generic one */
rip_enable_apply_all();
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
index 61c47c71..36423f6e 100644
--- a/ripd/rip_snmp.c
+++ b/ripd/rip_snmp.c
@@ -317,7 +317,7 @@ rip2PeerLookup (struct variable *v, oid name[], size_t *length,
peer = rip_peer_lookup (addr);
if (peer)
{
- if ((len < sizeof (struct in_addr) + 1) ||
+ if ((len < (int)sizeof (struct in_addr) + 1) ||
(peer->domain > name[v->namelen + sizeof (struct in_addr)]))
{
oid_copy_addr (name + v->namelen, &peer->addr,
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
index c476d8f4..326ea658 100644
--- a/ripd/rip_zebra.c
+++ b/ripd/rip_zebra.c
@@ -22,6 +22,7 @@
#include <zebra.h>
#include "command.h"
+#include "lib/route_types.h"
#include "prefix.h"
#include "stream.h"
#include "routemap.h"
diff --git a/ripd/ripd.c b/ripd/ripd.c
index 5a6dbc8c..9c951bf8 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.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 <zebra.h>
@@ -61,20 +61,25 @@ long rip_global_route_changes = 0;
/* RIP queries. */
long rip_global_queries = 0;
-
+
+/* Strings... */
+union rip_miyagi_string rip_enabled_string = { .cp = "enabled" } ;
+union rip_miyagi_string rip_static_string = { .cp = "static" } ;
+
+
/* Prototypes. */
static void rip_event (enum rip_event, int);
static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char);
static int rip_triggered_update (struct thread *);
static int rip_update_jitter (unsigned long);
-
+
/* RIP output routes type. */
enum
{
rip_all_route,
rip_changed_route
};
-
+
/* RIP command strings. */
static const struct message rip_msg[] =
{
@@ -86,22 +91,6 @@ static const struct message rip_msg[] =
{RIP_POLL_ENTRY, "POLL ENTRY"},
{0, NULL},
};
-
-/* Utility function to set boradcast option to the socket. */
-static int
-sockopt_broadcast (int sock)
-{
- int ret;
- int on = 1;
-
- ret = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof on);
- if (ret < 0)
- {
- zlog_warn ("can't set sockopt SO_BROADCAST to socket %d", sock);
- return -1;
- }
- return 0;
-}
static int
rip_route_rte (struct rip_info *rinfo)
@@ -133,7 +122,7 @@ rip_garbage_collect (struct thread *t)
/* Off timeout timer. */
RIP_TIMER_OFF (rinfo->t_timeout);
-
+
/* Get route_node pointer. */
rp = rinfo->rp;
@@ -160,7 +149,7 @@ rip_timeout (struct thread *t)
rn = rinfo->rp;
/* - The garbage-collection timer is set for 120 seconds. */
- RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect,
+ RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect,
rip->garbage_time);
rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rn->p, &rinfo->nexthop,
@@ -200,7 +189,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
/* Input distribute-list filtering. */
if (ri->list[RIP_FILTER_IN])
{
- if (access_list_apply (ri->list[RIP_FILTER_IN],
+ if (access_list_apply (ri->list[RIP_FILTER_IN],
(struct prefix *) p) == FILTER_DENY)
{
if (IS_RIP_DEBUG_PACKET)
@@ -211,7 +200,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
}
if (ri->prefix[RIP_FILTER_IN])
{
- if (prefix_list_apply (ri->prefix[RIP_FILTER_IN],
+ if (prefix_list_apply (ri->prefix[RIP_FILTER_IN],
(struct prefix *) p) == PREFIX_DENY)
{
if (IS_RIP_DEBUG_PACKET)
@@ -228,7 +217,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
if (dist->list[DISTRIBUTE_IN])
{
alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]);
-
+
if (alist)
{
if (access_list_apply (alist,
@@ -244,7 +233,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
if (dist->prefix[DISTRIBUTE_IN])
{
plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]);
-
+
if (plist)
{
if (prefix_list_apply (plist,
@@ -298,7 +287,7 @@ rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
if (dist->list[DISTRIBUTE_OUT])
{
alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]);
-
+
if (alist)
{
if (access_list_apply (alist,
@@ -314,7 +303,7 @@ rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
if (dist->prefix[DISTRIBUTE_OUT])
{
plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]);
-
+
if (plist)
{
if (prefix_list_apply (plist,
@@ -346,7 +335,7 @@ rip_nexthop_check (struct in_addr *addr)
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
{
for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc))
- {
+ {
p = ifc->address;
if (p->family == AF_INET
@@ -485,9 +474,9 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from,
/* Only connected routes may have a valid NULL distance */
if (rinfo->type != ZEBRA_ROUTE_CONNECT)
old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT;
- /* If imported route does not have STRICT precedence,
+ /* If imported route does not have STRICT precedence,
mark it as a ghost */
- if (new_dist > old_dist
+ if (new_dist > old_dist
|| rte->metric == RIP_METRIC_INFINITY)
{
route_unlock_node (rp);
@@ -497,10 +486,10 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from,
{
RIP_TIMER_OFF (rinfo->t_timeout);
RIP_TIMER_OFF (rinfo->t_garbage_collect);
-
+
rp->info = NULL;
if (rip_route_rte (rinfo))
- rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p,
+ rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p,
&rinfo->nexthop, rinfo->metric);
rip_info_free (rinfo);
rinfo = NULL;
@@ -715,7 +704,7 @@ rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv)
/* Dump each routing table entry. */
rte = packet->rte;
-
+
for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
{
if (packet->version == RIPv2)
@@ -773,7 +762,7 @@ rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv)
}
else
{
- zlog_debug (" %s family %d tag %d metric %ld",
+ zlog_debug (" %s family %d tag %d metric %ld",
inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ),
ntohs (rte->family), ntohs (rte->tag),
(u_long)ntohl (rte->metric));
@@ -832,7 +821,7 @@ rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from,
if (ri->auth_str)
{
auth_str = (char *) &rte->prefix;
-
+
if (strncmp (auth_str, ri->auth_str, 16) == 0)
return 1;
}
@@ -866,7 +855,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
u_char digest[RIP_AUTH_MD5_SIZE];
u_int16_t packet_len;
char auth_str[RIP_AUTH_MD5_SIZE];
-
+
if (IS_RIP_DEBUG_EVENT)
zlog_debug ("RIPv2 MD5 authentication from %s",
inet_ntoa (from->sin_addr));
@@ -905,7 +894,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
/* retrieve authentication data */
md5data = (struct rip_md5_data *) (((u_char *) packet) + packet_len);
-
+
memset (auth_str, 0, RIP_AUTH_MD5_SIZE);
if (ri->key_chain)
@@ -925,14 +914,14 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
if (auth_str[0] == 0)
return 0;
-
+
/* MD5 digest authentication. */
memset (&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE);
MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
MD5Final(digest, &ctx);
-
+
if (memcmp (md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0)
return packet_len;
else
@@ -948,7 +937,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
*
*/
static void
-rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key,
+rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key,
char *auth_str, int len)
{
assert (ri || key);
@@ -964,22 +953,22 @@ rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key,
/* Write RIPv2 simple password authentication information
*
- * auth_str is presumed to be 2 bytes and correctly prepared
+ * auth_str is presumed to be 2 bytes and correctly prepared
* (left justified and zero padded).
*/
static void
rip_auth_simple_write (struct stream *s, char *auth_str, int len)
{
assert (s && len == RIP_AUTH_SIMPLE_SIZE);
-
+
stream_putw (s, RIP_FAMILY_AUTH);
stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
stream_put (s, auth_str, RIP_AUTH_SIMPLE_SIZE);
-
+
return;
}
-/* write RIPv2 MD5 "authentication header"
+/* write RIPv2 MD5 "authentication header"
* (uses the auth key data field)
*
* Digest offset field is set to 0.
@@ -988,7 +977,7 @@ rip_auth_simple_write (struct stream *s, char *auth_str, int len)
* length to the auth-data MD5 digest is known.
*/
static size_t
-rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri,
+rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri,
struct key *key)
{
size_t doff = 0;
@@ -1013,9 +1002,9 @@ rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri,
else
stream_putc (s, 1);
- /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds
+ /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds
* however expect RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE so we allow for this
- * to be configurable.
+ * to be configurable.
*/
stream_putc (s, ri->md5_auth_len);
@@ -1024,7 +1013,7 @@ rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri,
arbitrary, but two suggestions are the time of the
message's creation or a simple message counter. */
stream_putl (s, time (NULL));
-
+
/* Reserved field must be zero. */
stream_putl (s, 0);
stream_putl (s, 0);
@@ -1037,11 +1026,11 @@ rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri,
* or 0 if this is not required
*/
static size_t
-rip_auth_header_write (struct stream *s, struct rip_interface *ri,
+rip_auth_header_write (struct stream *s, struct rip_interface *ri,
struct key *key, char *auth_str, int len)
{
assert (ri->auth_type != RIP_NO_AUTH);
-
+
switch (ri->auth_type)
{
case RIP_AUTH_SIMPLE_PASSWORD:
@@ -1068,7 +1057,7 @@ rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff,
authentication. */
assert ((ri->auth_type == RIP_AUTH_MD5) && (authlen == RIP_AUTH_MD5_SIZE));
assert (doff > 0);
-
+
/* Get packet length. */
len = stream_get_endp(s);
@@ -1081,7 +1070,7 @@ rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff,
/* Set the digest offset length in the header */
stream_putw_at (s, doff, len);
-
+
/* Set authentication data. */
stream_putw (s, RIP_FAMILY_AUTH);
stream_putw (s, RIP_AUTH_DATA);
@@ -1099,7 +1088,7 @@ rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff,
/* RIP routing information. */
static void
-rip_response_process (struct rip_packet *packet, int size,
+rip_response_process (struct rip_packet *packet, int size,
struct sockaddr_in *from, struct connected *ifc)
{
caddr_t lim;
@@ -1107,9 +1096,10 @@ rip_response_process (struct rip_packet *packet, int size,
struct prefix_ipv4 ifaddr;
struct prefix_ipv4 ifaddrclass;
int subnetted;
-
+
/* We don't know yet. */
subnetted = -1;
+ ifaddr.prefixlen = 0 ; /* eliminate a compiler warning */
/* The Response must be ignored if it is not from the RIP
port. (RFC2453 - Sec. 3.9.2)*/
@@ -1124,7 +1114,7 @@ rip_response_process (struct rip_packet *packet, int size,
/* The datagram's IPv4 source address should be checked to see
whether the datagram is from a valid neighbor; the source of the
datagram must be on a directly connected network (RFC2453 - Sec. 3.9.2) */
- if (if_lookup_address(from->sin_addr) == NULL)
+ if (if_lookup_address(from->sin_addr) == NULL)
{
zlog_info ("This datagram doesn't came from a valid neighbor: %s",
inet_ntoa (from->sin_addr));
@@ -1171,7 +1161,7 @@ rip_response_process (struct rip_packet *packet, int size,
zlog_info ("Network is net 0 or net 127 or it is not unicast network");
rip_peer_bad_route (from);
continue;
- }
+ }
/* Convert metric value to host byte order. */
rte->metric = ntohl (rte->metric);
@@ -1247,18 +1237,18 @@ rip_response_process (struct rip_packet *packet, int size,
}
}
- /* For RIPv1, there won't be a valid netmask.
+ /* For RIPv1, there won't be a valid netmask.
This is a best guess at the masks. If everyone was using old
Ciscos before the 'ip subnet zero' option, it would be almost
right too :-)
-
+
Cisco summarize ripv1 advertisments to the classful boundary
(/16 for class B's) except when the RIP packet does to inside
the classful network in question. */
- if ((packet->version == RIPv1 && rte->prefix.s_addr != 0)
- || (packet->version == RIPv2
+ if ((packet->version == RIPv1 && rte->prefix.s_addr != 0)
+ || (packet->version == RIPv2
&& (rte->prefix.s_addr != 0 && rte->mask.s_addr == 0)))
{
u_int32_t destination;
@@ -1309,8 +1299,8 @@ rip_response_process (struct rip_packet *packet, int size,
/* In case of RIPv2, if prefix in RTE is not netmask applied one
ignore the entry. */
- if ((packet->version == RIPv2)
- && (rte->mask.s_addr != 0)
+ if ((packet->version == RIPv2)
+ && (rte->mask.s_addr != 0)
&& ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr))
{
zlog_warn ("RIPv2 address %s is not mask /%d applied one",
@@ -1328,22 +1318,22 @@ rip_response_process (struct rip_packet *packet, int size,
zlog_debug ("Default route with non-zero netmask. Set zero to netmask");
rte->mask.s_addr = 0;
}
-
+
/* Routing table updates. */
rip_rte_process (rte, from, ifc->ifp);
}
}
/* Make socket for RIP protocol. */
-static int
+static int
rip_create_socket (struct sockaddr_in *from)
{
int ret;
int sock;
struct sockaddr_in addr;
-
+
memset (&addr, 0, sizeof (struct sockaddr_in));
-
+
if (!from)
{
addr.sin_family = AF_INET;
@@ -1354,21 +1344,21 @@ rip_create_socket (struct sockaddr_in *from)
} else {
memcpy(&addr, from, sizeof(addr));
}
-
+
/* sending port must always be the RIP port */
addr.sin_port = htons (RIP_PORT_DEFAULT);
-
+
/* Make datagram socket. */
sock = socket (AF_INET, SOCK_DGRAM, 0);
- if (sock < 0)
+ if (sock < 0)
{
zlog_err("Cannot create UDP socket: %s", safe_strerror(errno));
exit (1);
}
- sockopt_broadcast (sock);
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
+ setsockopt_broadcast (sock);
+ setsockopt_reuseaddr (sock);
+ setsockopt_reuseport (sock);
#ifdef RIP_RECVMSG
setsockopt_pktinfo (sock);
#endif /* RIP_RECVMSG */
@@ -1380,24 +1370,24 @@ rip_create_socket (struct sockaddr_in *from)
zlog_err ("rip_create_socket: could not raise privs");
setsockopt_so_recvbuf (sock, RIP_UDP_RCV_BUF);
if ( (ret = bind (sock, (struct sockaddr *) & addr, sizeof (addr))) < 0)
-
+
{
int save_errno = errno;
if (ripd_privs.change (ZPRIVS_LOWER))
zlog_err ("rip_create_socket: could not lower privs");
-
+
zlog_err("%s: Can't bind socket %d to %s port %d: %s", __func__,
- sock, inet_ntoa(addr.sin_addr),
- (int) ntohs(addr.sin_port),
+ sock, inet_ntoa(addr.sin_addr),
+ (int) ntohs(addr.sin_port),
safe_strerror(save_errno));
-
+
close (sock);
return ret;
}
-
+
if (ripd_privs.change (ZPRIVS_LOWER))
zlog_err ("rip_create_socket: could not lower privs");
-
+
return sock;
}
@@ -1411,15 +1401,15 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
{
int ret, send_sock;
struct sockaddr_in sin;
-
+
assert (ifc != NULL);
-
+
if (IS_RIP_DEBUG_PACKET)
{
#define ADDRESS_SIZE 20
char dst[ADDRESS_SIZE];
dst[ADDRESS_SIZE - 1] = '\0';
-
+
if (to)
{
strncpy (dst, inet_ntoa(to->sin_addr), ADDRESS_SIZE - 1);
@@ -1434,19 +1424,19 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
inet_ntoa(ifc->address->u.prefix4),
dst, ifc->ifp->name);
}
-
+
if ( CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY) )
{
/*
* ZEBRA_IFA_SECONDARY is set on linux when an interface is configured
* with multiple addresses on the same subnet: the first address
* on the subnet is configured "primary", and all subsequent addresses
- * on that subnet are treated as "secondary" addresses.
- * In order to avoid routing-table bloat on other rip listeners,
+ * on that subnet are treated as "secondary" addresses.
+ * In order to avoid routing-table bloat on other rip listeners,
* we do not send out RIP packets with ZEBRA_IFA_SECONDARY source addrs.
* XXX Since Linux is the only system for which the ZEBRA_IFA_SECONDARY
* flag is set, we would end up sending a packet for a "secondary"
- * source address on non-linux systems.
+ * source address on non-linux systems.
*/
if (IS_RIP_DEBUG_PACKET)
zlog_debug("duplicate dropped");
@@ -1470,10 +1460,10 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
else
{
struct sockaddr_in from;
-
+
sin.sin_port = htons (RIP_PORT_DEFAULT);
sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
-
+
/* multicast send should bind to local interface address */
from.sin_family = AF_INET;
from.sin_port = htons (RIP_PORT_DEFAULT);
@@ -1481,11 +1471,11 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
from.sin_len = sizeof (struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-
+
/*
* we have to open a new socket for each packet because this
* is the most portable way to bind to a different source
- * ipv4 address for each packet.
+ * ipv4 address for each packet.
*/
if ( (send_sock = rip_create_socket (&from)) < 0)
{
@@ -1499,7 +1489,7 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
sizeof (struct sockaddr_in));
if (IS_RIP_DEBUG_EVENT)
- zlog_debug ("SEND to %s.%d", inet_ntoa(sin.sin_addr),
+ zlog_debug ("SEND to %s.%d", inet_ntoa(sin.sin_addr),
ntohs (sin.sin_port));
if (ret < 0)
@@ -1513,7 +1503,7 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
/* Add redistributed route to RIP table. */
void
-rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p,
+rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p,
unsigned int ifindex, struct in_addr *nexthop,
unsigned int metric, unsigned char distance)
{
@@ -1532,7 +1522,7 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p,
if (rinfo)
{
- if (rinfo->type == ZEBRA_ROUTE_CONNECT
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT
&& rinfo->sub_type == RIP_ROUTE_INTERFACE
&& rinfo->metric != RIP_METRIC_INFINITY)
{
@@ -1541,7 +1531,7 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p,
}
/* Manually configured RIP route check. */
- if (rinfo->type == ZEBRA_ROUTE_RIP
+ if (rinfo->type == ZEBRA_ROUTE_RIP
&& ((rinfo->sub_type == RIP_ROUTE_STATIC) ||
(rinfo->sub_type == RIP_ROUTE_DEFAULT)) )
{
@@ -1561,12 +1551,12 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p,
rinfo->metric);
rp->info = NULL;
rip_info_free (rinfo);
-
- route_unlock_node (rp);
+
+ route_unlock_node (rp);
}
rinfo = rip_info_new ();
-
+
rinfo->type = type;
rinfo->sub_type = sub_type;
rinfo->ifindex = ifindex;
@@ -1600,7 +1590,7 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p,
/* Delete redistributed route from RIP table. */
void
-rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p,
+rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p,
unsigned int ifindex)
{
int ret;
@@ -1617,13 +1607,13 @@ rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p,
rinfo = rp->info;
if (rinfo != NULL
- && rinfo->type == type
- && rinfo->sub_type == sub_type
+ && rinfo->type == type
+ && rinfo->sub_type == sub_type
&& rinfo->ifindex == ifindex)
{
/* Perform poisoned reverse. */
rinfo->metric = RIP_METRIC_INFINITY;
- RIP_TIMER_ON (rinfo->t_garbage_collect,
+ RIP_TIMER_ON (rinfo->t_garbage_collect,
rip_garbage_collect, rip->garbage_time);
RIP_TIMER_OFF (rinfo->t_timeout);
rinfo->flags |= RIP_RTF_CHANGED;
@@ -1640,7 +1630,7 @@ rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p,
/* Response to request called from rip_read ().*/
static void
-rip_request_process (struct rip_packet *packet, int size,
+rip_request_process (struct rip_packet *packet, int size,
struct sockaddr_in *from, struct connected *ifc)
{
caddr_t lim;
@@ -1662,7 +1652,7 @@ rip_request_process (struct rip_packet *packet, int size,
/* When passive interface is specified, suppress responses */
if (ri->passive)
return;
-
+
/* RIP peer update. */
rip_peer_update (from, packet->version);
@@ -1681,7 +1671,7 @@ rip_request_process (struct rip_packet *packet, int size,
if (lim == ((caddr_t) (rte + 1)) &&
ntohs (rte->family) == 0 &&
ntohl (rte->metric) == RIP_METRIC_INFINITY)
- {
+ {
struct prefix_ipv4 saddr;
/* saddr will be used for determining which routes to split-horizon.
@@ -1712,7 +1702,7 @@ rip_request_process (struct rip_packet *packet, int size,
p.prefix = rte->prefix;
p.prefixlen = ip_masklen (rte->mask);
apply_mask_ipv4 (&p);
-
+
rp = route_node_lookup (rip->table, (struct prefix *) &p);
if (rp)
{
@@ -1737,7 +1727,7 @@ setsockopt_pktinfo (int sock)
{
int ret;
int val = 1;
-
+
ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
if (ret < 0)
zlog_warn ("Can't setsockopt IP_PKTINFO : %s", safe_strerror (errno));
@@ -1769,7 +1759,7 @@ rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from,
return ret;
for (ptr = ZCMSG_FIRSTHDR(&msg); ptr != NULL; ptr = CMSG_NXTHDR(&msg, ptr))
- if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO)
+ if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo *pktinfo;
int i;
@@ -1789,7 +1779,7 @@ rip_read_new (struct thread *t)
char buf[RIP_PACKET_MAXSIZ];
struct sockaddr_in from;
unsigned int ifindex;
-
+
/* Fetch socket then register myself. */
sock = THREAD_FD (t);
rip_event (RIP_READ, sock);
@@ -1834,16 +1824,16 @@ rip_read (struct thread *t)
memset (&from, 0, sizeof (struct sockaddr_in));
fromlen = sizeof (struct sockaddr_in);
- len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0,
+ len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0,
(struct sockaddr *) &from, &fromlen);
- if (len < 0)
+ if (len < 0)
{
zlog_info ("recvfrom failed: %s", safe_strerror (errno));
return len;
}
/* Check is this packet comming from myself? */
- if (if_check_address (from.sin_addr))
+ if (if_check_address (from.sin_addr))
{
if (IS_RIP_DEBUG_PACKET)
zlog_debug ("ignore packet comes from myself");
@@ -1852,7 +1842,7 @@ rip_read (struct thread *t)
/* Which interface is this packet comes from. */
ifp = if_lookup_address (from.sin_addr);
-
+
/* RIP packet received */
if (IS_RIP_DEBUG_EVENT)
zlog_debug ("RECV packet from %s port %d on %s",
@@ -1866,9 +1856,9 @@ rip_read (struct thread *t)
inet_ntoa(from.sin_addr), ntohs (from.sin_port));
return -1;
}
-
+
ifc = connected_lookup_address (ifp, from.sin_addr);
-
+
if (ifc == NULL)
{
zlog_info ("rip_read: cannot find connected address for packet from %s "
@@ -1942,7 +1932,7 @@ rip_read (struct thread *t)
if ((packet->version == RIPv1) && !(vrecv & RIPv1))
{
if (IS_RIP_DEBUG_PACKET)
- zlog_debug (" packet's v%d doesn't fit to if version spec",
+ zlog_debug (" packet's v%d doesn't fit to if version spec",
packet->version);
rip_peer_bad_packet (&from);
return -1;
@@ -1950,27 +1940,27 @@ rip_read (struct thread *t)
if ((packet->version == RIPv2) && !(vrecv & RIPv2))
{
if (IS_RIP_DEBUG_PACKET)
- zlog_debug (" packet's v%d doesn't fit to if version spec",
+ zlog_debug (" packet's v%d doesn't fit to if version spec",
packet->version);
rip_peer_bad_packet (&from);
return -1;
}
-
+
/* RFC2453 5.2 If the router is not configured to authenticate RIP-2
messages, then RIP-1 and unauthenticated RIP-2 messages will be
accepted; authenticated RIP-2 messages shall be discarded. */
- if ((ri->auth_type == RIP_NO_AUTH)
- && rtenum
- && (packet->version == RIPv2)
+ if ((ri->auth_type == RIP_NO_AUTH)
+ && rtenum
+ && (packet->version == RIPv2)
&& (packet->rte->family == htons(RIP_FAMILY_AUTH)))
{
if (IS_RIP_DEBUG_EVENT)
- zlog_debug ("packet RIPv%d is dropped because authentication disabled",
+ zlog_debug ("packet RIPv%d is dropped because authentication disabled",
packet->version);
rip_peer_bad_packet (&from);
return -1;
}
-
+
/* RFC:
If the router is configured to authenticate RIP-2 messages, then
RIP-1 messages and RIP-2 messages which pass authentication
@@ -1979,7 +1969,7 @@ rip_read (struct thread *t)
security, RIP-1 messages should be ignored when authentication is
in use (see section 4.1); otherwise, the routing information from
authenticated messages will be propagated by RIP-1 routers in an
- unauthenticated manner.
+ unauthenticated manner.
*/
/* We make an exception for RIPv1 REQUEST packets, to which we'll
* always reply regardless of authentication settings, because:
@@ -1996,7 +1986,7 @@ rip_read (struct thread *t)
* routing-information freely, while still requiring RIPv2
* authentication for any RESPONSEs might be vaguely useful.
*/
- if (ri->auth_type != RIP_NO_AUTH
+ if (ri->auth_type != RIP_NO_AUTH
&& packet->version == RIPv1)
{
/* Discard RIPv1 messages other than REQUESTs */
@@ -2011,7 +2001,7 @@ rip_read (struct thread *t)
else if (ri->auth_type != RIP_NO_AUTH)
{
const char *auth_desc;
-
+
if (rtenum == 0)
{
/* There definitely is no authentication in the packet. */
@@ -2020,7 +2010,7 @@ rip_read (struct thread *t)
rip_peer_bad_packet (&from);
return -1;
}
-
+
/* First RTE must be an Authentication Family RTE */
if (packet->rte->family != htons(RIP_FAMILY_AUTH))
{
@@ -2029,7 +2019,7 @@ rip_read (struct thread *t)
rip_peer_bad_packet (&from);
return -1;
}
-
+
/* Check RIPv2 authentication. */
switch (ntohs(packet->rte->tag))
{
@@ -2037,14 +2027,14 @@ rip_read (struct thread *t)
auth_desc = "simple";
ret = rip_auth_simple_password (packet->rte, &from, ifp);
break;
-
+
case RIP_AUTH_MD5:
auth_desc = "MD5";
ret = rip_auth_md5 (packet, &from, len, ifp);
/* Reset RIP packet length to trim MD5 data. */
len = ret;
break;
-
+
default:
ret = 0;
auth_desc = "unknown type";
@@ -2052,7 +2042,7 @@ rip_read (struct thread *t)
zlog_debug ("RIPv2 Unknown authentication type %d",
ntohs (packet->rte->tag));
}
-
+
if (ret)
{
if (IS_RIP_DEBUG_PACKET)
@@ -2066,7 +2056,7 @@ rip_read (struct thread *t)
return -1;
}
}
-
+
/* Process each command. */
switch (packet->command)
{
@@ -2079,12 +2069,12 @@ rip_read (struct thread *t)
break;
case RIP_TRACEON:
case RIP_TRACEOFF:
- zlog_info ("Obsolete command %s received, please sent it to routed",
+ zlog_info ("Obsolete command %s received, please sent it to routed",
lookup (rip_msg, packet->command));
rip_peer_bad_packet (&from);
break;
case RIP_POLL_ENTRY:
- zlog_info ("Obsolete command %s received",
+ zlog_info ("Obsolete command %s received",
lookup (rip_msg, packet->command));
rip_peer_bad_packet (&from);
break;
@@ -2132,7 +2122,7 @@ rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p,
/* Send update to the ifp or spcified neighbor. */
void
-rip_output_process (struct connected *ifc, struct sockaddr_in *to,
+rip_output_process (struct connected *ifc, struct sockaddr_in *to,
int route_type, u_char version)
{
int ret;
@@ -2171,7 +2161,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
/* Get RIP interface. */
ri = ifc->ifp->info;
-
+
/* If output interface is in simple password authentication mode, we
need space for authentication data. */
if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
@@ -2213,7 +2203,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
/* For RIPv1, if we are subnetted, output subnets in our network */
/* that have the same mask as the output "interface". For other */
/* networks, only the classfull version is output. */
-
+
if (version == RIPv1)
{
p = (struct prefix_ipv4 *) &rp->p;
@@ -2241,7 +2231,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
zlog_debug("RIPv1 mask check, %s/%d made it through",
inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen);
}
- else
+ else
p = (struct prefix_ipv4 *) &rp->p;
/* Apply output filters. */
@@ -2258,18 +2248,18 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
/* if (split_horizon == rip_split_horizon) */
if (ri->split_horizon == RIP_SPLIT_HORIZON)
{
- /*
- * We perform split horizon for RIP and connected route.
+ /*
+ * We perform split horizon for RIP and connected route.
* For rip routes, we want to suppress the route if we would
* end up sending the route back on the interface that we
* learned it from, with a higher metric. For connected routes,
* we suppress the route if the prefix is a subset of the
- * source address that we are going to use for the packet
+ * source address that we are going to use for the packet
* (in order to handle the case when multiple subnets are
* configured on the same interface).
*/
if (rinfo->type == ZEBRA_ROUTE_RIP &&
- rinfo->ifindex == ifc->ifp->ifindex)
+ rinfo->ifindex == ifc->ifp->ifindex)
continue;
if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
prefix_match((struct prefix *)p, ifc->address))
@@ -2296,8 +2286,8 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
/* Interface route-map */
if (ri->routemap[RIP_FILTER_OUT])
{
- ret = route_map_apply (ri->routemap[RIP_FILTER_OUT],
- (struct prefix *) p, RMAP_RIP,
+ ret = route_map_apply (ri->routemap[RIP_FILTER_OUT],
+ (struct prefix *) p, RMAP_RIP,
rinfo);
if (ret == RMAP_DENYMATCH)
@@ -2308,7 +2298,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
continue;
}
}
-
+
/* Apply redistribute route map - continue, if deny */
if (rip->route_map[rinfo->type].name
&& rinfo->sub_type != RIP_ROUTE_INTERFACE)
@@ -2316,7 +2306,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
ret = route_map_apply (rip->route_map[rinfo->type].map,
(struct prefix *)p, RMAP_RIP, rinfo);
- if (ret == RMAP_DENYMATCH)
+ if (ret == RMAP_DENYMATCH)
{
if (IS_RIP_DEBUG_PACKET)
zlog_debug ("%s/%d is filtered by route-map",
@@ -2338,7 +2328,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
{
/* If the route is not connected or localy generated
one, use default-metric value*/
- if (rinfo->type != ZEBRA_ROUTE_RIP
+ if (rinfo->type != ZEBRA_ROUTE_RIP
&& rinfo->type != ZEBRA_ROUTE_CONNECT
&& rinfo->metric != RIP_METRIC_INFINITY)
rinfo->metric_out = rip->default_metric;
@@ -2352,17 +2342,17 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
if (rinfo->metric_out > RIP_METRIC_INFINITY)
rinfo->metric_out = RIP_METRIC_INFINITY;
- /* Perform split-horizon with poisoned reverse
+ /* Perform split-horizon with poisoned reverse
* for RIP and connected routes.
**/
if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) {
- /*
- * We perform split horizon for RIP and connected route.
+ /*
+ * We perform split horizon for RIP and connected route.
* For rip routes, we want to suppress the route if we would
* end up sending the route back on the interface that we
* learned it from, with a higher metric. For connected routes,
* we suppress the route if the prefix is a subset of the
- * source address that we are going to use for the packet
+ * source address that we are going to use for the packet
* (in order to handle the case when multiple subnets are
* configured on the same interface).
*/
@@ -2373,20 +2363,20 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to,
prefix_match((struct prefix *)p, ifc->address))
rinfo->metric_out = RIP_METRIC_INFINITY;
}
-
+
/* Prepare preamble, auth headers, if needs be */
if (num == 0)
{
stream_putc (s, RIP_RESPONSE);
stream_putc (s, version);
stream_putw (s, 0);
-
+
/* auth header for !v1 && !no_auth */
if ( (ri->auth_type != RIP_NO_AUTH) && (version != RIPv1) )
- doff = rip_auth_header_write (s, ri, key, auth_str,
+ doff = rip_auth_header_write (s, ri, key, auth_str,
RIP_AUTH_SIMPLE_SIZE);
}
-
+
/* Write RTE to the stream. */
num = rip_write_rte (num, s, p, version, rinfo);
if (num == rtemax)
@@ -2431,7 +2421,7 @@ rip_update_interface (struct connected *ifc, u_char version, int route_type)
struct sockaddr_in to;
/* When RIP version is 2 and multicast enable interface. */
- if (version == RIPv2 && if_is_multicast (ifc->ifp))
+ if (version == RIPv2 && if_is_multicast (ifc->ifp))
{
if (IS_RIP_DEBUG_EVENT)
zlog_debug ("multicast announce on %s ", ifc->ifp->name);
@@ -2439,7 +2429,7 @@ rip_update_interface (struct connected *ifc, u_char version, int route_type)
rip_output_process (ifc, NULL, route_type, version);
return;
}
-
+
/* If we can't send multicast packet, send it with unicast. */
if (if_is_broadcast (ifc->ifp) || if_is_pointopoint (ifc->ifp))
{
@@ -2502,14 +2492,14 @@ rip_update_process (int route_type)
if (ri->running)
{
- /*
+ /*
* If there is no version configuration in the interface,
- * use rip's version setting.
+ * use rip's version setting.
*/
int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ?
rip->version_send : ri->ri_send);
- if (IS_RIP_DEBUG_EVENT)
+ if (IS_RIP_DEBUG_EVENT)
zlog_debug("SEND UPDATE to %s ifindex %d",
(ifp->name ? ifp->name : "_unknown_"), ifp->ifindex);
@@ -2540,14 +2530,14 @@ rip_update_process (int route_type)
inet_ntoa (p->prefix));
continue;
}
-
+
if ( (connected = connected_lookup_address (ifp, p->prefix)) == NULL)
{
zlog_warn ("Neighbor %s doesnt have connected network",
inet_ntoa (p->prefix));
continue;
}
-
+
/* Set destination address and port */
memset (&to, 0, sizeof (struct sockaddr_in));
to.sin_addr = p->prefix;
@@ -2613,7 +2603,7 @@ rip_triggered_interval (struct thread *t)
rip_triggered_update (t);
}
return 0;
-}
+}
/* Execute triggered update. */
static int
@@ -2650,7 +2640,7 @@ rip_triggered_update (struct thread *t)
update is triggered when the timer expires. */
interval = (random () % 5) + 1;
- rip->t_triggered_interval =
+ rip->t_triggered_interval =
thread_add_timer (master, rip_triggered_interval, NULL, interval);
return 0;
@@ -2674,7 +2664,7 @@ rip_redistribute_withdraw (int type)
{
/* Perform poisoned reverse. */
rinfo->metric = RIP_METRIC_INFINITY;
- RIP_TIMER_ON (rinfo->t_garbage_collect,
+ RIP_TIMER_ON (rinfo->t_garbage_collect,
rip_garbage_collect, rip->garbage_time);
RIP_TIMER_OFF (rinfo->t_timeout);
rinfo->flags |= RIP_RTF_CHANGED;
@@ -2742,20 +2732,20 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp,
rte = rip_packet.rte;
rte->metric = htonl (RIP_METRIC_INFINITY);
- if (connected)
+ if (connected)
{
- /*
+ /*
* connected is only sent for ripv1 case, or when
* interface does not support multicast. Caller loops
* over each connected address for this case.
*/
- if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet),
+ if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet),
to, connected) != sizeof (rip_packet))
return -1;
else
return sizeof (rip_packet);
}
-
+
/* send request on each connected network */
for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected))
{
@@ -2766,13 +2756,13 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp,
if (p->family != AF_INET)
continue;
- if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet),
+ if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet),
to, connected) != sizeof (rip_packet))
return -1;
}
return sizeof (rip_packet);
}
-
+
static int
rip_update_jitter (unsigned long time)
{
@@ -2782,14 +2772,14 @@ rip_update_jitter (unsigned long time)
The RIPv2 RFC says jitter should be small compared to
update_time. We consider 1/JITTER_BOUND to be small.
*/
-
+
int jitter_input = time;
int jitter;
-
+
if (jitter_input < JITTER_BOUND)
jitter_input = JITTER_BOUND;
-
- jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input));
+
+ jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input));
return jitter/JITTER_BOUND;
}
@@ -2811,22 +2801,22 @@ rip_event (enum rip_event event, int sock)
rip->t_update = NULL;
}
jitter = rip_update_jitter (rip->update_time);
- rip->t_update =
- thread_add_timer (master, rip_update, NULL,
+ rip->t_update =
+ thread_add_timer (master, rip_update, NULL,
sock ? 2 : rip->update_time + jitter);
break;
case RIP_TRIGGERED_UPDATE:
if (rip->t_triggered_interval)
rip->trigger = 1;
else if (! rip->t_triggered_update)
- rip->t_triggered_update =
+ rip->t_triggered_update =
thread_add_event (master, rip_triggered_update, NULL, 0);
break;
default:
break;
}
}
-
+
DEFUN (router_rip,
router_rip_cmd,
"router rip",
@@ -2882,7 +2872,7 @@ DEFUN (rip_version,
rip->version_recv = version;
return CMD_SUCCESS;
-}
+}
DEFUN (no_rip_version,
no_rip_version_cmd,
@@ -2895,7 +2885,7 @@ DEFUN (no_rip_version,
rip->version_recv = RI_RIP_VERSION_1_AND_2;
return CMD_SUCCESS;
-}
+}
ALIAS (no_rip_version,
no_rip_version_val_cmd,
@@ -2932,7 +2922,7 @@ DEFUN (rip_route,
return CMD_WARNING;
}
- node->info = (char *)"static";
+ node->info = rip_static_string.p ;
rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0);
@@ -3043,21 +3033,21 @@ DEFUN (rip_timers,
unsigned long RIP_TIMER_MIN = 5;
update = strtoul (argv[0], &endptr, 10);
- if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0')
+ if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0')
{
vty_out (vty, "update timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
timeout = strtoul (argv[1], &endptr, 10);
- if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0')
+ if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0')
{
vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
garbage = strtoul (argv[2], &endptr, 10);
- if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0')
+ if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0')
{
vty_out (vty, "garbage timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
@@ -3102,7 +3092,7 @@ ALIAS (no_rip_timers,
"Routing information timeout timer. Default is 180.\n"
"Garbage collection timer. Default is 120.\n")
-
+
struct route_table *rip_distance_table;
struct rip_distance
@@ -3280,7 +3270,7 @@ rip_distance_show (struct vty *vty)
struct rip_distance *rdistance;
int header = 1;
char buf[BUFSIZ];
-
+
vty_out (vty, " Distance: (default is %d)%s",
rip->distance ? rip->distance :ZEBRA_RIP_DISTANCE_DEFAULT,
VTY_NEWLINE);
@@ -3370,7 +3360,7 @@ DEFUN (no_rip_distance_source_access_list,
rip_distance_unset (vty, argv[0], argv[1], argv[2]);
return CMD_SUCCESS;
}
-
+
/* Print out routes update time. */
static void
rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo)
@@ -3436,7 +3426,7 @@ DEFUN (show_ip_rip,
" (i) - interface%s%s"
" Network Next Hop Metric From Tag Time%s",
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
+
for (np = route_top (rip->table); np; np = route_next (np))
if ((rinfo = np->info) != NULL)
{
@@ -3447,20 +3437,20 @@ DEFUN (show_ip_rip,
zebra_route_char(rinfo->type),
rip_route_type_print (rinfo->sub_type),
inet_ntoa (np->p.u.prefix4), np->p.prefixlen);
-
+
len = 24 - len;
if (len > 0)
vty_out (vty, "%*s", len, " ");
- if (rinfo->nexthop.s_addr)
+ if (rinfo->nexthop.s_addr)
vty_out (vty, "%-20s %2d ", inet_ntoa (rinfo->nexthop),
rinfo->metric);
else
vty_out (vty, "0.0.0.0 %2d ", rinfo->metric);
/* Route which exist in kernel routing table. */
- if ((rinfo->type == ZEBRA_ROUTE_RIP) &&
+ if ((rinfo->type == ZEBRA_ROUTE_RIP) &&
(rinfo->sub_type == RIP_ROUTE_RTE))
{
vty_out (vty, "%-15s ", inet_ntoa (rinfo->from));
@@ -3477,7 +3467,7 @@ DEFUN (show_ip_rip,
{
if (rinfo->external_metric)
{
- len = vty_out (vty, "self (%s:%d)",
+ len = vty_out (vty, "self (%s:%d)",
zebra_route_string(rinfo->type),
rinfo->external_metric);
len = 16 - len;
@@ -3516,7 +3506,7 @@ DEFUN (show_ip_rip_status,
vty_out (vty, "Routing Protocol is \"rip\"%s", VTY_NEWLINE);
vty_out (vty, " Sending updates every %ld seconds with +/-50%%,",
rip->update_time);
- vty_out (vty, " next due in %lu seconds%s",
+ vty_out (vty, " next due in %lu seconds%s",
thread_timer_remain_second(rip->t_update),
VTY_NEWLINE);
vty_out (vty, " Timeout after %ld seconds,", rip->timeout_time);
@@ -3525,7 +3515,7 @@ DEFUN (show_ip_rip_status,
/* Filtering status show. */
config_show_distribute (vty);
-
+
/* Default metric information. */
vty_out (vty, " Default redistribution metric is %d%s",
rip->default_metric, VTY_NEWLINE);
@@ -3563,7 +3553,7 @@ DEFUN (show_ip_rip_status,
receive_version = lookup (ri_version_msg, rip->version_recv);
else
receive_version = lookup (ri_version_msg, ri->ri_receive);
-
+
vty_out (vty, " %-17s%-3s %-3s %s%s", ifp->name,
send_version,
receive_version,
@@ -3573,7 +3563,7 @@ DEFUN (show_ip_rip_status,
}
vty_out (vty, " Routing for Networks:%s", VTY_NEWLINE);
- config_write_rip_network (vty, 0);
+ config_write_rip_network (vty, 0);
{
int found_passive = 0;
@@ -3615,16 +3605,16 @@ config_write_rip (struct vty *vty)
/* Router RIP statement. */
vty_out (vty, "router rip%s", VTY_NEWLINE);
write++;
-
+
/* RIP version statement. Default is RIP version 2. */
if (rip->version_send != RI_RIP_VERSION_2
|| rip->version_recv != RI_RIP_VERSION_1_AND_2)
vty_out (vty, " version %d%s", rip->version_send,
VTY_NEWLINE);
-
+
/* RIP timer configuration. */
- if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT
- || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT
+ if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT
+ || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT
|| rip->garbage_time != RIP_GARBAGE_TIMER_DEFAULT)
vty_out (vty, " timers basic %lu %lu %lu%s",
rip->update_time,
@@ -3651,7 +3641,7 @@ config_write_rip (struct vty *vty)
/* RIP enabled network and interface configuration. */
config_write_rip_network (vty, 1);
-
+
/* RIP default metric configuration */
if (rip->default_metric != RIP_DEFAULT_METRIC_DEFAULT)
vty_out (vty, " default-metric %d%s",
@@ -3678,7 +3668,7 @@ config_write_rip (struct vty *vty)
/* RIP static route configuration. */
for (rn = route_top (rip->route); rn; rn = route_next (rn))
if (rn->info)
- vty_out (vty, " route %s/%d%s",
+ vty_out (vty, " route %s/%d%s",
inet_ntoa (rn->p.u.prefix4),
rn->p.prefixlen,
VTY_NEWLINE);
@@ -3694,7 +3684,7 @@ static struct cmd_node rip_node =
"%s(config-router)# ",
1
};
-
+
/* Distribute-list update functions. */
static void
rip_distribute_update (struct distribute *dist)
@@ -3785,7 +3775,7 @@ rip_distribute_update_all_wrapper(struct access_list *notused)
{
rip_distribute_update_all(NULL);
}
-
+
/* Delete all added rip route. */
void
rip_clean (void)
@@ -3804,7 +3794,7 @@ rip_clean (void)
rinfo->sub_type == RIP_ROUTE_RTE)
rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p,
&rinfo->nexthop, rinfo->metric);
-
+
RIP_TIMER_OFF (rinfo->t_timeout);
RIP_TIMER_OFF (rinfo->t_garbage_collect);
@@ -3860,7 +3850,7 @@ rip_clean (void)
XFREE (MTYPE_ROUTE_TABLE, rip->table);
XFREE (MTYPE_ROUTE_TABLE, rip->route);
XFREE (MTYPE_ROUTE_TABLE, rip->neighbor);
-
+
XFREE (MTYPE_RIP, rip);
rip = NULL;
}
@@ -3951,10 +3941,10 @@ rip_routemap_update_redistribute (void)
if (rip)
{
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
{
if (rip->route_map[i].name)
- rip->route_map[i].map =
+ rip->route_map[i].map =
route_map_lookup_by_name (rip->route_map[i].name);
}
}
diff --git a/ripd/ripd.h b/ripd/ripd.h
index 45b07b9c..db2dbde0 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -302,7 +302,7 @@ struct rip_peer
struct in_addr addr;
/* Peer RIP tag value. */
- int domain;
+ unsigned int domain;
/* Last update time. */
time_t uptime;
@@ -438,4 +438,15 @@ extern struct thread_master *master;
/* RIP statistics for SNMP. */
extern long rip_global_route_changes;
extern long rip_global_queries;
+
+/* To avoid compiler warnings. */
+union rip_miyagi_string
+{
+ const char* cp ;
+ char* p ;
+} ;
+
+extern union rip_miyagi_string rip_enabled_string ;
+extern union rip_miyagi_string rip_static_string ;
+
#endif /* _ZEBRA_RIP_H */
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
index 6718fff2..cd458ef1 100644
--- a/ripngd/ripng_interface.c
+++ b/ripngd/ripng_interface.c
@@ -590,7 +590,7 @@ ripng_enable_network_add (struct prefix *p)
return -1;
}
else
- node->info = (char *) "enabled";
+ node->info = ripng_enabled_string.p ;
/* XXX: One should find a better solution than a generic one */
ripng_enable_apply_all();
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index 6e32d83c..d534b2b6 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.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>
@@ -55,6 +55,8 @@ enum
extern struct zebra_privs_t ripngd_privs;
+union ripng_miyagi_string ripng_enabled_string = { .cp = "enable" } ;
+
/* Prototypes. */
void
ripng_output_process (struct interface *, struct sockaddr_in6 *, int);
@@ -95,9 +97,9 @@ ripng_info_free (struct ripng_info *rinfo)
{
XFREE (MTYPE_RIPNG_ROUTE, rinfo);
}
-
+
/* Create ripng socket. */
-static int
+static int
ripng_make_socket (void)
{
int ret;
@@ -136,7 +138,7 @@ ripng_make_socket (void)
if (ripngd_privs.change (ZPRIVS_RAISE))
zlog_err ("ripng_make_socket: could not raise privs");
-
+
ret = bind (sock, (struct sockaddr *) &ripaddr, sizeof (ripaddr));
if (ret < 0)
{
@@ -152,7 +154,7 @@ ripng_make_socket (void)
/* Send RIPng packet. */
int
-ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
+ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
struct interface *ifp)
{
int ret;
@@ -212,7 +214,7 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
if (ret < 0) {
if (to)
- zlog_err ("RIPng send fail on %s to %s: %s", ifp->name,
+ zlog_err ("RIPng send fail on %s to %s: %s", ifp->name,
inet6_ntoa (to->sin6_addr), safe_strerror (errno));
else
zlog_err ("RIPng send fail on %s: %s", ifp->name, safe_strerror (errno));
@@ -224,14 +226,17 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
/* Receive UDP RIPng packet from socket. */
static int
ripng_recv_packet (int sock, u_char *buf, int bufsize,
- struct sockaddr_in6 *from, unsigned int *ifindex,
+ struct sockaddr_in6 *from, unsigned int *ifindex,
int *hoplimit)
{
int ret;
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsgptr;
- struct in6_addr dst;
+ struct in6_addr dst ;
+
+ dst.s6_addr[0] = 0xFF ; /* calm down compiler: also do nothing
+ if does not find a dst ! */
/* Ancillary data. This store cmsghdr and in6_pktinfo. But at this
point I can't determine size of cmsghdr */
@@ -253,14 +258,14 @@ ripng_recv_packet (int sock, u_char *buf, int bufsize,
return ret;
for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL;
- cmsgptr = CMSG_NXTHDR(&msg, cmsgptr))
+ cmsgptr = CMSG_NXTHDR(&msg, cmsgptr))
{
/* I want interface index which this packet comes from. */
if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
- cmsgptr->cmsg_type == IPV6_PKTINFO)
+ cmsgptr->cmsg_type == IPV6_PKTINFO)
{
struct in6_pktinfo *ptr;
-
+
ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
*ifindex = ptr->ipi6_ifindex;
dst = ptr->ipi6_addr;
@@ -303,7 +308,7 @@ ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv)
command_str = "unknown";
/* Dump packet header. */
- zlog_debug ("%s %s version %d packet size %d",
+ zlog_debug ("%s %s version %d packet size %d",
sndrcv, command_str, packet->version, size);
/* Dump each routing table entry. */
@@ -314,8 +319,8 @@ ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv)
if (rte->metric == RIPNG_METRIC_NEXTHOP)
zlog_debug (" nexthop %s/%d", inet6_ntoa (rte->addr), rte->prefixlen);
else
- zlog_debug (" %s/%d metric %d tag %d",
- inet6_ntoa (rte->addr), rte->prefixlen,
+ zlog_debug (" %s/%d metric %d tag %d",
+ inet6_ntoa (rte->addr), rte->prefixlen,
rte->metric, ntohs (rte->tag));
}
}
@@ -333,7 +338,7 @@ ripng_nexthop_rte (struct rte *rte,
zlog_debug ("RIPng nexthop RTE address %s tag %d prefixlen %d",
inet6_ntoa (rte->addr), ntohs (rte->tag), rte->prefixlen);
- /* RFC2080 2.1.1 Next Hop:
+ /* RFC2080 2.1.1 Next Hop:
The route tag and prefix length in the next hop RTE must be
set to zero on sending and ignored on receiption. */
if (ntohs (rte->tag) != 0)
@@ -411,7 +416,7 @@ ripng_garbage_collect (struct thread *t)
/* Off timeout timer. */
RIPNG_TIMER_OFF (rinfo->t_timeout);
-
+
/* Get route_node pointer. */
rp = rinfo->rp;
@@ -439,7 +444,7 @@ ripng_timeout (struct thread *t)
rp = rinfo->rp;
/* - The garbage-collection timer is set for 120 seconds. */
- RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect,
ripng->garbage_time);
/* Delete this route from the kernel. */
@@ -483,7 +488,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
/* Input distribute-list filtering. */
if (ri->list[RIPNG_FILTER_IN])
{
- if (access_list_apply (ri->list[RIPNG_FILTER_IN],
+ if (access_list_apply (ri->list[RIPNG_FILTER_IN],
(struct prefix *) p) == FILTER_DENY)
{
if (IS_RIPNG_DEBUG_PACKET)
@@ -494,7 +499,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
}
if (ri->prefix[RIPNG_FILTER_IN])
{
- if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN],
+ if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN],
(struct prefix *) p) == PREFIX_DENY)
{
if (IS_RIPNG_DEBUG_PACKET)
@@ -511,7 +516,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->list[DISTRIBUTE_IN])
{
alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]);
-
+
if (alist)
{
if (access_list_apply (alist,
@@ -527,7 +532,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->prefix[DISTRIBUTE_IN])
{
plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]);
-
+
if (plist)
{
if (prefix_list_apply (plist,
@@ -581,7 +586,7 @@ ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->list[DISTRIBUTE_OUT])
{
alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]);
-
+
if (alist)
{
if (access_list_apply (alist,
@@ -597,7 +602,7 @@ ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->prefix[DISTRIBUTE_OUT])
{
plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]);
-
+
if (plist)
{
if (prefix_list_apply (plist,
@@ -667,7 +672,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
newinfo.metric_out = rte->metric; /* XXX */
newinfo.tag = ntohs(rte->tag); /* XXX */
- ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN],
+ ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN],
(struct prefix *)&p, RMAP_RIPNG, &newinfo);
if (ret == RMAP_DENYMATCH)
@@ -705,7 +710,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
* arrived. If the result is greater than infinity, use infinity
* (RFC2453 Sec. 3.9.2)
**/
-
+
/* Zebra ripngd can handle offset-list in. */
ret = ripng_offset_list_apply_in (&p, ifp, &rte->metric);
@@ -753,7 +758,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
if (rte->metric != RIPNG_METRIC_INFINITY)
{
rinfo = ripng_info_new ();
-
+
/* - Setting the destination prefix and length to those in
the RTE. */
rp->info = rinfo;
@@ -797,12 +802,12 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
else
{
rinfo = rp->info;
-
+
/* If there is an existing route, compare the next hop address
to the address of the router from which the datagram came.
If this datagram is from the same router as the existing
route, reinitialize the timeout. */
- same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr)
+ same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr)
&& (rinfo->ifindex == ifp->ifindex));
if (same)
@@ -879,7 +884,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
if (oldmetric != RIPNG_METRIC_INFINITY)
{
/* - The garbage-collection timer is set for 120 seconds. */
- RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect,
ripng_garbage_collect, ripng->garbage_time);
RIPNG_TIMER_OFF (rinfo->t_timeout);
@@ -912,7 +917,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
/* Add redistributed route to RIPng table. */
void
-ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
+ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
unsigned int ifindex, struct in6_addr *nexthop)
{
struct route_node *rp;
@@ -957,7 +962,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
return;
}
}
-
+
RIPNG_TIMER_OFF (rinfo->t_timeout);
RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
@@ -982,10 +987,10 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
rinfo->ifindex = ifindex;
rinfo->metric = 1;
rinfo->rp = rp;
-
+
if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop))
rinfo->nexthop = *nexthop;
-
+
rinfo->flags |= RIPNG_RTF_FIB;
rp->info = rinfo;
@@ -1010,7 +1015,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
/* Delete redistributed route to RIPng table. */
void
-ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
+ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
unsigned int ifindex)
{
struct route_node *rp;
@@ -1037,13 +1042,13 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
rinfo = rp->info;
if (rinfo != NULL
- && rinfo->type == type
- && rinfo->sub_type == sub_type
+ && rinfo->type == type
+ && rinfo->sub_type == sub_type
&& rinfo->ifindex == ifindex)
{
/* Perform poisoned reverse. */
rinfo->metric = RIPNG_METRIC_INFINITY;
- RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect,
ripng_garbage_collect, ripng->garbage_time);
RIPNG_TIMER_OFF (rinfo->t_timeout);
@@ -1051,7 +1056,7 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
ripng_aggregate_decrement (rp, rinfo);
rinfo->flags |= RIPNG_RTF_CHANGED;
-
+
if (IS_RIPNG_DEBUG_EVENT)
zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]",
inet6_ntoa(p->prefix), p->prefixlen,
@@ -1071,7 +1076,7 @@ ripng_redistribute_withdraw (int type)
if (!ripng)
return;
-
+
for (rp = route_top (ripng->table); rp; rp = route_next (rp))
if ((rinfo = rp->info) != NULL)
{
@@ -1080,7 +1085,7 @@ ripng_redistribute_withdraw (int type)
{
/* Perform poisoned reverse. */
rinfo->metric = RIPNG_METRIC_INFINITY;
- RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect,
ripng_garbage_collect, ripng->garbage_time);
RIPNG_TIMER_OFF (rinfo->t_timeout);
@@ -1104,7 +1109,7 @@ ripng_redistribute_withdraw (int type)
/* RIP routing information. */
static void
-ripng_response_process (struct ripng_packet *packet, int size,
+ripng_response_process (struct ripng_packet *packet, int size,
struct sockaddr_in6 *from, struct interface *ifp,
int hoplimit)
{
@@ -1160,7 +1165,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
/* Update RIPng peer. */
ripng_peer_update (from, packet->version);
-
+
/* Reset nexthop. */
memset (&nexthop, 0, sizeof (struct ripng_nexthop));
nexthop.flag = RIPNG_NEXTHOP_UNSPEC;
@@ -1168,7 +1173,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
/* Set RTE pointer. */
rte = packet->rte;
- for (lim = ((caddr_t) packet) + size; (caddr_t) rte < lim; rte++)
+ for (lim = ((caddr_t) packet) + size; (caddr_t) rte < lim; rte++)
{
/* First of all, we have to check this RTE is next hop RTE or
not. Next hop RTE is completely different with normal RTE so
@@ -1237,7 +1242,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
/* Response to request message. */
static void
-ripng_request_process (struct ripng_packet *packet,int size,
+ripng_request_process (struct ripng_packet *packet,int size,
struct sockaddr_in6 *from, struct interface *ifp)
{
caddr_t lim;
@@ -1281,7 +1286,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
IN6_IS_ADDR_UNSPECIFIED (&rte->addr) &&
rte->prefixlen == 0 &&
rte->metric == RIPNG_METRIC_INFINITY)
- {
+ {
/* All route with split horizon */
ripng_output_process (ifp, from, ripng_all_route);
}
@@ -1304,7 +1309,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
p.prefix = rte->addr;
p.prefixlen = rte->prefixlen;
apply_mask_ipv6 (&p);
-
+
rp = route_node_lookup (ripng->table, (struct prefix *) &p);
if (rp)
@@ -1330,7 +1335,7 @@ ripng_read (struct thread *thread)
int sock;
struct sockaddr_in6 from;
struct ripng_packet *packet;
- unsigned int ifindex;
+ unsigned int ifindex = 0 ; /* avoid warning (gcc 4.2.1 !) */
struct interface *ifp;
int hoplimit = -1;
@@ -1347,10 +1352,10 @@ ripng_read (struct thread *thread)
ripng_event (RIPNG_READ, sock);
/* Read RIPng packet. */
- len = ripng_recv_packet (sock, STREAM_DATA (ripng->ibuf),
+ len = ripng_recv_packet (sock, STREAM_DATA (ripng->ibuf),
STREAM_SIZE (ripng->ibuf), &from, &ifindex,
&hoplimit);
- if (len < 0)
+ if (len < 0)
{
zlog_warn ("RIPng recvfrom failed: %s.", safe_strerror (errno));
return len;
@@ -1372,7 +1377,7 @@ ripng_read (struct thread *thread)
/* RIPng packet received. */
if (IS_RIPNG_DEBUG_EVENT)
zlog_debug ("RIPng packet received from %s port %d on %s",
- inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port),
+ inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port),
ifp ? ifp->name : "unknown");
/* Logging before packet checking. */
@@ -1387,9 +1392,9 @@ ripng_read (struct thread *thread)
}
/* Packet version mismatch checking. */
- if (packet->version != ripng->version)
+ if (packet->version != ripng->version)
{
- zlog_warn ("RIPng packet version %d doesn't fit to my version %d",
+ zlog_warn ("RIPng packet version %d doesn't fit to my version %d",
packet->version, ripng->version);
ripng_peer_bad_packet (&from);
return 0;
@@ -1461,7 +1466,7 @@ ripng_update (struct thread *t)
if (ri->ri_send == RIPNG_SEND_OFF)
{
if (IS_RIPNG_DEBUG_EVENT)
- zlog (NULL, LOG_DEBUG,
+ zlog (NULL, LOG_DEBUG,
"[Event] RIPng send to if %d is suppressed by config",
ifp->ifindex);
continue;
@@ -1498,7 +1503,7 @@ ripng_triggered_interval (struct thread *t)
ripng_triggered_update (t);
}
return 0;
-}
+}
/* Execute triggered update. */
int
@@ -1553,7 +1558,7 @@ ripng_triggered_update (struct thread *t)
update is triggered when the timer expires. */
interval = (random () % 5) + 1;
- ripng->t_triggered_interval =
+ ripng->t_triggered_interval =
thread_add_timer (master, ripng_triggered_interval, NULL, interval);
return 0;
@@ -1611,9 +1616,9 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
/* Get RIPng interface. */
ri = ifp->info;
-
+
ripng_rte_list = ripng_rte_new();
-
+
for (rp = route_top (ripng->table); rp; rp = route_next (rp))
{
if ((rinfo = rp->info) != NULL && rinfo->suppress == 0)
@@ -1665,8 +1670,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
{
int ret;
- ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
- (struct prefix *) p, RMAP_RIPNG,
+ ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
+ (struct prefix *) p, RMAP_RIPNG,
rinfo);
if (ret == RMAP_DENYMATCH)
@@ -1724,7 +1729,7 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
rinfo->metric_out = RIPNG_METRIC_INFINITY;
- /* Perform split-horizon with poisoned reverse
+ /* Perform split-horizon with poisoned reverse
* for RIPng routes.
**/
if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) {
@@ -1738,8 +1743,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
}
/* Process the aggregated RTE entry */
- if ((aggregate = rp->aggregate) != NULL &&
- aggregate->count > 0 &&
+ if ((aggregate = rp->aggregate) != NULL &&
+ aggregate->count > 0 &&
aggregate->suppress == 0)
{
/* If no route-map are applied, the RTE will be these following
@@ -1770,8 +1775,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
newinfo.tag = aggregate->tag;
newinfo.tag_out = aggregate->tag_out;
- ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
- (struct prefix *) p, RMAP_RIPNG,
+ ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
+ (struct prefix *) p, RMAP_RIPNG,
&newinfo);
if (ret == RMAP_DENYMATCH)
@@ -1831,7 +1836,7 @@ ripng_create (void)
ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT;
ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT;
ripng->default_metric = RIPNG_DEFAULT_METRIC_DEFAULT;
-
+
/* Make buffer. */
ripng->ibuf = stream_new (RIPNG_MAX_PACKET_SIZE * 5);
ripng->obuf = stream_new (RIPNG_MAX_PACKET_SIZE);
@@ -1840,7 +1845,7 @@ ripng_create (void)
ripng->table = route_table_init ();
ripng->route = route_table_init ();
ripng->aggregate = route_table_init ();
-
+
/* Make socket. */
ripng->sock = ripng_make_socket ();
if (ripng->sock < 0)
@@ -1877,11 +1882,11 @@ ripng_request (struct interface *ifp)
rte = ripng_packet.rte;
rte->metric = RIPNG_METRIC_INFINITY;
- return ripng_send_packet ((caddr_t) &ripng_packet, sizeof (ripng_packet),
+ return ripng_send_packet ((caddr_t) &ripng_packet, sizeof (ripng_packet),
NULL, ifp);
}
-
+
static int
ripng_update_jitter (int time)
{
@@ -1908,22 +1913,22 @@ ripng_event (enum ripng_event event, int sock)
/* Update timer jitter. */
jitter = ripng_update_jitter (ripng->update_time);
- ripng->t_update =
- thread_add_timer (master, ripng_update, NULL,
+ ripng->t_update =
+ thread_add_timer (master, ripng_update, NULL,
sock ? 2 : ripng->update_time + jitter);
break;
case RIPNG_TRIGGERED_UPDATE:
if (ripng->t_triggered_interval)
ripng->trigger = 1;
else if (! ripng->t_triggered_update)
- ripng->t_triggered_update =
+ ripng->t_triggered_update =
thread_add_event (master, ripng_triggered_update, NULL, 0);
break;
default:
break;
}
}
-
+
/* Print out routes update time. */
static void
@@ -1934,7 +1939,7 @@ ripng_vty_out_uptime (struct vty *vty, struct ripng_info *rinfo)
#define TIME_BUF 25
char timebuf [TIME_BUF];
struct thread *thread;
-
+
if ((thread = rinfo->t_timeout) != NULL)
{
clock = thread_timer_remain_second (thread);
@@ -1981,7 +1986,7 @@ ripng_route_subtype_print (struct ripng_info *rinfo)
strcat(str, "?");
break;
}
-
+
return str;
}
@@ -2001,7 +2006,7 @@ DEFUN (show_ipv6_ripng,
if (! ripng)
return CMD_SUCCESS;
- /* Header of display. */
+ /* Header of display. */
vty_out (vty, "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP%s"
"Sub-codes:%s"
" (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
@@ -2009,7 +2014,7 @@ DEFUN (show_ipv6_ripng,
" Network Next Hop Via Metric Tag Time%s",
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
+
for (rp = route_top (ripng->table); rp; rp = route_next (rp))
{
if ((aggregate = rp->aggregate) != NULL)
@@ -2021,7 +2026,7 @@ DEFUN (show_ipv6_ripng,
aggregate->count, aggregate->suppress,
inet6_ntoa (p->prefix), p->prefixlen);
#else
- len = vty_out (vty, "R(a) %s/%d ",
+ len = vty_out (vty, "R(a) %s/%d ",
inet6_ntoa (p->prefix), p->prefixlen);
#endif /* DEBUG */
vty_out (vty, "%s", VTY_NEWLINE);
@@ -2058,7 +2063,7 @@ DEFUN (show_ipv6_ripng,
len = vty_out (vty, "%*s", len, " ");
/* from */
- if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+ if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
(rinfo->sub_type == RIPNG_ROUTE_RTE))
{
len = vty_out (vty, "%s", ifindex2ifname(rinfo->ifindex));
@@ -2076,7 +2081,7 @@ DEFUN (show_ipv6_ripng,
rinfo->metric, rinfo->tag);
/* time */
- if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+ if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
(rinfo->sub_type == RIPNG_ROUTE_RTE))
{
/* RTE from remote RIP routers */
@@ -2139,7 +2144,7 @@ DEFUN (show_ipv6_ripng_status,
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
{
struct ripng_interface *ri;
-
+
ri = ifp->info;
if (ri->enable_network || ri->enable_interface)
@@ -2159,7 +2164,7 @@ DEFUN (show_ipv6_ripng_status,
vty_out (vty, " Gateway BadPackets BadRoutes Distance Last Update%s", VTY_NEWLINE);
ripng_peer_display (vty);
- return CMD_SUCCESS;
+ return CMD_SUCCESS;
}
DEFUN (router_ripng,
@@ -2488,14 +2493,14 @@ DEFUN (ripng_timers,
vty_out (vty, "update timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
timeout = strtoul (argv[1], &endptr, 10);
if (timeout == ULONG_MAX || *endptr != '\0')
{
vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
garbage = strtoul (argv[2], &endptr, 10);
if (garbage == ULONG_MAX || *endptr != '\0')
{
@@ -2552,7 +2557,7 @@ DEFUN (show_ipv6_protocols, show_ipv6_protocols_cmd,
return CMD_SUCCESS;
vty_out (vty, "Routing Protocol is \"ripng\"%s", VTY_NEWLINE);
-
+
vty_out (vty, "Sending updates every %ld seconds, next due in %d seconds%s",
ripng->update_time, 0,
VTY_NEWLINE);
@@ -2635,13 +2640,13 @@ ripng_config_write (struct vty *vty)
/* RIP offset-list configuration. */
config_write_ripng_offset_list (vty);
-
+
/* RIPng aggregate routes. */
for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp))
if (rp->info != NULL)
- vty_out (vty, " aggregate-address %s/%d%s",
+ vty_out (vty, " aggregate-address %s/%d%s",
inet6_ntoa (rp->p.u.prefix6),
- rp->p.prefixlen,
+ rp->p.prefixlen,
VTY_NEWLINE);
@@ -2780,7 +2785,7 @@ ripng_distribute_update_all_wrapper (struct access_list *notused)
{
ripng_distribute_update_all(NULL);
}
-
+
/* delete all the added ripng routes. */
void
ripng_clean()
@@ -2931,10 +2936,10 @@ ripng_routemap_update_redistribute (void)
if (ripng)
{
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
{
if (ripng->route_map[i].name)
- ripng->route_map[i].map =
+ ripng->route_map[i].map =
route_map_lookup_by_name (ripng->route_map[i].name);
}
}
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
index ab06d81b..f2124501 100644
--- a/ripngd/ripngd.h
+++ b/ripngd/ripngd.h
@@ -342,6 +342,15 @@ extern struct ripng *ripng;
extern struct thread_master *master;
+/* To avoid compiler warnings. */
+union ripng_miyagi_string
+{
+ const char* cp ;
+ char* p ;
+} ;
+
+extern union ripng_miyagi_string ripng_enabled_string ;
+
/* Prototypes. */
extern void ripng_init (void);
extern void ripng_reset (void);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4ab507bb..d6e24db7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,8 +5,9 @@ AM_CFLAGS = $(PICFLAGS)
AM_LDFLAGS = $(PILDFLAGS)
noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \
- aspathtest testprivs teststream testbgpcap ecommtest \
- testbgpmpattr testchecksum
+ aspathtest testprivs teststream testbgpcap ecommtest \
+ testbgpmpattr testchecksum testvector testsymtab \
+ testlistutil
testsig_SOURCES = test-sig.c
testbuffer_SOURCES = test-buffer.c
@@ -21,6 +22,9 @@ testbgpcap_SOURCES = bgp_capability_test.c
ecommtest_SOURCES = ecommunity_test.c
testbgpmpattr_SOURCES = bgp_mp_attr_test.c
testchecksum_SOURCES = test-checksum.c
+testvector_SOURCES = test-vector.c
+testsymtab_SOURCES = test-symtab.c
+testlistutil_SOURCES = test-list_util.c
testsig_LDADD = ../lib/libzebra.la @LIBCAP@
testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@
@@ -34,4 +38,7 @@ aspathtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a
testbgpcap_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a
ecommtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a
testbgpmpattr_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a
-testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@
+testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@
+testvector_LDADD = ../lib/libzebra.la @LIBCAP@
+testsymtab_LDADD = ../lib/libzebra.la @LIBCAP@
+testlistutil_LDADD = ../lib/libzebra.la @LIBCAP@
diff --git a/tests/aspath_test.c b/tests/aspath_test.c
index 4a2ce9aa..11008b25 100644
--- a/tests/aspath_test.c
+++ b/tests/aspath_test.c
@@ -22,7 +22,7 @@ struct thread_master *master = NULL;
static int failed = 0;
/* specification for a test - what the results should be */
-struct test_spec
+struct test_spec
{
const char *shouldbe; /* the string the path should parse to */
const char *shouldbe_delete_confed; /* ditto, but once confeds are deleted */
@@ -45,9 +45,9 @@ static struct test_segment {
const u_char asdata[1024];
int len;
struct test_spec sp;
-} test_segments [] =
+} test_segments [] =
{
- { /* 0 */
+ { /* 0 */
"seq1",
"seq(8466,3,52737,4096)",
{ 0x2,0x4, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00 },
@@ -69,7 +69,7 @@ static struct test_segment {
{ /* 2 */
"seq3",
"seq(8466,3,52737,4096,8722,4)",
- { 0x2,0x6, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00,
+ { 0x2,0x6, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00,
0x22,0x12, 0x00,0x04},
14,
{ "8466 3 52737 4096 8722 4",
@@ -91,7 +91,7 @@ static struct test_segment {
"seq(8467, 59649) set(4196,48658) set(17322,30745)",
{ 0x2,0x2, 0x21,0x13, 0xe9,0x01,
0x1,0x2, 0x10,0x64, 0xbe,0x12,
- 0x1,0x2, 0x43,0xaa, 0x78,0x19 },
+ 0x1,0x2, 0x43,0xaa, 0x78,0x19 },
18,
{ "8467 59649 {4196,48658} {17322,30745}",
"8467 59649 {4196,48658} {17322,30745}",
@@ -151,7 +151,7 @@ static struct test_segment {
{ /* 10 */
"seq4",
"seq(8466,2,52737,4096,8722,4)",
- { 0x2,0x6, 0x21,0x12, 0x00,0x02, 0xce,0x01, 0x10,0x00,
+ { 0x2,0x6, 0x21,0x12, 0x00,0x02, 0xce,0x01, 0x10,0x00,
0x22,0x12, 0x00,0x04},
14,
{ "8466 2 52737 4096 8722 4",
@@ -161,7 +161,7 @@ static struct test_segment {
{ /* 11 */
"tripleseq1",
"seq(8466,2,52737) seq(4096,8722,4) seq(8722)",
- { 0x2,0x3, 0x21,0x12, 0x00,0x02, 0xce,0x01,
+ { 0x2,0x3, 0x21,0x12, 0x00,0x02, 0xce,0x01,
0x2,0x3, 0x10,0x00, 0x22,0x12, 0x00,0x04,
0x2,0x1, 0x22,0x12},
20,
@@ -169,7 +169,7 @@ static struct test_segment {
"8466 2 52737 4096 8722 4 8722",
7, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 },
},
- { /* 12 */
+ { /* 12 */
"someprivate",
"seq(8466,64512,52737,65535)",
{ 0x2,0x4, 0x21,0x12, 0xfc,0x00, 0xce,0x01, 0xff,0xff },
@@ -178,7 +178,7 @@ static struct test_segment {
"8466 64512 52737 65535",
4, 0, NOT_ALL_PRIVATE, 65535, 4, 8466 },
},
- { /* 13 */
+ { /* 13 */
"allprivate",
"seq(65534,64512,64513,65535)",
{ 0x2,0x4, 0xff,0xfe, 0xfc,0x00, 0xfc,0x01, 0xff,0xff },
@@ -187,7 +187,7 @@ static struct test_segment {
"65534 64512 64513 65535",
4, 0, ALL_PRIVATE, 65534, 4, 65534 },
},
- { /* 14 */
+ { /* 14 */
"long",
"seq(8466,3,52737,4096,34285,<repeated 49 more times>)",
{ 0x2,0xfa, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
@@ -266,7 +266,7 @@ static struct test_segment {
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285",
-
+
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
@@ -294,7 +294,7 @@ static struct test_segment {
"8466 3 52737 4096 34285 8466 3 52737 4096 34285",
250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
},
- { /* 15 */
+ { /* 15 */
"seq1extra",
"seq(8466,3,52737,4096,3456)",
{ 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 },
@@ -310,7 +310,7 @@ static struct test_segment {
0,
{ "", "", 0, 0, 0, 0, 0, 0 },
},
- { /* 17 */
+ { /* 17 */
"redundantset",
"seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153)",
{ 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80,
@@ -338,7 +338,7 @@ static struct test_segment {
{ /* 19 */
"reconcile_new_asp",
"set(2457,61697,4369), seq(1842,41591,51793)",
- {
+ {
0x1,0x3, 0x09,0x99, 0xf1,0x01, 0x11,0x11,
0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51 },
16,
@@ -391,7 +391,7 @@ static struct test_segment {
"23456 23456 23456 6435 59408",
5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456 },
},
- { /* 24 */
+ { /* 24 */
"redundantset2",
"seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153,7099)",
{ 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80,
@@ -403,7 +403,7 @@ static struct test_segment {
"8466 3 52737 4096 3456 {7099,8153}",
6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
},
- { /* 25 */
+ { /* 25 */
"zero-size overflow",
"#ASNs = 0, data = seq(8466 3 52737 4096 3456)",
{ 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 },
@@ -411,17 +411,17 @@ static struct test_segment {
{ NULL, NULL,
0, 0, 0, 0, 0, 0 },
},
- { /* 26 */
+ { /* 26 */
"zero-size overflow + valid segment",
"seq(#AS=0:8466 3 52737),seq(4096 3456)",
- { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01,
+ { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01,
0x2,0x2, 0x10,0x00, 0x0d,0x80 },
14
,
{ NULL, NULL,
0, 0, 0, 0, 0, 0 },
},
- { /* 27 */
+ { /* 27 */
"invalid segment type",
"type=8(4096 3456)",
{ 0x8,0x2, 0x10,0x00, 0x0d,0x80 },
@@ -453,7 +453,7 @@ static struct aspath_tests {
AS2_DATA, 0,
0,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
10,
},
3,
@@ -466,7 +466,7 @@ static struct aspath_tests {
AS2_DATA, -1,
0,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
8,
},
3,
@@ -479,7 +479,7 @@ static struct aspath_tests {
AS2_DATA, -1,
0,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
12,
},
3,
@@ -492,7 +492,7 @@ static struct aspath_tests {
AS2_DATA, -1,
0,
{ BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
10,
},
3,
@@ -505,7 +505,7 @@ static struct aspath_tests {
AS2_DATA, -1,
0,
{ BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH,
+ BGP_ATTR_AS4_PATH,
10,
},
3,
@@ -518,7 +518,7 @@ static struct aspath_tests {
AS4_DATA, -1,
PEER_CAP_AS4_RCV,
{ BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH,
+ BGP_ATTR_AS4_PATH,
10,
},
3,
@@ -531,7 +531,7 @@ static struct aspath_tests {
AS4_DATA, 0,
PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
18,
},
3,
@@ -544,7 +544,7 @@ static struct aspath_tests {
AS4_DATA, -1,
PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
16,
},
3,
@@ -557,7 +557,7 @@ static struct aspath_tests {
AS4_DATA, -1,
PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
20,
},
3,
@@ -570,7 +570,7 @@ static struct aspath_tests {
AS4_DATA, -1,
PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
{ BGP_ATTR_FLAG_TRANS,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
22,
},
3,
@@ -583,7 +583,7 @@ static struct aspath_tests {
AS4_DATA, -1,
PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
{ BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS_PATH,
+ BGP_ATTR_AS_PATH,
18,
},
3,
@@ -596,7 +596,7 @@ static struct aspath_tests {
AS4_DATA, -1,
PEER_CAP_AS4_ADV,
{ BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
- BGP_ATTR_AS4_PATH,
+ BGP_ATTR_AS4_PATH,
14,
},
3,
@@ -609,7 +609,7 @@ static struct tests {
const struct test_segment *test1;
const struct test_segment *test2;
struct test_spec sp;
-} prepend_tests[] =
+} prepend_tests[] =
{
/* 0 */
{ &test_segments[0], &test_segments[1],
@@ -691,7 +691,7 @@ static struct tests {
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 2 52737 4096 8722 4 8722",
-
+
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
@@ -757,7 +757,7 @@ struct tests reconcile_tests[] =
},
{ NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } },
};
-
+
struct tests aggregate_tests[] =
{
{ &test_segments[0], &test_segments[2],
@@ -790,7 +790,7 @@ struct tests aggregate_tests[] =
{ NULL, NULL, { NULL, 0, 0} },
};
-struct compare_tests
+struct compare_tests
{
int test_index1;
int test_index2;
@@ -830,17 +830,17 @@ make_aspath (const u_char *data, size_t len, int use32bit)
{
struct stream *s = NULL;
struct aspath *as;
-
+
if (len)
{
s = stream_new (len);
stream_put (s, data, len);
}
- as = aspath_parse (s, len, use32bit);
-
+ as = aspath_parse (s, len, use32bit, 0);
+
if (s)
stream_free (s);
-
+
return as;
}
@@ -857,7 +857,7 @@ printbytes (const u_char *bytes, int len)
i++;
}
printf ("\n");
-}
+}
/* validate the given aspath */
static int
@@ -868,33 +868,33 @@ validate (struct aspath *as, const struct test_spec *sp)
const u_char *out;
static struct stream *s;
struct aspath *asinout, *asconfeddel, *asstr, *as4;
-
+
if (as == NULL && sp->shouldbe == NULL)
{
printf ("Correctly failed to parse\n");
return fails;
}
-
+
out = aspath_snmp_pathseg (as, &bytes);
asinout = make_aspath (out, bytes, 0);
-
+
/* Excercise AS4 parsing a bit, with a dogfood test */
if (!s)
s = stream_new (4096);
bytes4 = aspath_put (s, as, 1);
as4 = make_aspath (STREAM_DATA(s), bytes4, 1);
-
+
asstr = aspath_str2aspath (sp->shouldbe);
-
+
asconfeddel = aspath_delete_confed_seq (aspath_dup (asinout));
-
+
printf ("got: %s\n", aspath_print(as));
-
+
/* the parsed path should match the specified 'shouldbe' string.
* We should pass the "eat our own dog food" test, be able to output
* this path and then input it again. Ie the path resulting from:
*
- * aspath_parse(aspath_put(as))
+ * aspath_parse(aspath_put(as))
*
* should:
*
@@ -909,7 +909,7 @@ validate (struct aspath *as, const struct test_spec *sp)
*
* aspath_parse(aspath_put(as,USE32BIT))
*
- * Confederation related tests:
+ * Confederation related tests:
* - aspath_delete_confed_seq(aspath) should match shouldbe_confed
* - aspath_delete_confed_seq should be idempotent.
*/
@@ -930,9 +930,9 @@ validate (struct aspath *as, const struct test_spec *sp)
fails++;
printf ("shouldbe:\n%s\n", sp->shouldbe);
printf ("as4:\n%s\n", aspath_print (as4));
- printf ("hash keys: in: %d out->in: %d\n",
+ printf ("hash keys: in: %d out->in: %d\n",
aspath_key_make (as), aspath_key_make (asinout));
- printf ("hops: %d, counted %d %d\n", sp->hops,
+ printf ("hops: %d, counted %d %d\n", sp->hops,
aspath_count_hops (as),
aspath_count_hops (asinout) );
printf ("confeds: %d, counted %d %d\n", sp->confeds,
@@ -942,13 +942,13 @@ validate (struct aspath *as, const struct test_spec *sp)
printbytes (out, bytes);
}
/* basic confed related tests */
- if ((aspath_print (asconfeddel) == NULL
+ if ((aspath_print (asconfeddel) == NULL
&& sp->shouldbe_delete_confed != NULL)
- || (aspath_print (asconfeddel) != NULL
+ || (aspath_print (asconfeddel) != NULL
&& sp->shouldbe_delete_confed == NULL)
|| strcmp(aspath_print (asconfeddel), sp->shouldbe_delete_confed)
/* delete_confed_seq should be idempotent */
- || (aspath_key_make (asconfeddel)
+ || (aspath_key_make (asconfeddel)
!= aspath_key_make (aspath_delete_confed_seq (asconfeddel))))
{
failed++;
@@ -965,7 +965,7 @@ validate (struct aspath *as, const struct test_spec *sp)
fails++;
printf ("asstr: %s\n", aspath_print (asstr));
}
-
+
/* loop, private and first as checks */
if ((sp->does_loop && aspath_loop_check (as, sp->does_loop) == 0)
|| (sp->doesnt_loop && aspath_loop_check (as, sp->doesnt_loop) != 0)
@@ -983,13 +983,13 @@ validate (struct aspath *as, const struct test_spec *sp)
printf ("private check: %d %d\n", sp->private_as,
aspath_private_as_check (as));
}
- aspath_unintern (asinout);
- aspath_unintern (as4);
-
+ aspath_unintern (&asinout);
+ aspath_unintern (&as4);
+
aspath_free (asconfeddel);
aspath_free (asstr);
stream_reset (s);
-
+
return fails;
}
@@ -1004,9 +1004,9 @@ empty_get_test ()
printf ("%s\n", OK);
else
printf ("%s!\n", FAILED);
-
+
printf ("\n");
-
+
aspath_free (as);
}
@@ -1015,22 +1015,22 @@ static void
parse_test (struct test_segment *t)
{
struct aspath *asp;
-
+
printf ("%s: %s\n", t->name, t->desc);
asp = make_aspath (t->asdata, t->len, 0);
-
+
printf ("aspath: %s\nvalidating...:\n", aspath_print (asp));
if (!validate (asp, &t->sp))
printf (OK "\n");
else
printf (FAILED "\n");
-
+
printf ("\n");
-
+
if (asp)
- aspath_unintern (asp);
+ aspath_unintern (&asp);
}
/* prepend testing */
@@ -1038,27 +1038,27 @@ static void
prepend_test (struct tests *t)
{
struct aspath *asp1, *asp2, *ascratch;
-
+
printf ("prepend %s: %s\n", t->test1->name, t->test1->desc);
printf ("to %s: %s\n", t->test2->name, t->test2->desc);
-
+
asp1 = make_aspath (t->test1->asdata, t->test1->len, 0);
asp2 = make_aspath (t->test2->asdata, t->test2->len, 0);
-
+
ascratch = aspath_dup (asp2);
- aspath_unintern (asp2);
-
+ aspath_unintern (&asp2);
+
asp2 = aspath_prepend (asp1, ascratch);
-
+
printf ("aspath: %s\n", aspath_print (asp2));
-
+
if (!validate (asp2, &t->sp))
printf ("%s\n", OK);
else
printf ("%s!\n", FAILED);
-
+
printf ("\n");
- aspath_unintern (asp1);
+ aspath_unintern (&asp1);
aspath_free (asp2);
}
@@ -1067,27 +1067,27 @@ static void
empty_prepend_test (struct test_segment *t)
{
struct aspath *asp1, *asp2, *ascratch;
-
+
printf ("empty prepend %s: %s\n", t->name, t->desc);
-
+
asp1 = make_aspath (t->asdata, t->len, 0);
asp2 = aspath_empty ();
-
+
ascratch = aspath_dup (asp2);
- aspath_unintern (asp2);
-
+ aspath_unintern (&asp2);
+
asp2 = aspath_prepend (asp1, ascratch);
-
+
printf ("aspath: %s\n", aspath_print (asp2));
-
+
if (!validate (asp2, &t->sp))
printf (OK "\n");
else
printf (FAILED "!\n");
-
+
printf ("\n");
if (asp1)
- aspath_unintern (asp1);
+ aspath_unintern (&asp1);
aspath_free (asp2);
}
@@ -1096,23 +1096,23 @@ static void
as4_reconcile_test (struct tests *t)
{
struct aspath *asp1, *asp2, *ascratch;
-
+
printf ("reconciling %s:\n %s\n", t->test1->name, t->test1->desc);
printf ("with %s:\n %s\n", t->test2->name, t->test2->desc);
-
+
asp1 = make_aspath (t->test1->asdata, t->test1->len, 0);
asp2 = make_aspath (t->test2->asdata, t->test2->len, 0);
-
+
ascratch = aspath_reconcile_as4 (asp1, asp2);
-
+
if (!validate (ascratch, &t->sp))
printf (OK "\n");
else
printf (FAILED "!\n");
-
+
printf ("\n");
- aspath_unintern (asp1);
- aspath_unintern (asp2);
+ aspath_unintern (&asp1);
+ aspath_unintern (&asp2);
aspath_free (ascratch);
}
@@ -1122,23 +1122,23 @@ static void
aggregate_test (struct tests *t)
{
struct aspath *asp1, *asp2, *ascratch;
-
+
printf ("aggregate %s: %s\n", t->test1->name, t->test1->desc);
printf ("with %s: %s\n", t->test2->name, t->test2->desc);
-
+
asp1 = make_aspath (t->test1->asdata, t->test1->len, 0);
asp2 = make_aspath (t->test2->asdata, t->test2->len, 0);
-
+
ascratch = aspath_aggregate (asp1, asp2);
-
+
if (!validate (ascratch, &t->sp))
printf (OK "\n");
else
printf (FAILED "!\n");
-
+
printf ("\n");
- aspath_unintern (asp1);
- aspath_unintern (asp2);
+ aspath_unintern (&asp1);
+ aspath_unintern (&asp2);
aspath_free (ascratch);
/* aspath_unintern (ascratch);*/
}
@@ -1156,23 +1156,23 @@ cmp_test ()
struct test_segment *t1 = &test_segments[left_compare[i].test_index1];
struct test_segment *t2 = &test_segments[left_compare[i].test_index2];
struct aspath *asp1, *asp2;
-
+
printf ("left cmp %s: %s\n", t1->name, t1->desc);
printf ("and %s: %s\n", t2->name, t2->desc);
-
+
asp1 = make_aspath (t1->asdata, t1->len, 0);
asp2 = make_aspath (t2->asdata, t2->len, 0);
-
+
if (aspath_cmp_left (asp1, asp2) != left_compare[i].shouldbe_cmp
|| aspath_cmp_left (asp2, asp1) != left_compare[i].shouldbe_cmp
- || aspath_cmp_left_confed (asp1, asp2)
+ || aspath_cmp_left_confed (asp1, asp2)
!= left_compare[i].shouldbe_confed
- || aspath_cmp_left_confed (asp2, asp1)
+ || aspath_cmp_left_confed (asp2, asp1)
!= left_compare[i].shouldbe_confed)
{
failed++;
printf (FAILED "\n");
- printf ("result should be: cmp: %d, confed: %d\n",
+ printf ("result should be: cmp: %d, confed: %d\n",
left_compare[i].shouldbe_cmp,
left_compare[i].shouldbe_confed);
printf ("got: cmp %d, cmp_confed: %d\n",
@@ -1183,47 +1183,50 @@ cmp_test ()
}
else
printf (OK "\n");
-
+
printf ("\n");
- aspath_unintern (asp1);
- aspath_unintern (asp2);
+ aspath_unintern (&asp1);
+ aspath_unintern (&asp2);
}
}
static int
handle_attr_test (struct aspath_tests *t)
{
- struct bgp bgp = { 0 };
+ struct bgp bgp = { 0 };
struct peer peer = { 0 };
- struct attr attr = { 0 };
+ struct attr attr = { 0 };
int ret;
int initfail = failed;
struct aspath *asp;
size_t datalen;
-
+ char host[] = { "none" } ;
+
asp = make_aspath (t->segment->asdata, t->segment->len, 0);
-
+
peer.ibuf = stream_new (BGP_MAX_PACKET_SIZE);
peer.obuf = stream_fifo_new ();
peer.bgp = &bgp;
- peer.host = (char *)"none";
+ peer.host = host ;
+#if 0
peer.fd = -1;
+#endif
peer.cap = t->cap;
-
+
stream_write (peer.ibuf, t->attrheader, t->len);
datalen = aspath_put (peer.ibuf, asp, t->as4 == AS4_DATA);
-
+
ret = bgp_attr_parse (&peer, &attr, t->len + datalen, NULL, NULL);
-
+
if (ret != t->result)
{
printf ("bgp_attr_parse returned %d, expected %d\n", ret, t->result);
- printf ("datalen %d\n", datalen);
+ printf ("datalen %d\n", (int)datalen);
failed++;
}
if (ret != 0)
goto out;
-
+
if (attr.aspath == NULL)
{
printf ("aspath is NULL!\n");
@@ -1240,9 +1243,9 @@ handle_attr_test (struct aspath_tests *t)
out:
if (attr.aspath)
- aspath_unintern (attr.aspath);
+ aspath_unintern (&attr.aspath);
if (asp)
- aspath_unintern (asp);
+ aspath_unintern (&asp);
return failed - initfail;
}
@@ -1250,7 +1253,7 @@ static void
attr_test (struct aspath_tests *t)
{
printf ("%s\n", t->desc);
- printf ("%s\n\n", handle_attr_test (t) ? FAILED : OK);
+ printf ("%s\n\n", handle_attr_test (t) ? FAILED : OK);
}
int
@@ -1260,54 +1263,54 @@ main (void)
bgp_master_init ();
master = bm->master;
bgp_attr_init ();
-
+
while (test_segments[i].name)
{
printf ("test %u\n", i);
parse_test (&test_segments[i]);
empty_prepend_test (&test_segments[i++]);
}
-
+
i = 0;
while (prepend_tests[i].test1)
{
printf ("prepend test %u\n", i);
prepend_test (&prepend_tests[i++]);
}
-
+
i = 0;
while (aggregate_tests[i].test1)
{
printf ("aggregate test %u\n", i);
aggregate_test (&aggregate_tests[i++]);
}
-
+
i = 0;
-
+
while (reconcile_tests[i].test1)
{
printf ("reconcile test %u\n", i);
as4_reconcile_test (&reconcile_tests[i++]);
}
-
+
i = 0;
-
+
cmp_test();
-
+
i = 0;
-
+
empty_get_test();
-
+
i = 0;
-
+
while (aspath_tests[i].desc)
{
printf ("aspath_attr test %d\n", i);
attr_test (&aspath_tests[i++]);
}
-
+
printf ("failures: %d\n", failed);
printf ("aspath count: %ld\n", aspath_count());
-
+
return (failed + aspath_count());
}
diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c
index 0dbf4fb9..cae9a12d 100644
--- a/tests/bgp_capability_test.c
+++ b/tests/bgp_capability_test.c
@@ -1,4 +1,5 @@
#include <zebra.h>
+#include "miyagi.h"
#include "vty.h"
#include "stream.h"
@@ -36,7 +37,7 @@ static struct test_segment {
#define SHOULD_ERR -1
int parses; /* whether it should parse or not */
int peek_for; /* what peek_for_as4_capability should say */
-
+
/* AFI/SAFI validation */
int validate_afi;
afi_t afi;
@@ -44,10 +45,10 @@ static struct test_segment {
#define VALID_AFI 1
#define INVALID_AFI 0
int afi_valid;
-} test_segments [] =
+} test_segments [] =
{
/* 0 */
- { "caphdr",
+ { "caphdr",
"capability header, and no more",
{ CAPABILITY_CODE_REFRESH, 0x0 },
2, SHOULD_PARSE,
@@ -114,7 +115,7 @@ static struct test_segment mp_segments[] =
{ CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x80 },
6, SHOULD_PARSE, 0,
1, AFI_IP, BGP_SAFI_VPNV4, VALID_AFI,
- },
+ },
/* 9 */
{ "MP7",
"MP IP4/VPNv6",
@@ -150,9 +151,9 @@ static struct test_segment misc_segments[] =
/* 13 */
{ "ORF",
"ORF, simple, single entry, single tuple",
- { /* hdr */ CAPABILITY_CODE_ORF, 0x7,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x1,
+ { /* hdr */ CAPABILITY_CODE_ORF, 0x7,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x1,
/* tuples */ 0x40, 0x3
},
9, SHOULD_PARSE,
@@ -161,18 +162,18 @@ static struct test_segment misc_segments[] =
{ "ORF-many",
"ORF, multi entry/tuple",
{ /* hdr */ CAPABILITY_CODE_ORF, 0x21,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, ORF_MODE_BOTH,
0x80, ORF_MODE_RECEIVE,
0x80, ORF_MODE_SEND,
- /* mpc */ 0x0, 0x2, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x2, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, ORF_MODE_BOTH,
0x80, ORF_MODE_RECEIVE,
0x80, ORF_MODE_SEND,
/* mpc */ 0x0, 0x2, 0x0, 0x2,
- /* num */ 0x3,
+ /* num */ 0x3,
/* tuples */ 0x40, ORF_MODE_RECEIVE,
0x80, ORF_MODE_SEND,
0x80, ORF_MODE_BOTH,
@@ -183,18 +184,18 @@ static struct test_segment misc_segments[] =
{ "ORFlo",
"ORF, multi entry/tuple, hdr length too short",
{ /* hdr */ CAPABILITY_CODE_ORF, 0x15,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
/* mpc */ 0x0, 0x2, 0x0, 0x2,
- /* num */ 0x3,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
@@ -205,18 +206,18 @@ static struct test_segment misc_segments[] =
{ "ORFlu",
"ORF, multi entry/tuple, length too long",
{ /* hdr */ 0x3, 0x22,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
- /* mpc */ 0x0, 0x2, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x2, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
/* mpc */ 0x0, 0x2, 0x0, 0x2,
- /* num */ 0x3,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
@@ -227,18 +228,18 @@ static struct test_segment misc_segments[] =
{ "ORFnu",
"ORF, multi entry/tuple, entry number too long",
{ /* hdr */ 0x3, 0x21,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
- /* mpc */ 0x0, 0x2, 0x0, 0x1,
- /* num */ 0x4,
+ /* mpc */ 0x0, 0x2, 0x0, 0x1,
+ /* num */ 0x4,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
/* mpc */ 0x0, 0x2, 0x0, 0x2,
- /* num */ 0x3,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
@@ -249,13 +250,13 @@ static struct test_segment misc_segments[] =
{ "ORFno",
"ORF, multi entry/tuple, entry number too short",
{ /* hdr */ 0x3, 0x21,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
- /* mpc */ 0x0, 0x2, 0x0, 0x1,
- /* num */ 0x1,
+ /* mpc */ 0x0, 0x2, 0x0, 0x1,
+ /* num */ 0x1,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
@@ -271,18 +272,18 @@ static struct test_segment misc_segments[] =
{ "ORFpad",
"ORF, multi entry/tuple, padded to align",
{ /* hdr */ 0x3, 0x22,
- /* mpc */ 0x0, 0x1, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
- /* mpc */ 0x0, 0x2, 0x0, 0x1,
- /* num */ 0x3,
+ /* mpc */ 0x0, 0x2, 0x0, 0x1,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
/* mpc */ 0x0, 0x2, 0x0, 0x2,
- /* num */ 0x3,
+ /* num */ 0x3,
/* tuples */ 0x40, 0x3,
0x80, 0x1,
0x80, 0x2,
@@ -401,7 +402,7 @@ static struct test_segment misc_segments[] =
};
/* DYNAMIC message */
-struct test_segment dynamic_cap_msgs[] =
+struct test_segment dynamic_cap_msgs[] =
{
{ "DynCap",
"Dynamic Capability Message, IP/Multicast",
@@ -439,7 +440,7 @@ struct test_segment opt_params[] =
{ 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
0x02, 0x02, 0x80, 0x00, /* RR (old) */
- 0x02, 0x02, 0x02, 0x00, /* RR */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
},
24, SHOULD_PARSE,
},
@@ -449,7 +450,7 @@ struct test_segment opt_params[] =
0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
0x80, 0x00, /* RR (old) */
- 0x02, 0x00, /* RR */
+ 0x02, 0x00, /* RR */
},
18, SHOULD_PARSE,
},
@@ -469,7 +470,7 @@ struct test_segment opt_params[] =
0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
0x80, 0x00, /* RR (old) */
- 0x02, 0x00, /* RR */
+ 0x02, 0x00, /* RR */
0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */
},
24, SHOULD_PARSE, 196614,
@@ -503,6 +504,8 @@ struct test_segment opt_params[] =
{ NULL, NULL, {0}, 0, 0}
};
+extern int bgp_capability_receive(struct peer*, bgp_size_t) ;
+
/* basic parsing test */
static void
parse_test (struct peer *peer, struct test_segment *t, int type)
@@ -513,11 +516,11 @@ parse_test (struct peer *peer, struct test_segment *t, int type)
int oldfailed = failed;
int len = t->len;
#define RANDOM_FUZZ 35
-
+
stream_reset (peer->ibuf);
stream_put (peer->ibuf, NULL, RANDOM_FUZZ);
stream_set_getp (peer->ibuf, RANDOM_FUZZ);
-
+
switch (type)
{
case CAPABILITY:
@@ -532,7 +535,7 @@ parse_test (struct peer *peer, struct test_segment *t, int type)
break;
}
stream_write (peer->ibuf, t->data, t->len);
-
+
printf ("%s: %s\n", t->name, t->desc);
switch (type)
@@ -546,7 +549,7 @@ parse_test (struct peer *peer, struct test_segment *t, int type)
printf ("peek_for_as4: as4 is %u\n", as4);
/* and it should leave getp as it found it */
assert (stream_get_getp (peer->ibuf) == RANDOM_FUZZ);
-
+
ret = bgp_open_option_parse (peer, len, &capability);
break;
case DYNCAP:
@@ -556,49 +559,49 @@ parse_test (struct peer *peer, struct test_segment *t, int type)
printf ("unknown type %u\n", type);
exit(1);
}
-
+
if (!ret && t->validate_afi)
{
safi_t safi = t->safi;
-
+
if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid)
failed++;
-
+
printf ("MP: %u/%u (%u): recv %u, nego %u\n",
t->afi, t->safi, safi,
peer->afc_recv[t->afi][safi],
peer->afc_nego[t->afi][safi]);
-
+
if (t->afi_valid == VALID_AFI)
{
-
+
if (!peer->afc_recv[t->afi][safi])
failed++;
if (!peer->afc_nego[t->afi][safi])
failed++;
}
}
-
- if (as4 != t->peek_for)
+
+ if (as4 != (uint32_t)t->peek_for)
{
printf ("as4 %u != %u\n", as4, t->peek_for);
failed++;
}
-
+
printf ("parsed?: %s\n", ret ? "no" : "yes");
-
+
if (ret != t->parses)
failed++;
-
+
if (tty)
- printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
+ printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
: VT100_GREEN "OK" VT100_RESET);
else
printf ("%s", (failed > oldfailed) ? "failed!" : "OK" );
-
+
if (failed)
printf (" (%u)", failed);
-
+
printf ("\n\n");
}
@@ -610,7 +613,7 @@ main (void)
{
struct peer *peer;
int i, j;
-
+
conf_bgp_debug_fsm = -1UL;
conf_bgp_debug_events = -1UL;
conf_bgp_debug_packet = -1UL;
@@ -621,26 +624,26 @@ main (void)
term_bgp_debug_packet = -1UL;
term_bgp_debug_normal = -1UL;
term_bgp_debug_as4 = -1UL;
-
+
master = thread_master_create ();
bgp_master_init ();
-
- if (fileno (stdout) >= 0)
+
+ if (fileno (stdout) >= 0)
tty = isatty (fileno (stdout));
-
+
if (bgp_get (&bgp, &asn, NULL))
return -1;
-
+
peer = peer_create_accept (bgp);
- peer->host = "foo";
-
+ peer->host = miyagi("foo");
+
for (i = AFI_IP; i < AFI_MAX; i++)
for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
{
peer->afc[i][j] = 1;
peer->afc_adv[i][j] = 1;
}
-
+
i = 0;
while (mp_segments[i].name)
parse_test (peer, &mp_segments[i++], CAPABILITY);
@@ -649,9 +652,9 @@ main (void)
* one of the afc_nego's
*/
i = 0;
- while (test_segments[i].name)
+ while (test_segments[i].name)
parse_test (peer, &test_segments[i++], CAPABILITY);
-
+
i = 0;
while (misc_segments[i].name)
parse_test (peer, &misc_segments[i++], CAPABILITY);
@@ -661,12 +664,12 @@ main (void)
parse_test (peer, &opt_params[i++], OPT_PARAM);
SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV);
- peer->status = Established;
-
+ peer->state = bgp_peer_pEstablished;
+
i = 0;
while (dynamic_cap_msgs[i].name)
parse_test (peer, &dynamic_cap_msgs[i++], DYNCAP);
-
+
printf ("failures: %d\n", failed);
return failed;
}
diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c
index dde0df2f..6a445acf 100644
--- a/tests/bgp_mp_attr_test.c
+++ b/tests/bgp_mp_attr_test.c
@@ -1,4 +1,5 @@
#include <zebra.h>
+#include "miyagi.h"
#include "vty.h"
#include "stream.h"
@@ -36,17 +37,17 @@ static struct test_segment {
#define SHOULD_PARSE 0
#define SHOULD_ERR -1
int parses; /* whether it should parse or not */
-
+
/* AFI/SAFI validation */
afi_t afi;
safi_t safi;
#define VALID_AFI 1
#define INVALID_AFI 0
int afi_valid;
-} mp_reach_segments [] =
+} mp_reach_segments [] =
{
{ "IPv6",
- "IPV6 MP Reach, global nexthop, 1 NLRI",
+ "IPV6 MP Reach, global nexthop, 1 NLRI",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 16,
@@ -57,12 +58,12 @@ static struct test_segment {
/* SNPA (defunct, MBZ) */ 0x0,
/* NLRI tuples */ 32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
},
- (4 + 16 + 1 + 5),
+ (4 + 16 + 1 + 5),
SHOULD_PARSE,
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-2",
- "IPV6 MP Reach, global nexthop, 2 NLRIs",
+ "IPV6 MP Reach, global nexthop, 2 NLRIs",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 16,
@@ -71,18 +72,18 @@ static struct test_segment {
0x3, 0x4, 0x5, 0x6,
0xa1, 0xa2, 0xa3, 0xa4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
0x0, 0x2, 0x0, 0x3,
},
- (4 + 16 + 1 + 5 + 9),
+ (4 + 16 + 1 + 5 + 9),
SHOULD_PARSE,
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-default",
- "IPV6 MP Reach, global nexthop, 2 NLRIs + default",
+ "IPV6 MP Reach, global nexthop, 2 NLRIs + default",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 16,
@@ -91,7 +92,7 @@ static struct test_segment {
0x3, 0x4, 0x5, 0x6,
0xa1, 0xa2, 0xa3, 0xa4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -103,7 +104,7 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-lnh",
- "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default",
+ "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 32,
@@ -116,7 +117,7 @@ static struct test_segment {
0x2, 0x10, 0x2, 0xff,
0x1, 0x2, 0x3, 0x4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -128,7 +129,7 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-nhlen",
- "IPV6 MP Reach, inappropriate nexthop length",
+ "IPV6 MP Reach, inappropriate nexthop length",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 4,
@@ -141,7 +142,7 @@ static struct test_segment {
0x2, 0x10, 0x2, 0xff,
0x1, 0x2, 0x3, 0x4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -153,7 +154,7 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-nhlen2",
- "IPV6 MP Reach, invalid nexthop length",
+ "IPV6 MP Reach, invalid nexthop length",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 5,
@@ -166,7 +167,7 @@ static struct test_segment {
0x2, 0x10, 0x2, 0xff,
0x1, 0x2, 0x3, 0x4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -178,7 +179,7 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-nhlen3",
- "IPV6 MP Reach, nexthop length overflow",
+ "IPV6 MP Reach, nexthop length overflow",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 32,
@@ -192,7 +193,7 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-nhlen4",
- "IPV6 MP Reach, nexthop length short",
+ "IPV6 MP Reach, nexthop length short",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 16,
@@ -205,7 +206,7 @@ static struct test_segment {
0x2, 0x10, 0x2, 0xff,
0x1, 0x2, 0x3, 0x4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -217,7 +218,7 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-nlri",
- "IPV6 MP Reach, NLRI bitlen overflow",
+ "IPV6 MP Reach, NLRI bitlen overflow",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* nexthop bytes */ 32,
@@ -230,7 +231,7 @@ static struct test_segment {
0x2, 0x10, 0x2, 0xff,
0x1, 0x2, 0x3, 0x4,
/* SNPA (defunct, MBZ) */ 0x0,
- /* NLRI tuples */ 120,
+ /* NLRI tuples */ 120,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -242,11 +243,11 @@ static struct test_segment {
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4",
- "IPv4 MP Reach, 2 NLRIs + default",
+ "IPv4 MP Reach, 2 NLRIs + default",
{
/* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
/* nexthop bytes */ 4,
- /* Nexthop */ 192, 168, 0, 1,
+ /* Nexthop */ 192, 168, 0, 1,
/* SNPA (defunct, MBZ) */ 0x0,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
17, 10, 2, 3, /* 10.2.3/17 */
@@ -257,11 +258,11 @@ static struct test_segment {
AFI_IP, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4-nhlen",
- "IPv4 MP Reach, nexthop lenth overflow",
+ "IPv4 MP Reach, nexthop lenth overflow",
{
/* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
/* nexthop bytes */ 32,
- /* Nexthop */ 192, 168, 0, 1,
+ /* Nexthop */ 192, 168, 0, 1,
/* SNPA (defunct, MBZ) */ 0x0,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
17, 10, 2, 3, /* 10.2.3/17 */
@@ -272,14 +273,14 @@ static struct test_segment {
AFI_IP, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4-nlrilen",
- "IPv4 MP Reach, nlri lenth overflow",
+ "IPv4 MP Reach, nlri lenth overflow",
{
/* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
/* nexthop bytes */ 4,
- /* Nexthop */ 192, 168, 0, 1,
+ /* Nexthop */ 192, 168, 0, 1,
/* SNPA (defunct, MBZ) */ 0x0,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
- 30, 10,
+ 30, 10,
0, /* 0/0 */
},
(4 + 4 + 1 + 3 + 2 + 1),
@@ -287,13 +288,13 @@ static struct test_segment {
AFI_IP, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4-vpnv4",
- "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs",
{
/* AFI / SAFI */ 0x0, AFI_IP, BGP_SAFI_VPNV4,
/* nexthop bytes */ 12,
/* RD */ 0, 0, 1, 2,
0, 0xff, 3, 4,
- /* Nexthop */ 192, 168, 0, 1,
+ /* Nexthop */ 192, 168, 0, 1,
/* SNPA (defunct, MBZ) */ 0x0,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
17, 10, 2, 3, /* 10.2.3/17 */
@@ -305,17 +306,17 @@ static struct test_segment {
},
/* From bug #385 */
{ "IPv6-bug",
- "IPv6, global nexthop, 1 default NLRI",
+ "IPv6, global nexthop, 1 default NLRI",
{
/* AFI / SAFI */ 0x0, 0x2, 0x1,
/* nexthop bytes */ 0x20,
- /* Nexthop (global) */ 0x20, 0x01, 0x04, 0x70,
+ /* Nexthop (global) */ 0x20, 0x01, 0x04, 0x70,
0x00, 0x01, 0x00, 0x06,
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01,
- /* Nexthop (local) */ 0xfe, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ /* Nexthop (local) */ 0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x02, 0x0c, 0xdb, 0xff,
+ 0x02, 0x0c, 0xdb, 0xff,
0xfe, 0xfe, 0xeb, 0x00,
/* SNPA (defunct, MBZ) */ 0,
/* NLRI tuples */ /* Should have 0 here for ::/0, but dont */
@@ -324,7 +325,7 @@ static struct test_segment {
SHOULD_ERR,
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
-
+
{ NULL, NULL, {0}, 0, 0}
};
@@ -332,34 +333,34 @@ static struct test_segment {
static struct test_segment mp_unreach_segments [] =
{
{ "IPv6-unreach",
- "IPV6 MP Unreach, 1 NLRI",
+ "IPV6 MP Unreach, 1 NLRI",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
/* NLRI tuples */ 32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
},
- (3 + 5),
+ (3 + 5),
SHOULD_PARSE,
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-unreach2",
- "IPV6 MP Unreach, 2 NLRIs",
+ "IPV6 MP Unreach, 2 NLRIs",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
0x0, 0x2, 0x0, 0x3,
},
- (3 + 5 + 9),
+ (3 + 5 + 9),
SHOULD_PARSE,
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-unreach-default",
- "IPV6 MP Unreach, 2 NLRIs + default",
+ "IPV6 MP Unreach, 2 NLRIs + default",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
- /* NLRI tuples */ 32,
+ /* NLRI tuples */ 32,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -371,10 +372,10 @@ static struct test_segment mp_unreach_segments [] =
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv6-unreach-nlri",
- "IPV6 MP Unreach, NLRI bitlen overflow",
+ "IPV6 MP Unreach, NLRI bitlen overflow",
{
/* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
- /* NLRI tuples */ 120,
+ /* NLRI tuples */ 120,
0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
64,
0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
@@ -386,7 +387,7 @@ static struct test_segment mp_unreach_segments [] =
AFI_IP6, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4-unreach",
- "IPv4 MP Unreach, 2 NLRIs + default",
+ "IPv4 MP Unreach, 2 NLRIs + default",
{
/* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
@@ -398,11 +399,11 @@ static struct test_segment mp_unreach_segments [] =
AFI_IP, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4-unreach-nlrilen",
- "IPv4 MP Unreach, nlri length overflow",
+ "IPv4 MP Unreach, nlri length overflow",
{
/* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
- 30, 10,
+ 30, 10,
0, /* 0/0 */
},
(3 + 3 + 2 + 1),
@@ -410,13 +411,13 @@ static struct test_segment mp_unreach_segments [] =
AFI_IP, SAFI_UNICAST, VALID_AFI,
},
{ "IPv4-unreach-vpnv4",
- "IPv4/VPNv4 MP Unreach, RD, 3 NLRIs",
+ "IPv4/VPNv4 MP Unreach, RD, 3 NLRIs",
{
/* AFI / SAFI */ 0x0, AFI_IP, BGP_SAFI_VPNV4,
/* nexthop bytes */ 12,
/* RD */ 0, 0, 1, 2,
0, 0xff, 3, 4,
- /* Nexthop */ 192, 168, 0, 1,
+ /* Nexthop */ 192, 168, 0, 1,
/* SNPA (defunct, MBZ) */ 0x0,
/* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
17, 10, 2, 3, /* 10.2.3/17 */
@@ -439,13 +440,13 @@ parse_test (struct peer *peer, struct test_segment *t, int type)
struct attr attr;
struct bgp_nlri nlri;
#define RANDOM_FUZZ 35
-
+
stream_reset (peer->ibuf);
stream_put (peer->ibuf, NULL, RANDOM_FUZZ);
stream_set_getp (peer->ibuf, RANDOM_FUZZ);
-
+
stream_write (peer->ibuf, t->data, t->len);
-
+
printf ("%s: %s\n", t->name, t->desc);
if (type == BGP_ATTR_MP_REACH_NLRI)
@@ -456,30 +457,30 @@ parse_test (struct peer *peer, struct test_segment *t, int type)
if (!ret)
{
safi_t safi = t->safi;
-
+
if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid)
failed++;
-
+
printf ("MP: %u/%u (%u): recv %u, nego %u\n",
t->afi, t->safi, safi,
peer->afc_recv[t->afi][safi],
peer->afc_nego[t->afi][safi]);
}
-
+
printf ("parsed?: %s\n", ret ? "no" : "yes");
-
+
if (ret != t->parses)
failed++;
-
+
if (tty)
- printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
+ printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
: VT100_GREEN "OK" VT100_RESET);
else
printf ("%s", (failed > oldfailed) ? "failed!" : "OK" );
-
+
if (failed)
printf (" (%u)", failed);
-
+
printf ("\n\n");
}
@@ -491,7 +492,7 @@ main (void)
{
struct peer *peer;
int i, j;
-
+
conf_bgp_debug_fsm = -1UL;
conf_bgp_debug_events = -1UL;
conf_bgp_debug_packet = -1UL;
@@ -502,26 +503,26 @@ main (void)
term_bgp_debug_packet = -1UL;
term_bgp_debug_normal = -1UL;
term_bgp_debug_as4 = -1UL;
-
+
master = thread_master_create ();
bgp_master_init ();
-
- if (fileno (stdout) >= 0)
+
+ if (fileno (stdout) >= 0)
tty = isatty (fileno (stdout));
-
+
if (bgp_get (&bgp, &asn, NULL))
return -1;
-
+
peer = peer_create_accept (bgp);
- peer->host = "foo";
-
+ peer->host = miyagi("foo") ;
+
for (i = AFI_IP; i < AFI_MAX; i++)
for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
{
peer->afc[i][j] = 1;
peer->afc_adv[i][j] = 1;
}
-
+
i = 0;
while (mp_reach_segments[i].name)
parse_test (peer, &mp_reach_segments[i++], BGP_ATTR_MP_REACH_NLRI);
diff --git a/tests/ecommunity_test.c b/tests/ecommunity_test.c
index 418f659f..3a67228d 100644
--- a/tests/ecommunity_test.c
+++ b/tests/ecommunity_test.c
@@ -1,4 +1,5 @@
#include <zebra.h>
+#include "miyagi.h"
#include "vty.h"
#include "stream.h"
@@ -15,7 +16,7 @@ struct thread_master *master = NULL;
static int failed = 0;
/* specification for a test - what the results should be */
-struct test_spec
+struct test_spec
{
const char *shouldbe; /* the string the path should parse to */
};
@@ -28,7 +29,7 @@ static struct test_segment {
const u_char data[1024];
int len;
struct test_spec sp;
-} test_segments [] =
+} test_segments [] =
{
{ /* 0 */
"ipaddr",
@@ -73,7 +74,7 @@ validate (struct ecommunity *ecom, const struct test_spec *sp)
int fails = 0;
struct ecommunity *etmp;
char *str1, *str2;
-
+
printf ("got:\n %s\n", ecommunity_str (ecom));
str1 = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
etmp = ecommunity_str2com (str1, 0, 1);
@@ -81,7 +82,7 @@ validate (struct ecommunity *ecom, const struct test_spec *sp)
str2 = ecommunity_ecom2str (etmp, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
else
str2 = NULL;
-
+
if (strcmp (sp->shouldbe, str1))
{
failed++;
@@ -94,13 +95,13 @@ validate (struct ecommunity *ecom, const struct test_spec *sp)
fails++;
printf ("dogfood: in %s\n"
" in->out %s\n",
- str1,
+ str1,
(etmp && str2) ? str2 : "NULL");
}
- ecommunity_free (etmp);
+ ecommunity_free (&etmp);
XFREE (MTYPE_ECOMMUNITY_STR, str1);
XFREE (MTYPE_ECOMMUNITY_STR, str2);
-
+
return fails;
}
@@ -109,10 +110,10 @@ static void
parse_test (struct test_segment *t)
{
struct ecommunity *ecom;
-
+
printf ("%s: %s\n", t->name, t->desc);
- ecom = ecommunity_parse (t->data, t->len);
+ ecom = ecommunity_parse (miyagi(t->data), t->len);
printf ("ecom: %s\nvalidating...:\n", ecommunity_str (ecom));
@@ -120,12 +121,12 @@ parse_test (struct test_segment *t)
printf ("OK\n");
else
printf ("failed\n");
-
+
printf ("\n");
- ecommunity_unintern (ecom);
+ ecommunity_unintern (&ecom);
}
-
+
int
main (void)
{
@@ -133,7 +134,7 @@ main (void)
ecommunity_init();
while (test_segments[i].name)
parse_test (&test_segments[i++]);
-
+
printf ("failures: %d\n", failed);
//printf ("aspath count: %ld\n", aspath_count());
return failed;
diff --git a/tests/heavy-thread.c b/tests/heavy-thread.c
index cd3a3b9d..f7a99cc5 100644
--- a/tests/heavy-thread.c
+++ b/tests/heavy-thread.c
@@ -59,17 +59,17 @@ slow_func (struct vty *vty, const char *str, const int i)
{
double x = 1;
int j;
-
+
for (j = 0; j < 300; j++)
x += sin(x)*j;
-
+
if ((i % ITERS_LATER) == 0)
printf ("%s: %d, temporary error, save this somehow and do it later..\n",
__func__, i);
-
+
if ((i % ITERS_ERR) == 0)
printf ("%s: hard error\n", __func__);
-
+
if ((i % ITERS_PRINT) == 0)
printf ("%s did %d, x = %g\n", str, i, x);
}
@@ -78,8 +78,8 @@ static int
clear_something (struct thread *thread)
{
struct work_state *ws = THREAD_ARG(thread);
-
- /* this could be like iterating through 150k of route_table
+
+ /* this could be like iterating through 150k of route_table
* or worse, iterating through a list of peers, to bgp_stop them with
* each having 150k route tables to process...
*/
@@ -93,7 +93,7 @@ clear_something (struct thread *thread)
return 0;
}
}
-
+
/* All done! */
XFREE (MTYPE_TMP, ws->str);
XFREE (MTYPE_TMP, ws);
@@ -114,22 +114,22 @@ DEFUN (clear_foo,
vty_out (vty, "%% string argument required%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
str = argv_concat (argv, argc, 0);
-
+
if ((ws = XMALLOC(MTYPE_TMP, sizeof(*ws))) == NULL)
{
zlog_err ("%s: unable to allocate work_state", __func__);
return CMD_WARNING;
}
-
+
if (!(ws->str = XSTRDUP (MTYPE_TMP, str)))
{
zlog_err ("%s: unable to xstrdup", __func__);
XFREE (MTYPE_TMP, ws);
return CMD_WARNING;
}
-
+
ws->vty = vty;
ws->i = ITERS_FIRST;
@@ -138,8 +138,10 @@ DEFUN (clear_foo,
return CMD_SUCCESS;
}
+extern void test_init(void) ;
+
void
-test_init()
+test_init(void)
{
install_element (VIEW_NODE, &clear_foo_cmd);
}
diff --git a/tests/heavy-wq.c b/tests/heavy-wq.c
index a2c609d4..bf3ab85a 100644
--- a/tests/heavy-wq.c
+++ b/tests/heavy-wq.c
@@ -66,7 +66,7 @@ heavy_wq_add (struct vty *vty, const char *str, int i)
zlog_err ("%s: unable to allocate hn", __func__);
return;
}
-
+
hn->i = i;
if (!(hn->str = XSTRDUP (MTYPE_PREFIX_LIST_STR, str)))
{
@@ -74,47 +74,47 @@ heavy_wq_add (struct vty *vty, const char *str, int i)
XFREE (MTYPE_PREFIX_LIST, hn);
return;
}
-
+
work_queue_add (heavy_wq, hn);
-
+
return;
}
static void
-slow_func_err (struct work_queue *wq, struct work_queue_item *item)
+slow_func_err (struct work_queue *wq, work_queue_item item)
{
printf ("%s: running error function\n", __func__);
}
static void
-slow_func_del (struct work_queue *wq, void *data)
+slow_func_del (struct work_queue *wq, work_queue_item item)
{
- struct heavy_wq_node *hn = data;
+ struct heavy_wq_node *hn = item->args.data;
assert (hn && hn->str);
printf ("%s: %s\n", __func__, hn->str);
XFREE (MTYPE_PREFIX_LIST_STR, hn->str);
- hn->str = NULL;
+ hn->str = NULL;
XFREE(MTYPE_PREFIX_LIST, hn);
}
static wq_item_status
-slow_func (struct work_queue *wq, void *data)
+slow_func (struct work_queue *wq, work_queue_item item)
{
- struct heavy_wq_node *hn = data;
+ struct heavy_wq_node *hn = item->args.data;
double x = 1;
int j;
-
+
assert (hn && hn->str);
-
+
for (j = 0; j < 300; j++)
x += sin(x)*j;
-
+
if ((hn->i % ITERS_LATER) == 0)
return WQ_RETRY_LATER;
-
+
if ((hn->i % ITERS_ERR) == 0)
return WQ_RETRY_NOW;
-
+
if ((hn->i % ITERS_PRINT) == 0)
printf ("%s did %d, x = %g\n", hn->str, hn->i, x);
@@ -125,8 +125,8 @@ static void
clear_something (struct vty *vty, const char *str)
{
int i;
-
- /* this could be like iterating through 150k of route_table
+
+ /* this could be like iterating through 150k of route_table
* or worse, iterating through a list of peers, to bgp_stop them with
* each having 150k route tables to process...
*/
@@ -146,9 +146,9 @@ DEFUN (clear_foo,
vty_out (vty, "%% string argument required%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
str = argv_concat (argv, argc, 0);
-
+
clear_something (vty, str);
XFREE (MTYPE_TMP, str);
return CMD_SUCCESS;
@@ -162,18 +162,20 @@ heavy_wq_init ()
zlog_err ("%s: could not get new work queue!", __func__);
return -1;
}
-
- heavy_wq->spec.workfunc = &slow_func;
- heavy_wq->spec.errorfunc = &slow_func_err;
+
+ heavy_wq->spec.workfunc = &slow_func;
+ heavy_wq->spec.errorfunc = &slow_func_err;
heavy_wq->spec.del_item_data = &slow_func_del;
- heavy_wq->spec.max_retries = 3;
- heavy_wq->spec.hold = 1000;
-
+ heavy_wq->spec.max_retries = 3;
+ heavy_wq->spec.hold = 1000;
+
return 0;
}
-void
-test_init()
+extern void test_init(void) ;
+
+extern void
+test_init(void)
{
install_element (VIEW_NODE, &clear_foo_cmd);
heavy_wq_init();
diff --git a/tests/heavy.c b/tests/heavy.c
index 577a4816..901fb10f 100644
--- a/tests/heavy.c
+++ b/tests/heavy.c
@@ -50,27 +50,27 @@ slow_func (struct vty *vty, const char *str, const int i)
{
double x = 1;
int j;
-
+
for (j = 0; j < 300; j++)
x += sin(x)*j;
-
+
if ((i % ITERS_LATER) == 0)
- printf ("%s: %d, temporary error, save this somehow and do it later..\n",
+ printf ("%s: %d, temporary error, save this somehow and do it later..\n",
__func__, i);
-
+
if ((i % ITERS_ERR) == 0)
printf ("%s: hard error\n", __func__);
-
+
if ((i % ITERS_PRINT) == 0)
- printf ("%s did %d, x = %g%s", str, i, x, VTY_NEWLINE);
+ printf ("%s did %d, x = %g%s", str, i, x, VTY_NEWLINE);
}
static void
clear_something (struct vty *vty, const char *str)
{
int i;
-
- /* this could be like iterating through 150k of route_table
+
+ /* this could be like iterating through 150k of route_table
* or worse, iterating through a list of peers, to bgp_stop them with
* each having 150k route tables to process...
*/
@@ -90,9 +90,9 @@ DEFUN (clear_foo,
vty_out (vty, "%% string argument required%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
str = argv_concat (argv, argc, 0);
-
+
clear_something (vty, str);
XFREE (MTYPE_TMP, str);
return CMD_SUCCESS;
@@ -104,8 +104,10 @@ slow_vty_init()
install_element (VIEW_NODE, &clear_foo_cmd);
}
-void
-test_init()
+extern void test_init(void) ;
+
+extern void
+test_init(void)
{
slow_vty_init();
}
diff --git a/tests/main.c b/tests/main.c
index e0fbb4d5..f735e1e1 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -32,7 +32,7 @@ extern void test_init();
struct thread_master *master;
-struct option longopts[] =
+struct option longopts[] =
{
{ "daemon", no_argument, NULL, 'd'},
{ "config_file", required_argument, NULL, 'f'},
@@ -51,12 +51,14 @@ DEFUN (daemon_exit,
exit(0);
}
+extern int test_timer (struct thread *thread) ;
+
static int timer_count;
-int
+extern int
test_timer (struct thread *thread)
{
int *count = THREAD_ARG(thread);
-
+
printf ("run %d of timer\n", (*count)++);
thread_add_timer (master, test_timer, count, 5);
return 0;
@@ -81,7 +83,7 @@ usage (char *progname, int status)
if (status != 0)
fprintf (stderr, "Try `%s --help' for more information.\n", progname);
else
- {
+ {
printf ("Usage : %s [OPTION...]\n\
Daemon which does 'slow' things.\n\n\
-d, --daemon Runs in daemon mode\n\
@@ -95,8 +97,8 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
}
exit (status);
}
-
-
+
+
/* main routine. */
int
main (int argc, char **argv)
@@ -108,7 +110,7 @@ main (int argc, char **argv)
char *progname;
struct thread thread;
char *config_file = NULL;
-
+
/* Set umask before anything for security */
umask (0027);
@@ -118,16 +120,16 @@ main (int argc, char **argv)
/* master init. */
master = thread_master_create ();
- while (1)
+ while (1)
{
int opt;
opt = getopt_long (argc, argv, "dhf:A:P:v", longopts, 0);
-
+
if (opt == EOF)
break;
- switch (opt)
+ switch (opt)
{
case 0:
break;
@@ -146,7 +148,7 @@ main (int argc, char **argv)
{
vty_port = 0;
break;
- }
+ }
vty_port = atoi (optarg);
vty_port = (vty_port ? vty_port : 4000);
break;
@@ -182,16 +184,16 @@ main (int argc, char **argv)
/* Create VTY socket */
vty_serv_sock (vty_addr, vty_port, "/tmp/.heavy.sock");
-
+
/* Configuration file read*/
if (!config_file)
usage (progname, 1);
vty_read_config (config_file, NULL);
-
+
test_timer_init();
-
- test_init();
-
+
+ test_init();
+
/* Fetch next active thread. */
while (thread_fetch (master, &thread))
thread_call (&thread);
diff --git a/tests/test-checksum.c b/tests/test-checksum.c
index bd156baa..076dd4ad 100644
--- a/tests/test-checksum.c
+++ b/tests/test-checksum.c
@@ -13,7 +13,7 @@ struct acc_vals {
struct csum_vals {
struct acc_vals a;
- int x;
+ int x;
int y;
};
@@ -24,25 +24,27 @@ typedef uint16_t testoff_t;
/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX 4102
-
+
/* Accumulator phase of checksum */
-static
-struct acc_vals
+static struct acc_vals
+accumulate (u_char *buffer, testsz_t len, testoff_t off)
+ __attribute__((unused)) ;
+static struct acc_vals
accumulate (u_char *buffer, testsz_t len, testoff_t off)
{
u_int8_t *p;
u_int16_t *csum;
int i, init_len, partial_len;
struct acc_vals ret;
-
+
csum = (u_int16_t *) (buffer + off);
*(csum) = 0;
-
+
p = buffer;
ret.c0 = 0;
ret.c1 = 0;
init_len = len;
-
+
while (len != 0)
{
partial_len = MIN(len, MODX);
@@ -62,9 +64,9 @@ accumulate (u_char *buffer, testsz_t len, testoff_t off)
}
/* The final reduction phase.
- * This one should be the original ospfd version
+ * This one should be the original ospfd version
*/
-static u_int16_t
+static u_int16_t
reduce_ospfd (struct csum_vals *vals, testsz_t len, testoff_t off)
{
#define x vals->x
@@ -73,7 +75,7 @@ reduce_ospfd (struct csum_vals *vals, testsz_t len, testoff_t off)
#define c1 vals->a.c1
x = ((len - off - 1) * c0 - c1) % 255;
-
+
if (x <= 0)
x += 255;
y = 510 - c0 - x;
@@ -81,7 +83,7 @@ reduce_ospfd (struct csum_vals *vals, testsz_t len, testoff_t off)
y -= 255;
/* take care endian issue. */
- return htons ((x << 8) + y);
+ return htons ((x << 8) + y);
#undef x
#undef y
#undef c0
@@ -89,7 +91,7 @@ reduce_ospfd (struct csum_vals *vals, testsz_t len, testoff_t off)
}
/* slightly different concatenation */
-static u_int16_t
+static u_int16_t
reduce_ospfd1 (struct csum_vals *vals, testsz_t len, testoff_t off)
{
#define x vals->x
@@ -105,7 +107,7 @@ reduce_ospfd1 (struct csum_vals *vals, testsz_t len, testoff_t off)
y -= 255;
/* take care endian issue. */
- return htons ((x << 8) | (y & 0xff));
+ return htons ((x << 8) | (y & 0xff));
#undef x
#undef y
#undef c0
@@ -113,7 +115,7 @@ reduce_ospfd1 (struct csum_vals *vals, testsz_t len, testoff_t off)
}
/* original isisd version */
-static u_int16_t
+static u_int16_t
reduce_isisd (struct csum_vals *vals, testsz_t len, testoff_t off)
{
#define x vals->x
@@ -121,7 +123,7 @@ reduce_isisd (struct csum_vals *vals, testsz_t len, testoff_t off)
#define c0 vals->a.c0
#define c1 vals->a.c1
u_int32_t mul;
-
+
mul = (len - off)*(c0);
x = mul - c0 - c1;
y = c1 - mul - 1;
@@ -148,7 +150,7 @@ reduce_isisd (struct csum_vals *vals, testsz_t len, testoff_t off)
}
/* Is the -1 in y wrong perhaps? */
-static u_int16_t
+static u_int16_t
reduce_isisd_yfix (struct csum_vals *vals, testsz_t len, testoff_t off)
{
#define x vals->x
@@ -156,7 +158,7 @@ reduce_isisd_yfix (struct csum_vals *vals, testsz_t len, testoff_t off)
#define c0 vals->a.c0
#define c1 vals->a.c1
u_int32_t mul;
-
+
mul = (len - off)*(c0);
x = mul - c0 - c1;
y = c1 - mul;
@@ -183,7 +185,7 @@ reduce_isisd_yfix (struct csum_vals *vals, testsz_t len, testoff_t off)
}
/* Move the mods yp */
-static u_int16_t
+static u_int16_t
reduce_isisd_mod (struct csum_vals *vals, testsz_t len, testoff_t off)
{
#define x vals->x
@@ -191,7 +193,7 @@ reduce_isisd_mod (struct csum_vals *vals, testsz_t len, testoff_t off)
#define c0 vals->a.c0
#define c1 vals->a.c1
u_int32_t mul;
-
+
mul = (len - off)*(c0);
x = mul - c1 - c0;
y = c1 - mul - 1;
@@ -218,7 +220,7 @@ reduce_isisd_mod (struct csum_vals *vals, testsz_t len, testoff_t off)
}
/* Move the mods up + fix y */
-static u_int16_t
+static u_int16_t
reduce_isisd_mody (struct csum_vals *vals, testsz_t len, testoff_t off)
{
#define x vals->x
@@ -226,7 +228,7 @@ reduce_isisd_mody (struct csum_vals *vals, testsz_t len, testoff_t off)
#define c0 vals->a.c0
#define c1 vals->a.c1
u_int32_t mul;
-
+
mul = (len - off)*(c0);
x = mul - c0 - c1;
y = c1 - mul;
@@ -264,7 +266,7 @@ struct reductions_t {
{ .name = "isisd-mody", .f = reduce_isisd_mody },
{ NULL, NULL },
};
-
+
/* The original ospfd checksum */
static u_int16_t
ospfd_checksum (u_char *buffer, testsz_t len, testoff_t off)
@@ -276,7 +278,7 @@ ospfd_checksum (u_char *buffer, testsz_t len, testoff_t off)
csum = (u_int16_t *) (buffer + off);
*(csum) = 0;
-
+
sp = buffer;
for (ep = sp + len; sp < ep; sp = q)
@@ -292,27 +294,27 @@ ospfd_checksum (u_char *buffer, testsz_t len, testoff_t off)
c0 %= 255;
c1 %= 255;
}
-
+
ospfd_vals.a.c0 = c0;
ospfd_vals.a.c1 = c1;
-
+
//printf ("%s: len %u, off %u, c0 %d, c1 %d\n",
// __func__, len, off, c0, c1);
x = ((int)(len - off - 1) * (int)c0 - (int)c1) % 255;
-
+
if (x <= 0)
x += 255;
y = 510 - c0 - x;
if (y > 255)
y -= 255;
-
+
ospfd_vals.x = x;
ospfd_vals.y = y;
-
+
buffer[off] = x;
buffer[off + 1] = y;
-
+
/* take care endian issue. */
checksum = htons ((x << 8) | (y & 0xff));
@@ -334,15 +336,15 @@ iso_csum_create (u_char * buffer, testsz_t len, testoff_t off)
int i, init_len, partial_len;
checksum = 0;
-
+
csum = (u_int16_t *) (buffer + off);
*(csum) = checksum;
-
+
p = buffer;
c0 = 0;
c1 = 0;
init_len = len;
-
+
while (len != 0)
{
partial_len = MIN(len, MODX);
@@ -361,7 +363,7 @@ iso_csum_create (u_char * buffer, testsz_t len, testoff_t off)
isisd_vals.a.c0 = c0;
isisd_vals.a.c1 = c1;
-
+
mul = (init_len - off) * c0;
x = mul - c1 - c0;
@@ -379,14 +381,14 @@ iso_csum_create (u_char * buffer, testsz_t len, testoff_t off)
x = 255;
if (y == 0)
y = 1;
-
+
isisd_vals.x = x;
isisd_vals.y = y;
-
+
checksum = htons((x << 8) | (y & 0xFF));
-
+
*(csum) = checksum;
-
+
/* return the checksum for user usage */
return checksum;
}
@@ -399,7 +401,7 @@ verify (u_char * buffer, testsz_t len)
u_int32_t c1;
u_int16_t checksum;
int i, partial_len;
-
+
p = buffer;
checksum = 0;
@@ -427,6 +429,8 @@ verify (u_char * buffer, testsz_t len)
return 1;
}
+extern int in_cksum_optimized(void *parg, int nbytes) ;
+
int /* return checksum in low-order 16 bits */
in_cksum_optimized(void *parg, int nbytes)
{
@@ -458,6 +462,7 @@ in_cksum_optimized(void *parg, int nbytes)
return(answer);
}
+extern int in_cksum_rfc(void *parg, int count) ;
int /* return checksum in low-order 16 bits */
in_cksum_rfc(void *parg, int count)
@@ -495,29 +500,29 @@ main(int argc, char **argv)
u_char buffer[BUFSIZE];
int exercise = 0;
#define EXERCISESTEP 257
-
+
srandom (time (NULL));
-
+
while (1) {
u_int16_t ospfd, isisd, lib, in_csum, in_csum_res, in_csum_rfc;
int i,j;
exercise += EXERCISESTEP;
exercise %= MAXDATALEN;
-
+
for (i = 0; i < exercise; i += sizeof (long int)) {
long int rand = random ();
-
+
for (j = sizeof (long int); j > 0; j--)
buffer[i + (sizeof (long int) - j)] = (rand >> (j * 8)) & 0xff;
}
-
+
in_csum = in_cksum(buffer, exercise);
in_csum_res = in_cksum_optimized(buffer, exercise);
in_csum_rfc = in_cksum_rfc(buffer, exercise);
if (in_csum_res != in_csum || in_csum != in_csum_rfc)
printf ("verify: in_chksum failed in_csum:%x, in_csum_res:%x,"
- "in_csum_rfc %x, len:%d\n",
+ "in_csum_rfc %x, len:%d\n",
in_csum, in_csum_res, in_csum_rfc, exercise);
ospfd = ospfd_checksum (buffer, exercise + sizeof(u_int16_t), exercise);
@@ -529,7 +534,7 @@ main(int argc, char **argv)
lib = fletcher_checksum (buffer, exercise + sizeof(u_int16_t), exercise);
if (verify (buffer, exercise + sizeof(u_int16_t)))
printf ("verify: lib failed\n");
-
+
if (ospfd != lib) {
printf ("Mismatch in values at size %u\n"
"ospfd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
@@ -540,12 +545,12 @@ main(int argc, char **argv)
isisd, isisd_vals.a.c0, isisd_vals.a.c1, isisd_vals.x, isisd_vals.y,
lib
);
-
+
/* Investigate reduction phase discrepencies */
if (ospfd_vals.a.c0 == isisd_vals.a.c0
&& ospfd_vals.a.c1 == isisd_vals.a.c1) {
printf ("\n");
- for (i = 0; reducts[i].name != NULL; i++) {
+ for (i = 0; reducts[i].name != NULL; i++) {
ospfd = reducts[i].f (&ospfd_vals,
exercise + sizeof (u_int16_t),
exercise);
@@ -553,7 +558,7 @@ main(int argc, char **argv)
reducts[i].name, ospfd_vals.x & 0xff, ospfd_vals.y & 0xff, ospfd);
}
}
-
+
printf ("\n u_char testdata [] = {\n ");
for (i = 0; i < exercise; i++) {
printf ("0x%02x,%s",
diff --git a/tests/test-list_util.c b/tests/test-list_util.c
new file mode 100644
index 00000000..fc81a562
--- /dev/null
+++ b/tests/test-list_util.c
@@ -0,0 +1,2112 @@
+#include <zebra.h>
+#include <list_util.h>
+#include <string.h>
+
+/* List util torture tests
+ *
+ */
+
+struct thread_master *master; /* allow lib/zebra.c to link ! */
+
+/* prototypes */
+int main(int argc, char **argv);
+
+static void test_ssl(void);
+static void test_sdl(void);
+static void test_ddl(void);
+
+#define test_assert(true, message) \
+ do { if (!(true)) test_assert_fail(#true, message, __func__, __LINE__) ; \
+ } while (0)
+
+static void
+test_assert_fail(const char* true, const char* message, const char* func,
+ int line)
+{
+ printf("*** %s %d: (%s) not true: %s\n", func, line, true, message) ;
+
+} ;
+
+/*==============================================================================
+ * The tests.
+ */
+int
+main(int argc, char **argv)
+{
+ printf("Starting list_util tests -- %s\n",
+#ifdef __GNUC__LIST_UTIL
+ "GNUC version"
+#else
+ "not GNUC version"
+#endif
+ " -- v0.01 26-Feb-2010") ;
+
+ srand(22) ; /* Consistent testing required */
+
+ test_ssl() ;
+ test_sdl() ;
+ test_ddl() ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Construct a majic mark from two addresses
+ */
+static unsigned majic(void* a, void* b)
+{
+ uintptr_t z = (uintptr_t)a ^ (uintptr_t)b ^ (uintptr_t)&majic ;
+ return z ;
+} ;
+
+/*==============================================================================
+ * Single Base, Single Link
+ *
+ * ssl_push(base, item, next) -- add at head of list
+ * ssl_del(base, item, next) -- delete from list
+ * ssl_del_head(base, next) -- delete head of list
+ * ssl_pop(dst, base, next) -- pop head of list, if any
+ * ssl_head(base) -- return head of list
+ * ssl_next(item, next) -- step to next item, if any
+ *
+ * Cases to cover:
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ * c) deletion of first item and more than one item on list
+ * d) deletion of first item when only one item on the list
+ * e) deletion of arbitrary item (list implicitly not empty)
+ * f) deletion when item not on list and list not empty
+ * g) deletion when item not on list and list is empty
+ * h) deletion of NULL item and list not empty
+ * i) deletion of NULL item and list empty
+ * j) pop of head when list not empty
+ * k) pop of head when list contains one item
+ * l) pop of head when list empty
+ * m) deletion of head when list not empty
+ * n) deletion of head when contains one item
+ * o) deletion of head when list empty
+ * p) head when list not empty
+ * q) head when list is empty
+ * r) next when not last
+ * s) next when last
+ */
+
+typedef struct ssl_test* ssl_test ;
+struct ssl_test
+{
+ ssl_test next ; /* pointer at start of structure */
+
+ unsigned majic ;
+ char dummy ;
+
+ ssl_test other_next ; /* pointer elsewhere in structure */
+} ;
+
+struct ssl_test_parent
+{
+ unsigned rubbish ;
+ char fred ;
+
+ ssl_test base ;
+
+ int z[5] ;
+} ;
+
+static struct ssl_test_parent ssl_parent ;
+
+static void
+test_ssl(void)
+{
+ ssl_test base = NULL ;
+
+ ssl_test del = NULL ;
+ ssl_test other_del = NULL ;
+ ssl_test last = NULL ;
+ ssl_test first = NULL ;
+
+ struct ssl_test dummy ;
+
+ int n = 47 ;
+
+ int i_del = 7 ; /* NB: neither of these may be 0 or 1 */
+ int i_other_del = 37 ;
+
+ ssl_test prev ;
+ ssl_test this ;
+ ssl_test other_this ;
+ ssl_test take ;
+ ssl_test temp ;
+
+ int i ;
+ int ret ;
+
+ static struct ssl_test_parent* other = &ssl_parent ;
+
+ memset(other, 77, sizeof(struct ssl_test_parent)) ;
+ other->base = NULL ;
+
+ /* Repeated insertion, starting from empty list
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ *
+ * Creates lists for following tests.
+ */
+ printf("=== Testing ssl -- Single Base, Single Link -- stuff\n") ;
+
+ printf(" Creating list of items") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ ssl_test this = calloc(1, sizeof(struct ssl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+
+ this->dummy = i ;
+
+ ssl_push(base, this, next) ;
+ if (i & 1)
+ ssl_push(other->base, this, other_next) ;
+ else
+ ssl_push(ssl_parent.base, this, other_next) ;
+
+ if (i == i_del)
+ del = this ;
+ if (i == i_other_del)
+ other_del = this ;
+
+ first = this ;
+
+ printf("+") ;
+ } ;
+
+ test_assert((base == first) && (other->base == first),
+ "Failed to create consistent lists") ;
+ printf("\n") ;
+
+ passert((del != base) && (del != last)) ;
+ passert((other_del != base) && (other_del != last)) ;
+
+ /* Walk to check that have the expected items
+ *
+ * l) head when list not empty
+ * r) next when not last
+ * s) next when last
+ */
+ printf(" Walking list of items") ;
+
+ this = ssl_head(base) ;
+ test_assert(this == first, "ssl_head failed") ;
+
+ this = ssl_head(other->base) ;
+ test_assert(this == first, "ssl_head failed") ;
+
+ this = ssl_head(ssl_parent.base) ;
+ test_assert(this == first, "ssl_head failed") ;
+
+ this = ssl_next(del, next) ;
+ test_assert((this == del->next) && (this != NULL), "ssl_next failed") ;
+ this = ssl_next(last, next) ;
+ test_assert((this == last->next) && (this == NULL),
+ "ssl_next failed at end") ;
+
+ this = ssl_next(other_del, other_next) ;
+ test_assert((this == other_del->other_next) && (this != NULL),
+ "ssl_next failed") ;
+ this = ssl_next(last, other_next) ;
+ test_assert((this == last->other_next) && (this == NULL),
+ "ssl_next failed at end") ;
+
+ this = base ;
+ other_this = other->base ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ test_assert(this == other_this, "this and other_this not in step") ;
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf(".") ;
+ this = ssl_next(this, next) ;
+ other_this = ssl_next(other_this, other_next) ;
+ } ;
+ printf("\n") ;
+
+ /* Deletion specifically at the start of the list
+ *
+ * c) deletion of first item and more than one item on list
+ */
+ printf(" Deleting the first item") ;
+
+ this = base ;
+ first = base->next ;
+ ret = ssl_del(base, this, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(first == base, "ssl_del of first item failed") ;
+
+ this = other->base ;
+ ret = ssl_del(other->base, this, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(first == other->base, "ssl_del of first item failed") ;
+
+ printf("\n") ;
+
+ --n ; /* one less on the lists ! */
+
+ /* Deletion of items from arbitrary place in list
+ *
+ * e) deletion of arbitrary item (list implicitly not empty)
+ */
+ printf(" Deleting arbitrary items") ;
+ ret = ssl_del(base, del, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ ret = ssl_del(ssl_parent.base, other_del, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ printf("\n") ;
+
+ /* Deletion of items from arbitrary place in list
+ *
+ * f) deletion when item not on list and list not empty
+ */
+ printf(" Deleting non-existant items") ;
+ ret = ssl_del(base, &dummy, next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ ret = ssl_del(other->base, &dummy, other_next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ printf("\n") ;
+
+ /* Deletion of NULL items
+ *
+ * h) deletion of NULL item and list not empty
+ */
+ printf(" Deleting NULL items") ;
+
+ this = NULL ;
+
+ ret = ssl_del(base, this, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+
+ ret = ssl_del(ssl_parent.base, this, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+
+ printf("\n") ;
+
+ /* Scan lists to check after deletion */
+ printf(" Base list scan") ;
+
+ this = base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf("*") ;
+ this = ssl_next(this, next) ;
+ } ;
+ printf("\n") ;
+
+ printf(" Other list scan") ;
+
+ other_this = other->base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (other_this == NULL)
+ break ;
+
+ test_assert(other_this != prev, "this is same as prev") ;
+ prev = other_this ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(other_this->dummy == i--, "don't have the right dummy") ;
+ test_assert(other_this->majic == majic(other_this, &base),
+ "don't have the right majic") ;
+
+ printf("*") ;
+ other_this = ssl_next(other_this, other_next) ;
+ } ;
+ printf("\n") ;
+
+ /* Dismantle lists
+ *
+ * j) pop of head when list not empty
+ * k) pop of head when list contains one item
+ * l) pop of head when list empty
+ * p) head when list not empty
+ * q) head when list is empty
+ */
+ printf(" Popping the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = base ;
+ test_assert(this == ssl_head(base), "this is not head !") ;
+
+ temp = ssl_pop(&take, base, next) ;
+ test_assert(this == take, "this is not same as deleted head !") ;
+ test_assert(temp == take, "temp is not same as deleted head !") ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(base == this->next, "ssl_pop broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ this = ssl_head(base) ;
+ test_assert(this == NULL, "ssl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = other->base ;
+ test_assert(this == ssl_head(other->base), "this is not head !") ;
+
+ if (i & 1)
+ temp = ssl_pop(&take, other->base, other_next) ;
+ else
+ temp = ssl_pop(&take, ssl_parent.base, other_next) ;
+
+ test_assert(this == take, "this is not same as deleted head !") ;
+ test_assert(temp == take, "temp is not same as deleted head !") ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(other->base == this->other_next, "ssl_pop broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ this = ssl_head(other->base) ;
+ test_assert(this == NULL, "ssl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ /* Rebuild lists to do:
+ *
+ * m) deletion of head when list not empty
+ * n) deletion of head when contains one item
+ * o) deletion of head when list empty
+ */
+ passert((base == NULL) && (other->base == NULL)) ;
+
+ last = NULL ;
+ first = NULL ;
+ prev = NULL ;
+ printf(" Building list of items again") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ ssl_test this = calloc(1, sizeof(struct ssl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+ this->dummy = i ;
+
+ ssl_push(base, this, next) ;
+ if (i & 1)
+ ssl_push(ssl_parent.base, this, other_next) ;
+ else
+ ssl_push(other->base, this, other_next) ;
+
+ test_assert(this->next == prev, "broken ssl_push") ;
+ test_assert(this->other_next == prev, "broken ssl_push") ;
+
+ test_assert(base == this, "broken ssl_push") ;
+ test_assert(other->base == this, "broken ssl_push") ;
+
+ first = this ;
+ prev = this ;
+
+ printf("+") ;
+ } ;
+
+ printf("\n") ;
+
+ printf(" Deleting the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = base ;
+
+ ssl_del_head(base, next) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(base == this->next, "ssl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = other->base ;
+
+ if (i & 1)
+ ssl_del_head(ssl_parent.base, other_next) ;
+ else
+ ssl_del_head(other->base, other_next) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(other->base == this->other_next, "ssl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ printf("\n") ;
+
+ /* Final few tests
+ *
+ * d) deletion of first item when only one item on the list
+ * g) deletion when item not on list and list is empty
+ * i) deletion of NULL item and list empty
+ */
+
+ /* Deletion of items from arbitrary place in list */
+ printf(" Miscellaneous") ;
+
+ del->next = &dummy ;
+ ssl_push(base, del, next) ;
+ test_assert((base == del) && (del->next == NULL), "ssl_push failed ??") ;
+ ssl_del(base, del, next) ;
+ test_assert(base == NULL, "ssl_del of first and only item failed") ;
+
+ other_del->other_next = &dummy ;
+ ssl_push(other->base, other_del, other_next) ;
+ test_assert((other->base == other_del) && (other_del->other_next == NULL),
+ "ssl_push failed ??") ;
+ ssl_del(other->base, other_del, other_next) ;
+ test_assert(other->base == NULL, "ssl_del of first and only item failed") ;
+
+ ret = ssl_del(base, del, next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(base == NULL, "ssl_del on empty list") ;
+
+ ret = ssl_del(other->base, other_del, other_next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(other->base == NULL, "ssl_del on empty list") ;
+
+ this = NULL ;
+
+ ret = ssl_del(base, this, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(base == NULL, "ssl_del on empty list") ;
+
+ ret = ssl_del(other->base, this, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(other->base == NULL, "ssl_del on empty list") ;
+
+ printf("\n") ;
+
+} ;
+
+/*==============================================================================
+ * Single Base, Double Link
+ *
+ * sdl_push(base, item, list) -- add at head of list
+ * sdl_del(base, item, list) -- delete from list
+ * sdl_pop(&dst, base, next) -- pop head of list, if any
+ * sdl_head(base) -- return head of list
+ * sdl_next(item, next) -- step to next item, if any
+ * sdl_prev(item, next) -- step to prev item, if any
+ *
+ * Cases to cover:
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ * c) deletion of first item and more than one item on list
+ * d) deletion of first item when only one item on the list
+ * e) deletion of arbitrary item (list implicitly not empty)
+ * f) deletion of NULL item and list not empty
+ * g) deletion of NULL item and list empty
+ * h) pop of head when list not empty
+ * i) pop of head when list contains one item
+ * j) pop of head when list empty
+ * k) deletion of head when list not empty
+ * l) deletion of head when list contains one item
+ * m) deletion of head when list empty
+ * n) head when list not empty
+ * o) head when list is empty
+ * p) next when not last
+ * q) next when last
+ * r) prev when not first
+ * s) prev when first
+ *
+ * NB: unlike single link stuff, cannot attempt to remove item which is
+ * not on the list !
+ */
+
+typedef struct sdl_test* sdl_test ;
+struct sdl_test
+{
+ struct dl_list_pair(sdl_test) list ;
+
+ unsigned majic ;
+ char dummy ;
+
+ struct dl_list_pair(sdl_test) other_list ;
+};
+
+struct sdl_test_parent
+{
+ long rubbish ;
+ char fred ;
+
+ sdl_test base ;
+
+ int z[7] ;
+} ;
+
+static struct sdl_test_parent sdl_parent ;
+
+static void
+test_sdl(void)
+{
+ sdl_test base = NULL ;
+
+ sdl_test del = NULL ;
+ sdl_test other_del = NULL ;
+ sdl_test last = NULL ;
+ sdl_test first = NULL ;
+
+ struct sdl_test dummy ;
+
+ int n = 57 ;
+
+ int i_del = 9 ; /* NB: neither of these may be 0 or 1 */
+ int i_other_del = 49 ;
+
+ sdl_test prev ;
+ sdl_test this ;
+ sdl_test other_this ;
+ sdl_test take ;
+ sdl_test temp ;
+
+ int i ;
+
+ static struct sdl_test_parent* other = &sdl_parent ;
+
+ memset(other, 99, sizeof(struct sdl_test_parent)) ;
+ other->base = NULL ;
+
+ /* Repeated insertion, starting from empty list
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ *
+ * Creates lists for following tests.
+ */
+ printf("=== Testing sdl -- Single Base, Double Link -- stuff\n") ;
+
+ printf(" Creating list of items") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ sdl_test this = calloc(1, sizeof(struct sdl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+
+ this->dummy = i ;
+
+ sdl_push(base, this, list) ;
+ if (i & 1)
+ sdl_push(other->base, this, other_list) ;
+ else
+ sdl_push(sdl_parent.base, this, other_list) ;
+
+ if (i == i_del)
+ del = this ;
+ if (i == i_other_del)
+ other_del = this ;
+
+ first = this ;
+
+ printf("+") ;
+ } ;
+
+ test_assert((base == first) && (other->base == first),
+ "Failed to create consistent lists") ;
+
+ printf("\n") ;
+
+ passert((del != base) && (del != last)) ;
+ passert((other_del != base) && (other_del != last)) ;
+
+ /* Walk to check that have the expected items
+ *
+ * n) head when list not empty
+ * p) next when not last
+ * q) next when last
+ * r) prev when not first
+ * s) prev when first
+ */
+ printf(" Walking list of items") ;
+
+ this = sdl_head(base) ;
+ test_assert(this == first, "sdl_head failed") ;
+
+ this = sdl_head(other->base) ;
+ test_assert(this == first, "sdl_head failed") ;
+
+ this = sdl_head(sdl_parent.base) ;
+ test_assert(this == first, "sdl_head failed") ;
+
+ /* next on both lists */
+ this = sdl_next(first, list) ;
+ test_assert((this == first->list.next) && (this != NULL),
+ "sdl_next failed at start") ;
+ this = sdl_next(del, list) ;
+ test_assert((this == del->list.next) && (this != NULL), "sdl_next failed") ;
+ this = sdl_next(last, list) ;
+ test_assert((this == last->list.next) && (this == NULL),
+ "sdl_next failed at end") ;
+
+ this = sdl_next(first, other_list) ;
+ test_assert((this == first->other_list.next) && (this != NULL),
+ "sdl_next failed at start") ;
+ this = sdl_next(other_del, other_list) ;
+ test_assert((this == other_del->other_list.next) && (this != NULL),
+ "sdl_next failed") ;
+ this = sdl_next(last, other_list) ;
+ test_assert((this == last->other_list.next) && (this == NULL),
+ "sdl_next failed at end") ;
+
+ /* prev on both lists */
+ this = sdl_prev(first, list) ;
+ test_assert((this == first->list.prev) && (this == NULL),
+ "sdl_prev failed at start") ;
+ this = sdl_prev(del, list) ;
+ test_assert((this == del->list.prev) && (this != NULL), "sdl_prev failed") ;
+ this = sdl_prev(last, list) ;
+ test_assert((this == last->list.prev) && (this != NULL),
+ "sdl_prev failed at end") ;
+
+ this = sdl_prev(first, other_list) ;
+ test_assert((this == first->other_list.prev) && (this == NULL),
+ "sdl_prev failed at start") ;
+ this = sdl_prev(other_del, other_list) ;
+ test_assert((this == other_del->other_list.prev) && (this != NULL),
+ "sdl_prev failed") ;
+ this = sdl_prev(last, other_list) ;
+ test_assert((this == last->other_list.prev) && (this != NULL),
+ "sdl_prev failed at end") ;
+
+ this = base ;
+ other_this = other->base ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ test_assert(this == other_this, "this and other_this not in step") ;
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf(".") ;
+ this = sdl_next(this, list) ;
+ other_this = sdl_next(other_this, other_list) ;
+ } ;
+ printf("\n") ;
+
+ /* Deletion specifically at the start of the list
+ *
+ * c) deletion of first item and more than one item on list
+ */
+ printf(" Deleting the first item") ;
+
+ this = base ;
+ first = base->list.next ;
+ sdl_del(base, this, list) ;
+ test_assert(first == base, "sdl_del of first item failed") ;
+ test_assert((base == NULL) || (base->list.prev == NULL), "sdl_del failed") ;
+
+ this = other->base ;
+ sdl_del(sdl_parent.base, this, other_list) ;
+ test_assert(first == other->base, "sdl_del of first item failed") ;
+ test_assert((base == NULL) || (base->other_list.prev == NULL),
+ "sdl_del failed") ;
+
+ printf("\n") ;
+
+ --n ; /* one less on the lists ! */
+
+ /* Deletion of items from arbitrary place in list
+ *
+ * e) deletion of arbitrary item (list implicitly not empty)
+ */
+ printf(" Deleting arbitrary items") ;
+ sdl_del(base, del, list) ;
+ test_assert((base == NULL) || (base->list.prev == NULL), "sdl_del failed") ;
+ sdl_del(sdl_parent.base, other_del, other_list) ;
+ test_assert((base == NULL) || (base->other_list.prev == NULL),
+ "sdl_del failed") ;
+ printf("\n") ;
+
+ /* Deletion of NULL items
+ *
+ * f) deletion of NULL item and list not empty
+ */
+ printf(" Deleting NULL items") ;
+ this = NULL ;
+ sdl_del(base, this, list) ;
+ test_assert((base == NULL) || (base->list.prev == NULL), "sdl_del failed") ;
+ sdl_del(other->base, this, other_list) ;
+ test_assert((base == NULL) || (base->other_list.prev == NULL),
+ "sdl_del failed") ;
+ printf("\n") ;
+
+ /* Scan lists to check after deletion */
+ printf(" Base list scan") ;
+
+ this = base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ test_assert(this->list.prev == prev, "broken prev pointer") ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("*") ;
+
+ prev = this ;
+ this = sdl_next(this, list) ;
+
+ test_assert(this == prev->list.next, "broken sdl_next") ;
+ if (this != NULL)
+ test_assert(prev == sdl_prev(this, list), "broken sdl_prev") ;
+ } ;
+ printf("\n") ;
+
+ printf(" Other list scan") ;
+
+ this = other->base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ test_assert(this->other_list.prev == prev, "broken prev pointer") ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf("*") ;
+
+ prev = this ;
+ this = sdl_next(this, other_list) ;
+
+ test_assert(this == prev->other_list.next, "broken sdl_next") ;
+ if (this != NULL)
+ test_assert(prev == sdl_prev(this, other_list), "broken sdl_prev") ;
+ } ;
+ printf("\n") ;
+
+ /* Dismantle lists
+ *
+ * h) pop of head when list not empty
+ * i) pop of head when list contains one item
+ * j) pop of head when list empty
+ * o) head when list is empty
+ */
+ printf(" Popping the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = sdl_head(base) ;
+ test_assert(this == base, "broken sdl_head !") ;
+
+ temp = sdl_pop(&take, base, list) ;
+ test_assert(this == take, "take is not same as deleted head !") ;
+ test_assert(this == temp, "temp is not same as deleted head !") ;
+ if (base != NULL)
+ test_assert(base->list.prev == NULL, "sdl_pop failed") ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ this = sdl_head(base) ;
+ test_assert(this == NULL, "sdl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = sdl_head(other->base) ;
+ test_assert(this == other->base, "broken sdl_head !") ;
+
+ if (i & 1)
+ temp = sdl_pop(&take, sdl_parent.base, other_list) ;
+ else
+ temp = sdl_pop(&take, other->base, other_list) ;
+
+ test_assert(this == take, "take is not same as deleted head !") ;
+ test_assert(this == temp, "temp is not same as deleted head !") ;
+ if (other->base != NULL)
+ test_assert(other->base->other_list.prev == NULL,
+ "sdl_pop failed") ;
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ this = sdl_head(other->base) ;
+ test_assert(this == NULL, "sdl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ /* Rebuild lists to do:
+ *
+ * k) deletion of head when list not empty
+ * l) deletion of head when list contains one item
+ * m) deletion of head when list empty
+ */
+ passert((base == NULL) && (other->base == NULL)) ;
+
+ last = NULL ;
+ first = NULL ;
+ prev = NULL ;
+ printf(" Building list of items again") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ sdl_test this = calloc(1, sizeof(struct sdl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+ this->dummy = i ;
+
+ sdl_push(base, this, list) ;
+ if (i & 1)
+ sdl_push(sdl_parent.base, this, other_list) ;
+ else
+ sdl_push(other->base, this, other_list) ;
+
+ test_assert(this->list.next == prev, "broken sdl_push") ;
+ test_assert(this->other_list.next == prev, "broken sdl_push") ;
+
+ test_assert(base == this, "broken sdl_push") ;
+ test_assert(other->base == this, "broken sdl_push") ;
+
+ first = this ;
+ prev = this ;
+
+ printf("+") ;
+ } ;
+
+ printf("\n") ;
+
+ printf(" Deleting the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = base ;
+
+ sdl_del_head(base, list) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(base == this->list.next, "sdl_del_head broken") ;
+ if (base != NULL)
+ test_assert(base->list.prev == NULL, "sdl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = other->base ;
+
+ if (i & 1)
+ sdl_del_head(other->base, other_list) ;
+ else
+ sdl_del_head(sdl_parent.base, other_list) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(other->base == this->other_list.next, "sdl_del_head broken") ;
+ if (other->base != NULL)
+ test_assert(other->base->other_list.prev == NULL,
+ "sdl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ printf("\n") ;
+
+ /* Final few tests
+ *
+ * d) deletion of first item when only one item on the list
+ * g) deletion of NULL item and list empty
+ */
+
+ /* Deletion of items from arbitrary place in list */
+ printf(" Miscellaneous") ;
+
+ del->list.next = &dummy ;
+ sdl_push(base, del, list) ;
+ test_assert((base == del) && (del->list.next == NULL), "sdl_push failed ??") ;
+ sdl_del(base, del, list) ;
+ test_assert(base == NULL, "sdl_del of first and only item failed") ;
+
+ other_del->other_list.next = &dummy ;
+ sdl_push(other->base, other_del, other_list) ;
+ test_assert((other->base == other_del) && (other_del->other_list.next == NULL),
+ "sdl_push failed ??") ;
+ sdl_del(other->base, other_del, other_list) ;
+ test_assert(other->base == NULL, "sdl_del of first and only item failed") ;
+
+ this = NULL ;
+ sdl_del(base, this, list) ;
+ test_assert(base == NULL, "sdl_del of NULL item with empty list") ;
+
+ sdl_del(other->base, this, other_list) ;
+ test_assert(other->base == NULL, "sdl_del of NULL item with empty list") ;
+
+ printf("\n") ;
+} ;
+
+/*==============================================================================
+ * Double Base, Double Link
+ *
+ * ddl_init(base) -- initialise base
+ * ddl_push(base, item, list) -- insert at head of list
+ * ddl_append(base, item, list) -- insert at tail of list
+ * ddl_in_after(after, base, item, list) -- insert after
+ * ddl_in_before(before, base, item, list) -- insert before
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ * ddl_del(base, item, list) -- delete from list
+ * ddl_del_head(base, next) -- delete head of list
+ * ddl_del_tail(base, next) -- delete tail of list
+ * ddl_head(base) -- return head of list
+ * ddl_tail(base) -- return tail of list
+ * ddl_next(item, next) -- step to next item, if any
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Cases to cover:
+ */
+
+/* Testing runs two lists through struct ddt_item objects. */
+
+enum list { a_list, b_list, list_count } ;
+
+typedef struct ddt_item* ddt_item ;
+
+struct ddt_list_pair dl_list_pair(ddt_item) ; /* Example struct constructor */
+struct ddt_base_pair dl_base_pair(ddt_item) ;
+
+typedef struct dl_base_pair(ddt_item) ddt_base_pair_t ;
+ /* Example typedef constructor */
+typedef struct dl_base_pair(ddt_item)* p_ddt_base_pair ;
+ /* Example typedef constructor */
+
+typedef struct ddt_list_pair* ddt_list_pair ;
+typedef struct ddt_base_pair* ddt_base_pair ;
+
+typedef struct ddt_rank* ddt_rank ;
+
+struct ddt_rank /* information for each list */
+{
+ struct ddt_list_pair list ; /* the thing we are testing */
+
+ int lister ;
+ int list_found ;
+ unsigned majic ;
+
+ int ordinal ;
+};
+
+struct ddt_item /* the test items */
+{
+ struct ddt_rank a ;
+
+ char a_rubbish[21] ;
+
+ struct ddt_rank b ;
+
+ char b_rubbish[19] ;
+} ;
+
+/* The test list base pairs, and pointers to the actual bases, for use in
+ * the verification code.
+ */
+static ddt_base_pair ddt_bases[list_count] ;
+
+/* For some tests want to construct lists and know the first, last and
+ * somewhere between items.
+ */
+
+enum where { first, middle, last, where_count } ;
+
+struct ddt_test_list_items
+{
+ struct
+ {
+ ddt_item where[where_count] ;
+ } list[list_count] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The test list items -- keep track here also for use in verification.
+ */
+enum { ddt_max_items = 1000 } ;
+
+static unsigned ddt_item_count = 0 ;
+static unsigned ddt_item_alloc = 0 ;
+static ddt_item ddt_items[ddt_max_items] ;
+
+static inline ddt_item
+ddt_new_item(void)
+{
+ ddt_item item ;
+
+ assert(ddt_item_count <= ddt_item_alloc) ;
+
+ if (ddt_item_count == ddt_item_alloc)
+ {
+ assert(ddt_item_alloc < ddt_max_items) ;
+ ddt_items[ddt_item_alloc++] = malloc(sizeof(struct ddt_item)) ;
+ } ;
+
+ item = ddt_items[ddt_item_count++] ;
+
+ memset(item, ddt_item_count & 0xFF, sizeof(struct ddt_item)) ;
+
+ item->a.lister = 0 ;
+ item->b.lister = 0 ;
+
+ item->a.ordinal = 0 ;
+ item->b.ordinal = 0 ;
+
+ return item ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Given an item and a list ordinal, return pointer to "rank" for item.
+ */
+static inline ddt_rank
+ddt_get_rank(ddt_item item, enum list l)
+{
+ if (item == NULL)
+ return NULL ;
+
+ if (l == a_list)
+ return &item->a ;
+ if (l == b_list)
+ return &item->b ;
+
+ assert(0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Keep track of what should be found on the lists, and majic marker to check
+ * that don't get lost and point into space.
+ */
+static inline unsigned
+ddt_get_majic(ddt_item item, enum list l)
+{
+ return majic(item, ddt_bases[l]) ;
+} ;
+
+static void
+ddt_test_list_add(ddt_item item, enum list l)
+{
+ ddt_rank rank = ddt_get_rank(item, l) ;
+
+ test_assert(rank->lister == 0, "already on list") ;
+
+ rank->lister = 1 ;
+ rank->majic = ddt_get_majic(item, l) ;
+ rank->ordinal = 0 ; /* unknown ordinal */
+} ;
+
+static void
+ddt_test_list_del(ddt_item item, enum list l)
+{
+ ddt_rank rank ;
+
+ if (item == NULL)
+ return ;
+
+ rank = ddt_get_rank(item, l) ;
+
+ test_assert(rank->lister == 1, "not on list") ;
+
+ rank->lister = 0 ;
+ rank->majic = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Verification code.
+ *
+ * Blunt instrument to check that all known lists are valid. Checks:
+ *
+ * * bases are both NULL together, or both not NULL.
+ *
+ * * first and last items on the list have suitable prev/next pointers.
+ *
+ * * walk list to confirm, for each item:
+ *
+ * -- prev pointer valid for each item
+ * -- item majic is correct (so not pointing somewhere invalid)
+ * -- item is supposed to be on the list
+ * -- item has not already been seen on list (list bent)
+ * -- ordinal, if not zero, is bigger than any previous non-zero ordinal
+ *
+ * * last item visited on walk is what the tail points to
+ *
+ * * for any items which are supposed to be on list, but were not found
+ */
+static void
+ddt_verify_lists(void)
+{
+ ddt_base_pair base ;
+ ddt_rank r ;
+ ddt_item this ;
+ ddt_item prev ;
+ int l ;
+ int i ;
+
+ /* Wash the found flags */
+ for (l = 0 ; l < list_count ; ++l)
+ for (i = 0 ; i < (int)ddt_item_count ; ++i)
+ ddt_get_rank(ddt_items[i], l)->list_found = 0 ;
+
+ /* Walk the lists */
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ int ordinal = 0 ;
+
+ base = ddt_bases[l] ;
+ if (base == NULL)
+ continue ;
+
+ if ((base->head == NULL) || (base->tail == NULL))
+ test_assert(base->head == base->tail, "broken list bases") ;
+ else
+ {
+ r = ddt_get_rank(base->head, l) ;
+ test_assert(r->list.prev == NULL, "broken list first item->prev") ;
+ r = ddt_get_rank(base->tail, l) ;
+ test_assert(r->list.next == NULL, "broken list last item->next") ;
+
+ this = base->head ;
+ prev = NULL ;
+
+ while (this != NULL)
+ {
+ r = ddt_get_rank(this, l) ;
+
+ test_assert(r->list.prev == prev, "broken item->prev") ;
+
+ test_assert(r->lister, "should not be on this list") ;
+
+ test_assert(!r->list_found, "circular list") ;
+ r->list_found = 1 ;
+
+ if (r->ordinal != 0)
+ {
+ test_assert(r->ordinal > ordinal, "list out of order") ;
+ ordinal = r->ordinal ;
+ }
+
+ test_assert(r->majic == ddt_get_majic(this, l),
+ "wrong sort of majic") ;
+ prev = this ;
+ this = r->list.next ;
+ } ;
+
+ test_assert(base->tail == prev, "broken tail pointer") ;
+ } ;
+ } ;
+
+ /* Verify that did not miss anything should have found */
+ /* Wash the found flags */
+ for (l = 0 ; l < list_count ; ++l)
+ for (i = 0 ; i < (int)ddt_item_count ; ++i)
+ {
+ r = ddt_get_rank(ddt_items[i], l) ;
+
+ if (r->lister)
+ test_assert(r->list_found, "should have found this on list") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset the test list handling
+ *
+ */
+static void
+ddt_reset_lists(void)
+{
+ int l ;
+
+ for (l = 0 ; l < list_count ; ++l)
+ ddl_init(*(ddt_bases[l])) ;
+
+ ddt_item_count = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make lists with 'n' items each.
+ *
+ * If 'n' 0..3, makes lists with exactly that many items.
+ *
+ * Otherwise, list length has +/- 25% jitter.
+ */
+static void
+ddt_test_make_lists(struct ddt_test_list_items* test, int n)
+{
+ ddt_base_pair base ;
+ ddt_item item ;
+ ddt_rank rank ;
+ enum list l ;
+ int t ;
+ int m ;
+
+ int req[list_count] ;
+ int mid[list_count] ;
+
+ /* Capture the requirements */
+ t = 0 ;
+ m = 1 ;
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ m += m ;
+ int j = (n + 1) / 4 ;
+
+ if (n <= 3)
+ req[l] = n ;
+ else
+ req[l] = n - j + (rand() % (j + j + 1)) ;
+
+ mid[l] = req[l] / 2 ;
+
+ t += req[l] ;
+
+ test->list[l].where[first] = NULL ;
+ test->list[l].where[middle] = NULL ;
+ test->list[l].where[last] = NULL ;
+ } ;
+
+ ddt_reset_lists() ;
+
+ /* Have t = total number of items still required
+ * m = 2^n -- where there are 'n' lists
+ */
+ while (t != 0)
+ {
+ int b ;
+ int r ;
+ r = (rand() % (m - 1)) + 1 ; /* bit pattern, at least one set */
+
+ item = ddt_new_item() ;
+
+ b = 1 ;
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ if ((req[l] != 0) && ((r & b) != 0))
+ {
+ --req[l] ;
+ --t ;
+
+ ddt_test_list_add(item, l) ;
+
+ if (test->list[l].where[first] == NULL)
+ test->list[l].where[first] = item ;
+
+ if (mid[l]-- == 0)
+ test->list[l].where[middle] = item ;
+
+ test->list[l].where[last] = item ;
+
+ base = ddt_bases[l] ;
+ rank = ddt_get_rank(item, l) ;
+
+ if (base->head == NULL)
+ {
+ base->head = item ;
+ base->tail = item ;
+ rank->list.next = NULL ;
+ rank->list.prev = NULL ;
+ }
+ else if (rand() & 1)
+ {
+ rank->list.next = base->head ;
+ rank->list.prev = NULL ;
+ ddt_get_rank(base->head, l)->list.prev = item ;
+ base->head = item ;
+ }
+ else
+ {
+ rank->list.next = NULL ;
+ rank->list.prev = base->tail ;
+ ddt_get_rank(base->tail, l)->list.next = item ;
+ base->tail = item ;
+ }
+ } ;
+ b <<= 1 ;
+ }
+ } ;
+
+ /* Number the items */
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ int o = 0 ;
+
+ base = ddt_bases[l] ;
+
+ item = base->head ;
+ while (item != NULL)
+ {
+ rank = ddt_get_rank(item, l) ;
+ rank->ordinal = ++o ; /* first is 1 */
+ item = rank->list.next ;
+ } ;
+ } ;
+
+ ddt_verify_lists() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * ddl_init(base) -- initialise base
+ * ddl_push(base, item, list) -- insert at head of list
+ * ddl_append(base, item, list) -- insert at tail of list
+ * ddl_in_after(after, base, item, list) -- insert after
+ * ddl_in_before(before, base, item, list) -- insert before
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ * ddl_del(base, item, list) -- delete from list
+ * ddl_del_head(base, next) -- delete head of list
+ * ddl_del_tail(base, next) -- delete tail of list
+ * ddl_head(base) -- return head of list
+ * ddl_tail(base) -- return tail of list
+ * ddl_next(item, next) -- step to next item, if any
+ * ddl_prev(item, next) -- step to prev item, if any
+ */
+
+static struct ddt_parent
+{
+ char zlxq[37] ;
+
+ struct ddt_base_pair base ;
+
+ char qxlz[45] ;
+} ddt_parent ;
+
+static void
+test_ddl(void)
+{
+ struct ddt_base_pair a_base ;
+ struct ddt_parent* b ;
+
+ struct ddt_test_list_items test ;
+ int n ;
+ int o ;
+
+ int base_n = 23 ;
+ int rand_n = 17 ;
+
+ printf("=== Testing ddl -- Double Base, Double Link -- stuff\n") ;
+
+ /* Initialise the test support */
+ ddt_bases[a_list] = &a_base ;
+ ddt_bases[b_list] = &ddt_parent.base ;
+
+ ddt_item_count = 0 ;
+ ddt_item_alloc = 0 ;
+
+ /* Initialise the list bases */
+ b = &ddt_parent ;
+ memset(b, 42, sizeof(struct ddt_parent)) ;
+
+ ddl_init(a_base) ;
+ ddl_init(b->base) ;
+
+ ddt_verify_lists() ; /* start as mean to go on */
+
+
+ /* ddl_push(base, item, list) -- insert at head of list
+ *
+ * Cases: (a) empty list
+ * (b) list with one item
+ * (c) list with multiple items
+ */
+ printf(" ddl_push test") ;
+ ddt_reset_lists() ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ int r ;
+
+ printf(".") ;
+
+ item = ddt_new_item() ;
+ r = (rand() % 3) + 1 ;
+
+ if (r & 1)
+ {
+ ddl_push(a_base, item, a.list) ;
+ test_assert(a_base.head == item, "ddl_push broken") ;
+ ddt_test_list_add(item, a_list) ;
+ item->a.ordinal = n ;
+ } ;
+ ddt_verify_lists() ;
+
+ if (r & 2)
+ {
+ ddl_push(b->base, item, b.list) ;
+ test_assert(b->base.head == item, "ddl_push broken") ;
+ ddt_test_list_add(item, b_list) ;
+ item->b.ordinal = n ;
+ } ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_append(base, item, list) -- insert at tail of list
+ *
+ * Cases: (a) empty list
+ * (b) list with one item
+ * (c) list with multiple items
+ */
+ printf(" ddl_append test") ;
+ ddt_reset_lists() ;
+
+ n = base_n + (rand() % rand_n) ;
+ o = 0 ;
+ while (n)
+ {
+ ddt_item item ;
+ int r ;
+
+ printf(".") ;
+ ++o ;
+
+ item = ddt_new_item() ;
+ r = (rand() % 3) + 1 ;
+
+ if (r & 1)
+ {
+ ddl_append(a_base, item, a.list) ;
+ test_assert(a_base.tail == item, "ddl_append broken") ;
+ ddt_test_list_add(item, a_list) ;
+ item->a.ordinal = o ;
+ } ;
+ ddt_verify_lists() ;
+
+ if (r & 2)
+ {
+ ddl_append(b->base, item, b.list) ;
+ test_assert(b->base.tail == item, "ddl_append broken") ;
+ ddt_test_list_add(item, b_list) ;
+ item->b.ordinal = o ;
+ } ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_in_after(after, base, item, list) -- insert after
+ *
+ * Cases: (a) after first and only (so is also last)
+ * (b) after first when more than one
+ * (c) after last when more than one
+ * (d) after something between
+ */
+ printf(" ddl_in_after test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ ddt_item after ;
+
+ printf(".") ;
+ ddt_test_make_lists(&test, n) ;
+
+ item = ddt_new_item() ;
+ after = test.list[a_list].where[n % where_count] ;
+ ddl_in_after(after, a_base, item, a.list) ;
+ test_assert(after->a.list.next == item, "ddl_in_after broken") ;
+ test_assert(item->a.list.prev == after, "ddl_in_after broken") ;
+ ddt_test_list_add(item, a_list) ;
+ ddt_verify_lists() ;
+
+ item = ddt_new_item() ;
+ after = test.list[b_list].where[n % where_count] ;
+ ddl_in_after(after, b->base, item, b.list) ;
+ test_assert(after->b.list.next == item, "ddl_in_after broken") ;
+ test_assert(item->b.list.prev == after, "ddl_in_after broken") ;
+ ddt_test_list_add(item, b_list) ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_in_before(before, base, item, list) -- insert before
+ *
+ * Cases: (a) before first and only (so is also last)
+ * (b) before first when more than one
+ * (c) before last when more than one
+ * (d) before something between
+ */
+ printf(" ddl_in_before test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ ddt_item before ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ item = ddt_new_item() ;
+ before = test.list[a_list].where[n % where_count] ;
+ ddl_in_before(before, a_base, item, a.list) ;
+ test_assert(before->a.list.prev == item, "ddl_in_before broken") ;
+ test_assert(item->a.list.next == before, "ddl_in_before broken") ;
+ ddt_test_list_add(item, a_list) ;
+ ddt_verify_lists() ;
+
+ item = ddt_new_item() ;
+ before = test.list[b_list].where[n % where_count] ;
+ ddl_in_before(before, b->base, item, b.list) ;
+ test_assert(before->b.list.prev == item, "ddl_in_before broken") ;
+ test_assert(item->b.list.next == before, "ddl_in_before broken") ;
+ ddt_test_list_add(item, b_list) ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_pop test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item temp ;
+ ddt_item peek ;
+ int ordinal ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = a_base.head ;
+ temp = ddl_pop(&item, a_base, a.list) ;
+ test_assert(temp == item, "ddl_pop broken") ;
+ test_assert(peek == item, "ddl_pop broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ ++ordinal ;
+ test_assert(item->a.ordinal == ordinal, "ddl_pop not in order") ;
+ } ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = b->base.head ;
+ temp = ddl_pop(&item, b->base, b.list) ;
+ test_assert(temp == item, "ddl_pop broken") ;
+ test_assert(peek == item, "ddl_pop broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ ++ordinal ;
+ test_assert(item->b.ordinal == ordinal, "ddl_pop not in order") ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_crop(&dst, base, next) -- crop tail of list, if any
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+
+ printf(" ddl_crop test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item temp ;
+ ddt_item peek ;
+ int ordinal ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = a_base.tail ;
+ temp = ddl_crop(&item, a_base, a.list) ;
+ test_assert(temp == item, "ddl_crop broken") ;
+ test_assert(peek == item, "ddl_crop broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ if (ordinal == 0)
+ ordinal = item->a.ordinal ;
+ else
+ {
+ test_assert(ordinal > 0, "ordinal out of whack") ;
+ --ordinal ;
+ test_assert(item->a.ordinal == ordinal, "ddl_crop not in order") ;
+ } ;
+ } ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = b->base.tail ;
+ temp = ddl_crop(&item, b->base, b.list) ;
+ test_assert(temp == item, "ddl_crop broken") ;
+ test_assert(peek == item, "ddl_crop broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ if (ordinal == 0)
+ ordinal = item->b.ordinal ;
+ else
+ {
+ test_assert(ordinal > 0, "ordinal out of whack") ;
+ --ordinal ;
+ test_assert(item->b.ordinal == ordinal, "ddl_crop not in order") ;
+ } ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_del(base, item, list) -- delete from list
+ *
+ * Cases: (a) first and only (so is also last)
+ * (b) first when more than one
+ * (c) last when more than one
+ * (d) something between
+ */
+ printf(" ddl_del test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ item = test.list[a_list].where[n % where_count] ;
+ ddl_del(a_base, item, a.list) ;
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ item = test.list[b_list].where[n % where_count] ;
+ ddl_del(b->base, item, b.list) ;
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_del_head(base, next) -- delete head of list
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_del_head test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item peek ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ while (1)
+ {
+ item = a_base.head ;
+ peek = (item != NULL) ? item->a.list.next : NULL ;
+
+ ddl_del_head(a_base, a.list) ;
+
+ test_assert(a_base.head == peek, "ddl_del_head broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ while (1)
+ {
+ item = b->base.head ;
+ peek = (item != NULL) ? item->b.list.next : NULL ;
+
+ ddl_del_head(b->base, b.list) ;
+
+ test_assert(b->base.head == peek, "ddl_del_head broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_del_tail(base, next) -- delete tail of list
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_del_tail test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item peek ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ while (1)
+ {
+ item = a_base.tail ;
+ peek = (item != NULL) ? item->a.list.prev : NULL ;
+
+ ddl_del_tail(a_base, a.list) ;
+
+ test_assert(a_base.tail == peek, "ddl_del_tail broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ while (1)
+ {
+ item = b->base.tail ;
+ peek = (item != NULL) ? item->b.list.prev : NULL ;
+
+ ddl_del_tail(b->base, b.list) ;
+
+ test_assert(b->base.tail == peek, "ddl_del_tail broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_head(base) -- return head of list
+ * ddl_tail(base) -- return tail of list
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_head & ddl_tail test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ test_assert(ddl_head(a_base) == a_base.head, "ddl_head broken") ;
+ test_assert(ddl_tail(a_base) == a_base.tail, "ddl_head broken") ;
+ test_assert(ddl_head(b->base) == b->base.head, "ddl_head broken") ;
+ test_assert(ddl_tail(b->base) == b->base.tail, "ddl_head broken") ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+
+ /* ddl_next(item, next) -- step to next item, if any
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Cases: (a) at first and only (so is also last)
+ * (b) at first when more than one
+ * (c) at last when more than one
+ * (d) at something between
+ */
+ printf(" ddl_next and ddl_prev test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ ddt_item where ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ where = test.list[a_list].where[n % where_count] ;
+ item = ddl_next(where, a.list) ;
+ test_assert(item == where->a.list.next, "ddl_next broken") ;
+
+ where = test.list[b_list].where[n % where_count] ;
+ item = ddl_next(where, b.list) ;
+ test_assert(item == where->b.list.next, "ddl_next broken") ;
+
+ where = test.list[a_list].where[n % where_count] ;
+ item = ddl_prev(where, a.list) ;
+ test_assert(item == where->a.list.prev, "ddl_prev broken") ;
+
+ where = test.list[b_list].where[n % where_count] ;
+ item = ddl_prev(where, b.list) ;
+ test_assert(item == where->b.list.prev, "ddl_prev broken") ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+
+}
+
+/*
+ * TODO
+ *
+ */
diff --git a/tests/test-privs.c b/tests/test-privs.c
index a888ea0f..568fe79a 100644
--- a/tests/test-privs.c
+++ b/tests/test-privs.c
@@ -125,26 +125,42 @@ main (int argc, char **argv)
#define PRIV_STATE() \
((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered")
- printf ("%s\n", PRIV_STATE());
+ printf ("Initial state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
+
test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
+ printf ("Get ids %s\n", PRIV_STATE());
zprivs_get_ids (&ids);
/* terminate privileges */
zprivs_terminate(&test_privs);
/* but these should continue to work... */
- printf ("%s\n", PRIV_STATE());
+ printf ("Terminated state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
+
test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
+ printf ("Get ids %s\n", PRIV_STATE());
zprivs_get_ids (&ids);
printf ("terminating\n");
diff --git a/tests/test-sig.c b/tests/test-sig.c
index 63aab6f0..d9eb24f0 100644
--- a/tests/test-sig.c
+++ b/tests/test-sig.c
@@ -2,6 +2,10 @@
#include <sigevent.h>
#include "lib/log.h"
+extern void sighup (void) ;
+extern void sigusr1 (void) ;
+extern void sigusr2 (void) ;
+
void
sighup (void)
{
@@ -20,7 +24,7 @@ sigusr2 (void)
printf ("processed usr2\n");
}
-struct quagga_signal_t sigs[] =
+struct quagga_signal_t sigs[] =
{
{
.signal = SIGHUP,
@@ -44,13 +48,13 @@ main (void)
{
master = thread_master_create ();
signal_init (master, Q_SIGC(sigs), sigs);
-
+
zlog_default = openzlog("testsig", ZLOG_NONE,
LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
-
+
while (thread_fetch (master, &t))
thread_call (&t);
diff --git a/tests/test-stream.c b/tests/test-stream.c
index 785ce588..e145436f 100644
--- a/tests/test-stream.c
+++ b/tests/test-stream.c
@@ -2,7 +2,7 @@
#include <stream.h>
#include <thread.h>
-static long int ham = 0xdeadbeefdeadbeef;
+static int64_t ham = 0xdeadbeefdeadbeef;
struct thread_master *master;
static void
@@ -11,9 +11,9 @@ print_stream (struct stream *s)
size_t getp = stream_get_getp (s);
printf ("endp: %ld, readable: %ld, writeable: %ld\n",
- stream_get_endp (s),
- STREAM_READABLE (s),
- STREAM_WRITEABLE (s));
+ (long int)stream_get_endp (s),
+ (long int)STREAM_READABLE (s),
+ (long int)STREAM_WRITEABLE (s));
while (STREAM_READABLE (s))
{
@@ -48,7 +48,7 @@ main (void)
printf ("c: 0x%hhx\n", stream_getc (s));
printf ("w: 0x%hx\n", stream_getw (s));
printf ("l: 0x%x\n", stream_getl (s));
- printf ("q: 0x%lx\n", stream_getq (s));
+ printf ("q: 0x%llx\n", (long long unsigned)stream_getq (s));
return 0;
}
diff --git a/tests/test-symtab.c b/tests/test-symtab.c
new file mode 100644
index 00000000..ed83e607
--- /dev/null
+++ b/tests/test-symtab.c
@@ -0,0 +1,344 @@
+#include <zebra.h>
+#include <symtab.h>
+
+/* Symtab torture tests
+ *
+ */
+
+/* prototypes */
+void assert_true(int result, const char * message);
+int main(int argc, char **argv);
+void test_symbol_table_init_new(void);
+void test_symbol_table_lookup(void);
+void call_back_function_set(symbol sym, void* value);
+void call_back_function_change(symbol sym, void* value);
+void call_back_function_unset(symbol sym, void* value);
+void test_call_back(void);
+void test_ref(void);
+void test_ref_heavy(void);
+
+struct thread_master *master;
+
+void
+assert_true(int result, const char * message)
+{
+ if (!result)
+ {
+ printf("Assert failed: %s\n", message);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+
+ test_symbol_table_init_new();
+ test_symbol_table_lookup();
+ test_call_back();
+ test_ref();
+ test_ref_heavy();
+
+ return 0;
+}
+
+void
+test_symbol_table_init_new(void)
+{
+ symbol_table table = NULL;
+ char name[] = "name";
+ char value[] = "value";
+ symbol sym = NULL;
+ symbol sym2 = NULL;
+ void * old_value = NULL;
+
+ printf("test_symbol_table_init_new\n");
+ table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
+ assert_true(table != NULL, "table == NULL");
+
+ /* expect to not find */
+ sym = symbol_lookup(table, name, 0);
+ assert_true(sym == NULL, "sym != NULL");
+
+ /* add */
+ sym = symbol_lookup(table, name, 1);
+ symbol_set_value(sym, value);
+ assert_true(sym != NULL, "sym == NULL");
+ assert_true(strcmp(symbol_get_name(sym), name) == 0, "strcmp(symbol_get_name(sym), name) != 0");
+
+ /* find */
+ sym2 = symbol_lookup(table, name, 0);
+ assert_true(sym == sym2, "sym != sym2");
+ assert_true(symbol_get_value(sym) == value, "symbol_get_value(sym) != value");
+
+ old_value = symbol_delete(sym);
+ assert_true(value == old_value, "value != old_value");
+
+ while ((old_value = symbol_table_ream(table, 1)) != NULL)
+ {
+ }
+
+}
+
+void
+test_symbol_table_lookup(void)
+{
+ symbol_table table = NULL;
+ char buf[20];
+ symbol sym = NULL;
+ int i;
+ char *value = NULL;
+ const int len = 100000;
+ struct symbol_walker itr;
+ vector v = NULL;
+
+ printf("test_symbol_table_lookup\n");
+ table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
+
+ /* add */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d-name", i);
+ sym = symbol_lookup(table, buf, 1);
+ assert_true(sym != NULL, "add: sym == NULL");
+ assert_true(strcmp(symbol_get_name(sym), buf) == 0,
+ "strcmp(symbol_get_name(sym), buf) != 0");
+
+ sprintf(buf, "%d-value", i);
+ value = strdup(buf);
+ symbol_set_value(sym, value);
+ assert_true(symbol_get_value(sym) == value,
+ "symbol_get_value(sym) != value");
+ }
+
+ /* find */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d-name", i);
+ sym = symbol_lookup(table, buf, 0);
+ assert_true(sym != NULL, "find: sym == NULL");
+ assert_true(strcmp(symbol_get_name(sym), buf) == 0,
+ "strcmp(symbol_get_name(sym), buf) != 0");
+
+ sprintf(buf, "%d-value", i);
+ assert_true(strcmp(symbol_get_value(sym), buf) == 0,
+ "strcmp(symbol_get_value(sym), buf) != 0");
+ }
+
+ /* walk with symbol_walker */
+ symbol_walk_start(table, &itr);
+ i = 0;
+ do
+ {
+ sym = symbol_walk_next(&itr);
+ if (sym != NULL) {
+ ++i;
+ }
+ } while (sym != NULL);
+ assert_true(i == len, "i != len");
+
+ /* extract vector */
+ v = symbol_table_extract(table, NULL, NULL, 1, NULL);
+ assert_true(vector_end(v) == (unsigned)len, "vector_get_end(v) != len");
+ vector_free(v);
+
+ while ((value = symbol_table_ream(table, 1)) != NULL)
+ {
+ free(value);
+ }
+}
+
+void
+test_call_back(void)
+{
+ symbol_table table = NULL;
+ char name[] = "name";
+ char value[] = "value";
+ char new_value[] = "new value";
+ symbol sym = NULL;
+
+ printf("test_call_back\n");
+ table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
+ assert_true(table != NULL, "table == NULL");
+
+ /* add */
+ symbol_table_set_value_call_back(table, call_back_function_set);
+ sym = symbol_lookup(table, name, 1);
+ symbol_set_value(sym, value);
+
+ /* change */
+ symbol_table_set_value_call_back(table, call_back_function_change);
+ sym = symbol_lookup(table, name, 1);
+ symbol_set_value(sym, new_value);
+
+ /* delete */
+ symbol_table_set_value_call_back(table, call_back_function_unset);
+ symbol_unset_value(sym);
+
+ while ((symbol_table_ream(table, 1)) != NULL)
+ {
+ }
+}
+
+void call_back_function_set(symbol sym, void* value)
+{
+ assert_true(symbol_get_value(sym) != NULL && value == NULL,
+ "symbol_get_value(sym) == NULL || value != NULL");
+}
+
+void call_back_function_change(symbol sym, void* value)
+{
+ assert_true(symbol_get_value(sym) != NULL && value != NULL,
+ "symbol_get_value(sym) == NULL || value == NULL");
+}
+
+
+void call_back_function_unset(symbol sym, void* value)
+{
+ assert_true(symbol_get_value(sym) == NULL && value != NULL,
+ "symbol_get_value(sym) != NULL || value == NULL");
+}
+
+void
+test_ref(void)
+{
+ symbol_table table = NULL;
+ char name[] = "name";
+ char value[] = "value";
+ symbol sym = NULL;
+ symbol_ref ref = NULL;
+ symbol_ref ref1 = NULL;
+ symbol_ref ref2 = NULL;
+ struct symbol_ref walk;
+ const int num_refs = 2;
+ long int itag = 0;
+
+ printf("test_ref\n");
+ table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
+
+ /* add */
+ sym = symbol_lookup(table, name, 1);
+ symbol_set_value(sym, value);
+
+ /* create references, in reverse order so that walk in order */
+ ref2 = symbol_set_ref(NULL, sym);
+ assert_true(ref2 != NULL, "ref2 == NULL");
+ sym_ref_set_i_tag(ref2, 2);
+ assert_true(sym_ref_i_tag(ref2) == 2, "sym_ref_i_tag(ref2) != 2");
+
+ ref1 = symbol_set_ref(NULL, sym);
+ assert_true(ref1 != NULL, "ref1 == NULL");
+ sym_ref_set_i_tag(ref1, 1);
+ assert_true(sym_ref_i_tag(ref1) == 1, "sym_ref_i_tag(ref1) != 1");
+
+ /* walk references */
+ itag = 1;
+ symbol_ref_walk_start(sym, &walk) ;
+ assert_true(sym->ref_list == &walk, "sym->ref_list != &walk");
+ assert_true(walk.next == ref1, "walk.next != ref1");
+ assert_true(ref1->next == ref2, "ref1->next != ref2");
+ assert_true(ref2->next == NULL, "ref2->next != NULL");
+
+ while ((ref = symbol_ref_walk_step(&walk)) != NULL)
+ {
+ assert_true(sym_ref_i_tag(ref) == itag, "sym_ref_i_tag(ref) != itag");
+ ++itag;
+ }
+ assert_true(itag == num_refs + 1, "itag != num_refs + 1");
+
+ symbol_ref_walk_end(&walk);
+
+ /* clean up */
+ symbol_unset_ref(ref1, 1);
+ symbol_unset_ref(ref2, 1);
+
+ while ((symbol_table_ream(table, 1)) != NULL)
+ {
+ }
+}
+
+void
+test_ref_heavy(void)
+{
+ symbol_table table = NULL;
+ char name[] = "name";
+ char value[] = "value";
+ symbol sym = NULL;
+ symbol_ref ref = NULL;
+ struct symbol_ref walk;
+ const long int num_refs = 100000;
+ long int itag = 0;
+
+ printf("test_ref_heavy\n");
+ table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
+
+ /* add */
+ sym = symbol_lookup(table, name, 1);
+ symbol_set_value(sym, value);
+
+ /* create references, in reverse order so that walk in order */
+ for (itag = num_refs; itag > 0; --itag)
+ {
+ ref = symbol_set_ref(NULL, sym);
+ assert_true(ref != NULL, "ref == NULL");
+ sym_ref_set_i_tag(ref, itag);
+ assert_true(sym_ref_i_tag(ref) == itag, "sym_ref_i_tag(ref) != itag");
+ }
+
+ /* walk references */
+ itag = 1;
+ symbol_ref_walk_start(sym, &walk) ;
+ assert_true(sym->ref_list == &walk, "sym->ref_list != &walk");
+
+ while ((ref = symbol_ref_walk_step(&walk)) != NULL)
+ {
+ assert_true(sym_ref_i_tag(ref) == itag, "sym_ref_i_tag(ref) != itag");
+ ++itag;
+ symbol_unset_ref(ref, 1);
+ }
+ assert_true(itag == num_refs + 1, "itag != num_refs + 1");
+
+ symbol_ref_walk_end(&walk);
+
+ while ((symbol_table_ream(table, 1)) != NULL)
+ {
+ }
+}
+
+
+/*
+ *
+ * TODO
+ *
+
+symbol_table_set_parent
+symbol_table_get_parent
+symbol_hash_string
+symbol_hash_bytes
+symbol_table_set_value_call_back
+symbol_table_free
+symbol_unset_value
+symbol_select_cmp
+symbol_sort_cmp
+symbol_table_extract
+symbol_sort_cmp
+symbol_get_name_len
+symbol_get_table
+symbol_zero_ref
+symbol_dec_ref
+symbol_init_ref
+symbol_set_ref
+symbol_unset_ref
+symbol_unset_ref_free
+symbol_unset_ref_keep
+sym_ref_symbol
+sym_ref_value
+sym_ref_name
+sym_ref_name_len
+sym_ref_parent
+sym_ref_p_tag
+sym_ref_u_tag
+sym_ref_i_tag
+sym_ref_set_parent
+sym_ref_set_p_tag
+sym_ref_set_i_tag
+ */
diff --git a/tests/test-vector.c b/tests/test-vector.c
new file mode 100644
index 00000000..808a7666
--- /dev/null
+++ b/tests/test-vector.c
@@ -0,0 +1,1377 @@
+#include <zebra.h>
+#include <vector.h>
+
+/* Vector torture tests
+ *
+ */
+
+/* prototypes */
+void assert_true(int result, const char * message);
+int main(int argc, char **argv);
+void test_vector_init(void);
+void test_vector_set_index(void);
+void test_vector_lookup(void);
+void test_vector_lookup_ensure(void);
+void test_vector_unset(void);
+void test_vector_set_index_null(void);
+void test_vector_copy(void);
+void test_vector_set(void);
+void test_vector_growth(void);
+
+/* GMCH interface */
+void test_vector_init_new(void);
+void test_vector_set_item(void);
+void test_vector_insert_item(void);
+void test_vector_insert_item_here(void);
+void test_vector_delete_item(void);
+void do_test_insert(const int rider);
+int sort_cmp(const void* const* a, const void* const* b);
+void test_vector_sort(void);
+void test_vector_bsearch(void);
+void test_vector_move_item_here(void);
+void do_test_move_item_here(const int rider, vector_index ins, vector_index src,
+ char strings[][10], const vector_index len) ;
+void test_vector_part_reverse(void);
+void test_vector_copy_here(void);
+void test_vector_move_here(void);
+void test_vector_copy_append(void);
+void test_vector_move_append(void);
+void test_vector_insert(void);
+void test_vector_delete(void);
+void test_vector_discard(void);
+void test_vector_sak(void);
+
+struct thread_master *master;
+
+/* objects to put in the vectors */
+static char s0[5];
+static char s1000[5];
+static char s2000[5];
+
+void
+assert_true(int result, const char * message)
+{
+ if (!result)
+ {
+ printf("Assert failed: %s\n", message);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ strcpy(s0, "0");
+ strcpy(s1000, "1000");
+ strcpy(s2000, "2000");
+
+ test_vector_init();
+ test_vector_set_index();
+ test_vector_lookup();
+ test_vector_lookup_ensure();
+ test_vector_unset();
+ test_vector_set_index_null();
+ test_vector_copy();
+ test_vector_set();
+ test_vector_growth();
+
+ /* GMCH interface */
+ test_vector_init_new();
+ test_vector_set_item();
+ test_vector_insert_item();
+ test_vector_insert_item_here();
+ test_vector_delete_item();
+ test_vector_sort();
+ test_vector_bsearch();
+ test_vector_move_item_here();
+ test_vector_part_reverse();
+ test_vector_copy_here();
+ test_vector_move_here();
+ test_vector_copy_append();
+ test_vector_move_append();
+ test_vector_insert();
+ test_vector_delete();
+ test_vector_discard();
+ test_vector_sak();
+
+ return 0;
+}
+
+void
+test_vector_init(void)
+{
+ vector v = NULL;
+
+ printf("test_vector_init\n");
+ v = vector_init(10);
+
+ assert_true(v != NULL,
+ "v == NULL");
+ assert_true(vector_count(v) == 0,
+ "vector_count != 0");
+ assert_true(vector_active(v) == 0,
+ "vector_active != 0");
+
+ vector_free(v);
+}
+
+void
+test_vector_set_index(void)
+{
+ vector v = NULL;
+
+ printf("test_vector_set_index\n");
+ v = vector_init(0);
+
+ vector_set_index(v, 1000, s1000);
+ assert_true(vector_count(v) == 1,
+ "vector_count != 1");
+ assert_true(vector_active(v) == 1001,
+ "vector_active != 1001");
+ assert_true(vector_slot(v,1000) == s1000,
+ "vector_slot != 1000");
+
+ vector_free(v);
+}
+
+void
+test_vector_lookup(void)
+{
+ vector v = NULL;
+
+ printf("test_vector_lookup\n");
+ v = vector_init(0);
+
+ vector_set_index(v, 1000, s1000);
+
+ assert_true(vector_lookup(v,1000) == s1000,
+ "vector_lookup != 1000");
+ assert_true(vector_lookup(v,1001) == NULL,
+ "vector_lookup != NULL");
+
+ vector_free(v);
+}
+
+void
+test_vector_lookup_ensure(void)
+{
+ vector v = NULL;
+
+ printf("test_vector_lookup_ensure\n");
+ v = vector_init(0);
+
+ vector_set_index(v, 1000, s1000);
+
+ assert_true(vector_lookup_ensure(v,1000) == s1000,
+ "vector_lookup_ensure != 1000");
+ assert_true(vector_lookup_ensure(v,1001) == NULL,
+ "vector_lookup_ensure != NULL");
+
+ vector_free(v);
+}
+
+void
+test_vector_unset(void)
+{
+ vector v = NULL;
+
+ printf("test_vector_unset\n");
+ v = vector_init(0);
+
+ vector_set_index(v, 1000, s1000);
+ vector_set_index(v, 2000, s2000);
+ assert_true(vector_count(v) == 2,
+ "vector_count != 2");
+ assert_true(vector_active(v) == 2001,
+ "vector_active != 2001");
+
+ vector_unset(v, 2000);
+ assert_true(vector_count(v) == 1,
+ "vector_count != 1");
+ assert_true(vector_active(v) == 1001,
+ "vector_active != 1001");
+
+ vector_free(v);
+}
+
+void
+test_vector_set_index_null(void)
+{
+ vector v = NULL;
+
+ printf("test_vector_set_index_null\n");
+ v = vector_init(0);
+
+ vector_set_index(v, 1000, s1000);
+ vector_set_index(v, 2000, s2000);
+ assert_true(vector_count(v) == 2,
+ "vector_count != 2");
+ assert_true(vector_active(v) == 2001,
+ "vector_active != 2001");
+
+ /* storing a NULL is not the same as vector_unset() */
+ vector_set_index(v, 2000, NULL);
+ assert_true(vector_count(v) == 1,
+ "vector_count != 1");
+ assert_true(vector_active(v) == 2001,
+ "vector_active != 2001");
+
+ /* GMCH change in semantics */
+ vector_unset(v, 1000);
+ assert_true(vector_count(v) == 0,
+ "vector_count != 0");
+ assert_true(vector_active(v) == 2001,
+ "vector_active != 2001 ignore post GMCH");
+ assert_true(vector_active(v) == 0,
+ "vector_active != 0 ignore pre GMCH");
+
+ vector_free(v);
+}
+
+void
+test_vector_copy(void)
+{
+ vector v1 = NULL;
+ vector v2 = NULL;
+ int i;
+ const int len = 100;
+ char buf[10];
+
+ printf("test_vector_copy\n");
+
+ /* to help debug objects are strings of their index */
+ v1 = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d", i);
+ vector_set_index(v1, i, strdup(buf));
+ }
+
+ v2 = vector_copy(v1);
+ assert_true(v2 != NULL, "v2 == NULL");
+ assert_true(v1 != v2, "v1 == v2");
+
+ /* check contents are the same */
+ for (i = 0; i < len; ++i)
+ {
+ assert_true(vector_slot(v1, i) == vector_slot(v2, i), "v1 != v2");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_slot(v1, i));
+ }
+
+ vector_free(v1);
+ vector_free(v2);
+}
+
+void
+test_vector_set(void)
+{
+ vector v = NULL;
+ int i;
+ const int len = 1000;
+ char buf[10];
+
+ printf("test_vector_set\n");
+
+ /* to help debug objects are strings of their index */
+ v = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d", i);
+ vector_set_index(v, i, strdup(buf));
+ }
+
+ assert_true(vector_count(v) == (unsigned)len,
+ "vector_count != 1000");
+ assert_true(vector_active(v) == (unsigned)len,
+ "vector_active != 1000");
+
+ vector_set(v, s1000);
+ assert_true(vector_count(v) == len + 1,
+ "vector_count != 1001");
+ assert_true(vector_active(v) == len + 1,
+ "vector_active != 1001");
+ assert_true(vector_slot(v,1000) == s1000,
+ "vector_slot != 1000");
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_slot(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_growth(void)
+{
+ vector v = NULL;
+ int i;
+ const int len = 150000;
+ char buf[10];
+
+ printf("test_vector_growth\n");
+
+ /* to help debug objects are strings of their index */
+ v = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d", i);
+ vector_set_index(v, i, strdup(buf));
+ }
+
+ assert_true(vector_count(v) == (unsigned)len,
+ "vector_count != len");
+ assert_true(vector_active(v) == (unsigned)len,
+ "vector_active != len");
+
+ /* check contents are correct */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d", i);
+ assert_true(strcmp(vector_slot(v, i), buf) == 0, "vector_slot(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_slot(v, i));
+ }
+
+ vector_free(v);
+}
+
+
+/* post GMCH interface */
+
+void
+test_vector_init_new(void)
+{
+ vector v = NULL;
+ void * item = NULL;
+
+ printf("test_vector_init_new\n");
+
+ v = vector_init_new(v, 0);
+ assert_true(v != NULL, "v == NULL");
+ assert_true(vector_is_empty(v), "!vector_is_empty(v)");
+ assert_true(vector_end(v) == 0, "vector_end(v) != 0");
+
+ vector_push_item(v, s0);
+ assert_true(vector_end(v) == 1, "vector_end(v) != 1");
+
+ item = vector_pop_item(v);
+ assert_true(item == s0, "item != s0");
+ assert_true(vector_is_empty(v), "!vector_is_empty(v)");
+ assert_true(vector_end(v) == 0, "vector_end(v) != 0");
+
+ vector_free(v);
+}
+
+void
+test_vector_set_item(void)
+{
+ vector v = NULL;
+ void * item = NULL;
+
+ printf("test_vector_set_item\n");
+
+ v = vector_init_new(v, 0);
+ vector_set_item(v, 0, s0);
+ vector_set_item(v, 1000, s1000);
+ vector_set_item(v, 2000, s2000);
+ assert_true(vector_end(v) == 2001, "vector_end(v) != 2001");
+
+ item = vector_get_item(v, 0);
+ assert_true(item == s0, "item != s0");
+ item = vector_get_item(v, 1000);
+ assert_true(item == s1000, "item != s1000");
+ item = vector_get_item(v, 2000);
+ assert_true(item == s2000, "item != s2000");
+
+ item = vector_get_first_item(v);
+ assert_true(item == s0, "item != s0");
+
+ item = vector_get_last_item(v);
+ assert_true(item == s2000, "item != s2000");
+
+ vector_free(v);
+}
+void
+test_vector_insert_item(void)
+{
+ do_test_insert(2);
+}
+
+void
+test_vector_insert_item_here(void)
+{
+ do_test_insert(-1);
+ do_test_insert(0);
+ do_test_insert(1);
+}
+
+void
+test_vector_delete_item(void)
+{
+ do_test_insert(3);
+}
+
+void
+do_test_insert(const int rider)
+{
+ vector v = NULL;
+ const vector_index len = 100;
+ const vector_index ins = 50;
+ vector_index i;
+ char buf[10];
+ vector_index check_end = len + 1;
+ vector_index check_ins = ins;
+ int check_shift = 1;
+
+ switch(rider)
+ {
+ case -1:
+ printf("test_vector_insert_here before\n");
+ break;
+ case 0:
+ printf("test_vector_insert_here at\n");
+ check_shift = 0;
+ check_end = len;
+ break;
+ case 1:
+ printf("test_vector_insert_here after\n");
+ check_ins++;
+ break;
+ case 2:
+ printf("test_vector_insert\n");
+ break;
+ case 3:
+ printf("test_vector_delete\n");
+ check_shift = -1;
+ check_end = len - 1;
+ break;
+ }
+
+ v = vector_init_new(v, 0);
+
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%d", i);
+ vector_set_item(v, i, strdup(buf));
+ }
+
+ switch(rider)
+ {
+ case -1:
+ case 0:
+ case 1:
+ vector_insert_item_here(v, ins, rider, strdup(s1000));
+ break;
+ case 2:
+ vector_insert_item(v, ins, strdup(s1000));
+ break;
+ case 3:
+ free(vector_delete_item(v, ins));
+ break;
+ }
+
+ assert_true(vector_end(v) == check_end, "vector_end(v) != check_end");
+
+ /* check contents are correct */
+ for (i = 0; i < check_ins; ++i)
+ {
+ sprintf(buf, "%d", i);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "vector_get_item(v, i) != buf");
+ }
+
+ if (rider != 3)
+ {
+ assert_true(strcmp(vector_get_item(v, check_ins), s1000) == 0,
+ "vector_get_item(v, check_ins) != s1000");
+ }
+
+ for (i = check_ins + 1; i < check_end; ++i)
+ {
+ sprintf(buf, "%d", i - check_shift);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0, "vector_get_item(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < vector_end(v); ++i)
+ {
+ free(vector_slot(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_sort(void)
+{
+ vector v = NULL;
+ int i;
+ const int len = 100;
+ char buf[10];
+
+ printf("test_vector_sort\n");
+
+ v = vector_init(0);
+
+ vector_sort(v, sort_cmp); /* null sort */
+
+ /* initialize backwards, using width so that lexical = numerical order */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%9d", i);
+ vector_set_index(v, len - i - 1, strdup(buf));
+ }
+
+ vector_sort(v, sort_cmp);
+
+ /* check contents are correct */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%9d", i);
+ assert_true(strcmp(vector_slot(v, i), buf) == 0, "vector_slot(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_slot(v, i));
+ }
+
+ vector_free(v);
+}
+
+int
+sort_cmp(const void* const* a, const void* const* b)
+{
+ return strcmp(*a, *b);
+}
+
+void
+test_vector_bsearch(void)
+{
+ vector v = NULL;
+ int i;
+ const int len = 2000;
+ char buf[20];
+ char target[20];
+ vector_index target_index = 0;
+ int result;
+ vector_index index;
+
+ printf("test_vector_bsearch\n");
+
+ sprintf(target, "%9u", target_index);
+ v = vector_init(0);
+
+ index = vector_bsearch(v, sort_cmp, target, &result); /* null search */
+ assert_true(index == 0, "index != 0");
+ assert_true(result == -1, "result != -1");
+
+ /* initialize, using width so that lexical = numerical order */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%9d", i);
+ vector_set_index(v, i, strdup(buf));
+ }
+
+ for (target_index = 0; target_index < (unsigned)len; ++target_index)
+ {
+ sprintf(target, "%9u", target_index);
+ index = vector_bsearch(v, sort_cmp, target, &result);
+ assert_true(index == target_index, "index != target_index");
+ assert_true(result == 0, "result != 0");
+ assert_true(strcmp(vector_get_item(v, index), target) == 0,
+ "strcmp(vector_get_item(v, index), target)) != 0");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_slot(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_move_item_here(void)
+{
+ const uint n = 20 ;
+ const uint l = n - 1 ;
+ char strings[n][10] ;
+ uint i ;
+
+ for (i = 0 ; i < n ; ++i)
+ sprintf(strings[i], "item=%2u", i) ;
+
+ do_test_move_item_here(-1, 8, 16, strings, n);
+ do_test_move_item_here( 0, 8, 16, strings, n);
+ do_test_move_item_here(+1, 8, 16, strings, n);
+
+ do_test_move_item_here(-1, 16, 8, strings, n);
+ do_test_move_item_here( 0, 16, 8, strings, n);
+ do_test_move_item_here(+1, 16, 8, strings, n);
+
+ do_test_move_item_here(-1, 9, 9, strings, n);
+ do_test_move_item_here( 0, 9, 9, strings, n);
+ do_test_move_item_here(+1, 9, 9, strings, n);
+
+ do_test_move_item_here(-1, 10, 9, strings, n);
+ do_test_move_item_here( 0, 10, 9, strings, n);
+ do_test_move_item_here(+1, 10, 9, strings, n);
+
+ do_test_move_item_here(-1, 9, 10, strings, n);
+ do_test_move_item_here( 0, 9, 10, strings, n);
+ do_test_move_item_here(+1, 9, 10, strings, n);
+
+ do_test_move_item_here(-1, 11, 9, strings, n);
+ do_test_move_item_here( 0, 11, 9, strings, n);
+ do_test_move_item_here(+1, 11, 9, strings, n);
+
+ do_test_move_item_here(-1, 9, 11, strings, n);
+ do_test_move_item_here( 0, 9, 11, strings, n);
+ do_test_move_item_here(+1, 9, 11, strings, n);
+
+ do_test_move_item_here(-1, 0, 10, strings, n);
+ do_test_move_item_here( 0, 0, 10, strings, n);
+ do_test_move_item_here(+1, 0, 10, strings, n);
+
+ do_test_move_item_here(-1, 10, 0, strings, n);
+ do_test_move_item_here( 0, 10, 0, strings, n);
+ do_test_move_item_here(+1, 10, 0, strings, n);
+
+ do_test_move_item_here(-1, 0, l, strings, n);
+ do_test_move_item_here( 0, 0, l, strings, n);
+ do_test_move_item_here(+1, 0, l, strings, n);
+
+ do_test_move_item_here(-1, l, 0, strings, n);
+ do_test_move_item_here( 0, l, 0, strings, n);
+ do_test_move_item_here(+1, l, 0, strings, n);
+
+ do_test_move_item_here(-1, 10, l, strings, n);
+ do_test_move_item_here( 0, 10, l, strings, n);
+ do_test_move_item_here(+1, 10, l, strings, n);
+
+ do_test_move_item_here(-1, l, 10, strings, n);
+ do_test_move_item_here( 0, l, 10, strings, n);
+ do_test_move_item_here(+1, l, 10, strings, n);
+
+ do_test_move_item_here(-1, 0, 0, strings, n);
+ do_test_move_item_here( 0, 0, 0, strings, n);
+ do_test_move_item_here(+1, 0, 0, strings, n);
+
+ do_test_move_item_here(-1, 1, 1, strings, n);
+ do_test_move_item_here( 0, 1, 1, strings, n);
+ do_test_move_item_here(+1, 1, 1, strings, n);
+
+ do_test_move_item_here(-1, 0, 1, strings, n);
+ do_test_move_item_here( 0, 0, 1, strings, n);
+ do_test_move_item_here(+1, 0, 1, strings, n);
+
+ do_test_move_item_here(-1, 1, 0, strings, n);
+ do_test_move_item_here( 0, 1, 0, strings, n);
+ do_test_move_item_here(+1, 1, 0, strings, n);
+
+ do_test_move_item_here(-1, 0, 2, strings, n);
+ do_test_move_item_here( 0, 0, 2, strings, n);
+ do_test_move_item_here(+1, 0, 2, strings, n);
+
+ do_test_move_item_here(-1, 2, 0, strings, n);
+ do_test_move_item_here( 0, 2, 0, strings, n);
+ do_test_move_item_here(+1, 2, 0, strings, n);
+
+ do_test_move_item_here(-1, l, l, strings, n);
+ do_test_move_item_here( 0, l, l, strings, n);
+ do_test_move_item_here(+1, l, l, strings, n);
+
+ do_test_move_item_here(-1,l-1, l, strings, n);
+ do_test_move_item_here( 0,l-1, l, strings, n);
+ do_test_move_item_here(+1,l-1, l, strings, n);
+
+ do_test_move_item_here(-1, l,l-1, strings, n);
+ do_test_move_item_here( 0, l,l-1, strings, n);
+ do_test_move_item_here(+1, l,l-1, strings, n);
+}
+
+void
+do_test_move_item_here(const int rider, vector_index ins, vector_index src,
+ char strings[][10], const vector_index len)
+{
+ vector v = NULL;
+ vector_index i, e, check_end ;
+ vector_index hi, lo ;
+ char* expect[len] ;
+
+ p_vector_item dest_item = NULL;
+
+ switch(rider)
+ {
+ case -1:
+ printf("test_vector_move_here before dst=%2d, src=%2d\n", src, ins);
+ break;
+
+ case 0:
+ printf("test_vector_move_here at dst=%2d, src=%2d\n", src, ins);
+ --check_end ;
+ break;
+
+ case 1:
+ printf("test_vector_move_here after dst=%2d, src=%2d\n", src, ins);
+ break;
+ } ;
+
+ /* Build the test vector and perform action */
+
+ v = vector_init_new(v, 0);
+
+ for (i = 0; i < len; ++i)
+ vector_set_item(v, i, strdup(strings[i]));
+
+ dest_item = vector_get_item(v, ins); /* item to free if rider == 0 */
+
+ vector_move_item_here(v, ins, rider, src);
+
+ /* Build the expected result. */
+
+ if (ins <= src)
+ {
+ lo = ins ;
+ hi = src ;
+ }
+ else
+ {
+ lo = src ;
+ hi = ins ;
+ } ;
+
+ check_end = (rider != 0) ? len : len - 1 ;
+ i = 0 ;
+ e = 0 ;
+
+ while (i < lo)
+ expect[i++] = strings[e++] ;
+
+ if (lo == hi)
+ {
+ /* Special case -- do nothing if rider != 0
+ * drop entry if rider == 0
+ */
+ if (rider == 0)
+ ++e ;
+ }
+ else if (lo == (hi - 1))
+ {
+ /* Special case -- ins and src next to each other !
+ *
+ * If rider != 0 -- insert ins and src in the required order
+ * If rider == 0 -- insert just src
+ */
+ if (rider < 0)
+ {
+ expect[i++] = strings[src] ;
+ expect[i++] = strings[ins] ;
+ }
+ else if (rider == 0)
+ {
+ expect[i++] = strings[src] ;
+ }
+ else
+ {
+ expect[i++] = strings[ins] ;
+ expect[i++] = strings[src] ;
+ } ;
+ e += 2 ;
+ }
+ else
+ {
+ /* Now we know that ins and src are separated by at least 1 entry.
+ */
+ uint be ;
+
+ be = hi - 1 ;
+
+ if (ins < src)
+ {
+ /* At insertion point, so insert ins and src in the required order
+ * or insert juist the src.
+ */
+ if (rider < 0)
+ {
+ expect[i++] = strings[src] ;
+ expect[i++] = strings[ins] ;
+ ++be ;
+ }
+ else if (rider == 0)
+ {
+ expect[i++] = strings[src] ;
+ }
+ else
+ {
+ expect[i++] = strings[ins] ;
+ expect[i++] = strings[src] ;
+ ++be ;
+ } ;
+
+ ++be ;
+ } ;
+
+ e += 1 ;
+
+ while (i < be)
+ expect[i++] = strings[e++] ;
+
+ if (ins > src)
+ {
+ /* At insertion point, so insert ins and src in the required order
+ * or insert juist the src.
+ */
+ if (rider < 0)
+ {
+ expect[i++] = strings[src] ;
+ expect[i++] = strings[ins] ;
+ }
+ else if (rider == 0)
+ {
+ expect[i++] = strings[src] ;
+ }
+ else
+ {
+ expect[i++] = strings[ins] ;
+ expect[i++] = strings[src] ;
+ } ;
+ } ;
+
+ e = hi + 1 ;
+ } ;
+
+ while (i < check_end)
+ expect[i++] = strings[e++] ;
+
+ /* check contents are correct */
+ assert_true(vector_end(v) == check_end, "vector_end(v) != check_end");
+
+ for (i = 0 ; i < check_end ; ++i)
+ {
+ if (strcmp(vector_get_item(v, i), expect[i]) != 0)
+ {
+ assert_true(0, "vector_get_item(v, i) != expected") ;
+ break ;
+ } ;
+ } ;
+
+ /* free contents */
+ for (i = 0; i < vector_end(v); ++i)
+ free(vector_slot(v, i));
+
+ if (rider == 0)
+ free(dest_item);
+
+ vector_free(v);
+}
+
+void
+test_vector_part_reverse(void)
+{
+ vector v = NULL;
+ const vector_index len = 100;
+ const vector_index rstart = 50;
+ const vector_index rstop = 70;
+ vector_index i;
+ char buf[10];
+
+ printf("test_vector_part_reverse\n");
+
+ v = vector_init_new(v, 0);
+
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_item(v, i, strdup(buf));
+ }
+
+ vector_part_reverse(v, rstart, rstop - rstart);
+ assert_true(vector_end(v) == len, "vector_end(v) != len");
+
+ /* check contents are correct */
+
+ /* before reversed section */
+ for (i = 0; i < rstart - 1; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "before reversed vector_get_item(v, i) != buf");
+ }
+
+ /* reversed section */
+ for (i = rstart; i < rstop; ++i)
+ {
+ sprintf(buf, "%u", rstop - (i - rstart + 1));
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "reversed vector_get_item(v, i) != buf");
+ }
+
+ /* after reversed section */
+ for (i = rstop; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "after reversed vector_get_item(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < vector_end(v); ++i)
+ {
+ free(vector_slot(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_copy_here(void)
+{
+ vector v1 = NULL;
+ vector v2 = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ char buf[10];
+
+ printf("test_vector_copy_here\n");
+
+ /* to help debug objects are strings of their index */
+ v1 = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v1, i, strdup(buf));
+ }
+
+ v2 = vector_copy_here(v2, v1);
+ assert_true(v2 != NULL, "v2 == NULL");
+ assert_true(v1 != v2, "v1 == v2");
+
+ /* check contents are the same */
+ for (i = 0; i < len; ++i)
+ {
+ assert_true(vector_get_item(v1, i) == vector_get_item(v2, i), "v1 != v2");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_get_item(v1, i));
+ }
+
+ vector_free(v1);
+ vector_free(v2);
+}
+
+void
+test_vector_move_here(void)
+{
+ vector v1 = NULL;
+ vector v2 = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ char buf[10];
+
+ printf("test_vector_move_here\n");
+
+ /* to help debug objects are strings of their index */
+ v1 = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v1, i, strdup(buf));
+ }
+
+ v2 = vector_move_here(v2, v1);
+ assert_true(v2 != NULL, "v2 == NULL");
+ assert_true(v1 != v2, "v1 == v2");
+ assert_true(vector_end(v1) == 0, "vector_end(v1) != 0");
+
+ /* check contents are the same */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v2, i), buf) == 0,
+ "vector_get_item(v2, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_get_item(v2, i));
+ }
+
+ vector_free(v1);
+ vector_free(v2);
+}
+
+void
+test_vector_copy_append(void)
+{
+ vector v1 = NULL;
+ vector v2 = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ char buf[10];
+
+ printf("test_vector_copy_append\n");
+
+ /* to help debug objects are strings of their index */
+ v2 = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v2, i, strdup(buf));
+ }
+
+ v1 = vector_init(0);
+ for (i = len; i < 2 * len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v1, i - len, strdup(buf));
+ }
+
+ v2 = vector_copy_append(v2, v1);
+ assert_true(v2 != NULL, "v2 == NULL");
+ assert_true(v1 != v2, "v1 == v2");
+ assert_true(vector_end(v2) == 2 * len, "vector_end(v2) != 2 * len");
+
+ /* check contents */
+ for (i = 0; i < 2 * len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v2, i), buf) == 0,
+ "vector_get_item(v2, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < 2 * len; ++i)
+ {
+ free(vector_get_item(v2, i));
+ }
+
+ vector_free(v1);
+ vector_free(v2);
+}
+
+void
+test_vector_move_append(void)
+{
+ vector v1 = NULL;
+ vector v2 = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ char buf[10];
+
+ printf("test_vector_move_append\n");
+
+ /* to help debug objects are strings of their index */
+ v2 = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v2, i, strdup(buf));
+ }
+
+ v1 = vector_init(0);
+ for (i = len; i < 2 * len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v1, i - len, strdup(buf));
+ }
+
+ v2 = vector_move_append(v2, v1);
+ assert_true(v2 != NULL, "v2 == NULL");
+ assert_true(v1 != v2, "v1 == v2");
+ assert_true(vector_end(v2) == 2 * len, "vector_end(v2) != 2 * len");
+ assert_true(vector_end(v1) == 0, "vector_end(v1) != 0");
+
+ /* check contents */
+ for (i = 0; i < 2 * len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v2, i), buf) == 0,
+ "vector_get_item(v2, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < 2 * len; ++i)
+ {
+ free(vector_get_item(v2, i));
+ }
+
+ vector_free(v1);
+ vector_free(v2);
+}
+
+void
+test_vector_insert(void)
+{
+ vector v = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ const vector_index istart = 50;
+ const vector_index istop = 70;
+ char buf[10];
+
+ printf("test_vector_insert\n");
+
+ /* to help debug objects are strings of their index */
+
+ v = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v, i, strdup(buf));
+ }
+
+ vector_insert(v, istart, istop - istart);
+ assert_true(vector_end(v) == len + (istop - istart),
+ "vector_end(v) != len + (istop - istart)");
+
+ /* check contents */
+ for (i = 0; i < istart; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "vector_get_item(v, i) != buf");
+ }
+
+ for (i = istart + 1; i < istop; ++i)
+ {
+ assert_true(vector_get_item(v, i) == NULL,
+ "vector_get_item(v, i) != NULL");
+ }
+
+ for (i = istop; i < len + (istop - istart); ++i)
+ {
+ sprintf(buf, "%u", i - (istop - istart));
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "vector_get_item(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < len + (istop - istart); ++i)
+ {
+ free(vector_get_item(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_delete(void)
+{
+ vector v = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ const vector_index dstart = 50;
+ const vector_index dstop = 70;
+ char buf[10];
+
+ printf("test_vector_delete\n");
+
+ /* to help debug objects are strings of their index */
+
+ v = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ if (i < dstart || i >= dstop)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v, i, strdup(buf));
+ }
+ else
+ {
+ vector_set_index(v, i, s0);
+ }
+ }
+
+ vector_delete(v, dstart, dstop - dstart);
+ assert_true(vector_end(v) == len - (dstop - dstart),
+ "vector_end(v) != len - (dstop - dstart)");
+
+ /* check contents */
+ for (i = 0; i < dstart; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "vector_get_item(v, i) != buf");
+ }
+
+ for (i = dstart; i < len - (dstop - dstart); ++i)
+ {
+ sprintf(buf, "%u", i + (dstop - dstart));
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "vector_get_item(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < len - (dstop - dstart); ++i)
+ {
+ free(vector_get_item(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_discard(void)
+{
+ vector v = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ const vector_index dstart = 50;
+ char buf[10];
+
+ printf("test_vector_discard\n");
+
+ /* to help debug objects are strings of their index */
+
+ v = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ if (i < dstart)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v, i, strdup(buf));
+ }
+ else
+ {
+ vector_set_index(v, i, s0);
+ }
+ }
+
+ vector_discard(v, dstart);
+ assert_true(vector_end(v) == dstart,
+ "vector_end(v) != dstart");
+
+ /* check contents */
+ for (i = 0; i < dstart; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v, i), buf) == 0,
+ "vector_get_item(v, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < dstart; ++i)
+ {
+ free(vector_get_item(v, i));
+ }
+
+ vector_free(v);
+}
+
+void
+test_vector_sak(void)
+{
+ vector v1 = NULL;
+ vector v2 = NULL;
+ vector v3 = NULL;
+ vector_index i;
+ const vector_index len = 100;
+ const vector_index sstart = 60;
+ const vector_index sstop = 70;
+ const vector_index dstart = 40;
+ const vector_index dstop = 50;
+ char buf[10];
+
+ printf("test_vector_sak\n");
+
+ /* to help debug objects are strings of their index */
+
+ v2 = vector_init(0);
+ v3 = vector_init(0);
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ vector_set_index(v2, i, strdup(buf));
+ vector_set_index(v3, i, strdup(buf));
+ }
+
+ v1 = vector_sak(1, v1, v2, dstart, dstop - dstart,
+ v3, sstart, sstop - sstart, 0);
+ assert_true(v1 != NULL, "v1 == NULL");
+
+ assert_true(vector_end(v1) == (dstop - dstart),
+ "vector_end(v1) != (dstop - dstart)");
+ assert_true(vector_end(v2) == len,
+ "vector_end(v2) != len");
+ assert_true(vector_end(v3) == len,
+ "vector_end(v3) != len");
+
+ /* check contents v1 */
+ for (i = 0; i < dstop - dstart; ++i)
+ {
+ sprintf(buf, "%u", i + dstart);
+ assert_true(vector_get_item(v1, i) != NULL,
+ "vector_get_item(v1, i) == NULL");
+ assert_true(strcmp(vector_get_item(v1, i), buf) == 0,
+ "vector_get_item(v1, i) != buf");
+ }
+
+ /* check contents v2 */
+ for (i = 0; i < dstart; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v2, i), buf) == 0,
+ "vector_get_item(v2, i) != buf");
+ }
+ for (i = dstart; i < dstop; ++i)
+ {
+ sprintf(buf, "%u", i - dstart + sstart);
+ assert_true(strcmp(vector_get_item(v2, i), buf) == 0,
+ "vector_get_item(v2, i) != buf");
+ }
+ for (i = dstop; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v2, i), buf) == 0,
+ "vector_get_item(v2, i) != buf");
+ }
+
+ /* check contents v3 */
+ for (i = 0; i < len; ++i)
+ {
+ sprintf(buf, "%u", i);
+ assert_true(strcmp(vector_get_item(v3, i), buf) == 0,
+ "vector_get_item(v3, i) != buf");
+ }
+
+ /* free contents */
+ for (i = 0; i < len; ++i)
+ {
+ free(vector_get_item(v3, i));
+ }
+
+ vector_free(v1);
+ vector_free(v2);
+ vector_free(v3);
+}
+/*
+ * TODO
+ *
+
+vector_re_init
+vector_reset
+vector_ream
+vector_sak
+vector_chop
+vector_decant
+vector_extend_by_1
+
+*/
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 3f189adb..64c1b549 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -16,10 +16,10 @@
* 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>
+#include "zebra.h"
#include <sys/un.h>
#include <setjmp.h>
@@ -64,7 +64,7 @@ struct vtysh_client
/* We need direct access to ripd to implement vtysh_exit_ripd_only. */
static struct vtysh_client *ripd_client = NULL;
-
+
/* Using integrated config from Quagga.conf. Default is no. */
int vtysh_writeconfig_integrated = 0;
@@ -109,7 +109,7 @@ vtysh_client_config (struct vtysh_client *vclient, char *line)
vclient_close (vclient);
return CMD_SUCCESS;
}
-
+
/* Allow enough room for buffer to read more than a few pages from socket. */
bufsz = 5 * getpagesize() + 1;
buf = XMALLOC(MTYPE_TMP, bufsz);
@@ -191,7 +191,7 @@ vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp)
int ret;
char buf[1001];
int nbytes;
- int i;
+ int i;
int numnulls = 0;
if (vclient->fd < 0)
@@ -203,7 +203,7 @@ vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp)
vclient_close (vclient);
return CMD_SUCCESS;
}
-
+
while (1)
{
nbytes = read (vclient->fd, buf, sizeof(buf)-1);
@@ -222,19 +222,19 @@ vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp)
buf[nbytes] = '\0';
fputs (buf, fp);
fflush (fp);
-
- /* check for trailling \0\0\0<ret code>,
- * even if split across reads
+
+ /* check for trailling \0\0\0<ret code>,
+ * even if split across reads
* (see lib/vty.c::vtysh_read)
*/
- if (nbytes >= 4)
+ if (nbytes >= 4)
{
i = nbytes-4;
numnulls = 0;
}
else
i = 0;
-
+
while (i < nbytes && numnulls < 3)
{
if (buf[i++] == '\0')
@@ -277,20 +277,20 @@ vtysh_execute_func (const char *line, int pager)
{
int ret, cmd_stat;
u_int i;
- vector vline;
struct cmd_element *cmd;
FILE *fp = NULL;
int closepager = 0;
int tried = 0;
int saved_ret, saved_node;
- /* Split readline string up into the vector. */
- vline = cmd_make_strvec (line);
+ /* TODO: how well does vtysh_execute_func work ?? -- esp. qpthreads_enabled */
+ vty->buf = line ;
- if (vline == NULL)
- return CMD_SUCCESS;
+ saved_ret = ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
+
+ if ((ret == CMD_SUCCESS) && (cmd == NULL))
+ return ret ; /* quit if nothing to do ??? */
- saved_ret = ret = cmd_execute_command (vline, vty, &cmd, 1);
saved_node = vty->node;
/* If command doesn't succeeded in current node, try to walk up in node tree.
@@ -300,7 +300,7 @@ vtysh_execute_func (const char *line, int pager)
&& vty->node > CONFIG_NODE)
{
vty->node = node_parent(vty->node);
- ret = cmd_execute_command (vline, vty, &cmd, 1);
+ ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
tried++;
}
@@ -334,8 +334,6 @@ vtysh_execute_func (const char *line, int pager)
ret = saved_ret;
}
- cmd_free_strvec (vline);
-
cmd_stat = ret;
switch (ret)
{
@@ -383,23 +381,10 @@ vtysh_execute_func (const char *line, int pager)
if (cmd_stat)
{
line = "end";
- vline = cmd_make_strvec (line);
- if (vline == NULL)
- {
- if (pager && vtysh_pager_name && fp && closepager)
- {
- if (pclose (fp) == -1)
- {
- perror ("pclose failed for pager");
- }
- fp = NULL;
- }
- return CMD_SUCCESS;
- }
+ vty->buf = line ;
- ret = cmd_execute_command (vline, vty, &cmd, 1);
- cmd_free_strvec (vline);
+ ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
if (ret != CMD_SUCCESS_DAEMON)
break;
}
@@ -456,25 +441,18 @@ int
vtysh_config_from_file (struct vty *vty, FILE *fp)
{
int ret;
- vector vline;
struct cmd_element *cmd;
- while (fgets (vty->buf, VTY_BUFSIZ, fp))
- {
- if (vty->buf[0] == '!' || vty->buf[1] == '#')
- continue;
-
- vline = cmd_make_strvec (vty->buf);
+ /* TODO: (1) allocate buffer for vty->buf (2) what about CMD_QUEUED ?? */
- /* In case of comment line. */
- if (vline == NULL)
- continue;
+ while (fgets (vty->buf, VTY_BUFSIZ, fp))
+ {
/* Execute configuration command : this is strict match. */
- ret = cmd_execute_command_strict (vline, vty, &cmd);
+ ret = cmd_execute_command(vty, cmd_parse_strict, &cmd);
/* Try again with setting node to CONFIG_NODE. */
- if (ret != CMD_SUCCESS
+ if (ret != CMD_SUCCESS
&& ret != CMD_SUCCESS_DAEMON
&& ret != CMD_WARNING)
{
@@ -482,15 +460,15 @@ vtysh_config_from_file (struct vty *vty, FILE *fp)
{
vty->node = KEYCHAIN_NODE;
vtysh_exit_ripd_only ();
- ret = cmd_execute_command_strict (vline, vty, &cmd);
+ ret = cmd_execute_command(vty, cmd_parse_strict, &cmd);
- if (ret != CMD_SUCCESS
- && ret != CMD_SUCCESS_DAEMON
+ if (ret != CMD_SUCCESS
+ && ret != CMD_SUCCESS_DAEMON
&& ret != CMD_WARNING)
{
vtysh_exit_ripd_only ();
vty->node = CONFIG_NODE;
- ret = cmd_execute_command_strict (vline, vty, &cmd);
+ ret = cmd_execute_command(vty, cmd_parse_strict, &cmd);
}
}
else
@@ -498,9 +476,9 @@ vtysh_config_from_file (struct vty *vty, FILE *fp)
vtysh_execute ("end");
vtysh_execute ("configure terminal");
vty->node = CONFIG_NODE;
- ret = cmd_execute_command_strict (vline, vty, &cmd);
+ ret = cmd_execute_command(vty, cmd_parse_strict, &cmd);
}
- }
+ }
cmd_free_strvec (vline);
@@ -564,11 +542,11 @@ vtysh_rl_describe (void)
vline = vector_init (1);
vector_set (vline, '\0');
}
- else
+ else
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- describe = cmd_describe_command (vline, vty, &ret);
+ describe = cmd_describe_command (vline, vty->node, &ret);
fprintf (stdout,"\n");
@@ -587,7 +565,7 @@ vtysh_rl_describe (void)
rl_on_new_line ();
return 0;
break;
- }
+ }
/* Get width of command string. */
width = 0;
@@ -658,7 +636,7 @@ command_generator (const char *text, int state)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty, &complete_status);
+ matched = cmd_complete_command (vline, vty->node, &complete_status);
}
if (matched && matched[index])
@@ -952,7 +930,7 @@ DEFUNSH (VTYSH_RIPD,
{
vty->node = KEYCHAIN_NODE;
return CMD_SUCCESS;
-}
+}
DEFUNSH (VTYSH_RIPD,
key,
@@ -1047,7 +1025,7 @@ DEFUNSH (VTYSH_ALL,
}
DEFUNSH (VTYSH_ALL,
- vtysh_enable,
+ vtysh_enable,
vtysh_enable_cmd,
"enable",
"Turn on privileged mode command\n")
@@ -1057,7 +1035,7 @@ DEFUNSH (VTYSH_ALL,
}
DEFUNSH (VTYSH_ALL,
- vtysh_disable,
+ vtysh_disable,
vtysh_disable_cmd,
"disable",
"Turn off privileged mode command\n")
@@ -1303,7 +1281,7 @@ DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD,
"description .LINE",
"Interface specific description\n"
"Characters describing this interface\n")
-
+
DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD,
no_interface_desc_cmd,
"no description",
@@ -1334,16 +1312,16 @@ DEFUN (vtysh_show_memory,
unsigned int i;
int ret = CMD_SUCCESS;
char line[] = "show memory\n";
-
+
for (i = 0; i < VTYSH_INDEX_MAX; i++)
if ( vtysh_client[i].fd >= 0 )
{
- fprintf (stdout, "Memory statistics for %s:\n",
+ fprintf (stdout, "Memory statistics for %s:\n",
vtysh_client[i].name);
ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
fprintf (stdout,"\n");
}
-
+
return ret;
}
@@ -1357,16 +1335,16 @@ DEFUN (vtysh_show_logging,
unsigned int i;
int ret = CMD_SUCCESS;
char line[] = "show logging\n";
-
+
for (i = 0; i < VTYSH_INDEX_MAX; i++)
if ( vtysh_client[i].fd >= 0 )
{
- fprintf (stdout,"Logging configuration for %s:\n",
+ fprintf (stdout,"Logging configuration for %s:\n",
vtysh_client[i].name);
ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
fprintf (stdout,"\n");
}
-
+
return ret;
}
@@ -1734,7 +1712,7 @@ DEFUN (vtysh_write_terminal,
}
vty_out (vty, "end%s", VTY_NEWLINE);
-
+
return CMD_SUCCESS;
}
@@ -1779,7 +1757,7 @@ write_config_integrated(void)
unlink (integrate_sav);
rename (integrate_default, integrate_sav);
free (integrate_sav);
-
+
fp = fopen (integrate_default, "w");
if (fp == NULL)
{
@@ -1797,7 +1775,7 @@ write_config_integrated(void)
if (chmod (integrate_default, CONFIGFILE_MASK) != 0)
{
- fprintf (stdout,"%% Can't chmod configuration file %s: %s (%d)\n",
+ fprintf (stdout,"%% Can't chmod configuration file %s: %s (%d)\n",
integrate_default, safe_strerror(errno), errno);
return CMD_WARNING;
}
@@ -1818,16 +1796,16 @@ DEFUN (vtysh_write_memory,
int ret = CMD_SUCCESS;
char line[] = "write memory\n";
u_int i;
-
+
/* If integrated Quagga.conf explicitely set. */
if (vtysh_writeconfig_integrated)
return write_config_integrated();
fprintf (stdout,"Building Configuration...\n");
-
+
for (i = 0; i < VTYSH_INDEX_MAX; i++)
ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
-
+
fprintf (stdout,"[OK]\n");
return ret;
@@ -1835,7 +1813,7 @@ DEFUN (vtysh_write_memory,
ALIAS (vtysh_write_memory,
vtysh_copy_runningconfig_startupconfig_cmd,
- "copy running-config startup-config",
+ "copy running-config startup-config",
"Copy from one file to another\n"
"Copy from current system configuration\n"
"Copy to startup configuration\n")
@@ -2108,11 +2086,11 @@ vtysh_connect (struct vtysh_client *vclient)
ret = stat (vclient->path, &s_stat);
if (ret < 0 && errno != ENOENT)
{
- fprintf (stderr, "vtysh_connect(%s): stat = %s\n",
- vclient->path, safe_strerror(errno));
+ fprintf (stderr, "vtysh_connect(%s): stat = %s\n",
+ vclient->path, safe_strerror(errno));
exit(1);
}
-
+
if (ret >= 0)
{
if (! S_ISSOCK(s_stat.st_mode))
@@ -2121,7 +2099,7 @@ vtysh_connect (struct vtysh_client *vclient)
vclient->path);
exit (1);
}
-
+
}
sock = socket (AF_UNIX, SOCK_STREAM, 0);
@@ -2227,8 +2205,7 @@ void
vtysh_init_vty (void)
{
/* Make vty structure. */
- vty = vty_new ();
- vty->type = VTY_SHELL;
+ vty = vty_open(VTY_SHELL);
vty->node = VIEW_NODE;
/* Initialize commands. */
@@ -2383,7 +2360,7 @@ vtysh_init_vty (void)
/* "write terminal" command. */
install_element (ENABLE_NODE, &vtysh_write_terminal_cmd);
-
+
install_element (CONFIG_NODE, &vtysh_integrated_config_cmd);
install_element (CONFIG_NODE, &no_vtysh_integrated_config_cmd);
@@ -2422,7 +2399,7 @@ vtysh_init_vty (void)
install_element (ENABLE_NODE, &vtysh_start_shell_cmd);
install_element (ENABLE_NODE, &vtysh_start_bash_cmd);
install_element (ENABLE_NODE, &vtysh_start_zsh_cmd);
-
+
install_element (VIEW_NODE, &vtysh_show_memory_cmd);
install_element (ENABLE_NODE, &vtysh_show_memory_cmd);
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index fb8a1269..15938f43 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -101,7 +101,7 @@ config_get (int index, const char *line)
master->cmp = (int (*)(void *, void *)) config_cmp;
vector_set_index (configvec, index, master);
}
-
+
for (ALL_LIST_ELEMENTS (master, node, nnode, config_loop))
{
if (strcmp (config_loop->name, line) == 0)
@@ -265,7 +265,7 @@ vtysh_config_parse (char *line)
{
char *begin;
char *pnt;
-
+
begin = pnt = line;
while (*pnt != '\0')
@@ -353,11 +353,9 @@ vtysh_read_file (FILE *confp)
int ret;
struct vty *vty;
- vty = vty_new ();
- vty->fd = 0; /* stdout */
- vty->type = VTY_TERM;
+ vty = vty_open(VTY_STDOUT); /* stdout */
vty->node = CONFIG_NODE;
-
+
vtysh_execute_no_pager ("enable");
vtysh_execute_no_pager ("configure terminal");
@@ -369,7 +367,7 @@ vtysh_read_file (FILE *confp)
vty_close (vty);
- if (ret != CMD_SUCCESS)
+ if (ret != CMD_SUCCESS)
{
switch (ret)
{
@@ -380,7 +378,7 @@ vtysh_read_file (FILE *confp)
fprintf (stderr, "There is no such command.\n");
break;
}
- fprintf (stderr, "Error occured during reading below line.\n%s\n",
+ fprintf (stderr, "Error occured during reading below line.\n%s\n",
vty->buf);
exit (1);
}
diff --git a/watchquagga/watchquagga.c b/watchquagga/watchquagga.c
index fb628acc..2f01080c 100644
--- a/watchquagga/watchquagga.c
+++ b/watchquagga/watchquagga.c
@@ -180,7 +180,7 @@ struct daemon {
struct restart_info restart;
};
-static const struct option longopts[] =
+static const struct option longopts[] =
{
{ "daemon", no_argument, NULL, 'd'},
{ "statedir", required_argument, NULL, 'S'},
@@ -346,8 +346,14 @@ run_background(const char *shell_cmd)
if (setpgid(0,0) < 0)
zlog_warn("warning: setpgid(0,0) failed: %s",safe_strerror(errno));
{
- const char *argv[4] = { "sh", "-c", shell_cmd, NULL};
- execv("/bin/sh",(char *const *)argv);
+ union
+ {
+ const char* const* cp ;
+ char* const* p ;
+ } miyagi ;
+ const char *const argv[4] = { "sh", "-c", shell_cmd, NULL};
+ miyagi.cp = argv ;
+ execv("/bin/sh", miyagi.p);
zlog_err("execv(/bin/sh -c '%s') failed: %s",
shell_cmd,safe_strerror(errno));
_exit(127);
@@ -420,7 +426,7 @@ sigchild(void)
const char *what;
struct restart_info *restart;
- switch (child = waitpid(-1,&status,WNOHANG))
+ switch (child = waitpid(-1,&status,WNOHANG))
{
case -1:
zlog_err("waitpid failed: %s",safe_strerror(errno));
@@ -1232,7 +1238,7 @@ main(int argc, char **argv)
return usage(progname,1);
}
}
-
+
if (gs.unresponsive_restart && (gs.mode == MODE_MONITOR))
{
fputs("Option -z requires a -r or -R restart option.\n",stderr);
@@ -1278,7 +1284,7 @@ main(int argc, char **argv)
if (gs.stop_command)
gs.stop_command = translate_blanks(gs.stop_command,blankstr);
}
-
+
gs.restart.interval = gs.min_restart_interval;
master = thread_master_create();
signal_init (master, Q_SIGC(my_signals), my_signals);
diff --git a/zebra/Makefile.am b/zebra/Makefile.am
index 542f36f4..68762ccc 100644
--- a/zebra/Makefile.am
+++ b/zebra/Makefile.am
@@ -37,7 +37,8 @@ testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
noinst_HEADERS = \
connected.h ioctl.h rib.h rt.h zserv.h redistribute.h debug.h rtadv.h \
- interface.h ipforward.h irdp.h router-id.h kernel_socket.h
+ interface.h ipforward.h irdp.h router-id.h kernel_socket.h \
+ if_method.h rtread.h
zebra_LDADD = $(otherobj) $(LIBCAP) $(LIB_IPV6) ../lib/libzebra.la
diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c
index f357e154..14d40481 100644
--- a/zebra/if_ioctl.c
+++ b/zebra/if_ioctl.c
@@ -20,7 +20,10 @@
* 02111-1307, USA.
*/
+/* This is compiled and linked if found to be required at "configure" time. */
+
#include <zebra.h>
+#include "if_method.h"
#include "if.h"
#include "sockunion.h"
diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c
index fc384ea2..d86842b8 100644
--- a/zebra/if_ioctl_solaris.c
+++ b/zebra/if_ioctl_solaris.c
@@ -20,7 +20,10 @@
* 02111-1307, USA.
*/
+/* This is compiled and linked if found to be required at "configure" time. */
+
#include <zebra.h>
+#include "if_method.h"
#include "if.h"
#include "sockunion.h"
@@ -31,8 +34,6 @@
#include "log.h"
#include "privs.h"
-#include "zebra/interface.h"
-
void lifreq_set_name (struct lifreq *, const char *);
int if_get_flags_direct (const char *, uint64_t *, unsigned int af);
static int if_get_addr (struct interface *, struct sockaddr *, const char *);
diff --git a/zebra/if_method.h b/zebra/if_method.h
new file mode 100644
index 00000000..b5c95806
--- /dev/null
+++ b/zebra/if_method.h
@@ -0,0 +1,39 @@
+/* if_method header.
+ * 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.
+ */
+
+#ifndef _ZEBRA_IF_METHOD_H
+#define _ZEBRA_IF_METHOD_H
+
+/* There are (as at 17-Apr-2010) the following if methods:
+ *
+ * if_ioctl_solaris.c
+ * if_ioctl.c
+ * if_netlink.c
+ * if_sysctl.c
+ *
+ * one of which is selected at "configure" time, see: IF_METHOD
+ *
+ * Note that if_proc.c is NOT a member of this family !
+ */
+
+extern void interface_list (void) ;
+
+#endif /* _ZEBRA_IF_METHOD_H */
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 701c81b6..4a4a2862 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -17,10 +17,11 @@
* 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>
+#include "if_method.h"
extern int interface_lookup_netlink (void);
@@ -28,5 +29,5 @@ extern int interface_lookup_netlink (void);
void
interface_list (void)
{
- interface_lookup_netlink ();
+ interface_lookup_netlink ();
}
diff --git a/zebra/if_proc.c b/zebra/if_proc.c
index 3aec530b..bb83b993 100644
--- a/zebra/if_proc.c
+++ b/zebra/if_proc.c
@@ -19,6 +19,16 @@
* 02111-1307, USA.
*/
+/* This is compiled and linked if found to be required at "configure" time.
+ *
+ * HAVE_PROC_NET_DEV => /proc/net/dev exists
+ * HAVE_PROC_NET_IF_INET6 => /proc/net/if_inet6 exists
+ *
+ * One or both of this will be the case if this is being compiled.
+ *
+ * Appears NOT to be used if netlink is available.
+ */
+
#include <zebra.h>
#include "if.h"
@@ -29,6 +39,16 @@
#include "zebra/connected.h"
#include "zebra/interface.h"
+/* zebra/interface.h declares the extern functions for if_proc.c
+ *
+ * The following are not declared if HAVE_PROC_NET_DEV is not defined.
+ * So declare them here if required, in order to suppress warnings.
+ */
+#ifndef HAVE_PROC_NET_DEV
+extern void ifstat_update_proc (void);
+extern int interface_list_proc (void);
+#endif
+
/* Proc filesystem one line buffer. */
#define PROCBUFSIZ 1024
@@ -123,8 +143,8 @@ ifstat_dev_fields (int version, char *buf, struct interface *ifp)
}
/* Update interface's statistics. */
-void
-ifstat_update_proc (void)
+extern void
+ifstat_update_proc (void) /* declared in interface.h */
{
FILE *fp;
char buf[PROCBUFSIZ];
@@ -166,8 +186,8 @@ ifstat_update_proc (void)
}
/* Interface structure allocation by proc filesystem. */
-int
-interface_list_proc ()
+extern int
+interface_list_proc () /* declared in interface.h */
{
FILE *fp;
char buf[PROCBUFSIZ];
@@ -205,8 +225,8 @@ interface_list_proc ()
#define _PATH_PROC_NET_IF_INET6 "/proc/net/if_inet6"
#endif /* _PATH_PROC_NET_IF_INET6 */
-int
-ifaddr_proc_ipv6 ()
+extern int
+ifaddr_proc_ipv6 () /* declared in interface.h */
{
FILE *fp;
char buf[PROCBUFSIZ];
diff --git a/zebra/if_sysctl.c b/zebra/if_sysctl.c
index 5e809964..c7fecb77 100644
--- a/zebra/if_sysctl.c
+++ b/zebra/if_sysctl.c
@@ -20,7 +20,10 @@
* 02111-1307, USA.
*/
+/* This is compiled and linked if found to be required at "configure" time. */
+
#include <zebra.h>
+#include "if_method.h"
#include "if.h"
#include "sockunion.h"
@@ -32,6 +35,16 @@
#include "zebra/rt.h"
#include "zebra/kernel_socket.h"
+#include "zebra/interface.h"
+
+/* zebra/interface.h declares some extern functions for if_sysctl.c
+ *
+ * The following are not declared if HAVE_NET_RT_IFLIST is not defined.
+ * So declare them here if required, in order to suppress warnings.
+ */
+#ifndef HAVE_NET_RT_IFLIST
+extern void ifstat_update_sysctl (void);
+#endif
void
ifstat_update_sysctl (void)
diff --git a/zebra/interface.h b/zebra/interface.h
index 0cf66403..147521c6 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -225,21 +225,20 @@ extern int if_subnet_add (struct interface *, struct connected *);
extern int if_subnet_delete (struct interface *, struct connected *);
#ifdef HAVE_PROC_NET_DEV
-extern void ifstat_update_proc (void);
+extern void ifstat_update_proc (void); /* see if_proc.c */
+extern int interface_list_proc (void); /* see if_proc.c */
#endif /* HAVE_PROC_NET_DEV */
-#ifdef HAVE_NET_RT_IFLIST
-extern void ifstat_update_sysctl (void);
-#endif /* HAVE_NET_RT_IFLIST */
-#ifdef HAVE_PROC_NET_DEV
-extern int interface_list_proc (void);
-#endif /* HAVE_PROC_NET_DEV */
-#ifdef HAVE_PROC_NET_IF_INET6
-extern int ifaddr_proc_ipv6 (void);
+#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
+extern int ifaddr_proc_ipv6 (void); /* see if_proc.c */
#endif /* HAVE_PROC_NET_IF_INET6 */
+#ifdef HAVE_NET_RT_IFLIST
+extern void ifstat_update_sysctl (void); /* see if_sysctl.c */
+#endif /* HAVE_NET_RT_IFLIST */
+
#ifdef BSDI
-extern int if_kvm_get_mtu (struct interface *);
+extern int if_kvm_get_mtu (struct interface *); /* see mtu_kvm.c */
#endif /* BSDI */
#endif /* _ZEBRA_INTERFACE_H */
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index d783b0a3..ae8cadb6 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.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 <zebra.h>
@@ -52,7 +52,7 @@ if_ioctl (u_long request, caddr_t buffer)
{
int sock;
int ret;
- int err;
+ int err = 0 ; /* initialise to avoid warning */
if (zserv_privs.change(ZPRIVS_RAISE))
zlog (NULL, LOG_ERR, "Can't raise privileges");
@@ -70,8 +70,8 @@ if_ioctl (u_long request, caddr_t buffer)
if (zserv_privs.change(ZPRIVS_LOWER))
zlog (NULL, LOG_ERR, "Can't lower privileges");
close (sock);
-
- if (ret < 0)
+
+ if (ret < 0)
{
errno = err;
return ret;
@@ -80,12 +80,12 @@ if_ioctl (u_long request, caddr_t buffer)
}
#ifdef HAVE_IPV6
-static int
+extern int
if_ioctl_ipv6 (u_long request, caddr_t buffer)
{
int sock;
int ret;
- int err;
+ int err = 0 ; /* initialise to avoid warning */
if (zserv_privs.change(ZPRIVS_RAISE))
zlog (NULL, LOG_ERR, "Can't raise privileges");
@@ -105,8 +105,8 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer)
if (zserv_privs.change(ZPRIVS_LOWER))
zlog (NULL, LOG_ERR, "Can't lower privileges");
close (sock);
-
- if (ret < 0)
+
+ if (ret < 0)
{
errno = err;
return ret;
@@ -127,7 +127,7 @@ if_get_metric (struct interface *ifp)
ifreq_set_name (&ifreq, ifp);
- if (if_ioctl (SIOCGIFMETRIC, (caddr_t) &ifreq) < 0)
+ if (if_ioctl (SIOCGIFMETRIC, (caddr_t) &ifreq) < 0)
return;
ifp->metric = ifreq.ifr_metric;
if (ifp->metric == 0)
@@ -146,7 +146,7 @@ if_get_mtu (struct interface *ifp)
ifreq_set_name (&ifreq, ifp);
#if defined(SIOCGIFMTU)
- if (if_ioctl (SIOCGIFMTU, (caddr_t) & ifreq) < 0)
+ if (if_ioctl (SIOCGIFMTU, (caddr_t) & ifreq) < 0)
{
zlog_info ("Can't lookup mtu by ioctl(SIOCGIFMTU)");
ifp->mtu6 = ifp->mtu = -1;
@@ -216,7 +216,7 @@ if_set_prefix (struct interface *ifp, struct connected *ifc)
mask.sin_len = sizeof (struct sockaddr_in);
#endif
memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in));
-
+
ret = if_ioctl (SIOCAIFADDR, (caddr_t) &addreq);
if (ret < 0)
return ret;
@@ -254,7 +254,7 @@ if_unset_prefix (struct interface *ifp, struct connected *ifc)
mask.sin_len = sizeof (struct sockaddr_in);
#endif
memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in));
-
+
ret = if_ioctl (SIOCDIFADDR, (caddr_t) &addreq);
if (ret < 0)
return ret;
@@ -286,7 +286,7 @@ if_set_prefix (struct interface *ifp, struct connected *ifc)
ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq);
if (ret < 0)
return ret;
-
+
/* We need mask for make broadcast addr. */
masklen2ip (p->prefixlen, &mask.sin_addr);
@@ -356,7 +356,7 @@ if_get_flags (struct interface *ifp)
ifreq_set_name (&ifreq, ifp);
ret = if_ioctl (SIOCGIFFLAGS, (caddr_t) &ifreq);
- if (ret < 0)
+ if (ret < 0)
{
zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno));
return;
@@ -368,12 +368,12 @@ if_get_flags (struct interface *ifp)
* following practice on Linux and Solaris kernels
*/
SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
-
+
if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_LINKDETECTION))
{
(void) memset(&ifmr, 0, sizeof(ifmr));
strncpy (ifmr.ifm_name, ifp->name, IFNAMSIZ);
-
+
/* Seems not all interfaces implement this ioctl */
if (if_ioctl(SIOCGIFMEDIA, (caddr_t) &ifmr) < 0)
zlog_err("if_ioctl(SIOCGIFMEDIA) failed: %s", safe_strerror(errno));
@@ -441,7 +441,7 @@ if_unset_flags (struct interface *ifp, uint64_t flags)
#ifdef LINUX_IPV6
#ifndef _LINUX_IN6_H
/* linux/include/net/ipv6.h */
-struct in6_ifreq
+struct in6_ifreq
{
struct in6_addr ifr6_addr;
u_int32_t ifr6_prefixlen;
@@ -526,10 +526,10 @@ if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
addreq.ifra_lifetime.ia6t_vltime = 0xffffffff;
addreq.ifra_lifetime.ia6t_pltime = 0xffffffff;
-
-#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
- addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
- addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+
+#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
+ addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
#endif
ret = if_ioctl_ipv6 (SIOCAIFADDR_IN6, (caddr_t) &addreq);
@@ -569,8 +569,8 @@ if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6));
#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
- addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
- addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
#endif
ret = if_ioctl_ipv6 (SIOCDIFADDR_IN6, (caddr_t) &addreq);
diff --git a/zebra/ioctl.h b/zebra/ioctl.h
index fee9b725..1c4142de 100644
--- a/zebra/ioctl.h
+++ b/zebra/ioctl.h
@@ -23,6 +23,14 @@
#ifndef _ZEBRA_IOCTL_H
#define _ZEBRA_IOCTL_H
+/* There are (as at 17-Apr-2010) the following ioctl methods:
+ *
+ * * ioctl.c
+ * * ioctl_solaris.c
+ *
+ * one of which is selected at "configure" time, see: IOCTL_METHOD
+ */
+
/* Prototypes. */
extern void ifreq_set_name (struct ifreq *, struct interface *);
extern int if_ioctl (u_long, caddr_t);
@@ -40,10 +48,10 @@ extern void if_get_mtu (struct interface *);
#ifdef HAVE_IPV6
extern int if_prefix_add_ipv6 (struct interface *, struct connected *);
extern int if_prefix_delete_ipv6 (struct interface *, struct connected *);
+extern int if_ioctl_ipv6(u_long, caddr_t);
#endif /* HAVE_IPV6 */
#ifdef SOLARIS_IPV6
-extern int if_ioctl_ipv6(u_long, caddr_t);
extern struct connected *if_lookup_linklocal( struct interface *);
#define AF_IOCTL(af, request, buffer) \
diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c
index 6c1c254a..5c7c00b1 100644
--- a/zebra/ioctl_solaris.c
+++ b/zebra/ioctl_solaris.c
@@ -79,11 +79,11 @@ if_ioctl (u_long request, caddr_t buffer)
return 0;
}
+#ifdef HAVE_IPV6
int
if_ioctl_ipv6 (u_long request, caddr_t buffer)
{
-#ifdef HAVE_IPV6
int sock;
int ret;
int err;
@@ -115,11 +115,12 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer)
errno = err;
return ret;
}
-#endif /* HAVE_IPV6 */
return 0;
}
+#endif /* HAVE_IPV6 */
+
/*
* get interface metric
* -- if value is not avaliable set -1
diff --git a/zebra/ipforward.h b/zebra/ipforward.h
index 8a935c13..992ee269 100644
--- a/zebra/ipforward.h
+++ b/zebra/ipforward.h
@@ -22,6 +22,17 @@
#ifndef _ZEBRA_IPFORWARD_H
#define _ZEBRA_IPFORWARD_H
+/* There are (as at 17-Apr-2010) the following ipforward methods:
+ *
+ * * ipforward_proc.c
+ * * ipforward_solaris.c
+ * * ipforward_sysctl.c
+ * * ipforward_aix.c -- appears to be vestigial
+ * * ipforward_ews.c -- appears to be vestigial
+ *
+ * one of which is selected at "configure" time, see: IPFORWARD
+ */
+
extern int ipforward (void);
extern int ipforward_on (void);
extern int ipforward_off (void);
diff --git a/zebra/ipforward_aix.c b/zebra/ipforward_aix.c
index c79e7f1c..b9ef2d54 100644
--- a/zebra/ipforward_aix.c
+++ b/zebra/ipforward_aix.c
@@ -21,6 +21,7 @@
*/
#include <zebra.h>
+#include "zebra/ipforward.h"
int
ipforward ()
diff --git a/zebra/ipforward_ews.c b/zebra/ipforward_ews.c
index c872000a..4eb618f1 100644
--- a/zebra/ipforward_ews.c
+++ b/zebra/ipforward_ews.c
@@ -21,6 +21,7 @@
*/
#include <zebra.h>
+#include "zebra/ipforward.h"
int
ipforward ()
diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c
index c297979c..0658c397 100644
--- a/zebra/irdp_main.c
+++ b/zebra/irdp_main.c
@@ -29,7 +29,7 @@
*/
/*
- * Thanks to Jens Låås at Swedish University of Agricultural Sciences
+ * Thanks to Jens L��s at Swedish University of Agricultural Sciences
* for reviewing and tests.
*/
@@ -108,7 +108,7 @@ irdp_sock_init (void)
return ret;
};
- ret = setsockopt_ifindex (AF_INET, sock, 1);
+ ret = setsockopt_pktinfo (AF_INET, sock, 1);
if (ret < 0) {
zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno));
close(sock);
diff --git a/zebra/main.c b/zebra/main.c
index d829c046..d324acb5 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -39,6 +39,8 @@
#include "zebra/router-id.h"
#include "zebra/irdp.h"
#include "zebra/rtadv.h"
+#include "zebra/if_method.h"
+#include "zebra/rtread.h"
/* Zebra instance */
struct zebra_t zebrad =
diff --git a/zebra/misc_null.c b/zebra/misc_null.c
index 73594301..f37ce657 100644
--- a/zebra/misc_null.c
+++ b/zebra/misc_null.c
@@ -1,3 +1,7 @@
+/* misc_null.c
+ *
+ * This is used only in the testzebra build.
+ */
#include <zebra.h>
#include "prefix.h"
@@ -5,7 +9,18 @@
#include "zebra/irdp.h"
#include "zebra/interface.h"
+/* ifstat_update_proc() is defined in if_proc.c -- which is not part of the
+ * testzebra build.
+ *
+ * ifstat_update_proc() is declared in interface.h -- but only if
+ * #ifdef HAVE_PROC_NET_DEV.
+ *
+ * So declare it here as well, to avoid a compiler warning.
+ */
+extern void ifstat_update_proc (void);
+
void ifstat_update_proc (void) { return; }
-#pragma weak rtadv_config_write = ifstat_update_proc
-#pragma weak irdp_config_write = ifstat_update_proc
+
+#pragma weak rtadv_config_write = ifstat_update_proc
+#pragma weak irdp_config_write = ifstat_update_proc
#pragma weak ifstat_update_sysctl = ifstat_update_proc
diff --git a/zebra/mtu_kvm.c b/zebra/mtu_kvm.c
index d37bb9bb..c54741dd 100644
--- a/zebra/mtu_kvm.c
+++ b/zebra/mtu_kvm.c
@@ -24,9 +24,16 @@
#include <kvm.h>
#include <limits.h>
#include <fcntl.h>
+#include "zebra/interface.h" /* declares if_kvm_get_mtu() */
#include "if.h"
+/* This is compiled and linked for BSDI if found to be required at "configure"
+ * time.
+ *
+ * See: OTHER_METHOD
+ */
+
/* get interface MTU to use kvm_read */
void
if_kvm_get_mtu (struct interface *ifp)
diff --git a/zebra/router-id.c b/zebra/router-id.c
index 3d6b511c..72747bd9 100644
--- a/zebra/router-id.c
+++ b/zebra/router-id.c
@@ -1,7 +1,7 @@
/*
* Router ID for zebra daemon.
*
- * Copyright (C) 2004 James R. Leu
+ * Copyright (C) 2004 James R. Leu
*
* This file is part of Quagga routing suite.
*
@@ -66,11 +66,11 @@ router_id_bad_address (struct connected *ifc)
{
if (ifc->address->family != AF_INET)
return 1;
-
+
/* non-redistributable addresses shouldn't be used for RIDs either */
if (!zebra_check_addr (ifc->address))
return 1;
-
+
return 0;
}
@@ -134,7 +134,7 @@ router_id_add_address (struct connected *ifc)
l = &rid_lo_sorted_list;
else
l = &rid_all_sorted_list;
-
+
if (!router_id_find_node (l, ifc))
listnode_add_sort (l, ifc);
diff --git a/zebra/rt.h b/zebra/rt.h
index 8bfe5a42..41e3b070 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -23,6 +23,15 @@
#ifndef _ZEBRA_RT_H
#define _ZEBRA_RT_H
+/* There are (as at 17-Apr-2010) the following rt methods:
+ *
+ * rt_ioctl.c
+ * rt_netlink.c
+ * rt_socket.c
+ *
+ * one of which is selected at "configure" time, see: RT_METHOD
+ */
+
#include "prefix.h"
#include "if.h"
#include "zebra/rib.h"
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 7652f80a..fb5a79a2 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.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>
@@ -104,7 +104,7 @@ set_ifindex(struct interface *ifp, unsigned int ifi_index)
ifi_index, oifp->name, ifp->name);
if (if_is_up(oifp))
zlog_err("interface rename detected on up interface: index %d "
- "was renamed from %s to %s, results are uncertain!",
+ "was renamed from %s to %s, results are uncertain!",
ifi_index, oifp->name, ifp->name);
if_delete_update(oifp);
}
@@ -242,7 +242,7 @@ netlink_request (int family, int type, struct nlsock *nl)
req.nlh.nlmsg_seq = ++nl->seq;
req.g.rtgen_family = family;
- /* linux appears to check capabilities on every message
+ /* linux appears to check capabilities on every message
* have to raise caps for every message sent
*/
if (zserv_privs.change (ZPRIVS_RAISE))
@@ -310,7 +310,7 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
nl->name, msg.msg_namelen);
return -1;
}
-
+
for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status);
h = NLMSG_NEXT (h, status))
{
@@ -450,7 +450,7 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
/* Looking up interface name. */
memset (tb, 0, sizeof tb);
netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
-
+
#ifdef IFLA_WIRELESS
/* check for wireless messages to ignore */
if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0))
@@ -563,7 +563,7 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h)
buf, BUFSIZ), ifa->ifa_prefixlen);
if (tb[IFA_LABEL] && strcmp (ifp->name, RTA_DATA (tb[IFA_LABEL])))
zlog_debug (" IFA_LABEL %s", (char *)RTA_DATA (tb[IFA_LABEL]));
-
+
if (tb[IFA_CACHEINFO])
{
struct ifa_cacheinfo *ci = RTA_DATA (tb[IFA_CACHEINFO]);
@@ -571,13 +571,13 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h)
ci->ifa_prefered, ci->ifa_valid);
}
}
-
+
/* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */
if (tb[IFA_LOCAL] == NULL)
tb[IFA_LOCAL] = tb[IFA_ADDRESS];
if (tb[IFA_ADDRESS] == NULL)
tb[IFA_ADDRESS] = tb[IFA_LOCAL];
-
+
/* local interface address */
addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL);
@@ -934,7 +934,7 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
return 0;
}
#endif /* IFLA_WIRELESS */
-
+
if (tb[IFLA_IFNAME] == NULL)
return -1;
name = (char *) RTA_DATA (tb[IFLA_IFNAME]);
@@ -1037,7 +1037,9 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h)
}
/* Interface lookup by netlink socket. */
-int
+extern int interface_lookup_netlink (void) ; /* see: if_netlink.c */
+
+extern int
interface_lookup_netlink (void)
{
int ret;
@@ -1073,7 +1075,9 @@ interface_lookup_netlink (void)
/* Routing table read function using netlink interface. Only called
bootstrap time. */
-int
+extern int netlink_route_read (void) ; /* see: rtread_netlink.c */
+
+extern int
netlink_route_read (void)
{
int ret;
@@ -1099,7 +1103,7 @@ netlink_route_read (void)
return 0;
}
-/* Utility function comes from iproute2.
+/* Utility function comes from iproute2.
Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */
static int
addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
@@ -1109,7 +1113,7 @@ addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
len = RTA_LENGTH (alen);
- if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
+ if (NLMSG_ALIGN (n->nlmsg_len) + len > (unsigned)maxlen)
return -1;
rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
@@ -1141,7 +1145,7 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen)
return 0;
}
-/* Utility function comes from iproute2.
+/* Utility function comes from iproute2.
Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */
static int
addattr32 (struct nlmsghdr *n, int maxlen, int type, int data)
@@ -1151,7 +1155,7 @@ addattr32 (struct nlmsghdr *n, int maxlen, int type, int data)
len = RTA_LENGTH (4);
- if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
+ if (NLMSG_ALIGN (n->nlmsg_len) + len > (unsigned)maxlen)
return -1;
rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
@@ -1209,8 +1213,8 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
}
- /*
- * Get reply from netlink socket.
+ /*
+ * Get reply from netlink socket.
* The reply should either be an acknowlegement or an error.
*/
return netlink_parse_info (netlink_talk_filter, nl);
@@ -1379,7 +1383,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
#else
inet_ntoa (p->u.prefix4),
#endif /* HAVE_IPV6 */
-
+
p->prefixlen, nexthop_types_desc[nexthop->rtype]);
}
@@ -1648,7 +1652,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
if (nexthop->type == NEXTHOP_TYPE_IPV6
|| nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
|| nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
- {
+ {
rta_addattr_l (rta, 4096, RTA_GATEWAY,
&nexthop->gate.ipv6, bytelen);
@@ -1751,7 +1755,7 @@ kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate,
dest->prefixlen, gate, index, flags, table);
}
#endif /* HAVE_IPV6 */
-
+
/* Interface address modification. */
static int
netlink_address (int cmd, int family, struct interface *ifp,
diff --git a/zebra/rtread.h b/zebra/rtread.h
new file mode 100644
index 00000000..6dea5652
--- /dev/null
+++ b/zebra/rtread.h
@@ -0,0 +1,38 @@
+/*
+ * kernel routing table reading prototype.
+ * Copyright (C) 1998 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 _ZEBRA_RTREAD_H
+#define _ZEBRA_RTREAD_H
+
+/* There are (as at 17-Apr-2010) the following rtread methods:
+ *
+ * rtread_getmsg.c
+ * rtread_netlink.c
+ * rtread_proc.c
+ * rtread_sysctl.c
+ *
+ * one of which is selected at "configure" time, see: RTREAD_METHOD
+ */
+
+extern void route_read (void) ;
+
+#endif /* _ZEBRA_RTREAD_H */
diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c
index 3e065c6f..c8412fdf 100644
--- a/zebra/rtread_getmsg.c
+++ b/zebra/rtread_getmsg.c
@@ -27,7 +27,7 @@
#include "if.h"
#include "zebra/rib.h"
-#include "zebra/zserv.h"
+#include "zebra/rtread.h"
#include <sys/stream.h>
#include <sys/tihdr.h>
diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c
index 44715d94..aa09406e 100644
--- a/zebra/rtread_netlink.c
+++ b/zebra/rtread_netlink.c
@@ -17,13 +17,16 @@
* 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>
-extern void netlink_route_read (void);
-void route_read (void)
+#include "zebra/rtread.h"
+
+extern void netlink_route_read (void) ; /* see: rt_netlink.c */
+
+extern void route_read (void)
{
netlink_route_read ();
}
diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c
index b8f5bde7..3cceee7e 100644
--- a/zebra/rtread_sysctl.c
+++ b/zebra/rtread_sysctl.c
@@ -25,7 +25,7 @@
#include "memory.h"
#include "log.h"
-#include "zebra/zserv.h"
+#include "zebra/rtread.h"
#include "zebra/rt.h"
#include "zebra/kernel_socket.h"
diff --git a/zebra/test_main.c b/zebra/test_main.c
index 70a1a3a6..926bf4fb 100644
--- a/zebra/test_main.c
+++ b/zebra/test_main.c
@@ -32,6 +32,7 @@
#include "zebra/rib.h"
#include "zebra/zserv.h"
+#include "zebra/rtread.h"
#include "zebra/debug.h"
#include "zebra/router-id.h"
#include "zebra/interface.h"
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 12f3fa5a..0677cafd 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.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>
@@ -52,7 +52,7 @@ int rib_process_hold_time = 10;
/* Each route type's string and default distance value. */
static const struct
-{
+{
int key;
int distance;
} route_info[] =
@@ -68,7 +68,7 @@ static const struct
{ZEBRA_ROUTE_ISIS, 115},
{ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}
};
-
+
/* Vector for routing table. */
static vector vrf_vector;
@@ -141,7 +141,7 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id)
return vrf->stable[afi][safi];
}
-
+
/* Add nexthop to the end of the list. */
static void
nexthop_add (struct rib *rib, struct nexthop *nexthop)
@@ -226,7 +226,7 @@ nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
}
static struct nexthop *
-nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4,
+nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4,
struct in_addr *src, unsigned int ifindex)
{
struct nexthop *nexthop;
@@ -338,7 +338,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
while (rn)
{
route_unlock_node (rn);
-
+
/* If lookup self prefix return immediately. */
if (rn == top)
return 0;
@@ -354,7 +354,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
+ if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
@@ -371,7 +371,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
newhop = match->nexthop;
if (newhop && nexthop->type == NEXTHOP_TYPE_IPV4)
nexthop->ifindex = newhop->ifindex;
-
+
return 1;
}
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
@@ -439,7 +439,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
while (rn)
{
route_unlock_node (rn);
-
+
/* If lookup self prefix return immediately. */
if (rn == top)
return 0;
@@ -473,7 +473,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
if (newhop && nexthop->type == NEXTHOP_TYPE_IPV6)
nexthop->ifindex = newhop->ifindex;
-
+
return 1;
}
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
@@ -534,7 +534,7 @@ rib_match_ipv4 (struct in_addr addr)
while (rn)
{
route_unlock_node (rn);
-
+
/* Pick up selected route. */
for (match = rn->info; match; match = match->next)
{
@@ -546,7 +546,7 @@ rib_match_ipv4 (struct in_addr addr)
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
+ if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
@@ -607,7 +607,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p)
if (match->type == ZEBRA_ROUTE_CONNECT)
return match;
-
+
for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next)
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
return match;
@@ -665,7 +665,7 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate)
if (match->type == ZEBRA_ROUTE_CONNECT)
return ZEBRA_RIB_FOUND_CONNECTED;
-
+
/* Ok, we have a cood candidate, let's check it's nexthop list... */
for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next)
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
@@ -716,7 +716,7 @@ rib_match_ipv6 (struct in6_addr *addr)
while (rn)
{
route_unlock_node (rn);
-
+
/* Pick up selected route. */
for (match = rn->info; match; match = match->next)
{
@@ -728,7 +728,7 @@ rib_match_ipv6 (struct in6_addr *addr)
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
+ if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
@@ -900,7 +900,7 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
return rib->nexthop_active_num;
}
-
+
static void
rib_install_kernel (struct route_node *rn, struct rib *rib)
@@ -980,9 +980,9 @@ rib_process (struct route_node *rn)
int installed = 0;
struct nexthop *nexthop = NULL;
char buf[INET6_ADDRSTRLEN];
-
+
assert (rn);
-
+
if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q)
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
@@ -992,14 +992,14 @@ rib_process (struct route_node *rn)
* may be passed to rib_unlink() in the middle of iteration.
*/
next = rib->next;
-
+
/* Currently installed rib. */
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
{
assert (fib == NULL);
fib = rib;
}
-
+
/* Unlock removed routes, so they'll be freed, bar the FIB entry,
* which we need to do do further work with below.
*/
@@ -1014,10 +1014,10 @@ rib_process (struct route_node *rn)
}
else
del = rib;
-
+
continue;
}
-
+
/* Skip unreachable nexthop. */
if (! nexthop_active_update (rn, rib, 0))
continue;
@@ -1032,14 +1032,14 @@ rib_process (struct route_node *rn)
select = rib;
continue;
}
-
+
/* filter route selection in following order:
* - connected beats other types
* - lower distance beats higher
* - lower metric beats higher for equal distance
* - last, hence oldest, route wins tie break.
*/
-
+
/* Connected routes. Pick the last connected
* route of the set of lowest metric connected routes.
*/
@@ -1052,18 +1052,18 @@ rib_process (struct route_node *rn)
}
else if (select->type == ZEBRA_ROUTE_CONNECT)
continue;
-
+
/* higher distance loses */
if (rib->distance > select->distance)
continue;
-
+
/* lower wins */
if (rib->distance < select->distance)
{
select = rib;
continue;
}
-
+
/* metric tie-breaks equal distance */
if (rib->metric <= select->metric)
select = rib;
@@ -1090,14 +1090,14 @@ rib_process (struct route_node *rn)
/* Set real nexthop. */
nexthop_active_update (rn, select, 1);
-
+
if (! RIB_SYSTEM_ROUTE (select))
rib_install_kernel (rn, select);
redistribute_add (&rn->p, select);
}
else if (! RIB_SYSTEM_ROUTE (select))
{
- /* Housekeeping code to deal with
+ /* Housekeeping code to deal with
race conditions in kernel with linux
netlink reporting interface up before IPv4 or IPv6 protocol
is ready to add routes.
@@ -1110,7 +1110,7 @@ rib_process (struct route_node *rn)
installed = 1;
break;
}
- if (! installed)
+ if (! installed)
rib_install_kernel (rn, select);
}
goto end;
@@ -1167,7 +1167,7 @@ end:
}
/* Take a list of route_node structs and return 1, if there was a record
- * picked from it and processed by rib_process(). Don't process more,
+ * picked from it and processed by rib_process(). Don't process more,
* than one RN record; operate only in the specified sub-queue.
*/
static unsigned int
@@ -1202,9 +1202,9 @@ process_subq (struct list * subq, u_char qindex)
* is pointed to the meta queue structure.
*/
static wq_item_status
-meta_queue_process (struct work_queue *dummy, void *data)
+meta_queue_process (struct work_queue *dummy, work_queue_item item)
{
- struct meta_queue * mq = data;
+ struct meta_queue * mq = item->args.data ;
unsigned i;
for (i = 0; i < MQ_SIZE; i++)
@@ -1271,7 +1271,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
static void
rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
{
-
+
if (IS_ZEBRA_DEBUG_RIB_Q)
{
char buf[INET6_ADDRSTRLEN];
@@ -1289,7 +1289,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
* holder, if necessary, then push the work into it in any case.
* This semantics was introduced after 0.99.9 release.
*/
- if (!zebra->ribq->items->count)
+ if (zebra->ribq->head == NULL)
work_queue_add (zebra->ribq, zebra->mq);
rib_meta_queue_add (zebra->mq, rn);
@@ -1320,7 +1320,7 @@ meta_queue_new (void)
static void
rib_queue_init (struct zebra_t *zebra)
{
- if (! (zebra->ribq = work_queue_new (zebra->master,
+ if (! (zebra->ribq = work_queue_new (zebra->master,
"route_node processing")))
{
zlog_err ("%s: could not initialise work queue!", __func__);
@@ -1328,12 +1328,13 @@ rib_queue_init (struct zebra_t *zebra)
}
/* fill in the work queue spec */
- zebra->ribq->spec.workfunc = &meta_queue_process;
- zebra->ribq->spec.errorfunc = NULL;
+ zebra->ribq->spec.workfunc = &meta_queue_process;
+ zebra->ribq->spec.errorfunc = NULL;
+ zebra->ribq->spec.del_item_data = NULL ;
/* XXX: TODO: These should be runtime configurable via vty */
zebra->ribq->spec.max_retries = 3;
zebra->ribq->spec.hold = rib_process_hold_time;
-
+
if (!(zebra->mq = meta_queue_new ()))
zlog_err ("%s: could not initialise meta queue!", __func__);
}
@@ -1365,7 +1366,7 @@ rib_queue_init (struct zebra_t *zebra)
* state must be preserved as and when the head RIB entry of a
* route_node is changed by rib_unlink / rib_link. A small complication,
* but saves having to allocate a dedicated object for this.
- *
+ *
* Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
*
* - route_nodes: refcounted by:
@@ -1375,16 +1376,16 @@ rib_queue_init (struct zebra_t *zebra)
* - managed by: rib_addqueue, rib_process.
*
*/
-
+
/* Add RIB to head of the route node. */
static void
rib_link (struct route_node *rn, struct rib *rib)
{
struct rib *head;
char buf[INET6_ADDRSTRLEN];
-
+
assert (rib && rn);
-
+
route_lock_node (rn); /* rn route table reference */
if (IS_ZEBRA_DEBUG_RIB)
@@ -1412,8 +1413,8 @@ rib_link (struct route_node *rn, struct rib *rib)
static void
rib_addnode (struct route_node *rn, struct rib *rib)
{
- /* RIB node has been un-removed before route-node is processed.
- * route_node must hence already be on the queue for processing..
+ /* RIB node has been un-removed before route-node is processed.
+ * route_node must hence already be on the queue for processing..
*/
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
{
@@ -1453,7 +1454,7 @@ rib_unlink (struct route_node *rn, struct rib *rib)
else
{
rn->info = rib->next;
-
+
if (rn->info)
{
if (IS_ZEBRA_DEBUG_RIB)
@@ -1489,7 +1490,7 @@ rib_delnode (struct route_node *rn, struct rib *rib)
}
int
-rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
+rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
struct in_addr *gate, struct in_addr *src,
unsigned int ifindex, u_int32_t vrf_id,
u_int32_t metric, u_char distance)
@@ -1527,7 +1528,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
-
+
if (rib->type != type)
continue;
if (rib->type != ZEBRA_ROUTE_CONNECT)
@@ -1576,7 +1577,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: calling rib_addnode (%p, %p)", __func__, rn, rib);
rib_addnode (rn, rib);
-
+
/* Free implicit route.*/
if (same)
{
@@ -1584,7 +1585,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
zlog_debug ("%s: calling rib_delnode (%p, %p)", __func__, rn, rib);
rib_delnode (rn, same);
}
-
+
route_unlock_node (rn);
return 0;
}
@@ -1753,7 +1754,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
struct route_node *rn;
struct rib *same;
struct nexthop *nexthop;
-
+
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
@@ -1767,7 +1768,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
rib->distance = route_info[rib->type].distance;
/* iBGP distance is 200. */
- if (rib->type == ZEBRA_ROUTE_BGP
+ if (rib->type == ZEBRA_ROUTE_BGP
&& CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
rib->distance = 200;
}
@@ -1781,12 +1782,12 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
{
if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED))
continue;
-
+
if (same->type == rib->type && same->table == rib->table
&& same->type != ZEBRA_ROUTE_CONNECT)
break;
}
-
+
/* If this route is kernel route, set FIB flag to the route. */
if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT)
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
@@ -1812,7 +1813,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
}
rib_delnode (rn, same);
}
-
+
route_unlock_node (rn);
return 0;
}
@@ -1842,8 +1843,8 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
if (IS_ZEBRA_DEBUG_KERNEL && gate)
zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d",
inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
- p->prefixlen,
- inet_ntoa (*gate),
+ p->prefixlen,
+ inet_ntoa (*gate),
ifindex);
/* Lookup route node. */
@@ -1895,7 +1896,7 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
else if (gate == NULL ||
((nexthop = rib->nexthop) &&
(IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate) ||
- IPV4_ADDR_SAME (&nexthop->rgate.ipv4, gate))))
+ IPV4_ADDR_SAME (&nexthop->rgate.ipv4, gate))))
{
same = rib;
break;
@@ -1936,14 +1937,14 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
return ZEBRA_ERR_RTNOEXIST;
}
}
-
+
if (same)
rib_delnode (rn, same);
-
+
route_unlock_node (rn);
return 0;
}
-
+
/* Install static route into rib. */
static void
static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
@@ -1963,7 +1964,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
-
+
if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
break;
}
@@ -1991,7 +1992,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
{
/* This is new static route. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
-
+
rib->type = ZEBRA_ROUTE_STATIC;
rib->distance = si->distance;
rib->metric = 0;
@@ -2048,7 +2049,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return;
-
+
/* Lookup existing route with type and distance. */
rn = route_node_lookup (table, p);
if (! rn)
@@ -2080,7 +2081,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
route_unlock_node (rn);
return;
}
-
+
/* Check nexthop. */
if (rib->nexthop_num == 1)
rib_delnode (rn, rib);
@@ -2113,7 +2114,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id);
if (! stable)
return -1;
-
+
/* Lookup static route prefix. */
rn = route_node_get (stable, p);
@@ -2244,7 +2245,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
if (si->next)
si->next->prev = si->prev;
route_unlock_node (rn);
-
+
/* Free static route configuration. */
if (ifname)
XFREE (0, si->gate.ifname);
@@ -2255,7 +2256,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
return 1;
}
-
+
#ifdef HAVE_IPV6
static int
rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
@@ -2300,7 +2301,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
/* Set default distance by route type. */
if (!distance)
distance = route_info[type].distance;
-
+
if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
distance = 200;
@@ -2336,7 +2337,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
/* Allocate new rib structure. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
-
+
rib->type = type;
rib->distance = distance;
rib->flags = flags;
@@ -2367,7 +2368,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
/* Free implicit route.*/
if (same)
rib_delnode (rn, same);
-
+
route_unlock_node (rn);
return 0;
}
@@ -2393,7 +2394,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
if (! table)
return 0;
-
+
/* Lookup route node. */
rn = route_node_lookup (table, (struct prefix *) p);
if (! rn)
@@ -2487,11 +2488,11 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
if (same)
rib_delnode (rn, same);
-
+
route_unlock_node (rn);
return 0;
}
-
+
/* Install static route into rib. */
static void
static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
@@ -2540,7 +2541,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
{
/* This is new static route. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
-
+
rib->type = ZEBRA_ROUTE_STATIC;
rib->distance = si->distance;
rib->metric = 0;
@@ -2608,7 +2609,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
-
+
if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
break;
}
@@ -2630,7 +2631,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
route_unlock_node (rn);
return;
}
-
+
/* Check nexthop. */
if (rib->nexthop_num == 1)
{
@@ -2664,12 +2665,12 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
stable = vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id);
if (! stable)
return -1;
-
+
if (!gate &&
(type == STATIC_IPV6_GATEWAY || type == STATIC_IPV6_GATEWAY_IFNAME))
return -1;
-
- if (!ifname &&
+
+ if (!ifname &&
(type == STATIC_IPV6_GATEWAY_IFNAME || type == STATIC_IPV6_IFNAME))
return -1;
@@ -2679,7 +2680,7 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
/* Do nothing if there is a same static route. */
for (si = rn->info; si; si = si->next)
{
- if (distance == si->distance
+ if (distance == si->distance
&& type == si->type
&& (! gate || IPV6_ADDR_SAME (gate, &si->ipv6))
&& (! ifname || strcmp (ifname, si->ifname) == 0))
@@ -2757,7 +2758,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
/* Find same static route is the tree */
for (si = rn->info; si; si = si->next)
- if (distance == si->distance
+ if (distance == si->distance
&& type == si->type
&& (! gate || IPV6_ADDR_SAME (gate, &si->ipv6))
&& (! ifname || strcmp (ifname, si->ifname) == 0))
@@ -2780,7 +2781,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
rn->info = si->next;
if (si->next)
si->next->prev = si->prev;
-
+
/* Free static route configuration. */
if (ifname)
XFREE (0, si->ifname);
@@ -2789,14 +2790,14 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
return 1;
}
#endif /* HAVE_IPV6 */
-
+
/* RIB update function. */
void
rib_update (void)
{
struct route_node *rn;
struct route_table *table;
-
+
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
@@ -2810,7 +2811,7 @@ rib_update (void)
rib_queue_add (&zebrad, rn);
}
-
+
/* Remove all routes which comes from non main table. */
static void
rib_weed_table (struct route_table *table)
@@ -2841,7 +2842,7 @@ rib_weed_tables (void)
rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
-
+
/* Delete self installed routes after zebra is relaunched. */
static void
rib_sweep_table (struct route_table *table)
@@ -2860,7 +2861,7 @@ rib_sweep_table (struct route_table *table)
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
- if (rib->type == ZEBRA_ROUTE_KERNEL &&
+ if (rib->type == ZEBRA_ROUTE_KERNEL &&
CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELFROUTE))
{
ret = rib_uninstall_kernel (rn, rib);
@@ -2877,7 +2878,7 @@ rib_sweep_route (void)
rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
-
+
/* Close RIB and clean up kernel routes. */
static void
rib_close_table (struct route_table *table)
@@ -2902,7 +2903,7 @@ rib_close (void)
rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
-
+
/* Routing information base initialize. */
void
rib_init (void)
diff --git a/zebra/zserv.c b/zebra/zserv.c
index dc3d432b..398b74c8 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -1360,8 +1360,8 @@ zebra_serv ()
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- sockopt_reuseaddr (accept_sock);
- sockopt_reuseport (accept_sock);
+ setsockopt_reuseaddr (accept_sock);
+ setsockopt_reuseport (accept_sock);
if ( zserv_privs.change(ZPRIVS_RAISE) )
zlog (NULL, LOG_ERR, "Can't raise privileges");
diff --git a/zebra/zserv.h b/zebra/zserv.h
index a7371830..3ad3863c 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -92,9 +92,7 @@ extern void zebra_if_init (void);
extern void zebra_zserv_socket_init (void);
extern void hostinfo_get (void);
extern void rib_init (void);
-extern void interface_list (void);
extern void kernel_init (void);
-extern void route_read (void);
extern void zebra_route_map_init (void);
extern void zebra_snmp_init (void);
extern void zebra_vty_init (void);