diff --git a/qdevices/msg.c b/qdevices/msg.c index c3178171..4fd3e4a7 100644 --- a/qdevices/msg.c +++ b/qdevices/msg.c @@ -1,1005 +1,1018 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "msg.h" #define MSG_TYPE_LENGTH 2 #define MSG_LENGTH_LENGTH 4 #define MSG_STATIC_SUPPORTED_MESSAGES_SIZE 16 enum msg_type msg_static_supported_messages[MSG_STATIC_SUPPORTED_MESSAGES_SIZE] = { MSG_TYPE_PREINIT, MSG_TYPE_PREINIT_REPLY, MSG_TYPE_STARTTLS, MSG_TYPE_INIT, MSG_TYPE_INIT_REPLY, MSG_TYPE_SERVER_ERROR, MSG_TYPE_SET_OPTION, MSG_TYPE_SET_OPTION_REPLY, MSG_TYPE_ECHO_REQUEST, MSG_TYPE_ECHO_REPLY, MSG_TYPE_NODE_LIST, MSG_TYPE_NODE_LIST_REPLY, MSG_TYPE_ASK_FOR_VOTE, MSG_TYPE_ASK_FOR_VOTE_REPLY, MSG_TYPE_VOTE_INFO, MSG_TYPE_VOTE_INFO_REPLY, }; size_t msg_get_header_length(void) { return (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH); } static void msg_add_type(struct dynar *msg, enum msg_type type) { uint16_t ntype; ntype = htons((uint16_t)type); dynar_cat(msg, &ntype, sizeof(ntype)); } enum msg_type msg_get_type(const struct dynar *msg) { uint16_t ntype; uint16_t type; memcpy(&ntype, dynar_data(msg), sizeof(ntype)); type = ntohs(ntype); return (type); } /* * We don't know size of message before call of this function, so zero is * added. Real value is set afterwards by msg_set_len. */ static void msg_add_len(struct dynar *msg) { uint32_t len; len = 0; dynar_cat(msg, &len, sizeof(len)); } static void msg_set_len(struct dynar *msg, uint32_t len) { uint32_t nlen; nlen = htonl(len); memcpy(dynar_data(msg) + MSG_TYPE_LENGTH, &nlen, sizeof(nlen)); } /* * Used only for echo reply msg. All other messages should use msg_add_type. */ static void msg_set_type(struct dynar *msg, enum msg_type type) { uint16_t ntype; ntype = htons((uint16_t)type); memcpy(dynar_data(msg), &ntype, sizeof(ntype)); } uint32_t msg_get_len(const struct dynar *msg) { uint32_t nlen; uint32_t len; memcpy(&nlen, dynar_data(msg) + MSG_TYPE_LENGTH, sizeof(nlen)); len = ntohl(nlen); return (len); } size_t msg_create_preinit(struct dynar *msg, const char *cluster_name, int add_msg_seq_number, uint32_t msg_seq_number) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_PREINIT); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (tlv_add_cluster_name(msg, cluster_name) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_preinit_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_tls_supported tls_supported, int tls_client_cert_required) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_PREINIT_REPLY); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (tlv_add_tls_supported(msg, tls_supported) == -1) { goto small_buf_err; } if (tlv_add_tls_client_cert_required(msg, tls_client_cert_required) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_starttls(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_STARTTLS); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_server_error(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_reply_error_code reply_error_code) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_SERVER_ERROR); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (tlv_add_reply_error_code(msg, reply_error_code) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } static uint16_t * msg_convert_msg_type_array_to_u16_array(const enum msg_type *msg_type_array, size_t array_size) { uint16_t *u16a; size_t i; u16a = malloc(sizeof(*u16a) * array_size); if (u16a == NULL) { return (NULL); } for (i = 0; i < array_size; i++) { u16a[i] = (uint16_t)msg_type_array[i]; } return (u16a); } size_t msg_create_init(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_decision_algorithm_type decision_algorithm, const enum msg_type *supported_msgs, size_t no_supported_msgs, const enum tlv_opt_type *supported_opts, size_t no_supported_opts, uint32_t node_id, - uint32_t heartbeat_interval, const struct tlv_tie_breaker *tie_breaker) + uint32_t heartbeat_interval, const struct tlv_tie_breaker *tie_breaker, + const struct tlv_ring_id *ring_id) { uint16_t *u16a; int res; u16a = NULL; dynar_clean(msg); msg_add_type(msg, MSG_TYPE_INIT); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (supported_msgs != NULL && no_supported_msgs > 0) { u16a = msg_convert_msg_type_array_to_u16_array(supported_msgs, no_supported_msgs); if (u16a == NULL) { goto small_buf_err; } res = tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_MESSAGES, u16a, no_supported_msgs); free(u16a); if (res == -1) { goto small_buf_err; } } if (supported_opts != NULL && no_supported_opts > 0) { if (tlv_add_supported_options(msg, supported_opts, no_supported_opts) == -1) { goto small_buf_err; } } - if (tlv_add_node_id(msg, node_id) == -1) { + if (tlv_add_node_id(msg, node_id) == -1) { goto small_buf_err; - } + } if (tlv_add_decision_algorithm(msg, decision_algorithm) == -1) { goto small_buf_err; } if (tlv_add_heartbeat_interval(msg, heartbeat_interval) == -1) { goto small_buf_err; } if (tlv_add_tie_breaker(msg, tie_breaker) == -1) { goto small_buf_err; } + if (tlv_add_ring_id(msg, ring_id) == -1) { + goto small_buf_err; + } + msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_init_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_reply_error_code reply_error_code, const enum msg_type *supported_msgs, size_t no_supported_msgs, const enum tlv_opt_type *supported_opts, size_t no_supported_opts, size_t server_maximum_request_size, size_t server_maximum_reply_size, const enum tlv_decision_algorithm_type *supported_decision_algorithms, size_t no_supported_decision_algorithms) { uint16_t *u16a; int res; u16a = NULL; dynar_clean(msg); msg_add_type(msg, MSG_TYPE_INIT_REPLY); msg_add_len(msg); if (tlv_add_reply_error_code(msg, reply_error_code) == -1) { goto small_buf_err; } if (supported_msgs != NULL && no_supported_msgs > 0) { u16a = msg_convert_msg_type_array_to_u16_array(supported_msgs, no_supported_msgs); if (u16a == NULL) { goto small_buf_err; } res = tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_MESSAGES, u16a, no_supported_msgs); free(u16a); if (res == -1) { goto small_buf_err; } } if (supported_opts != NULL && no_supported_opts > 0) { if (tlv_add_supported_options(msg, supported_opts, no_supported_opts) == -1) { goto small_buf_err; } } if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (tlv_add_server_maximum_request_size(msg, server_maximum_request_size) == -1) { goto small_buf_err; } if (tlv_add_server_maximum_reply_size(msg, server_maximum_reply_size) == -1) { goto small_buf_err; } if (supported_decision_algorithms != NULL && no_supported_decision_algorithms > 0) { if (tlv_add_supported_decision_algorithms(msg, supported_decision_algorithms, no_supported_decision_algorithms) == -1) { goto small_buf_err; } } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_set_option(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, int add_heartbeat_interval, uint32_t heartbeat_interval) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_SET_OPTION); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (add_heartbeat_interval) { if (tlv_add_heartbeat_interval(msg, heartbeat_interval) == -1) { goto small_buf_err; } } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_set_option_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, uint32_t heartbeat_interval) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_SET_OPTION_REPLY); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } if (tlv_add_heartbeat_interval(msg, heartbeat_interval) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_echo_request(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_ECHO_REQUEST); msg_add_len(msg); if (add_msg_seq_number) { if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_echo_reply(struct dynar *msg, const struct dynar *echo_request_msg) { dynar_clean(msg); if (dynar_cat(msg, dynar_data(echo_request_msg), dynar_size(echo_request_msg)) == -1) { goto small_buf_err; } msg_set_type(msg, MSG_TYPE_ECHO_REPLY); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_node_list(struct dynar *msg, uint32_t msg_seq_number, enum tlv_node_list_type node_list_type, int add_ring_id, const struct tlv_ring_id *ring_id, int add_config_version, uint64_t config_version, int add_quorate, enum tlv_quorate quorate, const struct node_list *nodes) { struct node_list_entry *node_info; struct tlv_node_info tlv_ni; dynar_clean(msg); msg_add_type(msg, MSG_TYPE_NODE_LIST); msg_add_len(msg); if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } if (tlv_add_node_list_type(msg, node_list_type) == -1) { goto small_buf_err; } if (add_ring_id) { if (tlv_add_ring_id(msg, ring_id) == -1) { goto small_buf_err; } } if (add_config_version) { if (tlv_add_config_version(msg, config_version) == -1) { goto small_buf_err; } } if (add_quorate) { if (tlv_add_quorate(msg, quorate) == -1) { goto small_buf_err; } } TAILQ_FOREACH(node_info, nodes, entries) { node_list_entry_to_tlv_node_info(node_info, &tlv_ni); if (tlv_add_node_info(msg, &tlv_ni) == -1) { goto small_buf_err; } } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number, - enum tlv_node_list_type node_list_type, int add_ring_id, const struct tlv_ring_id *ring_id, + enum tlv_node_list_type node_list_type, const struct tlv_ring_id *ring_id, enum tlv_vote vote) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_NODE_LIST_REPLY); msg_add_len(msg); if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } if (tlv_add_node_list_type(msg, node_list_type) == -1) { goto small_buf_err; } - if (add_ring_id) { - if (tlv_add_ring_id(msg, ring_id) == -1) { - goto small_buf_err; - } + if (tlv_add_ring_id(msg, ring_id) == -1) { + goto small_buf_err; } if (tlv_add_vote(msg, vote) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_ask_for_vote(struct dynar *msg, uint32_t msg_seq_number) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_ASK_FOR_VOTE); msg_add_len(msg); if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t -msg_create_ask_for_vote_reply(struct dynar *msg, uint32_t msg_seq_number, enum tlv_vote vote) +msg_create_ask_for_vote_reply(struct dynar *msg, uint32_t msg_seq_number, + const struct tlv_ring_id *ring_id, enum tlv_vote vote) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_ASK_FOR_VOTE_REPLY); msg_add_len(msg); if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } if (tlv_add_vote(msg, vote) == -1) { goto small_buf_err; } + if (tlv_add_ring_id(msg, ring_id) == -1) { + goto small_buf_err; + } + msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t -msg_create_vote_info(struct dynar *msg, uint32_t msg_seq_number, enum tlv_vote vote) +msg_create_vote_info(struct dynar *msg, uint32_t msg_seq_number, const struct tlv_ring_id *ring_id, + enum tlv_vote vote) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_VOTE_INFO); msg_add_len(msg); if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } if (tlv_add_vote(msg, vote) == -1) { goto small_buf_err; } + if (tlv_add_ring_id(msg, ring_id) == -1) { + goto small_buf_err; + } + msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } size_t msg_create_vote_info_reply(struct dynar *msg, uint32_t msg_seq_number) { dynar_clean(msg); msg_add_type(msg, MSG_TYPE_VOTE_INFO_REPLY); msg_add_len(msg); if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) { goto small_buf_err; } msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH)); return (dynar_size(msg)); small_buf_err: return (0); } int msg_is_valid_msg_type(const struct dynar *msg) { enum msg_type type; size_t i; type = msg_get_type(msg); for (i = 0; i < MSG_STATIC_SUPPORTED_MESSAGES_SIZE; i++) { if (msg_static_supported_messages[i] == type) { return (1); } } return (0); } void msg_decoded_init(struct msg_decoded *decoded_msg) { memset(decoded_msg, 0, sizeof(*decoded_msg)); node_list_init(&decoded_msg->nodes); } void msg_decoded_destroy(struct msg_decoded *decoded_msg) { free(decoded_msg->cluster_name); free(decoded_msg->supported_messages); free(decoded_msg->supported_options); free(decoded_msg->supported_decision_algorithms); node_list_free(&decoded_msg->nodes); msg_decoded_init(decoded_msg); } /* * 0 - No error * -1 - option with invalid length * -2 - Unable to allocate memory * -3 - Inconsistent msg (tlv len > msg size) * -4 - invalid option content */ int msg_decode(const struct dynar *msg, struct msg_decoded *decoded_msg) { struct tlv_iterator tlv_iter; uint16_t *u16a; uint32_t u32; uint64_t u64; struct tlv_ring_id ring_id; struct tlv_node_info node_info; struct tlv_tie_breaker tie_breaker; size_t zi; enum tlv_opt_type opt_type; int iter_res; int res; msg_decoded_destroy(decoded_msg); decoded_msg->type = msg_get_type(msg); tlv_iter_init(msg, msg_get_header_length(), &tlv_iter); while ((iter_res = tlv_iter_next(&tlv_iter)) > 0) { opt_type = tlv_iter_get_type(&tlv_iter); switch (opt_type) { case TLV_OPT_MSG_SEQ_NUMBER: if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) { return (res); } decoded_msg->seq_number_set = 1; decoded_msg->seq_number = u32; break; case TLV_OPT_CLUSTER_NAME: if ((res = tlv_iter_decode_str(&tlv_iter, &decoded_msg->cluster_name, &decoded_msg->cluster_name_len)) != 0) { return (-2); } break; case TLV_OPT_TLS_SUPPORTED: if ((res = tlv_iter_decode_tls_supported(&tlv_iter, &decoded_msg->tls_supported)) != 0) { return (res); } decoded_msg->tls_supported_set = 1; break; case TLV_OPT_TLS_CLIENT_CERT_REQUIRED: if ((res = tlv_iter_decode_client_cert_required(&tlv_iter, &decoded_msg->tls_client_cert_required)) != 0) { return (res); } decoded_msg->tls_client_cert_required_set = 1; break; case TLV_OPT_SUPPORTED_MESSAGES: free(decoded_msg->supported_messages); if ((res = tlv_iter_decode_u16_array(&tlv_iter, &u16a, &decoded_msg->no_supported_messages)) != 0) { return (res); } decoded_msg->supported_messages = malloc(sizeof(enum msg_type) * decoded_msg->no_supported_messages); if (decoded_msg->supported_messages == NULL) { free(u16a); return (-2); } for (zi = 0; zi < decoded_msg->no_supported_messages; zi++) { decoded_msg->supported_messages[zi] = (enum msg_type)u16a[zi]; } free(u16a); break; case TLV_OPT_SUPPORTED_OPTIONS: free(decoded_msg->supported_options); if ((res = tlv_iter_decode_supported_options(&tlv_iter, &decoded_msg->supported_options, &decoded_msg->no_supported_options)) != 0) { return (res); } break; case TLV_OPT_REPLY_ERROR_CODE: if ((res = tlv_iter_decode_reply_error_code(&tlv_iter, &decoded_msg->reply_error_code)) != 0) { return (res); } decoded_msg->reply_error_code_set = 1; break; case TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE: if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) { return (res); } decoded_msg->server_maximum_request_size_set = 1; decoded_msg->server_maximum_request_size = u32; break; case TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE: if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) { return (res); } decoded_msg->server_maximum_reply_size_set = 1; decoded_msg->server_maximum_reply_size = u32; break; case TLV_OPT_NODE_ID: if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) { return (res); } decoded_msg->node_id_set = 1; decoded_msg->node_id = u32; break; case TLV_OPT_SUPPORTED_DECISION_ALGORITHMS: free(decoded_msg->supported_decision_algorithms); if ((res = tlv_iter_decode_supported_decision_algorithms(&tlv_iter, &decoded_msg->supported_decision_algorithms, &decoded_msg->no_supported_decision_algorithms)) != 0) { return (res); } break; case TLV_OPT_DECISION_ALGORITHM: if ((res = tlv_iter_decode_decision_algorithm(&tlv_iter, &decoded_msg->decision_algorithm)) != 0) { return (res); } decoded_msg->decision_algorithm_set = 1; break; case TLV_OPT_HEARTBEAT_INTERVAL: if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) { return (res); } decoded_msg->heartbeat_interval_set = 1; decoded_msg->heartbeat_interval = u32; break; case TLV_OPT_RING_ID: if ((res = tlv_iter_decode_ring_id(&tlv_iter, &ring_id)) != 0) { return (res); } decoded_msg->ring_id_set = 1; memcpy(&decoded_msg->ring_id, &ring_id, sizeof(ring_id)); break; case TLV_OPT_CONFIG_VERSION: if ((res = tlv_iter_decode_u64(&tlv_iter, &u64)) != 0) { return (res); } decoded_msg->config_version_set = 1; decoded_msg->config_version = u64; break; case TLV_OPT_DATA_CENTER_ID: if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) { return (res); } decoded_msg->data_center_id = u32; break; case TLV_OPT_NODE_STATE: if ((res = tlv_iter_decode_node_state(&tlv_iter, &decoded_msg->node_state)) != 0) { return (res); } break; case TLV_OPT_NODE_INFO: if ((res = tlv_iter_decode_node_info(&tlv_iter, &node_info)) != 0) { return (res); } if (node_list_add_from_node_info(&decoded_msg->nodes, &node_info) == NULL) { return (-2); } break; case TLV_OPT_NODE_LIST_TYPE: if ((res = tlv_iter_decode_node_list_type(&tlv_iter, &decoded_msg->node_list_type)) != 0) { return (res); } decoded_msg->node_list_type_set = 1; break; case TLV_OPT_VOTE: if ((res = tlv_iter_decode_vote(&tlv_iter, &decoded_msg->vote)) != 0) { return (res); } decoded_msg->vote_set = 1; break; case TLV_OPT_QUORATE: if ((res = tlv_iter_decode_quorate(&tlv_iter, &decoded_msg->quorate)) != 0) { return (res); } decoded_msg->quorate_set = 1; break; case TLV_OPT_TIE_BREAKER: if ((res = tlv_iter_decode_tie_breaker(&tlv_iter, &tie_breaker)) != 0) { return (res); } decoded_msg->tie_breaker_set = 1; memcpy(&decoded_msg->tie_breaker, &tie_breaker, sizeof(tie_breaker)); break; /* * Default is not defined intentionally. Compiler shows warning when * new tlv option is added. Also protocol ignores unknown options so * no extra work is needed. */ } } if (iter_res != 0) { return (-3); } return (0); } void msg_get_supported_messages(enum msg_type **supported_messages, size_t *no_supported_messages) { *supported_messages = msg_static_supported_messages; *no_supported_messages = MSG_STATIC_SUPPORTED_MESSAGES_SIZE; } const char * msg_type_to_str(enum msg_type type) { switch (type) { case MSG_TYPE_PREINIT: return ("Preinit"); break; case MSG_TYPE_PREINIT_REPLY: return ("Preinit reply"); break; case MSG_TYPE_STARTTLS: return ("StartTLS"); break; case MSG_TYPE_INIT: return ("Init"); break; case MSG_TYPE_INIT_REPLY: return ("Init reply"); break; case MSG_TYPE_SERVER_ERROR: return ("Server error"); break; case MSG_TYPE_SET_OPTION: return ("Set option"); break; case MSG_TYPE_SET_OPTION_REPLY: return ("Set option reply"); break; case MSG_TYPE_ECHO_REQUEST: return ("Echo request"); break; case MSG_TYPE_ECHO_REPLY: return ("Echo reply"); break; case MSG_TYPE_NODE_LIST: return ("Node list"); break; case MSG_TYPE_NODE_LIST_REPLY: return ("Node list reply"); break; case MSG_TYPE_ASK_FOR_VOTE: return ("Ask for vote"); break; case MSG_TYPE_ASK_FOR_VOTE_REPLY: return ("Ask for vote reply"); break; case MSG_TYPE_VOTE_INFO: return ("Vote info"); break; case MSG_TYPE_VOTE_INFO_REPLY: return ("Vote info reply"); break; } return ("Unknown message type"); } diff --git a/qdevices/msg.h b/qdevices/msg.h index 2f142adb..b36d6137 100644 --- a/qdevices/msg.h +++ b/qdevices/msg.h @@ -1,200 +1,201 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _MSG_H_ #define _MSG_H_ #include #include #include "dynar.h" #include "tlv.h" #include "node-list.h" #ifdef __cplusplus extern "C" { #endif enum msg_type { MSG_TYPE_PREINIT = 0, MSG_TYPE_PREINIT_REPLY = 1, MSG_TYPE_STARTTLS = 2, MSG_TYPE_INIT = 3, MSG_TYPE_INIT_REPLY = 4, MSG_TYPE_SERVER_ERROR = 5, MSG_TYPE_SET_OPTION = 6, MSG_TYPE_SET_OPTION_REPLY = 7, MSG_TYPE_ECHO_REQUEST = 8, MSG_TYPE_ECHO_REPLY = 9, MSG_TYPE_NODE_LIST = 10, MSG_TYPE_NODE_LIST_REPLY = 11, MSG_TYPE_ASK_FOR_VOTE = 12, MSG_TYPE_ASK_FOR_VOTE_REPLY = 13, MSG_TYPE_VOTE_INFO = 14, MSG_TYPE_VOTE_INFO_REPLY = 15, }; struct msg_decoded { enum msg_type type; uint8_t seq_number_set; uint32_t seq_number; /* Only valid if seq_number_set != 0 */ size_t cluster_name_len; /* Valid only if != NULL. Trailing \0 is added but not counted in cluster_name_len */ char *cluster_name; uint8_t tls_supported_set; enum tlv_tls_supported tls_supported; /* Valid only if tls_supported_set != 0. */ uint8_t tls_client_cert_required_set; uint8_t tls_client_cert_required; /* Valid only if tls_client_cert_required_set != 0 */ size_t no_supported_messages; enum msg_type *supported_messages; /* Valid only if != NULL */ size_t no_supported_options; enum tlv_opt_type *supported_options; /* Valid only if != NULL */ uint8_t reply_error_code_set; enum tlv_reply_error_code reply_error_code; /* Valid only if reply_error_code_set != 0 */ uint8_t server_maximum_request_size_set; /* Valid only if server_maximum_request_size_set != 0 */ size_t server_maximum_request_size; uint8_t server_maximum_reply_size_set; size_t server_maximum_reply_size; /* Valid only if server_maximum_reply_size_set != 0 */ uint8_t node_id_set; uint32_t node_id; size_t no_supported_decision_algorithms; /* Valid only if != NULL */ enum tlv_decision_algorithm_type *supported_decision_algorithms; uint8_t decision_algorithm_set; /* Valid only if decision_algorithm_set != 0 */ enum tlv_decision_algorithm_type decision_algorithm; uint8_t heartbeat_interval_set; uint32_t heartbeat_interval; /* Valid only if heartbeat_interval_set != 0 */ uint8_t ring_id_set; struct tlv_ring_id ring_id; /* Valid only if ring_id_set != 0 */ uint8_t config_version_set; uint64_t config_version; /* Valid only if config_version_set != 0 */ uint32_t data_center_id; /* Valid only if != 0 */ enum tlv_node_state node_state; /* Valid only if != TLV_NODE_STATE_NOT_SET */ struct node_list nodes; /* Valid only if node_list_is_empty(nodes) != 0 */ int node_list_type_set; enum tlv_node_list_type node_list_type; /* Valid only if node_list_type_set != 0 */ int vote_set; enum tlv_vote vote; /* Valid only if vote_set != 0 */ int quorate_set; enum tlv_quorate quorate; /* Valid only if quorate_set != 0 */ int tie_breaker_set; struct tlv_tie_breaker tie_breaker; }; extern size_t msg_create_preinit(struct dynar *msg, const char *cluster_name, int add_msg_seq_number, uint32_t msg_seq_number); extern size_t msg_create_preinit_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_tls_supported tls_supported, int tls_client_cert_required); extern size_t msg_create_starttls(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number); extern size_t msg_create_init(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_decision_algorithm_type decision_algorithm, const enum msg_type *supported_msgs, size_t no_supported_msgs, const enum tlv_opt_type *supported_opts, size_t no_supported_opts, uint32_t node_id, - uint32_t heartbeat_interval, const struct tlv_tie_breaker *tie_breaker); + uint32_t heartbeat_interval, const struct tlv_tie_breaker *tie_breaker, + const struct tlv_ring_id *ring_id); extern size_t msg_create_server_error(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_reply_error_code reply_error_code); extern size_t msg_create_init_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_reply_error_code reply_error_code, const enum msg_type *supported_msgs, size_t no_supported_msgs, const enum tlv_opt_type *supported_opts, size_t no_supported_opts, size_t server_maximum_request_size, size_t server_maximum_reply_size, const enum tlv_decision_algorithm_type *supported_decision_algorithms, size_t no_supported_decision_algorithms); extern size_t msg_create_set_option(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, int add_heartbeat_interval, uint32_t heartbeat_interval); extern size_t msg_create_set_option_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number, uint32_t heartbeat_interval); extern size_t msg_create_echo_request(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number); extern size_t msg_create_echo_reply(struct dynar *msg, const struct dynar *echo_request_msg); extern size_t msg_create_node_list(struct dynar *msg, uint32_t msg_seq_number, enum tlv_node_list_type node_list_type, int add_ring_id, const struct tlv_ring_id *ring_id, int add_config_version, uint64_t config_version, int add_quorate, enum tlv_quorate quorate, const struct node_list *nodes); extern size_t msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number, - enum tlv_node_list_type node_list_type, int add_ring_id, const struct tlv_ring_id *ring_id, + enum tlv_node_list_type node_list_type, const struct tlv_ring_id *ring_id, enum tlv_vote vote); extern size_t msg_create_ask_for_vote(struct dynar *msg, uint32_t msg_seq_number); extern size_t msg_create_ask_for_vote_reply(struct dynar *msg, uint32_t msg_seq_number, - enum tlv_vote vote); + const struct tlv_ring_id *ring_id, enum tlv_vote vote); extern size_t msg_create_vote_info(struct dynar *msg, uint32_t msg_seq_number, - enum tlv_vote vote); + const struct tlv_ring_id *ring_id, enum tlv_vote vote); extern size_t msg_create_vote_info_reply(struct dynar *msg, uint32_t msg_seq_number); extern size_t msg_get_header_length(void); extern uint32_t msg_get_len(const struct dynar *msg); extern enum msg_type msg_get_type(const struct dynar *msg); extern int msg_is_valid_msg_type(const struct dynar *msg); extern void msg_decoded_init(struct msg_decoded *decoded_msg); extern void msg_decoded_destroy(struct msg_decoded *decoded_msg); extern int msg_decode(const struct dynar *msg, struct msg_decoded *decoded_msg); extern void msg_get_supported_messages(enum msg_type **supported_messages, size_t *no_supported_messages); extern const char * msg_type_to_str(enum msg_type type); #ifdef __cplusplus } #endif #endif /* _MSG_H_ */ diff --git a/qdevices/nss-sock.c b/qdevices/nss-sock.c index bdc73ae8..f08ac88a 100644 --- a/qdevices/nss-sock.c +++ b/qdevices/nss-sock.c @@ -1,479 +1,479 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "nss-sock.h" int nss_sock_init_nss(char *config_dir) { if (config_dir == NULL) { if (NSS_NoDB_Init(NULL) != SECSuccess) { return (-1); } } else { if (NSS_Init(config_dir) != SECSuccess) { return (-1); } } if (NSS_SetDomesticPolicy() != SECSuccess) { return (-1); } return (0); } /* * Set NSS socket non-blocking */ int nss_sock_set_non_blocking(PRFileDesc *sock) { PRSocketOptionData sock_opt; memset(&sock_opt, 0, sizeof(sock_opt)); sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = PR_TRUE; if (PR_SetSocketOption(sock, &sock_opt) != PR_SUCCESS) { return (-1); } return (0); } /* * Create TCP socket with af family. If reuse_addr is set, socket option * for reuse address is set. */ static PRFileDesc * nss_sock_create_socket(PRIntn af, int reuse_addr) { PRFileDesc *sock; PRSocketOptionData socket_option; sock = PR_OpenTCPSocket(af); if (sock == NULL) { return (NULL); } if (reuse_addr) { socket_option.option = PR_SockOpt_Reuseaddr; socket_option.value.reuse_addr = PR_TRUE; if (PR_SetSocketOption(sock, &socket_option) != PR_SUCCESS) { return (NULL); - } + } } return (sock); } /* * Create listen socket and bind it to address. hostname can be NULL and then * any address is used. Address family (af) can be ether PR_AF_INET6, * PR_AF_INET or PR_AF_UNSPEC. */ PRFileDesc * nss_sock_create_listen_socket(const char *hostname, uint16_t port, PRIntn af) { PRNetAddr addr; PRFileDesc *sock; PRAddrInfo *addr_info; void *addr_iter; sock = NULL; if (hostname == NULL) { memset(&addr, 0, sizeof(addr)); if (PR_InitializeNetAddr(PR_IpAddrAny, port, &addr) != PR_SUCCESS) { return (NULL); } if (af == PR_AF_UNSPEC) { af = PR_AF_INET6; } addr.raw.family = af; sock = nss_sock_create_socket(af, 1); if (sock == NULL) { return (NULL); } if (PR_Bind(sock, &addr) != PR_SUCCESS) { PR_Close(sock); return (NULL); } } else { addr_info = PR_GetAddrInfoByName(hostname, af, PR_AI_ADDRCONFIG); if (addr_info == NULL) { return (NULL); } addr_iter = NULL; while ((addr_iter = PR_EnumerateAddrInfo(addr_iter, addr_info, port, &addr)) != NULL) { if (af == PR_AF_UNSPEC || addr.raw.family == af) { sock = nss_sock_create_socket(addr.raw.family, 1); if (sock == NULL) { continue; } if (PR_Bind(sock, &addr) != PR_SUCCESS) { PR_Close(sock); sock = NULL; continue; } /* * Socket is sucesfully bound */ break; } } PR_FreeAddrInfo(addr_info); if (sock == NULL) { /* * No address succeeded */ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, 0); return (NULL); } } return (sock); } PRFileDesc * nss_sock_create_client_socket(const char *hostname, uint16_t port, PRIntn af, PRIntervalTime timeout) { PRNetAddr addr; PRFileDesc *sock; PRAddrInfo *addr_info; void *addr_iter; PRStatus res; int connect_failed; PRIntn tmp_af; sock = NULL; connect_failed = 0; tmp_af = af; if (af == PR_AF_INET6) { tmp_af = PR_AF_UNSPEC; } addr_info = PR_GetAddrInfoByName(hostname, tmp_af, PR_AI_ADDRCONFIG); if (addr_info == NULL) { return (NULL); } addr_iter = NULL; while ((addr_iter = PR_EnumerateAddrInfo(addr_iter, addr_info, port, &addr)) != NULL) { if (af != PR_AF_UNSPEC && addr.raw.family != af) { continue; } sock = nss_sock_create_socket(addr.raw.family, 0); if (sock == NULL) { continue; } if ((res = PR_Connect(sock, &addr, timeout)) != PR_SUCCESS) { PR_Close(sock); sock = NULL; connect_failed = 1; } /* * Connection attempt finished */ break; } PR_FreeAddrInfo(addr_info); if (sock == NULL && !connect_failed) { PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, 0); } return (sock); } int nss_sock_non_blocking_client_init(const char *host_name, uint16_t port, PRIntn af, struct nss_sock_non_blocking_client *client) { PRIntn tmp_af; client->destroyed = 1; if ((client->host_name = strdup(host_name)) == NULL) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return (-1); } client->port = port; client->af = af; tmp_af = af; if (af == PR_AF_INET6) { tmp_af = PR_AF_UNSPEC; } client->addr_info = PR_GetAddrInfoByName(client->host_name, tmp_af, PR_AI_ADDRCONFIG); if (client->addr_info == NULL) { free(client->host_name); return (-1); } client->addr_iter = NULL; client->connect_attempts = 0; client->socket = NULL; client->destroyed = 0; return (0); } int nss_sock_non_blocking_client_try_next(struct nss_sock_non_blocking_client *client) { PRNetAddr addr; PRStatus res; if (client->destroyed) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return (-1); } if (client->socket != NULL) { PR_Close(client->socket); client->socket = NULL; } while ((client->addr_iter = PR_EnumerateAddrInfo(client->addr_iter, client->addr_info, client->port, &addr)) != NULL) { if (client->af != PR_AF_UNSPEC && addr.raw.family != client->af) { continue; } client->socket = nss_sock_create_socket(addr.raw.family, 0); if (client->socket == NULL) { continue; } if (nss_sock_set_non_blocking(client->socket) == -1) { PR_Close(client->socket); client->socket = NULL; continue; } res = PR_Connect(client->socket, &addr, PR_INTERVAL_NO_TIMEOUT); if (res == PR_SUCCESS || PR_GetError() == PR_IN_PROGRESS_ERROR) { return (0); } PR_Close(client->socket); client->socket = NULL; if (client->connect_attempts < INT_MAX) { client->connect_attempts++; } } if (client->connect_attempts == 0) { PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, 0); } return (-1); } void nss_sock_non_blocking_client_destroy(struct nss_sock_non_blocking_client *client) { if (client->destroyed) { return ; } if (client->addr_info != NULL) { PR_FreeAddrInfo(client->addr_info); client->addr_info = NULL; } free(client->host_name); client->host_name = NULL; client->destroyed = 1; } /* * -1 = Client connect failed * 0 = Client connect still in progress * 1 = Client successfuly connected */ int nss_sock_non_blocking_client_succeeded(const PRPollDesc *pfd) { int res; res = -1; if (PR_GetConnectStatus(pfd) == PR_SUCCESS) { res = 1; } else { if (PR_GetError() == PR_IN_PROGRESS_ERROR) { res = 0; } else { res = -1; } } return (res); } /* * Start client side SSL connection. This can block. * * ssl_url is expected server URL, bad_cert_hook is callback called when server certificate * verification fails. */ PRFileDesc * nss_sock_start_ssl_as_client(PRFileDesc *input_sock, const char *ssl_url, SSLBadCertHandler bad_cert_hook, SSLGetClientAuthData client_auth_hook, void *client_auth_hook_arg, int force_handshake, int *reset_would_block) { PRFileDesc *ssl_sock; if (force_handshake) { *reset_would_block = 0; } ssl_sock = SSL_ImportFD(NULL, input_sock); if (ssl_sock == NULL) { return (NULL); } if (SSL_SetURL(ssl_sock, ssl_url) != SECSuccess) { return (NULL); } if ((SSL_OptionSet(ssl_sock, SSL_SECURITY, PR_TRUE) != SECSuccess) || (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) || (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)) { return (NULL); } if (bad_cert_hook != NULL && SSL_BadCertHook(ssl_sock, bad_cert_hook, NULL) != SECSuccess) { return (NULL); } if (client_auth_hook != NULL && (SSL_GetClientAuthDataHook(ssl_sock, client_auth_hook, client_auth_hook_arg) != SECSuccess)) { return (NULL); } if (SSL_ResetHandshake(ssl_sock, PR_FALSE) != SECSuccess) { return (NULL); } if (force_handshake && SSL_ForceHandshake(ssl_sock) != SECSuccess) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) { /* * Mask would block error. */ *reset_would_block = 1; } else { - return (NULL); - } + return (NULL); + } } return (ssl_sock); } PRFileDesc * nss_sock_start_ssl_as_server(PRFileDesc *input_sock, CERTCertificate *server_cert, SECKEYPrivateKey *server_key, int require_client_cert, int force_handshake, int *reset_would_block) { PRFileDesc *ssl_sock; if (force_handshake) { *reset_would_block = 0; } ssl_sock = SSL_ImportFD(NULL, input_sock); if (ssl_sock == NULL) { return (NULL); } if (SSL_ConfigSecureServer(ssl_sock, server_cert, server_key, NSS_FindCertKEAType(server_cert)) != SECSuccess) { return (NULL); } if ((SSL_OptionSet(ssl_sock, SSL_SECURITY, PR_TRUE) != SECSuccess) || (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_SERVER, PR_TRUE) != SECSuccess) || (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE) != SECSuccess) || (SSL_OptionSet(ssl_sock, SSL_REQUEST_CERTIFICATE, require_client_cert) != SECSuccess) || (SSL_OptionSet(ssl_sock, SSL_REQUIRE_CERTIFICATE, require_client_cert) != SECSuccess)) { return (NULL); } if (SSL_ResetHandshake(ssl_sock, PR_TRUE) != SECSuccess) { return (NULL); } - if (force_handshake && SSL_ForceHandshake(ssl_sock) != SECSuccess) { + if (force_handshake && SSL_ForceHandshake(ssl_sock) != SECSuccess) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) { /* * Mask would block error. */ *reset_would_block = 1; } else { - return (NULL); - } - } + return (NULL); + } + } return (ssl_sock); } diff --git a/qdevices/qdevice-cmap.c b/qdevices/qdevice-cmap.c index ebf9353c..09dc47cf 100644 --- a/qdevices/qdevice-cmap.c +++ b/qdevices/qdevice-cmap.c @@ -1,465 +1,465 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "qdevice-config.h" #include "qdevice-cmap.h" #include "qdevice-log.h" #include "qdevice-log-debug.h" #include "qdevice-model.h" #include "utils.h" static uint32_t qdevice_cmap_autogenerate_node_id(const char *addr, int clear_node_high_byte) { struct addrinfo *ainfo; struct addrinfo ahints; int ret, i; memset(&ahints, 0, sizeof(ahints)); ahints.ai_socktype = SOCK_DGRAM; ahints.ai_protocol = IPPROTO_UDP; /* * Hardcoded AF_INET because autogenerated nodeid is valid only for ipv4 */ ahints.ai_family = AF_INET; ret = getaddrinfo(addr, NULL, &ahints, &ainfo); if (ret != 0) return (0); if (ainfo->ai_family != AF_INET) { freeaddrinfo(ainfo); return (0); } memcpy(&i, &((struct sockaddr_in *)ainfo->ai_addr)->sin_addr, sizeof(struct in_addr)); freeaddrinfo(ainfo); ret = htonl(i); if (clear_node_high_byte) { ret &= 0x7FFFFFFF; } return (ret); } int qdevice_cmap_get_nodelist(cmap_handle_t cmap_handle, struct node_list *list) { cs_error_t cs_err; cmap_iter_handle_t iter_handle; char key_name[CMAP_KEYNAME_MAXLEN + 1]; char tmp_key[CMAP_KEYNAME_MAXLEN + 1]; int res; int ret_value; unsigned int node_pos; uint32_t node_id; uint32_t data_center_id; char *tmp_str; char *addr0_str; int clear_node_high_byte; ret_value = 0; node_list_init(list); cs_err = cmap_iter_init(cmap_handle, "nodelist.node.", &iter_handle); if (cs_err != CS_OK) { return (-1); } while ((cs_err = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL)) == CS_OK) { res = sscanf(key_name, "nodelist.node.%u.%s", &node_pos, tmp_key); if (res != 2) { continue; } if (strcmp(tmp_key, "ring0_addr") != 0) { continue; } snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos); cs_err = cmap_get_uint32(cmap_handle, tmp_key, &node_id); if (cs_err == CS_ERR_NOT_EXIST) { /* * Nodeid doesn't exists -> autogenerate node id */ clear_node_high_byte = 0; if (cmap_get_string(cmap_handle, "totem.clear_node_high_bit", &tmp_str) == CS_OK) { if (strcmp (tmp_str, "yes") == 0) { clear_node_high_byte = 1; } free(tmp_str); } if (cmap_get_string(cmap_handle, key_name, &addr0_str) != CS_OK) { return (-1); } node_id = qdevice_cmap_autogenerate_node_id(addr0_str, clear_node_high_byte); free(addr0_str); } else if (cs_err != CS_OK) { ret_value = -1; goto iter_finalize; } snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.datacenterid", node_pos); if (cmap_get_uint32(cmap_handle, tmp_key, &data_center_id) != CS_OK) { data_center_id = 0; } if (node_list_add(list, node_id, data_center_id, TLV_NODE_STATE_NOT_SET) == NULL) { ret_value = -1; goto iter_finalize; } } iter_finalize: cmap_iter_finalize(cmap_handle, iter_handle); if (ret_value != 0) { node_list_free(list); } return (ret_value); } int qdevice_cmap_get_config_version(cmap_handle_t cmap_handle, uint64_t *config_version) { int res; if (cmap_get_uint64(cmap_handle, "totem.config_version", config_version) == CS_OK) { res = 0; } else { *config_version = 0; res = -1; } return (res); } int qdevice_cmap_store_config_node_list(struct qdevice_instance *instance) { int res; node_list_free(&instance->config_node_list); if (qdevice_cmap_get_nodelist(instance->cmap_handle, &instance->config_node_list) != 0) { qdevice_log(LOG_ERR, "Can't get configuration node list."); return (-1); } res = qdevice_cmap_get_config_version(instance->cmap_handle, &instance->config_node_list_version); instance->config_node_list_version_set = (res == 0); return (0); } void qdevice_cmap_init(struct qdevice_instance *instance) { cs_error_t res; int no_retries; no_retries = 0; while ((res = cmap_initialize(&instance->cmap_handle)) == CS_ERR_TRY_AGAIN && no_retries++ < instance->advanced_settings->max_cs_try_again) { (void)poll(NULL, 0, 1000); } - if (res != CS_OK) { + if (res != CS_OK) { errx(1, "Failed to initialize the cmap API. Error %s", cs_strerror(res)); } if ((res = cmap_context_set(instance->cmap_handle, (void *)instance)) != CS_OK) { errx(1, "Can't set cmap context. Error %s", cs_strerror(res)); } cmap_fd_get(instance->cmap_handle, &instance->cmap_poll_fd); } static void qdevice_cmap_node_list_event(struct qdevice_instance *instance) { struct node_list nlist; int config_version_set; uint64_t config_version; qdevice_log(LOG_DEBUG, "Node list configuration possibly changed"); if (qdevice_cmap_get_nodelist(instance->cmap_handle, &nlist) != 0) { qdevice_log(LOG_ERR, "Can't get configuration node list."); if (qdevice_model_get_config_node_list_failed(instance) != 0) { qdevice_log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit"); exit(2); } return ; } config_version_set = (qdevice_cmap_get_config_version(instance->cmap_handle, &config_version) == 0); if (node_list_eq(&instance->config_node_list, &nlist)) { return ; } qdevice_log(LOG_DEBUG, "Node list changed"); if (config_version_set) { qdevice_log(LOG_DEBUG, " config_version = "UTILS_PRI_CONFIG_VERSION, config_version); } qdevice_log_debug_dump_node_list(&nlist); if (qdevice_model_config_node_list_changed(instance, &nlist, config_version_set, config_version) != 0) { qdevice_log(LOG_DEBUG, "qdevice_model_config_node_list_changed returned error -> exit"); exit(2); } node_list_free(&instance->config_node_list); if (node_list_clone(&instance->config_node_list, &nlist) != 0) { qdevice_log(LOG_ERR, "Can't allocate instance->config_node_list clone"); node_list_free(&nlist); if (qdevice_model_get_config_node_list_failed(instance) != 0) { qdevice_log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit"); exit(2); } return ; } instance->config_node_list_version_set = config_version_set; if (config_version_set) { instance->config_node_list_version = config_version; } } static void qdevice_cmap_logging_event(struct qdevice_instance *instance) { qdevice_log(LOG_DEBUG, "Logging configuration possibly changed"); qdevice_log_configure(instance); } static void qdevice_cmap_reload_cb(cmap_handle_t cmap_handle, cmap_track_handle_t cmap_track_handle, int32_t event, const char *key_name, struct cmap_notify_value new_value, struct cmap_notify_value old_value, void *user_data) { cs_error_t cs_res; uint8_t reload; struct qdevice_instance *instance; int node_list_event; int logging_event; const char *node_list_prefix_str; const char *logging_prefix_str; node_list_event = 0; logging_event = 0; node_list_prefix_str = "nodelist."; logging_prefix_str = "logging."; if (cmap_context_get(cmap_handle, (const void **)&instance) != CS_OK) { qdevice_log(LOG_ERR, "Fatal error. Can't get cmap context"); exit(1); } /* * Wait for full reload */ if (strcmp(key_name, "config.totemconfig_reload_in_progress") == 0 && new_value.type == CMAP_VALUETYPE_UINT8 && new_value.len == sizeof(reload)) { reload = 1; if (memcmp(new_value.data, &reload, sizeof(reload)) == 0) { /* * Ignore nodelist changes */ instance->cmap_reload_in_progress = 1; return ; } else { instance->cmap_reload_in_progress = 0; node_list_event = 1; logging_event = 1; } } if (instance->cmap_reload_in_progress) { return ; } if (((cs_res = cmap_get_uint8(cmap_handle, "config.totemconfig_reload_in_progress", &reload)) == CS_OK) && reload == 1) { return ; } if (strncmp(key_name, node_list_prefix_str, strlen(node_list_prefix_str)) == 0) { node_list_event = 1; } if (strncmp(key_name, logging_prefix_str, strlen(logging_prefix_str)) == 0) { logging_event = 1; } if (logging_event) { qdevice_cmap_logging_event(instance); } if (node_list_event) { qdevice_cmap_node_list_event(instance); } } int qdevice_cmap_add_track(struct qdevice_instance *instance) { cs_error_t res; res = cmap_track_add(instance->cmap_handle, "config.totemconfig_reload_in_progress", CMAP_TRACK_ADD | CMAP_TRACK_MODIFY, qdevice_cmap_reload_cb, NULL, &instance->cmap_reload_track_handle); if (res != CS_OK) { qdevice_log(LOG_ERR, "Can't initialize cmap totemconfig_reload_in_progress tracking"); return (-1); } res = cmap_track_add(instance->cmap_handle, "nodelist.", CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, qdevice_cmap_reload_cb, NULL, &instance->cmap_nodelist_track_handle); if (res != CS_OK) { qdevice_log(LOG_ERR, "Can't initialize cmap nodelist tracking"); return (-1); } res = cmap_track_add(instance->cmap_handle, "logging.", CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, qdevice_cmap_reload_cb, NULL, &instance->cmap_logging_track_handle); if (res != CS_OK) { qdevice_log(LOG_ERR, "Can't initialize logging tracking"); return (-1); } return (0); } int qdevice_cmap_del_track(struct qdevice_instance *instance) { cs_error_t res; res = cmap_track_delete(instance->cmap_handle, instance->cmap_reload_track_handle); if (res != CS_OK) { qdevice_log(LOG_WARNING, "Can't delete cmap totemconfig_reload_in_progress tracking"); } res = cmap_track_delete(instance->cmap_handle, instance->cmap_nodelist_track_handle); if (res != CS_OK) { qdevice_log(LOG_WARNING, "Can't delete cmap nodelist tracking"); } res = cmap_track_delete(instance->cmap_handle, instance->cmap_logging_track_handle); if (res != CS_OK) { qdevice_log(LOG_WARNING, "Can't delete cmap logging tracking"); } return (0); } void qdevice_cmap_destroy(struct qdevice_instance *instance) { cs_error_t res; res = cmap_finalize(instance->cmap_handle); - if (res != CS_OK) { + if (res != CS_OK) { qdevice_log(LOG_WARNING, "Can't finalize cmap. Error %s", cs_strerror(res)); } } int qdevice_cmap_dispatch(struct qdevice_instance *instance) { cs_error_t res; /* * dispatch can block if corosync is during sync phase */ if (instance->sync_in_progress) { return (0); } res = cmap_dispatch(instance->cmap_handle, CS_DISPATCH_ALL); if (res != CS_OK && res != CS_ERR_TRY_AGAIN) { qdevice_log(LOG_ERR, "Can't dispatch cmap messages"); return (-1); } return (0); } diff --git a/qdevices/qdevice-ipc-cmd.c b/qdevices/qdevice-ipc-cmd.c index aeff5634..ff00ec0b 100644 --- a/qdevices/qdevice-ipc-cmd.c +++ b/qdevices/qdevice-ipc-cmd.c @@ -1,265 +1,265 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "qdevice-ipc-cmd.h" #include "qdevice-log.h" #include "qdevice-model.h" #include "dynar-str.h" #include "utils.h" static int qdevice_ipc_cmd_status_add_header(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { return ((dynar_str_catf(outbuf, "Qdevice information\n") != -1) && (dynar_str_catf(outbuf, "-------------------\n") != -1)); } static int qdevice_ipc_cmd_status_add_model(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { return (dynar_str_catf(outbuf, "Model:\t\t\t%s\n", qdevice_model_type_to_str(instance->model_type)) != -1); } static int qdevice_ipc_cmd_status_add_nodeid(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { return (dynar_str_catf(outbuf, "Node ID:\t\t"UTILS_PRI_NODE_ID"\n", instance->node_id) != -1); } static int qdevice_ipc_cmd_status_add_intervals(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { if (!verbose) { return (1); } return ((dynar_str_catf(outbuf, "HB interval:\t\t%"PRIu32"ms\n", instance->heartbeat_interval) != -1) && (dynar_str_catf(outbuf, "Sync HB interval:\t%"PRIu32"ms\n", instance->sync_heartbeat_interval) != -1)); } static int qdevice_ipc_cmd_status_add_config_node_list(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { - struct node_list_entry *node_info; + struct node_list_entry *node_info; size_t zi; if (instance->config_node_list_version_set) { if (dynar_str_catf(outbuf, "Configuration version:\t"UTILS_PRI_CONFIG_VERSION"\n", instance->config_node_list_version) == -1) { return (0); } } if (dynar_str_catf(outbuf, "Configured node list:\n") == -1) { return (0); } zi = 0; TAILQ_FOREACH(node_info, &instance->config_node_list, entries) { if ((dynar_str_catf(outbuf, " %zu\tNode ID = "UTILS_PRI_NODE_ID, zi, node_info->node_id) == -1) || (node_info->data_center_id != 0 && dynar_str_catf(outbuf, ", Data center ID = " UTILS_PRI_DATACENTER_ID, node_info->data_center_id) == -1) || (dynar_str_catf(outbuf, "\n") == -1)) { return (0); } zi++; } return (1); } static int qdevice_ipc_cmd_status_add_membership_node_list(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { uint32_t u32; if (verbose && dynar_str_catf(outbuf, "Ring ID:\t\t"UTILS_PRI_RING_ID"\n", instance->vq_node_list_ring_id.nodeid, instance->vq_node_list_ring_id.seq) == -1) { return (0); } if (dynar_str_catf(outbuf, "Membership node list:\t") == -1) { return (0); } for (u32 = 0; u32 < instance->vq_node_list_entries; u32++) { if (u32 != 0) { if (dynar_str_catf(outbuf, ", ") == -1) { return (0); } } if (dynar_str_catf(outbuf, UTILS_PRI_NODE_ID, instance->vq_node_list[u32]) == -1) { return (0); } } if (dynar_str_catf(outbuf, "\n") == -1) { return (0); } return (1); } static const char * qdevice_ipc_cmd_vq_nodestate_to_str(uint32_t state) { switch (state) { case VOTEQUORUM_NODESTATE_MEMBER: return ("member"); break; case VOTEQUORUM_NODESTATE_DEAD: return ("dead"); break; case VOTEQUORUM_NODESTATE_LEAVING: return ("leaving"); break; default: qdevice_log(LOG_ERR, "qdevice_ipc_cmd_vq_nodestate_to_str: Unhandled votequorum " "node state %"PRIu32, state); exit(1); break; } return ("Unhandled votequorum node state"); } static int qdevice_ipc_cmd_status_add_quorum_node_list(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { uint32_t u32; votequorum_node_t *node; if (!verbose) { return (1); } if (dynar_str_catf(outbuf, "Quorate:\t\t%s\n", (instance->vq_quorum_quorate ? "Yes" : "No")) == -1) { return (0); } if (dynar_str_catf(outbuf, "Quorum node list:\n") == -1) { return (0); } for (u32 = 0; u32 < instance->vq_quorum_node_list_entries; u32++) { node = &instance->vq_quorum_node_list[u32]; if (node->nodeid == 0) { continue ; } if (dynar_str_catf(outbuf, " %"PRIu32"\tNode ID = "UTILS_PRI_NODE_ID ", State = %s\n", u32, node->nodeid, qdevice_ipc_cmd_vq_nodestate_to_str(node->state)) == -1) { return (0); } } return (1); } static int qdevice_ipc_cmd_status_add_expected_votes(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { if (!verbose) { return (1); } return (dynar_str_catf(outbuf, "Expected votes:\t\t"UTILS_PRI_EXPECTED_VOTES"\n", instance->vq_expected_votes) != -1); } static int qdevice_ipc_cmd_status_add_last_poll(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { struct tm tm_res; if (!verbose) { return (1); } if (instance->vq_last_poll == ((time_t) -1)) { return (dynar_str_catf(outbuf, "Last poll call:\t\tNever\n") != -1); } localtime_r(&instance->vq_last_poll, &tm_res); if (dynar_str_catf(outbuf, "Last poll call:\t\t%04d-%02d-%02dT%02d:%02d:%02d%s\n", tm_res.tm_year + 1900, tm_res.tm_mon + 1, tm_res.tm_mday, tm_res.tm_hour, tm_res.tm_min, tm_res.tm_sec, (instance->vq_last_poll_cast_vote ? " (cast vote)" : "")) == -1) { return (0); } return (1); } int qdevice_ipc_cmd_status(struct qdevice_instance *instance, struct dynar *outbuf, int verbose) { if (qdevice_ipc_cmd_status_add_header(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_model(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_nodeid(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_intervals(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_config_node_list(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_membership_node_list(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_quorum_node_list(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_expected_votes(instance, outbuf, verbose) && qdevice_ipc_cmd_status_add_last_poll(instance, outbuf, verbose) && dynar_str_catf(outbuf, "\n") != -1 && qdevice_model_ipc_cmd_status(instance, outbuf, verbose) != -1) { return (0); } return (-1); } diff --git a/qdevices/qdevice-log-debug.c b/qdevices/qdevice-log-debug.c index 11da056a..4886ca3d 100644 --- a/qdevices/qdevice-log-debug.c +++ b/qdevices/qdevice-log-debug.c @@ -1,56 +1,56 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "qdevice-log-debug.h" #include "qdevice-log.h" #include "utils.h" void qdevice_log_debug_dump_node_list(const struct node_list *nlist) { struct node_list_entry *node_info; size_t zi; qdevice_log(LOG_DEBUG, " Node list:"); zi = 0; TAILQ_FOREACH(node_info, nlist, entries) { qdevice_log(LOG_DEBUG, " %zu node_id = "UTILS_PRI_NODE_ID", " "data_center_id = "UTILS_PRI_DATACENTER_ID", node_state = %s", zi, node_info->node_id, node_info->data_center_id, tlv_node_state_to_str(node_info->node_state)); zi++; - } + } } diff --git a/qdevices/qdevice-net-algo-2nodelms.c b/qdevices/qdevice-net-algo-2nodelms.c index 590e9be6..c2ea0bae 100644 --- a/qdevices/qdevice-net-algo-2nodelms.c +++ b/qdevices/qdevice-net-algo-2nodelms.c @@ -1,186 +1,207 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qdevice-net-algo-2nodelms.h" #include "qdevice-log.h" #include "qdevice-net-send.h" #include "qdevice-net-cast-vote-timer.h" int qdevice_net_algo_2nodelms_init(struct qdevice_net_instance *instance) { return (0); } int qdevice_net_algo_2nodelms_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_2nodelms_config_node_list_changed(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_2nodelms_votequorum_node_list_notify(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_2nodelms_votequorum_quorum_notify(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_2nodelms_votequorum_expected_votes_notify(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_2nodelms_config_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote) + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_2nodelms_membership_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_2nodelms_quorum_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_2nodelms_ask_for_vote_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_2nodelms_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_2nodelms_echo_reply_received(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number) { return (is_expected_seq_number ? 0 : -1); } int qdevice_net_algo_2nodelms_echo_reply_not_received(struct qdevice_net_instance *instance) { return (-1); } int qdevice_net_algo_2nodelms_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote) { return (0); } void qdevice_net_algo_2nodelms_destroy(struct qdevice_net_instance *instance) { } static struct qdevice_net_algorithm qdevice_net_algo_2nodelms = { .init = qdevice_net_algo_2nodelms_init, .connected = qdevice_net_algo_2nodelms_connected, .config_node_list_changed = qdevice_net_algo_2nodelms_config_node_list_changed, .votequorum_node_list_notify = qdevice_net_algo_2nodelms_votequorum_node_list_notify, .votequorum_quorum_notify = qdevice_net_algo_2nodelms_votequorum_quorum_notify, .votequorum_expected_votes_notify = qdevice_net_algo_2nodelms_votequorum_expected_votes_notify, .config_node_list_reply_received = qdevice_net_algo_2nodelms_config_node_list_reply_received, .membership_node_list_reply_received = qdevice_net_algo_2nodelms_membership_node_list_reply_received, .quorum_node_list_reply_received = qdevice_net_algo_2nodelms_quorum_node_list_reply_received, .ask_for_vote_reply_received = qdevice_net_algo_2nodelms_ask_for_vote_reply_received, .vote_info_received = qdevice_net_algo_2nodelms_vote_info_received, .echo_reply_received = qdevice_net_algo_2nodelms_echo_reply_received, .echo_reply_not_received = qdevice_net_algo_2nodelms_echo_reply_not_received, .disconnected = qdevice_net_algo_2nodelms_disconnected, .destroy = qdevice_net_algo_2nodelms_destroy, }; int qdevice_net_algo_2nodelms_register(void) { return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_2NODELMS, &qdevice_net_algo_2nodelms)); } diff --git a/qdevices/qdevice-net-algo-2nodelms.h b/qdevices/qdevice-net-algo-2nodelms.h index e52cca68..8372bd34 100644 --- a/qdevices/qdevice-net-algo-2nodelms.h +++ b/qdevices/qdevice-net-algo-2nodelms.h @@ -1,100 +1,102 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QDEVICE_NET_ALGO_2NODELMS_H_ #define _QDEVICE_NET_ALGO_2NODELMS_H_ #include "qdevice-net-algorithm.h" #ifdef __cplusplus extern "C" { #endif extern int qdevice_net_algo_2nodelms_init(struct qdevice_net_instance *instance); extern int qdevice_net_algo_2nodelms_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote); -extern int qdevice_net_algo_2nodelms_config_node_list_changed( +extern int qdevice_net_algo_2nodelms_config_node_list_changed( struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_votequorum_node_list_notify( struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_votequorum_quorum_notify( struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_votequorum_expected_votes_notify( struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_config_node_list_reply_received( - struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote); + struct qdevice_net_instance *instance, uint32_t seq_number, int initial, + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_membership_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id, - enum tlv_vote *vote); + int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_quorum_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_ask_for_vote_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); extern int qdevice_net_algo_2nodelms_echo_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number); extern int qdevice_net_algo_2nodelms_echo_reply_not_received( struct qdevice_net_instance *instance); extern int qdevice_net_algo_2nodelms_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote); extern void qdevice_net_algo_2nodelms_destroy(struct qdevice_net_instance *instance); extern int qdevice_net_algo_2nodelms_register(void); #ifdef __cplusplus } #endif #endif /* _QDEVICE_NET_ALGO_2NODELMS_H_ */ diff --git a/qdevices/qdevice-net-algo-ffsplit.c b/qdevices/qdevice-net-algo-ffsplit.c index 2478c23f..6fa513b8 100644 --- a/qdevices/qdevice-net-algo-ffsplit.c +++ b/qdevices/qdevice-net-algo-ffsplit.c @@ -1,271 +1,292 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qdevice-net-algo-ffsplit.h" #include "qdevice-log.h" #include "qdevice-net-send.h" #include "qdevice-net-cast-vote-timer.h" #include "qdevice-votequorum.h" #include "utils.h" static int check_vqinfo_validity(struct qdevice_net_instance *instance) { struct qdevice_instance *qdev_instance; struct votequorum_info vq_info; cs_error_t cs_res; struct node_list_entry *node; uint32_t node_id; qdev_instance = instance->qdevice_instance_ptr; TAILQ_FOREACH(node, &qdev_instance->config_node_list, entries) { node_id = node->node_id; cs_res = votequorum_getinfo(qdev_instance->votequorum_handle, node_id, &vq_info); if (cs_res == CS_ERR_NOT_EXIST) { continue ; } else if (cs_res != CS_OK) { qdevice_log(LOG_CRIT, "Can't get votequorum information for node " UTILS_PRI_NODE_ID ". Error %s", node_id, cs_strerror(cs_res)); return (-1); } if (vq_info.node_votes != 1) { qdevice_log(LOG_CRIT, "50:50 split algorithm works only if all nodes have " "exactly 1 vote. Node " UTILS_PRI_NODE_ID " has %u votes!", node_id, vq_info.node_votes); return (-1); } if (vq_info.qdevice_votes != 1) { qdevice_log(LOG_CRIT, "50:50 split algorithm works only if qdevice has " "exactly 1 vote. Node "UTILS_PRI_NODE_ID" has %u votes!", node_id, vq_info.qdevice_votes); return (-1); } } return (0); } static int check_cmap_validity(struct qdevice_net_instance *instance) { struct qdevice_instance *qdev_instance; uint32_t qdevice_votes; qdev_instance = instance->qdevice_instance_ptr; if (cmap_get_uint32(qdev_instance->cmap_handle, "quorum.device.votes", &qdevice_votes) != CS_OK || qdevice_votes != 1) { qdevice_log(LOG_CRIT, "50:50 split algorithm works only if quorum.device.votes" " configuration key is set to 1!"); return (-1); } return (0); } int qdevice_net_algo_ffsplit_init(struct qdevice_net_instance *instance) { if (check_cmap_validity(instance) != 0 || check_vqinfo_validity(instance) != 0) { return (-1); } return (0); } int qdevice_net_algo_ffsplit_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_ffsplit_config_node_list_changed(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote) { if (check_vqinfo_validity(instance) != 0) { return (-1); } return (0); } int qdevice_net_algo_ffsplit_votequorum_node_list_notify(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_ffsplit_votequorum_quorum_notify(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_ffsplit_votequorum_expected_votes_notify(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote) { if (check_vqinfo_validity(instance) != 0) { return (-1); } return (0); } int qdevice_net_algo_ffsplit_config_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote) + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_ffsplit_membership_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_ffsplit_quorum_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_ffsplit_ask_for_vote_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_ffsplit_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_ffsplit_echo_reply_received(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number) { return (is_expected_seq_number ? 0 : -1); } int qdevice_net_algo_ffsplit_echo_reply_not_received(struct qdevice_net_instance *instance) { return (-1); } int qdevice_net_algo_ffsplit_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote) { /* * We cannot depend on default behavior (until there is no change -> use old vote). * This could create two quorate clusters (2:2 -> first half get ACK -> first half * disconnects from qnetd -> second half get ACK -> two quorate clusters) */ *vote = TLV_VOTE_NACK; return (0); } void qdevice_net_algo_ffsplit_destroy(struct qdevice_net_instance *instance) { } static struct qdevice_net_algorithm qdevice_net_algo_ffsplit = { .init = qdevice_net_algo_ffsplit_init, .connected = qdevice_net_algo_ffsplit_connected, .config_node_list_changed = qdevice_net_algo_ffsplit_config_node_list_changed, .votequorum_node_list_notify = qdevice_net_algo_ffsplit_votequorum_node_list_notify, .votequorum_quorum_notify = qdevice_net_algo_ffsplit_votequorum_quorum_notify, .votequorum_expected_votes_notify = qdevice_net_algo_ffsplit_votequorum_expected_votes_notify, .config_node_list_reply_received = qdevice_net_algo_ffsplit_config_node_list_reply_received, .membership_node_list_reply_received = qdevice_net_algo_ffsplit_membership_node_list_reply_received, .quorum_node_list_reply_received = qdevice_net_algo_ffsplit_quorum_node_list_reply_received, .ask_for_vote_reply_received = qdevice_net_algo_ffsplit_ask_for_vote_reply_received, .vote_info_received = qdevice_net_algo_ffsplit_vote_info_received, .echo_reply_received = qdevice_net_algo_ffsplit_echo_reply_received, .echo_reply_not_received = qdevice_net_algo_ffsplit_echo_reply_not_received, .disconnected = qdevice_net_algo_ffsplit_disconnected, .destroy = qdevice_net_algo_ffsplit_destroy, }; int qdevice_net_algo_ffsplit_register(void) { return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_FFSPLIT, &qdevice_net_algo_ffsplit)); } diff --git a/qdevices/qdevice-net-algo-ffsplit.h b/qdevices/qdevice-net-algo-ffsplit.h index 2100c2e0..9e19ab05 100644 --- a/qdevices/qdevice-net-algo-ffsplit.h +++ b/qdevices/qdevice-net-algo-ffsplit.h @@ -1,100 +1,102 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QDEVICE_NET_ALGO_FFSPLIT_H_ #define _QDEVICE_NET_ALGO_FFSPLIT_H_ #include "qdevice-net-algorithm.h" #ifdef __cplusplus extern "C" { #endif extern int qdevice_net_algo_ffsplit_init(struct qdevice_net_instance *instance); extern int qdevice_net_algo_ffsplit_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote); -extern int qdevice_net_algo_ffsplit_config_node_list_changed( +extern int qdevice_net_algo_ffsplit_config_node_list_changed( struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_votequorum_node_list_notify( struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_votequorum_quorum_notify( struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_votequorum_expected_votes_notify( struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_config_node_list_reply_received( - struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote); + struct qdevice_net_instance *instance, uint32_t seq_number, int initial, + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_membership_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id, - enum tlv_vote *vote); + int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_quorum_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_ask_for_vote_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); extern int qdevice_net_algo_ffsplit_echo_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number); extern int qdevice_net_algo_ffsplit_echo_reply_not_received( struct qdevice_net_instance *instance); extern int qdevice_net_algo_ffsplit_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote); extern void qdevice_net_algo_ffsplit_destroy(struct qdevice_net_instance *instance); extern int qdevice_net_algo_ffsplit_register(void); #ifdef __cplusplus } #endif #endif /* _QDEVICE_NET_ALGO_FFSPLIT_H_ */ diff --git a/qdevices/qdevice-net-algo-lms.c b/qdevices/qdevice-net-algo-lms.c index 71375b2e..b9b47136 100644 --- a/qdevices/qdevice-net-algo-lms.c +++ b/qdevices/qdevice-net-algo-lms.c @@ -1,228 +1,256 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qdevice-net-algo-lms.h" #include "qdevice-log.h" #include "qdevice-net-send.h" #include "qdevice-net-cast-vote-timer.h" struct algo_lms_instance_data { uint32_t quorate; uint8_t have_wfa; enum tlv_vote vote; }; int qdevice_net_algo_lms_init(struct qdevice_net_instance *instance) { struct algo_lms_instance_data *data; int res; data = malloc(sizeof(struct algo_lms_instance_data)); if (!data) { return (-1); } instance->algorithm_data = data; data->quorate = 0; data->vote = TLV_VOTE_ASK_LATER; res = cmap_get_uint8(instance->qdevice_instance_ptr->cmap_handle, "quorum.wait_for_all", &data->have_wfa); if (res != CS_OK) { qdevice_log(LOG_DEBUG, "algo-lms: Can't get WFA res = %d", res); data->have_wfa = 0; } qdevice_log(LOG_DEBUG, "algo-lms: initialised. WFA = %d", data->have_wfa); return (0); } int qdevice_net_algo_lms_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_lms_config_node_list_changed(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_lms_votequorum_node_list_notify(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_lms_votequorum_quorum_notify(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote) { struct algo_lms_instance_data *data = instance->algorithm_data; data->quorate = quorate; qdevice_log(LOG_DEBUG, "algo-lms: quorum_notify. quorate = %d", data->quorate); return (0); } int qdevice_net_algo_lms_config_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote) + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_lms_membership_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_lms_quorum_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_lms_votequorum_expected_votes_notify(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote) { return (0); } int qdevice_net_algo_lms_ask_for_vote_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_lms_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { + + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } int qdevice_net_algo_lms_echo_reply_received(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number) { return (is_expected_seq_number ? 0 : -1); } int qdevice_net_algo_lms_echo_reply_not_received(struct qdevice_net_instance *instance) { struct algo_lms_instance_data *data = instance->algorithm_data; qdevice_log(LOG_DEBUG, "algo-lms: echo_not_recvd. quorate = %d, WFA = %d", data->quorate, data->have_wfa); /* qnetd server is disconnected, if we were already quorate AND WFA is enabled then we can continue to provide our vote. Otherwise ... no */ if (data->quorate && data->have_wfa) { return (0); } return (-1); } int qdevice_net_algo_lms_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote) { struct algo_lms_instance_data *data = instance->algorithm_data; qdevice_log(LOG_DEBUG, "algo-lms: disconnected. quorate = %d, WFA = %d", data->quorate, data->have_wfa); qdevice_log(LOG_DEBUG, "algo-lms: disconnected. reason = %d, WFA = %d", disconnect_reason, data->have_wfa); if (!data->quorate || !data->have_wfa) { *vote = TLV_VOTE_NACK; } *try_reconnect = 1; return (0); } void qdevice_net_algo_lms_destroy(struct qdevice_net_instance *instance) { free(instance->algorithm_data); } static struct qdevice_net_algorithm qdevice_net_algo_lms = { .init = qdevice_net_algo_lms_init, .connected = qdevice_net_algo_lms_connected, .config_node_list_changed = qdevice_net_algo_lms_config_node_list_changed, .votequorum_node_list_notify = qdevice_net_algo_lms_votequorum_node_list_notify, .votequorum_quorum_notify = qdevice_net_algo_lms_votequorum_quorum_notify, .votequorum_expected_votes_notify = qdevice_net_algo_lms_votequorum_expected_votes_notify, .config_node_list_reply_received = qdevice_net_algo_lms_config_node_list_reply_received, .membership_node_list_reply_received = qdevice_net_algo_lms_membership_node_list_reply_received, .quorum_node_list_reply_received = qdevice_net_algo_lms_quorum_node_list_reply_received, .ask_for_vote_reply_received = qdevice_net_algo_lms_ask_for_vote_reply_received, .vote_info_received = qdevice_net_algo_lms_vote_info_received, .echo_reply_received = qdevice_net_algo_lms_echo_reply_received, .echo_reply_not_received = qdevice_net_algo_lms_echo_reply_not_received, .disconnected = qdevice_net_algo_lms_disconnected, .destroy = qdevice_net_algo_lms_destroy, }; int qdevice_net_algo_lms_register(void) { return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_LMS, &qdevice_net_algo_lms)); } diff --git a/qdevices/qdevice-net-algo-lms.h b/qdevices/qdevice-net-algo-lms.h index fea9c69c..93b488c0 100644 --- a/qdevices/qdevice-net-algo-lms.h +++ b/qdevices/qdevice-net-algo-lms.h @@ -1,100 +1,102 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QDEVICE_NET_ALGO_LMS_H_ #define _QDEVICE_NET_ALGO_LMS_H_ #include "qdevice-net-algorithm.h" #ifdef __cplusplus extern "C" { #endif extern int qdevice_net_algo_lms_init(struct qdevice_net_instance *instance); extern int qdevice_net_algo_lms_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote); -extern int qdevice_net_algo_lms_config_node_list_changed( +extern int qdevice_net_algo_lms_config_node_list_changed( struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_lms_votequorum_node_list_notify( struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_lms_votequorum_quorum_notify( struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_lms_votequorum_expected_votes_notify( struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote); extern int qdevice_net_algo_lms_config_node_list_reply_received( - struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote); + struct qdevice_net_instance *instance, uint32_t seq_number, int initial, + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_lms_membership_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id, - enum tlv_vote *vote); + int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_lms_quorum_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_lms_ask_for_vote_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_lms_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); extern int qdevice_net_algo_lms_echo_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number); extern int qdevice_net_algo_lms_echo_reply_not_received( struct qdevice_net_instance *instance); extern int qdevice_net_algo_lms_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote); extern void qdevice_net_algo_lms_destroy(struct qdevice_net_instance *instance); extern int qdevice_net_algo_lms_register(void); #ifdef __cplusplus } #endif #endif /* _QDEVICE_NET_ALGO_LMS_H_ */ diff --git a/qdevices/qdevice-net-algo-test.c b/qdevices/qdevice-net-algo-test.c index 3949d66a..d3bf1c42 100644 --- a/qdevices/qdevice-net-algo-test.c +++ b/qdevices/qdevice-net-algo-test.c @@ -1,339 +1,375 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qdevice-net-algo-test.h" #include "qdevice-log.h" #include "qdevice-net-send.h" #include "qdevice-net-cast-vote-timer.h" /* * Called after qdevice_net_instance is initialized. Connection to server is not yet * established. Used mainly for allocating instance->algorithm_data. * * Callback should return 0 on success or -1 on failure. */ int qdevice_net_algo_test_init(struct qdevice_net_instance *instance) { instance->algorithm_data = NULL; qdevice_log(LOG_INFO, "algo-test: Initialized"); return (0); } /* * Called after qdevice connected to qnetd. * send_config_node_list, send_membership_node_list and send_quorum_node_list can be set to * nonzero (default) to make qdevice-net send given lists to qnetd * vote (default TLV_VOTE_WAIT_FOR_REPLY) can be set to update voting timer * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Connected"); return (0); } /* * Called after config node list changed. * * Callback can override send_node_list and vote. * Depending on net_instance->state, they are set acordingly: * If net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS * send_node_list = 0 * if cast_vote_timer_vote != TLV_VOTE_ACK * vote = TLV_VOTE_NO_CHANGE * if cast_vote_timer_vote = TLV_VOTE_ACK * vote = TLV_VOTE_NACK. * Otherwise send_node_list = 1 and vote = TLV_VOTE_NO_CHANGE * If send_node_list is set to non zero, node list is send to qnetd */ int qdevice_net_algo_test_config_node_list_changed(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Config node list changed"); return (0); } /* * Called after votequorum node list notify is dispatched. * * Callback should return 0 on success or -1 on failure (-> disconnect client). * * If net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS * send_node_list = 0 * if cast_vote_timer_vote != TLV_VOTE_ACK * vote = TLV_VOTE_NO_CHANGE * if cast_vote_timer_vote = TLV_VOTE_ACK * vote = TLV_VOTE_NACK. * Otherwise send_node_list = 1 and vote = TLV_VOTE_WAIT_FOR_REPLY * If send_node_list is set to non zero, node list is send to qnetd */ int qdevice_net_algo_test_votequorum_node_list_notify(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Votequorum list notify"); return (0); } /* * Called after votequorum quorum notify is dispatched. * * Callback should return 0 on success or -1 on failure (-> disconnect client). * * Callback can override send_node_list and vote. * Depending on net_instance->state, they are set acordingly: * If net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS * send_node_list = 0 * if cast_vote_timer_vote != TLV_VOTE_ACK * vote = TLV_VOTE_NO_CHANGE * if cast_vote_timer_vote = TLV_VOTE_ACK * vote = TLV_VOTE_NACK. * Otherwise send_node_list = 1 and vote = TLV_VOTE_NO_CHANGE * * If send_node_list is set to non zero, node list is send to qnetd */ int qdevice_net_algo_test_votequorum_quorum_notify(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Votequorum quorum notify"); return (0); } /* * Called after votequorum expected_votes notify is dispatched. * * Callback should return 0 on success or -1 on failure (-> disconnect client). * * Vote is set to TLV_VOTE_NO_CHANGE */ int qdevice_net_algo_test_votequorum_expected_votes_notify(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Votequorum expected votes notify"); return (0); } /* * Called when config node list reply is received. Vote is set to value returned by server (and can - * be overwriten by algorithm). + * be overwriten by algorithm). ring_id is returned by server. ring_id_is_valid is set to 1 only if + * received ring id matches last sent ring id. It usually make sense to not update timer. * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_config_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote) + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Config node list reply"); + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } /* * Called when membership node list reply (reply for votequorum votequorum_nodelist_notify_fn) * is received. Vote is set to value returned by server (and can be overwriten by algorithm). * * Also if server returned TLV_VOTE_ASK_LATER, it's good idea to create timer (call timer_list_add * with instance->main_timer_list parameter) and ask for reply (qdevice_net_send_ask_for_vote). * Another option may be to wait for vote_info message (if server algorithm is configured so). * + * ring_id and ring_id_is_valid have same meaning as for + * qdevice_net_algo_test_config_node_list_reply_received + * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_membership_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Membership node list reply"); + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } /* * Called when quorum node list reply (reply for votequorum votequorum_quorum_notify_fn) * is received. Vote is set to value returned by server (and can be overwriten by algorithm). * + * ring_id and ring_id_is_valid have same meaning as for + * qdevice_net_algo_test_config_node_list_reply_received + * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_quorum_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Quorum node list reply"); + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } /* * Called when reply for ask for vote message was received. * Vote is set to value returned by server (and can be overwriten by algorithm). * + * ring_id and ring_id_is_valid have same meaning as for + * qdevice_net_algo_test_config_node_list_reply_received + * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_ask_for_vote_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Ask for vote reply received"); + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } /* * Called when vote info message from server was received. * Vote is set to value sent by server (and can be overwriten by algorithm). * + * ring_id and ring_id_is_valid have same meaning as for + * qdevice_net_algo_test_config_node_list_reply_received + * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Vote info received"); + if (!ring_id_is_valid) { + *vote = TLV_VOTE_NO_CHANGE; + } + return (0); } /* * Called when echo reply message was received. * is_expected_seq_number is set to 1 if received seq_number was equal to last sent echo request. * * Callback should return 0 on success or -1 on failure (-> disconnect client). */ int qdevice_net_algo_test_echo_reply_received(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number) { qdevice_log(LOG_INFO, "algo-test: Echo reply received"); return (is_expected_seq_number ? 0 : -1); } /* * Called when client is about to send echo request but echo reply to previous echo request * was not yet received. * * Callback should return 0 if processing should continue (echo request is not send but timer is * scheduled again) otherwise -1 (-> disconnect client). */ int qdevice_net_algo_test_echo_reply_not_received(struct qdevice_net_instance *instance) { qdevice_log(LOG_INFO, "algo-test: Echo reply not received"); return (-1); } /* * Called when client disconnect from server. * * disconnect_reason contains one of QDEVICE_NET_DISCONNECT_REASON_ * try_reconnect can be set to non zero value if reconnect to server should be tried * vote (default TLV_VOTE_NO_CHANGE) can be set to update voting timer * * Callback should return 0 on success, -1 on failure (-> force exit) */ int qdevice_net_algo_test_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote) { qdevice_log(LOG_INFO, "algo-test: Disconnected"); return (0); } /* * Called when qdevice-net is going down. */ void qdevice_net_algo_test_destroy(struct qdevice_net_instance *instance) { qdevice_log(LOG_INFO, "algo-test: Destroy"); } static struct qdevice_net_algorithm qdevice_net_algo_test = { .init = qdevice_net_algo_test_init, .connected = qdevice_net_algo_test_connected, .config_node_list_changed = qdevice_net_algo_test_config_node_list_changed, .votequorum_node_list_notify = qdevice_net_algo_test_votequorum_node_list_notify, .votequorum_quorum_notify = qdevice_net_algo_test_votequorum_quorum_notify, .votequorum_expected_votes_notify = qdevice_net_algo_test_votequorum_expected_votes_notify, .config_node_list_reply_received = qdevice_net_algo_test_config_node_list_reply_received, .membership_node_list_reply_received = qdevice_net_algo_test_membership_node_list_reply_received, .quorum_node_list_reply_received = qdevice_net_algo_test_quorum_node_list_reply_received, .ask_for_vote_reply_received = qdevice_net_algo_test_ask_for_vote_reply_received, .vote_info_received = qdevice_net_algo_test_vote_info_received, .echo_reply_received = qdevice_net_algo_test_echo_reply_received, .echo_reply_not_received = qdevice_net_algo_test_echo_reply_not_received, .disconnected = qdevice_net_algo_test_disconnected, .destroy = qdevice_net_algo_test_destroy, }; int qdevice_net_algo_test_register(void) { return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_TEST, &qdevice_net_algo_test)); } diff --git a/qdevices/qdevice-net-algo-test.h b/qdevices/qdevice-net-algo-test.h index 619cae46..8afbf0bf 100644 --- a/qdevices/qdevice-net-algo-test.h +++ b/qdevices/qdevice-net-algo-test.h @@ -1,101 +1,102 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QDEVICE_NET_ALGO_TEST_H_ #define _QDEVICE_NET_ALGO_TEST_H_ #include "qdevice-net-algorithm.h" #ifdef __cplusplus extern "C" { #endif extern int qdevice_net_algo_test_init(struct qdevice_net_instance *instance); extern int qdevice_net_algo_test_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote); -extern int qdevice_net_algo_test_config_node_list_changed( +extern int qdevice_net_algo_test_config_node_list_changed( struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_test_votequorum_node_list_notify( struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_test_votequorum_quorum_notify( struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algo_test_votequorum_expected_votes_notify( struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote); extern int qdevice_net_algo_test_config_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, int initial, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_test_membership_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id, - enum tlv_vote *vote); + int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_test_quorum_node_list_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_test_ask_for_vote_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, - enum tlv_vote *vote); + const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algo_test_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); extern int qdevice_net_algo_test_echo_reply_received( struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number); extern int qdevice_net_algo_test_echo_reply_not_received( struct qdevice_net_instance *instance); extern int qdevice_net_algo_test_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote); extern void qdevice_net_algo_test_destroy(struct qdevice_net_instance *instance); extern int qdevice_net_algo_test_register(void); #ifdef __cplusplus } #endif #endif /* _QDEVICE_NET_ALGO_TEST_H_ */ diff --git a/qdevices/qdevice-net-algorithm.c b/qdevices/qdevice-net-algorithm.c index 079771ff..888d6127 100644 --- a/qdevices/qdevice-net-algorithm.c +++ b/qdevices/qdevice-net-algorithm.c @@ -1,329 +1,336 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "qnet-config.h" #include "qdevice-net-algorithm.h" #include "qdevice-log.h" #include "qdevice-net-algo-test.h" #include "qdevice-net-algo-ffsplit.h" #include "qdevice-net-algo-2nodelms.h" #include "qdevice-net-algo-lms.h" static struct qdevice_net_algorithm *qdevice_net_algorithm_array[QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE]; int qdevice_net_algorithm_init(struct qdevice_net_instance *instance) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_init unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]->init(instance)); } int qdevice_net_algorithm_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_connected unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]->connected(instance, send_config_node_list, send_membership_node_list, send_quorum_node_list, vote)); } int qdevice_net_algorithm_config_node_list_changed(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_connected unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> config_node_list_changed(instance, nlist, config_version_set, config_version, send_node_list, vote)); } int qdevice_net_algorithm_votequorum_node_list_notify(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_node_list_notify " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]->votequorum_node_list_notify( instance, ring_id, node_list_entries, node_list, send_node_list, vote)); } int qdevice_net_algorithm_votequorum_quorum_notify(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_quorum_notify " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> votequorum_quorum_notify(instance, quorate, node_list_entries, node_list, send_node_list, vote)); } int qdevice_net_algorithm_votequorum_expected_votes_notify(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_expected_votes_notify " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> votequorum_expected_votes_notify(instance, expected_votes, vote)); } int qdevice_net_algorithm_config_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote) + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_config_node_list_reply_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> - config_node_list_reply_received(instance, seq_number, initial, vote)); + config_node_list_reply_received(instance, seq_number, initial, ring_id, + ring_id_is_valid, vote)); } int qdevice_net_algorithm_membership_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_membership_node_list_reply_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> - membership_node_list_reply_received(instance, seq_number, ring_id, vote)); + membership_node_list_reply_received(instance, seq_number, ring_id, ring_id_is_valid, + vote)); } int qdevice_net_algorithm_quorum_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_quorum_node_list_reply_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> - quorum_node_list_reply_received(instance, seq_number, vote)); + quorum_node_list_reply_received(instance, seq_number, ring_id, ring_id_is_valid, + vote)); } int qdevice_net_algorithm_ask_for_vote_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_ask_for_vote_reply_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> - ask_for_vote_reply_received(instance, seq_number, vote)); + ask_for_vote_reply_received(instance, seq_number, ring_id, ring_id_is_valid, vote)); } int qdevice_net_algorithm_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote) + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_vote_info_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> - vote_info_received(instance, seq_number, vote)); + vote_info_received(instance, seq_number, ring_id, ring_id_is_valid, vote)); } int qdevice_net_algorithm_echo_reply_received(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_echo_reply_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> echo_reply_received(instance, seq_number, is_expected_seq_number)); } int qdevice_net_algorithm_echo_reply_not_received(struct qdevice_net_instance *instance) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_echo_reply_not_received " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> echo_reply_not_received(instance)); } int qdevice_net_algorithm_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_disconnected " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> disconnected(instance, disconnect_reason, try_reconnect, vote)); } void qdevice_net_algorithm_destroy(struct qdevice_net_instance *instance) { if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_algorithm_destroy " "unhandled decision algorithm"); exit(1); } return (qdevice_net_algorithm_array[instance->decision_algorithm]-> destroy(instance)); } int qdevice_net_algorithm_register(enum tlv_decision_algorithm_type algorithm_number, struct qdevice_net_algorithm *algorithm) { if (algorithm_number >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) { return (-1); } if (qdevice_net_algorithm_array[algorithm_number] != NULL) { return (-1); } qdevice_net_algorithm_array[algorithm_number] = algorithm; return (0); } int qdevice_net_algorithm_register_all(void) { if (qdevice_net_algo_test_register() != 0) { qdevice_log(LOG_CRIT, "Failed to register decision algorithm 'test' "); return (-1); } if (qdevice_net_algo_ffsplit_register() != 0) { qdevice_log(LOG_CRIT, "Failed to register decision algorithm 'ffsplit' "); return (-1); } if (qdevice_net_algo_2nodelms_register() != 0) { qdevice_log(LOG_CRIT, "Failed to register decision algorithm '2nodelms' "); return (-1); } if (qdevice_net_algo_lms_register() != 0) { qdevice_log(LOG_CRIT, "Failed to register decision algorithm 'lms' "); return (-1); } return (0); } diff --git a/qdevices/qdevice-net-algorithm.h b/qdevices/qdevice-net-algorithm.h index 61e8a8b6..15caf302 100644 --- a/qdevices/qdevice-net-algorithm.h +++ b/qdevices/qdevice-net-algorithm.h @@ -1,138 +1,145 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QDEVICE_NET_ALGORITHM_H_ #define _QDEVICE_NET_ALGORITHM_H_ #include #include #include "node-list.h" #include "qdevice-net-instance.h" #ifdef __cplusplus extern "C" { #endif extern int qdevice_net_algorithm_init(struct qdevice_net_instance *instance); extern int qdevice_net_algorithm_connected(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote); extern int qdevice_net_algorithm_config_node_list_changed(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algorithm_votequorum_node_list_notify(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algorithm_votequorum_quorum_notify(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote); extern int qdevice_net_algorithm_votequorum_expected_votes_notify(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote); extern int qdevice_net_algorithm_config_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote); + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); extern int qdevice_net_algorithm_membership_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algorithm_quorum_node_list_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algorithm_ask_for_vote_reply_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote); extern int qdevice_net_algorithm_vote_info_received(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); extern int qdevice_net_algorithm_echo_reply_received(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number); extern int qdevice_net_algorithm_echo_reply_not_received(struct qdevice_net_instance *instance); extern int qdevice_net_algorithm_disconnected(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote); extern void qdevice_net_algorithm_destroy(struct qdevice_net_instance *instance); struct qdevice_net_algorithm { int (*init)(struct qdevice_net_instance *instance); int (*connected)(struct qdevice_net_instance *instance, int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote); int (*config_node_list_changed)(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote); int (*votequorum_node_list_notify)(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote); int (*votequorum_quorum_notify)(struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote); int (*votequorum_expected_votes_notify)(struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote); int (*config_node_list_reply_received)(struct qdevice_net_instance *instance, - uint32_t seq_number, int initial, enum tlv_vote *vote); + uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); int (*membership_node_list_reply_received)(struct qdevice_net_instance *instance, - uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); int (*quorum_node_list_reply_received)(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); int (*ask_for_vote_reply_received)(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); int (*vote_info_received)(struct qdevice_net_instance *instance, - uint32_t seq_number, enum tlv_vote *vote); + uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, + enum tlv_vote *vote); int (*echo_reply_received)(struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number); int (*echo_reply_not_received)(struct qdevice_net_instance *instance); int (*disconnected)(struct qdevice_net_instance *instance, enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote); void (*destroy)(struct qdevice_net_instance *instance); }; extern int qdevice_net_algorithm_register( enum tlv_decision_algorithm_type algorithm_number, struct qdevice_net_algorithm *algorithm); extern int qdevice_net_algorithm_register_all(void); #ifdef __cplusplus } #endif #endif /* _QDEVICE_NET_ALGORITHM_H_ */ diff --git a/qdevices/qdevice-net-instance.h b/qdevices/qdevice-net-instance.h index 5cb44493..6a002569 100644 --- a/qdevices/qdevice-net-instance.h +++ b/qdevices/qdevice-net-instance.h @@ -1,130 +1,130 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QDEVICE_NET_INSTANCE_H_ #define _QDEVICE_NET_INSTANCE_H_ #include #include #include #include "nss-sock.h" #include "qdevice-instance.h" #include "dynar.h" #include "node-list.h" #include "pr-poll-array.h" #include "qdevice-net-disconnect-reason.h" #include "send-buffer-list.h" #include "tlv.h" #include "timer-list.h" #ifdef __cplusplus extern "C" { #endif enum qdevice_net_instance_state { QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT, QDEVICE_NET_INSTANCE_STATE_SENDING_PREINIT_REPLY, QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY, QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT, QDEVICE_NET_INSTANCE_STATE_WAITING_INIT_REPLY, QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS, }; struct qdevice_net_instance { PRFileDesc *socket; struct dynar receive_buffer; struct send_buffer_list send_buffer_list; int skipping_msg; size_t msg_already_received_bytes; enum qdevice_net_instance_state state; uint32_t last_msg_seq_num; uint32_t echo_request_expected_msg_seq_num; uint32_t echo_reply_received_msg_seq_num; enum tlv_tls_supported tls_supported; int using_tls; int tls_client_cert_sent; - uint32_t heartbeat_interval; /* Adjusted heartbeat interval during normal operation */ - uint32_t sync_heartbeat_interval; /* Adjusted heartbeat interval during corosync sync */ + uint32_t heartbeat_interval; /* Adjusted heartbeat interval during normal operation */ + uint32_t sync_heartbeat_interval; /* Adjusted heartbeat interval during corosync sync */ uint32_t cast_vote_timer_interval; /* Timer for cast vote */ uint32_t connect_timeout; struct timer_list_entry *cast_vote_timer; enum tlv_vote cast_vote_timer_vote; const char *host_addr; uint16_t host_port; const char *cluster_name; enum tlv_decision_algorithm_type decision_algorithm; struct timer_list main_timer_list; struct timer_list_entry *echo_request_timer; int schedule_disconnect; PRFileDesc *votequorum_poll_fd; PRFileDesc *cmap_poll_fd; PRFileDesc *ipc_socket_poll_fd; struct tlv_ring_id last_sent_ring_id; struct tlv_tie_breaker tie_breaker; void *algorithm_data; enum qdevice_net_disconnect_reason disconnect_reason; struct qdevice_instance *qdevice_instance_ptr; struct nss_sock_non_blocking_client non_blocking_client; struct timer_list_entry *connect_timer; int force_ip_version; struct pr_poll_array poll_array; time_t last_echo_reply_received_time; time_t connected_since_time; const struct qdevice_advanced_settings *advanced_settings; }; extern int qdevice_net_instance_init(struct qdevice_net_instance *instance, enum tlv_tls_supported tls_supported, enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval, uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval, const char *host_addr, uint16_t host_port, const char *cluster_name, const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout, int force_ip_version, int cmap_fd, int votequorum_fd, int local_socket_fd, const struct qdevice_advanced_settings *advanced_settings); extern void qdevice_net_instance_clean(struct qdevice_net_instance *instance); extern int qdevice_net_instance_destroy(struct qdevice_net_instance *instance); extern int qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance); #ifdef __cplusplus } #endif #endif /* _QDEVICE_NET_INSTANCE_H_ */ diff --git a/qdevices/qdevice-net-msg-received.c b/qdevices/qdevice-net-msg-received.c index d66d2693..ffc5841a 100644 --- a/qdevices/qdevice-net-msg-received.c +++ b/qdevices/qdevice-net-msg-received.c @@ -1,906 +1,925 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "qdevice-log.h" #include "qdevice-net-algorithm.h" #include "qdevice-net-cast-vote-timer.h" #include "qdevice-net-msg-received.h" #include "qdevice-net-send.h" #include "qdevice-net-votequorum.h" #include "qdevice-net-echo-request-timer.h" #include "msg.h" #include "utils.h" /* * -1 - Incompatible tls combination * 0 - Don't use TLS * 1 - Use TLS */ static int qdevice_net_msg_received_check_tls_compatibility(enum tlv_tls_supported server_tls, enum tlv_tls_supported client_tls) { int res; res = -1; switch (server_tls) { case TLV_TLS_UNSUPPORTED: switch (client_tls) { case TLV_TLS_UNSUPPORTED: res = 0; break; case TLV_TLS_SUPPORTED: res = 0; break; case TLV_TLS_REQUIRED: res = -1; break; } break; case TLV_TLS_SUPPORTED: switch (client_tls) { case TLV_TLS_UNSUPPORTED: res = 0; break; case TLV_TLS_SUPPORTED: res = 1; break; case TLV_TLS_REQUIRED: res = 1; break; } break; case TLV_TLS_REQUIRED: switch (client_tls) { case TLV_TLS_UNSUPPORTED: res = -1; break; case TLV_TLS_SUPPORTED: res = 1; break; case TLV_TLS_REQUIRED: res = 1; break; } break; } return (res); } static void qdevice_net_msg_received_log_msg_decode_error(int ret) { switch (ret) { case -1: qdevice_log(LOG_WARNING, "Received message with option with invalid length"); break; case -2: qdevice_log(LOG_CRIT, "Can't allocate memory"); break; case -3: qdevice_log(LOG_WARNING, "Received inconsistent msg (tlv len > msg size)"); break; case -4: qdevice_log(LOG_ERR, "Received message with option with invalid value"); break; default: qdevice_log(LOG_ERR, "Unknown error occured when decoding message"); break; } } static int qdevice_net_msg_received_unexpected_msg(struct qdevice_net_instance *instance, const struct msg_decoded *msg, const char *msg_str) { qdevice_log(LOG_ERR, "Received unexpected %s message. Disconnecting from server", msg_str); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } static int qdevice_net_msg_received_init(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "init")); } static int qdevice_net_msg_received_preinit(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "preinit")); } static int qdevice_net_msg_check_seq_number(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { if (!msg->seq_number_set || msg->seq_number != instance->last_msg_seq_num) { qdevice_log(LOG_ERR, "Received message doesn't contain seq_number or " "it's not expected one."); return (-1); } return (0); } static int qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { int res; struct send_buffer_list_entry *send_buffer; qdevice_log(LOG_DEBUG, "Received preinit reply msg"); if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY) { qdevice_log(LOG_ERR, "Received unexpected preinit reply message. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } if (qdevice_net_msg_check_seq_number(instance, msg) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } /* * Check TLS support */ if (!msg->tls_supported_set || !msg->tls_client_cert_required_set) { qdevice_log(LOG_ERR, "Required tls_supported or tls_client_cert_required " "option is unset"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } res = qdevice_net_msg_received_check_tls_compatibility(msg->tls_supported, instance->tls_supported); if (res == -1) { qdevice_log(LOG_ERR, "Incompatible tls configuration (server %u client %u)", msg->tls_supported, instance->tls_supported); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_TLS; return (-1); } else if (res == 1) { /* * Start TLS */ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for " "starttls msg"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; return (-1); } instance->last_msg_seq_num++; if (msg_create_starttls(&send_buffer->buffer, 1, instance->last_msg_seq_num) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for starttls msg"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&instance->send_buffer_list, send_buffer); instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT; } else if (res == 0) { if (qdevice_net_send_init(instance) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; return (-1); } } return (0); } static int qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { size_t zi; int res; int send_config_node_list; int send_membership_node_list; int send_quorum_node_list; enum tlv_vote vote; struct tlv_ring_id tlv_rid; enum tlv_quorate quorate; qdevice_log(LOG_DEBUG, "Received init reply msg"); if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_INIT_REPLY) { qdevice_log(LOG_ERR, "Received unexpected init reply message. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } if (qdevice_net_msg_check_seq_number(instance, msg) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } if (!msg->reply_error_code_set) { qdevice_log(LOG_ERR, "Received init reply message without error code." "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } if (msg->reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) { qdevice_log(LOG_ERR, "Received init reply message with error code %"PRIu16". " "Disconnecting from server", msg->reply_error_code); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR; return (-1); } if (!msg->server_maximum_request_size_set || !msg->server_maximum_reply_size_set) { qdevice_log(LOG_ERR, "Required maximum_request_size or maximum_reply_size " "option is unset"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } if (msg->supported_messages == NULL || msg->supported_options == NULL) { qdevice_log(LOG_ERR, "Required supported messages or supported options " "option is unset"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } if (msg->supported_decision_algorithms == NULL) { qdevice_log(LOG_ERR, "Required supported decision algorithms option is unset"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } if (msg->server_maximum_request_size < instance->advanced_settings->net_min_msg_send_size) { qdevice_log(LOG_ERR, "Server accepts maximum %zu bytes message but this client minimum " "is %zu bytes.", msg->server_maximum_request_size, instance->advanced_settings->net_min_msg_send_size); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE; return (-1); } if (msg->server_maximum_reply_size > instance->advanced_settings->net_max_msg_receive_size) { qdevice_log(LOG_ERR, "Server may send message up to %zu bytes message but this client maximum " "is %zu bytes.", msg->server_maximum_reply_size, instance->advanced_settings->net_max_msg_receive_size); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE; return (-1); } /* * Change buffer sizes */ dynar_set_max_size(&instance->receive_buffer, msg->server_maximum_reply_size); send_buffer_list_set_max_buffer_size(&instance->send_buffer_list, msg->server_maximum_request_size); /* * Check if server supports decision algorithm we need */ res = 0; for (zi = 0; zi < msg->no_supported_decision_algorithms && !res; zi++) { if (msg->supported_decision_algorithms[zi] == instance->decision_algorithm) { res = 1; } } if (!res) { qdevice_log(LOG_ERR, "Server doesn't support required decision algorithm"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_ALGORITHM; return (-1); } /* * Finally fully connected so it's possible to remove connection timer */ if (instance->connect_timer != NULL) { timer_list_delete(&instance->main_timer_list, instance->connect_timer); instance->connect_timer = NULL; } /* * Server accepted heartbeat interval -> schedule regular sending of echo request */ qdevice_net_echo_request_timer_schedule(instance); send_config_node_list = 1; send_membership_node_list = 1; send_quorum_node_list = 1; vote = TLV_VOTE_WAIT_FOR_REPLY; if (qdevice_net_algorithm_connected(instance, &send_config_node_list, &send_membership_node_list, &send_quorum_node_list, &vote) != 0) { qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting."); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_CONNECTED_ERR; return (-1); } else { qdevice_log(LOG_DEBUG, "Algorithm decided to %s config node list, %s membership " "node list, %s quorum node list and result vote is %s", (send_config_node_list ? "send" : "not send"), (send_membership_node_list ? "send" : "not send"), (send_quorum_node_list ? "send" : "not send"), tlv_vote_to_str(vote)); } /* * Now we can finally really send node list, votequorum node list and update timer */ if (send_config_node_list) { if (qdevice_net_send_config_node_list(instance, &instance->qdevice_instance_ptr->config_node_list, instance->qdevice_instance_ptr->config_node_list_version_set, instance->qdevice_instance_ptr->config_node_list_version, 1) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; return (-1); } } if (send_membership_node_list) { qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, &instance->qdevice_instance_ptr->vq_node_list_ring_id); if (qdevice_net_send_membership_node_list(instance, &tlv_rid, instance->qdevice_instance_ptr->vq_node_list_entries, instance->qdevice_instance_ptr->vq_node_list) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; return (-1); } } if (send_quorum_node_list) { quorate = (instance->qdevice_instance_ptr->vq_quorum_quorate ? TLV_QUORATE_QUORATE : TLV_QUORATE_INQUORATE); if (qdevice_net_send_quorum_node_list(instance, quorate, instance->qdevice_instance_ptr->vq_quorum_node_list_entries, instance->qdevice_instance_ptr->vq_quorum_node_list) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; return (-1); } } if (qdevice_net_cast_vote_timer_update(instance, vote) != 0) { qdevice_log(LOG_CRIT, "qdevice_net_msg_received_set_option_reply fatal error. " " Can't update cast vote timer vote"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER; } instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS; instance->connected_since_time = time(NULL); return (0); } static int qdevice_net_msg_received_starttls(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "starttls")); } static int qdevice_net_msg_received_server_error(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { if (!msg->reply_error_code_set) { qdevice_log(LOG_ERR, "Received server error without error code set. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; } else { qdevice_log(LOG_ERR, "Received server error %"PRIu16". " "Disconnecting from server", msg->reply_error_code); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR; } return (-1); } static int qdevice_net_msg_received_set_option(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "set option")); } static int qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) { qdevice_log(LOG_ERR, "Received unexpected set option reply message. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } if (qdevice_net_msg_check_seq_number(instance, msg) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } qdevice_net_echo_request_timer_schedule(instance); return (0); } static int qdevice_net_msg_received_echo_request(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "echo request")); } static int qdevice_net_msg_received_echo_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { if (!msg->seq_number_set) { qdevice_log(LOG_ERR, "Received echo reply message doesn't contain seq_number."); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } if (msg->seq_number != instance->echo_request_expected_msg_seq_num) { qdevice_log(LOG_WARNING, "Received echo reply message seq_number is not expected one."); } if (qdevice_net_algorithm_echo_reply_received(instance, msg->seq_number, msg->seq_number == instance->echo_request_expected_msg_seq_num) != 0) { qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_RECEIVED_ERR; return (-1); } instance->echo_reply_received_msg_seq_num = msg->seq_number; instance->last_echo_reply_received_time = time(NULL); return (0); } static int qdevice_net_msg_received_node_list(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "node list")); } static int qdevice_net_msg_received_node_list_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { const char *str; enum tlv_vote result_vote; int res; int case_processed; + int ring_id_is_valid; if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) { qdevice_log(LOG_ERR, "Received unexpected node list reply message. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } if (!msg->vote_set || !msg->seq_number_set || !msg->node_list_type_set) { qdevice_log(LOG_ERR, "Received node list reply message without " "required options. Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } - if (msg->node_list_type == TLV_NODE_LIST_TYPE_MEMBERSHIP && !msg->ring_id_set) { - qdevice_log(LOG_ERR, "Received node list reply message with type membership " + if (!msg->ring_id_set) { + qdevice_log(LOG_ERR, "Received node list reply message " "without ring id set. Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } str = NULL; switch (msg->node_list_type) { case TLV_NODE_LIST_TYPE_INITIAL_CONFIG: str = "initial config"; break; case TLV_NODE_LIST_TYPE_CHANGED_CONFIG: str = "changed config"; break; case TLV_NODE_LIST_TYPE_MEMBERSHIP: str ="membership"; break; case TLV_NODE_LIST_TYPE_QUORUM: str ="quorum"; break; /* * Default is not defined intentionally. Compiler shows warning when new node list type * is added */ } if (str == NULL) { qdevice_log(LOG_CRIT, "qdevice_net_msg_received_node_list_reply fatal error. " "Unhandled node_list_type (debug output)"); exit(1); } qdevice_log(LOG_DEBUG, "Received %s node list reply", str); qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number); qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote)); - if (msg->ring_id_set) { - qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")", + qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")", msg->ring_id.node_id, msg->ring_id.seq); - } /* * Call algorithm */ result_vote = msg->vote; + if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) { + ring_id_is_valid = 0; + qdevice_log(LOG_DEBUG, "Received node list reply with old ring id."); + } else { + ring_id_is_valid = 1; + } + case_processed = 0; switch (msg->node_list_type) { case TLV_NODE_LIST_TYPE_INITIAL_CONFIG: case TLV_NODE_LIST_TYPE_CHANGED_CONFIG: case_processed = 1; res = qdevice_net_algorithm_config_node_list_reply_received(instance, msg->seq_number, (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG), - &result_vote); + &msg->ring_id, ring_id_is_valid, &result_vote); break; case TLV_NODE_LIST_TYPE_MEMBERSHIP: case_processed = 1; res = qdevice_net_algorithm_membership_node_list_reply_received(instance, - msg->seq_number, &msg->ring_id, &result_vote); + msg->seq_number, &msg->ring_id, ring_id_is_valid, &result_vote); break; case TLV_NODE_LIST_TYPE_QUORUM: case_processed = 1; res = qdevice_net_algorithm_quorum_node_list_reply_received(instance, - msg->seq_number, &result_vote); + msg->seq_number, &msg->ring_id, ring_id_is_valid, &result_vote); break; /* * Default is not defined intentionally. Compiler shows warning when new node list type * is added */ } if (!case_processed) { qdevice_log(LOG_CRIT, "qdevice_net_msg_received_node_list_reply fatal error. " "Unhandled node_list_type (algorithm call)"); exit(1); } if (res != 0) { qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting."); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_NODE_LIST_REPLY_ERR; return (-1); } else { - qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(msg->vote)); - } - - if (result_vote != TLV_VOTE_NO_CHANGE) { - if (msg->node_list_type == TLV_NODE_LIST_TYPE_MEMBERSHIP && - !tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) { - qdevice_log(LOG_INFO, "Received membership node list reply with " - "old ring id. Not updating timer"); - } else { - if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) { - instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER; - return (-1); - } - } + qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote)); + } + + if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) { + instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER; + return (-1); } return (0); } static int qdevice_net_msg_received_ask_for_vote(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "ask for vote")); } static int qdevice_net_msg_received_ask_for_vote_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { enum tlv_vote result_vote; + int ring_id_is_valid; if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) { qdevice_log(LOG_ERR, "Received unexpected ask for vote reply message. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } - if (!msg->vote_set || !msg->seq_number_set) { + if (!msg->vote_set || !msg->seq_number_set || !msg->ring_id_set) { qdevice_log(LOG_ERR, "Received node list reply message without " "required options. Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } qdevice_log(LOG_DEBUG, "Received ask for vote reply"); qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number); qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote)); + qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")", + msg->ring_id.node_id, msg->ring_id.seq); result_vote = msg->vote; + if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) { + ring_id_is_valid = 0; + qdevice_log(LOG_DEBUG, "Received ask for vote reply with old ring id."); + } else { + ring_id_is_valid = 1; + } + if (qdevice_net_algorithm_ask_for_vote_reply_received(instance, msg->seq_number, - &result_vote) != 0) { + &msg->ring_id, ring_id_is_valid, &result_vote) != 0) { qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting."); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_ASK_FOR_VOTE_REPLY_ERR; return (-1); } else { - qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(msg->vote)); + qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote)); } if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER; return (-1); } return (0); } static int qdevice_net_msg_received_vote_info(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { struct send_buffer_list_entry *send_buffer; enum tlv_vote result_vote; + int ring_id_is_valid; if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) { qdevice_log(LOG_ERR, "Received unexpected vote info message. " "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; return (-1); } - if (!msg->vote_set || !msg->seq_number_set) { + if (!msg->vote_set || !msg->seq_number_set || !msg->ring_id_set) { qdevice_log(LOG_ERR, "Received node list reply message without " "required options. Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING; return (-1); } qdevice_log(LOG_DEBUG, "Received vote info"); qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number); qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote)); + qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")", + msg->ring_id.node_id, msg->ring_id.seq); result_vote = msg->vote; + + if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) { + ring_id_is_valid = 0; + qdevice_log(LOG_DEBUG, "Received vote info with old ring id."); + } else { + ring_id_is_valid = 1; + } + if (qdevice_net_algorithm_vote_info_received(instance, msg->seq_number, - &result_vote) != 0) { + &msg->ring_id, ring_id_is_valid, &result_vote) != 0) { qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting."); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTE_INFO_ERR; return (-1); } else { qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote)); } if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) { instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER; return (-1); } /* * Create reply message */ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for " "vote info reply msg"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; return (-1); } if (msg_create_vote_info_reply(&send_buffer->buffer, msg->seq_number) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for " "vote info reply list msg"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER; send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&instance->send_buffer_list, send_buffer); return (0); } static int qdevice_net_msg_received_vote_info_reply(struct qdevice_net_instance *instance, const struct msg_decoded *msg) { return (qdevice_net_msg_received_unexpected_msg(instance, msg, "vote info reply")); } int qdevice_net_msg_received(struct qdevice_net_instance *instance) { struct msg_decoded msg; int res; int ret_val; int msg_processed; msg_decoded_init(&msg); res = msg_decode(&instance->receive_buffer, &msg); if (res != 0) { /* * Error occurred. Disconnect. */ qdevice_net_msg_received_log_msg_decode_error(res); qdevice_log(LOG_ERR, "Disconnecting from server"); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR; return (-1); } ret_val = 0; msg_processed = 0; switch (msg.type) { case MSG_TYPE_INIT: msg_processed = 1; ret_val = qdevice_net_msg_received_init(instance, &msg); break; case MSG_TYPE_PREINIT: msg_processed = 1; ret_val = qdevice_net_msg_received_preinit(instance, &msg); break; case MSG_TYPE_PREINIT_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_preinit_reply(instance, &msg); break; case MSG_TYPE_STARTTLS: msg_processed = 1; ret_val = qdevice_net_msg_received_starttls(instance, &msg); break; case MSG_TYPE_SERVER_ERROR: msg_processed = 1; ret_val = qdevice_net_msg_received_server_error(instance, &msg); break; case MSG_TYPE_INIT_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_init_reply(instance, &msg); break; case MSG_TYPE_SET_OPTION: msg_processed = 1; ret_val = qdevice_net_msg_received_set_option(instance, &msg); break; case MSG_TYPE_SET_OPTION_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_set_option_reply(instance, &msg); break; case MSG_TYPE_ECHO_REQUEST: msg_processed = 1; ret_val = qdevice_net_msg_received_echo_request(instance, &msg); break; case MSG_TYPE_ECHO_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_echo_reply(instance, &msg); break; case MSG_TYPE_NODE_LIST: msg_processed = 1; ret_val = qdevice_net_msg_received_node_list(instance, &msg); break; case MSG_TYPE_NODE_LIST_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_node_list_reply(instance, &msg); break; case MSG_TYPE_ASK_FOR_VOTE: msg_processed = 1; ret_val = qdevice_net_msg_received_ask_for_vote(instance, &msg); break; case MSG_TYPE_ASK_FOR_VOTE_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_ask_for_vote_reply(instance, &msg); break; case MSG_TYPE_VOTE_INFO: msg_processed = 1; ret_val = qdevice_net_msg_received_vote_info(instance, &msg); break; case MSG_TYPE_VOTE_INFO_REPLY: msg_processed = 1; ret_val = qdevice_net_msg_received_vote_info_reply(instance, &msg); break; /* * Default is not defined intentionally. Compiler shows warning when msg type is added */ } if (!msg_processed) { qdevice_log(LOG_ERR, "Received unsupported message %u. " "Disconnecting from server", msg.type); instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG; ret_val = -1; } msg_decoded_destroy(&msg); return (ret_val); } diff --git a/qdevices/qdevice-net-nss.c b/qdevices/qdevice-net-nss.c index cf677ac6..7907c2db 100644 --- a/qdevices/qdevice-net-nss.c +++ b/qdevices/qdevice-net-nss.c @@ -1,74 +1,74 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qdevice-log.h" #include "qdevice-net-nss.h" #include "qdevice-net-instance.h" #include "qnet-config.h" SECStatus qdevice_net_nss_bad_cert_hook(void *arg, PRFileDesc *fd) { if (PR_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE || PR_GetError() == SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE || PR_GetError() == SEC_ERROR_CRL_EXPIRED || PR_GetError() == SEC_ERROR_KRL_EXPIRED || PR_GetError() == SSL_ERROR_EXPIRED_CERT_ALERT) { qdevice_log(LOG_WARNING, "Server certificate is expired."); return (SECSuccess); - } + } qdevice_log_nss(LOG_ERR, "Server certificate verification failure."); return (SECFailure); } SECStatus qdevice_net_nss_get_client_auth_data(void *arg, PRFileDesc *sock, struct CERTDistNamesStr *caNames, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { struct qdevice_net_instance *instance; qdevice_log(LOG_DEBUG, "Sending client auth data."); instance = (struct qdevice_net_instance *)arg; instance->tls_client_cert_sent = 1; return (NSS_GetClientAuthData((void *)instance->advanced_settings->net_nss_client_cert_nickname, sock, caNames, pRetCert, pRetKey)); } diff --git a/qdevices/qdevice-net-send.c b/qdevices/qdevice-net-send.c index f7e68cdc..b4ebefc2 100644 --- a/qdevices/qdevice-net-send.c +++ b/qdevices/qdevice-net-send.c @@ -1,313 +1,319 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "qdevice-log.h" #include "qdevice-log-debug.h" #include "qdevice-net-send.h" #include "qdevice-net-votequorum.h" #include "msg.h" #include "utils.h" int qdevice_net_send_echo_request(struct qdevice_net_instance *instance) { struct send_buffer_list_entry *send_buffer; send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_CRIT, "Can't allocate send list buffer for reply msg."); return (-1); } instance->echo_request_expected_msg_seq_num++; if (msg_create_echo_request(&send_buffer->buffer, 1, instance->echo_request_expected_msg_seq_num) == -1) { qdevice_log(LOG_ERR, "Can't allocate send buffer for echo request msg"); return (-1); } send_buffer_list_put(&instance->send_buffer_list, send_buffer); return (0); } int qdevice_net_send_preinit(struct qdevice_net_instance *instance) { struct send_buffer_list_entry *send_buffer; send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for preinit msg"); return (-1); } if (msg_create_preinit(&send_buffer->buffer, instance->cluster_name, 1, instance->last_msg_seq_num) == 0) { qdevice_log(LOG_ERR, "Can't allocate buffer"); send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&instance->send_buffer_list, send_buffer); instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY; return (0); } int qdevice_net_send_init(struct qdevice_net_instance *instance) { enum msg_type *supported_msgs; size_t no_supported_msgs; enum tlv_opt_type *supported_opts; size_t no_supported_opts; struct send_buffer_list_entry *send_buffer; + struct tlv_ring_id tlv_rid; tlv_get_supported_options(&supported_opts, &no_supported_opts); msg_get_supported_messages(&supported_msgs, &no_supported_msgs); instance->last_msg_seq_num++; send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for init msg"); return (-1); } + qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, + &instance->qdevice_instance_ptr->vq_node_list_ring_id); + if (msg_create_init(&send_buffer->buffer, 1, instance->last_msg_seq_num, instance->decision_algorithm, supported_msgs, no_supported_msgs, supported_opts, no_supported_opts, instance->qdevice_instance_ptr->node_id, instance->heartbeat_interval, - &instance->tie_breaker) == 0) { + &instance->tie_breaker, &tlv_rid) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for init msg"); send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } + memcpy(&instance->last_sent_ring_id, &tlv_rid, sizeof(instance->last_sent_ring_id)); + send_buffer_list_put(&instance->send_buffer_list, send_buffer); instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_INIT_REPLY; return (0); } int qdevice_net_send_ask_for_vote(struct qdevice_net_instance *instance) { struct send_buffer_list_entry *send_buffer; send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for ask for vote msg"); return (-1); } instance->last_msg_seq_num++; qdevice_log(LOG_DEBUG, "Sending ask for vote seq = "UTILS_PRI_MSG_SEQ, instance->last_msg_seq_num); if (msg_create_ask_for_vote(&send_buffer->buffer, instance->last_msg_seq_num) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for ask for vote msg"); send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&instance->send_buffer_list, send_buffer); return (0); } int qdevice_net_send_config_node_list(struct qdevice_net_instance *instance, const struct node_list *nlist, int config_version_set, uint64_t config_version, int initial) { struct send_buffer_list_entry *send_buffer; send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for config " "node list msg"); return (-1); } instance->last_msg_seq_num++; qdevice_log(LOG_DEBUG, "Sending config node list seq = "UTILS_PRI_MSG_SEQ, instance->last_msg_seq_num); qdevice_log_debug_dump_node_list(nlist); if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num, (initial ? TLV_NODE_LIST_TYPE_INITIAL_CONFIG : TLV_NODE_LIST_TYPE_CHANGED_CONFIG), 0, NULL, config_version_set, config_version, 0, TLV_QUORATE_INQUORATE, nlist) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for config list msg"); send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&instance->send_buffer_list, send_buffer); return (0); } int qdevice_net_send_membership_node_list(struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[]) { struct node_list nlist; struct send_buffer_list_entry *send_buffer; uint32_t i; node_list_init(&nlist); for (i = 0; i < node_list_entries; i++) { if (node_list_add(&nlist, node_list[i], 0, TLV_NODE_STATE_NOT_SET) == NULL) { qdevice_log(LOG_ERR, "Can't allocate membership node list."); node_list_free(&nlist); return (-1); } } send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for membership " "node list msg"); node_list_free(&nlist); return (-1); } instance->last_msg_seq_num++; qdevice_log(LOG_DEBUG, "Sending membership node list seq = "UTILS_PRI_MSG_SEQ", " "ringid = ("UTILS_PRI_RING_ID").", instance->last_msg_seq_num, ring_id->node_id, ring_id->seq); qdevice_log_debug_dump_node_list(&nlist); if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num, TLV_NODE_LIST_TYPE_MEMBERSHIP, 1, ring_id, 0, 0, 0, 0, &nlist) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for membership list msg"); node_list_free(&nlist); send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } memcpy(&instance->last_sent_ring_id, ring_id, sizeof(instance->last_sent_ring_id)); node_list_free(&nlist); send_buffer_list_put(&instance->send_buffer_list, send_buffer); return (0); } int qdevice_net_send_quorum_node_list(struct qdevice_net_instance *instance, enum tlv_quorate quorate, uint32_t node_list_entries, votequorum_node_t node_list[]) { struct node_list nlist; struct send_buffer_list_entry *send_buffer; uint32_t i; node_list_init(&nlist); for (i = 0; i < node_list_entries; i++) { if (node_list[i].nodeid == 0) { continue; } if (node_list_add(&nlist, node_list[i].nodeid, 0, qdevice_net_votequorum_node_state_to_tlv(node_list[i].state)) == NULL) { qdevice_log(LOG_ERR, "Can't allocate quorum node list."); node_list_free(&nlist); return (-1); } } send_buffer = send_buffer_list_get_new(&instance->send_buffer_list); if (send_buffer == NULL) { qdevice_log(LOG_ERR, "Can't allocate send list buffer for quorum " "node list msg"); node_list_free(&nlist); return (-1); } instance->last_msg_seq_num++; qdevice_log(LOG_DEBUG, "Sending quorum node list seq = "UTILS_PRI_MSG_SEQ", quorate = %u", instance->last_msg_seq_num, quorate); qdevice_log_debug_dump_node_list(&nlist); if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num, TLV_NODE_LIST_TYPE_QUORUM, 0, NULL, 0, 0, 1, quorate, &nlist) == 0) { qdevice_log(LOG_ERR, "Can't allocate send buffer for quorum list msg"); node_list_free(&nlist); send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer); return (-1); } node_list_free(&nlist); send_buffer_list_put(&instance->send_buffer_list, send_buffer); return (0); } diff --git a/qdevices/qdevice-votequorum.c b/qdevices/qdevice-votequorum.c index 562a731c..dd4350b6 100644 --- a/qdevices/qdevice-votequorum.c +++ b/qdevices/qdevice-votequorum.c @@ -1,301 +1,301 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "qdevice-config.h" #include "qdevice-log.h" #include "qdevice-votequorum.h" #include "qdevice-model.h" #include "utils.h" static void qdevice_votequorum_quorum_notify_callback(votequorum_handle_t votequorum_handle, uint64_t context, uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[]) { struct qdevice_instance *instance; uint32_t u32; if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) { qdevice_log(LOG_CRIT, "Fatal error. Can't get votequorum context"); exit(1); } instance->sync_in_progress = 0; qdevice_log(LOG_DEBUG, "Votequorum quorum notify callback:"); qdevice_log(LOG_DEBUG, " Quorate = %u", quorate); qdevice_log(LOG_DEBUG, " Node list (size = %"PRIu32"):", node_list_entries); for (u32 = 0; u32 < node_list_entries; u32++) { qdevice_log(LOG_DEBUG, " %"PRIu32" nodeid = "UTILS_PRI_NODE_ID", state = %"PRIu32, u32, node_list[u32].nodeid, node_list[u32].state); } if (qdevice_model_votequorum_quorum_notify(instance, quorate, node_list_entries, node_list) != 0) { qdevice_log(LOG_DEBUG, "qdevice_model_votequorum_quorum_notify returned error -> exit"); exit(2); } instance->vq_quorum_quorate = quorate; instance->vq_quorum_node_list_entries = node_list_entries; free(instance->vq_quorum_node_list); instance->vq_quorum_node_list = malloc(sizeof(*node_list) * node_list_entries); if (instance->vq_quorum_node_list == NULL) { qdevice_log(LOG_CRIT, "Can't alloc votequorum node list memory"); exit(1); } memcpy(instance->vq_quorum_node_list, node_list, sizeof(*node_list) * node_list_entries); } static void qdevice_votequorum_node_list_notify_callback(votequorum_handle_t votequorum_handle, uint64_t context, votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[]) { struct qdevice_instance *instance; uint32_t u32; if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) { qdevice_log(LOG_CRIT, "Fatal error. Can't get votequorum context"); exit(1); } instance->sync_in_progress = 1; qdevice_log(LOG_DEBUG, "Votequorum nodelist notify callback:"); qdevice_log(LOG_DEBUG, " Ring_id = ("UTILS_PRI_RING_ID")", votequorum_ring_id.nodeid, votequorum_ring_id.seq); qdevice_log(LOG_DEBUG, " Node list (size = %"PRIu32"):", node_list_entries); for (u32 = 0; u32 < node_list_entries; u32++) { qdevice_log(LOG_DEBUG, " %"PRIu32" nodeid = "UTILS_PRI_NODE_ID, u32, node_list[u32]); } if (qdevice_model_votequorum_node_list_notify(instance, votequorum_ring_id, node_list_entries, node_list) != 0) { qdevice_log(LOG_DEBUG, "qdevice_votequorum_node_list_notify_callback returned error -> exit"); exit(2); } instance->vq_node_list_ring_id_set = 1; memcpy(&instance->vq_node_list_ring_id, &votequorum_ring_id, sizeof(votequorum_ring_id)); instance->vq_node_list_entries = node_list_entries; free(instance->vq_node_list); instance->vq_node_list = malloc(sizeof(*node_list) * node_list_entries); if (instance->vq_node_list == NULL) { qdevice_log(LOG_CRIT, "Can't alloc votequorum node list memory"); exit(1); } memcpy(instance->vq_node_list, node_list, sizeof(*node_list) * node_list_entries); } static void qdevice_votequorum_expected_votes_notify_callback(votequorum_handle_t votequorum_handle, uint64_t context, uint32_t expected_votes) { struct qdevice_instance *instance; if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) { qdevice_log(LOG_CRIT, "Fatal error. Can't get votequorum context"); exit(1); } qdevice_log(LOG_DEBUG, "Votequorum expected_votes notify callback:"); qdevice_log(LOG_DEBUG, " Expected_votes: "UTILS_PRI_EXPECTED_VOTES, expected_votes); if (qdevice_model_votequorum_expected_votes_notify(instance, expected_votes) != 0) { qdevice_log(LOG_DEBUG, "qdevice_votequorum_expected_votes_notify_callback returned error -> exit"); exit(2); } instance->vq_expected_votes = expected_votes; } void qdevice_votequorum_init(struct qdevice_instance *instance) { votequorum_callbacks_t votequorum_callbacks; votequorum_handle_t votequorum_handle; cs_error_t res; int no_retries; struct votequorum_info vq_info; memset(&votequorum_callbacks, 0, sizeof(votequorum_callbacks)); votequorum_callbacks.votequorum_quorum_notify_fn = qdevice_votequorum_quorum_notify_callback; votequorum_callbacks.votequorum_nodelist_notify_fn = qdevice_votequorum_node_list_notify_callback; votequorum_callbacks.votequorum_expectedvotes_notify_fn = qdevice_votequorum_expected_votes_notify_callback; no_retries = 0; while ((res = votequorum_initialize(&votequorum_handle, &votequorum_callbacks)) == CS_ERR_TRY_AGAIN && no_retries++ < instance->advanced_settings->max_cs_try_again) { (void)poll(NULL, 0, 1000); } - if (res != CS_OK) { + if (res != CS_OK) { qdevice_log(LOG_CRIT, "Failed to initialize the votequorum API. Error %s", cs_strerror(res)); exit(1); } if ((res = votequorum_qdevice_register(votequorum_handle, instance->advanced_settings->votequorum_device_name)) != CS_OK) { qdevice_log(LOG_CRIT, "Can't register votequorum device. Error %s", cs_strerror(res)); exit(1); } if ((res = votequorum_context_set(votequorum_handle, (void *)instance)) != CS_OK) { qdevice_log(LOG_CRIT, "Can't set votequorum context. Error %s", cs_strerror(res)); exit(1); } if ((res = votequorum_getinfo(votequorum_handle, VOTEQUORUM_QDEVICE_NODEID, &vq_info)) != CS_OK) { qdevice_log(LOG_CRIT, "Can't get votequorum information. Error %s", cs_strerror(res)); exit(1); } instance->vq_expected_votes = vq_info.node_expected_votes; instance->votequorum_handle = votequorum_handle; votequorum_fd_get(votequorum_handle, &instance->votequorum_poll_fd); if ((res = votequorum_trackstart(instance->votequorum_handle, 0, CS_TRACK_CHANGES)) != CS_OK) { qdevice_log(LOG_CRIT, "Can't start tracking votequorum changes. Error %s", cs_strerror(res)); exit(1); } } void qdevice_votequorum_destroy(struct qdevice_instance *instance) { cs_error_t res; free(instance->vq_quorum_node_list); instance->vq_quorum_node_list = NULL; free(instance->vq_node_list); instance->vq_node_list = NULL; res = votequorum_trackstop(instance->votequorum_handle); if (res != CS_OK) { qdevice_log(LOG_WARNING, "Can't start tracking votequorum changes. Error %s", cs_strerror(res)); } res = votequorum_qdevice_unregister(instance->votequorum_handle, instance->advanced_settings->votequorum_device_name); - if (res != CS_OK) { - qdevice_log(LOG_WARNING, "Unable to unregister votequorum device. Error %s", cs_strerror(res)); + if (res != CS_OK) { + qdevice_log(LOG_WARNING, "Unable to unregister votequorum device. Error %s", cs_strerror(res)); } res = votequorum_finalize(instance->votequorum_handle); if (res != CS_OK) { qdevice_log(LOG_WARNING, "Unable to finalize votequorum. Error %s", cs_strerror(res)); } } int qdevice_votequorum_wait_for_ring_id(struct qdevice_instance *instance) { int no_retries; no_retries = 0; - while (!instance->vq_node_list_ring_id_set && - qdevice_votequorum_dispatch(instance) != -1 && - no_retries++ < instance->advanced_settings->max_cs_try_again) { + while (qdevice_votequorum_dispatch(instance) != -1 && + no_retries++ < instance->advanced_settings->max_cs_try_again && + !instance->vq_node_list_ring_id_set) { (void)poll(NULL, 0, 1000); } if (!instance->vq_node_list_ring_id_set) { qdevice_log(LOG_CRIT, "Can't get initial votequorum membership information."); return (-1); } return (0); } int qdevice_votequorum_dispatch(struct qdevice_instance *instance) { cs_error_t res; res = votequorum_dispatch(instance->votequorum_handle, CS_DISPATCH_ALL); if (res != CS_OK && res != CS_ERR_TRY_AGAIN) { qdevice_log(LOG_ERR, "Can't dispatch votequorum messages"); return (-1); } return (0); } int qdevice_votequorum_poll(struct qdevice_instance *instance, int cast_vote) { cs_error_t res; instance->vq_last_poll = time(NULL); instance->vq_last_poll_cast_vote = cast_vote; res = votequorum_qdevice_poll(instance->votequorum_handle, instance->advanced_settings->votequorum_device_name, cast_vote, instance->vq_node_list_ring_id); if (res != CS_OK && res != CS_ERR_TRY_AGAIN) { if (res == CS_ERR_MESSAGE_ERROR) { qdevice_log(LOG_INFO, "qdevice_votequorum_poll called with old ring id"); } else { qdevice_log(LOG_CRIT, "Can't call votequorum_qdevice_poll. Error %s", cs_strerror(res)); return (-1); } } return (0); } diff --git a/qdevices/qnet-config.h b/qdevices/qnet-config.h index 906907f5..d2772816 100644 --- a/qdevices/qnet-config.h +++ b/qdevices/qnet-config.h @@ -1,142 +1,142 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _QNET_CONFIG_H_ #define _QNET_CONFIG_H_ #include #include "tlv.h" #ifdef __cplusplus extern "C" { #endif /* * There are "hardcoded" defaults for both qnetd and qdevice-net. It's not so good * idea to change them as long as you are not 100% sure what you are doing. Also * most of them can be changed in CLI via advanced_settings (-S). */ #define QNETD_PROGRAM_NAME "corosync-qnetd" #define QNETD_DEFAULT_HOST_PORT 5403 #define QNETD_DEFAULT_LISTEN_BACKLOG 10 #define QNETD_MIN_LISTEN_BACKLOG 1 #define QNETD_DEFAULT_MAX_CLIENT_SEND_BUFFERS 10 #define QNETD_MIN_CLIENT_SEND_BUFFERS 2 #define QNETD_DEFAULT_MAX_CLIENT_SEND_SIZE (1 << 15) #define QNETD_DEFAULT_MAX_CLIENT_RECEIVE_SIZE (1 << 15) #define QNETD_MIN_CLIENT_RECEIVE_SEND_SIZE 16 #define QNETD_DEFAULT_MAX_CLIENTS 0 #define QNETD_DEFAULT_NSS_DB_DIR COROSYSCONFDIR "/qdevice/net/qnetd/nssdb" #define QNETD_DEFAULT_CERT_NICKNAME "QNetd Cert" #define QNETD_DEFAULT_TLS_SUPPORTED TLV_TLS_SUPPORTED #define QNETD_DEFAULT_TLS_CLIENT_CERT_REQUIRED 1 #define QNETD_DEFAULT_HEARTBEAT_INTERVAL_MIN (1*1000) #define QNETD_DEFAULT_HEARTBEAT_INTERVAL_MAX (2*60*1000) #define QNETD_MIN_HEARTBEAT_INTERVAL 1 #define QNETD_DEFAULT_DPD_ENABLED 1 #define QNETD_DEFAULT_DPD_INTERVAL (10*1000) #define QNETD_MIN_DPD_INTERVAL 1 #define QNETD_DEFAULT_LOCK_FILE LOCALSTATEDIR"/run/corosync-qnetd.pid" #define QNETD_DEFAULT_LOCAL_SOCKET_FILE LOCALSTATEDIR"/run/corosync-qnetd.sock" #define QNETD_DEFAULT_LOCAL_SOCKET_BACKLOG 10 #define QNETD_MIN_LOCAL_SOCKET_BACKLOG 1 #define QNETD_DEFAULT_IPC_MAX_CLIENTS 10 #define QNETD_MIN_IPC_MAX_CLIENTS 0 #define QNETD_DEFAULT_IPC_MAX_RECEIVE_SIZE (4*1024) #define QNETD_DEFAULT_IPC_MAX_SEND_SIZE (10*1024*1024) #define QNETD_MIN_IPC_RECEIVE_SEND_SIZE 1024 #define QNETD_TOOL_PROGRAM_NAME "corosync-qnetd-tool" #define QDEVICE_NET_DEFAULT_NSS_DB_DIR COROSYSCONFDIR "/qdevice/net/node/nssdb" #define QDEVICE_NET_DEFAULT_INITIAL_MSG_RECEIVE_SIZE (1 << 15) #define QDEVICE_NET_DEFAULT_INITIAL_MSG_SEND_SIZE (1 << 15) #define QDEVICE_NET_DEFAULT_MIN_MSG_SEND_SIZE QDEVICE_NET_DEFAULT_INITIAL_MSG_SEND_SIZE #define QDEVICE_NET_DEFAULT_MAX_MSG_RECEIVE_SIZE (1 << 24) #define QDEVICE_NET_DEFAULT_MAX_SEND_BUFFERS 10 #define QDEVICE_NET_MIN_MAX_SEND_BUFFERS 2 #define QDEVICE_NET_MIN_MSG_RECEIVE_SEND_SIZE 16 #define QDEVICE_NET_DEFAULT_NSS_QNETD_CN "Qnetd Server" #define QDEVICE_NET_DEFAULT_NSS_CLIENT_CERT_NICKNAME "Cluster Cert" #define QDEVICE_NET_DEFAULT_ALGORITHM TLV_DECISION_ALGORITHM_TYPE_TEST #define QDEVICE_NET_DEFAULT_TLS_SUPPORTED TLV_TLS_SUPPORTED #define QDEVICE_NET_DEFAULT_TIE_BREAKER_MODE TLV_TIE_BREAKER_MODE_LOWEST #define QDEVICE_NET_DEFAULT_HEARTBEAT_INTERVAL_MIN QNETD_DEFAULT_HEARTBEAT_INTERVAL_MIN #define QDEVICE_NET_DEFAULT_HEARTBEAT_INTERVAL_MAX QNETD_DEFAULT_HEARTBEAT_INTERVAL_MAX #define QDEVICE_NET_MIN_HEARTBEAT_INTERVAL 1 #define QDEVICE_NET_DEFAULT_MIN_CONNECT_TIMEOUT (1*1000) #define QDEVICE_NET_DEFAULT_MAX_CONNECT_TIMEOUT (2*60*1000) #define QDEVICE_NET_MIN_CONNECT_TIMEOUT 1 #ifdef DEBUG #define QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED 1 #else #define QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED 0 #endif #define QDEVICE_NET_DEFAULT_DELAY_BEFORE_RECONNECT (1000) #define QDEVICE_NET_MIN_DELAY_BEFORE_RECONNECT 1 /* * Decision algorithms supported by qnetd */ -#define QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE 4 +#define QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE 4 extern enum tlv_decision_algorithm_type qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE]; #define QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE #ifdef __cplusplus } #endif #endif /* _QNET_CONFIG_H_ */ diff --git a/qdevices/qnetd-algo-2nodelms.c b/qdevices/qnetd-algo-2nodelms.c index aaf7085f..a62b9a9c 100644 --- a/qdevices/qnetd-algo-2nodelms.c +++ b/qdevices/qnetd-algo-2nodelms.c @@ -1,321 +1,321 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Christine Caulfield (ccaulfie@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* * This is a simple 'last man standing' algorithm for 2 node clusters * * If the node is the only one left in the cluster that can see the * qdevice server then we return a vote. * * If more than one node can see the qdevice server but the nodes can't * see each other then we return a vote to the nominated tie_breaker node * * If there are more than two nodes, then we don't return a vote. * this is not our job. */ #include #include #include #include "qnetd-algo-2nodelms.h" #include "qnetd-log.h" #include "qnetd-cluster-list.h" #include "qnetd-algo-utils.h" #include "utils.h" struct qnetd_algo_2nodelms_info { int num_config_nodes; enum tlv_vote last_result; }; enum tlv_reply_error_code qnetd_algo_2nodelms_client_init(struct qnetd_client *client) { struct qnetd_algo_2nodelms_info *info; info = malloc(sizeof(struct qnetd_algo_2nodelms_info)); if (!info) { return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } client->algorithm_data = info; info->last_result = 0; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent configuration node list * All client fields are already set. Nodes is actual node list, initial is used * to distinquish between initial node list and changed node list. * msg_seq_num is 32-bit number set by client. If client sent config file version, * config_version_set is set to 1 and config_version contains valid config file version. * * Function has to return result_vote. This can be one of ack/nack, ask_later (client * should ask later for a vote) or wait_for_reply (client should wait for reply). * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_2nodelms_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial, enum tlv_vote *result_vote) { struct node_list_entry *node_info; struct qnetd_algo_2nodelms_info *info = client->algorithm_data; int node_count = 0; /* Check this is a 2 node cluster */ TAILQ_FOREACH(node_info, nodes, entries) { node_count++; } info->num_config_nodes = node_count; qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s config_list has %d nodes", client->cluster_name, node_count); if (node_count != 2) { qnetd_log(LOG_INFO, "algo-2nodelms: cluster %s does not have 2 configured nodes, it has %d", client->cluster_name, node_count); *result_vote = TLV_VOTE_NACK; return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM); } *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent membership node list. * All client fields are already set. Nodes is actual node list. * msg_seq_num is 32-bit number set by client. If client sent config file version, * config_version_set is set to 1 and config_version contains valid config file version. * ring_id and quorate are copied from client votequorum callback. * * Function has to return result_vote. This can be one of ack/nack, ask_later (client * should ask later for a vote) or wait_for_reply (client should wait for reply). * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_2nodelms_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes, enum tlv_vote *result_vote) { struct node_list_entry *node_info; struct qnetd_client *other_client; struct qnetd_algo_2nodelms_info *info = client->algorithm_data; int node_count = 0; int low_node_id = INT_MAX; int high_node_id = 0; /* If we're a newcomer and there is another active partition, then we must NACK * to avoid quorum moving to us from already active nodes. */ if (info->last_result == 0) { TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) { struct qnetd_algo_2nodelms_info *other_info = other_client->algorithm_data; if (!tlv_ring_id_eq(ring_id, &other_client->last_ring_id) && other_info->last_result == TLV_VOTE_ACK) { /* Don't save NACK, we need to know subsequently if we haven't been voting */ *result_vote = TLV_VOTE_NACK; qnetd_log(LOG_DEBUG, "algo-2nodelms: we are a new partition and another active partition exists. NACK"); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } } } /* If both nodes are present, then we're OK. return a vote */ TAILQ_FOREACH(node_info, nodes, entries) { node_count++; } qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s (client %p nodeid "UTILS_PRI_NODE_ID") membership list has %d member nodes (ring ID "UTILS_PRI_RING_ID")", client->cluster_name, client, client->node_id, node_count, ring_id->node_id, ring_id->seq); if (node_count == 2) { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running normally. Both nodes active", client->cluster_name); *result_vote = info->last_result = TLV_VOTE_ACK; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* Now look for other clients connected from this cluster that can't see us any more */ node_count = 0; TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) { node_count++; qnetd_log(LOG_DEBUG, "algo-2nodelms: seen nodeid "UTILS_PRI_NODE_ID" on client %p (ring ID "UTILS_PRI_RING_ID")", other_client->node_id, other_client, other_client->last_ring_id.node_id, other_client->last_ring_id.seq); if (other_client->node_id < low_node_id) { low_node_id = other_client->node_id; } if (other_client->node_id > high_node_id) { high_node_id = other_client->node_id; } } qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s %d nodes running independently", client->cluster_name, node_count); /* Only 1 node alive .. allow it to continue */ if (node_count == 1) { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on 'last-man'", client->cluster_name); *result_vote = info->last_result = TLV_VOTE_ACK; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* Both nodes are alive. Only give a vote to the nominated tie-breaker node */ switch (client->tie_breaker.mode) { case TLV_TIE_BREAKER_MODE_LOWEST: if (client->node_id == low_node_id) { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on low node-id %d", client->cluster_name, low_node_id); *result_vote = info->last_result = TLV_VOTE_ACK; } else { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because low nodeid %d is active", client->cluster_name, client->node_id, low_node_id); *result_vote = info->last_result = TLV_VOTE_NACK; } break; case TLV_TIE_BREAKER_MODE_HIGHEST: if (client->node_id == high_node_id) { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on high node-id %d", client->cluster_name, high_node_id); *result_vote = info->last_result = TLV_VOTE_ACK; } else { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because high nodeid %d is active", client->cluster_name, client->node_id, high_node_id); *result_vote = info->last_result = TLV_VOTE_NACK; } break; case TLV_TIE_BREAKER_MODE_NODE_ID: if (client->node_id == client->tie_breaker.node_id) { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on nominated tie-breaker node %d", client->cluster_name, client->tie_breaker.node_id); *result_vote = info->last_result = TLV_VOTE_ACK; } else { qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because nominated tie-breaker nodeid %d is active", client->cluster_name, client->node_id, client->tie_breaker.node_id); *result_vote = info->last_result = TLV_VOTE_NACK; } break; default: qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because tie-breaker option is invalid: %d", client->cluster_name, client->node_id, client->tie_breaker.mode); *result_vote = info->last_result = TLV_VOTE_NACK; } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_2nodelms_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes, enum tlv_vote *result_vote) { *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client disconnect. Client structure is still existing (and it's part * of a client->cluster), but it is destroyed (and removed from cluster) right after * this callback finishes. Callback is used mainly for destroing client->algorithm_data. */ void qnetd_algo_2nodelms_client_disconnect(struct qnetd_client *client, int server_going_down) { qnetd_log(LOG_INFO, "algo-2nodelms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "disconnect", client, client->cluster_name, client->node_id); qnetd_log(LOG_INFO, "algo-2nodelms: server going down %u", server_going_down); free(client->algorithm_data); } /* * Called after client sent ask for vote message. This is usually happening after server * replied TLV_VOTE_ASK_LATER. */ enum tlv_reply_error_code qnetd_algo_2nodelms_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote) { struct qnetd_algo_2nodelms_info *info = client->algorithm_data; qnetd_log(LOG_INFO, "algo-2nodelms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "asked for a vote", client, client->cluster_name, client->node_id); if (info->last_result == 0) { *result_vote = TLV_VOTE_ASK_LATER; } else { *result_vote = info->last_result; } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_2nodelms_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { qnetd_log(LOG_INFO, "algo-2nodelms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "replied back to vote info message", client, client->cluster_name, client->node_id); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_2nodelms_timer_callback(struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote) { return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static struct qnetd_algorithm qnetd_algo_2nodelms = { - .init = qnetd_algo_2nodelms_client_init, - .config_node_list_received = qnetd_algo_2nodelms_config_node_list_received, - .membership_node_list_received = qnetd_algo_2nodelms_membership_node_list_received, - .quorum_node_list_received = qnetd_algo_2nodelms_quorum_node_list_received, - .client_disconnect = qnetd_algo_2nodelms_client_disconnect, - .ask_for_vote_received = qnetd_algo_2nodelms_ask_for_vote_received, - .vote_info_reply_received = qnetd_algo_2nodelms_vote_info_reply_received, - .timer_callback = qnetd_algo_2nodelms_timer_callback, + .init = qnetd_algo_2nodelms_client_init, + .config_node_list_received = qnetd_algo_2nodelms_config_node_list_received, + .membership_node_list_received = qnetd_algo_2nodelms_membership_node_list_received, + .quorum_node_list_received = qnetd_algo_2nodelms_quorum_node_list_received, + .client_disconnect = qnetd_algo_2nodelms_client_disconnect, + .ask_for_vote_received = qnetd_algo_2nodelms_ask_for_vote_received, + .vote_info_reply_received = qnetd_algo_2nodelms_vote_info_reply_received, + .timer_callback = qnetd_algo_2nodelms_timer_callback, }; enum tlv_reply_error_code qnetd_algo_2nodelms_register() { return qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_2NODELMS, &qnetd_algo_2nodelms); } diff --git a/qdevices/qnetd-algo-ffsplit.c b/qdevices/qnetd-algo-ffsplit.c index b1649353..fb8d2d33 100644 --- a/qdevices/qnetd-algo-ffsplit.c +++ b/qdevices/qnetd-algo-ffsplit.c @@ -1,298 +1,298 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qnetd-algo-ffsplit.h" #include "qnetd-log.h" #include "qnetd-cluster-list.h" #include "qnetd-cluster.h" struct ffsplit_cluster_data { uint8_t leader_set; uint32_t leader_id; }; enum tlv_reply_error_code qnetd_algo_ffsplit_client_init(struct qnetd_client *client) { struct ffsplit_cluster_data *cluster_data; if (qnetd_cluster_size(client->cluster) == 1) { cluster_data = malloc(sizeof(struct ffsplit_cluster_data)); if (cluster_data == NULL) { qnetd_log(LOG_ERR, "ffsplit: Can't initialize cluster data for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } memset(cluster_data, 0, sizeof(*cluster_data)); client->cluster->algorithm_data = cluster_data; } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static int qnetd_algo_ffsplit_is_prefered_partition(struct qnetd_client *client, const struct node_list *config_node_list, const struct node_list *membership_node_list) { uint32_t prefered_node_id; struct node_list_entry *node_entry; switch (client->tie_breaker.mode) { case TLV_TIE_BREAKER_MODE_LOWEST: node_entry = TAILQ_FIRST(config_node_list); prefered_node_id = node_entry->node_id; TAILQ_FOREACH(node_entry, config_node_list, entries) { if (node_entry->node_id < prefered_node_id) { prefered_node_id = node_entry->node_id; } } break; case TLV_TIE_BREAKER_MODE_HIGHEST: node_entry = TAILQ_FIRST(config_node_list); prefered_node_id = node_entry->node_id; TAILQ_FOREACH(node_entry, config_node_list, entries) { if (node_entry->node_id > prefered_node_id) { prefered_node_id = node_entry->node_id; } } break; case TLV_TIE_BREAKER_MODE_NODE_ID: prefered_node_id = client->tie_breaker.node_id; break; } return (node_list_find_node_id(membership_node_list, prefered_node_id) != NULL); } static enum tlv_vote qnetd_algo_ffsplit_do(struct qnetd_client *client, const struct node_list *config_node_list, const struct node_list *membership_node_list) { struct ffplist_cluster_data *cluster_data; cluster_data = (struct ffplist_cluster_data *)client->cluster->algorithm_data; if (node_list_size(config_node_list) % 2 != 0) { /* * Odd clusters never split into 50:50. */ if (node_list_size(membership_node_list) > node_list_size(config_node_list) / 2) { return (TLV_VOTE_ACK); } else { return (TLV_VOTE_NACK); } } else { if (node_list_size(membership_node_list) > node_list_size(config_node_list) / 2) { return (TLV_VOTE_ACK); } else if (node_list_size(membership_node_list) < node_list_size(config_node_list) / 2) { return (TLV_VOTE_NACK); } else { /* * 50:50 split */ if (qnetd_algo_ffsplit_is_prefered_partition(client, config_node_list, membership_node_list)) { return (TLV_VOTE_ACK); } else { return (TLV_VOTE_NACK); } } } } enum tlv_reply_error_code qnetd_algo_ffsplit_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial, enum tlv_vote *result_vote) { if (node_list_size(nodes) == 0) { /* * Empty node list shouldn't happen */ qnetd_log(LOG_ERR, "ffsplit: Received empty config node list for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_CONFIG_NODE_LIST); } if (node_list_find_node_id(nodes, client->node_id) == NULL) { /* * Current node is not in node list */ qnetd_log(LOG_ERR, "ffsplit: Received config node list without client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_CONFIG_NODE_LIST); } if (initial || node_list_size(&client->last_membership_node_list) == 0) { /* * Initial node list -> membership is going to be send by client */ *result_vote = TLV_VOTE_ASK_LATER; } else { *result_vote = qnetd_algo_ffsplit_do(client, nodes, &client->last_membership_node_list); } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent membership node list. * All client fields are already set. Nodes is actual node list. * msg_seq_num is 32-bit number set by client. If client sent config file version, * config_version_set is set to 1 and config_version contains valid config file version. * ring_id and quorate are copied from client votequorum callback. * * Function has to return result_vote. This can be one of ack/nack, ask_later (client * should ask later for a vote) or wait_for_reply (client should wait for reply). * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_ffsplit_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes, enum tlv_vote *result_vote) { if (node_list_size(nodes) == 0) { /* * Empty node list shouldn't happen */ qnetd_log(LOG_ERR, "ffsplit: Received empty membership node list for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_MEMBERSHIP_NODE_LIST); } if (node_list_find_node_id(nodes, client->node_id) == NULL) { /* * Current node is not in node list */ qnetd_log(LOG_ERR, "ffsplit: Received membership node list without client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_MEMBERSHIP_NODE_LIST); } if (node_list_size(&client->configuration_node_list) == 0) { /* * Config node list not received -> it's going to be sent later */ *result_vote = TLV_VOTE_ASK_LATER; } else { *result_vote = qnetd_algo_ffsplit_do(client, &client->configuration_node_list, nodes); } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_ffsplit_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes, enum tlv_vote *result_vote) { /* * Quorum node list is informative -> no change */ *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } void qnetd_algo_ffsplit_client_disconnect(struct qnetd_client *client, int server_going_down) { if (qnetd_cluster_size(client->cluster) == 1) { /* * Last client in the cluster */ free(client->cluster->algorithm_data); } } enum tlv_reply_error_code qnetd_algo_ffsplit_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote) { return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM_MESSAGE); } enum tlv_reply_error_code qnetd_algo_ffsplit_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM_MESSAGE); } enum tlv_reply_error_code qnetd_algo_ffsplit_timer_callback(struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote) { return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static struct qnetd_algorithm qnetd_algo_ffsplit = { - .init = qnetd_algo_ffsplit_client_init, - .config_node_list_received = qnetd_algo_ffsplit_config_node_list_received, - .membership_node_list_received = qnetd_algo_ffsplit_membership_node_list_received, - .quorum_node_list_received = qnetd_algo_ffsplit_quorum_node_list_received, - .client_disconnect = qnetd_algo_ffsplit_client_disconnect, - .ask_for_vote_received = qnetd_algo_ffsplit_ask_for_vote_received, - .vote_info_reply_received = qnetd_algo_ffsplit_vote_info_reply_received, - .timer_callback = qnetd_algo_ffsplit_timer_callback, + .init = qnetd_algo_ffsplit_client_init, + .config_node_list_received = qnetd_algo_ffsplit_config_node_list_received, + .membership_node_list_received = qnetd_algo_ffsplit_membership_node_list_received, + .quorum_node_list_received = qnetd_algo_ffsplit_quorum_node_list_received, + .client_disconnect = qnetd_algo_ffsplit_client_disconnect, + .ask_for_vote_received = qnetd_algo_ffsplit_ask_for_vote_received, + .vote_info_reply_received = qnetd_algo_ffsplit_vote_info_reply_received, + .timer_callback = qnetd_algo_ffsplit_timer_callback, }; enum tlv_reply_error_code qnetd_algo_ffsplit_register() { return (qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_FFSPLIT, &qnetd_algo_ffsplit)); } diff --git a/qdevices/qnetd-algo-lms.c b/qdevices/qnetd-algo-lms.c index 6fd45cb8..55a1bb29 100644 --- a/qdevices/qnetd-algo-lms.c +++ b/qdevices/qnetd-algo-lms.c @@ -1,381 +1,381 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Christine Caulfield (ccaulfie@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* * This is a 'last man standing' algorithm for 2+ node clusters * * If the node is the only one left in the cluster that can see the * qdevice server then we return a vote. * * If more than one node can see the qdevice server but some nodes can't * see each other then we divide the cluster up into 'partitions' based on * their ring_id and return a vote to nodes in the partition that contains * a nominated nodeid. (lowest, highest, etc) * */ #include #include #include #include #include "qnetd-algo-lms.h" #include "qnetd-log.h" #include "qnetd-cluster-list.h" #include "qnetd-algo-utils.h" #include "qnetd-client-algo-timer.h" #include "utils.h" struct qnetd_algo_lms_info { int num_config_nodes; enum tlv_vote last_result; partitions_list_t partition_list; }; static enum tlv_reply_error_code do_lms_algorithm(struct qnetd_client *client, const struct tlv_ring_id *cur_ring_id, enum tlv_vote *result_vote) { struct qnetd_client *other_client; struct qnetd_algo_lms_info *info = client->algorithm_data; struct qnetd_algo_partition *cur_partition; struct qnetd_algo_partition *largest_partition; const struct tlv_ring_id *ring_id = cur_ring_id; int num_partitions; int joint_leader; /* We are running the algorithm, don't do it again unless we say so */ qnetd_client_algo_timer_abort(client); if (qnetd_algo_all_ring_ids_match(client, ring_id) == -1) { qnetd_log(LOG_DEBUG, "algo-lms: nodeid %d: ring ID %d/%ld not unique in this membership, waiting", client->node_id, ring_id->node_id, ring_id->seq); qnetd_client_algo_timer_schedule(client); *result_vote = info->last_result = TLV_VOTE_WAIT_FOR_REPLY; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* Create and count the number of separate partitions */ if ( (num_partitions = qnetd_algo_create_partitions(client, &info->partition_list, ring_id)) == -1) { qnetd_log(LOG_DEBUG, "algo-lms: Error creating partition list"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } /* This can happen if we are first on the block */ if (num_partitions == 0) { qnetd_log(LOG_DEBUG, "algo-lms: No partitions found"); qnetd_client_algo_timer_schedule(client); *result_vote = info->last_result = TLV_VOTE_WAIT_FOR_REPLY; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } qnetd_algo_dump_partitions(&info->partition_list); /* Only 1 partition - let votequorum sort it out */ if (num_partitions == 1) { qnetd_log(LOG_DEBUG, "algo-lms: Only 1 partition. This is votequorum's problem, not ours"); qnetd_algo_free_partitions(&info->partition_list); *result_vote = info->last_result = TLV_VOTE_ACK; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* If we're a newcomer and there is another active partition, then we must NACK * to avoid quorum moving to us from already active nodes. */ if (info->last_result == 0) { TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) { struct qnetd_algo_lms_info *other_info = other_client->algorithm_data; if (!tlv_ring_id_eq(ring_id, &other_client->last_ring_id) && other_info->last_result == TLV_VOTE_ACK) { qnetd_algo_free_partitions(&info->partition_list); /* Don't save NACK, we need to know subsequently if we haven't been voting */ *result_vote = TLV_VOTE_NACK; qnetd_log(LOG_DEBUG, "algo-lms: we are a new partition and another active partition exists. NACK"); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } } } /* Find the largest partition */ largest_partition = NULL; TAILQ_FOREACH(cur_partition, &info->partition_list, entries) { if (!largest_partition || largest_partition->num_nodes < cur_partition->num_nodes) { largest_partition = cur_partition; } } qnetd_log(LOG_DEBUG, "algo-lms: largest partition is %d/%ld with %d nodes", largest_partition->ring_id.node_id, largest_partition->ring_id.seq, largest_partition->num_nodes); /* Now check if it's really the largest, and not just the joint-largest */ joint_leader = 0; TAILQ_FOREACH(cur_partition, &info->partition_list, entries) { if (largest_partition != cur_partition && largest_partition->num_nodes == cur_partition->num_nodes) { joint_leader = 1; } } if (!joint_leader) { /* Largest partition is unique, allow us to run if we're in that partition. */ if (tlv_ring_id_eq(&largest_partition->ring_id, ring_id)) { qnetd_log(LOG_DEBUG, "algo-lms: We are in the largest partition. ACK"); *result_vote = info->last_result = TLV_VOTE_ACK; } else { qnetd_log(LOG_DEBUG, "algo-lms: We are NOT in the largest partition. NACK"); *result_vote = info->last_result = TLV_VOTE_NACK; } } else { int tb_node_id; struct tlv_ring_id tb_node_ring_id = {0LL, 0}; /* Look for the tie-breaker node */ if (client->tie_breaker.mode == TLV_TIE_BREAKER_MODE_LOWEST) { tb_node_id = INT_MAX; } else if (client->tie_breaker.mode == TLV_TIE_BREAKER_MODE_HIGHEST) { tb_node_id = 0; } else if (client->tie_breaker.mode == TLV_TIE_BREAKER_MODE_NODE_ID) { tb_node_id = client->tie_breaker.node_id; } else { qnetd_log(LOG_DEBUG, "algo-lms: denied vote because tie-breaker option is invalid: %d", client->tie_breaker.mode); tb_node_id = -1; } /* Find the tie_breaker node */ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) { switch (client->tie_breaker.mode) { case TLV_TIE_BREAKER_MODE_LOWEST: if (other_client->node_id < tb_node_id) { tb_node_id = other_client->node_id; memcpy(&tb_node_ring_id, &other_client->last_ring_id, sizeof(struct tlv_ring_id)); qnetd_log(LOG_DEBUG, "algo-lms: Looking for low node ID. found %d (%d/%ld)", tb_node_id, tb_node_ring_id.node_id, tb_node_ring_id.seq); } break; case TLV_TIE_BREAKER_MODE_HIGHEST: if (other_client->node_id > tb_node_id) { tb_node_id = other_client->node_id; memcpy(&tb_node_ring_id, &other_client->last_ring_id, sizeof(struct tlv_ring_id)); qnetd_log(LOG_DEBUG, "algo-lms: Looking for high node ID. found %d (%d/%ld)", tb_node_id, tb_node_ring_id.node_id, tb_node_ring_id.seq); } break; case TLV_TIE_BREAKER_MODE_NODE_ID: if (client->tie_breaker.node_id == client->node_id) { memcpy(&tb_node_ring_id, &other_client->last_ring_id, sizeof(struct tlv_ring_id)); qnetd_log(LOG_DEBUG, "algo-lms: Looking for nominated node ID. found %d (%d/%ld)", tb_node_id, tb_node_ring_id.node_id, tb_node_ring_id.seq); } break; default: qnetd_log(LOG_DEBUG, "algo-lms: denied vote because tie-breaker option is invalid: %d", client->tie_breaker.mode); memset(&tb_node_ring_id, 0, sizeof(struct tlv_ring_id)); } } if (client->node_id == tb_node_id || tlv_ring_id_eq(&tb_node_ring_id, ring_id)) { qnetd_log(LOG_DEBUG, "algo-lms: We are in the same partition (%d/%ld) as tie-breaker node id %d. ACK", tb_node_ring_id.node_id, tb_node_ring_id.seq, tb_node_id); *result_vote = info->last_result = TLV_VOTE_ACK; } else { qnetd_log(LOG_DEBUG, "algo-lms: We are NOT in the same partition (%d/%ld) as tie-breaker node id %d. NACK", tb_node_ring_id.node_id, tb_node_ring_id.seq, tb_node_id); *result_vote = info->last_result = TLV_VOTE_NACK; } } qnetd_algo_free_partitions(&info->partition_list); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_lms_client_init(struct qnetd_client *client) { struct qnetd_algo_lms_info *info; info = malloc(sizeof(struct qnetd_algo_lms_info)); if (!info) { return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } memset(info, 0, sizeof(*info)); client->algorithm_data = info; info->last_result = 0; /* status unknown, or NEW */ TAILQ_INIT(&info->partition_list); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * We got the config node list. Simply count the number of available nodes * and wait for the quorum list. */ enum tlv_reply_error_code qnetd_algo_lms_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial, enum tlv_vote *result_vote) { struct node_list_entry *node_info; struct qnetd_algo_lms_info *info = client->algorithm_data; int node_count = 0; TAILQ_FOREACH(node_info, nodes, entries) { node_count++; } info->num_config_nodes = node_count; qnetd_log(LOG_DEBUG, "algo-lms: cluster %s config_list has %d nodes", client->cluster_name, node_count); *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * membership node list. This is where we get to work. */ enum tlv_reply_error_code qnetd_algo_lms_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes, enum tlv_vote *result_vote) { qnetd_log(LOG_DEBUG, " "); qnetd_log(LOG_DEBUG, "algo-lms: membership list from node %d partition %d/%ld", client->node_id, ring_id->node_id, ring_id->seq); return do_lms_algorithm(client, ring_id, result_vote); } /* * The quorum node list is received after corosync has decided which nodes are in the cluster. * We run our algorithm again to be sure that things still match. By this time we will (or should) * all know the current ring_id (not guaranteed when the membership list is received). So this * might be the most reliable return. */ enum tlv_reply_error_code qnetd_algo_lms_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes, enum tlv_vote *result_vote) { qnetd_log(LOG_DEBUG, " "); qnetd_log(LOG_DEBUG, "algo-lms: quorum node list from node %d partition %d/%ld", client->node_id, client->last_ring_id.node_id, client->last_ring_id.seq); return do_lms_algorithm(client, &client->last_ring_id, result_vote); } /* * Called after client disconnect. Client structure is still existing (and it's part * of a client->cluster), but it is destroyed (and removed from cluster) right after * this callback finishes. Callback is used mainly for destroing client->algorithm_data. */ void qnetd_algo_lms_client_disconnect(struct qnetd_client *client, int server_going_down) { qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "disconnect", client, client->cluster_name, client->node_id); qnetd_log(LOG_INFO, "algo-lms: server going down %u", server_going_down); free(client->algorithm_data); } /* * Called after client sent ask for vote message. This is usually happening after server * replied TLV_VOTE_WAIT_FOR_REPLY. */ enum tlv_reply_error_code qnetd_algo_lms_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote) { qnetd_log(LOG_DEBUG, " "); qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "asked for a vote", client, client->cluster_name, client->node_id); return do_lms_algorithm(client, &client->last_ring_id, result_vote); } enum tlv_reply_error_code qnetd_algo_lms_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "replied back to vote info message", client, client->cluster_name, client->node_id); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_lms_timer_callback(struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote) { enum tlv_reply_error_code ret; qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") " "Timer callback", client, client->cluster_name, client->node_id); ret = do_lms_algorithm(client, &client->last_ring_id, result_vote); if (ret == TLV_REPLY_ERROR_CODE_NO_ERROR && (*result_vote == TLV_VOTE_ACK || *result_vote == TLV_VOTE_NACK)) { *send_vote = 1; } return ret; } static struct qnetd_algorithm qnetd_algo_lms = { - .init = qnetd_algo_lms_client_init, - .config_node_list_received = qnetd_algo_lms_config_node_list_received, - .membership_node_list_received = qnetd_algo_lms_membership_node_list_received, - .quorum_node_list_received = qnetd_algo_lms_quorum_node_list_received, - .client_disconnect = qnetd_algo_lms_client_disconnect, - .ask_for_vote_received = qnetd_algo_lms_ask_for_vote_received, - .vote_info_reply_received = qnetd_algo_lms_vote_info_reply_received, - .timer_callback = qnetd_algo_lms_timer_callback, + .init = qnetd_algo_lms_client_init, + .config_node_list_received = qnetd_algo_lms_config_node_list_received, + .membership_node_list_received = qnetd_algo_lms_membership_node_list_received, + .quorum_node_list_received = qnetd_algo_lms_quorum_node_list_received, + .client_disconnect = qnetd_algo_lms_client_disconnect, + .ask_for_vote_received = qnetd_algo_lms_ask_for_vote_received, + .vote_info_reply_received = qnetd_algo_lms_vote_info_reply_received, + .timer_callback = qnetd_algo_lms_timer_callback, }; enum tlv_reply_error_code qnetd_algo_lms_register() { return qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_LMS, &qnetd_algo_lms); } diff --git a/qdevices/qnetd-algo-test.c b/qdevices/qnetd-algo-test.c index 5f9f9c75..816d6f66 100644 --- a/qdevices/qnetd-algo-test.c +++ b/qdevices/qnetd-algo-test.c @@ -1,262 +1,263 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qnetd-algo-test.h" #include "qnetd-log.h" #include "qnetd-cluster-list.h" #include "qnetd-client-send.h" #include "qnetd-log-debug.h" #include "qnetd-client-algo-timer.h" #include "utils.h" /* * Called right after client sent init message. This happens after initial accept of client, * tls handshake and sending basic information about cluster/client. * Known information: * - client->cluster_name (client->cluster_name_len) * - client->node_id (client->node_id_set = 1) * - client->decision_algorithm * - client->cluster + * - client->last_ring_id * * Callback is designed mainly for allocating client->algorithm_data. It's also already * part of the cluster, so can access (alloc) client->cluster->algorithm_data. * * client is initialized qnetd_client structure. * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_test_client_init(struct qnetd_client *client) { int *algo_data; qnetd_log(LOG_WARNING, "algo-test: Client %s (cluster = '%s', node_id = " UTILS_PRI_NODE_ID") initiated test algorithm. It's not recommended to use test " "algorithm because it can create multiple quorate partitions!", client->addr_str, client->cluster_name, client->node_id); qnetd_log(LOG_INFO, "algo-test: client_init"); client->algorithm_data = malloc(sizeof(int)); if (client->algorithm_data == NULL) { return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } algo_data = client->algorithm_data; *algo_data = 42; if (qnetd_cluster_size(client->cluster) == 1) { /* * First client in the cluster */ qnetd_log(LOG_INFO, "algo-test: Initializing cluster->algorithm data"); client->cluster->algorithm_data = malloc(sizeof(int)); if (client->cluster->algorithm_data == NULL) { return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } algo_data = client->cluster->algorithm_data; *algo_data = 42; } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent configuration node list * All client fields are already set. Nodes is actual node list, initial is used * for distrinquish between initial node list and changed node list. * msg_seq_num is 32-bit number set by client. If client sent config file version, * config_version_set is set to 1 and config_version contains valid config file version. * * Function has to return result_vote. This can be one of ack/nack, ask_later (client * should ask later for a vote) or wait_for_reply (client should wait for reply). * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_test_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial, enum tlv_vote *result_vote) { qnetd_log(LOG_INFO, "algo-test: node_list_received"); *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent membership node list. * All client fields are already set. Nodes is actual node list. * msg_seq_num is 32-bit number set by client. * ring_id is copied from client votequorum callback. * * Function has to return result_vote. This can be one of ack/nack, ask_later (client * should ask later for a vote) or wait_for_reply (client should wait for reply). * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_test_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes, enum tlv_vote *result_vote) { qnetd_log(LOG_INFO, "algo-test: membership_node_list_received"); *result_vote = TLV_VOTE_ACK; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent quorum node list. * All client fields are already set. Nodes is actual node list. * msg_seq_num is 32-bit number set by client. * quorate is copied from client votequorum callback. * Function is just informative. If client vote is required to change, it's possible * to use qnetd_client_send_vote_info. * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_test_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes, enum tlv_vote *result_vote) { qnetd_log(LOG_INFO, "algo-test: quorum_node_list_received"); *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client disconnect. Client structure is still existing (and it's part * of a client->cluster), but it is destroyed (and removed from cluster) right after * this callback finishes. Callback is used mainly for destroing client->algorithm_data. */ void qnetd_algo_test_client_disconnect(struct qnetd_client *client, int server_going_down) { qnetd_log(LOG_INFO, "algo-test: client_disconnect"); free(client->algorithm_data); if (qnetd_cluster_size(client->cluster) == 1) { /* * Last client in the cluster */ qnetd_log(LOG_INFO, "algo-test: Finalizing cluster->algorithm data"); free(client->cluster->algorithm_data); } } /* * Called after client sent ask for vote message. This is usually happening after server * replied TLV_VOTE_ASK_LATER. */ enum tlv_reply_error_code qnetd_algo_test_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote) { qnetd_log(LOG_INFO, "algo-test: ask_for_vote_received"); *result_vote = TLV_VOTE_ACK; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_test_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { qnetd_log(LOG_INFO, "algo-test: vote_info_reply_received"); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called as a result of qnetd_client_algo_timer_schedule function call after timeout expires. * * If send_vote is set by callback to non zero value, result_vote must also be set and such vote is * send to client. Result_vote is ignored if send_vote = 0 (default). * * If reschedule timer (default value = 0) is set to non zero value, callback is called again later * with same timeout as originaly created. * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_test_timer_callback(struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote) { return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static struct qnetd_algorithm qnetd_algo_test = { .init = qnetd_algo_test_client_init, .config_node_list_received = qnetd_algo_test_config_node_list_received, .membership_node_list_received = qnetd_algo_test_membership_node_list_received, .quorum_node_list_received = qnetd_algo_test_quorum_node_list_received, .client_disconnect = qnetd_algo_test_client_disconnect, .ask_for_vote_received = qnetd_algo_test_ask_for_vote_received, .vote_info_reply_received = qnetd_algo_test_vote_info_reply_received, .timer_callback = qnetd_algo_test_timer_callback, }; enum tlv_reply_error_code qnetd_algo_test_register() { return (qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_TEST, &qnetd_algo_test)); } diff --git a/qdevices/qnetd-algorithm.c b/qdevices/qnetd-algorithm.c index e9908a92..fa8046aa 100644 --- a/qdevices/qnetd-algorithm.c +++ b/qdevices/qnetd-algorithm.c @@ -1,216 +1,216 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "qnet-config.h" #include "qnetd-algorithm.h" #include "qnetd-algo-test.h" #include "qnetd-algo-ffsplit.h" #include "qnetd-algo-2nodelms.h" #include "qnetd-algo-lms.h" #include "qnetd-log.h" static struct qnetd_algorithm *qnetd_algorithm_array[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE]; enum tlv_reply_error_code qnetd_algorithm_client_init(struct qnetd_client *client) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_client_init unhandled decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->init(client)); } enum tlv_reply_error_code qnetd_algorithm_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial, enum tlv_vote *result_vote) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_config_node_list_received unhandled " - "decision algorithm"); + "decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->config_node_list_received( client, msg_seq_num, config_version_set, config_version, nodes, initial, result_vote)); } enum tlv_reply_error_code qnetd_algorithm_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes, enum tlv_vote *result_vote) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_membership_node_list_received unhandled " "decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->membership_node_list_received( client, msg_seq_num, ring_id, nodes, result_vote)); } enum tlv_reply_error_code qnetd_algorithm_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes, enum tlv_vote *result_vote) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "algorithm_quorum_node_list_received unhandled " "decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->quorum_node_list_received( client, msg_seq_num, quorate, nodes, result_vote)); } void qnetd_algorithm_client_disconnect(struct qnetd_client *client, int server_going_down) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_client_disconnect unhandled decision " "algorithm"); return; } qnetd_algorithm_array[client->decision_algorithm]->client_disconnect(client, server_going_down); } enum tlv_reply_error_code qnetd_algorithm_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_ask_for_vote_received unhandled " "decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->ask_for_vote_received( client, msg_seq_num, result_vote)); } enum tlv_reply_error_code qnetd_algorithm_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_vote_info_reply_received unhandled decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->vote_info_reply_received( client, msg_seq_num)); } enum tlv_reply_error_code qnetd_algorithm_timer_callback(struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote) { if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE || qnetd_algorithm_array[client->decision_algorithm] == NULL) { qnetd_log(LOG_CRIT, "qnetd_algorithm_timer_callback unhandled decision algorithm"); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } return (qnetd_algorithm_array[client->decision_algorithm]->timer_callback( client, reschedule_timer, send_vote, result_vote)); } int qnetd_algorithm_register(enum tlv_decision_algorithm_type algorithm_number, struct qnetd_algorithm *algorithm) { if (algorithm_number >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) { qnetd_log(LOG_CRIT, "Failed to register unsupported decision algorithm %u", algorithm_number); return (-1); } if (qnetd_algorithm_array[algorithm_number] != NULL) { qnetd_log(LOG_CRIT, "Failed to register decision algorithm %u, " "it's already registered.", algorithm_number); return (-1); } qnetd_algorithm_array[algorithm_number] = algorithm; return (0); } int qnetd_algorithm_register_all(void) { if (qnetd_algo_test_register() != 0) { qnetd_log(LOG_CRIT, "Failed to register decision algorithm 'test'"); return (-1); } if (qnetd_algo_ffsplit_register() != 0) { qnetd_log(LOG_CRIT, "Failed to register decision algorithm 'ffsplit'"); return (-1); } if (qnetd_algo_2nodelms_register() != 0) { qnetd_log(LOG_CRIT, "Failed to register decision algorithm '2nodelms'"); return (-1); } if (qnetd_algo_lms_register() != 0) { qnetd_log(LOG_CRIT, "Failed to register decision algorithm 'lms'"); return (-1); } return (0); } diff --git a/qdevices/qnetd-client-msg-received.c b/qdevices/qnetd-client-msg-received.c index 547bddd5..ba8e3586 100644 --- a/qdevices/qnetd-client-msg-received.c +++ b/qdevices/qnetd-client-msg-received.c @@ -1,1141 +1,1150 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "qnetd-algorithm.h" #include "qnetd-instance.h" #include "qnetd-log.h" #include "qnetd-log-debug.h" #include "qnetd-client-send.h" #include "msg.h" #include "nss-sock.h" #include "qnetd-client-msg-received.h" /* * 0 - Success * -1 - Disconnect client * -2 - Error reply sent, but no need to disconnect client */ static int qnetd_client_msg_received_check_tls(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { int check_certificate; int tls_required; CERTCertificate *peer_cert; int case_processed; check_certificate = 0; tls_required = 0; case_processed = 0; switch (instance->tls_supported) { case TLV_TLS_UNSUPPORTED: case_processed = 1; tls_required = 0; check_certificate = 0; break; case TLV_TLS_SUPPORTED: case_processed = 1; tls_required = 0; if (client->tls_started && instance->tls_client_cert_required && !client->tls_peer_certificate_verified) { check_certificate = 1; } break; case TLV_TLS_REQUIRED: case_processed = 1; tls_required = 1; if (instance->tls_client_cert_required && !client->tls_peer_certificate_verified) { check_certificate = 1; } break; /* * Default is not defined intentionally. Compiler shows warning when new * tls supported is added */ } if (!case_processed) { qnetd_log(LOG_ERR, "Unhandled instance tls supported %u", instance->tls_supported); exit(1); } if (tls_required && !client->tls_started) { qnetd_log(LOG_ERR, "TLS is required but doesn't started yet. " "Sending back error message"); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_TLS_REQUIRED) != 0) { return (-1); } return (-2); } if (check_certificate) { peer_cert = SSL_PeerCertificate(client->socket); if (peer_cert == NULL) { qnetd_log(LOG_ERR, "Client doesn't sent valid certificate. " "Disconnecting client"); return (-1); } if (CERT_VerifyCertName(peer_cert, client->cluster_name) != SECSuccess) { qnetd_log(LOG_ERR, "Client doesn't sent certificate with valid CN. " "Disconnecting client"); CERT_DestroyCertificate(peer_cert); return (-1); } CERT_DestroyCertificate(peer_cert); client->tls_peer_certificate_verified = 1; } return (0); } static int qnetd_client_msg_received_preinit(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { struct send_buffer_list_entry *send_buffer; if (msg->cluster_name == NULL) { qnetd_log(LOG_ERR, "Received preinit message without cluster name. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } client->cluster_name = malloc(msg->cluster_name_len + 1); if (client->cluster_name == NULL) { qnetd_log(LOG_ERR, "Can't allocate cluster name. Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INTERNAL_ERROR) != 0) { return (-1); } return (0); } memset(client->cluster_name, 0, msg->cluster_name_len + 1); memcpy(client->cluster_name, msg->cluster_name, msg->cluster_name_len); client->cluster_name_len = msg->cluster_name_len; client->preinit_received = 1; send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc preinit reply msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_preinit_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number, instance->tls_supported, instance->tls_client_cert_required) == 0) { qnetd_log(LOG_ERR, "Can't alloc preinit reply msg. " "Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); }; send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } static int qnetd_client_msg_received_unexpected_msg(struct qnetd_client *client, const struct msg_decoded *msg, const char *msg_str) { qnetd_log(LOG_ERR, "Received %s message. Sending back error message", msg_str); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_UNEXPECTED_MESSAGE) != 0) { return (-1); } return (0); } static int qnetd_client_msg_received_preinit_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "preinit reply")); } static int qnetd_client_msg_received_starttls(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { PRFileDesc *new_pr_fd; if (!client->preinit_received) { qnetd_log(LOG_ERR, "Received starttls before preinit message. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_PREINIT_REQUIRED) != 0) { return (-1); } return (0); } if ((new_pr_fd = nss_sock_start_ssl_as_server(client->socket, instance->server.cert, instance->server.private_key, instance->tls_client_cert_required, 0, NULL)) == NULL) { qnetd_log_nss(LOG_ERR, "Can't start TLS. Disconnecting client."); return (-1); } client->tls_started = 1; client->tls_peer_certificate_verified = 0; client->socket = new_pr_fd; return (0); } static int qnetd_client_msg_received_server_error(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "server error")); } /* * Checks if new client send information are valid. It means: * - in cluster is no duplicate node with same nodeid * - it has same tie_breaker as other nodes in cluster * - it has same algorithm as other nodes in cluster */ static enum tlv_reply_error_code qnetd_client_msg_received_init_check_new_client(struct qnetd_instance *instance, struct qnetd_client *new_client) { struct qnetd_cluster *cluster; struct qnetd_client *client; cluster = qnetd_cluster_list_find_by_name(&instance->clusters, new_client->cluster_name, new_client->cluster_name_len); if (cluster == NULL) { return (TLV_REPLY_ERROR_CODE_NO_ERROR); } TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) { if (!tlv_tie_breaker_eq(&new_client->tie_breaker, &client->tie_breaker)) { qnetd_log(LOG_ERR, "Received init message contains tie-breaker which " "differs from rest of cluster. Sending error reply"); return (TLV_REPLY_ERROR_CODE_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES); } if (new_client->decision_algorithm != client->decision_algorithm) { qnetd_log(LOG_ERR, "Received init message contains algorithm which " "differs from rest of cluster. Sending error reply"); return (TLV_REPLY_ERROR_CODE_ALGORITHM_DIFFERS_FROM_OTHER_NODES); } if (new_client->node_id == client->node_id) { qnetd_log(LOG_ERR, "Received init message contains node id which is " "duplicate of other node in cluster. Sending error reply"); return (TLV_REPLY_ERROR_CODE_DUPLICATE_NODE_ID); } } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static int qnetd_client_msg_received_init(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { int res; size_t zi; enum msg_type *supported_msgs; size_t no_supported_msgs; enum tlv_opt_type *supported_opts; size_t no_supported_opts; struct send_buffer_list_entry *send_buffer; enum tlv_reply_error_code reply_error_code; struct qnetd_cluster *cluster; supported_msgs = NULL; supported_opts = NULL; no_supported_msgs = 0; no_supported_opts = 0; reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR; if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) { return (res == -1 ? -1 : 0); } if (!client->preinit_received) { qnetd_log(LOG_ERR, "Received init before preinit message. Sending error reply."); reply_error_code = TLV_REPLY_ERROR_CODE_PREINIT_REQUIRED; } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->node_id_set) { qnetd_log(LOG_ERR, "Received init message without node id set. " "Sending error reply."); reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION; } else { client->node_id_set = 1; client->node_id = msg->node_id; } + if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->ring_id_set) { + qnetd_log(LOG_ERR, "Received init message without ring id set. " + "Sending error reply."); + + reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION; + } else { + memcpy(&client->last_ring_id, &msg->ring_id, sizeof(struct tlv_ring_id)); + } + if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->heartbeat_interval_set) { qnetd_log(LOG_ERR, "Received init message without heartbeat interval set. " "Sending error reply."); reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION; } else { if (msg->heartbeat_interval < instance->advanced_settings->heartbeat_interval_min || msg->heartbeat_interval > instance->advanced_settings->heartbeat_interval_max) { qnetd_log(LOG_ERR, "Client requested invalid heartbeat interval %u. " "Sending error reply.", msg->heartbeat_interval); reply_error_code = TLV_REPLY_ERROR_CODE_INVALID_HEARTBEAT_INTERVAL; } else { client->heartbeat_interval = msg->heartbeat_interval; } } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->tie_breaker_set) { qnetd_log(LOG_ERR, "Received init message without tie-breaker set. " "Sending error reply."); reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION; } else { memcpy(&client->tie_breaker, &msg->tie_breaker, sizeof(msg->tie_breaker)); } if (msg->supported_messages != NULL) { /* * Client sent supported messages. For now this is ignored but in the future * this may be used to ensure backward compatibility. */ /* for (i = 0; i < msg->no_supported_messages; i++) { qnetd_log(LOG_DEBUG, "Client supports %u message", (int)msg->supported_messages[i]); } */ /* * Sent back supported messages */ msg_get_supported_messages(&supported_msgs, &no_supported_msgs); } if (msg->supported_options != NULL) { /* * Client sent supported options. For now this is ignored but in the future * this may be used to ensure backward compatibility. */ /* for (i = 0; i < msg->no_supported_options; i++) { qnetd_log(LOG_DEBUG, "Client supports %u option", (int)msg->supported_messages[i]); } */ /* * Send back supported options */ tlv_get_supported_options(&supported_opts, &no_supported_opts); } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->decision_algorithm_set) { qnetd_log(LOG_ERR, "Received init message without decision algorithm. " "Sending error reply."); reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION; } else { /* * Check if decision algorithm requested by client is supported */ res = 0; for (zi = 0; zi < QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE && !res; zi++) { if (qnetd_static_supported_decision_algorithms[zi] == msg->decision_algorithm) { res = 1; } } if (!res) { qnetd_log(LOG_ERR, "Client requested unsupported decision algorithm %u. " "Sending error reply.", msg->decision_algorithm); reply_error_code = TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM; } client->decision_algorithm = msg->decision_algorithm; } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) { reply_error_code = qnetd_client_msg_received_init_check_new_client(instance, client); } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) { cluster = qnetd_cluster_list_add_client(&instance->clusters, client); if (cluster == NULL) { qnetd_log(LOG_ERR, "Can't add client to cluster list. " "Sending error reply."); reply_error_code = TLV_REPLY_ERROR_CODE_INTERNAL_ERROR; } else { client->cluster = cluster; client->cluster_list = &instance->clusters; } } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) { qnetd_log_debug_new_client_connected(client); reply_error_code = qnetd_algorithm_client_init(client); } if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) { /* * Correct init received */ client->init_received = 1; } else { qnetd_log(LOG_ERR, "Algorithm returned error code. Sending error reply."); } send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc init reply msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_init_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number, reply_error_code, supported_msgs, no_supported_msgs, supported_opts, no_supported_opts, instance->advanced_settings->max_client_receive_size, instance->advanced_settings->max_client_send_size, qnetd_static_supported_decision_algorithms, QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) == -1) { qnetd_log(LOG_ERR, "Can't alloc init reply msg. Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } static int qnetd_client_msg_received_init_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "init reply")); } static int qnetd_client_msg_received_set_option_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "set option reply")); } static int qnetd_client_msg_received_set_option(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { int res; struct send_buffer_list_entry *send_buffer; if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) { return (res == -1 ? -1 : 0); } if (!client->init_received) { qnetd_log(LOG_ERR, "Received set option message before init message. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) { return (-1); } return (0); } if (msg->heartbeat_interval_set) { /* * Check if heartbeat interval is valid */ if (msg->heartbeat_interval < instance->advanced_settings->heartbeat_interval_min || msg->heartbeat_interval > instance->advanced_settings->heartbeat_interval_max) { qnetd_log(LOG_ERR, "Client requested invalid heartbeat interval %u. " "Sending error reply.", msg->heartbeat_interval); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INVALID_HEARTBEAT_INTERVAL) != 0) { return (-1); } return (0); } client->heartbeat_interval = msg->heartbeat_interval; } send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc set option reply msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_set_option_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number, client->heartbeat_interval) == -1) { qnetd_log(LOG_ERR, "Can't alloc set option reply msg. " "Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } static int qnetd_client_msg_received_echo_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "echo reply")); } static int qnetd_client_msg_received_echo_request(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg, const struct dynar *msg_orig) { int res; struct send_buffer_list_entry *send_buffer; if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) { return (res == -1 ? -1 : 0); } if (!client->init_received) { qnetd_log(LOG_ERR, "Received echo request before init message. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) { return (-1); } return (0); } send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc echo reply msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_echo_reply(&send_buffer->buffer, msg_orig) == -1) { qnetd_log(LOG_ERR, "Can't alloc echo reply msg. Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } static int qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { int res; struct send_buffer_list_entry *send_buffer; enum tlv_reply_error_code reply_error_code; enum tlv_vote result_vote; int case_processed; reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR; if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) { return (res == -1 ? -1 : 0); } if (!client->init_received) { qnetd_log(LOG_ERR, "Received node list message before init message. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) { return (-1); } return (0); } if (!msg->node_list_type_set) { qnetd_log(LOG_ERR, "Received node list message without node list type set. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } if (!msg->seq_number_set) { qnetd_log(LOG_ERR, "Received node list message without seq number set. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } result_vote = TLV_VOTE_NO_CHANGE; case_processed = 0; switch (msg->node_list_type) { case TLV_NODE_LIST_TYPE_INITIAL_CONFIG: case TLV_NODE_LIST_TYPE_CHANGED_CONFIG: case_processed = 1; qnetd_log_debug_config_node_list_received(client, msg->seq_number, msg->config_version_set, msg->config_version, &msg->nodes, (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG)); reply_error_code = qnetd_algorithm_config_node_list_received(client, msg->seq_number, msg->config_version_set, msg->config_version, &msg->nodes, (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG), &result_vote); break; case TLV_NODE_LIST_TYPE_MEMBERSHIP: case_processed = 1; if (!msg->ring_id_set) { qnetd_log(LOG_ERR, "Received node list message without ring id number set. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } qnetd_log_debug_membership_node_list_received(client, msg->seq_number, &msg->ring_id, &msg->nodes); reply_error_code = qnetd_algorithm_membership_node_list_received(client, msg->seq_number, &msg->ring_id, &msg->nodes, &result_vote); break; case TLV_NODE_LIST_TYPE_QUORUM: case_processed = 1; if (!msg->quorate_set) { qnetd_log(LOG_ERR, "Received quorum list message without quorate set. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } qnetd_log_debug_quorum_node_list_received(client, msg->seq_number,msg->quorate, &msg->nodes); reply_error_code = qnetd_algorithm_quorum_node_list_received(client, msg->seq_number,msg->quorate, &msg->nodes, &result_vote); break; /* * Default is not defined intentionally. Compiler shows warning when new * node list type is added */ } if (!case_processed) { qnetd_log(LOG_ERR, "qnetd_client_msg_received_node_list fatal error. " "Unhandled node_list_type"); exit(1); } if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) { qnetd_log(LOG_ERR, "Algorithm returned error code. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, reply_error_code) != 0) { return (-1); } return (0); } else { qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote)); } if (msg->node_list_type == TLV_NODE_LIST_TYPE_MEMBERSHIP && result_vote == TLV_VOTE_NO_CHANGE) { qnetd_log(LOG_ERR, "qnetd_client_msg_received_node_list fatal error. " "node_list_type is membership and algorithm result vote is no_change"); exit(1); } /* * Store node list for future use */ case_processed = 0; switch (msg->node_list_type) { case TLV_NODE_LIST_TYPE_INITIAL_CONFIG: case TLV_NODE_LIST_TYPE_CHANGED_CONFIG: case_processed = 1; node_list_free(&client->configuration_node_list); if (node_list_clone(&client->configuration_node_list, &msg->nodes) == -1) { qnetd_log(LOG_ERR, "Can't alloc config node list clone. " "Disconnecting client connection."); return (-1); } client->config_version_set = msg->config_version_set; client->config_version = msg->config_version; break; case TLV_NODE_LIST_TYPE_MEMBERSHIP: case_processed = 1; node_list_free(&client->last_membership_node_list); if (node_list_clone(&client->last_membership_node_list, &msg->nodes) == -1) { qnetd_log(LOG_ERR, "Can't alloc membership node list clone. " "Disconnecting client connection."); return (-1); } memcpy(&client->last_ring_id, &msg->ring_id, sizeof(struct tlv_ring_id)); break; case TLV_NODE_LIST_TYPE_QUORUM: case_processed = 1; node_list_free(&client->last_quorum_node_list); if (node_list_clone(&client->last_quorum_node_list, &msg->nodes) == -1) { qnetd_log(LOG_ERR, "Can't alloc quorum node list clone. " "Disconnecting client connection."); return (-1); } break; /* * Default is not defined intentionally. Compiler shows warning when new * node list type is added */ } if (!case_processed) { qnetd_log(LOG_ERR, "qnetd_client_msg_received_node_list fatal error. " "Unhandled node_list_type"); exit(1); } /* * Store result vote */ client->last_sent_vote = result_vote; if (result_vote == TLV_VOTE_ACK || result_vote == TLV_VOTE_NACK) { client->last_sent_ack_nack_vote = result_vote; } send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc node list reply msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_node_list_reply(&send_buffer->buffer, msg->seq_number, msg->node_list_type, - msg->ring_id_set, &msg->ring_id, result_vote) == -1) { + &client->last_ring_id, result_vote) == -1) { qnetd_log(LOG_ERR, "Can't alloc node list reply msg. " "Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } static int qnetd_client_msg_received_node_list_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "node list reply")); } static int qnetd_client_msg_received_ask_for_vote(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { int res; struct send_buffer_list_entry *send_buffer; enum tlv_reply_error_code reply_error_code; enum tlv_vote result_vote; reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR; if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) { return (res == -1 ? -1 : 0); } if (!client->init_received) { qnetd_log(LOG_ERR, "Received ask for vote message before init message. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) { return (-1); } return (0); } if (!msg->seq_number_set) { qnetd_log(LOG_ERR, "Received ask for vote message without seq number set. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } qnetd_log_debug_ask_for_vote_received(client, msg->seq_number); reply_error_code = qnetd_algorithm_ask_for_vote_received(client, msg->seq_number, &result_vote); if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) { qnetd_log(LOG_ERR, "Algorithm returned error code. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, reply_error_code) != 0) { return (-1); } return (0); } else { qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote)); } /* * Store result vote */ client->last_sent_vote = result_vote; if (result_vote == TLV_VOTE_ACK || result_vote == TLV_VOTE_NACK) { client->last_sent_ack_nack_vote = result_vote; } send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc ask for vote reply msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_ask_for_vote_reply(&send_buffer->buffer, msg->seq_number, - result_vote) == -1) { + &client->last_ring_id, result_vote) == -1) { qnetd_log(LOG_ERR, "Can't alloc ask for vote reply msg. " "Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); } send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } static int qnetd_client_msg_received_ask_for_vote_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "ask for vote reply")); } static int qnetd_client_msg_received_vote_info(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { return (qnetd_client_msg_received_unexpected_msg(client, msg, "vote info")); } static int qnetd_client_msg_received_vote_info_reply(struct qnetd_instance *instance, struct qnetd_client *client, const struct msg_decoded *msg) { int res; enum tlv_reply_error_code reply_error_code; reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR; if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) { return (res == -1 ? -1 : 0); } if (!client->init_received) { qnetd_log(LOG_ERR, "Received vote info reply before init message. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) { return (-1); } return (0); } if (!msg->seq_number_set) { qnetd_log(LOG_ERR, "Received vote info reply message without seq number set. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) { return (-1); } return (0); } qnetd_log_debug_vote_info_reply_received(client, msg->seq_number); reply_error_code = qnetd_algorithm_vote_info_reply_received(client, msg->seq_number); if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) { qnetd_log(LOG_ERR, "Algorithm returned error code. " "Sending error reply."); if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number, reply_error_code) != 0) { return (-1); } return (0); } return (0); } int qnetd_client_msg_received(struct qnetd_instance *instance, struct qnetd_client *client) { struct msg_decoded msg; int res; int ret_val; int msg_processed; client->dpd_msg_received_since_last_check = 1; msg_decoded_init(&msg); res = msg_decode(&client->receive_buffer, &msg); if (res != 0) { /* * Error occurred. Send server error. */ qnetd_log_msg_decode_error(res); qnetd_log(LOG_INFO, "Sending back error message"); if (qnetd_client_send_err(client, msg.seq_number_set, msg.seq_number, TLV_REPLY_ERROR_CODE_ERROR_DECODING_MSG) != 0) { return (-1); } return (0); } ret_val = 0; msg_processed = 0; switch (msg.type) { case MSG_TYPE_PREINIT: msg_processed = 1; ret_val = qnetd_client_msg_received_preinit(instance, client, &msg); break; case MSG_TYPE_PREINIT_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_preinit_reply(instance, client, &msg); break; case MSG_TYPE_STARTTLS: msg_processed = 1; ret_val = qnetd_client_msg_received_starttls(instance, client, &msg); break; case MSG_TYPE_INIT: msg_processed = 1; ret_val = qnetd_client_msg_received_init(instance, client, &msg); break; case MSG_TYPE_INIT_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_init_reply(instance, client, &msg); break; case MSG_TYPE_SERVER_ERROR: msg_processed = 1; ret_val = qnetd_client_msg_received_server_error(instance, client, &msg); break; case MSG_TYPE_SET_OPTION: msg_processed = 1; ret_val = qnetd_client_msg_received_set_option(instance, client, &msg); break; case MSG_TYPE_SET_OPTION_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_set_option_reply(instance, client, &msg); break; case MSG_TYPE_ECHO_REQUEST: msg_processed = 1; ret_val = qnetd_client_msg_received_echo_request(instance, client, &msg, &client->receive_buffer); break; case MSG_TYPE_ECHO_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_echo_reply(instance, client, &msg); break; case MSG_TYPE_NODE_LIST: msg_processed = 1; ret_val = qnetd_client_msg_received_node_list(instance, client, &msg); break; case MSG_TYPE_NODE_LIST_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_node_list_reply(instance, client, &msg); break; case MSG_TYPE_ASK_FOR_VOTE: msg_processed = 1; ret_val = qnetd_client_msg_received_ask_for_vote(instance, client, &msg); break; case MSG_TYPE_ASK_FOR_VOTE_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_ask_for_vote_reply(instance, client, &msg); break; case MSG_TYPE_VOTE_INFO: msg_processed = 1; ret_val = qnetd_client_msg_received_vote_info(instance, client, &msg); break; case MSG_TYPE_VOTE_INFO_REPLY: msg_processed = 1; ret_val = qnetd_client_msg_received_vote_info_reply(instance, client, &msg); break; /* * Default is not defined intentionally. Compiler shows warning when new * msg type is added. */ } if (!msg_processed) { qnetd_log(LOG_ERR, "Unsupported message %u received from client. " "Sending back error message", msg.type); if (qnetd_client_send_err(client, msg.seq_number_set, msg.seq_number, TLV_REPLY_ERROR_CODE_UNSUPPORTED_MESSAGE) != 0) { ret_val = -1; } } msg_decoded_destroy(&msg); return (ret_val); } diff --git a/qdevices/qnetd-client-net.c b/qdevices/qnetd-client-net.c index 7a8ebc7e..93ee50ea 100644 --- a/qdevices/qnetd-client-net.c +++ b/qdevices/qnetd-client-net.c @@ -1,248 +1,248 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "msgio.h" #include "msg.h" #include "nss-sock.h" #include "qnetd-log.h" #include "qnetd-client-net.h" #include "qnetd-client-send.h" #include "qnetd-client-msg-received.h" #define CLIENT_ADDR_STR_LEN_COLON_PORT (1 + 5 + 1) #define CLIENT_ADDR_STR_LEN (INET6_ADDRSTRLEN + CLIENT_ADDR_STR_LEN_COLON_PORT) static int qnetd_client_net_write_finished(struct qnetd_instance *instance, struct qnetd_client *client) { /* * Callback is currently unused */ return (0); } int qnetd_client_net_write(struct qnetd_instance *instance, struct qnetd_client *client) { int res; struct send_buffer_list_entry *send_buffer; send_buffer = send_buffer_list_get_active(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log_nss(LOG_CRIT, "send_buffer_list_get_active returned NULL"); return (-1); } res = msgio_write(client->socket, &send_buffer->buffer, &send_buffer->msg_already_sent_bytes); if (res == 1) { send_buffer_list_delete(&client->send_buffer_list, send_buffer); if (qnetd_client_net_write_finished(instance, client) == -1) { return (-1); } } if (res == -1) { qnetd_log_nss(LOG_CRIT, "PR_Send returned 0"); return (-1); } if (res == -2) { qnetd_log_nss(LOG_ERR, "Unhandled error when sending message to client"); return (-1); } return (0); } /* * -1 means end of connection (EOF) or some other unhandled error. 0 = success */ int qnetd_client_net_read(struct qnetd_instance *instance, struct qnetd_client *client) { int res; int ret_val; int orig_skipping_msg; orig_skipping_msg = client->skipping_msg; res = msgio_read(client->socket, &client->receive_buffer, &client->msg_already_received_bytes, &client->skipping_msg); if (!orig_skipping_msg && client->skipping_msg) { qnetd_log(LOG_DEBUG, "msgio_read set skipping_msg"); } ret_val = 0; switch (res) { case 0: /* * Partial read */ break; case -1: qnetd_log(LOG_DEBUG, "Client closed connection"); ret_val = -1; break; case -2: qnetd_log_nss(LOG_ERR, "Unhandled error when reading from client. " "Disconnecting client"); ret_val = -1; break; case -3: qnetd_log(LOG_ERR, "Can't store message header from client. Disconnecting client"); ret_val = -1; break; case -4: qnetd_log(LOG_ERR, "Can't store message from client. Skipping message"); client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_ERROR_DECODING_MSG; break; case -5: qnetd_log(LOG_WARNING, "Client sent unsupported msg type %u. Skipping message", msg_get_type(&client->receive_buffer)); client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_UNSUPPORTED_MESSAGE; break; case -6: qnetd_log(LOG_WARNING, "Client wants to send too long message %u bytes. Skipping message", msg_get_len(&client->receive_buffer)); client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_MESSAGE_TOO_LONG; break; case 1: /* * Full message received / skipped */ if (!client->skipping_msg) { if (qnetd_client_msg_received(instance, client) == -1) { ret_val = -1; } } else { if (qnetd_client_send_err(client, 0, 0, client->skipping_msg_reason) != 0) { ret_val = -1; } } client->skipping_msg = 0; client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_NO_ERROR; client->msg_already_received_bytes = 0; dynar_clean(&client->receive_buffer); break; default: qnetd_log(LOG_ERR, "Unhandled msgio_read error %d\n", res); exit(1); break; } return (ret_val); } int qnetd_client_net_accept(struct qnetd_instance *instance) { PRNetAddr client_addr; PRFileDesc *client_socket; struct qnetd_client *client; char *client_addr_str; int res_err; client_addr_str = NULL; res_err = -1; - if ((client_socket = PR_Accept(instance->server.socket, &client_addr, + if ((client_socket = PR_Accept(instance->server.socket, &client_addr, PR_INTERVAL_NO_TIMEOUT)) == NULL) { qnetd_log_nss(LOG_ERR, "Can't accept connection"); return (-1); } if (nss_sock_set_non_blocking(client_socket) != 0) { qnetd_log_nss(LOG_ERR, "Can't set client socket to non blocking mode"); goto exit_close; } if (instance->max_clients != 0 && qnetd_client_list_no_clients(&instance->clients) >= instance->max_clients) { qnetd_log(LOG_ERR, "Maximum clients reached. Not accepting connection"); goto exit_close; } client_addr_str = malloc(CLIENT_ADDR_STR_LEN); if (client_addr_str == NULL) { qnetd_log(LOG_ERR, "Can't alloc client addr str memory. Not accepting connection"); goto exit_close; } if (PR_NetAddrToString(&client_addr, client_addr_str, CLIENT_ADDR_STR_LEN) != PR_SUCCESS) { qnetd_log_nss(LOG_ERR, "Can't convert client address to string. Not accepting connection"); goto exit_close; } if (snprintf(client_addr_str + strlen(client_addr_str), CLIENT_ADDR_STR_LEN_COLON_PORT, ":%"PRIu16, ntohs(client_addr.ipv6.port)) >= CLIENT_ADDR_STR_LEN_COLON_PORT) { qnetd_log(LOG_ERR, "Can't store port to client addr str. Not accepting connection"); goto exit_close; } client = qnetd_client_list_add(&instance->clients, client_socket, &client_addr, client_addr_str, instance->advanced_settings->max_client_receive_size, instance->advanced_settings->max_client_send_buffers, instance->advanced_settings->max_client_send_size, &instance->main_timer_list); if (client == NULL) { qnetd_log(LOG_ERR, "Can't add client to list"); res_err = -2; goto exit_close; } return (0); exit_close: free(client_addr_str); PR_Close(client_socket); return (res_err); } diff --git a/qdevices/qnetd-client-send.c b/qdevices/qnetd-client-send.c index 059a5a3f..4c48717e 100644 --- a/qdevices/qnetd-client-send.c +++ b/qdevices/qnetd-client-send.c @@ -1,107 +1,108 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qnetd-client-send.h" #include "qnetd-log.h" #include "qnetd-log-debug.h" #include "msg.h" int qnetd_client_send_err(struct qnetd_client *client, int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_reply_error_code reply) { struct send_buffer_list_entry *send_buffer; send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc server error msg from list. " "Disconnecting client connection."); return (-1); } if (msg_create_server_error(&send_buffer->buffer, add_msg_seq_number, msg_seq_number, reply) == 0) { qnetd_log(LOG_ERR, "Can't alloc server error msg. " "Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); }; send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } int qnetd_client_send_vote_info(struct qnetd_client *client, uint32_t msg_seq_number, enum tlv_vote vote) { struct send_buffer_list_entry *send_buffer; - /* + /* * Store result vote */ client->last_sent_vote = vote; if (vote == TLV_VOTE_ACK || vote == TLV_VOTE_NACK) { client->last_sent_ack_nack_vote = vote; } qnetd_log_debug_send_vote_info(client, msg_seq_number, vote); send_buffer = send_buffer_list_get_new(&client->send_buffer_list); if (send_buffer == NULL) { qnetd_log(LOG_ERR, "Can't alloc vote info msg from list. " "Disconnecting client connection."); return (-1); } - if (msg_create_vote_info(&send_buffer->buffer, msg_seq_number, vote) == 0) { + if (msg_create_vote_info(&send_buffer->buffer, msg_seq_number, + &client->last_ring_id, vote) == 0) { qnetd_log(LOG_ERR, "Can't alloc vote info msg. " "Disconnecting client connection."); send_buffer_list_discard_new(&client->send_buffer_list, send_buffer); return (-1); }; send_buffer_list_put(&client->send_buffer_list, send_buffer); return (0); } diff --git a/qdevices/qnetd-cluster-list.c b/qdevices/qnetd-cluster-list.c index 0849d84b..8739bb33 100644 --- a/qdevices/qnetd-cluster-list.c +++ b/qdevices/qnetd-cluster-list.c @@ -1,139 +1,139 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "qnetd-cluster-list.h" void qnetd_cluster_list_init(struct qnetd_cluster_list *list) { TAILQ_INIT(list); } struct qnetd_cluster * qnetd_cluster_list_find_by_name(struct qnetd_cluster_list *list, const char *cluster_name, size_t cluster_name_len) { struct qnetd_cluster *cluster; TAILQ_FOREACH(cluster, list, entries) { if (cluster->cluster_name_len == cluster_name_len && memcmp(cluster->cluster_name, cluster_name, cluster_name_len) == 0) { return (cluster); } } return (NULL); } struct qnetd_cluster * qnetd_cluster_list_add_client(struct qnetd_cluster_list *list, struct qnetd_client *client) { struct qnetd_cluster *cluster; cluster = qnetd_cluster_list_find_by_name(list, client->cluster_name, client->cluster_name_len); if (cluster == NULL) { cluster = (struct qnetd_cluster *)malloc(sizeof(*cluster)); if (cluster == NULL) { return (NULL); } if (qnetd_cluster_init(cluster, client->cluster_name, client->cluster_name_len) != 0) { free(cluster); return (NULL); } TAILQ_INSERT_TAIL(list, cluster, entries); } TAILQ_INSERT_TAIL(&cluster->client_list, client, cluster_entries); return (cluster); } void qnetd_cluster_list_del_client(struct qnetd_cluster_list *list, struct qnetd_cluster *cluster, struct qnetd_client *client) { TAILQ_REMOVE(&cluster->client_list, client, cluster_entries); if (TAILQ_EMPTY(&cluster->client_list)) { TAILQ_REMOVE(list, cluster, entries); qnetd_cluster_destroy(cluster); free(cluster); } } void qnetd_cluster_list_free(struct qnetd_cluster_list *list) { struct qnetd_cluster *cluster; struct qnetd_cluster *cluster_next; cluster = TAILQ_FIRST(list); while (cluster != NULL) { cluster_next = TAILQ_NEXT(cluster, entries); qnetd_cluster_destroy(cluster); free(cluster); cluster = cluster_next; - } + } TAILQ_INIT(list); } size_t qnetd_cluster_list_size(const struct qnetd_cluster_list *list) { size_t res; struct qnetd_cluster *cluster; res = 0; TAILQ_FOREACH(cluster, list, entries) { res++; } return (res); } diff --git a/qdevices/qnetd-log-debug.c b/qdevices/qnetd-log-debug.c index c8e512e7..b217e461 100644 --- a/qdevices/qnetd-log-debug.c +++ b/qdevices/qnetd-log-debug.c @@ -1,166 +1,168 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "qnetd-log.h" #include "qnetd-log-debug.h" #include "utils.h" void qnetd_log_debug_dump_cluster(struct qnetd_cluster *cluster) { struct qnetd_client *client; qnetd_log(LOG_DEBUG, " cluster dump:"); TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) { qnetd_log(LOG_DEBUG, " client = %s, node_id = "UTILS_PRI_NODE_ID, client->addr_str, client->node_id); } } void qnetd_log_debug_new_client_connected(struct qnetd_client *client) { qnetd_log(LOG_DEBUG, "New client connected"); qnetd_log(LOG_DEBUG, " cluster name = %s", client->cluster_name); qnetd_log(LOG_DEBUG, " tls started = %u", client->tls_started); qnetd_log(LOG_DEBUG, " tls peer certificate verified = %u", client->tls_peer_certificate_verified); qnetd_log(LOG_DEBUG, " node_id = "UTILS_PRI_NODE_ID, client->node_id); qnetd_log(LOG_DEBUG, " pointer = %p", client); qnetd_log(LOG_DEBUG, " addr_str = %s", client->addr_str); + qnetd_log(LOG_DEBUG, " ring id = (" UTILS_PRI_RING_ID ")", client->last_ring_id.node_id, + client->last_ring_id.seq); qnetd_log_debug_dump_cluster(client->cluster); } void qnetd_log_debug_dump_node_list(struct qnetd_client *client, const struct node_list *nodes) { struct node_list_entry *node_info; qnetd_log(LOG_DEBUG, " node list:"); TAILQ_FOREACH(node_info, nodes, entries) { qnetd_log(LOG_DEBUG, " node_id = "UTILS_PRI_NODE_ID", " "data_center_id = "UTILS_PRI_DATACENTER_ID", " "node_state = %s", node_info->node_id, node_info->data_center_id, tlv_node_state_to_str(node_info->node_state)); } } void qnetd_log_debug_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial) { qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") " "sent %s node list.", client->addr_str, client->cluster_name, client->node_id, (initial ? "initial" : "changed")); qnetd_log(LOG_DEBUG, " msg seq num "UTILS_PRI_MSG_SEQ, msg_seq_num); if (config_version_set) { qnetd_log(LOG_DEBUG, " config version " UTILS_PRI_CONFIG_VERSION, config_version); } qnetd_log_debug_dump_node_list(client, nodes); } void qnetd_log_debug_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes) { qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") " "sent membership node list.", client->addr_str, client->cluster_name, client->node_id); qnetd_log(LOG_DEBUG, " msg seq num "UTILS_PRI_MSG_SEQ, msg_seq_num); qnetd_log(LOG_DEBUG, " ring id = (" UTILS_PRI_RING_ID ")", ring_id->node_id, ring_id->seq); qnetd_log_debug_dump_node_list(client, nodes); } void qnetd_log_debug_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes) { qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") " "sent quorum node list.", client->addr_str, client->cluster_name, client->node_id); qnetd_log(LOG_DEBUG, " msg seq num "UTILS_PRI_MSG_SEQ, msg_seq_num); qnetd_log(LOG_DEBUG, " quorate = %u", quorate); qnetd_log_debug_dump_node_list(client, nodes); } void qnetd_log_debug_client_disconnect(struct qnetd_client *client, int server_going_down) { qnetd_log(LOG_DEBUG, "Client %s (init_received %u, cluster %s, node_id " UTILS_PRI_NODE_ID") disconnect%s", client->addr_str, client->init_received, client->cluster_name, client->node_id, (server_going_down ? " (server is going down)" : "")); } void qnetd_log_debug_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num) { qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") " "asked for a vote", client->addr_str, client->cluster_name, client->node_id); qnetd_log(LOG_DEBUG, " msg seq num "UTILS_PRI_MSG_SEQ, msg_seq_num); } void qnetd_log_debug_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") " "replied back to vote info message", client->addr_str, client->cluster_name, client->node_id); qnetd_log(LOG_DEBUG, " msg seq num "UTILS_PRI_MSG_SEQ, msg_seq_num); } void qnetd_log_debug_send_vote_info(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote vote) { qnetd_log(LOG_DEBUG, "Sending vote info to client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") ", client->addr_str, client->cluster_name, client->node_id); qnetd_log(LOG_DEBUG, " msg seq num "UTILS_PRI_MSG_SEQ, msg_seq_num); qnetd_log(LOG_DEBUG, " vote %s", tlv_vote_to_str(vote)); } diff --git a/qdevices/tlv.c b/qdevices/tlv.c index 143876b2..a7eae953 100644 --- a/qdevices/tlv.c +++ b/qdevices/tlv.c @@ -1,1038 +1,1038 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include /* * 64-bit variant of ntoh is not exactly standard... */ #if defined(__linux__) #include #elif defined(__FreeBSD__) || defined(__NetBSD__) #include #elif defined(__OpenBSD__) #include #define be64toh(x) betoh64(x) #endif #include "tlv.h" #define TLV_TYPE_LENGTH 2 #define TLV_LENGTH_LENGTH 2 -#define TLV_STATIC_SUPPORTED_OPTIONS_SIZE 22 +#define TLV_STATIC_SUPPORTED_OPTIONS_SIZE 22 enum tlv_opt_type tlv_static_supported_options[TLV_STATIC_SUPPORTED_OPTIONS_SIZE] = { TLV_OPT_MSG_SEQ_NUMBER, TLV_OPT_CLUSTER_NAME, TLV_OPT_TLS_SUPPORTED, TLV_OPT_TLS_CLIENT_CERT_REQUIRED, TLV_OPT_SUPPORTED_MESSAGES, TLV_OPT_SUPPORTED_OPTIONS, TLV_OPT_REPLY_ERROR_CODE, TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE, TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE, TLV_OPT_NODE_ID, TLV_OPT_SUPPORTED_DECISION_ALGORITHMS, TLV_OPT_DECISION_ALGORITHM, TLV_OPT_HEARTBEAT_INTERVAL, TLV_OPT_RING_ID, TLV_OPT_CONFIG_VERSION, TLV_OPT_DATA_CENTER_ID, TLV_OPT_NODE_STATE, TLV_OPT_NODE_INFO, TLV_OPT_NODE_LIST_TYPE, TLV_OPT_VOTE, TLV_OPT_QUORATE, TLV_OPT_TIE_BREAKER, }; int tlv_add(struct dynar *msg, enum tlv_opt_type opt_type, uint16_t opt_len, const void *value) { uint16_t nlen; uint16_t nopt_type; if (dynar_size(msg) + sizeof(nopt_type) + sizeof(nlen) + opt_len > dynar_max_size(msg)) { return (-1); } nopt_type = htons((uint16_t)opt_type); nlen = htons(opt_len); dynar_cat(msg, &nopt_type, sizeof(nopt_type)); dynar_cat(msg, &nlen, sizeof(nlen)); dynar_cat(msg, value, opt_len); return (0); } int tlv_add_u32(struct dynar *msg, enum tlv_opt_type opt_type, uint32_t u32) { uint32_t nu32; nu32 = htonl(u32); return (tlv_add(msg, opt_type, sizeof(nu32), &nu32)); } int tlv_add_u8(struct dynar *msg, enum tlv_opt_type opt_type, uint8_t u8) { return (tlv_add(msg, opt_type, sizeof(u8), &u8)); } int tlv_add_u16(struct dynar *msg, enum tlv_opt_type opt_type, uint16_t u16) { uint16_t nu16; nu16 = htons(u16); return (tlv_add(msg, opt_type, sizeof(nu16), &nu16)); } int tlv_add_u64(struct dynar *msg, enum tlv_opt_type opt_type, uint64_t u64) { uint64_t nu64; nu64 = htobe64(u64); return (tlv_add(msg, opt_type, sizeof(nu64), &nu64)); } int tlv_add_string(struct dynar *msg, enum tlv_opt_type opt_type, const char *str) { return (tlv_add(msg, opt_type, strlen(str), str)); } int tlv_add_msg_seq_number(struct dynar *msg, uint32_t msg_seq_number) { return (tlv_add_u32(msg, TLV_OPT_MSG_SEQ_NUMBER, msg_seq_number)); } int tlv_add_cluster_name(struct dynar *msg, const char *cluster_name) { return (tlv_add_string(msg, TLV_OPT_CLUSTER_NAME, cluster_name)); } int tlv_add_tls_supported(struct dynar *msg, enum tlv_tls_supported tls_supported) { return (tlv_add_u8(msg, TLV_OPT_TLS_SUPPORTED, tls_supported)); } int tlv_add_tls_client_cert_required(struct dynar *msg, int tls_client_cert_required) { return (tlv_add_u8(msg, TLV_OPT_TLS_CLIENT_CERT_REQUIRED, tls_client_cert_required)); } int tlv_add_u16_array(struct dynar *msg, enum tlv_opt_type opt_type, const uint16_t *array, size_t array_size) { size_t i; uint16_t *nu16a; uint16_t opt_len; int res; nu16a = malloc(sizeof(uint16_t) * array_size); if (nu16a == NULL) { return (-1); } for (i = 0; i < array_size; i++) { nu16a[i] = htons(array[i]); } opt_len = sizeof(uint16_t) * array_size; res = tlv_add(msg, opt_type, opt_len, nu16a); free(nu16a); return (res); } int tlv_add_supported_options(struct dynar *msg, const enum tlv_opt_type *supported_options, size_t no_supported_options) { uint16_t *u16a; size_t i; int res; u16a = malloc(sizeof(*u16a) * no_supported_options); if (u16a == NULL) { return (-1); } for (i = 0; i < no_supported_options; i++) { u16a[i] = (uint16_t)supported_options[i]; } res = (tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_OPTIONS, u16a, no_supported_options)); free(u16a); return (res); } int tlv_add_supported_decision_algorithms(struct dynar *msg, const enum tlv_decision_algorithm_type *supported_algorithms, size_t no_supported_algorithms) { uint16_t *u16a; size_t i; int res; u16a = malloc(sizeof(*u16a) * no_supported_algorithms); if (u16a == NULL) { return (-1); } for (i = 0; i < no_supported_algorithms; i++) { u16a[i] = (uint16_t)supported_algorithms[i]; } res = (tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_DECISION_ALGORITHMS, u16a, no_supported_algorithms)); free(u16a); return (res); } int tlv_add_reply_error_code(struct dynar *msg, enum tlv_reply_error_code error_code) { return (tlv_add_u16(msg, TLV_OPT_REPLY_ERROR_CODE, (uint16_t)error_code)); } int tlv_add_server_maximum_request_size(struct dynar *msg, size_t server_maximum_request_size) { return (tlv_add_u32(msg, TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE, server_maximum_request_size)); } int tlv_add_server_maximum_reply_size(struct dynar *msg, size_t server_maximum_reply_size) { return (tlv_add_u32(msg, TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE, server_maximum_reply_size)); } int tlv_add_node_id(struct dynar *msg, uint32_t node_id) { return (tlv_add_u32(msg, TLV_OPT_NODE_ID, node_id)); } int tlv_add_decision_algorithm(struct dynar *msg, enum tlv_decision_algorithm_type decision_algorithm) { return (tlv_add_u16(msg, TLV_OPT_DECISION_ALGORITHM, (uint16_t)decision_algorithm)); } int tlv_add_heartbeat_interval(struct dynar *msg, uint32_t heartbeat_interval) { return (tlv_add_u32(msg, TLV_OPT_HEARTBEAT_INTERVAL, heartbeat_interval)); } int tlv_add_ring_id(struct dynar *msg, const struct tlv_ring_id *ring_id) { uint64_t nu64; uint32_t nu32; char tmp_buf[12]; nu32 = htonl(ring_id->node_id); nu64 = htobe64(ring_id->seq); memcpy(tmp_buf, &nu32, sizeof(nu32)); memcpy(tmp_buf + sizeof(nu32), &nu64, sizeof(nu64)); return (tlv_add(msg, TLV_OPT_RING_ID, sizeof(tmp_buf), tmp_buf)); } int tlv_add_tie_breaker(struct dynar *msg, const struct tlv_tie_breaker *tie_breaker) { uint32_t nu32; uint8_t u8; char tmp_buf[5]; u8 = tie_breaker->mode; nu32 = (tie_breaker->mode == TLV_TIE_BREAKER_MODE_NODE_ID ? htonl(tie_breaker->node_id) : 0); memcpy(tmp_buf, &u8, sizeof(u8)); memcpy(tmp_buf + sizeof(u8), &nu32, sizeof(nu32)); return (tlv_add(msg, TLV_OPT_TIE_BREAKER, sizeof(tmp_buf), tmp_buf)); } int tlv_add_config_version(struct dynar *msg, uint64_t config_version) { return (tlv_add_u64(msg, TLV_OPT_CONFIG_VERSION, config_version)); } int tlv_add_data_center_id(struct dynar *msg, uint32_t data_center_id) { return (tlv_add_u32(msg, TLV_OPT_DATA_CENTER_ID, data_center_id)); } int tlv_add_node_state(struct dynar *msg, enum tlv_node_state node_state) { return (tlv_add_u8(msg, TLV_OPT_NODE_STATE, node_state)); } int tlv_add_node_info(struct dynar *msg, const struct tlv_node_info *node_info) { struct dynar opt_value; int res; res = 0; /* * Create sub message, */ dynar_init(&opt_value, 1024); if ((res = tlv_add_node_id(&opt_value, node_info->node_id)) != 0) { goto exit_dynar_destroy; } if (node_info->data_center_id != 0) { if ((res = tlv_add_data_center_id(&opt_value, node_info->data_center_id)) != 0) { goto exit_dynar_destroy; } } if (node_info->node_state != TLV_NODE_STATE_NOT_SET) { if ((res = tlv_add_node_state(&opt_value, node_info->node_state)) != 0) { goto exit_dynar_destroy; } } res = tlv_add(msg, TLV_OPT_NODE_INFO, dynar_size(&opt_value), dynar_data(&opt_value)); if (res != 0) { goto exit_dynar_destroy; } exit_dynar_destroy: dynar_destroy(&opt_value); return (res); } int tlv_add_node_list_type(struct dynar *msg, enum tlv_node_list_type node_list_type) { return (tlv_add_u8(msg, TLV_OPT_NODE_LIST_TYPE, node_list_type)); } int tlv_add_vote(struct dynar *msg, enum tlv_vote vote) { return (tlv_add_u8(msg, TLV_OPT_VOTE, vote)); } int tlv_add_quorate(struct dynar *msg, enum tlv_quorate quorate) { return (tlv_add_u8(msg, TLV_OPT_QUORATE, quorate)); } void tlv_iter_init_str(const char *msg, size_t msg_len, size_t msg_header_len, struct tlv_iterator *tlv_iter) { tlv_iter->msg = msg; tlv_iter->msg_len = msg_len; tlv_iter->current_pos = 0; tlv_iter->msg_header_len = msg_header_len; tlv_iter->iter_next_called = 0; } void tlv_iter_init(const struct dynar *msg, size_t msg_header_len, struct tlv_iterator *tlv_iter) { tlv_iter_init_str(dynar_data(msg), dynar_size(msg), msg_header_len, tlv_iter); } enum tlv_opt_type tlv_iter_get_type(const struct tlv_iterator *tlv_iter) { uint16_t ntype; uint16_t type; memcpy(&ntype, tlv_iter->msg + tlv_iter->current_pos, sizeof(ntype)); type = ntohs(ntype); return (type); } uint16_t tlv_iter_get_len(const struct tlv_iterator *tlv_iter) { uint16_t nlen; uint16_t len; memcpy(&nlen, tlv_iter->msg + tlv_iter->current_pos + TLV_TYPE_LENGTH, sizeof(nlen)); len = ntohs(nlen); return (len); } const char * tlv_iter_get_data(const struct tlv_iterator *tlv_iter) { return (tlv_iter->msg + tlv_iter->current_pos + TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH); } int tlv_iter_next(struct tlv_iterator *tlv_iter) { uint16_t len; if (tlv_iter->iter_next_called == 0) { tlv_iter->iter_next_called = 1; tlv_iter->current_pos = tlv_iter->msg_header_len; goto check_tlv_validity; } len = tlv_iter_get_len(tlv_iter); if (tlv_iter->current_pos + TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH + len >= tlv_iter->msg_len) { return (0); } tlv_iter->current_pos += TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH + len; check_tlv_validity: /* * Check if tlv is valid = is not larger than whole message */ len = tlv_iter_get_len(tlv_iter); if (tlv_iter->current_pos + TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH + len > tlv_iter->msg_len) { return (-1); } return (1); } int tlv_iter_decode_u32(struct tlv_iterator *tlv_iter, uint32_t *res) { const char *opt_data; uint16_t opt_len; uint32_t nu32; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); if (opt_len != sizeof(nu32)) { return (-1); } memcpy(&nu32, opt_data, sizeof(nu32)); *res = ntohl(nu32); return (0); } int tlv_iter_decode_u8(struct tlv_iterator *tlv_iter, uint8_t *res) { const char *opt_data; uint16_t opt_len; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); if (opt_len != sizeof(*res)) { return (-1); } memcpy(res, opt_data, sizeof(*res)); return (0); } int tlv_iter_decode_client_cert_required(struct tlv_iterator *tlv_iter, uint8_t *client_cert_required) { return (tlv_iter_decode_u8(tlv_iter, client_cert_required)); } int tlv_iter_decode_str(struct tlv_iterator *tlv_iter, char **str, size_t *str_len) { const char *opt_data; uint16_t opt_len; char *tmp_str; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); tmp_str = malloc(opt_len + 1); if (tmp_str == NULL) { return (-1); } memcpy(tmp_str, opt_data, opt_len); tmp_str[opt_len] = '\0'; *str = tmp_str; *str_len = opt_len; return (0); } int tlv_iter_decode_u16_array(struct tlv_iterator *tlv_iter, uint16_t **u16a, size_t *no_items) { uint16_t opt_len; uint16_t *u16a_res; size_t i; opt_len = tlv_iter_get_len(tlv_iter); if (opt_len % sizeof(uint16_t) != 0) { return (-1); } *no_items = opt_len / sizeof(uint16_t); u16a_res = malloc(sizeof(uint16_t) * *no_items); if (u16a_res == NULL) { return (-2); } memcpy(u16a_res, tlv_iter_get_data(tlv_iter), opt_len); for (i = 0; i < *no_items; i++) { u16a_res[i] = ntohs(u16a_res[i]); } *u16a = u16a_res; return (0); } int tlv_iter_decode_supported_options(struct tlv_iterator *tlv_iter, enum tlv_opt_type **supported_options, size_t *no_supported_options) { uint16_t *u16a; enum tlv_opt_type *tlv_opt_array; size_t i; int res; res = tlv_iter_decode_u16_array(tlv_iter, &u16a, no_supported_options); if (res != 0) { return (res); } tlv_opt_array = malloc(sizeof(enum tlv_opt_type) * *no_supported_options); if (tlv_opt_array == NULL) { free(u16a); return (-2); } for (i = 0; i < *no_supported_options; i++) { tlv_opt_array[i] = (enum tlv_opt_type)u16a[i]; } free(u16a); *supported_options = tlv_opt_array; return (0); } int tlv_iter_decode_supported_decision_algorithms(struct tlv_iterator *tlv_iter, enum tlv_decision_algorithm_type **supported_decision_algorithms, size_t *no_supported_decision_algorithms) { uint16_t *u16a; enum tlv_decision_algorithm_type *tlv_decision_algorithm_type_array; size_t i; int res; res = tlv_iter_decode_u16_array(tlv_iter, &u16a, no_supported_decision_algorithms); if (res != 0) { return (res); } tlv_decision_algorithm_type_array = malloc( sizeof(enum tlv_decision_algorithm_type) * *no_supported_decision_algorithms); if (tlv_decision_algorithm_type_array == NULL) { free(u16a); return (-2); } for (i = 0; i < *no_supported_decision_algorithms; i++) { tlv_decision_algorithm_type_array[i] = (enum tlv_decision_algorithm_type)u16a[i]; } free(u16a); *supported_decision_algorithms = tlv_decision_algorithm_type_array; return (0); } int tlv_iter_decode_u16(struct tlv_iterator *tlv_iter, uint16_t *u16) { const char *opt_data; uint16_t opt_len; uint16_t nu16; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); if (opt_len != sizeof(nu16)) { return (-1); } memcpy(&nu16, opt_data, sizeof(nu16)); *u16 = ntohs(nu16); return (0); } int tlv_iter_decode_u64(struct tlv_iterator *tlv_iter, uint64_t *u64) { const char *opt_data; uint64_t opt_len; uint64_t nu64; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); if (opt_len != sizeof(nu64)) { return (-1); } memcpy(&nu64, opt_data, sizeof(nu64)); *u64 = be64toh(nu64); return (0); } int tlv_iter_decode_reply_error_code(struct tlv_iterator *tlv_iter, enum tlv_reply_error_code *reply_error_code) { return (tlv_iter_decode_u16(tlv_iter, (uint16_t *)reply_error_code)); } int tlv_iter_decode_tls_supported(struct tlv_iterator *tlv_iter, enum tlv_tls_supported *tls_supported) { uint8_t u8; enum tlv_tls_supported tmp_tls_supported; if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) { return (-1); } tmp_tls_supported = u8; if (tmp_tls_supported != TLV_TLS_UNSUPPORTED && tmp_tls_supported != TLV_TLS_SUPPORTED && tmp_tls_supported != TLV_TLS_REQUIRED) { return (-4); } *tls_supported = tmp_tls_supported; return (0); } int tlv_iter_decode_decision_algorithm(struct tlv_iterator *tlv_iter, enum tlv_decision_algorithm_type *decision_algorithm) { uint16_t u16; if (tlv_iter_decode_u16(tlv_iter, &u16) != 0) { return (-1); } *decision_algorithm = (enum tlv_decision_algorithm_type)u16; return (0); } int tlv_iter_decode_ring_id(struct tlv_iterator *tlv_iter, struct tlv_ring_id *ring_id) { const char *opt_data; uint16_t opt_len; uint32_t nu32; uint64_t nu64; char tmp_buf[12]; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); if (opt_len != sizeof(tmp_buf)) { return (-1); } memcpy(&nu32, opt_data, sizeof(nu32)); memcpy(&nu64, opt_data + sizeof(nu32), sizeof(nu64)); ring_id->node_id = ntohl(nu32); ring_id->seq = be64toh(nu64); return (0); } int tlv_iter_decode_tie_breaker(struct tlv_iterator *tlv_iter, struct tlv_tie_breaker *tie_breaker) { const char *opt_data; uint16_t opt_len; uint32_t nu32; uint8_t u8; enum tlv_tie_breaker_mode tie_breaker_mode; char tmp_buf[5]; opt_len = tlv_iter_get_len(tlv_iter); opt_data = tlv_iter_get_data(tlv_iter); if (opt_len != sizeof(tmp_buf)) { return (-1); } memcpy(&u8, opt_data, sizeof(u8)); tie_breaker_mode = u8; if (tie_breaker_mode != TLV_TIE_BREAKER_MODE_LOWEST && tie_breaker_mode != TLV_TIE_BREAKER_MODE_HIGHEST && tie_breaker_mode != TLV_TIE_BREAKER_MODE_NODE_ID) { return (-4); } memcpy(&nu32, opt_data + sizeof(u8), sizeof(nu32)); tie_breaker->mode = tie_breaker_mode; tie_breaker->node_id = (tie_breaker->mode == TLV_TIE_BREAKER_MODE_NODE_ID ? ntohl(nu32) : 0); return (0); } int tlv_iter_decode_node_state(struct tlv_iterator *tlv_iter, enum tlv_node_state *node_state) { uint8_t u8; enum tlv_node_state tmp_node_state; if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) { return (-1); } tmp_node_state = u8; if (tmp_node_state != TLV_NODE_STATE_MEMBER && tmp_node_state != TLV_NODE_STATE_DEAD && tmp_node_state != TLV_NODE_STATE_LEAVING) { return (-4); } *node_state = tmp_node_state; return (0); } int tlv_iter_decode_node_info(struct tlv_iterator *tlv_iter, struct tlv_node_info *node_info) { struct tlv_iterator data_tlv_iter; int iter_res; int res; enum tlv_opt_type opt_type; struct tlv_node_info tmp_node_info; memset(&tmp_node_info, 0, sizeof(tmp_node_info)); tlv_iter_init_str(tlv_iter_get_data(tlv_iter), tlv_iter_get_len(tlv_iter), 0, &data_tlv_iter); while ((iter_res = tlv_iter_next(&data_tlv_iter)) > 0) { opt_type = tlv_iter_get_type(&data_tlv_iter); switch (opt_type) { case TLV_OPT_NODE_ID: if ((res = tlv_iter_decode_u32(&data_tlv_iter, &tmp_node_info.node_id)) != 0) { return (res); } break; case TLV_OPT_DATA_CENTER_ID: if ((res = tlv_iter_decode_u32(&data_tlv_iter, &tmp_node_info.data_center_id)) != 0) { return (res); } break; case TLV_OPT_NODE_STATE: if ((res = tlv_iter_decode_node_state(&data_tlv_iter, &tmp_node_info.node_state)) != 0) { return (res); } break; default: /* * Other options are not processed */ break; } } if (iter_res != 0) { return (-3); } if (tmp_node_info.node_id == 0) { return (-4); } memcpy(node_info, &tmp_node_info, sizeof(tmp_node_info)); return (0); } int tlv_iter_decode_node_list_type(struct tlv_iterator *tlv_iter, enum tlv_node_list_type *node_list_type) { uint8_t u8; enum tlv_node_list_type tmp_node_list_type; if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) { return (-1); } tmp_node_list_type = u8; if (tmp_node_list_type != TLV_NODE_LIST_TYPE_INITIAL_CONFIG && tmp_node_list_type != TLV_NODE_LIST_TYPE_CHANGED_CONFIG && tmp_node_list_type != TLV_NODE_LIST_TYPE_MEMBERSHIP && tmp_node_list_type != TLV_NODE_LIST_TYPE_QUORUM) { return (-4); } *node_list_type = tmp_node_list_type; return (0); } int tlv_iter_decode_vote(struct tlv_iterator *tlv_iter, enum tlv_vote *vote) { uint8_t u8; enum tlv_vote tmp_vote; if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) { return (-1); } tmp_vote = u8; if (tmp_vote != TLV_VOTE_ACK && tmp_vote != TLV_VOTE_NACK && tmp_vote != TLV_VOTE_ASK_LATER && tmp_vote != TLV_VOTE_WAIT_FOR_REPLY && tmp_vote != TLV_VOTE_NO_CHANGE) { return (-4); } *vote = tmp_vote; return (0); } int tlv_iter_decode_quorate(struct tlv_iterator *tlv_iter, enum tlv_quorate *quorate) { uint8_t u8; enum tlv_quorate tmp_quorate; if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) { return (-1); } tmp_quorate = u8; if (tmp_quorate != TLV_QUORATE_QUORATE && tmp_quorate != TLV_QUORATE_INQUORATE) { return (-4); } *quorate = tmp_quorate; return (0); } void tlv_get_supported_options(enum tlv_opt_type **supported_options, size_t *no_supported_options) { *supported_options = tlv_static_supported_options; *no_supported_options = TLV_STATIC_SUPPORTED_OPTIONS_SIZE; } int tlv_ring_id_eq(const struct tlv_ring_id *rid1, const struct tlv_ring_id *rid2) { return (rid1->node_id == rid2->node_id && rid1->seq == rid2->seq); } int tlv_tie_breaker_eq(const struct tlv_tie_breaker *tb1, const struct tlv_tie_breaker *tb2) { if (tb1->mode == tb2->mode && tb1->mode == TLV_TIE_BREAKER_MODE_NODE_ID) { return (tb1->node_id == tb2->node_id); } return (tb1->mode == tb2->mode); } const char * tlv_vote_to_str(enum tlv_vote vote) { switch (vote) { case TLV_VOTE_UNDEFINED: break; case TLV_VOTE_ACK: return ("ACK"); break; case TLV_VOTE_NACK: return ("NACK"); break; case TLV_VOTE_ASK_LATER: return ("Ask later"); break; case TLV_VOTE_WAIT_FOR_REPLY: return ("Wait for reply"); break; case TLV_VOTE_NO_CHANGE: return ("No change"); break; } return ("Unknown vote value"); } const char * tlv_node_state_to_str(enum tlv_node_state state) { switch (state) { case TLV_NODE_STATE_NOT_SET: return ("not set"); break; case TLV_NODE_STATE_MEMBER: return ("member"); break; case TLV_NODE_STATE_DEAD: return ("dead"); break; case TLV_NODE_STATE_LEAVING: return ("leaving"); break; } return ("Unhandled node state"); } const char * tlv_tls_supported_to_str(enum tlv_tls_supported tls_supported) { switch (tls_supported) { case TLV_TLS_UNSUPPORTED: return ("Unsupported"); break; case TLV_TLS_SUPPORTED: return ("Supported"); break; case TLV_TLS_REQUIRED: return ("Required"); break; } return ("Unhandled tls supported state"); } const char * tlv_decision_algorithm_type_to_str(enum tlv_decision_algorithm_type algorithm) { switch (algorithm) { case TLV_DECISION_ALGORITHM_TYPE_TEST: return ("Test"); break; case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT: return ("Fifty-Fifty split"); break; case TLV_DECISION_ALGORITHM_TYPE_2NODELMS: return ("2 Node LMS"); break; case TLV_DECISION_ALGORITHM_TYPE_LMS: return ("LMS"); break; } return ("Unknown algorithm"); } diff --git a/qdevices/unix-socket.c b/qdevices/unix-socket.c index c62de6ea..cfe5c841 100644 --- a/qdevices/unix-socket.c +++ b/qdevices/unix-socket.c @@ -1,205 +1,205 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "unix-socket.h" static int unix_socket_set_non_blocking(int fd) { int flags; flags = fcntl(fd, F_GETFL, NULL); if (flags < 0) { return (-1); } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) { return (-1); - } + } - return (0); + return (0); } int unix_socket_server_create(const char *path, int non_blocking, int backlog) { int s; struct sockaddr_un sun; if (strlen(path) >= sizeof(sun.sun_path)) { errno = ENAMETOOLONG; return (-1); } if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { return (-1); } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; strncpy(sun.sun_path, path, sizeof(sun.sun_path)); unlink(path); if (bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) != 0) { close(s); return (-1); } if (non_blocking) { if (unix_socket_set_non_blocking(s) != 0) { close(s); return (-1); } } if (listen(s, backlog) != 0) { close(s); return (-1); } return (s); } int unix_socket_client_create(const char *path, int non_blocking) { int s; struct sockaddr_un sun; if (strlen(path) >= sizeof(sun.sun_path)) { errno = ENAMETOOLONG; return (-1); } if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { return (-1); } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; strncpy(sun.sun_path, path, sizeof(sun.sun_path)); if (non_blocking) { if (unix_socket_set_non_blocking(s) != 0) { close(s); return (-1); } } if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) != 0) { close(s); return (-1); } return (s); } int unix_socket_server_destroy(int sock, const char *path) { int res; res = 0; if (close(sock) != 0) { res = -1; } if (unlink(path) != 0) { res = -1; } return (res); } int unix_socket_server_accept(int sock, int non_blocking) { struct sockaddr_un sun; socklen_t sun_len; int client_sock; sun_len = sizeof(sun); if ((client_sock = accept(sock, (struct sockaddr *)&sun, &sun_len)) < 0) { return (-1); } if (non_blocking) { if (unix_socket_set_non_blocking(client_sock) != 0) { close(client_sock); return (-1); } } return (client_sock); } int unix_socket_close(int sock) { return (close(sock)); } ssize_t unix_socket_read(int sock, void *buf, size_t len) { return (recv(sock, buf, len, 0)); } ssize_t unix_socket_write(int sock, void *buf, size_t len) { return (send(sock, buf, len, 0)); }