diff --git a/bird-rtrlib-cli.c b/bird-rtrlib-cli.c index 98a0f7c..c2af83b 100644 --- a/bird-rtrlib-cli.c +++ b/bird-rtrlib-cli.c @@ -19,6 +19,8 @@ * Website: https://github.com/rtrlib/bird-rtrlib-cli */ +#include +#include #include #include #include @@ -30,26 +32,35 @@ #include "cli.h" #include "config.h" #include "rtr.h" +#include #define CMD_EXIT "exit" - +#define BIRD_RSP_SIZE (200) // Socket to BIRD. static int bird_socket = -1; - // Buffer for BIRD commands. static char *bird_command = 0; - // Length of buffer for BIRD commands. static size_t bird_command_length = -1; - // "add roa" BIRD command "table" part. Defaults to an empty string and becomes // "table " + config->bird_roa_table if provided. static char *bird_add_roa_table_arg = ""; +// Main configuration. +static struct config config; + +/** + * Handle SIGPIPE on bird_socket, write error log entry + */ +void sigpipe_handler(int signum) +{ + syslog(LOG_ERR, "Caught SIGPIPE %d.", signum); +} /** * Performs cleanup on resources allocated by `init()`. */ -void cleanup(void) { +void cleanup(void) +{ closelog(); } @@ -57,9 +68,9 @@ void cleanup(void) { * Frees memory allocated with the " table " clause for the * "add roa" BIRD command. */ -void cleanup_bird_add_roa_table_arg(void) { - // If the buffer is "", it has never been changed, thus there is no malloc'd - // buffer. +void cleanup_bird_add_roa_table_arg(void) +{ + // free buffer only, if not empty - i.e., mem was malloc'd if (strcmp(bird_add_roa_table_arg, "") != 0) free(bird_add_roa_table_arg); } @@ -67,7 +78,8 @@ void cleanup_bird_add_roa_table_arg(void) { /** * Frees memory allocated with the BIRD command buffer. */ -void cleanup_bird_command(void) { +void cleanup_bird_command(void) +{ if (bird_command) { free(bird_command); bird_command = 0; @@ -78,7 +90,8 @@ void cleanup_bird_command(void) { /** * Initializes the application prerequisites. */ -void init(void) { +void init(void) +{ openlog(NULL, LOG_PERROR | LOG_CONS | LOG_PID, LOG_DAEMON); } @@ -86,13 +99,12 @@ void init(void) { * Creates and populates the "add roa" command's "table" argument buffer. * @param bird_roa_table */ -void init_bird_add_roa_table_arg(char *bird_roa_table) { +void init_bird_add_roa_table_arg(char *bird_roa_table) +{ // Size of the buffer (" table " + + \0). const size_t length = (8 + strlen(bird_roa_table)) * sizeof (char); - // Allocate buffer. bird_add_roa_table_arg = malloc(length); - // Populate buffer. snprintf(bird_add_roa_table_arg, length, " table %s", bird_roa_table); } @@ -100,7 +112,8 @@ void init_bird_add_roa_table_arg(char *bird_roa_table) { /** * Creates the buffer for the "add roa" command. */ -void init_bird_command(void) { +void init_bird_command(void) +{ // Size of the buffer ("add roa " + + "/" + + " max " + // + " as " + + + \0) bird_command_length = ( @@ -115,7 +128,6 @@ void init_bird_command(void) { strlen(bird_add_roa_table_arg) + // length of fixed " table " + 1 // \0 ) * sizeof (char); - // Allocate buffer. bird_command = malloc(bird_command_length); } @@ -134,13 +146,10 @@ static void pfx_update_callback(struct pfx_table *table, { // IP address buffer. static char ip_addr_str[INET6_ADDRSTRLEN]; - // Buffer for BIRD response. - static char bird_response[200]; - + static char bird_response[BIRD_RSP_SIZE]; // Fetch IP address as string. lrtr_ip_addr_to_str(&(record.prefix), ip_addr_str, sizeof(ip_addr_str)); - // Write BIRD command to buffer. if ( snprintf( @@ -159,14 +168,28 @@ static void pfx_update_callback(struct pfx_table *table, syslog(LOG_ERR, "BIRD command too long."); return; } - // Log the BIRD command and send it to the BIRD server. syslog(LOG_INFO, "To BIRD: %s", bird_command); - write(bird_socket, bird_command, strlen(bird_command)); - - // Fetch the answer and log. - bird_response[read(bird_socket, bird_response, sizeof(bird_response)-1)] = 0; - syslog(LOG_INFO, "From BIRD: %s", bird_response); + // reconnect bird_socket on SIGPIPE error, and resend BIRD command + while ((write(bird_socket, bird_command, strlen(bird_command)) < 0) && + (errno == EPIPE)) { + syslog(LOG_ERR, "BIRD socket send failed, try reconnect!"); + close(bird_socket); + bird_socket = bird_connect(config.bird_socket_path); + } + // Fetch BIRD answer, reconnect bird_socket on SIGPIPE while receive + int size = -1; + while (((size = read(bird_socket, bird_response, BIRD_RSP_SIZE-1)) <0) && + (errno == EPIPE)){ + syslog(LOG_ERR, "BIRD socket recv failed, try reconnect!"); + close(bird_socket); + bird_socket = bird_connect(config.bird_socket_path); + } + // log answer, if any valid response + if (size > 0) { + bird_response[size] = 0; + syslog(LOG_INFO, "From BIRD: %s", bird_response); + } } /** @@ -175,53 +198,45 @@ static void pfx_update_callback(struct pfx_table *table, * @param argv * @return */ -int main(int argc, char *argv[]) { - // Main configuration. - struct config config; - +int main(int argc, char *argv[]) +{ // Buffer for commands and its length. char *command = 0; size_t command_len = 0; - // Initialize variables. config_init(&config); - // Initialize framework. init(); - // Parse CLI arguments into config and bail out on error. if (!parse_cli(argc, argv, &config)) { cleanup(); + fprintf(stderr, "Invalid command line parameter!\n"); return EXIT_FAILURE; } - // Check config. - if (!config_check(&config)) { + if (config_check(&config)) { cleanup(); + fprintf(stderr, "Invalid configuration parameters!\n"); return EXIT_FAILURE; } - // Setup BIRD ROA table command argument. if (config.bird_roa_table) { init_bird_add_roa_table_arg(config.bird_roa_table); } - // Setup BIRD command buffer. init_bird_command(); - // Try to connect to BIRD and bail out on failure. bird_socket = bird_connect(config.bird_socket_path); if (bird_socket == -1) { cleanup(); + fprintf(stderr, "Failed to connect to BIRD socket!\n"); return EXIT_FAILURE; } struct tr_socket tr_sock; struct tr_tcp_config *tcp_config; struct tr_ssh_config *ssh_config; - - // Try to connect to the RTR server depending on the requested connection - // type. + // Try to connect to the RTR server depending on requested connection type. switch (config.rtr_connection_type) { case tcp: tcp_config = rtr_create_tcp_config( @@ -237,53 +252,56 @@ int main(int argc, char *argv[]) { break; default: cleanup(); + fprintf(stderr, "Invalid connection type, use tcp or ssh!\n"); return EXIT_FAILURE; } struct rtr_socket rtr; struct rtr_mgr_config *conf; struct rtr_mgr_group groups[1]; - + // init rtr_socket and groups rtr.tr_socket = &tr_sock; groups[0].sockets_len = 1; groups[0].sockets = malloc(1 * sizeof(rtr)); groups[0].sockets[0] = &rtr; groups[0].preference = 1; - + // init rtr_mgr int ret = rtr_mgr_init(&conf, groups, 1, 30, 600, 600, pfx_update_callback, NULL, NULL, NULL); - - if (ret == RTR_ERROR) - printf("Error in rtr_mgr_init!\n"); - else if (ret == RTR_INVALID_PARAM) - printf("Invalid params passed to rtr_mgr_init\n"); - - if (!conf) + // check for init errors + if (ret == RTR_ERROR) { + fprintf(stderr, "Error in rtr_mgr_init!\n"); return EXIT_FAILURE; - + } + else if (ret == RTR_INVALID_PARAM) { + fprintf(stderr, "Invalid params passed to rtr_mgr_init\n"); + return EXIT_FAILURE; + } + // check if rtr_mgr config valid + if (!conf) { + fprintf(stderr, "No config for rtr manager!\n"); + return EXIT_FAILURE; + } + // set handler for SIGPIPE + signal(SIGPIPE, sigpipe_handler); + // start rtr_mgr rtr_mgr_start(conf); - // Server loop. Read commands from stdin. while (getline(&command, &command_len, stdin) != -1) { if (strncmp(command, CMD_EXIT, strlen(CMD_EXIT)) == 0) break; } - // Clean up RTRLIB memory. rtr_mgr_stop(conf); rtr_mgr_free(conf); free(groups[0].sockets); - // Close BIRD socket. close(bird_socket); - // Cleanup memory. cleanup_bird_command(); cleanup_bird_add_roa_table_arg(); - // Cleanup framework. cleanup(); - // Exit with success. return EXIT_SUCCESS; } diff --git a/bird.c b/bird.c index 47899e1..bf99321 100644 --- a/bird.c +++ b/bird.c @@ -27,38 +27,33 @@ #include "bird.h" -int bird_connect(const char *socket_path) { +int bird_connect(const char *socket_path) +{ // Result value containing the socket to the BIRD. int bird_socket = -1; - // Socket address to the BIRD. struct sockaddr_un addr; - // Check socket path length. if (strlen(socket_path) >= sizeof addr.sun_path) { syslog(LOG_EMERG, "Socket path too long"); return -1; } - // Create socket and bail out on error. bird_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (bird_socket < 0) { syslog(LOG_EMERG, "Socket creation error: %m"); return -1; } - // Create socket address. memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, socket_path); - // Try to connect to BIRD. if (connect(bird_socket, (struct sockaddr *) &addr, sizeof addr) == -1) { syslog(LOG_EMERG, "BIRD connection to %s failed: %m", socket_path); close(bird_socket); return -1; } - // Return socket. return bird_socket; } diff --git a/cli.c b/cli.c index 6dbca2c..58b1f30 100644 --- a/cli.c +++ b/cli.c @@ -35,10 +35,10 @@ #define ARGKEY_RTRSSH_USERNAME 0x103 // Parser function for argp_parse(). -static error_t argp_parser(int key, char *arg, struct argp_state *state) { +static error_t argp_parser(int key, char *arg, struct argp_state *state) +{ // Shortcut to config object passed to argp_parse(). struct config *config = state->input; - // Process command line argument. switch (key) { case ARGKEY_BIRD_ROA_TABLE: @@ -71,13 +71,13 @@ static error_t argp_parser(int key, char *arg, struct argp_state *state) { // Process unknown argument. return ARGP_ERR_UNKNOWN; } - // Return success. return 0; } // Parses the specified command line arguments into the program config. -int parse_cli(int argc, char **argv, struct config *config) { +int parse_cli(int argc, char **argv, struct config *config) +{ // Command line options definition. const struct argp_option argp_options[] = { { @@ -150,7 +150,6 @@ int parse_cli(int argc, char **argv, struct config *config) { }, {0} }; - // argp structure to be passed to argp_parse(). const struct argp argp = { argp_options, @@ -161,10 +160,8 @@ int parse_cli(int argc, char **argv, struct config *config) { NULL, NULL }; - // Parse command line. Exits on errors. argp_parse(&argp, argc, argv, 0, NULL, config); - // Return success. return 1; } diff --git a/config.c b/config.c index 0104c3e..f5e68c0 100644 --- a/config.c +++ b/config.c @@ -25,51 +25,48 @@ #include "config.h" /** - * Checks the specified application config for errors. Returns `1` if it has no - * errors, or `0` otherwise. + * Checks the specified application config for errors. Returns 0 on success, + * or >0 otherwise. * @param config * @return */ -int config_check(const struct config *config) { +int config_check(const struct config *config) +{ // Check BIRD control socket path availability. if (!config->bird_socket_path) { fprintf(stderr, "Missing path to BIRD control socket.\n"); - return 0; + return 1; } - // Check RTR host availability. if (!config->rtr_host) { fprintf(stderr, "Missing RTR server host.\n"); - return 0; + return 1; } - // Check RTR port availability. if (!config->rtr_port) { fprintf(stderr, "Missing RTR server port.\n"); - return 0; + return 1; } - // Checks to be done for SSH connections. if (config->rtr_connection_type == ssh) { // Check SSH username availability. if (!config->rtr_ssh_username) { fprintf(stderr, "Missing SSH username.\n"); - return 0; + return 1; } } - // Return success. - return 1; + return 0; } /** * Initializes the specified application configuration. * @param config */ -void config_init(struct config *config) { +void config_init(struct config *config) +{ // Reset memory. memset(config, 0, sizeof (struct config)); - // Default connection type is TCP. config->rtr_connection_type = tcp; } diff --git a/config.h b/config.h index 2dce8e3..0bb1314 100644 --- a/config.h +++ b/config.h @@ -24,11 +24,8 @@ /// Specifies a type of server connection to be used. enum connection_type { - // Plain TCP connection - tcp, - - // SSH connection - ssh + tcp, // Plain TCP connection + ssh // SSH connection }; /** diff --git a/rtr.c b/rtr.c index 4aa192c..e22a363 100644 --- a/rtr.c +++ b/rtr.c @@ -35,7 +35,6 @@ struct tr_ssh_config *rtr_create_ssh_config(const char *host, // Initialize result. struct tr_ssh_config *result = malloc(sizeof (struct tr_ssh_config)); memset(result, 0, sizeof (struct tr_ssh_config)); - // Assign host, port and username (mandatory). if (host) result->host = strdup(host); @@ -45,28 +44,25 @@ struct tr_ssh_config *rtr_create_ssh_config(const char *host, } if (username) result->username = strdup(username); - // Assign bind address if available. if (bindaddr) result->bindaddr = strdup(bindaddr); - // Assign key paths (optional). if (server_hostkey_path) result->server_hostkey_path = strdup(server_hostkey_path); if (client_privkey_path) result->client_privkey_path = strdup(client_privkey_path); - // Return result. return result; } -struct tr_tcp_config *rtr_create_tcp_config( - const char *host, const char *port, const char *bindaddr) +struct tr_tcp_config *rtr_create_tcp_config(const char *host, + const char *port, + const char *bindaddr) { // Initialize result. - struct tr_tcp_config *result = malloc(sizeof (struct tr_tcp_config)); - memset(result, 0, sizeof (struct tr_tcp_config)); - + struct tr_tcp_config *result = malloc(sizeof(struct tr_tcp_config)); + memset(result, 0, sizeof(struct tr_tcp_config)); // Populate result. if(host) result->host = strdup(host); @@ -74,7 +70,6 @@ struct tr_tcp_config *rtr_create_tcp_config( result->port = strdup(port); if (bindaddr) result->bindaddr = strdup(bindaddr); - // Return result. return result; }