summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_main.c')
-rw-r--r--bgpd/bgp_main.c605
1 files changed, 482 insertions, 123 deletions
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);
+} ;
+