From 856ad4ebd2611d8f3d22264c55de48332bf4e3ec Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Mon, 6 Nov 2000 22:12:44 +0000 Subject: [PATCH] Added support to retry when connecting. --- apps/silcd/idlist.h | 2 +- apps/silcd/protocol.h | 2 + apps/silcd/server.c | 316 +++++++++++++++++++---------------- apps/silcd/server.h | 30 +++- apps/silcd/server_internal.h | 23 ++- apps/silcd/serverconfig.c | 29 +--- apps/silcd/testi2.conf | 9 +- 7 files changed, 233 insertions(+), 178 deletions(-) diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 51a143bb..52d3307e 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -430,7 +430,7 @@ typedef struct SilcIDListStruct { } *SilcIDList; /* - Temporary ID Entry object. + ID Entry for Unknown connections. This is used during authentication phases where we still don't know what kind of connection remote connection is, hence, we will use this diff --git a/apps/silcd/protocol.h b/apps/silcd/protocol.h index 67d76a63..7748faa1 100644 --- a/apps/silcd/protocol.h +++ b/apps/silcd/protocol.h @@ -30,6 +30,7 @@ /* Internal context for Key Exchange protocol. */ typedef struct { void *server; + void *context; SilcSocketConnection sock; SilcRng rng; @@ -49,6 +50,7 @@ typedef struct { /* Internal context for connection authentication protocol */ typedef struct { void *server; + void *context; SilcSocketConnection sock; /* TRUE if we are receiving part of the protocol */ diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 149b34b9..d9535286 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -28,6 +28,7 @@ #include "server_internal.h" /* Static prototypes */ +SILC_TASK_CALLBACK(silc_server_connect_router); SILC_TASK_CALLBACK(silc_server_connect_to_router); SILC_TASK_CALLBACK(silc_server_connect_to_router_second); SILC_TASK_CALLBACK(silc_server_connect_to_router_final); @@ -86,6 +87,9 @@ void silc_server_free(SilcServer server) } silc_dlist_uninit(server->sim); + if (server->params) + silc_free(server->params); + silc_math_primegen_uninit(); /* XXX */ silc_free(server); } @@ -108,6 +112,15 @@ int silc_server_init(SilcServer server) assert(server); assert(server->config); + /* XXX After server is made as Silc Server Library this can be given + as argument, for now this is hard coded */ + server->params = silc_calloc(1, sizeof(*server->params)); + server->params->retry_count = SILC_SERVER_RETRY_COUNT; + server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN; + server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX; + server->params->retry_keep_trying = FALSE; + server->params->protocol_timeout = 60; + /* Set log files where log message should be saved. */ server->config->server = server; silc_config_server_setlogfiles(server->config); @@ -355,6 +368,120 @@ void silc_server_run(SilcServer server) silc_schedule(); } +/* Timeout callback that will be called to retry connecting to remote + router. This is used by both normal and router server. This will wait + before retrying the connecting. The timeout is generated by exponential + backoff algorithm. */ + +SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) +{ + SilcServerConnection sconn = (SilcServerConnection)context; + SilcServer server = sconn->server; + + SILC_LOG_INFO(("Retrying connecting to a router")); + + /* Calculate next timeout */ + if (sconn->retry_count >= 1) { + sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER; + if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX) + sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX; + } else { + sconn->retry_timeout = server->params->retry_interval_min; + } + sconn->retry_count++; + sconn->retry_timeout = sconn->retry_timeout + + silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER; + + /* If we've reached max retry count, give up. */ + if (sconn->retry_count > server->params->retry_count && + server->params->retry_keep_trying == FALSE) { + SILC_LOG_ERROR(("Could not connect to router, giving up")); + return; + } + + /* Wait one before retrying */ + silc_task_register(server->timeout_queue, fd, silc_server_connect_router, + context, sconn->retry_timeout, + server->params->retry_interval_min_usec, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); +} + +/* Generic routine to use connect to a router. */ + +SILC_TASK_CALLBACK(silc_server_connect_router) +{ + SilcServerConnection sconn = (SilcServerConnection)context; + SilcServer server = sconn->server; + SilcSocketConnection newsocket; + SilcProtocol protocol; + SilcServerKEInternalContext *proto_ctx; + int sock; + + /* Connect to remote host */ + sock = silc_net_create_connection(sconn->remote_port, + sconn->remote_host); + if (sock < 0) { + SILC_LOG_ERROR(("Could not connect to router")); + silc_task_register(server->timeout_queue, fd, + silc_server_connect_to_router_retry, + context, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } + + /* Set socket options */ + silc_net_set_socket_nonblock(sock); + silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + + /* Create socket connection for the connection. Even though we + know that we are connecting to a router we will mark the socket + to be unknown connection until we have executed authentication + protocol. */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); + server->sockets[sock] = newsocket; + newsocket->hostname = sconn->remote_host; + newsocket->port = sconn->remote_port; + sconn->sock = newsocket; + + /* Allocate internal protocol context. This is sent as context + to the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = (void *)server; + proto_ctx->context = (void *)sconn; + proto_ctx->sock = newsocket; + proto_ctx->rng = server->rng; + proto_ctx->responder = FALSE; + + /* Perform key exchange protocol. silc_server_connect_to_router_second + will be called after the protocol is finished. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, + &protocol, proto_ctx, + silc_server_connect_to_router_second); + newsocket->protocol = protocol; + + /* Register a timeout task that will be executed if the protocol + is not executed within set limit. */ + proto_ctx->timeout_task = + silc_task_register(server->timeout_queue, sock, + silc_server_timeout_remote, + server, server->params->protocol_timeout, + server->params->protocol_timeout_usec, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as + well. However, this doesn't set the scheduler for outgoing traffic, + it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + context = (void *)server; + SILC_REGISTER_CONNECTION_FOR_IO(sock); + + /* Run the protocol */ + protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0); +} + /* This function connects to our primary router or if we are a router this establishes all our primary routes. This is called at the start of the server to do authentication and key exchange with our router - called @@ -363,148 +490,53 @@ void silc_server_run(SilcServer server) SILC_TASK_CALLBACK(silc_server_connect_to_router) { SilcServer server = (SilcServer)context; - SilcSocketConnection newsocket; - int sock; + SilcServerConnection sconn; SILC_LOG_DEBUG(("Connecting to router(s)")); - /* if we are normal SILC server we need to connect to our cell's + /* If we are normal SILC server we need to connect to our cell's router. */ if (server->server_type == SILC_SERVER) { - SilcProtocol protocol; - SilcServerKEInternalContext *proto_ctx; + SILC_LOG_DEBUG(("We are normal server")); /* Create connection to the router, if configured. */ if (server->config->routers) { - sock = silc_net_create_connection(server->config->routers->port, - server->config->routers->host); - if (sock < 0) { - SILC_LOG_ERROR(("Could not connect to router")); - silc_schedule_stop(); - return; - } - /* Set socket options */ - silc_net_set_socket_nonblock(sock); - silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); - - /* Create socket connection for the connection. Even though we - know that we are connecting to a router we will mark the socket - to be unknown connection until we have executed authentication - protocol. */ - silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); - server->sockets[sock] = newsocket; - newsocket->hostname = server->config->routers->host; - newsocket->port = server->config->routers->port; - - /* Allocate internal protocol context. This is sent as context - to the protocol. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->server = context; - proto_ctx->sock = newsocket; - proto_ctx->rng = server->rng; - proto_ctx->responder = FALSE; - - /* Perform key exchange protocol. silc_server_connect_to_router_second - will be called after the protocol is finished. */ - silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, - &protocol, proto_ctx, - silc_server_connect_to_router_second); - newsocket->protocol = protocol; - - /* Register a timeout task that will be executed if the protocol - is not executed within 60 seconds. For now, this is a hard coded - limit. After 60 secs the connection will be closed if the key - exchange protocol has not been executed. */ - proto_ctx->timeout_task = - silc_task_register(server->timeout_queue, sock, - silc_server_timeout_remote, - context, 60, 0, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_LOW); - - /* Register the connection for network input and output. This sets - that scheduler will listen for incoming packets for this connection - and sets that outgoing packets may be sent to this connection as - well. However, this doesn't set the scheduler for outgoing traffic, - it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, - later when outgoing data is available. */ - SILC_REGISTER_CONNECTION_FOR_IO(sock); - - /* Run the protocol */ - protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0); + /* Allocate connection object for hold connection specific stuff. */ + sconn = silc_calloc(1, sizeof(*sconn)); + sconn->server = server; + sconn->remote_host = server->config->routers->host; + sconn->remote_port = server->config->routers->port; + + silc_task_register(server->timeout_queue, fd, + silc_server_connect_router, + (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); return; } } - - /* if we are a SILC router we need to establish all of our primary + + /* If we are a SILC router we need to establish all of our primary routes. */ if (server->server_type == SILC_ROUTER) { SilcConfigServerSectionServerConnection *ptr; + SILC_LOG_DEBUG(("We are router")); + /* Create the connections to all our routes */ ptr = server->config->routers; while (ptr) { - SilcProtocol protocol; - SilcServerKEInternalContext *proto_ctx; - - /* Create the connection to the remote end */ - sock = silc_net_create_connection(ptr->port, ptr->host); - if (sock < 0) { - SILC_LOG_ERROR(("Could not connect to router")); - silc_schedule_stop(); - return; - } - /* Set socket options */ - silc_net_set_socket_nonblock(sock); - silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); - - /* Create socket connection for the connection. Even though we - know that we are connecting to a router we will mark the socket - to be unknown connection until we have executed authentication - protocol. */ - silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); - server->sockets[sock] = newsocket; - newsocket->hostname = ptr->host; - newsocket->port = ptr->port; - - /* Allocate internal protocol context. This is sent as context - to the protocol. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->server = context; - proto_ctx->sock = newsocket; - proto_ctx->rng = server->rng; - proto_ctx->responder = FALSE; - - /* Perform key exchange protocol. silc_server_connect_to_router_final - will be called after the protocol is finished. */ - silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, - &protocol, proto_ctx, - silc_server_connect_to_router_second); - newsocket->protocol = protocol; - - /* Register a timeout task that will be executed if the protocol - is not executed within 60 seconds. For now, this is a hard coded - limit. After 60 secs the connection will be closed if the key - exchange protocol has not been executed. */ - proto_ctx->timeout_task = - silc_task_register(server->timeout_queue, sock, - silc_server_timeout_remote, - context, 60, 0, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_LOW); - - /* Register the connection for network input and output. This sets - that scheduler will listen for incoming packets for this connection - and sets that outgoing packets may be sent to this connection as - well. However, this doesn't set the scheduler for outgoing traffic, - it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, - later when outgoing data is available. */ - SILC_REGISTER_CONNECTION_FOR_IO(sock); - - /* Run the protocol */ - protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0); + /* Allocate connection object for hold connection specific stuff. */ + sconn = silc_calloc(1, sizeof(*sconn)); + sconn->server = server; + sconn->remote_host = ptr->host; + sconn->remote_port = ptr->port; + + silc_task_register(server->timeout_queue, fd, + silc_server_connect_router, + (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); if (!ptr->next) return; @@ -521,14 +553,11 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) /* Add a task to the queue. This task receives new connections to the server. This task remains on the queue until the end of the program. */ - if (silc_task_register(server->io_queue, fd, - silc_server_accept_new_connection, - (void *)server, 0, 0, - SILC_TASK_FD, - SILC_TASK_PRI_NORMAL) == NULL) { - silc_schedule_stop(); - return; - } + silc_task_register(server->io_queue, fd, + silc_server_accept_new_connection, + (void *)server, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL); } /* Second part of connecting to router(s). Key exchange protocol has been @@ -540,6 +569,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) SilcServerKEInternalContext *ctx = (SilcServerKEInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; + SilcServerConnection sconn = (SilcServerConnection)ctx->context; SilcSocketConnection sock = NULL; SilcServerConnAuthInternalContext *proto_ctx; @@ -565,6 +595,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) is sent as context for the protocol. */ proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); proto_ctx->server = (void *)server; + proto_ctx->context = (void *)sconn; proto_ctx->sock = sock = server->sockets[fd]; proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */ proto_ctx->dest_id_type = ctx->dest_id_type; @@ -633,6 +664,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) SilcServerConnAuthInternalContext *ctx = (SilcServerConnAuthInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; + SilcServerConnection sconn = (SilcServerConnection)ctx->context; SilcSocketConnection sock = ctx->sock; SilcServerEntry id_entry; SilcUnknownEntry conn_data; @@ -660,16 +692,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) /* Add a task to the queue. This task receives new connections to the server. This task remains on the queue until the end of the program. */ if (!server->listenning) { - if (silc_task_register(server->io_queue, server->sock, - silc_server_accept_new_connection, - (void *)server, 0, 0, - SILC_TASK_FD, - SILC_TASK_PRI_NORMAL) == NULL) { - silc_schedule_stop(); - return; - } else { - server->listenning = TRUE; - } + silc_task_register(server->io_queue, server->sock, + silc_server_accept_new_connection, + (void *)server, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL); + server->listenning = TRUE; } /* Send NEW_SERVER packet to the router. We will become registered @@ -713,6 +741,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) /* Free the temporary connection data context from key exchange */ silc_free(conn_data); + if (sconn) + silc_free(sconn); /* Free the protocol object */ silc_protocol_free(protocol); @@ -2395,10 +2425,10 @@ int silc_server_client_on_channel(SilcClientEntry client, SILC_TASK_CALLBACK(silc_server_timeout_remote) { - SilcServer server = (SilcServer)context; - SilcSocketConnection sock = server->sockets[fd]; + SilcServerConnection sconn = (SilcServerConnection)context; + SilcSocketConnection sock = sconn->server->sockets[fd]; - silc_server_disconnect_remote(server, sock, + silc_server_disconnect_remote(sconn->server, sock, "Server closed connection: " "Connection timeout"); } diff --git a/apps/silcd/server.h b/apps/silcd/server.h index 41379eab..1e3dd564 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -24,15 +24,43 @@ /* Forward declaration for SILC Server object. The actual object is defined in internal header file for server routines. I want to keep the object private hence this declaration. */ -typedef struct SilcServerObjectStruct *SilcServer; +typedef struct SilcServerStruct *SilcServer; #define SILC_SERVER_MAX_CONNECTIONS 10000 /* General definitions */ +/* Server and router. Used internally by the code. */ #define SILC_SERVER 0 #define SILC_ROUTER 1 +/* Connection retry timeout. We implement exponential backoff algorithm + in connection retry. The interval of timeuot grows when retry count + grows. */ +#define SILC_SERVER_RETRY_COUNT 3 /* Max retry count */ +#define SILC_SERVER_RETRY_MULTIPLIER 7 / 4 /* Interval growth */ +#define SILC_SERVER_RETRY_RANDOMIZER 2 /* timeout += rnd % 2 */ +#define SILC_SERVER_RETRY_INTERVAL_MIN 2 /* Min retry timeout */ +#define SILC_SERVER_RETRY_INTERVAL_MAX 30 /* Max generated timeout */ + +/* + Silc Server Params. + + Structure to hold various default parameters for server that can be + given before running the server. + +*/ +typedef struct { + unsigned int retry_count; + unsigned long retry_interval_min; + unsigned long retry_interval_min_usec; + unsigned long retry_interval_max; + unsigned int retry_keep_trying; + + unsigned long protocol_timeout; + unsigned long protocol_timeout_usec; +} *SilcServerParams; + /* Macros */ /* This macro is used to send notify messages with formatted string. The diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h index e0c037e8..0b748b52 100644 --- a/apps/silcd/server_internal.h +++ b/apps/silcd/server_internal.h @@ -28,11 +28,27 @@ typedef struct { } SilcServerStatistics; +typedef struct { + void *id_entry; + SilcSocketConnection sock; + + /* Remote host name and port */ + char *remote_host; + int remote_port; + + /* Current connection retry info */ + unsigned int retry_count; + unsigned int retry_timeout; + + /* Back pointer to server */ + SilcServer server; +} *SilcServerConnection; + /* SILC Server Object. */ -typedef struct SilcServerObjectStruct { +struct SilcServerStruct { char *server_name; int server_type; int sock; @@ -81,11 +97,14 @@ typedef struct SilcServerObjectStruct { /* Server statistics */ SilcServerStatistics stats; + /* Default parameteres for server */ + SilcServerParams params; + #ifdef SILC_SIM /* SIM (SILC Module) list */ SilcDList sim; #endif -} SilcServerObject; +}; /* Macros */ diff --git a/apps/silcd/serverconfig.c b/apps/silcd/serverconfig.c index 34985bad..25c562f4 100644 --- a/apps/silcd/serverconfig.c +++ b/apps/silcd/serverconfig.c @@ -17,34 +17,7 @@ GNU General Public License for more details. */ -/* - * $Id$ - * $Log$ - * Revision 1.5 2000/11/02 22:15:23 priikone - * Some bugfixes. - * Changed server->sim to SilcDList and code accordingly. - * Changes SilcClientEntry->channel to SilcList and added - * back-pointer to SilcChannelClientEntry which is now used by both - * client entry and channel entry - * - * Revision 1.4 2000/10/06 08:10:23 priikone - * Added WHOIS to send multiple replies if multiple nicknames are - * found. - * Added MOTD command and [motd] config section and server also sends - * motd to client on connection now. - * Fixed TOPIC command some more. - * - * Revision 1.3 2000/07/10 05:41:20 priikone - * Added missing token to administrative information. - * - * Revision 1.2 2000/07/05 06:14:01 priikone - * Global costemic changes. - * - * Revision 1.1.1.1 2000/06/27 11:36:56 priikone - * Imported from internal CVS/Added Log headers. - * - * - */ +/* $Id$ */ #include "serverincludes.h" #include "server_internal.h" diff --git a/apps/silcd/testi2.conf b/apps/silcd/testi2.conf index 8d444563..bf4d2408 100644 --- a/apps/silcd/testi2.conf +++ b/apps/silcd/testi2.conf @@ -22,9 +22,9 @@ lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1334 10.2.1.6:10.2.1.6:1334 [Logging] -infologfile:silcd.log:10000 +infologfile:silcd2.log:10000 #warninglogfile:/var/log/silcd_warning.log:10000 -errorlogfile:silcd2_error.log:10000 +errorlogfile:silcd2.log:10000 #fatallogfile:/var/log/silcd_error.log: [ConnectionClass] @@ -34,14 +34,17 @@ errorlogfile:silcd2_error.log:10000 [ClientConnection] 10.2.1.199:passwd:priikone:333:1 :::1333:1 +:::1334:1 +:::1335:1 [AdminConnection] 10.2.1.199:passwd:priikone:priikone:1 [ServerConnection] +10.2.1.6:passwd:priikone:1335:1:1 [RouterConnection] -10.2.1.6:passwd:priikone:1333:1:1 +#10.2.1.6:passwd:priikone:1333:1:1 [DenyConnection] [RedirectClient] -- 2.24.0